relay-runtime 18.2.0 → 20.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.
Files changed (83) hide show
  1. package/experimental.js +1 -1
  2. package/experimental.js.flow +8 -6
  3. package/index.js +1 -1
  4. package/index.js.flow +3 -0
  5. package/lib/experimental.js +5 -2
  6. package/lib/index.js +3 -0
  7. package/lib/multi-actor-environment/ActorSpecificEnvironment.js +1 -1
  8. package/lib/mutations/RelayRecordSourceProxy.js +2 -1
  9. package/lib/mutations/createUpdatableProxy.js +1 -1
  10. package/lib/mutations/validateMutation.js +2 -2
  11. package/lib/network/RelayObservable.js +1 -3
  12. package/lib/network/wrapNetworkWithLogObserver.js +2 -2
  13. package/lib/query/fetchQuery.js +1 -1
  14. package/lib/store/DataChecker.js +12 -8
  15. package/lib/store/OperationExecutor.js +93 -43
  16. package/lib/store/RelayModernEnvironment.js +13 -4
  17. package/lib/store/RelayModernFragmentSpecResolver.js +4 -4
  18. package/lib/store/RelayModernStore.js +49 -24
  19. package/lib/store/RelayPublishQueue.js +11 -15
  20. package/lib/store/RelayReader.js +134 -151
  21. package/lib/store/RelayReferenceMarker.js +14 -7
  22. package/lib/store/RelayResponseNormalizer.js +57 -31
  23. package/lib/store/RelayStoreSubscriptions.js +2 -2
  24. package/lib/store/RelayStoreUtils.js +8 -0
  25. package/lib/store/ResolverFragments.js +2 -2
  26. package/lib/store/createRelayLoggingContext.js +17 -0
  27. package/lib/store/generateTypenamePrefixedDataID.js +9 -0
  28. package/lib/store/live-resolvers/LiveResolverCache.js +4 -2
  29. package/lib/store/live-resolvers/resolverDataInjector.js +4 -4
  30. package/lib/store/normalizeResponse.js +2 -2
  31. package/lib/store/observeFragmentExperimental.js +60 -13
  32. package/lib/store/observeQueryExperimental.js +21 -0
  33. package/lib/util/RelayFeatureFlags.js +7 -1
  34. package/lib/util/handlePotentialSnapshotErrors.js +11 -8
  35. package/multi-actor-environment/ActorSpecificEnvironment.js.flow +1 -0
  36. package/mutations/RelayRecordSourceProxy.js.flow +4 -0
  37. package/mutations/createUpdatableProxy.js.flow +1 -1
  38. package/mutations/validateMutation.js.flow +3 -3
  39. package/network/RelayNetworkTypes.js.flow +3 -0
  40. package/network/RelayObservable.js.flow +1 -5
  41. package/network/wrapNetworkWithLogObserver.js.flow +19 -1
  42. package/package.json +1 -1
  43. package/query/fetchQuery.js.flow +1 -1
  44. package/store/DataChecker.js.flow +16 -4
  45. package/store/OperationExecutor.js.flow +101 -15
  46. package/store/RelayExperimentalGraphResponseTransform.js.flow +4 -4
  47. package/store/RelayModernEnvironment.js.flow +22 -6
  48. package/store/RelayModernFragmentSpecResolver.js.flow +6 -6
  49. package/store/RelayModernSelector.js.flow +2 -0
  50. package/store/RelayModernStore.js.flow +86 -27
  51. package/store/RelayPublishQueue.js.flow +32 -21
  52. package/store/RelayReader.js.flow +168 -97
  53. package/store/RelayReferenceMarker.js.flow +15 -5
  54. package/store/RelayResponseNormalizer.js.flow +104 -69
  55. package/store/RelayStoreSubscriptions.js.flow +2 -2
  56. package/store/RelayStoreTypes.js.flow +34 -4
  57. package/store/RelayStoreUtils.js.flow +29 -0
  58. package/store/ResolverCache.js.flow +2 -2
  59. package/store/ResolverFragments.js.flow +5 -3
  60. package/store/StoreInspector.js.flow +5 -0
  61. package/store/createRelayContext.js.flow +3 -2
  62. package/store/createRelayLoggingContext.js.flow +46 -0
  63. package/store/generateTypenamePrefixedDataID.js.flow +25 -0
  64. package/store/live-resolvers/LiveResolverCache.js.flow +7 -2
  65. package/store/live-resolvers/resolverDataInjector.js.flow +10 -6
  66. package/store/normalizeResponse.js.flow +2 -0
  67. package/store/observeFragmentExperimental.js.flow +82 -28
  68. package/store/observeQueryExperimental.js.flow +61 -0
  69. package/store/waitForFragmentExperimental.js.flow +4 -3
  70. package/util/NormalizationNode.js.flow +2 -1
  71. package/util/RelayConcreteNode.js.flow +2 -0
  72. package/util/RelayError.js.flow +1 -0
  73. package/util/RelayFeatureFlags.js.flow +28 -0
  74. package/util/RelayRuntimeTypes.js.flow +6 -3
  75. package/util/getPaginationVariables.js.flow +2 -0
  76. package/util/handlePotentialSnapshotErrors.js.flow +23 -11
  77. package/util/registerEnvironmentWithDevTools.js.flow +4 -2
  78. package/util/withProvidedVariables.js.flow +1 -0
  79. package/util/withStartAndDuration.js.flow +3 -0
  80. package/relay-runtime-experimental.js +0 -4
  81. package/relay-runtime-experimental.min.js +0 -9
  82. package/relay-runtime.js +0 -4
  83. package/relay-runtime.min.js +0 -9
