@chillicream/nitro-server-adapter-plugin
Version:
Server Adapter Plugin for Nitro GraphQL IDE
188 lines (183 loc) • 7.07 kB
JavaScript
/*!
* @license ChilliCream License 1.0
*
* Copyright (c) ChilliCream, Inc.
*
* This source code is licensed under the ChilliCream License 1.0 found in the
* LICENSE file in the root directory of this source tree.
*/
;
var fs = require('node:fs');
var module$1 = require('node:module');
var path = require('node:path');
const Defaults = {
CDN_URL: "https://cdn.chillicream.com/web",
VERSION: "latest",
EMBEDDED_ID: "@chillicream/nitro-embedded",
CONFIG: {
mode: "cdn",
},
OPTIONS: {
useBrowserUrlAsEndpoint: true,
},
};
const Paths = {
ROOT: "/",
INDEX: "/index.html",
CONFIG: "/nitro-config.json",
};
function resolveUrl(target) {
return typeof target === "string"
? target
: `${target?.baseUrl || Defaults.CDN_URL}/${target?.version || Defaults.VERSION}`;
}
function resolvePath(target) {
const require$1 = module$1.createRequire(require('url').pathToFileURL(__filename).toString());
return path.dirname(require$1.resolve(target || Defaults.EMBEDDED_ID));
}
function resolveConfig(config) {
return Object.assign({}, Defaults.CONFIG, config);
}
function resolveOptions(options) {
return Object.assign({}, Defaults.OPTIONS, options);
}
function useNitro(config) {
const resolvedConfig = resolveConfig(config);
const basePath = resolveBasePath(resolvedConfig.path);
return {
onRequest({ request, url, fetchAPI, endResponse }) {
if (request.method === "GET" || request.method === "HEAD") {
const acceptHeader = request.headers.get("accept");
// handle root request
if (url.pathname === basePath &&
(acceptHeader?.includes("*/*") || acceptHeader?.includes("text/html"))) {
const [path, query] = request.url.split("?");
if (!path.endsWith("/")) {
const newPath = path + "/" + (query?.length ? "?" + query : "");
return endResponse(fetchAPI.Response.redirect(newPath, 301));
}
}
const configPath = basePath + Paths.CONFIG;
// handle config request
if (url.pathname === configPath &&
(acceptHeader?.includes("*/*") ||
acceptHeader?.includes("application/json"))) {
const resolvedOptions = resolveOptions(resolvedConfig.options);
const responseMetadata = {
status: 200,
statusText: "OK",
headers: {
"Content-Type": "application/json",
"Content-Length": JSON.stringify(resolvedOptions).length + "",
},
};
if (request.method === "GET") {
return endResponse(fetchAPI.Response.json(resolvedOptions, responseMetadata));
}
else {
return endResponse(new fetchAPI.Response(undefined, responseMetadata));
}
}
const pathname = url.pathname.substring(basePath.length);
if (resolvedConfig.mode === "cdn") {
const baseUrl = resolveUrl(resolvedConfig.target);
const requestHeaders = new Headers(request.headers);
requestHeaders.delete("host");
requestHeaders.delete("accept-encoding");
return fetchAPI
.fetch(baseUrl + pathname, {
method: request.method,
headers: requestHeaders,
body: request.body,
mode: request.mode,
credentials: request.credentials,
cache: request.cache,
redirect: request.redirect,
referrer: request.referrer,
referrerPolicy: request.referrerPolicy,
integrity: request.integrity,
keepalive: request.keepalive,
// NOTE: if uncommented, leads to an unhandled exception in the
// fetchNodeHttp module; needs to be investigated!
//signal: request.signal,
})
.then(endResponse, () => endResponse(fetchAPI.Response.error()));
}
else {
const baseFilePath = resolvePath(resolvedConfig.target);
return serverStatic(pathname, baseFilePath, fetchAPI, endResponse);
}
}
},
};
}
function resolveBasePath(path) {
return path?.length
? ensureStartsWithSlash(ensureEndsNotWithSlash(path))
: "/graphql";
}
function ensureStartsWithSlash(path) {
return path.startsWith("/") ? path : "/" + path;
}
function ensureEndsNotWithSlash(path) {
return path.endsWith("/") && path !== Paths.ROOT
? path.substring(0, path.length - 1)
: path;
}
function serverStatic(path, baseFilePath, fetchAPI, endResponse) {
function write(filePath, contentType) {
try {
const data = fs.readFileSync(filePath);
return endResponse(new fetchAPI.Response(data, {
status: 200,
statusText: "OK",
headers: {
"Content-Type": contentType,
"Content-Length": data.length + "",
},
}));
}
catch { }
}
if (path.endsWith(".css")) {
return write(baseFilePath + path, "text/css");
}
if (path.endsWith(".html")) {
return write(baseFilePath + path, "text/html");
}
if (path.endsWith(".ico")) {
return write(baseFilePath + path, "image/x-icon");
}
if (path.endsWith(".js")) {
return write(baseFilePath + path, "application/javascript");
}
if (path.endsWith(".json")) {
return write(baseFilePath + path, "application/json");
}
if (path.endsWith(".png")) {
return write(baseFilePath + path, "image/png");
}
if (path.endsWith(".svg")) {
return write(baseFilePath + path, "image/svg+xml");
}
if (path.endsWith(".ttf")) {
return write(baseFilePath + path, "font/ttf");
}
if (path.endsWith(".txt")) {
return write(baseFilePath + path, "text/plain");
}
if (path.endsWith(".webmanifest")) {
return write(baseFilePath + path, "application/manifest+json");
}
if (path.endsWith(".woff2")) {
return write(baseFilePath + path, "font/woff2");
}
if (path.endsWith(".woff2")) {
return write(baseFilePath + path, "font/woff2");
}
if (path === Paths.ROOT) {
return write(baseFilePath + "/index.html", "text/html");
}
}
exports.resolveBasePath = resolveBasePath;
exports.useNitro = useNitro;