55 ExecutionArgs ,
66 getOperationAST ,
77 GraphQLDirective ,
8+ GraphQLSchema ,
89 GraphQLType ,
910 isListType ,
1011 isNonNullType ,
@@ -35,6 +36,7 @@ import {
3536} from '@graphql-tools/utils' ;
3637import { handleMaybePromise , MaybePromise } from '@whatwg-node/promise-helpers' ;
3738import type { Cache , CacheEntityRecord } from './cache.js' ;
39+ import { getScopeFromQuery } from './get-scope.js' ;
3840import { hashSHA256 } from './hash-sha256.js' ;
3941import { createInMemoryCache } from './in-memory-cache.js' ;
4042
@@ -52,6 +54,13 @@ export type BuildResponseCacheKeyFunction = (params: {
5254 sessionId : Maybe < string > ;
5355 /** GraphQL Context */
5456 context : ExecutionArgs [ 'contextValue' ] ;
57+ /** Extras of the query (won't be computed if not requested) */
58+ extras : {
59+ scope : NonNullable < CacheControlDirective [ 'scope' ] > ;
60+ metadata : {
61+ privateProperty ?: string ;
62+ } ;
63+ } ;
5564} ) => MaybePromise < string > ;
5665
5766export type GetDocumentStringFunction = ( executionArgs : ExecutionArgs ) => string ;
@@ -295,20 +304,38 @@ const getDocumentWithMetadataAndTTL = memoize4(function addTypeNameToDocument(
295304 return [ visit ( document , visitWithTypeInfo ( typeInfo , visitor ) ) , ttl ] ;
296305} ) ;
297306
298- type CacheControlDirective = {
307+ export type CacheControlDirective = {
299308 maxAge ?: number ;
300309 scope ?: 'PUBLIC' | 'PRIVATE' ;
301310} ;
302311
312+ export let schema : GraphQLSchema ;
313+ let ttlPerSchemaCoordinate : Record < string , CacheControlDirective [ 'maxAge' ] > = { } ;
314+ let scopePerSchemaCoordinate : Record < string , CacheControlDirective [ 'scope' ] > = { } ;
315+
316+ export function isPrivate (
317+ typeName : string ,
318+ data ?: Record < string , NonNullable < CacheControlDirective [ 'scope' ] > > ,
319+ ) : boolean {
320+ if ( scopePerSchemaCoordinate [ typeName ] === 'PRIVATE' ) {
321+ return true ;
322+ }
323+ return data
324+ ? Object . keys ( data ) . some (
325+ fieldName => scopePerSchemaCoordinate [ `${ typeName } .${ fieldName } ` ] === 'PRIVATE' ,
326+ )
327+ : false ;
328+ }
329+
303330export function useResponseCache < PluginContext extends Record < string , any > = { } > ( {
304331 cache = createInMemoryCache ( ) ,
305332 ttl : globalTtl = Infinity ,
306333 session,
307334 enabled,
308335 ignoredTypes = [ ] ,
309336 ttlPerType,
310- ttlPerSchemaCoordinate = { } ,
311- scopePerSchemaCoordinate = { } ,
337+ ttlPerSchemaCoordinate : localTtlPerSchemaCoordinate = { } ,
338+ scopePerSchemaCoordinate : localScopePerSchemaCoordinate = { } ,
312339 idFields = [ 'id' ] ,
313340 invalidateViaMutation = true ,
314341 buildResponseCacheKey = defaultBuildResponseCacheKey ,
@@ -326,7 +353,7 @@ export function useResponseCache<PluginContext extends Record<string, any> = {}>
326353 enabled = enabled ? memoize1 ( enabled ) : enabled ;
327354
328355 // never cache Introspections
329- ttlPerSchemaCoordinate = { 'Query.__schema' : 0 , ...ttlPerSchemaCoordinate } ;
356+ ttlPerSchemaCoordinate = { 'Query.__schema' : 0 , ...localTtlPerSchemaCoordinate } ;
330357 if ( ttlPerType ) {
331358 // eslint-disable-next-line no-console
332359 console . warn (
@@ -341,8 +368,8 @@ export function useResponseCache<PluginContext extends Record<string, any> = {}>
341368 queries : { invalidateViaMutation, ttlPerSchemaCoordinate } ,
342369 mutations : { invalidateViaMutation } , // remove ttlPerSchemaCoordinate for mutations to skip TTL calculation
343370 } ;
371+ scopePerSchemaCoordinate = { ...localScopePerSchemaCoordinate } ;
344372 const idFieldByTypeName = new Map < string , string > ( ) ;
345- let schema : any ;
346373
347374 function isPrivate ( typeName : string , data : Record < string , unknown > ) : boolean {
348375 if ( scopePerSchemaCoordinate [ typeName ] === 'PRIVATE' ) {
@@ -558,13 +585,26 @@ export function useResponseCache<PluginContext extends Record<string, any> = {}>
558585
559586 return handleMaybePromise (
560587 ( ) =>
561- buildResponseCacheKey ( {
562- documentString : getDocumentString ( onExecuteParams . args ) ,
563- variableValues : onExecuteParams . args . variableValues ,
564- operationName : onExecuteParams . args . operationName ,
565- sessionId,
566- context : onExecuteParams . args . contextValue ,
567- } ) ,
588+ buildResponseCacheKey (
589+ new Proxy (
590+ {
591+ documentString : getDocumentString ( onExecuteParams . args ) ,
592+ variableValues : onExecuteParams . args . variableValues ,
593+ operationName : onExecuteParams . args . operationName ,
594+ sessionId,
595+ context : onExecuteParams . args . contextValue ,
596+ extras : undefined as any ,
597+ } ,
598+ {
599+ get : ( obj , prop ) => {
600+ if ( prop === 'extras' ) {
601+ return getScopeFromQuery ( schema , onExecuteParams . args . document . loc . source . body ) ;
602+ }
603+ return obj [ prop as keyof typeof obj ] ;
604+ } ,
605+ } ,
606+ ) ,
607+ ) ,
568608 cacheKey => {
569609 const cacheInstance = cacheFactory ( onExecuteParams . args . contextValue ) ;
570610 if ( cacheInstance == null ) {
0 commit comments