@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
package/LICENSE ADDED
@@ -0,0 +1,49 @@
1
+ Octane binding
2
+ ==============
3
+
4
+ MIT License
5
+
6
+ Copyright (c) 2026 Dominic Gannaway
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all
16
+ copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ SOFTWARE.
25
+
26
+ Apollo Client-derived adapter sources
27
+ =====================================
28
+
29
+ The MIT License (MIT)
30
+
31
+ Copyright (c) 2022 Apollo Graph, Inc. (Formerly Meteor Development Group, Inc.)
32
+
33
+ Permission is hereby granted, free of charge, to any person obtaining a copy
34
+ of this software and associated documentation files (the "Software"), to deal
35
+ in the Software without restriction, including without limitation the rights
36
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
37
+ copies of the Software, and to permit persons to whom the Software is
38
+ furnished to do so, subject to the following conditions:
39
+
40
+ The above copyright notice and this permission notice shall be included in all
41
+ copies or substantial portions of the Software.
42
+
43
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
44
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
45
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
46
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
47
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
48
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
49
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # @octanejs/apollo-client
2
+
3
+ Apollo Client 4 bindings for Octane. The package reuses Apollo's client, cache,
4
+ links, GraphQL types, and testing core unchanged, while porting the published
5
+ React hook adapter to Octane's compiler-slotted hooks.
6
+
7
+ The adapter is pinned and surface-tested against `@apollo/client@4.2.6`.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pnpm add @octanejs/apollo-client @apollo/client graphql rxjs
13
+ ```
14
+
15
+ ## Use
16
+
17
+ Apollo 4 keeps its framework-neutral API at the package root and its component
18
+ adapter on `/react`. The Octane package mirrors that split:
19
+
20
+ ```ts
21
+ import {
22
+ ApolloClient,
23
+ InMemoryCache,
24
+ HttpLink,
25
+ gql,
26
+ type TypedDocumentNode,
27
+ } from '@octanejs/apollo-client';
28
+ import { ApolloProvider, useQuery } from '@octanejs/apollo-client/react';
29
+ ```
30
+
31
+ ```tsrx
32
+ interface ViewerData {
33
+ viewer: {
34
+ id: string;
35
+ name: string;
36
+ };
37
+ }
38
+
39
+ const client = new ApolloClient({
40
+ cache: new InMemoryCache(),
41
+ link: new HttpLink({ uri: '/graphql' }),
42
+ });
43
+
44
+ const GET_VIEWER: TypedDocumentNode<ViewerData> = gql`
45
+ query Viewer {
46
+ viewer {
47
+ id
48
+ name
49
+ }
50
+ }
51
+ `;
52
+
53
+ function Viewer() @{
54
+ const result = useQuery(GET_VIEWER);
55
+
56
+ @if (result.loading) {
57
+ <p>Loading…</p>
58
+ } @else if (result.error) {
59
+ <p>{result.error.message}</p>
60
+ } @else {
61
+ <p>{(result.data?.viewer.name ?? 'Unknown viewer') as string}</p>
62
+ }
63
+ }
64
+
65
+ export function App() @{
66
+ <ApolloProvider client={client}>
67
+ <Viewer />
68
+ </ApolloProvider>
69
+ }
70
+ ```
71
+
72
+ The complete Apollo 4.2.6 client hook surface is available, including lazy
73
+ queries, mutations, subscriptions, fragments, Suspense queries, background and
74
+ loadable queries, query references, `skipToken`, and query preloading.
75
+
76
+ For tests, import Apollo's framework-neutral mocks from
77
+ `@octanejs/apollo-client/testing` and the Octane `MockedProvider` from
78
+ `@octanejs/apollo-client/testing/react`.
79
+
80
+ ## Compatibility
81
+
82
+ - Hooks and public TypeScript overloads track Apollo Client 4.2.6.
83
+ - Suspense unwraps Apollo's stable query promises through Octane `use()` rather
84
+ than throwing promises.
85
+ - Omitted React dependency arrays that intentionally mean “every render” are
86
+ explicit `null` in the port so Octane does not infer a narrower dependency
87
+ set.
88
+ - React Server Components and Apollo's React Compiler output are out of scope.
89
+ - `MockedProvider` accepts normal Octane children blocks; use a descriptor-style
90
+ `children={<App />}` prop when `childProps` must be cloned onto the child.
91
+ - Apollo-aware buffered SSR/cache extraction is not included in this first
92
+ client release.
93
+
94
+ The adapter sources are derived from Apollo Client at commit
95
+ `f934b60720fc828a61e04b00988eeefb83d273bc`, under Apollo's MIT license. Local
96
+ changes are limited to Octane imports, hook semantics, Suspense site identity,
97
+ the render-phase guard, and the functional `MockedProvider`.
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@octanejs/apollo-client",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "engines": {
8
+ "node": ">=22"
9
+ },
10
+ "description": "Apollo Client bindings for the octane renderer — reuses Apollo's framework-neutral core and ports its React adapter onto octane.",
11
+ "author": {
12
+ "name": "Dominic Gannaway",
13
+ "email": "dg@domgan.com"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/octanejs/octane.git",
21
+ "directory": "packages/apollo-client"
22
+ },
23
+ "main": "src/index.js",
24
+ "module": "src/index.js",
25
+ "types": "src/index.d.ts",
26
+ "files": [
27
+ "src",
28
+ "README.md"
29
+ ],
30
+ "exports": {
31
+ ".": {
32
+ "types": "./src/index.d.ts",
33
+ "default": "./src/index.js"
34
+ },
35
+ "./react": {
36
+ "types": "./src/react/index.d.ts",
37
+ "default": "./src/react/index.js"
38
+ },
39
+ "./react/internal": {
40
+ "types": "./src/react/internal/index.d.ts",
41
+ "default": "./src/react/internal/index.js"
42
+ },
43
+ "./testing": {
44
+ "types": "./src/testing/index.d.ts",
45
+ "default": "./src/testing/index.js"
46
+ },
47
+ "./testing/react": {
48
+ "types": "./src/testing/react/index.d.ts",
49
+ "default": "./src/testing/react/index.js"
50
+ }
51
+ },
52
+ "dependencies": {
53
+ "@graphql-typed-document-node/core": "^3.1.1",
54
+ "@wry/equality": "^0.5.6",
55
+ "@wry/trie": "^0.5.0"
56
+ },
57
+ "peerDependencies": {
58
+ "@apollo/client": ">=4.2.6 <4.3.0",
59
+ "graphql": "^16.0.0 || ^17.0.0",
60
+ "rxjs": "^7.3.0",
61
+ "octane": "0.1.5"
62
+ },
63
+ "devDependencies": {
64
+ "@apollo/client": "4.2.6",
65
+ "@tsrx/react": "^0.2.37",
66
+ "esbuild": "^0.28.1",
67
+ "graphql": "^16.11.0",
68
+ "react": "^19.2.0",
69
+ "react-dom": "^19.2.0",
70
+ "rxjs": "^7.8.2",
71
+ "vitest": "^4.1.9",
72
+ "octane": "0.1.5"
73
+ },
74
+ "scripts": {
75
+ "test": "vitest run --root ../.. --project apollo-client"
76
+ }
77
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from '@apollo/client';
package/src/index.js ADDED
@@ -0,0 +1,3 @@
1
+ // Apollo Client 4 keeps its framework-neutral client/cache/link surface at the
2
+ // package root. Hooks intentionally remain on the matching `/react` subpath.
3
+ export * from '@apollo/client';
@@ -0,0 +1,8 @@
1
+ import type { Context } from 'octane';
2
+ import type { ApolloClient } from '@apollo/client';
3
+ import type { HookWrappers, wrapperSymbol } from '@octanejs/apollo-client/react/internal';
4
+ export interface ApolloContextValue {
5
+ client?: ApolloClient;
6
+ [wrapperSymbol]?: HookWrappers;
7
+ }
8
+ export declare function getApolloContext(): Context<ApolloContextValue>;
@@ -0,0 +1,21 @@
1
+ import * as React from 'octane';
2
+ import { invariant } from '@apollo/client/utilities/invariant';
3
+ // To make sure Apollo Client doesn't create more than one React context
4
+ // (which can lead to problems like having an Apollo Client instance added
5
+ // in one context, then attempting to retrieve it from another different
6
+ // context), a single Apollo context is created and tracked in global state.
7
+ const contextKey = Symbol.for('__APOLLO_CONTEXT__');
8
+ export function getApolloContext() {
9
+ invariant('createContext' in React, 37);
10
+ let context = React.createContext[contextKey];
11
+ if (!context) {
12
+ Object.defineProperty(React.createContext, contextKey, {
13
+ value: (context = React.createContext({})),
14
+ enumerable: false,
15
+ writable: false,
16
+ configurable: true,
17
+ });
18
+ context.displayName = 'ApolloContext';
19
+ }
20
+ return context;
21
+ }
@@ -0,0 +1,9 @@
1
+ import type { ComponentBody } from 'octane';
2
+ import type { ApolloClient } from '@apollo/client';
3
+ export declare namespace ApolloProvider {
4
+ interface Props {
5
+ client: ApolloClient;
6
+ children?: unknown;
7
+ }
8
+ }
9
+ export declare const ApolloProvider: ComponentBody<ApolloProvider.Props>;
@@ -0,0 +1,15 @@
1
+ import * as React from 'octane';
2
+ import { invariant } from '@apollo/client/utilities/invariant';
3
+ import { getApolloContext } from './ApolloContext.js';
4
+ export const ApolloProvider = ({ client, children }) => {
5
+ const ApolloContext = getApolloContext();
6
+ const parentContext = React.useContext(ApolloContext);
7
+ const context = React.useMemo(() => {
8
+ return {
9
+ ...parentContext,
10
+ client: client || parentContext.client,
11
+ };
12
+ }, [parentContext, client]);
13
+ invariant(context.client, 38);
14
+ return React.createElement(ApolloContext.Provider, { value: context }, children);
15
+ };
@@ -0,0 +1,2 @@
1
+ export declare const skipToken: unique symbol;
2
+ export type SkipToken = typeof skipToken;
@@ -0,0 +1 @@
1
+ export const skipToken = Symbol.for('apollo.skipToken');
@@ -0,0 +1,3 @@
1
+ type Use = <T>(promise: Promise<T>, site?: symbol | string) => T;
2
+ export declare const __use: Use;
3
+ export {};
@@ -0,0 +1,9 @@
1
+ import * as React from 'octane';
2
+ const fallbackSite = Symbol('apollo.__use');
3
+ // Apollo's React adapter falls back to throwing decorated promises on React 18.
4
+ // Octane always has use(), and raw promise throws are errors rather than
5
+ // suspension. The public hook's compiler-provided site is threaded through on
6
+ // the server so conditional sibling hooks keep stable SSR cache identities.
7
+ export function __use(promise, site) {
8
+ return React.use(promise, site ?? fallbackSite);
9
+ }
@@ -0,0 +1,5 @@
1
+ export { useDeepMemo } from './useDeepMemo.js';
2
+ export { useRenderGuard } from './useRenderGuard.js';
3
+ export { useSuspenseHookCacheKey } from './useSuspenseHookCacheKey.js';
4
+ export { __use } from './__use.js';
5
+ export { wrapHook } from './wrapHook.js';
@@ -0,0 +1,6 @@
1
+ // These hooks are used internally and are not exported publicly by the library
2
+ export { useDeepMemo } from './useDeepMemo.js';
3
+ export { useRenderGuard } from './useRenderGuard.js';
4
+ export { useSuspenseHookCacheKey } from './useSuspenseHookCacheKey.js';
5
+ export { __use } from './__use.js';
6
+ export { wrapHook } from './wrapHook.js';
@@ -0,0 +1,2 @@
1
+ export declare function subSite(site: symbol | undefined, tag: string): symbol;
2
+ export declare function isCompilerSite(value: unknown, reserved?: symbol): value is symbol;
@@ -0,0 +1,20 @@
1
+ const ROOT_SITE = Symbol('apollo.root');
2
+ const subSites = new Map();
3
+
4
+ /** Return a stable child site for repeated helper hooks inside one public hook. */
5
+ export function subSite(site, tag) {
6
+ const parent = typeof site === 'symbol' ? site : ROOT_SITE;
7
+ let children = subSites.get(parent);
8
+ if (!children) subSites.set(parent, (children = new Map()));
9
+ let child = children.get(tag);
10
+ if (!child) {
11
+ child = Symbol(`${parent.description || 'apollo'}:${tag}`);
12
+ children.set(tag, child);
13
+ }
14
+ return child;
15
+ }
16
+
17
+ /** Compiler hook sites are symbols, but Apollo's skipToken is user data. */
18
+ export function isCompilerSite(value, reserved) {
19
+ return typeof value === 'symbol' && value !== reserved;
20
+ }
@@ -0,0 +1,5 @@
1
+ export declare function useDeepMemo<TValue>(
2
+ memoFn: () => TValue,
3
+ deps: readonly unknown[],
4
+ site?: symbol,
5
+ ): TValue;
@@ -0,0 +1,11 @@
1
+ import { equal } from '@wry/equality';
2
+ import * as React from 'octane';
3
+ export function useDeepMemo(memoFn, deps, site) {
4
+ const ref = React.useRef(void 0, site);
5
+ // eslint-disable-next-line react-hooks/refs
6
+ if (!ref.current || !equal(ref.current.deps, deps)) {
7
+ ref.current = { value: memoFn(), deps };
8
+ }
9
+ // eslint-disable-next-line react-hooks/refs
10
+ return ref.current.value;
11
+ }
@@ -0,0 +1,2 @@
1
+ import * as React from 'octane';
2
+ export declare const useIsomorphicLayoutEffect: typeof React.useLayoutEffect;
@@ -0,0 +1,8 @@
1
+ import * as React from 'octane';
2
+ import { canUseDOM } from '@apollo/client/utilities/internal';
3
+ // use canUseDOM here instead of canUseLayoutEffect because we want to be able
4
+ // to use useLayoutEffect in our jest tests. useLayoutEffect seems to work fine
5
+ // in useSuspenseQuery tests, but to honor the original comment about the
6
+ // warnings for useSyncExternalStore implementation, canUseLayoutEffect is left
7
+ // alone.
8
+ export const useIsomorphicLayoutEffect = canUseDOM ? React.useLayoutEffect : React.useEffect;
@@ -0,0 +1 @@
1
+ export declare function useRenderGuard(): () => boolean;
@@ -0,0 +1,14 @@
1
+ import * as React from 'octane';
2
+ export function useRenderGuard() {
3
+ return React.useCallback(() => {
4
+ try {
5
+ // Unlike React, Octane context reads are valid outside a render. A
6
+ // slot-keyed hook is the reliable render-phase probe: conditional
7
+ // hooks are supported, and useRef throws when no component is active.
8
+ React.useRef(null);
9
+ return true;
10
+ } catch {
11
+ return false;
12
+ }
13
+ }, []);
14
+ }
@@ -0,0 +1,13 @@
1
+ import type { DocumentNode } from 'graphql';
2
+ import type { OperationVariables } from '@apollo/client';
3
+ import type { SkipToken } from '../constants.js';
4
+ export declare namespace useSuspenseHookCacheKey {
5
+ interface Options {
6
+ variables?: OperationVariables;
7
+ queryKey?: string | number | any[];
8
+ }
9
+ }
10
+ export declare function useSuspenseHookCacheKey(
11
+ query: DocumentNode,
12
+ options: (SkipToken & Partial<useSuspenseHookCacheKey.Options>) | useSuspenseHookCacheKey.Options,
13
+ ): [DocumentNode, string, ...any[]];
@@ -0,0 +1,19 @@
1
+ import * as React from 'octane';
2
+ import { canonicalStringify } from '@apollo/client/utilities';
3
+ import { skipToken } from '../constants.js';
4
+ export function useSuspenseHookCacheKey(query, options) {
5
+ const { queryKey = [], variables } = options;
6
+ const canonicalVariables = canonicalStringify(variables);
7
+ // This state value let's us maintain the variables used for the cache key
8
+ // when `skipToken` is used to skip a query after its been executed.
9
+ // Since options aren't provided when using `skipToken`, `variables` would
10
+ // otherwise disappear which means we'd return a new cache key without a
11
+ // variables value which creates a new `ObservableQuery` instance. This was
12
+ // particularly problematic when `refetchQueries` was used because it meant
13
+ // refetching against an `ObservableQuery` instance that had no variables.
14
+ let [cacheKeyVariables, setCacheKeyVariables] = React.useState(canonicalVariables);
15
+ if (options !== skipToken && cacheKeyVariables !== canonicalVariables) {
16
+ setCacheKeyVariables((cacheKeyVariables = canonicalVariables));
17
+ }
18
+ return [query, cacheKeyVariables, ...[].concat(queryKey)];
19
+ }
@@ -0,0 +1,4 @@
1
+ import type { ApolloClient, OperationVariables } from '@apollo/client';
2
+ export declare function validateSuspenseHookOptions<TData, TVariables extends OperationVariables>(
3
+ options: ApolloClient.WatchQueryOptions<TData, TVariables>,
4
+ ): void;
@@ -0,0 +1,16 @@
1
+ import { __DEV__ } from '@apollo/client/utilities/environment';
2
+ import { invariant } from '@apollo/client/utilities/invariant';
3
+ export function validateSuspenseHookOptions(options) {
4
+ const { fetchPolicy, returnPartialData } = options;
5
+ validateFetchPolicy(fetchPolicy);
6
+ validatePartialDataReturn(fetchPolicy, returnPartialData);
7
+ }
8
+ function validateFetchPolicy(fetchPolicy = 'cache-first') {
9
+ const supportedFetchPolicies = ['cache-first', 'network-only', 'no-cache', 'cache-and-network'];
10
+ invariant(supportedFetchPolicies.includes(fetchPolicy), 35, fetchPolicy);
11
+ }
12
+ function validatePartialDataReturn(fetchPolicy, returnPartialData) {
13
+ if (fetchPolicy === 'no-cache' && returnPartialData) {
14
+ __DEV__ && invariant.warn(36);
15
+ }
16
+ }
@@ -0,0 +1,77 @@
1
+ import type { ApolloClient } from '@apollo/client';
2
+ import type { ObservableQuery } from '@apollo/client';
3
+ import type { createQueryPreloader } from '@octanejs/apollo-client/react';
4
+ import type {
5
+ useBackgroundQuery,
6
+ useFragment,
7
+ useQuery,
8
+ useQueryRefHandlers,
9
+ useReadQuery,
10
+ useSuspenseFragment,
11
+ useSuspenseQuery,
12
+ } from '@octanejs/apollo-client/react';
13
+ type FunctionSignature<T> = T extends (...args: infer A) => infer R ? (...args: A) => R : never;
14
+ interface WrappableHooks {
15
+ createQueryPreloader: FunctionSignature<typeof createQueryPreloader>;
16
+ useQuery: FunctionSignature<typeof useQuery>;
17
+ useSuspenseQuery: FunctionSignature<typeof useSuspenseQuery>;
18
+ useSuspenseFragment: FunctionSignature<typeof useSuspenseFragment>;
19
+ useBackgroundQuery: FunctionSignature<typeof useBackgroundQuery>;
20
+ useReadQuery: FunctionSignature<typeof useReadQuery>;
21
+ useFragment: FunctionSignature<typeof useFragment>;
22
+ useQueryRefHandlers: FunctionSignature<typeof useQueryRefHandlers>;
23
+ }
24
+ /**
25
+ * @internal
26
+ * Can be used to correctly type the [Symbol.for("apollo.hook.wrappers")] property of
27
+ * `QueryManager`, to override/wrap hook functionality.
28
+ *
29
+ * @deprecated This is an internal API and should not be used directly. This can be removed or changed at any time.
30
+ */
31
+ export type HookWrappers = {
32
+ [K in keyof WrappableHooks]?: (originalHook: WrappableHooks[K]) => WrappableHooks[K];
33
+ };
34
+ /**
35
+ * @internal
36
+ *
37
+ * Makes an Apollo Client hook "wrappable".
38
+ * That means that the Apollo Client instance can expose a "wrapper" that will be
39
+ * used to wrap the original hook implementation with additional logic.
40
+ * @example
41
+ *
42
+ * ```tsx
43
+ * // this is already done in `@apollo/client` for all wrappable hooks (see `WrappableHooks`)
44
+ * // following this pattern
45
+ * function useQuery() {
46
+ * return wrapHook('useQuery', _useQuery, options.client)(query, options);
47
+ * }
48
+ * function _useQuery(query, options) {
49
+ * // original implementation
50
+ * }
51
+ *
52
+ * // this is what a library like `@apollo/client-react-streaming` would do
53
+ * class ApolloClientWithStreaming extends ApolloClient {
54
+ * constructor(options) {
55
+ * super(options);
56
+ * this.queryManager[Symbol.for("apollo.hook.wrappers")] = {
57
+ * useQuery: (original) => (query, options) => {
58
+ * console.log("useQuery was called with options", options);
59
+ * return original(query, options);
60
+ * }
61
+ * }
62
+ * }
63
+ * }
64
+ *
65
+ * // this will now log the options and then call the original `useQuery`
66
+ * const client = new ApolloClientWithStreaming({ ... });
67
+ * useQuery(query, { client });
68
+ * ```
69
+ *
70
+ * @deprecated This is an internal API and should not be used directly. This can be removed or changed at any time.
71
+ */
72
+ export declare function wrapHook<Hook extends (...args: any[]) => any>(
73
+ hookName: keyof WrappableHooks,
74
+ useHook: Hook,
75
+ clientOrObsQuery: ObservableQuery<any> | ApolloClient,
76
+ ): Hook;
77
+ export {};
@@ -0,0 +1,64 @@
1
+ import * as React from 'octane';
2
+ import { wrapperSymbol } from '@octanejs/apollo-client/react/internal';
3
+ // direct import to avoid circular dependency
4
+ import { getApolloContext } from '../../context/ApolloContext.js';
5
+ /**
6
+ * @internal
7
+ *
8
+ * Makes an Apollo Client hook "wrappable".
9
+ * That means that the Apollo Client instance can expose a "wrapper" that will be
10
+ * used to wrap the original hook implementation with additional logic.
11
+ * @example
12
+ *
13
+ * ```tsx
14
+ * // this is already done in `@apollo/client` for all wrappable hooks (see `WrappableHooks`)
15
+ * // following this pattern
16
+ * function useQuery() {
17
+ * return wrapHook('useQuery', _useQuery, options.client)(query, options);
18
+ * }
19
+ * function _useQuery(query, options) {
20
+ * // original implementation
21
+ * }
22
+ *
23
+ * // this is what a library like `@apollo/client-react-streaming` would do
24
+ * class ApolloClientWithStreaming extends ApolloClient {
25
+ * constructor(options) {
26
+ * super(options);
27
+ * this.queryManager[Symbol.for("apollo.hook.wrappers")] = {
28
+ * useQuery: (original) => (query, options) => {
29
+ * console.log("useQuery was called with options", options);
30
+ * return original(query, options);
31
+ * }
32
+ * }
33
+ * }
34
+ * }
35
+ *
36
+ * // this will now log the options and then call the original `useQuery`
37
+ * const client = new ApolloClientWithStreaming({ ... });
38
+ * useQuery(query, { client });
39
+ * ```
40
+ *
41
+ * @deprecated This is an internal API and should not be used directly. This can be removed or changed at any time.
42
+ */
43
+ export function wrapHook(hookName, useHook, clientOrObsQuery) {
44
+ // Priority-wise, the later entries in this array wrap
45
+ // previous entries and could prevent them (and in the end,
46
+ // even the original hook) from running
47
+ const wrapperSources = [
48
+ clientOrObsQuery['queryManager'],
49
+ // if we are a hook (not `preloadQuery`), we are guaranteed to be inside of
50
+ // a React render and can use context
51
+ hookName.startsWith('use')
52
+ ? // eslint-disable-next-line react-hooks/rules-of-hooks
53
+ React.useContext(getApolloContext())
54
+ : undefined,
55
+ ];
56
+ let wrapped = useHook;
57
+ for (const source of wrapperSources) {
58
+ const wrapper = source?.[wrapperSymbol]?.[hookName];
59
+ if (wrapper) {
60
+ wrapped = wrapper(wrapped);
61
+ }
62
+ }
63
+ return wrapped;
64
+ }
@@ -0,0 +1,17 @@
1
+ import type { ApolloClient } from '@apollo/client';
2
+ /**
3
+ * @example
4
+ *
5
+ * ```jsx
6
+ * import { useApolloClient } from "@octanejs/apollo-client/react";
7
+ *
8
+ * function SomeComponent() {
9
+ * const client = useApolloClient();
10
+ * // `client` is now set to the `ApolloClient` instance being used by the
11
+ * // application (that was configured using something like `ApolloProvider`)
12
+ * }
13
+ * ```
14
+ *
15
+ * @returns The `ApolloClient` instance being used by the application.
16
+ */
17
+ export declare function useApolloClient(override?: ApolloClient): ApolloClient;
@@ -0,0 +1,27 @@
1
+ import * as React from 'octane';
2
+ import { invariant } from '@apollo/client/utilities/invariant';
3
+ import { getApolloContext } from '../context/ApolloContext.js';
4
+ /**
5
+ * @example
6
+ *
7
+ * ```jsx
8
+ * import { useApolloClient } from "@octanejs/apollo-client/react";
9
+ *
10
+ * function SomeComponent() {
11
+ * const client = useApolloClient();
12
+ * // `client` is now set to the `ApolloClient` instance being used by the
13
+ * // application (that was configured using something like `ApolloProvider`)
14
+ * }
15
+ * ```
16
+ *
17
+ * @returns The `ApolloClient` instance being used by the application.
18
+ */
19
+ export function useApolloClient(override) {
20
+ // A zero-argument custom hook call is compiled as useApolloClient(site).
21
+ // ApolloClient is an object, so a symbol here can only be Octane's site.
22
+ if (typeof override === 'symbol') override = undefined;
23
+ const context = React.useContext(getApolloContext());
24
+ const client = override || context.client;
25
+ invariant(!!client, 28);
26
+ return client;
27
+ }