env-runner 0.2.3 → 0.3.1

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.
Files changed (33) hide show
  1. package/README.md +370 -20
  2. package/dist/_chunks/common-base-runner.d.mts +486 -15
  3. package/dist/_chunks/common-base-runner.mjs +712 -32
  4. package/dist/_chunks/common-process-data.mjs +13 -0
  5. package/dist/_chunks/common-worker-utils.mjs +47 -114
  6. package/dist/_chunks/deno-process-runner.d.mts +4 -1
  7. package/dist/_chunks/deno-process-runner.mjs +9 -6
  8. package/dist/_chunks/lexer.mjs +2 -0
  9. package/dist/_chunks/libs/cjs-module-lexer.mjs +6 -1
  10. package/dist/_chunks/miniflare-runner.d.mts +26 -14
  11. package/dist/_chunks/miniflare-runner.mjs +430 -134
  12. package/dist/_chunks/netlify-runner.d.mts +3 -1
  13. package/dist/_chunks/node-worker-runner.d.mts +3 -1
  14. package/dist/_chunks/node-worker-runner.mjs +8 -4
  15. package/dist/_chunks/plugin-hooks.mjs +1656 -0
  16. package/dist/_chunks/server.mjs +21 -5
  17. package/dist/_chunks/types.d.mts +37 -1
  18. package/dist/_chunks/vercel-runner.d.mts +3 -1
  19. package/dist/cli.mjs +0 -0
  20. package/dist/index.d.mts +21 -4
  21. package/dist/runners/bun-process/runner.d.mts +4 -1
  22. package/dist/runners/bun-process/runner.mjs +12 -9
  23. package/dist/runners/bun-process/worker.mjs +12 -11
  24. package/dist/runners/deno-process/worker.mjs +12 -11
  25. package/dist/runners/node-process/runner.d.mts +3 -1
  26. package/dist/runners/node-process/runner.mjs +2 -6
  27. package/dist/runners/node-process/worker.mjs +12 -11
  28. package/dist/runners/node-worker/worker.mjs +10 -10
  29. package/dist/runners/self/runner.d.mts +5 -2
  30. package/dist/runners/self/runner.mjs +12 -0
  31. package/package.json +26 -26
  32. package/dist/_chunks/common-host-env.mjs +0 -11
  33. package/dist/_chunks/virtual-loader.mjs +0 -71
@@ -168,9 +168,16 @@ var RunnerManager = class {
168
168
  await this._runner.reloadModule(timeout);
169
169
  this._moduleInvalidated = false;
170
170
  }
171
+ async updateVirtualModules(changes, timeout) {
172
+ const runner = await this._waitForRunner();
173
+ if (!runner?.updateVirtualModules) throw new Error("Active runner does not support updateVirtualModules()");
174
+ await runner.updateVirtualModules(changes, timeout);
175
+ this._moduleInvalidated = true;
176
+ }
171
177
  async invalidateModule(specifier, timeout) {
172
- if (!this._runner?.invalidateModule) throw new Error("Active runner does not support invalidateModule()");
173
- await this._runner.invalidateModule(specifier, timeout);
178
+ const runner = await this._waitForRunner();
179
+ if (!runner?.invalidateModule) throw new Error("Active runner does not support invalidateModule()");
180
+ await runner.invalidateModule(specifier, timeout);
174
181
  this._moduleInvalidated = true;
175
182
  }
