relay-runtime 13.0.3 → 13.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +1 -1
- package/index.js.flow +4 -2
- package/lib/index.js +3 -3
- package/lib/mutations/readUpdatableQuery_EXPERIMENTAL.js +3 -4
- package/lib/query/GraphQLTag.js +13 -0
- package/lib/query/fetchQuery.js +2 -5
- package/lib/store/RelayModernEnvironment.js +3 -3
- package/lib/store/RelayModernFragmentSpecResolver.js +6 -6
- package/lib/store/RelayModernSelector.js +16 -2
- package/lib/store/RelayReader.js +71 -6
- package/lib/store/RelayStoreSubscriptions.js +4 -2
- package/lib/store/RelayStoreUtils.js +1 -0
- package/lib/store/ResolverCache.js +25 -13
- package/lib/store/experimental-live-resolvers/LiveResolverCache.js +316 -0
- package/lib/store/experimental-live-resolvers/LiveResolverStore.js +787 -0
- package/lib/store/hasOverlappingIDs.js +1 -1
- package/lib/util/RelayConcreteNode.js +2 -0
- package/lib/util/RelayFeatureFlags.js +0 -3
- package/lib/util/handlePotentialSnapshotErrors.js +73 -0
- package/mutations/RelayRecordSourceProxy.js.flow +7 -3
- package/mutations/RelayRecordSourceSelectorProxy.js.flow +7 -3
- package/mutations/readUpdatableQuery_EXPERIMENTAL.js.flow +14 -12
- package/network/RelayNetworkTypes.js.flow +1 -1
- package/package.json +1 -1
- package/query/GraphQLTag.js.flow +38 -3
- package/query/fetchQuery.js.flow +6 -4
- package/relay-runtime.js +2 -2
- package/relay-runtime.min.js +2 -2
- package/store/RelayModernEnvironment.js.flow +3 -3
- package/store/RelayModernFragmentSpecResolver.js.flow +11 -7
- package/store/RelayModernSelector.js.flow +32 -8
- package/store/RelayReader.js.flow +65 -23
- package/store/RelayResponseNormalizer.js.flow +1 -1
- package/store/RelayStoreSubscriptions.js.flow +2 -0
- package/store/RelayStoreTypes.js.flow +22 -8
- package/store/RelayStoreUtils.js.flow +2 -1
- package/store/ResolverCache.js.flow +45 -10
- package/store/defaultGetDataID.js.flow +1 -1
- package/store/experimental-live-resolvers/LiveResolverCache.js.flow +410 -0
- package/store/experimental-live-resolvers/LiveResolverStore.js.flow +822 -0
- package/store/hasOverlappingIDs.js.flow +1 -1
- package/util/ReaderNode.js.flow +17 -1
- package/util/RelayConcreteNode.js.flow +9 -1
- package/util/RelayFeatureFlags.js.flow +0 -6
- package/util/RelayRuntimeTypes.js.flow +20 -1
- package/util/deepFreeze.js.flow +1 -1
- package/util/handlePotentialSnapshotErrors.js.flow +63 -0
- package/util/isEmptyObject.js.flow +1 -1
- package/lib/util/reportMissingRequiredFields.js +0 -48
- package/util/reportMissingRequiredFields.js.flow +0 -51
|
@@ -0,0 +1,787 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
// flowlint ambiguous-object-type:error
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
14
|
+
|
|
15
|
+
var _createForOfIteratorHelper2 = _interopRequireDefault(require("@babel/runtime/helpers/createForOfIteratorHelper"));
|
|
16
|
+
|
|
17
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
18
|
+
|
|
19
|
+
var _require = require('../../multi-actor-environment/ActorIdentifier'),
|
|
20
|
+
INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE = _require.INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE,
|
|
21
|
+
assertInternalActorIndentifier = _require.assertInternalActorIndentifier;
|
|
22
|
+
|
|
23
|
+
var deepFreeze = require('../../util/deepFreeze');
|
|
24
|
+
|
|
25
|
+
var RelayFeatureFlags = require('../../util/RelayFeatureFlags');
|
|
26
|
+
|
|
27
|
+
var resolveImmediate = require('../../util/resolveImmediate');
|
|
28
|
+
|
|
29
|
+
var DataChecker = require('../DataChecker');
|
|
30
|
+
|
|
31
|
+
var defaultGetDataID = require('../defaultGetDataID');
|
|
32
|
+
|
|
33
|
+
var RelayModernRecord = require('../RelayModernRecord');
|
|
34
|
+
|
|
35
|
+
var RelayOptimisticRecordSource = require('../RelayOptimisticRecordSource');
|
|
36
|
+
|
|
37
|
+
var RelayReader = require('../RelayReader');
|
|
38
|
+
|
|
39
|
+
var RelayReferenceMarker = require('../RelayReferenceMarker');
|
|
40
|
+
|
|
41
|
+
var RelayStoreReactFlightUtils = require('../RelayStoreReactFlightUtils');
|
|
42
|
+
|
|
43
|
+
var RelayStoreSubscriptions = require('../RelayStoreSubscriptions');
|
|
44
|
+
|
|
45
|
+
var RelayStoreUtils = require('../RelayStoreUtils');
|
|
46
|
+
|
|
47
|
+
var _require2 = require('../RelayStoreUtils'),
|
|
48
|
+
ROOT_ID = _require2.ROOT_ID,
|
|
49
|
+
ROOT_TYPE = _require2.ROOT_TYPE;
|
|
50
|
+
|
|
51
|
+
var _require3 = require('./LiveResolverCache'),
|
|
52
|
+
LiveResolverCache = _require3.LiveResolverCache;
|
|
53
|
+
|
|
54
|
+
var invariant = require('invariant');
|
|
55
|
+
|
|
56
|
+
var DEFAULT_RELEASE_BUFFER_SIZE = 10;
|
|
57
|
+
/**
|
|
58
|
+
* @public
|
|
59
|
+
*
|
|
60
|
+
* Experimental fork of RelayModernStore
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
var LiveResolverStore = /*#__PURE__*/function () {
|
|
64
|
+
function LiveResolverStore(source, options) {
|
|
65
|
+
var _this = this;
|
|
66
|
+
|
|
67
|
+
var _options$gcReleaseBuf, _options$gcScheduler, _options$getDataID, _options$log, _options$operationLoa;
|
|
68
|
+
|
|
69
|
+
(0, _defineProperty2["default"])(this, "_gcStep", function () {
|
|
70
|
+
if (_this._gcRun) {
|
|
71
|
+
if (_this._gcRun.next().done) {
|
|
72
|
+
_this._gcRun = null;
|
|
73
|
+
} else {
|
|
74
|
+
_this._gcScheduler(_this._gcStep);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Prevent mutation of a record from outside the store.
|
|
80
|
+
if (process.env.NODE_ENV !== "production") {
|
|
81
|
+
var storeIDs = source.getRecordIDs();
|
|
82
|
+
|
|
83
|
+
for (var ii = 0; ii < storeIDs.length; ii++) {
|
|
84
|
+
var record = source.get(storeIDs[ii]);
|
|
85
|
+
|
|
86
|
+
if (record) {
|
|
87
|
+
RelayModernRecord.freeze(record);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
this._currentWriteEpoch = 0;
|
|
93
|
+
this._gcHoldCounter = 0;
|
|
94
|
+
this._gcReleaseBufferSize = (_options$gcReleaseBuf = options === null || options === void 0 ? void 0 : options.gcReleaseBufferSize) !== null && _options$gcReleaseBuf !== void 0 ? _options$gcReleaseBuf : DEFAULT_RELEASE_BUFFER_SIZE;
|
|
95
|
+
this._gcRun = null;
|
|
96
|
+
this._gcScheduler = (_options$gcScheduler = options === null || options === void 0 ? void 0 : options.gcScheduler) !== null && _options$gcScheduler !== void 0 ? _options$gcScheduler : resolveImmediate;
|
|
97
|
+
this._getDataID = (_options$getDataID = options === null || options === void 0 ? void 0 : options.getDataID) !== null && _options$getDataID !== void 0 ? _options$getDataID : defaultGetDataID;
|
|
98
|
+
this._globalInvalidationEpoch = null;
|
|
99
|
+
this._invalidationSubscriptions = new Set();
|
|
100
|
+
this._invalidatedRecordIDs = new Set();
|
|
101
|
+
this.__log = (_options$log = options === null || options === void 0 ? void 0 : options.log) !== null && _options$log !== void 0 ? _options$log : null;
|
|
102
|
+
this._queryCacheExpirationTime = options === null || options === void 0 ? void 0 : options.queryCacheExpirationTime;
|
|
103
|
+
this._operationLoader = (_options$operationLoa = options === null || options === void 0 ? void 0 : options.operationLoader) !== null && _options$operationLoa !== void 0 ? _options$operationLoa : null;
|
|
104
|
+
this._optimisticSource = null;
|
|
105
|
+
this._recordSource = source;
|
|
106
|
+
this._releaseBuffer = [];
|
|
107
|
+
this._roots = new Map();
|
|
108
|
+
this._shouldScheduleGC = false;
|
|
109
|
+
this._resolverCache = new LiveResolverCache(function () {
|
|
110
|
+
return _this._getMutableRecordSource();
|
|
111
|
+
}, this);
|
|
112
|
+
this._storeSubscriptions = new RelayStoreSubscriptions(options === null || options === void 0 ? void 0 : options.log, this._resolverCache);
|
|
113
|
+
this._updatedRecordIDs = new Set();
|
|
114
|
+
this._shouldProcessClientComponents = options === null || options === void 0 ? void 0 : options.shouldProcessClientComponents;
|
|
115
|
+
initializeRecordSource(this._recordSource);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
var _proto = LiveResolverStore.prototype;
|
|
119
|
+
|
|
120
|
+
_proto.getSource = function getSource() {
|
|
121
|
+
var _this$_optimisticSour;
|
|
122
|
+
|
|
123
|
+
return (_this$_optimisticSour = this._optimisticSource) !== null && _this$_optimisticSour !== void 0 ? _this$_optimisticSour : this._recordSource;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
_proto._getMutableRecordSource = function _getMutableRecordSource() {
|
|
127
|
+
var _this$_optimisticSour2;
|
|
128
|
+
|
|
129
|
+
return (_this$_optimisticSour2 = this._optimisticSource) !== null && _this$_optimisticSour2 !== void 0 ? _this$_optimisticSour2 : this._recordSource;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
_proto.check = function check(operation, options) {
|
|
133
|
+
var _options$handlers, _options$getSourceFor, _options$getTargetFor, _options$defaultActor;
|
|
134
|
+
|
|
135
|
+
var selector = operation.root;
|
|
136
|
+
|
|
137
|
+
var source = this._getMutableRecordSource();
|
|
138
|
+
|
|
139
|
+
var globalInvalidationEpoch = this._globalInvalidationEpoch;
|
|
140
|
+
|
|
141
|
+
var rootEntry = this._roots.get(operation.request.identifier);
|
|
142
|
+
|
|
143
|
+
var operationLastWrittenAt = rootEntry != null ? rootEntry.epoch : null; // Check if store has been globally invalidated
|
|
144
|
+
|
|
145
|
+
if (globalInvalidationEpoch != null) {
|
|
146
|
+
// If so, check if the operation we're checking was last written
|
|
147
|
+
// before or after invalidation occurred.
|
|
148
|
+
if (operationLastWrittenAt == null || operationLastWrittenAt <= globalInvalidationEpoch) {
|
|
149
|
+
// If the operation was written /before/ global invalidation occurred,
|
|
150
|
+
// or if this operation has never been written to the store before,
|
|
151
|
+
// we will consider the data for this operation to be stale
|
|
152
|
+
// (i.e. not resolvable from the store).
|
|
153
|
+
return {
|
|
154
|
+
status: 'stale'
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
var handlers = (_options$handlers = options === null || options === void 0 ? void 0 : options.handlers) !== null && _options$handlers !== void 0 ? _options$handlers : [];
|
|
160
|
+
var getSourceForActor = (_options$getSourceFor = options === null || options === void 0 ? void 0 : options.getSourceForActor) !== null && _options$getSourceFor !== void 0 ? _options$getSourceFor : function (actorIdentifier) {
|
|
161
|
+
assertInternalActorIndentifier(actorIdentifier);
|
|
162
|
+
return source;
|
|
163
|
+
};
|
|
164
|
+
var getTargetForActor = (_options$getTargetFor = options === null || options === void 0 ? void 0 : options.getTargetForActor) !== null && _options$getTargetFor !== void 0 ? _options$getTargetFor : function (actorIdentifier) {
|
|
165
|
+
assertInternalActorIndentifier(actorIdentifier);
|
|
166
|
+
return source;
|
|
167
|
+
};
|
|
168
|
+
var operationAvailability = DataChecker.check(getSourceForActor, getTargetForActor, (_options$defaultActor = options === null || options === void 0 ? void 0 : options.defaultActorIdentifier) !== null && _options$defaultActor !== void 0 ? _options$defaultActor : INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE, selector, handlers, this._operationLoader, this._getDataID, this._shouldProcessClientComponents);
|
|
169
|
+
return getAvailabilityStatus(operationAvailability, operationLastWrittenAt, rootEntry === null || rootEntry === void 0 ? void 0 : rootEntry.fetchTime, this._queryCacheExpirationTime);
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
_proto.retain = function retain(operation) {
|
|
173
|
+
var _this2 = this;
|
|
174
|
+
|
|
175
|
+
var id = operation.request.identifier;
|
|
176
|
+
var disposed = false;
|
|
177
|
+
|
|
178
|
+
var dispose = function dispose() {
|
|
179
|
+
// Ensure each retain can only dispose once
|
|
180
|
+
if (disposed) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
disposed = true; // For Flow: guard against the entry somehow not existing
|
|
185
|
+
|
|
186
|
+
var rootEntry = _this2._roots.get(id);
|
|
187
|
+
|
|
188
|
+
if (rootEntry == null) {
|
|
189
|
+
return;
|
|
190
|
+
} // Decrement the ref count: if it becomes zero it is eligible
|
|
191
|
+
// for release.
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
rootEntry.refCount--;
|
|
195
|
+
|
|
196
|
+
if (rootEntry.refCount === 0) {
|
|
197
|
+
var _queryCacheExpirationTime = _this2._queryCacheExpirationTime;
|
|
198
|
+
|
|
199
|
+
var rootEntryIsStale = rootEntry.fetchTime != null && _queryCacheExpirationTime != null && rootEntry.fetchTime <= Date.now() - _queryCacheExpirationTime;
|
|
200
|
+
|
|
201
|
+
if (rootEntryIsStale) {
|
|
202
|
+
_this2._roots["delete"](id);
|
|
203
|
+
|
|
204
|
+
_this2.scheduleGC();
|
|
205
|
+
} else {
|
|
206
|
+
_this2._releaseBuffer.push(id); // If the release buffer is now over-full, remove the least-recently
|
|
207
|
+
// added entry and schedule a GC. Note that all items in the release
|
|
208
|
+
// buffer have a refCount of 0.
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
if (_this2._releaseBuffer.length > _this2._gcReleaseBufferSize) {
|
|
212
|
+
var _id = _this2._releaseBuffer.shift();
|
|
213
|
+
|
|
214
|
+
_this2._roots["delete"](_id);
|
|
215
|
+
|
|
216
|
+
_this2.scheduleGC();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
var rootEntry = this._roots.get(id);
|
|
223
|
+
|
|
224
|
+
if (rootEntry != null) {
|
|
225
|
+
if (rootEntry.refCount === 0) {
|
|
226
|
+
// This entry should be in the release buffer, but it no longer belongs
|
|
227
|
+
// there since it's retained. Remove it to maintain the invariant that
|
|
228
|
+
// all release buffer entries have a refCount of 0.
|
|
229
|
+
this._releaseBuffer = this._releaseBuffer.filter(function (_id) {
|
|
230
|
+
return _id !== id;
|
|
231
|
+
});
|
|
232
|
+
} // If we've previously retained this operation, increment the refCount
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
rootEntry.refCount += 1;
|
|
236
|
+
} else {
|
|
237
|
+
// Otherwise create a new entry for the operation
|
|
238
|
+
this._roots.set(id, {
|
|
239
|
+
operation: operation,
|
|
240
|
+
refCount: 1,
|
|
241
|
+
epoch: null,
|
|
242
|
+
fetchTime: null
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
dispose: dispose
|
|
248
|
+
};
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
_proto.lookup = function lookup(selector) {
|
|
252
|
+
var source = this.getSource();
|
|
253
|
+
var snapshot = RelayReader.read(source, selector, this._resolverCache);
|
|
254
|
+
|
|
255
|
+
if (process.env.NODE_ENV !== "production") {
|
|
256
|
+
deepFreeze(snapshot);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return snapshot;
|
|
260
|
+
} // This method will return a list of updated owners from the subscriptions
|
|
261
|
+
;
|
|
262
|
+
|
|
263
|
+
_proto.notify = function notify(sourceOperation, invalidateStore) {
|
|
264
|
+
var _this3 = this;
|
|
265
|
+
|
|
266
|
+
var log = this.__log;
|
|
267
|
+
|
|
268
|
+
if (log != null) {
|
|
269
|
+
log({
|
|
270
|
+
name: 'store.notify.start',
|
|
271
|
+
sourceOperation: sourceOperation
|
|
272
|
+
});
|
|
273
|
+
} // Increment the current write when notifying after executing
|
|
274
|
+
// a set of changes to the store.
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
this._currentWriteEpoch++;
|
|
278
|
+
|
|
279
|
+
if (invalidateStore === true) {
|
|
280
|
+
this._globalInvalidationEpoch = this._currentWriteEpoch;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (RelayFeatureFlags.ENABLE_RELAY_RESOLVERS) {
|
|
284
|
+
// When a record is updated, we need to also handle records that depend on it,
|
|
285
|
+
// specifically Relay Resolver result records containing results based on the
|
|
286
|
+
// updated records. This both adds to updatedRecordIDs and invalidates any
|
|
287
|
+
// cached data as needed.
|
|
288
|
+
this._resolverCache.invalidateDataIDs(this._updatedRecordIDs);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
var source = this.getSource();
|
|
292
|
+
var updatedOwners = [];
|
|
293
|
+
|
|
294
|
+
this._storeSubscriptions.updateSubscriptions(source, this._updatedRecordIDs, updatedOwners, sourceOperation);
|
|
295
|
+
|
|
296
|
+
this._invalidationSubscriptions.forEach(function (subscription) {
|
|
297
|
+
_this3._updateInvalidationSubscription(subscription, invalidateStore === true);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
if (log != null) {
|
|
301
|
+
log({
|
|
302
|
+
name: 'store.notify.complete',
|
|
303
|
+
sourceOperation: sourceOperation,
|
|
304
|
+
updatedRecordIDs: this._updatedRecordIDs,
|
|
305
|
+
invalidatedRecordIDs: this._invalidatedRecordIDs
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
this._updatedRecordIDs.clear();
|
|
310
|
+
|
|
311
|
+
this._invalidatedRecordIDs.clear(); // If a source operation was provided (indicating the operation
|
|
312
|
+
// that produced this update to the store), record the current epoch
|
|
313
|
+
// at which this operation was written.
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
if (sourceOperation != null) {
|
|
317
|
+
// We only track the epoch at which the operation was written if
|
|
318
|
+
// it was previously retained, to keep the size of our operation
|
|
319
|
+
// epoch map bounded. If a query wasn't retained, we assume it can
|
|
320
|
+
// may be deleted at any moment and thus is not relevant for us to track
|
|
321
|
+
// for the purposes of invalidation.
|
|
322
|
+
var id = sourceOperation.request.identifier;
|
|
323
|
+
|
|
324
|
+
var rootEntry = this._roots.get(id);
|
|
325
|
+
|
|
326
|
+
if (rootEntry != null) {
|
|
327
|
+
rootEntry.epoch = this._currentWriteEpoch;
|
|
328
|
+
rootEntry.fetchTime = Date.now();
|
|
329
|
+
} else if (sourceOperation.request.node.params.operationKind === 'query' && this._gcReleaseBufferSize > 0 && this._releaseBuffer.length < this._gcReleaseBufferSize) {
|
|
330
|
+
// The operation isn't retained but there is space in the release buffer:
|
|
331
|
+
// temporarily track this operation in case the data can be reused soon.
|
|
332
|
+
var temporaryRootEntry = {
|
|
333
|
+
operation: sourceOperation,
|
|
334
|
+
refCount: 0,
|
|
335
|
+
epoch: this._currentWriteEpoch,
|
|
336
|
+
fetchTime: Date.now()
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
this._releaseBuffer.push(id);
|
|
340
|
+
|
|
341
|
+
this._roots.set(id, temporaryRootEntry);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return updatedOwners;
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
_proto.publish = function publish(source, idsMarkedForInvalidation) {
|
|
349
|
+
var target = this._getMutableRecordSource();
|
|
350
|
+
|
|
351
|
+
updateTargetFromSource(target, source, // We increment the current epoch at the end of the set of updates,
|
|
352
|
+
// in notify(). Here, we pass what will be the incremented value of
|
|
353
|
+
// the epoch to use to write to invalidated records.
|
|
354
|
+
this._currentWriteEpoch + 1, idsMarkedForInvalidation, this._updatedRecordIDs, this._invalidatedRecordIDs); // NOTE: log *after* processing the source so that even if a bad log function
|
|
355
|
+
// mutates the source, it doesn't affect Relay processing of it.
|
|
356
|
+
|
|
357
|
+
var log = this.__log;
|
|
358
|
+
|
|
359
|
+
if (log != null) {
|
|
360
|
+
log({
|
|
361
|
+
name: 'store.publish',
|
|
362
|
+
source: source,
|
|
363
|
+
optimistic: target === this._optimisticSource
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
_proto.subscribe = function subscribe(snapshot, callback) {
|
|
369
|
+
return this._storeSubscriptions.subscribe(snapshot, callback);
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
_proto.holdGC = function holdGC() {
|
|
373
|
+
var _this4 = this;
|
|
374
|
+
|
|
375
|
+
if (this._gcRun) {
|
|
376
|
+
this._gcRun = null;
|
|
377
|
+
this._shouldScheduleGC = true;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
this._gcHoldCounter++;
|
|
381
|
+
|
|
382
|
+
var dispose = function dispose() {
|
|
383
|
+
if (_this4._gcHoldCounter > 0) {
|
|
384
|
+
_this4._gcHoldCounter--;
|
|
385
|
+
|
|
386
|
+
if (_this4._gcHoldCounter === 0 && _this4._shouldScheduleGC) {
|
|
387
|
+
_this4.scheduleGC();
|
|
388
|
+
|
|
389
|
+
_this4._shouldScheduleGC = false;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
return {
|
|
395
|
+
dispose: dispose
|
|
396
|
+
};
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
_proto.toJSON = function toJSON() {
|
|
400
|
+
return 'LiveResolverStore()';
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
_proto.getEpoch = function getEpoch() {
|
|
404
|
+
return this._currentWriteEpoch;
|
|
405
|
+
} // Internal API
|
|
406
|
+
;
|
|
407
|
+
|
|
408
|
+
_proto.__getUpdatedRecordIDs = function __getUpdatedRecordIDs() {
|
|
409
|
+
return this._updatedRecordIDs;
|
|
410
|
+
} // HACK
|
|
411
|
+
// This should return an InvalidationState, but that's an opaque type that only the
|
|
412
|
+
// real RelayModernStore can create. For now we just use any.
|
|
413
|
+
// $FlowFixMe
|
|
414
|
+
;
|
|
415
|
+
|
|
416
|
+
_proto.lookupInvalidationState = function lookupInvalidationState(dataIDs) {
|
|
417
|
+
var _this5 = this;
|
|
418
|
+
|
|
419
|
+
var invalidations = new Map();
|
|
420
|
+
dataIDs.forEach(function (dataID) {
|
|
421
|
+
var _RelayModernRecord$ge;
|
|
422
|
+
|
|
423
|
+
var record = _this5.getSource().get(dataID);
|
|
424
|
+
|
|
425
|
+
invalidations.set(dataID, (_RelayModernRecord$ge = RelayModernRecord.getInvalidationEpoch(record)) !== null && _RelayModernRecord$ge !== void 0 ? _RelayModernRecord$ge : null);
|
|
426
|
+
});
|
|
427
|
+
invalidations.set('global', this._globalInvalidationEpoch);
|
|
428
|
+
var invalidationState = {
|
|
429
|
+
dataIDs: dataIDs,
|
|
430
|
+
invalidations: invalidations
|
|
431
|
+
};
|
|
432
|
+
return invalidationState;
|
|
433
|
+
} // HACK
|
|
434
|
+
// This should accept an InvalidationState, but that's an opaque type that only the
|
|
435
|
+
// real RelayModernStore can create. For now we just use any.
|
|
436
|
+
// $FlowFixMe
|
|
437
|
+
;
|
|
438
|
+
|
|
439
|
+
_proto.checkInvalidationState = function checkInvalidationState(prevInvalidationState) {
|
|
440
|
+
var latestInvalidationState = this.lookupInvalidationState(prevInvalidationState.dataIDs);
|
|
441
|
+
var currentInvalidations = latestInvalidationState.invalidations;
|
|
442
|
+
var prevInvalidations = prevInvalidationState.invalidations; // Check if global invalidation has changed
|
|
443
|
+
|
|
444
|
+
if (currentInvalidations.get('global') !== prevInvalidations.get('global')) {
|
|
445
|
+
return true;
|
|
446
|
+
} // Check if the invalidation state for any of the ids has changed.
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
var _iterator = (0, _createForOfIteratorHelper2["default"])(prevInvalidationState.dataIDs),
|
|
450
|
+
_step;
|
|
451
|
+
|
|
452
|
+
try {
|
|
453
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
454
|
+
var dataID = _step.value;
|
|
455
|
+
|
|
456
|
+
if (currentInvalidations.get(dataID) !== prevInvalidations.get(dataID)) {
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
} catch (err) {
|
|
461
|
+
_iterator.e(err);
|
|
462
|
+
} finally {
|
|
463
|
+
_iterator.f();
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return false;
|
|
467
|
+
} // HACK
|
|
468
|
+
// This should accept an InvalidationState, but that's an opaque type that only the
|
|
469
|
+
// real RelayModernStore can create. For now we just use any.
|
|
470
|
+
;
|
|
471
|
+
|
|
472
|
+
_proto.subscribeToInvalidationState = function subscribeToInvalidationState( // $FlowFixMe
|
|
473
|
+
invalidationState, callback) {
|
|
474
|
+
var _this6 = this;
|
|
475
|
+
|
|
476
|
+
var subscription = {
|
|
477
|
+
callback: callback,
|
|
478
|
+
invalidationState: invalidationState
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
var dispose = function dispose() {
|
|
482
|
+
_this6._invalidationSubscriptions["delete"](subscription);
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
this._invalidationSubscriptions.add(subscription);
|
|
486
|
+
|
|
487
|
+
return {
|
|
488
|
+
dispose: dispose
|
|
489
|
+
};
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
_proto._updateInvalidationSubscription = function _updateInvalidationSubscription(subscription, invalidatedStore) {
|
|
493
|
+
var _this7 = this;
|
|
494
|
+
|
|
495
|
+
var callback = subscription.callback,
|
|
496
|
+
invalidationState = subscription.invalidationState;
|
|
497
|
+
var dataIDs = invalidationState.dataIDs;
|
|
498
|
+
var isSubscribedToInvalidatedIDs = invalidatedStore || dataIDs.some(function (dataID) {
|
|
499
|
+
return _this7._invalidatedRecordIDs.has(dataID);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
if (!isSubscribedToInvalidatedIDs) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
callback();
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
_proto.snapshot = function snapshot() {
|
|
510
|
+
!(this._optimisticSource == null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'LiveResolverStore: Unexpected call to snapshot() while a previous ' + 'snapshot exists.') : invariant(false) : void 0;
|
|
511
|
+
var log = this.__log;
|
|
512
|
+
|
|
513
|
+
if (log != null) {
|
|
514
|
+
log({
|
|
515
|
+
name: 'store.snapshot'
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
this._storeSubscriptions.snapshotSubscriptions(this.getSource());
|
|
520
|
+
|
|
521
|
+
if (this._gcRun) {
|
|
522
|
+
this._gcRun = null;
|
|
523
|
+
this._shouldScheduleGC = true;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
this._optimisticSource = RelayOptimisticRecordSource.create(this.getSource());
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
_proto.restore = function restore() {
|
|
530
|
+
!(this._optimisticSource != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'LiveResolverStore: Unexpected call to restore(), expected a snapshot ' + 'to exist (make sure to call snapshot()).') : invariant(false) : void 0;
|
|
531
|
+
var log = this.__log;
|
|
532
|
+
|
|
533
|
+
if (log != null) {
|
|
534
|
+
log({
|
|
535
|
+
name: 'store.restore'
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
this._optimisticSource = null;
|
|
540
|
+
|
|
541
|
+
if (this._shouldScheduleGC) {
|
|
542
|
+
this.scheduleGC();
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
this._storeSubscriptions.restoreSubscriptions();
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
_proto.scheduleGC = function scheduleGC() {
|
|
549
|
+
if (this._gcHoldCounter > 0) {
|
|
550
|
+
this._shouldScheduleGC = true;
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (this._gcRun) {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
this._gcRun = this._collect();
|
|
559
|
+
|
|
560
|
+
this._gcScheduler(this._gcStep);
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Run a full GC synchronously.
|
|
564
|
+
*/
|
|
565
|
+
;
|
|
566
|
+
|
|
567
|
+
_proto.__gc = function __gc() {
|
|
568
|
+
// Don't run GC while there are optimistic updates applied
|
|
569
|
+
if (this._optimisticSource != null) {
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
var gcRun = this._collect();
|
|
574
|
+
|
|
575
|
+
while (!gcRun.next().done) {}
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
_proto._collect = function* _collect() {
|
|
579
|
+
/* eslint-disable no-labels */
|
|
580
|
+
top: while (true) {
|
|
581
|
+
var startEpoch = this._currentWriteEpoch;
|
|
582
|
+
var references = new Set(); // Mark all records that are traversable from a root
|
|
583
|
+
|
|
584
|
+
var _iterator2 = (0, _createForOfIteratorHelper2["default"])(this._roots.values()),
|
|
585
|
+
_step2;
|
|
586
|
+
|
|
587
|
+
try {
|
|
588
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
589
|
+
var operation = _step2.value.operation;
|
|
590
|
+
var selector = operation.root;
|
|
591
|
+
RelayReferenceMarker.mark(this._recordSource, selector, references, this._operationLoader, this._shouldProcessClientComponents); // Yield for other work after each operation
|
|
592
|
+
|
|
593
|
+
yield; // If the store was updated, restart
|
|
594
|
+
|
|
595
|
+
if (startEpoch !== this._currentWriteEpoch) {
|
|
596
|
+
continue top;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
} catch (err) {
|
|
600
|
+
_iterator2.e(err);
|
|
601
|
+
} finally {
|
|
602
|
+
_iterator2.f();
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
var log = this.__log;
|
|
606
|
+
|
|
607
|
+
if (log != null) {
|
|
608
|
+
log({
|
|
609
|
+
name: 'store.gc',
|
|
610
|
+
references: references
|
|
611
|
+
});
|
|
612
|
+
} // Sweep records without references
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
if (references.size === 0) {
|
|
616
|
+
// Short-circuit if *nothing* is referenced
|
|
617
|
+
this._recordSource.clear();
|
|
618
|
+
} else {
|
|
619
|
+
// Evict any unreferenced nodes
|
|
620
|
+
var storeIDs = this._recordSource.getRecordIDs();
|
|
621
|
+
|
|
622
|
+
for (var ii = 0; ii < storeIDs.length; ii++) {
|
|
623
|
+
var dataID = storeIDs[ii];
|
|
624
|
+
|
|
625
|
+
if (!references.has(dataID)) {
|
|
626
|
+
this._recordSource.remove(dataID);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
return LiveResolverStore;
|
|
636
|
+
}();
|
|
637
|
+
|
|
638
|
+
function initializeRecordSource(target) {
|
|
639
|
+
if (!target.has(ROOT_ID)) {
|
|
640
|
+
var rootRecord = RelayModernRecord.create(ROOT_ID, ROOT_TYPE);
|
|
641
|
+
target.set(ROOT_ID, rootRecord);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Updates the target with information from source, also updating a mapping of
|
|
646
|
+
* which records in the target were changed as a result.
|
|
647
|
+
* Additionally, will mark records as invalidated at the current write epoch
|
|
648
|
+
* given the set of record ids marked as stale in this update.
|
|
649
|
+
*/
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
function updateTargetFromSource(target, source, currentWriteEpoch, idsMarkedForInvalidation, updatedRecordIDs, invalidatedRecordIDs) {
|
|
653
|
+
// First, update any records that were marked for invalidation.
|
|
654
|
+
// For each provided dataID that was invalidated, we write the
|
|
655
|
+
// INVALIDATED_AT_KEY on the record, indicating
|
|
656
|
+
// the epoch at which the record was invalidated.
|
|
657
|
+
if (idsMarkedForInvalidation) {
|
|
658
|
+
idsMarkedForInvalidation.forEach(function (dataID) {
|
|
659
|
+
var targetRecord = target.get(dataID);
|
|
660
|
+
var sourceRecord = source.get(dataID); // If record was deleted during the update (and also invalidated),
|
|
661
|
+
// we don't need to count it as an invalidated id
|
|
662
|
+
|
|
663
|
+
if (sourceRecord === null) {
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
var nextRecord;
|
|
668
|
+
|
|
669
|
+
if (targetRecord != null) {
|
|
670
|
+
// If the target record exists, use it to set the epoch
|
|
671
|
+
// at which it was invalidated. This record will be updated with
|
|
672
|
+
// any changes from source in the section below
|
|
673
|
+
// where we update the target records based on the source.
|
|
674
|
+
nextRecord = RelayModernRecord.clone(targetRecord);
|
|
675
|
+
} else {
|
|
676
|
+
// If the target record doesn't exist, it means that a new record
|
|
677
|
+
// in the source was created (and also invalidated), so we use that
|
|
678
|
+
// record to set the epoch at which it was invalidated. This record
|
|
679
|
+
// will be updated with any changes from source in the section below
|
|
680
|
+
// where we update the target records based on the source.
|
|
681
|
+
nextRecord = sourceRecord != null ? RelayModernRecord.clone(sourceRecord) : null;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
if (!nextRecord) {
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
RelayModernRecord.setValue(nextRecord, RelayStoreUtils.INVALIDATED_AT_KEY, currentWriteEpoch);
|
|
689
|
+
invalidatedRecordIDs.add(dataID);
|
|
690
|
+
target.set(dataID, nextRecord);
|
|
691
|
+
});
|
|
692
|
+
} // Update the target based on the changes present in source
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
var dataIDs = source.getRecordIDs();
|
|
696
|
+
|
|
697
|
+
for (var ii = 0; ii < dataIDs.length; ii++) {
|
|
698
|
+
var dataID = dataIDs[ii];
|
|
699
|
+
var sourceRecord = source.get(dataID);
|
|
700
|
+
var targetRecord = target.get(dataID); // Prevent mutation of a record from outside the store.
|
|
701
|
+
|
|
702
|
+
if (process.env.NODE_ENV !== "production") {
|
|
703
|
+
if (sourceRecord) {
|
|
704
|
+
RelayModernRecord.freeze(sourceRecord);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
if (sourceRecord && targetRecord) {
|
|
709
|
+
// ReactFlightClientResponses are lazy and only materialize when readRoot
|
|
710
|
+
// is called when we read the field, so if the record is a Flight field
|
|
711
|
+
// we always use the new record's data regardless of whether
|
|
712
|
+
// it actually changed. Let React take care of reconciliation instead.
|
|
713
|
+
var nextRecord = RelayModernRecord.getType(targetRecord) === RelayStoreReactFlightUtils.REACT_FLIGHT_TYPE_NAME ? sourceRecord : RelayModernRecord.update(targetRecord, sourceRecord);
|
|
714
|
+
|
|
715
|
+
if (nextRecord !== targetRecord) {
|
|
716
|
+
// Prevent mutation of a record from outside the store.
|
|
717
|
+
if (process.env.NODE_ENV !== "production") {
|
|
718
|
+
RelayModernRecord.freeze(nextRecord);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
updatedRecordIDs.add(dataID);
|
|
722
|
+
target.set(dataID, nextRecord);
|
|
723
|
+
}
|
|
724
|
+
} else if (sourceRecord === null) {
|
|
725
|
+
target["delete"](dataID);
|
|
726
|
+
|
|
727
|
+
if (targetRecord !== null) {
|
|
728
|
+
updatedRecordIDs.add(dataID);
|
|
729
|
+
}
|
|
730
|
+
} else if (sourceRecord) {
|
|
731
|
+
target.set(dataID, sourceRecord);
|
|
732
|
+
updatedRecordIDs.add(dataID);
|
|
733
|
+
} // don't add explicit undefined
|
|
734
|
+
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Returns an OperationAvailability given the Availability returned
|
|
739
|
+
* by checking an operation, and when that operation was last written to the store.
|
|
740
|
+
* Specifically, the provided Availability of an operation will contain the
|
|
741
|
+
* value of when a record referenced by the operation was most recently
|
|
742
|
+
* invalidated; given that value, and given when this operation was last
|
|
743
|
+
* written to the store, this function will return the overall
|
|
744
|
+
* OperationAvailability for the operation.
|
|
745
|
+
*/
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
function getAvailabilityStatus(operationAvailability, operationLastWrittenAt, operationFetchTime, queryCacheExpirationTime) {
|
|
749
|
+
var mostRecentlyInvalidatedAt = operationAvailability.mostRecentlyInvalidatedAt,
|
|
750
|
+
status = operationAvailability.status;
|
|
751
|
+
|
|
752
|
+
if (typeof mostRecentlyInvalidatedAt === 'number') {
|
|
753
|
+
// If some record referenced by this operation is stale, then the operation itself is stale
|
|
754
|
+
// if either the operation itself was never written *or* the operation was last written
|
|
755
|
+
// before the most recent invalidation of its reachable records.
|
|
756
|
+
if (operationLastWrittenAt == null || mostRecentlyInvalidatedAt > operationLastWrittenAt) {
|
|
757
|
+
return {
|
|
758
|
+
status: 'stale'
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
if (status === 'missing') {
|
|
764
|
+
return {
|
|
765
|
+
status: 'missing'
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (operationFetchTime != null && queryCacheExpirationTime != null) {
|
|
770
|
+
var isStale = operationFetchTime <= Date.now() - queryCacheExpirationTime;
|
|
771
|
+
|
|
772
|
+
if (isStale) {
|
|
773
|
+
return {
|
|
774
|
+
status: 'stale'
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
} // There were no invalidations of any reachable records *or* the operation is known to have
|
|
778
|
+
// been fetched after the most recent record invalidation.
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
return {
|
|
782
|
+
status: 'available',
|
|
783
|
+
fetchTime: operationFetchTime !== null && operationFetchTime !== void 0 ? operationFetchTime : null
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
module.exports = LiveResolverStore;
|