env-runner 0.1.1 → 0.1.2

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/dist/cli.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as EnvServer } from "./_chunks/server.mjs";
2
- import { serve } from "srvx";
3
2
  import { resolve } from "node:path";
3
+ import { serve } from "srvx";
4
4
  import { parseArgs } from "node:util";
5
5
  //#region src/cli.ts
6
6
  const { values, positionals } = parseArgs({
package/dist/index.d.mts CHANGED
@@ -1,6 +1,7 @@
1
- import { a as NodeUpgradeContext, c as UpgradeContext, d as WorkerHooks, i as FetchHandler, l as UpgradeHandler, n as EnvRunnerData, o as RunnerMessageListener, r as EnvRunner, s as RunnerRPCHooks, t as BaseEnvRunner, u as WorkerAddress } from "./_chunks/base-runner.mjs";
1
+ import { a as RunnerMessageListener, c as UpgradeHandler, i as RPCOptions, l as WorkerAddress, n as FetchHandler, o as RunnerRPCHooks, r as NodeUpgradeContext, s as UpgradeContext, t as EnvRunner, u as WorkerHooks } from "./_chunks/types.mjs";
2
+ import { n as EnvRunnerData, t as BaseEnvRunner } from "./_chunks/base-runner.mjs";
2
3
  import { t as DenoProcessEnvRunner } from "./_chunks/runner.mjs";
3
- import { n as MiniflareEnvRunnerOptions, t as MiniflareEnvRunner } from "./_chunks/runner2.mjs";
4
+ import { i as TransformResult, n as MiniflareEnvRunnerOptions, r as MiniflareExportInfo, t as MiniflareEnvRunner } from "./_chunks/runner2.mjs";
4
5
  import { ServerOptions } from "srvx";
5
6
 
6
7
  //#region src/manager.d.ts
@@ -24,6 +25,11 @@ declare class RunnerManager implements EnvRunner {
24
25
  sendMessage(message: unknown): void;
25
26
  onMessage(listener: RunnerMessageListener): void;
26
27
  offMessage(listener: RunnerMessageListener): void;
28
+ waitForReady(timeout?: number): Promise<void>;
29
+ rpc<T = unknown>(name: string, data?: unknown, opts?: {
30
+ timeout?: number;
31
+ }): Promise<T>;
32
+ reloadModule(timeout?: number): Promise<void>;
27
33
  close(): Promise<void>;
28
34
  onClose?: (runner: EnvRunner, cause?: unknown) => void;
29
35
  onReady?: (runner: EnvRunner, address?: WorkerAddress) => void;
@@ -92,9 +98,10 @@ interface AppEntryIPC {
92
98
  }
93
99
  interface AppEntry {
94
100
  fetch: ServerOptions["fetch"];
101
+ upgrade?: (context: UpgradeContext) => void;
95
102
  middleware?: ServerOptions["middleware"];
96
103
  plugins?: ServerOptions["plugins"];
97
104
  ipc?: AppEntryIPC;
98
105
  }
99
106
  //#endregion
100
- export { type AppEntry, type AppEntryIPC, type AppEntryIPCContext, BaseEnvRunner, DenoProcessEnvRunner, type EnvRunnerData as DenoProcessEnvRunnerData, type EnvRunnerData, type EnvRunner, EnvServer, type EnvServerOptions, type FetchHandler, type LoadRunnerOptions, MiniflareEnvRunner, type MiniflareEnvRunnerOptions, type NodeUpgradeContext, RunnerManager, type RunnerMessageListener, type RunnerName, type RunnerRPCHooks, type UpgradeContext, type UpgradeHandler, type WorkerAddress, type WorkerHooks, loadRunner };
107
+ export { type AppEntry, type AppEntryIPC, type AppEntryIPCContext, BaseEnvRunner, DenoProcessEnvRunner, type EnvRunnerData as DenoProcessEnvRunnerData, type EnvRunnerData, type EnvRunner, EnvServer, type EnvServerOptions, type FetchHandler, type LoadRunnerOptions, MiniflareEnvRunner, type MiniflareEnvRunnerOptions, type MiniflareExportInfo, type NodeUpgradeContext, type RPCOptions, RunnerManager, type RunnerMessageListener, type RunnerName, type RunnerRPCHooks, type TransformResult, type UpgradeContext, type UpgradeHandler, type WorkerAddress, type WorkerHooks, loadRunner };
@@ -1,4 +1,5 @@
1
- import { d as WorkerHooks, n as EnvRunnerData, t as BaseEnvRunner } from "../../_chunks/base-runner.mjs";
1
+ import { u as WorkerHooks } from "../../_chunks/types.mjs";
2
+ import { n as EnvRunnerData, t as BaseEnvRunner } from "../../_chunks/base-runner.mjs";
2
3
 
3
4
  //#region src/runners/bun-process/runner.d.ts
4
5
  declare class BunProcessEnvRunner extends BaseEnvRunner {
@@ -1,26 +1,47 @@
1
- import { n as resolveEntry, t as parseServerAddress } from "../../_chunks/worker-utils.mjs";
1
+ import { n as reloadEntryModule, r as resolveEntry, t as parseServerAddress } from "../../_chunks/worker-utils.mjs";
2
2
  import { serve } from "srvx";
3
3
  //#region src/runners/bun-process/worker.ts
4
- const entry = await resolveEntry(JSON.parse(process.env.ENV_RUNNER_DATA || "{}").entry);
4
+ const data = JSON.parse(process.env.ENV_RUNNER_DATA || "{}");
5
+ let entry = await resolveEntry(data.entry);
6
+ const sendMessage = (message) => process.send(message);
5
7
  const server = serve({
6
8
  port: 0,
7
9
  hostname: "127.0.0.1",
8
10
  silent: true,
9
- fetch: entry.fetch,
11
+ fetch: (request) => entry.fetch(request),
10
12
  middleware: entry.middleware,
11
13
  plugins: entry.plugins,
12
14
  gracefulShutdown: false
13
15
  });
14
16
  await server.ready();
15
- if (entry.ipc) await entry.ipc.onOpen?.({ sendMessage: (message) => process.send(message) });
17
+ if (entry.upgrade) server.node?.server?.on("upgrade", (req, socket, head) => {
18
+ entry.upgrade({ node: {
19
+ req,
20
+ socket,
21
+ head
22
+ } });
23
+ });
24
+ if (entry.ipc) await entry.ipc.onOpen?.({ sendMessage });
16
25
  process.send({ address: parseServerAddress(server) });
17
- process.on("message", (message) => {
26
+ process.on("message", async (message) => {
18
27
  if (message?.event === "shutdown") {
19
28
  Promise.resolve(entry.ipc?.onClose?.()).then(() => server.close()).then(() => {
20
29
  process.send({ event: "exit" });
21
30
  });
22
31
  return;
23
32
  }
33
+ if (message?.event === "reload-module") {
34
+ try {
35
+ entry = await reloadEntryModule(data.entry, entry, sendMessage);
36
+ process.send({ event: "module-reloaded" });
37
+ } catch (error) {
38
+ process.send({
39
+ event: "module-reloaded",
40
+ error: error?.message || String(error)
41
+ });
42
+ }
43
+ return;
44
+ }
24
45
  if (message?.type === "ping") {
25
46
  process.send({
26
47
  type: "pong",
@@ -1,26 +1,47 @@
1
- import { n as resolveEntry, t as parseServerAddress } from "../../_chunks/worker-utils.mjs";
1
+ import { n as reloadEntryModule, r as resolveEntry, t as parseServerAddress } from "../../_chunks/worker-utils.mjs";
2
2
  import { serve } from "srvx";
3
3
  //#region src/runners/deno-process/worker.ts
4
- const entry = await resolveEntry(JSON.parse(process.env.ENV_RUNNER_DATA || "{}").entry);
4
+ const data = JSON.parse(process.env.ENV_RUNNER_DATA || "{}");
5
+ let entry = await resolveEntry(data.entry);
6
+ const sendMessage = (message) => process.send(message);
5
7
  const server = serve({
6
8
  port: 0,
7
9
  hostname: "127.0.0.1",
8
10
  silent: true,
9
- fetch: entry.fetch,
11
+ fetch: (request) => entry.fetch(request),
10
12
  middleware: entry.middleware,
11
13
  plugins: entry.plugins,
12
14
  gracefulShutdown: false
13
15
  });
14
16
  await server.ready();
15
- if (entry.ipc) await entry.ipc.onOpen?.({ sendMessage: (message) => process.send(message) });
17
+ if (entry.upgrade) server.node?.server?.on("upgrade", (req, socket, head) => {
18
+ entry.upgrade({ node: {
19
+ req,
20
+ socket,
21
+ head
22
+ } });
23
+ });
24
+ if (entry.ipc) await entry.ipc.onOpen?.({ sendMessage });
16
25
  process.send({ address: parseServerAddress(server) });
17
- process.on("message", (message) => {
26
+ process.on("message", async (message) => {
18
27
  if (message?.event === "shutdown") {
19
28
  Promise.resolve(entry.ipc?.onClose?.()).then(() => server.close()).then(() => {
20
29
  process.send({ event: "exit" });
21
30
  });
22
31
  return;
23
32
  }
33
+ if (message?.event === "reload-module") {
34
+ try {
35
+ entry = await reloadEntryModule(data.entry, entry, sendMessage);
36
+ process.send({ event: "module-reloaded" });
37
+ } catch (error) {
38
+ process.send({
39
+ event: "module-reloaded",
40
+ error: error?.message || String(error)
41
+ });
42
+ }
43
+ return;
44
+ }
24
45
  if (message?.type === "ping") {
25
46
  process.send({
26
47
  type: "pong",
@@ -1,3 +1,3 @@
1
1
  import { n as EnvRunnerData } from "../../_chunks/base-runner.mjs";
2
- import { n as MiniflareEnvRunnerOptions, t as MiniflareEnvRunner } from "../../_chunks/runner2.mjs";
3
- export { MiniflareEnvRunner, EnvRunnerData as MiniflareEnvRunnerData, MiniflareEnvRunnerOptions };
2
+ import { i as TransformResult, n as MiniflareEnvRunnerOptions, r as MiniflareExportInfo, t as MiniflareEnvRunner } from "../../_chunks/runner2.mjs";
3
+ export { MiniflareEnvRunner, EnvRunnerData as MiniflareEnvRunnerData, MiniflareEnvRunnerOptions, MiniflareExportInfo, TransformResult };
@@ -1,4 +1,5 @@
1
- import { d as WorkerHooks, n as EnvRunnerData, t as BaseEnvRunner } from "../../_chunks/base-runner.mjs";
1
+ import { u as WorkerHooks } from "../../_chunks/types.mjs";
2
+ import { n as EnvRunnerData, t as BaseEnvRunner } from "../../_chunks/base-runner.mjs";
2
3
 
3
4
  //#region src/runners/node-process/runner.d.ts
4
5
  declare class NodeProcessEnvRunner extends BaseEnvRunner {
@@ -1,26 +1,47 @@
1
- import { n as resolveEntry, t as parseServerAddress } from "../../_chunks/worker-utils.mjs";
1
+ import { n as reloadEntryModule, r as resolveEntry, t as parseServerAddress } from "../../_chunks/worker-utils.mjs";
2
2
  import { serve } from "srvx";
3
3
  //#region src/runners/node-process/worker.ts
4
- const entry = await resolveEntry(JSON.parse(process.env.ENV_RUNNER_DATA || "{}").entry);
4
+ const data = JSON.parse(process.env.ENV_RUNNER_DATA || "{}");
5
+ let entry = await resolveEntry(data.entry);
6
+ const sendMessage = (message) => process.send(message);
5
7
  const server = serve({
6
8
  port: 0,
7
9
  hostname: "127.0.0.1",
8
10
  silent: true,
9
- fetch: entry.fetch,
11
+ fetch: (request) => entry.fetch(request),
10
12
  middleware: entry.middleware,
11
13
  plugins: entry.plugins,
12
14
  gracefulShutdown: false
13
15
  });
14
16
  await server.ready();
15
- if (entry.ipc) await entry.ipc.onOpen?.({ sendMessage: (message) => process.send(message) });
17
+ if (entry.upgrade) server.node?.server?.on("upgrade", (req, socket, head) => {
18
+ entry.upgrade({ node: {
19
+ req,
20
+ socket,
21
+ head
22
+ } });
23
+ });
24
+ if (entry.ipc) await entry.ipc.onOpen?.({ sendMessage });
16
25
  process.send({ address: parseServerAddress(server) });
17
- process.on("message", (message) => {
26
+ process.on("message", async (message) => {
18
27
  if (message?.event === "shutdown") {
19
28
  Promise.resolve(entry.ipc?.onClose?.()).then(() => server.close()).then(() => {
20
29
  process.send({ event: "exit" });
21
30
  });
22
31
  return;
23
32
  }
33
+ if (message?.event === "reload-module") {
34
+ try {
35
+ entry = await reloadEntryModule(data.entry, entry, sendMessage);
36
+ process.send({ event: "module-reloaded" });
37
+ } catch (error) {
38
+ process.send({
39
+ event: "module-reloaded",
40
+ error: error?.message || String(error)
41
+ });
42
+ }
43
+ return;
44
+ }
24
45
  if (message?.type === "ping") {
25
46
  process.send({
26
47
  type: "pong",
@@ -1,4 +1,5 @@
1
- import { d as WorkerHooks, n as EnvRunnerData, t as BaseEnvRunner } from "../../_chunks/base-runner.mjs";
1
+ import { u as WorkerHooks } from "../../_chunks/types.mjs";
2
+ import { n as EnvRunnerData, t as BaseEnvRunner } from "../../_chunks/base-runner.mjs";
2
3
 
3
4
  //#region src/runners/node-worker/runner.d.ts
4
5
  declare class NodeWorkerEnvRunner extends BaseEnvRunner {
@@ -1,27 +1,48 @@
1
- import { n as resolveEntry, t as parseServerAddress } from "../../_chunks/worker-utils.mjs";
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
4
  //#region src/runners/node-worker/worker.ts
5
- const entry = await resolveEntry((workerData || {}).entry);
5
+ const data = workerData || {};
6
+ let entry = await resolveEntry(data.entry);
7
+ const sendMessage = (message) => parentPort?.postMessage(message);
6
8
  const server = serve({
7
9
  port: 0,
8
10
  hostname: "127.0.0.1",
9
11
  silent: true,
10
- fetch: entry.fetch,
12
+ fetch: (request) => entry.fetch(request),
11
13
  middleware: entry.middleware,
12
14
  plugins: entry.plugins,
13
15
  gracefulShutdown: false
14
16
  });
15
17
  await server.ready();
16
- if (entry.ipc) await entry.ipc.onOpen?.({ sendMessage: (message) => parentPort?.postMessage(message) });
18
+ if (entry.upgrade) server.node?.server?.on("upgrade", (req, socket, head) => {
19
+ entry.upgrade({ node: {
20
+ req,
21
+ socket,
22
+ head
23
+ } });
24
+ });
25
+ if (entry.ipc) await entry.ipc.onOpen?.({ sendMessage });
17
26
  parentPort?.postMessage({ address: parseServerAddress(server) });
18
- parentPort?.on("message", (message) => {
27
+ parentPort?.on("message", async (message) => {
19
28
  if (message?.event === "shutdown") {
20
29
  Promise.resolve(entry.ipc?.onClose?.()).then(() => server.close()).then(() => {
21
30
  parentPort?.postMessage({ event: "exit" });
22
31
  });
23
32
  return;
24
33
  }
34
+ if (message?.event === "reload-module") {
35
+ try {
36
+ entry = await reloadEntryModule(data.entry, entry, sendMessage);
37
+ parentPort?.postMessage({ event: "module-reloaded" });
38
+ } catch (error) {
39
+ parentPort?.postMessage({
40
+ event: "module-reloaded",
41
+ error: error?.message || String(error)
42
+ });
43
+ }
44
+ return;
45
+ }
25
46
  if (message?.type === "ping") {
26
47
  parentPort?.postMessage({
27
48
  type: "pong",
@@ -1,4 +1,7 @@
1
- import { d as WorkerHooks, n as EnvRunnerData, t as BaseEnvRunner } from "../../_chunks/base-runner.mjs";
1
+ import { u as WorkerHooks } from "../../_chunks/types.mjs";
2
+ import { n as EnvRunnerData, t as BaseEnvRunner } from "../../_chunks/base-runner.mjs";
3
+ import * as node_http0 from "node:http";
4
+ import * as node_net0 from "node:net";
2
5
 
3
6
  //#region src/runners/self/runner.d.ts
4
7
  declare class SelfEnvRunner extends BaseEnvRunner {
@@ -9,7 +12,15 @@ declare class SelfEnvRunner extends BaseEnvRunner {
9
12
  data?: EnvRunnerData;
10
13
  });
11
14
  fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
15
+ upgrade(context: {
16
+ node: {
17
+ req: node_http0.IncomingMessage;
18
+ socket: node_net0.Socket;
19
+ head: any;
20
+ };
21
+ }): Promise<void>;
12
22
  sendMessage(message: unknown): void;
23
+ reloadModule(): Promise<void>;
13
24
  protected _hasRuntime(): boolean;
14
25
  protected _runtimeType(): string;
15
26
  protected _closeRuntime(): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  import "../../_chunks/libs/std-env.mjs";
2
2
  import { t as BaseEnvRunner } from "../../_chunks/base-runner.mjs";
3
- import { n as resolveEntry } from "../../_chunks/worker-utils.mjs";
3
+ import { n as reloadEntryModule, r as resolveEntry } from "../../_chunks/worker-utils.mjs";
4
4
  //#region src/runners/self/runner.ts
5
5
  var SelfEnvRunner = class extends BaseEnvRunner {
6
6
  #active = false;
@@ -17,6 +17,10 @@ var SelfEnvRunner = class extends BaseEnvRunner {
17
17
  const request = input instanceof Request ? input : new Request(input, init);
18
18
  return this.#entry.fetch(request);
19
19
  }
20
+ async upgrade(context) {
21
+ if (!this.#entry?.upgrade || this.closed) return;
22
+ this.#entry.upgrade(context);
23
+ }
20
24
  sendMessage(message) {
21
25
  if (!this.#active) throw new Error("Self env runner should be initialized before sending messages.");
22
26
  if (message?.type === "ping") {
@@ -28,6 +32,14 @@ var SelfEnvRunner = class extends BaseEnvRunner {
28
32
  }
29
33
  this.#entry?.ipc?.onMessage?.(message);
30
34
  }
35
+ async reloadModule() {
36
+ const entryPath = this._data?.entry;
37
+ if (!entryPath || !this.#entry) throw new Error("Cannot reload: no entry loaded");
38
+ const sendFn = (message) => {
39
+ queueMicrotask(() => this._handleMessage(message));
40
+ };
41
+ this.#entry = await reloadEntryModule(entryPath, this.#entry, sendFn);
42
+ }
31
43
  _hasRuntime() {
32
44
  return this.#active;
33
45
  }
@@ -0,0 +1,32 @@
1
+ import { o as RunnerRPCHooks } from "./_chunks/types.mjs";
2
+
3
+ //#region src/vite.d.ts
4
+ /** Vite HotChannel-compatible interface (avoids hard dependency on vite types). */
5
+ interface ViteHotChannel {
6
+ send: (data: any) => void;
7
+ on: (event: string, handler: (...args: any[]) => void) => void;
8
+ off: (event: string, handler: (...args: any[]) => void) => void;
9
+ }
10
+ /** Vite ModuleRunner transport-compatible interface. */
11
+ interface ViteTransport {
12
+ connect: (handlers: {
13
+ onMessage: (payload: any) => void;
14
+ }) => void;
15
+ send: (payload: any) => void;
16
+ }
17
+ /**
18
+ * Create a Vite `HotChannel` from an env-runner's RPC hooks.
19
+ *
20
+ * Use on the **host side** to bridge env-runner IPC → Vite's DevEnvironment transport.
21
+ * Messages are namespaced by `envName` so multiple Vite environments can share one runner.
22
+ */
23
+ declare function createViteHotChannel(hooks: RunnerRPCHooks, envName: string): ViteHotChannel;
24
+ /**
25
+ * Create a Vite `ModuleRunner` transport from worker-side IPC primitives.
26
+ *
27
+ * Use on the **worker side** to bridge worker IPC → Vite's `ModuleRunner` transport.
28
+ * Filters messages by `envName` so multiple Vite environments can share one IPC channel.
29
+ */
30
+ declare function createViteTransport(sendMessage: (data: any) => void, onMessage: (listener: (value: any) => void) => void, envName: string): ViteTransport;
31
+ //#endregion
32
+ export { ViteHotChannel, ViteTransport, createViteHotChannel, createViteTransport };
package/dist/vite.mjs ADDED
@@ -0,0 +1,58 @@
1
+ //#region src/vite.ts
2
+ /**
3
+ * Create a Vite `HotChannel` from an env-runner's RPC hooks.
4
+ *
5
+ * Use on the **host side** to bridge env-runner IPC → Vite's DevEnvironment transport.
6
+ * Messages are namespaced by `envName` so multiple Vite environments can share one runner.
7
+ */
8
+ function createViteHotChannel(hooks, envName) {
9
+ const listeners = /* @__PURE__ */ new WeakMap();
10
+ return {
11
+ send: (data) => hooks.sendMessage({
12
+ ...data,
13
+ viteEnv: envName
14
+ }),
15
+ on: (event, handler) => {
16
+ if (event === "connection") return;
17
+ const listener = (value) => {
18
+ if (value?.type === "custom" && value.event === event && value.viteEnv === envName) handler(value.data, { send: (payload) => hooks.sendMessage({
19
+ ...payload,
20
+ viteEnv: envName
21
+ }) });
22
+ };
23
+ listeners.set(handler, listener);
24
+ hooks.onMessage(listener);
25
+ },
26
+ off: (event, handler) => {
27
+ if (event === "connection") return;
28
+ const listener = listeners.get(handler);
29
+ if (listener) {
30
+ hooks.offMessage(listener);
31
+ listeners.delete(handler);
32
+ }
33
+ }
34
+ };
35
+ }
36
+ /**
37
+ * Create a Vite `ModuleRunner` transport from worker-side IPC primitives.
38
+ *
39
+ * Use on the **worker side** to bridge worker IPC → Vite's `ModuleRunner` transport.
40
+ * Filters messages by `envName` so multiple Vite environments can share one IPC channel.
41
+ */
42
+ function createViteTransport(sendMessage, onMessage, envName) {
43
+ return {
44
+ connect({ onMessage: onRunnerMessage }) {
45
+ onMessage((payload) => {
46
+ if (payload?.type === "custom" && payload.viteEnv === envName) onRunnerMessage(payload);
47
+ });
48
+ },
49
+ send(payload) {
50
+ sendMessage?.({
51
+ ...payload,
52
+ viteEnv: envName
53
+ });
54
+ }
55
+ };
56
+ }
57
+ //#endregion
58
+ export { createViteHotChannel, createViteTransport };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "env-runner",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Generic environment runner for JavaScript runtimes.",
5
5
  "license": "MIT",
6
6
  "repository": "unjs/env-runner",
@@ -23,7 +23,8 @@
23
23
  "./runners/deno-process": "./dist/runners/deno-process/runner.mjs",
24
24
  "./runners/deno-process/worker": "./dist/runners/deno-process/worker.mjs",
25
25
  "./runners/self": "./dist/runners/self/runner.mjs",
26
- "./runners/miniflare": "./dist/runners/miniflare/runner.mjs"
26
+ "./runners/miniflare": "./dist/runners/miniflare/runner.mjs",
27
+ "./vite": "./dist/vite.mjs"
27
28
  },
28
29
  "scripts": {
29
30
  "build": "obuild",