env-runner 0.1.10 → 0.1.12
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 +18 -1
- package/dist/THIRD-PARTY-LICENSES.md +19 -0
- package/dist/_chunks/{base-runner.d.mts → common-base-runner.d.mts} +34 -0
- package/dist/_chunks/{base-runner.mjs → common-base-runner.mjs} +81 -44
- package/dist/_chunks/{worker-utils.mjs → common-worker-utils.mjs} +54 -17
- package/dist/_chunks/{runner.d.mts → deno-process-runner.d.mts} +1 -1
- package/dist/_chunks/{runner.mjs → deno-process-runner.mjs} +2 -2
- package/dist/_chunks/libs/es-module-lexer.mjs +83 -0
- package/dist/_chunks/{runner2.d.mts → miniflare-runner.d.mts} +13 -1
- package/dist/_chunks/{runner2.mjs → miniflare-runner.mjs} +93 -54
- package/dist/_chunks/{runner5.d.mts → netlify-runner.d.mts} +2 -2
- package/dist/_chunks/{runner5.mjs → netlify-runner.mjs} +2 -1
- package/dist/_chunks/{runner3.d.mts → node-worker-runner.d.mts} +1 -1
- package/dist/_chunks/{runner3.mjs → node-worker-runner.mjs} +2 -2
- package/dist/_chunks/server.mjs +37 -10
- package/dist/_chunks/types.d.mts +11 -1
- package/dist/_chunks/{runner4.d.mts → vercel-runner.d.mts} +2 -2
- package/dist/_chunks/{runner4.mjs → vercel-runner.mjs} +2 -1
- package/dist/_chunks/virtual-loader.mjs +38 -6
- package/dist/index.d.mts +34 -8
- package/dist/index.mjs +5 -5
- package/dist/runners/bun-process/runner.d.mts +1 -1
- package/dist/runners/bun-process/runner.mjs +2 -2
- package/dist/runners/bun-process/worker.mjs +5 -1
- package/dist/runners/deno-process/runner.d.mts +2 -2
- package/dist/runners/deno-process/runner.mjs +1 -1
- package/dist/runners/deno-process/worker.mjs +5 -1
- package/dist/runners/miniflare/runner.d.mts +2 -2
- package/dist/runners/miniflare/runner.mjs +1 -1
- package/dist/runners/netlify/runner.d.mts +2 -2
- package/dist/runners/netlify/runner.mjs +1 -1
- package/dist/runners/node-process/runner.d.mts +1 -1
- package/dist/runners/node-process/runner.mjs +2 -2
- package/dist/runners/node-process/worker.mjs +5 -1
- package/dist/runners/node-worker/runner.d.mts +2 -2
- package/dist/runners/node-worker/runner.mjs +1 -1
- package/dist/runners/node-worker/worker.mjs +5 -1
- package/dist/runners/self/runner.d.mts +2 -1
- package/dist/runners/self/runner.mjs +6 -3
- package/dist/runners/vercel/runner.d.mts +2 -2
- package/dist/runners/vercel/runner.mjs +1 -1
- package/package.json +2 -1
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { virtualModuleFormat } from "./virtual-loader.mjs";
|
|
2
|
-
import { BaseEnvRunner } from "./base-runner.mjs";
|
|
1
|
+
import { expandVirtualInvalidation, stripVirtualTypeScript, virtualModuleFormat } from "./virtual-loader.mjs";
|
|
2
|
+
import { BaseEnvRunner } from "./common-base-runner.mjs";
|
|
3
3
|
import { init, parse } from "./libs/cjs-module-lexer.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { init as init$1, parse as parse$1 } from "./libs/es-module-lexer.mjs";
|
|
5
|
+
import { isVirtualSpecifier } from "./common-worker-utils.mjs";
|
|
5
6
|
import { createRequire } from "node:module";
|
|
6
7
|
import { proxyUpgrade } from "httpxy";
|
|
7
|
-
import { readFileSync } from "node:fs";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { readFileSync } from "node:fs";
|
|
9
10
|
import { basename, dirname, isAbsolute, resolve } from "node:path";
|
|
10
11
|
import { resolveModulePath } from "exsolve";
|
|
11
12
|
const IPC_PATH = "/__env_runner_ipc";
|
|
@@ -184,6 +185,9 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
184
185
|
#miniflareOptions;
|
|
185
186
|
#transformRequest;
|
|
186
187
|
#reloadCounter = 0;
|
|
188
|
+
#virtual;
|
|
189
|
+
#virtualVersions = /* @__PURE__ */ new Map();
|
|
190
|
+
#cacheEntry;
|
|
187
191
|
#ws;
|
|
188
192
|
#persistent;
|
|
189
193
|
#cacheKey;
|
|
@@ -215,7 +219,7 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
215
219
|
this.#ws.close();
|
|
216
220
|
this.#ws = void 0;
|
|
217
221
|
}
|
|
218
|
-
if (this.#cacheKey) _miniflareCache.delete(this.#cacheKey);
|
|
222
|
+
if (this.#cacheKey && _miniflareCache.get(this.#cacheKey)?.mf === this.#miniflare) _miniflareCache.delete(this.#cacheKey);
|
|
219
223
|
await this.#miniflare.dispose();
|
|
220
224
|
this.#miniflare = void 0;
|
|
221
225
|
}
|
|
@@ -224,7 +228,8 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
224
228
|
async fetch(input, init) {
|
|
225
229
|
for (let i = 0; i < 5 && !this._address && !this.closed; i++) await new Promise((r) => setTimeout(r, 100 * Math.pow(2, i)));
|
|
226
230
|
if (!this.#miniflare || this.closed) return new Response("miniflare env runner is unavailable", { status: 503 });
|
|
227
|
-
const
|
|
231
|
+
const resolved = this._resolveFetchInput(input);
|
|
232
|
+
const url = typeof resolved === "string" ? resolved : resolved instanceof URL ? resolved.href : resolved.url;
|
|
228
233
|
const res = await this.#miniflare.dispatchFetch(url, init);
|
|
229
234
|
if (res instanceof Response) return res;
|
|
230
235
|
return new Response(res.body, {
|
|
@@ -251,30 +256,27 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
251
256
|
if (!this.#ws) throw new Error("Miniflare env runner should be initialized before reloading.");
|
|
252
257
|
if (!this._data?.entry) return;
|
|
253
258
|
this.#reloadCounter++;
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
cleanup();
|
|
263
|
-
if (msg.error) reject(typeof msg.error === "string" ? new Error(msg.error) : msg.error);
|
|
264
|
-
else resolve();
|
|
265
|
-
}
|
|
266
|
-
};
|
|
267
|
-
const cleanup = () => {
|
|
268
|
-
clearTimeout(timer);
|
|
269
|
-
this.offMessage(listener);
|
|
270
|
-
};
|
|
271
|
-
this.onMessage(listener);
|
|
272
|
-
this.#ws.send(JSON.stringify({
|
|
273
|
-
type: "reload",
|
|
274
|
-
version
|
|
275
|
-
}));
|
|
259
|
+
await this._request({
|
|
260
|
+
type: "reload",
|
|
261
|
+
version: this.#reloadCounter
|
|
262
|
+
}, {
|
|
263
|
+
match: (msg) => msg?.event === "module-reloaded",
|
|
264
|
+
timeout,
|
|
265
|
+
timeoutError: "Module reload timed out",
|
|
266
|
+
send: (message) => this.#ws.send(JSON.stringify(message))
|
|
276
267
|
});
|
|
277
268
|
}
|
|
269
|
+
async invalidateModule(specifier, _timeout) {
|
|
270
|
+
const virtual = this.#virtual;
|
|
271
|
+
if (!virtual || !Object.hasOwn(virtual, specifier)) {
|
|
272
|
+
const hasVirtual = Object.keys(this._data?.virtual ?? {}).length > 0;
|
|
273
|
+
throw !virtual && hasVirtual && !this.closed ? /* @__PURE__ */ new Error("Miniflare env runner should be initialized before invalidating modules.") : /* @__PURE__ */ new Error(`Cannot invalidate "${specifier}" (not a registered virtual module)`);
|
|
274
|
+
}
|
|
275
|
+
const source = await this._refreshVirtualSource(specifier);
|
|
276
|
+
if (source !== void 0) virtual[specifier] = await this.#prepareVirtualSource(specifier, source);
|
|
277
|
+
for (const key of expandVirtualInvalidation(virtual, specifier)) this.#virtualVersions.set(key, (this.#virtualVersions.get(key) ?? 0) + 1);
|
|
278
|
+
if (this.#cacheKey && _miniflareCache.get(this.#cacheKey) === this.#cacheEntry) _miniflareCache.delete(this.#cacheKey);
|
|
279
|
+
}
|
|
278
280
|
_hasRuntime() {
|
|
279
281
|
return Boolean(this.#miniflare);
|
|
280
282
|
}
|
|
@@ -288,14 +290,12 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
288
290
|
this.#ws.close();
|
|
289
291
|
this.#ws = void 0;
|
|
290
292
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (
|
|
296
|
-
|
|
297
|
-
await this.#miniflare.dispose();
|
|
298
|
-
}
|
|
293
|
+
const entry = this.#cacheEntry;
|
|
294
|
+
if (entry) {
|
|
295
|
+
entry.refCount--;
|
|
296
|
+
if (entry.refCount <= 0) {
|
|
297
|
+
if (this.#cacheKey && _miniflareCache.get(this.#cacheKey) === entry) _miniflareCache.delete(this.#cacheKey);
|
|
298
|
+
await this.#miniflare.dispose();
|
|
299
299
|
}
|
|
300
300
|
} else await this.#miniflare.dispose();
|
|
301
301
|
this.#miniflare = void 0;
|
|
@@ -322,21 +322,21 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
322
322
|
const virtual = this._data?.virtual;
|
|
323
323
|
if (!virtual || Object.keys(virtual).length === 0) return;
|
|
324
324
|
const out = {};
|
|
325
|
-
|
|
326
|
-
for (const [specifier, source] of Object.entries(virtual)) if (virtualModuleFormat(specifier) === "module-typescript") {
|
|
327
|
-
if (!strip) {
|
|
328
|
-
const { stripTypeScriptTypes } = await import("node:module");
|
|
329
|
-
if (typeof stripTypeScriptTypes !== "function") throw new TypeError(`[env-runner] virtual TypeScript module "${specifier}" requires \`module.stripTypeScriptTypes\` on the host (workerd does not parse TypeScript); upgrade Node.js or provide a pre-transpiled JavaScript source instead.`);
|
|
330
|
-
strip = stripTypeScriptTypes;
|
|
331
|
-
}
|
|
332
|
-
out[specifier] = strip(source);
|
|
333
|
-
} else out[specifier] = source;
|
|
325
|
+
for (const [specifier, source] of Object.entries(virtual)) out[specifier] = await this.#prepareVirtualSource(specifier, source);
|
|
334
326
|
return out;
|
|
335
327
|
}
|
|
328
|
+
async #prepareVirtualSource(specifier, source) {
|
|
329
|
+
if (virtualModuleFormat(specifier) !== "module-typescript") return source;
|
|
330
|
+
return stripVirtualTypeScript(specifier, source, await _getStripTypeScriptTypes(), {
|
|
331
|
+
requirement: "on the host (workerd does not parse TypeScript)",
|
|
332
|
+
remedy: "upgrade Node.js"
|
|
333
|
+
});
|
|
334
|
+
}
|
|
336
335
|
async #initAsync() {
|
|
337
336
|
const { Miniflare } = await import("miniflare");
|
|
338
337
|
const entryPath = this._data?.entry;
|
|
339
338
|
const virtual = await this.#prepareVirtualModules();
|
|
339
|
+
this.#virtual = virtual;
|
|
340
340
|
const userFlags = this.#miniflareOptions.compatibilityFlags || [];
|
|
341
341
|
const userDirectSockets = this.#miniflareOptions.unsafeDirectSockets || [];
|
|
342
342
|
const options = {
|
|
@@ -395,13 +395,15 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
395
395
|
if (!options.unsafeModuleFallbackService) {
|
|
396
396
|
const _require = createRequire(entryBase);
|
|
397
397
|
const _virtual = virtual;
|
|
398
|
+
const _virtualVersions = this.#virtualVersions;
|
|
398
399
|
const _transformRequest = this.#transformRequest;
|
|
399
400
|
const _exportConditions = this.#exportConditions;
|
|
401
|
+
const _applyVirtualVersions = (code) => applyVirtualVersions(code, _virtualVersions);
|
|
400
402
|
options.unsafeUseModuleFallbackService = true;
|
|
401
403
|
const modulePathMap = /* @__PURE__ */ new Map();
|
|
402
|
-
const
|
|
404
|
+
const _lexersReady = Promise.all([ensureCjsLexer(), init$1]);
|
|
403
405
|
options.unsafeModuleFallbackService = async (request) => {
|
|
404
|
-
await
|
|
406
|
+
await _lexersReady;
|
|
405
407
|
const url = new URL(request.url);
|
|
406
408
|
const specifier = url.searchParams.get("specifier");
|
|
407
409
|
const rawSpecifier = url.searchParams.get("rawSpecifier");
|
|
@@ -424,7 +426,7 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
424
426
|
json: source
|
|
425
427
|
}) : Response.json({
|
|
426
428
|
name,
|
|
427
|
-
esModule: source
|
|
429
|
+
esModule: _applyVirtualVersions(source)
|
|
428
430
|
});
|
|
429
431
|
}
|
|
430
432
|
}
|
|
@@ -480,7 +482,7 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
480
482
|
modulePathMap.set(name, resolvedPath);
|
|
481
483
|
return Response.json({
|
|
482
484
|
name,
|
|
483
|
-
esModule: result.code
|
|
485
|
+
esModule: _applyVirtualVersions(result.code)
|
|
484
486
|
});
|
|
485
487
|
}
|
|
486
488
|
} catch {}
|
|
@@ -489,7 +491,7 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
489
491
|
modulePathMap.set(name, resolvedPath);
|
|
490
492
|
if (resolvedPath.endsWith(".mjs") || !resolvedPath.endsWith(".cjs") && /\b(import\s|import\(|export\s|export\{|import\.meta\b)/.test(contents)) return Response.json({
|
|
491
493
|
name,
|
|
492
|
-
esModule: contents
|
|
494
|
+
esModule: _applyVirtualVersions(contents)
|
|
493
495
|
});
|
|
494
496
|
const cjsSuffix = "?__cjs";
|
|
495
497
|
if (specifier.endsWith(cjsSuffix)) return Response.json({
|
|
@@ -517,15 +519,23 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
517
519
|
if (cached) {
|
|
518
520
|
this.#miniflare = cached.mf;
|
|
519
521
|
cached.refCount++;
|
|
522
|
+
this.#cacheEntry = cached;
|
|
523
|
+
this.#virtual = cached.virtual;
|
|
524
|
+
this.#virtualVersions = cached.versions;
|
|
520
525
|
}
|
|
521
526
|
}
|
|
522
527
|
if (!this.#miniflare) {
|
|
523
528
|
this.#miniflare = new Miniflare(options);
|
|
524
529
|
await this.#miniflare.ready;
|
|
525
|
-
if (this.#persistent && this.#cacheKey)
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
530
|
+
if (this.#persistent && this.#cacheKey) {
|
|
531
|
+
this.#cacheEntry = {
|
|
532
|
+
mf: this.#miniflare,
|
|
533
|
+
refCount: 1,
|
|
534
|
+
virtual,
|
|
535
|
+
versions: this.#virtualVersions
|
|
536
|
+
};
|
|
537
|
+
_miniflareCache.set(this.#cacheKey, this.#cacheEntry);
|
|
538
|
+
}
|
|
529
539
|
}
|
|
530
540
|
const initRes = await this.#miniflare.dispatchFetch("http://localhost/__env_runner_ipc", { headers: { upgrade: "websocket" } });
|
|
531
541
|
const ws = initRes.webSocket;
|
|
@@ -571,6 +581,35 @@ function computeCacheKey(entryPath, opts) {
|
|
|
571
581
|
for (const [k, v] of Object.entries(opts)) if (typeof v !== "function") serializableOpts[k] = v;
|
|
572
582
|
return `${resolve(entryPath)}::${JSON.stringify(serializableOpts)}`;
|
|
573
583
|
}
|
|
584
|
+
function applyVirtualVersions(code, versions) {
|
|
585
|
+
if (versions.size === 0) return code;
|
|
586
|
+
let imports;
|
|
587
|
+
try {
|
|
588
|
+
[imports] = parse$1(code);
|
|
589
|
+
} catch {
|
|
590
|
+
return code;
|
|
591
|
+
}
|
|
592
|
+
let out = "";
|
|
593
|
+
let last = 0;
|
|
594
|
+
for (const imp of imports) {
|
|
595
|
+
let specifier = imp.n;
|
|
596
|
+
if (specifier === void 0 && imp.d > -1) {
|
|
597
|
+
const expr = code.slice(imp.s, imp.e);
|
|
598
|
+
if (expr.length > 1 && expr[0] === "`" && expr.endsWith("`") && !expr.includes("${")) specifier = expr.slice(1, -1);
|
|
599
|
+
}
|
|
600
|
+
const version = specifier === void 0 ? void 0 : versions.get(specifier);
|
|
601
|
+
if (!version) continue;
|
|
602
|
+
const versioned = `${specifier}?v=${version}`;
|
|
603
|
+
out += code.slice(last, imp.s) + (imp.d > -1 ? JSON.stringify(versioned) : versioned);
|
|
604
|
+
last = imp.e;
|
|
605
|
+
}
|
|
606
|
+
return out + code.slice(last);
|
|
607
|
+
}
|
|
608
|
+
let _stripTypesPromise;
|
|
609
|
+
function _getStripTypeScriptTypes() {
|
|
610
|
+
_stripTypesPromise ??= import("node:module").then((m) => m.stripTypeScriptTypes);
|
|
611
|
+
return _stripTypesPromise;
|
|
612
|
+
}
|
|
574
613
|
let _cjsLexerReady;
|
|
575
614
|
function ensureCjsLexer() {
|
|
576
615
|
if (!_cjsLexerReady) _cjsLexerReady = init();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { WorkerHooks } from "./types.mjs";
|
|
2
|
-
import { EnvRunnerData } from "./base-runner.mjs";
|
|
3
|
-
import { NodeWorkerEnvRunner } from "./
|
|
2
|
+
import { EnvRunnerData } from "./common-base-runner.mjs";
|
|
3
|
+
import { NodeWorkerEnvRunner } from "./node-worker-runner.mjs";
|
|
4
4
|
declare class NetlifyEnvRunner extends NodeWorkerEnvRunner {
|
|
5
5
|
constructor(opts: {
|
|
6
6
|
name: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NodeWorkerEnvRunner } from "./
|
|
1
|
+
import { NodeWorkerEnvRunner } from "./node-worker-runner.mjs";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
let _defaultEntry;
|
|
4
4
|
var NetlifyEnvRunner = class extends NodeWorkerEnvRunner {
|
|
@@ -10,6 +10,7 @@ var NetlifyEnvRunner = class extends NodeWorkerEnvRunner {
|
|
|
10
10
|
});
|
|
11
11
|
}
|
|
12
12
|
async fetch(input, init) {
|
|
13
|
+
input = this._resolveFetchInput(input);
|
|
13
14
|
const headers = new Headers(input instanceof Request ? input.headers : init?.headers);
|
|
14
15
|
const clientIp = headers.get("x-forwarded-for")?.split(",")[0]?.trim() || headers.get("x-real-ip") || "127.0.0.1";
|
|
15
16
|
if (!headers.has("x-nf-client-connection-ip")) headers.set("x-nf-client-connection-ip", clientIp);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WorkerHooks } from "./types.mjs";
|
|
2
|
-
import { BaseEnvRunner, EnvRunnerData } from "./base-runner.mjs";
|
|
2
|
+
import { BaseEnvRunner, EnvRunnerData } from "./common-base-runner.mjs";
|
|
3
3
|
declare class NodeWorkerEnvRunner extends BaseEnvRunner {
|
|
4
4
|
#private;
|
|
5
5
|
constructor(opts: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { BaseEnvRunner } from "./base-runner.mjs";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
1
|
+
import { BaseEnvRunner } from "./common-base-runner.mjs";
|
|
3
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
4
|
import { Worker } from "node:worker_threads";
|
|
5
5
|
let _defaultEntry;
|
|
6
6
|
var NodeWorkerEnvRunner = class extends BaseEnvRunner {
|
package/dist/_chunks/server.mjs
CHANGED
|
@@ -5,6 +5,8 @@ var RunnerManager = class {
|
|
|
5
5
|
_messageListeners = /* @__PURE__ */ new Set();
|
|
6
6
|
_closed = false;
|
|
7
7
|
_reloading = false;
|
|
8
|
+
_moduleInvalidated = false;
|
|
9
|
+
_pendingModuleReload;
|
|
8
10
|
_closeListeners = /* @__PURE__ */ new Set();
|
|
9
11
|
_readyListeners = /* @__PURE__ */ new Set();
|
|
10
12
|
constructor(runner) {
|
|
@@ -18,18 +20,35 @@ var RunnerManager = class {
|
|
|
18
20
|
}
|
|
19
21
|
async reload(runner) {
|
|
20
22
|
this._reloading = true;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
try {
|
|
24
|
+
runner ??= await this._createRunner();
|
|
25
|
+
const prev = this._runner;
|
|
26
|
+
this._detach();
|
|
27
|
+
this._attach(runner);
|
|
28
|
+
this._moduleInvalidated = false;
|
|
29
|
+
if (prev) await prev.close();
|
|
30
|
+
} finally {
|
|
31
|
+
this._reloading = false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
_createRunner() {
|
|
35
|
+
throw new Error("reload() requires a runner argument (this manager has no runner factory)");
|
|
26
36
|
}
|
|
27
37
|
fetch = (input, init) => this._fetch(input, init);
|
|
28
38
|
async _fetch(input, init) {
|
|
29
39
|
const runner = await this._waitForRunner();
|
|
30
40
|
if (!runner) return new Response("Runner is unavailable", { status: 503 });
|
|
41
|
+
if (this._moduleInvalidated) await this._flushInvalidation(runner);
|
|
31
42
|
return runner.fetch(input, init);
|
|
32
43
|
}
|
|
44
|
+
_flushInvalidation(runner) {
|
|
45
|
+
this._pendingModuleReload ??= Promise.resolve(runner.reloadModule?.()).then(() => {
|
|
46
|
+
this._moduleInvalidated = false;
|
|
47
|
+
}).finally(() => {
|
|
48
|
+
this._pendingModuleReload = void 0;
|
|
49
|
+
});
|
|
50
|
+
return this._pendingModuleReload;
|
|
51
|
+
}
|
|
33
52
|
upgrade = (context) => {
|
|
34
53
|
this._runner?.upgrade?.(context);
|
|
35
54
|
};
|
|
@@ -94,7 +113,13 @@ var RunnerManager = class {
|
|
|
94
113
|
}
|
|
95
114
|
async reloadModule(timeout) {
|
|
96
115
|
if (!this._runner?.reloadModule) throw new Error("Active runner does not support reloadModule()");
|
|
97
|
-
|
|
116
|
+
await this._runner.reloadModule(timeout);
|
|
117
|
+
this._moduleInvalidated = false;
|
|
118
|
+
}
|
|
119
|
+
async invalidateModule(specifier, timeout) {
|
|
120
|
+
if (!this._runner?.invalidateModule) throw new Error("Active runner does not support invalidateModule()");
|
|
121
|
+
await this._runner.invalidateModule(specifier, timeout);
|
|
122
|
+
this._moduleInvalidated = true;
|
|
98
123
|
}
|
|
99
124
|
async close() {
|
|
100
125
|
this._closed = true;
|
|
@@ -202,6 +227,10 @@ var EnvServer = class extends RunnerManager {
|
|
|
202
227
|
});
|
|
203
228
|
return this._startPromise;
|
|
204
229
|
}
|
|
230
|
+
async reload(runner) {
|
|
231
|
+
this.runner = runner ?? await this._createRunner();
|
|
232
|
+
await super.reload(this.runner);
|
|
233
|
+
}
|
|
205
234
|
async close() {
|
|
206
235
|
this._stopWatching();
|
|
207
236
|
await super.close();
|
|
@@ -211,8 +240,7 @@ var EnvServer = class extends RunnerManager {
|
|
|
211
240
|
return super._fetch(input, init);
|
|
212
241
|
}
|
|
213
242
|
async _start() {
|
|
214
|
-
|
|
215
|
-
await this.reload(this.runner);
|
|
243
|
+
await this.reload();
|
|
216
244
|
if (this._opts.watch) this._startWatching();
|
|
217
245
|
return this;
|
|
218
246
|
}
|
|
@@ -245,8 +273,7 @@ var EnvServer = class extends RunnerManager {
|
|
|
245
273
|
clearTimeout(this._reloadTimeout);
|
|
246
274
|
this._reloadTimeout = setTimeout(async () => {
|
|
247
275
|
try {
|
|
248
|
-
|
|
249
|
-
await this.reload(this.runner);
|
|
276
|
+
await this.reload();
|
|
250
277
|
for (const fn of this._reloadListeners) fn();
|
|
251
278
|
} catch (error) {
|
|
252
279
|
console.error("Failed to reload runner:", error);
|
package/dist/_chunks/types.d.mts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { IncomingMessage } from "node:http";
|
|
2
2
|
import { Socket } from "node:net";
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Handler for proxying HTTP requests to the worker.
|
|
5
|
+
*
|
|
6
|
+
* A relative string input (e.g. `"/path"`) is resolved against a placeholder
|
|
7
|
+
* `http://localhost` origin before dispatching.
|
|
8
|
+
*/
|
|
4
9
|
type FetchHandler = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
5
10
|
/** Callback for receiving messages from the worker. */
|
|
6
11
|
type RunnerMessageListener = (data: unknown) => void;
|
|
@@ -67,6 +72,11 @@ interface EnvRunner extends RunnerRPCHooks, AsyncDisposable {
|
|
|
67
72
|
rpc<T = unknown>(name: string, data?: unknown, opts?: RPCOptions): Promise<T>;
|
|
68
73
|
/** Re-import the entry module without restarting the worker/process. */
|
|
69
74
|
reloadModule?(timeout?: number): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Invalidate a virtual module so the next `reloadModule()` re-evaluates it.
|
|
77
|
+
* A factory-valued source is re-run for fresh contents.
|
|
78
|
+
*/
|
|
79
|
+
invalidateModule?(specifier: string, timeout?: number): Promise<void>;
|
|
70
80
|
/** Gracefully shut down the worker. */
|
|
71
81
|
close(): Promise<void>;
|
|
72
82
|
/** Alias for `close()`, enabling `await using` (explicit resource management). */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { WorkerHooks } from "./types.mjs";
|
|
2
|
-
import { EnvRunnerData } from "./base-runner.mjs";
|
|
3
|
-
import { NodeWorkerEnvRunner } from "./
|
|
2
|
+
import { EnvRunnerData } from "./common-base-runner.mjs";
|
|
3
|
+
import { NodeWorkerEnvRunner } from "./node-worker-runner.mjs";
|
|
4
4
|
declare class VercelEnvRunner extends NodeWorkerEnvRunner {
|
|
5
5
|
constructor(opts: {
|
|
6
6
|
name: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NodeWorkerEnvRunner } from "./
|
|
1
|
+
import { NodeWorkerEnvRunner } from "./node-worker-runner.mjs";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import { randomBytes } from "node:crypto";
|
|
4
4
|
let _warned = false;
|
|
@@ -56,6 +56,7 @@ var VercelEnvRunner = class extends NodeWorkerEnvRunner {
|
|
|
56
56
|
warnIfVercelOidcTokenInvalid();
|
|
57
57
|
}
|
|
58
58
|
async fetch(input, init) {
|
|
59
|
+
input = this._resolveFetchInput(input);
|
|
59
60
|
const headers = new Headers(input instanceof Request ? input.headers : init?.headers);
|
|
60
61
|
const requestId = generateVercelId();
|
|
61
62
|
if (this._address && this._address.port != null && !headers.has("x-vercel-deployment-url")) {
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
+
import { pathToFileURL } from "node:url";
|
|
1
2
|
async function resolveVirtualModules(virtual) {
|
|
2
3
|
const entries = await Promise.all(Object.entries(virtual).map(async ([key, value]) => [key, typeof value === "function" ? await value() : value]));
|
|
3
4
|
return Object.fromEntries(entries);
|
|
4
5
|
}
|
|
5
6
|
const VIRTUAL_SCHEME = "virtual:";
|
|
6
|
-
function createVirtualHooks(virtual) {
|
|
7
|
+
function createVirtualHooks(virtual, versions, parentURL = _defaultParentURL()) {
|
|
7
8
|
const resolve = (specifier, context, nextResolve) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
const key = _stripQuery(specifier);
|
|
10
|
+
if (Object.hasOwn(virtual, key)) {
|
|
11
|
+
const version = versions?.get(key);
|
|
12
|
+
return {
|
|
13
|
+
url: VIRTUAL_SCHEME + encodeURIComponent(specifier) + (version ? `?v=${version}` : ""),
|
|
14
|
+
shortCircuit: true
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
if (context.parentURL?.startsWith(VIRTUAL_SCHEME)) return nextResolve(specifier, {
|
|
18
|
+
...context,
|
|
19
|
+
parentURL
|
|
20
|
+
});
|
|
12
21
|
return nextResolve(specifier, context);
|
|
13
22
|
};
|
|
14
23
|
const load = (url, context, nextLoad) => {
|
|
@@ -32,8 +41,31 @@ function virtualModuleFormat(specifier) {
|
|
|
32
41
|
if (specifier.endsWith(".ts") || specifier.endsWith(".mts")) return "module-typescript";
|
|
33
42
|
return "module";
|
|
34
43
|
}
|
|
44
|
+
function stripVirtualTypeScript(specifier, source, stripTypeScriptTypes, hints) {
|
|
45
|
+
if (typeof stripTypeScriptTypes !== "function") throw new TypeError(`[env-runner] virtual TypeScript module "${specifier}" requires \`module.stripTypeScriptTypes\` ${hints.requirement}; ${hints.remedy} or provide a pre-transpiled JavaScript source instead.`);
|
|
46
|
+
return stripTypeScriptTypes(source);
|
|
47
|
+
}
|
|
48
|
+
function expandVirtualInvalidation(virtual, specifier) {
|
|
49
|
+
const invalidated = [specifier];
|
|
50
|
+
const seen = new Set(invalidated);
|
|
51
|
+
for (const target of invalidated) {
|
|
52
|
+
const refs = [
|
|
53
|
+
`"${target}"`,
|
|
54
|
+
`'${target}'`,
|
|
55
|
+
"`" + target + "`"
|
|
56
|
+
];
|
|
57
|
+
for (const [key, source] of Object.entries(virtual)) if (!seen.has(key) && refs.some((ref) => source.includes(ref))) {
|
|
58
|
+
seen.add(key);
|
|
59
|
+
invalidated.push(key);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return invalidated;
|
|
63
|
+
}
|
|
35
64
|
function _stripQuery(specifier) {
|
|
36
65
|
const qIndex = specifier.indexOf("?");
|
|
37
66
|
return qIndex === -1 ? specifier : specifier.slice(0, qIndex);
|
|
38
67
|
}
|
|
39
|
-
|
|
68
|
+
function _defaultParentURL() {
|
|
69
|
+
return pathToFileURL(process.cwd() + "/").href;
|
|
70
|
+
}
|
|
71
|
+
export { createVirtualHooks, expandVirtualInvalidation, resolveVirtualModules, stripVirtualTypeScript, virtualModuleFormat };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { EnvRunner, FetchHandler, NodeUpgradeContext, RPCOptions, RunnerMessageListener, RunnerRPCHooks, UpgradeContext, UpgradeHandler, WorkerAddress, WorkerHooks } from "./_chunks/types.mjs";
|
|
2
|
-
import { BaseEnvRunner, EnvRunnerData, VirtualModuleSource, VirtualModules } from "./_chunks/base-runner.mjs";
|
|
3
|
-
import { DenoProcessEnvRunner } from "./_chunks/runner.mjs";
|
|
4
|
-
import { MiniflareEnvRunner, MiniflareEnvRunnerOptions, MiniflareExportInfo, TransformResult } from "./_chunks/
|
|
5
|
-
import { VercelEnvRunner } from "./_chunks/
|
|
6
|
-
import { NetlifyEnvRunner } from "./_chunks/
|
|
2
|
+
import { BaseEnvRunner, EnvRunnerData, VirtualModuleSource, VirtualModules } from "./_chunks/common-base-runner.mjs";
|
|
3
|
+
import { DenoProcessEnvRunner } from "./_chunks/deno-process-runner.mjs";
|
|
4
|
+
import { MiniflareEnvRunner, MiniflareEnvRunnerOptions, MiniflareExportInfo, TransformResult } from "./_chunks/miniflare-runner.mjs";
|
|
5
|
+
import { VercelEnvRunner } from "./_chunks/vercel-runner.mjs";
|
|
6
|
+
import { NetlifyEnvRunner } from "./_chunks/netlify-runner.mjs";
|
|
7
7
|
import { ServerOptions } from "srvx";
|
|
8
8
|
import { Hooks } from "crossws";
|
|
9
9
|
/**
|
|
@@ -16,15 +16,30 @@ declare class RunnerManager implements EnvRunner, AsyncDisposable {
|
|
|
16
16
|
private _messageListeners;
|
|
17
17
|
private _closed;
|
|
18
18
|
private _reloading;
|
|
19
|
+
private _moduleInvalidated;
|
|
20
|
+
private _pendingModuleReload;
|
|
19
21
|
private _closeListeners;
|
|
20
22
|
private _readyListeners;
|
|
21
23
|
constructor(runner?: EnvRunner);
|
|
22
24
|
get ready(): boolean;
|
|
23
25
|
get closed(): boolean;
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Replace the active runner with a new one. Closes the previous runner.
|
|
28
|
+
*
|
|
29
|
+
* When called without a runner, a fresh one is created via `_createRunner()`
|
|
30
|
+
* (only available on subclasses with a runner factory, e.g. `EnvServer`).
|
|
31
|
+
*/
|
|
32
|
+
reload(runner?: EnvRunner): Promise<void>;
|
|
33
|
+
/** Create a fresh runner for argument-less `reload()`. Overridden by subclasses with a runner factory. */
|
|
34
|
+
protected _createRunner(): EnvRunner | Promise<EnvRunner>;
|
|
26
35
|
fetch: FetchHandler;
|
|
27
36
|
protected _fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
|
|
37
|
+
/**
|
|
38
|
+
* Lazily satisfy a pending `invalidateModule()` with a single entry reload
|
|
39
|
+
* before serving — concurrent fetches share the same reload. A failed reload
|
|
40
|
+
* keeps the invalidation pending, so the next fetch retries.
|
|
41
|
+
*/
|
|
42
|
+
private _flushInvalidation;
|
|
28
43
|
upgrade: UpgradeHandler;
|
|
29
44
|
sendMessage(message: unknown): void;
|
|
30
45
|
onMessage(listener: RunnerMessageListener): void;
|
|
@@ -34,6 +49,12 @@ declare class RunnerManager implements EnvRunner, AsyncDisposable {
|
|
|
34
49
|
timeout?: number;
|
|
35
50
|
}): Promise<T>;
|
|
36
51
|
reloadModule(timeout?: number): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Invalidate a virtual module on the active runner and mark the manager
|
|
54
|
+
* dirty: the next `fetch()` reloads the entry automatically, so callers
|
|
55
|
+
* don't need to pair the call with an explicit `reloadModule()`.
|
|
56
|
+
*/
|
|
57
|
+
invalidateModule(specifier: string, timeout?: number): Promise<void>;
|
|
37
58
|
close(): Promise<void>;
|
|
38
59
|
[Symbol.asyncDispose](): Promise<void>;
|
|
39
60
|
onClose(listener: (runner: EnvRunner, cause?: unknown) => void): void;
|
|
@@ -95,11 +116,16 @@ declare class EnvServer extends RunnerManager {
|
|
|
95
116
|
* server. A failed start resets so a later call can retry.
|
|
96
117
|
*/
|
|
97
118
|
start(): Promise<this>;
|
|
119
|
+
/**
|
|
120
|
+
* Replace the active runner. When called without an argument, a fresh
|
|
121
|
+
* runner is created from the server options.
|
|
122
|
+
*/
|
|
123
|
+
reload(runner?: EnvRunner): Promise<void>;
|
|
98
124
|
close(): Promise<void>;
|
|
99
125
|
/** Auto-start on first fetch so an explicit `start()` call is optional. */
|
|
100
126
|
protected _fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
|
|
101
127
|
private _start;
|
|
102
|
-
|
|
128
|
+
protected _createRunner(): Promise<EnvRunner>;
|
|
103
129
|
private _startWatching;
|
|
104
130
|
private _stopWatching;
|
|
105
131
|
private _scheduleReload;
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { BaseEnvRunner } from "./_chunks/base-runner.mjs";
|
|
1
|
+
import { BaseEnvRunner } from "./_chunks/common-base-runner.mjs";
|
|
2
2
|
import { EnvServer, RunnerManager, loadRunner } from "./_chunks/server.mjs";
|
|
3
|
-
import { DenoProcessEnvRunner } from "./_chunks/runner.mjs";
|
|
4
|
-
import { MiniflareEnvRunner } from "./_chunks/
|
|
5
|
-
import { VercelEnvRunner } from "./_chunks/
|
|
6
|
-
import { NetlifyEnvRunner } from "./_chunks/
|
|
3
|
+
import { DenoProcessEnvRunner } from "./_chunks/deno-process-runner.mjs";
|
|
4
|
+
import { MiniflareEnvRunner } from "./_chunks/miniflare-runner.mjs";
|
|
5
|
+
import { VercelEnvRunner } from "./_chunks/vercel-runner.mjs";
|
|
6
|
+
import { NetlifyEnvRunner } from "./_chunks/netlify-runner.mjs";
|
|
7
7
|
export { BaseEnvRunner, DenoProcessEnvRunner, EnvServer, MiniflareEnvRunner, NetlifyEnvRunner, RunnerManager, VercelEnvRunner, loadRunner };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WorkerHooks } from "../../_chunks/types.mjs";
|
|
2
|
-
import { BaseEnvRunner, EnvRunnerData } from "../../_chunks/base-runner.mjs";
|
|
2
|
+
import { BaseEnvRunner, EnvRunnerData } from "../../_chunks/common-base-runner.mjs";
|
|
3
3
|
declare class BunProcessEnvRunner extends BaseEnvRunner {
|
|
4
4
|
#private;
|
|
5
5
|
constructor(opts: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { BaseEnvRunner } from "../../_chunks/base-runner.mjs";
|
|
1
|
+
import { BaseEnvRunner } from "../../_chunks/common-base-runner.mjs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
2
3
|
import { existsSync } from "node:fs";
|
|
3
4
|
import { execSync, spawn } from "node:child_process";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
let _defaultEntry;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isVirtualSpecifier, parseServerAddress, registerVirtualModules, reloadEntryModule, resolveEntry } from "../../_chunks/worker-utils.mjs";
|
|
1
|
+
import { handleInvalidateModule, isVirtualSpecifier, parseServerAddress, registerVirtualModules, reloadEntryModule, resolveEntry } from "../../_chunks/common-worker-utils.mjs";
|
|
2
2
|
import { serve } from "srvx";
|
|
3
3
|
import { plugin } from "crossws/server";
|
|
4
4
|
process.on("disconnect", () => process.exit(0));
|
|
@@ -58,6 +58,10 @@ process.on("message", async (message) => {
|
|
|
58
58
|
}
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
61
|
+
if (message?.event === "invalidate-module") {
|
|
62
|
+
handleInvalidateModule(message, sendMessage);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
61
65
|
if (message?.type === "ping") {
|
|
62
66
|
process.send({
|
|
63
67
|
type: "pong",
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { EnvRunnerData } from "../../_chunks/base-runner.mjs";
|
|
2
|
-
import { DenoProcessEnvRunner } from "../../_chunks/runner.mjs";
|
|
1
|
+
import { EnvRunnerData } from "../../_chunks/common-base-runner.mjs";
|
|
2
|
+
import { DenoProcessEnvRunner } from "../../_chunks/deno-process-runner.mjs";
|
|
3
3
|
export { DenoProcessEnvRunner, type EnvRunnerData as DenoProcessEnvRunnerData };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { DenoProcessEnvRunner } from "../../_chunks/runner.mjs";
|
|
1
|
+
import { DenoProcessEnvRunner } from "../../_chunks/deno-process-runner.mjs";
|
|
2
2
|
export { DenoProcessEnvRunner };
|