@mintlify/previewing
Version:
Preview Mintlify docs locally
151 lines (150 loc) • 6.01 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
import open from 'better-opn';
import express from 'express';
import { createServer } from 'http';
import { Server as SocketServer } from 'socket.io';
import { CMD_EXEC_PATH, NEXT_PUBLIC_PATH } from '../constants.js';
import { addLog, removeLastLog } from '../logging-state.js';
import { LaunchLog, UpdateLog } from '../logs.js';
import { maybeFixMissingWindowsEnvVar } from '../util.js';
import listener, { initializeImportCache } from './listener/index.js';
import { refreshTrackedReferencedFiles } from './listener/referencedFiles.js';
import { getLocalNetworkIp } from './network.js';
import { setupNext } from './setupNext.js';
const PROXY_RESPONSE_HEADER_DENYLIST = new Set([
'connection',
'keep-alive',
'proxy-authenticate',
'proxy-authorization',
'te',
'trailer',
'transfer-encoding',
'upgrade',
'content-encoding',
'content-length',
]);
const ASSISTANT_MESSAGE_JSON_LIMIT = '5mb';
function applyUpstreamProxyHeaders(upstream, res) {
const headers = upstream.headers;
const hasGetSetCookie = typeof headers.getSetCookie === 'function';
const setCookies = hasGetSetCookie ? headers.getSetCookie() : undefined;
headers.forEach((value, key) => {
const lower = key.toLowerCase();
if (PROXY_RESPONSE_HEADER_DENYLIST.has(lower))
return;
if (lower === 'set-cookie' && hasGetSetCookie)
return;
res.setHeader(key, value);
});
if (setCookies?.length) {
for (const cookie of setCookies) {
res.appendHeader('Set-Cookie', cookie);
}
}
}
export const run = async (argv) => {
const port = argv.port || '3000';
const currentPort = parseInt(port, 10) || 3000;
const app = express();
const server = createServer(app);
const io = new SocketServer(server);
const requestHandler = await setupNext();
const localIp = getLocalNetworkIp();
const apiUrl = argv.apiUrl ?? process.env.MINTLIFY_API_URL ?? 'http://localhost:5000';
const accessToken = argv.accessToken;
const subdomain = argv.subdomain;
process.env.MINTLIFY_CLI_ACCESS_TOKEN = accessToken ?? '';
process.env.MINTLIFY_API_URL = apiUrl;
if (subdomain)
process.env.MINTLIFY_CLI_SUBDOMAIN = subdomain;
// next-server is bugged, public files added after starting aren't served
app.use('/', express.static(NEXT_PUBLIC_PATH));
app.post('/_mintlify/api-public/search/:subdomain', express.json(), async (req, res) => {
const targetSubdomain = subdomain ?? req.params.subdomain;
try {
const upstream = await fetch(`${apiUrl}/api/cli/${targetSubdomain}/search`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),
},
body: JSON.stringify(req.body),
});
const data = await upstream.json();
res.status(upstream.status).json(data);
}
catch {
res.status(502).json({ error: 'search proxy failed' });
}
});
app.post('/_mintlify/api-public/assistant/:subdomain/message', express.json({ limit: ASSISTANT_MESSAGE_JSON_LIMIT }), async (req, res) => {
const targetSubdomain = subdomain ?? req.params.subdomain;
try {
const upstream = await fetch(`${apiUrl}/api/cli/${targetSubdomain}/assistant/message`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),
},
body: JSON.stringify(req.body),
});
res.status(upstream.status);
applyUpstreamProxyHeaders(upstream, res);
if (upstream.body) {
const reader = upstream.body.getReader();
const pump = async () => {
const { done, value } = await reader.read();
if (done) {
res.end();
return;
}
res.write(value);
return pump();
};
await pump();
}
else {
res.end();
}
}
catch {
if (!res.headersSent) {
res.status(502).json({ error: 'assistant proxy failed' });
}
else {
res.destroy();
}
}
});
app.all('*', (req, res) => requestHandler(req, res));
const onChange = () => {
io.emit('reload');
};
server.listen(currentPort, () => {
removeLastLog();
if (argv.needsUpdate) {
addLog(_jsx(UpdateLog, { updateCommand: `${argv.packageName} update` }));
}
addLog(_jsx(LaunchLog, { localUrl: `http://localhost:${port}`, networkUrl: localIp ? `http://${localIp}:${port}` : undefined }));
/**
* We're running into a known bug with the `open` package, where Windows machines error out because process.env.SYSTEMROOT is not set:
* https://github.com/sindresorhus/open/issues/292
*
* Let's use the same workaround that this project did:
* https://github.com/sanity-io/sanity/pull/4221/files#diff-aeb574e1becf61f21fdf87fbea709669c93d604d660dad4b0f9e24527a2fb54bR256-R262
*/
maybeFixMissingWindowsEnvVar();
if (argv.open) {
void open(`http://localhost:${port}`);
}
// exit with successful status
const onExit = () => {
process.exit(0);
};
process.on('SIGINT', onExit);
process.on('SIGTERM', onExit);
});
await initializeImportCache(CMD_EXEC_PATH, argv.fileImportsMap);
await refreshTrackedReferencedFiles();
listener(onChange);
};