@octanejs/apollo-client 0.1.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.
Files changed (85) hide show
  1. package/LICENSE +49 -0
  2. package/README.md +97 -0
  3. package/package.json +77 -0
  4. package/src/index.d.ts +1 -0
  5. package/src/index.js +3 -0
  6. package/src/react/context/ApolloContext.d.ts +8 -0
  7. package/src/react/context/ApolloContext.js +21 -0
  8. package/src/react/context/ApolloProvider.d.ts +9 -0
  9. package/src/react/context/ApolloProvider.js +15 -0
  10. package/src/react/hooks/constants.d.ts +2 -0
  11. package/src/react/hooks/constants.js +1 -0
  12. package/src/react/hooks/internal/__use.d.ts +3 -0
  13. package/src/react/hooks/internal/__use.js +9 -0
  14. package/src/react/hooks/internal/index.d.ts +5 -0
  15. package/src/react/hooks/internal/index.js +6 -0
  16. package/src/react/hooks/internal/site.d.ts +2 -0
  17. package/src/react/hooks/internal/site.js +20 -0
  18. package/src/react/hooks/internal/useDeepMemo.d.ts +5 -0
  19. package/src/react/hooks/internal/useDeepMemo.js +11 -0
  20. package/src/react/hooks/internal/useIsomorphicLayoutEffect.d.ts +2 -0
  21. package/src/react/hooks/internal/useIsomorphicLayoutEffect.js +8 -0
  22. package/src/react/hooks/internal/useRenderGuard.d.ts +1 -0
  23. package/src/react/hooks/internal/useRenderGuard.js +14 -0
  24. package/src/react/hooks/internal/useSuspenseHookCacheKey.d.ts +13 -0
  25. package/src/react/hooks/internal/useSuspenseHookCacheKey.js +19 -0
  26. package/src/react/hooks/internal/validateSuspenseHookOptions.d.ts +4 -0
  27. package/src/react/hooks/internal/validateSuspenseHookOptions.js +16 -0
  28. package/src/react/hooks/internal/wrapHook.d.ts +77 -0
  29. package/src/react/hooks/internal/wrapHook.js +64 -0
  30. package/src/react/hooks/useApolloClient.d.ts +17 -0
  31. package/src/react/hooks/useApolloClient.js +27 -0
  32. package/src/react/hooks/useBackgroundQuery.d.ts +3061 -0
  33. package/src/react/hooks/useBackgroundQuery.js +88 -0
  34. package/src/react/hooks/useFragment.d.ts +205 -0
  35. package/src/react/hooks/useFragment.js +75 -0
  36. package/src/react/hooks/useLazyQuery.d.ts +947 -0
  37. package/src/react/hooks/useLazyQuery.js +156 -0
  38. package/src/react/hooks/useLoadableQuery.d.ts +955 -0
  39. package/src/react/hooks/useLoadableQuery.js +98 -0
  40. package/src/react/hooks/useMutation.d.ts +585 -0
  41. package/src/react/hooks/useMutation.js +121 -0
  42. package/src/react/hooks/useQuery.d.ts +1401 -0
  43. package/src/react/hooks/useQuery.js +234 -0
  44. package/src/react/hooks/useQueryRefHandlers.d.ts +85 -0
  45. package/src/react/hooks/useQueryRefHandlers.js +79 -0
  46. package/src/react/hooks/useReactiveVar.d.ts +21 -0
  47. package/src/react/hooks/useReactiveVar.js +41 -0
  48. package/src/react/hooks/useReadQuery.d.ts +104 -0
  49. package/src/react/hooks/useReadQuery.js +83 -0
  50. package/src/react/hooks/useSubscription.d.ts +289 -0
  51. package/src/react/hooks/useSubscription.js +258 -0
  52. package/src/react/hooks/useSuspenseFragment.d.ts +154 -0
  53. package/src/react/hooks/useSuspenseFragment.js +50 -0
  54. package/src/react/hooks/useSuspenseQuery.d.ts +1629 -0
  55. package/src/react/hooks/useSuspenseQuery.js +130 -0
  56. package/src/react/hooks/useSyncExternalStore.d.ts +2 -0
  57. package/src/react/hooks/useSyncExternalStore.js +125 -0
  58. package/src/react/index.d.ts +68 -0
  59. package/src/react/index.js +18 -0
  60. package/src/react/internal/cache/FragmentReference.d.ts +41 -0
  61. package/src/react/internal/cache/FragmentReference.js +117 -0
  62. package/src/react/internal/cache/QueryReference.d.ts +180 -0
  63. package/src/react/internal/cache/QueryReference.js +310 -0
  64. package/src/react/internal/cache/SuspenseCache.d.ts +38 -0
  65. package/src/react/internal/cache/SuspenseCache.js +39 -0
  66. package/src/react/internal/cache/getSuspenseCache.d.ts +26 -0
  67. package/src/react/internal/cache/getSuspenseCache.js +8 -0
  68. package/src/react/internal/cache/types.d.ts +13 -0
  69. package/src/react/internal/cache/types.js +1 -0
  70. package/src/react/internal/index.d.ts +16 -0
  71. package/src/react/internal/index.js +10 -0
  72. package/src/react/internal/types.d.ts +15 -0
  73. package/src/react/internal/types.js +1 -0
  74. package/src/react/query-preloader/createQueryPreloader.d.ts +630 -0
  75. package/src/react/query-preloader/createQueryPreloader.js +89 -0
  76. package/src/react/types/deprecated.d.ts +140 -0
  77. package/src/react/types/deprecated.js +1 -0
  78. package/src/react/types/types.documentation.d.ts +551 -0
  79. package/src/react/types/types.documentation.js +1 -0
  80. package/src/testing/index.d.ts +1 -0
  81. package/src/testing/index.js +1 -0
  82. package/src/testing/react/MockedProvider.d.ts +21 -0
  83. package/src/testing/react/MockedProvider.js +53 -0
  84. package/src/testing/react/index.d.ts +2 -0
  85. package/src/testing/react/index.js +1 -0
