env-runner 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -1
- package/dist/_chunks/runner2.d.mts +9 -0
- package/dist/_chunks/runner2.mjs +74 -3
- package/dist/index.d.mts +2 -0
- package/dist/runners/bun-process/worker.mjs +2 -1
- package/dist/runners/deno-process/worker.mjs +2 -1
- package/dist/runners/node-process/worker.mjs +2 -1
- package/dist/runners/node-worker/worker.mjs +2 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -375,8 +375,19 @@ export default {
|
|
|
375
375
|
fetch(request: Request) {
|
|
376
376
|
return new Response("Hello!");
|
|
377
377
|
},
|
|
378
|
+
websocket: {
|
|
379
|
+
// Optional: crossws WebSocket hooks (recommended)
|
|
380
|
+
open(peer) {
|
|
381
|
+
peer.send("Welcome!");
|
|
382
|
+
},
|
|
383
|
+
message(peer, message) {
|
|
384
|
+
peer.send(`Echo: ${message.text()}`);
|
|
385
|
+
},
|
|
386
|
+
close(peer, details) {},
|
|
387
|
+
error(peer, error) {},
|
|
388
|
+
},
|
|
378
389
|
upgrade(context) {
|
|
379
|
-
// Optional:
|
|
390
|
+
// Optional: raw WebSocket upgrade handler (Node.js only)
|
|
380
391
|
// context.node gives { req, socket, head }
|
|
381
392
|
},
|
|
382
393
|
middleware: [], // Optional srvx middleware
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { u as WorkerHooks } from "./types.mjs";
|
|
2
2
|
import { n as EnvRunnerData, t as BaseEnvRunner } from "./base-runner.mjs";
|
|
3
|
+
import { IncomingMessage } from "node:http";
|
|
4
|
+
import { Socket } from "node:net";
|
|
3
5
|
|
|
4
6
|
//#region src/runners/miniflare/runner.d.ts
|
|
5
7
|
/** Result from a module transform (compatible with Vite's `TransformResult`). */
|
|
@@ -65,6 +67,13 @@ declare class MiniflareEnvRunner extends BaseEnvRunner {
|
|
|
65
67
|
protected _hasRuntime(): boolean;
|
|
66
68
|
protected _runtimeType(): string;
|
|
67
69
|
protected _closeRuntime(): Promise<void>;
|
|
70
|
+
upgrade(context: {
|
|
71
|
+
node: {
|
|
72
|
+
req: IncomingMessage;
|
|
73
|
+
socket: Socket;
|
|
74
|
+
head: any;
|
|
75
|
+
};
|
|
76
|
+
}): Promise<void>;
|
|
68
77
|
}
|
|
69
78
|
//#endregion
|
|
70
79
|
export { TransformResult as i, MiniflareEnvRunnerOptions as n, MiniflareExportInfo as r, MiniflareEnvRunner as t };
|
package/dist/_chunks/runner2.mjs
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { t as BaseEnvRunner } from "./base-runner.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
+
import { proxyUpgrade } from "httpxy";
|
|
3
4
|
import { readFileSync } from "node:fs";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
5
6
|
import { dirname, resolve } from "node:path";
|
|
6
7
|
//#region src/runners/miniflare/wrapper.ts
|
|
7
8
|
const IPC_PATH$1 = "/__env_runner_ipc";
|
|
9
|
+
/** Service binding name used for cross-request IPC (worker → runner). */
|
|
10
|
+
const IPC_BINDING = "__ENV_RUNNER_IPC";
|
|
8
11
|
/**
|
|
9
12
|
* Generates a wrapper module that imports the user entry and adds IPC glue.
|
|
10
13
|
*
|
|
@@ -15,6 +18,10 @@ const IPC_PATH$1 = "/__env_runner_ipc";
|
|
|
15
18
|
* - Reload: `{ type: "reload" }` triggers cache-busted re-import
|
|
16
19
|
* - Shutdown: `{ type: "shutdown" }` calls `ipc.onClose()`
|
|
17
20
|
*
|
|
21
|
+
* For outgoing messages during fetch request handling, uses a service binding
|
|
22
|
+
* (`__ENV_RUNNER_IPC`) to avoid workerd's cross-request I/O restriction on
|
|
23
|
+
* the WebSocket object.
|
|
24
|
+
*
|
|
18
25
|
* Passed as an in-memory `script` to Miniflare (no temp files needed).
|
|
19
26
|
*/
|
|
20
27
|
function generateWrapper(entryPath, opts) {
|
|
@@ -40,10 +47,12 @@ ${staticReExport}
|
|
|
40
47
|
${explicitExports}
|
|
41
48
|
|
|
42
49
|
const __IPC_PATH = "${IPC_PATH$1}";
|
|
50
|
+
const __IPC_BINDING = "${IPC_BINDING}";
|
|
43
51
|
const __entryPath = ${JSON.stringify(entryPath)};
|
|
44
52
|
let __userEntry;
|
|
45
53
|
let __ipcInitialized = false;
|
|
46
54
|
let __serverWs;
|
|
55
|
+
let __currentEnv;
|
|
47
56
|
|
|
48
57
|
async function __loadEntry(env, path) {
|
|
49
58
|
globalThis.__ENV_RUNNER_UNSAFE_EVAL__ = env.__ENV_RUNNER_UNSAFE_EVAL__;
|
|
@@ -57,8 +66,17 @@ async function __loadEntry(env, path) {
|
|
|
57
66
|
}
|
|
58
67
|
|
|
59
68
|
function __sendMessage(message) {
|
|
69
|
+
const payload = JSON.stringify(message);
|
|
70
|
+
const env = __currentEnv;
|
|
71
|
+
if (env && env[__IPC_BINDING]) {
|
|
72
|
+
env[__IPC_BINDING].fetch("http://localhost/__ipc", {
|
|
73
|
+
method: "POST",
|
|
74
|
+
body: payload,
|
|
75
|
+
}).catch(() => {});
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
60
78
|
if (__serverWs) {
|
|
61
|
-
__serverWs.send(
|
|
79
|
+
__serverWs.send(payload);
|
|
62
80
|
}
|
|
63
81
|
}
|
|
64
82
|
|
|
@@ -81,6 +99,7 @@ async function __handleWsMessage(env, data) {
|
|
|
81
99
|
await __userEntry.ipc.onClose();
|
|
82
100
|
}
|
|
83
101
|
__userEntry = newEntry;
|
|
102
|
+
__crosswsAdapter = undefined;
|
|
84
103
|
__ipcInitialized = false;
|
|
85
104
|
if (__userEntry.ipc?.onOpen) {
|
|
86
105
|
__ipcInitialized = true;
|
|
@@ -101,6 +120,19 @@ async function __handleWsMessage(env, data) {
|
|
|
101
120
|
}
|
|
102
121
|
}
|
|
103
122
|
|
|
123
|
+
let __crosswsAdapter;
|
|
124
|
+
|
|
125
|
+
async function __initCrossws(env, hooks) {
|
|
126
|
+
if (__crosswsAdapter) return __crosswsAdapter;
|
|
127
|
+
const importFn = env.__ENV_RUNNER_UNSAFE_EVAL__.newAsyncFunction(
|
|
128
|
+
"return await import('crossws/adapters/cloudflare')",
|
|
129
|
+
"loadCrossws"
|
|
130
|
+
);
|
|
131
|
+
const { default: cloudflareAdapter } = await importFn();
|
|
132
|
+
__crosswsAdapter = cloudflareAdapter({ hooks });
|
|
133
|
+
return __crosswsAdapter;
|
|
134
|
+
}
|
|
135
|
+
|
|
104
136
|
export default {
|
|
105
137
|
async fetch(request, env, ctx) {
|
|
106
138
|
const url = new URL(request.url);
|
|
@@ -139,11 +171,23 @@ export default {
|
|
|
139
171
|
if (!__userEntry) {
|
|
140
172
|
return new Response("Worker not initialized", { status: 503 });
|
|
141
173
|
}
|
|
174
|
+
|
|
175
|
+
// Handle WebSocket upgrade via crossws cloudflare adapter
|
|
176
|
+
if (__userEntry.websocket && request.headers.get("upgrade") === "websocket") {
|
|
177
|
+
const adapter = await __initCrossws(env, __userEntry.websocket);
|
|
178
|
+
return adapter.handleUpgrade(request, env, ctx);
|
|
179
|
+
}
|
|
180
|
+
|
|
142
181
|
const entryFetch = __userEntry.fetch;
|
|
143
182
|
if (!entryFetch) {
|
|
144
183
|
return new Response("No fetch handler exported", { status: 500 });
|
|
145
184
|
}
|
|
146
|
-
|
|
185
|
+
__currentEnv = env;
|
|
186
|
+
try {
|
|
187
|
+
${fetchBody}
|
|
188
|
+
} finally {
|
|
189
|
+
__currentEnv = undefined;
|
|
190
|
+
}
|
|
147
191
|
}
|
|
148
192
|
};
|
|
149
193
|
`;
|
|
@@ -285,15 +329,32 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
285
329
|
this.close(error);
|
|
286
330
|
});
|
|
287
331
|
}
|
|
332
|
+
async upgrade(context) {
|
|
333
|
+
if (!this.#miniflare || this.closed) {
|
|
334
|
+
context.node.socket.destroy();
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const mfUrl = await this.#miniflare.unsafeGetDirectURL();
|
|
338
|
+
const address = new URL(mfUrl);
|
|
339
|
+
await proxyUpgrade({
|
|
340
|
+
host: address.hostname,
|
|
341
|
+
port: Number(address.port)
|
|
342
|
+
}, context.node.req, context.node.socket, context.node.head);
|
|
343
|
+
}
|
|
288
344
|
async #initAsync() {
|
|
289
345
|
const { Miniflare } = await import("miniflare");
|
|
290
346
|
const entryPath = this._data?.entry;
|
|
291
347
|
const userFlags = this.#miniflareOptions.compatibilityFlags || [];
|
|
348
|
+
const userDirectSockets = this.#miniflareOptions.unsafeDirectSockets || [];
|
|
292
349
|
const options = {
|
|
293
350
|
compatibilityDate: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
294
351
|
modules: true,
|
|
295
352
|
...this.#miniflareOptions,
|
|
296
|
-
compatibilityFlags: [...new Set(["nodejs_compat", ...userFlags])]
|
|
353
|
+
compatibilityFlags: [...new Set(["nodejs_compat", ...userFlags])],
|
|
354
|
+
unsafeDirectSockets: [{
|
|
355
|
+
host: "127.0.0.1",
|
|
356
|
+
port: 0
|
|
357
|
+
}, ...userDirectSockets]
|
|
297
358
|
};
|
|
298
359
|
if (entryPath && !options.script && !options.scriptPath) {
|
|
299
360
|
const resolvedEntry = resolve(entryPath);
|
|
@@ -315,6 +376,16 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
315
376
|
options.scriptPath = entryDir + "/__env_runner_wrapper.mjs";
|
|
316
377
|
if (!options.modulesRoot) options.modulesRoot = "/";
|
|
317
378
|
options.unsafeEvalBinding = "__ENV_RUNNER_UNSAFE_EVAL__";
|
|
379
|
+
options.serviceBindings = {
|
|
380
|
+
...options.serviceBindings || {},
|
|
381
|
+
[IPC_BINDING]: async (request) => {
|
|
382
|
+
try {
|
|
383
|
+
const message = await request.json();
|
|
384
|
+
this._handleMessage(message);
|
|
385
|
+
} catch {}
|
|
386
|
+
return new Response(null, { status: 204 });
|
|
387
|
+
}
|
|
388
|
+
};
|
|
318
389
|
if (this.#transformRequest && !options.modulesRules) options.modulesRules = [{
|
|
319
390
|
type: "ESModule",
|
|
320
391
|
include: [
|
package/dist/index.d.mts
CHANGED
|
@@ -3,6 +3,7 @@ import { n as EnvRunnerData, t as BaseEnvRunner } from "./_chunks/base-runner.mj
|
|
|
3
3
|
import { t as DenoProcessEnvRunner } from "./_chunks/runner.mjs";
|
|
4
4
|
import { i as TransformResult, n as MiniflareEnvRunnerOptions, r as MiniflareExportInfo, t as MiniflareEnvRunner } from "./_chunks/runner2.mjs";
|
|
5
5
|
import { ServerOptions } from "srvx";
|
|
6
|
+
import { Hooks } from "crossws";
|
|
6
7
|
|
|
7
8
|
//#region src/manager.d.ts
|
|
8
9
|
/**
|
|
@@ -99,6 +100,7 @@ interface AppEntryIPC {
|
|
|
99
100
|
interface AppEntry {
|
|
100
101
|
fetch: ServerOptions["fetch"];
|
|
101
102
|
upgrade?: (context: UpgradeContext) => void;
|
|
103
|
+
websocket?: Partial<Hooks>;
|
|
102
104
|
middleware?: ServerOptions["middleware"];
|
|
103
105
|
plugins?: ServerOptions["plugins"];
|
|
104
106
|
ipc?: AppEntryIPC;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { n as reloadEntryModule, r as resolveEntry, t as parseServerAddress } from "../../_chunks/worker-utils.mjs";
|
|
2
2
|
import { serve } from "srvx";
|
|
3
|
+
import { plugin } from "crossws/server";
|
|
3
4
|
//#region src/runners/bun-process/worker.ts
|
|
4
5
|
const data = JSON.parse(process.env.ENV_RUNNER_DATA || "{}");
|
|
5
6
|
let entry = await resolveEntry(data.entry);
|
|
@@ -10,7 +11,7 @@ const server = serve({
|
|
|
10
11
|
silent: true,
|
|
11
12
|
fetch: (request) => entry.fetch(request),
|
|
12
13
|
middleware: entry.middleware,
|
|
13
|
-
plugins: entry.plugins,
|
|
14
|
+
plugins: [...entry.plugins || [], ...entry.websocket ? [plugin(entry.websocket)] : []],
|
|
14
15
|
gracefulShutdown: false
|
|
15
16
|
});
|
|
16
17
|
await server.ready();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { n as reloadEntryModule, r as resolveEntry, t as parseServerAddress } from "../../_chunks/worker-utils.mjs";
|
|
2
2
|
import { serve } from "srvx";
|
|
3
|
+
import { plugin } from "crossws/server";
|
|
3
4
|
//#region src/runners/deno-process/worker.ts
|
|
4
5
|
const data = JSON.parse(process.env.ENV_RUNNER_DATA || "{}");
|
|
5
6
|
let entry = await resolveEntry(data.entry);
|
|
@@ -10,7 +11,7 @@ const server = serve({
|
|
|
10
11
|
silent: true,
|
|
11
12
|
fetch: (request) => entry.fetch(request),
|
|
12
13
|
middleware: entry.middleware,
|
|
13
|
-
plugins: entry.plugins,
|
|
14
|
+
plugins: [...entry.plugins || [], ...entry.websocket ? [plugin(entry.websocket)] : []],
|
|
14
15
|
gracefulShutdown: false
|
|
15
16
|
});
|
|
16
17
|
await server.ready();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { n as reloadEntryModule, r as resolveEntry, t as parseServerAddress } from "../../_chunks/worker-utils.mjs";
|
|
2
2
|
import { serve } from "srvx";
|
|
3
|
+
import { plugin } from "crossws/server/node";
|
|
3
4
|
//#region src/runners/node-process/worker.ts
|
|
4
5
|
const data = JSON.parse(process.env.ENV_RUNNER_DATA || "{}");
|
|
5
6
|
let entry = await resolveEntry(data.entry);
|
|
@@ -10,7 +11,7 @@ const server = serve({
|
|
|
10
11
|
silent: true,
|
|
11
12
|
fetch: (request) => entry.fetch(request),
|
|
12
13
|
middleware: entry.middleware,
|
|
13
|
-
plugins: entry.plugins,
|
|
14
|
+
plugins: [...entry.plugins || [], ...entry.websocket ? [plugin(entry.websocket)] : []],
|
|
14
15
|
gracefulShutdown: false
|
|
15
16
|
});
|
|
16
17
|
await server.ready();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { n as reloadEntryModule, r as resolveEntry, t as parseServerAddress } from "../../_chunks/worker-utils.mjs";
|
|
2
2
|
import { parentPort, workerData } from "node:worker_threads";
|
|
3
3
|
import { serve } from "srvx";
|
|
4
|
+
import { plugin } from "crossws/server/node";
|
|
4
5
|
//#region src/runners/node-worker/worker.ts
|
|
5
6
|
const data = workerData || {};
|
|
6
7
|
let entry = await resolveEntry(data.entry);
|
|
@@ -11,7 +12,7 @@ const server = serve({
|
|
|
11
12
|
silent: true,
|
|
12
13
|
fetch: (request) => entry.fetch(request),
|
|
13
14
|
middleware: entry.middleware,
|
|
14
|
-
plugins: entry.plugins,
|
|
15
|
+
plugins: [...entry.plugins || [], ...entry.websocket ? [plugin(entry.websocket)] : []],
|
|
15
16
|
gracefulShutdown: false
|
|
16
17
|
});
|
|
17
18
|
await server.ready();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "env-runner",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Generic environment runner for JavaScript runtimes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "unjs/env-runner",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"typecheck": "tsgo --noEmit --skipLibCheck"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
+
"crossws": "^0.4.4",
|
|
41
42
|
"httpxy": "^0.3.1",
|
|
42
43
|
"srvx": "^0.11.9"
|
|
43
44
|
},
|