relay-runtime 11.0.0-rc.0 → 12.0.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/handlers/connection/ConnectionHandler.js.flow +7 -0
- package/handlers/connection/MutationHandlers.js.flow +28 -0
- package/index.js +1 -1
- package/index.js.flow +20 -3
- package/lib/handlers/RelayDefaultHandlerProvider.js +1 -1
- package/lib/handlers/connection/ConnectionHandler.js +12 -6
- package/lib/handlers/connection/MutationHandlers.js +67 -8
- package/lib/index.js +15 -0
- package/lib/multi-actor-environment/ActorIdentifier.js +33 -0
- package/lib/multi-actor-environment/ActorSpecificEnvironment.js +148 -0
- package/lib/multi-actor-environment/ActorUtils.js +27 -0
- package/lib/multi-actor-environment/MultiActorEnvironment.js +406 -0
- package/lib/multi-actor-environment/MultiActorEnvironmentTypes.js +11 -0
- package/lib/multi-actor-environment/index.js +21 -0
- package/lib/mutations/RelayRecordProxy.js +1 -1
- package/lib/mutations/RelayRecordSourceMutator.js +1 -1
- package/lib/mutations/RelayRecordSourceProxy.js +1 -1
- package/lib/mutations/RelayRecordSourceSelectorProxy.js +7 -2
- package/lib/mutations/applyOptimisticMutation.js +1 -1
- package/lib/mutations/commitMutation.js +5 -2
- package/lib/mutations/validateMutation.js +39 -17
- package/lib/network/RelayNetwork.js +1 -1
- package/lib/network/RelayObservable.js +3 -1
- package/lib/network/RelayQueryResponseCache.js +20 -3
- package/lib/network/wrapNetworkWithLogObserver.js +78 -0
- package/lib/query/GraphQLTag.js +1 -1
- package/lib/query/fetchQuery.js +1 -1
- package/lib/query/fetchQueryInternal.js +1 -1
- package/lib/store/DataChecker.js +132 -50
- package/lib/store/{RelayModernQueryExecutor.js → OperationExecutor.js} +524 -187
- package/lib/store/RelayConcreteVariables.js +29 -4
- package/lib/store/RelayModernEnvironment.js +137 -220
- package/lib/store/RelayModernFragmentSpecResolver.js +49 -23
- package/lib/store/RelayModernRecord.js +36 -2
- package/lib/store/RelayModernSelector.js +1 -1
- package/lib/store/RelayModernStore.js +53 -22
- package/lib/store/RelayOperationTracker.js +34 -24
- package/lib/store/RelayPublishQueue.js +30 -8
- package/lib/store/RelayReader.js +177 -29
- package/lib/store/RelayRecordSource.js +87 -3
- package/lib/store/RelayReferenceMarker.js +53 -28
- package/lib/store/RelayResponseNormalizer.js +247 -108
- package/lib/store/RelayStoreReactFlightUtils.js +7 -11
- package/lib/store/RelayStoreSubscriptions.js +8 -5
- package/lib/store/RelayStoreUtils.js +10 -4
- package/lib/store/ResolverCache.js +213 -0
- package/lib/store/ResolverFragments.js +57 -0
- package/lib/store/cloneRelayHandleSourceField.js +1 -1
- package/lib/store/cloneRelayScalarHandleSourceField.js +1 -1
- package/lib/store/createRelayContext.js +2 -2
- package/lib/store/defaultGetDataID.js +3 -1
- package/lib/store/readInlineData.js +1 -1
- package/lib/subscription/requestSubscription.js +32 -6
- package/lib/util/RelayConcreteNode.js +3 -0
- package/lib/util/RelayFeatureFlags.js +5 -4
- package/lib/util/RelayProfiler.js +17 -187
- package/lib/util/RelayReplaySubject.js +22 -7
- package/lib/util/deepFreeze.js +1 -0
- package/lib/util/getPaginationMetadata.js +41 -0
- package/lib/util/getPaginationVariables.js +67 -0
- package/lib/util/getPendingOperationsForFragment.js +55 -0
- package/lib/util/getRefetchMetadata.js +36 -0
- package/lib/util/getRelayHandleKey.js +1 -1
- package/lib/util/getRequestIdentifier.js +1 -1
- package/lib/util/getValueAtPath.js +51 -0
- package/lib/util/isEmptyObject.js +1 -1
- package/lib/util/registerEnvironmentWithDevTools.js +26 -0
- package/lib/util/withDuration.js +31 -0
- package/multi-actor-environment/ActorIdentifier.js.flow +43 -0
- package/multi-actor-environment/ActorSpecificEnvironment.js.flow +217 -0
- package/multi-actor-environment/ActorUtils.js.flow +33 -0
- package/multi-actor-environment/MultiActorEnvironment.js.flow +485 -0
- package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +245 -0
- package/multi-actor-environment/index.js.flow +27 -0
- package/mutations/RelayRecordSourceSelectorProxy.js.flow +7 -2
- package/mutations/commitMutation.js.flow +3 -1
- package/mutations/validateMutation.js.flow +42 -16
- package/network/RelayNetworkTypes.js.flow +17 -8
- package/network/RelayObservable.js.flow +2 -0
- package/network/RelayQueryResponseCache.js.flow +31 -17
- package/network/wrapNetworkWithLogObserver.js.flow +99 -0
- package/package.json +3 -2
- package/relay-runtime.js +2 -2
- package/relay-runtime.min.js +2 -2
- package/store/ClientID.js.flow +5 -1
- package/store/DataChecker.js.flow +148 -44
- package/store/{RelayModernQueryExecutor.js.flow → OperationExecutor.js.flow} +578 -237
- package/store/RelayConcreteVariables.js.flow +31 -1
- package/store/RelayModernEnvironment.js.flow +132 -220
- package/store/RelayModernFragmentSpecResolver.js.flow +40 -14
- package/store/RelayModernOperationDescriptor.js.flow +9 -3
- package/store/RelayModernRecord.js.flow +49 -0
- package/store/RelayModernStore.js.flow +57 -17
- package/store/RelayOperationTracker.js.flow +56 -34
- package/store/RelayPublishQueue.js.flow +37 -11
- package/store/RelayReader.js.flow +186 -27
- package/store/RelayRecordSource.js.flow +72 -6
- package/store/RelayReferenceMarker.js.flow +51 -21
- package/store/RelayResponseNormalizer.js.flow +251 -67
- package/store/RelayStoreReactFlightUtils.js.flow +6 -9
- package/store/RelayStoreSubscriptions.js.flow +10 -3
- package/store/RelayStoreTypes.js.flow +144 -21
- package/store/RelayStoreUtils.js.flow +19 -4
- package/store/ResolverCache.js.flow +247 -0
- package/store/ResolverFragments.js.flow +128 -0
- package/store/createRelayContext.js.flow +1 -1
- package/store/defaultGetDataID.js.flow +3 -1
- package/subscription/requestSubscription.js.flow +43 -8
- package/util/NormalizationNode.js.flow +16 -3
- package/util/ReaderNode.js.flow +29 -2
- package/util/RelayConcreteNode.js.flow +3 -0
- package/util/RelayFeatureFlags.js.flow +10 -6
- package/util/RelayProfiler.js.flow +22 -194
- package/util/RelayReplaySubject.js.flow +7 -6
- package/util/RelayRuntimeTypes.js.flow +4 -2
- package/util/deepFreeze.js.flow +2 -1
- package/util/getPaginationMetadata.js.flow +74 -0
- package/util/getPaginationVariables.js.flow +112 -0
- package/util/getPendingOperationsForFragment.js.flow +62 -0
- package/util/getRefetchMetadata.js.flow +80 -0
- package/util/getValueAtPath.js.flow +46 -0
- package/util/isEmptyObject.js.flow +2 -1
- package/util/registerEnvironmentWithDevTools.js.flow +33 -0
- package/util/withDuration.js.flow +32 -0
- package/lib/store/RelayRecordSourceMapImpl.js +0 -107
- package/lib/store/RelayStoreSubscriptionsUsingMapByID.js +0 -318
- package/store/RelayRecordSourceMapImpl.js.flow +0 -91
- package/store/RelayStoreSubscriptionsUsingMapByID.js.flow +0 -283
|
@@ -15,12 +15,13 @@
|
|
|
15
15
|
const RelayFeatureFlags = require('../util/RelayFeatureFlags');
|
|
16
16
|
|
|
17
17
|
const areEqual = require('areEqual');
|
|
18
|
+
const getPendingOperationsForFragment = require('../util/getPendingOperationsForFragment');
|
|
18
19
|
const invariant = require('invariant');
|
|
19
20
|
const isScalarAndEqual = require('../util/isScalarAndEqual');
|
|
21
|
+
const recycleNodesInto = require('../util/recycleNodesInto');
|
|
20
22
|
const reportMissingRequiredFields = require('../util/reportMissingRequiredFields');
|
|
21
23
|
const warning = require('warning');
|
|
22
24
|
|
|
23
|
-
const {getPromiseForActiveRequest} = require('../query/fetchQueryInternal');
|
|
24
25
|
const {createRequestDescriptor} = require('./RelayModernOperationDescriptor');
|
|
25
26
|
const {
|
|
26
27
|
areEqualSelectors,
|
|
@@ -137,13 +138,16 @@ class RelayModernFragmentSpecResolver implements FragmentSpecResolver {
|
|
|
137
138
|
return this._data;
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
setCallback(callback: () => void): void {
|
|
141
|
+
setCallback(props: Props, callback: () => void): void {
|
|
141
142
|
this._callback = callback;
|
|
143
|
+
if (RelayFeatureFlags.ENABLE_CONTAINERS_SUBSCRIBE_ON_COMMIT === true) {
|
|
144
|
+
this.setProps(props);
|
|
145
|
+
}
|
|
142
146
|
}
|
|
143
147
|
|
|
144
148
|
setProps(props: Props): void {
|
|
145
|
-
const ownedSelectors = getSelectorsFromObject(this._fragments, props);
|
|
146
149
|
this._props = {};
|
|
150
|
+
const ownedSelectors = getSelectorsFromObject(this._fragments, props);
|
|
147
151
|
|
|
148
152
|
for (const key in ownedSelectors) {
|
|
149
153
|
if (ownedSelectors.hasOwnProperty(key)) {
|
|
@@ -160,6 +164,7 @@ class RelayModernFragmentSpecResolver implements FragmentSpecResolver {
|
|
|
160
164
|
this._context.environment,
|
|
161
165
|
this._rootIsQueryRenderer,
|
|
162
166
|
ownedSelector,
|
|
167
|
+
this._callback != null,
|
|
163
168
|
this._onChange,
|
|
164
169
|
);
|
|
165
170
|
} else {
|
|
@@ -176,6 +181,7 @@ class RelayModernFragmentSpecResolver implements FragmentSpecResolver {
|
|
|
176
181
|
this._context.environment,
|
|
177
182
|
this._rootIsQueryRenderer,
|
|
178
183
|
ownedSelector,
|
|
184
|
+
this._callback != null,
|
|
179
185
|
this._onChange,
|
|
180
186
|
);
|
|
181
187
|
} else {
|
|
@@ -232,6 +238,7 @@ class SelectorResolver {
|
|
|
232
238
|
environment: IEnvironment,
|
|
233
239
|
rootIsQueryRenderer: boolean,
|
|
234
240
|
selector: SingularReaderSelector,
|
|
241
|
+
subscribeOnConstruction: boolean,
|
|
235
242
|
callback: () => void,
|
|
236
243
|
) {
|
|
237
244
|
const snapshot = environment.lookup(selector);
|
|
@@ -242,7 +249,13 @@ class SelectorResolver {
|
|
|
242
249
|
this._environment = environment;
|
|
243
250
|
this._rootIsQueryRenderer = rootIsQueryRenderer;
|
|
244
251
|
this._selector = selector;
|
|
245
|
-
|
|
252
|
+
if (RelayFeatureFlags.ENABLE_CONTAINERS_SUBSCRIBE_ON_COMMIT === true) {
|
|
253
|
+
if (subscribeOnConstruction) {
|
|
254
|
+
this._subscription = environment.subscribe(snapshot, this._onChange);
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
this._subscription = environment.subscribe(snapshot, this._onChange);
|
|
258
|
+
}
|
|
246
259
|
}
|
|
247
260
|
|
|
248
261
|
dispose(): void {
|
|
@@ -253,10 +266,7 @@ class SelectorResolver {
|
|
|
253
266
|
}
|
|
254
267
|
|
|
255
268
|
resolve(): ?Object {
|
|
256
|
-
if (
|
|
257
|
-
RelayFeatureFlags.ENABLE_RELAY_CONTAINERS_SUSPENSE === true &&
|
|
258
|
-
this._isMissingData === true
|
|
259
|
-
) {
|
|
269
|
+
if (this._isMissingData === true) {
|
|
260
270
|
// NOTE: This branch exists to handle the case in which:
|
|
261
271
|
// - A RelayModern container is rendered as a descendant of a Relay Hook
|
|
262
272
|
// root using a "partial" renderPolicy (this means that eargerly
|
|
@@ -278,11 +288,12 @@ class SelectorResolver {
|
|
|
278
288
|
// This should eventually go away with something like @optional, where we only
|
|
279
289
|
// suspend at specific boundaries depending on whether the boundary
|
|
280
290
|
// can be fulfilled or not.
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
this.
|
|
284
|
-
|
|
285
|
-
|
|
291
|
+
const pendingOperationsResult = getPendingOperationsForFragment(
|
|
292
|
+
this._environment,
|
|
293
|
+
this._selector.node,
|
|
294
|
+
this._selector.owner,
|
|
295
|
+
);
|
|
296
|
+
const promise: void | Promise<void> = pendingOperationsResult?.promise;
|
|
286
297
|
if (promise != null) {
|
|
287
298
|
if (this._rootIsQueryRenderer) {
|
|
288
299
|
warning(
|
|
@@ -293,6 +304,8 @@ class SelectorResolver {
|
|
|
293
304
|
this._selector.node.name,
|
|
294
305
|
);
|
|
295
306
|
} else {
|
|
307
|
+
const pendingOperations =
|
|
308
|
+
pendingOperationsResult?.pendingOperations ?? [];
|
|
296
309
|
warning(
|
|
297
310
|
false,
|
|
298
311
|
'Relay: Relay Container for fragment `%s` suspended. When using ' +
|
|
@@ -300,6 +313,15 @@ class SelectorResolver {
|
|
|
300
313
|
'of a Relay Container.',
|
|
301
314
|
this._selector.node.name,
|
|
302
315
|
);
|
|
316
|
+
this._environment.__log({
|
|
317
|
+
name: 'suspense.fragment',
|
|
318
|
+
data: this._data,
|
|
319
|
+
fragment: this._selector.node,
|
|
320
|
+
isRelayHooks: false,
|
|
321
|
+
isMissingData: this._isMissingData,
|
|
322
|
+
isPromiseCached: false,
|
|
323
|
+
pendingOperations,
|
|
324
|
+
});
|
|
303
325
|
throw promise;
|
|
304
326
|
}
|
|
305
327
|
}
|
|
@@ -322,7 +344,7 @@ class SelectorResolver {
|
|
|
322
344
|
}
|
|
323
345
|
this.dispose();
|
|
324
346
|
const snapshot = this._environment.lookup(selector);
|
|
325
|
-
this._data = snapshot.data;
|
|
347
|
+
this._data = recycleNodesInto(this._data, snapshot.data);
|
|
326
348
|
this._isMissingData = snapshot.isMissingData;
|
|
327
349
|
this._missingRequiredFields = snapshot.missingRequiredFields;
|
|
328
350
|
this._selector = selector;
|
|
@@ -375,11 +397,13 @@ class SelectorListResolver {
|
|
|
375
397
|
_resolvers: Array<SelectorResolver>;
|
|
376
398
|
_rootIsQueryRenderer: boolean;
|
|
377
399
|
_stale: boolean;
|
|
400
|
+
_subscribeOnConstruction: boolean;
|
|
378
401
|
|
|
379
402
|
constructor(
|
|
380
403
|
environment: IEnvironment,
|
|
381
404
|
rootIsQueryRenderer: boolean,
|
|
382
405
|
selector: PluralReaderSelector,
|
|
406
|
+
subscribeOnConstruction: boolean,
|
|
383
407
|
callback: () => void,
|
|
384
408
|
) {
|
|
385
409
|
this._callback = callback;
|
|
@@ -388,6 +412,7 @@ class SelectorListResolver {
|
|
|
388
412
|
this._resolvers = [];
|
|
389
413
|
this._stale = true;
|
|
390
414
|
this._rootIsQueryRenderer = rootIsQueryRenderer;
|
|
415
|
+
this._subscribeOnConstruction = subscribeOnConstruction;
|
|
391
416
|
|
|
392
417
|
this.setSelector(selector);
|
|
393
418
|
}
|
|
@@ -433,6 +458,7 @@ class SelectorListResolver {
|
|
|
433
458
|
this._environment,
|
|
434
459
|
this._rootIsQueryRenderer,
|
|
435
460
|
selectors[ii],
|
|
461
|
+
this._subscribeOnConstruction,
|
|
436
462
|
this._onChange,
|
|
437
463
|
);
|
|
438
464
|
}
|
|
@@ -23,7 +23,13 @@ const {
|
|
|
23
23
|
const {ROOT_ID} = require('./RelayStoreUtils');
|
|
24
24
|
|
|
25
25
|
import type {ConcreteRequest} from '../util/RelayConcreteNode';
|
|
26
|
-
import type {
|
|
26
|
+
import type {
|
|
27
|
+
CacheConfig,
|
|
28
|
+
DataID,
|
|
29
|
+
Variables,
|
|
30
|
+
VariablesOf,
|
|
31
|
+
OperationType,
|
|
32
|
+
} from '../util/RelayRuntimeTypes';
|
|
27
33
|
import type {OperationDescriptor, RequestDescriptor} from './RelayStoreTypes';
|
|
28
34
|
|
|
29
35
|
/**
|
|
@@ -32,9 +38,9 @@ import type {OperationDescriptor, RequestDescriptor} from './RelayStoreTypes';
|
|
|
32
38
|
* are filtered to exclude variables that do not match defined arguments on the
|
|
33
39
|
* operation, and default values are populated for null values.
|
|
34
40
|
*/
|
|
35
|
-
function createOperationDescriptor(
|
|
41
|
+
function createOperationDescriptor<TQuery: OperationType>(
|
|
36
42
|
request: ConcreteRequest,
|
|
37
|
-
variables:
|
|
43
|
+
variables: VariablesOf<TQuery>,
|
|
38
44
|
cacheConfig?: ?CacheConfig,
|
|
39
45
|
dataID?: DataID = ROOT_ID,
|
|
40
46
|
): OperationDescriptor {
|
|
@@ -19,6 +19,7 @@ const warning = require('warning');
|
|
|
19
19
|
|
|
20
20
|
const {isClientID} = require('./ClientID');
|
|
21
21
|
const {
|
|
22
|
+
ACTOR_IDENTIFIER_KEY,
|
|
22
23
|
ID_KEY,
|
|
23
24
|
REF_KEY,
|
|
24
25
|
REFS_KEY,
|
|
@@ -27,6 +28,7 @@ const {
|
|
|
27
28
|
ROOT_ID,
|
|
28
29
|
} = require('./RelayStoreUtils');
|
|
29
30
|
|
|
31
|
+
import type {ActorIdentifier} from '../multi-actor-environment/ActorIdentifier';
|
|
30
32
|
import type {DataID} from '../util/RelayRuntimeTypes';
|
|
31
33
|
import type {Record} from './RelayStoreTypes';
|
|
32
34
|
|
|
@@ -384,6 +386,51 @@ function setLinkedRecordIDs(
|
|
|
384
386
|
record[storageKey] = links;
|
|
385
387
|
}
|
|
386
388
|
|
|
389
|
+
/**
|
|
390
|
+
* @public
|
|
391
|
+
*
|
|
392
|
+
* Set the value of a field to a reference to another record in the actor specific store.
|
|
393
|
+
*/
|
|
394
|
+
function setActorLinkedRecordID(
|
|
395
|
+
record: Record,
|
|
396
|
+
storageKey: string,
|
|
397
|
+
actorIdentifier: ActorIdentifier,
|
|
398
|
+
linkedID: DataID,
|
|
399
|
+
): void {
|
|
400
|
+
// See perf note above for why we aren't using computed property access.
|
|
401
|
+
const link = {};
|
|
402
|
+
link[REF_KEY] = linkedID;
|
|
403
|
+
link[ACTOR_IDENTIFIER_KEY] = actorIdentifier;
|
|
404
|
+
record[storageKey] = link;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* @public
|
|
409
|
+
*
|
|
410
|
+
* Get link to a record and the actor identifier for the store.
|
|
411
|
+
*/
|
|
412
|
+
function getActorLinkedRecordID(
|
|
413
|
+
record: Record,
|
|
414
|
+
storageKey: string,
|
|
415
|
+
): ?[ActorIdentifier, DataID] {
|
|
416
|
+
const link = record[storageKey];
|
|
417
|
+
if (link == null) {
|
|
418
|
+
return link;
|
|
419
|
+
}
|
|
420
|
+
invariant(
|
|
421
|
+
typeof link === 'object' &&
|
|
422
|
+
typeof link[REF_KEY] === 'string' &&
|
|
423
|
+
link[ACTOR_IDENTIFIER_KEY] != null,
|
|
424
|
+
'RelayModernRecord.getActorLinkedRecordID(): Expected `%s.%s` to be an actor specific linked ID, ' +
|
|
425
|
+
'was `%s`.',
|
|
426
|
+
record[ID_KEY],
|
|
427
|
+
storageKey,
|
|
428
|
+
JSON.stringify(link),
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
return [(link[ACTOR_IDENTIFIER_KEY]: any), (link[REF_KEY]: any)];
|
|
432
|
+
}
|
|
433
|
+
|
|
387
434
|
module.exports = {
|
|
388
435
|
clone,
|
|
389
436
|
copyFields,
|
|
@@ -400,4 +447,6 @@ module.exports = {
|
|
|
400
447
|
setLinkedRecordID,
|
|
401
448
|
setLinkedRecordIDs,
|
|
402
449
|
update,
|
|
450
|
+
getActorLinkedRecordID,
|
|
451
|
+
setActorLinkedRecordID,
|
|
403
452
|
};
|
|
@@ -16,12 +16,10 @@ const DataChecker = require('./DataChecker');
|
|
|
16
16
|
const RelayFeatureFlags = require('../util/RelayFeatureFlags');
|
|
17
17
|
const RelayModernRecord = require('./RelayModernRecord');
|
|
18
18
|
const RelayOptimisticRecordSource = require('./RelayOptimisticRecordSource');
|
|
19
|
-
const RelayProfiler = require('../util/RelayProfiler');
|
|
20
19
|
const RelayReader = require('./RelayReader');
|
|
21
20
|
const RelayReferenceMarker = require('./RelayReferenceMarker');
|
|
22
21
|
const RelayStoreReactFlightUtils = require('./RelayStoreReactFlightUtils');
|
|
23
22
|
const RelayStoreSubscriptions = require('./RelayStoreSubscriptions');
|
|
24
|
-
const RelayStoreSubscriptionsUsingMapByID = require('./RelayStoreSubscriptionsUsingMapByID');
|
|
25
23
|
const RelayStoreUtils = require('./RelayStoreUtils');
|
|
26
24
|
|
|
27
25
|
const deepFreeze = require('../util/deepFreeze');
|
|
@@ -29,8 +27,14 @@ const defaultGetDataID = require('./defaultGetDataID');
|
|
|
29
27
|
const invariant = require('invariant');
|
|
30
28
|
const resolveImmediate = require('../util/resolveImmediate');
|
|
31
29
|
|
|
30
|
+
const {
|
|
31
|
+
INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE,
|
|
32
|
+
assertInternalActorIndentifier,
|
|
33
|
+
} = require('../multi-actor-environment/ActorIdentifier');
|
|
32
34
|
const {ROOT_ID, ROOT_TYPE} = require('./RelayStoreUtils');
|
|
35
|
+
const {RecordResolverCache} = require('./ResolverCache');
|
|
33
36
|
|
|
37
|
+
import type {ActorIdentifier} from '../multi-actor-environment/ActorIdentifier';
|
|
34
38
|
import type {DataID, Disposable} from '../util/RelayRuntimeTypes';
|
|
35
39
|
import type {Availability} from './DataChecker';
|
|
36
40
|
import type {GetDataID} from './RelayResponseNormalizer';
|
|
@@ -50,6 +54,7 @@ import type {
|
|
|
50
54
|
StoreSubscriptions,
|
|
51
55
|
DataIDSet,
|
|
52
56
|
} from './RelayStoreTypes';
|
|
57
|
+
import type {ResolverCache} from './ResolverCache';
|
|
53
58
|
|
|
54
59
|
export opaque type InvalidationState = {|
|
|
55
60
|
dataIDs: $ReadOnlyArray<DataID>,
|
|
@@ -90,6 +95,7 @@ class RelayModernStore implements Store {
|
|
|
90
95
|
_operationLoader: ?OperationLoader;
|
|
91
96
|
_optimisticSource: ?MutableRecordSource;
|
|
92
97
|
_recordSource: MutableRecordSource;
|
|
98
|
+
_resolverCache: ResolverCache;
|
|
93
99
|
_releaseBuffer: Array<string>;
|
|
94
100
|
_roots: Map<
|
|
95
101
|
string,
|
|
@@ -103,6 +109,7 @@ class RelayModernStore implements Store {
|
|
|
103
109
|
_shouldScheduleGC: boolean;
|
|
104
110
|
_storeSubscriptions: StoreSubscriptions;
|
|
105
111
|
_updatedRecordIDs: DataIDSet;
|
|
112
|
+
_shouldProcessClientComponents: ?boolean;
|
|
106
113
|
|
|
107
114
|
constructor(
|
|
108
115
|
source: MutableRecordSource,
|
|
@@ -113,6 +120,7 @@ class RelayModernStore implements Store {
|
|
|
113
120
|
getDataID?: ?GetDataID,
|
|
114
121
|
gcReleaseBufferSize?: ?number,
|
|
115
122
|
queryCacheExpirationTime?: ?number,
|
|
123
|
+
shouldProcessClientComponents?: ?boolean,
|
|
116
124
|
|},
|
|
117
125
|
) {
|
|
118
126
|
// Prevent mutation of a record from outside the store.
|
|
@@ -143,11 +151,16 @@ class RelayModernStore implements Store {
|
|
|
143
151
|
this._releaseBuffer = [];
|
|
144
152
|
this._roots = new Map();
|
|
145
153
|
this._shouldScheduleGC = false;
|
|
146
|
-
this.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
154
|
+
this._resolverCache = new RecordResolverCache(() =>
|
|
155
|
+
this._getMutableRecordSource(),
|
|
156
|
+
);
|
|
157
|
+
this._storeSubscriptions = new RelayStoreSubscriptions(
|
|
158
|
+
options?.log,
|
|
159
|
+
this._resolverCache,
|
|
160
|
+
);
|
|
150
161
|
this._updatedRecordIDs = new Set();
|
|
162
|
+
this._shouldProcessClientComponents =
|
|
163
|
+
options?.shouldProcessClientComponents;
|
|
151
164
|
|
|
152
165
|
initializeRecordSource(this._recordSource);
|
|
153
166
|
}
|
|
@@ -156,12 +169,16 @@ class RelayModernStore implements Store {
|
|
|
156
169
|
return this._optimisticSource ?? this._recordSource;
|
|
157
170
|
}
|
|
158
171
|
|
|
172
|
+
_getMutableRecordSource(): MutableRecordSource {
|
|
173
|
+
return this._optimisticSource ?? this._recordSource;
|
|
174
|
+
}
|
|
175
|
+
|
|
159
176
|
check(
|
|
160
177
|
operation: OperationDescriptor,
|
|
161
178
|
options?: CheckOptions,
|
|
162
179
|
): OperationAvailability {
|
|
163
180
|
const selector = operation.root;
|
|
164
|
-
const source = this.
|
|
181
|
+
const source = this._getMutableRecordSource();
|
|
165
182
|
const globalInvalidationEpoch = this._globalInvalidationEpoch;
|
|
166
183
|
|
|
167
184
|
const rootEntry = this._roots.get(operation.request.identifier);
|
|
@@ -170,7 +187,7 @@ class RelayModernStore implements Store {
|
|
|
170
187
|
// Check if store has been globally invalidated
|
|
171
188
|
if (globalInvalidationEpoch != null) {
|
|
172
189
|
// If so, check if the operation we're checking was last written
|
|
173
|
-
// before or after invalidation
|
|
190
|
+
// before or after invalidation occurred.
|
|
174
191
|
if (
|
|
175
192
|
operationLastWrittenAt == null ||
|
|
176
193
|
operationLastWrittenAt <= globalInvalidationEpoch
|
|
@@ -183,15 +200,29 @@ class RelayModernStore implements Store {
|
|
|
183
200
|
}
|
|
184
201
|
}
|
|
185
202
|
|
|
186
|
-
const target = options?.target ?? source;
|
|
187
203
|
const handlers = options?.handlers ?? [];
|
|
204
|
+
const getSourceForActor =
|
|
205
|
+
options?.getSourceForActor ??
|
|
206
|
+
(actorIdentifier => {
|
|
207
|
+
assertInternalActorIndentifier(actorIdentifier);
|
|
208
|
+
return source;
|
|
209
|
+
});
|
|
210
|
+
const getTargetForActor =
|
|
211
|
+
options?.getTargetForActor ??
|
|
212
|
+
(actorIdentifier => {
|
|
213
|
+
assertInternalActorIndentifier(actorIdentifier);
|
|
214
|
+
return source;
|
|
215
|
+
});
|
|
216
|
+
|
|
188
217
|
const operationAvailability = DataChecker.check(
|
|
189
|
-
|
|
190
|
-
|
|
218
|
+
getSourceForActor,
|
|
219
|
+
getTargetForActor,
|
|
220
|
+
options?.defaultActorIdentifier ?? INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE,
|
|
191
221
|
selector,
|
|
192
222
|
handlers,
|
|
193
223
|
this._operationLoader,
|
|
194
224
|
this._getDataID,
|
|
225
|
+
this._shouldProcessClientComponents,
|
|
195
226
|
);
|
|
196
227
|
|
|
197
228
|
return getAvailabilityStatus(
|
|
@@ -270,7 +301,7 @@ class RelayModernStore implements Store {
|
|
|
270
301
|
|
|
271
302
|
lookup(selector: SingularReaderSelector): Snapshot {
|
|
272
303
|
const source = this.getSource();
|
|
273
|
-
const snapshot = RelayReader.read(source, selector);
|
|
304
|
+
const snapshot = RelayReader.read(source, selector, this._resolverCache);
|
|
274
305
|
if (__DEV__) {
|
|
275
306
|
deepFreeze(snapshot);
|
|
276
307
|
}
|
|
@@ -298,6 +329,13 @@ class RelayModernStore implements Store {
|
|
|
298
329
|
this._globalInvalidationEpoch = this._currentWriteEpoch;
|
|
299
330
|
}
|
|
300
331
|
|
|
332
|
+
if (RelayFeatureFlags.ENABLE_RELAY_RESOLVERS) {
|
|
333
|
+
// When a record is updated, we need to also handle records that depend on it,
|
|
334
|
+
// specifically Relay Resolver result records containing results based on the
|
|
335
|
+
// updated records. This both adds to updatedRecordIDs and invalidates any
|
|
336
|
+
// cached data as needed.
|
|
337
|
+
this._resolverCache.invalidateDataIDs(this._updatedRecordIDs);
|
|
338
|
+
}
|
|
301
339
|
const source = this.getSource();
|
|
302
340
|
const updatedOwners = [];
|
|
303
341
|
this._storeSubscriptions.updateSubscriptions(
|
|
@@ -360,7 +398,7 @@ class RelayModernStore implements Store {
|
|
|
360
398
|
}
|
|
361
399
|
|
|
362
400
|
publish(source: RecordSource, idsMarkedForInvalidation?: DataIDSet): void {
|
|
363
|
-
const target = this.
|
|
401
|
+
const target = this._getMutableRecordSource();
|
|
364
402
|
updateTargetFromSource(
|
|
365
403
|
target,
|
|
366
404
|
source,
|
|
@@ -413,6 +451,10 @@ class RelayModernStore implements Store {
|
|
|
413
451
|
return 'RelayModernStore()';
|
|
414
452
|
}
|
|
415
453
|
|
|
454
|
+
getEpoch(): number {
|
|
455
|
+
return this._currentWriteEpoch;
|
|
456
|
+
}
|
|
457
|
+
|
|
416
458
|
// Internal API
|
|
417
459
|
__getUpdatedRecordIDs(): DataIDSet {
|
|
418
460
|
return this._updatedRecordIDs;
|
|
@@ -574,6 +616,7 @@ class RelayModernStore implements Store {
|
|
|
574
616
|
selector,
|
|
575
617
|
references,
|
|
576
618
|
this._operationLoader,
|
|
619
|
+
this._shouldProcessClientComponents,
|
|
577
620
|
);
|
|
578
621
|
// Yield for other work after each operation
|
|
579
622
|
yield;
|
|
@@ -672,6 +715,7 @@ function updateTargetFromSource(
|
|
|
672
715
|
currentWriteEpoch,
|
|
673
716
|
);
|
|
674
717
|
invalidatedRecordIDs.add(dataID);
|
|
718
|
+
// $FlowFixMe[incompatible-call]
|
|
675
719
|
target.set(dataID, nextRecord);
|
|
676
720
|
});
|
|
677
721
|
}
|
|
@@ -763,8 +807,4 @@ function getAvailabilityStatus(
|
|
|
763
807
|
return {status: 'available', fetchTime: operationFetchTime ?? null};
|
|
764
808
|
}
|
|
765
809
|
|
|
766
|
-
RelayProfiler.instrumentMethods(RelayModernStore.prototype, {
|
|
767
|
-
lookup: 'RelayModernStore.prototype.lookup',
|
|
768
|
-
});
|
|
769
|
-
|
|
770
810
|
module.exports = RelayModernStore;
|
|
@@ -17,17 +17,21 @@ const invariant = require('invariant');
|
|
|
17
17
|
import type {RequestDescriptor} from './RelayStoreTypes';
|
|
18
18
|
|
|
19
19
|
class RelayOperationTracker {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
_ownersToPendingOperations: Map<string, Map<string, RequestDescriptor>>;
|
|
21
|
+
_pendingOperationsToOwners: Map<string, Set<string>>;
|
|
22
|
+
_ownersToPendingPromise: Map<
|
|
23
23
|
string,
|
|
24
|
-
{|
|
|
24
|
+
{|
|
|
25
|
+
promise: Promise<void>,
|
|
26
|
+
resolve: () => void,
|
|
27
|
+
pendingOperations: $ReadOnlyArray<RequestDescriptor>,
|
|
28
|
+
|},
|
|
25
29
|
>;
|
|
26
30
|
|
|
27
31
|
constructor() {
|
|
28
|
-
this.
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
32
|
+
this._ownersToPendingOperations = new Map();
|
|
33
|
+
this._pendingOperationsToOwners = new Map();
|
|
34
|
+
this._ownersToPendingPromise = new Map();
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
/**
|
|
@@ -45,7 +49,7 @@ class RelayOperationTracker {
|
|
|
45
49
|
const newlyAffectedOwnersIdentifier = new Set();
|
|
46
50
|
for (const owner of affectedOwners) {
|
|
47
51
|
const ownerIdentifier = owner.identifier;
|
|
48
|
-
const pendingOperationsAffectingOwner = this.
|
|
52
|
+
const pendingOperationsAffectingOwner = this._ownersToPendingOperations.get(
|
|
49
53
|
ownerIdentifier,
|
|
50
54
|
);
|
|
51
55
|
if (pendingOperationsAffectingOwner != null) {
|
|
@@ -53,14 +57,17 @@ class RelayOperationTracker {
|
|
|
53
57
|
// We just need to detect, is it the same operation that we already
|
|
54
58
|
// have in the list, or it's a new operation
|
|
55
59
|
if (!pendingOperationsAffectingOwner.has(pendingOperationIdentifier)) {
|
|
56
|
-
pendingOperationsAffectingOwner.
|
|
60
|
+
pendingOperationsAffectingOwner.set(
|
|
61
|
+
pendingOperationIdentifier,
|
|
62
|
+
pendingOperation,
|
|
63
|
+
);
|
|
57
64
|
newlyAffectedOwnersIdentifier.add(ownerIdentifier);
|
|
58
65
|
}
|
|
59
66
|
} else {
|
|
60
67
|
// This is a new `ownerIdentifier` that is affected by the operation
|
|
61
|
-
this.
|
|
68
|
+
this._ownersToPendingOperations.set(
|
|
62
69
|
ownerIdentifier,
|
|
63
|
-
new
|
|
70
|
+
new Map([[pendingOperationIdentifier, pendingOperation]]),
|
|
64
71
|
);
|
|
65
72
|
newlyAffectedOwnersIdentifier.add(ownerIdentifier);
|
|
66
73
|
}
|
|
@@ -72,19 +79,18 @@ class RelayOperationTracker {
|
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
// But, if some owners were affected we need to add them to
|
|
75
|
-
// the `
|
|
76
|
-
const
|
|
77
|
-
this.
|
|
78
|
-
|
|
79
|
-
) || new Set();
|
|
82
|
+
// the `_pendingOperationsToOwners` set
|
|
83
|
+
const ownersAffectedByPendingOperation =
|
|
84
|
+
this._pendingOperationsToOwners.get(pendingOperationIdentifier) ||
|
|
85
|
+
new Set();
|
|
80
86
|
|
|
81
87
|
for (const ownerIdentifier of newlyAffectedOwnersIdentifier) {
|
|
82
88
|
this._resolveOwnerResolvers(ownerIdentifier);
|
|
83
|
-
|
|
89
|
+
ownersAffectedByPendingOperation.add(ownerIdentifier);
|
|
84
90
|
}
|
|
85
|
-
this.
|
|
91
|
+
this._pendingOperationsToOwners.set(
|
|
86
92
|
pendingOperationIdentifier,
|
|
87
|
-
|
|
93
|
+
ownersAffectedByPendingOperation,
|
|
88
94
|
);
|
|
89
95
|
}
|
|
90
96
|
|
|
@@ -94,7 +100,7 @@ class RelayOperationTracker {
|
|
|
94
100
|
*/
|
|
95
101
|
complete(pendingOperation: RequestDescriptor): void {
|
|
96
102
|
const pendingOperationIdentifier = pendingOperation.identifier;
|
|
97
|
-
const affectedOwnersIdentifier = this.
|
|
103
|
+
const affectedOwnersIdentifier = this._pendingOperationsToOwners.get(
|
|
98
104
|
pendingOperationIdentifier,
|
|
99
105
|
);
|
|
100
106
|
if (affectedOwnersIdentifier == null) {
|
|
@@ -107,7 +113,7 @@ class RelayOperationTracker {
|
|
|
107
113
|
// and some other operations
|
|
108
114
|
const updatedOwnersIdentifier = new Set();
|
|
109
115
|
for (const ownerIdentifier of affectedOwnersIdentifier) {
|
|
110
|
-
const pendingOperationsAffectingOwner = this.
|
|
116
|
+
const pendingOperationsAffectingOwner = this._ownersToPendingOperations.get(
|
|
111
117
|
ownerIdentifier,
|
|
112
118
|
);
|
|
113
119
|
if (!pendingOperationsAffectingOwner) {
|
|
@@ -124,7 +130,7 @@ class RelayOperationTracker {
|
|
|
124
130
|
// Complete subscriptions for all owners, affected by `pendingOperationIdentifier`
|
|
125
131
|
for (const ownerIdentifier of completedOwnersIdentifier) {
|
|
126
132
|
this._resolveOwnerResolvers(ownerIdentifier);
|
|
127
|
-
this.
|
|
133
|
+
this._ownersToPendingOperations.delete(ownerIdentifier);
|
|
128
134
|
}
|
|
129
135
|
|
|
130
136
|
// Update all ownerIdentifier that were updated by `pendingOperationIdentifier` but still
|
|
@@ -134,31 +140,42 @@ class RelayOperationTracker {
|
|
|
134
140
|
}
|
|
135
141
|
|
|
136
142
|
// Finally, remove pending operation identifier
|
|
137
|
-
this.
|
|
138
|
-
pendingOperationIdentifier,
|
|
139
|
-
);
|
|
143
|
+
this._pendingOperationsToOwners.delete(pendingOperationIdentifier);
|
|
140
144
|
}
|
|
141
145
|
|
|
142
146
|
_resolveOwnerResolvers(ownerIdentifier: string): void {
|
|
143
|
-
const promiseEntry = this.
|
|
147
|
+
const promiseEntry = this._ownersToPendingPromise.get(ownerIdentifier);
|
|
144
148
|
if (promiseEntry != null) {
|
|
145
149
|
promiseEntry.resolve();
|
|
146
150
|
}
|
|
147
|
-
this.
|
|
151
|
+
this._ownersToPendingPromise.delete(ownerIdentifier);
|
|
148
152
|
}
|
|
149
153
|
|
|
150
|
-
|
|
154
|
+
getPendingOperationsAffectingOwner(
|
|
151
155
|
owner: RequestDescriptor,
|
|
152
|
-
):
|
|
156
|
+
): {|
|
|
157
|
+
promise: Promise<void>,
|
|
158
|
+
pendingOperations: $ReadOnlyArray<RequestDescriptor>,
|
|
159
|
+
|} | null {
|
|
153
160
|
const ownerIdentifier = owner.identifier;
|
|
154
|
-
|
|
161
|
+
const pendingOperationsForOwner = this._ownersToPendingOperations.get(
|
|
162
|
+
ownerIdentifier,
|
|
163
|
+
);
|
|
164
|
+
if (
|
|
165
|
+
pendingOperationsForOwner == null ||
|
|
166
|
+
pendingOperationsForOwner.size === 0
|
|
167
|
+
) {
|
|
155
168
|
return null;
|
|
156
169
|
}
|
|
157
|
-
|
|
170
|
+
|
|
171
|
+
const cachedPromiseEntry = this._ownersToPendingPromise.get(
|
|
158
172
|
ownerIdentifier,
|
|
159
173
|
);
|
|
160
174
|
if (cachedPromiseEntry != null) {
|
|
161
|
-
return
|
|
175
|
+
return {
|
|
176
|
+
promise: cachedPromiseEntry.promise,
|
|
177
|
+
pendingOperations: cachedPromiseEntry.pendingOperations,
|
|
178
|
+
};
|
|
162
179
|
}
|
|
163
180
|
let resolve;
|
|
164
181
|
const promise = new Promise(r => {
|
|
@@ -169,8 +186,13 @@ class RelayOperationTracker {
|
|
|
169
186
|
'RelayOperationTracker: Expected resolver to be defined. If you' +
|
|
170
187
|
'are seeing this, it is likely a bug in Relay.',
|
|
171
188
|
);
|
|
172
|
-
|
|
173
|
-
|
|
189
|
+
const pendingOperations = Array.from(pendingOperationsForOwner.values());
|
|
190
|
+
this._ownersToPendingPromise.set(ownerIdentifier, {
|
|
191
|
+
promise,
|
|
192
|
+
resolve,
|
|
193
|
+
pendingOperations,
|
|
194
|
+
});
|
|
195
|
+
return {promise, pendingOperations};
|
|
174
196
|
}
|
|
175
197
|
}
|
|
176
198
|
|