Initial Sample.

This commit is contained in:
2024-06-03 20:23:50 +05:30
parent ef2b65f673
commit 5269ec3c66
2575 changed files with 282312 additions and 0 deletions

View File

@@ -0,0 +1,100 @@
import type { ApolloQueryResult, ObservableQuery, OperationVariables, WatchQueryOptions } from "../../../core/index.js";
import type { PromiseWithState } from "../../../utilities/index.js";
import type { QueryKey } from "./types.js";
type QueryRefPromise<TData> = PromiseWithState<ApolloQueryResult<TData>>;
type Listener<TData> = (promise: QueryRefPromise<TData>) => void;
type FetchMoreOptions<TData> = Parameters<ObservableQuery<TData>["fetchMore"]>[0];
declare const QUERY_REFERENCE_SYMBOL: unique symbol;
declare const PROMISE_SYMBOL: unique symbol;
/**
* A `QueryReference` is an opaque object returned by {@link useBackgroundQuery}.
* A child component reading the `QueryReference` via {@link useReadQuery} will
* suspend until the promise resolves.
*/
export interface QueryReference<TData = unknown, TVariables = unknown> {
/** @internal */
readonly [QUERY_REFERENCE_SYMBOL]: InternalQueryReference<TData>;
/** @internal */
[PROMISE_SYMBOL]: QueryRefPromise<TData>;
/**
* A function that returns a promise that resolves when the query has finished
* loading. The promise resolves with the `QueryReference` itself.
*
* @remarks
* This method is useful for preloading queries in data loading routers, such
* as [React Router](https://reactrouter.com/en/main) or [TanStack Router](https://tanstack.com/router),
* to prevent routes from transitioning until the query has finished loading.
* `data` is not exposed on the promise to discourage using the data in
* `loader` functions and exposing it to your route components. Instead, we
* prefer you rely on `useReadQuery` to access the data to ensure your
* component can rerender with cache updates. If you need to access raw query
* data, use `client.query()` directly.
*
* @example
* Here's an example using React Router's `loader` function:
* ```ts
* import { createQueryPreloader } from "@apollo/client";
*
* const preloadQuery = createQueryPreloader(client);
*
* export async function loader() {
* const queryRef = preloadQuery(GET_DOGS_QUERY);
*
* return queryRef.toPromise();
* }
*
* export function RouteComponent() {
* const queryRef = useLoaderData();
* const { data } = useReadQuery(queryRef);
*
* // ...
* }
* ```
*
* @alpha
*/
toPromise(): Promise<QueryReference<TData, TVariables>>;
}
interface InternalQueryReferenceOptions {
onDispose?: () => void;
autoDisposeTimeoutMs?: number;
}
export declare function wrapQueryRef<TData, TVariables extends OperationVariables>(internalQueryRef: InternalQueryReference<TData>): QueryReference<TData, TVariables>;
export declare function getWrappedPromise<TData>(queryRef: QueryReference<TData, any>): QueryRefPromise<TData>;
export declare function unwrapQueryRef<TData>(queryRef: QueryReference<TData>): InternalQueryReference<TData>;
export declare function updateWrappedQueryRef<TData>(queryRef: QueryReference<TData>, promise: QueryRefPromise<TData>): void;
declare const OBSERVED_CHANGED_OPTIONS: readonly ["canonizeResults", "context", "errorPolicy", "fetchPolicy", "refetchWritePolicy", "returnPartialData"];
type ObservedOptions = Pick<WatchQueryOptions, (typeof OBSERVED_CHANGED_OPTIONS)[number]>;
export declare class InternalQueryReference<TData = unknown> {
result: ApolloQueryResult<TData>;
readonly key: QueryKey;
readonly observable: ObservableQuery<TData>;
promise: QueryRefPromise<TData>;
private subscription;
private listeners;
private autoDisposeTimeoutId?;
private resolve;
private reject;
private references;
constructor(observable: ObservableQuery<TData, any>, options: InternalQueryReferenceOptions);
get disposed(): boolean;
get watchQueryOptions(): WatchQueryOptions<OperationVariables, TData>;
reinitialize(): void;
retain(): () => void;
didChangeOptions(watchQueryOptions: ObservedOptions): boolean;
applyOptions(watchQueryOptions: ObservedOptions): QueryRefPromise<TData>;
listen(listener: Listener<TData>): () => void;
refetch(variables: OperationVariables | undefined): Promise<ApolloQueryResult<TData>>;
fetchMore(options: FetchMoreOptions<TData>): Promise<ApolloQueryResult<TData>>;
private dispose;
private onDispose;
private handleNext;
private handleError;
private deliver;
private initiateFetch;
private subscribeToQuery;
private setResult;
private createPendingPromise;
}
export {};
//# sourceMappingURL=QueryReference.d.ts.map

