@graphql-box/cache-manager 2.1.2 → 2.3.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.
- package/lib/browser/index.js +1 -1
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/production.analysis.txt +139 -17
- package/lib/main/debug/log-cache-entry/index.js.map +1 -1
- package/lib/main/debug/log-partial-compiled/index.js.map +1 -1
- package/lib/main/helpers/buildKeysAndPaths.js +73 -0
- package/lib/main/helpers/buildKeysAndPaths.js.map +1 -0
- package/lib/main/helpers/checkFieldPathChecklist.js +40 -0
- package/lib/main/helpers/checkFieldPathChecklist.js.map +1 -0
- package/lib/main/helpers/createFragmentSpreadChecklist.js +28 -0
- package/lib/main/helpers/createFragmentSpreadChecklist.js.map +1 -0
- package/lib/main/helpers/deriveOpCacheability.js +46 -0
- package/lib/main/helpers/deriveOpCacheability.js.map +1 -0
- package/lib/main/helpers/filterField.js +97 -0
- package/lib/main/helpers/filterField.js.map +1 -0
- package/lib/main/helpers/filterFragmentDefinitions.js +50 -0
- package/lib/main/helpers/filterFragmentDefinitions.js.map +1 -0
- package/lib/main/helpers/filterFragmentSpreads.js +37 -0
- package/lib/main/helpers/filterFragmentSpreads.js.map +1 -0
- package/lib/main/helpers/filterIDsAndTypeNames.js +47 -0
- package/lib/main/helpers/filterIDsAndTypeNames.js.map +1 -0
- package/lib/main/helpers/filterInlineFragments.js +42 -0
- package/lib/main/helpers/filterInlineFragments.js.map +1 -0
- package/lib/main/helpers/filterOutPropsWithArgsOrDirectives.js +39 -0
- package/lib/main/helpers/filterOutPropsWithArgsOrDirectives.js.map +1 -0
- package/lib/main/helpers/filterQuery.js +59 -0
- package/lib/main/helpers/filterQuery.js.map +1 -0
- package/lib/main/helpers/normalizeResponseData.js +23 -0
- package/lib/main/helpers/normalizeResponseData.js.map +1 -0
- package/lib/main/helpers/validTypeIDValue.js +20 -0
- package/lib/main/helpers/validTypeIDValue.js.map +1 -0
- package/lib/main/main/index.js +477 -476
- package/lib/main/main/index.js.map +1 -1
- package/lib/module/debug/log-cache-entry/index.js.map +1 -1
- package/lib/module/debug/log-partial-compiled/index.js.map +1 -1
- package/lib/module/helpers/buildKeysAndPaths.js +54 -0
- package/lib/module/helpers/buildKeysAndPaths.js.map +1 -0
- package/lib/module/helpers/checkFieldPathChecklist.js +31 -0
- package/lib/module/helpers/checkFieldPathChecklist.js.map +1 -0
- package/lib/module/helpers/createFragmentSpreadChecklist.js +15 -0
- package/lib/module/helpers/createFragmentSpreadChecklist.js.map +1 -0
- package/lib/module/helpers/deriveOpCacheability.js +32 -0
- package/lib/module/helpers/deriveOpCacheability.js.map +1 -0
- package/lib/module/helpers/filterField.js +81 -0
- package/lib/module/helpers/filterField.js.map +1 -0
- package/lib/module/helpers/filterFragmentDefinitions.js +39 -0
- package/lib/module/helpers/filterFragmentDefinitions.js.map +1 -0
- package/lib/module/helpers/filterFragmentSpreads.js +23 -0
- package/lib/module/helpers/filterFragmentSpreads.js.map +1 -0
- package/lib/module/helpers/filterIDsAndTypeNames.js +36 -0
- package/lib/module/helpers/filterIDsAndTypeNames.js.map +1 -0
- package/lib/module/helpers/filterInlineFragments.js +32 -0
- package/lib/module/helpers/filterInlineFragments.js.map +1 -0
- package/lib/module/helpers/filterOutPropsWithArgsOrDirectives.js +25 -0
- package/lib/module/helpers/filterOutPropsWithArgsOrDirectives.js.map +1 -0
- package/lib/module/helpers/filterQuery.js +43 -0
- package/lib/module/helpers/filterQuery.js.map +1 -0
- package/lib/module/helpers/normalizeResponseData.js +11 -0
- package/lib/module/helpers/normalizeResponseData.js.map +1 -0
- package/lib/module/helpers/validTypeIDValue.js +8 -0
- package/lib/module/helpers/validTypeIDValue.js.map +1 -0
- package/lib/module/main/index.js +474 -475
- package/lib/module/main/index.js.map +1 -1
- package/lib/types/debug/log-cache-entry/index.d.ts.map +1 -1
- package/lib/types/debug/log-cache-query/index.d.ts.map +1 -1
- package/lib/types/debug/log-partial-compiled/index.d.ts.map +1 -1
- package/lib/types/defs/index.d.ts +19 -9
- package/lib/types/defs/index.d.ts.map +1 -1
- package/lib/types/helpers/buildKeysAndPaths.d.ts +10 -0
- package/lib/types/helpers/buildKeysAndPaths.d.ts.map +1 -0
- package/lib/types/helpers/checkFieldPathChecklist.d.ts +4 -0
- package/lib/types/helpers/checkFieldPathChecklist.d.ts.map +1 -0
- package/lib/types/helpers/createFragmentSpreadChecklist.d.ts +11 -0
- package/lib/types/helpers/createFragmentSpreadChecklist.d.ts.map +1 -0
- package/lib/types/helpers/deriveOpCacheability.d.ts +10 -0
- package/lib/types/helpers/deriveOpCacheability.d.ts.map +1 -0
- package/lib/types/helpers/filterField.d.ts +6 -0
- package/lib/types/helpers/filterField.d.ts.map +1 -0
- package/lib/types/helpers/filterFragmentDefinitions.d.ts +10 -0
- package/lib/types/helpers/filterFragmentDefinitions.d.ts.map +1 -0
- package/lib/types/helpers/filterFragmentSpreads.d.ts +6 -0
- package/lib/types/helpers/filterFragmentSpreads.d.ts.map +1 -0
- package/lib/types/helpers/filterIDsAndTypeNames.d.ts +5 -0
- package/lib/types/helpers/filterIDsAndTypeNames.d.ts.map +1 -0
- package/lib/types/helpers/filterInlineFragments.d.ts +5 -0
- package/lib/types/helpers/filterInlineFragments.d.ts.map +1 -0
- package/lib/types/helpers/filterOutPropsWithArgsOrDirectives.d.ts +6 -0
- package/lib/types/helpers/filterOutPropsWithArgsOrDirectives.d.ts.map +1 -0
- package/lib/types/helpers/filterQuery.d.ts +5 -0
- package/lib/types/helpers/filterQuery.d.ts.map +1 -0
- package/lib/types/helpers/normalizeResponseData.d.ts +10 -0
- package/lib/types/helpers/normalizeResponseData.d.ts.map +1 -0
- package/lib/types/helpers/validTypeIDValue.d.ts +3 -0
- package/lib/types/helpers/validTypeIDValue.d.ts.map +1 -0
- package/lib/types/main/index.d.ts +13 -20
- package/lib/types/main/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__snapshots__/index.test.ts.snap +17449 -7185
- package/src/debug/log-cache-entry/index.ts +1 -1
- package/src/debug/log-partial-compiled/index.ts +1 -1
- package/src/defs/index.ts +18 -10
- package/src/helpers/buildKeysAndPaths.ts +71 -0
- package/src/helpers/checkFieldPathChecklist.ts +21 -0
- package/src/helpers/createFragmentSpreadChecklist.ts +17 -0
- package/src/helpers/deriveOpCacheability.ts +32 -0
- package/src/helpers/filterField.ts +73 -0
- package/src/helpers/filterFragmentDefinitions.ts +40 -0
- package/src/helpers/filterFragmentSpreads.ts +28 -0
- package/src/helpers/filterIDsAndTypeNames.ts +31 -0
- package/src/helpers/filterInlineFragments.ts +29 -0
- package/src/helpers/filterOutPropsWithArgsOrDirectives.ts +30 -0
- package/src/helpers/filterQuery.ts +38 -0
- package/src/helpers/normalizeResponseData.ts +9 -0
- package/src/helpers/validTypeIDValue.ts +11 -0
- package/src/index.test.ts +179 -3
- package/src/main/index.ts +516 -499
|
@@ -11,7 +11,7 @@ export default function logCacheEntry() {
|
|
|
11
11
|
if (!method) return;
|
|
12
12
|
|
|
13
13
|
descriptor.value = async function descriptorValue(...args: any[]): Promise<any> {
|
|
14
|
-
return new Promise(async resolve => {
|
|
14
|
+
return new Promise<void>(async resolve => {
|
|
15
15
|
const { debugManager, ...otherContext } = args[5] as RequestContext;
|
|
16
16
|
|
|
17
17
|
if (!debugManager) {
|
|
@@ -11,7 +11,7 @@ export default function logPartialCompiled() {
|
|
|
11
11
|
if (!method) return;
|
|
12
12
|
|
|
13
13
|
descriptor.value = async function descriptorValue(...args: any[]): Promise<any> {
|
|
14
|
-
return new Promise(async resolve => {
|
|
14
|
+
return new Promise<void>(async resolve => {
|
|
15
15
|
const { debugManager, ...otherContext } = args[3] as RequestContext;
|
|
16
16
|
|
|
17
17
|
if (!debugManager) {
|
package/src/defs/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
RequestOptions,
|
|
13
13
|
ResponseData,
|
|
14
14
|
} from "@graphql-box/core";
|
|
15
|
+
import { FragmentDefinitionNodeMap } from "@graphql-box/helpers";
|
|
15
16
|
import Cacheability from "cacheability";
|
|
16
17
|
|
|
17
18
|
export interface UserOptions {
|
|
@@ -62,6 +63,11 @@ export interface ConstructorOptions {
|
|
|
62
63
|
typeIDKey: string;
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
export interface CacheManagerContext extends RequestContext {
|
|
67
|
+
fragmentDefinitions?: FragmentDefinitionNodeMap;
|
|
68
|
+
typeIDKey?: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
65
71
|
export interface PartialQueryResponse {
|
|
66
72
|
cacheMetadata: CacheMetadata;
|
|
67
73
|
data: PlainObjectMap;
|
|
@@ -75,6 +81,8 @@ export interface FieldCount {
|
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
export interface FieldPathChecklistValue {
|
|
84
|
+
fragmentKind: string | undefined;
|
|
85
|
+
fragmentName: string | undefined;
|
|
78
86
|
hasData: boolean;
|
|
79
87
|
typeName?: string | undefined;
|
|
80
88
|
}
|
|
@@ -100,12 +108,6 @@ export interface AncestorKeysAndPaths {
|
|
|
100
108
|
responseDataPath?: string;
|
|
101
109
|
}
|
|
102
110
|
|
|
103
|
-
export interface CachedFieldData {
|
|
104
|
-
cacheability?: Cacheability;
|
|
105
|
-
dataEntityData?: any;
|
|
106
|
-
requestFieldPathData?: any;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
111
|
export interface MergedCachedFieldData {
|
|
110
112
|
cacheability?: Cacheability;
|
|
111
113
|
data: any;
|
|
@@ -113,7 +115,9 @@ export interface MergedCachedFieldData {
|
|
|
113
115
|
|
|
114
116
|
export interface CachedAncestorFieldData {
|
|
115
117
|
cacheability?: Cacheability;
|
|
116
|
-
|
|
118
|
+
entityData?: any;
|
|
119
|
+
fragmentKind?: string;
|
|
120
|
+
fragmentName?: string;
|
|
117
121
|
index?: number;
|
|
118
122
|
requestFieldCacheKey?: string;
|
|
119
123
|
requestFieldPath?: string;
|
|
@@ -136,14 +140,18 @@ export interface KeysAndPaths {
|
|
|
136
140
|
responseDataPath: string;
|
|
137
141
|
}
|
|
138
142
|
|
|
139
|
-
export interface
|
|
143
|
+
export interface TypeNamesAndKind {
|
|
140
144
|
dataTypeName: string | undefined;
|
|
141
145
|
fieldTypeName: string | undefined;
|
|
146
|
+
fragmentKind: string | undefined;
|
|
147
|
+
fragmentName: string | undefined;
|
|
142
148
|
}
|
|
143
149
|
|
|
150
|
+
export type FragmentSpreadFieldCounter = Record<string, { hasData: number; total: number }>;
|
|
151
|
+
|
|
144
152
|
export interface ResponseDataForCaching {
|
|
145
153
|
cacheMetadata: CacheMetadata;
|
|
146
|
-
|
|
154
|
+
entityData: PlainObjectMap;
|
|
147
155
|
requestFieldPathData: PlainObjectMap;
|
|
148
156
|
}
|
|
149
157
|
|
|
@@ -165,7 +173,7 @@ export interface AnalyzeQueryResult {
|
|
|
165
173
|
|
|
166
174
|
export interface CheckCacheEntryResult {
|
|
167
175
|
cacheability: Cacheability;
|
|
168
|
-
entry:
|
|
176
|
+
entry: any;
|
|
169
177
|
}
|
|
170
178
|
|
|
171
179
|
export interface QueryResponseCacheEntry {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { FieldTypeInfo, PlainObjectMap } from "@graphql-box/core";
|
|
2
|
+
import { getAlias, getArguments, getName, hashRequest } from "@graphql-box/helpers";
|
|
3
|
+
import { FieldNode } from "graphql";
|
|
4
|
+
import { isNumber } from "lodash";
|
|
5
|
+
import { CacheManagerContext, KeysAndPaths, KeysAndPathsOptions } from "../defs";
|
|
6
|
+
|
|
7
|
+
export const buildKey = (path: string, key: string | number) => {
|
|
8
|
+
const paths: (string | number)[] = [];
|
|
9
|
+
|
|
10
|
+
if (path.length) {
|
|
11
|
+
paths.push(path);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
paths.push(key);
|
|
15
|
+
return paths.join(".");
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const buildRequestFieldCacheKey = (
|
|
19
|
+
name: string,
|
|
20
|
+
requestFieldCacheKey: string,
|
|
21
|
+
args: PlainObjectMap | undefined,
|
|
22
|
+
directives?: FieldTypeInfo["directives"],
|
|
23
|
+
index?: number,
|
|
24
|
+
) => {
|
|
25
|
+
let key = `${isNumber(index) ? index : name}`;
|
|
26
|
+
|
|
27
|
+
if (args) {
|
|
28
|
+
key = `${key}(${JSON.stringify(args)})`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (directives?.inherited?.length) {
|
|
32
|
+
key = `${directives.inherited.join(" ")} ${key}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (directives?.own?.length) {
|
|
36
|
+
key = `${key} ${directives.own.join(" ")}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return buildKey(requestFieldCacheKey, key);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const buildFieldKeysAndPaths = (
|
|
43
|
+
field: FieldNode,
|
|
44
|
+
options: KeysAndPathsOptions,
|
|
45
|
+
context: CacheManagerContext,
|
|
46
|
+
): KeysAndPaths => {
|
|
47
|
+
const name = getName(field) as FieldNode["name"]["value"];
|
|
48
|
+
const { index, requestFieldCacheKey = "", requestFieldPath = "", responseDataPath = "" } = options;
|
|
49
|
+
const fieldAliasOrName = getAlias(field) || name;
|
|
50
|
+
const updatedRequestFieldPath = isNumber(index) ? requestFieldPath : buildKey(requestFieldPath, fieldAliasOrName);
|
|
51
|
+
const fieldTypeInfo = context.fieldTypeMap.get(updatedRequestFieldPath);
|
|
52
|
+
|
|
53
|
+
const updatedRequestFieldCacheKey = buildRequestFieldCacheKey(
|
|
54
|
+
name,
|
|
55
|
+
requestFieldCacheKey,
|
|
56
|
+
getArguments(field),
|
|
57
|
+
fieldTypeInfo?.directives,
|
|
58
|
+
index,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const propNameOrIndex = isNumber(index) ? index : fieldAliasOrName;
|
|
62
|
+
const updatedResponseDataPath = buildKey(responseDataPath, propNameOrIndex);
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
hashedRequestFieldCacheKey: hashRequest(updatedRequestFieldCacheKey),
|
|
66
|
+
propNameOrIndex,
|
|
67
|
+
requestFieldCacheKey: updatedRequestFieldCacheKey,
|
|
68
|
+
requestFieldPath: updatedRequestFieldPath,
|
|
69
|
+
responseDataPath: updatedResponseDataPath,
|
|
70
|
+
};
|
|
71
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CheckFieldPathChecklistResult, FieldPathChecklistValue } from "../defs";
|
|
2
|
+
|
|
3
|
+
export default (
|
|
4
|
+
fieldPathChecklistValues: FieldPathChecklistValue[] | undefined,
|
|
5
|
+
fieldTypeName: string | undefined,
|
|
6
|
+
): CheckFieldPathChecklistResult => {
|
|
7
|
+
if (!fieldPathChecklistValues || !fieldPathChecklistValues.length) {
|
|
8
|
+
return { hasData: false, typeUnused: !!fieldTypeName };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (fieldPathChecklistValues.length === 1) {
|
|
12
|
+
const { hasData, typeName } = fieldPathChecklistValues[0];
|
|
13
|
+
const typeUnused = !typeName ? undefined : typeName !== fieldTypeName;
|
|
14
|
+
return { hasData, typeUnused };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
hasData: fieldPathChecklistValues.some(({ hasData, typeName }) => typeName === fieldTypeName && hasData),
|
|
19
|
+
typeUnused: !fieldPathChecklistValues.every(({ typeName }) => typeName === fieldTypeName),
|
|
20
|
+
};
|
|
21
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getFragmentDefinitions } from "@graphql-box/helpers";
|
|
2
|
+
import { DocumentNode } from "graphql";
|
|
3
|
+
import { keys } from "lodash";
|
|
4
|
+
|
|
5
|
+
export type FragmentSpreadCheckist = {
|
|
6
|
+
[key: string]: {
|
|
7
|
+
deleted: number;
|
|
8
|
+
paths: string[];
|
|
9
|
+
total: number;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default (ast: DocumentNode) =>
|
|
14
|
+
keys(getFragmentDefinitions(ast) ?? {}).reduce((acc: FragmentSpreadCheckist, name) => {
|
|
15
|
+
acc[name] = { deleted: 0, paths: [], total: 0 };
|
|
16
|
+
return acc;
|
|
17
|
+
}, {});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { DehydratedCacheMetadata } from "@graphql-box/core";
|
|
2
|
+
import Cacheability from "cacheability";
|
|
3
|
+
import { isEmpty } from "lodash";
|
|
4
|
+
import { HEADER_CACHE_CONTROL } from "../consts";
|
|
5
|
+
|
|
6
|
+
export type Params = {
|
|
7
|
+
_cacheMetadata?: DehydratedCacheMetadata;
|
|
8
|
+
fallback: string;
|
|
9
|
+
headers?: Headers;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default ({ _cacheMetadata, fallback, headers }: Params): Cacheability => {
|
|
13
|
+
if (_cacheMetadata && !isEmpty(_cacheMetadata)) {
|
|
14
|
+
const [first, ...rest] = Object.values(_cacheMetadata);
|
|
15
|
+
|
|
16
|
+
return new Cacheability({
|
|
17
|
+
metadata: rest.reduce((acc, metadata) => {
|
|
18
|
+
if (metadata.ttl < acc.ttl) {
|
|
19
|
+
return metadata;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return acc;
|
|
23
|
+
}, first),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (headers && headers.has(HEADER_CACHE_CONTROL)) {
|
|
28
|
+
return new Cacheability({ headers });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return new Cacheability({ cacheControl: fallback });
|
|
32
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { TYPE_NAME_KEY } from "@graphql-box/core";
|
|
2
|
+
import { FRAGMENT_SPREAD, deleteChildFields, getChildFields, getName, hasChildFields } from "@graphql-box/helpers";
|
|
3
|
+
import { FieldNode, FragmentDefinitionNode } from "graphql";
|
|
4
|
+
import { CacheManagerContext, FieldPathChecklist, FragmentSpreadFieldCounter } from "../defs";
|
|
5
|
+
import { buildFieldKeysAndPaths } from "./buildKeysAndPaths";
|
|
6
|
+
import checkFieldPathChecklist from "./checkFieldPathChecklist";
|
|
7
|
+
import { FragmentSpreadCheckist } from "./createFragmentSpreadChecklist";
|
|
8
|
+
import filterFragmentSpreads from "./filterFragmentSpreads";
|
|
9
|
+
import filterIDsAndTypeNames from "./filterIDsAndTypeNames";
|
|
10
|
+
import filterInlineFragments from "./filterInlineFragments";
|
|
11
|
+
|
|
12
|
+
const filterField = (
|
|
13
|
+
field: FieldNode | FragmentDefinitionNode,
|
|
14
|
+
fieldPathChecklist: FieldPathChecklist,
|
|
15
|
+
fragmentSpreadChecklist: FragmentSpreadCheckist,
|
|
16
|
+
ancestorRequestFieldPath: string,
|
|
17
|
+
context: CacheManagerContext,
|
|
18
|
+
): boolean => {
|
|
19
|
+
const { fragmentDefinitions, typeIDKey } = context;
|
|
20
|
+
const fieldsAndTypeNames = getChildFields(field, { fragmentDefinitions });
|
|
21
|
+
|
|
22
|
+
if (!fieldsAndTypeNames) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const fragmentSpreadFieldCounter: FragmentSpreadFieldCounter = {};
|
|
27
|
+
|
|
28
|
+
for (let i = fieldsAndTypeNames.length - 1; i >= 0; i -= 1) {
|
|
29
|
+
const { fieldNode: childField, fragmentKind, fragmentName, typeName: childTypeName } = fieldsAndTypeNames[i];
|
|
30
|
+
|
|
31
|
+
if (fragmentKind === FRAGMENT_SPREAD && fragmentName && !fragmentSpreadFieldCounter[fragmentName]) {
|
|
32
|
+
fragmentSpreadFieldCounter[fragmentName] = {
|
|
33
|
+
hasData: 0,
|
|
34
|
+
total: fragmentDefinitions?.[fragmentName]
|
|
35
|
+
? getChildFields(fragmentDefinitions?.[fragmentName], { fragmentDefinitions })?.length ?? 0
|
|
36
|
+
: 0,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const childFieldName = getName(childField);
|
|
41
|
+
|
|
42
|
+
if (childFieldName === typeIDKey || childFieldName === TYPE_NAME_KEY) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const { requestFieldPath } = buildFieldKeysAndPaths(
|
|
47
|
+
childField,
|
|
48
|
+
{
|
|
49
|
+
requestFieldPath: ancestorRequestFieldPath,
|
|
50
|
+
},
|
|
51
|
+
context,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const { hasData, typeUnused } = checkFieldPathChecklist(fieldPathChecklist.get(requestFieldPath), childTypeName);
|
|
55
|
+
|
|
56
|
+
if (hasData || typeUnused) {
|
|
57
|
+
if (fragmentKind === FRAGMENT_SPREAD) {
|
|
58
|
+
fragmentSpreadFieldCounter[fragmentName as string].hasData += 1;
|
|
59
|
+
} else if (!hasChildFields(childField, { fragmentDefinitions })) {
|
|
60
|
+
deleteChildFields(field, childField);
|
|
61
|
+
} else if (filterField(childField, fieldPathChecklist, fragmentSpreadChecklist, requestFieldPath, context)) {
|
|
62
|
+
deleteChildFields(field, childField);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
filterFragmentSpreads(field, fragmentSpreadFieldCounter, fragmentSpreadChecklist, ancestorRequestFieldPath);
|
|
68
|
+
filterInlineFragments(field, context);
|
|
69
|
+
filterIDsAndTypeNames(field, context);
|
|
70
|
+
return !hasChildFields(field, { fragmentDefinitions });
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export default filterField;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { deleteFragmentDefinitions, getFragmentDefinitions } from "@graphql-box/helpers";
|
|
2
|
+
import { DocumentNode } from "graphql";
|
|
3
|
+
import { keys } from "lodash";
|
|
4
|
+
import { CacheManagerContext, FieldPathChecklist } from "../defs";
|
|
5
|
+
import { FragmentSpreadCheckist } from "./createFragmentSpreadChecklist";
|
|
6
|
+
import filterField from "./filterField";
|
|
7
|
+
|
|
8
|
+
export default (
|
|
9
|
+
ast: DocumentNode,
|
|
10
|
+
fieldPathChecklist: FieldPathChecklist,
|
|
11
|
+
fragmentSpreadChecklist: FragmentSpreadCheckist,
|
|
12
|
+
context: CacheManagerContext,
|
|
13
|
+
) => {
|
|
14
|
+
const definitionsToFilter = keys(fragmentSpreadChecklist).reduce(
|
|
15
|
+
(namesAndPaths: { name: string; path: string }[], key) => {
|
|
16
|
+
const { deleted, total } = fragmentSpreadChecklist[key];
|
|
17
|
+
|
|
18
|
+
return deleted === 0 && total === 1
|
|
19
|
+
? [...namesAndPaths, { name: key, path: fragmentSpreadChecklist[key].paths[0] as string }]
|
|
20
|
+
: namesAndPaths;
|
|
21
|
+
},
|
|
22
|
+
[],
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const fragmentDefinitions = getFragmentDefinitions(ast) ?? {};
|
|
26
|
+
|
|
27
|
+
definitionsToFilter.forEach(({ name, path }) => {
|
|
28
|
+
const fragmentDefinition = fragmentDefinitions[name];
|
|
29
|
+
filterField(fragmentDefinition, fieldPathChecklist, fragmentSpreadChecklist, path, context);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const definitionsToDelete = keys(fragmentSpreadChecklist).reduce((names: string[], key) => {
|
|
33
|
+
const { deleted, total } = fragmentSpreadChecklist[key];
|
|
34
|
+
return deleted > 0 && deleted === total ? [...names, key] : names;
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
return deleteFragmentDefinitions(ast, {
|
|
38
|
+
include: definitionsToDelete,
|
|
39
|
+
});
|
|
40
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { deleteFragmentSpreads } from "@graphql-box/helpers";
|
|
2
|
+
import { FieldNode, FragmentDefinitionNode } from "graphql";
|
|
3
|
+
import { isEmpty, keys } from "lodash";
|
|
4
|
+
import { FragmentSpreadFieldCounter } from "../defs";
|
|
5
|
+
import { FragmentSpreadCheckist } from "./createFragmentSpreadChecklist";
|
|
6
|
+
|
|
7
|
+
export default (
|
|
8
|
+
field: FieldNode | FragmentDefinitionNode,
|
|
9
|
+
fragmentSpreadFieldCounter: FragmentSpreadFieldCounter,
|
|
10
|
+
fragmentSpreadChecklist: FragmentSpreadCheckist,
|
|
11
|
+
ancestorRequestFieldPath: string,
|
|
12
|
+
) => {
|
|
13
|
+
if (isEmpty(fragmentSpreadFieldCounter)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
keys(fragmentSpreadFieldCounter).forEach(key => {
|
|
18
|
+
fragmentSpreadChecklist[key].total += 1;
|
|
19
|
+
fragmentSpreadChecklist[key].paths.push(ancestorRequestFieldPath);
|
|
20
|
+
|
|
21
|
+
const { hasData, total } = fragmentSpreadFieldCounter[key];
|
|
22
|
+
|
|
23
|
+
if (hasData === total) {
|
|
24
|
+
deleteFragmentSpreads(field, key);
|
|
25
|
+
fragmentSpreadChecklist[key].deleted += 1;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { TYPE_NAME_KEY } from "@graphql-box/core";
|
|
2
|
+
import { deleteChildFields, getChildFields, getName } from "@graphql-box/helpers";
|
|
3
|
+
import { FieldNode, FragmentDefinitionNode } from "graphql";
|
|
4
|
+
import { CacheManagerContext } from "../defs";
|
|
5
|
+
|
|
6
|
+
export default (field: FieldNode | FragmentDefinitionNode, { fragmentDefinitions, typeIDKey }: CacheManagerContext) => {
|
|
7
|
+
const fieldsAndTypeNames = getChildFields(field, { fragmentDefinitions });
|
|
8
|
+
|
|
9
|
+
if (!fieldsAndTypeNames || fieldsAndTypeNames.length > 3) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const fieldNames = fieldsAndTypeNames.map(({ fieldNode }) => getName(fieldNode) as FieldNode["name"]["value"]);
|
|
14
|
+
|
|
15
|
+
if (fieldNames.length === 2 && fieldNames.every(name => name === typeIDKey || name === TYPE_NAME_KEY)) {
|
|
16
|
+
deleteChildFields(
|
|
17
|
+
field,
|
|
18
|
+
fieldsAndTypeNames.map(({ fieldNode }) => fieldNode),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if ((fieldNames.length === 1 && fieldNames[0] === typeIDKey) || fieldNames[0] === TYPE_NAME_KEY) {
|
|
25
|
+
const { fieldNode } = fieldsAndTypeNames[0];
|
|
26
|
+
deleteChildFields(field, fieldNode);
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return false;
|
|
31
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { deleteInlineFragments, getChildFields, getInlineFragments, getName } from "@graphql-box/helpers";
|
|
2
|
+
import { FieldNode, FragmentDefinitionNode } from "graphql";
|
|
3
|
+
import { CacheManagerContext } from "..";
|
|
4
|
+
|
|
5
|
+
export default (field: FieldNode | FragmentDefinitionNode, { fragmentDefinitions, typeIDKey }: CacheManagerContext) => {
|
|
6
|
+
const inlineFragments = getInlineFragments(field);
|
|
7
|
+
let filtered = false;
|
|
8
|
+
|
|
9
|
+
inlineFragments.forEach(fragment => {
|
|
10
|
+
const fieldsAndTypeNames = getChildFields(fragment, { fragmentDefinitions });
|
|
11
|
+
|
|
12
|
+
if (!fieldsAndTypeNames || !fieldsAndTypeNames.length) {
|
|
13
|
+
deleteInlineFragments(field, fragment);
|
|
14
|
+
filtered = true;
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (fieldsAndTypeNames.length === 1) {
|
|
19
|
+
const { fieldNode } = fieldsAndTypeNames[0];
|
|
20
|
+
|
|
21
|
+
if (getName(fieldNode) === typeIDKey) {
|
|
22
|
+
deleteInlineFragments(field, fragment);
|
|
23
|
+
filtered = true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return filtered;
|
|
29
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { PlainObjectMap } from "@graphql-box/core";
|
|
2
|
+
import { getName, resolveFragments } from "@graphql-box/helpers";
|
|
3
|
+
import { FieldNode, SelectionNode } from "graphql";
|
|
4
|
+
import { keys } from "lodash";
|
|
5
|
+
import { CacheManagerContext, KeysAndPaths } from "../defs";
|
|
6
|
+
import { buildFieldKeysAndPaths } from "./buildKeysAndPaths";
|
|
7
|
+
|
|
8
|
+
export default (
|
|
9
|
+
fieldData: PlainObjectMap,
|
|
10
|
+
selectionNodes: readonly SelectionNode[],
|
|
11
|
+
ancestorKeysAndPaths: KeysAndPaths,
|
|
12
|
+
context: CacheManagerContext,
|
|
13
|
+
) => {
|
|
14
|
+
const fieldAndTypeName = resolveFragments(selectionNodes, context.fragmentDefinitions);
|
|
15
|
+
|
|
16
|
+
return keys(fieldData).reduce((acc: PlainObjectMap, key) => {
|
|
17
|
+
const match = fieldAndTypeName.find(({ fieldNode }) => (getName(fieldNode) as FieldNode["name"]["value"]) === key);
|
|
18
|
+
|
|
19
|
+
if (match) {
|
|
20
|
+
const { requestFieldPath } = buildFieldKeysAndPaths(match.fieldNode, ancestorKeysAndPaths, context);
|
|
21
|
+
const fieldTypeInfo = context.fieldTypeMap.get(requestFieldPath);
|
|
22
|
+
|
|
23
|
+
if (!fieldTypeInfo?.hasArguments && !fieldTypeInfo?.hasDirectives) {
|
|
24
|
+
acc[key] = fieldData[key];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return acc;
|
|
29
|
+
}, {});
|
|
30
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { RequestData } from "@graphql-box/core";
|
|
2
|
+
import { deleteChildFields, getChildFields, getOperationDefinitions } from "@graphql-box/helpers";
|
|
3
|
+
import { CacheManagerContext, CachedResponseData } from "../defs";
|
|
4
|
+
import { buildFieldKeysAndPaths } from "./buildKeysAndPaths";
|
|
5
|
+
import createFragmentSpreadChecklist from "./createFragmentSpreadChecklist";
|
|
6
|
+
import filterField from "./filterField";
|
|
7
|
+
import filterFragmentDefinitions from "./filterFragmentDefinitions";
|
|
8
|
+
|
|
9
|
+
export default ({ ast }: RequestData, { fieldPathChecklist }: CachedResponseData, context: CacheManagerContext) => {
|
|
10
|
+
const queryNode = getOperationDefinitions(ast, context.operation)[0];
|
|
11
|
+
const { fragmentDefinitions } = context;
|
|
12
|
+
const fieldsAndTypeNames = getChildFields(queryNode, { fragmentDefinitions });
|
|
13
|
+
|
|
14
|
+
if (!fieldsAndTypeNames) {
|
|
15
|
+
return ast;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const fragmentSpreadChecklist = createFragmentSpreadChecklist(ast);
|
|
19
|
+
|
|
20
|
+
for (let i = fieldsAndTypeNames.length - 1; i >= 0; i -= 1) {
|
|
21
|
+
const { fieldNode } = fieldsAndTypeNames[i];
|
|
22
|
+
|
|
23
|
+
const { requestFieldPath } = buildFieldKeysAndPaths(
|
|
24
|
+
fieldNode,
|
|
25
|
+
{
|
|
26
|
+
requestFieldPath: context.operation,
|
|
27
|
+
},
|
|
28
|
+
context,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
if (filterField(fieldNode, fieldPathChecklist, fragmentSpreadChecklist, requestFieldPath, context)) {
|
|
32
|
+
deleteChildFields(queryNode, fieldNode);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
context.queryFiltered = true;
|
|
37
|
+
return filterFragmentDefinitions(ast, fieldPathChecklist, fragmentSpreadChecklist, context);
|
|
38
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { RawResponseDataWithMaybeCacheMetadata } from "@graphql-box/core";
|
|
2
|
+
import { set } from "lodash";
|
|
3
|
+
|
|
4
|
+
export default ({ data, path, ...rest }: RawResponseDataWithMaybeCacheMetadata) => {
|
|
5
|
+
return {
|
|
6
|
+
...rest,
|
|
7
|
+
data: set({}, path as (string | number)[], data),
|
|
8
|
+
};
|
|
9
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FieldTypeInfo } from "@graphql-box/core";
|
|
2
|
+
import { isPlainObject } from "lodash";
|
|
3
|
+
|
|
4
|
+
export const getValidTypeIDValue = (
|
|
5
|
+
requestFieldPathData: any,
|
|
6
|
+
{ typeIDValue }: FieldTypeInfo,
|
|
7
|
+
typeIDKey: string,
|
|
8
|
+
): string | number | undefined => {
|
|
9
|
+
const requestFieldPathDataIDValue = isPlainObject(requestFieldPathData) ? requestFieldPathData[typeIDKey] : undefined;
|
|
10
|
+
return typeIDValue || requestFieldPathDataIDValue;
|
|
11
|
+
};
|