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,410 @@
|
|
|
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
|
+
* @flow strict-local
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// flowlint ambiguous-object-type:error
|
|
12
|
+
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
import type {
|
|
16
|
+
ReaderRelayLiveResolver,
|
|
17
|
+
ReaderRelayResolver,
|
|
18
|
+
} from '../../util/ReaderNode';
|
|
19
|
+
import type {DataID, Variables} from '../../util/RelayRuntimeTypes';
|
|
20
|
+
import type {
|
|
21
|
+
MissingRequiredFields,
|
|
22
|
+
MutableRecordSource,
|
|
23
|
+
Record,
|
|
24
|
+
RelayResolverErrors,
|
|
25
|
+
SingularReaderSelector,
|
|
26
|
+
} from '../RelayStoreTypes';
|
|
27
|
+
import type {ResolverCache} from '../ResolverCache';
|
|
28
|
+
import type {LiveState} from './LiveResolverStore';
|
|
29
|
+
|
|
30
|
+
const recycleNodesInto = require('../../util/recycleNodesInto');
|
|
31
|
+
const {RELAY_LIVE_RESOLVER} = require('../../util/RelayConcreteNode');
|
|
32
|
+
const {generateClientID} = require('../ClientID');
|
|
33
|
+
const RelayModernRecord = require('../RelayModernRecord');
|
|
34
|
+
const RelayRecordSource = require('../RelayRecordSource');
|
|
35
|
+
const {
|
|
36
|
+
RELAY_RESOLVER_ERROR_KEY,
|
|
37
|
+
RELAY_RESOLVER_INPUTS_KEY,
|
|
38
|
+
RELAY_RESOLVER_INVALIDATION_KEY,
|
|
39
|
+
RELAY_RESOLVER_MISSING_REQUIRED_FIELDS_KEY,
|
|
40
|
+
RELAY_RESOLVER_READER_SELECTOR_KEY,
|
|
41
|
+
RELAY_RESOLVER_VALUE_KEY,
|
|
42
|
+
getStorageKey,
|
|
43
|
+
} = require('../RelayStoreUtils');
|
|
44
|
+
const LiveResolverStore = require('./LiveResolverStore');
|
|
45
|
+
const invariant = require('invariant');
|
|
46
|
+
const warning = require('warning');
|
|
47
|
+
|
|
48
|
+
// When this experiment gets promoted to stable, these keys will move into
|
|
49
|
+
// `RelayStoreUtils`.
|
|
50
|
+
const RELAY_RESOLVER_LIVE_STATE_SUBSCRIPTION_KEY =
|
|
51
|
+
'__resolverLieStateSubscription';
|
|
52
|
+
const RELAY_RESOLVER_LIVE_STATE_VALUE = '__resolverLiveStateValue';
|
|
53
|
+
const RELAY_RESOLVER_LIVE_STATE_DIRTY = '__resolverLiveStateDirty';
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* An experimental fork of store/ResolverCache.js intended to let us experiment
|
|
57
|
+
* with Live Resolvers.
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
type ResolverID = string;
|
|
61
|
+
|
|
62
|
+
type EvaluationResult<T> = {|
|
|
63
|
+
resolverResult: T,
|
|
64
|
+
fragmentValue: {...},
|
|
65
|
+
resolverID: ResolverID,
|
|
66
|
+
seenRecordIDs: Set<DataID>,
|
|
67
|
+
readerSelector: SingularReaderSelector,
|
|
68
|
+
errors: RelayResolverErrors,
|
|
69
|
+
missingRequiredFields: ?MissingRequiredFields,
|
|
70
|
+
|};
|
|
71
|
+
|
|
72
|
+
// $FlowFixMe[unclear-type] - will always be empty
|
|
73
|
+
const emptySet: $ReadOnlySet<any> = new Set();
|
|
74
|
+
|
|
75
|
+
function addDependencyEdge(
|
|
76
|
+
edges: Map<ResolverID, Set<DataID>> | Map<DataID, Set<ResolverID>>,
|
|
77
|
+
from: ResolverID | DataID,
|
|
78
|
+
to: ResolverID | DataID,
|
|
79
|
+
): void {
|
|
80
|
+
let set = edges.get(from);
|
|
81
|
+
if (!set) {
|
|
82
|
+
set = new Set();
|
|
83
|
+
edges.set(from, set);
|
|
84
|
+
}
|
|
85
|
+
set.add(to);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
class LiveResolverCache implements ResolverCache {
|
|
89
|
+
_resolverIDToRecordIDs: Map<ResolverID, Set<DataID>>;
|
|
90
|
+
_recordIDToResolverIDs: Map<DataID, Set<ResolverID>>;
|
|
91
|
+
|
|
92
|
+
_getRecordSource: () => MutableRecordSource;
|
|
93
|
+
_store: LiveResolverStore;
|
|
94
|
+
|
|
95
|
+
constructor(
|
|
96
|
+
getRecordSource: () => MutableRecordSource,
|
|
97
|
+
store: LiveResolverStore,
|
|
98
|
+
) {
|
|
99
|
+
this._resolverIDToRecordIDs = new Map();
|
|
100
|
+
this._recordIDToResolverIDs = new Map();
|
|
101
|
+
this._getRecordSource = getRecordSource;
|
|
102
|
+
this._store = store;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
readFromCacheOrEvaluate<T>(
|
|
106
|
+
record: Record,
|
|
107
|
+
field: ReaderRelayResolver | ReaderRelayLiveResolver,
|
|
108
|
+
variables: Variables,
|
|
109
|
+
evaluate: () => EvaluationResult<T>,
|
|
110
|
+
getDataForResolverFragment: SingularReaderSelector => mixed,
|
|
111
|
+
): [
|
|
112
|
+
T /* Answer */,
|
|
113
|
+
?DataID /* Seen record */,
|
|
114
|
+
RelayResolverErrors,
|
|
115
|
+
?MissingRequiredFields,
|
|
116
|
+
] {
|
|
117
|
+
const recordSource = this._getRecordSource();
|
|
118
|
+
const recordID = RelayModernRecord.getDataID(record);
|
|
119
|
+
|
|
120
|
+
const storageKey = getStorageKey(field, variables);
|
|
121
|
+
let linkedID = RelayModernRecord.getLinkedRecordID(record, storageKey);
|
|
122
|
+
let linkedRecord = linkedID == null ? null : recordSource.get(linkedID);
|
|
123
|
+
|
|
124
|
+
if (
|
|
125
|
+
linkedRecord == null ||
|
|
126
|
+
this._isInvalid(linkedRecord, getDataForResolverFragment)
|
|
127
|
+
) {
|
|
128
|
+
// Cache miss; evaluate the selector and store the result in a new record:
|
|
129
|
+
linkedID = linkedID ?? generateClientID(recordID, storageKey);
|
|
130
|
+
linkedRecord = RelayModernRecord.create(linkedID, '__RELAY_RESOLVER__');
|
|
131
|
+
|
|
132
|
+
const evaluationResult = evaluate();
|
|
133
|
+
|
|
134
|
+
if (field.kind === RELAY_LIVE_RESOLVER) {
|
|
135
|
+
if (__DEV__) {
|
|
136
|
+
invariant(
|
|
137
|
+
isLiveStateValue(evaluationResult.resolverResult),
|
|
138
|
+
'Expected a @live Relay Resolver to return a value that implements LiveState.',
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
const liveState: LiveState<mixed> =
|
|
142
|
+
// $FlowFixMe[incompatible-type] - casting mixed
|
|
143
|
+
evaluationResult.resolverResult;
|
|
144
|
+
this._setLiveStateValue(linkedRecord, linkedID, liveState);
|
|
145
|
+
} else {
|
|
146
|
+
RelayModernRecord.setValue(
|
|
147
|
+
linkedRecord,
|
|
148
|
+
RELAY_RESOLVER_VALUE_KEY,
|
|
149
|
+
evaluationResult.resolverResult,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
RelayModernRecord.setValue(
|
|
153
|
+
linkedRecord,
|
|
154
|
+
RELAY_RESOLVER_INPUTS_KEY,
|
|
155
|
+
evaluationResult.fragmentValue,
|
|
156
|
+
);
|
|
157
|
+
RelayModernRecord.setValue(
|
|
158
|
+
linkedRecord,
|
|
159
|
+
RELAY_RESOLVER_READER_SELECTOR_KEY,
|
|
160
|
+
evaluationResult.readerSelector,
|
|
161
|
+
);
|
|
162
|
+
RelayModernRecord.setValue(
|
|
163
|
+
linkedRecord,
|
|
164
|
+
RELAY_RESOLVER_MISSING_REQUIRED_FIELDS_KEY,
|
|
165
|
+
evaluationResult.missingRequiredFields,
|
|
166
|
+
);
|
|
167
|
+
RelayModernRecord.setValue(
|
|
168
|
+
linkedRecord,
|
|
169
|
+
RELAY_RESOLVER_ERROR_KEY,
|
|
170
|
+
evaluationResult.errors,
|
|
171
|
+
);
|
|
172
|
+
recordSource.set(linkedID, linkedRecord);
|
|
173
|
+
|
|
174
|
+
// Link the resolver value record to the resolver field of the record being read:
|
|
175
|
+
const nextRecord = RelayModernRecord.clone(record);
|
|
176
|
+
RelayModernRecord.setLinkedRecordID(nextRecord, storageKey, linkedID);
|
|
177
|
+
recordSource.set(RelayModernRecord.getDataID(nextRecord), nextRecord);
|
|
178
|
+
|
|
179
|
+
// Put records observed by the resolver into the dependency graph:
|
|
180
|
+
const resolverID = evaluationResult.resolverID;
|
|
181
|
+
addDependencyEdge(this._resolverIDToRecordIDs, resolverID, linkedID);
|
|
182
|
+
addDependencyEdge(this._recordIDToResolverIDs, recordID, resolverID);
|
|
183
|
+
for (const seenRecordID of evaluationResult.seenRecordIDs) {
|
|
184
|
+
addDependencyEdge(
|
|
185
|
+
this._recordIDToResolverIDs,
|
|
186
|
+
seenRecordID,
|
|
187
|
+
resolverID,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
} else if (
|
|
191
|
+
field.kind === RELAY_LIVE_RESOLVER &&
|
|
192
|
+
RelayModernRecord.getValue(linkedRecord, RELAY_RESOLVER_LIVE_STATE_DIRTY)
|
|
193
|
+
) {
|
|
194
|
+
// If this is an Live Resolver, we might have a cache hit (the
|
|
195
|
+
// fragment data hasn't changed since we last evaluated the resolver),
|
|
196
|
+
// but it might still be "dirty" (the live state changed and we need
|
|
197
|
+
// to call `.read()` again).
|
|
198
|
+
linkedID = linkedID ?? generateClientID(recordID, storageKey);
|
|
199
|
+
linkedRecord = RelayModernRecord.clone(linkedRecord);
|
|
200
|
+
// $FlowFixMe[incompatible-type] - casting mixed
|
|
201
|
+
const liveState: LiveState<mixed> = RelayModernRecord.getValue(
|
|
202
|
+
linkedRecord,
|
|
203
|
+
RELAY_RESOLVER_LIVE_STATE_VALUE,
|
|
204
|
+
);
|
|
205
|
+
// Set the new value for this and future reads.
|
|
206
|
+
RelayModernRecord.setValue(
|
|
207
|
+
linkedRecord,
|
|
208
|
+
RELAY_RESOLVER_VALUE_KEY,
|
|
209
|
+
liveState.read(),
|
|
210
|
+
);
|
|
211
|
+
// Mark the resolver as clean again.
|
|
212
|
+
RelayModernRecord.setValue(
|
|
213
|
+
linkedRecord,
|
|
214
|
+
RELAY_RESOLVER_LIVE_STATE_DIRTY,
|
|
215
|
+
false,
|
|
216
|
+
);
|
|
217
|
+
recordSource.set(linkedID, linkedRecord);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// $FlowFixMe[incompatible-type] - will always be empty
|
|
221
|
+
const answer: T = linkedRecord[RELAY_RESOLVER_VALUE_KEY];
|
|
222
|
+
|
|
223
|
+
const missingRequiredFields: ?MissingRequiredFields =
|
|
224
|
+
// $FlowFixMe[incompatible-type] - casting mixed
|
|
225
|
+
linkedRecord[RELAY_RESOLVER_MISSING_REQUIRED_FIELDS_KEY];
|
|
226
|
+
|
|
227
|
+
// $FlowFixMe[incompatible-type] - casting mixed
|
|
228
|
+
const errors: RelayResolverErrors = linkedRecord[RELAY_RESOLVER_ERROR_KEY];
|
|
229
|
+
return [answer, linkedID, errors, missingRequiredFields];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Register a new Live State object in the store, subscribing to future
|
|
233
|
+
// updates.
|
|
234
|
+
_setLiveStateValue(
|
|
235
|
+
linkedRecord: Record,
|
|
236
|
+
linkedID: DataID,
|
|
237
|
+
liveState: LiveState<mixed>,
|
|
238
|
+
) {
|
|
239
|
+
// If there's an existing subscription, unsubscribe.
|
|
240
|
+
// $FlowFixMe[incompatible-type] - casting mixed
|
|
241
|
+
const previousUnsubscribe: () => void = RelayModernRecord.getValue(
|
|
242
|
+
linkedRecord,
|
|
243
|
+
RELAY_RESOLVER_LIVE_STATE_SUBSCRIPTION_KEY,
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
if (previousUnsubscribe != null) {
|
|
247
|
+
previousUnsubscribe();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Subscribe to future values
|
|
251
|
+
const handler = this._makeLiveStateHandler(linkedID);
|
|
252
|
+
const unsubscribe = liveState.subscribe(handler);
|
|
253
|
+
|
|
254
|
+
// Store the live state value for future re-reads.
|
|
255
|
+
RelayModernRecord.setValue(
|
|
256
|
+
linkedRecord,
|
|
257
|
+
RELAY_RESOLVER_LIVE_STATE_VALUE,
|
|
258
|
+
liveState,
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
// Store the current value, for this read, and future cached reads.
|
|
262
|
+
RelayModernRecord.setValue(
|
|
263
|
+
linkedRecord,
|
|
264
|
+
RELAY_RESOLVER_VALUE_KEY,
|
|
265
|
+
liveState.read(),
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
// Mark the field as clean.
|
|
269
|
+
RelayModernRecord.setValue(
|
|
270
|
+
linkedRecord,
|
|
271
|
+
RELAY_RESOLVER_LIVE_STATE_DIRTY,
|
|
272
|
+
false,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
// Store our our unsubscribe function for future cleanup.
|
|
276
|
+
RelayModernRecord.setValue(
|
|
277
|
+
linkedRecord,
|
|
278
|
+
RELAY_RESOLVER_LIVE_STATE_SUBSCRIPTION_KEY,
|
|
279
|
+
unsubscribe,
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Create a callback to handle notifications from the live source that the
|
|
284
|
+
// value may have changed.
|
|
285
|
+
_makeLiveStateHandler(linkedID: DataID): () => void {
|
|
286
|
+
return () => {
|
|
287
|
+
const currentSource = this._getRecordSource();
|
|
288
|
+
const currentRecord = currentSource.get(linkedID);
|
|
289
|
+
if (!currentRecord) {
|
|
290
|
+
warning(
|
|
291
|
+
false,
|
|
292
|
+
'Expected a resolver record with ID %s, but it was missing.',
|
|
293
|
+
linkedID,
|
|
294
|
+
);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const nextSource = RelayRecordSource.create();
|
|
299
|
+
const nextRecord = RelayModernRecord.clone(currentRecord);
|
|
300
|
+
|
|
301
|
+
// Mark the field as dirty. The next time it's read, we will call
|
|
302
|
+
// `LiveState.read()`.
|
|
303
|
+
RelayModernRecord.setValue(
|
|
304
|
+
nextRecord,
|
|
305
|
+
RELAY_RESOLVER_LIVE_STATE_DIRTY,
|
|
306
|
+
true,
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
nextSource.set(linkedID, nextRecord);
|
|
310
|
+
this._store.publish(nextSource);
|
|
311
|
+
|
|
312
|
+
// In the future, this notify might be defferred if we are within a
|
|
313
|
+
// transaction.
|
|
314
|
+
this._store.notify();
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
invalidateDataIDs(
|
|
319
|
+
updatedDataIDs: Set<DataID>, // Mutated in place
|
|
320
|
+
): void {
|
|
321
|
+
const recordSource = this._getRecordSource();
|
|
322
|
+
const visited: Set<string> = new Set();
|
|
323
|
+
const recordsToVisit = Array.from(updatedDataIDs);
|
|
324
|
+
while (recordsToVisit.length) {
|
|
325
|
+
const recordID = recordsToVisit.pop();
|
|
326
|
+
updatedDataIDs.add(recordID);
|
|
327
|
+
for (const fragment of this._recordIDToResolverIDs.get(recordID) ??
|
|
328
|
+
emptySet) {
|
|
329
|
+
if (!visited.has(fragment)) {
|
|
330
|
+
for (const anotherRecordID of this._resolverIDToRecordIDs.get(
|
|
331
|
+
fragment,
|
|
332
|
+
) ?? emptySet) {
|
|
333
|
+
this._markInvalidatedResolverRecord(anotherRecordID, recordSource);
|
|
334
|
+
if (!visited.has(anotherRecordID)) {
|
|
335
|
+
recordsToVisit.push(anotherRecordID);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
_markInvalidatedResolverRecord(
|
|
344
|
+
dataID: DataID,
|
|
345
|
+
recordSource: MutableRecordSource, // Written to
|
|
346
|
+
) {
|
|
347
|
+
const record = recordSource.get(dataID);
|
|
348
|
+
if (!record) {
|
|
349
|
+
warning(
|
|
350
|
+
false,
|
|
351
|
+
'Expected a resolver record with ID %s, but it was missing.',
|
|
352
|
+
dataID,
|
|
353
|
+
);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
const nextRecord = RelayModernRecord.clone(record);
|
|
357
|
+
RelayModernRecord.setValue(
|
|
358
|
+
nextRecord,
|
|
359
|
+
RELAY_RESOLVER_INVALIDATION_KEY,
|
|
360
|
+
true,
|
|
361
|
+
);
|
|
362
|
+
recordSource.set(dataID, nextRecord);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
_isInvalid(
|
|
366
|
+
record: Record,
|
|
367
|
+
getDataForResolverFragment: SingularReaderSelector => mixed,
|
|
368
|
+
): boolean {
|
|
369
|
+
if (!RelayModernRecord.getValue(record, RELAY_RESOLVER_INVALIDATION_KEY)) {
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
const originalInputs = RelayModernRecord.getValue(
|
|
373
|
+
record,
|
|
374
|
+
RELAY_RESOLVER_INPUTS_KEY,
|
|
375
|
+
);
|
|
376
|
+
// $FlowFixMe[incompatible-type] - storing values in records is not typed
|
|
377
|
+
const readerSelector: ?SingularReaderSelector = RelayModernRecord.getValue(
|
|
378
|
+
record,
|
|
379
|
+
RELAY_RESOLVER_READER_SELECTOR_KEY,
|
|
380
|
+
);
|
|
381
|
+
if (originalInputs == null || readerSelector == null) {
|
|
382
|
+
warning(
|
|
383
|
+
false,
|
|
384
|
+
'Expected previous inputs and reader selector on resolver record with ID %s, but they were missing.',
|
|
385
|
+
RelayModernRecord.getDataID(record),
|
|
386
|
+
);
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
const latestValues = getDataForResolverFragment(readerSelector);
|
|
390
|
+
const recycled = recycleNodesInto(originalInputs, latestValues);
|
|
391
|
+
if (recycled !== originalInputs) {
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Validate that a value is live state
|
|
399
|
+
// $FlowFixMe
|
|
400
|
+
function isLiveStateValue(v: Object): boolean {
|
|
401
|
+
return (
|
|
402
|
+
v != null &&
|
|
403
|
+
typeof v.read === 'function' &&
|
|
404
|
+
typeof v.subscribe === 'function'
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
module.exports = {
|
|
409
|
+
LiveResolverCache,
|
|
410
|
+
};
|