View File

@@ -0,0 +1,279 @@
import { __assign } from "tslib";
import { equal } from "@wry/equality";
import { createFulfilledPromise, createRejectedPromise, } from "../../../utilities/index.js";
import { wrapPromiseWithState } from "../../../utilities/index.js";
var QUERY_REFERENCE_SYMBOL = Symbol();
var PROMISE_SYMBOL = Symbol();
export function wrapQueryRef(internalQueryRef) {
var _a;
var ref = (_a = {
toPromise: function () {
// We avoid resolving this promise with the query data because we want to
// discourage using the server data directly from the queryRef. Instead,
// the data should be accessed through `useReadQuery`. When the server
// data is needed, its better to use `client.query()` directly.
//
// Here we resolve with the ref itself to make using this in React Router
// or TanStack Router `loader` functions a bit more ergonomic e.g.
//
// function loader() {
// return { queryRef: await preloadQuery(query).toPromise() }
// }
return getWrappedPromise(ref).then(function () { return ref; });
}
},
_a[QUERY_REFERENCE_SYMBOL] = internalQueryRef,
_a[PROMISE_SYMBOL] = internalQueryRef.promise,
_a);
return ref;
}
export function getWrappedPromise(queryRef) {
var internalQueryRef = unwrapQueryRef(queryRef);
return internalQueryRef.promise.status === "fulfilled" ?
internalQueryRef.promise
: queryRef[PROMISE_SYMBOL];
}
export function unwrapQueryRef(queryRef) {
return queryRef[QUERY_REFERENCE_SYMBOL];
}
export function updateWrappedQueryRef(queryRef, promise) {
queryRef[PROMISE_SYMBOL] = promise;
}
var OBSERVED_CHANGED_OPTIONS = [
"canonizeResults",
"context",
"errorPolicy",
"fetchPolicy",
"refetchWritePolicy",
"returnPartialData",
];
var InternalQueryReference = /** @class */ (function () {
function InternalQueryReference(observable, options) {
var _this = this;
this.key = {};
this.listeners = new Set();
this.references = 0;
this.handleNext = this.handleNext.bind(this);
this.handleError = this.handleError.bind(this);
this.dispose = this.dispose.bind(this);
this.observable = observable;
if (options.onDispose) {
this.onDispose = options.onDispose;
}
this.setResult();
this.subscribeToQuery();
// Start a timer that will automatically dispose of the query if the
// suspended resource does not use this queryRef in the given time. This
// helps prevent memory leaks when a component has unmounted before the
// query has finished loading.
var startDisposeTimer = function () {
var _a;
if (!_this.references) {
_this.autoDisposeTimeoutId = setTimeout(_this.dispose, (_a = options.autoDisposeTimeoutMs) !== null && _a !== void 0 ? _a : 30000);
}
};
// We wait until the request has settled to ensure we don't dispose of the
// query ref before the request finishes, otherwise we would leave the
// promise in a pending state rendering the suspense boundary indefinitely.
this.promise.then(startDisposeTimer, startDisposeTimer);
}
Object.defineProperty(InternalQueryReference.prototype, "disposed", {
get: function () {
return this.subscription.closed;
},
enumerable: false,
configurable: true
});
Object.defineProperty(InternalQueryReference.prototype, "watchQueryOptions", {
get: function () {
return this.observable.options;
},
enumerable: false,
configurable: true
});
InternalQueryReference.prototype.reinitialize = function () {
var observable = this.observable;
var originalFetchPolicy = this.watchQueryOptions.fetchPolicy;
try {
if (originalFetchPolicy !== "no-cache") {
observable.resetLastResults();
observable.silentSetOptions({ fetchPolicy: "cache-first" });
}
else {
observable.silentSetOptions({ fetchPolicy: "standby" });
}
this.subscribeToQuery();
if (originalFetchPolicy === "no-cache") {
return;
}
observable.resetDiff();
this.setResult();
}
finally {
observable.silentSetOptions({ fetchPolicy: originalFetchPolicy });
}
};
InternalQueryReference.prototype.retain = function () {
var _this = this;
this.references++;
clearTimeout(this.autoDisposeTimeoutId);
var disposed = false;
return function () {
if (disposed) {
return;
}
disposed = true;
_this.references--;
// Wait before fully disposing in case the app is running in strict mode.
setTimeout(function () {
if (!_this.references) {
_this.dispose();
}
});
};
};
InternalQueryReference.prototype.didChangeOptions = function (watchQueryOptions) {
var _this = this;
return OBSERVED_CHANGED_OPTIONS.some(function (option) {
return !equal(_this.watchQueryOptions[option], watchQueryOptions[option]);
});
};
InternalQueryReference.prototype.applyOptions = function (watchQueryOptions) {
var _a = this.watchQueryOptions, currentFetchPolicy = _a.fetchPolicy, currentCanonizeResults = _a.canonizeResults;
// "standby" is used when `skip` is set to `true`. Detect when we've
// enabled the query (i.e. `skip` is `false`) to execute a network request.
if (currentFetchPolicy === "standby" &&
currentFetchPolicy !== watchQueryOptions.fetchPolicy) {
this.initiateFetch(this.observable.reobserve(watchQueryOptions));
}
else {
this.observable.silentSetOptions(watchQueryOptions);
if (currentCanonizeResults !== watchQueryOptions.canonizeResults) {
this.result = __assign(__assign({}, this.result), this.observable.getCurrentResult());
this.promise = createFulfilledPromise(this.result);
}
}
return this.promise;
};
InternalQueryReference.prototype.listen = function (listener) {
var _this = this;
this.listeners.add(listener);
return function () {
_this.listeners.delete(listener);
};
};
InternalQueryReference.prototype.refetch = function (variables) {
return this.initiateFetch(this.observable.refetch(variables));
};
InternalQueryReference.prototype.fetchMore = function (options) {
return this.initiateFetch(this.observable.fetchMore(options));
};
InternalQueryReference.prototype.dispose = function () {
this.subscription.unsubscribe();
this.onDispose();
};
InternalQueryReference.prototype.onDispose = function () {
// noop. overridable by options
};
InternalQueryReference.prototype.handleNext = function (result) {
var _a;
switch (this.promise.status) {
case "pending": {
// Maintain the last successful `data` value if the next result does not
// have one.
if (result.data === void 0) {
result.data = this.result.data;
}
this.result = result;
(_a = this.resolve) === null || _a === void 0 ? void 0 : _a.call(this, result);
break;
}
default: {
// This occurs when switching to a result that is fully cached when this
// class is instantiated. ObservableQuery will run reobserve when
// subscribing, which delivers a result from the cache.
if (result.data === this.result.data &&
result.networkStatus === this.result.networkStatus) {
return;
}
// Maintain the last successful `data` value if the next result does not
// have one.
if (result.data === void 0) {
result.data = this.result.data;
}
this.result = result;
this.promise = createFulfilledPromise(result);
this.deliver(this.promise);
break;
}
}
};
InternalQueryReference.prototype.handleError = function (error) {
var _a;
this.subscription.unsubscribe();
this.subscription = this.observable.resubscribeAfterError(this.handleNext, this.handleError);
switch (this.promise.status) {
case "pending": {
(_a = this.reject) === null || _a === void 0 ? void 0 : _a.call(this, error);
break;
}
default: {
this.promise = createRejectedPromise(error);
this.deliver(this.promise);
}
}
};
InternalQueryReference.prototype.deliver = function (promise) {
this.listeners.forEach(function (listener) { return listener(promise); });
};
InternalQueryReference.prototype.initiateFetch = function (returnedPromise) {
var _this = this;
this.promise = this.createPendingPromise();
this.promise.catch(function () { });
// If the data returned from the fetch is deeply equal to the data already
// in the cache, `handleNext` will not be triggered leaving the promise we
// created in a pending state forever. To avoid this situtation, we attempt
// to resolve the promise if `handleNext` hasn't been run to ensure the
// promise is resolved correctly.
returnedPromise
.then(function (result) {
var _a;
if (_this.promise.status === "pending") {
_this.result = result;
(_a = _this.resolve) === null || _a === void 0 ? void 0 : _a.call(_this, result);
}
})
.catch(function () { });
return returnedPromise;
};
InternalQueryReference.prototype.subscribeToQuery = function () {
var _this = this;
this.subscription = this.observable
.filter(function (result) { return !equal(result.data, {}) && !equal(result, _this.result); })
.subscribe(this.handleNext, this.handleError);
};
InternalQueryReference.prototype.setResult = function () {
// Don't save this result as last result to prevent delivery of last result
// when first subscribing
var result = this.observable.getCurrentResult(false);
if (equal(result, this.result)) {
return;
}
this.result = result;
this.promise =
(result.data &&
(!result.partial || this.watchQueryOptions.returnPartialData)) ?
createFulfilledPromise(result)
: this.createPendingPromise();
};
InternalQueryReference.prototype.createPendingPromise = function () {
var _this = this;
return wrapPromiseWithState(new Promise(function (resolve, reject) {
_this.resolve = resolve;
_this.reject = reject;
}));
};
return InternalQueryReference;
}());
export { InternalQueryReference };
//# sourceMappingURL=QueryReference.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
import type { ObservableQuery } from "../../../core/index.js";
import { InternalQueryReference } from "./QueryReference.js";
import type { CacheKey } from "./types.js";
export interface SuspenseCacheOptions {
/**
* Specifies the amount of time, in milliseconds, the suspense cache will wait
* for a suspended component to read from the suspense cache before it
* automatically disposes of the query. This prevents memory leaks when a
* component unmounts before a suspended resource finishes loading. Increase
* the timeout if your queries take longer than than the specified time to
* prevent your queries from suspending over and over.
*
* Defaults to 30 seconds.
*/
autoDisposeTimeoutMs?: number;
}
export declare class SuspenseCache {
private queryRefs;
private options;
constructor(options?: SuspenseCacheOptions);
getQueryRef<TData = any>(cacheKey: CacheKey, createObservable: () => ObservableQuery<TData>): InternalQueryReference<TData>;
}
//# sourceMappingURL=SuspenseCache.d.ts.map

