relay-runtime 20.1.1 → 21.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 (262) hide show
  1. package/experimental.js +1 -1
  2. package/experimental.js.flow +8 -8
  3. package/handlers/connection/ConnectionHandler.js.flow +5 -5
  4. package/handlers/connection/ConnectionInterface.js.flow +1 -1
  5. package/index.js +1 -1
  6. package/index.js.flow +125 -62
  7. package/lib/experimental.js +3 -3
  8. package/lib/index.js +105 -57
  9. package/lib/multi-actor-environment/ActorIdentifier.js +2 -2
  10. package/lib/multi-actor-environment/MultiActorEnvironment.js +3 -1
  11. package/lib/mutations/commitMutation.js +8 -8
  12. package/lib/mutations/validateMutation.js +4 -4
  13. package/lib/query/GraphQLTag.js +3 -3
  14. package/lib/query/fetchQuery.js +15 -3
  15. package/lib/store/DataChecker.js +38 -4
  16. package/lib/store/NormalizationEngine.js +373 -0
  17. package/lib/store/OperationExecutor.js +172 -113
  18. package/lib/store/RelayConcreteVariables.js +1 -1
  19. package/lib/store/RelayErrorTrie.js +2 -2
  20. package/lib/store/RelayExperimentalGraphResponseTransform.js +8 -8
  21. package/lib/store/RelayModernEnvironment.js +26 -19
  22. package/lib/store/RelayModernRecord.js +18 -8
  23. package/lib/store/RelayModernSelector.js +9 -9
  24. package/lib/store/RelayModernStore.js +152 -43
  25. package/lib/store/RelayPublishQueue.js +1 -1
  26. package/lib/store/RelayReader.js +76 -38
  27. package/lib/store/RelayRecordSource.js +6 -0
  28. package/lib/store/RelayReferenceMarker.js +2 -1
  29. package/lib/store/RelayResponseNormalizer.js +88 -55
  30. package/lib/store/RelayStoreSubscriptions.js +34 -10
  31. package/lib/store/RelayStoreUtils.js +8 -1
  32. package/lib/store/ResolverFragments.js +2 -2
  33. package/lib/store/live-resolvers/LiveResolverCache.js +25 -9
  34. package/lib/store/observeFragmentExperimental.js +17 -1
  35. package/lib/store/observeQueryExperimental.js +2 -2
  36. package/lib/subscription/requestSubscription.js +3 -3
  37. package/lib/util/RelayError.js +3 -0
  38. package/lib/util/RelayFeatureFlags.js +6 -2
  39. package/lib/util/RelayReplaySubject.js +4 -4
  40. package/lib/util/handlePotentialSnapshotErrors.js +2 -2
  41. package/lib/util/stableCopy.js +2 -2
  42. package/llm-docs/api-reference/entrypoint-apis/entrypoint-container.mdx +38 -0
  43. package/llm-docs/api-reference/entrypoint-apis/load-entrypoint.mdx +77 -0
  44. package/llm-docs/api-reference/entrypoint-apis/use-entrypoint-loader.mdx +99 -0
  45. package/llm-docs/api-reference/graphql/graphql-directives.mdx +378 -0
  46. package/llm-docs/api-reference/hooks/_use-lazy-load-query-extra.mdx +16 -0
  47. package/llm-docs/api-reference/hooks/load-query.mdx +84 -0
  48. package/llm-docs/api-reference/hooks/relay-environment-provider.mdx +78 -0
  49. package/llm-docs/api-reference/hooks/use-client-query.mdx +65 -0
  50. package/llm-docs/api-reference/hooks/use-fragment.mdx +69 -0
  51. package/llm-docs/api-reference/hooks/use-lazy-load-query.mdx +62 -0
  52. package/llm-docs/api-reference/hooks/use-mutation.mdx +94 -0
  53. package/llm-docs/api-reference/hooks/use-pagination-fragment.mdx +166 -0
  54. package/llm-docs/api-reference/hooks/use-prefetchable-forward-pagination-fragment.mdx +134 -0
  55. package/llm-docs/api-reference/hooks/use-preloaded-query.mdx +84 -0
  56. package/llm-docs/api-reference/hooks/use-query-loader.mdx +95 -0
  57. package/llm-docs/api-reference/hooks/use-refetchable-fragment.mdx +122 -0
  58. package/llm-docs/api-reference/hooks/use-relay-environment.mdx +37 -0
  59. package/llm-docs/api-reference/hooks/use-subscription.mdx +66 -0
  60. package/llm-docs/api-reference/relay-resolvers/docblock-format.mdx +321 -0
  61. package/llm-docs/api-reference/relay-resolvers/runtime-functions.mdx +94 -0
  62. package/llm-docs/api-reference/relay-runtime/commit-mutation.mdx +65 -0
  63. package/llm-docs/api-reference/relay-runtime/fetch-query.mdx +113 -0
  64. package/llm-docs/api-reference/relay-runtime/field-logger.mdx +170 -0
  65. package/llm-docs/api-reference/relay-runtime/observe-fragment.mdx +92 -0
  66. package/llm-docs/api-reference/relay-runtime/relay-environment.mdx +53 -0
  67. package/llm-docs/api-reference/relay-runtime/request-subscription.mdx +54 -0
  68. package/llm-docs/api-reference/relay-runtime/runtime-configuration.mdx +52 -0
  69. package/llm-docs/api-reference/relay-runtime/store.mdx +734 -0
  70. package/llm-docs/api-reference/relay-runtime/wait-for-fragment-data.mdx +89 -0
  71. package/llm-docs/api-reference/types/CacheConfig.mdx +8 -0
  72. package/llm-docs/api-reference/types/Disposable.mdx +4 -0
  73. package/llm-docs/api-reference/types/GraphQLSubscriptionConfig.mdx +17 -0
  74. package/llm-docs/api-reference/types/MutationConfig.mdx +31 -0
  75. package/llm-docs/api-reference/types/SelectorStoreUpdater.mdx +6 -0
  76. package/llm-docs/api-reference/types/UploadableMap.mdx +3 -0
  77. package/llm-docs/community/learning-resources.mdx +64 -0
  78. package/llm-docs/debugging/declarative-mutation-directives.mdx +34 -0
  79. package/llm-docs/debugging/disallowed-id-types-error.mdx +43 -0
  80. package/llm-docs/debugging/inconsistent-typename-error.mdx +47 -0
  81. package/llm-docs/debugging/relay-devtools.mdx +73 -0
  82. package/llm-docs/debugging/why-null.mdx +116 -0
  83. package/llm-docs/editor-support.mdx +55 -0
  84. package/llm-docs/error-reference/unknown-field.mdx +36 -0
  85. package/llm-docs/getting-started/babel-plugin.mdx +31 -0
  86. package/llm-docs/getting-started/compiler-config.mdx +25 -0
  87. package/llm-docs/getting-started/compiler.mdx +82 -0
  88. package/llm-docs/getting-started/lint-rules.mdx +87 -0
  89. package/llm-docs/getting-started/production.mdx +30 -0
  90. package/llm-docs/getting-started/quick-start.mdx +213 -0
  91. package/llm-docs/glossary/glossary.mdx +1040 -0
  92. package/llm-docs/guided-tour/list-data/advanced-pagination.mdx +157 -0
  93. package/llm-docs/guided-tour/list-data/connections.mdx +81 -0
  94. package/llm-docs/guided-tour/list-data/pagination.mdx +193 -0
  95. package/llm-docs/guided-tour/list-data/rendering-connections.mdx +112 -0
  96. package/llm-docs/guided-tour/list-data/streaming-pagination.mdx +87 -0
  97. package/llm-docs/guided-tour/managing-data-outside-react/retaining-queries.mdx +51 -0
  98. package/llm-docs/guided-tour/refetching/refetching-queries-with-different-data.mdx +337 -0
  99. package/llm-docs/guided-tour/refetching/refreshing-queries.mdx +350 -0
  100. package/llm-docs/guided-tour/rendering/environment.mdx +59 -0
  101. package/llm-docs/guided-tour/rendering/error-states.mdx +295 -0
  102. package/llm-docs/guided-tour/rendering/fragments.mdx +354 -0
  103. package/llm-docs/guided-tour/rendering/loading-states.mdx +245 -0
  104. package/llm-docs/guided-tour/rendering/queries.mdx +261 -0
  105. package/llm-docs/guided-tour/rendering/variables.mdx +233 -0
  106. package/llm-docs/guided-tour/reusing-cached-data/fetch-policies.mdx +56 -0
  107. package/llm-docs/guided-tour/reusing-cached-data/filling-in-missing-data.mdx +102 -0
  108. package/llm-docs/guided-tour/reusing-cached-data/introduction.mdx +22 -0
  109. package/llm-docs/guided-tour/reusing-cached-data/presence-of-data.mdx +93 -0
  110. package/llm-docs/guided-tour/reusing-cached-data/rendering-partially-cached-data.mdx +175 -0
  111. package/llm-docs/guided-tour/reusing-cached-data/staleness-of-data.mdx +116 -0
  112. package/llm-docs/guided-tour/updating-data/client-only-data.mdx +115 -0
  113. package/llm-docs/guided-tour/updating-data/graphql-mutations.mdx +334 -0
  114. package/llm-docs/guided-tour/updating-data/graphql-subscriptions.mdx +279 -0
  115. package/llm-docs/guided-tour/updating-data/imperatively-modifying-linked-fields.mdx +511 -0
  116. package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data-legacy.mdx +142 -0
  117. package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data.mdx +275 -0
  118. package/llm-docs/guided-tour/updating-data/introduction.mdx +25 -0
  119. package/llm-docs/guided-tour/updating-data/local-data-updates.mdx +71 -0
  120. package/llm-docs/guided-tour/updating-data/typesafe-updaters-faq.mdx +83 -0
  121. package/llm-docs/guided-tour/updating-data/updating-connections.mdx +592 -0
  122. package/llm-docs/guides/alias-directive.mdx +160 -0
  123. package/llm-docs/guides/catch-directive.mdx +167 -0
  124. package/llm-docs/guides/client-schema-extensions.mdx +208 -0
  125. package/llm-docs/guides/codemods.mdx +66 -0
  126. package/llm-docs/guides/data-driven-dependencies/client-3d.mdx +255 -0
  127. package/llm-docs/guides/data-driven-dependencies/configuration.mdx +127 -0
  128. package/llm-docs/guides/data-driven-dependencies/introduction.mdx +39 -0
  129. package/llm-docs/guides/data-driven-dependencies/server-3d.mdx +664 -0
  130. package/llm-docs/guides/document-comparison.mdx +106 -0
  131. package/llm-docs/guides/graphql-server-specification.mdx +453 -0
  132. package/llm-docs/guides/network-layer.mdx +69 -0
  133. package/llm-docs/guides/persisted-queries.mdx +328 -0
  134. package/llm-docs/guides/relay-resolvers/context.mdx +99 -0
  135. package/llm-docs/guides/relay-resolvers/defining-fields.mdx +151 -0
  136. package/llm-docs/guides/relay-resolvers/defining-types.mdx +164 -0
  137. package/llm-docs/guides/relay-resolvers/deprecated.mdx +27 -0
  138. package/llm-docs/guides/relay-resolvers/derived-fields.mdx +127 -0
  139. package/llm-docs/guides/relay-resolvers/descriptions.mdx +44 -0
  140. package/llm-docs/guides/relay-resolvers/enabling.mdx +41 -0
  141. package/llm-docs/guides/relay-resolvers/errors.mdx +64 -0
  142. package/llm-docs/guides/relay-resolvers/field-arguments.mdx +63 -0
  143. package/llm-docs/guides/relay-resolvers/introduction.mdx +62 -0
  144. package/llm-docs/guides/relay-resolvers/limitations.mdx +30 -0
  145. package/llm-docs/guides/relay-resolvers/live-fields.mdx +164 -0
  146. package/llm-docs/guides/relay-resolvers/return-types.mdx +161 -0
  147. package/llm-docs/guides/relay-resolvers/suspense.mdx +41 -0
  148. package/llm-docs/guides/required-directive.mdx +240 -0
  149. package/llm-docs/guides/semantic-nullability.mdx +93 -0
  150. package/llm-docs/guides/testing-relay-components.mdx +642 -0
  151. package/llm-docs/guides/testing-relay-with-preloaded-queries.mdx +160 -0
  152. package/llm-docs/guides/throw-on-field-error-directive.mdx +58 -0
  153. package/llm-docs/guides/type-emission.mdx +414 -0
  154. package/llm-docs/home.mdx +32 -0
  155. package/llm-docs/principles-and-architecture/architecture-overview.mdx +24 -0
  156. package/llm-docs/principles-and-architecture/compiler-architecture.mdx +106 -0
  157. package/llm-docs/principles-and-architecture/runtime-architecture.mdx +249 -0
  158. package/llm-docs/principles-and-architecture/thinking-in-graphql.mdx +309 -0
  159. package/llm-docs/principles-and-architecture/thinking-in-relay.mdx +104 -0
  160. package/llm-docs/principles-and-architecture/videos.mdx +50 -0
  161. package/llm-docs/tutorial/arrays-lists.mdx +126 -0
  162. package/llm-docs/tutorial/fragments-1.mdx +487 -0
  163. package/llm-docs/tutorial/graphql.mdx +172 -0
  164. package/llm-docs/tutorial/interfaces-polymorphism.mdx +161 -0
  165. package/llm-docs/tutorial/intro.mdx +58 -0
  166. package/llm-docs/tutorial/mutations-updates.mdx +624 -0
  167. package/llm-docs/tutorial/organizing-mutations-queries-and-subscriptions.mdx +13 -0
  168. package/llm-docs/tutorial/queries-1.mdx +267 -0
  169. package/llm-docs/tutorial/queries-2.mdx +389 -0
  170. package/llm-docs/tutorial/refetchable-fragments.mdx +352 -0
  171. package/multi-actor-environment/ActorIdentifier.js.flow +2 -2
  172. package/multi-actor-environment/ActorSpecificEnvironment.js.flow +7 -7
  173. package/multi-actor-environment/ActorUtils.js.flow +1 -1
  174. package/multi-actor-environment/MultiActorEnvironment.js.flow +12 -8
  175. package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +3 -3
  176. package/mutations/RelayDeclarativeMutationConfig.js.flow +9 -9
  177. package/mutations/RelayRecordProxy.js.flow +8 -11
  178. package/mutations/RelayRecordSourceMutator.js.flow +4 -4
  179. package/mutations/RelayRecordSourceProxy.js.flow +4 -4
  180. package/mutations/RelayRecordSourceSelectorProxy.js.flow +6 -6
  181. package/mutations/applyOptimisticMutation.js.flow +2 -2
  182. package/mutations/commitMutation.js.flow +20 -16
  183. package/mutations/createUpdatableProxy.js.flow +19 -19
  184. package/mutations/readUpdatableFragment.js.flow +3 -3
  185. package/mutations/readUpdatableQuery.js.flow +3 -3
  186. package/mutations/validateMutation.js.flow +7 -7
  187. package/network/RelayNetworkTypes.js.flow +4 -4
  188. package/network/RelayObservable.js.flow +16 -14
  189. package/network/RelayQueryResponseCache.js.flow +3 -3
  190. package/network/wrapNetworkWithLogObserver.js.flow +1 -1
  191. package/package.json +2 -1
  192. package/query/GraphQLTag.js.flow +22 -10
  193. package/query/fetchQuery.js.flow +23 -10
  194. package/query/fetchQuery_DEPRECATED.js.flow +1 -1
  195. package/store/DataChecker.js.flow +43 -9
  196. package/store/NormalizationEngine.js.flow +779 -0
  197. package/store/OperationExecutor.js.flow +173 -70
  198. package/store/RelayConcreteVariables.js.flow +5 -5
  199. package/store/RelayErrorTrie.js.flow +12 -12
  200. package/store/RelayExperimentalGraphResponseHandler.js.flow +3 -3
  201. package/store/RelayExperimentalGraphResponseTransform.js.flow +10 -10
  202. package/store/RelayModernEnvironment.js.flow +41 -26
  203. package/store/RelayModernFragmentSpecResolver.js.flow +1 -1
  204. package/store/RelayModernOperationDescriptor.js.flow +1 -1
  205. package/store/RelayModernRecord.js.flow +44 -20
  206. package/store/RelayModernSelector.js.flow +21 -21
  207. package/store/RelayModernStore.js.flow +219 -58
  208. package/store/RelayOperationTracker.js.flow +2 -2
  209. package/store/RelayOptimisticRecordSource.js.flow +2 -2
  210. package/store/RelayPublishQueue.js.flow +21 -12
  211. package/store/RelayReader.js.flow +129 -57
  212. package/store/RelayRecordSource.js.flow +10 -0
  213. package/store/RelayRecordState.js.flow +1 -1
  214. package/store/RelayReferenceMarker.js.flow +5 -4
  215. package/store/RelayResponseNormalizer.js.flow +125 -57
  216. package/store/RelayStoreSubscriptions.js.flow +52 -8
  217. package/store/RelayStoreTypes.js.flow +153 -64
  218. package/store/RelayStoreUtils.js.flow +15 -7
  219. package/store/ResolverCache.js.flow +2 -2
  220. package/store/ResolverFragments.js.flow +12 -12
  221. package/store/StoreInspector.js.flow +6 -7
  222. package/store/cloneRelayHandleSourceField.js.flow +1 -1
  223. package/store/cloneRelayScalarHandleSourceField.js.flow +1 -1
  224. package/store/createRelayContext.js.flow +1 -1
  225. package/store/createRelayLoggingContext.js.flow +4 -4
  226. package/store/defaultGetDataID.js.flow +2 -2
  227. package/store/isRelayModernEnvironment.js.flow +4 -2
  228. package/store/live-resolvers/LiveResolverCache.js.flow +55 -20
  229. package/store/live-resolvers/LiveResolverSuspenseSentinel.js.flow +3 -3
  230. package/store/live-resolvers/getOutputTypeRecordIDs.js.flow +1 -1
  231. package/store/live-resolvers/isLiveStateValue.js.flow +2 -2
  232. package/store/live-resolvers/resolverDataInjector.js.flow +8 -5
  233. package/store/observeFragmentExperimental.js.flow +49 -20
  234. package/store/observeQueryExperimental.js.flow +5 -5
  235. package/store/readInlineData.js.flow +4 -4
  236. package/store/waitForFragmentExperimental.js.flow +3 -3
  237. package/subscription/requestSubscription.js.flow +7 -7
  238. package/util/NormalizationNode.js.flow +34 -32
  239. package/util/ReaderNode.js.flow +32 -30
  240. package/util/RelayConcreteNode.js.flow +5 -5
  241. package/util/RelayError.js.flow +4 -1
  242. package/util/RelayFeatureFlags.js.flow +21 -1
  243. package/util/RelayProfiler.js.flow +1 -1
  244. package/util/RelayReplaySubject.js.flow +3 -3
  245. package/util/RelayRuntimeTypes.js.flow +11 -11
  246. package/util/createPayloadFor3DField.js.flow +9 -5
  247. package/util/deepFreeze.js.flow +2 -2
  248. package/util/getFragmentIdentifier.js.flow +1 -1
  249. package/util/getPaginationMetadata.js.flow +1 -1
  250. package/util/getPaginationVariables.js.flow +1 -1
  251. package/util/getPendingOperationsForFragment.js.flow +2 -2
  252. package/util/getRefetchMetadata.js.flow +6 -5
  253. package/util/getValueAtPath.js.flow +3 -3
  254. package/util/handlePotentialSnapshotErrors.js.flow +5 -5
  255. package/util/isEmptyObject.js.flow +1 -1
  256. package/util/isPromise.js.flow +2 -2
  257. package/util/isScalarAndEqual.js.flow +1 -1
  258. package/util/recycleNodesInto.js.flow +2 -2
  259. package/util/registerEnvironmentWithDevTools.js.flow +1 -1
  260. package/util/shallowFreeze.js.flow +1 -1
  261. package/util/stableCopy.js.flow +5 -5
  262. package/util/withProvidedVariables.js.flow +14 -10
