graphql-anywhere
Advanced tools
Comparing version 2.0.0 to 2.1.0
# Change log | ||
### vNext | ||
### v2.1.0 | ||
Tolerate missing variables (to support optional arguments) | ||
Remove a circular dependency | ||
### v2.0.0 | ||
@@ -4,0 +11,0 @@ |
@@ -1,2 +0,1 @@ | ||
/// <reference types="graphql" /> | ||
import { SelectionNode } from 'graphql'; | ||
@@ -3,0 +2,0 @@ export declare function shouldInclude(selection: SelectionNode, variables?: { |
@@ -1,2 +0,1 @@ | ||
/// <reference types="graphql" /> | ||
import { DocumentNode, OperationDefinitionNode, FragmentDefinitionNode } from 'graphql'; | ||
@@ -3,0 +2,0 @@ export declare function getMutationDefinition(doc: DocumentNode): OperationDefinitionNode; |
@@ -1,29 +0,4 @@ | ||
/// <reference types="graphql" /> | ||
import { DocumentNode } from 'graphql'; | ||
import { FragmentMap } from './getFromAST'; | ||
export { filter, check, propType } from './utilities'; | ||
export declare type Resolver = (fieldName: string, rootValue: any, args: any, context: any, info: ExecInfo) => any; | ||
export declare type VariableMap = { | ||
[name: string]: any; | ||
}; | ||
export declare type ResultMapper = (values: { | ||
[fieldName: string]: any; | ||
}, rootValue: any) => any; | ||
export declare type FragmentMatcher = (rootValue: any, typeCondition: string, context: any) => boolean; | ||
export declare type ExecContext = { | ||
fragmentMap: FragmentMap; | ||
contextValue: any; | ||
variableValues: VariableMap; | ||
resultMapper: ResultMapper; | ||
resolver: Resolver; | ||
fragmentMatcher: FragmentMatcher; | ||
}; | ||
export declare type ExecInfo = { | ||
isLeaf: boolean; | ||
resultKey: string; | ||
}; | ||
export declare type ExecOptions = { | ||
resultMapper?: ResultMapper; | ||
fragmentMatcher?: FragmentMatcher; | ||
}; | ||
export default function graphql(resolver: Resolver, document: DocumentNode, rootValue?: any, contextValue?: any, variableValues?: VariableMap, execOptions?: ExecOptions): any; | ||
export { Resolver, VariableMap, ResultMapper, FragmentMatcher, ExecContext, ExecInfo, ExecOptions } from './graphql'; | ||
import { graphql } from './graphql'; | ||
export default graphql; |
"use strict"; | ||
var getFromAST_1 = require("./getFromAST"); | ||
var directives_1 = require("./directives"); | ||
var storeUtils_1 = require("./storeUtils"); | ||
var utilities_1 = require("./utilities"); | ||
@@ -9,109 +6,5 @@ exports.filter = utilities_1.filter; | ||
exports.propType = utilities_1.propType; | ||
function graphql(resolver, document, rootValue, contextValue, variableValues, execOptions) { | ||
if (execOptions === void 0) { execOptions = {}; } | ||
var mainDefinition = getFromAST_1.getMainDefinition(document); | ||
var fragments = getFromAST_1.getFragmentDefinitions(document); | ||
var fragmentMap = getFromAST_1.createFragmentMap(fragments) || {}; | ||
var resultMapper = execOptions.resultMapper; | ||
var fragmentMatcher = execOptions.fragmentMatcher || (function () { return true; }); | ||
var execContext = { | ||
fragmentMap: fragmentMap, | ||
contextValue: contextValue, | ||
variableValues: variableValues, | ||
resultMapper: resultMapper, | ||
resolver: resolver, | ||
fragmentMatcher: fragmentMatcher, | ||
}; | ||
return executeSelectionSet(mainDefinition.selectionSet, rootValue, execContext); | ||
} | ||
var graphql_1 = require("./graphql"); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.default = graphql; | ||
function executeSelectionSet(selectionSet, rootValue, execContext) { | ||
var fragmentMap = execContext.fragmentMap, contextValue = execContext.contextValue, variables = execContext.variableValues; | ||
var result = {}; | ||
selectionSet.selections.forEach(function (selection) { | ||
if (!directives_1.shouldInclude(selection, variables)) { | ||
return; | ||
} | ||
if (storeUtils_1.isField(selection)) { | ||
var fieldResult = executeField(selection, rootValue, execContext); | ||
var resultFieldKey = storeUtils_1.resultKeyNameFromField(selection); | ||
if (fieldResult !== undefined) { | ||
result[resultFieldKey] = fieldResult; | ||
} | ||
} | ||
else { | ||
var fragment = void 0; | ||
if (storeUtils_1.isInlineFragment(selection)) { | ||
fragment = selection; | ||
} | ||
else { | ||
fragment = fragmentMap[selection.name.value]; | ||
if (!fragment) { | ||
throw new Error("No fragment named " + selection.name.value); | ||
} | ||
} | ||
var typeCondition = fragment.typeCondition.name.value; | ||
if (execContext.fragmentMatcher(rootValue, typeCondition, contextValue)) { | ||
var fragmentResult = executeSelectionSet(fragment.selectionSet, rootValue, execContext); | ||
merge(result, fragmentResult); | ||
} | ||
} | ||
}); | ||
if (execContext.resultMapper) { | ||
return execContext.resultMapper(result, rootValue); | ||
} | ||
return result; | ||
} | ||
function executeField(field, rootValue, execContext) { | ||
var variables = execContext.variableValues, contextValue = execContext.contextValue, resolver = execContext.resolver; | ||
var fieldName = field.name.value; | ||
var args = storeUtils_1.argumentsObjectFromField(field, variables); | ||
var info = { | ||
isLeaf: !field.selectionSet, | ||
resultKey: storeUtils_1.resultKeyNameFromField(field), | ||
}; | ||
var result = resolver(fieldName, rootValue, args, contextValue, info); | ||
if (!field.selectionSet) { | ||
return result; | ||
} | ||
if (result === null || typeof result === 'undefined') { | ||
return result; | ||
} | ||
if (Array.isArray(result)) { | ||
return executeSubSelectedArray(field, result, execContext); | ||
} | ||
return executeSelectionSet(field.selectionSet, result, execContext); | ||
} | ||
function executeSubSelectedArray(field, result, execContext) { | ||
return result.map(function (item) { | ||
if (item === null) { | ||
return null; | ||
} | ||
if (Array.isArray(item)) { | ||
return executeSubSelectedArray(field, item, execContext); | ||
} | ||
return executeSelectionSet(field.selectionSet, item, execContext); | ||
}); | ||
} | ||
function merge(dest, src) { | ||
if (src === null || | ||
typeof src === 'undefined' || | ||
typeof src === 'string' || | ||
typeof src === 'number' || | ||
typeof src === 'boolean' || | ||
Array.isArray(src)) { | ||
return src; | ||
} | ||
Object.keys(dest).forEach(function (destKey) { | ||
if (src.hasOwnProperty(destKey)) { | ||
merge(dest[destKey], src[destKey]); | ||
} | ||
}); | ||
Object.keys(src).forEach(function (srcKey) { | ||
if (!dest.hasOwnProperty(srcKey)) { | ||
dest[srcKey] = src[srcKey]; | ||
} | ||
}); | ||
} | ||
exports.default = graphql_1.graphql; | ||
//# sourceMappingURL=index.js.map |
@@ -1,2 +0,1 @@ | ||
/// <reference types="graphql" /> | ||
import { FieldNode, InlineFragmentNode, SelectionNode, ExecutionResult } from 'graphql'; | ||
@@ -3,0 +2,0 @@ export declare function argumentsObjectFromField(field: FieldNode, variables: Object): Object; |
@@ -39,6 +39,3 @@ "use strict"; | ||
else if (isVariable(value)) { | ||
if (!variables || !(value.name.value in variables)) { | ||
throw new Error("The inline argument \"" + value.name.value + "\" is expected as a variable but was not provided."); | ||
} | ||
var variableValue = variables[value.name.value]; | ||
var variableValue = (variables || {})[value.name.value]; | ||
argObj[name.value] = variableValue; | ||
@@ -45,0 +42,0 @@ } |
@@ -1,2 +0,1 @@ | ||
/// <reference types="graphql" /> | ||
import { DocumentNode } from 'graphql'; | ||
@@ -3,0 +2,0 @@ export declare function filter(doc: DocumentNode, data: any): any; |
"use strict"; | ||
var index_1 = require("./index"); | ||
var graphql_1 = require("./graphql"); | ||
function filter(doc, data) { | ||
@@ -7,3 +7,3 @@ var resolver = function (fieldName, root, args, context, info) { | ||
}; | ||
return index_1.default(resolver, doc, data); | ||
return graphql_1.graphql(resolver, doc, data); | ||
} | ||
@@ -18,3 +18,3 @@ exports.filter = filter; | ||
}; | ||
index_1.default(resolver, doc, data, {}, {}, { | ||
graphql_1.graphql(resolver, doc, data, {}, {}, { | ||
fragmentMatcher: function () { return false; }, | ||
@@ -21,0 +21,0 @@ }); |
{ | ||
"name": "graphql-anywhere", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "Run GraphQL queries with no schema and just one resolver", | ||
@@ -5,0 +5,0 @@ "main": "./lib/src/index.js", |
273
src/index.ts
@@ -1,27 +0,1 @@ | ||
import { | ||
DocumentNode, | ||
SelectionSetNode, | ||
FieldNode, | ||
FragmentDefinitionNode, | ||
InlineFragmentNode, | ||
} from 'graphql'; | ||
import { | ||
getMainDefinition, | ||
getFragmentDefinitions, | ||
createFragmentMap, | ||
FragmentMap, | ||
} from './getFromAST'; | ||
import { | ||
shouldInclude, | ||
} from './directives'; | ||
import { | ||
isField, | ||
isInlineFragment, | ||
resultKeyNameFromField, | ||
argumentsObjectFromField, | ||
} from './storeUtils'; | ||
export { | ||
@@ -33,239 +7,14 @@ filter, | ||
export type Resolver = ( | ||
fieldName: string, | ||
rootValue: any, | ||
args: any, | ||
context: any, | ||
info: ExecInfo | ||
) => any; | ||
export { | ||
Resolver, | ||
VariableMap, | ||
ResultMapper, | ||
FragmentMatcher, | ||
ExecContext, | ||
ExecInfo, | ||
ExecOptions, | ||
} from './graphql'; | ||
export type VariableMap = { [name: string]: any }; | ||
import { graphql } from './graphql'; | ||
export type ResultMapper = (values: {[fieldName: string]: any}, rootValue: any) => any; | ||
export type FragmentMatcher = (rootValue: any, typeCondition: string, context: any) => boolean; | ||
export type ExecContext = { | ||
fragmentMap: FragmentMap; | ||
contextValue: any; | ||
variableValues: VariableMap; | ||
resultMapper: ResultMapper; | ||
resolver: Resolver; | ||
fragmentMatcher: FragmentMatcher; | ||
} | ||
export type ExecInfo = { | ||
isLeaf: boolean; | ||
resultKey: string; | ||
} | ||
export type ExecOptions = { | ||
resultMapper?: ResultMapper; | ||
fragmentMatcher?: FragmentMatcher; | ||
} | ||
// Based on graphql function from graphql-js: | ||
// graphql( | ||
// schema: GraphQLSchema, | ||
// requestString: string, | ||
// rootValue?: ?any, | ||
// contextValue?: ?any, | ||
// variableValues?: ?{[key: string]: any}, | ||
// operationName?: ?string | ||
// ): Promise<GraphQLResult> | ||
export default function graphql( | ||
resolver: Resolver, | ||
document: DocumentNode, | ||
rootValue?: any, | ||
contextValue?: any, | ||
variableValues?: VariableMap, | ||
execOptions: ExecOptions = {}, | ||
) { | ||
const mainDefinition = getMainDefinition(document); | ||
const fragments = getFragmentDefinitions(document); | ||
const fragmentMap = createFragmentMap(fragments) || {}; | ||
const resultMapper = execOptions.resultMapper; | ||
// Default matcher always matches all fragments | ||
const fragmentMatcher = execOptions.fragmentMatcher || (() => true); | ||
const execContext: ExecContext = { | ||
fragmentMap, | ||
contextValue, | ||
variableValues, | ||
resultMapper, | ||
resolver, | ||
fragmentMatcher, | ||
}; | ||
return executeSelectionSet( | ||
mainDefinition.selectionSet, | ||
rootValue, | ||
execContext | ||
); | ||
} | ||
function executeSelectionSet( | ||
selectionSet: SelectionSetNode, | ||
rootValue: any, | ||
execContext: ExecContext | ||
) { | ||
const { | ||
fragmentMap, | ||
contextValue, | ||
variableValues: variables, | ||
} = execContext; | ||
const result = {}; | ||
selectionSet.selections.forEach((selection) => { | ||
if (! shouldInclude(selection, variables)) { | ||
// Skip this entirely | ||
return; | ||
} | ||
if (isField(selection)) { | ||
const fieldResult = executeField( | ||
selection, | ||
rootValue, | ||
execContext | ||
); | ||
const resultFieldKey = resultKeyNameFromField(selection); | ||
if (fieldResult !== undefined) { | ||
result[resultFieldKey] = fieldResult; | ||
} | ||
} else { | ||
let fragment: InlineFragmentNode | FragmentDefinitionNode; | ||
if (isInlineFragment(selection)) { | ||
fragment = selection; | ||
} else { | ||
// This is a named fragment | ||
fragment = fragmentMap[selection.name.value]; | ||
if (!fragment) { | ||
throw new Error(`No fragment named ${selection.name.value}`); | ||
} | ||
} | ||
const typeCondition = fragment.typeCondition.name.value; | ||
if (execContext.fragmentMatcher(rootValue, typeCondition, contextValue)) { | ||
const fragmentResult = executeSelectionSet( | ||
fragment.selectionSet, | ||
rootValue, | ||
execContext | ||
); | ||
merge(result, fragmentResult); | ||
} | ||
} | ||
}); | ||
if (execContext.resultMapper) { | ||
return execContext.resultMapper(result, rootValue); | ||
} | ||
return result; | ||
} | ||
function executeField( | ||
field: FieldNode, | ||
rootValue: any, | ||
execContext: ExecContext | ||
): any { | ||
const { | ||
variableValues: variables, | ||
contextValue, | ||
resolver, | ||
} = execContext; | ||
const fieldName = field.name.value; | ||
const args = argumentsObjectFromField(field, variables); | ||
const info: ExecInfo = { | ||
isLeaf: ! field.selectionSet, | ||
resultKey: resultKeyNameFromField(field), | ||
}; | ||
const result = resolver(fieldName, rootValue, args, contextValue, info); | ||
// Handle all scalar types here | ||
if (! field.selectionSet) { | ||
return result; | ||
} | ||
// From here down, the field has a selection set, which means it's trying to | ||
// query a GraphQLObjectType | ||
if (result === null || typeof result === 'undefined') { | ||
// Basically any field in a GraphQL response can be null, or missing | ||
return result; | ||
} | ||
if (Array.isArray(result)) { | ||
return executeSubSelectedArray(field, result, execContext); | ||
} | ||
// Returned value is an object, and the query has a sub-selection. Recurse. | ||
return executeSelectionSet( | ||
field.selectionSet, | ||
result, | ||
execContext | ||
); | ||
} | ||
function executeSubSelectedArray( | ||
field, | ||
result, | ||
execContext | ||
) { | ||
return result.map((item) => { | ||
// null value in array | ||
if (item === null) { | ||
return null; | ||
} | ||
// This is a nested array, recurse | ||
if (Array.isArray(item)) { | ||
return executeSubSelectedArray(field, item, execContext); | ||
} | ||
// This is an object, run the selection set on it | ||
return executeSelectionSet( | ||
field.selectionSet, | ||
item, | ||
execContext | ||
); | ||
}); | ||
} | ||
function merge(dest, src) { | ||
if ( | ||
src === null || | ||
typeof src === 'undefined' || | ||
typeof src === 'string' || | ||
typeof src === 'number' || | ||
typeof src === 'boolean' || | ||
Array.isArray(src) | ||
) { | ||
// These types just override whatever was in dest | ||
return src; | ||
} | ||
// Merge sub-objects | ||
Object.keys(dest).forEach((destKey) => { | ||
if (src.hasOwnProperty(destKey)) { | ||
merge(dest[destKey], src[destKey]); | ||
} | ||
}); | ||
// Add props only on src | ||
Object.keys(src).forEach((srcKey) => { | ||
if (! dest.hasOwnProperty(srcKey)) { | ||
dest[srcKey] = src[srcKey]; | ||
} | ||
}); | ||
} | ||
export default graphql; |
@@ -63,6 +63,3 @@ import { | ||
} else if (isVariable(value)) { | ||
if (! variables || !(value.name.value in variables)) { | ||
throw new Error(`The inline argument "${value.name.value}" is expected as a variable but was not provided.`); | ||
} | ||
const variableValue = variables[value.name.value]; | ||
const variableValue = (variables || {})[value.name.value]; | ||
argObj[name.value] = variableValue; | ||
@@ -69,0 +66,0 @@ } else if (isList(value)) { |
@@ -5,3 +5,3 @@ import { | ||
import graphql from './index'; | ||
import { graphql } from './graphql'; | ||
@@ -8,0 +8,0 @@ export function filter(doc: DocumentNode, data: any): any { |
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
65698
32
1143
1