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,316 @@
|
|
|
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 recycleNodesInto = require('../../util/recycleNodesInto');
|
|
18
|
+
|
|
19
|
+
var _require = require('../../util/RelayConcreteNode'),
|
|
20
|
+
RELAY_LIVE_RESOLVER = _require.RELAY_LIVE_RESOLVER;
|
|
21
|
+
|
|
22
|
+
var _require2 = require('../ClientID'),
|
|
23
|
+
generateClientID = _require2.generateClientID;
|
|
24
|
+
|
|
25
|
+
var RelayModernRecord = require('../RelayModernRecord');
|
|
26
|
+
|
|
27
|
+
var RelayRecordSource = require('../RelayRecordSource');
|
|
28
|
+
|
|
29
|
+
var _require3 = require('../RelayStoreUtils'),
|
|
30
|
+
RELAY_RESOLVER_ERROR_KEY = _require3.RELAY_RESOLVER_ERROR_KEY,
|
|
31
|
+
RELAY_RESOLVER_INPUTS_KEY = _require3.RELAY_RESOLVER_INPUTS_KEY,
|
|
32
|
+
RELAY_RESOLVER_INVALIDATION_KEY = _require3.RELAY_RESOLVER_INVALIDATION_KEY,
|
|
33
|
+
RELAY_RESOLVER_MISSING_REQUIRED_FIELDS_KEY = _require3.RELAY_RESOLVER_MISSING_REQUIRED_FIELDS_KEY,
|
|
34
|
+
RELAY_RESOLVER_READER_SELECTOR_KEY = _require3.RELAY_RESOLVER_READER_SELECTOR_KEY,
|
|
35
|
+
RELAY_RESOLVER_VALUE_KEY = _require3.RELAY_RESOLVER_VALUE_KEY,
|
|
36
|
+
getStorageKey = _require3.getStorageKey;
|
|
37
|
+
|
|
38
|
+
var LiveResolverStore = require('./LiveResolverStore');
|
|
39
|
+
|
|
40
|
+
var invariant = require('invariant');
|
|
41
|
+
|
|
42
|
+
var warning = require("fbjs/lib/warning"); // When this experiment gets promoted to stable, these keys will move into
|
|
43
|
+
// `RelayStoreUtils`.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
var RELAY_RESOLVER_LIVE_STATE_SUBSCRIPTION_KEY = '__resolverLieStateSubscription';
|
|
47
|
+
var RELAY_RESOLVER_LIVE_STATE_VALUE = '__resolverLiveStateValue';
|
|
48
|
+
var RELAY_RESOLVER_LIVE_STATE_DIRTY = '__resolverLiveStateDirty';
|
|
49
|
+
/**
|
|
50
|
+
* An experimental fork of store/ResolverCache.js intended to let us experiment
|
|
51
|
+
* with Live Resolvers.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
// $FlowFixMe[unclear-type] - will always be empty
|
|
55
|
+
var emptySet = new Set();
|
|
56
|
+
|
|
57
|
+
function addDependencyEdge(edges, from, to) {
|
|
58
|
+
var set = edges.get(from);
|
|
59
|
+
|
|
60
|
+
if (!set) {
|
|
61
|
+
set = new Set();
|
|
62
|
+
edges.set(from, set);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
set.add(to);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
var LiveResolverCache = /*#__PURE__*/function () {
|
|
69
|
+
function LiveResolverCache(getRecordSource, store) {
|
|
70
|
+
this._resolverIDToRecordIDs = new Map();
|
|
71
|
+
this._recordIDToResolverIDs = new Map();
|
|
72
|
+
this._getRecordSource = getRecordSource;
|
|
73
|
+
this._store = store;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
var _proto = LiveResolverCache.prototype;
|
|
77
|
+
|
|
78
|
+
_proto.readFromCacheOrEvaluate = function readFromCacheOrEvaluate(record, field, variables, evaluate, getDataForResolverFragment) {
|
|
79
|
+
var recordSource = this._getRecordSource();
|
|
80
|
+
|
|
81
|
+
var recordID = RelayModernRecord.getDataID(record);
|
|
82
|
+
var storageKey = getStorageKey(field, variables);
|
|
83
|
+
var linkedID = RelayModernRecord.getLinkedRecordID(record, storageKey);
|
|
84
|
+
var linkedRecord = linkedID == null ? null : recordSource.get(linkedID);
|
|
85
|
+
|
|
86
|
+
if (linkedRecord == null || this._isInvalid(linkedRecord, getDataForResolverFragment)) {
|
|
87
|
+
var _linkedID;
|
|
88
|
+
|
|
89
|
+
// Cache miss; evaluate the selector and store the result in a new record:
|
|
90
|
+
linkedID = (_linkedID = linkedID) !== null && _linkedID !== void 0 ? _linkedID : generateClientID(recordID, storageKey);
|
|
91
|
+
linkedRecord = RelayModernRecord.create(linkedID, '__RELAY_RESOLVER__');
|
|
92
|
+
var evaluationResult = evaluate();
|
|
93
|
+
|
|
94
|
+
if (field.kind === RELAY_LIVE_RESOLVER) {
|
|
95
|
+
if (process.env.NODE_ENV !== "production") {
|
|
96
|
+
!isLiveStateValue(evaluationResult.resolverResult) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Expected a @live Relay Resolver to return a value that implements LiveState.') : invariant(false) : void 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
var liveState = // $FlowFixMe[incompatible-type] - casting mixed
|
|
100
|
+
evaluationResult.resolverResult;
|
|
101
|
+
|
|
102
|
+
this._setLiveStateValue(linkedRecord, linkedID, liveState);
|
|
103
|
+
} else {
|
|
104
|
+
RelayModernRecord.setValue(linkedRecord, RELAY_RESOLVER_VALUE_KEY, evaluationResult.resolverResult);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
RelayModernRecord.setValue(linkedRecord, RELAY_RESOLVER_INPUTS_KEY, evaluationResult.fragmentValue);
|
|
108
|
+
RelayModernRecord.setValue(linkedRecord, RELAY_RESOLVER_READER_SELECTOR_KEY, evaluationResult.readerSelector);
|
|
109
|
+
RelayModernRecord.setValue(linkedRecord, RELAY_RESOLVER_MISSING_REQUIRED_FIELDS_KEY, evaluationResult.missingRequiredFields);
|
|
110
|
+
RelayModernRecord.setValue(linkedRecord, RELAY_RESOLVER_ERROR_KEY, evaluationResult.errors);
|
|
111
|
+
recordSource.set(linkedID, linkedRecord); // Link the resolver value record to the resolver field of the record being read:
|
|
112
|
+
|
|
113
|
+
var nextRecord = RelayModernRecord.clone(record);
|
|
114
|
+
RelayModernRecord.setLinkedRecordID(nextRecord, storageKey, linkedID);
|
|
115
|
+
recordSource.set(RelayModernRecord.getDataID(nextRecord), nextRecord); // Put records observed by the resolver into the dependency graph:
|
|
116
|
+
|
|
117
|
+
var resolverID = evaluationResult.resolverID;
|
|
118
|
+
addDependencyEdge(this._resolverIDToRecordIDs, resolverID, linkedID);
|
|
119
|
+
addDependencyEdge(this._recordIDToResolverIDs, recordID, resolverID);
|
|
120
|
+
|
|
121
|
+
var _iterator = (0, _createForOfIteratorHelper2["default"])(evaluationResult.seenRecordIDs),
|
|
122
|
+
_step;
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
126
|
+
var seenRecordID = _step.value;
|
|
127
|
+
addDependencyEdge(this._recordIDToResolverIDs, seenRecordID, resolverID);
|
|
128
|
+
}
|
|
129
|
+
} catch (err) {
|
|
130
|
+
_iterator.e(err);
|
|
131
|
+
} finally {
|
|
132
|
+
_iterator.f();
|
|
133
|
+
}
|
|
134
|
+
} else if (field.kind === RELAY_LIVE_RESOLVER && RelayModernRecord.getValue(linkedRecord, RELAY_RESOLVER_LIVE_STATE_DIRTY)) {
|
|
135
|
+
var _linkedID2;
|
|
136
|
+
|
|
137
|
+
// If this is an Live Resolver, we might have a cache hit (the
|
|
138
|
+
// fragment data hasn't changed since we last evaluated the resolver),
|
|
139
|
+
// but it might still be "dirty" (the live state changed and we need
|
|
140
|
+
// to call `.read()` again).
|
|
141
|
+
linkedID = (_linkedID2 = linkedID) !== null && _linkedID2 !== void 0 ? _linkedID2 : generateClientID(recordID, storageKey);
|
|
142
|
+
linkedRecord = RelayModernRecord.clone(linkedRecord); // $FlowFixMe[incompatible-type] - casting mixed
|
|
143
|
+
|
|
144
|
+
var _liveState = RelayModernRecord.getValue(linkedRecord, RELAY_RESOLVER_LIVE_STATE_VALUE); // Set the new value for this and future reads.
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
RelayModernRecord.setValue(linkedRecord, RELAY_RESOLVER_VALUE_KEY, _liveState.read()); // Mark the resolver as clean again.
|
|
148
|
+
|
|
149
|
+
RelayModernRecord.setValue(linkedRecord, RELAY_RESOLVER_LIVE_STATE_DIRTY, false);
|
|
150
|
+
recordSource.set(linkedID, linkedRecord);
|
|
151
|
+
} // $FlowFixMe[incompatible-type] - will always be empty
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
var answer = linkedRecord[RELAY_RESOLVER_VALUE_KEY];
|
|
155
|
+
var missingRequiredFields = // $FlowFixMe[incompatible-type] - casting mixed
|
|
156
|
+
linkedRecord[RELAY_RESOLVER_MISSING_REQUIRED_FIELDS_KEY]; // $FlowFixMe[incompatible-type] - casting mixed
|
|
157
|
+
|
|
158
|
+
var errors = linkedRecord[RELAY_RESOLVER_ERROR_KEY];
|
|
159
|
+
return [answer, linkedID, errors, missingRequiredFields];
|
|
160
|
+
} // Register a new Live State object in the store, subscribing to future
|
|
161
|
+
// updates.
|
|
162
|
+
;
|
|
163
|
+
|
|
164
|
+
_proto._setLiveStateValue = function _setLiveStateValue(linkedRecord, linkedID, liveState) {
|
|
165
|
+
// If there's an existing subscription, unsubscribe.
|
|
166
|
+
// $FlowFixMe[incompatible-type] - casting mixed
|
|
167
|
+
var previousUnsubscribe = RelayModernRecord.getValue(linkedRecord, RELAY_RESOLVER_LIVE_STATE_SUBSCRIPTION_KEY);
|
|
168
|
+
|
|
169
|
+
if (previousUnsubscribe != null) {
|
|
170
|
+
previousUnsubscribe();
|
|
171
|
+
} // Subscribe to future values
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
var handler = this._makeLiveStateHandler(linkedID);
|
|
175
|
+
|
|
176
|
+
var unsubscribe = liveState.subscribe(handler); // Store the live state value for future re-reads.
|
|
177
|
+
|
|
178
|
+
RelayModernRecord.setValue(linkedRecord, RELAY_RESOLVER_LIVE_STATE_VALUE, liveState); // Store the current value, for this read, and future cached reads.
|
|
179
|
+
|
|
180
|
+
RelayModernRecord.setValue(linkedRecord, RELAY_RESOLVER_VALUE_KEY, liveState.read()); // Mark the field as clean.
|
|
181
|
+
|
|
182
|
+
RelayModernRecord.setValue(linkedRecord, RELAY_RESOLVER_LIVE_STATE_DIRTY, false); // Store our our unsubscribe function for future cleanup.
|
|
183
|
+
|
|
184
|
+
RelayModernRecord.setValue(linkedRecord, RELAY_RESOLVER_LIVE_STATE_SUBSCRIPTION_KEY, unsubscribe);
|
|
185
|
+
} // Create a callback to handle notifications from the live source that the
|
|
186
|
+
// value may have changed.
|
|
187
|
+
;
|
|
188
|
+
|
|
189
|
+
_proto._makeLiveStateHandler = function _makeLiveStateHandler(linkedID) {
|
|
190
|
+
var _this = this;
|
|
191
|
+
|
|
192
|
+
return function () {
|
|
193
|
+
var currentSource = _this._getRecordSource();
|
|
194
|
+
|
|
195
|
+
var currentRecord = currentSource.get(linkedID);
|
|
196
|
+
|
|
197
|
+
if (!currentRecord) {
|
|
198
|
+
process.env.NODE_ENV !== "production" ? warning(false, 'Expected a resolver record with ID %s, but it was missing.', linkedID) : void 0;
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
var nextSource = RelayRecordSource.create();
|
|
203
|
+
var nextRecord = RelayModernRecord.clone(currentRecord); // Mark the field as dirty. The next time it's read, we will call
|
|
204
|
+
// `LiveState.read()`.
|
|
205
|
+
|
|
206
|
+
RelayModernRecord.setValue(nextRecord, RELAY_RESOLVER_LIVE_STATE_DIRTY, true);
|
|
207
|
+
nextSource.set(linkedID, nextRecord);
|
|
208
|
+
|
|
209
|
+
_this._store.publish(nextSource); // In the future, this notify might be defferred if we are within a
|
|
210
|
+
// transaction.
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
_this._store.notify();
|
|
214
|
+
};
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
_proto.invalidateDataIDs = function invalidateDataIDs(updatedDataIDs) {
|
|
218
|
+
var recordSource = this._getRecordSource();
|
|
219
|
+
|
|
220
|
+
var visited = new Set();
|
|
221
|
+
var recordsToVisit = Array.from(updatedDataIDs);
|
|
222
|
+
|
|
223
|
+
while (recordsToVisit.length) {
|
|
224
|
+
var recordID = recordsToVisit.pop();
|
|
225
|
+
updatedDataIDs.add(recordID);
|
|
226
|
+
|
|
227
|
+
var _iterator2 = (0, _createForOfIteratorHelper2["default"])((_this$_recordIDToReso = this._recordIDToResolverIDs.get(recordID)) !== null && _this$_recordIDToReso !== void 0 ? _this$_recordIDToReso : emptySet),
|
|
228
|
+
_step2;
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
232
|
+
var _this$_recordIDToReso;
|
|
233
|
+
|
|
234
|
+
var fragment = _step2.value;
|
|
235
|
+
|
|
236
|
+
if (!visited.has(fragment)) {
|
|
237
|
+
var _iterator3 = (0, _createForOfIteratorHelper2["default"])((_this$_resolverIDToRe = this._resolverIDToRecordIDs.get(fragment)) !== null && _this$_resolverIDToRe !== void 0 ? _this$_resolverIDToRe : emptySet),
|
|
238
|
+
_step3;
|
|
239
|
+
|
|
240
|
+
try {
|
|
241
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
242
|
+
var _this$_resolverIDToRe;
|
|
243
|
+
|
|
244
|
+
var anotherRecordID = _step3.value;
|
|
245
|
+
|
|
246
|
+
this._markInvalidatedResolverRecord(anotherRecordID, recordSource);
|
|
247
|
+
|
|
248
|
+
if (!visited.has(anotherRecordID)) {
|
|
249
|
+
recordsToVisit.push(anotherRecordID);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
} catch (err) {
|
|
253
|
+
_iterator3.e(err);
|
|
254
|
+
} finally {
|
|
255
|
+
_iterator3.f();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
} catch (err) {
|
|
260
|
+
_iterator2.e(err);
|
|
261
|
+
} finally {
|
|
262
|
+
_iterator2.f();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
_proto._markInvalidatedResolverRecord = function _markInvalidatedResolverRecord(dataID, recordSource) // Written to
|
|
268
|
+
{
|
|
269
|
+
var record = recordSource.get(dataID);
|
|
270
|
+
|
|
271
|
+
if (!record) {
|
|
272
|
+
process.env.NODE_ENV !== "production" ? warning(false, 'Expected a resolver record with ID %s, but it was missing.', dataID) : void 0;
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
var nextRecord = RelayModernRecord.clone(record);
|
|
277
|
+
RelayModernRecord.setValue(nextRecord, RELAY_RESOLVER_INVALIDATION_KEY, true);
|
|
278
|
+
recordSource.set(dataID, nextRecord);
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
_proto._isInvalid = function _isInvalid(record, getDataForResolverFragment) {
|
|
282
|
+
if (!RelayModernRecord.getValue(record, RELAY_RESOLVER_INVALIDATION_KEY)) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
var originalInputs = RelayModernRecord.getValue(record, RELAY_RESOLVER_INPUTS_KEY); // $FlowFixMe[incompatible-type] - storing values in records is not typed
|
|
287
|
+
|
|
288
|
+
var readerSelector = RelayModernRecord.getValue(record, RELAY_RESOLVER_READER_SELECTOR_KEY);
|
|
289
|
+
|
|
290
|
+
if (originalInputs == null || readerSelector == null) {
|
|
291
|
+
process.env.NODE_ENV !== "production" ? warning(false, 'Expected previous inputs and reader selector on resolver record with ID %s, but they were missing.', RelayModernRecord.getDataID(record)) : void 0;
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
var latestValues = getDataForResolverFragment(readerSelector);
|
|
296
|
+
var recycled = recycleNodesInto(originalInputs, latestValues);
|
|
297
|
+
|
|
298
|
+
if (recycled !== originalInputs) {
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return false;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
return LiveResolverCache;
|
|
306
|
+
}(); // Validate that a value is live state
|
|
307
|
+
// $FlowFixMe
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
function isLiveStateValue(v) {
|
|
311
|
+
return v != null && typeof v.read === 'function' && typeof v.subscribe === 'function';
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
module.exports = {
|
|
315
|
+
LiveResolverCache: LiveResolverCache
|
|
316
|
+
};
|