graphile-build
Version:
Build a GraphQL schema from plugins
177 lines • 9.41 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodePlugin = exports.NODE_ID_HANDLER_BY_TYPE_NAME = exports.NODE_ID_CODECS = void 0;
require("graphile-config");
const grafast_1 = require("grafast");
const utils_ts_1 = require("../utils.js");
/**
* @internal
*/
exports.NODE_ID_CODECS = Symbol("nodeIdCodecs");
/**
* @internal
*/
exports.NODE_ID_HANDLER_BY_TYPE_NAME = Symbol("nodeIdHandlerByTypeName");
exports.NodePlugin = {
name: "NodePlugin",
version: "1.0.0",
description: `Adds the interfaces required to support the GraphQL Global Object Identification Specification`,
inflection: {
add: {
nodeIdFieldName() {
return "id";
},
},
},
schema: {
hooks: {
build(build) {
const nodeIdCodecs = Object.create(null);
const nodeIdHandlerByTypeName = Object.create(null);
// Add the 'raw' encoder
nodeIdCodecs.raw = (0, utils_ts_1.EXPORTABLE)((markSyncAndSafe) => ({
name: "raw",
encode: markSyncAndSafe(function rawEncode(value) {
return typeof value === "string" ? value : null;
}),
decode: markSyncAndSafe(function rawDecode(value) {
return typeof value === "string" ? value : null;
}),
}), [grafast_1.markSyncAndSafe], "rawNodeIdCodec");
return build.extend(build, {
/** @internal */
[exports.NODE_ID_CODECS]: nodeIdCodecs,
/** @internal */
[exports.NODE_ID_HANDLER_BY_TYPE_NAME]: nodeIdHandlerByTypeName,
getNodeIdHandlerByTypeName() {
return nodeIdHandlerByTypeName;
},
registerNodeIdCodec(codec) {
const codecName = codec.name;
if (nodeIdCodecs[codecName]) {
throw new Error(`Node ID codec '${codecName}' is already registered`);
}
nodeIdCodecs[codecName] = codec;
},
getNodeIdCodec(codecName) {
const codec = nodeIdCodecs[codecName];
if (!codec) {
throw new Error(`Could not find Node ID codec '${codecName}'`);
}
return codec;
},
registerNodeIdHandler(handler) {
const { typeName } = handler;
if (nodeIdHandlerByTypeName[typeName]) {
throw new Error(`Node ID handler for type '${typeName}' already registered`);
}
const details = build.getTypeMetaByName(typeName);
if (!details) {
throw new Error(`Attempted to register Node ID handler for type '${typeName}', but that type isn't registered (yet)`);
}
if (details.Constructor !== build.graphql.GraphQLObjectType) {
throw new Error(`Attempted to register Node ID handler for type '${typeName}', but that isn't an object type! (constructor: ${details.Constructor.name})`);
}
(0, utils_ts_1.exportNameHint)(handler, `nodeIdHandler_${typeName}`);
nodeIdHandlerByTypeName[typeName] = handler;
},
getNodeIdHandler(typeName) {
return nodeIdHandlerByTypeName[typeName];
},
getNodeTypeNames() {
return Object.keys(nodeIdHandlerByTypeName);
},
}, "Adding helpers from NodePlugin");
},
init(_, build) {
const { EXPORTABLE, graphql: { GraphQLNonNull, GraphQLID }, grafast: { access, lambda }, } = build;
const nodeTypeName = build.inflection.builtin("Node");
const nodeIdFieldName = build.inflection.nodeIdFieldName();
build.registerInterfaceType(nodeTypeName, {}, () => {
const nodeIdHandlerByTypeName = build.getNodeIdHandlerByTypeName();
const decodeNodeId = EXPORTABLE((makeDecodeNodeId, nodeIdHandlerByTypeName) => makeDecodeNodeId(Object.values(nodeIdHandlerByTypeName)), [grafast_1.makeDecodeNodeId, nodeIdHandlerByTypeName]);
const muteWarnings = build.options.muteWarnings ?? false;
const displayWarning = grafast_1.isDev && !muteWarnings;
const findTypeNameMatch = EXPORTABLE((displayWarning, inspect, nodeIdHandlerByTypeName) => function findTypeNameMatch(specifier) {
if (!specifier)
return null;
for (const [typeName, typeSpec] of Object.entries(nodeIdHandlerByTypeName)) {
const value = specifier[typeSpec.codec.name];
if (value != null && typeSpec.match(value)) {
return typeName;
}
}
if (displayWarning) {
console.warn(`Could not find a type that matched the specifier '${inspect(specifier)}'`);
}
return null;
}, [displayWarning, grafast_1.inspect, nodeIdHandlerByTypeName]);
return {
description: build.wrapDescription("An object with a globally unique `ID`.", "type"),
fields: {
[nodeIdFieldName]: {
description: build.wrapDescription("A globally unique identifier. Can be used in various places throughout the system to identify this single value.", "field"),
type: new GraphQLNonNull(GraphQLID),
},
},
planType: EXPORTABLE((access, decodeNodeId, findTypeNameMatch, lambda, nodeIdHandlerByTypeName) => function planType($nodeId) {
const $specifier = decodeNodeId($nodeId);
const $__typename = lambda($specifier, findTypeNameMatch, true);
return {
$__typename,
planForType(type) {
const spec = nodeIdHandlerByTypeName[type.name];
if (spec) {
return spec.get(spec.getSpec(access($specifier, [spec.codec.name])));
}
else {
throw new Error(`Failed to find handler for ${type.name}`);
}
},
};
}, [
access,
decodeNodeId,
findTypeNameMatch,
lambda,
nodeIdHandlerByTypeName,
]),
};
}, "Node interface from NodePlugin");
return _;
},
// Add the 'node' field to the root Query type
GraphQLObjectType_fields(fields, build, context) {
const { scope: { isRootQuery }, fieldWithHooks, } = context;
if (!isRootQuery) {
return fields;
}
const { getTypeByName, extend, graphql: { GraphQLNonNull, GraphQLID }, inflection, } = build;
const nodeIdFieldName = build.inflection.nodeIdFieldName();
const nodeType = getTypeByName(inflection.builtin("Node"));
if (!nodeType) {
return fields;
}
return extend(fields, {
node: fieldWithHooks({
fieldName: "node",
isRootNodeField: true,
}, () => ({
description: build.wrapDescription("Fetches an object given its globally unique `ID`.", "field"),
type: nodeType,
args: {
[nodeIdFieldName]: {
description: build.wrapDescription("The globally unique `ID`.", "arg"),
type: new GraphQLNonNull(GraphQLID),
},
},
plan: (0, utils_ts_1.EXPORTABLE)((nodeIdFieldName) => function plan(_$root, fieldArgs) {
return fieldArgs.getRaw(nodeIdFieldName);
}, [nodeIdFieldName]),
})),
}, `Adding Relay Global Object Identification support to the root Query via 'node' and '${nodeIdFieldName}' fields`);
},
},
},
};
//# sourceMappingURL=NodePlugin.js.map