grafserv
Version:
A highly optimized server for GraphQL, powered by Grafast
235 lines • 9.57 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.H3Grafserv = void 0;
exports.grafserv = grafserv;
const node_stream_1 = require("node:stream");
const graphql_ws_1 = require("graphql-ws");
const h3_1 = require("h3");
const index_ts_1 = require("../../../index.js");
const utils_ts_1 = require("../../../utils.js");
function getDigest(event) {
const req = event.node.req;
const res = event.node.res;
return {
httpVersionMajor: req.httpVersionMajor,
httpVersionMinor: req.httpVersionMinor,
isSecure: (0, h3_1.getRequestProtocol)(event) === "https",
method: event.method,
path: event.path,
headers: (0, index_ts_1.processHeaders)((0, h3_1.getRequestHeaders)(event)),
getQueryParams() {
return (0, h3_1.getQuery)(event);
},
async getBody() {
const buffer = await (0, h3_1.readRawBody)(event, false);
if (!buffer) {
throw new Error("Failed to retrieve body from h3");
}
return {
type: "buffer",
buffer,
};
},
requestContext: {
h3v1: {
event,
},
node: {
req,
res,
},
},
};
}
class H3Grafserv extends index_ts_1.GrafservBase {
constructor(config) {
super(config);
}
/**
* @deprecated use handleGraphQLEvent instead
*/
async handleEvent(event) {
return this.handleGraphQLEvent(event);
}
async handleGraphQLEvent(event) {
const digest = getDigest(event);
const handlerResult = await this.graphqlHandler((0, index_ts_1.normalizeRequest)(digest), this.graphiqlHandler);
const result = await (0, index_ts_1.convertHandlerResultToResult)(handlerResult);
return this.send(event, result);
}
async handleGraphiqlEvent(event) {
const digest = getDigest(event);
const handlerResult = await this.graphiqlHandler((0, index_ts_1.normalizeRequest)(digest));
const result = await (0, index_ts_1.convertHandlerResultToResult)(handlerResult);
return this.send(event, result);
}
async handleGraphiqlStaticEvent(event) {
const digest = getDigest(event);
const handlerResult = await this.graphiqlStaticHandler((0, index_ts_1.normalizeRequest)(digest));
const result = await (0, index_ts_1.convertHandlerResultToResult)(handlerResult);
return this.send(event, result);
}
async handleEventStreamEvent(event) {
const digest = getDigest(event);
const handlerResult = {
type: "event-stream",
request: (0, index_ts_1.normalizeRequest)(digest),
dynamicOptions: this.dynamicOptions,
payload: this.makeStream(),
statusCode: 200,
};
const result = await (0, index_ts_1.convertHandlerResultToResult)(handlerResult);
return this.send(event, result);
}
async send(event, result) {
if (result === null) {
// 404
(0, h3_1.setResponseStatus)(event, 404);
return "¯\\_(ツ)_/¯";
}
switch (result.type) {
case "error": {
const { statusCode, headers } = result;
(0, h3_1.setResponseHeaders)(event, headers);
(0, h3_1.setResponseStatus)(event, statusCode);
// DEBT: mutating the error is probably bad form...
const errorWithStatus = Object.assign(result.error, {
status: statusCode,
});
throw errorWithStatus;
}
case "buffer": {
const { statusCode, headers, buffer } = result;
(0, h3_1.setResponseHeaders)(event, headers);
(0, h3_1.setResponseStatus)(event, statusCode);
return buffer;
}
case "json": {
const { statusCode, headers, json } = result;
(0, h3_1.setResponseHeaders)(event, headers);
(0, h3_1.setResponseStatus)(event, statusCode);
return json;
}
case "noContent": {
const { statusCode, headers } = result;
(0, h3_1.setResponseHeaders)(event, headers);
(0, h3_1.setResponseStatus)(event, statusCode);
return null;
}
case "bufferStream": {
const { statusCode, headers, lowLatency, bufferIterator } = result;
let bufferIteratorHandled = false;
try {
if (lowLatency) {
event.node.req.socket.setTimeout(0);
event.node.req.socket.setNoDelay(true);
event.node.req.socket.setKeepAlive(true);
}
(0, h3_1.setResponseHeaders)(event, headers);
(0, h3_1.setResponseStatus)(event, statusCode);
const stream = new node_stream_1.PassThrough();
(0, h3_1.sendStream)(event, stream).catch((e) => {
console.error("An error occured when streaming to h3:");
console.error(e);
});
// Fork off and convert bufferIterator to
try {
bufferIteratorHandled = true;
for await (const buffer of bufferIterator) {
stream.write(buffer);
}
}
finally {
stream.end();
}
}
catch (e) {
if (!bufferIteratorHandled) {
try {
if (bufferIterator.return) {
bufferIterator.return().then(null, utils_ts_1.noop);
}
else if (bufferIterator.throw) {
bufferIterator.throw(e).then(null, utils_ts_1.noop);
}
}
catch (e2) {
/* nom nom nom */
}
}
throw e;
}
return;
}
default: {
const never = result;
console.log("Unhandled:");
console.dir(never);
(0, h3_1.setResponseHeader)(event, "Content-Type", "text/plain");
(0, h3_1.setResponseStatus)(event, 501);
return "Server hasn't implemented this yet";
}
}
}
async addTo(app) {
const dynamicOptions = this.dynamicOptions;
const router = (0, h3_1.createRouter)();
app.use(router);
router.use(this.dynamicOptions.graphqlPath, (0, h3_1.eventHandler)((event) => this.handleGraphQLEvent(event)), this.dynamicOptions.graphqlOverGET ||
this.dynamicOptions.graphiqlOnGraphQLGET
? ["get", "post"]
: ["post"]);
if (this.resolvedPreset.grafserv?.websockets) {
app.use(this.dynamicOptions.graphqlPath, (0, h3_1.defineWebSocketHandler)(this.makeWsHandler()));
}
if (dynamicOptions.graphiql) {
router.get(this.dynamicOptions.graphiqlPath, (0, h3_1.eventHandler)((event) => this.handleGraphiqlEvent(event)));
}
if (dynamicOptions.watch) {
router.get(this.dynamicOptions.eventStreamPath, (0, h3_1.eventHandler)((event) => this.handleEventStreamEvent(event)));
}
if (dynamicOptions.graphiql) {
// Must come last, because wildcard
router.get(this.dynamicOptions.graphiqlStaticPath + "*", (0, h3_1.eventHandler)((event) => this.handleGraphiqlStaticEvent(event)));
}
}
makeWsHandler() {
const graphqlWsServer = (0, graphql_ws_1.makeServer)((0, index_ts_1.makeGraphQLWSConfig)(this));
const clients = new Map();
return {
open(peer) {
const client = {};
clients.set(peer, client);
const onClose = graphqlWsServer.opened({
protocol: peer.websocket.protocol ?? graphql_ws_1.GRAPHQL_TRANSPORT_WS_PROTOCOL, // will be validated
send(data) {
peer.send(data);
},
close(code, reason) {
return peer.close(code, reason); // there are protocol standard closures
},
onMessage(cb) {
client.handleMessage = cb;
},
}, { socket: peer.websocket, request: peer.request });
client.closed = onClose;
},
message(peer, message) {
return clients.get(peer)?.handleMessage?.(message.text());
},
close(peer, details) {
const client = clients.get(peer);
clients.delete(peer);
return client?.closed?.(details.code, details.reason);
},
error(peer, _error) {
clients.delete(peer);
},
};
}
}
exports.H3Grafserv = H3Grafserv;
function grafserv(config) {
return new H3Grafserv(config);
}
//# sourceMappingURL=index.js.map