@graphql-box/connection-resolver 5.4.23-unstable-12345.1 → 5.4.23-unstable-12345.3

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 (31) hide show
  1. package/dist/cjs/index.cjs +1 -1
  2. package/dist/cjs/index.cjs.map +1 -1
  3. package/dist/esm/index.mjs +1 -1
  4. package/dist/esm/index.mjs.map +1 -1
  5. package/dist/production.analysis.txt +46 -46
  6. package/dist/types/cjs/__testUtils__/generateCursorCache.d.cts.map +1 -1
  7. package/dist/types/cjs/helpers/cacheCursors.d.cts +2 -1
  8. package/dist/types/cjs/helpers/cacheCursors.d.cts.map +1 -1
  9. package/dist/types/cjs/helpers/requestAndCachePages.d.cts +2 -8
  10. package/dist/types/cjs/helpers/requestAndCachePages.d.cts.map +1 -1
  11. package/dist/types/cjs/types.d.cts +1 -0
  12. package/dist/types/cjs/types.d.cts.map +1 -1
  13. package/dist/types/esm/__testUtils__/generateCursorCache.d.ts.map +1 -1
  14. package/dist/types/esm/helpers/cacheCursors.d.ts +2 -1
  15. package/dist/types/esm/helpers/cacheCursors.d.ts.map +1 -1
  16. package/dist/types/esm/helpers/requestAndCachePages.d.ts +2 -8
  17. package/dist/types/esm/helpers/requestAndCachePages.d.ts.map +1 -1
  18. package/dist/types/esm/types.d.ts +1 -0
  19. package/dist/types/esm/types.d.ts.map +1 -1
  20. package/dist/types/tsconfig.build.tsbuildinfo +1 -1
  21. package/package.json +6 -6
  22. package/src/__testUtils__/generateCursorCache.ts +6 -3
  23. package/src/helpers/cacheCursors.ts +4 -3
  24. package/src/helpers/getIndexesOnCurrentPage.test.ts +10 -2
  25. package/src/helpers/getPageNumbersToRequest.test.ts +2 -0
  26. package/src/helpers/getStartAndEndIndexes.test.ts +5 -0
  27. package/src/helpers/getStartAndEndPageNumbers.test.ts +5 -0
  28. package/src/helpers/requestAndCachePages.ts +47 -32
  29. package/src/helpers/validateCursor.test.ts +1 -1
  30. package/src/helpers/validateCursor.ts +1 -1
  31. package/src/types.ts +1 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@graphql-box/connection-resolver",
3
3
  "description": "The GraphQL Box connection resolver module.",
4
- "version": "5.4.23-unstable-12345.1",
4
+ "version": "5.4.23-unstable-12345.3",
5
5
  "author": "miami-man",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/badbatch/graphql-box",
@@ -32,22 +32,22 @@
32
32
  "access": "public"
33
33
  },
34
34
  "dependencies": {
35
- "@cachemap/core": "^5.2.8",
35
+ "@cachemap/core": "^5.3.0",
36
36
  "@types/lodash-es": "^4.17.12",
37
37
  "core-js": "^3.43.0",
38
38
  "js-base64": "^3.7.7",
39
- "lodash-es": "^4.17.21",
39
+ "lodash-es": "^4.17.23",
40
40
  "type-fest": "^4.41.0",
41
- "@graphql-box/core": "5.4.14-unstable-12345.1"
41
+ "@graphql-box/core": "5.4.14-unstable-12345.3"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "graphql": "<17",
45
45
  "winston": "<4"
46
46
  },
