UNPKG

nexus

Version:

Scalable, strongly typed GraphQL schema development

626 lines (625 loc) 33.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.makeResolveFn = exports.connectionPlugin = exports.BackwardOnlyStrictArgs = exports.BackwardPaginateArgs = exports.ForwardOnlyStrictArgs = exports.ForwardPaginateArgs = void 0; const tslib_1 = require("tslib"); const graphql_1 = require("graphql"); const args_1 = require("../definitions/args"); const nonNull_1 = require("../definitions/nonNull"); const nullable_1 = require("../definitions/nullable"); const objectType_1 = require("../definitions/objectType"); const wrapping_1 = require("../definitions/wrapping"); const dynamicMethod_1 = require("../dynamicMethod"); const plugin_1 = require("../plugin"); const utils_1 = require("../utils"); exports.ForwardPaginateArgs = { first: (0, nullable_1.nullable)((0, args_1.intArg)({ description: 'Returns the first n elements from the list.' })), after: (0, nullable_1.nullable)((0, args_1.stringArg)({ description: 'Returns the elements in the list that come after the specified cursor' })), }; exports.ForwardOnlyStrictArgs = Object.assign(Object.assign({}, exports.ForwardPaginateArgs), { first: (0, nonNull_1.nonNull)((0, args_1.intArg)({ description: 'Returns the first n elements from the list.' })) }); exports.BackwardPaginateArgs = { last: (0, nullable_1.nullable)((0, args_1.intArg)({ description: 'Returns the last n elements from the list.' })), before: (0, nullable_1.nullable)((0, args_1.stringArg)({ description: 'Returns the elements in the list that come before the specified cursor' })), }; exports.BackwardOnlyStrictArgs = Object.assign(Object.assign({}, exports.BackwardPaginateArgs), { last: (0, nonNull_1.nonNull)((0, args_1.intArg)({ description: 'Returns the last n elements from the list.' })) }); function base64Encode(str) { return Buffer.from(str, 'utf8').toString('base64'); } function base64Decode(str) { return Buffer.from(str, 'base64').toString('utf8'); } const connectionPlugin = (connectionPluginConfig) => { var _a; const pluginConfig = Object.assign({}, connectionPluginConfig); // Define the plugin with the appropriate configuration. return (0, plugin_1.plugin)({ name: 'ConnectionPlugin', fieldDefTypes: [ (0, utils_1.printedGenTypingImport)({ module: (_a = connectionPluginConfig === null || connectionPluginConfig === void 0 ? void 0 : connectionPluginConfig.nexusSchemaImportId) !== null && _a !== void 0 ? _a : (0, utils_1.getOwnPackage)().name, bindings: ['core', 'connectionPluginCore'], }), ], // Defines the field added to the definition block: // t.connectionField('users', { // type: User // }) onInstall(b) { let dynamicConfig = []; const { additionalArgs = {}, extendConnection: pluginExtendConnection, extendEdge: pluginExtendEdge, includeNodesField = false, nexusFieldName = 'connectionField', } = pluginConfig; // If to add fields to every connection, we require the resolver be defined on the // field definition, unless fromResolve: true is passed in the config if (pluginExtendConnection) { (0, utils_1.eachObj)(pluginExtendConnection, (val, key) => { dynamicConfig.push(`${key}${val.requireResolver === false ? '?:' : ':'} connectionPluginCore.ConnectionFieldResolver<TypeName, FieldName, "${key}">`); }); } if (pluginExtendEdge) { const edgeFields = (0, utils_1.mapObj)(pluginExtendEdge, (val, key) => `${key}${val.requireResolver === false ? '?:' : ':'} connectionPluginCore.EdgeFieldResolver<TypeName, FieldName, "${key}">`); dynamicConfig.push(`edgeFields: { ${edgeFields.join(', ')} }`); } let printedDynamicConfig = ''; if (dynamicConfig.length > 0) { printedDynamicConfig = ` & { ${dynamicConfig.join(', ')} }`; } // Add the t.connectionField (or something else if we've changed the name) b.addType((0, dynamicMethod_1.dynamicOutputMethod)({ name: nexusFieldName, typeDescription: ` Adds a Relay-style connection to the type, with numerous options for configuration @see https://nexusjs.org/docs/plugins/connection `, typeDefinition: `<FieldName extends string>( fieldName: FieldName, config: connectionPluginCore.ConnectionFieldConfig<TypeName, FieldName>${printedDynamicConfig} ): void`, factory({ typeName: parentTypeName, typeDef: t, args: factoryArgs, stage, builder, wrapping }) { var _a, _b; const [fieldName, fieldConfig] = factoryArgs; const targetType = fieldConfig.type; /* istanbul ignore if */ if (wrapping === null || wrapping === void 0 ? void 0 : wrapping.includes('List')) { throw new Error(`Cannot chain .list with connectionField (on ${parentTypeName}.${fieldName})`); } const { targetTypeName, connectionName, edgeName } = getTypeNames(fieldName, parentTypeName, fieldConfig, pluginConfig); if (stage === 'build') { assertCorrectConfig(parentTypeName, fieldName, pluginConfig, fieldConfig); } // Add the "Connection" type to the schema if it doesn't exist already if (!b.hasType(connectionName)) { b.addType((0, objectType_1.objectType)({ name: connectionName, definition(t2) { t2.list.field('edges', { type: edgeName, description: `https://facebook.github.io/relay/graphql/connections.htm#sec-Edge-Types`, }); t2.nonNull.field('pageInfo', { type: 'PageInfo', description: `https://facebook.github.io/relay/graphql/connections.htm#sec-undefined.PageInfo`, }); if (includeNodesField) { t2.list.field('nodes', { type: targetType, description: `Flattened list of ${targetTypeName} type`, }); } if (pluginExtendConnection) { (0, utils_1.eachObj)(pluginExtendConnection, (extensionFieldConfig, extensionFieldName) => { t2.field(extensionFieldName, extensionFieldConfig); }); } provideSourceAndArgs(t2, () => { if (fieldConfig.extendConnection instanceof Function) { fieldConfig.extendConnection(t2); } }); }, nonNullDefaults: (_a = fieldConfig.nonNullDefaults) !== null && _a !== void 0 ? _a : pluginConfig.nonNullDefaults, })); } // Add the "Edge" type to the schema if it doesn't exist already if (!b.hasType(edgeName)) { b.addType((0, objectType_1.objectType)({ name: edgeName, definition(t2) { t2.field('cursor', { type: cursorType !== null && cursorType !== void 0 ? cursorType : (0, nonNull_1.nonNull)('String'), description: 'https://facebook.github.io/relay/graphql/connections.htm#sec-Cursor', }); t2.field('node', { type: targetType, description: 'https://facebook.github.io/relay/graphql/connections.htm#sec-Node', }); if (pluginExtendEdge) { (0, utils_1.eachObj)(pluginExtendEdge, (val, key) => { t2.field(key, val); }); } provideArgs(t2, () => { if (fieldConfig.extendEdge instanceof Function) { fieldConfig.extendEdge(t2); } }); }, nonNullDefaults: (_b = fieldConfig.nonNullDefaults) !== null && _b !== void 0 ? _b : pluginConfig.nonNullDefaults, })); } // Add the "PageInfo" type to the schema if it doesn't exist already if (!b.hasType('PageInfo')) { b.addType((0, objectType_1.objectType)({ name: 'PageInfo', description: 'PageInfo cursor, as defined in https://facebook.github.io/relay/graphql/connections.htm#sec-undefined.PageInfo', definition(t2) { t2.nonNull.field('hasNextPage', { type: 'Boolean', description: `Used to indicate whether more edges exist following the set defined by the clients arguments.`, }); t2.nonNull.field('hasPreviousPage', { type: 'Boolean', description: `Used to indicate whether more edges exist prior to the set defined by the clients arguments.`, }); t2.nullable.field('startCursor', { type: 'String', description: `The cursor corresponding to the first nodes in edges. Null if the connection is empty.`, }); t2.nullable.field('endCursor', { type: 'String', description: `The cursor corresponding to the last nodes in edges. Null if the connection is empty.`, }); }, })); } const { disableBackwardPagination, disableForwardPagination, validateArgs = defaultValidateArgs, strictArgs = true, cursorType, } = Object.assign(Object.assign({}, pluginConfig), fieldConfig); let specArgs = {}; if (disableForwardPagination !== true && disableBackwardPagination !== true) { specArgs = Object.assign(Object.assign({}, exports.ForwardPaginateArgs), exports.BackwardPaginateArgs); } else if (disableForwardPagination !== true) { specArgs = strictArgs ? Object.assign({}, exports.ForwardOnlyStrictArgs) : Object.assign({}, exports.ForwardPaginateArgs); } else if (disableBackwardPagination !== true) { specArgs = strictArgs ? Object.assign({}, exports.BackwardOnlyStrictArgs) : Object.assign({}, exports.BackwardPaginateArgs); } // If we have additional args, let fieldAdditionalArgs = {}; if (fieldConfig.additionalArgs) { if (additionalArgs && fieldConfig.inheritAdditionalArgs) { fieldAdditionalArgs = Object.assign(Object.assign({}, additionalArgs), fieldConfig.additionalArgs); } else { fieldAdditionalArgs = Object.assign({}, fieldConfig.additionalArgs); } } else if (additionalArgs) { fieldAdditionalArgs = Object.assign({}, additionalArgs); } const fieldArgs = Object.assign(Object.assign({}, fieldAdditionalArgs), specArgs); let resolveFn; if (fieldConfig.resolve) { if (includeNodesField) { resolveFn = (root, args, ctx, info) => { return (0, plugin_1.completeValue)(fieldConfig.resolve(root, args, ctx, info), (val) => { if (val && val.nodes === undefined) { return withArgs(args, Object.assign({ get nodes() { return (0, plugin_1.completeValue)(val.edges, (edges) => edges.map((edge) => edge.node)); } }, val)); } return withArgs(args, Object.assign({}, val)); }); }; } else { resolveFn = fieldConfig.resolve; } } else { resolveFn = makeResolveFn(pluginConfig, fieldConfig); } let wrappedConnectionName = connectionName; if (wrapping) { if (typeof fieldConfig.nullable === 'boolean') { throw new Error('[connectionPlugin]: You cannot chain .null/.nonNull and also set the nullable in the connectionField definition.'); } wrappedConnectionName = (0, wrapping_1.applyNexusWrapping)(connectionName, wrapping); } else { if (fieldConfig.nullable === true) { wrappedConnectionName = (0, nullable_1.nullable)(wrappedConnectionName); } else if (fieldConfig.nullable === false) { wrappedConnectionName = (0, nonNull_1.nonNull)(wrappedConnectionName); } } // Add the field to the type. t.field(fieldName, Object.assign(Object.assign({}, nonConnectionFieldProps(fieldConfig)), { args: fieldArgs, type: wrappedConnectionName, resolve(root, args, ctx, info) { // TODO(2.0): Maybe switch the arguments around here to be consistent w/ resolver (breaking change)? validateArgs(args, info, root, ctx); return resolveFn(root, args, ctx, info); } })); }, })); }, }); }; exports.connectionPlugin = connectionPlugin; // Extract all of the non-connection related field config we may want to apply for plugin purposes function nonConnectionFieldProps(fieldConfig) { const { additionalArgs, cursorFromNode, disableBackwardPagination, disableForwardPagination, extendConnection, extendEdge, inheritAdditionalArgs, nodes, pageInfoFromNodes, resolve, type, validateArgs, strictArgs, nullable } = fieldConfig, rest = (0, tslib_1.__rest)(fieldConfig, ["additionalArgs", "cursorFromNode", "disableBackwardPagination", "disableForwardPagination", "extendConnection", "extendEdge", "inheritAdditionalArgs", "nodes", "pageInfoFromNodes", "resolve", "type", "validateArgs", "strictArgs", "nullable"]); return rest; } function makeResolveFn(pluginConfig, fieldConfig) { const mergedConfig = Object.assign(Object.assign({}, pluginConfig), fieldConfig); return (root, args, ctx, info) => { const { nodes: nodesResolve } = fieldConfig; const { decodeCursor = base64Decode, encodeCursor = base64Encode } = pluginConfig; const { pageInfoFromNodes = defaultPageInfoFromNodes, cursorFromNode = defaultCursorFromNode } = mergedConfig; if (!nodesResolve) { return null; } const formattedArgs = Object.assign({}, args); if (args.before) { formattedArgs.before = decodeCursor(args.before).replace(CURSOR_PREFIX, ''); } if (args.after) { formattedArgs.after = decodeCursor(args.after).replace(CURSOR_PREFIX, ''); } if (args.last && !args.before && cursorFromNode === defaultCursorFromNode) { throw new Error(`Cannot paginate backward without a "before" cursor by default.`); } // Local variable to cache the execution of fetching the nodes, // which is needed for all fields. let cachedNodes; let cachedEdges; let hasPromise = false; // Get all the nodes, before any pagination slicing const resolveAllNodes = () => { var _a; if (cachedNodes !== undefined) { return cachedNodes; } cachedNodes = (0, plugin_1.completeValue)((_a = nodesResolve(root, formattedArgs, ctx, info)) !== null && _a !== void 0 ? _a : null, (allNodes) => { return allNodes ? Array.from(allNodes) : allNodes; }); return cachedNodes; }; const resolveEdgesAndNodes = () => { if (cachedEdges !== undefined) { return cachedEdges; } cachedEdges = (0, plugin_1.completeValue)(resolveAllNodes(), (allNodes) => { if (!allNodes) { const arrPath = JSON.stringify((0, utils_1.pathToArray)(info.path)); console.warn(`You resolved null/undefined from nodes() at path ${arrPath}, this is likely an error. Return an empty array to suppress this warning.`); return { edges: [], nodes: [] }; } const resolvedEdgeList = []; const resolvedNodeList = []; iterateNodes(allNodes, args, (maybeNode, i) => { if ((0, utils_1.isPromiseLike)(maybeNode)) { hasPromise = true; resolvedNodeList.push(maybeNode); resolvedEdgeList.push(maybeNode.then((node) => { return (0, plugin_1.completeValue)(cursorFromNode(maybeNode, formattedArgs, ctx, info, { index: i, nodes: allNodes, }), (rawCursor) => wrapEdge(pluginConfig, fieldConfig, formattedArgs, { cursor: encodeCursor(rawCursor), node, })); })); } else { resolvedNodeList.push(maybeNode); resolvedEdgeList.push(wrapEdge(pluginConfig, fieldConfig, formattedArgs, { node: maybeNode, cursor: (0, plugin_1.completeValue)(cursorFromNode(maybeNode, formattedArgs, ctx, info, { index: i, nodes: allNodes, }), (rawCursor) => encodeCursor(rawCursor)), })); } }); if (hasPromise) { return Promise.all([Promise.all(resolvedEdgeList), Promise.all(resolvedNodeList)]).then(([edges, nodes]) => ({ edges, nodes })); } return { nodes: resolvedNodeList, // todo find type-safe way of doing this edges: resolvedEdgeList, }; }); return cachedEdges; }; const resolvePageInfo = () => { return (0, plugin_1.completeValue)(resolveAllNodes(), (allNodes) => (0, plugin_1.completeValue)(resolveEdgesAndNodes(), ({ edges }) => (0, plugin_1.completeValue)(allNodes ? pageInfoFromNodes(allNodes, args, ctx, info) : { hasNextPage: false, hasPreviousPage: false, }, (basePageInfo) => { var _a, _b, _c; return (Object.assign(Object.assign({}, basePageInfo), { startCursor: ((_a = edges === null || edges === void 0 ? void 0 : edges[0]) === null || _a === void 0 ? void 0 : _a.cursor) ? edges[0].cursor : null, endCursor: (_c = (_b = edges === null || edges === void 0 ? void 0 : edges[edges.length - 1]) === null || _b === void 0 ? void 0 : _b.cursor) !== null && _c !== void 0 ? _c : null })); }))); }; const connectionResult = withSource(root, formattedArgs, { get nodes() { return (0, plugin_1.completeValue)(resolveEdgesAndNodes(), (o) => o.nodes); }, get edges() { return (0, plugin_1.completeValue)(resolveEdgesAndNodes(), (o) => o.edges); }, get pageInfo() { return resolvePageInfo(); }, }); if (pluginConfig.extendConnection) { Object.keys(pluginConfig.extendConnection).forEach((connectionField) => { var _a; const resolve = (_a = fieldConfig[connectionField]) !== null && _a !== void 0 ? _a : graphql_1.defaultFieldResolver; Object.defineProperty(connectionResult, connectionField, { value: (args, ctx, info) => { return resolve(root, Object.assign(Object.assign({}, formattedArgs), args), ctx, info); }, }); }); } return connectionResult; }; } exports.makeResolveFn = makeResolveFn; function wrapEdge(pluginConfig, fieldConfig, formattedArgs, edgeParentType) { const edge = withArgs(formattedArgs, edgeParentType); if (pluginConfig.extendEdge) { Object.keys(pluginConfig.extendEdge).forEach((edgeField) => { var _a, _b; const resolve = (_b = (_a = fieldConfig.edgeFields) === null || _a === void 0 ? void 0 : _a[edgeField]) !== null && _b !== void 0 ? _b : graphql_1.defaultFieldResolver; Object.defineProperty(edge, edgeField, { value: (args, ctx, info) => { return resolve(edge, Object.assign(Object.assign({}, formattedArgs), args), ctx, info); }, }); }); } return edge; } /** * Adds __connectionArgs to the object representing the Connection type, so it can be accessed by other fields * in the top level * * @param args * @param connectionParentType */ function withArgs(args, connectionParentType) { Object.defineProperty(connectionParentType, '__connectionArgs', { value: args, enumerable: false, }); return connectionParentType; } /** * Adds __connectionSource to the object representing the Connection type, so it can be accessed by other * fields in the top level * * @param args * @param connectionParentType */ function withSource(source, args, connectionParentType) { Object.defineProperty(connectionParentType, '__connectionSource', { value: source, enumerable: false, }); return withArgs(args, connectionParentType); } /** Takes __connectionArgs from the source object and merges with the args provided by the */ function mergeArgs(obj, fieldArgs) { return Object.assign(Object.assign({}, obj.__connectionArgs), fieldArgs); } /** * Takes a "builder", and a function which takes a builder, and ensures that all fields defined within that * function invocation are provided the __connectionArgs defined by the connection */ function provideArgs(block, fn) { const fieldDef = block.field; block.field = function (...args) { let config = args.length === 2 ? Object.assign({ name: args[0] }, args[1]) : args[0]; const { resolve = graphql_1.defaultFieldResolver } = config; fieldDef.call(this, Object.assign(Object.assign({}, config), { resolve(root, args, ctx, info) { return resolve(root, mergeArgs(root, args), ctx, info); } })); }; fn(); block.field = fieldDef; } function provideSourceAndArgs(block, fn) { const fieldDef = block.field; block.field = function (...args) { let config = args.length === 2 ? Object.assign({ name: args[0] }, args[1]) : args[0]; const { resolve = graphql_1.defaultFieldResolver } = config; fieldDef.call(this, Object.assign(Object.assign({}, config), { resolve(root, args, ctx, info) { return resolve(root.__connectionSource, mergeArgs(root, args), ctx, info); } })); }; fn(); block.field = fieldDef; } function iterateNodes(nodes, args, cb) { // If we want the first N of an array of nodes, it's pretty straightforward. if (typeof args.first === 'number') { const len = Math.min(args.first, nodes.length); for (let i = 0; i < len; i++) { cb(nodes[i], i); } } else if (typeof args.last === 'number') { const len = Math.min(args.last, nodes.length); for (let i = 0; i < len; i++) { cb(nodes[i], i); } } else { // Only happens if we have a custom validateArgs that ignores first/last for (let i = 0; i < nodes.length; i++) { cb(nodes[i], i); } } } function defaultPageInfoFromNodes(nodes, args) { return { hasNextPage: defaultHasNextPage(nodes, args), hasPreviousPage: defaultHasPreviousPage(nodes, args), }; } function defaultHasNextPage(nodes, args) { // If we're paginating forward, and we don't have an "after", we'll assume that we don't have // a previous page, otherwise we will assume we have one, unless the after cursor === "0". if (typeof args.first === 'number') { return nodes.length > args.first; } // If we're paginating backward, and there are as many results as we asked for, then we'll assume // that we have a previous page if (typeof args.last === 'number') { if (args.before && args.before !== '0') { return true; } return false; } /* istanbul ignore next */ throw new Error('Unreachable'); } /** A sensible default for determining "previous page". */ function defaultHasPreviousPage(nodes, args) { // If we're paginating forward, and we don't have an "after", we'll assume that we don't have // a previous page, otherwise we will assume we have one, unless the after cursor === "0". if (typeof args.first === 'number') { if (args.after && args.after !== '0') { return true; } return false; } // If we're paginating backward, and there are as many results as we asked for, then we'll assume // that we have a previous page if (typeof args.last === 'number') { return nodes.length >= args.last; } /* istanbul ignore next */ throw new Error('Unreachable'); } const CURSOR_PREFIX = 'cursor:'; // Assumes we're only paginating in one direction. function defaultCursorFromNode(node, args, ctx, info, { index, nodes }) { let cursorIndex = index; // If we're paginating forward, assume we're incrementing from the offset provided via "after", // e.g. [0...20] (first: 5, after: "cursor:5") -> [cursor:6, cursor:7, cursor:8, cursor:9, cursor: 10] if (typeof args.first === 'number') { if (args.after) { const offset = parseInt(args.after, 10); cursorIndex = offset + index + 1; } } // If we're paginating backward, assume we're working backward from the assumed length // e.g. [0...20] (last: 5, before: "cursor:20") -> [cursor:15, cursor:16, cursor:17, cursor:18, cursor:19] if (typeof args.last === 'number') { if (args.before) { const offset = parseInt(args.before, 10); const len = Math.min(nodes.length, args.last); cursorIndex = offset - len + index; } else { /* istanbul ignore next */ throw new Error('Unreachable'); } } return `${CURSOR_PREFIX}${cursorIndex}`; } const getTypeNames = (fieldName, parentTypeName, fieldConfig, pluginConfig) => { const targetTypeName = typeof fieldConfig.type === 'string' ? fieldConfig.type : fieldConfig.type.name; // If we have changed the config specific to this field, on either the connection, // edge, or page info, then we need a custom type for the connection & edge. let connectionName; if (fieldConfig.getConnectionName) { connectionName = fieldConfig.getConnectionName(fieldName, parentTypeName); } else if (pluginConfig.getConnectionName) { connectionName = pluginConfig.getConnectionName(fieldName, parentTypeName); } else if (isConnectionFieldExtended(fieldConfig)) { connectionName = `${parentTypeName}${upperFirst(fieldName)}_Connection`; } else { connectionName = `${pluginConfig.typePrefix || ''}${targetTypeName}Connection`; } // If we have modified the "edge" at all, then we need let edgeName; if (fieldConfig.getEdgeName) { edgeName = fieldConfig.getEdgeName(fieldName, parentTypeName); } else if (pluginConfig.getEdgeName) { edgeName = pluginConfig.getEdgeName(fieldName, parentTypeName); } else if (isEdgeFieldExtended(fieldConfig)) { edgeName = `${parentTypeName}${upperFirst(fieldName)}_Edge`; } else { edgeName = `${pluginConfig.typePrefix || ''}${targetTypeName}Edge`; } return { edgeName, targetTypeName, connectionName, }; }; const isConnectionFieldExtended = (fieldConfig) => { if (fieldConfig.extendConnection || isEdgeFieldExtended(fieldConfig)) { return true; } return false; }; const isEdgeFieldExtended = (fieldConfig) => { if (fieldConfig.extendEdge || fieldConfig.cursorType) { return true; } return false; }; const upperFirst = (fieldName) => { return fieldName.slice(0, 1).toUpperCase().concat(fieldName.slice(1)); }; // Add some sanity checking beyond the normal type checks. const assertCorrectConfig = (typeName, fieldName, pluginConfig, fieldConfig) => { if (typeof fieldConfig.nodes !== 'function' && typeof fieldConfig.resolve !== 'function') { console.error(new Error(`Nexus Connection Plugin: Missing nodes or resolve property for ${typeName}.${fieldName}`)); } (0, utils_1.eachObj)(pluginConfig.extendConnection || {}, (val, key) => { if (typeof fieldConfig[key] !== 'function' && val.requireResolver !== false) { console.error(new Error(`Nexus Connection Plugin: Missing ${key} resolver property for ${typeName}.${fieldName}. Set requireResolver to "false" on the field config if you do not need a resolver.`)); } }); (0, utils_1.eachObj)(pluginConfig.extendEdge || {}, (val, key) => { var _a; if (typeof ((_a = fieldConfig.edgeFields) === null || _a === void 0 ? void 0 : _a[key]) !== 'function' && val.requireResolver !== false) { console.error(new Error(`Nexus Connection Plugin: Missing edgeFields.${key} resolver property for ${typeName}.${fieldName}. Set requireResolver to "false" on the edge field config if you do not need a resolver.`)); } }); }; function defaultValidateArgs(args = {}, info) { if (!(args.first || args.first === 0) && !(args.last || args.last === 0)) { throw new Error(`The ${info.parentType}.${info.fieldName} connection field requires a "first" or "last" argument`); } if (args.first && args.last) { throw new Error(`The ${info.parentType}.${info.fieldName} connection field requires a "first" or "last" argument, not both`); } if (args.first && args.before) { throw new Error(`The ${info.parentType}.${info.fieldName} connection field does not allow a "before" argument with "first"`); } if (args.last && args.after) { throw new Error(`The ${info.parentType}.${info.fieldName} connection field does not allow a "last" argument with "after"`); } } // Provided for use if you create a custom implementation and want to call the original. exports.connectionPlugin.defaultCursorFromNode = defaultCursorFromNode; exports.connectionPlugin.defaultValidateArgs = defaultValidateArgs; exports.connectionPlugin.defaultHasPreviousPage = defaultHasPreviousPage; exports.connectionPlugin.defaultHasNextPage = defaultHasNextPage; exports.connectionPlugin.base64Encode = base64Encode; exports.connectionPlugin.base64Decode = base64Decode; exports.connectionPlugin.CURSOR_PREFIX = CURSOR_PREFIX; //# sourceMappingURL=connectionPlugin.js.map