@@ -12,6 +12,7 @@
12
12
  'use strict';
13
13
 
14
14
  import type {RelayContext} from './RelayStoreTypes.js';
15
+ import type {Context} from 'react';
15
16
  import typeof {createContext} from 'react';
16
17
 
17
18
  const invariant = require('invariant');
@@ -24,10 +25,10 @@ type React = $ReadOnly<{
24
25
  ...
25
26
  }>;
26
27
 
27
- let relayContext: ?React$Context<RelayContext | null>;
28
+ let relayContext: ?Context<RelayContext | null>;
28
29
  let firstReact: ?React;
29
30
 
30
- function createRelayContext(react: React): React$Context<RelayContext | null> {
31
+ function createRelayContext(react: React): Context<RelayContext | null> {
31
32
  if (!relayContext) {
32
33
  relayContext = react.createContext(null);
33
34
  if (__DEV__) {
@@ -0,0 +1,46 @@
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
+ * @oncall relay
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ import type {Context} from 'react';
15
+ import typeof {createContext} from 'react';
16
+
17
+ const invariant = require('invariant');
18
+
19
+ // Ideally, we'd just import the type of the react module, but this causes Flow
20
+ // problems.
21
+ type React = $ReadOnly<{
22
+ createContext: createContext<mixed | null>,
23
+ version: string,
24
+ ...
25
+ }>;
26
+
27
+ let relayLoggingContext: ?Context<mixed | null>;
28
+ let firstReact: ?React;
29
+
30
+ function createRelayLoggingContext(react: React): Context<mixed | null> {
31
+ if (!relayLoggingContext) {
32
+ relayLoggingContext = react.createContext(null);
33
+ if (__DEV__) {
34
+ relayLoggingContext.displayName = 'RelayLoggingContext';
35
+ }
36
+ firstReact = react;
37
+ }
38
+ invariant(
39
+ react === firstReact,
40
+ '[createRelayLoggingContext]: You are passing a different instance of React',
41
+ react.version,
42
+ );
43
+ return relayLoggingContext;
44
+ }
45
+
46
+ module.exports = createRelayLoggingContext;
@@ -0,0 +1,25 @@
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
+ * @oncall relay
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ import type {DataID} from 'relay-runtime/util/RelayRuntimeTypes';
15
+
16
+ const TYPENAME_PREFIX = '__type:';
17
+
18
+ function generateTypenamePrefixedDataID(
19
+ typeName: string,
20
+ dataID: DataID,
21
+ ): DataID {
22
+ return `${TYPENAME_PREFIX}${typeName}:${dataID}`;
23
+ }
24
+
25
+ module.exports = {generateTypenamePrefixedDataID};
@@ -48,6 +48,7 @@ const {
48
48
  RELAY_RESOLVER_OUTPUT_TYPE_RECORD_IDS,
49
49
  RELAY_RESOLVER_SNAPSHOT_KEY,
50
50
  RELAY_RESOLVER_VALUE_KEY,
51
+ getReadTimeResolverStorageKey,
51
52
  getStorageKey,
52
53
  } = require('../RelayStoreUtils');
53
54
  const getOutputTypeRecordIDs = require('./getOutputTypeRecordIDs');
@@ -129,7 +130,7 @@ class LiveResolverCache implements ResolverCache {
129
130
  // resolvers on this parent record.
130
131
  const record = expectRecord(recordSource, recordID);
131
132
 
132
- const storageKey = getStorageKey(field, variables);
133
+ const storageKey = getReadTimeResolverStorageKey(field, variables);
133
134
  let linkedID = RelayModernRecord.getLinkedRecordID(record, storageKey);
134
135
  let linkedRecord = linkedID == null ? null : recordSource.get(linkedID);
135
136
 
@@ -753,7 +754,8 @@ class LiveResolverCache implements ResolverCache {
753
754
  outputTypeDataID,
754
755
  variables,
755
756
  );
756
-
757
+ // LiveResolverCache is only used by read time resolvers, so this flag should be hardcoded as false.
758
+ const useExecTimeResolvers = false;
757
759
  const normalizationOptions =
758
760
  this._store.__getNormalizationOptions(fieldPath);
759
761
  // The resulted `source` is the normalized version of the
@@ -761,6 +763,7 @@ class LiveResolverCache implements ResolverCache {
761
763
  // All records in the `source` should have IDs that
762
764
  // is "prefix-ed" with the parent resolver record `ID`
763
765
  // and they don't expect to have a "strong" identifier.
766
+
764
767
  return normalize(
765
768
  source,
766
769
  selector,
@@ -770,6 +773,8 @@ class LiveResolverCache implements ResolverCache {
770
773
  // $FlowFixMe[incompatible-variance]
771
774
  value,
772
775
  normalizationOptions,
776
+ undefined,
777
+ useExecTimeResolvers,
773
778
  ).source;
774
779
  }
775
780
  // For weak models we have a simpler case. We simply need to update a
@@ -12,12 +12,12 @@
12
12
  'use strict';
13
13
 
14
14
  import type {Fragment} from '../../util/RelayRuntimeTypes';
15
- import type {FragmentType} from '../RelayStoreTypes';
15
+ import type {FragmentType, ResolverContext} from '../RelayStoreTypes';
16
16
 
17
17
  const {readFragment} = require('../ResolverFragments');
18
18
  const invariant = require('invariant');
19
19
 
20
- type ResolverFn = ($FlowFixMe, ?$FlowFixMe) => mixed;
20
+ type ResolverFn = ($FlowFixMe, ?$FlowFixMe, ResolverContext) => mixed;
21
21
 
22
22
  /**
23
23
  *
@@ -40,7 +40,11 @@ function resolverDataInjector<TFragmentType: FragmentType, TData: ?{...}>(
40
40
  isRequiredField?: boolean,
41
41
  ): (fragmentKey: TFragmentType, args: mixed) => mixed {
42
42
  const resolverFn: ResolverFn = _resolverFn;
43
- return (fragmentKey: TFragmentType, args: mixed): mixed => {
43
+ return (
44
+ fragmentKey: TFragmentType,
45
+ args: mixed,
46
+ resolverContext: ResolverContext,
47
+ ): mixed => {
44
48
  const data = readFragment(fragment, fragmentKey);
45
49
  if (fieldName != null) {
46
50
  if (data == null) {
@@ -52,7 +56,7 @@ function resolverDataInjector<TFragmentType: FragmentType, TData: ?{...}>(
52
56
  fragment.name,
53
57
  );
54
58
  } else {
55
- return resolverFn(null, args);
59
+ return resolverFn(null, args, resolverContext); // TODO: This statement does not seem to be covered by a test?
56
60
  }
57
61
  }
58
62
 
@@ -70,7 +74,7 @@ function resolverDataInjector<TFragmentType: FragmentType, TData: ?{...}>(
70
74
  }
71
75
 
72
76
  // $FlowFixMe[invalid-computed-prop]
73
- return resolverFn(data[fieldName], args);
77
+ return resolverFn(data[fieldName], args, resolverContext);
74
78
  } else {
75
79
  // If both `data` and `fieldName` is available, we expect the
76
80
  // `fieldName` field in the `data` object.
@@ -83,7 +87,7 @@ function resolverDataInjector<TFragmentType: FragmentType, TData: ?{...}>(
83
87
  }
84
88
  } else {
85
89
  // By default we will pass the full set of the fragment data to the resolver
86
- return resolverFn(data, args);
90
+ return resolverFn(null, args, resolverContext); // TODO: This statement does not seem to be covered by a test?
87
91
  }
88
92
  };
89
93
  }
@@ -27,6 +27,7 @@ function normalizeResponse(
27
27
  selector: NormalizationSelector,
28
28
  typeName: string,
29
29
  options: NormalizationOptions,
30
+ useExecTimeResolvers: boolean,
30
31
  ): RelayResponsePayload {
31
32
  const {data, errors} = response;
32
33
  const source = RelayRecordSource.create();
@@ -38,6 +39,7 @@ function normalizeResponse(
38
39
  data,
39
40
  options,
40
41
  errors,
42
+ useExecTimeResolvers,
41
43
  );
42
44
  return {
43
45
  ...relayPayload,
@@ -9,7 +9,7 @@
9
9
  * @oncall relay
10
10
  */
11
11
 
12
- import type {RequestDescriptor} from './RelayStoreTypes';
12
+ import type {PluralReaderSelector, RequestDescriptor} from './RelayStoreTypes';
13
13
  import type {
14
14
  Fragment,
15
15
  FragmentType,
@@ -20,8 +20,8 @@ import type {
20
20
  } from 'relay-runtime';
21
21
 
22
22
  const Observable = require('../network/RelayObservable');
23
+ const {getObservableForActiveRequest} = require('../query/fetchQueryInternal');
23
24
  const {getFragment} = require('../query/GraphQLTag');
24
- const getPendingOperationsForFragment = require('../util/getPendingOperationsForFragment');
25
25
  const {
26
26
  handlePotentialSnapshotErrors,
27
27
  } = require('../util/handlePotentialSnapshotErrors');
@@ -47,8 +47,7 @@ export type HasSpread<TFragmentType> = {
47
47
 
48
48
  /**
49
49
  * EXPERIMENTAL: This API is experimental and does not yet support all Relay
50
- * features. Notably, it does not correectly handle plural fragments or some
51
- * features of Relay Resolvers.
50
+ * features. Notably, it does not correctly handle some features of Relay Resolvers.
52
51
  *
53
52
  * Given a fragment and a fragment reference, returns a promise that resolves
54
53
  * once the fragment data is available, or rejects if the fragment has an error.
@@ -63,7 +62,9 @@ export type HasSpread<TFragmentType> = {
63
62
  async function waitForFragmentData<TFragmentType: FragmentType, TData>(
64
63
  environment: IEnvironment,
65
64
  fragment: Fragment<TFragmentType, TData>,
66
- fragmentRef: HasSpread<TFragmentType>,
65
+ fragmentRef:
66
+ | HasSpread<TFragmentType>
67
+ | $ReadOnlyArray<HasSpread<TFragmentType>>,
67
68
  ): Promise<TData> {
68
69
  let subscription: ?Subscription;
69
70
 
@@ -94,13 +95,14 @@ async function waitForFragmentData<TFragmentType: FragmentType, TData>(
94
95
  declare function observeFragment<TFragmentType: FragmentType, TData>(
95
96
  environment: IEnvironment,
96
97
  fragment: Fragment<TFragmentType, TData>,
97
- fragmentRef: HasSpread<TFragmentType>,
98
+ fragmentRef:
99
+ | HasSpread<TFragmentType>
100
+ | $ReadOnlyArray<HasSpread<TFragmentType>>,
98
101
  ): Observable<FragmentState<TData>>;
99
102
 
100
103
  /**
101
104
  * EXPERIMENTAL: This API is experimental and does not yet support all Relay
102
- * features. Notably, it does not correectly handle plural fragments or some
103
- * features of Relay Resolvers.
105
+ * features. Notably, it does not correctly handle some features of Relay Resolvers.
104
106
  *
105
107
  * Given a fragment and a fragment reference, returns an observable that emits
106
108
  * the state of the fragment over time. The observable will emit the following
@@ -114,7 +116,7 @@ function observeFragment<TFragmentType: FragmentType, TData>(
114
116
  environment: IEnvironment,
115
117
  fragment: Fragment<TFragmentType, TData>,
116
118
  fragmentRef: mixed,
117
- ): Observable<FragmentState<TData>> {
119
+ ): mixed {
118
120
  const fragmentNode = getFragment(fragment);
119
121
  const fragmentSelector = getSelector(fragmentNode, fragmentRef);
120
122
  invariant(
@@ -124,24 +126,19 @@ function observeFragment<TFragmentType: FragmentType, TData>(
124
126
  invariant(fragmentSelector != null, 'Expected a selector, got null.');
125
127
  switch (fragmentSelector.kind) {
126
128
  case 'SingularReaderSelector':
127
- return observeSelector(environment, fragment, fragmentSelector);
129
+ return observeSingularSelector(environment, fragment, fragmentSelector);
128
130
  case 'PluralReaderSelector': {
129
- // TODO: We could use something like this RXJS's combineLatest to create
130
- // an observable for each selector and merge them.
131
- // https://github.com/ReactiveX/rxjs/blob/master/packages/rxjs/src/internal/observable/combineLatest.ts
132
- //
133
- // Note that this problem is a bit tricky because Relay currently only
134
- // lets you subscribe at a singular fragment granularity. This makes it
135
- // hard to batch updates such that when a store update causes multiple
136
- // fragments to change, we can only publish a single update to the
137
- // fragment owner.
138
- invariant(false, 'Plural fragments are not supported');
131
+ return observePluralSelector(
132
+ environment,
133
+ (fragment: $FlowFixMe),
134
+ fragmentSelector,
135
+ );
139
136
  }
140
137
  }
141
138
  invariant(false, 'Unsupported fragment selector kind');
142
139
  }
143
140
 
144
- function observeSelector<TFragmentType: FragmentType, TData>(
141
+ function observeSingularSelector<TFragmentType: FragmentType, TData>(
145
142
  environment: IEnvironment,
146
143
  fragmentNode: Fragment<TFragmentType, TData>,
147
144
  fragmentSelector: SingularReaderSelector,
@@ -173,6 +170,49 @@ function observeSelector<TFragmentType: FragmentType, TData>(
173
170
  });
174
171
  }
175
172
 
173
+ function observePluralSelector<
174
+ TFragmentType: FragmentType,
175
+ TData: Array<mixed>,
176
+ >(
177
+ environment: IEnvironment,
178
+ fragmentNode: Fragment<TFragmentType, TData>,
179
+ fragmentSelector: PluralReaderSelector,
180
+ ): Observable<FragmentState<TData>> {
181
+ const snapshots = fragmentSelector.selectors.map(selector =>
182
+ environment.lookup(selector),
183
+ );
184
+
185
+ return Observable.create(sink => {
186
+ // This array is mutable since each subscription updates the array in place.
187
+ const states = snapshots.map((snapshot, index) =>
188
+ snapshotToFragmentState(
189
+ environment,
190
+ fragmentNode,
191
+ fragmentSelector.selectors[index].owner,
192
+ snapshot,
193
+ ),
194
+ );
195
+
196
+ sink.next((mergeFragmentStates(states): $FlowFixMe));
197
+
198
+ const subscriptions = snapshots.map((snapshot, index) =>
199
+ environment.subscribe(snapshot, latestSnapshot => {
200
+ states[index] = snapshotToFragmentState(
201
+ environment,
202
+ fragmentNode,
203
+ fragmentSelector.selectors[index].owner,
204
+ latestSnapshot,
205
+ );
206
+ // This doesn't batch updates, so it will notify the subscriber multiple times
207
+ // if a store update impacting multiple items in the list is published.
208
+ sink.next((mergeFragmentStates(states): $FlowFixMe));
209
+ }),
210
+ );
211
+
212
+ return () => subscriptions.forEach(subscription => subscription.dispose());
213
+ });
214
+ }
215
+
176
216
  function snapshotToFragmentState<TFragmentType: FragmentType, TData>(
177
217
  environment: IEnvironment,
178
218
  fragmentNode: Fragment<TFragmentType, TData>,
@@ -201,18 +241,18 @@ function snapshotToFragmentState<TFragmentType: FragmentType, TData>(
201
241
  }
202
242
 
203
243
  if (snapshot.isMissingData) {
204
- const pendingOperations = getPendingOperationsForFragment(
205
- environment,
206
- fragmentNode,
207
- owner,
208
- );
209
- if (pendingOperations != null) {
244
+ if (
245
+ getObservableForActiveRequest(environment, owner) != null ||
246
+ environment
247
+ .getOperationTracker()
248
+ .getPendingOperationsAffectingOwner(owner) != null
249
+ ) {
210
250
  return {state: 'loading'};
211
251
  }
212
252
  }
213
253
 
214
254
  try {
215
- handlePotentialSnapshotErrors(environment, snapshot.errorResponseFields);
255
+ handlePotentialSnapshotErrors(environment, snapshot.fieldErrors);
216
256
  } catch (error) {
217
257
  return {error, state: 'error'};
218
258
  }
@@ -229,6 +269,20 @@ function snapshotToFragmentState<TFragmentType: FragmentType, TData>(
229
269
  return {state: 'ok', value: (snapshot.data: $FlowFixMe)};
230
270
  }
231
271
 
272
+ function mergeFragmentStates<T>(
273
+ states: $ReadOnlyArray<FragmentState<T>>,
274
+ ): FragmentState<Array<T>> {
275
+ const value = [];
276
+ for (const state of states) {
277
+ if (state.state === 'ok') {
278
+ value.push(state.value);
279
+ } else {
280
+ return state;
281
+ }
282
+ }
283
+ return {state: 'ok', value};
284
+ }
285
+
232
286
  module.exports = {
233
287
  observeFragment,
234
288
  waitForFragmentData,
@@ -0,0 +1,61 @@
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
+ * @oncall relay
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ import type RelayObservable from '../network/RelayObservable';
15
+ import type {FragmentState} from './observeFragmentExperimental';
16
+ import type {OperationDescriptor} from './RelayStoreTypes';
17
+ import type {Fragment, IEnvironment, Query, Variables} from 'relay-runtime';
18
+
19
+ const {observeFragment} = require('./observeFragmentExperimental');
20
+ const {createOperationDescriptor} = require('./RelayModernOperationDescriptor');
21
+
22
+ /**
23
+ * This function returns an observable that can be used to subscribe to the data
24
+ * contained in a query. It does not return the full response shape, but rather
25
+ * the contents of the query body minus any fragment spreads. If you wish to
26
+ * read the contents of a fragment spread into this query you may pass the
27
+ * object into which the fragment was spread to `observeFragment`.
28
+ *
29
+ * NOTE: `observeQuery` assumes that you have already fetched and retained the
30
+ * query via some other means, such as `fetchQuery`.
31
+ *
32
+ * This feature is still experimental and does not properly handle some resolver
33
+ * features such as client-to-server edges.
34
+ */
35
+ function observeQuery<TVariables: Variables, TData>(
36
+ environment: IEnvironment,
37
+ gqlQuery: Query<TVariables, TData>,
38
+ variables: TVariables,
39
+ ): RelayObservable<FragmentState<TData>> {
40
+ const operation: OperationDescriptor = createOperationDescriptor(
41
+ gqlQuery,
42
+ variables,
43
+ );
44
+
45
+ const rootFragmentRef: $FlowFixMe = {
46
+ __id: operation.fragment.dataID,
47
+ __fragments: {
48
+ [operation.fragment.node.name]: operation.request.variables,
49
+ },
50
+ __fragmentOwner: operation.request,
51
+ };
52
+
53
+ const fragmentNode: Fragment<$FlowFixMe, TData> = (operation.request.node
54
+ .fragment: $FlowFixMe);
55
+
56
+ return observeFragment(environment, fragmentNode, rootFragmentRef);
57
+ }
58
+
59
+ module.exports = {
60
+ observeQuery,
61
+ };
@@ -21,8 +21,7 @@ const {observeFragment} = require('./observeFragmentExperimental');
21
21
 
22
22
  /**
23
23
  * EXPERIMENTAL: This API is experimental and does not yet support all Relay
24
- * features. Notably, it does not correectly handle plural fragments or some
25
- * features of Relay Resolvers.
24
+ * features. Notably, it does not correctly handle some features of Relay Resolvers.
26
25
  *
27
26
  * Given a fragment and a fragment reference, returns a promise that resolves
28
27
  * once the fragment data is available, or rejects if the fragment has an error.
@@ -37,7 +36,9 @@ const {observeFragment} = require('./observeFragmentExperimental');
37
36
  async function waitForFragmentData<TFragmentType: FragmentType, TData>(
38
37
  environment: IEnvironment,
39
38
  fragment: Fragment<TFragmentType, TData>,
40
- fragmentRef: HasSpread<TFragmentType>,
39
+ fragmentRef:
40
+ | HasSpread<TFragmentType>
41
+ | $ReadOnlyArray<HasSpread<TFragmentType>>,
41
42
  ): Promise<TData> {
42
43
  let subscription: ?Subscription;
43
44
 
@@ -12,7 +12,7 @@
12
12
  'use strict';
13
13
 
14
14
  import type {ResolverFunction, ResolverModule} from './ReaderNode';
15
- import type {ConcreteRequest} from './RelayConcreteNode';
15
+ import type {ConcreteRequest, ProvidedVariableType} from './RelayConcreteNode';
16
16
  import type {JSResourceReference} from 'JSResourceReference';
17
17
 
18
18
  /**
@@ -28,6 +28,7 @@ export type NormalizationOperation = {
28
28
  +[string]: $ReadOnlyArray<string>,
29
29
  },
30
30
  +use_exec_time_resolvers?: boolean,
31
+ +exec_time_resolvers_enabled_provider?: ProvidedVariableType,
31
32
  };
32
33
 
33
34
  export type NormalizationHandle =
@@ -39,6 +39,8 @@ export type NormalizationRootNode =
39
39
  | ConcreteRequest
40
40
  | NormalizationSplitOperation;
41
41
 
42
+ export type ProvidedVariableType = {get(): mixed};
43
+
42
44
  export type ProvidedVariablesType = {+[key: string]: {get(): mixed}};
43
45
 
44
46
  /**
@@ -25,6 +25,7 @@ function createError(
25
25
  String(messageParams[index++]),
26
26
  );
27
27
  const err = new Error(message);
28
+ // $FlowFixMe[unsafe-object-assign]
28
29
  const error = Object.assign((err: any), {
29
30
  name,
30
31
  messageFormat,
@@ -26,6 +26,7 @@ export type FeatureFlags = {
26
26
  STRING_INTERN_LEVEL: number,
27
27
  LOG_MISSING_RECORDS_IN_PROD: boolean,
28
28
  ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE: boolean,
29
+ ENABLE_UI_CONTEXT_ON_RELAY_LOGGER: boolean,
29
30
 
30
31
  // Some GraphQL servers are noncompliant with the GraphQL specification and
31
32
  // return an empty list instead of null when there is a field error on a list.
@@ -58,6 +59,27 @@ export type FeatureFlags = {
58
59
  // Adds a prefix to the storage key of read time resolvers. This is used to
59
60
  // disambiguate the same resolver being used at both read time and exec time.
60
61
  ENABLE_READ_TIME_RESOLVER_STORAGE_KEY_PREFIX: boolean,
62
+
63
+ // Enable the fix for usePaginationFragment stucking in loading state
64
+ ENABLE_USE_PAGINATION_IS_LOADING_FIX: boolean,
65
+
66
+ // Enable logging an ID collision in the Relay store
67
+ ENABLE_STORE_ID_COLLISION_LOGGING: boolean,
68
+
69
+ // Throw on nested store updates
70
+ DISALLOW_NESTED_UPDATES: boolean,
71
+
72
+ // Enable prefixing of DataID in the store with __typename
73
+ ENABLE_TYPENAME_PREFIXED_DATA_ID: boolean,
74
+
75
+ // Relay previously had a bug where it would fail to check for missing client
76
+ // edge to server data in fragments nested within client edge Relay Resolver
77
+ // fields. This feature flag fixes the behavior but comes with a perf cost.
78
+ // This flag is here to allow us to gradually rollout the fix and track the perf
79
+ // impact.
80
+ //
81
+ // See https://github.com/facebook/relay/issues/4882
82
+ CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES: boolean,
61
83
  };
62
84
 
63
85
  const RelayFeatureFlags: FeatureFlags = {
@@ -72,6 +94,7 @@ const RelayFeatureFlags: FeatureFlags = {
72
94
  MAX_DATA_ID_LENGTH: null,
73
95
  STRING_INTERN_LEVEL: 0,
74
96
  LOG_MISSING_RECORDS_IN_PROD: false,
97
+ ENABLE_STORE_ID_COLLISION_LOGGING: false,
75
98
  ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS: false,
76
99
  ENABLE_LOOSE_SUBSCRIPTION_ATTRIBUTION: false,
77
100
  ENABLE_OPERATION_TRACKER_OPTIMISTIC_UPDATES: false,
@@ -81,6 +104,11 @@ const RelayFeatureFlags: FeatureFlags = {
81
104
  ENABLE_CYLE_DETECTION_IN_VARIABLES: false,
82
105
  ENABLE_ACTIVITY_COMPATIBILITY: false,
83
106
  ENABLE_READ_TIME_RESOLVER_STORAGE_KEY_PREFIX: true,
107
+ ENABLE_USE_PAGINATION_IS_LOADING_FIX: false,
108
+ DISALLOW_NESTED_UPDATES: false,
109
+ ENABLE_TYPENAME_PREFIXED_DATA_ID: false,
110
+ ENABLE_UI_CONTEXT_ON_RELAY_LOGGER: false,
111
+ CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES: false,
84
112
  };
85
113
 
86
114
  module.exports = RelayFeatureFlags;
@@ -53,9 +53,10 @@ export type VariablesOf<T: OperationType> = T['variables'];
53
53
  * state of any configured response cache.
54
54
  * - `poll`: causes a query to live update by polling at the specified interval
55
55
  * in milliseconds. (This value will be passed to setTimeout.)
56
- * - `liveConfigId`: causes a query to live update by calling GraphQLLiveQuery,
57
- * it represents a configuration of gateway when doing live query
58
- * - `onSubscribe`: Not in use.
56
+ * - `liveConfigId`: Makes a query live by sending through RTI stack.
57
+ * - `onSubscribe`: Callback to be called when a live query stream is started.
58
+ * - `onPause`: Callback to be called when a live query stream is paused, e.g. due to a network disconnection.
59
+ * - `onResume`: Callback to be called when a live query stream is resumed, e.g. from a network disconnection.
59
60
  * - `metadata`: user-supplied metadata.
60
61
  * - `transactionId`: a user-supplied value, intended for use as a unique id for
61
62
  * a given instance of executing an operation.
@@ -65,6 +66,8 @@ export type CacheConfig = {
65
66
  poll?: ?number,
66
67
  liveConfigId?: ?string,
67
68
  onSubscribe?: () => void,
69
+ onResume?: (pauseTimeMs: number) => void,
70
+ onPause?: (mqttConnectionIsOk: boolean, internetIsOk: boolean) => void,
68
71
  metadata?: {[key: string]: mixed, ...},
69
72
  transactionId?: ?string,
70
73
  };
@@ -56,6 +56,7 @@ function getPaginationVariables(
56
56
  ...baseVariables,
57
57
  ...extraVariables,
58
58
  [backwardMetadata.cursor]: cursor,
59
+ // $FlowFixMe[incompatible-type]
59
60
  [backwardMetadata.count]: count,
60
61
  };
61
62
  if (forwardMetadata && forwardMetadata.cursor) {
@@ -92,6 +93,7 @@ function getPaginationVariables(
92
93
  ...baseVariables,
93
94
  ...extraVariables,
94
95
  [forwardMetadata.cursor]: cursor,
96
+ // $FlowFixMe[incompatible-type]
95
97
  [forwardMetadata.count]: count,
96
98
  };
97
99
  if (backwardMetadata && backwardMetadata.cursor) {