47
47
  "devDependencies": {
48
- "@cachemap/map": "^5.0.10",
48
+ "@cachemap/map": "^5.1.0",
49
49
  "@jest/globals": "^29.3.1",
50
- "cts-types": "^0.0.10",
50
+ "cts-types": "^0.0.11",
51
51
  "del-cli": "^6.0.0",
52
52
  "graphql": "^16.11.0",
53
53
  "winston": "^3.17.0"
@@ -29,6 +29,7 @@ export const generateCursorCache = async ({
29
29
 
30
30
  if (pageRanges.length > 0 && resultsPerPage) {
31
31
  const pages = generatePages(pageRanges);
32
+ let cachedNodeIds: (string | number)[] = [];
32
33
 
33
34
  await Promise.all(
34
35
  pages.map(async page => {
@@ -47,14 +48,16 @@ export const generateCursorCache = async ({
47
48
  return { cursor: `${id}::${group}`, node: { id } };
48
49
  });
49
50
 
50
- await cacheCursors(cursorCache, { edges, group, headers, page, totalPages, totalResults });
51
+ cachedNodeIds = [...cachedNodeIds, ...edges.map(edge => edge.node.id)];
52
+
53
+ await cacheCursors(cursorCache, { cachedNodeIds, edges, group, headers, page, totalPages, totalResults });
51
54
  }),
52
55
  );
53
56
  } else {
54
57
  await cursorCache.set(
55
58
  `${group}-metadata`,
56
- { totalPages, totalResults },
57
- { cacheHeaders: { cacheControl: headers.get('cache-control') ?? undefined } },
59
+ { cachedNodeIds: [], totalPages, totalResults },
60
+ { cacheOptions: { cacheControl: headers.get('cache-control') ?? undefined } },
58
61
  );
59
62
  }
60
63
 
@@ -2,6 +2,7 @@ import { type Core } from '@cachemap/core';
2
2
  import { type Edge } from '../types.ts';
3
3
 
4
4
  export type Params = {
5
+ cachedNodeIds: (string | number)[];
5
6
  edges: Edge[];
6
7
  group: string;
7
8
  headers: Headers;
@@ -12,14 +13,14 @@ export type Params = {
12
13
 
13
14
  export const cacheCursors = async (
14
15
  cursorCache: Core,
15
- { edges, group, headers, page, totalPages, totalResults }: Params,
16
+ { cachedNodeIds, edges, group, headers, page, totalPages, totalResults }: Params,
16
17
  ) => {
17
18
  const cacheControl = headers.get('cache-control');
18
- const options = cacheControl ? { cacheHeaders: { cacheControl } } : undefined;
19
+ const options = cacheControl ? { cacheOptions: { cacheControl } } : undefined;
19
20
 
20
21
  await Promise.all(
21
22
  edges.map(async ({ cursor, node }, index) => cursorCache.set(cursor, { group, index, node, page }, options)),
22
23
  );
23
24
 
24
- await cursorCache.set(`${group}-metadata`, { totalPages, totalResults }, options);
25
+ await cursorCache.set(`${group}-metadata`, { cachedNodeIds, totalPages, totalResults }, options);
25
26
  };
@@ -4,7 +4,11 @@ describe('getIndexesOnCurrentPage', () => {
4
4
  describe('when current page is last page', () => {
5
5
  it('should return the correct value', () => {
6
6
  expect(
7
- getIndexesOnCurrentPage({ metadata: { totalPages: 6, totalResults: 53 }, page: 6, resultsPerPage: 10 }),
7
+ getIndexesOnCurrentPage({
8
+ metadata: { cachedNodeIds: [], totalPages: 6, totalResults: 53 },
9
+ page: 6,
10
+ resultsPerPage: 10,
11
+ }),
8
12
  ).toBe(2);
9
13
  });
10
14
  });
@@ -12,7 +16,11 @@ describe('getIndexesOnCurrentPage', () => {
12
16
  describe('when current page is NOT last page', () => {
13
17
  it('should return the correct value', () => {
14
18
  expect(
15
- getIndexesOnCurrentPage({ metadata: { totalPages: 6, totalResults: 53 }, page: 5, resultsPerPage: 10 }),
19
+ getIndexesOnCurrentPage({
20
+ metadata: { cachedNodeIds: [], totalPages: 6, totalResults: 53 },
21
+ page: 5,
22
+ resultsPerPage: 10,
23
+ }),
16
24
  ).toBe(9);
17
25
  });
18
26
  });
@@ -15,6 +15,7 @@ describe('getPageNumbersToRequest', () => {
15
15
  page: 2,
16
16
  },
17
17
  metadata: {
18
+ cachedNodeIds: [],
18
19
  totalPages: 5,
19
20
  totalResults: 50,
20
21
  },
@@ -41,6 +42,7 @@ describe('getPageNumbersToRequest', () => {
41
42
  page: 4,
42
43
  },
43
44
  metadata: {
45
+ cachedNodeIds: [],
44
46
  totalPages: 5,
45
47
  totalResults: 50,
46
48
  },
@@ -146,6 +146,7 @@ describe('getEndIndex', () => {
146
146
  page: 3,
147
147
  },
148
148
  metadata: {
149
+ cachedNodeIds: [],
149
150
  totalPages: 5,
150
151
  totalResults: 50,
151
152
  },
@@ -172,6 +173,7 @@ describe('getEndIndex', () => {
172
173
  page: 6,
173
174
  },
174
175
  metadata: {
176
+ cachedNodeIds: [],
175
177
  totalPages: 6,
176
178
  totalResults: 57,
177
179
  },
@@ -197,6 +199,7 @@ describe('getEndIndex', () => {
197
199
  page: 6,
198
200
  },
199
201
  metadata: {
202
+ cachedNodeIds: [],
200
203
  totalPages: 6,
201
204
  totalResults: 57,
202
205
  },
@@ -223,6 +226,7 @@ describe('getEndIndex', () => {
223
226
  page: 4,
224
227
  },
225
228
  metadata: {
229
+ cachedNodeIds: [],
226
230
  totalPages: 6,
227
231
  totalResults: 57,
228
232
  },
@@ -248,6 +252,7 @@ describe('getEndIndex', () => {
248
252
  page: 4,
249
253
  },
250
254
  metadata: {
255
+ cachedNodeIds: [],
251
256
  totalPages: 6,
252
257
  totalResults: 57,
253
258
  },
@@ -93,6 +93,7 @@ describe('getEndPageNumber', () => {
93
93
  const ctx = {
94
94
  endIndex: { absolute: 5, relative: 5 },
95
95
  metadata: {
96
+ cachedNodeIds: [],
96
97
  totalPages: 5,
97
98
  totalResults: 50,
98
99
  },
@@ -114,6 +115,7 @@ describe('getEndPageNumber', () => {
114
115
  const ctx = {
115
116
  endIndex: { absolute: 5, relative: 5 },
116
117
  metadata: {
118
+ cachedNodeIds: [],
117
119
  totalPages: 5,
118
120
  totalResults: 50,
119
121
  },
@@ -134,6 +136,7 @@ describe('getEndPageNumber', () => {
134
136
  const ctx = {
135
137
  endIndex: { absolute: 9, relative: 9 },
136
138
  metadata: {
139
+ cachedNodeIds: [],
137
140
  totalPages: 5,
138
141
  totalResults: 50,
139
142
  },
@@ -154,6 +157,7 @@ describe('getEndPageNumber', () => {
154
157
  const ctx = {
155
158
  endIndex: { absolute: 37, relative: 6 },
156
159
  metadata: {
160
+ cachedNodeIds: [],
157
161
  totalPages: 5,
158
162
  totalResults: 50,
159
163
  },
@@ -174,6 +178,7 @@ describe('getEndPageNumber', () => {
174
178
  const ctx = {
175
179
  endIndex: { absolute: 22, relative: 4 },
176
180
  metadata: {
181
+ cachedNodeIds: [],
177
182
  totalPages: 5,
178
183
  totalResults: 50,
179
184
  },
@@ -1,6 +1,13 @@
1
1
  import { type Core } from '@cachemap/core';
2
2
  import { type PlainObject } from '@graphql-box/core';
3
- import { type Getters, type Node, type ResourceResolver, type SetCacheMetadata } from '../types.ts';
3
+ import {
4
+ type CachedEdges,
5
+ type CursorGroupMetadata,
6
+ type Getters,
7
+ type Node,
8
+ type ResourceResolver,
9
+ type SetCacheMetadata,
10
+ } from '../types.ts';
4
11
  import { cacheCursors } from './cacheCursors.ts';
5
12
  import { makeEdges } from './makeEdges.ts';
6
13
 
@@ -14,6 +21,10 @@ export type Context<Resource extends PlainObject, ResourceNode extends Node> = {
14
21
  setCacheMetadata: SetCacheMetadata | undefined;
15
22
  };
16
23
 
24
+ const filterOutCachedNodes = (nodes: Node[], cachedNodeIds: (string | number)[]): Node[] => {
25
+ return nodes.filter(node => !cachedNodeIds.includes(node.id));
26
+ };
27
+
17
28
  export const requestAndCachePages = async <Resource extends PlainObject, ResourceNode extends Node>(
18
29
  pages: number[],
19
30
  {
@@ -27,43 +38,47 @@ export const requestAndCachePages = async <Resource extends PlainObject, Resourc
27
38
  }: Context<Resource, ResourceNode>,
28
39
  ) => {
29
40
  const errors: Error[] = [];
41
+ const metadata = await cursorCache.get<CursorGroupMetadata>(`${groupCursor}-metadata`);
42
+ let cachedNodeIds = metadata?.cachedNodeIds ?? [];
43
+ const cachedEdges: CachedEdges[] = [];
30
44
 
31
- const cachedEdges = await Promise.all(
32
- pages.map(async page => {
33
- const {
34
- data: pageResultData,
35
- errors: pageResultErrors,
36
- headers: pageResultHeaders,
37
- } = await resourceResolver({
38
- page,
39
- });
45
+ for (const page of pages) {
46
+ const {
47
+ data: pageResultData,
48
+ errors: pageResultErrors,
49
+ headers: pageResultHeaders,
50
+ } = await resourceResolver({
51
+ page,
52
+ });
40
53
 
41
- if (pageResultData) {
42
- setCacheMetadata?.(fieldPath, pageResultHeaders);
43
- }
54
+ if (pageResultData) {
55
+ setCacheMetadata?.(fieldPath, pageResultHeaders);
56
+ }
44
57
 
45
- if (pageResultData && !pageResultErrors?.length) {
46
- const edges = makeEdges(getters.nodes(pageResultData), node => makeIDCursor(node.id));
58
+ if (pageResultData && !pageResultErrors?.length) {
59
+ const nodes = filterOutCachedNodes(getters.nodes(pageResultData), cachedNodeIds);
60
+ const edges = makeEdges(nodes, node => makeIDCursor(node.id));
61
+ cachedNodeIds = [...cachedNodeIds, ...edges.map(edge => edge.node.id)];
47
62
 
48
- await cacheCursors(cursorCache, {
49
- edges,
50
- group: groupCursor,
51
- headers: pageResultHeaders,
52
- page,
53
- totalPages: getters.totalPages(pageResultData),
54
- totalResults: getters.totalResults(pageResultData),
55
- });
56
-
57
- return { edges, pageNumber: page };
58
- }
63
+ await cacheCursors(cursorCache, {
64
+ cachedNodeIds,
65
+ edges,
66
+ group: groupCursor,
67
+ headers: pageResultHeaders,
68
+ page,
69
+ totalPages: getters.totalPages(pageResultData),
70
+ totalResults: getters.totalResults(pageResultData),
71
+ });
59
72
 
60
- if (pageResultErrors?.length) {
61
- errors.push(...pageResultErrors);
62
- }
73
+ cachedEdges.push({ edges, pageNumber: page });
74
+ } else {
75
+ cachedEdges.push({ edges: [], pageNumber: page });
76
+ }
63
77
 
64
- return { edges: [], pageNumber: page };
65
- }),
66
- );
78
+ if (pageResultErrors?.length) {
79
+ errors.push(...pageResultErrors);
80
+ }
81
+ }
67
82
 
68
83
  return { cachedEdges, errors };
69
84
  };
@@ -72,7 +72,7 @@ describe('validateCursor', () => {
72
72
 
73
73
  it('should return the correct error', async () => {
74
74
  const result = await validateCursor(args, info as GraphQLResolveInfo, ctx);
75
- expect(result?.message).toBe('Curser cannot be supplied without previously being provided.');
75
+ expect(result?.message).toBe('Cursor cannot be supplied without previously being provided.');
76
76
  });
77
77
  });
78
78
 
@@ -48,7 +48,7 @@ export const validateCursor = async (
48
48
  const metadata = await cursorCache.get<CursorGroupMetadata>(`${groupCursor}-metadata`);
49
49
 
50
50
  if (!metadata) {
51
- return new GraphQLError('Curser cannot be supplied without previously being provided.', { nodes: fieldNodes });
51
+ return new GraphQLError('Cursor cannot be supplied without previously being provided.', { nodes: fieldNodes });
52
52
  }
53
53
 
54
54
  const cursor = getCursor({ after, before });
package/src/types.ts CHANGED
@@ -16,6 +16,7 @@ export type CursorCacheEntry = {
16
16
  export type PartialCursorCacheEntry = SetOptional<CursorCacheEntry, 'group' | 'node'>;
17
17
 
18
18
  export type CursorGroupMetadata = {
19
+ cachedNodeIds: (string | number)[];
19
20
  totalPages: number;
20
21
  totalResults: number;
21
22
  };