houdini 1.1.7 → 1.2.0-next.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/README.md +1 -1
- package/build/cmd-cjs/index.js +30737 -30175
- package/build/cmd-esm/index.js +30737 -30175
- package/build/codegen/generators/artifacts/selection.d.ts +2 -1
- package/build/codegen/generators/typescript/inlineType.d.ts +5 -2
- package/build/codegen/generators/typescript/loadingState.d.ts +8 -0
- package/build/codegen-cjs/index.js +30791 -30254
- package/build/codegen-esm/index.js +30791 -30254
- package/build/lib/code.d.ts +11 -0
- package/build/lib/config.d.ts +5 -0
- package/build/lib/graphql.d.ts +5 -1
- package/build/lib/index.d.ts +1 -0
- package/build/lib/parse.d.ts +0 -7
- package/build/lib/types.d.ts +4 -1
- package/build/lib-cjs/index.js +29888 -29668
- package/build/lib-esm/index.js +29885 -29668
- package/build/runtime/cache/cache.d.ts +5 -6
- package/build/runtime/client/documentStore.d.ts +3 -0
- package/build/runtime/client/index.d.ts +7 -6
- package/build/runtime/client/plugins/cache.d.ts +1 -1
- package/build/runtime/client/plugins/fetch.d.ts +1 -0
- package/build/runtime/client/plugins/subscription.d.ts +1 -0
- package/build/runtime/client/plugins/throwOnError.d.ts +2 -1
- package/build/runtime/lib/pageInfo.d.ts +7 -0
- package/build/runtime/lib/pagination.d.ts +27 -0
- package/build/runtime/lib/selection.d.ts +1 -1
- package/build/runtime/lib/types.d.ts +75 -3
- package/build/runtime-cjs/cache/cache.d.ts +5 -6
- package/build/runtime-cjs/cache/cache.js +78 -36
- package/build/runtime-cjs/cache/subscription.js +5 -5
- package/build/runtime-cjs/client/documentStore.d.ts +3 -0
- package/build/runtime-cjs/client/documentStore.js +20 -7
- package/build/runtime-cjs/client/index.d.ts +7 -6
- package/build/runtime-cjs/client/index.js +12 -4
- package/build/runtime-cjs/client/plugins/cache.d.ts +1 -1
- package/build/runtime-cjs/client/plugins/cache.js +12 -2
- package/build/runtime-cjs/client/plugins/fetch.d.ts +1 -0
- package/build/runtime-cjs/client/plugins/fetch.js +3 -2
- package/build/runtime-cjs/client/plugins/fragment.js +8 -1
- package/build/runtime-cjs/client/plugins/query.js +2 -1
- package/build/runtime-cjs/client/plugins/subscription.d.ts +1 -0
- package/build/runtime-cjs/client/plugins/subscription.js +1 -0
- package/build/runtime-cjs/client/plugins/throwOnError.d.ts +2 -1
- package/build/runtime-cjs/lib/config.js +2 -1
- package/build/runtime-cjs/lib/pageInfo.d.ts +7 -0
- package/build/runtime-cjs/lib/pageInfo.js +79 -0
- package/build/runtime-cjs/lib/pagination.d.ts +27 -0
- package/build/runtime-cjs/lib/pagination.js +219 -0
- package/build/runtime-cjs/lib/scalars.js +1 -1
- package/build/runtime-cjs/lib/selection.d.ts +1 -1
- package/build/runtime-cjs/lib/selection.js +28 -1
- package/build/runtime-cjs/lib/types.d.ts +75 -3
- package/build/runtime-cjs/lib/types.js +3 -0
- package/build/runtime-esm/cache/cache.d.ts +5 -6
- package/build/runtime-esm/cache/cache.js +79 -37
- package/build/runtime-esm/cache/subscription.js +5 -5
- package/build/runtime-esm/client/documentStore.d.ts +3 -0
- package/build/runtime-esm/client/documentStore.js +20 -7
- package/build/runtime-esm/client/index.d.ts +7 -6
- package/build/runtime-esm/client/index.js +15 -7
- package/build/runtime-esm/client/plugins/cache.d.ts +1 -1
- package/build/runtime-esm/client/plugins/cache.js +12 -2
- package/build/runtime-esm/client/plugins/fetch.d.ts +1 -0
- package/build/runtime-esm/client/plugins/fetch.js +3 -2
- package/build/runtime-esm/client/plugins/fragment.js +8 -1
- package/build/runtime-esm/client/plugins/query.js +2 -1
- package/build/runtime-esm/client/plugins/subscription.d.ts +1 -0
- package/build/runtime-esm/client/plugins/subscription.js +1 -0
- package/build/runtime-esm/client/plugins/throwOnError.d.ts +2 -1
- package/build/runtime-esm/lib/config.js +2 -1
- package/build/runtime-esm/lib/pageInfo.d.ts +7 -0
- package/build/runtime-esm/lib/pageInfo.js +52 -0
- package/build/runtime-esm/lib/pagination.d.ts +27 -0
- package/build/runtime-esm/lib/pagination.js +194 -0
- package/build/runtime-esm/lib/scalars.js +1 -1
- package/build/runtime-esm/lib/selection.d.ts +1 -1
- package/build/runtime-esm/lib/selection.js +28 -1
- package/build/runtime-esm/lib/types.d.ts +75 -3
- package/build/runtime-esm/lib/types.js +2 -0
- package/build/test/index.d.ts +15 -0
- package/build/test-cjs/index.js +30699 -30140
- package/build/test-esm/index.js +30699 -30140
- package/build/vite-cjs/index.js +30765 -30205
- package/build/vite-esm/index.js +30765 -30205
- package/package.json +5 -1
|
@@ -2,25 +2,33 @@ import { flatten } from "../lib/flatten";
|
|
|
2
2
|
import { DocumentStore } from "./documentStore";
|
|
3
3
|
import {
|
|
4
4
|
fetch as fetchPlugin,
|
|
5
|
+
fetchParams as fetchParamsPlugin,
|
|
6
|
+
fragment as fragmentPlugin,
|
|
5
7
|
mutation as mutationPlugin,
|
|
6
8
|
query as queryPlugin,
|
|
7
|
-
|
|
8
|
-
throwOnError as throwOnErrorPlugin,
|
|
9
|
-
fetchParams as fetchParamsPlugin
|
|
9
|
+
throwOnError as throwOnErrorPlugin
|
|
10
10
|
} from "./plugins";
|
|
11
11
|
import pluginsFromPlugins from "./plugins/injectedPlugins";
|
|
12
12
|
import { DocumentStore as DocumentStore2 } from "./documentStore";
|
|
13
13
|
import { fetch, mutation, query, subscription } from "./plugins";
|
|
14
14
|
class HoudiniClient {
|
|
15
15
|
url;
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
plugins;
|
|
17
|
+
throwOnError_operations;
|
|
18
|
+
constructor({
|
|
19
|
+
url,
|
|
20
|
+
fetchParams,
|
|
21
|
+
plugins,
|
|
22
|
+
pipeline,
|
|
23
|
+
throwOnError
|
|
24
|
+
}) {
|
|
18
25
|
if (plugins && pipeline) {
|
|
19
26
|
throw new Error(
|
|
20
27
|
"A client cannot be given a pipeline and a list of plugins at the same time."
|
|
21
28
|
);
|
|
22
29
|
}
|
|
23
|
-
this
|
|
30
|
+
this.throwOnError_operations = throwOnError?.operations ?? [];
|
|
31
|
+
this.plugins = flatten(
|
|
24
32
|
[].concat(
|
|
25
33
|
throwOnError ? [throwOnErrorPlugin(throwOnError)] : [],
|
|
26
34
|
fetchParamsPlugin(fetchParams),
|
|
@@ -46,7 +54,7 @@ class HoudiniClient {
|
|
|
46
54
|
return new DocumentStore({
|
|
47
55
|
client: this,
|
|
48
56
|
artifact,
|
|
49
|
-
plugins: createPluginHooks(this
|
|
57
|
+
plugins: createPluginHooks(this.plugins),
|
|
50
58
|
cache,
|
|
51
59
|
initialValue,
|
|
52
60
|
fetching
|
|
@@ -3,7 +3,7 @@ import { Cache } from '../../cache/cache';
|
|
|
3
3
|
import type { ClientPlugin } from '../documentStore';
|
|
4
4
|
export declare const cachePolicy: ({ enabled, setFetching, cache: localCache, serverSideFallback, }: {
|
|
5
5
|
enabled: boolean;
|
|
6
|
-
setFetching: (val: boolean) => void;
|
|
6
|
+
setFetching: (val: boolean, data?: any) => void;
|
|
7
7
|
cache?: Cache | undefined;
|
|
8
8
|
serverSideFallback?: boolean | undefined;
|
|
9
9
|
}) => ClientPlugin;
|
|
@@ -12,7 +12,7 @@ const cachePolicy = ({
|
|
|
12
12
|
network(ctx, { initialValue, next, resolve, marshalVariables }) {
|
|
13
13
|
const { policy, artifact } = ctx;
|
|
14
14
|
let useCache = false;
|
|
15
|
-
if (enabled && artifact.kind === ArtifactKind.Query && !ctx.cacheParams?.disableRead) {
|
|
15
|
+
if (enabled && (artifact.kind === ArtifactKind.Query || artifact.kind === ArtifactKind.Fragment) && !ctx.cacheParams?.disableRead) {
|
|
16
16
|
if (policy !== CachePolicy.NetworkOnly) {
|
|
17
17
|
const value = localCache.read({
|
|
18
18
|
selection: artifact.selection,
|
|
@@ -53,7 +53,17 @@ const cachePolicy = ({
|
|
|
53
53
|
localCache._internal_unstable.collectGarbage();
|
|
54
54
|
}, 0);
|
|
55
55
|
}
|
|
56
|
-
|
|
56
|
+
if (!ctx.stuff?.silenceLoading) {
|
|
57
|
+
let fetchingState = null;
|
|
58
|
+
if (!useCache && "enableLoadingState" in artifact && artifact.enableLoadingState) {
|
|
59
|
+
fetchingState = localCache.read({
|
|
60
|
+
selection: artifact.selection,
|
|
61
|
+
variables: marshalVariables(ctx),
|
|
62
|
+
loading: true
|
|
63
|
+
}).data;
|
|
64
|
+
}
|
|
65
|
+
setFetching(!useCache, fetchingState);
|
|
66
|
+
}
|
|
57
67
|
return next(ctx);
|
|
58
68
|
},
|
|
59
69
|
afterNetwork(ctx, { resolve, value, marshalVariables }) {
|
|
@@ -21,6 +21,7 @@ export type FetchContext = {
|
|
|
21
21
|
export type RequestHandlerArgs = FetchContext & FetchParams;
|
|
22
22
|
export type RequestHandler<_Data = any> = (args: RequestHandlerArgs) => Promise<RequestPayload<_Data>>;
|
|
23
23
|
export type FetchParams = {
|
|
24
|
+
name: string;
|
|
24
25
|
text: string;
|
|
25
26
|
hash: string;
|
|
26
27
|
variables: {
|
|
@@ -8,6 +8,7 @@ const fetch = (target) => {
|
|
|
8
8
|
}
|
|
9
9
|
const fetch2 = ctx.fetch ?? globalThis.fetch;
|
|
10
10
|
const fetchParams = {
|
|
11
|
+
name: ctx.artifact.name,
|
|
11
12
|
text: ctx.text,
|
|
12
13
|
hash: ctx.hash,
|
|
13
14
|
variables: marshalVariables(ctx)
|
|
@@ -48,10 +49,10 @@ const defaultFetch = (url, params) => {
|
|
|
48
49
|
"Could not find configured client url. Please specify one in your HoudiniClient constructor."
|
|
49
50
|
);
|
|
50
51
|
}
|
|
51
|
-
return async ({ fetch: fetch2, text, variables }) => {
|
|
52
|
+
return async ({ fetch: fetch2, name, text, variables }) => {
|
|
52
53
|
const result = await fetch2(url, {
|
|
53
54
|
method: "POST",
|
|
54
|
-
body: JSON.stringify({ query: text, variables }),
|
|
55
|
+
body: JSON.stringify({ operationName: name, query: text, variables }),
|
|
55
56
|
...params,
|
|
56
57
|
headers: {
|
|
57
58
|
Accept: "application/graphql+json, application/json",
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import cache from "../../cache";
|
|
2
|
+
import { deepEquals } from "../../lib/deepEquals";
|
|
2
3
|
import { ArtifactKind, DataSource } from "../../lib/types";
|
|
3
4
|
import { documentPlugin } from "../utils";
|
|
4
5
|
const fragment = documentPlugin(ArtifactKind.Fragment, function() {
|
|
5
6
|
let subscriptionSpec = null;
|
|
7
|
+
let lastReference = null;
|
|
6
8
|
return {
|
|
7
9
|
start(ctx, { next, resolve, variablesChanged, marshalVariables }) {
|
|
8
10
|
if (!ctx.stuff.parentID) {
|
|
9
11
|
return next(ctx);
|
|
10
12
|
}
|
|
11
|
-
|
|
13
|
+
const currentReference = {
|
|
14
|
+
parent: ctx.stuff.parentID,
|
|
15
|
+
variables: marshalVariables(ctx)
|
|
16
|
+
};
|
|
17
|
+
if (!ctx.cacheParams?.disableSubscriptions && (!deepEquals(lastReference, currentReference) || variablesChanged(ctx))) {
|
|
12
18
|
if (subscriptionSpec) {
|
|
13
19
|
cache.unsubscribe(subscriptionSpec, subscriptionSpec.variables?.() || {});
|
|
14
20
|
}
|
|
@@ -31,6 +37,7 @@ const fragment = documentPlugin(ArtifactKind.Fragment, function() {
|
|
|
31
37
|
}
|
|
32
38
|
};
|
|
33
39
|
cache.subscribe(subscriptionSpec, variables);
|
|
40
|
+
lastReference = currentReference;
|
|
34
41
|
}
|
|
35
42
|
next(ctx);
|
|
36
43
|
},
|
|
@@ -18,10 +18,11 @@ const query = documentPlugin(ArtifactKind.Query, function() {
|
|
|
18
18
|
cache.unsubscribe(subscriptionSpec, subscriptionSpec.variables?.() || {});
|
|
19
19
|
}
|
|
20
20
|
lastVariables = { ...marshalVariables(ctx) };
|
|
21
|
+
const variables = lastVariables;
|
|
21
22
|
subscriptionSpec = {
|
|
22
23
|
rootType: ctx.artifact.rootType,
|
|
23
24
|
selection: ctx.artifact.selection,
|
|
24
|
-
variables: () =>
|
|
25
|
+
variables: () => variables,
|
|
25
26
|
set: (newValue) => {
|
|
26
27
|
resolve(ctx, {
|
|
27
28
|
data: newValue,
|
|
@@ -3,6 +3,7 @@ export declare function subscription(factory: SubscriptionHandler): import("../d
|
|
|
3
3
|
export type SubscriptionHandler = (ctx: ClientPluginContext) => SubscriptionClient;
|
|
4
4
|
export type SubscriptionClient = {
|
|
5
5
|
subscribe: (payload: {
|
|
6
|
+
operationName: string;
|
|
6
7
|
query: string;
|
|
7
8
|
variables?: {};
|
|
8
9
|
}, handlers: {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { QueryResult } from '../../lib';
|
|
2
2
|
import type { ClientPlugin, ClientPluginContext } from '../documentStore';
|
|
3
|
+
export type ThrowOnErrorOperations = 'all' | 'query' | 'mutation' | 'subscription';
|
|
3
4
|
export type ThrowOnErrorParams = {
|
|
4
|
-
operations:
|
|
5
|
+
operations: ThrowOnErrorOperations[];
|
|
5
6
|
error?: (errors: NonNullable<QueryResult<any, any>['errors']>, ctx: ClientPluginContext) => unknown;
|
|
6
7
|
};
|
|
7
8
|
export declare const throwOnError: ({ operations, error }: ThrowOnErrorParams) => ClientPlugin;
|
|
@@ -24,7 +24,8 @@ function defaultConfigValues(file) {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
function keyFieldsForType(configFile, type) {
|
|
27
|
-
|
|
27
|
+
const withDefault = defaultConfigValues(configFile);
|
|
28
|
+
return withDefault.types?.[type]?.keys || withDefault.defaultKeys;
|
|
28
29
|
}
|
|
29
30
|
function computeID(configFile, type, data) {
|
|
30
31
|
const fields = keyFieldsForType(configFile, type);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { GraphQLObject, PageInfo } from './types';
|
|
2
|
+
export declare function nullPageInfo(): PageInfo;
|
|
3
|
+
export declare function missingPageSizeError(fnName: string): {
|
|
4
|
+
message: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function extractPageInfo(data: any, path: string[]): PageInfo;
|
|
7
|
+
export declare function countPage<_Data extends GraphQLObject>(source: string[], value: _Data | null): number;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { siteURL } from "./constants";
|
|
2
|
+
function nullPageInfo() {
|
|
3
|
+
return { startCursor: null, endCursor: null, hasNextPage: false, hasPreviousPage: false };
|
|
4
|
+
}
|
|
5
|
+
function missingPageSizeError(fnName) {
|
|
6
|
+
return {
|
|
7
|
+
message: `${fnName} is missing the required page arguments. For more information, please visit this link: ${siteURL}/guides/pagination`
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function extractPageInfo(data, path) {
|
|
11
|
+
if (!data) {
|
|
12
|
+
return {
|
|
13
|
+
startCursor: null,
|
|
14
|
+
endCursor: null,
|
|
15
|
+
hasNextPage: false,
|
|
16
|
+
hasPreviousPage: false
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
let localPath = [...path];
|
|
20
|
+
let current = data;
|
|
21
|
+
while (localPath.length > 0) {
|
|
22
|
+
if (!current) {
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
current = current[localPath.shift()];
|
|
26
|
+
}
|
|
27
|
+
return current?.pageInfo ?? nullPageInfo();
|
|
28
|
+
}
|
|
29
|
+
function countPage(source, value) {
|
|
30
|
+
let data = value;
|
|
31
|
+
if (value === null || data === null || data === void 0) {
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
for (const field of source) {
|
|
35
|
+
const obj = data[field];
|
|
36
|
+
if (obj && !Array.isArray(obj)) {
|
|
37
|
+
data = obj;
|
|
38
|
+
} else if (!data) {
|
|
39
|
+
throw new Error("Could not count page size");
|
|
40
|
+
}
|
|
41
|
+
if (Array.isArray(obj)) {
|
|
42
|
+
return obj.length;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
export {
|
|
48
|
+
countPage,
|
|
49
|
+
extractPageInfo,
|
|
50
|
+
missingPageSizeError,
|
|
51
|
+
nullPageInfo
|
|
52
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SendParams } from '../client/documentStore';
|
|
2
|
+
import type { CursorHandlers, FetchFn, GraphQLObject, QueryArtifact, QueryResult, FetchParams } from './types';
|
|
3
|
+
export declare function cursorHandlers<_Data extends GraphQLObject, _Input extends Record<string, any>>({ artifact, fetchUpdate: parentFetchUpdate, fetch: parentFetch, getState, getVariables, getSession, }: {
|
|
4
|
+
artifact: QueryArtifact;
|
|
5
|
+
getState: () => _Data | null;
|
|
6
|
+
getVariables: () => _Input;
|
|
7
|
+
getSession: () => Promise<App.Session>;
|
|
8
|
+
fetch: FetchFn<_Data, _Input>;
|
|
9
|
+
fetchUpdate: (arg: SendParams, updates: string[]) => ReturnType<FetchFn<_Data, _Input>>;
|
|
10
|
+
}): CursorHandlers<_Data, _Input>;
|
|
11
|
+
export declare function offsetHandlers<_Data extends GraphQLObject, _Input extends {}>({ artifact, storeName, getState, getVariables, fetch: parentFetch, fetchUpdate: parentFetchUpdate, getSession, }: {
|
|
12
|
+
artifact: QueryArtifact;
|
|
13
|
+
fetch: FetchFn<_Data, _Input>;
|
|
14
|
+
fetchUpdate: (arg: SendParams) => ReturnType<FetchFn<_Data, _Input>>;
|
|
15
|
+
storeName: string;
|
|
16
|
+
getState: () => _Data | null;
|
|
17
|
+
getVariables: () => _Input;
|
|
18
|
+
getSession: () => Promise<App.Session>;
|
|
19
|
+
}): {
|
|
20
|
+
loadNextPage: ({ limit, offset, fetch, metadata, }?: {
|
|
21
|
+
limit?: number | undefined;
|
|
22
|
+
offset?: number | undefined;
|
|
23
|
+
fetch?: typeof fetch | undefined;
|
|
24
|
+
metadata?: {} | undefined;
|
|
25
|
+
}) => Promise<void>;
|
|
26
|
+
fetch(params?: FetchParams<_Input>): Promise<QueryResult<_Data, _Input>>;
|
|
27
|
+
};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { getCurrentConfig } from "./config";
|
|
2
|
+
import { deepEquals } from "./deepEquals";
|
|
3
|
+
import { countPage, extractPageInfo, missingPageSizeError } from "./pageInfo";
|
|
4
|
+
import { CachePolicy } from "./types";
|
|
5
|
+
function cursorHandlers({
|
|
6
|
+
artifact,
|
|
7
|
+
fetchUpdate: parentFetchUpdate,
|
|
8
|
+
fetch: parentFetch,
|
|
9
|
+
getState,
|
|
10
|
+
getVariables,
|
|
11
|
+
getSession
|
|
12
|
+
}) {
|
|
13
|
+
const loadPage = async ({
|
|
14
|
+
pageSizeVar,
|
|
15
|
+
input,
|
|
16
|
+
functionName,
|
|
17
|
+
metadata = {},
|
|
18
|
+
fetch,
|
|
19
|
+
where
|
|
20
|
+
}) => {
|
|
21
|
+
const config = getCurrentConfig();
|
|
22
|
+
const loadVariables = {
|
|
23
|
+
...getVariables(),
|
|
24
|
+
...input
|
|
25
|
+
};
|
|
26
|
+
if (!loadVariables[pageSizeVar] && !artifact.refetch.pageSize) {
|
|
27
|
+
throw missingPageSizeError(functionName);
|
|
28
|
+
}
|
|
29
|
+
let isSinglePage = artifact.refetch?.mode === "SinglePage";
|
|
30
|
+
await (isSinglePage ? parentFetch : parentFetchUpdate)(
|
|
31
|
+
{
|
|
32
|
+
variables: loadVariables,
|
|
33
|
+
fetch,
|
|
34
|
+
metadata,
|
|
35
|
+
policy: isSinglePage ? artifact.policy : CachePolicy.NetworkOnly,
|
|
36
|
+
session: await getSession()
|
|
37
|
+
},
|
|
38
|
+
isSinglePage ? [] : [where === "start" ? "prepend" : "append"]
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
const getPageInfo = () => {
|
|
42
|
+
return extractPageInfo(getState(), artifact.refetch?.path ?? []);
|
|
43
|
+
};
|
|
44
|
+
return {
|
|
45
|
+
loadNextPage: async ({
|
|
46
|
+
first,
|
|
47
|
+
after,
|
|
48
|
+
fetch,
|
|
49
|
+
metadata
|
|
50
|
+
} = {}) => {
|
|
51
|
+
const currentPageInfo = getPageInfo();
|
|
52
|
+
if (!currentPageInfo.hasNextPage) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const input = {
|
|
56
|
+
first: first ?? artifact.refetch.pageSize,
|
|
57
|
+
after: after ?? currentPageInfo.endCursor,
|
|
58
|
+
before: null,
|
|
59
|
+
last: null
|
|
60
|
+
};
|
|
61
|
+
return await loadPage({
|
|
62
|
+
pageSizeVar: "first",
|
|
63
|
+
functionName: "loadNextPage",
|
|
64
|
+
input,
|
|
65
|
+
fetch,
|
|
66
|
+
metadata,
|
|
67
|
+
where: "end"
|
|
68
|
+
});
|
|
69
|
+
},
|
|
70
|
+
loadPreviousPage: async ({
|
|
71
|
+
last,
|
|
72
|
+
before,
|
|
73
|
+
fetch,
|
|
74
|
+
metadata
|
|
75
|
+
} = {}) => {
|
|
76
|
+
const currentPageInfo = getPageInfo();
|
|
77
|
+
if (!currentPageInfo.hasPreviousPage) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const input = {
|
|
81
|
+
before: before ?? currentPageInfo.startCursor,
|
|
82
|
+
last: last ?? artifact.refetch.pageSize,
|
|
83
|
+
first: null,
|
|
84
|
+
after: null
|
|
85
|
+
};
|
|
86
|
+
return await loadPage({
|
|
87
|
+
pageSizeVar: "last",
|
|
88
|
+
functionName: "loadPreviousPage",
|
|
89
|
+
input,
|
|
90
|
+
fetch,
|
|
91
|
+
metadata,
|
|
92
|
+
where: "start"
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
async fetch(args) {
|
|
96
|
+
const { variables } = args ?? {};
|
|
97
|
+
if (variables && !deepEquals(getVariables(), variables)) {
|
|
98
|
+
return await parentFetch(args);
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
var currentPageInfo = extractPageInfo(getState(), artifact.refetch.path);
|
|
102
|
+
} catch {
|
|
103
|
+
return await parentFetch(args);
|
|
104
|
+
}
|
|
105
|
+
const queryVariables = {};
|
|
106
|
+
const count = countPage(artifact.refetch.path.concat("edges"), getState()) || artifact.refetch.pageSize;
|
|
107
|
+
if (count && count > artifact.refetch.pageSize) {
|
|
108
|
+
if (currentPageInfo.hasPreviousPage && currentPageInfo.hasNextPage && !(variables?.["first"] && variables?.["after"] || variables?.["last"] && variables?.["before"])) {
|
|
109
|
+
console.warn(`\u26A0\uFE0F Encountered a fetch() in the middle of the connection.
|
|
110
|
+
Make sure to pass a cursor value by hand that includes the current set (ie the entry before startCursor)
|
|
111
|
+
`);
|
|
112
|
+
}
|
|
113
|
+
if (!currentPageInfo.hasPreviousPage) {
|
|
114
|
+
queryVariables["first"] = count;
|
|
115
|
+
queryVariables["after"] = null;
|
|
116
|
+
queryVariables["last"] = null;
|
|
117
|
+
queryVariables["before"] = null;
|
|
118
|
+
} else if (!currentPageInfo.hasNextPage) {
|
|
119
|
+
queryVariables["last"] = count;
|
|
120
|
+
queryVariables["first"] = null;
|
|
121
|
+
queryVariables["after"] = null;
|
|
122
|
+
queryVariables["before"] = null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
Object.assign(queryVariables, variables ?? {});
|
|
126
|
+
const result = await parentFetch({
|
|
127
|
+
...args,
|
|
128
|
+
variables: queryVariables
|
|
129
|
+
});
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function offsetHandlers({
|
|
135
|
+
artifact,
|
|
136
|
+
storeName,
|
|
137
|
+
getState,
|
|
138
|
+
getVariables,
|
|
139
|
+
fetch: parentFetch,
|
|
140
|
+
fetchUpdate: parentFetchUpdate,
|
|
141
|
+
getSession
|
|
142
|
+
}) {
|
|
143
|
+
let getOffset = () => artifact.refetch?.start || countPage(artifact.refetch.path, getState()) || artifact.refetch.pageSize;
|
|
144
|
+
let currentOffset = getOffset() ?? 0;
|
|
145
|
+
return {
|
|
146
|
+
loadNextPage: async ({
|
|
147
|
+
limit,
|
|
148
|
+
offset,
|
|
149
|
+
fetch,
|
|
150
|
+
metadata
|
|
151
|
+
} = {}) => {
|
|
152
|
+
const queryVariables = {
|
|
153
|
+
...getVariables(),
|
|
154
|
+
offset: offset ?? getOffset()
|
|
155
|
+
};
|
|
156
|
+
if (limit || limit === 0) {
|
|
157
|
+
queryVariables.limit = limit;
|
|
158
|
+
}
|
|
159
|
+
if (!queryVariables.limit && !artifact.refetch.pageSize) {
|
|
160
|
+
throw missingPageSizeError("loadNextPage");
|
|
161
|
+
}
|
|
162
|
+
let isSinglePage = artifact.refetch?.mode === "SinglePage";
|
|
163
|
+
const targetFetch = isSinglePage ? parentFetch : parentFetchUpdate;
|
|
164
|
+
await targetFetch({
|
|
165
|
+
variables: queryVariables,
|
|
166
|
+
fetch,
|
|
167
|
+
metadata,
|
|
168
|
+
policy: isSinglePage ? artifact.policy : CachePolicy.NetworkOnly,
|
|
169
|
+
session: await getSession()
|
|
170
|
+
});
|
|
171
|
+
const pageSize = queryVariables.limit || artifact.refetch.pageSize;
|
|
172
|
+
currentOffset = offset + pageSize;
|
|
173
|
+
},
|
|
174
|
+
async fetch(params = {}) {
|
|
175
|
+
const { variables } = params;
|
|
176
|
+
if (variables && !deepEquals(getVariables(), variables)) {
|
|
177
|
+
return parentFetch.call(this, params);
|
|
178
|
+
}
|
|
179
|
+
const count = currentOffset || getOffset();
|
|
180
|
+
const queryVariables = {};
|
|
181
|
+
if (!artifact.refetch.pageSize || count > artifact.refetch.pageSize) {
|
|
182
|
+
queryVariables.limit = count;
|
|
183
|
+
}
|
|
184
|
+
return await parentFetch.call(this, {
|
|
185
|
+
...params,
|
|
186
|
+
variables: queryVariables
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
export {
|
|
192
|
+
cursorHandlers,
|
|
193
|
+
offsetHandlers
|
|
194
|
+
};
|
|
@@ -11,7 +11,7 @@ async function marshalSelection({
|
|
|
11
11
|
if (Array.isArray(data)) {
|
|
12
12
|
return await Promise.all(data.map((val) => marshalSelection({ selection, data: val })));
|
|
13
13
|
}
|
|
14
|
-
const targetSelection = getFieldsForType(selection, data["__typename"]);
|
|
14
|
+
const targetSelection = getFieldsForType(selection, data["__typename"], false);
|
|
15
15
|
return Object.fromEntries(
|
|
16
16
|
await Promise.all(
|
|
17
17
|
Object.entries(data).map(async ([fieldName, value]) => {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { SubscriptionSelection } from './types';
|
|
2
|
-
export declare function getFieldsForType(selection: SubscriptionSelection, __typename: string | undefined | null): Required<SubscriptionSelection>['fields'];
|
|
2
|
+
export declare function getFieldsForType(selection: SubscriptionSelection, __typename: string | undefined | null, loading: boolean): Required<SubscriptionSelection>['fields'];
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
function getFieldsForType(selection, __typename) {
|
|
1
|
+
function getFieldsForType(selection, __typename, loading) {
|
|
2
|
+
if (loading) {
|
|
3
|
+
if (selection.loadingTypes && selection.loadingTypes.length > 0) {
|
|
4
|
+
return deepMerge(
|
|
5
|
+
...selection.loadingTypes.map((type) => selection.abstractFields?.fields[type])
|
|
6
|
+
);
|
|
7
|
+
}
|
|
8
|
+
return selection.fields ?? {};
|
|
9
|
+
}
|
|
2
10
|
let targetSelection = selection.fields || {};
|
|
3
11
|
if (selection.abstractFields && __typename) {
|
|
4
12
|
const mappedType = selection.abstractFields.typeMap[__typename];
|
|
@@ -10,6 +18,25 @@ function getFieldsForType(selection, __typename) {
|
|
|
10
18
|
}
|
|
11
19
|
return targetSelection;
|
|
12
20
|
}
|
|
21
|
+
function deepMerge(...objects) {
|
|
22
|
+
const mergedObj = {};
|
|
23
|
+
for (let obj of objects) {
|
|
24
|
+
if (!obj) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
for (let prop in obj) {
|
|
28
|
+
if (prop in obj) {
|
|
29
|
+
const val = obj[prop];
|
|
30
|
+
if (typeof val === "object" && val !== null && !Array.isArray(val)) {
|
|
31
|
+
mergedObj[prop] = deepMerge(mergedObj[prop] || {}, val);
|
|
32
|
+
} else {
|
|
33
|
+
mergedObj[prop] = val;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return mergedObj;
|
|
39
|
+
}
|
|
13
40
|
export {
|
|
14
41
|
getFieldsForType
|
|
15
42
|
};
|
|
@@ -25,6 +25,7 @@ declare global {
|
|
|
25
25
|
};
|
|
26
26
|
optimisticResponse?: GraphQLObject;
|
|
27
27
|
parentID?: string;
|
|
28
|
+
silenceLoading?: boolean;
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
}
|
|
@@ -52,9 +53,12 @@ export type CompiledDocumentKind = ArtifactKinds;
|
|
|
52
53
|
export type QueryArtifact = BaseCompiledDocument<'HoudiniQuery'> & {
|
|
53
54
|
policy?: CachePolicies;
|
|
54
55
|
partial?: boolean;
|
|
56
|
+
enableLoadingState?: boolean;
|
|
55
57
|
};
|
|
56
58
|
export type MutationArtifact = BaseCompiledDocument<'HoudiniMutation'>;
|
|
57
|
-
export type FragmentArtifact = BaseCompiledDocument<'HoudiniFragment'
|
|
59
|
+
export type FragmentArtifact = BaseCompiledDocument<'HoudiniFragment'> & {
|
|
60
|
+
enableLoadingState?: boolean;
|
|
61
|
+
};
|
|
58
62
|
export type SubscriptionArtifact = BaseCompiledDocument<'HoudiniSubscription'>;
|
|
59
63
|
export declare const RefetchUpdateMode: {
|
|
60
64
|
readonly append: "append";
|
|
@@ -128,12 +132,31 @@ export type GraphQLObject = {
|
|
|
128
132
|
[key: string]: GraphQLValue;
|
|
129
133
|
};
|
|
130
134
|
export type GraphQLValue = number | string | boolean | null | GraphQLObject | GraphQLValue[] | undefined;
|
|
135
|
+
export type LoadingSpec = {
|
|
136
|
+
kind: 'continue';
|
|
137
|
+
list?: {
|
|
138
|
+
depth: number;
|
|
139
|
+
count: number;
|
|
140
|
+
};
|
|
141
|
+
} | {
|
|
142
|
+
kind: 'value';
|
|
143
|
+
value?: any;
|
|
144
|
+
list?: {
|
|
145
|
+
depth: number;
|
|
146
|
+
count: number;
|
|
147
|
+
};
|
|
148
|
+
};
|
|
131
149
|
export type SubscriptionSelection = {
|
|
132
|
-
|
|
150
|
+
loadingTypes?: string[];
|
|
151
|
+
fragments?: Record<string, {
|
|
152
|
+
arguments: ValueMap;
|
|
153
|
+
loading?: boolean;
|
|
154
|
+
}>;
|
|
133
155
|
fields?: {
|
|
134
156
|
[fieldName: string]: {
|
|
135
157
|
type: string;
|
|
136
158
|
nullable?: boolean;
|
|
159
|
+
required?: boolean;
|
|
137
160
|
keyRaw: string;
|
|
138
161
|
operations?: MutationOperation[];
|
|
139
162
|
list?: {
|
|
@@ -141,6 +164,7 @@ export type SubscriptionSelection = {
|
|
|
141
164
|
connection: boolean;
|
|
142
165
|
type: string;
|
|
143
166
|
};
|
|
167
|
+
loading?: LoadingSpec;
|
|
144
168
|
directives?: {
|
|
145
169
|
name: string;
|
|
146
170
|
arguments: ValueMap;
|
|
@@ -153,6 +177,7 @@ export type SubscriptionSelection = {
|
|
|
153
177
|
}>;
|
|
154
178
|
selection?: SubscriptionSelection;
|
|
155
179
|
abstract?: boolean;
|
|
180
|
+
abstractHasRequired?: boolean;
|
|
156
181
|
};
|
|
157
182
|
};
|
|
158
183
|
abstractFields?: {
|
|
@@ -194,9 +219,54 @@ export type RequestPayload<GraphQLObject = any> = {
|
|
|
194
219
|
};
|
|
195
220
|
export type NestedList<_Result = string> = (_Result | null | NestedList<_Result>)[];
|
|
196
221
|
export type ValueOf<Parent> = Parent[keyof Parent];
|
|
197
|
-
export declare const fragmentKey
|
|
222
|
+
export declare const fragmentKey: " $fragments";
|
|
198
223
|
export type ValueNode = VariableNode | IntValueNode | FloatValueNode | StringValueNode | BooleanValueNode | NullValueNode | EnumValueNode | ListValueNode | ObjectValueNode;
|
|
199
224
|
export type ValueMap = Record<string, ValueNode>;
|
|
225
|
+
export type FetchParams<_Input> = {
|
|
226
|
+
variables?: _Input;
|
|
227
|
+
/**
|
|
228
|
+
* The policy to use when performing the fetch. If set to CachePolicy.NetworkOnly,
|
|
229
|
+
* a request will always be sent, even if the variables are the same as the last call
|
|
230
|
+
* to fetch.
|
|
231
|
+
*/
|
|
232
|
+
policy?: CachePolicies;
|
|
233
|
+
/**
|
|
234
|
+
* An object that will be passed to the fetch function.
|
|
235
|
+
* You can do what you want with it!
|
|
236
|
+
*/
|
|
237
|
+
metadata?: App.Metadata;
|
|
238
|
+
};
|
|
239
|
+
export type FetchFn<_Data extends GraphQLObject, _Input = any> = (params?: FetchParams<_Input>) => Promise<QueryResult<_Data, _Input>>;
|
|
240
|
+
export type CursorHandlers<_Data extends GraphQLObject, _Input> = {
|
|
241
|
+
loadNextPage: (args?: {
|
|
242
|
+
first?: number;
|
|
243
|
+
after?: string;
|
|
244
|
+
fetch?: typeof globalThis.fetch;
|
|
245
|
+
metadata?: {};
|
|
246
|
+
}) => Promise<void>;
|
|
247
|
+
loadPreviousPage: (args?: {
|
|
248
|
+
last?: number;
|
|
249
|
+
before?: string;
|
|
250
|
+
fetch?: typeof globalThis.fetch;
|
|
251
|
+
metadata?: {};
|
|
252
|
+
}) => Promise<void>;
|
|
253
|
+
fetch(args?: FetchParams<_Input> | undefined): Promise<QueryResult<_Data, _Input>>;
|
|
254
|
+
};
|
|
255
|
+
export type OffsetHandlers<_Data extends GraphQLObject, _Input> = {
|
|
256
|
+
loadNextPage: (args?: {
|
|
257
|
+
limit?: number;
|
|
258
|
+
offset?: number;
|
|
259
|
+
metadata?: {};
|
|
260
|
+
fetch?: typeof globalThis.fetch;
|
|
261
|
+
}) => Promise<void>;
|
|
262
|
+
fetch(args?: FetchParams<_Input> | undefined): Promise<QueryResult<_Data, _Input>>;
|
|
263
|
+
};
|
|
264
|
+
export type PageInfo = {
|
|
265
|
+
startCursor: string | null;
|
|
266
|
+
endCursor: string | null;
|
|
267
|
+
hasNextPage: boolean;
|
|
268
|
+
hasPreviousPage: boolean;
|
|
269
|
+
};
|
|
200
270
|
interface IntValueNode {
|
|
201
271
|
readonly kind: 'IntValue';
|
|
202
272
|
readonly value: string;
|
|
@@ -241,4 +311,6 @@ interface VariableNode {
|
|
|
241
311
|
readonly kind: 'Variable';
|
|
242
312
|
readonly name: NameNode;
|
|
243
313
|
}
|
|
314
|
+
export declare const PendingValue: unique symbol;
|
|
315
|
+
export type LoadingType = typeof PendingValue;
|
|
244
316
|
export {};
|