env-runner 0.1.6 → 0.1.8
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 +39 -9
- package/dist/_chunks/base-runner.d.mts +0 -3
- package/dist/_chunks/base-runner.mjs +0 -2
- package/dist/_chunks/libs/cjs-module-lexer.mjs +0 -2
- package/dist/_chunks/runner.d.mts +0 -3
- package/dist/_chunks/runner.mjs +17 -8
- package/dist/_chunks/runner2.d.mts +8 -3
- package/dist/_chunks/runner2.mjs +17 -42
- package/dist/_chunks/runner3.d.mts +16 -0
- package/dist/_chunks/runner3.mjs +60 -0
- package/dist/_chunks/runner4.d.mts +14 -0
- package/dist/_chunks/runner4.mjs +93 -0
- package/dist/_chunks/runner5.d.mts +14 -0
- package/dist/_chunks/runner5.mjs +45 -0
- package/dist/_chunks/server.mjs +3 -15
- package/dist/_chunks/types.d.mts +0 -3
- package/dist/_chunks/worker-utils.mjs +0 -6
- package/dist/cli.mjs +0 -2
- package/dist/index.d.mts +6 -11
- package/dist/index.mjs +3 -2
- package/dist/runners/bun-process/runner.d.mts +0 -3
- package/dist/runners/bun-process/runner.mjs +0 -2
- package/dist/runners/bun-process/worker.mjs +0 -2
- package/dist/runners/deno-process/runner.d.mts +1 -1
- package/dist/runners/deno-process/runner.mjs +0 -1
- package/dist/runners/deno-process/worker.mjs +31 -10
- package/dist/runners/miniflare/runner.d.mts +1 -1
- package/dist/runners/miniflare/runner.mjs +0 -2
- package/dist/runners/netlify/runner.d.mts +3 -0
- package/dist/runners/netlify/runner.mjs +2 -0
- package/dist/runners/netlify/worker.d.mts +1 -0
- package/dist/runners/netlify/worker.mjs +41 -0
- package/dist/runners/node-process/runner.d.mts +0 -3
- package/dist/runners/node-process/runner.mjs +0 -2
- package/dist/runners/node-process/worker.mjs +0 -2
- package/dist/runners/node-worker/runner.d.mts +2 -18
- package/dist/runners/node-worker/runner.mjs +1 -61
- package/dist/runners/node-worker/worker.mjs +0 -2
- package/dist/runners/self/runner.d.mts +2 -7
- package/dist/runners/self/runner.mjs +0 -2
- package/dist/runners/vercel/queue-dev.d.mts +44 -0
- package/dist/runners/vercel/queue-dev.mjs +36 -0
- package/dist/runners/vercel/runner.d.mts +3 -0
- package/dist/runners/vercel/runner.mjs +2 -0
- package/dist/runners/vercel/worker.d.mts +1 -0
- package/dist/runners/vercel/worker.mjs +51 -0
- package/dist/vite.d.mts +0 -3
- package/dist/vite.mjs +0 -14
- package/package.json +34 -18
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
<!-- /automd -->
|
|
9
9
|
|
|
10
|
-
Generic environment runner for JavaScript runtimes. Run your server apps across Node.js worker threads, child processes, Bun, Deno, Cloudflare Workers (via miniflare), or in-process — with hot-reload, WebSocket proxying, and bidirectional messaging.
|
|
10
|
+
Generic environment runner for JavaScript runtimes. Run your server apps across Node.js worker threads, child processes, Bun, Deno, Cloudflare Workers (via miniflare), Vercel, Netlify, or in-process — with hot-reload, WebSocket proxying, and bidirectional messaging.
|
|
11
11
|
|
|
12
12
|
## Usage
|
|
13
13
|
|
|
@@ -120,6 +120,8 @@ import { BunProcessEnvRunner } from "env-runner/runners/bun-process";
|
|
|
120
120
|
import { DenoProcessEnvRunner } from "env-runner/runners/deno-process";
|
|
121
121
|
import { SelfEnvRunner } from "env-runner/runners/self";
|
|
122
122
|
import { MiniflareEnvRunner } from "env-runner/runners/miniflare";
|
|
123
|
+
import { VercelEnvRunner } from "env-runner/runners/vercel";
|
|
124
|
+
import { NetlifyEnvRunner } from "env-runner/runners/netlify";
|
|
123
125
|
```
|
|
124
126
|
|
|
125
127
|
All runners implement the [`EnvRunner`](./src/types.ts) interface:
|
|
@@ -160,14 +162,16 @@ await runner.close();
|
|
|
160
162
|
|
|
161
163
|
**Available runners:**
|
|
162
164
|
|
|
163
|
-
| Runner | Isolation
|
|
164
|
-
| ---------------------- |
|
|
165
|
-
| `NodeWorkerEnvRunner` | Worker thread
|
|
166
|
-
| `NodeProcessEnvRunner` | Child process (`fork`)
|
|
167
|
-
| `BunProcessEnvRunner` | Bun or Node.js process
|
|
168
|
-
| `DenoProcessEnvRunner` | Deno process
|
|
169
|
-
| `SelfEnvRunner` | In-process
|
|
170
|
-
| `MiniflareEnvRunner` | Cloudflare Workers (miniflare)
|
|
165
|
+
| Runner | Isolation | IPC mechanism |
|
|
166
|
+
| ---------------------- | ------------------------------- | ---------------------------------- |
|
|
167
|
+
| `NodeWorkerEnvRunner` | Worker thread | `workerData` / `parentPort` |
|
|
168
|
+
| `NodeProcessEnvRunner` | Child process (`fork`) | `ENV_RUNNER_DATA` / `process.send` |
|
|
169
|
+
| `BunProcessEnvRunner` | Bun or Node.js process | `Bun.spawn` IPC or `fork()` |
|
|
170
|
+
| `DenoProcessEnvRunner` | Deno process | `deno run` with IPC channel |
|
|
171
|
+
| `SelfEnvRunner` | In-process | In-memory channel |
|
|
172
|
+
| `MiniflareEnvRunner` | Cloudflare Workers (miniflare) | WebSocket pair via `dispatchFetch` |
|
|
173
|
+
| `VercelEnvRunner` | Worker thread (Vercel context) | `workerData` / `parentPort` |
|
|
174
|
+
| `NetlifyEnvRunner` | Worker thread (Netlify context) | `workerData` / `parentPort` |
|
|
171
175
|
|
|
172
176
|
#### Miniflare Runner
|
|
173
177
|
|
|
@@ -289,6 +293,32 @@ const runner2 = new MiniflareEnvRunner({
|
|
|
289
293
|
// Fully destroy: runner.dispose() or MiniflareEnvRunner.disposeAll()
|
|
290
294
|
```
|
|
291
295
|
|
|
296
|
+
#### Vercel Runner
|
|
297
|
+
|
|
298
|
+
Simulates a Vercel deployment environment with automatic header injection (`x-vercel-deployment-url`, `x-vercel-forwarded-for`, forwarding headers) and global context.
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
import { VercelEnvRunner } from "env-runner/runners/vercel";
|
|
302
|
+
|
|
303
|
+
const runner = new VercelEnvRunner({
|
|
304
|
+
name: "my-app",
|
|
305
|
+
data: { entry: "./app.ts" },
|
|
306
|
+
});
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
#### Netlify Runner
|
|
310
|
+
|
|
311
|
+
Simulates a Netlify deployment environment with automatic header injection (`x-nf-client-connection-ip`, `x-nf-account-id`, `x-nf-site-id`, `x-nf-deploy-id`, `x-nf-deploy-context`, `x-nf-geo`, `x-nf-request-id`, forwarding headers) and `globalThis.Netlify` setup:
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
import { NetlifyEnvRunner } from "env-runner/runners/netlify";
|
|
315
|
+
|
|
316
|
+
const runner = new NetlifyEnvRunner({
|
|
317
|
+
name: "my-app",
|
|
318
|
+
data: { entry: "./app.ts" },
|
|
319
|
+
});
|
|
320
|
+
```
|
|
321
|
+
|
|
292
322
|
### Vite Environment API
|
|
293
323
|
|
|
294
324
|
env-runner provides helpers for integrating with Vite's [Environment API](https://vite.dev/guide/api-environment-runtimes.html):
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { a as RunnerMessageListener, l as WorkerAddress, t as EnvRunner, u as WorkerHooks } from "./types.mjs";
|
|
2
2
|
import { IncomingMessage } from "node:http";
|
|
3
3
|
import { Socket } from "node:net";
|
|
4
|
-
|
|
5
|
-
//#region src/common/base-runner.d.ts
|
|
6
4
|
interface EnvRunnerData {
|
|
7
5
|
name?: string;
|
|
8
6
|
[key: string]: unknown;
|
|
@@ -45,5 +43,4 @@ declare abstract class BaseEnvRunner implements EnvRunner {
|
|
|
45
43
|
protected abstract _closeRuntime(): Promise<void>;
|
|
46
44
|
protected abstract _runtimeType(): string;
|
|
47
45
|
}
|
|
48
|
-
//#endregion
|
|
49
46
|
export { EnvRunnerData as n, BaseEnvRunner as t };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { rm } from "node:fs/promises";
|
|
2
2
|
import { proxyFetch, proxyUpgrade } from "httpxy";
|
|
3
|
-
//#region src/common/base-runner.ts
|
|
4
3
|
var BaseEnvRunner = class {
|
|
5
4
|
closed = false;
|
|
6
5
|
_name;
|
|
@@ -125,5 +124,4 @@ var BaseEnvRunner = class {
|
|
|
125
124
|
this._address = void 0;
|
|
126
125
|
}
|
|
127
126
|
};
|
|
128
|
-
//#endregion
|
|
129
127
|
export { BaseEnvRunner as t };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region node_modules/.pnpm/cjs-module-lexer@2.2.0/node_modules/cjs-module-lexer/dist/lexer.mjs
|
|
2
1
|
let A;
|
|
3
2
|
const B = 1 === new Uint8Array(new Uint16Array([1]).buffer)[0];
|
|
4
3
|
function parse(E, g = "@") {
|
|
@@ -143,5 +142,4 @@ function init() {
|
|
|
143
142
|
A = Q;
|
|
144
143
|
})());
|
|
145
144
|
}
|
|
146
|
-
//#endregion
|
|
147
145
|
export { parse as n, init as t };
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { u as WorkerHooks } from "./types.mjs";
|
|
2
2
|
import { n as EnvRunnerData, t as BaseEnvRunner } from "./base-runner.mjs";
|
|
3
|
-
|
|
4
|
-
//#region src/runners/deno-process/runner.d.ts
|
|
5
3
|
declare class DenoProcessEnvRunner extends BaseEnvRunner {
|
|
6
4
|
#private;
|
|
7
5
|
constructor(opts: {
|
|
@@ -16,5 +14,4 @@ declare class DenoProcessEnvRunner extends BaseEnvRunner {
|
|
|
16
14
|
protected _runtimeType(): string;
|
|
17
15
|
protected _closeRuntime(): Promise<void>;
|
|
18
16
|
}
|
|
19
|
-
//#endregion
|
|
20
17
|
export { DenoProcessEnvRunner as t };
|
package/dist/_chunks/runner.mjs
CHANGED
|
@@ -2,7 +2,6 @@ import { t as BaseEnvRunner } from "./base-runner.mjs";
|
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
//#region src/runners/deno-process/runner.ts
|
|
6
5
|
let _defaultEntry;
|
|
7
6
|
var DenoProcessEnvRunner = class extends BaseEnvRunner {
|
|
8
7
|
#process;
|
|
@@ -44,8 +43,9 @@ var DenoProcessEnvRunner = class extends BaseEnvRunner {
|
|
|
44
43
|
};
|
|
45
44
|
const child = spawn("deno", [
|
|
46
45
|
"run",
|
|
47
|
-
"
|
|
46
|
+
"-A",
|
|
48
47
|
"--node-modules-dir=auto",
|
|
48
|
+
"--no-lock",
|
|
49
49
|
...execArgv || [],
|
|
50
50
|
this._workerEntry
|
|
51
51
|
], {
|
|
@@ -53,8 +53,7 @@ var DenoProcessEnvRunner = class extends BaseEnvRunner {
|
|
|
53
53
|
stdio: [
|
|
54
54
|
"pipe",
|
|
55
55
|
"pipe",
|
|
56
|
-
"pipe"
|
|
57
|
-
"ipc"
|
|
56
|
+
"pipe"
|
|
58
57
|
]
|
|
59
58
|
});
|
|
60
59
|
const exited = new Promise((resolve) => {
|
|
@@ -63,7 +62,9 @@ var DenoProcessEnvRunner = class extends BaseEnvRunner {
|
|
|
63
62
|
const handle = {
|
|
64
63
|
pid: child.pid,
|
|
65
64
|
kill: () => child.kill(),
|
|
66
|
-
send: (message) =>
|
|
65
|
+
send: (message) => {
|
|
66
|
+
child.stdin.write(JSON.stringify(message) + "\n");
|
|
67
|
+
},
|
|
67
68
|
exited,
|
|
68
69
|
_exitCode: void 0,
|
|
69
70
|
removeAllListeners: () => child.removeAllListeners()
|
|
@@ -78,11 +79,19 @@ var DenoProcessEnvRunner = class extends BaseEnvRunner {
|
|
|
78
79
|
this.close(error);
|
|
79
80
|
}
|
|
80
81
|
});
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
let buffer = "";
|
|
83
|
+
child.stdout.on("data", (chunk) => {
|
|
84
|
+
buffer += chunk.toString();
|
|
85
|
+
let newlineIdx;
|
|
86
|
+
while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
|
|
87
|
+
const line = buffer.slice(0, newlineIdx);
|
|
88
|
+
buffer = buffer.slice(newlineIdx + 1);
|
|
89
|
+
if (line.startsWith("{")) try {
|
|
90
|
+
this._handleMessage(JSON.parse(line));
|
|
91
|
+
} catch {}
|
|
92
|
+
}
|
|
83
93
|
});
|
|
84
94
|
this.#process = handle;
|
|
85
95
|
}
|
|
86
96
|
};
|
|
87
|
-
//#endregion
|
|
88
97
|
export { DenoProcessEnvRunner as t };
|
|
@@ -2,8 +2,6 @@ import { u as WorkerHooks } from "./types.mjs";
|
|
|
2
2
|
import { n as EnvRunnerData, t as BaseEnvRunner } from "./base-runner.mjs";
|
|
3
3
|
import { IncomingMessage } from "node:http";
|
|
4
4
|
import { Socket } from "node:net";
|
|
5
|
-
|
|
6
|
-
//#region src/runners/miniflare/runner.d.ts
|
|
7
5
|
/** Result from a module transform (compatible with Vite's `TransformResult`). */
|
|
8
6
|
interface TransformResult {
|
|
9
7
|
code: string;
|
|
@@ -46,6 +44,14 @@ interface MiniflareEnvRunnerOptions {
|
|
|
46
44
|
persistent?: boolean;
|
|
47
45
|
/** Wrap the user's `fetch` in a try/catch that returns structured JSON error responses. Default: `true`. */
|
|
48
46
|
captureErrors?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Export conditions for bare-specifier module resolution in the module
|
|
49
|
+
* fallback service. Ensures packages with conditional exports (e.g.
|
|
50
|
+
* `"workerd"`) resolve to the correct entry instead of the Node.js one.
|
|
51
|
+
*
|
|
52
|
+
* Defaults to `["workerd", "worker"]`.
|
|
53
|
+
*/
|
|
54
|
+
exportConditions?: string[];
|
|
49
55
|
}
|
|
50
56
|
declare class MiniflareEnvRunner extends BaseEnvRunner {
|
|
51
57
|
#private;
|
|
@@ -75,5 +81,4 @@ declare class MiniflareEnvRunner extends BaseEnvRunner {
|
|
|
75
81
|
};
|
|
76
82
|
}): Promise<void>;
|
|
77
83
|
}
|
|
78
|
-
//#endregion
|
|
79
84
|
export { TransformResult as i, MiniflareEnvRunnerOptions as n, MiniflareExportInfo as r, MiniflareEnvRunner as t };
|
package/dist/_chunks/runner2.mjs
CHANGED
|
@@ -5,26 +5,9 @@ import { proxyUpgrade } from "httpxy";
|
|
|
5
5
|
import { readFileSync } from "node:fs";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { basename, dirname, resolve } from "node:path";
|
|
8
|
-
|
|
9
|
-
const IPC_PATH
|
|
10
|
-
/** Service binding name used for cross-request IPC (worker → runner). */
|
|
8
|
+
import { resolveModulePath } from "exsolve";
|
|
9
|
+
const IPC_PATH = "/__env_runner_ipc";
|
|
11
10
|
const IPC_BINDING = "__ENV_RUNNER_IPC";
|
|
12
|
-
/**
|
|
13
|
-
* Generates a wrapper module that imports the user entry and adds IPC glue.
|
|
14
|
-
*
|
|
15
|
-
* The user module is expected to export `fetch` and optionally `ipc`.
|
|
16
|
-
* The wrapper uses a persistent WebSocket pair for bidirectional IPC:
|
|
17
|
-
* - Init: `fetch` with `upgrade: websocket` creates a WebSocketPair
|
|
18
|
-
* - Messages: JSON over the WebSocket (no per-message `dispatchFetch`)
|
|
19
|
-
* - Reload: `{ type: "reload" }` triggers cache-busted re-import
|
|
20
|
-
* - Shutdown: `{ type: "shutdown" }` calls `ipc.onClose()`
|
|
21
|
-
*
|
|
22
|
-
* For outgoing messages during fetch request handling, uses a service binding
|
|
23
|
-
* (`__ENV_RUNNER_IPC`) to avoid workerd's cross-request I/O restriction on
|
|
24
|
-
* the WebSocket object.
|
|
25
|
-
*
|
|
26
|
-
* Passed as an in-memory `script` to Miniflare (no temp files needed).
|
|
27
|
-
*/
|
|
28
11
|
function generateWrapper(entryPath, opts) {
|
|
29
12
|
const staticReExport = opts?.dynamicOnly ? "" : `export * from ${JSON.stringify(entryPath)};`;
|
|
30
13
|
const explicitExports = opts?.dynamicOnly && opts.exports?.length ? opts.exports.map((name) => `export { ${name} } from ${JSON.stringify(entryPath)};`).join("\n") : "";
|
|
@@ -47,7 +30,7 @@ if (!globalThis.process) { globalThis.process = __process; }
|
|
|
47
30
|
${staticReExport}
|
|
48
31
|
${explicitExports}
|
|
49
32
|
|
|
50
|
-
const __IPC_PATH = "${IPC_PATH
|
|
33
|
+
const __IPC_PATH = "${IPC_PATH}";
|
|
51
34
|
const __IPC_BINDING = "${IPC_BINDING}";
|
|
52
35
|
const __entryPath = ${JSON.stringify(entryPath)};
|
|
53
36
|
let __userEntry;
|
|
@@ -193,9 +176,6 @@ export default {
|
|
|
193
176
|
};
|
|
194
177
|
`;
|
|
195
178
|
}
|
|
196
|
-
//#endregion
|
|
197
|
-
//#region src/runners/miniflare/runner.ts
|
|
198
|
-
const IPC_PATH = "/__env_runner_ipc";
|
|
199
179
|
const _miniflareCache = /* @__PURE__ */ new Map();
|
|
200
180
|
var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
201
181
|
#miniflare;
|
|
@@ -207,6 +187,7 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
207
187
|
#cacheKey;
|
|
208
188
|
#exports;
|
|
209
189
|
#captureErrors;
|
|
190
|
+
#exportConditions;
|
|
210
191
|
constructor(opts) {
|
|
211
192
|
super({
|
|
212
193
|
...opts,
|
|
@@ -217,15 +198,14 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
217
198
|
this.#persistent = opts.persistent ?? false;
|
|
218
199
|
this.#exports = opts.exports ?? {};
|
|
219
200
|
this.#captureErrors = opts.captureErrors ?? true;
|
|
201
|
+
this.#exportConditions = opts.exportConditions ?? ["workerd", "worker"];
|
|
220
202
|
this.#init();
|
|
221
203
|
}
|
|
222
|
-
/** Dispose all persistent Miniflare instances from the cache. */
|
|
223
204
|
static async disposeAll() {
|
|
224
205
|
const entries = [..._miniflareCache.values()];
|
|
225
206
|
_miniflareCache.clear();
|
|
226
207
|
for (const entry of entries) await entry.mf.dispose().catch(() => {});
|
|
227
208
|
}
|
|
228
|
-
/** Fully dispose the Miniflare instance (even if persistent). */
|
|
229
209
|
async dispose() {
|
|
230
210
|
if (this.#miniflare) {
|
|
231
211
|
if (this.#ws) {
|
|
@@ -264,13 +244,6 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
264
244
|
data: message
|
|
265
245
|
}));
|
|
266
246
|
}
|
|
267
|
-
/**
|
|
268
|
-
* Hot-reload the user entry module without recreating the Miniflare instance.
|
|
269
|
-
*
|
|
270
|
-
* Sends `reload-module` event over the WebSocket. The worker wrapper uses
|
|
271
|
-
* `unsafeEvalBinding` to re-import the entry with a cache-busting query string
|
|
272
|
-
* and responds with `module-reloaded` when done.
|
|
273
|
-
*/
|
|
274
247
|
async reloadModule(timeout = 5e3) {
|
|
275
248
|
if (!this.#ws) throw new Error("Miniflare env runner should be initialized before reloading.");
|
|
276
249
|
if (!this._data?.entry) return;
|
|
@@ -399,6 +372,7 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
399
372
|
if (!options.unsafeModuleFallbackService) {
|
|
400
373
|
const _require = createRequire(resolvedEntry);
|
|
401
374
|
const _transformRequest = this.#transformRequest;
|
|
375
|
+
const _exportConditions = this.#exportConditions;
|
|
402
376
|
options.unsafeUseModuleFallbackService = true;
|
|
403
377
|
const modulePathMap = /* @__PURE__ */ new Map();
|
|
404
378
|
const _cjsLexerReady = ensureCjsLexer();
|
|
@@ -422,6 +396,7 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
422
396
|
const referrerKey = referrer.startsWith("/") ? referrer.slice(1) : referrer;
|
|
423
397
|
const referrerReal = modulePathMap.get(referrerKey);
|
|
424
398
|
const contextRequire = referrerReal ? createRequire(referrerReal) : _require;
|
|
399
|
+
if (cleanRaw.startsWith("cloudflare:")) return new Response(null, { status: 404 });
|
|
425
400
|
if (cleanRaw.startsWith("node:")) {
|
|
426
401
|
const nodeName = cleanRaw.slice(5);
|
|
427
402
|
try {
|
|
@@ -430,7 +405,11 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
430
405
|
return new Response(null, { status: 404 });
|
|
431
406
|
}
|
|
432
407
|
} else try {
|
|
433
|
-
resolvedPath =
|
|
408
|
+
resolvedPath = resolveModulePath(cleanRaw, {
|
|
409
|
+
from: referrerReal || resolvedEntry,
|
|
410
|
+
conditions: _exportConditions,
|
|
411
|
+
try: true
|
|
412
|
+
}) || contextRequire.resolve(cleanRaw);
|
|
434
413
|
} catch {
|
|
435
414
|
const name = cleanSpecifier.startsWith("/") ? cleanSpecifier.slice(1) : cleanSpecifier;
|
|
436
415
|
return Response.json({
|
|
@@ -486,7 +465,10 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
486
465
|
}
|
|
487
466
|
}
|
|
488
467
|
if (this.#persistent && entryPath) {
|
|
489
|
-
this.#cacheKey = computeCacheKey(entryPath,
|
|
468
|
+
this.#cacheKey = computeCacheKey(entryPath, {
|
|
469
|
+
...this.#miniflareOptions,
|
|
470
|
+
_exportConditions: this.#exportConditions
|
|
471
|
+
});
|
|
490
472
|
const cached = _miniflareCache.get(this.#cacheKey);
|
|
491
473
|
if (cached) {
|
|
492
474
|
this.#miniflare = cached.mf;
|
|
@@ -501,7 +483,7 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
501
483
|
refCount: 1
|
|
502
484
|
});
|
|
503
485
|
}
|
|
504
|
-
const initRes = await this.#miniflare.dispatchFetch("http://localhost"
|
|
486
|
+
const initRes = await this.#miniflare.dispatchFetch("http://localhost/__env_runner_ipc", { headers: { upgrade: "websocket" } });
|
|
505
487
|
const ws = initRes.webSocket;
|
|
506
488
|
if (!ws) {
|
|
507
489
|
const body = await initRes.text().catch(() => "");
|
|
@@ -521,10 +503,6 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
521
503
|
} });
|
|
522
504
|
}
|
|
523
505
|
};
|
|
524
|
-
/**
|
|
525
|
-
* Detect `export class` declarations in the entry file.
|
|
526
|
-
* Merges with explicitly declared exports from options.
|
|
527
|
-
*/
|
|
528
506
|
function detectExportedClasses(entryPath, explicit) {
|
|
529
507
|
const names = new Set(Object.keys(explicit));
|
|
530
508
|
try {
|
|
@@ -535,11 +513,9 @@ function detectExportedClasses(entryPath, explicit) {
|
|
|
535
513
|
} catch {}
|
|
536
514
|
return [...names];
|
|
537
515
|
}
|
|
538
|
-
/** Convert PascalCase/camelCase to SCREAMING_SNAKE_CASE (e.g. `Counter` → `COUNTER`, `MyDurableObject` → `MY_DURABLE_OBJECT`). */
|
|
539
516
|
function toScreamingSnakeCase(name) {
|
|
540
517
|
return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase();
|
|
541
518
|
}
|
|
542
|
-
/** Compute a stable cache key for persistent Miniflare instances. */
|
|
543
519
|
function computeCacheKey(entryPath, opts) {
|
|
544
520
|
const serializableOpts = {};
|
|
545
521
|
for (const [k, v] of Object.entries(opts)) if (typeof v !== "function") serializableOpts[k] = v;
|
|
@@ -560,5 +536,4 @@ function createCjsEsmShim(cjsSpecifier, contents) {
|
|
|
560
536
|
for (const name of namedExports) shim += `export var ${name} = __cjs_mod__["${name}"];\n`;
|
|
561
537
|
return shim;
|
|
562
538
|
}
|
|
563
|
-
//#endregion
|
|
564
539
|
export { MiniflareEnvRunner as t };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { u as WorkerHooks } from "./types.mjs";
|
|
2
|
+
import { n as EnvRunnerData, t as BaseEnvRunner } from "./base-runner.mjs";
|
|
3
|
+
declare class NodeWorkerEnvRunner extends BaseEnvRunner {
|
|
4
|
+
#private;
|
|
5
|
+
constructor(opts: {
|
|
6
|
+
name: string;
|
|
7
|
+
workerEntry?: string;
|
|
8
|
+
hooks?: WorkerHooks;
|
|
9
|
+
data?: EnvRunnerData;
|
|
10
|
+
});
|
|
11
|
+
sendMessage(message: unknown): void;
|
|
12
|
+
protected _hasRuntime(): boolean;
|
|
13
|
+
protected _runtimeType(): string;
|
|
14
|
+
protected _closeRuntime(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export { NodeWorkerEnvRunner as t };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { t as BaseEnvRunner } from "./base-runner.mjs";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { Worker } from "node:worker_threads";
|
|
5
|
+
let _defaultEntry;
|
|
6
|
+
var NodeWorkerEnvRunner = class extends BaseEnvRunner {
|
|
7
|
+
#worker;
|
|
8
|
+
constructor(opts) {
|
|
9
|
+
_defaultEntry ||= fileURLToPath(import.meta.resolve("env-runner/runners/node-worker/worker"));
|
|
10
|
+
super({
|
|
11
|
+
...opts,
|
|
12
|
+
workerEntry: opts.workerEntry || _defaultEntry
|
|
13
|
+
});
|
|
14
|
+
this.#initWorker();
|
|
15
|
+
}
|
|
16
|
+
sendMessage(message) {
|
|
17
|
+
if (!this.#worker) throw new Error("Worker thread should be initialized before sending messages.");
|
|
18
|
+
this.#worker.postMessage(message);
|
|
19
|
+
}
|
|
20
|
+
_hasRuntime() {
|
|
21
|
+
return Boolean(this.#worker);
|
|
22
|
+
}
|
|
23
|
+
_runtimeType() {
|
|
24
|
+
return "worker";
|
|
25
|
+
}
|
|
26
|
+
async _closeRuntime() {
|
|
27
|
+
if (!this.#worker) return;
|
|
28
|
+
this.#worker.removeAllListeners();
|
|
29
|
+
await this.#worker.terminate().catch((error) => {
|
|
30
|
+
console.error(error);
|
|
31
|
+
});
|
|
32
|
+
this.#worker = void 0;
|
|
33
|
+
}
|
|
34
|
+
#initWorker() {
|
|
35
|
+
if (!existsSync(this._workerEntry)) {
|
|
36
|
+
this.close(`worker entry not found in "${this._workerEntry}".`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const worker = new Worker(this._workerEntry, {
|
|
40
|
+
env: { ...process.env },
|
|
41
|
+
workerData: {
|
|
42
|
+
name: this._name,
|
|
43
|
+
...this._data
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
worker.once("exit", (code) => {
|
|
47
|
+
worker._exitCode = code;
|
|
48
|
+
this.close(`worker exited with code ${code}`);
|
|
49
|
+
});
|
|
50
|
+
worker.once("error", (error) => {
|
|
51
|
+
console.error(`Worker error:`, error);
|
|
52
|
+
this.close(error);
|
|
53
|
+
});
|
|
54
|
+
worker.on("message", (message) => {
|
|
55
|
+
this._handleMessage(message);
|
|
56
|
+
});
|
|
57
|
+
this.#worker = worker;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
export { NodeWorkerEnvRunner as t };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { u as WorkerHooks } from "./types.mjs";
|
|
2
|
+
import { n as EnvRunnerData } from "./base-runner.mjs";
|
|
3
|
+
import { t as NodeWorkerEnvRunner } from "./runner3.mjs";
|
|
4
|
+
declare class VercelEnvRunner extends NodeWorkerEnvRunner {
|
|
5
|
+
constructor(opts: {
|
|
6
|
+
name: string;
|
|
7
|
+
workerEntry?: string;
|
|
8
|
+
hooks?: WorkerHooks;
|
|
9
|
+
data?: EnvRunnerData;
|
|
10
|
+
});
|
|
11
|
+
fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
|
|
12
|
+
protected _runtimeType(): string;
|
|
13
|
+
}
|
|
14
|
+
export { VercelEnvRunner as t };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { t as NodeWorkerEnvRunner } from "./runner3.mjs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { randomBytes } from "node:crypto";
|
|
4
|
+
let _warned = false;
|
|
5
|
+
function warnIfVercelOidcTokenInvalid(token) {
|
|
6
|
+
const result = _checkVercelOidcToken(token);
|
|
7
|
+
if (_warned) return result;
|
|
8
|
+
if (result.status === "missing") {
|
|
9
|
+
_warned = true;
|
|
10
|
+
console.warn("[env-runner:vercel] VERCEL_OIDC_TOKEN is not set. Vercel SDK features (e.g. @vercel/functions waitUntil, cache) may not work. Run `vercel env pull` to set it.");
|
|
11
|
+
} else if (result.status === "expired") {
|
|
12
|
+
_warned = true;
|
|
13
|
+
console.warn(`[env-runner:vercel] VERCEL_OIDC_TOKEN expired at ${result.expiresAt.toISOString()}. Vercel SDK authentication will fail. Run \`vercel env pull\` to refresh it.`);
|
|
14
|
+
} else if (result.status === "invalid") {
|
|
15
|
+
_warned = true;
|
|
16
|
+
console.warn("[env-runner:vercel] VERCEL_OIDC_TOKEN is malformed (not a valid JWT). Vercel SDK authentication will fail. Run `vercel env pull` to get a fresh token.");
|
|
17
|
+
}
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
function _checkVercelOidcToken(token = process.env.VERCEL_OIDC_TOKEN) {
|
|
21
|
+
if (!token) return { status: "missing" };
|
|
22
|
+
const parts = token.split(".");
|
|
23
|
+
if (parts.length !== 3) return { status: "invalid" };
|
|
24
|
+
let payload;
|
|
25
|
+
try {
|
|
26
|
+
const json = Buffer.from(parts[1], "base64url").toString("utf8");
|
|
27
|
+
payload = JSON.parse(json);
|
|
28
|
+
} catch {
|
|
29
|
+
return { status: "invalid" };
|
|
30
|
+
}
|
|
31
|
+
if (!payload || typeof payload !== "object") return { status: "invalid" };
|
|
32
|
+
const exp = payload.exp;
|
|
33
|
+
if (typeof exp !== "number" || !Number.isFinite(exp)) return { status: "invalid" };
|
|
34
|
+
const expiresAt = /* @__PURE__ */ new Date(exp * 1e3);
|
|
35
|
+
if (expiresAt.getTime() <= Date.now()) return {
|
|
36
|
+
status: "expired",
|
|
37
|
+
expiresAt
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
status: "valid",
|
|
41
|
+
expiresAt
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
let _defaultEntry;
|
|
45
|
+
const _podId = Math.random().toString(32).slice(-5);
|
|
46
|
+
function generateVercelId() {
|
|
47
|
+
return `dev1::${_podId}-${Date.now().toString(36)}-${randomBytes(6).toString("hex")}`;
|
|
48
|
+
}
|
|
49
|
+
var VercelEnvRunner = class extends NodeWorkerEnvRunner {
|
|
50
|
+
constructor(opts) {
|
|
51
|
+
_defaultEntry ||= fileURLToPath(import.meta.resolve("env-runner/runners/vercel/worker"));
|
|
52
|
+
super({
|
|
53
|
+
...opts,
|
|
54
|
+
workerEntry: opts.workerEntry || _defaultEntry
|
|
55
|
+
});
|
|
56
|
+
warnIfVercelOidcTokenInvalid();
|
|
57
|
+
}
|
|
58
|
+
async fetch(input, init) {
|
|
59
|
+
const headers = new Headers(input instanceof Request ? input.headers : init?.headers);
|
|
60
|
+
const requestId = generateVercelId();
|
|
61
|
+
if (this._address && this._address.port != null && !headers.has("x-vercel-deployment-url")) {
|
|
62
|
+
const host = this._address.host || "127.0.0.1";
|
|
63
|
+
headers.set("x-vercel-deployment-url", `http://${host}:${this._address.port}`);
|
|
64
|
+
}
|
|
65
|
+
if (!headers.has("x-vercel-id")) headers.set("x-vercel-id", requestId);
|
|
66
|
+
const clientIp = headers.get("x-forwarded-for")?.split(",")[0]?.trim() || headers.get("x-real-ip") || "127.0.0.1";
|
|
67
|
+
if (!headers.has("x-vercel-forwarded-for")) headers.set("x-vercel-forwarded-for", clientIp);
|
|
68
|
+
if (!headers.has("x-forwarded-for")) headers.set("x-forwarded-for", clientIp);
|
|
69
|
+
if (!headers.has("x-real-ip")) headers.set("x-real-ip", clientIp);
|
|
70
|
+
try {
|
|
71
|
+
const url = new URL(input instanceof Request ? input.url : input.toString());
|
|
72
|
+
if (!headers.has("x-forwarded-proto")) headers.set("x-forwarded-proto", url.protocol.replace(":", ""));
|
|
73
|
+
if (!headers.has("x-forwarded-host")) headers.set("x-forwarded-host", headers.get("host") || url.host);
|
|
74
|
+
} catch {}
|
|
75
|
+
const res = await super.fetch(input, {
|
|
76
|
+
...init,
|
|
77
|
+
headers
|
|
78
|
+
});
|
|
79
|
+
const resHeaders = new Headers(res.headers);
|
|
80
|
+
if (!resHeaders.has("server")) resHeaders.set("server", "Vercel");
|
|
81
|
+
if (!resHeaders.has("x-vercel-id")) resHeaders.set("x-vercel-id", requestId);
|
|
82
|
+
if (!resHeaders.has("x-vercel-cache")) resHeaders.set("x-vercel-cache", "MISS");
|
|
83
|
+
return new Response(res.body, {
|
|
84
|
+
status: res.status,
|
|
85
|
+
statusText: res.statusText,
|
|
86
|
+
headers: resHeaders
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
_runtimeType() {
|
|
90
|
+
return "vercel";
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
export { VercelEnvRunner as t };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { u as WorkerHooks } from "./types.mjs";
|
|
2
|
+
import { n as EnvRunnerData } from "./base-runner.mjs";
|
|
3
|
+
import { t as NodeWorkerEnvRunner } from "./runner3.mjs";
|
|
4
|
+
declare class NetlifyEnvRunner extends NodeWorkerEnvRunner {
|
|
5
|
+
constructor(opts: {
|
|
6
|
+
name: string;
|
|
7
|
+
workerEntry?: string;
|
|
8
|
+
hooks?: WorkerHooks;
|
|
9
|
+
data?: EnvRunnerData;
|
|
10
|
+
});
|
|
11
|
+
fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
|
|
12
|
+
protected _runtimeType(): string;
|
|
13
|
+
}
|
|
14
|
+
export { NetlifyEnvRunner as t };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { t as NodeWorkerEnvRunner } from "./runner3.mjs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
let _defaultEntry;
|
|
4
|
+
var NetlifyEnvRunner = class extends NodeWorkerEnvRunner {
|
|
5
|
+
constructor(opts) {
|
|
6
|
+
_defaultEntry ||= fileURLToPath(import.meta.resolve("env-runner/runners/netlify/worker"));
|
|
7
|
+
super({
|
|
8
|
+
...opts,
|
|
9
|
+
workerEntry: opts.workerEntry || _defaultEntry
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
async fetch(input, init) {
|
|
13
|
+
const headers = new Headers(input instanceof Request ? input.headers : init?.headers);
|
|
14
|
+
const clientIp = headers.get("x-forwarded-for")?.split(",")[0]?.trim() || headers.get("x-real-ip") || "127.0.0.1";
|
|
15
|
+
if (!headers.has("x-nf-client-connection-ip")) headers.set("x-nf-client-connection-ip", clientIp);
|
|
16
|
+
if (!headers.has("x-nf-account-id")) headers.set("x-nf-account-id", "0");
|
|
17
|
+
if (!headers.has("x-nf-site-id")) headers.set("x-nf-site-id", "0");
|
|
18
|
+
if (!headers.has("x-nf-deploy-id")) headers.set("x-nf-deploy-id", "0");
|
|
19
|
+
if (!headers.has("x-nf-deploy-context")) headers.set("x-nf-deploy-context", "dev");
|
|
20
|
+
if (!headers.has("x-nf-geo")) headers.set("x-nf-geo", btoa(JSON.stringify({
|
|
21
|
+
city: "localhost",
|
|
22
|
+
country: { code: "dev" }
|
|
23
|
+
})));
|
|
24
|
+
if (!headers.has("x-nf-request-id")) headers.set("x-nf-request-id", crypto.randomUUID());
|
|
25
|
+
if (!headers.has("x-forwarded-for")) headers.set("x-forwarded-for", clientIp);
|
|
26
|
+
if (!headers.has("x-real-ip")) headers.set("x-real-ip", clientIp);
|
|
27
|
+
try {
|
|
28
|
+
const url = new URL(input instanceof Request ? input.url : input.toString());
|
|
29
|
+
if (!headers.has("x-forwarded-proto")) headers.set("x-forwarded-proto", url.protocol.replace(":", ""));
|
|
30
|
+
if (!headers.has("x-forwarded-host")) headers.set("x-forwarded-host", headers.get("host") || url.host);
|
|
31
|
+
} catch {}
|
|
32
|
+
if (input instanceof Request) return super.fetch(new Request(input, {
|
|
33
|
+
...init,
|
|
34
|
+
headers
|
|
35
|
+
}));
|
|
36
|
+
return super.fetch(input, {
|
|
37
|
+
...init,
|
|
38
|
+
headers
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
_runtimeType() {
|
|
42
|
+
return "netlify";
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
export { NetlifyEnvRunner as t };
|