Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

graphql-introspection-filtering

Package Overview
Dependencies
Maintainers
1
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

graphql-introspection-filtering - npm Package Compare versions

Comparing version 2.0.0-beta.2 to 2.0.0-rc.0

dist/tools/parseDirectiveAst.d.ts

15

dist/classes/Hook.d.ts
import Once from './Once';
import type { GraphQLResolveInfo } from 'graphql';
import type { ClassDirectiveConfig, IntrospectionDirectiveVisitor, VisitableIntrospectionType, VisitableSchemaType } from '../types';
export default class Hook {
import type { OperationDefinitionNode } from 'graphql/language/ast';
import type { ClassDirectiveConfig, IntrospectionDirectiveVisitor, VisitableIntrospectionType, VisitableSchemaType, VisitorResult } from '../types';
/**
* Introspection field hook
*/
export default class Hook<C> {
protected _directives: ClassDirectiveConfig[];
protected _method: keyof IntrospectionDirectiveVisitor;
protected _once: Once;
protected _once: Once<OperationDefinitionNode>;
/**

@@ -13,5 +17,4 @@ * Hook constructor

* @param method directives method to be called on resolve
* @param cacheTtl ttl for field resolve cache default is 1s
*/
constructor(directives: ClassDirectiveConfig[], method: keyof IntrospectionDirectiveVisitor, cacheTtl: number);
constructor(directives: ClassDirectiveConfig[], method: keyof IntrospectionDirectiveVisitor);
/**

@@ -25,3 +28,3 @@ * Resolve type/field/enum/directive of user schema

*/
resolve<S extends VisitableIntrospectionType, R extends VisitableSchemaType = any, C = any>(subject: S, root: R, context: C, info: GraphQLResolveInfo): Promise<S | null> | S | null;
resolve<S extends VisitableIntrospectionType, R extends VisitableSchemaType = any>(subject: S, root: R, context: C, info: GraphQLResolveInfo): VisitorResult<S>;
}

12

dist/classes/Hook.js

@@ -6,2 +6,5 @@ "use strict";

var Once_1 = tslib_1.__importDefault(require("./Once"));
/**
* Introspection field hook
*/
var Hook = /** @class */ (function () {

@@ -13,8 +16,7 @@ /**

* @param method directives method to be called on resolve
* @param cacheTtl ttl for field resolve cache default is 1s
*/
function Hook(directives, method, cacheTtl) {
function Hook(directives, method) {
this._once = new Once_1.default();
this._directives = directives;
this._method = method;
this._once = new Once_1.default(cacheTtl);
}

@@ -31,4 +33,4 @@ /**

var e_1, _a;
var session = this._once.session(context);
if (session.isRunning) {
var session = this._once.session(info.operation);
if (session.canJoin) {
return session.join();

@@ -35,0 +37,0 @@ }

import { defaultFieldResolver } from 'graphql';
import type { GraphQLResolveInfo } from 'graphql';
import type { VisitableIntrospectionType, VisitableSchemaType } from '../types';
import Manager from './Manager';
import type { GraphQLResolveInfo, GraphQLField } from 'graphql';
import type { VisitableIntrospectionType, VisitableSchemaType, VisitorResult } from '../types';
/**
* Introspection hook helper
*/
export default class Introspection {

@@ -10,3 +14,3 @@ /**

*/
static hook(subject: any): void;
static hook(subject: GraphQLField<any, any>): void;
/**

@@ -21,3 +25,3 @@ * Hooked introspection object/field/arg... resolver

*/
protected static resolve<R extends VisitableSchemaType, C, A>(this: typeof defaultFieldResolver, root: R, args: A, context: C, info: GraphQLResolveInfo): any;
protected static resolve<R extends VisitableSchemaType, C, A>(this: typeof defaultFieldResolver, root: R, args: A, context: C, info: GraphQLResolveInfo): VisitorResult<any>;
/**

@@ -30,2 +34,11 @@ * Exclude hooked and fundamental types

protected static isExcluded(subject: VisitableIntrospectionType): boolean;
/**
* Tells whether current introspection query be hooked
*
* @param manager
* @param context
* @param info
* @protected
*/
protected static shouldHook<C>(manager: Manager<C>, context: C, info: GraphQLResolveInfo): boolean;
}

@@ -9,2 +9,5 @@ "use strict";

var Manager_1 = tslib_1.__importDefault(require("./Manager"));
/**
* Introspection hook helper
*/
var Introspection = /** @class */ (function () {

@@ -36,3 +39,3 @@ function Introspection() {

var subject = this(root, args, context, info);
if (!subject || !(typeof subject === 'object')) {
if (!subject || !(typeof subject === 'object') || info.operation[constants_1.SHOULD_HOOK_QUERY] === false) {
return subject;

@@ -42,2 +45,5 @@ }

if (manager) {
if (!Introspection.shouldHook(manager, context, info)) {
return subject;
}
if (Array.isArray(subject)) {

@@ -65,3 +71,3 @@ var resolved = chainArray_1.default(subject, function (item) {

Introspection.isExcluded = function (subject) {
// exclude already hooked
// exclude already hooked top level objects/fields
if (subject[constants_1.INTROSPECTION_HOOK]) {

@@ -77,2 +83,16 @@ return true;

};
/**
* Tells whether current introspection query be hooked
*
* @param manager
* @param context
* @param info
* @protected
*/
Introspection.shouldHook = function (manager, context, info) {
if (info.operation[constants_1.SHOULD_HOOK_QUERY] === undefined) {
info.operation[constants_1.SHOULD_HOOK_QUERY] = manager.shouldHookQuery(context);
}
return info.operation[constants_1.SHOULD_HOOK_QUERY];
};
return Introspection;

@@ -79,0 +99,0 @@ }());

import Hook from './Hook';
import type { DirectiveNode } from 'graphql/language/ast';
import type { GraphQLResolveInfo, GraphQLSchema } from 'graphql';
import type { ClassDirectiveConfig, DirectiveConfig, IntrospectionDirectiveVisitor, VisitableIntrospectionType, IntrospectionDirectiveVisitorStatic, VisitableSchemaType } from '../types';
import type { ClassDirectiveConfig, IntrospectionDirectiveVisitor, VisitableIntrospectionType, IntrospectionDirectiveVisitorStatic, VisitableSchemaType, ExecutableSchemaDefinition, ShouldSkipQueryPredicate, VisitorResult } from '../types';
/**
* Introspection schema hooks manager
*/
export default class Manager {
export default class Manager<C> {
/**

@@ -14,6 +13,6 @@ * Extract Manager from schema

*/
static extract(schema: GraphQLSchema): Manager | undefined;
static extract<C>(schema: GraphQLSchema): Manager<C> | undefined;
protected _directives: Record<string, IntrospectionDirectiveVisitorStatic>;
protected _schema: GraphQLSchema;
private _cacheTtl;
protected _shouldSkipQuery: null | number | ShouldSkipQueryPredicate<C>;
/**

@@ -23,4 +22,5 @@ * Manager constructor

* @param schema graphql schema want to visit
* @param config schema configuration (definition)
*/
constructor(directives: Record<string, IntrospectionDirectiveVisitorStatic>, schema: GraphQLSchema);
constructor(directives: Record<string, IntrospectionDirectiveVisitorStatic>, schema: GraphQLSchema, config: ExecutableSchemaDefinition<C>);
/**

@@ -34,11 +34,11 @@ * Enhanced schema resolver

*/
resolve<T extends VisitableIntrospectionType, R extends VisitableSchemaType = any, C = any>(subject: T, root: R, context: C, info: GraphQLResolveInfo): Promise<T | null> | T | null;
resolve<T extends VisitableIntrospectionType, R extends VisitableSchemaType = any>(subject: T, root: R, context: C, info: GraphQLResolveInfo): VisitorResult<T>;
/**
* Change default introspection resolver cache ttl
* This cache is used to not perform duplicated
* resolutions for the same type/field during single request
* It may be useful to occasionally not hook the introspection query
* For example to hash it by apollo, which requires sync resolution
* It's always the first introspection query made o the instance
*
* @param ttl
* @param context current query context
*/
setCacheTtl(ttl: number): void;
shouldHookQuery(context: C): boolean;
/**

@@ -52,18 +52,17 @@ * Resolve visitor method for subject

protected expectedMethodFor(subject: VisitableIntrospectionType, root: VisitableSchemaType): keyof IntrospectionDirectiveVisitor;
protected prepareDirectives(subject: VisitableIntrospectionType): ClassDirectiveConfig[];
/**
* Prepare Hook for given class
* Prepare directives assigned to given field/type/...
*
* @param subject type/field that we are creating the Hook for
* @param root subject's root
* @param subject
* @protected
*/
protected prepare(subject: VisitableIntrospectionType, root: VisitableSchemaType): false | Hook;
protected findDirectives(subject: VisitableIntrospectionType): ClassDirectiveConfig[];
/**
* Grabs directive name and arguments from AST
* Prepare Hook for given subject
*
* @param directives array of AST definitions for directives applied to type/field
* @param subject type/field that we are creating the Hook for
* @param root subject's root
* @protected
*/
protected parseDirectiveAst(directives: ReadonlyArray<DirectiveNode>): DirectiveConfig[];
protected prepare(subject: VisitableIntrospectionType, root: VisitableSchemaType): Hook<C> | false;
}

@@ -7,2 +7,3 @@ "use strict";

var hasOwn_1 = tslib_1.__importDefault(require("../tools/hasOwn"));
var parseDirectiveAst_1 = tslib_1.__importDefault(require("../tools/parseDirectiveAst"));
var Hook_1 = tslib_1.__importDefault(require("./Hook"));

@@ -17,7 +18,9 @@ /**

* @param schema graphql schema want to visit
* @param config schema configuration (definition)
*/
function Manager(directives, schema) {
this._cacheTtl = 1000;
function Manager(directives, schema, config) {
this._shouldSkipQuery = null;
this._directives = directives;
this._schema = schema;
this._shouldSkipQuery = config.shouldSkipQuery || null;
}

@@ -53,10 +56,20 @@ /**

/**
* Change default introspection resolver cache ttl
* This cache is used to not perform duplicated
* resolutions for the same type/field during single request
* It may be useful to occasionally not hook the introspection query
* For example to hash it by apollo, which requires sync resolution
* It's always the first introspection query made o the instance
*
* @param ttl
* @param context current query context
*/
Manager.prototype.setCacheTtl = function (ttl) {
this._cacheTtl = ttl;
Manager.prototype.shouldHookQuery = function (context) {
if (typeof this._shouldSkipQuery === 'number') {
if (this._shouldSkipQuery > 0) {
--this._shouldSkipQuery;
return false;
}
return true;
}
else if (typeof this._shouldSkipQuery === 'function') {
return !this._shouldSkipQuery(context);
}
return true;
};

@@ -101,3 +114,9 @@ /**

};
Manager.prototype.prepareDirectives = function (subject) {
/**
* Prepare directives assigned to given field/type/...
*
* @param subject
* @protected
*/
Manager.prototype.findDirectives = function (subject) {
var _this = this;

@@ -118,3 +137,3 @@ var _a;

if (directives.length > 0) {
return this.parseDirectiveAst(directives)
return parseDirectiveAst_1.default(directives)
.filter(function (_a) {

@@ -129,3 +148,3 @@ var name = _a.name;

/**
* Prepare Hook for given class
* Prepare Hook for given subject
*

@@ -137,6 +156,6 @@ * @param subject type/field that we are creating the Hook for

Manager.prototype.prepare = function (subject, root) {
var parsedDirectives = this.prepareDirectives(subject);
if (parsedDirectives.length > 0) {
var matchingDirectives = this.findDirectives(subject);
if (matchingDirectives.length > 0) {
var method_1 = this.expectedMethodFor(subject, root);
var filteredDirectives = parsedDirectives
var filteredDirectives = matchingDirectives
.filter(function (_a) {

@@ -147,3 +166,3 @@ var cls = _a.cls;

if (filteredDirectives.length > 0) {
return new Hook_1.default(filteredDirectives, method_1, this._cacheTtl);
return new Hook_1.default(filteredDirectives, method_1);
}

@@ -153,22 +172,2 @@ }

};
/**
* Grabs directive name and arguments from AST
*
* @param directives array of AST definitions for directives applied to type/field
* @protected
*/
Manager.prototype.parseDirectiveAst = function (directives) {
return directives
.map(function (_a) {
var name = _a.name.value, _b = _a.arguments, args = _b === void 0 ? [] : _b;
return {
name: name,
args: args.reduce(function (args, _a) {
var name = _a.name.value, value = _a.value;
args[name] = value.value || null;
return args;
}, {})
};
});
};
return Manager;

@@ -175,0 +174,0 @@ }());

import OnceSession from './OnceSession';
export default class Once {
protected _store: Map<any, any>;
protected _cacheTtl: number;
constructor(cacheTtl: number);
/**
* Resolves (and creates when needed) introspection query session managers from current request
*/
export default class Once<C> {
/**
* Creates new session manager
* @protected
*/
protected newSession(): OnceSession;
session(context: any): OnceSession;
/**
* Extracts store from given queryContext (operation context)
* @param queryContext
* @protected
*/
protected getStore(queryContext: C): Map<Once<C>, OnceSession>;
/**
* Resolve session manager for given queryContext (operation context)
* @param queryContext
*/
session(queryContext: C): OnceSession;
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var constants_1 = require("../constants");
var OnceSession_1 = tslib_1.__importDefault(require("./OnceSession"));
/**
* Resolves (and creates when needed) introspection query session managers from current request
*/
var Once = /** @class */ (function () {
function Once(cacheTtl) {
this._store = new Map();
this._cacheTtl = cacheTtl;
function Once() {
}
/**
* Creates new session manager
* @protected
*/
Once.prototype.newSession = function () {
return new OnceSession_1.default();
};
Once.prototype.session = function (context) {
var _this = this;
if (this._store.has(context)) {
return this._store.get(context);
/**
* Extracts store from given queryContext (operation context)
* @param queryContext
* @protected
*/
Once.prototype.getStore = function (queryContext) {
if (!queryContext[constants_1.ONCE_CACHE]) {
queryContext[constants_1.ONCE_CACHE] = new Map();
}
return queryContext[constants_1.ONCE_CACHE];
};
/**
* Resolve session manager for given queryContext (operation context)
* @param queryContext
*/
Once.prototype.session = function (queryContext) {
var store = this.getStore(queryContext);
if (store.has(this)) {
return store.get(this);
}
var ses = this.newSession();
this._store.set(context, ses);
setTimeout(function () { return _this._store.delete(context); }, this._cacheTtl);
store.set(this, ses);
return ses;

@@ -22,0 +42,0 @@ };

@@ -1,10 +0,34 @@

export default class OnceSession {
protected _awaiters: any[];
/**
* Manages introspection query request repeated resolutions queue and cache for single field
*/
export default class OnceSession<V = any> {
protected _awaiters: ((cached: V) => void)[];
protected _started: boolean;
protected _finished: boolean;
protected _cache: any;
get isRunning(): boolean;
join(): any;
protected _cache: V | undefined;
/**
* Tells whether session can be joined.
*
* Returns true is session was started and is running or finished.
* Returns false if it wasn't started.
*/
get canJoin(): boolean;
/**
* Joins waiting session. If session was already finished session,
* the result is returned, if session is not finished yet awaiter promise is returned.
* Attempt to join not started session ends up in error.
*/
join(): Promise<V> | V;
/**
* Starts session
*
* If session was already stated throws an error.
*/
start(): void;
complete(value: any): void;
/**
* Completes session and saves it's result for late comers
*
* @param value
*/
complete(value: V): void;
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Manages introspection query request repeated resolutions queue and cache for single field
*/
var OnceSession = /** @class */ (function () {

@@ -9,3 +12,9 @@ function OnceSession() {

}
Object.defineProperty(OnceSession.prototype, "isRunning", {
Object.defineProperty(OnceSession.prototype, "canJoin", {
/**
* Tells whether session can be joined.
*
* Returns true is session was started and is running or finished.
* Returns false if it wasn't started.
*/
get: function () {

@@ -17,2 +26,7 @@ return this._started || this._finished;

});
/**
* Joins waiting session. If session was already finished session,
* the result is returned, if session is not finished yet awaiter promise is returned.
* Attempt to join not started session ends up in error.
*/
OnceSession.prototype.join = function () {

@@ -28,5 +42,18 @@ var _this = this;

};
/**
* Starts session
*
* If session was already stated throws an error.
*/
OnceSession.prototype.start = function () {
if (this._started) {
throw new Error('Session already started!');
}
this._started = true;
};
/**
* Completes session and saves it's result for late comers
*
* @param value
*/
OnceSession.prototype.complete = function (value) {

@@ -33,0 +60,0 @@ this._cache = value;

@@ -6,1 +6,3 @@ import type { IntrospectionDirectiveVisitor } from './types';

export declare const SCHEMA_HOOK: unique symbol;
export declare const SHOULD_HOOK_QUERY: unique symbol;
export declare const ONCE_CACHE: unique symbol;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SCHEMA_HOOK = exports.SCHEMA_MANAGER = exports.INTROSPECTION_HOOK = exports.INTROSPECTION_VISITOR_METHODS = void 0;
exports.ONCE_CACHE = exports.SHOULD_HOOK_QUERY = exports.SCHEMA_HOOK = exports.SCHEMA_MANAGER = exports.INTROSPECTION_HOOK = exports.INTROSPECTION_VISITOR_METHODS = void 0;
exports.INTROSPECTION_VISITOR_METHODS = [

@@ -20,2 +20,4 @@ 'visitIntrospectionScalar',

exports.SCHEMA_HOOK = Symbol('SCHEMA_HOOK');
exports.SHOULD_HOOK_QUERY = Symbol('SHOULD_HOOK_QUERY');
exports.ONCE_CACHE = Symbol('ONCE_CACHE');
//# sourceMappingURL=constants.js.map

@@ -1,2 +0,2 @@

declare const _default: <T>(valueOrPromise: T | Promise<T>, fn: (v: T) => T) => T | Promise<T>;
declare const _default: <T>(valueOrPromise: T | Promise<T>, fn: (v: T) => T | Promise<T>) => T | Promise<T>;
/**

@@ -3,0 +3,0 @@ * Executes fn on resolver value or Promise

@@ -1,2 +0,10 @@

declare const _default: <T = any>(array: T[], fn: (item: T) => T) => T[] | Promise<T[]>;
declare const _default: <T = any>(array: T[], fn: (item: T) => T | Promise<T>) => T[] | Promise<T[]>;
/**
* Maps array of items of given types with `fn` callback,
* if callback returns promise for eny element,
* result will be wrapped with `Promise.all`
*
* @param array array of values
* @param fn function to transform values, can return promises
*/
export default _default;

@@ -6,2 +6,10 @@ "use strict";

var isPromise_1 = tslib_1.__importDefault(require("./isPromise"));
/**
* Maps array of items of given types with `fn` callback,
* if callback returns promise for eny element,
* result will be wrapped with `Promise.all`
*
* @param array array of values
* @param fn function to transform values, can return promises
*/
exports.default = (function (array, fn) {

@@ -8,0 +16,0 @@ var e_1, _a;

import type { GraphQLSchema } from 'graphql';
import type { IExecutableSchemaDefinition } from 'graphql-tools';
import type { BuilderSig } from '../types';
declare const _default: <TContext = any>(config: IExecutableSchemaDefinition<TContext>, builder?: BuilderSig<TContext>) => GraphQLSchema;
import type { BuilderSig, ExecutableSchemaDefinition } from '../types';
declare const _default: <TContext = any>(config: ExecutableSchemaDefinition<TContext>, builder?: BuilderSig<TContext>) => GraphQLSchema;
/**

@@ -6,0 +5,0 @@ * Create graphql executable schema with injected Manager

@@ -50,3 +50,3 @@ "use strict";

}
schema[constants_1.SCHEMA_MANAGER] = new Manager_1.default(introspectionDirectives, schema);
schema[constants_1.SCHEMA_MANAGER] = new Manager_1.default(introspectionDirectives, schema, config);
}

@@ -53,0 +53,0 @@ }

@@ -42,2 +42,7 @@ import type { GraphQLArgument, GraphQLDirective, GraphQLEnumType, GraphQLEnumValue, GraphQLField, GraphQLInputField, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLObjectType, GraphQLResolveInfo, GraphQLScalarType, GraphQLSchema, GraphQLUnionType, GraphQLNamedType } from 'graphql';

export interface IntrospectionDirectiveVisitorStatic {
/**
* IntrospectionDirectiveVisitor constructor
* (derived from SchemaDirectiveVisitor)
* @param config
*/
new (config: {

@@ -56,3 +61,10 @@ name: string;

export interface DirectiveConfig {
/**
* Directive name
*/
name: string;
/**
* Directive arguments
* (unavailable for directive visitor!)
*/
args: Record<string, any>;

@@ -65,5 +77,22 @@ }

export interface ClassDirectiveConfig extends DirectiveConfig {
/**
* Introspection schema visitor class constructor
* (matched by name)
*/
cls: IntrospectionDirectiveVisitorStatic;
}
/**
* Decides whether query should be hooked
*/
export declare type ShouldSkipQueryPredicate<TContext = any> = (context: TContext) => boolean;
/**
* Executable schema definition (config) with introspection Manager config
*/
export interface ExecutableSchemaDefinition<TContext = any> extends IExecutableSchemaDefinition<TContext> {
/**
* Skip query predicate or counter
*/
shouldSkipQuery?: null | undefined | number | ShouldSkipQueryPredicate<TContext>;
}
/**
* makeExecutableSchema executable schema builder signature

@@ -70,0 +99,0 @@ * Internal helper type

{
"name": "graphql-introspection-filtering",
"version": "2.0.0-beta.2",
"version": "2.0.0-rc.0",
"description": "Filter graphql schema introspection result to hide restricted fields and types",

@@ -20,3 +20,6 @@ "main": "./dist/index.js",

"prepare": "yarn lint && yarn test && yarn build",
"test": "jest --config .jestrc.json",
"test": "jest --no-cache --config .jestrc.json",
"test:integration": "yarn test ./tests/integration",
"test:unit": "yarn test ./tests/unit",
"test:coverage": "yarn test:integration --ci --coverage && yarn test:unit --ci --coverage",
"example": "ts-node -P ./tsconfig.all.json ./example/index.ts"

@@ -34,4 +37,4 @@ },

"peerDependencies": {
"graphql": ">=14.0.2",
"graphql-tools": ">=4.0.7"
"graphql": ">=14.0.0",
"graphql-tools": ">=4.0.0"
},

@@ -38,0 +41,0 @@ "devDependencies": {

@@ -164,5 +164,2 @@ # graphql-introspection-filtering

# TODO
* cache ttl config in config
* skip hash generation introspection
* unit tests
* update docs

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc