houdini-react 2.0.0-next.40 → 2.0.0-next.41

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "houdini-react",
3
- "version": "2.0.0-next.40",
3
+ "version": "2.0.0-next.41",
4
4
  "description": "The React plugin for houdini",
5
5
  "keywords": [
6
6
  "typescript",
@@ -81,13 +81,13 @@
81
81
  }
82
82
  },
83
83
  "optionalDependencies": {
84
- "houdini-react-darwin-x64": "2.0.0-next.40",
85
- "houdini-react-darwin-arm64": "2.0.0-next.40",
86
- "houdini-react-linux-x64": "2.0.0-next.40",
87
- "houdini-react-linux-arm64": "2.0.0-next.40",
88
- "houdini-react-win32-x64": "2.0.0-next.40",
89
- "houdini-react-win32-arm64": "2.0.0-next.40",
90
- "houdini-react-wasm": "2.0.0-next.40"
84
+ "houdini-react-darwin-x64": "2.0.0-next.41",
85
+ "houdini-react-darwin-arm64": "2.0.0-next.41",
86
+ "houdini-react-linux-x64": "2.0.0-next.41",
87
+ "houdini-react-linux-arm64": "2.0.0-next.41",
88
+ "houdini-react-win32-x64": "2.0.0-next.41",
89
+ "houdini-react-win32-arm64": "2.0.0-next.41",
90
+ "houdini-react-wasm": "2.0.0-next.41"
91
91
  },
