@trackunit/react-graphql-hooks 2.10.10 → 2.10.11

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/index.cjs.js CHANGED
@@ -249,6 +249,13 @@ const createDirectionAwareUnion = (direction) => {
249
249
  }
250
250
  return unionEdgesByNodeKey;
251
251
  };
252
+ /**
253
+ * An empty previous result becomes undefined, which Apollo treats as "no update". Writing `{}` to the cache would log
254
+ * missing fields.
255
+ */
256
+ const emptyAsUndefined = (value) => value === undefined || value === null || sharedUtils.objectKeys(value).length === 0
257
+ ? undefined
258
+ : value;
252
259
  /**
253
260
  * Creates an update query handler for Apollo's fetchMore.
254
261
  * Handles aborted requests and merges new data with existing data.
@@ -257,19 +264,16 @@ const createUpdateQueryHandler = (params) => {
257
264
  return (_previousResult, { fetchMoreResult }) => {
258
265
  if (params.abortSignal.aborted) {
259
266
  // https://github.com/apollographql/apollo-client/issues/8677
260
- if (params.prev !== undefined && params.prev !== null) {
261
- return sharedUtils.objectKeys(params.prev).length === 0
262
- ? undefined
263
- : params.prev;
264
- }
265
- return undefined;
267
+ return emptyAsUndefined(params.prev);
266
268
  }
267
- // Apollo can return undefined fetchMoreResult when the query document
268
- // changes and triggers a spurious re-execution (e.g. dynamic query
269
- // rebuilt from visibleColumns). Skip the update and keep previous data.
269
+ // Apollo passes no fetchMoreResult when the response carried only GraphQL
270
+ // errors, or when the query document changes and triggers a spurious
271
+ // re-execution (e.g. dynamic query rebuilt from visibleColumns). Keep the
272
+ // previous result: on the cached path Apollo writes it back, on the
273
+ // no-cache path (pollInterval) it reports it as the query's data.
270
274
  if (fetchMoreResult === undefined || fetchMoreResult === null) {
271
275
  params.onLoadingComplete();
272
- return _previousResult;
276
+ return emptyAsUndefined(_previousResult);
273
277
  }
274
278
  // Type assertion required: Apollo's fetchMoreResult has complex internal types
275
279
  // that need to be cast to our TData generic for type-safe usage
@@ -480,6 +484,10 @@ const usePaginationQuery = (document, props) => {
480
484
  const signal = abortController.signal;
481
485
  const filteredRelayVars = Object.fromEntries(Object.entries(variables).filter(([key]) => declaredVarNames.has(key)));
482
486
  const fetchMoreVariables = { ...stableVariables, ...filteredRelayVars };
487
+ const reportError = (error) => {
488
+ dispatch({ type: "SET_ERROR", payload: error });
489
+ onErrorRef.current?.(error);
490
+ };
483
491
  fetchMore({
484
492
  variables: fetchMoreVariables,
485
493
  updateQuery: createUpdateQueryHandler({
@@ -514,13 +522,18 @@ const usePaginationQuery = (document, props) => {
514
522
  },
515
523
  onLoadingComplete: () => setIsLoading(false),
516
524
  }),
517
- }).catch(error => {
525
+ }).then(result => {
526
+ const hasData = result.data !== undefined && result.data !== null;
527
+ if (signal.aborted || hasData || result.errors === undefined || result.errors.length === 0) {
528
+ return;
529
+ }
530
+ reportError(new client.ApolloError({ graphQLErrors: result.errors }));
531
+ }, error => {
518
532
  setIsLoading(false);
519
533
  if (signal.aborted) {
520
534
  return;
521
535
  }
522
- dispatch({ type: "SET_ERROR", payload: error });
523
- onErrorRef.current?.(error);
536
+ reportError(error);
524
537
  });
525
538
  }, [
526
539
  props.skip,
package/index.esm.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import 'react/jsx-runtime';
2
2
  import { registerTranslations } from '@trackunit/i18n-library-translation';
3
3
  import { useCallback, useMemo, useState, useReducer, useRef, useEffect } from 'react';
4
- import { useLazyQuery as useLazyQuery$1, NetworkStatus, useQuery as useQuery$1 } from '@apollo/client';
4
+ import { useLazyQuery as useLazyQuery$1, NetworkStatus, ApolloError, useQuery as useQuery$1 } from '@apollo/client';
5
5
  import { omit, isEqual } from 'es-toolkit';
6
6
  import { defaultPageSize, useRelayPagination, useWatch } from '@trackunit/react-components';
7
7
  import { truthy, objectKeys } from '@trackunit/shared-utils';
@@ -247,6 +247,13 @@ const createDirectionAwareUnion = (direction) => {
247
247
  }
248
248
  return unionEdgesByNodeKey;
249
249
  };
250
+ /**
251
+ * An empty previous result becomes undefined, which Apollo treats as "no update". Writing `{}` to the cache would log
252
+ * missing fields.
253
+ */
254
+ const emptyAsUndefined = (value) => value === undefined || value === null || objectKeys(value).length === 0
255
+ ? undefined
256
+ : value;
250
257
  /**
251
258
  * Creates an update query handler for Apollo's fetchMore.
252
259
  * Handles aborted requests and merges new data with existing data.
@@ -255,19 +262,16 @@ const createUpdateQueryHandler = (params) => {
255
262
  return (_previousResult, { fetchMoreResult }) => {
256
263
  if (params.abortSignal.aborted) {
257
264
  // https://github.com/apollographql/apollo-client/issues/8677
258
- if (params.prev !== undefined && params.prev !== null) {
259
- return objectKeys(params.prev).length === 0
260
- ? undefined
261
- : params.prev;
262
- }
263
- return undefined;
265
+ return emptyAsUndefined(params.prev);
264
266
  }
265
- // Apollo can return undefined fetchMoreResult when the query document
266
- // changes and triggers a spurious re-execution (e.g. dynamic query
267
- // rebuilt from visibleColumns). Skip the update and keep previous data.
267
+ // Apollo passes no fetchMoreResult when the response carried only GraphQL
268
+ // errors, or when the query document changes and triggers a spurious
269
+ // re-execution (e.g. dynamic query rebuilt from visibleColumns). Keep the
270
+ // previous result: on the cached path Apollo writes it back, on the
271
+ // no-cache path (pollInterval) it reports it as the query's data.
268
272
  if (fetchMoreResult === undefined || fetchMoreResult === null) {
269
273
  params.onLoadingComplete();
270
- return _previousResult;
274
+ return emptyAsUndefined(_previousResult);
271
275
  }
272
276
  // Type assertion required: Apollo's fetchMoreResult has complex internal types
273
277
  // that need to be cast to our TData generic for type-safe usage
@@ -478,6 +482,10 @@ const usePaginationQuery = (document, props) => {
478
482
  const signal = abortController.signal;
479
483
  const filteredRelayVars = Object.fromEntries(Object.entries(variables).filter(([key]) => declaredVarNames.has(key)));
480
484
  const fetchMoreVariables = { ...stableVariables, ...filteredRelayVars };
485
+ const reportError = (error) => {
486
+ dispatch({ type: "SET_ERROR", payload: error });
487
+ onErrorRef.current?.(error);
488
+ };
481
489
  fetchMore({
482
490
  variables: fetchMoreVariables,
483
491
  updateQuery: createUpdateQueryHandler({
@@ -512,13 +520,18 @@ const usePaginationQuery = (document, props) => {
512
520
  },
513
521
  onLoadingComplete: () => setIsLoading(false),
514
522
  }),
515
- }).catch(error => {
523
+ }).then(result => {
524
+ const hasData = result.data !== undefined && result.data !== null;
525
+ if (signal.aborted || hasData || result.errors === undefined || result.errors.length === 0) {
526
+ return;
527
+ }
528
+ reportError(new ApolloError({ graphQLErrors: result.errors }));
529
+ }, error => {
516
530
  setIsLoading(false);
517
531
  if (signal.aborted) {
518
532
  return;
519
533
  }
520
- dispatch({ type: "SET_ERROR", payload: error });
521
- onErrorRef.current?.(error);
534
+ reportError(error);
522
535
  });
523
536
  }, [
524
537
  props.skip,
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@trackunit/react-graphql-hooks",
3
- "version": "2.10.10",
3
+ "version": "2.10.11",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
7
7
  "node": ">=24.x"
8
8
  },
9
9
  "dependencies": {
10
- "@trackunit/i18n-library-translation": "2.8.6",
10
+ "@trackunit/i18n-library-translation": "2.8.7",
11
11
  "@trackunit/shared-utils": "1.19.6",
12
12
  "es-toolkit": "^1.52.0",
13
- "@trackunit/react-components": "3.8.4"
13
+ "@trackunit/react-components": "3.8.5"
14
14
  },
15
15
  "peerDependencies": {
16
16
  "@apollo/client": "^3.13.8",
@@ -7,6 +7,10 @@ import { type UpdateQueryOptions } from "./utils/createUpdateQueryHandler";
7
7
  export type PaginationQuery<TData> = {
8
8
  data: TData | undefined;
9
9
  loading: boolean;
10
+ /**
11
+ * Set when the last page request failed: a network error, or a response that carried GraphQL errors and no data.
12
+ * Partial data with GraphQL errors is merged into `data` and does not set `error`.
13
+ */
10
14
  error?: unknown;
11
15
  lastFetchedData?: TData;
12
16
  pagination: RelayTableSupport;