import { AddFlagFn } from './cli' ; import { Server , IncomingMessage } from 'http' ; import { CreateRequestHandlerOptions , HttpRequestHandler , PostGraphileOptions , } from '../interfaces' ; import { WithPostGraphileContextFn } from './withPostGraphileContext' ; import { version } from '../../package.json' ; import * as graphql from 'graphql' ; import * as graphqlWs from 'graphql-ws' ; import { Extra as GraphQLWSContextExtra } from 'graphql-ws/lib/use/ws' ; import { ExecutionParams } from 'subscriptions-transport-ws' ; import { PostGraphileResponse } from './http/frameworks' ; type PromiseOrValue < T > = T | Promise < T > ; export type HookFn < TArg , TContext = any > = ( arg : TArg , context : TContext ) => TArg ; export type PluginHookFn = < TArgument , TContext = Record < string , any > > ( hookName : string , argument : TArgument , context ? : TContext ) => TArgument ; export interface PostGraphileHTTPResult { statusCode ? : number ; result ? : Record < string , any > ; errors ? : Array < Record < string , any > > ; meta ? : Record < string , any > ; } export interface PostGraphileHTTPEnd { statusCode ? : number ; result : Record < string , any > | Array < Record < string , any > > ; } export interface PostGraphilePlugin { init ? : HookFn < null , { version : string ; graphql : typeof import ( 'graphql' ) ; } > ; pluginHook ? : HookFn < PluginHookFn > ; 'cli:flags:add:standard' ? : HookFn < AddFlagFn > ; 'cli:flags:add:schema' ? : HookFn < AddFlagFn > ; 'cli:flags:add:errorHandling' ? : HookFn < AddFlagFn > ; 'cli:flags:add:plugins' ? : HookFn < AddFlagFn > ; 'cli:flags:add:noServer' ? : HookFn < AddFlagFn > ; 'cli:flags:add:webserver' ? : HookFn < AddFlagFn > ; 'cli:flags:add:jwt' ? : HookFn < AddFlagFn > ; 'cli:flags:add' ? : HookFn < AddFlagFn > ; 'cli:flags:add:deprecated' ? : HookFn < AddFlagFn > ; 'cli:flags:add:workarounds' ? : HookFn < AddFlagFn > ; 'cli:library:options' ? : HookFn < PostGraphileOptions , { config : any ; cliOptions : any ; } > ; 'cli:server:middleware' ? : HookFn < HttpRequestHandler > ; 'cli:server:created' ? : HookFn < Server > ; 'cli:greeting' ? : HookFn < Array < string | null | void > > ; 'postgraphile:options' ? : HookFn < PostGraphileOptions > ; 'postgraphile:validationRules:static' ? : HookFn < typeof graphql . specifiedRules > ; 'postgraphile:graphiql:html' ? : HookFn < string > ; 'postgraphile:middleware' ? : HookFn < HttpRequestHandler > ; 'postgraphile:http:handler' ? : HookFn < IncomingMessage > ; 'postgraphile:http:eventStreamRouteHandler' ? : HookFn < IncomingMessage , { options : CreateRequestHandlerOptions ; response : PostGraphileResponse ; } > ; 'postgraphile:http:faviconRouteHandler' ? : HookFn < IncomingMessage , { options : CreateRequestHandlerOptions ; response : PostGraphileResponse ; } > ; 'postgraphile:http:graphiqlRouteHandler' ? : HookFn < IncomingMessage , { options : CreateRequestHandlerOptions ; response : PostGraphileResponse ; } > ; 'postgraphile:http:graphqlRouteHandler' ? : HookFn < IncomingMessage , { options : CreateRequestHandlerOptions ; response : PostGraphileResponse ; } > ; 'postgraphile:http:result' ? : HookFn < PostGraphileHTTPResult > ; 'postgraphile:http:end' ? : HookFn < PostGraphileHTTPEnd > ; 'postgraphile:httpParamsList' ? : HookFn < Array < Record < string , any > > > ; 'postgraphile:validationRules' ? : HookFn < typeof graphql . specifiedRules > ; 'postgraphile:ws:onOperation' ? : HookFn < ExecutionParams > ; 'postgraphile:ws:onSubscribe' ? : HookFn < graphql . ExecutionArgs & { document : graphql . DocumentNode | null ; } , { context : graphqlWs . Context < GraphQLWSContextExtra > ; message : graphqlWs . SubscribeMessage ; options : CreateRequestHandlerOptions ; } > ; 'postgraphile:liveSubscribe:executionResult' ? : HookFn < PromiseOrValue < graphql . ExecutionResult < { [ key ] : any ; } , { [ key ] : any ; } > > , { schema : graphql . GraphQLSchema ; document : graphql . DocumentNode ; rootValue : any ; contextValue : any ; variableValues : { [ key ] : any ; } | undefined ; operationName : string | undefined ; fieldResolver : graphql . GraphQLFieldResolver < any , any , { [ argName ] : any ; } > | undefined ; subscribeFieldResolver : graphql . GraphQLFieldResolver < any , any , { [ argName ] : any ; } > | undefined ; } > ; withPostGraphileContext ? : HookFn < WithPostGraphileContextFn > ; } type HookName = keyof PostGraphilePlugin ; const identityHook = < T > ( input ) : T => input ; const identityPluginHook = ( _hookName , input , _options ) => input ; function contextIsSame ( context1 , context2 ) { if ( context1 === context2 ) { return true ; } const keys1 = Object . keys ( context1 ) ; const keys2 = Object . keys ( context2 ) ; if ( keys1 . length !== keys2 . length ) { return false ; } for ( let i = 0 , l = keys1 . length ; i < l ; i ++ ) { const key = keys1 [ i ] ; if ( context1 [ key ] !== context2 [ key ] ) { return false ; } if ( ! keys2 . includes ( key ) ) { return false ; } } return true ; } function memoizeHook < T > ( hook ) { let lastCall = null ; return ( argument , context ) : T => { if ( lastCall && lastCall . argument === argument && contextIsSame ( lastCall . context , context ) ) { return lastCall . result ; } else { const result = hook ( argument , context ) ; lastCall = { argument , context , result , } ; return result ; } } ; } function shouldMemoizeHook ( hookName ) { return hookName === 'withPostGraphileContext' ; } function makeHook < T > ( plugins , hookName ) { const combinedHook = plugins . reduce ( ( previousHook , plugin ) => { if ( typeof plugin [ hookName ] === 'function' ) { return ( argument , context ) => { return plugin [ hookName ] ( previousHook ( argument , context ) , context ) ; } ; } else { return previousHook ; } } , identityHook ) ; if ( combinedHook === identityHook ) { return identityHook ; } else if ( shouldMemoizeHook ( hookName ) ) { return memoizeHook < T > ( combinedHook ) ; } else { return combinedHook ; } } export function makePluginHook ( plugins ) { const hooks = { } ; const emptyObject = { } ; function rawPluginHook < T > ( hookName , argument , context = emptyObject ) { if ( ! hooks [ hookName ] ) { hooks [ hookName ] = makeHook ( plugins , hookName ) ; } return hooks [ hookName ] ( argument , context ) ; } const pluginHook = rawPluginHook ( 'pluginHook' , rawPluginHook , { } ) ; pluginHook ( 'init' , null , { version , graphql } ) ; return pluginHook ; } export function pluginHookFromOptions ( options ) { if ( typeof options . pluginHook === 'function' ) { return options . pluginHook ; } else { return identityPluginHook ; } }