@storm-stack/core
Version:
A build toolkit and runtime used by Storm Software in TypeScript applications
169 lines (167 loc) • 5.42 kB
JavaScript
import { createLog } from './chunk-RDCOIWVB.js';
import { __name } from './chunk-43IZMM3W.js';
import { LogLevelLabel } from '@storm-software/config-tools/types';
import { findFileName } from '@stryke/path/file-path-fns';
import { defu } from 'defu';
import { Worker as Worker$1 } from 'jest-worker';
import { Transform } from 'node:stream';
var RESTARTED = Symbol("restarted");
var cleanupWorkers = /* @__PURE__ */ __name((worker) => {
for (const curWorker of worker._workerPool?._workers || []) {
curWorker._child?.kill("SIGINT");
}
}, "cleanupWorkers");
var Worker = class {
static {
__name(this, "Worker");
}
workerPath;
options;
#worker;
#log;
constructor(workerPath, options) {
this.workerPath = workerPath;
this.options = options;
const { timeout, onRestart, name, context, ...farmOptions } = this.options;
this.#log = createLog(name || workerPath);
let restartPromise;
let resolveRestartPromise;
let activeTasks = 0;
this.#worker = void 0;
process.on("exit", () => {
this.close();
});
const createWorker2 = /* @__PURE__ */ __name(() => {
this.#worker = new Worker$1(workerPath, {
...farmOptions,
forkOptions: {
...farmOptions.forkOptions,
env: {
...farmOptions.forkOptions?.env ?? {},
...process.env,
STORM_STACK_LOCAL: process.env.STORM_STACK_LOCAL || "0"
}
},
maxRetries: 0
});
restartPromise = new Promise((resolve) => {
resolveRestartPromise = resolve;
});
if (!farmOptions.enableWorkerThreads) {
for (const worker of this.#worker._workerPool?._workers || []) {
worker._child?.on("exit", (code, signal) => {
if ((code || signal && signal !== "SIGINT") && this.#worker) {
this.#log(LogLevelLabel.ERROR, `${this.options.name} worker exited with code: ${code} and signal: ${signal}`);
process.exit(code ?? 1);
}
});
worker._child?.on("message", ([, data]) => {
if (data && typeof data === "object" && "type" in data && data.type === "activity") {
onActivity();
}
});
}
}
let aborted = false;
const onActivityAbort = /* @__PURE__ */ __name(() => {
if (!aborted) {
this.options.onActivityAbort?.();
aborted = true;
}
}, "onActivityAbort");
const abortActivityStreamOnLog = new Transform({
transform(_chunk, _encoding, callback) {
onActivityAbort();
callback();
}
});
this.#worker.getStdout().pipe(abortActivityStreamOnLog);
this.#worker.getStderr().pipe(abortActivityStreamOnLog);
this.#worker.getStdout().pipe(process.stdout);
this.#worker.getStderr().pipe(process.stderr);
}, "createWorker");
createWorker2();
const onHanging = /* @__PURE__ */ __name(() => {
const worker = this.#worker;
if (!worker) {
return;
}
const resolve = resolveRestartPromise;
createWorker2();
this.#log(LogLevelLabel.WARN, `Sending SIGTERM signal to static worker due to timeout${timeout ? ` of ${timeout / 1e3} seconds` : ""}. Subsequent errors may be a result of the worker exiting.`);
void worker.end().then(() => {
resolve(RESTARTED);
});
}, "onHanging");
let hangingTimer = false;
const onActivity = /* @__PURE__ */ __name(() => {
if (hangingTimer) {
clearTimeout(hangingTimer);
}
if (this.options.onActivity) {
this.options.onActivity();
}
hangingTimer = activeTasks > 0 && setTimeout(onHanging, timeout);
}, "onActivity");
for (const method of farmOptions.exposedMethods) {
if (method.startsWith("_")) {
continue;
}
this[method] = timeout ? async (...args) => {
activeTasks++;
try {
let attempts = 0;
for (; ; ) {
onActivity();
const params = defu(args.length > 0 && args[0] ? args[0] : {}, context ?? {});
const result = await Promise.race([
// eslint-disable-next-line ts/no-unsafe-call
this.#worker[method](...params),
restartPromise
]);
if (result !== RESTARTED) {
return result;
}
if (onRestart) {
onRestart(method, args, ++attempts);
}
}
} finally {
activeTasks--;
onActivity();
}
} : this.#worker[method].bind(this.#worker);
}
}
async end() {
const worker = this.#worker;
if (!worker) {
throw new Error("Farm is ended, no more calls can be done to it");
}
cleanupWorkers(worker);
this.#worker = void 0;
return worker.end();
}
/**
* Quietly end the worker if it exists
*/
close() {
if (this.#worker) {
cleanupWorkers(this.#worker);
void this.#worker.end();
}
}
};
function createWorker(workerPath, exposedMethods, numWorkers = 1) {
return new Worker(workerPath, {
name: findFileName(workerPath, {
withExtension: false
}),
exposedMethods,
numWorkers
});
}
__name(createWorker, "createWorker");
export { Worker, createWorker };
//# sourceMappingURL=chunk-O2R2KDOD.js.map
//# sourceMappingURL=chunk-O2R2KDOD.js.map