@@ -66,7 +66,7 @@ const warning = require('warning');
66
66
  */
67
67
  function getSingularSelector(
68
68
  fragment: ReaderFragment,
69
- item: mixed,
69
+ item: unknown,
70
70
  ): ?SingularReaderSelector {
71
71
  invariant(
72
72
  typeof item === 'object' && item !== null && !Array.isArray(item),
@@ -90,9 +90,9 @@ function getSingularSelector(
90
90
  (mixedClientEdgeTraversalPath == null ||
91
91
  Array.isArray(mixedClientEdgeTraversalPath))
92
92
  ) {
93
- const owner: RequestDescriptor = (mixedOwner: $FlowFixMe);
93
+ const owner: RequestDescriptor = mixedOwner as $FlowFixMe;
94
94
  const clientEdgeTraversalPath: ?ClientEdgeTraversalPath =
95
- (mixedClientEdgeTraversalPath: $FlowFixMe);
95
+ mixedClientEdgeTraversalPath as $FlowFixMe;
96
96
 
97
97
  const argumentVariables = fragments[fragment.name];
98
98
  const fragmentVariables = getFragmentVariables(
@@ -146,7 +146,7 @@ function getSingularSelector(
146
146
  */
147
147
  function getPluralSelector(
148
148
  fragment: ReaderFragment,
149
- items: $ReadOnlyArray<mixed>,
149
+ items: ReadonlyArray<unknown>,
150
150
  ): ?PluralReaderSelector {
151
151
  let selectors: null | Array<SingularReaderSelector> = null;
152
152
  items.forEach((item, ii) => {
@@ -168,7 +168,7 @@ function getPluralSelector(
168
168
 
169
169
  function getSelector(
170
170
  fragment: ReaderFragment,
171
- item: mixed | Array<mixed>,
171
+ item: unknown | Array<unknown>,
172
172
  ): ?ReaderSelector {
173
173
  if (item == null) {
174
174
  return item;
@@ -207,7 +207,7 @@ function getSelector(
207
207
  */
208
208
  function getSelectorsFromObject(
209
209
  fragments: {[key: string]: ReaderFragment, ...},
210
- object: {[key: string]: mixed, ...},
210
+ object: {[key: string]: unknown, ...},
211
211
  ): {[key: string]: ?ReaderSelector, ...} {
212
212
  const selectors: {[string]: ?ReaderSelector} = {};
213
213
  for (const key in fragments) {
@@ -231,7 +231,7 @@ function getSelectorsFromObject(
231
231
  */
232
232
  function getDataIDsFromObject(
233
233
  fragments: {[key: string]: ReaderFragment, ...},
234
- object: {[key: string]: mixed, ...},
234
+ object: {[key: string]: unknown, ...},
235
235
  ): {[key: string]: ?(DataID | Array<DataID>), ...} {
236
236
  const ids: {[string]: ?(DataID | Array<DataID>)} = {};
237
237
  for (const key in fragments) {
@@ -246,7 +246,7 @@ function getDataIDsFromObject(
246
246
 
247
247
  function getDataIDsFromFragment(
248
248
  fragment: ReaderFragment,
249
- item: mixed | Array<mixed>,
249
+ item: unknown | Array<unknown>,
250
250
  ): ?DataID | ?Array<DataID> {
251
251
  if (item == null) {
252
252
  return item;
@@ -278,7 +278,7 @@ function getDataIDsFromFragment(
278
278
  */
279
279
  function getDataIDs(
280
280
  fragment: ReaderFragment,
281
- items: $ReadOnlyArray<mixed>,
281
+ items: ReadonlyArray<unknown>,
282
282
  ): ?Array<DataID> {
283
283
  let ids: null | Array<DataID> = null;
284
284
  items.forEach(item => {
@@ -294,7 +294,7 @@ function getDataIDs(
294
294
  /**
295
295
  * @internal
296
296
  */
297
- function getDataID(fragment: ReaderFragment, item: mixed): ?DataID {
297
+ function getDataID(fragment: ReaderFragment, item: unknown): ?DataID {
298
298
  invariant(
299
299
  typeof item === 'object' && item !== null && !Array.isArray(item),
300
300
  'RelayModernSelector: Expected value for fragment `%s` to be an object, got ' +
@@ -332,7 +332,7 @@ function getDataID(fragment: ReaderFragment, item: mixed): ?DataID {
332
332
  */
333
333
  function getVariablesFromObject(
334
334
  fragments: {[key: string]: ReaderFragment, ...},
335
- object: {[key: string]: mixed, ...},
335
+ object: {[key: string]: unknown, ...},
336
336
  ): Variables {
337
337
  const variables = {};
338
338
  for (const key in fragments) {
@@ -349,7 +349,7 @@ function getVariablesFromObject(
349
349
 
350
350
  function getVariablesFromFragment(
351
351
  fragment: ReaderFragment,
352
- item: mixed | $ReadOnlyArray<mixed>,
352
+ item: unknown | ReadonlyArray<unknown>,
353
353
  ): Variables {
354
354
  if (item == null) {
355
355
  return {};
@@ -380,7 +380,7 @@ function getVariablesFromFragment(
380
380
 
381
381
  function getVariablesFromSingularFragment(
382
382
  fragment: ReaderFragment,
383
- item: mixed,
383
+ item: unknown,
384
384
  ): ?Variables {
385
385
  const selector = getSingularSelector(fragment, item);
386
386
  if (!selector) {
@@ -391,7 +391,7 @@ function getVariablesFromSingularFragment(
391
391
 
392
392
  function getVariablesFromPluralFragment(
393
393
  fragment: ReaderFragment,
394
- items: $ReadOnlyArray<mixed>,
394
+ items: ReadonlyArray<unknown>,
395
395
  ): Variables {
396
396
  const variables = {};
397
397
  items.forEach((value, ii) => {
@@ -519,13 +519,13 @@ function createReaderSelector(
519
519
  clientEdgeTraversalPath: ?ClientEdgeTraversalPath,
520
520
  ): SingularReaderSelector {
521
521
  return {
522
- kind: 'SingularReaderSelector',
522
+ clientEdgeTraversalPath: clientEdgeTraversalPath ?? null,
523
523
  dataID,
524
524
  isWithinUnmatchedTypeRefinement,
525
- clientEdgeTraversalPath: clientEdgeTraversalPath ?? null,
525
+ kind: 'SingularReaderSelector',
526
526
  node: fragment,
527
- variables,
528
527
  owner: request,
528
+ variables,
529
529
  };
530
530
  }
531
531
 
@@ -539,16 +539,16 @@ function createNormalizationSelector(
539
539
 
540
540
  module.exports = {
541
541
  areEqualSelectors,
542
- createReaderSelector,
543
542
  createNormalizationSelector,
543
+ createReaderSelector,
544
544
  getDataIDsFromFragment,
545
545
  getDataIDsFromObject,
546
- getSingularSelector,
547
546
  getPluralSelector,
548
547
  getSelector,
549
548
  getSelectorsFromObject,
550
- getVariablesFromSingularFragment,
551
- getVariablesFromPluralFragment,
549
+ getSingularSelector,
552
550
  getVariablesFromFragment,
553
551
  getVariablesFromObject,
552
+ getVariablesFromPluralFragment,
553
+ getVariablesFromSingularFragment,
554
554
  };
@@ -40,6 +40,7 @@ const {
40
40
  assertInternalActorIdentifier,
41
41
  } = require('../multi-actor-environment/ActorIdentifier');
42
42
  const deepFreeze = require('../util/deepFreeze');
43
+ const RelayFeatureFlags = require('../util/RelayFeatureFlags');
43
44
  const resolveImmediate = require('../util/resolveImmediate');
44
45
  const DataChecker = require('./DataChecker');
45
46
  const defaultGetDataID = require('./defaultGetDataID');
@@ -54,11 +55,16 @@ const RelayReader = require('./RelayReader');
54
55
  const RelayReferenceMarker = require('./RelayReferenceMarker');
55
56
  const RelayStoreSubscriptions = require('./RelayStoreSubscriptions');
56
57
  const RelayStoreUtils = require('./RelayStoreUtils');
57
- const {ROOT_ID, ROOT_TYPE} = require('./RelayStoreUtils');
58
+ const {
59
+ FIELD_GRANULAR_NOTIFICATIONS_KEY,
60
+ ROOT_ID,
61
+ ROOT_TYPE,
62
+ getFieldNotificationKey,
63
+ } = require('./RelayStoreUtils');
58
64
  const invariant = require('invariant');
59
65
 
60
66
  export opaque type InvalidationState = {
61
- dataIDs: $ReadOnlyArray<DataID>,
67
+ dataIDs: ReadonlyArray<DataID>,
62
68
  invalidations: Map<DataID, ?number>,
63
69
  };
64
70
 
@@ -67,6 +73,11 @@ type InvalidationSubscription = {
67
73
  invalidationState: InvalidationState,
68
74
  };
69
75
 
76
+ type Batch = {
77
+ sourceOperations: Array<OperationDescriptor>,
78
+ invalidateStore: boolean,
79
+ };
80
+
70
81
  const DEFAULT_RELEASE_BUFFER_SIZE = 10;
71
82
 
72
83
  /**
@@ -115,6 +126,8 @@ class RelayModernStore implements Store {
115
126
  _resolverContext: ?ResolverContext;
116
127
  _actorIdentifier: ?ActorIdentifier;
117
128
  _treatMissingFieldsAsNull: boolean;
129
+ _deferDeduplicatedFields: boolean;
130
+ _batch: Batch | null;
118
131
 
119
132
  constructor(
120
133
  source: MutableRecordSource,
@@ -134,6 +147,7 @@ class RelayModernStore implements Store {
134
147
  // These additional config options are only used if the experimental
135
148
  // @outputType resolver feature is used
136
149
  treatMissingFieldsAsNull?: ?boolean,
150
+ deferDeduplicatedFields?: ?boolean,
137
151
  actorIdentifier?: ?ActorIdentifier,
138
152
  },
139
153
  ) {
@@ -182,7 +196,9 @@ class RelayModernStore implements Store {
182
196
  options?.shouldProcessClientComponents ?? false;
183
197
 
184
198
  this._treatMissingFieldsAsNull = options?.treatMissingFieldsAsNull ?? false;
199
+ this._deferDeduplicatedFields = options?.deferDeduplicatedFields ?? false;
185
200
  this._actorIdentifier = options?.actorIdentifier;
201
+ this._batch = null;
186
202
 
187
203
  initializeRecordSource(this._recordSource);
188
204
  }
@@ -235,6 +251,60 @@ class RelayModernStore implements Store {
235
251
  }
236
252
  }
237
253
 
254
+ /**
255
+ * Batch multiple store updates into a single notification pass.
256
+ *
257
+ * Fragments will still correctly Suspend on their own parent query during
258
+ * a batch. However, cross-operation Suspense (a fragment suspending on a
259
+ * *different* in-flight operation) may not work correctly for operations
260
+ * that complete during the batch.
261
+ */
262
+ experimental_batchUpdates(callback: () => void): void {
263
+ if (this._batch != null) {
264
+ throw new Error(
265
+ 'RelayModernStore: Cannot batch updates while already batching updates.',
266
+ );
267
+ }
268
+ const log = this.__log;
269
+ if (log != null) {
270
+ log({name: 'store.batch.start'});
271
+ }
272
+ const batch: Batch = {sourceOperations: [], invalidateStore: false};
273
+ this._batch = batch;
274
+ try {
275
+ callback();
276
+ } finally {
277
+ this._batch = null;
278
+ this.notify(undefined, batch.invalidateStore);
279
+ for (const sourceOperation of batch.sourceOperations) {
280
+ this._recordSourceOperation(sourceOperation);
281
+ }
282
+ if (log != null) {
283
+ log({
284
+ name: 'store.batch.complete',
285
+ sourceOperations: batch.sourceOperations,
286
+ invalidateStore: batch.invalidateStore,
287
+ });
288
+ }
289
+ }
290
+ }
291
+
292
+ batchLiveStateUpdatesWithoutNotify(callback: () => void): boolean {
293
+ if (this.__log != null) {
294
+ this.__log({name: 'liveresolver.batch.start'});
295
+ }
296
+ let hasPublished = false;
297
+ try {
298
+ hasPublished =
299
+ this._resolverCache.batchLiveStateUpdatesWithoutNotify(callback);
300
+ } finally {
301
+ if (this.__log != null) {
302
+ this.__log({name: 'liveresolver.batch.end'});
303
+ }
304
+ }
305
+ return hasPublished;
306
+ }
307
+
238
308
  check(
239
309
  operation: OperationDescriptor,
240
310
  options?: CheckOptions,
@@ -341,7 +411,7 @@ class RelayModernStore implements Store {
341
411
  if (this._releaseBuffer.length > this._gcReleaseBufferSize) {
342
412
  const _id = this._releaseBuffer.shift();
343
413
  if (!this._shouldRetainWithinTTL_EXPERIMENTAL) {
344
- // $FlowFixMe[incompatible-call]
414
+ // $FlowFixMe[incompatible-type]
345
415
  this._roots.delete(_id);
346
416
  }
347
417
  this.scheduleGC();
@@ -385,6 +455,7 @@ class RelayModernStore implements Store {
385
455
  const snapshot = RelayReader.read(
386
456
  source,
387
457
  selector,
458
+ log,
388
459
  this._resolverCache,
389
460
  this._resolverContext,
390
461
  );
@@ -404,7 +475,22 @@ class RelayModernStore implements Store {
404
475
  notify(
405
476
  sourceOperation?: OperationDescriptor,
406
477
  invalidateStore?: boolean,
407
- ): $ReadOnlyArray<RequestDescriptor> {
478
+ ): ReadonlyArray<RequestDescriptor> {
479
+ // If we're inside a batchUpdates() call, defer notification.
480
+ const batch = this._batch;
481
+ if (batch != null) {
482
+ if (sourceOperation != null) {
483
+ batch.sourceOperations.push(sourceOperation);
484
+ }
485
+ if (invalidateStore === true) {
486
+ batch.invalidateStore = true;
487
+ }
488
+ // Returning [] here means the OperationExecutor's _updateOperationTracker
489
+ // will be a no-op for this call, since it relies on notify() returning
490
+ // the list of affected owners. See the experimental_batchUpdates JSDoc.
491
+ return [];
492
+ }
493
+
408
494
  const log = this.__log;
409
495
  if (log != null) {
410
496
  log({
@@ -413,69 +499,78 @@ class RelayModernStore implements Store {
413
499
  });
414
500
  }
415
501
 
416
- // Increment the current write when notifying after executing
417
- // a set of changes to the store.
418
- this._currentWriteEpoch++;
502
+ if (!RelayFeatureFlags.OPTIMIZE_NOTIFY) {
503
+ // Increment the current write when notifying after executing
504
+ // a set of changes to the store.
505
+ this._currentWriteEpoch++;
419
506
 
420
- if (invalidateStore === true) {
421
- this._globalInvalidationEpoch = this._currentWriteEpoch;
507
+ if (invalidateStore === true) {
508
+ this._globalInvalidationEpoch = this._currentWriteEpoch;
509
+ }
422
510
  }
423
511
 
424
512
  // When a record is updated, we need to also handle records that depend on it,
425
513
  // specifically Relay Resolver result records containing results based on the
426
514
  // updated records. This both adds to updatedRecordIDs and invalidates any
427
515
  // cached data as needed.
428
- this._resolverCache.invalidateDataIDs(this._updatedRecordIDs);
516
+ if (!RelayFeatureFlags.OPTIMIZE_NOTIFY || this._updatedRecordIDs.size > 0) {
517
+ this._resolverCache.invalidateDataIDs(this._updatedRecordIDs);
518
+ }
429
519
 
430
520
  const source = this.getSource();
431
521
  const updatedOwners: Array<RequestDescriptor> = [];
432
- this._storeSubscriptions.updateSubscriptions(
433
- source,
434
- this._updatedRecordIDs,
435
- updatedOwners,
436
- sourceOperation,
437
- );
438
- this._invalidationSubscriptions.forEach(subscription => {
439
- this._updateInvalidationSubscription(
440
- subscription,
441
- invalidateStore === true,
522
+ if (!RelayFeatureFlags.OPTIMIZE_NOTIFY || this._updatedRecordIDs.size > 0) {
523
+ this._storeSubscriptions.updateSubscriptions(
524
+ source,
525
+ this._updatedRecordIDs,
526
+ updatedOwners,
527
+ sourceOperation,
442
528
  );
443
- });
529
+ } else {
530
+ // If no record is updated, we still need to traverse stale subscriptions for
531
+ // subscriptions that were using values from optimistic updates
532
+ this._storeSubscriptions.updateStaleSubscriptions(
533
+ source,
534
+ this._updatedRecordIDs,
535
+ updatedOwners,
536
+ sourceOperation,
537
+ );
538
+ }
444
539
 
445
- // If a source operation was provided (indicating the operation
446
- // that produced this update to the store), record the current epoch
447
- // at which this operation was written.
448
- if (sourceOperation != null) {
449
- // We only track the epoch at which the operation was written if
450
- // it was previously retained, to keep the size of our operation
451
- // epoch map bounded. If a query wasn't retained, we assume it can
452
- // may be deleted at any moment and thus is not relevant for us to track
453
- // for the purposes of invalidation.
454
- const id = sourceOperation.request.identifier;
455
- const rootEntry = this._roots.get(id);
456
- if (rootEntry != null) {
457
- rootEntry.epoch = this._currentWriteEpoch;
458
- rootEntry.fetchTime = Date.now();
459
- } else if (
460
- sourceOperation.request.node.params.operationKind === 'query' &&
461
- this._gcReleaseBufferSize > 0 &&
462
- this._releaseBuffer.length < this._gcReleaseBufferSize
463
- ) {
464
- // The operation isn't retained but there is space in the release buffer:
465
- // temporarily track this operation in case the data can be reused soon.
466
- const temporaryRootEntry = {
467
- operation: sourceOperation,
468
- refCount: 0,
469
- epoch: this._currentWriteEpoch,
470
- fetchTime: Date.now(),
471
- };
472
- this._releaseBuffer.push(id);
473
- /* $FlowFixMe[incompatible-call] Natural Inference rollout. See
474
- * https://fburl.com/gdoc/y8dn025u */
475
- this._roots.set(id, temporaryRootEntry);
540
+ if (
541
+ RelayFeatureFlags.OPTIMIZE_NOTIFY &&
542
+ (this._updatedRecordIDs.size > 0 ||
543
+ updatedOwners.length > 0 ||
544
+ this._invalidatedRecordIDs.size > 0 ||
545
+ invalidateStore === true ||
546
+ this._globalInvalidationEpoch === this._currentWriteEpoch)
547
+ ) {
548
+ // Increment the current write when notifying after executing
549
+ // a set of changes to the store.
550
+ this._currentWriteEpoch++;
551
+
552
+ if (invalidateStore === true) {
553
+ this._globalInvalidationEpoch = this._currentWriteEpoch;
476
554
  }
477
555
  }
478
556
 
557
+ if (
558
+ !RelayFeatureFlags.OPTIMIZE_NOTIFY ||
559
+ this._invalidatedRecordIDs.size > 0 ||
560
+ invalidateStore === true
561
+ ) {
562
+ this._invalidationSubscriptions.forEach(subscription => {
563
+ this._updateInvalidationSubscription(
564
+ subscription,
565
+ invalidateStore === true,
566
+ );
567
+ });
568
+ }
569
+
570
+ if (sourceOperation != null) {
571
+ this._recordSourceOperation(sourceOperation);
572
+ }
573
+
479
574
  if (log != null) {
480
575
  log({
481
576
  name: 'store.notify.complete',
@@ -493,6 +588,40 @@ class RelayModernStore implements Store {
493
588
  return updatedOwners;
494
589
  }
495
590
 
591
+ /**
592
+ * Record that a source operation was written at the current epoch.
593
+ * We only track the epoch at which the operation was written if
594
+ * it was previously retained, to keep the size of our operation
595
+ * epoch map bounded. If a query wasn't retained, we assume it can
596
+ * be deleted at any moment and thus is not relevant for us to track
597
+ * for the purposes of invalidation.
598
+ */
599
+ _recordSourceOperation(sourceOperation: OperationDescriptor): void {
600
+ const id = sourceOperation.request.identifier;
601
+ const rootEntry = this._roots.get(id);
602
+ if (rootEntry != null) {
603
+ rootEntry.epoch = this._currentWriteEpoch;
604
+ rootEntry.fetchTime = Date.now();
605
+ } else if (
606
+ sourceOperation.request.node.params.operationKind === 'query' &&
607
+ this._gcReleaseBufferSize > 0 &&
608
+ this._releaseBuffer.length < this._gcReleaseBufferSize
609
+ ) {
610
+ // The operation isn't retained but there is space in the release buffer:
611
+ // temporarily track this operation in case the data can be reused soon.
612
+ const temporaryRootEntry = {
613
+ operation: sourceOperation,
614
+ refCount: 0,
615
+ epoch: this._currentWriteEpoch,
616
+ fetchTime: Date.now(),
617
+ };
618
+ this._releaseBuffer.push(id);
619
+ /* $FlowFixMe[incompatible-type] Natural Inference rollout. See
620
+ * https://fburl.com/gdoc/y8dn025u */
621
+ this._roots.set(id, temporaryRootEntry);
622
+ }
623
+ }
624
+
496
625
  publish(source: RecordSource, idsMarkedForInvalidation?: DataIDSet): void {
497
626
  const target = this._getMutableRecordSource();
498
627
  updateTargetFromSource(
@@ -543,7 +672,7 @@ class RelayModernStore implements Store {
543
672
  return {dispose};
544
673
  }
545
674
 
546
- toJSON(): mixed {
675
+ toJSON(): unknown {
547
676
  return 'RelayModernStore()';
548
677
  }
549
678
 
@@ -556,7 +685,7 @@ class RelayModernStore implements Store {
556
685
  return this._updatedRecordIDs;
557
686
  }
558
687
 
559
- lookupInvalidationState(dataIDs: $ReadOnlyArray<DataID>): InvalidationState {
688
+ lookupInvalidationState(dataIDs: ReadonlyArray<DataID>): InvalidationState {
560
689
  const invalidations = new Map<DataID, ?number>();
561
690
  dataIDs.forEach(dataID => {
562
691
  const record = this.getSource().get(dataID);
@@ -795,7 +924,7 @@ class RelayModernStore implements Store {
795
924
  RELAY_RESOLVER_LIVE_STATE_SUBSCRIPTION_KEY,
796
925
  );
797
926
  if (maybeResolverSubscription != null) {
798
- // $FlowFixMe - this value if it is not null, it is a function
927
+ // $FlowFixMe[not-a-function] - this value if it is not null, it is a function
799
928
  maybeResolverSubscription();
800
929
  }
801
930
  }
@@ -819,14 +948,13 @@ class RelayModernStore implements Store {
819
948
  }
820
949
 
821
950
  // Internal API for normalizing @outputType payloads in LiveResolverCache.
822
- __getNormalizationOptions(
823
- path: $ReadOnlyArray<string>,
824
- ): NormalizationOptions {
951
+ __getNormalizationOptions(path: ReadonlyArray<string>): NormalizationOptions {
825
952
  return {
826
953
  path,
827
954
  getDataID: this._getDataID,
828
955
  log: this.__log,
829
956
  treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
957
+ deferDeduplicatedFields: this._deferDeduplicatedFields,
830
958
  shouldProcessClientComponents: this._shouldProcessClientComponents,
831
959
  actorIdentifier: this._actorIdentifier,
832
960
  };
@@ -846,6 +974,13 @@ class RelayModernStore implements Store {
846
974
  function initializeRecordSource(target: MutableRecordSource) {
847
975
  if (!target.has(ROOT_ID)) {
848
976
  const rootRecord = RelayModernRecord.create(ROOT_ID, ROOT_TYPE);
977
+ if (RelayFeatureFlags.ENABLE_FIELD_GRANULAR_NOTIFICATIONS) {
978
+ RelayModernRecord.setValue(
979
+ rootRecord,
980
+ FIELD_GRANULAR_NOTIFICATIONS_KEY,
981
+ true,
982
+ );
983
+ }
849
984
  target.set(ROOT_ID, rootRecord);
850
985
  }
851
986
  }
@@ -930,6 +1065,32 @@ function updateTargetFromSource(
930
1065
  }
931
1066
  updatedRecordIDs.add(dataID);
932
1067
  target.set(dataID, nextRecord);
1068
+
1069
+ // Add per-field notification keys for field-granular records
1070
+ if (
1071
+ RelayModernRecord.getValue(
1072
+ nextRecord,
1073
+ FIELD_GRANULAR_NOTIFICATIONS_KEY,
1074
+ )
1075
+ ) {
1076
+ const fields = RelayModernRecord.getFields(nextRecord);
1077
+ for (let jj = 0; jj < fields.length; jj++) {
1078
+ const storageKey = fields[jj];
1079
+ // Skip internal metadata keys (all start with '__')
1080
+ if (storageKey.startsWith('__')) {
1081
+ continue;
1082
+ }
1083
+ if (
1084
+ RelayModernRecord.hasFieldChanged(
1085
+ targetRecord,
1086
+ nextRecord,
1087
+ storageKey,
1088
+ )
1089
+ ) {
1090
+ updatedRecordIDs.add(getFieldNotificationKey(dataID, storageKey));
1091
+ }
1092
+ }
1093
+ }
933
1094
  }
934
1095
  } else if (sourceRecord === null) {
935
1096
  target.delete(dataID);
@@ -23,7 +23,7 @@ class RelayOperationTracker {
23
23
  {
24
24
  promise: Promise<void>,
25
25
  resolve: () => void,
26
- pendingOperations: $ReadOnlyArray<RequestDescriptor>,
26
+ pendingOperations: ReadonlyArray<RequestDescriptor>,
27
27
  },
28
28
  >;
29
29
 
@@ -150,7 +150,7 @@ class RelayOperationTracker {
150
150
 
151
151
  getPendingOperationsAffectingOwner(owner: RequestDescriptor): {
152
152
  promise: Promise<void>,
153
- pendingOperations: $ReadOnlyArray<RequestDescriptor>,
153
+ pendingOperations: ReadonlyArray<RequestDescriptor>,
154
154
  } | null {
155
155
  const ownerIdentifier = owner.identifier;
156
156
  const pendingOperationsForOwner =
@@ -25,7 +25,7 @@ const RelayRecordSource = require('./RelayRecordSource');
25
25
  const invariant = require('invariant');
26
26
 
27
27
  const UNPUBLISH_RECORD_SENTINEL = RelayModernRecord.fromObject(
28
- // $FlowFixMe[incompatible-call] frozen objects are readonly
28
+ // $FlowFixMe[incompatible-type] frozen objects are readonly
29
29
  Object.freeze({
30
30
  __UNPUBLISH_RECORD_SENTINEL: true,
31
31
  }),
@@ -88,7 +88,7 @@ class RelayOptimisticRecordSource implements MutableRecordSource {
88
88
  }
89
89
 
90
90
  remove(dataID: DataID): void {
91
- // $FlowFixMe[incompatible-call] frozen objects are readonly
91
+ // $FlowFixMe[incompatible-type] frozen objects are readonly
92
92
  this._sink.set(dataID, UNPUBLISH_RECORD_SENTINEL);
93
93
  }
94
94