env-runner 0.2.2 → 0.3.0
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 +203 -26
- package/dist/_chunks/common-base-runner.d.mts +58 -51
- package/dist/_chunks/common-base-runner.mjs +127 -33
- package/dist/_chunks/common-process-data.mjs +13 -0
- package/dist/_chunks/common-runtime-deps.d.mts +7 -30
- package/dist/_chunks/common-worker-utils.mjs +464 -73
- package/dist/_chunks/deno-process-runner.mjs +11 -37
- package/dist/_chunks/lexer.mjs +2 -0
- package/dist/_chunks/libs/cjs-module-lexer.mjs +6 -1
- package/dist/_chunks/miniflare-runner.d.mts +56 -107
- package/dist/_chunks/miniflare-runner.mjs +834 -154
- package/dist/_chunks/netlify-runner.d.mts +3 -21
- package/dist/_chunks/node-worker-runner.mjs +1 -2
- package/dist/_chunks/server.mjs +20 -5
- package/dist/_chunks/types.d.mts +36 -9
- package/dist/_chunks/vercel-runner.mjs +1 -0
- package/dist/_chunks/virtual-loader.mjs +303 -29
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +31 -69
- package/dist/runners/bun-process/runner.mjs +4 -8
- package/dist/runners/bun-process/worker.mjs +8 -8
- package/dist/runners/deno-process/worker.mjs +17 -39
- package/dist/runners/node-process/runner.mjs +2 -6
- package/dist/runners/node-process/worker.mjs +8 -8
- package/dist/runners/node-worker/worker.mjs +6 -7
- package/dist/runners/self/runner.d.mts +2 -1
- package/dist/runners/self/runner.mjs +8 -0
- package/dist/runners/vercel/queue-dev.d.mts +9 -45
- package/dist/vite.d.mts +2 -12
- package/package.json +13 -13
- package/dist/_chunks/common-host-env.mjs +0 -11
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
|
|
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 } 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";
|
|
@@ -7,10 +7,7 @@ import { VercelEnvRunner } from "./_chunks/vercel-runner.mjs";
|
|
|
7
7
|
import { NetlifyEnvRunner } from "./_chunks/netlify-runner.mjs";
|
|
8
8
|
import { ServerOptions, ServerPlugin } from "srvx";
|
|
9
9
|
import { Hooks } from "crossws";
|
|
10
|
-
/**
|
|
11
|
-
* Manages an active `EnvRunner` instance, proxying all calls to it.
|
|
12
|
-
* Supports hot-reload, auto-restart on unexpected exit, and message queueing.
|
|
13
|
-
*/
|
|
10
|
+
/** Proxies an active `EnvRunner` with hot-reload, auto-restart and message queueing. */
|
|
14
11
|
export declare class RunnerManager implements EnvRunner, AsyncDisposable {
|
|
15
12
|
private _runner;
|
|
16
13
|
private _messageQueue;
|
|
@@ -27,30 +24,18 @@ export declare class RunnerManager implements EnvRunner, AsyncDisposable {
|
|
|
27
24
|
get closed(): boolean;
|
|
28
25
|
get address(): WorkerAddress | undefined;
|
|
29
26
|
/**
|
|
30
|
-
* Replace the active runner
|
|
31
|
-
*
|
|
32
|
-
* When called without a runner, a fresh one is created via `_createRunner()`
|
|
33
|
-
* (only available on subclasses with a runner factory, e.g. `EnvServer`).
|
|
27
|
+
* Replace (and close) the active runner. Without an argument, subclasses with
|
|
28
|
+
* `_createRunner()` (e.g. `EnvServer`) create a fresh one.
|
|
34
29
|
*/
|
|
35
30
|
reload(runner?: EnvRunner): Promise<void>;
|
|
36
31
|
/** Create a fresh runner for argument-less `reload()`. Overridden by subclasses with a runner factory. */
|
|
37
32
|
protected _createRunner(): EnvRunner | Promise<EnvRunner>;
|
|
38
33
|
fetch: FetchHandler;
|
|
39
34
|
protected _fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
|
|
40
|
-
/**
|
|
41
|
-
* Lazily satisfy a pending `invalidateModule()` with a single entry reload
|
|
42
|
-
* before serving — concurrent fetches share the same reload. A failed reload
|
|
43
|
-
* keeps the invalidation pending, so the next fetch retries.
|
|
44
|
-
*/
|
|
35
|
+
/** Apply a pending invalidation with one shared reload; a failure retries on the next fetch. */
|
|
45
36
|
private _flushInvalidation;
|
|
46
37
|
upgrade: UpgradeHandler;
|
|
47
|
-
/**
|
|
48
|
-
* Create a runtime-native WebSocket reverse-proxy plugin for the public srvx
|
|
49
|
-
* server. Attach it via `serve({ plugins: [await manager.wsSrvxPlugin()] })`:
|
|
50
|
-
* on Node it proxies the raw upgrade socket to the worker, and on Bun/Deno it
|
|
51
|
-
* bridges the WebSocket with crossws. The plugin reads the active runner
|
|
52
|
-
* lazily, so it keeps working across hot-reloads.
|
|
53
|
-
*/
|
|
38
|
+
/** WebSocket proxy plugin: `serve({ plugins: [await manager.wsSrvxPlugin()] })`. */
|
|
54
39
|
wsSrvxPlugin(): Promise<ServerPlugin>;
|
|
55
40
|
sendMessage(message: unknown): void;
|
|
56
41
|
onMessage(listener: RunnerMessageListener): void;
|
|
@@ -61,10 +46,11 @@ export declare class RunnerManager implements EnvRunner, AsyncDisposable {
|
|
|
61
46
|
}): Promise<T>;
|
|
62
47
|
reloadModule(timeout?: number): Promise<void>;
|
|
63
48
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* don't need to pair the call with an explicit `reloadModule()`.
|
|
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.
|
|
67
51
|
*/
|
|
52
|
+
updateVirtualModules(changes: VirtualModuleUpdates, timeout?: number): Promise<void>;
|
|
53
|
+
/** Invalidate a virtual module; the next `fetch()` reloads the entry automatically. */
|
|
68
54
|
invalidateModule(specifier: string, timeout?: number): Promise<void>;
|
|
69
55
|
close(): Promise<void>;
|
|
70
56
|
[Symbol.asyncDispose](): Promise<void>;
|
|
@@ -75,7 +61,8 @@ export declare class RunnerManager implements EnvRunner, AsyncDisposable {
|
|
|
75
61
|
private _internalListener;
|
|
76
62
|
private _attach;
|
|
77
63
|
private _detach;
|
|
78
|
-
|
|
64
|
+
/** The active runner, waiting for one during a reload. */
|
|
65
|
+
protected _waitForRunner(timeout?: number): Promise<EnvRunner | undefined>;
|
|
79
66
|
private _flushQueue;
|
|
80
67
|
}
|
|
81
68
|
type RunnerName = "node-worker" | "node-process" | "bun-process" | "deno-process" | "self" | "miniflare" | "vercel" | "netlify";
|
|
@@ -85,11 +72,7 @@ interface LoadRunnerOptions {
|
|
|
85
72
|
hooks?: WorkerHooks;
|
|
86
73
|
data?: EnvRunnerData;
|
|
87
74
|
execArgv?: string[];
|
|
88
|
-
/**
|
|
89
|
-
* Additional runner-specific options (passed through to the runner
|
|
90
|
-
* constructor) — e.g. `miniflare` (the imported `miniflare` package) for the
|
|
91
|
-
* `miniflare` runner, which this package never imports itself.
|
|
92
|
-
*/
|
|
75
|
+
/** Runner-specific constructor options (e.g. `miniflare` for the `miniflare` runner). */
|
|
93
76
|
[key: string]: unknown;
|
|
94
77
|
}
|
|
95
78
|
export declare function loadRunner(runner: RunnerName, opts: LoadRunnerOptions): Promise<EnvRunner>;
|
|
@@ -110,13 +93,7 @@ interface EnvServerOptions {
|
|
|
110
93
|
watch?: boolean;
|
|
111
94
|
/** Additional paths to watch (directories or files). */
|
|
112
95
|
watchPaths?: string[];
|
|
113
|
-
/**
|
|
114
|
-
* Extra runner-specific constructor options, spread into the runner.
|
|
115
|
-
*
|
|
116
|
-
* Runners that build on external packages take them as explicit options —
|
|
117
|
-
* e.g. `{ miniflare }` for the `miniflare` runner (env-runner never imports
|
|
118
|
-
* those packages itself).
|
|
119
|
-
*/
|
|
96
|
+
/** Runner-specific constructor options (e.g. `{ miniflare }`). */
|
|
120
97
|
runnerOptions?: Record<string, unknown>;
|
|
121
98
|
}
|
|
122
99
|
export declare class EnvServer extends RunnerManager {
|
|
@@ -125,6 +102,7 @@ export declare class EnvServer extends RunnerManager {
|
|
|
125
102
|
private _reloadTimeout;
|
|
126
103
|
private _reloadListeners;
|
|
127
104
|
private _startPromise;
|
|
105
|
+
private _virtual;
|
|
128
106
|
runner: Awaited<ReturnType<typeof loadRunner>> | null;
|
|
129
107
|
/** Register a listener called when the runner is reloaded due to a file change. */
|
|
130
108
|
onReload(listener: () => void): void;
|
|
@@ -132,18 +110,18 @@ export declare class EnvServer extends RunnerManager {
|
|
|
132
110
|
offReload(listener: () => void): void;
|
|
133
111
|
constructor(opts: EnvServerOptions);
|
|
134
112
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* Idempotent — concurrent and repeated calls share one startup. Calling
|
|
138
|
-
* `start()` explicitly is optional: the first `fetch()` auto-starts the
|
|
139
|
-
* server. A failed start resets so a later call can retry.
|
|
113
|
+
* Load and attach the runner. Idempotent and optional (the first `fetch()`
|
|
114
|
+
* auto-starts); a failed start can be retried.
|
|
140
115
|
*/
|
|
141
116
|
start(): Promise<this>;
|
|
117
|
+
/** Replace the active runner; without an argument, create one from the server options. */
|
|
118
|
+
reload(runner?: EnvRunner): Promise<void>;
|
|
142
119
|
/**
|
|
143
|
-
*
|
|
144
|
-
*
|
|
120
|
+
* Also kept in the server's own map, so runners it creates later (`reload()`,
|
|
121
|
+
* watch mode) start with the changes. Without an active runner (before the
|
|
122
|
+
* first start), the changes are only recorded.
|
|
145
123
|
*/
|
|
146
|
-
|
|
124
|
+
updateVirtualModules(changes: VirtualModuleUpdates, timeout?: number): Promise<void>;
|
|
147
125
|
close(): Promise<void>;
|
|
148
126
|
/** Auto-start on first fetch so an explicit `start()` call is optional. */
|
|
149
127
|
protected _fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
|
|
@@ -162,12 +140,8 @@ interface AppEntryIPC {
|
|
|
162
140
|
onClose?: () => void | Promise<void>;
|
|
163
141
|
}
|
|
164
142
|
/**
|
|
165
|
-
* User entry module
|
|
166
|
-
*
|
|
167
|
-
* `maxRequestBodySize`, `trustProxy`, `node`/`bun`/`deno`) is forwarded to
|
|
168
|
-
* `serve()` by the built-in workers. The listener-related keys in
|
|
169
|
-
* {@link RESERVED_SERVER_OPTIONS} are owned by the worker (it sits behind the
|
|
170
|
-
* runner's proxy) and are ignored if set.
|
|
143
|
+
* User entry module. Other srvx `ServerOptions` are forwarded to `serve()`,
|
|
144
|
+
* except {@link RESERVED_SERVER_OPTIONS}.
|
|
171
145
|
*/
|
|
172
146
|
interface AppEntry extends Omit<ServerOptions, "fetch"> {
|
|
173
147
|
fetch: ServerOptions["fetch"];
|
|
@@ -175,29 +149,17 @@ interface AppEntry extends Omit<ServerOptions, "fetch"> {
|
|
|
175
149
|
websocket?: Partial<Hooks>;
|
|
176
150
|
ipc?: AppEntryIPC;
|
|
177
151
|
}
|
|
178
|
-
/**
|
|
179
|
-
* srvx options the built-in workers always control themselves: the worker
|
|
180
|
-
* listens on a random loopback port behind the runner's proxy, so the entry
|
|
181
|
-
* must not be able to relocate or TLS-terminate it.
|
|
182
|
-
*/
|
|
152
|
+
/** Worker-owned srvx options: the worker listens on a random loopback port behind the proxy. */
|
|
183
153
|
export declare const RESERVED_SERVER_OPTIONS: ["port", "hostname", "protocol", "tls", "silent", "manual", "gracefulShutdown"];
|
|
184
154
|
/**
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
* otherwise bypass {@link RESERVED_SERVER_OPTIONS}. `node.http2` is dropped
|
|
188
|
-
* too: srvx requires a TLS certificate for it, which the worker never has.
|
|
155
|
+
* srvx spreads `node`/`bun`/`deno` after its resolved listener options, so
|
|
156
|
+
* these would bypass {@link RESERVED_SERVER_OPTIONS}. `node.http2` needs TLS.
|
|
189
157
|
*/
|
|
190
158
|
export declare const RESERVED_RUNTIME_OPTIONS: {
|
|
191
159
|
readonly node: readonly ["port", "host", "path", "http2", "cert", "key", "passphrase"];
|
|
192
160
|
readonly bun: readonly ["port", "hostname", "unix", "tls"];
|
|
193
161
|
readonly deno: readonly ["port", "hostname", "path", "cert", "key"];
|
|
194
162
|
};
|
|
195
|
-
/**
|
|
196
|
-
* Build the srvx `serve()` options for a user entry: forwards every srvx
|
|
197
|
-
* option exported by the entry, drops env-runner specific keys (`fetch`,
|
|
198
|
-
* `upgrade`, `websocket`, `ipc`) and pins the worker-owned listener options
|
|
199
|
-
* (top-level and nested in `node`/`bun`/`deno`). Callers add `fetch` (bound
|
|
200
|
-
* lazily so module reloads swap the handler) and the crossws plugin themselves.
|
|
201
|
-
*/
|
|
163
|
+
/** Callers add `fetch` (bound lazily so reloads swap it) and the crossws plugin. */
|
|
202
164
|
export declare function toServerOptions(entry: AppEntry): Omit<ServerOptions, "fetch">;
|
|
203
|
-
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 };
|
|
165
|
+
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 VirtualModule, type VirtualModuleContent, type VirtualModuleFormat, type VirtualModuleSource, type VirtualModuleUpdates, type VirtualModules, type WorkerAddress, type WorkerHooks, resolveRuntimeDep };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { BaseEnvRunner } from "../../_chunks/common-base-runner.mjs";
|
|
2
|
-
import {
|
|
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
5
|
import { execSync, spawn } from "node:child_process";
|
|
7
6
|
import { homedir } from "node:os";
|
|
8
7
|
let _defaultEntry;
|
|
@@ -57,10 +56,7 @@ var BunProcessEnvRunner = class extends BaseEnvRunner {
|
|
|
57
56
|
this.close(`process entry not found in "${this._workerEntry}".`);
|
|
58
57
|
return;
|
|
59
58
|
}
|
|
60
|
-
const env =
|
|
61
|
-
ENV_RUNNER_NAME: this._name,
|
|
62
|
-
ENV_RUNNER_DATA: JSON.stringify(this._data || {})
|
|
63
|
-
});
|
|
59
|
+
const env = this._processEnv();
|
|
64
60
|
if (_isBun) this.#initBunProcess(execArgv, env);
|
|
65
61
|
else this.#initNodeProcess(execArgv, env);
|
|
66
62
|
}
|
|
@@ -78,7 +74,7 @@ var BunProcessEnvRunner = class extends BaseEnvRunner {
|
|
|
78
74
|
"pipe"
|
|
79
75
|
],
|
|
80
76
|
ipc: (message) => {
|
|
81
|
-
this.
|
|
77
|
+
this._handleProcessMessage(message);
|
|
82
78
|
}
|
|
83
79
|
});
|
|
84
80
|
const child = {
|
|
@@ -128,7 +124,7 @@ var BunProcessEnvRunner = class extends BaseEnvRunner {
|
|
|
128
124
|
}
|
|
129
125
|
});
|
|
130
126
|
child.on("message", (message) => {
|
|
131
|
-
this.
|
|
127
|
+
this._handleProcessMessage(message);
|
|
132
128
|
});
|
|
133
129
|
child.stdout?.pipe(process.stdout);
|
|
134
130
|
child.stderr?.pipe(process.stderr);
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { formatInitError, handleUpdateVirtualModules, isVirtualEntry, parseServerAddress, registerVirtualModules, reloadEntryModule, resolveEntry, toServerOptions } from "../../_chunks/common-worker-utils.mjs";
|
|
2
|
+
import { receiveProcessData } from "../../_chunks/common-process-data.mjs";
|
|
2
3
|
import { serve } from "srvx";
|
|
3
4
|
import { plugin } from "crossws/server";
|
|
4
5
|
process.on("disconnect", () => process.exit(0));
|
|
5
|
-
const data =
|
|
6
|
+
const data = await receiveProcessData();
|
|
6
7
|
const sendMessage = (message) => process.send(message);
|
|
7
|
-
const virtualEntry = isVirtualSpecifier(data.entry, data.virtual);
|
|
8
8
|
let unregisterVirtualModules;
|
|
9
9
|
let entry;
|
|
10
10
|
let server;
|
|
11
11
|
try {
|
|
12
12
|
unregisterVirtualModules = await registerVirtualModules(data.virtual);
|
|
13
|
-
entry = await resolveEntry(data.entry,
|
|
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 =
|
|
21
|
+
const message = formatInitError(error);
|
|
22
22
|
sendMessage({
|
|
23
23
|
event: "init-error",
|
|
24
24
|
error: message
|
|
@@ -45,7 +45,7 @@ process.on("message", async (message) => {
|
|
|
45
45
|
}
|
|
46
46
|
if (message?.event === "reload-module") {
|
|
47
47
|
try {
|
|
48
|
-
entry = await reloadEntryModule(data.entry, entry, sendMessage,
|
|
48
|
+
entry = await reloadEntryModule(data.entry, entry, sendMessage, isVirtualEntry(data.entry));
|
|
49
49
|
process.send({ event: "module-reloaded" });
|
|
50
50
|
} catch (error) {
|
|
51
51
|
process.send({
|
|
@@ -55,8 +55,8 @@ process.on("message", async (message) => {
|
|
|
55
55
|
}
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
|
-
if (message?.event === "
|
|
59
|
-
|
|
58
|
+
if (message?.event === "update-virtual-modules") {
|
|
59
|
+
handleUpdateVirtualModules(message, sendMessage);
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
62
|
if (message?.type === "ping") {
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { formatInitError, handleUpdateVirtualModules, isVirtualEntry, parseServerAddress, registerVirtualModules, reloadEntryModule, resolveEntry, toServerOptions } from "../../_chunks/common-worker-utils.mjs";
|
|
2
|
+
import { receiveProcessData } from "../../_chunks/common-process-data.mjs";
|
|
2
3
|
import { serve } from "srvx";
|
|
3
4
|
import { plugin } from "crossws/server";
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const sendMessage = (message) =>
|
|
7
|
-
const _stdin = globalThis.Deno?.stdin?.readable || process.stdin;
|
|
8
|
-
const virtualEntry = isVirtualSpecifier(data.entry, data.virtual);
|
|
5
|
+
process.on("disconnect", () => process.exit(0));
|
|
6
|
+
const data = await receiveProcessData();
|
|
7
|
+
const sendMessage = (message) => process.send(message);
|
|
9
8
|
let unregisterVirtualModules;
|
|
10
9
|
let entry;
|
|
11
10
|
let server;
|
|
12
11
|
try {
|
|
13
12
|
unregisterVirtualModules = await registerVirtualModules(data.virtual);
|
|
14
|
-
entry = await resolveEntry(data.entry,
|
|
13
|
+
entry = await resolveEntry(data.entry, isVirtualEntry(data.entry));
|
|
15
14
|
server = serve({
|
|
16
15
|
...toServerOptions(entry),
|
|
17
16
|
fetch: (request) => entry.fetch(request),
|
|
@@ -19,7 +18,7 @@ try {
|
|
|
19
18
|
});
|
|
20
19
|
await server.ready();
|
|
21
20
|
} catch (error) {
|
|
22
|
-
const message =
|
|
21
|
+
const message = formatInitError(error);
|
|
23
22
|
sendMessage({
|
|
24
23
|
event: "init-error",
|
|
25
24
|
error: message
|
|
@@ -35,59 +34,38 @@ if (entry.upgrade) server.node?.server?.on("upgrade", (req, socket, head) => {
|
|
|
35
34
|
} });
|
|
36
35
|
});
|
|
37
36
|
if (entry.ipc) await entry.ipc.onOpen?.({ sendMessage });
|
|
38
|
-
|
|
39
|
-
async
|
|
40
|
-
const decoder = new TextDecoder();
|
|
41
|
-
let buffer = "";
|
|
42
|
-
for await (const chunk of _stdin) {
|
|
43
|
-
buffer += decoder.decode(chunk, { stream: true });
|
|
44
|
-
let newlineIdx;
|
|
45
|
-
while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
|
|
46
|
-
const line = buffer.slice(0, newlineIdx);
|
|
47
|
-
buffer = buffer.slice(newlineIdx + 1);
|
|
48
|
-
if (!line) continue;
|
|
49
|
-
let message;
|
|
50
|
-
try {
|
|
51
|
-
message = JSON.parse(line);
|
|
52
|
-
} catch {
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
await handleMessage(message);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
async function handleMessage(message) {
|
|
37
|
+
process.send({ address: parseServerAddress(server) });
|
|
38
|
+
process.on("message", async (message) => {
|
|
60
39
|
if (message?.event === "shutdown") {
|
|
61
40
|
Promise.resolve(entry.ipc?.onClose?.()).then(() => server.close()).then(() => {
|
|
62
41
|
unregisterVirtualModules();
|
|
63
|
-
|
|
42
|
+
process.send({ event: "exit" });
|
|
64
43
|
});
|
|
65
44
|
return;
|
|
66
45
|
}
|
|
67
46
|
if (message?.event === "reload-module") {
|
|
68
47
|
try {
|
|
69
|
-
entry = await reloadEntryModule(data.entry, entry, sendMessage,
|
|
70
|
-
|
|
48
|
+
entry = await reloadEntryModule(data.entry, entry, sendMessage, isVirtualEntry(data.entry));
|
|
49
|
+
process.send({ event: "module-reloaded" });
|
|
71
50
|
} catch (error) {
|
|
72
|
-
|
|
51
|
+
process.send({
|
|
73
52
|
event: "module-reloaded",
|
|
74
53
|
error: error?.message || String(error)
|
|
75
54
|
});
|
|
76
55
|
}
|
|
77
56
|
return;
|
|
78
57
|
}
|
|
79
|
-
if (message?.event === "
|
|
80
|
-
|
|
58
|
+
if (message?.event === "update-virtual-modules") {
|
|
59
|
+
handleUpdateVirtualModules(message, sendMessage);
|
|
81
60
|
return;
|
|
82
61
|
}
|
|
83
62
|
if (message?.type === "ping") {
|
|
84
|
-
|
|
63
|
+
process.send({
|
|
85
64
|
type: "pong",
|
|
86
65
|
data: message.data
|
|
87
66
|
});
|
|
88
67
|
return;
|
|
89
68
|
}
|
|
90
69
|
entry.ipc?.onMessage?.(message);
|
|
91
|
-
}
|
|
92
|
-
readMessages().catch(() => {});
|
|
70
|
+
});
|
|
93
71
|
export {};
|
|
@@ -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:
|
|
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.
|
|
56
|
+
this._handleProcessMessage(message);
|
|
61
57
|
});
|
|
62
58
|
child.stdout?.pipe(process.stdout);
|
|
63
59
|
child.stderr?.pipe(process.stderr);
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { formatInitError, handleUpdateVirtualModules, isVirtualEntry, parseServerAddress, registerVirtualModules, reloadEntryModule, resolveEntry, toServerOptions } from "../../_chunks/common-worker-utils.mjs";
|
|
2
|
+
import { receiveProcessData } from "../../_chunks/common-process-data.mjs";
|
|
2
3
|
import { serve } from "srvx";
|
|
3
4
|
import { plugin } from "crossws/server";
|
|
4
5
|
process.on("disconnect", () => process.exit(0));
|
|
5
|
-
const data =
|
|
6
|
+
const data = await receiveProcessData();
|
|
6
7
|
const sendMessage = (message) => process.send(message);
|
|
7
|
-
const virtualEntry = isVirtualSpecifier(data.entry, data.virtual);
|
|
8
8
|
let unregisterVirtualModules;
|
|
9
9
|
let entry;
|
|
10
10
|
let server;
|
|
11
11
|
try {
|
|
12
12
|
unregisterVirtualModules = await registerVirtualModules(data.virtual);
|
|
13
|
-
entry = await resolveEntry(data.entry,
|
|
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 =
|
|
21
|
+
const message = formatInitError(error);
|
|
22
22
|
sendMessage({
|
|
23
23
|
event: "init-error",
|
|
24
24
|
error: message
|
|
@@ -45,7 +45,7 @@ process.on("message", async (message) => {
|
|
|
45
45
|
}
|
|
46
46
|
if (message?.event === "reload-module") {
|
|
47
47
|
try {
|
|
48
|
-
entry = await reloadEntryModule(data.entry, entry, sendMessage,
|
|
48
|
+
entry = await reloadEntryModule(data.entry, entry, sendMessage, isVirtualEntry(data.entry));
|
|
49
49
|
process.send({ event: "module-reloaded" });
|
|
50
50
|
} catch (error) {
|
|
51
51
|
process.send({
|
|
@@ -55,8 +55,8 @@ process.on("message", async (message) => {
|
|
|
55
55
|
}
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
|
-
if (message?.event === "
|
|
59
|
-
|
|
58
|
+
if (message?.event === "update-virtual-modules") {
|
|
59
|
+
handleUpdateVirtualModules(message, sendMessage);
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
62
|
if (message?.type === "ping") {
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { formatInitError, handleUpdateVirtualModules, isVirtualEntry, parseServerAddress, registerVirtualModules, reloadEntryModule, resolveEntry, toServerOptions } from "../../_chunks/common-worker-utils.mjs";
|
|
2
2
|
import { parentPort, workerData } from "node:worker_threads";
|
|
3
3
|
import { serve } from "srvx";
|
|
4
4
|
import { plugin } from "crossws/server";
|
|
5
5
|
const data = workerData || {};
|
|
6
6
|
const sendMessage = (message) => parentPort?.postMessage(message);
|
|
7
|
-
const virtualEntry = isVirtualSpecifier(data.entry, data.virtual);
|
|
8
7
|
let unregisterVirtualModules;
|
|
9
8
|
let entry;
|
|
10
9
|
let server;
|
|
11
10
|
try {
|
|
12
11
|
unregisterVirtualModules = await registerVirtualModules(data.virtual);
|
|
13
|
-
entry = await resolveEntry(data.entry,
|
|
12
|
+
entry = await resolveEntry(data.entry, isVirtualEntry(data.entry));
|
|
14
13
|
server = serve({
|
|
15
14
|
...toServerOptions(entry),
|
|
16
15
|
fetch: (request) => entry.fetch(request),
|
|
@@ -18,7 +17,7 @@ try {
|
|
|
18
17
|
});
|
|
19
18
|
await server.ready();
|
|
20
19
|
} catch (error) {
|
|
21
|
-
const message =
|
|
20
|
+
const message = formatInitError(error);
|
|
22
21
|
sendMessage({
|
|
23
22
|
event: "init-error",
|
|
24
23
|
error: message
|
|
@@ -45,7 +44,7 @@ parentPort?.on("message", async (message) => {
|
|
|
45
44
|
}
|
|
46
45
|
if (message?.event === "reload-module") {
|
|
47
46
|
try {
|
|
48
|
-
entry = await reloadEntryModule(data.entry, entry, sendMessage,
|
|
47
|
+
entry = await reloadEntryModule(data.entry, entry, sendMessage, isVirtualEntry(data.entry));
|
|
49
48
|
parentPort?.postMessage({ event: "module-reloaded" });
|
|
50
49
|
} catch (error) {
|
|
51
50
|
parentPort?.postMessage({
|
|
@@ -55,8 +54,8 @@ parentPort?.on("message", async (message) => {
|
|
|
55
54
|
}
|
|
56
55
|
return;
|
|
57
56
|
}
|
|
58
|
-
if (message?.event === "
|
|
59
|
-
|
|
57
|
+
if (message?.event === "update-virtual-modules") {
|
|
58
|
+
handleUpdateVirtualModules(message, sendMessage);
|
|
60
59
|
return;
|
|
61
60
|
}
|
|
62
61
|
if (message?.type === "ping") {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WorkerHooks } from "../../_chunks/types.mjs";
|
|
1
|
+
import { VirtualModuleUpdates, WorkerHooks } from "../../_chunks/types.mjs";
|
|
2
2
|
import { BaseEnvRunner, EnvRunnerData } from "../../_chunks/common-base-runner.mjs";
|
|
3
3
|
export declare class SelfEnvRunner extends BaseEnvRunner {
|
|
4
4
|
#private;
|
|
@@ -16,6 +16,7 @@ export declare class SelfEnvRunner extends BaseEnvRunner {
|
|
|
16
16
|
};
|
|
17
17
|
}): Promise<void>;
|
|
18
18
|
sendMessage(message: unknown): void;
|
|
19
|
+
updateVirtualModules(_changes: VirtualModuleUpdates): Promise<void>;
|
|
19
20
|
invalidateModule(specifier: string): Promise<void>;
|
|
20
21
|
reloadModule(): Promise<void>;
|
|
21
22
|
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,13 @@ 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
|
+
}
|
|
82
89
|
this.#active = true;
|
|
83
90
|
resolveEntry(entryPath).then(async (entry) => {
|
|
91
|
+
if (this.closed) return;
|
|
84
92
|
this.#entry = entry;
|
|
85
93
|
await entry.ipc?.onOpen?.({ sendMessage: (message) => {
|
|
86
94
|
queueMicrotask(() => this._handleMessage(message));
|
|
@@ -20,11 +20,7 @@ export type VercelQueueRetryDirective = {
|
|
|
20
20
|
export type VercelQueueMessageHandler<T = unknown> = (message: T, metadata: VercelQueueMessageMetadata) => Promise<void> | void;
|
|
21
21
|
/** Called when the message handler throws (`RetryHandler`). */
|
|
22
22
|
export type VercelQueueRetryHandler = (error: unknown, metadata: VercelQueueMessageMetadata) => VercelQueueRetryDirective | void | undefined;
|
|
23
|
-
/**
|
|
24
|
-
* The `@vercel/queue` package, as imported by the consumer
|
|
25
|
-
* (`import * as queue from "@vercel/queue"`). Structurally typed so the real
|
|
26
|
-
* module namespace is assignable without env-runner depending on the package.
|
|
27
|
-
*/
|
|
23
|
+
/** `@vercel/queue` namespace, structurally typed to avoid depending on the package. */
|
|
28
24
|
export interface VercelQueueSdk {
|
|
29
25
|
QueueClient: new (options?: any) => any;
|
|
30
26
|
registerDevConsumer?: (options: {
|
|
@@ -39,20 +35,8 @@ export interface VercelQueueSdk {
|
|
|
39
35
|
}
|
|
40
36
|
export interface VercelQueueDevConsumer {
|
|
41
37
|
/**
|
|
42
|
-
* The `@vercel/queue` package
|
|
43
|
-
*
|
|
44
|
-
* ```ts
|
|
45
|
-
* import * as sdk from "@vercel/queue";
|
|
46
|
-
* await registerVercelQueueConsumer({ sdk, topic: "orders", handler });
|
|
47
|
-
*
|
|
48
|
-
* // or, equivalently
|
|
49
|
-
* await registerVercelQueueConsumer({ sdk: "@vercel/queue", topic, handler });
|
|
50
|
-
* ```
|
|
51
|
-
*
|
|
52
|
-
* Passing it explicitly is preferred (`@vercel/queue` is not a dependency of
|
|
53
|
-
* `env-runner`). Bare specifiers resolve from the current working directory.
|
|
54
|
-
* When omitted, `@vercel/queue` is imported optionally and registration
|
|
55
|
-
* becomes a no-op if the package isn't installed.
|
|
38
|
+
* The `@vercel/queue` package (`import * as sdk from "@vercel/queue"`) or a
|
|
39
|
+
* specifier resolved from cwd. Omitted: imported optionally.
|
|
56
40
|
*/
|
|
57
41
|
sdk?: RuntimeDep<VercelQueueSdk>;
|
|
58
42
|
/** Topic name. Wildcard patterns (e.g. `"user-*"`) are supported. */
|
|
@@ -60,39 +44,19 @@ export interface VercelQueueDevConsumer {
|
|
|
60
44
|
/** Function invoked with each delivered message. */
|
|
61
45
|
handler: VercelQueueMessageHandler;
|
|
62
46
|
/**
|
|
63
|
-
*
|
|
64
|
-
* topic replaces the previous handler (HMR-safe). Use distinct groups to fan
|
|
65
|
-
* a topic out to multiple coexisting handlers.
|
|
66
|
-
*
|
|
47
|
+
* Same group + topic replaces the handler; distinct groups coexist.
|
|
67
48
|
* @default "env-runner-vercel-dev"
|
|
68
49
|
*/
|
|
69
50
|
consumerGroup?: string;
|
|
70
|
-
/**
|
|
71
|
-
* Lock duration for in-flight messages. Forwarded to the SDK's
|
|
72
|
-
* `coreHandleCallback`.
|
|
73
|
-
*/
|
|
51
|
+
/** Lock duration for in-flight messages (SDK `coreHandleCallback`). */
|
|
74
52
|
visibilityTimeoutSeconds?: number;
|
|
75
|
-
/**
|
|
76
|
-
* Convenience: rescheduled re-delivery delay applied when the handler throws.
|
|
77
|
-
* Equivalent to `retry: () => ({ afterSeconds })`. Ignored if `retry` is set.
|
|
78
|
-
*/
|
|
53
|
+
/** Re-delivery delay when the handler throws; shorthand for `retry` (which wins). */
|
|
79
54
|
retryAfterSeconds?: number;
|
|
80
|
-
/**
|
|
81
|
-
* Full retry handler. Receives the thrown error and message metadata; return
|
|
82
|
-
* `{ afterSeconds }` to reschedule, `{ acknowledge: true }` to drop, or
|
|
83
|
-
* `undefined` to let the error propagate.
|
|
84
|
-
*/
|
|
55
|
+
/** Return `{ afterSeconds }` to reschedule, `{ acknowledge: true }` to drop, or `undefined` to rethrow. */
|
|
85
56
|
retry?: VercelQueueRetryHandler;
|
|
86
57
|
}
|
|
87
58
|
/**
|
|
88
|
-
* Bind a handler to a topic
|
|
89
|
-
*
|
|
90
|
-
* A shared `QueueClient` is constructed from `sdk` on first use and reused for
|
|
91
|
-
* later registrations with the same SDK. Re-registering with the same topic
|
|
92
|
-
* replaces the handler (HMR-safe; the SDK keys consumers by `consumerGroup`,
|
|
93
|
-
* so calling unregister on a replaced registration is a no-op).
|
|
94
|
-
*
|
|
95
|
-
* If no SDK is available, or it does not expose `registerDevConsumer`,
|
|
96
|
-
* resolves to a no-op unregister and logs a one-time warning.
|
|
59
|
+
* Bind a handler to a topic; resolves to an unregister function (a no-op once
|
|
60
|
+
* replaced). Without a usable SDK, warns once and no-ops.
|
|
97
61
|
*/
|
|
98
62
|
export declare function registerVercelQueueConsumer(consumer: VercelQueueDevConsumer): Promise<() => void>;
|
package/dist/vite.d.mts
CHANGED
|
@@ -12,17 +12,7 @@ export interface ViteTransport {
|
|
|
12
12
|
}) => void;
|
|
13
13
|
send: (payload: any) => void;
|
|
14
14
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Create a Vite `HotChannel` from an env-runner's RPC hooks.
|
|
17
|
-
*
|
|
18
|
-
* Use on the **host side** to bridge env-runner IPC → Vite's DevEnvironment transport.
|
|
19
|
-
* Messages are namespaced by `envName` so multiple Vite environments can share one runner.
|
|
20
|
-
*/
|
|
15
|
+
/** Host-side Vite `HotChannel`, namespaced by `envName` so environments can share a runner. */
|
|
21
16
|
export declare function createViteHotChannel(hooks: RunnerRPCHooks, envName: string): ViteHotChannel;
|
|
22
|
-
/**
|
|
23
|
-
* Create a Vite `ModuleRunner` transport from worker-side IPC primitives.
|
|
24
|
-
*
|
|
25
|
-
* Use on the **worker side** to bridge worker IPC → Vite's `ModuleRunner` transport.
|
|
26
|
-
* Filters messages by `envName` so multiple Vite environments can share one IPC channel.
|
|
27
|
-
*/
|
|
17
|
+
/** Worker-side Vite `ModuleRunner` transport, filtered by `envName` to share one IPC channel. */
|
|
28
18
|
export declare function createViteTransport(sendMessage: (data: any) => void, onMessage: (listener: (value: any) => void) => void, envName: string): ViteTransport;
|