55 ExecutionArgs ,
66 getOperationAST ,
77 GraphQLDirective ,
8+ GraphQLSchema ,
89 Kind ,
910 print ,
1011 TypeInfo ,
@@ -30,6 +31,7 @@ import {
3031 mergeIncrementalResult ,
3132} from '@graphql-tools/utils' ;
3233import type { Cache , CacheEntityRecord } from './cache.js' ;
34+ import { getScopeFromQuery } from './get-scope.js' ;
3335import { hashSHA256 } from './hash-sha256.js' ;
3436import { createInMemoryCache } from './in-memory-cache.js' ;
3537
@@ -47,6 +49,8 @@ export type BuildResponseCacheKeyFunction = (params: {
4749 sessionId : Maybe < string > ;
4850 /** GraphQL Context */
4951 context : ExecutionArgs [ 'contextValue' ] ;
52+ /** Callback to get the scope */
53+ getScope : ( ) => NonNullable < CacheControlDirective [ 'scope' ] > ;
5054} ) => Promise < string > ;
5155
5256export type GetDocumentStringFunction = ( executionArgs : ExecutionArgs ) => string ;
@@ -76,8 +80,8 @@ export type UseResponseCacheParameter<PluginContext extends Record<string, any>
7680 * In the unusual case where you actually want to cache introspection query operations,
7781 * you need to provide the value `{ 'Query.__schema': undefined }`.
7882 */
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' ] > ;
8185 /**
8286 * Allows to cache responses based on the resolved session id.
8387 * Return a unique value for each session.
@@ -215,11 +219,11 @@ const getDocumentWithMetadataAndTTL = memoize4(function addTypeNameToDocument(
215219 ttlPerSchemaCoordinate,
216220 } : {
217221 invalidateViaMutation : boolean ;
218- ttlPerSchemaCoordinate ?: Record < string , number | undefined > ;
222+ ttlPerSchemaCoordinate ?: Record < string , CacheControlDirective [ 'maxAge' ] > ;
219223 } ,
220224 schema : any ,
221225 idFieldByTypeName : Map < string , string > ,
222- ) : [ DocumentNode , number | undefined ] {
226+ ) : [ DocumentNode , CacheControlDirective [ 'maxAge' ] ] {
223227 const typeInfo = new TypeInfo ( schema ) ;
224228 let ttl : number | undefined ;
225229 const visitor : ASTVisitor = {
@@ -238,7 +242,7 @@ const getDocumentWithMetadataAndTTL = memoize4(function addTypeNameToDocument(
238242 const parentType = typeInfo . getParentType ( ) ;
239243 if ( parentType ) {
240244 const schemaCoordinate = `${ parentType . name } .${ fieldNode . name . value } ` ;
241- const maybeTtl = ttlPerSchemaCoordinate [ schemaCoordinate ] as unknown ;
245+ const maybeTtl = ttlPerSchemaCoordinate [ schemaCoordinate ] ;
242246 ttl = calculateTtl ( maybeTtl , ttl ) ;
243247 }
244248 } ,
@@ -279,20 +283,37 @@ const getDocumentWithMetadataAndTTL = memoize4(function addTypeNameToDocument(
279283 return [ visit ( document , visitWithTypeInfo ( typeInfo , visitor ) ) , ttl ] ;
280284} ) ;
281285
282- type CacheControlDirective = {
286+ export type CacheControlDirective = {
283287 maxAge ?: number ;
284288 scope ?: 'PUBLIC' | 'PRIVATE' ;
285289} ;
286290
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+
287308export function useResponseCache < PluginContext extends Record < string , any > = { } > ( {
288309 cache = createInMemoryCache ( ) ,
289310 ttl : globalTtl = Infinity ,
290311 session,
291312 enabled,
292313 ignoredTypes = [ ] ,
293314 ttlPerType = { } ,
294- ttlPerSchemaCoordinate = { } ,
295- scopePerSchemaCoordinate = { } ,
315+ ttlPerSchemaCoordinate : localTtlPerSchemaCoordinate = { } ,
316+ scopePerSchemaCoordinate : localScopePerSchemaCoordinate = { } ,
296317 idFields = [ 'id' ] ,
297318 invalidateViaMutation = true ,
298319 buildResponseCacheKey = defaultBuildResponseCacheKey ,
@@ -308,22 +329,14 @@ export function useResponseCache<PluginContext extends Record<string, any> = {}>
308329 enabled = enabled ? memoize1 ( enabled ) : enabled ;
309330
310331 // never cache Introspections
311- ttlPerSchemaCoordinate = { 'Query.__schema' : 0 , ...ttlPerSchemaCoordinate } ;
332+ ttlPerSchemaCoordinate = { 'Query.__schema' : 0 , ...localTtlPerSchemaCoordinate } ;
312333 const documentMetadataOptions = {
313334 queries : { invalidateViaMutation, ttlPerSchemaCoordinate } ,
314335 mutations : { invalidateViaMutation } , // remove ttlPerSchemaCoordinate for mutations to skip TTL calculation
315336 } ;
337+ scopePerSchemaCoordinate = { ...localScopePerSchemaCoordinate } ;
316338 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 ;
327340
328341 return {
329342 onSchemaChange ( { schema : newSchema } ) {
@@ -522,6 +535,7 @@ export function useResponseCache<PluginContext extends Record<string, any> = {}>
522535 operationName : onExecuteParams . args . operationName ,
523536 sessionId,
524537 context : onExecuteParams . args . contextValue ,
538+ getScope : ( ) => getScopeFromQuery ( schema , onExecuteParams . args . document . loc . source . body ) ,
525539 } ) ;
526540
527541 const cachedResponse = ( await cache . get ( cacheKey ) ) as ResponseCacheExecutionResult ;
0 commit comments