@@ -0,0 +1,88 @@
1
+ import * as React from 'octane';
2
+ import {
3
+ getSuspenseCache,
4
+ unwrapQueryRef,
5
+ updateWrappedQueryRef,
6
+ wrapQueryRef,
7
+ } from '@octanejs/apollo-client/react/internal';
8
+ import { skipToken } from './constants.js';
9
+ import { useSuspenseHookCacheKey, wrapHook } from './internal/index.js';
10
+ import { isCompilerSite } from './internal/site.js';
11
+ import { useApolloClient } from './useApolloClient.js';
12
+ import { useWatchQueryOptions } from './useSuspenseQuery.js';
13
+ export const useBackgroundQuery = function useBackgroundQuery(query, options) {
14
+ 'use no memo';
15
+ if (isCompilerSite(options, skipToken)) options = undefined;
16
+ return wrapHook(
17
+ 'useBackgroundQuery',
18
+ useBackgroundQuery_,
19
+ useApolloClient(typeof options === 'object' ? options.client : undefined),
20
+ )(query, options ?? {});
21
+ };
22
+ function useBackgroundQuery_(query, options) {
23
+ const client = useApolloClient(options.client);
24
+ const suspenseCache = getSuspenseCache(client);
25
+ const watchQueryOptions = useWatchQueryOptions({ client, query, options });
26
+ const { fetchPolicy } = watchQueryOptions;
27
+ const cacheKey = useSuspenseHookCacheKey(query, options);
28
+ // This ref tracks the first time query execution is enabled to determine
29
+ // whether to return a query ref or `undefined`. When initialized
30
+ // in a skipped state (either via `skip: true` or `skipToken`) we return
31
+ // `undefined` for the `queryRef` until the query has been enabled. Once
32
+ // enabled, a query ref is always returned regardless of whether the query is
33
+ // skipped again later.
34
+ const didFetchResult = React.useRef(fetchPolicy !== 'standby');
35
+ didFetchResult.current ||= fetchPolicy !== 'standby';
36
+ const queryRef = suspenseCache.getQueryRef(cacheKey, () => client.watchQuery(watchQueryOptions));
37
+ const [wrappedQueryRef, setWrappedQueryRef] = React.useState(wrapQueryRef(queryRef));
38
+ if (unwrapQueryRef(wrappedQueryRef) !== queryRef) {
39
+ setWrappedQueryRef(wrapQueryRef(queryRef));
40
+ }
41
+ if (queryRef.didChangeOptions(watchQueryOptions)) {
42
+ const promise = queryRef.applyOptions(watchQueryOptions);
43
+ updateWrappedQueryRef(wrappedQueryRef, promise);
44
+ }
45
+ // This prevents issues where rerendering useBackgroundQuery after the
46
+ // queryRef has been disposed would cause the hook to return a new queryRef
47
+ // instance since disposal also removes it from the suspense cache. We add
48
+ // the queryRef back in the suspense cache so that the next render will reuse
49
+ // this queryRef rather than initializing a new instance.
50
+ React.useEffect(() => {
51
+ // Since the queryRef is disposed async via `setTimeout`, we have to wait a
52
+ // tick before checking it and adding back to the suspense cache.
53
+ const id = setTimeout(() => {
54
+ if (queryRef.disposed) {
55
+ suspenseCache.add(cacheKey, queryRef);
56
+ }
57
+ });
58
+ return () => clearTimeout(id);
59
+ // Omitting the deps is intentional. This avoids stale closures and the
60
+ // conditional ensures we aren't running the logic on each render.
61
+ }, null);
62
+ const fetchMore = React.useCallback(
63
+ (options) => {
64
+ const promise = queryRef.fetchMore(options);
65
+ setWrappedQueryRef(wrapQueryRef(queryRef));
66
+ return promise;
67
+ },
68
+ [queryRef],
69
+ );
70
+ const refetch = React.useCallback(
71
+ (variables) => {
72
+ const promise = queryRef.refetch(variables);
73
+ setWrappedQueryRef(wrapQueryRef(queryRef));
74
+ return promise;
75
+ },
76
+ [queryRef],
77
+ );
78
+ React.useEffect(() => queryRef.softRetain(), [queryRef]);
79
+ return [
80
+ didFetchResult.current ? wrappedQueryRef : void 0,
81
+ {
82
+ fetchMore,
83
+ refetch,
84
+ // TODO: The internalQueryRef doesn't have TVariables' type information so we have to cast it here
85
+ subscribeToMore: queryRef.observable.subscribeToMore,
86
+ },
87
+ ];
88
+ }
@@ -0,0 +1,205 @@
1
+ import type {
2
+ ApolloClient,
3
+ DataValue,
4
+ DocumentNode,
5
+ GetDataState,
6
+ OperationVariables,
7
+ TypedDocumentNode,
8
+ } from '@apollo/client';
9
+ import type { ApolloCache, MissingTree } from '@apollo/client/cache';
10
+ import type { MaybeMasked } from '@apollo/client/masking';
11
+ import type { NoInfer } from '@apollo/client/utilities/internal';
12
+ export declare namespace useFragment {
13
+ import _self = useFragment;
14
+ interface Options<TData, TVariables extends OperationVariables> {
15
+ /**
16
+ * A GraphQL document created using the `gql` template string tag from
17
+ * `graphql-tag` with one or more fragments which will be used to determine
18
+ * the shape of data to read. If you provide more than one fragment in this
19
+ * document then you must also specify `fragmentName` to select a single.
20
+ */
21
+ fragment: DocumentNode | TypedDocumentNode<TData, TVariables>;
22
+ /**
23
+ * The name of the fragment in your GraphQL document to be used. If you do
24
+ * not provide a `fragmentName` and there is only one fragment in your
25
+ * `fragment` document then that fragment will be used.
26
+ */
27
+ fragmentName?: string;
28
+ /**
29
+ * Any variables that the GraphQL query may depend on.
30
+ */
31
+ variables?: NoInfer<TVariables>;
32
+ /**
33
+ * An object or array containing a `__typename` and primary key fields
34
+ * (such as `id`) identifying the entity object from which the fragment will
35
+ * be retrieved, or a `{ __ref: "..." }` reference, or a `string` ID (uncommon).
36
+ */
37
+ from:
38
+ | useFragment.FromOptionValue<TData>
39
+ | Array<useFragment.FromOptionValue<TData> | null>
40
+ | null;
41
+ /**
42
+ * Whether to read from optimistic or non-optimistic cache data. If
43
+ * this named option is provided, the optimistic parameter of the
44
+ * readQuery method can be omitted.
45
+ *
46
+ * @defaultValue true
47
+ */
48
+ optimistic?: boolean;
49
+ /**
50
+ * The instance of `ApolloClient` to use to look up the fragment.
51
+ *
52
+ * By default, the instance that's passed down via context is used, but you
53
+ * can provide a different instance here.
54
+ *
55
+ * @docGroup 1. Operation options
56
+ */
57
+ client?: ApolloClient;
58
+ }
59
+ namespace DocumentationTypes {
60
+ namespace useFragment {
61
+ interface Options<
62
+ TData = unknown,
63
+ TVariables extends OperationVariables = OperationVariables,
64
+ > extends _self.Options<TData, TVariables> {}
65
+ }
66
+ }
67
+ /**
68
+ * Acceptable values provided to the `from` option.
69
+ */
70
+ type FromOptionValue<TData> = ApolloCache.FromOptionValue<TData>;
71
+ type Result<TData> =
72
+ | ({
73
+ /**
74
+ *
75
+ */
76
+ complete: true;
77
+ /**
78
+ * A tree of all `MissingFieldError` messages reported during fragment reading, where the branches of the tree indicate the paths of the errors within the query result.
79
+ */
80
+ missing?: never;
81
+ } & GetDataState<MaybeMasked<TData>, 'complete'>)
82
+ | {
83
+ /**
84
+ *
85
+ */
86
+ complete: false;
87
+ /**
88
+ * A tree of all `MissingFieldError` messages reported during fragment reading, where the branches of the tree indicate the paths of the errors within the query result.
89
+ */
90
+ missing?: MissingTree;
91
+ /**
92
+ * An object containing the result of your GraphQL query after it completes.
93
+ *
94
+ * This value might be `undefined` if a query results in one or more errors (depending on the query's `errorPolicy`).
95
+ *
96
+ * @docGroup 1. Operation data
97
+ */
98
+ data: TData extends Array<infer TItem>
99
+ ? Array<DataValue.Partial<TItem> | null>
100
+ : DataValue.Partial<TData>;
101
+ /**
102
+ * Describes the completeness of `data`.
103
+ *
104
+ * - `empty`: No data could be fulfilled from the cache or the result is
105
+ * incomplete. `data` is `undefined`.
106
+ * - `partial`: Some data could be fulfilled from the cache but `data` is
107
+ * incomplete. This is only possible when `returnPartialData` is `true`.
108
+ * - `streaming`: `data` is incomplete as a result of a deferred query and
109
+ * the result is still streaming in.
110
+ * - `complete`: `data` is a fully satisfied query result fulfilled
111
+ * either from the cache or network.
112
+ *
113
+ * @docGroup 1. Operation data
114
+ */
115
+ dataState: 'partial';
116
+ };
117
+ namespace DocumentationTypes {
118
+ namespace useFragment {
119
+ interface Result<TData> {
120
+ data: MaybeMasked<TData> | DataValue.Partial<MaybeMasked<TData>>;
121
+ complete: boolean;
122
+ /**
123
+ * A tree of all `MissingFieldError` messages reported during fragment reading, where the branches of the tree indicate the paths of the errors within the query result.
124
+ */
125
+ missing?: MissingTree;
126
+ }
127
+ }
128
+ }
129
+ namespace DocumentationTypes {
130
+ /**
131
+ * `useFragment` represents a lightweight live binding into the Apollo Client Cache and enables Apollo Client to broadcast very specific fragment results to individual components. This hook returns an always-up-to-date view of whatever data the cache currently contains for a given fragment. `useFragment` never triggers network requests of its own.
132
+ *
133
+ * Note that the `useQuery` hook remains the primary hook responsible for querying and populating data in the cache ([see the API reference](./hooks#usequery)). As a result, the component reading the fragment data via `useFragment` is still subscribed to all changes in the query data, but receives updates only when that fragment's specific data change.
134
+ *
135
+ * To view a `useFragment` example, see the [Fragments](https://www.apollographql.com/docs/react/data/fragments#usefragment) page.
136
+ */
137
+ function useFragment<
138
+ TData = unknown,
139
+ TVariables extends OperationVariables = OperationVariables,
140
+ >({
141
+ fragment,
142
+ from,
143
+ fragmentName,
144
+ variables,
145
+ optimistic,
146
+ client,
147
+ }: useFragment.Options<TData, TVariables>): useFragment.Result<TData>;
148
+ }
149
+ }
150
+ /**
151
+ * `useFragment` represents a lightweight live binding into the Apollo Client Cache and enables Apollo Client to broadcast very specific fragment results to individual components. This hook returns an always-up-to-date view of whatever data the cache currently contains for a given fragment. `useFragment` never triggers network requests of its own.
152
+ *
153
+ * Note that the `useQuery` hook remains the primary hook responsible for querying and populating data in the cache ([see the API reference](./hooks#usequery)). As a result, the component reading the fragment data via `useFragment` is still subscribed to all changes in the query data, but receives updates only when that fragment's specific data change.
154
+ *
155
+ * To view a `useFragment` example, see the [Fragments](https://www.apollographql.com/docs/react/data/fragments#usefragment) page.
156
+ */
157
+ export declare function useFragment<
158
+ TData = unknown,
159
+ TVariables extends OperationVariables = OperationVariables,
160
+ >(
161
+ options: useFragment.Options<TData, TVariables> & {
162
+ from: Array<useFragment.FromOptionValue<TData>>;
163
+ },
164
+ ): useFragment.Result<Array<TData>>;
165
+ /**
166
+ * `useFragment` represents a lightweight live binding into the Apollo Client Cache and enables Apollo Client to broadcast very specific fragment results to individual components. This hook returns an always-up-to-date view of whatever data the cache currently contains for a given fragment. `useFragment` never triggers network requests of its own.
167
+ *
168
+ * Note that the `useQuery` hook remains the primary hook responsible for querying and populating data in the cache ([see the API reference](./hooks#usequery)). As a result, the component reading the fragment data via `useFragment` is still subscribed to all changes in the query data, but receives updates only when that fragment's specific data change.
169
+ *
170
+ * To view a `useFragment` example, see the [Fragments](https://www.apollographql.com/docs/react/data/fragments#usefragment) page.
171
+ */
172
+ export declare function useFragment<
173
+ TData = unknown,
174
+ TVariables extends OperationVariables = OperationVariables,
175
+ >(
176
+ options: useFragment.Options<TData, TVariables> & {
177
+ from: Array<null>;
178
+ },
179
+ ): useFragment.Result<Array<null>>;
180
+ /**
181
+ * `useFragment` represents a lightweight live binding into the Apollo Client Cache and enables Apollo Client to broadcast very specific fragment results to individual components. This hook returns an always-up-to-date view of whatever data the cache currently contains for a given fragment. `useFragment` never triggers network requests of its own.
182
+ *
183
+ * Note that the `useQuery` hook remains the primary hook responsible for querying and populating data in the cache ([see the API reference](./hooks#usequery)). As a result, the component reading the fragment data via `useFragment` is still subscribed to all changes in the query data, but receives updates only when that fragment's specific data change.
184
+ *
185
+ * To view a `useFragment` example, see the [Fragments](https://www.apollographql.com/docs/react/data/fragments#usefragment) page.
186
+ */
187
+ export declare function useFragment<
188
+ TData = unknown,
189
+ TVariables extends OperationVariables = OperationVariables,
190
+ >(
191
+ options: useFragment.Options<TData, TVariables> & {
192
+ from: Array<useFragment.FromOptionValue<TData> | null>;
193
+ },
194
+ ): useFragment.Result<Array<TData | null>>;
195
+ /**
196
+ * `useFragment` represents a lightweight live binding into the Apollo Client Cache and enables Apollo Client to broadcast very specific fragment results to individual components. This hook returns an always-up-to-date view of whatever data the cache currently contains for a given fragment. `useFragment` never triggers network requests of its own.
197
+ *
198
+ * Note that the `useQuery` hook remains the primary hook responsible for querying and populating data in the cache ([see the API reference](./hooks#usequery)). As a result, the component reading the fragment data via `useFragment` is still subscribed to all changes in the query data, but receives updates only when that fragment's specific data change.
199
+ *
200
+ * To view a `useFragment` example, see the [Fragments](https://www.apollographql.com/docs/react/data/fragments#usefragment) page.
201
+ */
202
+ export declare function useFragment<
203
+ TData = unknown,
204
+ TVariables extends OperationVariables = OperationVariables,
205
+ >(options: useFragment.Options<TData, TVariables>): useFragment.Result<TData>;
@@ -0,0 +1,75 @@
1
+ import * as React from 'octane';
2
+ import { useDeepMemo, wrapHook } from './internal/index.js';
3
+ import { subSite } from './internal/site.js';
4
+ import { useApolloClient } from './useApolloClient.js';
5
+ import { useSyncExternalStore } from './useSyncExternalStore.js';
6
+ export function useFragment(options, site) {
7
+ 'use no memo';
8
+ return wrapHook('useFragment', useFragment_, useApolloClient(options.client))(options, site);
9
+ }
10
+ function useFragment_(options, site) {
11
+ const client = useApolloClient(options.client);
12
+ const { from, ...rest } = options;
13
+ const { cache } = client;
14
+ // We calculate the cache id seperately because we don't want changes to non
15
+ // key fields in the `from` property to recreate the observable. If the cache
16
+ // identifier stays the same between renders, we want to reuse the existing
17
+ // subscription.
18
+ const ids = useDeepMemo(
19
+ () => {
20
+ const fromArray = Array.isArray(from) ? from : [from];
21
+ const ids = fromArray.map((value) =>
22
+ typeof value === 'string' ? value : value === null ? null : cache.identify(value),
23
+ );
24
+ return Array.isArray(from) ? ids : ids[0];
25
+ },
26
+ [cache, from],
27
+ subSite(site, 'ids'),
28
+ );
29
+ const stableOptions = useDeepMemo(
30
+ () => ({ ...rest, from: ids }),
31
+ [rest, ids],
32
+ subSite(site, 'options'),
33
+ );
34
+ const observable = React.useMemo(
35
+ () => client.watchFragment(stableOptions),
36
+ [client, stableOptions],
37
+ );
38
+ // Unfortunately we forgot to update the use case of `from: null` on
39
+ // useFragment in 4.0 to match `useSuspenseFragment`. As such, we need to
40
+ // fallback to data: {} with complete: false when `from` is `null` to maintain
41
+ // backwards compatibility. We should plan to change this in v5.
42
+ const getSnapshot = React.useCallback(
43
+ () => (from === null ? nullResult : observable.getCurrentResult()),
44
+ [from, observable],
45
+ );
46
+ return useSyncExternalStore(
47
+ React.useCallback(
48
+ (update) => {
49
+ let lastTimeout = 0;
50
+ const subscription = observable.subscribe({
51
+ next: () => {
52
+ // If we get another update before we've re-rendered, bail out of
53
+ // the update and try again. This ensures that the relative timing
54
+ // between useQuery and useFragment stays roughly the same as
55
+ // fixed in https://github.com/apollographql/apollo-client/pull/11083
56
+ clearTimeout(lastTimeout);
57
+ lastTimeout = setTimeout(update);
58
+ },
59
+ });
60
+ return () => {
61
+ subscription.unsubscribe();
62
+ clearTimeout(lastTimeout);
63
+ };
64
+ },
65
+ [observable],
66
+ ),
67
+ getSnapshot,
68
+ getSnapshot,
69
+ );
70
+ }
71
+ const nullResult = Object.freeze({
72
+ data: {},
73
+ dataState: 'partial',
74
+ complete: false,
75
+ });