relay-runtime 20.1.0 → 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 +46 -22
  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 +130 -58
  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 +130 -54
  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
@@ -15,7 +15,9 @@ import type {ActorIdentifier} from '../multi-actor-environment/ActorIdentifier';
15
15
  import type {PayloadData, PayloadError} from '../network/RelayNetworkTypes';
16
16
  import type {
17
17
  NormalizationActorChange,
18
+ NormalizationClientEdgeToClientObject,
18
19
  NormalizationDefer,
20
+ NormalizationInlineFragment,
19
21
  NormalizationLinkedField,
20
22
  NormalizationLiveResolverField,
21
23
  NormalizationModuleImport,
@@ -54,6 +56,7 @@ const RelayModernRecord = require('./RelayModernRecord');
54
56
  const {createNormalizationSelector} = require('./RelayModernSelector');
55
57
  const {
56
58
  ROOT_ID,
59
+ ROOT_TYPE,
57
60
  TYPENAME_KEY,
58
61
  getArgumentValues,
59
62
  getHandleStorageKey,
@@ -67,15 +70,16 @@ const invariant = require('invariant');
67
70
  const warning = require('warning');
68
71
 
69
72
  export type GetDataID = (
70
- fieldValue: {+[string]: mixed},
73
+ fieldValue: {+[string]: unknown},
71
74
  typeName: string,
72
- ) => mixed;
75
+ ) => unknown;
73
76
 
74
77
  export type NormalizationOptions = {
75
78
  +getDataID: GetDataID,
76
79
  +treatMissingFieldsAsNull: boolean,
80
+ +deferDeduplicatedFields: boolean,
77
81
  +log: ?LogFunction,
78
- +path?: $ReadOnlyArray<string>,
82
+ +path?: ReadonlyArray<string>,
79
83
  +shouldProcessClientComponents?: ?boolean,
80
84
  +actorIdentifier?: ?ActorIdentifier,
81
85
  };
@@ -112,6 +116,7 @@ class RelayResponseNormalizer {
112
116
  _getDataId: GetDataID;
113
117
  _handleFieldPayloads: Array<HandleFieldPayload>;
114
118
  _treatMissingFieldsAsNull: boolean;
119
+ _deferDeduplicatedFields: boolean;
115
120
  _incrementalPlaceholders: Array<IncrementalDataPlaceholder>;
116
121
  _isClientExtension: boolean;
117
122
  _isUnmatchedAbstractType: boolean;
@@ -123,6 +128,17 @@ class RelayResponseNormalizer {
123
128
  _shouldProcessClientComponents: ?boolean;
124
129
  _errorTrie: RelayErrorTrie | null;
125
130
  _log: ?LogFunction;
131
+ _s2cExecutions: Map<
132
+ DataID,
133
+ {
134
+ selections: Array<
135
+ | NormalizationClientEdgeToClientObject
136
+ | NormalizationLiveResolverField
137
+ | NormalizationResolverField,
138
+ >,
139
+ typeName: string,
140
+ },
141
+ >;
126
142
 
127
143
  constructor(
128
144
  recordSource: MutableRecordSource,
@@ -134,6 +150,7 @@ class RelayResponseNormalizer {
134
150
  this._getDataId = options.getDataID;
135
151
  this._handleFieldPayloads = [];
136
152
  this._treatMissingFieldsAsNull = options.treatMissingFieldsAsNull;
153
+ this._deferDeduplicatedFields = options.deferDeduplicatedFields;
137
154
  this._incrementalPlaceholders = [];
138
155
  this._isClientExtension = false;
139
156
  this._isUnmatchedAbstractType = false;
@@ -144,6 +161,7 @@ class RelayResponseNormalizer {
144
161
  this._variables = variables;
145
162
  this._shouldProcessClientComponents = options.shouldProcessClientComponents;
146
163
  this._log = options.log;
164
+ this._s2cExecutions = new Map();
147
165
  }
148
166
 
149
167
  normalizeResponse(
@@ -164,10 +182,20 @@ class RelayResponseNormalizer {
164
182
  return {
165
183
  errors,
166
184
  fieldPayloads: this._handleFieldPayloads,
167
- incrementalPlaceholders: this._incrementalPlaceholders,
168
185
  followupPayloads: this._followupPayloads,
169
- source: this._recordSource,
186
+ incrementalPlaceholders: this._incrementalPlaceholders,
170
187
  isFinal: false,
188
+ s2cExecutions:
189
+ this._s2cExecutions.size > 0
190
+ ? Array.from(this._s2cExecutions.entries()).map(
191
+ ([recordID, entry]) => ({
192
+ recordID,
193
+ selections: entry.selections,
194
+ typeName: entry.typeName,
195
+ }),
196
+ )
197
+ : undefined,
198
+ source: this._recordSource,
171
199
  };
172
200
  }
173
201
 
@@ -192,7 +220,7 @@ class RelayResponseNormalizer {
192
220
  }
193
221
  }
194
222
 
195
- _getVariableValue(name: string): mixed {
223
+ _getVariableValue(name: string): unknown {
196
224
  invariant(
197
225
  this._variables.hasOwnProperty(name),
198
226
  'RelayResponseNormalizer(): Undefined variable `%s`.',
@@ -202,7 +230,7 @@ class RelayResponseNormalizer {
202
230
  }
203
231
 
204
232
  _getRecordType(data: PayloadData): string {
205
- const typeName = (data: any)[TYPENAME_KEY];
233
+ const typeName = (data as any)[TYPENAME_KEY];
206
234
  invariant(
207
235
  typeName != null,
208
236
  'RelayResponseNormalizer(): Expected a typename for record `%s`.',
@@ -243,35 +271,16 @@ class RelayResponseNormalizer {
243
271
  break;
244
272
  }
245
273
  case 'InlineFragment': {
246
- const {abstractKey} = selection;
247
- if (abstractKey == null) {
248
- const typeName = RelayModernRecord.getType(record);
249
- if (typeName === selection.type) {
250
- this._traverseSelections(selection, record, data);
251
- }
252
- } else {
253
- const implementsInterface = data.hasOwnProperty(abstractKey);
254
- const typeName = RelayModernRecord.getType(record);
255
- const typeID = generateTypeID(typeName);
256
- let typeRecord = this._recordSource.get(typeID);
257
- if (typeRecord == null) {
258
- typeRecord = RelayModernRecord.create(typeID, TYPE_SCHEMA_TYPE);
259
- this._recordSource.set(typeID, typeRecord);
260
- }
261
- RelayModernRecord.setValue(
262
- typeRecord,
263
- abstractKey,
264
- implementsInterface,
265
- );
266
- if (implementsInterface) {
267
- this._traverseSelections(selection, record, data);
268
- }
269
- }
274
+ this._normalizeInlineFragment(selection, record, data);
270
275
  break;
271
276
  }
272
277
  case 'TypeDiscriminator': {
273
278
  const {abstractKey} = selection;
274
- const implementsInterface = data.hasOwnProperty(abstractKey);
279
+ // $FlowFixMe[method-unbinding] - data could be prototype less
280
+ const implementsInterface = Object.prototype.hasOwnProperty.call(
281
+ data,
282
+ abstractKey,
283
+ );
275
284
  const typeName = RelayModernRecord.getType(record);
276
285
  const typeID = generateTypeID(typeName);
277
286
  let typeRecord = this._recordSource.get(typeID);
@@ -298,10 +307,10 @@ class RelayResponseNormalizer {
298
307
  dataID: RelayModernRecord.getDataID(record),
299
308
  fieldKey,
300
309
  handle: selection.handle,
301
- handleKey,
302
310
  handleArgs: selection.handleArgs
303
311
  ? getArgumentValues(selection.handleArgs, this._variables)
304
312
  : {},
313
+ handleKey,
305
314
  });
306
315
  break;
307
316
  case 'ModuleImport':
@@ -332,15 +341,21 @@ class RelayResponseNormalizer {
332
341
  case 'RelayLiveResolver':
333
342
  if (!this._useExecTimeResolvers) {
334
343
  this._normalizeResolver(selection, record, data);
344
+ } else if (selection.resolverInfo?.rootFragment != null) {
345
+ this._collectS2CExecution(selection, record);
335
346
  }
336
347
  break;
337
348
  case 'ClientEdgeToClientObject':
338
349
  if (!this._useExecTimeResolvers) {
339
350
  this._normalizeResolver(selection.backingField, record, data);
351
+ } else if (
352
+ selection.backingField.resolverInfo?.rootFragment != null
353
+ ) {
354
+ this._collectS2CExecution(selection, record);
340
355
  }
341
356
  break;
342
357
  default:
343
- (selection: empty);
358
+ selection as empty;
344
359
  invariant(
345
360
  false,
346
361
  'RelayResponseNormalizer(): Unexpected ast kind `%s`.',
@@ -350,16 +365,75 @@ class RelayResponseNormalizer {
350
365
  }
351
366
  }
352
367
 
368
+ _normalizeInlineFragment(
369
+ selection: NormalizationInlineFragment,
370
+ record: Record,
371
+ data: PayloadData,
372
+ ) {
373
+ const {abstractKey} = selection;
374
+ if (abstractKey == null) {
375
+ const typeName = RelayModernRecord.getType(record);
376
+ if (
377
+ typeName === selection.type ||
378
+ // The root record type is a special `__Root` type and may not match the
379
+ // type on the ast, so ignore type mismatches at the root. We currently
380
+ // detect whether we're at the root by checking against ROOT_ID, but this
381
+ // does not work for mutations/subscriptions which generate unique root
382
+ // ids. This is acceptable in practice as we don't read data for
383
+ // mutations/subscriptions in a situation where we would use
384
+ // isMissingData to decide whether to suspend or not.
385
+ // TODO T96653810: Correctly detect reading from root of mutation/subscription
386
+ typeName === ROOT_TYPE
387
+ ) {
388
+ this._traverseSelections(selection, record, data);
389
+ }
390
+ } else {
391
+ // $FlowFixMe[method-unbinding] - data could be prototype less
392
+ const implementsInterface = Object.prototype.hasOwnProperty.call(
393
+ data,
394
+ abstractKey,
395
+ );
396
+ const typeName = RelayModernRecord.getType(record);
397
+ const typeID = generateTypeID(typeName);
398
+ let typeRecord = this._recordSource.get(typeID);
399
+ if (typeRecord == null) {
400
+ typeRecord = RelayModernRecord.create(typeID, TYPE_SCHEMA_TYPE);
401
+ this._recordSource.set(typeID, typeRecord);
402
+ }
403
+ RelayModernRecord.setValue(typeRecord, abstractKey, implementsInterface);
404
+ if (implementsInterface) {
405
+ this._traverseSelections(selection, record, data);
406
+ }
407
+ }
408
+ }
409
+
353
410
  _normalizeResolver(
354
411
  resolver: NormalizationResolverField | NormalizationLiveResolverField,
355
412
  record: Record,
356
413
  data: PayloadData,
357
414
  ) {
358
415
  if (resolver.fragment != null) {
359
- this._traverseSelections(resolver.fragment, record, data);
416
+ this._normalizeInlineFragment(resolver.fragment, record, data);
360
417
  }
361
418
  }
362
419
 
420
+ _collectS2CExecution(
421
+ selection:
422
+ | NormalizationClientEdgeToClientObject
423
+ | NormalizationLiveResolverField
424
+ | NormalizationResolverField,
425
+ record: Record,
426
+ ): void {
427
+ const recordID = RelayModernRecord.getDataID(record);
428
+ const typeName = RelayModernRecord.getType(record);
429
+ let entry = this._s2cExecutions.get(recordID);
430
+ if (entry == null) {
431
+ entry = {selections: [], typeName};
432
+ this._s2cExecutions.set(recordID, entry);
433
+ }
434
+ entry.selections.push(selection);
435
+ }
436
+
363
437
  _normalizeDefer(
364
438
  defer: NormalizationDefer,
365
439
  record: Record,
@@ -382,8 +456,9 @@ class RelayResponseNormalizer {
382
456
  // Otherwise data *for this selection* should not be present: enqueue
383
457
  // metadata to process the subsequent response chunk.
384
458
  this._incrementalPlaceholders.push({
385
- kind: 'defer',
459
+ actorIdentifier: this._actorIdentifier,
386
460
  data,
461
+ kind: 'defer',
387
462
  label: defer.label,
388
463
  path: [...this._path],
389
464
  selector: createNormalizationSelector(
@@ -392,7 +467,6 @@ class RelayResponseNormalizer {
392
467
  this._variables,
393
468
  ),
394
469
  typeName: RelayModernRecord.getType(record),
395
- actorIdentifier: this._actorIdentifier,
396
470
  });
397
471
  }
398
472
  }
@@ -419,13 +493,13 @@ class RelayResponseNormalizer {
419
493
  // If streaming is enabled, *also* emit metadata to process any
420
494
  // response chunks that may be delivered.
421
495
  this._incrementalPlaceholders.push({
496
+ actorIdentifier: this._actorIdentifier,
422
497
  kind: 'stream',
423
498
  label: stream.label,
424
- path: [...this._path],
425
- parentID: RelayModernRecord.getDataID(record),
426
499
  node: stream,
500
+ parentID: RelayModernRecord.getDataID(record),
501
+ path: [...this._path],
427
502
  variables: this._variables,
428
- actorIdentifier: this._actorIdentifier,
429
503
  });
430
504
  }
431
505
  }
@@ -458,15 +532,15 @@ class RelayResponseNormalizer {
458
532
  );
459
533
  if (operationReference != null) {
460
534
  this._followupPayloads.push({
461
- kind: 'ModuleImportPayload',
535
+ actorIdentifier: this._actorIdentifier,
462
536
  args: moduleImport.args,
463
537
  data,
464
538
  dataID: RelayModernRecord.getDataID(record),
539
+ kind: 'ModuleImportPayload',
465
540
  operationReference,
466
541
  path: [...this._path],
467
542
  typeName,
468
543
  variables: this._variables,
469
- actorIdentifier: this._actorIdentifier,
470
544
  });
471
545
  }
472
546
  }
@@ -500,7 +574,9 @@ class RelayResponseNormalizer {
500
574
  // client is assumed to be correctly configured with
501
575
  // treatMissingFieldsAsNull=true.
502
576
  const isOptionalField =
503
- this._isClientExtension || this._isUnmatchedAbstractType;
577
+ this._isClientExtension ||
578
+ this._isUnmatchedAbstractType ||
579
+ this._deferDeduplicatedFields;
504
580
 
505
581
  if (isOptionalField) {
506
582
  // Field not expected to exist regardless of whether the server is pruning null
@@ -575,7 +651,7 @@ class RelayResponseNormalizer {
575
651
  this._errorTrie = oldErrorTrie;
576
652
  this._path.pop();
577
653
  } else {
578
- (selection: empty);
654
+ selection as empty;
579
655
  invariant(
580
656
  false,
581
657
  'RelayResponseNormalizer(): Unexpected ast kind `%s` during normalization.',
@@ -640,11 +716,11 @@ class RelayResponseNormalizer {
640
716
  return;
641
717
  }
642
718
 
643
- // $FlowFixMe[incompatible-call]
719
+ // $FlowFixMe[incompatible-type]
644
720
  const typeName = field.concreteType ?? this._getRecordType(fieldValue);
645
721
  const nextID =
646
722
  this._getDataId(
647
- // $FlowFixMe[incompatible-call]
723
+ // $FlowFixMe[incompatible-type]
648
724
  fieldValue,
649
725
  typeName,
650
726
  ) ||
@@ -665,14 +741,14 @@ class RelayResponseNormalizer {
665
741
  );
666
742
 
667
743
  this._followupPayloads.push({
668
- kind: 'ActorPayload',
669
- data: (fieldValue: $FlowFixMe),
744
+ actorIdentifier,
745
+ data: fieldValue as $FlowFixMe,
670
746
  dataID: nextID,
747
+ kind: 'ActorPayload',
748
+ node: field,
671
749
  path: [...this._path, responseKey],
672
750
  typeName,
673
751
  variables: this._variables,
674
- node: field,
675
- actorIdentifier,
676
752
  });
677
753
  }
678
754
 
@@ -680,7 +756,7 @@ class RelayResponseNormalizer {
680
756
  field: NormalizationLinkedField,
681
757
  record: Record,
682
758
  storageKey: string,
683
- fieldValue: mixed,
759
+ fieldValue: unknown,
684
760
  ): void {
685
761
  invariant(
686
762
  typeof fieldValue === 'object' && fieldValue,
@@ -725,7 +801,7 @@ class RelayResponseNormalizer {
725
801
  field: NormalizationLinkedField,
726
802
  record: Record,
727
803
  storageKey: string,
728
- fieldValue: mixed,
804
+ fieldValue: unknown,
729
805
  ): void {
730
806
  invariant(
731
807
  Array.isArray(fieldValue),
@@ -815,8 +891,8 @@ class RelayResponseNormalizer {
815
891
  if (!expected) {
816
892
  const logEvent: IdCollisionTypenameLogEvent = {
817
893
  name: 'idCollision.typename',
818
- previous_typename: RelayModernRecord.getType(record),
819
894
  new_typename: typeName,
895
+ previous_typename: RelayModernRecord.getType(record),
820
896
  };
821
897
  if (this._log != null) {
822
898
  this._log(logEvent);
@@ -850,7 +926,7 @@ class RelayResponseNormalizer {
850
926
  _validateConflictingFieldsWithIdenticalId(
851
927
  record: Record,
852
928
  storageKey: string,
853
- fieldValue: mixed,
929
+ fieldValue: unknown,
854
930
  ): void {
855
931
  // NOTE: Only emit a warning in DEV
856
932
  if (__DEV__) {
@@ -43,6 +43,9 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
43
43
  __log: ?LogFunction;
44
44
  _resolverCache: ResolverCache;
45
45
  _resolverContext: ?ResolverContext;
46
+ // Stores `subscriptions` with stale snapshots, used for reducing the traversal
47
+ // that has to happen on `notify`
48
+ _staleSubscriptions: Set<Subscription>;
46
49
 
47
50
  constructor(
48
51
  log?: ?LogFunction,
@@ -50,6 +53,7 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
50
53
  resolverContext?: ResolverContext,
51
54
  ) {
52
55
  this._subscriptions = new Set();
56
+ this._staleSubscriptions = new Set();
53
57
  this.__log = log;
54
58
  this._resolverCache = resolverCache;
55
59
  this._resolverContext = resolverContext;
@@ -59,9 +63,17 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
59
63
  snapshot: Snapshot,
60
64
  callback: (snapshot: Snapshot) => void,
61
65
  ): Disposable {
62
- const subscription = {backup: null, callback, snapshot, stale: false};
66
+ const subscription: Subscription = {
67
+ backup: null,
68
+ callback,
69
+ snapshot,
70
+ stale: false,
71
+ };
63
72
  const dispose = () => {
64
73
  this._subscriptions.delete(subscription);
74
+ if (RelayFeatureFlags.OPTIMIZE_NOTIFY && subscription.stale) {
75
+ this._staleSubscriptions.delete(subscription);
76
+ }
65
77
  };
66
78
  this._subscriptions.add(subscription);
67
79
  return {dispose};
@@ -89,11 +101,12 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
89
101
  const backup = RelayReader.read(
90
102
  source,
91
103
  snapshot.selector,
104
+ this.__log,
92
105
  this._resolverCache,
93
106
  this._resolverContext,
94
107
  );
95
108
  const nextData = recycleNodesInto(snapshot.data, backup.data);
96
- (backup: $FlowFixMe).data = nextData; // backup owns the snapshot and can safely mutate
109
+ (backup as $FlowFixMe).data = nextData; // backup owns the snapshot and can safely mutate
97
110
  subscription.backup = backup;
98
111
  });
99
112
  }
@@ -107,20 +120,26 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
107
120
  // This subscription's data changed in the optimistic state. We will
108
121
  // need to re-read.
109
122
  subscription.stale = true;
123
+ if (RelayFeatureFlags.OPTIMIZE_NOTIFY) {
124
+ this._staleSubscriptions.add(subscription);
125
+ }
110
126
  }
111
127
  subscription.snapshot = {
112
128
  data: subscription.snapshot.data,
129
+ fieldErrors: backup.fieldErrors,
113
130
  isMissingData: backup.isMissingData,
114
131
  missingClientEdges: backup.missingClientEdges,
115
132
  missingLiveResolverFields: backup.missingLiveResolverFields,
116
133
  seenRecords: backup.seenRecords,
117
134
  selector: backup.selector,
118
- fieldErrors: backup.fieldErrors,
119
135
  };
120
136
  } else {
121
137
  // This subscription was created during the optimisitic state. We should
122
138
  // re-read.
123
139
  subscription.stale = true;
140
+ if (RelayFeatureFlags.OPTIMIZE_NOTIFY) {
141
+ this._staleSubscriptions.add(subscription);
142
+ }
124
143
  }
125
144
  });
126
145
  }
@@ -146,6 +165,27 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
146
165
  });
147
166
  }
148
167
 
168
+ updateStaleSubscriptions(
169
+ source: RecordSource,
170
+ updatedRecordIDs: DataIDSet,
171
+ updatedOwners: Array<RequestDescriptor>,
172
+ sourceOperation?: OperationDescriptor,
173
+ ) {
174
+ const hasUpdatedRecords = updatedRecordIDs.size !== 0;
175
+ this._staleSubscriptions.forEach(subscription => {
176
+ const owner = this._updateSubscription(
177
+ source,
178
+ subscription,
179
+ updatedRecordIDs,
180
+ hasUpdatedRecords,
181
+ sourceOperation,
182
+ );
183
+ if (owner != null) {
184
+ updatedOwners.push(owner);
185
+ }
186
+ });
187
+ }
188
+
149
189
  /**
150
190
  * Notifies the callback for the subscription if the data for the associated
151
191
  * snapshot has changed.
@@ -173,32 +213,36 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
173
213
  ? RelayReader.read(
174
214
  source,
175
215
  snapshot.selector,
216
+ this.__log,
176
217
  this._resolverCache,
177
218
  this._resolverContext,
178
219
  )
179
220
  : backup;
180
221
  const nextData = recycleNodesInto(snapshot.data, nextSnapshot.data);
181
- nextSnapshot = ({
222
+ nextSnapshot = {
182
223
  data: nextData,
224
+ fieldErrors: nextSnapshot.fieldErrors,
183
225
  isMissingData: nextSnapshot.isMissingData,
184
226
  missingClientEdges: nextSnapshot.missingClientEdges,
185
227
  missingLiveResolverFields: nextSnapshot.missingLiveResolverFields,
186
228
  seenRecords: nextSnapshot.seenRecords,
187
229
  selector: nextSnapshot.selector,
188
- fieldErrors: nextSnapshot.fieldErrors,
189
- }: Snapshot);
230
+ } as Snapshot;
190
231
  if (__DEV__) {
191
232
  deepFreeze(nextSnapshot);
192
233
  }
193
234
  subscription.snapshot = nextSnapshot;
194
235
  subscription.stale = false;
236
+ if (RelayFeatureFlags.OPTIMIZE_NOTIFY && stale) {
237
+ this._staleSubscriptions.delete(subscription);
238
+ }
195
239
  if (nextSnapshot.data !== snapshot.data) {
196
240
  if (this.__log && RelayFeatureFlags.ENABLE_NOTIFY_SUBSCRIPTION) {
197
241
  this.__log({
198
242
  name: 'store.notify.subscription',
199
- sourceOperation,
200
- snapshot,
201
243
  nextSnapshot,
244
+ snapshot,
245
+ sourceOperation,
202
246
  });
203
247
  }
204
248
  callback(nextSnapshot);