@isograph/react 0.1.1 → 0.3.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/dist/core/FragmentReference.d.ts +25 -0
- package/dist/core/FragmentReference.d.ts.map +1 -0
- package/dist/core/FragmentReference.js +16 -0
- package/dist/core/IsographEnvironment.d.ts +89 -0
- package/dist/core/IsographEnvironment.d.ts.map +1 -0
- package/dist/core/IsographEnvironment.js +65 -0
- package/dist/core/PromiseWrapper.d.ts +28 -0
- package/dist/core/PromiseWrapper.d.ts.map +1 -0
- package/dist/core/PromiseWrapper.js +57 -0
- package/dist/core/areEqualWithDeepComparison.d.ts +5 -0
- package/dist/core/areEqualWithDeepComparison.d.ts.map +1 -0
- package/dist/core/areEqualWithDeepComparison.js +95 -0
- package/dist/core/cache.d.ts +44 -0
- package/dist/core/cache.d.ts.map +1 -0
- package/dist/core/cache.js +514 -0
- package/dist/core/check.d.ts +18 -0
- package/dist/core/check.d.ts.map +1 -0
- package/dist/core/check.js +127 -0
- package/dist/core/componentCache.d.ts +5 -0
- package/dist/core/componentCache.d.ts.map +1 -0
- package/dist/core/componentCache.js +38 -0
- package/dist/core/entrypoint.d.ts +69 -0
- package/dist/core/entrypoint.d.ts.map +1 -0
- package/dist/core/entrypoint.js +7 -0
- package/dist/core/garbageCollection.d.ts +13 -0
- package/dist/core/garbageCollection.d.ts.map +1 -0
- package/dist/core/garbageCollection.js +107 -0
- package/dist/core/logging.d.ts +69 -0
- package/dist/core/logging.d.ts.map +1 -0
- package/dist/core/logging.js +19 -0
- package/dist/core/makeNetworkRequest.d.ts +9 -0
- package/dist/core/makeNetworkRequest.d.ts.map +1 -0
- package/dist/core/makeNetworkRequest.js +118 -0
- package/dist/core/read.d.ts +27 -0
- package/dist/core/read.d.ts.map +1 -0
- package/dist/core/read.js +478 -0
- package/dist/core/reader.d.ts +87 -0
- package/dist/core/reader.d.ts.map +1 -0
- package/dist/core/reader.js +2 -0
- package/dist/core/util.d.ts +19 -0
- package/dist/core/util.d.ts.map +1 -0
- package/dist/core/util.js +2 -0
- package/dist/index.d.ts +26 -120
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -306
- package/dist/loadable-hooks/useClientSideDefer.d.ts +16 -0
- package/dist/loadable-hooks/useClientSideDefer.d.ts.map +1 -0
- package/dist/loadable-hooks/useClientSideDefer.js +13 -0
- package/dist/loadable-hooks/useConnectionSpecPagination.d.ts +34 -0
- package/dist/loadable-hooks/useConnectionSpecPagination.d.ts.map +1 -0
- package/dist/loadable-hooks/useConnectionSpecPagination.js +160 -0
- package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts +6 -0
- package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts.map +1 -0
- package/dist/loadable-hooks/useImperativeExposedMutationField.js +14 -0
- package/dist/loadable-hooks/useImperativeLoadableField.d.ts +17 -0
- package/dist/loadable-hooks/useImperativeLoadableField.d.ts.map +1 -0
- package/dist/loadable-hooks/useImperativeLoadableField.js +14 -0
- package/dist/loadable-hooks/useSkipLimitPagination.d.ts +27 -0
- package/dist/loadable-hooks/useSkipLimitPagination.d.ts.map +1 -0
- package/dist/loadable-hooks/useSkipLimitPagination.js +162 -0
- package/dist/react/FragmentReader.d.ts +16 -0
- package/dist/react/FragmentReader.d.ts.map +1 -0
- package/dist/{EntrypointReader.js → react/FragmentReader.js} +7 -5
- package/dist/react/IsographEnvironmentProvider.d.ts +11 -0
- package/dist/react/IsographEnvironmentProvider.d.ts.map +1 -0
- package/dist/{IsographEnvironment.js → react/IsographEnvironmentProvider.js} +4 -22
- package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts +10 -0
- package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts.map +1 -0
- package/dist/react/RenderAfterCommit__DO_NOT_USE.js +15 -0
- package/dist/react/useImperativeReference.d.ts +12 -0
- package/dist/react/useImperativeReference.d.ts.map +1 -0
- package/dist/react/useImperativeReference.js +35 -0
- package/dist/react/useLazyReference.d.ts +10 -0
- package/dist/react/useLazyReference.d.ts.map +1 -0
- package/dist/react/useLazyReference.js +21 -0
- package/dist/react/useReadAndSubscribe.d.ts +20 -0
- package/dist/react/useReadAndSubscribe.d.ts.map +1 -0
- package/dist/react/useReadAndSubscribe.js +40 -0
- package/dist/react/useRerenderOnChange.d.ts +8 -0
- package/dist/react/useRerenderOnChange.d.ts.map +1 -0
- package/dist/react/useRerenderOnChange.js +22 -0
- package/dist/react/useResult.d.ts +9 -0
- package/dist/react/useResult.d.ts.map +1 -0
- package/dist/react/useResult.js +39 -0
- package/docs/how-useLazyReference-works.md +117 -0
- package/isograph.config.json +7 -0
- package/package.json +18 -9
- package/schema.graphql +17 -0
- package/src/core/FragmentReference.ts +49 -0
- package/src/core/IsographEnvironment.ts +192 -0
- package/src/core/PromiseWrapper.ts +86 -0
- package/src/core/areEqualWithDeepComparison.ts +112 -0
- package/src/core/cache.ts +835 -0
- package/src/core/check.ts +207 -0
- package/src/core/componentCache.ts +62 -0
- package/src/core/entrypoint.ts +106 -0
- package/src/core/garbageCollection.ts +173 -0
- package/src/core/logging.ts +116 -0
- package/src/core/makeNetworkRequest.ts +175 -0
- package/src/core/read.ts +722 -0
- package/src/core/reader.ts +160 -0
- package/src/core/util.ts +27 -0
- package/src/index.ts +99 -0
- package/src/loadable-hooks/useClientSideDefer.ts +58 -0
- package/src/loadable-hooks/useConnectionSpecPagination.ts +331 -0
- package/src/loadable-hooks/useImperativeExposedMutationField.ts +17 -0
- package/src/loadable-hooks/useImperativeLoadableField.ts +52 -0
- package/src/loadable-hooks/useSkipLimitPagination.ts +352 -0
- package/src/react/FragmentReader.tsx +43 -0
- package/src/react/IsographEnvironmentProvider.tsx +33 -0
- package/src/react/RenderAfterCommit__DO_NOT_USE.tsx +17 -0
- package/src/react/useImperativeReference.ts +68 -0
- package/src/react/useLazyReference.ts +42 -0
- package/src/react/useReadAndSubscribe.ts +81 -0
- package/src/react/useRerenderOnChange.ts +38 -0
- package/src/react/useResult.ts +73 -0
- package/src/tests/__isograph/Query/meName/entrypoint.ts +52 -0
- package/src/tests/__isograph/Query/meName/output_type.ts +3 -0
- package/src/tests/__isograph/Query/meName/param_type.ts +9 -0
- package/src/tests/__isograph/Query/meName/resolver_reader.ts +33 -0
- package/src/tests/__isograph/Query/meNameSuccessor/entrypoint.ts +90 -0
- package/src/tests/__isograph/Query/meNameSuccessor/output_type.ts +3 -0
- package/src/tests/__isograph/Query/meNameSuccessor/param_type.ts +14 -0
- package/src/tests/__isograph/Query/meNameSuccessor/resolver_reader.ts +57 -0
- package/src/tests/__isograph/Query/nodeField/entrypoint.ts +57 -0
- package/src/tests/__isograph/Query/nodeField/output_type.ts +3 -0
- package/src/tests/__isograph/Query/nodeField/param_type.ts +10 -0
- package/src/tests/__isograph/Query/nodeField/parameters_type.ts +3 -0
- package/src/tests/__isograph/Query/nodeField/resolver_reader.ts +38 -0
- package/src/tests/__isograph/Query/subquery/entrypoint.ts +67 -0
- package/src/tests/__isograph/Query/subquery/output_type.ts +3 -0
- package/src/tests/__isograph/Query/subquery/param_type.ts +12 -0
- package/src/tests/__isograph/Query/subquery/parameters_type.ts +3 -0
- package/src/tests/__isograph/Query/subquery/resolver_reader.ts +47 -0
- package/src/tests/__isograph/iso.ts +99 -0
- package/src/tests/garbageCollection.test.ts +142 -0
- package/src/tests/meNameSuccessor.ts +25 -0
- package/src/tests/nodeQuery.ts +19 -0
- package/src/tests/normalizeData.test.ts +120 -0
- package/src/tests/tsconfig.json +21 -0
- package/tsconfig.json +6 -0
- package/tsconfig.pkg.json +7 -1
- package/vitest.config.ts +20 -0
- package/dist/EntrypointReader.d.ts +0 -6
- package/dist/IsographEnvironment.d.ts +0 -56
- package/dist/PromiseWrapper.d.ts +0 -13
- package/dist/PromiseWrapper.js +0 -22
- package/dist/cache.d.ts +0 -26
- package/dist/cache.js +0 -274
- package/dist/componentCache.d.ts +0 -6
- package/dist/componentCache.js +0 -31
- package/dist/useImperativeReference.d.ts +0 -8
- package/dist/useImperativeReference.js +0 -28
- package/src/EntrypointReader.tsx +0 -20
- package/src/IsographEnvironment.tsx +0 -120
- package/src/PromiseWrapper.ts +0 -29
- package/src/cache.tsx +0 -484
- package/src/componentCache.ts +0 -41
- package/src/index.tsx +0 -617
- package/src/useImperativeReference.ts +0 -58
@@ -0,0 +1,160 @@
|
|
1
|
+
import { Factory } from '@isograph/disposable-types';
|
2
|
+
import {
|
3
|
+
FragmentReference,
|
4
|
+
ExtractParameters,
|
5
|
+
ExtractData,
|
6
|
+
} from './FragmentReference';
|
7
|
+
import {
|
8
|
+
ComponentOrFieldName,
|
9
|
+
IsographEnvironment,
|
10
|
+
type Link,
|
11
|
+
} from './IsographEnvironment';
|
12
|
+
import {
|
13
|
+
IsographEntrypoint,
|
14
|
+
IsographEntrypointLoader,
|
15
|
+
RefetchQueryNormalizationArtifact,
|
16
|
+
RefetchQueryNormalizationArtifactWrapper,
|
17
|
+
} from './entrypoint';
|
18
|
+
import { Arguments } from './util';
|
19
|
+
import { FetchOptions } from './check';
|
20
|
+
|
21
|
+
export type TopLevelReaderArtifact<
|
22
|
+
TReadFromStore extends { parameters: object; data: object },
|
23
|
+
TClientFieldValue,
|
24
|
+
TComponentProps extends Record<PropertyKey, never>,
|
25
|
+
> =
|
26
|
+
| EagerReaderArtifact<TReadFromStore, TClientFieldValue>
|
27
|
+
| ComponentReaderArtifact<TReadFromStore, TComponentProps>;
|
28
|
+
|
29
|
+
export type EagerReaderArtifact<
|
30
|
+
TReadFromStore extends { parameters: object; data: object },
|
31
|
+
TClientFieldValue,
|
32
|
+
> = {
|
33
|
+
readonly kind: 'EagerReaderArtifact';
|
34
|
+
readonly readerAst: ReaderAst<TReadFromStore>;
|
35
|
+
readonly resolver: (
|
36
|
+
data: ResolverFirstParameter<TReadFromStore>,
|
37
|
+
) => TClientFieldValue;
|
38
|
+
};
|
39
|
+
|
40
|
+
export type ComponentReaderArtifact<
|
41
|
+
TReadFromStore extends { parameters: object; data: object },
|
42
|
+
TComponentProps extends Record<string, unknown> = Record<PropertyKey, never>,
|
43
|
+
> = {
|
44
|
+
readonly kind: 'ComponentReaderArtifact';
|
45
|
+
readonly componentName: ComponentOrFieldName;
|
46
|
+
readonly readerAst: ReaderAst<TReadFromStore>;
|
47
|
+
readonly resolver: (
|
48
|
+
data: ResolverFirstParameter<TReadFromStore>,
|
49
|
+
runtimeProps: TComponentProps,
|
50
|
+
) => React.ReactNode;
|
51
|
+
};
|
52
|
+
|
53
|
+
export type ResolverFirstParameter<
|
54
|
+
TReadFromStore extends { data: object; parameters: object },
|
55
|
+
> = {
|
56
|
+
data: ExtractData<TReadFromStore>;
|
57
|
+
parameters: ExtractParameters<TReadFromStore>;
|
58
|
+
};
|
59
|
+
|
60
|
+
export type RefetchReaderArtifact = {
|
61
|
+
readonly kind: 'RefetchReaderArtifact';
|
62
|
+
readonly readerAst: ReaderAst<unknown>;
|
63
|
+
readonly resolver: (
|
64
|
+
environment: IsographEnvironment,
|
65
|
+
artifact: RefetchQueryNormalizationArtifact,
|
66
|
+
// TODO type this better
|
67
|
+
variables: any,
|
68
|
+
// TODO type this better
|
69
|
+
filteredVariables: any,
|
70
|
+
rootLink: Link,
|
71
|
+
readerArtifact: TopLevelReaderArtifact<any, any, any> | null,
|
72
|
+
// TODO type this better
|
73
|
+
nestedRefetchQueries: RefetchQueryNormalizationArtifactWrapper[],
|
74
|
+
) => () => void;
|
75
|
+
};
|
76
|
+
|
77
|
+
export type ReaderAstNode =
|
78
|
+
| ReaderScalarField
|
79
|
+
| ReaderLinkedField
|
80
|
+
| ReaderNonLoadableResolverField
|
81
|
+
| ReaderImperativelyLoadedField
|
82
|
+
| ReaderLoadableField;
|
83
|
+
|
84
|
+
// @ts-ignore
|
85
|
+
export type ReaderAst<TReadFromStore> = ReadonlyArray<ReaderAstNode>;
|
86
|
+
|
87
|
+
export type ReaderScalarField = {
|
88
|
+
readonly kind: 'Scalar';
|
89
|
+
readonly fieldName: string;
|
90
|
+
readonly alias: string | null;
|
91
|
+
readonly arguments: Arguments | null;
|
92
|
+
};
|
93
|
+
export type ReaderLinkedField = {
|
94
|
+
readonly kind: 'Linked';
|
95
|
+
readonly fieldName: string;
|
96
|
+
readonly alias: string | null;
|
97
|
+
readonly selections: ReaderAst<unknown>;
|
98
|
+
readonly arguments: Arguments | null;
|
99
|
+
readonly condition: EagerReaderArtifact<
|
100
|
+
{ data: object; parameters: object },
|
101
|
+
boolean | Link | null
|
102
|
+
> | null;
|
103
|
+
};
|
104
|
+
|
105
|
+
export type ReaderNonLoadableResolverField = {
|
106
|
+
readonly kind: 'Resolver';
|
107
|
+
readonly alias: string;
|
108
|
+
// TODO don't type this as any
|
109
|
+
readonly readerArtifact: TopLevelReaderArtifact<any, any, any>;
|
110
|
+
readonly arguments: Arguments | null;
|
111
|
+
readonly usedRefetchQueries: number[];
|
112
|
+
};
|
113
|
+
|
114
|
+
export type ReaderImperativelyLoadedField = {
|
115
|
+
readonly kind: 'ImperativelyLoadedField';
|
116
|
+
readonly alias: string;
|
117
|
+
readonly refetchReaderArtifact: RefetchReaderArtifact;
|
118
|
+
readonly refetchQuery: number;
|
119
|
+
readonly name: string;
|
120
|
+
};
|
121
|
+
|
122
|
+
export type ReaderLoadableField = {
|
123
|
+
readonly kind: 'LoadablySelectedField';
|
124
|
+
readonly alias: string;
|
125
|
+
|
126
|
+
// To generate a stable id, we need the parent id + the name + the args that
|
127
|
+
// we pass to the field, which come from: queryArgs, refetchReaderAst
|
128
|
+
// (technically, but in practice that is always "id") and the user-provided args.
|
129
|
+
readonly name: string;
|
130
|
+
readonly queryArguments: Arguments | null;
|
131
|
+
readonly refetchReaderAst: ReaderAst<any>;
|
132
|
+
|
133
|
+
// TODO we should not type these as any
|
134
|
+
readonly entrypoint:
|
135
|
+
| IsographEntrypoint<any, any>
|
136
|
+
| IsographEntrypointLoader<any, any>;
|
137
|
+
};
|
138
|
+
|
139
|
+
type StableId = string;
|
140
|
+
/// Why is LoadableField the way it is? Let's work backwards.
|
141
|
+
///
|
142
|
+
/// We ultimately need a stable id (for deduplication) and a way to produce a
|
143
|
+
/// FragmentReference (i.e. a Factory). However, this stable id depends on the
|
144
|
+
/// arguments that we pass in, hence we get the current form of LoadableField.
|
145
|
+
///
|
146
|
+
/// Passing TArgs to the LoadableField should be cheap and do no "actual" work,
|
147
|
+
/// except to stringify the args or whatnot. Calling the factory can be
|
148
|
+
/// expensive. For example, doing so will probably trigger a network request.
|
149
|
+
export type LoadableField<
|
150
|
+
TReadFromStore extends { data: object; parameters: object },
|
151
|
+
TResult,
|
152
|
+
TArgs = ExtractParameters<TReadFromStore>,
|
153
|
+
> = (
|
154
|
+
args: TArgs | void,
|
155
|
+
// Note: fetchOptions is not nullable here because a LoadableField is not a
|
156
|
+
// user-facing API. Users should only interact with LoadableFields via APIs
|
157
|
+
// like useClientSideDefer. These APIs should have a nullable fetchOptions
|
158
|
+
// parameter, and provide a default value ({}) to the LoadableField.
|
159
|
+
fetchOptions: FetchOptions,
|
160
|
+
) => [StableId, Factory<FragmentReference<TReadFromStore, TResult>>];
|
package/src/core/util.ts
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
export type ExtractSecondParam<T extends (arg1: any, arg2: any) => any> =
|
2
|
+
T extends (arg1: any, arg2: infer P) => any ? P : never;
|
3
|
+
export type CombineWithIntrinsicAttributes<T> =
|
4
|
+
T extends Record<PropertyKey, never>
|
5
|
+
? JSX.IntrinsicAttributes
|
6
|
+
: T & JSX.IntrinsicAttributes;
|
7
|
+
|
8
|
+
export type Arguments = Argument[];
|
9
|
+
export type Argument = [ArgumentName, ArgumentValue];
|
10
|
+
export type ArgumentName = string;
|
11
|
+
export type ArgumentValue =
|
12
|
+
| {
|
13
|
+
readonly kind: 'Variable';
|
14
|
+
readonly name: string;
|
15
|
+
}
|
16
|
+
| {
|
17
|
+
readonly kind: 'Literal';
|
18
|
+
readonly value: any;
|
19
|
+
}
|
20
|
+
| {
|
21
|
+
readonly kind: 'String';
|
22
|
+
readonly value: string;
|
23
|
+
}
|
24
|
+
| {
|
25
|
+
readonly kind: 'Enum';
|
26
|
+
readonly value: string;
|
27
|
+
};
|
package/src/index.ts
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
export {
|
2
|
+
retainQuery,
|
3
|
+
unretainQuery,
|
4
|
+
type RetainedQuery,
|
5
|
+
garbageCollectEnvironment,
|
6
|
+
} from './core/garbageCollection';
|
7
|
+
export {
|
8
|
+
type PromiseWrapper,
|
9
|
+
readPromise,
|
10
|
+
getPromiseState,
|
11
|
+
wrapResolvedValue,
|
12
|
+
wrapPromise,
|
13
|
+
} from './core/PromiseWrapper';
|
14
|
+
export { subscribe, normalizeData } from './core/cache';
|
15
|
+
export { makeNetworkRequest } from './core/makeNetworkRequest';
|
16
|
+
export {
|
17
|
+
ROOT_ID,
|
18
|
+
type DataId,
|
19
|
+
type DataTypeValue,
|
20
|
+
type IsographEnvironment,
|
21
|
+
type IsographNetworkFunction,
|
22
|
+
type IsographStore,
|
23
|
+
type Link,
|
24
|
+
type StoreRecord,
|
25
|
+
createIsographEnvironment,
|
26
|
+
createIsographStore,
|
27
|
+
} from './core/IsographEnvironment';
|
28
|
+
export {
|
29
|
+
type EagerReaderArtifact,
|
30
|
+
type ComponentReaderArtifact,
|
31
|
+
type RefetchReaderArtifact,
|
32
|
+
type ReaderAst,
|
33
|
+
type ReaderAstNode,
|
34
|
+
type ReaderLinkedField,
|
35
|
+
type ReaderNonLoadableResolverField,
|
36
|
+
type ReaderScalarField,
|
37
|
+
type TopLevelReaderArtifact,
|
38
|
+
type LoadableField,
|
39
|
+
type ResolverFirstParameter,
|
40
|
+
} from './core/reader';
|
41
|
+
export {
|
42
|
+
type NormalizationAst,
|
43
|
+
type NormalizationAstNode,
|
44
|
+
type NormalizationLinkedField,
|
45
|
+
type NormalizationScalarField,
|
46
|
+
type IsographEntrypoint,
|
47
|
+
assertIsEntrypoint,
|
48
|
+
type RefetchQueryNormalizationArtifact,
|
49
|
+
type RefetchQueryNormalizationArtifactWrapper,
|
50
|
+
type ExtractProps,
|
51
|
+
type ExtractReadFromStore,
|
52
|
+
type ExtractResolverResult,
|
53
|
+
type NetworkRequestInfo,
|
54
|
+
} from './core/entrypoint';
|
55
|
+
export { readButDoNotEvaluate } from './core/read';
|
56
|
+
export {
|
57
|
+
type ExtractSecondParam,
|
58
|
+
type CombineWithIntrinsicAttributes,
|
59
|
+
type Argument,
|
60
|
+
type ArgumentName,
|
61
|
+
type ArgumentValue,
|
62
|
+
type Arguments,
|
63
|
+
} from './core/util';
|
64
|
+
export {
|
65
|
+
type FragmentReference,
|
66
|
+
type Variables,
|
67
|
+
type ExtractParameters,
|
68
|
+
type ExtractData,
|
69
|
+
stableIdForFragmentReference,
|
70
|
+
} from './core/FragmentReference';
|
71
|
+
export {
|
72
|
+
type LogMessage,
|
73
|
+
type LogFunction,
|
74
|
+
logMessage,
|
75
|
+
registerLogger,
|
76
|
+
} from './core/logging';
|
77
|
+
export { check, CheckResult, FetchOptions, ShouldFetch } from './core/check';
|
78
|
+
|
79
|
+
export {
|
80
|
+
IsographEnvironmentProvider,
|
81
|
+
useIsographEnvironment,
|
82
|
+
type IsographEnvironmentProviderProps,
|
83
|
+
} from './react/IsographEnvironmentProvider';
|
84
|
+
export { useImperativeReference } from './react/useImperativeReference';
|
85
|
+
export { FragmentReader } from './react/FragmentReader';
|
86
|
+
export { useResult } from './react/useResult';
|
87
|
+
export {
|
88
|
+
useReadAndSubscribe,
|
89
|
+
useSubscribeToMultiple,
|
90
|
+
} from './react/useReadAndSubscribe';
|
91
|
+
export { useLazyReference } from './react/useLazyReference';
|
92
|
+
export { useRerenderOnChange } from './react/useRerenderOnChange';
|
93
|
+
export { RenderAfterCommit__DO_NOT_USE } from './react/RenderAfterCommit__DO_NOT_USE';
|
94
|
+
|
95
|
+
export { useClientSideDefer } from './loadable-hooks/useClientSideDefer';
|
96
|
+
export { useImperativeExposedMutationField } from './loadable-hooks/useImperativeExposedMutationField';
|
97
|
+
export { useSkipLimitPagination } from './loadable-hooks/useSkipLimitPagination';
|
98
|
+
export { useConnectionSpecPagination } from './loadable-hooks/useConnectionSpecPagination';
|
99
|
+
export { useImperativeLoadableField } from './loadable-hooks/useImperativeLoadableField';
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import {
|
2
|
+
ExtractParameters,
|
3
|
+
FragmentReference,
|
4
|
+
} from '../core/FragmentReference';
|
5
|
+
import { useIsographEnvironment } from '../react/IsographEnvironmentProvider';
|
6
|
+
import { getOrCreateItemInSuspenseCache } from '../core/cache';
|
7
|
+
import { useLazyDisposableState } from '@isograph/react-disposable-state';
|
8
|
+
import { LoadableField } from '../core/reader';
|
9
|
+
import { FetchOptions } from '../core/check';
|
10
|
+
|
11
|
+
export function useClientSideDefer<
|
12
|
+
TReadFromStore extends { data: object; parameters: object },
|
13
|
+
TResult,
|
14
|
+
>(
|
15
|
+
loadableField: LoadableField<
|
16
|
+
TReadFromStore,
|
17
|
+
TResult,
|
18
|
+
ExtractParameters<TReadFromStore>
|
19
|
+
>,
|
20
|
+
args?: Record<PropertyKey, never>,
|
21
|
+
fetchOptions?: FetchOptions,
|
22
|
+
): { fragmentReference: FragmentReference<TReadFromStore, TResult> };
|
23
|
+
|
24
|
+
export function useClientSideDefer<
|
25
|
+
TReadFromStore extends { data: object; parameters: object },
|
26
|
+
TResult,
|
27
|
+
TProvidedArgs extends object,
|
28
|
+
>(
|
29
|
+
loadableField: LoadableField<
|
30
|
+
TReadFromStore,
|
31
|
+
TResult,
|
32
|
+
Omit<ExtractParameters<TReadFromStore>, keyof TProvidedArgs>
|
33
|
+
>,
|
34
|
+
args: Omit<ExtractParameters<TReadFromStore>, keyof TProvidedArgs>,
|
35
|
+
fetchOptions?: FetchOptions,
|
36
|
+
): { fragmentReference: FragmentReference<TReadFromStore, TResult> };
|
37
|
+
|
38
|
+
export function useClientSideDefer<
|
39
|
+
TReadFromStore extends { data: object; parameters: object },
|
40
|
+
TResult,
|
41
|
+
TProvidedArgs extends object,
|
42
|
+
>(
|
43
|
+
loadableField: LoadableField<
|
44
|
+
TReadFromStore,
|
45
|
+
TResult,
|
46
|
+
Omit<ExtractParameters<TReadFromStore>, keyof TProvidedArgs>
|
47
|
+
>,
|
48
|
+
args?: Omit<ExtractParameters<TReadFromStore>, keyof TProvidedArgs>,
|
49
|
+
fetchOptions?: FetchOptions,
|
50
|
+
): { fragmentReference: FragmentReference<TReadFromStore, TResult> } {
|
51
|
+
const [id, loader] = loadableField(args, fetchOptions ?? {});
|
52
|
+
const environment = useIsographEnvironment();
|
53
|
+
const cache = getOrCreateItemInSuspenseCache(environment, id, loader);
|
54
|
+
|
55
|
+
const fragmentReference = useLazyDisposableState(cache).state;
|
56
|
+
|
57
|
+
return { fragmentReference };
|
58
|
+
}
|
@@ -0,0 +1,331 @@
|
|
1
|
+
import { ItemCleanupPair } from '@isograph/disposable-types';
|
2
|
+
import {
|
3
|
+
UNASSIGNED_STATE,
|
4
|
+
useUpdatableDisposableState,
|
5
|
+
} from '@isograph/react-disposable-state';
|
6
|
+
import {
|
7
|
+
createReferenceCountedPointer,
|
8
|
+
ReferenceCountedPointer,
|
9
|
+
} from '@isograph/reference-counted-pointer';
|
10
|
+
import { useState } from 'react';
|
11
|
+
import { subscribeToAnyChange } from '../core/cache';
|
12
|
+
import { FragmentReference } from '../core/FragmentReference';
|
13
|
+
import { getPromiseState, readPromise } from '../core/PromiseWrapper';
|
14
|
+
import {
|
15
|
+
readButDoNotEvaluate,
|
16
|
+
type WithEncounteredRecords,
|
17
|
+
} from '../core/read';
|
18
|
+
import { LoadableField, type ReaderAst } from '../core/reader';
|
19
|
+
import { useIsographEnvironment } from '../react/IsographEnvironmentProvider';
|
20
|
+
import { useSubscribeToMultiple } from '../react/useReadAndSubscribe';
|
21
|
+
import { maybeUnwrapNetworkRequest } from '../react/useResult';
|
22
|
+
import { FetchOptions } from '../core/check';
|
23
|
+
|
24
|
+
type UsePaginationReturnValue<
|
25
|
+
TReadFromStore extends { parameters: object; data: object },
|
26
|
+
TItem,
|
27
|
+
> =
|
28
|
+
| {
|
29
|
+
kind: 'Pending';
|
30
|
+
pendingFragment: FragmentReference<TReadFromStore, Connection<TItem>>;
|
31
|
+
results: ReadonlyArray<TItem>;
|
32
|
+
}
|
33
|
+
| {
|
34
|
+
kind: 'Complete';
|
35
|
+
fetchMore: (count: number, fetchOptions?: FetchOptions) => void;
|
36
|
+
results: ReadonlyArray<TItem>;
|
37
|
+
hasNextPage: boolean;
|
38
|
+
};
|
39
|
+
|
40
|
+
type LoadedFragmentReferences<
|
41
|
+
TReadFromStore extends { parameters: object; data: object },
|
42
|
+
TItem,
|
43
|
+
> = ReadonlyArray<LoadedFragmentReference<TReadFromStore, TItem>>;
|
44
|
+
|
45
|
+
type LoadedFragmentReference<
|
46
|
+
TReadFromStore extends { parameters: object; data: object },
|
47
|
+
TItem,
|
48
|
+
> = ItemCleanupPair<
|
49
|
+
ReferenceCountedPointer<FragmentReference<TReadFromStore, TItem>>
|
50
|
+
>;
|
51
|
+
|
52
|
+
function flatten<T>(arr: ReadonlyArray<ReadonlyArray<T>>): ReadonlyArray<T> {
|
53
|
+
let outArray: Array<T> = [];
|
54
|
+
for (const subarr of arr) {
|
55
|
+
for (const item of subarr) {
|
56
|
+
outArray.push(item);
|
57
|
+
}
|
58
|
+
}
|
59
|
+
return outArray;
|
60
|
+
}
|
61
|
+
|
62
|
+
type PageInfo = {
|
63
|
+
readonly hasNextPage: boolean;
|
64
|
+
readonly endCursor: string | null;
|
65
|
+
};
|
66
|
+
|
67
|
+
type Connection<T> = {
|
68
|
+
readonly edges: ReadonlyArray<T> | null;
|
69
|
+
readonly pageInfo: PageInfo;
|
70
|
+
};
|
71
|
+
|
72
|
+
type NonNullConnection<T> = {
|
73
|
+
readonly edges: ReadonlyArray<T>;
|
74
|
+
readonly pageInfo: PageInfo;
|
75
|
+
};
|
76
|
+
|
77
|
+
type UseConnectionSpecPaginationArgs = {
|
78
|
+
first: number;
|
79
|
+
after: string | null;
|
80
|
+
};
|
81
|
+
|
82
|
+
export function useConnectionSpecPagination<
|
83
|
+
TReadFromStore extends {
|
84
|
+
parameters: object;
|
85
|
+
data: object;
|
86
|
+
},
|
87
|
+
TItem,
|
88
|
+
>(
|
89
|
+
loadableField: LoadableField<
|
90
|
+
TReadFromStore,
|
91
|
+
Connection<TItem>,
|
92
|
+
UseConnectionSpecPaginationArgs
|
93
|
+
>,
|
94
|
+
initialState?: PageInfo,
|
95
|
+
): UsePaginationReturnValue<TReadFromStore, TItem> {
|
96
|
+
const networkRequestOptions = {
|
97
|
+
suspendIfInFlight: true,
|
98
|
+
throwOnNetworkError: true,
|
99
|
+
};
|
100
|
+
const { state, setState } =
|
101
|
+
useUpdatableDisposableState<
|
102
|
+
LoadedFragmentReferences<TReadFromStore, Connection<TItem>>
|
103
|
+
>();
|
104
|
+
|
105
|
+
const environment = useIsographEnvironment();
|
106
|
+
|
107
|
+
// TODO move this out of useSkipLimitPagination, and pass environment and networkRequestOptions
|
108
|
+
// as parameters (or recreate networkRequestOptions)
|
109
|
+
function readCompletedFragmentReferences(
|
110
|
+
completedReferences: FragmentReference<TReadFromStore, Connection<TItem>>[],
|
111
|
+
): NonNullConnection<TItem> {
|
112
|
+
const results = completedReferences.map((fragmentReference, i) => {
|
113
|
+
const readerWithRefetchQueries = readPromise(
|
114
|
+
fragmentReference.readerWithRefetchQueries,
|
115
|
+
);
|
116
|
+
|
117
|
+
// invariant: readOutDataAndRecords.length === completedReferences.length
|
118
|
+
const data = readOutDataAndRecords[i]?.item;
|
119
|
+
if (data == null) {
|
120
|
+
throw new Error(
|
121
|
+
'Parameter data is unexpectedly null. This is indicative of a bug in Isograph.',
|
122
|
+
);
|
123
|
+
}
|
124
|
+
|
125
|
+
const firstParameter = {
|
126
|
+
data,
|
127
|
+
parameters: fragmentReference.variables,
|
128
|
+
};
|
129
|
+
|
130
|
+
if (
|
131
|
+
readerWithRefetchQueries.readerArtifact.kind !== 'EagerReaderArtifact'
|
132
|
+
) {
|
133
|
+
throw new Error(
|
134
|
+
`@loadable field of kind "${readerWithRefetchQueries.readerArtifact.kind}" is not supported by useSkipLimitPagination`,
|
135
|
+
);
|
136
|
+
}
|
137
|
+
|
138
|
+
return readerWithRefetchQueries.readerArtifact.resolver(firstParameter);
|
139
|
+
});
|
140
|
+
|
141
|
+
const items = flatten(results.map((result) => result.edges ?? []));
|
142
|
+
|
143
|
+
return {
|
144
|
+
edges: items,
|
145
|
+
pageInfo: results[results.length - 1]?.pageInfo ?? {
|
146
|
+
endCursor: null,
|
147
|
+
hasNextPage: true,
|
148
|
+
},
|
149
|
+
};
|
150
|
+
}
|
151
|
+
|
152
|
+
function subscribeCompletedFragmentReferences(
|
153
|
+
completedReferences: FragmentReference<TReadFromStore, Connection<TItem>>[],
|
154
|
+
) {
|
155
|
+
return completedReferences.map(
|
156
|
+
(
|
157
|
+
fragmentReference,
|
158
|
+
i,
|
159
|
+
): {
|
160
|
+
records: WithEncounteredRecords<TReadFromStore>;
|
161
|
+
callback: (
|
162
|
+
updatedRecords: WithEncounteredRecords<TReadFromStore>,
|
163
|
+
) => void;
|
164
|
+
fragmentReference: FragmentReference<TReadFromStore, Connection<TItem>>;
|
165
|
+
readerAst: ReaderAst<Connection<TItem>>;
|
166
|
+
} => {
|
167
|
+
maybeUnwrapNetworkRequest(
|
168
|
+
fragmentReference.networkRequest,
|
169
|
+
networkRequestOptions,
|
170
|
+
);
|
171
|
+
|
172
|
+
const readerWithRefetchQueries = readPromise(
|
173
|
+
fragmentReference.readerWithRefetchQueries,
|
174
|
+
);
|
175
|
+
|
176
|
+
const records = readOutDataAndRecords[i];
|
177
|
+
if (records == null) {
|
178
|
+
throw new Error(
|
179
|
+
'subscribeCompletedFragmentReferences records is unexpectedly null',
|
180
|
+
);
|
181
|
+
}
|
182
|
+
|
183
|
+
return {
|
184
|
+
fragmentReference,
|
185
|
+
readerAst: readerWithRefetchQueries.readerArtifact.readerAst,
|
186
|
+
records,
|
187
|
+
callback(_data) {
|
188
|
+
rerender({});
|
189
|
+
},
|
190
|
+
};
|
191
|
+
},
|
192
|
+
);
|
193
|
+
}
|
194
|
+
|
195
|
+
const getFetchMore =
|
196
|
+
(after: string | null) =>
|
197
|
+
(count: number, fetchOptions?: FetchOptions): void => {
|
198
|
+
const loadedField = loadableField(
|
199
|
+
{
|
200
|
+
after: after,
|
201
|
+
first: count,
|
202
|
+
},
|
203
|
+
fetchOptions ?? {},
|
204
|
+
)[1]();
|
205
|
+
const newPointer = createReferenceCountedPointer(loadedField);
|
206
|
+
const clonedPointers = loadedReferences.map(([refCountedPointer]) => {
|
207
|
+
const clonedRefCountedPointer = refCountedPointer.cloneIfNotDisposed();
|
208
|
+
if (clonedRefCountedPointer == null) {
|
209
|
+
throw new Error(
|
210
|
+
'This reference counted pointer has already been disposed. \
|
211
|
+
This is indicative of a bug in useSkipLimitPagination.',
|
212
|
+
);
|
213
|
+
}
|
214
|
+
return clonedRefCountedPointer;
|
215
|
+
});
|
216
|
+
clonedPointers.push(newPointer);
|
217
|
+
|
218
|
+
const totalItemCleanupPair: ItemCleanupPair<
|
219
|
+
ReadonlyArray<
|
220
|
+
ItemCleanupPair<
|
221
|
+
ReferenceCountedPointer<
|
222
|
+
FragmentReference<TReadFromStore, Connection<TItem>>
|
223
|
+
>
|
224
|
+
>
|
225
|
+
>
|
226
|
+
> = [
|
227
|
+
clonedPointers,
|
228
|
+
() => {
|
229
|
+
clonedPointers.forEach(([, dispose]) => {
|
230
|
+
dispose();
|
231
|
+
});
|
232
|
+
},
|
233
|
+
];
|
234
|
+
|
235
|
+
setState(totalItemCleanupPair);
|
236
|
+
};
|
237
|
+
|
238
|
+
const [, rerender] = useState({});
|
239
|
+
|
240
|
+
const loadedReferences = state === UNASSIGNED_STATE ? [] : state;
|
241
|
+
|
242
|
+
const mostRecentItem:
|
243
|
+
| LoadedFragmentReference<TReadFromStore, Connection<TItem>>
|
244
|
+
| undefined = loadedReferences[loadedReferences.length - 1];
|
245
|
+
const mostRecentFragmentReference =
|
246
|
+
mostRecentItem?.[0].getItemIfNotDisposed();
|
247
|
+
|
248
|
+
if (mostRecentItem && mostRecentFragmentReference === null) {
|
249
|
+
throw new Error(
|
250
|
+
'FragmentReference is unexpectedly disposed. \
|
251
|
+
This is indicative of a bug in Isograph.',
|
252
|
+
);
|
253
|
+
}
|
254
|
+
|
255
|
+
const networkRequestStatus =
|
256
|
+
mostRecentFragmentReference &&
|
257
|
+
getPromiseState(mostRecentFragmentReference.networkRequest);
|
258
|
+
|
259
|
+
const slicedFragmentReferences =
|
260
|
+
networkRequestStatus?.kind === 'Ok'
|
261
|
+
? loadedReferences
|
262
|
+
: loadedReferences.slice(0, loadedReferences.length - 1);
|
263
|
+
|
264
|
+
const completedFragmentReferences = slicedFragmentReferences.map(
|
265
|
+
([pointer]) => {
|
266
|
+
const fragmentReference = pointer.getItemIfNotDisposed();
|
267
|
+
if (fragmentReference == null) {
|
268
|
+
throw new Error(
|
269
|
+
'FragmentReference is unexpectedly disposed. \
|
270
|
+
This is indicative of a bug in Isograph.',
|
271
|
+
);
|
272
|
+
}
|
273
|
+
return fragmentReference;
|
274
|
+
},
|
275
|
+
);
|
276
|
+
|
277
|
+
const readOutDataAndRecords = completedFragmentReferences.map(
|
278
|
+
(fragmentReference) =>
|
279
|
+
readButDoNotEvaluate(
|
280
|
+
environment,
|
281
|
+
fragmentReference,
|
282
|
+
networkRequestOptions,
|
283
|
+
),
|
284
|
+
);
|
285
|
+
|
286
|
+
useSubscribeToMultiple<TReadFromStore>(
|
287
|
+
subscribeCompletedFragmentReferences(completedFragmentReferences),
|
288
|
+
);
|
289
|
+
|
290
|
+
if (!networkRequestStatus) {
|
291
|
+
return {
|
292
|
+
kind: 'Complete',
|
293
|
+
fetchMore: getFetchMore(initialState?.endCursor ?? null),
|
294
|
+
results: [],
|
295
|
+
hasNextPage: initialState?.hasNextPage ?? true,
|
296
|
+
};
|
297
|
+
}
|
298
|
+
|
299
|
+
switch (networkRequestStatus.kind) {
|
300
|
+
case 'Pending': {
|
301
|
+
const unsubscribe = subscribeToAnyChange(environment, () => {
|
302
|
+
unsubscribe();
|
303
|
+
rerender({});
|
304
|
+
});
|
305
|
+
|
306
|
+
const results = readCompletedFragmentReferences(
|
307
|
+
completedFragmentReferences,
|
308
|
+
);
|
309
|
+
return {
|
310
|
+
results: results.edges,
|
311
|
+
kind: 'Pending',
|
312
|
+
pendingFragment: mostRecentFragmentReference,
|
313
|
+
};
|
314
|
+
}
|
315
|
+
case 'Err': {
|
316
|
+
throw networkRequestStatus.error;
|
317
|
+
}
|
318
|
+
case 'Ok': {
|
319
|
+
const results = readCompletedFragmentReferences(
|
320
|
+
completedFragmentReferences,
|
321
|
+
);
|
322
|
+
|
323
|
+
return {
|
324
|
+
results: results.edges,
|
325
|
+
hasNextPage: results.pageInfo.hasNextPage,
|
326
|
+
kind: 'Complete',
|
327
|
+
fetchMore: getFetchMore(results.pageInfo.endCursor),
|
328
|
+
};
|
329
|
+
}
|
330
|
+
}
|
331
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
type UseImperativeLoadableFieldReturn<TArgs> = {
|
2
|
+
loadField: (args: TArgs) => void;
|
3
|
+
};
|
4
|
+
|
5
|
+
// Note: this function doesn't seem to work if there are additional arguments,
|
6
|
+
// e.g. with set_pet_tagline. Why? This seems to straightforwardly call
|
7
|
+
// exposedField(args)[1](); Odd.
|
8
|
+
export function useImperativeExposedMutationField<TArgs>(
|
9
|
+
exposedField: (args: TArgs) => [string, () => void],
|
10
|
+
): UseImperativeLoadableFieldReturn<TArgs> {
|
11
|
+
return {
|
12
|
+
loadField: (args: TArgs) => {
|
13
|
+
const [_id, loader] = exposedField(args);
|
14
|
+
loader();
|
15
|
+
},
|
16
|
+
};
|
17
|
+
}
|