@envelop/response-cache 2.4.0-alpha-aca44e1.0 → 2.4.0-alpha-e9434aa.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/cjs/cache.js +2 -0
- package/cjs/hashSHA256.js +66 -0
- package/cjs/in-memory-cache.js +86 -0
- package/cjs/index.js +6 -0
- package/cjs/package.json +1 -0
- package/cjs/plugin.js +270 -0
- package/esm/cache.js +2 -0
- package/esm/hashSHA256.js +66 -0
- package/esm/in-memory-cache.js +88 -0
- package/esm/index.js +19 -0
- package/esm/plugin.js +272 -0
- package/package.json +35 -14
- package/{cache.d.ts → typings/cache.d.ts} +0 -0
- package/{hashSHA256.d.ts → typings/hashSHA256.d.ts} +0 -0
- package/{in-memory-cache.d.ts → typings/in-memory-cache.d.ts} +2 -2
- package/typings/index.d.ts +3 -0
- package/{plugin.d.ts → typings/plugin.d.ts} +1 -1
- package/README.md +0 -491
- package/index.d.ts +0 -3
- package/index.js +0 -422
- package/index.mjs +0 -411
package/cjs/cache.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hashSHA256 = void 0;
|
|
4
|
+
const webCrypto = globalThis.crypto;
|
|
5
|
+
const sha256Browser = (bytes) => {
|
|
6
|
+
const hash = webCrypto.subtle.digest({ name: 'SHA-256' }, bytes);
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
Promise.resolve(hash)
|
|
9
|
+
.then(function (result) {
|
|
10
|
+
resolve(new Uint8Array(result));
|
|
11
|
+
})
|
|
12
|
+
.catch(function (error) {
|
|
13
|
+
reject(error);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
let nodeCrypto;
|
|
18
|
+
if (typeof window === 'undefined' && !webCrypto) {
|
|
19
|
+
// Indirect eval'd require/import to guarantee no side-effects in module scope
|
|
20
|
+
// (optimization for minifier)
|
|
21
|
+
try {
|
|
22
|
+
// eslint-disable-next-line no-new-func
|
|
23
|
+
nodeCrypto = Promise.resolve(new Function('require', 'return require("crypto")')(require));
|
|
24
|
+
}
|
|
25
|
+
catch (_error) {
|
|
26
|
+
try {
|
|
27
|
+
// eslint-disable-next-line no-new-func
|
|
28
|
+
nodeCrypto = new Function('return import("crypto")')();
|
|
29
|
+
}
|
|
30
|
+
catch (_error) { }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const hashSHA256 = async (text) => {
|
|
34
|
+
// Node.js support
|
|
35
|
+
if (nodeCrypto) {
|
|
36
|
+
return nodeCrypto.then(crypto => crypto.createHash('sha256').update(text).digest('hex'));
|
|
37
|
+
}
|
|
38
|
+
if (webCrypto) {
|
|
39
|
+
let buf;
|
|
40
|
+
if (typeof TextEncoder !== 'undefined') {
|
|
41
|
+
buf = new TextEncoder().encode(text);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
buf = new Uint8Array(text.length);
|
|
45
|
+
for (let i = 0, l = text.length; i < l; i++) {
|
|
46
|
+
// NOTE: We assume that the input GraphQL Query only uses UTF-8 at most
|
|
47
|
+
// since GraphQL mostly consists of ASCII, this is completely fine
|
|
48
|
+
buf[i] = text.charCodeAt(i);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const out = await sha256Browser(buf);
|
|
52
|
+
let hash = '';
|
|
53
|
+
for (let i = 0, l = out.length; i < l; i++) {
|
|
54
|
+
const hex = out[i].toString(16);
|
|
55
|
+
hash += '00'.slice(0, Math.max(0, 2 - hex.length)) + hex;
|
|
56
|
+
}
|
|
57
|
+
return hash;
|
|
58
|
+
}
|
|
59
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
60
|
+
// eslint-disable-next-line no-console
|
|
61
|
+
console.warn('[@envelop/response-cache]: The Node Crypto and Web Crypto APIs are not available.\n' +
|
|
62
|
+
'This is an unexpected error. Please report it by filing a GitHub Issue.');
|
|
63
|
+
}
|
|
64
|
+
return Promise.resolve('');
|
|
65
|
+
};
|
|
66
|
+
exports.hashSHA256 = hashSHA256;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultBuildEntityId = exports.createInMemoryCache = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const lru_cache_1 = tslib_1.__importDefault(require("lru-cache"));
|
|
6
|
+
const createInMemoryCache = (params) => {
|
|
7
|
+
var _a;
|
|
8
|
+
const buildEntityId = (_a = params === null || params === void 0 ? void 0 : params.buildEntityId) !== null && _a !== void 0 ? _a : exports.defaultBuildEntityId;
|
|
9
|
+
const cachedResponses = new lru_cache_1.default({
|
|
10
|
+
max: params === null || params === void 0 ? void 0 : params.max,
|
|
11
|
+
stale: false,
|
|
12
|
+
noDisposeOnSet: true,
|
|
13
|
+
dispose(responseId) {
|
|
14
|
+
purgeResponse(responseId, false);
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
const entityToResponseIds = new Map();
|
|
18
|
+
const responseIdToEntityIds = new Map();
|
|
19
|
+
function purgeResponse(responseId, shouldRemove = true) {
|
|
20
|
+
var _a;
|
|
21
|
+
const entityIds = responseIdToEntityIds.get(responseId);
|
|
22
|
+
// get entities related to the response
|
|
23
|
+
if (entityIds !== undefined) {
|
|
24
|
+
for (const entityId of entityIds) {
|
|
25
|
+
// remove the response mapping from the entity
|
|
26
|
+
(_a = entityToResponseIds.get(entityId)) === null || _a === void 0 ? void 0 : _a.delete(responseId);
|
|
27
|
+
}
|
|
28
|
+
// remove all the entity mappings from the response
|
|
29
|
+
responseIdToEntityIds.delete(responseId);
|
|
30
|
+
}
|
|
31
|
+
if (shouldRemove) {
|
|
32
|
+
// remove the response from the cache
|
|
33
|
+
cachedResponses.del(responseId);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function purgeEntity(entity) {
|
|
37
|
+
const responseIds = entityToResponseIds.get(entity);
|
|
38
|
+
if (responseIds !== undefined) {
|
|
39
|
+
for (const responseId of responseIds) {
|
|
40
|
+
purgeResponse(responseId);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
set(responseId, result, collectedEntities, ttl) {
|
|
46
|
+
cachedResponses.set(responseId, result, ttl);
|
|
47
|
+
const entityIds = new Set();
|
|
48
|
+
responseIdToEntityIds.set(responseId, entityIds);
|
|
49
|
+
for (const { typename, id } of collectedEntities) {
|
|
50
|
+
let operationIds = entityToResponseIds.get(typename);
|
|
51
|
+
if (operationIds == null) {
|
|
52
|
+
operationIds = new Set();
|
|
53
|
+
entityToResponseIds.set(typename, operationIds);
|
|
54
|
+
}
|
|
55
|
+
// typename => operation
|
|
56
|
+
operationIds.add(responseId);
|
|
57
|
+
// operation => typename
|
|
58
|
+
entityIds.add(typename);
|
|
59
|
+
if (id !== undefined) {
|
|
60
|
+
const entityId = buildEntityId(typename, id);
|
|
61
|
+
let responseIds = entityToResponseIds.get(entityId);
|
|
62
|
+
if (responseIds == null) {
|
|
63
|
+
responseIds = new Set();
|
|
64
|
+
entityToResponseIds.set(entityId, responseIds);
|
|
65
|
+
}
|
|
66
|
+
// typename:id => operation
|
|
67
|
+
responseIds.add(responseId);
|
|
68
|
+
// operation => typename:id
|
|
69
|
+
entityIds.add(entityId);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
get(responseId) {
|
|
74
|
+
var _a;
|
|
75
|
+
return (_a = cachedResponses.get(responseId)) !== null && _a !== void 0 ? _a : null;
|
|
76
|
+
},
|
|
77
|
+
invalidate(entitiesToRemove) {
|
|
78
|
+
for (const { typename, id } of entitiesToRemove) {
|
|
79
|
+
purgeEntity(id != null ? buildEntityId(typename, id) : typename);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
exports.createInMemoryCache = createInMemoryCache;
|
|
85
|
+
const defaultBuildEntityId = (typename, id) => `${typename}:${id}`;
|
|
86
|
+
exports.defaultBuildEntityId = defaultBuildEntityId;
|
package/cjs/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("./in-memory-cache.js"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./plugin.js"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./cache.js"), exports);
|
package/cjs/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
package/cjs/plugin.js
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useResponseCache = exports.defaultGetDocumentStringFromContext = exports.defaultShouldCacheResult = exports.defaultBuildResponseCacheKey = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const core_1 = require("@envelop/core");
|
|
6
|
+
const utils_1 = require("@graphql-tools/utils");
|
|
7
|
+
const graphql_1 = require("graphql");
|
|
8
|
+
const fast_json_stable_stringify_1 = tslib_1.__importDefault(require("fast-json-stable-stringify"));
|
|
9
|
+
const in_memory_cache_js_1 = require("./in-memory-cache.js");
|
|
10
|
+
const hashSHA256_js_1 = require("./hashSHA256.js");
|
|
11
|
+
const contextSymbol = Symbol('responseCache');
|
|
12
|
+
const rawDocumentStringSymbol = Symbol('rawDocumentString');
|
|
13
|
+
/**
|
|
14
|
+
* Default function used for building the response cache key.
|
|
15
|
+
* It is exported here for advanced use-cases. E.g. if you want to short circuit and serve responses from the cache on a global level in order to completely by-pass the GraphQL flow.
|
|
16
|
+
*/
|
|
17
|
+
const defaultBuildResponseCacheKey = params => {
|
|
18
|
+
var _a, _b, _c;
|
|
19
|
+
return (0, hashSHA256_js_1.hashSHA256)([
|
|
20
|
+
params.documentString,
|
|
21
|
+
(_a = params.operationName) !== null && _a !== void 0 ? _a : '',
|
|
22
|
+
(0, fast_json_stable_stringify_1.default)((_b = params.variableValues) !== null && _b !== void 0 ? _b : {}),
|
|
23
|
+
(_c = params.sessionId) !== null && _c !== void 0 ? _c : '',
|
|
24
|
+
].join('|'));
|
|
25
|
+
};
|
|
26
|
+
exports.defaultBuildResponseCacheKey = defaultBuildResponseCacheKey;
|
|
27
|
+
/**
|
|
28
|
+
* Default function used to check if the result should be cached.
|
|
29
|
+
*
|
|
30
|
+
* It is exported here for advanced use-cases. E.g. if you want to choose if
|
|
31
|
+
* results with certain error types should be cached.
|
|
32
|
+
*
|
|
33
|
+
* By default, results with errors (unexpected, EnvelopError, or GraphQLError) are not cached.
|
|
34
|
+
*/
|
|
35
|
+
const defaultShouldCacheResult = (params) => {
|
|
36
|
+
if (params.result.errors) {
|
|
37
|
+
// eslint-disable-next-line no-console
|
|
38
|
+
console.warn('[useResponseCache] Failed to cache due to errors');
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
};
|
|
43
|
+
exports.defaultShouldCacheResult = defaultShouldCacheResult;
|
|
44
|
+
const defaultGetDocumentStringFromContext = context => context[rawDocumentStringSymbol];
|
|
45
|
+
exports.defaultGetDocumentStringFromContext = defaultGetDocumentStringFromContext;
|
|
46
|
+
function useResponseCache({ cache = (0, in_memory_cache_js_1.createInMemoryCache)(), ttl: globalTtl = Infinity, session = () => null, enabled, ignoredTypes = [], ttlPerType = {}, ttlPerSchemaCoordinate = {}, idFields = ['id'], invalidateViaMutation = true, buildResponseCacheKey = exports.defaultBuildResponseCacheKey, getDocumentStringFromContext = exports.defaultGetDocumentStringFromContext, shouldCacheResult = exports.defaultShouldCacheResult,
|
|
47
|
+
// eslint-disable-next-line dot-notation
|
|
48
|
+
includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_ENV'] === 'development' : false, } = {}) {
|
|
49
|
+
const appliedTransform = Symbol('responseCache.appliedTransform');
|
|
50
|
+
const ignoredTypesMap = new Set(ignoredTypes);
|
|
51
|
+
// never cache Introspections
|
|
52
|
+
ttlPerSchemaCoordinate = { 'Query.__schema': 0, ...ttlPerSchemaCoordinate };
|
|
53
|
+
return {
|
|
54
|
+
onSchemaChange({ schema, replaceSchema }) {
|
|
55
|
+
var _a;
|
|
56
|
+
// @ts-expect-error See https://github.com/graphql/graphql-js/pull/3511 - remove this comments once merged
|
|
57
|
+
if (((_a = schema.extensions) === null || _a === void 0 ? void 0 : _a[appliedTransform]) === true) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const patchedSchema = applyResponseCacheLogic(schema, idFields);
|
|
61
|
+
patchedSchema.extensions = {
|
|
62
|
+
...patchedSchema.extensions,
|
|
63
|
+
[appliedTransform]: true,
|
|
64
|
+
};
|
|
65
|
+
replaceSchema(patchedSchema);
|
|
66
|
+
},
|
|
67
|
+
onParse(parseCtx) {
|
|
68
|
+
return function onParseEnd(ctx) {
|
|
69
|
+
if (ctx.result && 'kind' in ctx.result) {
|
|
70
|
+
const source = parseCtx.params.source;
|
|
71
|
+
const rawDocumentString = typeof source === 'string' ? source : source.body;
|
|
72
|
+
ctx.extendContext({
|
|
73
|
+
[rawDocumentStringSymbol]: rawDocumentString,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
async onExecute(ctx) {
|
|
79
|
+
var _a;
|
|
80
|
+
const identifier = new Map();
|
|
81
|
+
const types = new Set();
|
|
82
|
+
const context = {
|
|
83
|
+
identifier,
|
|
84
|
+
types,
|
|
85
|
+
currentTtl: undefined,
|
|
86
|
+
ttlPerType,
|
|
87
|
+
ignoredTypesMap,
|
|
88
|
+
skip: false,
|
|
89
|
+
};
|
|
90
|
+
ctx.extendContext({
|
|
91
|
+
[contextSymbol]: context,
|
|
92
|
+
});
|
|
93
|
+
if (isMutation(ctx.args.document)) {
|
|
94
|
+
if (invalidateViaMutation === false) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
onExecuteDone({ result, setResult }) {
|
|
99
|
+
if ((0, core_1.isAsyncIterable)(result)) {
|
|
100
|
+
// eslint-disable-next-line no-console
|
|
101
|
+
console.warn('[useResponseCache] AsyncIterable returned from execute is currently unsupported.');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
cache.invalidate(context.identifier.values());
|
|
105
|
+
if (includeExtensionMetadata) {
|
|
106
|
+
setResult({
|
|
107
|
+
...result,
|
|
108
|
+
extensions: {
|
|
109
|
+
...result.extensions,
|
|
110
|
+
responseCache: {
|
|
111
|
+
invalidatedEntities: Array.from(context.identifier.values()),
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const documentString = getDocumentStringFromContext(ctx.args.contextValue);
|
|
120
|
+
if (documentString != null) {
|
|
121
|
+
const operationId = await buildResponseCacheKey({
|
|
122
|
+
documentString,
|
|
123
|
+
variableValues: ctx.args.variableValues,
|
|
124
|
+
operationName: ctx.args.operationName,
|
|
125
|
+
sessionId: session(ctx.args.contextValue),
|
|
126
|
+
});
|
|
127
|
+
if (((_a = enabled === null || enabled === void 0 ? void 0 : enabled(ctx.args.contextValue)) !== null && _a !== void 0 ? _a : true) === true) {
|
|
128
|
+
const cachedResponse = await cache.get(operationId);
|
|
129
|
+
if (cachedResponse != null) {
|
|
130
|
+
if (includeExtensionMetadata) {
|
|
131
|
+
ctx.setResultAndStopExecution({
|
|
132
|
+
...cachedResponse,
|
|
133
|
+
extensions: {
|
|
134
|
+
responseCache: {
|
|
135
|
+
hit: true,
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
ctx.setResultAndStopExecution(cachedResponse);
|
|
142
|
+
}
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (ttlPerSchemaCoordinate) {
|
|
147
|
+
const typeInfo = new graphql_1.TypeInfo(ctx.args.schema);
|
|
148
|
+
(0, graphql_1.visit)(ctx.args.document, (0, graphql_1.visitWithTypeInfo)(typeInfo, {
|
|
149
|
+
Field(fieldNode) {
|
|
150
|
+
const parentType = typeInfo.getParentType();
|
|
151
|
+
if (parentType) {
|
|
152
|
+
const schemaCoordinate = `${parentType.name}.${fieldNode.name.value}`;
|
|
153
|
+
const maybeTtl = ttlPerSchemaCoordinate[schemaCoordinate];
|
|
154
|
+
if (maybeTtl !== undefined) {
|
|
155
|
+
context.currentTtl = calculateTtl(maybeTtl, context.currentTtl);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
onExecuteDone({ result, setResult }) {
|
|
163
|
+
var _a;
|
|
164
|
+
if ((0, core_1.isAsyncIterable)(result)) {
|
|
165
|
+
// eslint-disable-next-line no-console
|
|
166
|
+
console.warn('[useResponseCache] AsyncIterable returned from execute is currently unsupported.');
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (context.skip) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (!shouldCacheResult({ result })) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
// we only use the global ttl if no currentTtl has been determined.
|
|
176
|
+
const finalTtl = (_a = context.currentTtl) !== null && _a !== void 0 ? _a : globalTtl;
|
|
177
|
+
if (finalTtl === 0) {
|
|
178
|
+
if (includeExtensionMetadata) {
|
|
179
|
+
setResult({
|
|
180
|
+
...result,
|
|
181
|
+
extensions: {
|
|
182
|
+
responseCache: {
|
|
183
|
+
hit: false,
|
|
184
|
+
didCache: false,
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
cache.set(operationId, result, identifier.values(), finalTtl);
|
|
192
|
+
if (includeExtensionMetadata) {
|
|
193
|
+
setResult({
|
|
194
|
+
...result,
|
|
195
|
+
extensions: {
|
|
196
|
+
responseCache: {
|
|
197
|
+
hit: false,
|
|
198
|
+
didCache: true,
|
|
199
|
+
ttl: finalTtl,
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
// eslint-disable-next-line no-console
|
|
208
|
+
console.warn(`[useResponseCache] Failed extracting document string from the context. The response will not be cached or served from the cache. ` +
|
|
209
|
+
`If you are overriding the 'parse' behavior make sure to pass a custom 'getDocumentStringFromContext' function for getting the document string, which is required for building the response cache key.`);
|
|
210
|
+
return undefined;
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
exports.useResponseCache = useResponseCache;
|
|
215
|
+
function isOperationDefinition(node) {
|
|
216
|
+
return (node === null || node === void 0 ? void 0 : node.kind) === 'OperationDefinition';
|
|
217
|
+
}
|
|
218
|
+
function calculateTtl(typeTtl, currentTtl) {
|
|
219
|
+
if (typeof currentTtl === 'number') {
|
|
220
|
+
return Math.min(currentTtl, typeTtl);
|
|
221
|
+
}
|
|
222
|
+
return typeTtl;
|
|
223
|
+
}
|
|
224
|
+
function isMutation(doc) {
|
|
225
|
+
return doc.definitions.find(isOperationDefinition).operation === 'mutation';
|
|
226
|
+
}
|
|
227
|
+
function runWith(input, callback) {
|
|
228
|
+
if (input instanceof Promise) {
|
|
229
|
+
input.then(callback, () => undefined);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
callback(input);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function applyResponseCacheLogic(schema, idFieldNames) {
|
|
236
|
+
return (0, utils_1.mapSchema)(schema, {
|
|
237
|
+
[utils_1.MapperKind.OBJECT_FIELD](fieldConfig, fieldName, typename) {
|
|
238
|
+
if (idFieldNames.includes(fieldName)) {
|
|
239
|
+
return {
|
|
240
|
+
...fieldConfig,
|
|
241
|
+
resolve(src, args, context, info) {
|
|
242
|
+
var _a;
|
|
243
|
+
const result = ((_a = fieldConfig.resolve) !== null && _a !== void 0 ? _a : graphql_1.defaultFieldResolver)(src, args, context, info);
|
|
244
|
+
runWith(result, (id) => {
|
|
245
|
+
const ctx = context[contextSymbol];
|
|
246
|
+
if (ctx !== undefined) {
|
|
247
|
+
if (ctx.ignoredTypesMap.has(typename)) {
|
|
248
|
+
ctx.skip = true;
|
|
249
|
+
}
|
|
250
|
+
if (ctx.skip === true) {
|
|
251
|
+
ctx.skip = true;
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
255
|
+
// @ts-ignore TODO: investigate what to do if id is something unexpected
|
|
256
|
+
ctx.identifier.set(`${typename}:${id}`, { typename, id });
|
|
257
|
+
ctx.types.add(typename);
|
|
258
|
+
if (typename in ctx.ttlPerType) {
|
|
259
|
+
ctx.currentTtl = calculateTtl(ctx.ttlPerType[typename], ctx.currentTtl);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
return result;
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
return fieldConfig;
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
}
|
package/esm/cache.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hashSHA256 = void 0;
|
|
4
|
+
const webCrypto = globalThis.crypto;
|
|
5
|
+
const sha256Browser = (bytes) => {
|
|
6
|
+
const hash = webCrypto.subtle.digest({ name: 'SHA-256' }, bytes);
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
Promise.resolve(hash)
|
|
9
|
+
.then(function (result) {
|
|
10
|
+
resolve(new Uint8Array(result));
|
|
11
|
+
})
|
|
12
|
+
.catch(function (error) {
|
|
13
|
+
reject(error);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
let nodeCrypto;
|
|
18
|
+
if (typeof window === 'undefined' && !webCrypto) {
|
|
19
|
+
// Indirect eval'd require/import to guarantee no side-effects in module scope
|
|
20
|
+
// (optimization for minifier)
|
|
21
|
+
try {
|
|
22
|
+
// eslint-disable-next-line no-new-func
|
|
23
|
+
nodeCrypto = Promise.resolve(new Function('require', 'return require("crypto")')(require));
|
|
24
|
+
}
|
|
25
|
+
catch (_error) {
|
|
26
|
+
try {
|
|
27
|
+
// eslint-disable-next-line no-new-func
|
|
28
|
+
nodeCrypto = new Function('return import("crypto")')();
|
|
29
|
+
}
|
|
30
|
+
catch (_error) { }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const hashSHA256 = async (text) => {
|
|
34
|
+
// Node.js support
|
|
35
|
+
if (nodeCrypto) {
|
|
36
|
+
return nodeCrypto.then(crypto => crypto.createHash('sha256').update(text).digest('hex'));
|
|
37
|
+
}
|
|
38
|
+
if (webCrypto) {
|
|
39
|
+
let buf;
|
|
40
|
+
if (typeof TextEncoder !== 'undefined') {
|
|
41
|
+
buf = new TextEncoder().encode(text);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
buf = new Uint8Array(text.length);
|
|
45
|
+
for (let i = 0, l = text.length; i < l; i++) {
|
|
46
|
+
// NOTE: We assume that the input GraphQL Query only uses UTF-8 at most
|
|
47
|
+
// since GraphQL mostly consists of ASCII, this is completely fine
|
|
48
|
+
buf[i] = text.charCodeAt(i);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const out = await sha256Browser(buf);
|
|
52
|
+
let hash = '';
|
|
53
|
+
for (let i = 0, l = out.length; i < l; i++) {
|
|
54
|
+
const hex = out[i].toString(16);
|
|
55
|
+
hash += '00'.slice(0, Math.max(0, 2 - hex.length)) + hex;
|
|
56
|
+
}
|
|
57
|
+
return hash;
|
|
58
|
+
}
|
|
59
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
60
|
+
// eslint-disable-next-line no-console
|
|
61
|
+
console.warn('[@envelop/response-cache]: The Node Crypto and Web Crypto APIs are not available.\n' +
|
|
62
|
+
'This is an unexpected error. Please report it by filing a GitHub Issue.');
|
|
63
|
+
}
|
|
64
|
+
return Promise.resolve('');
|
|
65
|
+
};
|
|
66
|
+
exports.hashSHA256 = hashSHA256;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.defaultBuildEntityId = exports.createInMemoryCache = void 0;
|
|
7
|
+
const lru_cache_1 = __importDefault(require("lru-cache"));
|
|
8
|
+
const createInMemoryCache = (params) => {
|
|
9
|
+
var _a;
|
|
10
|
+
const buildEntityId = (_a = params === null || params === void 0 ? void 0 : params.buildEntityId) !== null && _a !== void 0 ? _a : exports.defaultBuildEntityId;
|
|
11
|
+
const cachedResponses = new lru_cache_1.default({
|
|
12
|
+
max: params === null || params === void 0 ? void 0 : params.max,
|
|
13
|
+
stale: false,
|
|
14
|
+
noDisposeOnSet: true,
|
|
15
|
+
dispose(responseId) {
|
|
16
|
+
purgeResponse(responseId, false);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
const entityToResponseIds = new Map();
|
|
20
|
+
const responseIdToEntityIds = new Map();
|
|
21
|
+
function purgeResponse(responseId, shouldRemove = true) {
|
|
22
|
+
var _a;
|
|
23
|
+
const entityIds = responseIdToEntityIds.get(responseId);
|
|
24
|
+
// get entities related to the response
|
|
25
|
+
if (entityIds !== undefined) {
|
|
26
|
+
for (const entityId of entityIds) {
|
|
27
|
+
// remove the response mapping from the entity
|
|
28
|
+
(_a = entityToResponseIds.get(entityId)) === null || _a === void 0 ? void 0 : _a.delete(responseId);
|
|
29
|
+
}
|
|
30
|
+
// remove all the entity mappings from the response
|
|
31
|
+
responseIdToEntityIds.delete(responseId);
|
|
32
|
+
}
|
|
33
|
+
if (shouldRemove) {
|
|
34
|
+
// remove the response from the cache
|
|
35
|
+
cachedResponses.del(responseId);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function purgeEntity(entity) {
|
|
39
|
+
const responseIds = entityToResponseIds.get(entity);
|
|
40
|
+
if (responseIds !== undefined) {
|
|
41
|
+
for (const responseId of responseIds) {
|
|
42
|
+
purgeResponse(responseId);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
set(responseId, result, collectedEntities, ttl) {
|
|
48
|
+
cachedResponses.set(responseId, result, ttl);
|
|
49
|
+
const entityIds = new Set();
|
|
50
|
+
responseIdToEntityIds.set(responseId, entityIds);
|
|
51
|
+
for (const { typename, id } of collectedEntities) {
|
|
52
|
+
let operationIds = entityToResponseIds.get(typename);
|
|
53
|
+
if (operationIds == null) {
|
|
54
|
+
operationIds = new Set();
|
|
55
|
+
entityToResponseIds.set(typename, operationIds);
|
|
56
|
+
}
|
|
57
|
+
// typename => operation
|
|
58
|
+
operationIds.add(responseId);
|
|
59
|
+
// operation => typename
|
|
60
|
+
entityIds.add(typename);
|
|
61
|
+
if (id !== undefined) {
|
|
62
|
+
const entityId = buildEntityId(typename, id);
|
|
63
|
+
let responseIds = entityToResponseIds.get(entityId);
|
|
64
|
+
if (responseIds == null) {
|
|
65
|
+
responseIds = new Set();
|
|
66
|
+
entityToResponseIds.set(entityId, responseIds);
|
|
67
|
+
}
|
|
68
|
+
// typename:id => operation
|
|
69
|
+
responseIds.add(responseId);
|
|
70
|
+
// operation => typename:id
|
|
71
|
+
entityIds.add(entityId);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
get(responseId) {
|
|
76
|
+
var _a;
|
|
77
|
+
return (_a = cachedResponses.get(responseId)) !== null && _a !== void 0 ? _a : null;
|
|
78
|
+
},
|
|
79
|
+
invalidate(entitiesToRemove) {
|
|
80
|
+
for (const { typename, id } of entitiesToRemove) {
|
|
81
|
+
purgeEntity(id != null ? buildEntityId(typename, id) : typename);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
exports.createInMemoryCache = createInMemoryCache;
|
|
87
|
+
const defaultBuildEntityId = (typename, id) => `${typename}:${id}`;
|
|
88
|
+
exports.defaultBuildEntityId = defaultBuildEntityId;
|
package/esm/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./in-memory-cache.js"), exports);
|
|
18
|
+
__exportStar(require("./plugin.js"), exports);
|
|
19
|
+
__exportStar(require("./cache.js"), exports);
|