UNPKG

@pothos/plugin-errors

Version:

A Pothos plugin for adding typed errors into your schema

295 lines (294 loc) 14.8 kB
import './global-types.js'; import SchemaBuilder, { BasePlugin, PothosSchemaError, sortClasses, typeBrandKey, unwrapOutputFieldType } from '@pothos/core'; export * from './types.js'; const pluginName = "errors"; export default pluginName; export function capitalize(s) { return `${s.slice(0, 1).toUpperCase()}${s.slice(1)}`; } export const defaultGetResultName = ({ parentTypeName, fieldName }) => `${parentTypeName}${capitalize(fieldName)}Success`; export const defaultGetListItemResultName = ({ parentTypeName, fieldName }) => `${parentTypeName}${capitalize(fieldName)}ItemSuccess`; export const defaultGetUnionName = ({ parentTypeName, fieldName }) => `${parentTypeName}${capitalize(fieldName)}Result`; export const defaultGetListItemUnionName = ({ parentTypeName, fieldName }) => `${parentTypeName}${capitalize(fieldName)}ItemResult`; export const unwrapError = Symbol.for("Pothos.unwrapErrors"); function createErrorProxy(target, ref, state) { return new Proxy(target, { get(err, val, receiver) { if (val === unwrapError) { return () => { state.wrapped = false; }; } if (val === typeBrandKey) { return ref; } return Reflect.get(err, val, receiver); }, getPrototypeOf(err) { const proto = Reflect.getPrototypeOf(err); if (!state.wrapped || !proto) { return proto; } return createErrorProxy(proto, ref, state); } }); } const errorTypeMap = new WeakMap(); export class PothosErrorsPlugin extends BasePlugin { wrapIsTypeOf(isTypeOf) { if (isTypeOf) { return (parent, context, info) => { if (typeof parent === "object" && parent) { var _parent_unwrapError; (_parent_unwrapError = parent[unwrapError]) === null || _parent_unwrapError === void 0 ? void 0 : _parent_unwrapError.call(parent); } return isTypeOf(parent, context, info); }; } return isTypeOf; } onOutputFieldConfig(fieldConfig) { const errorOptions = fieldConfig.pothosOptions.errors; const itemErrorOptions = fieldConfig.pothosOptions.itemErrors; const errorBuilderOptions = this.builder.options.errors; if (!errorOptions && !itemErrorOptions) { return fieldConfig; } const parentTypeName = this.buildCache.getTypeConfig(fieldConfig.parentType).name; var _itemErrorOptions_types, _errorBuilderOptions_defaultTypes; const itemErrorTypes = itemErrorOptions && sortClasses([ ...new Set([ ...(_itemErrorOptions_types = itemErrorOptions === null || itemErrorOptions === void 0 ? void 0 : itemErrorOptions.types) !== null && _itemErrorOptions_types !== void 0 ? _itemErrorOptions_types : [], ...(_errorBuilderOptions_defaultTypes = errorBuilderOptions === null || errorBuilderOptions === void 0 ? void 0 : errorBuilderOptions.defaultTypes) !== null && _errorBuilderOptions_defaultTypes !== void 0 ? _errorBuilderOptions_defaultTypes : [] ]) ]); var _errorOptions_types, _errorBuilderOptions_defaultTypes1; const errorTypes = errorOptions && sortClasses([ ...new Set([ ...(_errorOptions_types = errorOptions === null || errorOptions === void 0 ? void 0 : errorOptions.types) !== null && _errorOptions_types !== void 0 ? _errorOptions_types : [], ...(_errorBuilderOptions_defaultTypes1 = errorBuilderOptions === null || errorBuilderOptions === void 0 ? void 0 : errorBuilderOptions.defaultTypes) !== null && _errorBuilderOptions_defaultTypes1 !== void 0 ? _errorBuilderOptions_defaultTypes1 : [] ]) ]); let resultType = fieldConfig.pothosOptions.type; if (itemErrorOptions) { if (!Array.isArray(fieldConfig.pothosOptions.type) || fieldConfig.type.kind !== "List") { throw new PothosSchemaError(`Field ${parentTypeName}.${fieldConfig.name} must return a list when 'itemErrors' is set`); } const itemFieldType = fieldConfig.type.type; const itemType = fieldConfig.pothosOptions.type[0]; resultType = [ this.createResultType(parentTypeName, fieldConfig.name, itemType, itemFieldType, itemErrorOptions, `Field ${parentTypeName}.${fieldConfig.name} list items must be an ObjectType when 'directResult' is set to true`, defaultGetListItemResultName, defaultGetListItemUnionName, errorBuilderOptions === null || errorBuilderOptions === void 0 ? void 0 : errorBuilderOptions.defaultItemResultOptions, errorBuilderOptions === null || errorBuilderOptions === void 0 ? void 0 : errorBuilderOptions.defaultItemUnionOptions) ]; if (!errorOptions) { return { ...fieldConfig, extensions: { ...fieldConfig.extensions, pothosItemErrors: itemErrorTypes }, type: { ...fieldConfig.type, type: { kind: "Union", ref: resultType[0], nullable: fieldConfig.type.type.nullable } } }; } } const unionType = this.createResultType(parentTypeName, fieldConfig.name, resultType, fieldConfig.type, errorOptions, `Field ${parentTypeName}.${fieldConfig.name} must return an ObjectType when 'directResult' is set to true`, defaultGetResultName, defaultGetUnionName, errorBuilderOptions === null || errorBuilderOptions === void 0 ? void 0 : errorBuilderOptions.defaultResultOptions, errorBuilderOptions === null || errorBuilderOptions === void 0 ? void 0 : errorBuilderOptions.defaultUnionOptions); return { ...fieldConfig, extensions: { ...fieldConfig.extensions, pothosErrors: errorTypes, pothosItemErrors: itemErrorTypes }, type: { kind: "Union", ref: unionType, nullable: fieldConfig.type.nullable } }; } wrapResolve(resolver, fieldConfig) { var _fieldConfig_extensions, _fieldConfig_extensions1; const pothosErrors = (_fieldConfig_extensions = fieldConfig.extensions) === null || _fieldConfig_extensions === void 0 ? void 0 : _fieldConfig_extensions.pothosErrors; const pothosItemErrors = (_fieldConfig_extensions1 = fieldConfig.extensions) === null || _fieldConfig_extensions1 === void 0 ? void 0 : _fieldConfig_extensions1.pothosItemErrors; if (!pothosErrors && !pothosItemErrors) { return resolver; } return async (source, args, context, info) => { if (fieldConfig.kind === "Subscription" && errorTypeMap.has(source)) { return source; } try { const result = await resolver(source, args, context, info); if (pothosItemErrors && result && typeof result === "object" && Symbol.iterator in result) { return yieldErrors(result, pothosItemErrors); } if (pothosItemErrors && result && typeof result === "object" && Symbol.asyncIterator in result) { console.log(result, yieldAsyncErrors); return yieldAsyncErrors(result, pothosItemErrors); } return result; } catch (error) { return wrapOrThrow(error, pothosErrors !== null && pothosErrors !== void 0 ? pothosErrors : []); } }; } wrapSubscribe(subscribe, fieldConfig) { var _fieldConfig_extensions; const pothosErrors = (_fieldConfig_extensions = fieldConfig.extensions) === null || _fieldConfig_extensions === void 0 ? void 0 : _fieldConfig_extensions.pothosErrors; if (!pothosErrors) { return subscribe; } return (...args) => { async function* yieldSubscribeErrors() { try { const iter = await subscribe(...args); if (!iter) { return iter; } for await (const value of iter) { yield value; } } catch (error) { yield wrapOrThrow(error, pothosErrors !== null && pothosErrors !== void 0 ? pothosErrors : []); } } return yieldSubscribeErrors(); }; } createResultType(parentTypeName, fieldName, type, fieldType, errorOptions, directResultError, defaultResultName, defaultUnionName, builderResultOptions = {}, builderUnionOptions = {}) { const errorBuilderOptions = this.builder.options.errors; const { name: getResultName = defaultResultName, ...defaultResultOptions } = builderResultOptions !== null && builderResultOptions !== void 0 ? builderResultOptions : {}; const { name: getUnionName = defaultUnionName, ...defaultUnionOptions } = builderUnionOptions !== null && builderUnionOptions !== void 0 ? builderUnionOptions : {}; const { types = [], result: { name: resultName = getResultName({ parentTypeName, fieldName }), fields: resultFieldOptions, ...resultObjectOptions } = {}, union: { name: unionName = getUnionName({ parentTypeName, fieldName }), ...unionOptions } = {}, dataField: { name: dataFieldName = "data", ...dataField } = {} } = errorOptions; var _errorBuilderOptions_defaultTypes; const errorTypes = sortClasses([ ...new Set([ ...types, ...(_errorBuilderOptions_defaultTypes = errorBuilderOptions === null || errorBuilderOptions === void 0 ? void 0 : errorBuilderOptions.defaultTypes) !== null && _errorBuilderOptions_defaultTypes !== void 0 ? _errorBuilderOptions_defaultTypes : [] ]) ]); var _errorOptions_directResult, _ref; const directResult = (_ref = (_errorOptions_directResult = errorOptions.directResult) !== null && _errorOptions_directResult !== void 0 ? _errorOptions_directResult : errorBuilderOptions === null || errorBuilderOptions === void 0 ? void 0 : errorBuilderOptions.directResult) !== null && _ref !== void 0 ? _ref : false; const typeRef = unwrapOutputFieldType(fieldType); const typeName = this.builder.configStore.getTypeConfig(typeRef).name; return this.runUnique(resultName, () => { var _this_buildCache_getTypeConfig_extensions; let resultType; if (directResult && !Array.isArray(fieldType)) { resultType = type; const resultConfig = this.builder.configStore.getTypeConfig(resultType); if (resultConfig.graphqlKind !== "Object") { throw new PothosSchemaError(directResultError); } } else { resultType = this.builder.objectRef(resultName); resultType.implement({ ...defaultResultOptions, ...resultObjectOptions, fields: (t) => ({ ...resultFieldOptions === null || resultFieldOptions === void 0 ? void 0 : resultFieldOptions(t), [dataFieldName]: t.field({ ...dataField, type, nullable: fieldType.kind === "List" ? { items: fieldType.type.nullable, list: false } : false, resolve: (data) => data }) }) }); } const getDataloader = (_this_buildCache_getTypeConfig_extensions = this.buildCache.getTypeConfig(unwrapOutputFieldType(fieldType)).extensions) === null || _this_buildCache_getTypeConfig_extensions === void 0 ? void 0 : _this_buildCache_getTypeConfig_extensions.getDataloader; return this.builder.unionType(unionName, { types: [ ...errorTypes, resultType ], resolveType: (obj) => { var _errorTypeMap_get; return (_errorTypeMap_get = errorTypeMap.get(obj)) !== null && _errorTypeMap_get !== void 0 ? _errorTypeMap_get : resultType; }, ...defaultUnionOptions, ...unionOptions, extensions: { ...unionOptions.extensions, getDataloader, pothosIndirectInclude: { getType: () => typeName, path: directResult ? [] : [ { type: resultName, name: dataFieldName } ] } } }); }); } } SchemaBuilder.registerPlugin(pluginName, PothosErrorsPlugin, { v3: (options) => ({ errorOptions: undefined, errors: options === null || options === void 0 ? void 0 : options.errorOptions }) }); function wrapOrThrow(error, pothosErrors) { for (const errorType of pothosErrors) { if (error instanceof errorType) { const result = createErrorProxy(error, errorType, { wrapped: true }); errorTypeMap.set(result, errorType); return result; } } throw error; } function* yieldErrors(result, pothosErrors) { try { for (const item of result) { if (item instanceof Error) { yield wrapOrThrow(item, pothosErrors); } else { yield item; } } } catch (error) { yield wrapOrThrow(error, pothosErrors); } } async function* yieldAsyncErrors(result, pothosErrors) { try { for await (const item of result) { if (item instanceof Error) { yield wrapOrThrow(item, pothosErrors); } else { yield item; } } } catch (error) { yield wrapOrThrow(error, pothosErrors); } } //# sourceMappingURL=index.js.map