UNPKG

40 kBJavaScriptView Raw
1import { __assign, __extends } from "tslib";
2import { invariant } from "../utilities/globals/index.js";
3import { equal } from "@wry/equality";
4import { NetworkStatus, isNetworkRequestInFlight } from "./networkStatus.js";
5import { cloneDeep, compact, getOperationDefinition, Observable, iterateObserversSafely, fixObservableSubclass, getQueryDefinition, } from "../utilities/index.js";
6import { equalByQuery } from "./equalByQuery.js";
7var assign = Object.assign, hasOwnProperty = Object.hasOwnProperty;
8var ObservableQuery = /** @class */ (function (_super) {
9 __extends(ObservableQuery, _super);
10 function ObservableQuery(_a) {
11 var queryManager = _a.queryManager, queryInfo = _a.queryInfo, options = _a.options;
12 var _this = _super.call(this, function (observer) {
13 // Zen Observable has its own error function, so in order to log correctly
14 // we need to provide a custom error callback.
15 try {
16 var subObserver = observer._subscription._observer;
17 if (subObserver && !subObserver.error) {
18 subObserver.error = defaultSubscriptionObserverErrorCallback;
19 }
20 }
21 catch (_a) { }
22 var first = !_this.observers.size;
23 _this.observers.add(observer);
24 // Deliver most recent error or result.
25 var last = _this.last;
26 if (last && last.error) {
27 observer.error && observer.error(last.error);
28 }
29 else if (last && last.result) {
30 observer.next && observer.next(last.result);
31 }
32 // Initiate observation of this query if it hasn't been reported to
33 // the QueryManager yet.
34 if (first) {
35 // Blindly catching here prevents unhandled promise rejections,
36 // and is safe because the ObservableQuery handles this error with
37 // this.observer.error, so we're not just swallowing the error by
38 // ignoring it here.
39 _this.reobserve().catch(function () { });
40 }
41 return function () {
42 if (_this.observers.delete(observer) && !_this.observers.size) {
43 _this.tearDownQuery();
44 }
45 };
46 }) || this;
47 _this.observers = new Set();
48 _this.subscriptions = new Set();
49 // related classes
50 _this.queryInfo = queryInfo;
51 _this.queryManager = queryManager;
52 // active state
53 _this.waitForOwnResult = skipCacheDataFor(options.fetchPolicy);
54 _this.isTornDown = false;
55 var _b = queryManager.defaultOptions.watchQuery, _c = _b === void 0 ? {} : _b, _d = _c.fetchPolicy, defaultFetchPolicy = _d === void 0 ? "cache-first" : _d;
56 var _e = options.fetchPolicy, fetchPolicy = _e === void 0 ? defaultFetchPolicy : _e,
57 // Make sure we don't store "standby" as the initialFetchPolicy.
58 _f = options.initialFetchPolicy,
59 // Make sure we don't store "standby" as the initialFetchPolicy.
60 initialFetchPolicy = _f === void 0 ? fetchPolicy === "standby" ? defaultFetchPolicy : (fetchPolicy) : _f;
61 _this.options = __assign(__assign({}, options), {
62 // Remember the initial options.fetchPolicy so we can revert back to this
63 // policy when variables change. This information can also be specified
64 // (or overridden) by providing options.initialFetchPolicy explicitly.
65 initialFetchPolicy: initialFetchPolicy,
66 // This ensures this.options.fetchPolicy always has a string value, in
67 // case options.fetchPolicy was not provided.
68 fetchPolicy: fetchPolicy });
69 _this.queryId = queryInfo.queryId || queryManager.generateQueryId();
70 var opDef = getOperationDefinition(_this.query);
71 _this.queryName = opDef && opDef.name && opDef.name.value;
72 return _this;
73 }
74 Object.defineProperty(ObservableQuery.prototype, "query", {
75 // The `query` computed property will always reflect the document transformed
76 // by the last run query. `this.options.query` will always reflect the raw
77 // untransformed query to ensure document transforms with runtime conditionals
78 // are run on the original document.
79 get: function () {
80 return this.lastQuery || this.options.query;
81 },
82 enumerable: false,
83 configurable: true
84 });
85 Object.defineProperty(ObservableQuery.prototype, "variables", {
86 // Computed shorthand for this.options.variables, preserved for
87 // backwards compatibility.
88 /**
89 * An object containing the variables that were provided for the query.
90 */
91 get: function () {
92 return this.options.variables;
93 },
94 enumerable: false,
95 configurable: true
96 });
97 ObservableQuery.prototype.result = function () {
98 var _this = this;
99 return new Promise(function (resolve, reject) {
100 // TODO: this code doesn’t actually make sense insofar as the observer
101 // will never exist in this.observers due how zen-observable wraps observables.
102 // https://github.com/zenparsing/zen-observable/blob/master/src/Observable.js#L169
103 var observer = {
104 next: function (result) {
105 resolve(result);
106 // Stop the query within the QueryManager if we can before
107 // this function returns.
108 //
109 // We do this in order to prevent observers piling up within
110 // the QueryManager. Notice that we only fully unsubscribe
111 // from the subscription in a setTimeout(..., 0) call. This call can
112 // actually be handled by the browser at a much later time. If queries
113 // are fired in the meantime, observers that should have been removed
114 // from the QueryManager will continue to fire, causing an unnecessary
115 // performance hit.
116 _this.observers.delete(observer);
117 if (!_this.observers.size) {
118 _this.queryManager.removeQuery(_this.queryId);
119 }
120 setTimeout(function () {
121 subscription.unsubscribe();
122 }, 0);
123 },
124 error: reject,
125 };
126 var subscription = _this.subscribe(observer);
127 });
128 };
129 /** @internal */
130 ObservableQuery.prototype.resetDiff = function () {
131 this.queryInfo.resetDiff();
132 };
133 ObservableQuery.prototype.getCurrentResult = function (saveAsLastResult) {
134 if (saveAsLastResult === void 0) { saveAsLastResult = true; }
135 // Use the last result as long as the variables match this.variables.
136 var lastResult = this.getLastResult(true);
137 var networkStatus = this.queryInfo.networkStatus ||
138 (lastResult && lastResult.networkStatus) ||
139 NetworkStatus.ready;
140 var result = __assign(__assign({}, lastResult), { loading: isNetworkRequestInFlight(networkStatus), networkStatus: networkStatus });
141 var _a = this.options.fetchPolicy, fetchPolicy = _a === void 0 ? "cache-first" : _a;
142 if (
143 // These fetch policies should never deliver data from the cache, unless
144 // redelivering a previously delivered result.
145 skipCacheDataFor(fetchPolicy) ||
146 // If this.options.query has @client(always: true) fields, we cannot
147 // trust diff.result, since it was read from the cache without running
148 // local resolvers (and it's too late to run resolvers now, since we must
149 // return a result synchronously).
150 this.queryManager.getDocumentInfo(this.query).hasForcedResolvers) {
151 // Fall through.
152 }
153 else if (this.waitForOwnResult) {
154 // This would usually be a part of `QueryInfo.getDiff()`.
155 // which we skip in the waitForOwnResult case since we are not
156 // interested in the diff.
157 this.queryInfo["updateWatch"]();
158 }
159 else {
160 var diff = this.queryInfo.getDiff();
161 if (diff.complete || this.options.returnPartialData) {
162 result.data = diff.result;
163 }
164 if (equal(result.data, {})) {
165 result.data = void 0;
166 }
167 if (diff.complete) {
168 // Similar to setting result.partial to false, but taking advantage of the
169 // falsiness of missing fields.
170 delete result.partial;
171 // If the diff is complete, and we're using a FetchPolicy that
172 // terminates after a complete cache read, we can assume the next result
173 // we receive will have NetworkStatus.ready and !loading.
174 if (diff.complete &&
175 result.networkStatus === NetworkStatus.loading &&
176 (fetchPolicy === "cache-first" || fetchPolicy === "cache-only")) {
177 result.networkStatus = NetworkStatus.ready;
178 result.loading = false;
179 }
180 }
181 else {
182 result.partial = true;
183 }
184 if (globalThis.__DEV__ !== false &&
185 !diff.complete &&
186 !this.options.partialRefetch &&
187 !result.loading &&
188 !result.data &&
189 !result.error) {
190 logMissingFieldErrors(diff.missing);
191 }
192 }
193 if (saveAsLastResult) {
194 this.updateLastResult(result);
195 }
196 return result;
197 };
198 // Compares newResult to the snapshot we took of this.lastResult when it was
199 // first received.
200 ObservableQuery.prototype.isDifferentFromLastResult = function (newResult, variables) {
201 if (!this.last) {
202 return true;
203 }
204 var resultIsDifferent = this.queryManager.getDocumentInfo(this.query).hasNonreactiveDirective ?
205 !equalByQuery(this.query, this.last.result, newResult, this.variables)
206 : !equal(this.last.result, newResult);
207 return (resultIsDifferent || (variables && !equal(this.last.variables, variables)));
208 };
209 ObservableQuery.prototype.getLast = function (key, variablesMustMatch) {
210 var last = this.last;
211 if (last &&
212 last[key] &&
213 (!variablesMustMatch || equal(last.variables, this.variables))) {
214 return last[key];
215 }
216 };
217 ObservableQuery.prototype.getLastResult = function (variablesMustMatch) {
218 return this.getLast("result", variablesMustMatch);
219 };
220 ObservableQuery.prototype.getLastError = function (variablesMustMatch) {
221 return this.getLast("error", variablesMustMatch);
222 };
223 ObservableQuery.prototype.resetLastResults = function () {
224 delete this.last;
225 this.isTornDown = false;
226 };
227 ObservableQuery.prototype.resetQueryStoreErrors = function () {
228 this.queryManager.resetErrors(this.queryId);
229 };
230 /**
231 * Update the variables of this observable query, and fetch the new results.
232 * This method should be preferred over `setVariables` in most use cases.
233 *
234 * @param variables - The new set of variables. If there are missing variables,
235 * the previous values of those variables will be used.
236 */
237 ObservableQuery.prototype.refetch = function (variables) {
238 var _a;
239 var reobserveOptions = {
240 // Always disable polling for refetches.
241 pollInterval: 0,
242 };
243 // Unless the provided fetchPolicy always consults the network
244 // (no-cache, network-only, or cache-and-network), override it with
245 // network-only to force the refetch for this fetchQuery call.
246 var fetchPolicy = this.options.fetchPolicy;
247 if (fetchPolicy === "cache-and-network") {
248 reobserveOptions.fetchPolicy = fetchPolicy;
249 }
250 else if (fetchPolicy === "no-cache") {
251 reobserveOptions.fetchPolicy = "no-cache";
252 }
253 else {
254 reobserveOptions.fetchPolicy = "network-only";
255 }
256 if (globalThis.__DEV__ !== false && variables && hasOwnProperty.call(variables, "variables")) {
257 var queryDef = getQueryDefinition(this.query);
258 var vars = queryDef.variableDefinitions;
259 if (!vars || !vars.some(function (v) { return v.variable.name.value === "variables"; })) {
260 globalThis.__DEV__ !== false && invariant.warn(
261 20,
262 variables,
263 ((_a = queryDef.name) === null || _a === void 0 ? void 0 : _a.value) || queryDef
264 );
265 }
266 }
267 if (variables && !equal(this.options.variables, variables)) {
268 // Update the existing options with new variables
269 reobserveOptions.variables = this.options.variables = __assign(__assign({}, this.options.variables), variables);
270 }
271 this.queryInfo.resetLastWrite();
272 return this.reobserve(reobserveOptions, NetworkStatus.refetch);
273 };
274 /**
275 * A function that helps you fetch the next set of results for a [paginated list field](https://www.apollographql.com/docs/react/pagination/core-api/).
276 */
277 ObservableQuery.prototype.fetchMore = function (fetchMoreOptions) {
278 var _this = this;
279 var combinedOptions = __assign(__assign({}, (fetchMoreOptions.query ? fetchMoreOptions : (__assign(__assign(__assign(__assign({}, this.options), { query: this.options.query }), fetchMoreOptions), { variables: __assign(__assign({}, this.options.variables), fetchMoreOptions.variables) })))), {
280 // The fetchMore request goes immediately to the network and does
281 // not automatically write its result to the cache (hence no-cache
282 // instead of network-only), because we allow the caller of
283 // fetchMore to provide an updateQuery callback that determines how
284 // the data gets written to the cache.
285 fetchPolicy: "no-cache" });
286 combinedOptions.query = this.transformDocument(combinedOptions.query);
287 var qid = this.queryManager.generateQueryId();
288 // If a temporary query is passed to `fetchMore`, we don't want to store
289 // it as the last query result since it may be an optimized query for
290 // pagination. We will however run the transforms on the original document
291 // as well as the document passed in `fetchMoreOptions` to ensure the cache
292 // uses the most up-to-date document which may rely on runtime conditionals.
293 this.lastQuery =
294 fetchMoreOptions.query ?
295 this.transformDocument(this.options.query)
296 : combinedOptions.query;
297 // Simulate a loading result for the original query with
298 // result.networkStatus === NetworkStatus.fetchMore.
299 var queryInfo = this.queryInfo;
300 var originalNetworkStatus = queryInfo.networkStatus;
301 queryInfo.networkStatus = NetworkStatus.fetchMore;
302 if (combinedOptions.notifyOnNetworkStatusChange) {
303 this.observe();
304 }
305 var updatedQuerySet = new Set();
306 return this.queryManager
307 .fetchQuery(qid, combinedOptions, NetworkStatus.fetchMore)
308 .then(function (fetchMoreResult) {
309 _this.queryManager.removeQuery(qid);
310 if (queryInfo.networkStatus === NetworkStatus.fetchMore) {
311 queryInfo.networkStatus = originalNetworkStatus;
312 }
313 // Performing this cache update inside a cache.batch transaction ensures
314 // any affected cache.watch watchers are notified at most once about any
315 // updates. Most watchers will be using the QueryInfo class, which
316 // responds to notifications by calling reobserveCacheFirst to deliver
317 // fetchMore cache results back to this ObservableQuery.
318 _this.queryManager.cache.batch({
319 update: function (cache) {
320 var updateQuery = fetchMoreOptions.updateQuery;
321 if (updateQuery) {
322 cache.updateQuery({
323 query: _this.query,
324 variables: _this.variables,
325 returnPartialData: true,
326 optimistic: false,
327 }, function (previous) {
328 return updateQuery(previous, {
329 fetchMoreResult: fetchMoreResult.data,
330 variables: combinedOptions.variables,
331 });
332 });
333 }
334 else {
335 // If we're using a field policy instead of updateQuery, the only
336 // thing we need to do is write the new data to the cache using
337 // combinedOptions.variables (instead of this.variables, which is
338 // what this.updateQuery uses, because it works by abusing the
339 // original field value, keyed by the original variables).
340 cache.writeQuery({
341 query: combinedOptions.query,
342 variables: combinedOptions.variables,
343 data: fetchMoreResult.data,
344 });
345 }
346 },
347 onWatchUpdated: function (watch) {
348 // Record the DocumentNode associated with any watched query whose
349 // data were updated by the cache writes above.
350 updatedQuerySet.add(watch.query);
351 },
352 });
353 return fetchMoreResult;
354 })
355 .finally(function () {
356 // In case the cache writes above did not generate a broadcast
357 // notification (which would have been intercepted by onWatchUpdated),
358 // likely because the written data were the same as what was already in
359 // the cache, we still want fetchMore to deliver its final loading:false
360 // result with the unchanged data.
361 if (!updatedQuerySet.has(_this.query)) {
362 reobserveCacheFirst(_this);
363 }
364 });
365 };
366 // XXX the subscription variables are separate from the query variables.
367 // if you want to update subscription variables, right now you have to do that separately,
368 // and you can only do it by stopping the subscription and then subscribing again with new variables.
369 /**
370 * A function that enables you to execute a [subscription](https://www.apollographql.com/docs/react/data/subscriptions/), usually to subscribe to specific fields that were included in the query.
371 *
372 * This function returns _another_ function that you can call to terminate the subscription.
373 */
374 ObservableQuery.prototype.subscribeToMore = function (options) {
375 var _this = this;
376 var subscription = this.queryManager
377 .startGraphQLSubscription({
378 query: options.document,
379 variables: options.variables,
380 context: options.context,
381 })
382 .subscribe({
383 next: function (subscriptionData) {
384 var updateQuery = options.updateQuery;
385 if (updateQuery) {
386 _this.updateQuery(function (previous, _a) {
387 var variables = _a.variables;
388 return updateQuery(previous, {
389 subscriptionData: subscriptionData,
390 variables: variables,
391 });
392 });
393 }
394 },
395 error: function (err) {
396 if (options.onError) {
397 options.onError(err);
398 return;
399 }
400 globalThis.__DEV__ !== false && invariant.error(21, err);
401 },
402 });
403 this.subscriptions.add(subscription);
404 return function () {
405 if (_this.subscriptions.delete(subscription)) {
406 subscription.unsubscribe();
407 }
408 };
409 };
410 ObservableQuery.prototype.setOptions = function (newOptions) {
411 return this.reobserve(newOptions);
412 };
413 ObservableQuery.prototype.silentSetOptions = function (newOptions) {
414 var mergedOptions = compact(this.options, newOptions || {});
415 assign(this.options, mergedOptions);
416 };
417 /**
418 * Update the variables of this observable query, and fetch the new results
419 * if they've changed. Most users should prefer `refetch` instead of
420 * `setVariables` in order to to be properly notified of results even when
421 * they come from the cache.
422 *
423 * Note: the `next` callback will *not* fire if the variables have not changed
424 * or if the result is coming from cache.
425 *
426 * Note: the promise will return the old results immediately if the variables
427 * have not changed.
428 *
429 * Note: the promise will return null immediately if the query is not active
430 * (there are no subscribers).
431 *
432 * @param variables - The new set of variables. If there are missing variables,
433 * the previous values of those variables will be used.
434 */
435 ObservableQuery.prototype.setVariables = function (variables) {
436 if (equal(this.variables, variables)) {
437 // If we have no observers, then we don't actually want to make a network
438 // request. As soon as someone observes the query, the request will kick
439 // off. For now, we just store any changes. (See #1077)
440 return this.observers.size ? this.result() : Promise.resolve();
441 }
442 this.options.variables = variables;
443 // See comment above
444 if (!this.observers.size) {
445 return Promise.resolve();
446 }
447 return this.reobserve({
448 // Reset options.fetchPolicy to its original value.
449 fetchPolicy: this.options.initialFetchPolicy,
450 variables: variables,
451 }, NetworkStatus.setVariables);
452 };
453 /**
454 * A function that enables you to update the query's cached result without executing a followup GraphQL operation.
455 *
456 * See [using updateQuery and updateFragment](https://www.apollographql.com/docs/react/caching/cache-interaction/#using-updatequery-and-updatefragment) for additional information.
457 */
458 ObservableQuery.prototype.updateQuery = function (mapFn) {
459 var queryManager = this.queryManager;
460 var result = queryManager.cache.diff({
461 query: this.options.query,
462 variables: this.variables,
463 returnPartialData: true,
464 optimistic: false,
465 }).result;
466 var newResult = mapFn(result, {
467 variables: this.variables,
468 });
469 if (newResult) {
470 queryManager.cache.writeQuery({
471 query: this.options.query,
472 data: newResult,
473 variables: this.variables,
474 });
475 queryManager.broadcastQueries();
476 }
477 };
478 /**
479 * A function that instructs the query to begin re-executing at a specified interval (in milliseconds).
480 */
481 ObservableQuery.prototype.startPolling = function (pollInterval) {
482 this.options.pollInterval = pollInterval;
483 this.updatePolling();
484 };
485 /**
486 * A function that instructs the query to stop polling after a previous call to `startPolling`.
487 */
488 ObservableQuery.prototype.stopPolling = function () {
489 this.options.pollInterval = 0;
490 this.updatePolling();
491 };
492 // Update options.fetchPolicy according to options.nextFetchPolicy.
493 ObservableQuery.prototype.applyNextFetchPolicy = function (reason,
494 // It's possible to use this method to apply options.nextFetchPolicy to
495 // options.fetchPolicy even if options !== this.options, though that happens
496 // most often when the options are temporary, used for only one request and
497 // then thrown away, so nextFetchPolicy may not end up mattering.
498 options) {
499 if (options.nextFetchPolicy) {
500 var _a = options.fetchPolicy, fetchPolicy = _a === void 0 ? "cache-first" : _a, _b = options.initialFetchPolicy, initialFetchPolicy = _b === void 0 ? fetchPolicy : _b;
501 if (fetchPolicy === "standby") {
502 // Do nothing, leaving options.fetchPolicy unchanged.
503 }
504 else if (typeof options.nextFetchPolicy === "function") {
505 // When someone chooses "cache-and-network" or "network-only" as their
506 // initial FetchPolicy, they often do not want future cache updates to
507 // trigger unconditional network requests, which is what repeatedly
508 // applying the "cache-and-network" or "network-only" policies would
509 // seem to imply. Instead, when the cache reports an update after the
510 // initial network request, it may be desirable for subsequent network
511 // requests to be triggered only if the cache result is incomplete. To
512 // that end, the options.nextFetchPolicy option provides an easy way to
513 // update options.fetchPolicy after the initial network request, without
514 // having to call observableQuery.setOptions.
515 options.fetchPolicy = options.nextFetchPolicy(fetchPolicy, {
516 reason: reason,
517 options: options,
518 observable: this,
519 initialFetchPolicy: initialFetchPolicy,
520 });
521 }
522 else if (reason === "variables-changed") {
523 options.fetchPolicy = initialFetchPolicy;
524 }
525 else {
526 options.fetchPolicy = options.nextFetchPolicy;
527 }
528 }
529 return options.fetchPolicy;
530 };
531 ObservableQuery.prototype.fetch = function (options, newNetworkStatus, query) {
532 // TODO Make sure we update the networkStatus (and infer fetchVariables)
533 // before actually committing to the fetch.
534 this.queryManager.setObservableQuery(this);
535 return this.queryManager["fetchConcastWithInfo"](this.queryId, options, newNetworkStatus, query);
536 };
537 // Turns polling on or off based on this.options.pollInterval.
538 ObservableQuery.prototype.updatePolling = function () {
539 var _this = this;
540 // Avoid polling in SSR mode
541 if (this.queryManager.ssrMode) {
542 return;
543 }
544 var _a = this, pollingInfo = _a.pollingInfo, pollInterval = _a.options.pollInterval;
545 if (!pollInterval) {
546 if (pollingInfo) {
547 clearTimeout(pollingInfo.timeout);
548 delete this.pollingInfo;
549 }
550 return;
551 }
552 if (pollingInfo && pollingInfo.interval === pollInterval) {
553 return;
554 }
555 invariant(pollInterval, 22);
556 var info = pollingInfo || (this.pollingInfo = {});
557 info.interval = pollInterval;
558 var maybeFetch = function () {
559 var _a, _b;
560 if (_this.pollingInfo) {
561 if (!isNetworkRequestInFlight(_this.queryInfo.networkStatus) &&
562 !((_b = (_a = _this.options).skipPollAttempt) === null || _b === void 0 ? void 0 : _b.call(_a))) {
563 _this.reobserve({
564 // Most fetchPolicy options don't make sense to use in a polling context, as
565 // users wouldn't want to be polling the cache directly. However, network-only and
566 // no-cache are both useful for when the user wants to control whether or not the
567 // polled results are written to the cache.
568 fetchPolicy: _this.options.initialFetchPolicy === "no-cache" ?
569 "no-cache"
570 : "network-only",
571 }, NetworkStatus.poll).then(poll, poll);
572 }
573 else {
574 poll();
575 }
576 }
577 };
578 var poll = function () {
579 var info = _this.pollingInfo;
580 if (info) {
581 clearTimeout(info.timeout);
582 info.timeout = setTimeout(maybeFetch, info.interval);
583 }
584 };
585 poll();
586 };
587 ObservableQuery.prototype.updateLastResult = function (newResult, variables) {
588 if (variables === void 0) { variables = this.variables; }
589 var error = this.getLastError();
590 // Preserve this.last.error unless the variables have changed.
591 if (error && this.last && !equal(variables, this.last.variables)) {
592 error = void 0;
593 }
594 return (this.last = __assign({ result: this.queryManager.assumeImmutableResults ?
595 newResult
596 : cloneDeep(newResult), variables: variables }, (error ? { error: error } : null)));
597 };
598 ObservableQuery.prototype.reobserveAsConcast = function (newOptions, newNetworkStatus) {
599 var _this = this;
600 this.isTornDown = false;
601 var useDisposableConcast =
602 // Refetching uses a disposable Concast to allow refetches using different
603 // options/variables, without permanently altering the options of the
604 // original ObservableQuery.
605 newNetworkStatus === NetworkStatus.refetch ||
606 // The fetchMore method does not actually call the reobserve method, but,
607 // if it did, it would definitely use a disposable Concast.
608 newNetworkStatus === NetworkStatus.fetchMore ||
609 // Polling uses a disposable Concast so the polling options (which force
610 // fetchPolicy to be "network-only" or "no-cache") won't override the original options.
611 newNetworkStatus === NetworkStatus.poll;
612 // Save the old variables, since Object.assign may modify them below.
613 var oldVariables = this.options.variables;
614 var oldFetchPolicy = this.options.fetchPolicy;
615 var mergedOptions = compact(this.options, newOptions || {});
616 var options = useDisposableConcast ?
617 // Disposable Concast fetches receive a shallow copy of this.options
618 // (merged with newOptions), leaving this.options unmodified.
619 mergedOptions
620 : assign(this.options, mergedOptions);
621 // Don't update options.query with the transformed query to avoid
622 // overwriting this.options.query when we aren't using a disposable concast.
623 // We want to ensure we can re-run the custom document transforms the next
624 // time a request is made against the original query.
625 var query = this.transformDocument(options.query);
626 this.lastQuery = query;
627 if (!useDisposableConcast) {
628 // We can skip calling updatePolling if we're not changing this.options.
629 this.updatePolling();
630 // Reset options.fetchPolicy to its original value when variables change,
631 // unless a new fetchPolicy was provided by newOptions.
632 if (newOptions &&
633 newOptions.variables &&
634 !equal(newOptions.variables, oldVariables) &&
635 // Don't mess with the fetchPolicy if it's currently "standby".
636 options.fetchPolicy !== "standby" &&
637 // If we're changing the fetchPolicy anyway, don't try to change it here
638 // using applyNextFetchPolicy. The explicit options.fetchPolicy wins.
639 options.fetchPolicy === oldFetchPolicy) {
640 this.applyNextFetchPolicy("variables-changed", options);
641 if (newNetworkStatus === void 0) {
642 newNetworkStatus = NetworkStatus.setVariables;
643 }
644 }
645 }
646 this.waitForOwnResult && (this.waitForOwnResult = skipCacheDataFor(options.fetchPolicy));
647 var finishWaitingForOwnResult = function () {
648 if (_this.concast === concast) {
649 _this.waitForOwnResult = false;
650 }
651 };
652 var variables = options.variables && __assign({}, options.variables);
653 var _a = this.fetch(options, newNetworkStatus, query), concast = _a.concast, fromLink = _a.fromLink;
654 var observer = {
655 next: function (result) {
656 if (equal(_this.variables, variables)) {
657 finishWaitingForOwnResult();
658 _this.reportResult(result, variables);
659 }
660 },
661 error: function (error) {
662 if (equal(_this.variables, variables)) {
663 finishWaitingForOwnResult();
664 _this.reportError(error, variables);
665 }
666 },
667 };
668 if (!useDisposableConcast && (fromLink || !this.concast)) {
669 // We use the {add,remove}Observer methods directly to avoid wrapping
670 // observer with an unnecessary SubscriptionObserver object.
671 if (this.concast && this.observer) {
672 this.concast.removeObserver(this.observer);
673 }
674 this.concast = concast;
675 this.observer = observer;
676 }
677 concast.addObserver(observer);
678 return concast;
679 };
680 ObservableQuery.prototype.reobserve = function (newOptions, newNetworkStatus) {
681 return this.reobserveAsConcast(newOptions, newNetworkStatus)
682 .promise;
683 };
684 ObservableQuery.prototype.resubscribeAfterError = function () {
685 var args = [];
686 for (var _i = 0; _i < arguments.length; _i++) {
687 args[_i] = arguments[_i];
688 }
689 // If `lastError` is set in the current when the subscription is re-created,
690 // the subscription will immediately receive the error, which will
691 // cause it to terminate again. To avoid this, we first clear
692 // the last error/result from the `observableQuery` before re-starting
693 // the subscription, and restore the last value afterwards so that the
694 // subscription has a chance to stay open.
695 var last = this.last;
696 this.resetLastResults();
697 var subscription = this.subscribe.apply(this, args);
698 this.last = last;
699 return subscription;
700 };
701 // (Re)deliver the current result to this.observers without applying fetch
702 // policies or making network requests.
703 ObservableQuery.prototype.observe = function () {
704 this.reportResult(
705 // Passing false is important so that this.getCurrentResult doesn't
706 // save the fetchMore result as this.lastResult, causing it to be
707 // ignored due to the this.isDifferentFromLastResult check in
708 // this.reportResult.
709 this.getCurrentResult(false), this.variables);
710 };
711 ObservableQuery.prototype.reportResult = function (result, variables) {
712 var lastError = this.getLastError();
713 var isDifferent = this.isDifferentFromLastResult(result, variables);
714 // Update the last result even when isDifferentFromLastResult returns false,
715 // because the query may be using the @nonreactive directive, and we want to
716 // save the the latest version of any nonreactive subtrees (in case
717 // getCurrentResult is called), even though we skip broadcasting changes.
718 if (lastError || !result.partial || this.options.returnPartialData) {
719 this.updateLastResult(result, variables);
720 }
721 if (lastError || isDifferent) {
722 iterateObserversSafely(this.observers, "next", result);
723 }
724 };
725 ObservableQuery.prototype.reportError = function (error, variables) {
726 // Since we don't get the current result on errors, only the error, we
727 // must mirror the updates that occur in QueryStore.markQueryError here
728 var errorResult = __assign(__assign({}, this.getLastResult()), { error: error, errors: error.graphQLErrors, networkStatus: NetworkStatus.error, loading: false });
729 this.updateLastResult(errorResult, variables);
730 iterateObserversSafely(this.observers, "error", (this.last.error = error));
731 };
732 ObservableQuery.prototype.hasObservers = function () {
733 return this.observers.size > 0;
734 };
735 ObservableQuery.prototype.tearDownQuery = function () {
736 if (this.isTornDown)
737 return;
738 if (this.concast && this.observer) {
739 this.concast.removeObserver(this.observer);
740 delete this.concast;
741 delete this.observer;
742 }
743 this.stopPolling();
744 // stop all active GraphQL subscriptions
745 this.subscriptions.forEach(function (sub) { return sub.unsubscribe(); });
746 this.subscriptions.clear();
747 this.queryManager.stopQuery(this.queryId);
748 this.observers.clear();
749 this.isTornDown = true;
750 };
751 ObservableQuery.prototype.transformDocument = function (document) {
752 return this.queryManager.transform(document);
753 };
754 return ObservableQuery;
755}(Observable));
756export { ObservableQuery };
757// Necessary because the ObservableQuery constructor has a different
758// signature than the Observable constructor.
759fixObservableSubclass(ObservableQuery);
760// Reobserve with fetchPolicy effectively set to "cache-first", triggering
761// delivery of any new data from the cache, possibly falling back to the network
762// if any cache data are missing. This allows _complete_ cache results to be
763// delivered without also kicking off unnecessary network requests when
764// this.options.fetchPolicy is "cache-and-network" or "network-only". When
765// this.options.fetchPolicy is any other policy ("cache-first", "cache-only",
766// "standby", or "no-cache"), we call this.reobserve() as usual.
767export function reobserveCacheFirst(obsQuery) {
768 var _a = obsQuery.options, fetchPolicy = _a.fetchPolicy, nextFetchPolicy = _a.nextFetchPolicy;
769 if (fetchPolicy === "cache-and-network" || fetchPolicy === "network-only") {
770 return obsQuery.reobserve({
771 fetchPolicy: "cache-first",
772 // Use a temporary nextFetchPolicy function that replaces itself with the
773 // previous nextFetchPolicy value and returns the original fetchPolicy.
774 nextFetchPolicy: function (currentFetchPolicy, context) {
775 // Replace this nextFetchPolicy function in the options object with the
776 // original this.options.nextFetchPolicy value.
777 this.nextFetchPolicy = nextFetchPolicy;
778 // If the original nextFetchPolicy value was a function, give it a
779 // chance to decide what happens here.
780 if (typeof this.nextFetchPolicy === "function") {
781 return this.nextFetchPolicy(currentFetchPolicy, context);
782 }
783 // Otherwise go back to the original this.options.fetchPolicy.
784 return fetchPolicy;
785 },
786 });
787 }
788 return obsQuery.reobserve();
789}
790function defaultSubscriptionObserverErrorCallback(error) {
791 globalThis.__DEV__ !== false && invariant.error(23, error.message, error.stack);
792}
793export function logMissingFieldErrors(missing) {
794 if (globalThis.__DEV__ !== false && missing) {
795 globalThis.__DEV__ !== false && invariant.debug(24, missing);
796 }
797}
798function skipCacheDataFor(fetchPolicy /* `undefined` would mean `"cache-first"` */) {
799 return (fetchPolicy === "network-only" ||
800 fetchPolicy === "no-cache" ||
801 fetchPolicy === "standby");
802}
803//# sourceMappingURL=ObservableQuery.js.map
\No newline at end of file