donobu
Version:
Create browser automations with an LLM agent and replay them as Playwright scripts.
117 lines • 5.08 kB
JavaScript
;
/**
* @fileoverview Application initialization and global error handling setup.
*
* This module configures comprehensive error handling for the Node.js application,
* including unhandled promise rejections, uncaught exceptions, and graceful
* shutdown handling for various system signals.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.installCrashHandlers = installCrashHandlers;
exports.installShutdownHandlers = installShutdownHandlers;
const fileUploadWorkerRegistry_1 = require("./persistence/files/fileUploadWorkerRegistry");
const JsonUtils_1 = require("./utils/JsonUtils");
const Logger_1 = require("./utils/Logger");
const SHUTDOWN_FLUSH_TIMEOUT_MS = 30_000;
/**
* Installs global error handlers that crash the process on unhandled errors.
*
* **This must only be called when running as a standalone server** (i.e. from
* the CLI entry point), never when the library is imported by external
* consumers. Registering process-wide exit handlers from a library hijacks the
* host application's error handling and can kill test runners or other
* long-lived processes unexpectedly.
*/
function installCrashHandlers() {
/**
* Handles unhandled promise rejections to prevent silent failures.
*
* When a Promise is rejected and no rejection handler is attached,
* this handler logs the error details and terminates the process
* to ensure failures are not ignored.
*
* @see {@link https://nodejs.org/api/process.html#event-unhandledrejection}
*/
process.on('unhandledRejection', (reason, promise) => {
const errorDetails = JSON.stringify(JsonUtils_1.JsonUtils.objectToJson({
reason: reason instanceof Error
? {
message: reason.message,
name: reason.name,
stack: reason.stack,
}
: reason,
promise: promise,
}), null, 2);
Logger_1.appLogger.error(`Unhandled Promise Rejection: ${errorDetails}`);
process.exit(1);
});
/**
* Handles uncaught exceptions to prevent the application from crashing silently.
*
* When an exception is thrown and not caught by any try-catch block,
* this handler logs the error details and terminates the process gracefully.
* This is a last resort error handler and indicates a serious application bug.
*
* @see {@link https://nodejs.org/api/process.html#event-uncaughtexception}
*/
process.on('uncaughtException', (error) => {
const errorDetails = JSON.stringify(JsonUtils_1.JsonUtils.objectToJson({
message: error.message,
name: error.name,
stack: error.stack,
}), null, 2);
Logger_1.appLogger.error(`Uncaught Exception: ${errorDetails}`);
process.exit(1);
});
}
/**
* Installs SIGTERM/SIGINT handlers that drain any in-flight file uploads
* (bounded by {@link SHUTDOWN_FLUSH_TIMEOUT_MS}) before exiting cleanly.
*
* **Intended primarily as a developer / CLI fallback.** In production the
* desktop app drives shutdown explicitly: it queries
* `GET /api/uploads/status`, surfaces the count to the user, and either
* (a) waits for the worker to drain on its own and then terminates the
* backend, or (b) terminates immediately on user request. In that path
* these handlers may not fire at all — terminations from the host are
* what they are.
*
* For `node dist/main.js` runs (developers, integration tests), Ctrl+C
* still benefits from a graceful drain so test artefacts don't pile up
* as `.uploading.<token>` markers.
*
* Same constraint as {@link installCrashHandlers}: only call this when
* running as a standalone server, never when imported as a library —
* process-wide signal handlers would hijack the host.
*
* Uploads that don't complete within the timeout stay on disk as
* `.uploading.<token>` markers; another Studio process will reclaim them
* once the claim goes stale (5 min default).
*/
function installShutdownHandlers() {
let shuttingDown = false;
const handle = (signal) => {
if (shuttingDown) {
return;
}
shuttingDown = true;
void (async () => {
Logger_1.appLogger.info(`Received ${signal}; draining file upload workers.`);
try {
const result = await (0, fileUploadWorkerRegistry_1.shutdownFileUploadWorkers)(SHUTDOWN_FLUSH_TIMEOUT_MS);
if (!result.drained) {
Logger_1.appLogger.warn(`File upload queue did not fully drain at shutdown; ` +
`${result.totalRemaining} upload(s) remain on disk and will resume on next start.`);
}
}
catch (err) {
Logger_1.appLogger.error(`Error draining file upload workers at shutdown: ${err.message}`);
}
process.exit(0);
})();
};
process.on('SIGTERM', handle);
process.on('SIGINT', handle);
}
//# sourceMappingURL=init.js.map