176
183
  async close() {
@@ -208,11 +215,11 @@ var RunnerManager = class {
208
215
  runner.onMessage(this._internalListener);
209
216
  for (const listener of this._messageListeners) runner.onMessage(listener);
210
217
  const originalClose = runner.close.bind(runner);
211
- runner.close = async () => {
212
- await originalClose();
218
+ runner.close = async (cause) => {
219
+ await originalClose(cause);
213
220
  if (this._runner === runner) {
214
221
  this._runner = void 0;
215
- for (const fn of this._closeListeners) fn(this);
222
+ for (const fn of this._closeListeners) fn(this, cause);
216
223
  }
217
224
  };
218
225
  if (runner.ready) this._flushQueue();
@@ -263,6 +270,7 @@ var EnvServer = class extends RunnerManager {
263
270
  _reloadTimeout;
264
271
  _reloadListeners = /* @__PURE__ */ new Set();
265
272
  _startPromise;
273
+ _virtual;
266
274
  runner = null;
267
275
  onReload(listener) {
268
276
  this._reloadListeners.add(listener);
@@ -285,6 +293,12 @@ var EnvServer = class extends RunnerManager {
285
293
  this.runner = runner ?? await this._createRunner();
286
294
  await super.reload(this.runner);
287
295
  }
296
+ async updateVirtualModules(changes, timeout) {
297
+ const virtual = this._virtual ??= { ...this._opts.data?.virtual };
298
+ for (const [key, source] of Object.entries(changes)) if (source === null) delete virtual[key];
299
+ else if (source !== void 0) virtual[key] = source;
300
+ if (await this._waitForRunner()) await super.updateVirtualModules(changes, timeout);
301
+ }
288
302
  async close() {
289
303
  this._stopWatching();
290
304
  await super.close();
@@ -303,8 +317,10 @@ var EnvServer = class extends RunnerManager {
303
317
  ...this._opts.runnerOptions,
304
318
  name: this._opts.name || this._opts.entry,
305
319
  hooks: this._opts.hooks,
320
+ plugins: this._opts.plugins,
306
321
  data: {
307
322
  ...this._opts.data,
323
+ ...this._virtual && { virtual: { ...this._virtual } },
308
324
  entry: this._opts.entry
309
325
  },
310
326
  execArgv: this._opts.execArgv
@@ -1,5 +1,35 @@
1
- import { IncomingMessage } from "node:http";
2
1
  import { Socket } from "node:net";
2
+ import { IncomingMessage } from "node:http";
3
+ /**
4
+ * How a virtual module is served. Code formats are Node's load formats, plus
5
+ * `jsx`/`tsx` (Bun only). Raw formats follow import attribute types: `text`
6
+ * default-exports a string, `bytes` a `Uint8Array`, and `wasm` a compiled
7
+ * `WebAssembly.Module` (like workerd's Wasm modules).
8
+ */
9
+ export type VirtualModuleFormat = "module" | "commonjs" | "module-typescript" | "commonjs-typescript" | "jsx" | "tsx" | "json" | "text" | "bytes" | "wasm";
10
+ /** A source with an explicit format. */
11
+ export interface VirtualModule {
12
+ /** Code or text; a `Uint8Array` for the `bytes` and `wasm` formats. */
13
+ source: string | Uint8Array;
14
+ /**
15
+ * Default: by the key's extension (`.cjs`, `.json`, `.ts`, `.wasm`, ...),
16
+ * else `module` for a string and `bytes` for a `Uint8Array`.
17
+ */
18
+ format?: VirtualModuleFormat;
19
+ }
20
+ /** A source whose format follows its key (string or bytes), or a source with its format. */
21
+ export type VirtualModuleContent = string | Uint8Array | VirtualModule;
22
+ /** Factories run on the host: before the worker spawns, and on updates. */
23
+ export type VirtualModuleSource = VirtualModuleContent | (() => VirtualModuleContent | Promise<VirtualModuleContent>);
24
+ /** Virtual modules as a `specifier => source` map. */
25
+ export type VirtualModules = Record<string, VirtualModuleSource>;
26
+ /** Changes to apply to a running map: a source sets (adds or replaces) a key, `null` removes it. */
27
+ export type VirtualModuleUpdates = Record<string, VirtualModuleSource | null>;
28
+ /**
29
+ * A validated module, as workers receive it: a string follows its key's
30
+ * extension, any other source carries its format.
31
+ */
32
+ export type ResolvedVirtualModule = string | Required<VirtualModule>;
3
33
  /** Proxy a request to the worker; relative inputs resolve against `http://localhost`. */
4
34
  export type FetchHandler = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
5
35
  /** Callback for receiving messages from the worker. */
@@ -65,6 +95,12 @@ export interface EnvRunner extends RunnerRPCHooks, AsyncDisposable {
65
95
  rpc<T = unknown>(name: string, data?: unknown, opts?: RPCOptions): Promise<T>;
66
96
  /** Re-import the entry module without restarting the worker/process. */
67
97
  reloadModule?(timeout?: number): Promise<void>;
98
+ /**
99
+ * Set (add or replace) and remove (`null`) virtual modules in one round trip.
100
+ * Changed and removed modules, and their importers, evaluate fresh on the
101
+ * next `reloadModule()`; a removed key falls through to normal resolution.
102
+ */
103
+ updateVirtualModules?(changes: VirtualModuleUpdates, timeout?: number): Promise<void>;
68
104
  /**
69
105
  * Invalidate a virtual module so the next `reloadModule()` re-evaluates it.
70
106
  * A factory-valued source is re-run for fresh contents.
@@ -1,5 +1,5 @@
1
1
  import { WorkerHooks } from "./types.mjs";
2
- import { EnvRunnerData } from "./common-base-runner.mjs";
2
+ import { EnvRunnerData, EnvRunnerPluginOption } from "./common-base-runner.mjs";
3
3
  import { NodeWorkerEnvRunner } from "./node-worker-runner.mjs";
4
4
  export declare class VercelEnvRunner extends NodeWorkerEnvRunner {
5
5
  constructor(opts: {
@@ -7,6 +7,8 @@ export declare class VercelEnvRunner extends NodeWorkerEnvRunner {
7
7
  workerEntry?: string;
8
8
  hooks?: WorkerHooks;
9
9
  data?: EnvRunnerData;
10
+ /** Host-side transform plugins (see `EnvRunnerPluginOption`). */
11
+ plugins?: EnvRunnerPluginOption[];
10
12
  });
11
13
  fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
12
14
  protected _runtimeType(): string;
package/dist/cli.mjs CHANGED
File without changes
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { EnvRunner, FetchHandler, NodeUpgradeContext, RPCOptions, RunnerMessageListener, RunnerRPCHooks, UpgradeContext, UpgradeHandler, WorkerAddress, WorkerHooks } from "./_chunks/types.mjs";
2
- import { BaseEnvRunner, EnvRunnerData, VirtualModuleSource, VirtualModules } from "./_chunks/common-base-runner.mjs";
1
+ import { EnvRunner, FetchHandler, NodeUpgradeContext, RPCOptions, RunnerMessageListener, RunnerRPCHooks, UpgradeContext, UpgradeHandler, VirtualModule, VirtualModuleContent, VirtualModuleFormat, VirtualModuleSource, VirtualModuleUpdates, VirtualModules, WorkerAddress, WorkerHooks } from "./_chunks/types.mjs";
2
+ import { BaseEnvRunner, EnvRunnerData, EnvRunnerPlugin, EnvRunnerPluginOption, PluginContext, PluginContextResolveOptions, PluginFilterExpression, PluginHook, PluginHookFilter, PluginLoadHandler, PluginLoadResult, PluginLog, PluginLogPosition, PluginModuleType, PluginResolveIdHandler, PluginResolveIdHook, PluginResolveIdOptions, PluginResolveIdResult, PluginResolvedId, PluginStringFilter, PluginTopLevelFilterExpression, PluginTransformFilter, PluginTransformHandler, PluginTransformMeta, PluginTransformResult } from "./_chunks/common-base-runner.mjs";
3
3
  import { RuntimeDep, resolveRuntimeDep } from "./_chunks/common-runtime-deps.mjs";
4
4
  import { DenoProcessEnvRunner } from "./_chunks/deno-process-runner.mjs";
5
5
  import { MiniflareEnvRunner, MiniflareEnvRunnerOptions, MiniflareExportInfo, TransformResult } from "./_chunks/miniflare-runner.mjs";
@@ -45,6 +45,11 @@ export declare class RunnerManager implements EnvRunner, AsyncDisposable {
45
45
  timeout?: number;
46
46
  }): Promise<T>;
47
47
  reloadModule(timeout?: number): Promise<void>;
48
+ /**
49
+ * Set or remove (`null`) virtual modules of the active runner (waiting for it
50
+ * during a reload); the next `fetch()` reloads the entry automatically.
51
+ */
52
+ updateVirtualModules(changes: VirtualModuleUpdates, timeout?: number): Promise<void>;
48
53
  /** Invalidate a virtual module; the next `fetch()` reloads the entry automatically. */
49
54
  invalidateModule(specifier: string, timeout?: number): Promise<void>;
50
55
  close(): Promise<void>;
@@ -56,7 +61,8 @@ export declare class RunnerManager implements EnvRunner, AsyncDisposable {
56
61
  private _internalListener;
57
62
  private _attach;
58
63
  private _detach;
59
- private _waitForRunner;
64
+ /** The active runner, waiting for one during a reload. */
65
+ protected _waitForRunner(timeout?: number): Promise<EnvRunner | undefined>;
60
66
  private _flushQueue;
61
67
  }
62
68
  type RunnerName = "node-worker" | "node-process" | "bun-process" | "deno-process" | "self" | "miniflare" | "vercel" | "netlify";
@@ -65,6 +71,8 @@ interface LoadRunnerOptions {
65
71
  workerEntry?: string;
66
72
  hooks?: WorkerHooks;
67
73
  data?: EnvRunnerData;
74
+ /** Host-side transform plugins (see `EnvRunnerPluginOption`). */
75
+ plugins?: EnvRunnerPluginOption[];
68
76
  execArgv?: string[];
69
77
  /** Runner-specific constructor options (e.g. `miniflare` for the `miniflare` runner). */
70
78
  [key: string]: unknown;
@@ -81,6 +89,8 @@ interface EnvServerOptions {
81
89
  hooks?: WorkerHooks;
82
90
  /** Additional data passed to the runner. */
83
91
  data?: Record<string, unknown>;
92
+ /** Host-side transform plugins, passed to every runner the server creates. */
93
+ plugins?: EnvRunnerPluginOption[];
84
94
  /** Custom exec arguments (e.g. `--inspect`). */
85
95
  execArgv?: string[];
86
96
  /** Enable watch mode to auto-reload on entry file changes. */
@@ -96,6 +106,7 @@ export declare class EnvServer extends RunnerManager {
96
106
  private _reloadTimeout;
97
107
  private _reloadListeners;
98
108
  private _startPromise;
109
+ private _virtual;
99
110
  runner: Awaited<ReturnType<typeof loadRunner>> | null;
100
111
  /** Register a listener called when the runner is reloaded due to a file change. */
101
112
  onReload(listener: () => void): void;
@@ -109,6 +120,12 @@ export declare class EnvServer extends RunnerManager {
109
120
  start(): Promise<this>;
110
121
  /** Replace the active runner; without an argument, create one from the server options. */
111
122
  reload(runner?: EnvRunner): Promise<void>;
123
+ /**
124
+ * Also kept in the server's own map, so runners it creates later (`reload()`,
125
+ * watch mode) start with the changes. Without an active runner (before the
126
+ * first start), the changes are only recorded.
127
+ */
128
+ updateVirtualModules(changes: VirtualModuleUpdates, timeout?: number): Promise<void>;
112
129
  close(): Promise<void>;
113
130
  /** Auto-start on first fetch so an explicit `start()` call is optional. */
114
131
  protected _fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
@@ -149,4 +166,4 @@ export declare const RESERVED_RUNTIME_OPTIONS: {
149
166
  };
150
167
  /** Callers add `fetch` (bound lazily so reloads swap it) and the crossws plugin. */
151
168
  export declare function toServerOptions(entry: AppEntry): Omit<ServerOptions, "fetch">;
152
- export { type AppEntry, type AppEntryIPC, type AppEntryIPCContext, BaseEnvRunner, DenoProcessEnvRunner, type EnvRunnerData as DenoProcessEnvRunnerData, type EnvRunner, type EnvRunnerData, type EnvServerOptions, type FetchHandler, type LoadRunnerOptions, MiniflareEnvRunner, type MiniflareEnvRunnerOptions, type MiniflareExportInfo, NetlifyEnvRunner, type NodeUpgradeContext, type RPCOptions, type RunnerMessageListener, type RunnerName, type RunnerRPCHooks, type RuntimeDep, type TransformResult, type UpgradeContext, type UpgradeHandler, VercelEnvRunner, type VirtualModuleSource, type VirtualModules, type WorkerAddress, type WorkerHooks, resolveRuntimeDep };
169
+ export { type AppEntry, type AppEntryIPC, type AppEntryIPCContext, BaseEnvRunner, DenoProcessEnvRunner, type EnvRunnerData as DenoProcessEnvRunnerData, type EnvRunner, type EnvRunnerData, type EnvRunnerPlugin, type EnvRunnerPluginOption, type EnvServerOptions, type FetchHandler, type LoadRunnerOptions, MiniflareEnvRunner, type MiniflareEnvRunnerOptions, type MiniflareExportInfo, NetlifyEnvRunner, type NodeUpgradeContext, type PluginContext, type PluginContextResolveOptions, type PluginFilterExpression, type PluginHook, type PluginHookFilter, type PluginLoadHandler, type PluginLoadResult, type PluginLog, type PluginLogPosition, type PluginModuleType, type PluginResolveIdHandler, type PluginResolveIdHook, type PluginResolveIdOptions, type PluginResolveIdResult, type PluginResolvedId, type PluginStringFilter, type PluginTopLevelFilterExpression, type PluginTransformFilter, type PluginTransformHandler, type PluginTransformMeta, type PluginTransformResult, type RPCOptions, type RunnerMessageListener, type RunnerName, type RunnerRPCHooks, type RuntimeDep, type TransformResult, type UpgradeContext, type UpgradeHandler, VercelEnvRunner, type VirtualModule, type VirtualModuleContent, type VirtualModuleFormat, type VirtualModuleSource, type VirtualModuleUpdates, type VirtualModules, type WorkerAddress, type WorkerHooks, resolveRuntimeDep };
@@ -1,5 +1,5 @@
1
1
  import { WorkerHooks } from "../../_chunks/types.mjs";
2
- import { BaseEnvRunner, EnvRunnerData } from "../../_chunks/common-base-runner.mjs";
2
+ import { BaseEnvRunner, EnvRunnerData, EnvRunnerPluginOption } from "../../_chunks/common-base-runner.mjs";
3
3
  export declare class BunProcessEnvRunner extends BaseEnvRunner {
4
4
  #private;
5
5
  constructor(opts: {
@@ -7,11 +7,14 @@ export declare class BunProcessEnvRunner extends BaseEnvRunner {
7
7
  workerEntry?: string;
8
8
  hooks?: WorkerHooks;
9
9
  data?: EnvRunnerData;
10
+ /** Host-side transform plugins (see `EnvRunnerPluginOption`). */
11
+ plugins?: EnvRunnerPluginOption[];
10
12
  execArgv?: string[];
11
13
  });
12
14
  sendMessage(message: unknown): void;
13
15
  protected _hasRuntime(): boolean;
14
16
  protected _runtimeType(): string;
17
+ protected _resolveConditions(): string[];
15
18
  protected _closeRuntime(): Promise<void>;
16
19
  }
17
20
  export type { EnvRunnerData as BunProcessEnvRunnerData };
@@ -1,10 +1,9 @@
1
1
  import { BaseEnvRunner } from "../../_chunks/common-base-runner.mjs";
2
- import { hostEnv } from "../../_chunks/common-host-env.mjs";
2
+ import { join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { existsSync } from "node:fs";
5
- import { join } from "node:path";
6
- import { execSync, spawn } from "node:child_process";
7
5
  import { homedir } from "node:os";
6
+ import { execSync, spawn } from "node:child_process";
8
7
  let _defaultEntry;
9
8
  let _bunPath;
10
9
  const _isBun = typeof Bun !== "undefined";
@@ -44,6 +43,13 @@ var BunProcessEnvRunner = class extends BaseEnvRunner {
44
43
  _runtimeType() {
45
44
  return "process";
46
45
  }
46
+ _resolveConditions() {
47
+ return [
48
+ "bun",
49
+ "node",
50
+ "import"
51
+ ];
52
+ }
47
53
  async _closeRuntime() {
48
54
  if (!this.#process) return;
49
55
  this.#process.removeAllListeners?.();
@@ -57,10 +63,7 @@ var BunProcessEnvRunner = class extends BaseEnvRunner {
57
63
  this.close(`process entry not found in "${this._workerEntry}".`);
58
64
  return;
59
65
  }
60
- const env = hostEnv({
61
- ENV_RUNNER_NAME: this._name,
62
- ENV_RUNNER_DATA: JSON.stringify(this._data || {})
63
- });
66
+ const env = this._processEnv();
64
67
  if (_isBun) this.#initBunProcess(execArgv, env);
65
68
  else this.#initNodeProcess(execArgv, env);
66
69
  }
@@ -78,7 +81,7 @@ var BunProcessEnvRunner = class extends BaseEnvRunner {
78
81
  "pipe"
79
82
  ],
80
83
  ipc: (message) => {
81
- this._handleMessage(message);
84
+ this._handleProcessMessage(message);
82
85
  }
83
86
  });
84
87
  const child = {
@@ -128,7 +131,7 @@ var BunProcessEnvRunner = class extends BaseEnvRunner {
128
131
  }
129
132
  });
130
133
  child.on("message", (message) => {
131
- this._handleMessage(message);
134
+ this._handleProcessMessage(message);
132
135
  });
133
136
  child.stdout?.pipe(process.stdout);
134
137
  child.stderr?.pipe(process.stderr);
@@ -1,16 +1,17 @@
1
- import { handleInvalidateModule, isVirtualSpecifier, parseServerAddress, registerVirtualModules, reloadEntryModule, resolveEntry, toServerOptions } from "../../_chunks/common-worker-utils.mjs";
1
+ import { handleUpdateVirtualModules } from "../../_chunks/plugin-hooks.mjs";
2
+ import { formatInitError, isVirtualEntry, parseServerAddress, registerWorkerHooks, reloadEntryModule, resolveEntry, toServerOptions } from "../../_chunks/common-worker-utils.mjs";
3
+ import { receiveProcessData } from "../../_chunks/common-process-data.mjs";
2
4
  import { serve } from "srvx";
3
5
  import { plugin } from "crossws/server";
4
6
  process.on("disconnect", () => process.exit(0));
5
- const data = JSON.parse(process.env.ENV_RUNNER_DATA || "{}");
7
+ const data = await receiveProcessData();
6
8
  const sendMessage = (message) => process.send(message);
7
- const virtualEntry = isVirtualSpecifier(data.entry, data.virtual);
8
- let unregisterVirtualModules;
9
+ let unregisterHooks;
9
10
  let entry;
10
11
  let server;
11
12
  try {
12
- unregisterVirtualModules = await registerVirtualModules(data.virtual);
13
- entry = await resolveEntry(data.entry, virtualEntry);
13
+ unregisterHooks = await registerWorkerHooks(data);
14
+ entry = await resolveEntry(data.entry, isVirtualEntry(data.entry));
14
15
  server = serve({
15
16
  ...toServerOptions(entry),
16
17
  fetch: (request) => entry.fetch(request),
@@ -18,7 +19,7 @@ try {
18
19
  });
19
20
  await server.ready();
20
21
  } catch (error) {
21
- const message = error?.message || String(error);
22
+ const message = formatInitError(error);
22
23
  sendMessage({
23
24
  event: "init-error",
24
25
  error: message
@@ -38,14 +39,14 @@ process.send({ address: parseServerAddress(server) });
38
39
  process.on("message", async (message) => {
39
40
  if (message?.event === "shutdown") {
40
41
  Promise.resolve(entry.ipc?.onClose?.()).then(() => server.close()).then(() => {
41
- unregisterVirtualModules();
42
+ unregisterHooks();
42
43
  process.send({ event: "exit" });
43
44
  });
44
45
  return;
45
46
  }
46
47
  if (message?.event === "reload-module") {
47
48
  try {
48
- entry = await reloadEntryModule(data.entry, entry, sendMessage, virtualEntry);
49
+ entry = await reloadEntryModule(data.entry, entry, sendMessage, isVirtualEntry(data.entry));
49
50
  process.send({ event: "module-reloaded" });
50
51
  } catch (error) {
51
52
  process.send({
@@ -55,8 +56,8 @@ process.on("message", async (message) => {
55
56
  }
56
57
  return;
57
58
  }
58
- if (message?.event === "invalidate-module") {
59
- handleInvalidateModule(message, sendMessage);
59
+ if (message?.event === "update-virtual-modules") {
60
+ handleUpdateVirtualModules(message, sendMessage);
60
61
  return;
61
62
  }
62
63
  if (message?.type === "ping") {
@@ -1,16 +1,17 @@
1
- import { handleInvalidateModule, isVirtualSpecifier, parseServerAddress, registerVirtualModules, reloadEntryModule, resolveEntry, toServerOptions } from "../../_chunks/common-worker-utils.mjs";
1
+ import { handleUpdateVirtualModules } from "../../_chunks/plugin-hooks.mjs";
2
+ import { formatInitError, isVirtualEntry, parseServerAddress, registerWorkerHooks, reloadEntryModule, resolveEntry, toServerOptions } from "../../_chunks/common-worker-utils.mjs";
3
+ import { receiveProcessData } from "../../_chunks/common-process-data.mjs";
2
4
  import { serve } from "srvx";
3
5
  import { plugin } from "crossws/server";
4
6
  process.on("disconnect", () => process.exit(0));
5
- const data = JSON.parse(process.env.ENV_RUNNER_DATA || "{}");
7
+ const data = await receiveProcessData();
6
8
  const sendMessage = (message) => process.send(message);
7
- const virtualEntry = isVirtualSpecifier(data.entry, data.virtual);
8
- let unregisterVirtualModules;
9
+ let unregisterHooks;
9
10
  let entry;
10
11
  let server;
11
12
  try {
12
- unregisterVirtualModules = await registerVirtualModules(data.virtual);
13
- entry = await resolveEntry(data.entry, virtualEntry);
13
+ unregisterHooks = await registerWorkerHooks(data);
14
+ entry = await resolveEntry(data.entry, isVirtualEntry(data.entry));
14
15
  server = serve({
15
16
  ...toServerOptions(entry),
16
17
  fetch: (request) => entry.fetch(request),
@@ -18,7 +19,7 @@ try {
18
19
  });
19
20
  await server.ready();
20
21
  } catch (error) {
21
- const message = error?.message || String(error);
22
+ const message = formatInitError(error);
22
23
  sendMessage({
23
24
  event: "init-error",
24
25
  error: message
@@ -38,14 +39,14 @@ process.send({ address: parseServerAddress(server) });
38
39
  process.on("message", async (message) => {
39
40
  if (message?.event === "shutdown") {
40
41
  Promise.resolve(entry.ipc?.onClose?.()).then(() => server.close()).then(() => {
41
- unregisterVirtualModules();
42
+ unregisterHooks();
42
43
  process.send({ event: "exit" });
43
44
  });
44
45
  return;
45
46
  }
46
47
  if (message?.event === "reload-module") {
47
48
  try {
48
- entry = await reloadEntryModule(data.entry, entry, sendMessage, virtualEntry);
49
+ entry = await reloadEntryModule(data.entry, entry, sendMessage, isVirtualEntry(data.entry));
49
50
  process.send({ event: "module-reloaded" });
50
51
  } catch (error) {
51
52
  process.send({
@@ -55,8 +56,8 @@ process.on("message", async (message) => {
55
56
  }
56
57
  return;
57
58
  }
58
- if (message?.event === "invalidate-module") {
59
- handleInvalidateModule(message, sendMessage);
59
+ if (message?.event === "update-virtual-modules") {
60
+ handleUpdateVirtualModules(message, sendMessage);
60
61
  return;
61
62
  }
62
63
  if (message?.type === "ping") {
@@ -1,5 +1,5 @@
1
1
  import { WorkerHooks } from "../../_chunks/types.mjs";
2
- import { BaseEnvRunner, EnvRunnerData } from "../../_chunks/common-base-runner.mjs";
2
+ import { BaseEnvRunner, EnvRunnerData, EnvRunnerPluginOption } from "../../_chunks/common-base-runner.mjs";
3
3
  export declare class NodeProcessEnvRunner extends BaseEnvRunner {
4
4
  #private;
5
5
  constructor(opts: {
@@ -7,6 +7,8 @@ export declare class NodeProcessEnvRunner extends BaseEnvRunner {
7
7
  workerEntry?: string;
8
8
  hooks?: WorkerHooks;
9
9
  data?: EnvRunnerData;
10
+ /** Host-side transform plugins (see `EnvRunnerPluginOption`). */
11
+ plugins?: EnvRunnerPluginOption[];
10
12
  execArgv?: string[];
11
13
  });
12
14
  sendMessage(message: unknown): void;
@@ -1,5 +1,4 @@
1
1
  import { BaseEnvRunner } from "../../_chunks/common-base-runner.mjs";
2
- import { hostEnv } from "../../_chunks/common-host-env.mjs";
3
2
  import { fileURLToPath } from "node:url";
4
3
  import { existsSync } from "node:fs";
5
4
  import { fork } from "node:child_process";
@@ -36,10 +35,7 @@ var NodeProcessEnvRunner = class extends BaseEnvRunner {
36
35
  return;
37
36
  }
38
37
  const child = fork(this._workerEntry, [], {
39
- env: hostEnv({
40
- ENV_RUNNER_NAME: this._name,
41
- ENV_RUNNER_DATA: JSON.stringify(this._data || {})
42
- }),
38
+ env: this._processEnv(),
43
39
  stdio: [
44
40
  "pipe",
45
41
  "pipe",
@@ -57,7 +53,7 @@ var NodeProcessEnvRunner = class extends BaseEnvRunner {
57
53
  this.close(error);
58
54
  });
59
55
  child.on("message", (message) => {
60
- this._handleMessage(message);
56
+ this._handleProcessMessage(message);
61
57
  });
62
58
  child.stdout?.pipe(process.stdout);
63
59
  child.stderr?.pipe(process.stderr);
@@ -1,16 +1,17 @@
1
- import { handleInvalidateModule, isVirtualSpecifier, parseServerAddress, registerVirtualModules, reloadEntryModule, resolveEntry, toServerOptions } from "../../_chunks/common-worker-utils.mjs";
1
+ import { handleUpdateVirtualModules } from "../../_chunks/plugin-hooks.mjs";
2
+ import { formatInitError, isVirtualEntry, parseServerAddress, registerWorkerHooks, reloadEntryModule, resolveEntry, toServerOptions } from "../../_chunks/common-worker-utils.mjs";
3
+ import { receiveProcessData } from "../../_chunks/common-process-data.mjs";
2
4
  import { serve } from "srvx";
3
5
  import { plugin } from "crossws/server";
4
6
  process.on("disconnect", () => process.exit(0));
5
- const data = JSON.parse(process.env.ENV_RUNNER_DATA || "{}");
7
+ const data = await receiveProcessData();
6
8
  const sendMessage = (message) => process.send(message);
7
- const virtualEntry = isVirtualSpecifier(data.entry, data.virtual);
8
- let unregisterVirtualModules;
9
+ let unregisterHooks;
9
10
  let entry;
10
11
  let server;
11
12
  try {
12
- unregisterVirtualModules = await registerVirtualModules(data.virtual);
13
- entry = await resolveEntry(data.entry, virtualEntry);
13
+ unregisterHooks = await registerWorkerHooks(data);
14
+ entry = await resolveEntry(data.entry, isVirtualEntry(data.entry));
14
15
  server = serve({
15
16
  ...toServerOptions(entry),
16
17
  fetch: (request) => entry.fetch(request),
@@ -18,7 +19,7 @@ try {
18
19
  });
19
20
  await server.ready();
20
21
  } catch (error) {
21
- const message = error?.message || String(error);
22
+ const message = formatInitError(error);
22
23
  sendMessage({
23
24
  event: "init-error",
24
25
  error: message
@@ -38,14 +39,14 @@ process.send({ address: parseServerAddress(server) });
38
39
  process.on("message", async (message) => {
39
40
  if (message?.event === "shutdown") {
40
41
  Promise.resolve(entry.ipc?.onClose?.()).then(() => server.close()).then(() => {
41
- unregisterVirtualModules();
42
+ unregisterHooks();
42
43
  process.send({ event: "exit" });
43
44
  });
44
45
  return;
45
46
  }
46
47
  if (message?.event === "reload-module") {
47
48
  try {
48
- entry = await reloadEntryModule(data.entry, entry, sendMessage, virtualEntry);
49
+ entry = await reloadEntryModule(data.entry, entry, sendMessage, isVirtualEntry(data.entry));
49
50
  process.send({ event: "module-reloaded" });
50
51
  } catch (error) {
51
52
  process.send({
@@ -55,8 +56,8 @@ process.on("message", async (message) => {
55
56
  }
56
57
  return;
57
58
  }
58
- if (message?.event === "invalidate-module") {
59
- handleInvalidateModule(message, sendMessage);
59
+ if (message?.event === "update-virtual-modules") {
60
+ handleUpdateVirtualModules(message, sendMessage);
60
61
  return;
61
62
  }
62
63
  if (message?.type === "ping") {
@@ -1,16 +1,16 @@
1
- import { handleInvalidateModule, isVirtualSpecifier, parseServerAddress, registerVirtualModules, reloadEntryModule, resolveEntry, toServerOptions } from "../../_chunks/common-worker-utils.mjs";
1
+ import { handleUpdateVirtualModules } from "../../_chunks/plugin-hooks.mjs";
2
+ import { formatInitError, isVirtualEntry, parseServerAddress, registerWorkerHooks, reloadEntryModule, resolveEntry, toServerOptions } from "../../_chunks/common-worker-utils.mjs";
2
3
  import { parentPort, workerData } from "node:worker_threads";
3
4
  import { serve } from "srvx";
4
5
  import { plugin } from "crossws/server";
5
6
  const data = workerData || {};
6
7
  const sendMessage = (message) => parentPort?.postMessage(message);
7
- const virtualEntry = isVirtualSpecifier(data.entry, data.virtual);
8
- let unregisterVirtualModules;
8
+ let unregisterHooks;
9
9
  let entry;
10
10
  let server;
11
11
  try {
12
- unregisterVirtualModules = await registerVirtualModules(data.virtual);
13
- entry = await resolveEntry(data.entry, virtualEntry);
12
+ unregisterHooks = await registerWorkerHooks(data);
13
+ entry = await resolveEntry(data.entry, isVirtualEntry(data.entry));
14
14
  server = serve({
15
15
  ...toServerOptions(entry),
16
16
  fetch: (request) => entry.fetch(request),
@@ -18,7 +18,7 @@ try {
18
18
  });
19
19
  await server.ready();
20
20
  } catch (error) {
21
- const message = error?.message || String(error);
21
+ const message = formatInitError(error);
22
22
  sendMessage({
23
23
  event: "init-error",
24
24
  error: message
@@ -38,14 +38,14 @@ parentPort?.postMessage({ address: parseServerAddress(server) });
38
38
  parentPort?.on("message", async (message) => {
39
39
  if (message?.event === "shutdown") {
40
40
  Promise.resolve(entry.ipc?.onClose?.()).then(() => server.close()).then(() => {
41
- unregisterVirtualModules();
41
+ unregisterHooks();
42
42
  parentPort?.postMessage({ event: "exit" });
43
43
  });
44
44
  return;
45
45
  }
46
46
  if (message?.event === "reload-module") {
47
47
  try {
48
- entry = await reloadEntryModule(data.entry, entry, sendMessage, virtualEntry);
48
+ entry = await reloadEntryModule(data.entry, entry, sendMessage, isVirtualEntry(data.entry));
49
49
  parentPort?.postMessage({ event: "module-reloaded" });
50
50
  } catch (error) {
51
51
  parentPort?.postMessage({
@@ -55,8 +55,8 @@ parentPort?.on("message", async (message) => {
55
55
  }
56
56
  return;
57
57
  }
58
- if (message?.event === "invalidate-module") {
59
- handleInvalidateModule(message, sendMessage);
58
+ if (message?.event === "update-virtual-modules") {
59
+ handleUpdateVirtualModules(message, sendMessage);
60
60
  return;
61
61
  }
62
62
  if (message?.type === "ping") {
@@ -1,11 +1,13 @@
1
- import { WorkerHooks } from "../../_chunks/types.mjs";
2
- import { BaseEnvRunner, EnvRunnerData } from "../../_chunks/common-base-runner.mjs";
1
+ import { VirtualModuleUpdates, WorkerHooks } from "../../_chunks/types.mjs";
2
+ import { BaseEnvRunner, EnvRunnerData, EnvRunnerPluginOption } from "../../_chunks/common-base-runner.mjs";
3
3
  export declare class SelfEnvRunner extends BaseEnvRunner {
4
4
  #private;
5
5
  constructor(opts: {
6
6
  name: string;
7
7
  hooks?: WorkerHooks;
8
8
  data?: EnvRunnerData;
9
+ /** Not supported: the runner closes with an error. */
10
+ plugins?: EnvRunnerPluginOption[];
9
11
  });
10
12
  fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
11
13
  upgrade(context: {
@@ -16,6 +18,7 @@ export declare class SelfEnvRunner extends BaseEnvRunner {
16
18
  };
17
19
  }): Promise<void>;
18
20
  sendMessage(message: unknown): void;
21
+ updateVirtualModules(_changes: VirtualModuleUpdates): Promise<void>;
19
22
  invalidateModule(specifier: string): Promise<void>;
20
23
  reloadModule(): Promise<void>;
21
24
  protected _hasRuntime(): boolean;
@@ -39,6 +39,9 @@ var SelfEnvRunner = class extends BaseEnvRunner {
39
39
  }
40
40
  this.#entry?.ipc?.onMessage?.(message);
41
41
  }
42
+ async updateVirtualModules(_changes) {
43
+ throw new Error("Cannot update virtual modules: the self runner does not support virtual modules");
44
+ }
42
45
  async invalidateModule(specifier) {
43
46
  throw new Error(`Cannot invalidate "${specifier}": the self runner does not support virtual modules`);
44
47
  }
@@ -79,8 +82,17 @@ var SelfEnvRunner = class extends BaseEnvRunner {
79
82
  this.close("self runner requires data.entry");
80
83
  return;
81
84
  }
85
+ if (Object.keys(this._data?.virtual ?? {}).length > 0) {
86
+ this.close(/* @__PURE__ */ new Error("Cannot use data.virtual: the self runner does not support virtual modules"));
87
+ return;
88
+ }
89
+ if (this._plugins) {
90
+ this.close(/* @__PURE__ */ new Error("Cannot use plugins: the self runner does not support them"));
91
+ return;
92
+ }
82
93
  this.#active = true;
83
94
  resolveEntry(entryPath).then(async (entry) => {
95
+ if (this.closed) return;
84
96
  this.#entry = entry;
85
97
  await entry.ipc?.onOpen?.({ sendMessage: (message) => {
86
98
  queueMicrotask(() => this._handleMessage(message));