diff --git a/package.json b/package.json index 027b2d9..21acb28 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "deno-http-worker", - "version": "0.0.15", + "version": "0.0.16", "description": "", "main": "dist/index.js", "types": "./dist/index.d.ts", "scripts": { "test": "vitest run", "test:watch": "vitest", - "lint" : "eslint . && npm run lint:deno-bootstrap && npm run lint:deno-test-files", + "lint": "eslint . && npm run lint:deno-bootstrap && npm run lint:deno-test-files", "lint:deno-bootstrap": "cd deno-bootstrap && deno lint", "lint:deno-test-files": "cd src/test && deno lint", "build": "tsc --build", diff --git a/src/DenoHTTPWorker.test.ts b/src/DenoHTTPWorker.test.ts index 3c42f2c..794703a 100644 --- a/src/DenoHTTPWorker.test.ts +++ b/src/DenoHTTPWorker.test.ts @@ -3,6 +3,7 @@ import { DenoHTTPWorker, newDenoHTTPWorker } from "./index.js"; import fs from "fs"; import path from "path"; import { Worker } from "worker_threads"; +import { SpawnOptions, spawn } from "child_process"; // Uncomment this if you want to debug serial test execution // const it = _it.concurrent; @@ -71,6 +72,29 @@ describe("DenoHTTPWorker", { timeout: 1000 }, () => { worker.terminate(); }); + it("alternate spawnFunc can be provided", async () => { + let firstArg: string = ""; + const worker = await newDenoHTTPWorker( + ` + export default { async fetch (req: Request): Promise { + let headers = {}; + for (let [key, value] of req.headers.entries()) { + headers[key] = value; + } + return Response.json({ ok: req.url, headers: headers }) + } } + `, + { + spawnFunc: (command: string, args: string[], options: SpawnOptions) => { + firstArg = args[0] as string; + return spawn(command, args, options); + }, + } + ); + expect(firstArg).toEqual("run"); + worker.terminate(); + }); + it("dont crash on socket removal", async () => { const worker = await newDenoHTTPWorker( ` @@ -104,9 +128,11 @@ describe("DenoHTTPWorker", { timeout: 1000 }, () => { { printOutput: true } ); for (let i = 0; i < 10; i++) { - const json = await jsonRequest(worker, "https://localhost/hello?isee=you", { - headers: { accept: "application/json" }, - }); + const json = await jsonRequest( + worker, + "https://localhost/hello?isee=you", + { headers: { accept: "application/json" } } + ); expect(json).toEqual({ ok: "https://localhost/hello?isee=you", headers: { accept: "application/json" }, diff --git a/src/DenoHTTPWorker.ts b/src/DenoHTTPWorker.ts index 1449890..f7e4cff 100644 --- a/src/DenoHTTPWorker.ts +++ b/src/DenoHTTPWorker.ts @@ -1,5 +1,5 @@ import path, { resolve } from "node:path"; -import { ChildProcess, spawn, SpawnOptions } from "node:child_process"; +import { spawn, SpawnOptions } from "node:child_process"; import { Readable } from "node:stream"; import readline from "node:readline"; import http from "node:http"; @@ -20,6 +20,26 @@ interface OnExitListener { (exitCode: number, signal: string): void; } +interface MinimalChildProcess { + stdout: Readable | null; + stderr: Readable | null; + readonly pid?: number | undefined; + readonly exitCode: number | null; + kill(signal?: NodeJS.Signals | number): boolean; + on(event: string, listener: (...args: any[]) => void): this; + on( + event: "close", + listener: (code: number | null, signal: NodeJS.Signals | null) => void + ): this; + on(event: "disconnect", listener: () => void): this; + on(event: "error", listener: (err: Error) => void): this; + on( + event: "exit", + listener: (code: number | null, signal: NodeJS.Signals | null) => void + ): this; + on(event: "spawn", listener: () => void): this; +} + export interface DenoWorkerOptions { /** * The path to the executable that should be use when spawning the subprocess. @@ -66,7 +86,16 @@ export interface DenoWorkerOptions { /** * Callback that is called when the process is spawned. */ - onSpawn?: (process: ChildProcess) => void; + onSpawn?: (process: MinimalChildProcess) => void; + + /** + * Provide an alternative spawn functions. Defaults to child_process.spawn. + */ + spawnFunc: ( + command: string, + args: string[], + options: SpawnOptions + ) => MinimalChildProcess; } /** @@ -83,6 +112,7 @@ export const newDenoHTTPWorker = async ( printCommandAndArguments: false, spawnOptions: {}, printOutput: false, + spawnFunc: spawn, ...options, }; @@ -157,7 +187,7 @@ export const newDenoHTTPWorker = async ( console.log("Spawning deno process:", [command, ...args]); } - const process = spawn(command, args, _options.spawnOptions); + const process = _options.spawnFunc(command, args, _options.spawnOptions); let running = false; let exited = false; let worker: DenoHTTPWorker | undefined = undefined; @@ -251,7 +281,7 @@ export interface DenoHTTPWorker { class denoHTTPWorker { #onexitListeners: OnExitListener[]; - #process: ChildProcess; + #process: MinimalChildProcess; #socketFile: string; #stderr: Readable; #stdout: Readable; @@ -260,7 +290,7 @@ class denoHTTPWorker { constructor( socketFile: string, - process: ChildProcess, + process: MinimalChildProcess, stdout: Readable, stderr: Readable ) {