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.
- package/experimental.js +1 -1
- package/experimental.js.flow +8 -8
- package/handlers/connection/ConnectionHandler.js.flow +5 -5
- package/handlers/connection/ConnectionInterface.js.flow +1 -1
- package/index.js +1 -1
- package/index.js.flow +125 -62
- package/lib/experimental.js +3 -3
- package/lib/index.js +105 -57
- package/lib/multi-actor-environment/ActorIdentifier.js +2 -2
- package/lib/multi-actor-environment/MultiActorEnvironment.js +3 -1
- package/lib/mutations/commitMutation.js +8 -8
- package/lib/mutations/validateMutation.js +4 -4
- package/lib/query/GraphQLTag.js +3 -3
- package/lib/query/fetchQuery.js +15 -3
- package/lib/store/DataChecker.js +38 -4
- package/lib/store/NormalizationEngine.js +373 -0
- package/lib/store/OperationExecutor.js +172 -113
- package/lib/store/RelayConcreteVariables.js +1 -1
- package/lib/store/RelayErrorTrie.js +2 -2
- package/lib/store/RelayExperimentalGraphResponseTransform.js +8 -8
- package/lib/store/RelayModernEnvironment.js +26 -19
- package/lib/store/RelayModernRecord.js +18 -8
- package/lib/store/RelayModernSelector.js +9 -9
- package/lib/store/RelayModernStore.js +152 -43
- package/lib/store/RelayPublishQueue.js +1 -1
- package/lib/store/RelayReader.js +76 -38
- package/lib/store/RelayRecordSource.js +6 -0
- package/lib/store/RelayReferenceMarker.js +2 -1
- package/lib/store/RelayResponseNormalizer.js +88 -55
- package/lib/store/RelayStoreSubscriptions.js +34 -10
- package/lib/store/RelayStoreUtils.js +8 -1
- package/lib/store/ResolverFragments.js +2 -2
- package/lib/store/live-resolvers/LiveResolverCache.js +25 -9
- package/lib/store/observeFragmentExperimental.js +17 -1
- package/lib/store/observeQueryExperimental.js +2 -2
- package/lib/subscription/requestSubscription.js +3 -3
- package/lib/util/RelayError.js +3 -0
- package/lib/util/RelayFeatureFlags.js +6 -2
- package/lib/util/RelayReplaySubject.js +4 -4
- package/lib/util/handlePotentialSnapshotErrors.js +2 -2
- package/lib/util/stableCopy.js +2 -2
- package/llm-docs/api-reference/entrypoint-apis/entrypoint-container.mdx +38 -0
- package/llm-docs/api-reference/entrypoint-apis/load-entrypoint.mdx +77 -0
- package/llm-docs/api-reference/entrypoint-apis/use-entrypoint-loader.mdx +99 -0
- package/llm-docs/api-reference/graphql/graphql-directives.mdx +378 -0
- package/llm-docs/api-reference/hooks/_use-lazy-load-query-extra.mdx +16 -0
- package/llm-docs/api-reference/hooks/load-query.mdx +84 -0
- package/llm-docs/api-reference/hooks/relay-environment-provider.mdx +78 -0
- package/llm-docs/api-reference/hooks/use-client-query.mdx +65 -0
- package/llm-docs/api-reference/hooks/use-fragment.mdx +69 -0
- package/llm-docs/api-reference/hooks/use-lazy-load-query.mdx +62 -0
- package/llm-docs/api-reference/hooks/use-mutation.mdx +94 -0
- package/llm-docs/api-reference/hooks/use-pagination-fragment.mdx +166 -0
- package/llm-docs/api-reference/hooks/use-prefetchable-forward-pagination-fragment.mdx +134 -0
- package/llm-docs/api-reference/hooks/use-preloaded-query.mdx +84 -0
- package/llm-docs/api-reference/hooks/use-query-loader.mdx +95 -0
- package/llm-docs/api-reference/hooks/use-refetchable-fragment.mdx +122 -0
- package/llm-docs/api-reference/hooks/use-relay-environment.mdx +37 -0
- package/llm-docs/api-reference/hooks/use-subscription.mdx +66 -0
- package/llm-docs/api-reference/relay-resolvers/docblock-format.mdx +321 -0
- package/llm-docs/api-reference/relay-resolvers/runtime-functions.mdx +94 -0
- package/llm-docs/api-reference/relay-runtime/commit-mutation.mdx +65 -0
- package/llm-docs/api-reference/relay-runtime/fetch-query.mdx +113 -0
- package/llm-docs/api-reference/relay-runtime/field-logger.mdx +170 -0
- package/llm-docs/api-reference/relay-runtime/observe-fragment.mdx +92 -0
- package/llm-docs/api-reference/relay-runtime/relay-environment.mdx +53 -0
- package/llm-docs/api-reference/relay-runtime/request-subscription.mdx +54 -0
- package/llm-docs/api-reference/relay-runtime/runtime-configuration.mdx +52 -0
- package/llm-docs/api-reference/relay-runtime/store.mdx +734 -0
- package/llm-docs/api-reference/relay-runtime/wait-for-fragment-data.mdx +89 -0
- package/llm-docs/api-reference/types/CacheConfig.mdx +8 -0
- package/llm-docs/api-reference/types/Disposable.mdx +4 -0
- package/llm-docs/api-reference/types/GraphQLSubscriptionConfig.mdx +17 -0
- package/llm-docs/api-reference/types/MutationConfig.mdx +31 -0
- package/llm-docs/api-reference/types/SelectorStoreUpdater.mdx +6 -0
- package/llm-docs/api-reference/types/UploadableMap.mdx +3 -0
- package/llm-docs/community/learning-resources.mdx +64 -0
- package/llm-docs/debugging/declarative-mutation-directives.mdx +34 -0
- package/llm-docs/debugging/disallowed-id-types-error.mdx +43 -0
- package/llm-docs/debugging/inconsistent-typename-error.mdx +47 -0
- package/llm-docs/debugging/relay-devtools.mdx +73 -0
- package/llm-docs/debugging/why-null.mdx +116 -0
- package/llm-docs/editor-support.mdx +55 -0
- package/llm-docs/error-reference/unknown-field.mdx +36 -0
- package/llm-docs/getting-started/babel-plugin.mdx +31 -0
- package/llm-docs/getting-started/compiler-config.mdx +25 -0
- package/llm-docs/getting-started/compiler.mdx +82 -0
- package/llm-docs/getting-started/lint-rules.mdx +87 -0
- package/llm-docs/getting-started/production.mdx +30 -0
- package/llm-docs/getting-started/quick-start.mdx +213 -0
- package/llm-docs/glossary/glossary.mdx +1040 -0
- package/llm-docs/guided-tour/list-data/advanced-pagination.mdx +157 -0
- package/llm-docs/guided-tour/list-data/connections.mdx +81 -0
- package/llm-docs/guided-tour/list-data/pagination.mdx +193 -0
- package/llm-docs/guided-tour/list-data/rendering-connections.mdx +112 -0
- package/llm-docs/guided-tour/list-data/streaming-pagination.mdx +87 -0
- package/llm-docs/guided-tour/managing-data-outside-react/retaining-queries.mdx +51 -0
- package/llm-docs/guided-tour/refetching/refetching-queries-with-different-data.mdx +337 -0
- package/llm-docs/guided-tour/refetching/refreshing-queries.mdx +350 -0
- package/llm-docs/guided-tour/rendering/environment.mdx +59 -0
- package/llm-docs/guided-tour/rendering/error-states.mdx +295 -0
- package/llm-docs/guided-tour/rendering/fragments.mdx +354 -0
- package/llm-docs/guided-tour/rendering/loading-states.mdx +245 -0
- package/llm-docs/guided-tour/rendering/queries.mdx +261 -0
- package/llm-docs/guided-tour/rendering/variables.mdx +233 -0
- package/llm-docs/guided-tour/reusing-cached-data/fetch-policies.mdx +56 -0
- package/llm-docs/guided-tour/reusing-cached-data/filling-in-missing-data.mdx +102 -0
- package/llm-docs/guided-tour/reusing-cached-data/introduction.mdx +22 -0
- package/llm-docs/guided-tour/reusing-cached-data/presence-of-data.mdx +93 -0
- package/llm-docs/guided-tour/reusing-cached-data/rendering-partially-cached-data.mdx +175 -0
- package/llm-docs/guided-tour/reusing-cached-data/staleness-of-data.mdx +116 -0
- package/llm-docs/guided-tour/updating-data/client-only-data.mdx +115 -0
- package/llm-docs/guided-tour/updating-data/graphql-mutations.mdx +334 -0
- package/llm-docs/guided-tour/updating-data/graphql-subscriptions.mdx +279 -0
- package/llm-docs/guided-tour/updating-data/imperatively-modifying-linked-fields.mdx +511 -0
- package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data-legacy.mdx +142 -0
- package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data.mdx +275 -0
- package/llm-docs/guided-tour/updating-data/introduction.mdx +25 -0
- package/llm-docs/guided-tour/updating-data/local-data-updates.mdx +71 -0
- package/llm-docs/guided-tour/updating-data/typesafe-updaters-faq.mdx +83 -0
- package/llm-docs/guided-tour/updating-data/updating-connections.mdx +592 -0
- package/llm-docs/guides/alias-directive.mdx +160 -0
- package/llm-docs/guides/catch-directive.mdx +167 -0
- package/llm-docs/guides/client-schema-extensions.mdx +208 -0
- package/llm-docs/guides/codemods.mdx +66 -0
- package/llm-docs/guides/data-driven-dependencies/client-3d.mdx +255 -0
- package/llm-docs/guides/data-driven-dependencies/configuration.mdx +127 -0
- package/llm-docs/guides/data-driven-dependencies/introduction.mdx +39 -0
- package/llm-docs/guides/data-driven-dependencies/server-3d.mdx +664 -0
- package/llm-docs/guides/document-comparison.mdx +106 -0
- package/llm-docs/guides/graphql-server-specification.mdx +453 -0
- package/llm-docs/guides/network-layer.mdx +69 -0
- package/llm-docs/guides/persisted-queries.mdx +328 -0
- package/llm-docs/guides/relay-resolvers/context.mdx +99 -0
- package/llm-docs/guides/relay-resolvers/defining-fields.mdx +151 -0
- package/llm-docs/guides/relay-resolvers/defining-types.mdx +164 -0
- package/llm-docs/guides/relay-resolvers/deprecated.mdx +27 -0
- package/llm-docs/guides/relay-resolvers/derived-fields.mdx +127 -0
- package/llm-docs/guides/relay-resolvers/descriptions.mdx +44 -0
- package/llm-docs/guides/relay-resolvers/enabling.mdx +41 -0
- package/llm-docs/guides/relay-resolvers/errors.mdx +64 -0
- package/llm-docs/guides/relay-resolvers/field-arguments.mdx +63 -0
- package/llm-docs/guides/relay-resolvers/introduction.mdx +62 -0
- package/llm-docs/guides/relay-resolvers/limitations.mdx +30 -0
- package/llm-docs/guides/relay-resolvers/live-fields.mdx +164 -0
- package/llm-docs/guides/relay-resolvers/return-types.mdx +161 -0
- package/llm-docs/guides/relay-resolvers/suspense.mdx +41 -0
- package/llm-docs/guides/required-directive.mdx +240 -0
- package/llm-docs/guides/semantic-nullability.mdx +93 -0
- package/llm-docs/guides/testing-relay-components.mdx +642 -0
- package/llm-docs/guides/testing-relay-with-preloaded-queries.mdx +160 -0
- package/llm-docs/guides/throw-on-field-error-directive.mdx +58 -0
- package/llm-docs/guides/type-emission.mdx +414 -0
- package/llm-docs/home.mdx +32 -0
- package/llm-docs/principles-and-architecture/architecture-overview.mdx +24 -0
- package/llm-docs/principles-and-architecture/compiler-architecture.mdx +106 -0
- package/llm-docs/principles-and-architecture/runtime-architecture.mdx +249 -0
- package/llm-docs/principles-and-architecture/thinking-in-graphql.mdx +309 -0
- package/llm-docs/principles-and-architecture/thinking-in-relay.mdx +104 -0
- package/llm-docs/principles-and-architecture/videos.mdx +50 -0
- package/llm-docs/tutorial/arrays-lists.mdx +126 -0
- package/llm-docs/tutorial/fragments-1.mdx +487 -0
- package/llm-docs/tutorial/graphql.mdx +172 -0
- package/llm-docs/tutorial/interfaces-polymorphism.mdx +161 -0
- package/llm-docs/tutorial/intro.mdx +58 -0
- package/llm-docs/tutorial/mutations-updates.mdx +624 -0
- package/llm-docs/tutorial/organizing-mutations-queries-and-subscriptions.mdx +13 -0
- package/llm-docs/tutorial/queries-1.mdx +267 -0
- package/llm-docs/tutorial/queries-2.mdx +389 -0
- package/llm-docs/tutorial/refetchable-fragments.mdx +352 -0
- package/multi-actor-environment/ActorIdentifier.js.flow +2 -2
- package/multi-actor-environment/ActorSpecificEnvironment.js.flow +7 -7
- package/multi-actor-environment/ActorUtils.js.flow +1 -1
- package/multi-actor-environment/MultiActorEnvironment.js.flow +12 -8
- package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +3 -3
- package/mutations/RelayDeclarativeMutationConfig.js.flow +9 -9
- package/mutations/RelayRecordProxy.js.flow +8 -11
- package/mutations/RelayRecordSourceMutator.js.flow +4 -4
- package/mutations/RelayRecordSourceProxy.js.flow +4 -4
- package/mutations/RelayRecordSourceSelectorProxy.js.flow +6 -6
- package/mutations/applyOptimisticMutation.js.flow +2 -2
- package/mutations/commitMutation.js.flow +20 -16
- package/mutations/createUpdatableProxy.js.flow +19 -19
- package/mutations/readUpdatableFragment.js.flow +3 -3
- package/mutations/readUpdatableQuery.js.flow +3 -3
- package/mutations/validateMutation.js.flow +7 -7
- package/network/RelayNetworkTypes.js.flow +4 -4
- package/network/RelayObservable.js.flow +16 -14
- package/network/RelayQueryResponseCache.js.flow +3 -3
- package/network/wrapNetworkWithLogObserver.js.flow +1 -1
- package/package.json +2 -1
- package/query/GraphQLTag.js.flow +22 -10
- package/query/fetchQuery.js.flow +23 -10
- package/query/fetchQuery_DEPRECATED.js.flow +1 -1
- package/store/DataChecker.js.flow +43 -9
- package/store/NormalizationEngine.js.flow +779 -0
- package/store/OperationExecutor.js.flow +173 -70
- package/store/RelayConcreteVariables.js.flow +5 -5
- package/store/RelayErrorTrie.js.flow +12 -12
- package/store/RelayExperimentalGraphResponseHandler.js.flow +3 -3
- package/store/RelayExperimentalGraphResponseTransform.js.flow +10 -10
- package/store/RelayModernEnvironment.js.flow +41 -26
- package/store/RelayModernFragmentSpecResolver.js.flow +1 -1
- package/store/RelayModernOperationDescriptor.js.flow +1 -1
- package/store/RelayModernRecord.js.flow +46 -22
- package/store/RelayModernSelector.js.flow +21 -21
- package/store/RelayModernStore.js.flow +219 -58
- package/store/RelayOperationTracker.js.flow +2 -2
- package/store/RelayOptimisticRecordSource.js.flow +2 -2
- package/store/RelayPublishQueue.js.flow +21 -12
- package/store/RelayReader.js.flow +130 -58
- package/store/RelayRecordSource.js.flow +10 -0
- package/store/RelayRecordState.js.flow +1 -1
- package/store/RelayReferenceMarker.js.flow +5 -4
- package/store/RelayResponseNormalizer.js.flow +130 -54
- package/store/RelayStoreSubscriptions.js.flow +52 -8
- package/store/RelayStoreTypes.js.flow +153 -64
- package/store/RelayStoreUtils.js.flow +15 -7
- package/store/ResolverCache.js.flow +2 -2
- package/store/ResolverFragments.js.flow +12 -12
- package/store/StoreInspector.js.flow +6 -7
- package/store/cloneRelayHandleSourceField.js.flow +1 -1
- package/store/cloneRelayScalarHandleSourceField.js.flow +1 -1
- package/store/createRelayContext.js.flow +1 -1
- package/store/createRelayLoggingContext.js.flow +4 -4
- package/store/defaultGetDataID.js.flow +2 -2
- package/store/isRelayModernEnvironment.js.flow +4 -2
- package/store/live-resolvers/LiveResolverCache.js.flow +55 -20
- package/store/live-resolvers/LiveResolverSuspenseSentinel.js.flow +3 -3
- package/store/live-resolvers/getOutputTypeRecordIDs.js.flow +1 -1
- package/store/live-resolvers/isLiveStateValue.js.flow +2 -2
- package/store/live-resolvers/resolverDataInjector.js.flow +8 -5
- package/store/observeFragmentExperimental.js.flow +49 -20
- package/store/observeQueryExperimental.js.flow +5 -5
- package/store/readInlineData.js.flow +4 -4
- package/store/waitForFragmentExperimental.js.flow +3 -3
- package/subscription/requestSubscription.js.flow +7 -7
- package/util/NormalizationNode.js.flow +34 -32
- package/util/ReaderNode.js.flow +32 -30
- package/util/RelayConcreteNode.js.flow +5 -5
- package/util/RelayError.js.flow +4 -1
- package/util/RelayFeatureFlags.js.flow +21 -1
- package/util/RelayProfiler.js.flow +1 -1
- package/util/RelayReplaySubject.js.flow +3 -3
- package/util/RelayRuntimeTypes.js.flow +11 -11
- package/util/createPayloadFor3DField.js.flow +9 -5
- package/util/deepFreeze.js.flow +2 -2
- package/util/getFragmentIdentifier.js.flow +1 -1
- package/util/getPaginationMetadata.js.flow +1 -1
- package/util/getPaginationVariables.js.flow +1 -1
- package/util/getPendingOperationsForFragment.js.flow +2 -2
- package/util/getRefetchMetadata.js.flow +6 -5
- package/util/getValueAtPath.js.flow +3 -3
- package/util/handlePotentialSnapshotErrors.js.flow +5 -5
- package/util/isEmptyObject.js.flow +1 -1
- package/util/isPromise.js.flow +2 -2
- package/util/isScalarAndEqual.js.flow +1 -1
- package/util/recycleNodesInto.js.flow +2 -2
- package/util/registerEnvironmentWithDevTools.js.flow +1 -1
- package/util/shallowFreeze.js.flow +1 -1
- package/util/stableCopy.js.flow +5 -5
- 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]:
|
|
73
|
+
fieldValue: {+[string]: unknown},
|
|
71
74
|
typeName: string,
|
|
72
|
-
) =>
|
|
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?:
|
|
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
|
-
|
|
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):
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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 ||
|
|
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
|
-
|
|
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-
|
|
719
|
+
// $FlowFixMe[incompatible-type]
|
|
644
720
|
const typeName = field.concreteType ?? this._getRecordType(fieldValue);
|
|
645
721
|
const nextID =
|
|
646
722
|
this._getDataId(
|
|
647
|
-
// $FlowFixMe[incompatible-
|
|
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
|
-
|
|
669
|
-
data:
|
|
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:
|
|
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:
|
|
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:
|
|
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 = {
|
|
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
|
|
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
|
-
|
|
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);
|