5
5
ExecutionArgs ,
6
6
getOperationAST ,
7
7
GraphQLDirective ,
8
+ GraphQLSchema ,
8
9
Kind ,
9
10
print ,
10
11
TypeInfo ,
@@ -30,6 +31,7 @@ import {
30
31
mergeIncrementalResult ,
31
32
} from '@graphql-tools/utils' ;
32
33
import type { Cache , CacheEntityRecord } from './cache.js' ;
34
+ import { getScopeFromQuery } from './get-scope.js' ;
33
35
import { hashSHA256 } from './hash-sha256.js' ;
34
36
import { createInMemoryCache } from './in-memory-cache.js' ;
35
37
@@ -47,6 +49,8 @@ export type BuildResponseCacheKeyFunction = (params: {
47
49
sessionId : Maybe < string > ;
48
50
/** GraphQL Context */
49
51
context : ExecutionArgs [ 'contextValue' ] ;
52
+ /** Callback to get the scope */
53
+ getScope : ( ) => NonNullable < CacheControlDirective [ 'scope' ] > ;
50
54
} ) => Promise < string > ;
51
55
52
56
export type GetDocumentStringFunction = ( executionArgs : ExecutionArgs ) => string ;
@@ -76,8 +80,8 @@ export type UseResponseCacheParameter<PluginContext extends Record<string, any>
76
80
* In the unusual case where you actually want to cache introspection query operations,
77
81
* you need to provide the value `{ 'Query.__schema': undefined }`.
78
82
*/
79
- ttlPerSchemaCoordinate ?: Record < string , number | undefined > ;
80
- scopePerSchemaCoordinate ?: Record < string , 'PRIVATE' | 'PUBLIC' | undefined > ;
83
+ ttlPerSchemaCoordinate ?: Record < string , CacheControlDirective [ 'maxAge' ] > ;
84
+ scopePerSchemaCoordinate ?: Record < string , CacheControlDirective [ 'scope' ] > ;
81
85
/**
82
86
* Allows to cache responses based on the resolved session id.
83
87
* Return a unique value for each session.
@@ -215,11 +219,11 @@ const getDocumentWithMetadataAndTTL = memoize4(function addTypeNameToDocument(
215
219
ttlPerSchemaCoordinate,
216
220
} : {
217
221
invalidateViaMutation : boolean ;
218
- ttlPerSchemaCoordinate ?: Record < string , number | undefined > ;
222
+ ttlPerSchemaCoordinate ?: Record < string , CacheControlDirective [ 'maxAge' ] > ;
219
223
} ,
220
224
schema : any ,
221
225
idFieldByTypeName : Map < string , string > ,
222
- ) : [ DocumentNode , number | undefined ] {
226
+ ) : [ DocumentNode , CacheControlDirective [ 'maxAge' ] ] {
223
227
const typeInfo = new TypeInfo ( schema ) ;
224
228
let ttl : number | undefined ;
225
229
const visitor : ASTVisitor = {
@@ -238,7 +242,7 @@ const getDocumentWithMetadataAndTTL = memoize4(function addTypeNameToDocument(
238
242
const parentType = typeInfo . getParentType ( ) ;
239
243
if ( parentType ) {
240
244
const schemaCoordinate = `${ parentType . name } .${ fieldNode . name . value } ` ;
241
- const maybeTtl = ttlPerSchemaCoordinate [ schemaCoordinate ] as unknown ;
245
+ const maybeTtl = ttlPerSchemaCoordinate [ schemaCoordinate ] ;
242
246
ttl = calculateTtl ( maybeTtl , ttl ) ;
243
247
}
244
248
} ,
@@ -279,20 +283,37 @@ const getDocumentWithMetadataAndTTL = memoize4(function addTypeNameToDocument(
279
283
return [ visit ( document , visitWithTypeInfo ( typeInfo , visitor ) ) , ttl ] ;
280
284
} ) ;
281
285
282
- type CacheControlDirective = {
286
+ export type CacheControlDirective = {
283
287
maxAge ?: number ;
284
288
scope ?: 'PUBLIC' | 'PRIVATE' ;
285
289
} ;
286
290
291
+ let ttlPerSchemaCoordinate : Record < string , CacheControlDirective [ 'maxAge' ] > = { } ;
292
+ let scopePerSchemaCoordinate : Record < string , CacheControlDirective [ 'scope' ] > = { } ;
293
+
294
+ export function isPrivate (
295
+ typeName : string ,
296
+ data ?: Record < string , NonNullable < CacheControlDirective [ 'scope' ] > > ,
297
+ ) : boolean {
298
+ if ( scopePerSchemaCoordinate [ typeName ] === 'PRIVATE' ) {
299
+ return true ;
300
+ }
301
+ return data
302
+ ? Object . keys ( data ) . some (
303
+ fieldName => scopePerSchemaCoordinate [ `${ typeName } .${ fieldName } ` ] === 'PRIVATE' ,
304
+ )
305
+ : false ;
306
+ }
307
+
287
308
export function useResponseCache < PluginContext extends Record < string , any > = { } > ( {
288
309
cache = createInMemoryCache ( ) ,
289
310
ttl : globalTtl = Infinity ,
290
311
session,
291
312
enabled,
292
313
ignoredTypes = [ ] ,
293
314
ttlPerType = { } ,
294
- ttlPerSchemaCoordinate = { } ,
295
- scopePerSchemaCoordinate = { } ,
315
+ ttlPerSchemaCoordinate : localTtlPerSchemaCoordinate = { } ,
316
+ scopePerSchemaCoordinate : localScopePerSchemaCoordinate = { } ,
296
317
idFields = [ 'id' ] ,
297
318
invalidateViaMutation = true ,
298
319
buildResponseCacheKey = defaultBuildResponseCacheKey ,
@@ -308,22 +329,14 @@ export function useResponseCache<PluginContext extends Record<string, any> = {}>
308
329
enabled = enabled ? memoize1 ( enabled ) : enabled ;
309
330
310
331
// never cache Introspections
311
- ttlPerSchemaCoordinate = { 'Query.__schema' : 0 , ...ttlPerSchemaCoordinate } ;
332
+ ttlPerSchemaCoordinate = { 'Query.__schema' : 0 , ...localTtlPerSchemaCoordinate } ;
312
333
const documentMetadataOptions = {
313
334
queries : { invalidateViaMutation, ttlPerSchemaCoordinate } ,
314
335
mutations : { invalidateViaMutation } , // remove ttlPerSchemaCoordinate for mutations to skip TTL calculation
315
336
} ;
337
+ scopePerSchemaCoordinate = { ...localScopePerSchemaCoordinate } ;
316
338
const idFieldByTypeName = new Map < string , string > ( ) ;
317
- let schema : any ;
318
-
319
- function isPrivate ( typeName : string , data : Record < string , unknown > ) : boolean {
320
- if ( scopePerSchemaCoordinate [ typeName ] === 'PRIVATE' ) {
321
- return true ;
322
- }
323
- return Object . keys ( data ) . some (
324
- fieldName => scopePerSchemaCoordinate [ `${ typeName } .${ fieldName } ` ] === 'PRIVATE' ,
325
- ) ;
326
- }
339
+ let schema : GraphQLSchema ;
327
340
328
341
return {
329
342
onSchemaChange ( { schema : newSchema } ) {
@@ -522,6 +535,7 @@ export function useResponseCache<PluginContext extends Record<string, any> = {}>
522
535
operationName : onExecuteParams . args . operationName ,
523
536
sessionId,
524
537
context : onExecuteParams . args . contextValue ,
538
+ getScope : ( ) => getScopeFromQuery ( schema , onExecuteParams . args . document . loc . source . body ) ,
525
539
} ) ;
526
540
527
541
const cachedResponse = ( await cache . get ( cacheKey ) ) as ResponseCacheExecutionResult ;
0 commit comments