View File

@@ -0,0 +1,25 @@
import { Trie } from "@wry/trie";
import { canUseWeakMap } from "../../../utilities/index.js";
import { InternalQueryReference } from "./QueryReference.js";
var SuspenseCache = /** @class */ (function () {
function SuspenseCache(options) {
if (options === void 0) { options = Object.create(null); }
this.queryRefs = new Trie(canUseWeakMap);
this.options = options;
}
SuspenseCache.prototype.getQueryRef = function (cacheKey, createObservable) {
var ref = this.queryRefs.lookupArray(cacheKey);
if (!ref.current) {
ref.current = new InternalQueryReference(createObservable(), {
autoDisposeTimeoutMs: this.options.autoDisposeTimeoutMs,
onDispose: function () {
delete ref.current;
},
});
}
return ref.current;
};
return SuspenseCache;
}());
export { SuspenseCache };
//# sourceMappingURL=SuspenseCache.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"SuspenseCache.js","sourceRoot":"","sources":["../../../../src/react/internal/cache/SuspenseCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAiB7D;IAME,uBAAY,OAAmD;QAAnD,wBAAA,EAAA,UAAgC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;QALvD,cAAS,GAAG,IAAI,IAAI,CAC1B,aAAa,CACd,CAAC;QAIA,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,mCAAW,GAAX,UACE,QAAkB,EAClB,gBAA8C;QAE9C,IAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,QAAQ,CAE9C,CAAC;QAEF,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;YACjB,GAAG,CAAC,OAAO,GAAG,IAAI,sBAAsB,CAAC,gBAAgB,EAAE,EAAE;gBAC3D,oBAAoB,EAAE,IAAI,CAAC,OAAO,CAAC,oBAAoB;gBACvD,SAAS,EAAE;oBACT,OAAO,GAAG,CAAC,OAAO,CAAC;gBACrB,CAAC;aACF,CAAC,CAAC;QACL,CAAC;QAED,OAAO,GAAG,CAAC,OAAO,CAAC;IACrB,CAAC;IACH,oBAAC;AAAD,CAAC,AA7BD,IA6BC","sourcesContent":["import { Trie } from \"@wry/trie\";\nimport type { ObservableQuery } from \"../../../core/index.js\";\nimport { canUseWeakMap } from \"../../../utilities/index.js\";\nimport { InternalQueryReference } from \"./QueryReference.js\";\nimport type { CacheKey } from \"./types.js\";\n\nexport interface SuspenseCacheOptions {\n /**\n * Specifies the amount of time, in milliseconds, the suspense cache will wait\n * for a suspended component to read from the suspense cache before it\n * automatically disposes of the query. This prevents memory leaks when a\n * component unmounts before a suspended resource finishes loading. Increase\n * the timeout if your queries take longer than than the specified time to\n * prevent your queries from suspending over and over.\n *\n * Defaults to 30 seconds.\n */\n autoDisposeTimeoutMs?: number;\n}\n\nexport class SuspenseCache {\n private queryRefs = new Trie<{ current?: InternalQueryReference }>(\n canUseWeakMap\n );\n private options: SuspenseCacheOptions;\n\n constructor(options: SuspenseCacheOptions = Object.create(null)) {\n this.options = options;\n }\n\n getQueryRef<TData = any>(\n cacheKey: CacheKey,\n createObservable: () => ObservableQuery<TData>\n ) {\n const ref = this.queryRefs.lookupArray(cacheKey) as {\n current?: InternalQueryReference<TData>;\n };\n\n if (!ref.current) {\n ref.current = new InternalQueryReference(createObservable(), {\n autoDisposeTimeoutMs: this.options.autoDisposeTimeoutMs,\n onDispose: () => {\n delete ref.current;\n },\n });\n }\n\n return ref.current;\n }\n}\n"]}

