@rivetkit/next-js
Version:
Next.js integration for RivetKit actors and client
160 lines (156 loc) • 5.15 kB
JavaScript
;Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// src/mod.ts
var _fs = require('fs');
var _path = require('path');
var _utils = require('rivetkit/utils');
// src/log.ts
var _log = require('rivetkit/log');
function logger() {
return _log.getLogger.call(void 0, "driver-next-js");
}
// src/mod.ts
var toNextHandler = (registry, inputConfig = {}) => {
inputConfig.disableDefaultServer = true;
inputConfig.runnerKind = "serverless";
if (process.env.NODE_ENV !== "production") {
logger().debug(
"detected development environment, auto-starting engine and auto-configuring serverless"
);
const publicUrl = _nullishCoalesce(_nullishCoalesce(process.env.NEXT_PUBLIC_SITE_URL, () => ( process.env.NEXT_PUBLIC_VERCEL_URL)), () => ( `http://127.0.0.1:${_nullishCoalesce(process.env.PORT, () => ( 3e3))}`));
inputConfig.runEngine = true;
inputConfig.autoConfigureServerless = {
url: `${publicUrl}/api/rivet`,
minRunners: 0,
maxRunners: 1e5,
requestLifespan: 300,
slotsPerRunner: 1,
metadata: { provider: "next-js" }
};
} else {
logger().debug(
"detected production environment, will not auto-start engine and auto-configure serverless"
);
}
inputConfig.noWelcome = true;
const { fetch } = registry.start(inputConfig);
const fetchWrapper = async (request, { params }) => {
const { all } = await params;
const newUrl = new URL(request.url);
newUrl.pathname = all.join("/");
if (process.env.NODE_ENV !== "development") {
const newReq = new Request(newUrl, request);
return await fetch(newReq);
} else {
return await handleRequestWithFileWatcher(request, newUrl, fetch);
}
};
return {
GET: fetchWrapper,
POST: fetchWrapper,
PUT: fetchWrapper,
PATCH: fetchWrapper,
HEAD: fetchWrapper,
OPTIONS: fetchWrapper
};
};
async function handleRequestWithFileWatcher(request, newUrl, fetch) {
var _a;
const mergedController = new AbortController();
const abortMerged = () => mergedController.abort();
(_a = request.signal) == null ? void 0 : _a.addEventListener("abort", abortMerged);
const watchIntervalId = watchRouteFile(mergedController);
request.signal.addEventListener("abort", () => {
logger().debug("clearing file watcher interval: request aborted");
clearInterval(watchIntervalId);
});
const newReq = new Request(newUrl, {
// Copy old request properties
method: request.method,
headers: request.headers,
body: request.body,
credentials: request.credentials,
cache: request.cache,
redirect: request.redirect,
referrer: request.referrer,
integrity: request.integrity,
// Override with new signal
signal: mergedController.signal,
// Required for streaming body
duplex: "half"
});
const response = await fetch(newReq);
if (response.body) {
const wrappedStream = waitForStreamFinish(response.body, () => {
logger().debug("clearing file watcher interval: stream finished");
clearInterval(watchIntervalId);
});
return new Response(wrappedStream, {
status: response.status,
statusText: response.statusText,
headers: response.headers
});
} else {
logger().debug("clearing file watcher interval: no response body");
clearInterval(watchIntervalId);
return response;
}
}
function watchRouteFile(abortController) {
logger().debug("starting file watcher");
const routePath = _path.join.call(void 0,
process.cwd(),
".next/server/app/api/rivet/[...all]/route.js"
);
let lastMtime = null;
const checkFile = () => {
logger().debug({ msg: "checking for file changes", routePath });
try {
if (!_fs.existsSync.call(void 0, routePath)) {
return;
}
const stats = _fs.statSync.call(void 0, routePath);
const mtime = stats.mtimeMs;
if (lastMtime !== null && mtime !== lastMtime) {
logger().info({ msg: "route file changed", routePath });
abortController.abort();
}
lastMtime = mtime;
} catch (err) {
logger().info({
msg: "failed to check for route file change",
err: _utils.stringifyError.call(void 0, err)
});
}
};
checkFile();
return setInterval(checkFile, 1e3);
}
function waitForStreamFinish(body, onFinish) {
const reader = body.getReader();
return new ReadableStream({
async start(controller) {
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
logger().debug("stream completed");
onFinish();
controller.close();
break;
}
controller.enqueue(value);
}
} catch (err) {
logger().debug("stream errored");
onFinish();
controller.error(err);
}
},
cancel() {
logger().debug("stream cancelled");
onFinish();
reader.cancel();
}
});
}
exports.toNextHandler = toNextHandler;
//# sourceMappingURL=mod.js.map