UNPKG

6.1 kBJavaScriptView Raw
1/**
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @emails oncall+relay
8 *
9 * @format
10 */
11'use strict';
12
13var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
14
15var ReplaySubject = require('relay-runtime/lib/util/RelayReplaySubject');
16
17var getRequestIdentifier = require('relay-runtime/lib/util/getRequestIdentifier');
18
19var invariant = require("fbjs/lib/invariant");
20
21var _require = require('relay-runtime'),
22 createOperationDescriptor = _require.createOperationDescriptor,
23 Environment = _require.Environment,
24 getRequest = _require.getRequest,
25 Observable = _require.Observable;
26
27// Expire results by this delay after they resolve.
28var DEFAULT_PREFETCH_TIMEOUT = 30 * 1000; // 30 seconds
29
30var WEAKMAP_SUPPORTED = typeof WeakMap === 'function';
31var STORE_OR_NETWORK_DEFAULT = 'store-or-network';
32var pendingQueriesByEnvironment = WEAKMAP_SUPPORTED ? new WeakMap() : new Map();
33
34function preloadQuery(environment, preloadableRequest, variables, options, environmentProviderOptions) {
35 !(environment instanceof Environment) ? process.env.NODE_ENV !== "production" ? invariant(false, 'preloadQuery(): Expected a RelayModernEnvironment') : invariant(false) : void 0;
36
37 var _pendingQueries = pendingQueriesByEnvironment.get(environment);
38
39 if (_pendingQueries == null) {
40 _pendingQueries = new Map();
41 pendingQueriesByEnvironment.set(environment, _pendingQueries);
42 }
43
44 var pendingQueries = _pendingQueries; // store in a const for flow
45
46 var queryEntry = preloadQueryDeduped(environment, pendingQueries, preloadableRequest, variables, options);
47 var source = queryEntry.kind === 'network' ? Observable.create(function (sink) {
48 var subscription = queryEntry.subject.subscribe(sink);
49 return function () {
50 subscription.unsubscribe();
51 cleanup(pendingQueries, queryEntry);
52 };
53 }) : null;
54 return {
55 environment: environment,
56 environmentProviderOptions: environmentProviderOptions,
57 fetchKey: queryEntry.fetchKey,
58 fetchPolicy: queryEntry.fetchPolicy,
59 name: queryEntry.name,
60 source: source,
61 variables: variables
62 };
63}
64
65function preloadQueryDeduped(environment, pendingQueries, preloadableRequest, variables, options) {
66 var _ref;
67
68 var params;
69 var query;
70
71 if (typeof preloadableRequest.getModuleIfRequired === 'function') {
72 var preloadableConcreteRequest = preloadableRequest;
73 params = preloadableConcreteRequest.params;
74 query = preloadableConcreteRequest.getModuleIfRequired();
75 } else {
76 query = getRequest(preloadableRequest);
77 params = query.params;
78 }
79
80 var network = environment.getNetwork();
81 var fetchPolicy = (_ref = options === null || options === void 0 ? void 0 : options.fetchPolicy) !== null && _ref !== void 0 ? _ref : STORE_OR_NETWORK_DEFAULT;
82 var fetchKey = options === null || options === void 0 ? void 0 : options.fetchKey;
83 var cacheKey = "".concat(getRequestIdentifier(params, variables)).concat(fetchKey != null ? "-".concat(fetchKey) : '');
84 var prevQueryEntry = pendingQueries.get(cacheKey);
85 var shouldFulfillFromCache = fetchPolicy === STORE_OR_NETWORK_DEFAULT && query != null && ((prevQueryEntry === null || prevQueryEntry === void 0 ? void 0 : prevQueryEntry.kind) === 'cache' || environment.check(createOperationDescriptor(query, variables).root));
86 var nextQueryEntry;
87
88 if (shouldFulfillFromCache) {
89 nextQueryEntry = prevQueryEntry && prevQueryEntry.kind === 'cache' ? prevQueryEntry : {
90 cacheKey: cacheKey,
91 fetchKey: fetchKey,
92 fetchPolicy: fetchPolicy,
93 kind: 'cache',
94 name: params.name
95 };
96
97 if (ExecutionEnvironment.canUseDOM && prevQueryEntry == null) {
98 setTimeout(function () {
99 // Clear the cache entry after the default timeout
100 // null-check for Flow
101 if (nextQueryEntry != null) {
102 cleanup(pendingQueries, nextQueryEntry);
103 }
104 }, DEFAULT_PREFETCH_TIMEOUT);
105 }
106 } else if (prevQueryEntry == null || prevQueryEntry.kind !== 'network') {
107 // Should fetch but we're not already fetching: fetch!
108 var _environment$__create = environment.__createLogObserver(params, variables),
109 logObserver = _environment$__create[0],
110 logRequestInfo = _environment$__create[1];
111
112 var source = network.execute(params, variables, {}, null, logRequestInfo);
113 var subject = new ReplaySubject();
114 nextQueryEntry = {
115 cacheKey: cacheKey,
116 fetchKey: fetchKey,
117 fetchPolicy: fetchPolicy,
118 kind: 'network',
119 name: params.name,
120 subject: subject,
121 subscription: source["finally"](function () {
122 if (!ExecutionEnvironment.canUseDOM) {
123 return;
124 }
125
126 setTimeout(function () {
127 // Clear the cache entry after the default timeout
128 // null-check for Flow
129 if (nextQueryEntry != null) {
130 cleanup(pendingQueries, nextQueryEntry);
131 }
132 }, DEFAULT_PREFETCH_TIMEOUT);
133 })["do"](logObserver).subscribe({
134 complete: function complete() {
135 subject.complete();
136 },
137 error: function error(_error) {
138 subject.error(_error);
139 },
140 next: function next(response) {
141 subject.next(response);
142 }
143 })
144 };
145 } else {
146 nextQueryEntry = prevQueryEntry;
147 }
148
149 pendingQueries.set(cacheKey, nextQueryEntry);
150 return nextQueryEntry;
151}
152
153function cleanup(pendingQueries, entry) {
154 // Reload the entry by its cache key and only invalidate if its the identical
155 // entry instance. This ensures that if the same query/variables are fetched
156 // successively that a timeout/expiration from an earlier fetch doesn't clear
157 // a subsequent fetch.
158 var currentEntry = pendingQueries.get(entry.cacheKey);
159
160 if (currentEntry != null && currentEntry === entry) {
161 if (currentEntry.kind === 'network') {
162 currentEntry.subscription.unsubscribe();
163 }
164
165 pendingQueries["delete"](currentEntry.cacheKey);
166 }
167}
168
169module.exports = preloadQuery;
\No newline at end of file