View File

@@ -0,0 +1,16 @@
import type { SuspenseCacheOptions } from "../index.js";
import { SuspenseCache } from "./SuspenseCache.js";
import type { ApolloClient } from "../../../core/ApolloClient.js";
declare module "../../../core/ApolloClient.js" {
interface DefaultOptions {
react?: {
suspense?: Readonly<SuspenseCacheOptions>;
};
}
}
declare const suspenseCacheSymbol: unique symbol;
export declare function getSuspenseCache(client: ApolloClient<object> & {
[suspenseCacheSymbol]?: SuspenseCache;
}): SuspenseCache;
export {};
//# sourceMappingURL=getSuspenseCache.d.ts.map

View File

@@ -0,0 +1,10 @@
import { SuspenseCache } from "./SuspenseCache.js";
var suspenseCacheSymbol = Symbol.for("apollo.suspenseCache");
export function getSuspenseCache(client) {
var _a;
if (!client[suspenseCacheSymbol]) {
client[suspenseCacheSymbol] = new SuspenseCache((_a = client.defaultOptions.react) === null || _a === void 0 ? void 0 : _a.suspense);
}
return client[suspenseCacheSymbol];
}
//# sourceMappingURL=getSuspenseCache.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"getSuspenseCache.js","sourceRoot":"","sources":["../../../../src/react/internal/cache/getSuspenseCache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAWnD,IAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;AAE/D,MAAM,UAAU,gBAAgB,CAC9B,MAEC;;IAED,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,EAAE,CAAC;QACjC,MAAM,CAAC,mBAAmB,CAAC,GAAG,IAAI,aAAa,CAC7C,MAAA,MAAM,CAAC,cAAc,CAAC,KAAK,0CAAE,QAAQ,CACtC,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC,mBAAmB,CAAC,CAAC;AACrC,CAAC","sourcesContent":["import type { SuspenseCacheOptions } from \"../index.js\";\nimport { SuspenseCache } from \"./SuspenseCache.js\";\nimport type { ApolloClient } from \"../../../core/ApolloClient.js\";\n\ndeclare module \"../../../core/ApolloClient.js\" {\n interface DefaultOptions {\n react?: {\n suspense?: Readonly<SuspenseCacheOptions>;\n };\n }\n}\n\nconst suspenseCacheSymbol = Symbol.for(\"apollo.suspenseCache\");\n\nexport function getSuspenseCache(\n client: ApolloClient<object> & {\n [suspenseCacheSymbol]?: SuspenseCache;\n }\n) {\n if (!client[suspenseCacheSymbol]) {\n client[suspenseCacheSymbol] = new SuspenseCache(\n client.defaultOptions.react?.suspense\n );\n }\n\n return client[suspenseCacheSymbol];\n}\n"]}

View File

@@ -0,0 +1,10 @@
import type { DocumentNode } from "graphql";
export type CacheKey = [
query: DocumentNode,
stringifiedVariables: string,
...queryKey: any[]
];
export interface QueryKey {
__queryKey?: string;
}
//# sourceMappingURL=types.d.ts.map

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/react/internal/cache/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { DocumentNode } from \"graphql\";\n\nexport type CacheKey = [\n query: DocumentNode,\n stringifiedVariables: string,\n ...queryKey: any[],\n];\n\nexport interface QueryKey {\n __queryKey?: string;\n}\n"]}