92
92
  "scripts": {
93
93
  "compile": "scripts build-go",
package/postInstall.js CHANGED
@@ -5,7 +5,7 @@ const https = require('https')
5
5
  const child_process = require('child_process')
6
6
 
7
7
  // Adjust the version you want to install. You can also make this dynamic.
8
- const BINARY_DISTRIBUTION_VERSION = '2.0.0-next.40'
8
+ const BINARY_DISTRIBUTION_VERSION = '2.0.0-next.41'
9
9
 
10
10
  // Windows binaries end with .exe so we need to special case them.
11
11
  const binaryName = process.platform === 'win32' ? 'houdini-react.exe' : 'houdini-react'
@@ -24,10 +24,7 @@ export function useFragmentHandle<
24
24
  reference: _Data | { ' $fragments': _ReferenceType } | null,
25
25
  document: { artifact: FragmentArtifact; refetchArtifact?: QueryArtifact }
26
26
  ): any {
27
- // get the fragment values
28
27
  const fragmentData = useFragment<_Data, _ReferenceType, _Input>(reference, document)
29
-
30
- // look at the fragment reference to get the variables
31
28
  const { variables } = fragmentReference<_Data, _Input, _ReferenceType>(reference, document)
32
29
 
33
30
  const client = useClient()
@@ -36,30 +33,25 @@ export function useFragmentHandle<
36
33
  const [forwardPending, setForwardPending] = React.useState(false)
37
34
  const [backwardPending, setBackwardPending] = React.useState(false)
38
35
 
39
- // Stable cursor stacks for SinglePage pagination — must survive re-renders
40
36
  const previousCursorsRef = React.useRef<(string | null)[]>([])
41
37
  const nextCursorsRef = React.useRef<(string | null)[]>([])
42
38
 
43
39
  const refetchArtifact = document.refetchArtifact as QueryArtifact | undefined
44
40
  const refetchPath = refetchArtifact?.refetch?.path
45
41
 
46
- // Dedicated observer for pagination queries — separate from the fragment observer.
47
- // cursorHandlers derives entity variables (e.g. { id }) and artifact defaults
48
- // automatically via the type config, so no manual variable extraction is needed here.
49
42
  const paginationObserver = React.useMemo(() => {
50
43
  if (!refetchArtifact?.refetch?.paginated) return null
51
44
  return client.observe<_Data, _Input>({ artifact: refetchArtifact })
52
45
  }, [refetchArtifact?.name])
53
46
 
54
- // Subscribe to the pagination observer so React re-renders whenever a new page is fetched
55
- // or served from cache (CacheOrNetwork). The fragment store subscription only watches the
56
- // initial page's cache key; the observer is the live source of truth for SinglePage
57
- // pagination where each page lives at its own per-cursor cache key.
47
+ const isSinglePage = refetchArtifact?.refetch?.mode === 'SinglePage'
48
+
49
+ // Subscribe to the pagination observer so the component re-renders when a new page lands.
50
+ // For SinglePage we pass disablePartial so partial cache hits (entity found but connection
51
+ // not yet fetched) are never resolved back to the observer — we go straight to the network
52
+ // and update the observer only once we have a complete page.
58
53
  const subscribeToObserver = React.useCallback(
59
- (onChange: () => void) => {
60
- if (!paginationObserver) return () => {}
61
- return paginationObserver.subscribe(onChange)
62
- },
54
+ (fn: () => void) => paginationObserver?.subscribe(() => fn()) ?? (() => {}),
63
55
  [paginationObserver]
64
56
  )
65
57
  const getObserverSnapshot = React.useCallback(
@@ -72,28 +64,13 @@ export function useFragmentHandle<
72
64
  getObserverSnapshot
73
65
  )
74
66
 
75
- // Extract entity-level data from the pagination query response. For Node targetType
76
- // the response is { node: EntityData }; we take the first root field to handle any type.
77
- // Guard against partial cache hits (artifact has partial:true): only use the entity once the
78
- // paginated connection field at refetch.path[0] is actually present in the response.
79
67
  const paginationEntityData = React.useMemo<_Data | null>(() => {
80
68
  if (!paginationData || !refetchArtifact?.selection?.fields) return null
81
69
  const rootField = Object.keys(refetchArtifact.selection.fields)[0]
82
70
  if (!rootField) return null
83
- const entity = (paginationData as any)[rootField]
84
- if (!entity) return null
85
- const path = refetchArtifact.refetch?.path
86
- if (path && path.length > 0 && (entity as any)[path[0]] == null) {
87
- return null
88
- }
89
- return entity as _Data
71
+ return (paginationData as any)[rootField] ?? null
90
72
  }, [paginationData, refetchArtifact])
91
73
 
92
- const isSinglePage = refetchArtifact?.refetch?.mode === 'SinglePage'
93
-
94
- // For SinglePage: use the pagination observer's entity data (each page has its own
95
- // cache key) once a page fetch has landed. For Infinite: always use fragmentData,
96
- // which reads accumulated pages from cache via the fragment's cache subscription.
97
74
  const displayData =
98
75
  isSinglePage && paginationEntityData !== null ? paginationEntityData : fragmentData
99
76
 
@@ -120,7 +97,19 @@ export function useFragmentHandle<
120
97
  if (!refetchArtifact?.refetch?.paginated || !paginationObserver) return null
121
98
 
122
99
  const fetchFn: FetchFn<_Data, _Input> = (args) => {
123
- return paginationObserver.send({ ...args, session })
100
+ return paginationObserver.send({
101
+ ...args,
102
+ session,
103
+ stuff: { silenceLoading: true },
104
+ cacheParams: {
105
+ disableSubscriptions: true,
106
+ // Suppress partial cache hits so an in-flight forward navigation never
107
+ // briefly resolves with an entity that is missing its connection field.
108
+ // Full cache hits (partial: false) still resolve, so backward navigation
109
+ // continues to be served instantly from cache.
110
+ disablePartial: true,
111
+ },
112
+ })
124
113
  }
125
114
 
126
115
  const fetchUpdate = (args: any, updates: string[]) => {
@@ -139,8 +128,6 @@ export function useFragmentHandle<
139
128
  const handlers = cursorHandlers<_Data, _Input>({
140
129
  artifact: refetchArtifact,
141
130
  getState: () => displayData as _Data | null,
142
- // Use the observer's own variable state so cursor history is preserved
143
- // across page navigations without manual tracking in the hook.
144
131
  getVariables: () =>
145
132
  (paginationObserver.state.variables ?? variables) as NonNullable<_Input>,
146
133
  fetch: fetchFn,