env-runner 0.2.3 → 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 +154 -19
- package/dist/_chunks/common-base-runner.d.mts +52 -14
- package/dist/_chunks/common-base-runner.mjs +123 -32
- package/dist/_chunks/common-process-data.mjs +13 -0
- package/dist/_chunks/common-worker-utils.mjs +464 -73
- package/dist/_chunks/deno-process-runner.mjs +2 -6
- package/dist/_chunks/lexer.mjs +2 -0
- package/dist/_chunks/libs/cjs-module-lexer.mjs +6 -1
- package/dist/_chunks/miniflare-runner.d.mts +21 -12
- package/dist/_chunks/miniflare-runner.mjs +266 -96
- package/dist/_chunks/node-worker-runner.mjs +1 -2
- package/dist/_chunks/server.mjs +20 -5
- package/dist/_chunks/types.d.mts +36 -0
- package/dist/_chunks/virtual-loader.mjs +303 -29
- package/dist/index.d.mts +17 -4
- 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 +8 -8
- 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/package.json +11 -11
- package/dist/_chunks/common-host-env.mjs +0 -11
package/dist/_chunks/server.mjs
CHANGED
|
@@ -168,9 +168,16 @@ var RunnerManager = class {
|
|
|
168
168
|
await this._runner.reloadModule(timeout);
|
|
169
169
|
this._moduleInvalidated = false;
|
|
170
170
|
}
|
|
171
|
+
async updateVirtualModules(changes, timeout) {
|
|
172
|
+
const runner = await this._waitForRunner();
|
|
173
|
+
if (!runner?.updateVirtualModules) throw new Error("Active runner does not support updateVirtualModules()");
|
|
174
|
+
await runner.updateVirtualModules(changes, timeout);
|
|
175
|
+
this._moduleInvalidated = true;
|
|
176
|
+
}
|
|
171
177
|
async invalidateModule(specifier, timeout) {
|
|
172
|
-
|
|
173
|
-
|
|
178
|
+
const runner = await this._waitForRunner();
|
|
179
|
+
if (!runner?.invalidateModule) throw new Error("Active runner does not support invalidateModule()");
|
|
180
|
+
await runner.invalidateModule(specifier, timeout);
|
|
174
181
|
this._moduleInvalidated = true;
|
|
175
182
|
}
|
|
176
183
|
async close() {
|
|
@@ -208,11 +215,11 @@ var RunnerManager = class {
|
|
|
208
215
|
runner.onMessage(this._internalListener);
|
|
209
216
|
for (const listener of this._messageListeners) runner.onMessage(listener);
|
|
210
217
|
const originalClose = runner.close.bind(runner);
|
|
211
|
-
runner.close = async () => {
|
|
212
|
-
await originalClose();
|
|
218
|
+
runner.close = async (cause) => {
|
|
219
|
+
await originalClose(cause);
|
|
213
220
|
if (this._runner === runner) {
|
|
214
221
|
this._runner = void 0;
|
|
215
|
-
for (const fn of this._closeListeners) fn(this);
|
|
222
|
+
for (const fn of this._closeListeners) fn(this, cause);
|
|
216
223
|
}
|
|
217
224
|
};
|
|
218
225
|
if (runner.ready) this._flushQueue();
|
|
@@ -263,6 +270,7 @@ var EnvServer = class extends RunnerManager {
|
|
|
263
270
|
_reloadTimeout;
|
|
264
271
|
_reloadListeners = /* @__PURE__ */ new Set();
|
|
265
272
|
_startPromise;
|
|
273
|
+
_virtual;
|
|
266
274
|
runner = null;
|
|
267
275
|
onReload(listener) {
|
|
268
276
|
this._reloadListeners.add(listener);
|
|
@@ -285,6 +293,12 @@ var EnvServer = class extends RunnerManager {
|
|
|
285
293
|
this.runner = runner ?? await this._createRunner();
|
|
286
294
|
await super.reload(this.runner);
|
|
287
295
|
}
|
|
296
|
+
async updateVirtualModules(changes, timeout) {
|
|
297
|
+
const virtual = this._virtual ??= { ...this._opts.data?.virtual };
|
|
298
|
+
for (const [key, source] of Object.entries(changes)) if (source === null) delete virtual[key];
|
|
299
|
+
else if (source !== void 0) virtual[key] = source;
|
|
300
|
+
if (await this._waitForRunner()) await super.updateVirtualModules(changes, timeout);
|
|
301
|
+
}
|
|
288
302
|
async close() {
|
|
289
303
|
this._stopWatching();
|
|
290
304
|
await super.close();
|
|
@@ -305,6 +319,7 @@ var EnvServer = class extends RunnerManager {
|
|
|
305
319
|
hooks: this._opts.hooks,
|
|
306
320
|
data: {
|
|
307
321
|
...this._opts.data,
|
|
322
|
+
...this._virtual && { virtual: { ...this._virtual } },
|
|
308
323
|
entry: this._opts.entry
|
|
309
324
|
},
|
|
310
325
|
execArgv: this._opts.execArgv
|
package/dist/_chunks/types.d.mts
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
import { IncomingMessage } from "node:http";
|
|
2
2
|
import { Socket } from "node:net";
|
|
3
|
+
/**
|
|
4
|
+
* How a virtual module is served. Code formats are Node's load formats, plus
|
|
5
|
+
* `jsx`/`tsx` (Bun only). Raw formats follow import attribute types: `text`
|
|
6
|
+
* default-exports a string, `bytes` a `Uint8Array`, and `wasm` a compiled
|
|
7
|
+
* `WebAssembly.Module` (like workerd's Wasm modules).
|
|
8
|
+
*/
|
|
9
|
+
export type VirtualModuleFormat = "module" | "commonjs" | "module-typescript" | "commonjs-typescript" | "jsx" | "tsx" | "json" | "text" | "bytes" | "wasm";
|
|
10
|
+
/** A source with an explicit format. */
|
|
11
|
+
export interface VirtualModule {
|
|
12
|
+
/** Code or text; a `Uint8Array` for the `bytes` and `wasm` formats. */
|
|
13
|
+
source: string | Uint8Array;
|
|
14
|
+
/**
|
|
15
|
+
* Default: by the key's extension (`.cjs`, `.json`, `.ts`, `.wasm`, ...),
|
|
16
|
+
* else `module` for a string and `bytes` for a `Uint8Array`.
|
|
17
|
+
*/
|
|
18
|
+
format?: VirtualModuleFormat;
|
|
19
|
+
}
|
|
20
|
+
/** A source whose format follows its key (string or bytes), or a source with its format. */
|
|
21
|
+
export type VirtualModuleContent = string | Uint8Array | VirtualModule;
|
|
22
|
+
/** Factories run on the host: before the worker spawns, and on updates. */
|
|
23
|
+
export type VirtualModuleSource = VirtualModuleContent | (() => VirtualModuleContent | Promise<VirtualModuleContent>);
|
|
24
|
+
/** Virtual modules as a `specifier => source` map. */
|
|
25
|
+
export type VirtualModules = Record<string, VirtualModuleSource>;
|
|
26
|
+
/** Changes to apply to a running map: a source sets (adds or replaces) a key, `null` removes it. */
|
|
27
|
+
export type VirtualModuleUpdates = Record<string, VirtualModuleSource | null>;
|
|
28
|
+
/**
|
|
29
|
+
* A validated module, as workers receive it: a string follows its key's
|
|
30
|
+
* extension, any other source carries its format.
|
|
31
|
+
*/
|
|
32
|
+
export type ResolvedVirtualModule = string | Required<VirtualModule>;
|
|
3
33
|
/** Proxy a request to the worker; relative inputs resolve against `http://localhost`. */
|
|
4
34
|
export type FetchHandler = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
5
35
|
/** Callback for receiving messages from the worker. */
|
|
@@ -65,6 +95,12 @@ export interface EnvRunner extends RunnerRPCHooks, AsyncDisposable {
|
|
|
65
95
|
rpc<T = unknown>(name: string, data?: unknown, opts?: RPCOptions): Promise<T>;
|
|
66
96
|
/** Re-import the entry module without restarting the worker/process. */
|
|
67
97
|
reloadModule?(timeout?: number): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Set (add or replace) and remove (`null`) virtual modules in one round trip.
|
|
100
|
+
* Changed and removed modules, and their importers, evaluate fresh on the
|
|
101
|
+
* next `reloadModule()`; a removed key falls through to normal resolution.
|
|
102
|
+
*/
|
|
103
|
+
updateVirtualModules?(changes: VirtualModuleUpdates, timeout?: number): Promise<void>;
|
|
68
104
|
/**
|
|
69
105
|
* Invalidate a virtual module so the next `reloadModule()` re-evaluates it.
|
|
70
106
|
* A factory-valued source is re-run for fresh contents.
|
|
@@ -1,71 +1,345 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import { isAbsolute } from "node:path";
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
|
+
const VIRTUAL_MODULE_FORMATS = /* @__PURE__ */ new Set([
|
|
5
|
+
"module",
|
|
6
|
+
"commonjs",
|
|
7
|
+
"module-typescript",
|
|
8
|
+
"commonjs-typescript",
|
|
9
|
+
"jsx",
|
|
10
|
+
"tsx",
|
|
11
|
+
"json",
|
|
12
|
+
"text",
|
|
13
|
+
"bytes",
|
|
14
|
+
"wasm"
|
|
15
|
+
]);
|
|
16
|
+
const EXTENSION_FORMATS = {
|
|
17
|
+
".cjs": "commonjs",
|
|
18
|
+
".json": "json",
|
|
19
|
+
".ts": "module-typescript",
|
|
20
|
+
".mts": "module-typescript",
|
|
21
|
+
".cts": "commonjs-typescript",
|
|
22
|
+
".jsx": "jsx",
|
|
23
|
+
".tsx": "tsx",
|
|
24
|
+
".wasm": "wasm"
|
|
25
|
+
};
|
|
26
|
+
function virtualModuleFormat(key, module) {
|
|
27
|
+
return typeof module === "object" ? module.format : _extensionFormat(key) ?? "module";
|
|
28
|
+
}
|
|
29
|
+
function virtualModuleCodeSource(key, module) {
|
|
30
|
+
if (module === void 0) return;
|
|
31
|
+
const format = virtualModuleFormat(key, module);
|
|
32
|
+
if (format === "json" || format === "text" || _isBinaryFormat(format)) return;
|
|
33
|
+
return typeof module === "string" ? module : module.source;
|
|
34
|
+
}
|
|
35
|
+
function normalizeVirtualModule(key, content) {
|
|
36
|
+
const module = typeof content === "string" || content instanceof Uint8Array ? { source: content } : content;
|
|
37
|
+
const source = module?.source;
|
|
38
|
+
if (typeof source !== "string" && !(source instanceof Uint8Array)) throw new TypeError(`[env-runner] virtual module "${key}" must be a string, a Uint8Array or { source, format }.`);
|
|
39
|
+
const binary = typeof source !== "string";
|
|
40
|
+
const format = module.format ?? _extensionFormat(key) ?? (binary ? "bytes" : "module");
|
|
41
|
+
if (!VIRTUAL_MODULE_FORMATS.has(format)) throw new TypeError(`[env-runner] virtual module "${key}" has an unknown format "${format}" (expected one of: ${[...VIRTUAL_MODULE_FORMATS].join(", ")}).`);
|
|
42
|
+
if (binary !== _isBinaryFormat(format)) throw new TypeError(`[env-runner] virtual module "${key}" has format "${format}", which needs ${binary ? "a string" : "a Uint8Array"} source.`);
|
|
43
|
+
if (format === "wasm" && !WebAssembly.validate(source)) throw new TypeError(`[env-runner] virtual module "${key}" is not a valid WebAssembly binary.`);
|
|
44
|
+
if (typeof content === "string") return content;
|
|
45
|
+
return {
|
|
46
|
+
source: binary ? new Uint8Array(source) : source,
|
|
47
|
+
format
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function normalizeVirtualModules(virtual) {
|
|
51
|
+
const out = {};
|
|
52
|
+
for (const [key, content] of Object.entries(virtual)) out[key] = normalizeVirtualModule(key, content);
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
2
55
|
async function resolveVirtualModules(virtual) {
|
|
3
|
-
const entries = await Promise.all(Object.entries(virtual).map(async ([key, value]) =>
|
|
56
|
+
const entries = await Promise.all(Object.entries(virtual).map(async ([key, value]) => {
|
|
57
|
+
return [key, normalizeVirtualModule(key, typeof value === "function" ? await value() : value)];
|
|
58
|
+
}));
|
|
4
59
|
return Object.fromEntries(entries);
|
|
5
60
|
}
|
|
61
|
+
function encodeVirtualModules(virtual) {
|
|
62
|
+
const out = {};
|
|
63
|
+
for (const [key, module] of Object.entries(virtual)) out[key] = module !== null && typeof module === "object" && typeof module.source !== "string" ? {
|
|
64
|
+
source: _toBase64(module.source),
|
|
65
|
+
format: module.format,
|
|
66
|
+
encoding: "base64"
|
|
67
|
+
} : module;
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
function decodeVirtualModules(virtual) {
|
|
71
|
+
const out = {};
|
|
72
|
+
for (const [key, module] of Object.entries(virtual)) out[key] = module !== null && typeof module === "object" && "encoding" in module ? {
|
|
73
|
+
source: new Uint8Array(Buffer.from(module.source, "base64")),
|
|
74
|
+
format: module.format
|
|
75
|
+
} : module;
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
function virtualModuleCode(format, source) {
|
|
79
|
+
if (format === "text") return `export default ${JSON.stringify(source)};\n`;
|
|
80
|
+
const bytes = [
|
|
81
|
+
`const base64 = ${JSON.stringify(_toBase64(source))};`,
|
|
82
|
+
`let bytes;`,
|
|
83
|
+
`if (typeof Uint8Array.fromBase64 === "function") {`,
|
|
84
|
+
` bytes = Uint8Array.fromBase64(base64);`,
|
|
85
|
+
`} else {`,
|
|
86
|
+
` const binary = atob(base64);`,
|
|
87
|
+
` bytes = new Uint8Array(binary.length);`,
|
|
88
|
+
` for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);`,
|
|
89
|
+
`}`
|
|
90
|
+
];
|
|
91
|
+
const value = format === "wasm" ? "new WebAssembly.Module(bytes)" : "bytes";
|
|
92
|
+
return `${bytes.join("\n")}\nexport default ${value};\n`;
|
|
93
|
+
}
|
|
94
|
+
function unsupportedVirtualJSXError(key, format, runtime) {
|
|
95
|
+
return /* @__PURE__ */ new TypeError(`[env-runner] virtual module "${key}" has format "${format}", which ${runtime} can't load from memory; pre-transpile it to JavaScript (and pass { source, format: "module" } to keep the key).`);
|
|
96
|
+
}
|
|
6
97
|
const VIRTUAL_SCHEME = "virtual:";
|
|
7
|
-
function createVirtualHooks(virtual,
|
|
98
|
+
function createVirtualHooks(virtual, opts = {}) {
|
|
99
|
+
const { versions, importers, forcePlainModule } = opts;
|
|
100
|
+
const baseURL = opts.baseURL ?? _defaultBaseURL();
|
|
101
|
+
const keyURLs = /* @__PURE__ */ new Map();
|
|
102
|
+
const urlKeys = /* @__PURE__ */ new Map();
|
|
103
|
+
for (const key of Object.keys(virtual)) {
|
|
104
|
+
const url = _virtualKeyURL(key);
|
|
105
|
+
if (url) {
|
|
106
|
+
keyURLs.set(key, url);
|
|
107
|
+
urlKeys.set(url, key);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const keyOf = (url) => {
|
|
111
|
+
if (url.startsWith(VIRTUAL_SCHEME)) {
|
|
112
|
+
const key = _virtualURLKey(url);
|
|
113
|
+
return key !== void 0 && Object.hasOwn(virtual, key) ? key : void 0;
|
|
114
|
+
}
|
|
115
|
+
return urlKeys.size > 0 && url.startsWith("file:") ? urlKeys.get(_bareURL(url)) : void 0;
|
|
116
|
+
};
|
|
117
|
+
const moduleFiles = importers && /* @__PURE__ */ new Set();
|
|
118
|
+
const track = (node, context) => {
|
|
119
|
+
const { parentURL } = context;
|
|
120
|
+
if (!importers || !parentURL) return;
|
|
121
|
+
let importer = keyOf(parentURL);
|
|
122
|
+
if (importer === void 0 && !context.conditions?.includes("require")) {
|
|
123
|
+
const file = _bareURL(parentURL);
|
|
124
|
+
importer = moduleFiles.has(file) ? file : void 0;
|
|
125
|
+
}
|
|
126
|
+
if (importer !== void 0 && importer !== node) {
|
|
127
|
+
let set = importers.get(node);
|
|
128
|
+
if (!set) importers.set(node, set = /* @__PURE__ */ new Set());
|
|
129
|
+
set.add(importer);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
const serve = (key, url, context) => {
|
|
133
|
+
track(key, context);
|
|
134
|
+
if (opts.opaquePathURL && keyURLs.has(key) && opts.opaquePathURL(keyURLs.get(key))) url = _virtualURL(key) + url.slice(_bareURL(url).length);
|
|
135
|
+
const version = versions?.get(key);
|
|
136
|
+
return {
|
|
137
|
+
url: version ? _appendQuery(url, `v=${version}`) : url,
|
|
138
|
+
shortCircuit: true
|
|
139
|
+
};
|
|
140
|
+
};
|
|
8
141
|
const resolve = (specifier, context, nextResolve) => {
|
|
9
142
|
const key = _stripQuery(specifier);
|
|
10
143
|
if (Object.hasOwn(virtual, key)) {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
144
|
+
const keyURL = keyURLs.get(key);
|
|
145
|
+
const url = keyURL ? keyURL + specifier.slice(key.length) : _virtualURL(specifier);
|
|
146
|
+
return serve(key, url, context);
|
|
147
|
+
}
|
|
148
|
+
let parentURL = context.parentURL;
|
|
149
|
+
if (parentURL?.startsWith(VIRTUAL_SCHEME)) {
|
|
150
|
+
const parentKey = keyOf(parentURL);
|
|
151
|
+
parentURL = parentKey !== void 0 && keyURLs.get(parentKey) || baseURL;
|
|
16
152
|
}
|
|
17
|
-
if (
|
|
153
|
+
if (urlKeys.size > 0) {
|
|
154
|
+
const url = _resolvePathSpecifier(specifier, parentURL);
|
|
155
|
+
const pathKey = url && urlKeys.get(_bareURL(url));
|
|
156
|
+
if (pathKey) return serve(pathKey, url, context);
|
|
157
|
+
}
|
|
158
|
+
const result = nextResolve(specifier, parentURL === context.parentURL ? context : {
|
|
18
159
|
...context,
|
|
19
160
|
parentURL
|
|
20
161
|
});
|
|
21
|
-
|
|
162
|
+
const resolvedKey = urlKeys.size > 0 ? urlKeys.get(_bareURL(result.url)) : void 0;
|
|
163
|
+
if (resolvedKey) return serve(resolvedKey, result.url, context);
|
|
164
|
+
if (result.url.startsWith("file:")) {
|
|
165
|
+
const file = _bareURL(result.url);
|
|
166
|
+
track(file, context);
|
|
167
|
+
const version = versions?.get(file);
|
|
168
|
+
if (version) return {
|
|
169
|
+
...result,
|
|
170
|
+
url: _appendQuery(result.url, `v=${version}`)
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
return result;
|
|
22
174
|
};
|
|
23
175
|
const load = (url, context, nextLoad) => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
176
|
+
const key = keyOf(url);
|
|
177
|
+
if (key !== void 0) {
|
|
178
|
+
const module = virtual[key];
|
|
179
|
+
if (forcePlainModule) return {
|
|
180
|
+
format: "module",
|
|
181
|
+
source: module,
|
|
182
|
+
shortCircuit: true
|
|
183
|
+
};
|
|
184
|
+
return {
|
|
185
|
+
..._nodeLoadResult(key, module),
|
|
29
186
|
shortCircuit: true
|
|
30
187
|
};
|
|
31
188
|
}
|
|
32
|
-
|
|
189
|
+
const result = nextLoad(url, context);
|
|
190
|
+
if (moduleFiles && url.startsWith("file:") && _isModuleFormat(result.format)) moduleFiles.add(_bareURL(url));
|
|
191
|
+
return result;
|
|
192
|
+
};
|
|
193
|
+
const updateKeys = (keys) => {
|
|
194
|
+
for (const key of keys) {
|
|
195
|
+
const url = keyURLs.get(key) ?? _virtualKeyURL(key);
|
|
196
|
+
if (!url) continue;
|
|
197
|
+
if (Object.hasOwn(virtual, key)) {
|
|
198
|
+
keyURLs.set(key, url);
|
|
199
|
+
urlKeys.set(url, key);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
keyURLs.delete(key);
|
|
203
|
+
if (urlKeys.get(url) === key) {
|
|
204
|
+
urlKeys.delete(url);
|
|
205
|
+
for (const [other, otherURL] of keyURLs) if (otherURL === url) urlKeys.set(url, other);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
33
208
|
};
|
|
34
209
|
return {
|
|
35
210
|
resolve,
|
|
36
|
-
load
|
|
211
|
+
load,
|
|
212
|
+
updateKeys
|
|
37
213
|
};
|
|
38
214
|
}
|
|
39
|
-
function
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
215
|
+
function _nodeLoadResult(key, module) {
|
|
216
|
+
const format = virtualModuleFormat(key, module);
|
|
217
|
+
const source = typeof module === "string" ? module : module.source;
|
|
218
|
+
if (format === "text" || format === "bytes" || format === "wasm") return {
|
|
219
|
+
format: "module",
|
|
220
|
+
source: virtualModuleCode(format, source)
|
|
221
|
+
};
|
|
222
|
+
if (format === "jsx" || format === "tsx") throw unsupportedVirtualJSXError(key, format, "Node.js");
|
|
223
|
+
return {
|
|
224
|
+
format,
|
|
225
|
+
source
|
|
226
|
+
};
|
|
43
227
|
}
|
|
44
228
|
function stripVirtualTypeScript(specifier, source, stripTypeScriptTypes, hints) {
|
|
45
229
|
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
|
-
|
|
230
|
+
try {
|
|
231
|
+
return stripTypeScriptTypes(source);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
throw new SyntaxError(`[env-runner] failed to strip types from virtual module "${specifier}": ${error?.message || error}`, { cause: error });
|
|
234
|
+
}
|
|
47
235
|
}
|
|
48
|
-
function expandVirtualInvalidation(virtual, specifier) {
|
|
49
|
-
const invalidated = [specifier];
|
|
236
|
+
function expandVirtualInvalidation(virtual, specifier, importers) {
|
|
237
|
+
const invalidated = [...new Set(typeof specifier === "string" ? [specifier] : specifier)];
|
|
50
238
|
const seen = new Set(invalidated);
|
|
239
|
+
const add = (key) => {
|
|
240
|
+
if (!seen.has(key)) {
|
|
241
|
+
seen.add(key);
|
|
242
|
+
invalidated.push(key);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
51
245
|
for (const target of invalidated) {
|
|
246
|
+
for (const key of importers?.get(target) ?? []) add(key);
|
|
247
|
+
if (!Object.hasOwn(virtual, target)) continue;
|
|
52
248
|
const refs = [
|
|
53
249
|
`"${target}"`,
|
|
54
250
|
`'${target}'`,
|
|
55
251
|
"`" + target + "`"
|
|
56
252
|
];
|
|
57
|
-
for (const [key,
|
|
58
|
-
|
|
59
|
-
|
|
253
|
+
for (const [key, module] of Object.entries(virtual)) {
|
|
254
|
+
const code = virtualModuleCodeSource(key, module);
|
|
255
|
+
if (code !== void 0 && refs.some((ref) => code.includes(ref))) add(key);
|
|
60
256
|
}
|
|
61
257
|
}
|
|
62
258
|
return invalidated;
|
|
63
259
|
}
|
|
260
|
+
function findVirtualPathKey(virtual, specifier) {
|
|
261
|
+
const url = _virtualKeyURL(_stripQuery(specifier));
|
|
262
|
+
if (!url) return;
|
|
263
|
+
let match;
|
|
264
|
+
for (const key of Object.keys(virtual)) if (_virtualKeyURL(key) === url) match = key;
|
|
265
|
+
return match;
|
|
266
|
+
}
|
|
267
|
+
const _warnedPathCollisions = /* @__PURE__ */ new Set();
|
|
268
|
+
function warnVirtualPathCollisions(keys) {
|
|
269
|
+
try {
|
|
270
|
+
const urlKeys = /* @__PURE__ */ new Map();
|
|
271
|
+
for (const key of keys) {
|
|
272
|
+
const url = _virtualKeyURL(key);
|
|
273
|
+
if (!url) continue;
|
|
274
|
+
const other = urlKeys.get(url);
|
|
275
|
+
if (other !== void 0 && !_warnedPathCollisions.has(`${other}\0${key}`)) {
|
|
276
|
+
_warnedPathCollisions.add(`${other}\0${key}`);
|
|
277
|
+
console.warn(`[env-runner] virtual modules "${other}" and "${key}" name the same file; keep only one of them (which one is served depends on the runtime).`);
|
|
278
|
+
}
|
|
279
|
+
urlKeys.set(url, key);
|
|
280
|
+
}
|
|
281
|
+
} catch {}
|
|
282
|
+
}
|
|
283
|
+
const virtualKeyURL = _virtualKeyURL;
|
|
284
|
+
function virtualModuleCacheId(key) {
|
|
285
|
+
const url = _virtualKeyURL(key);
|
|
286
|
+
return url ? fileURLToPath(url) : _virtualURL(key);
|
|
287
|
+
}
|
|
288
|
+
function _virtualKeyURL(key) {
|
|
289
|
+
if (key.startsWith("file:")) try {
|
|
290
|
+
return _bareURL(new URL(key).href);
|
|
291
|
+
} catch {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
return isAbsolute(key) ? pathToFileURL(key).href : void 0;
|
|
295
|
+
}
|
|
296
|
+
function _virtualURL(specifier) {
|
|
297
|
+
return VIRTUAL_SCHEME + specifier.replace(/[\0-\x20"%<>`\x7F-\u{10FFFF}]/gu, encodeURIComponent);
|
|
298
|
+
}
|
|
299
|
+
function _virtualURLKey(url) {
|
|
300
|
+
try {
|
|
301
|
+
return decodeURIComponent(_stripQuery(url.slice(8)));
|
|
302
|
+
} catch {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function _extensionFormat(key) {
|
|
307
|
+
const extension = /\.[^./\\]+$/.exec(key)?.[0];
|
|
308
|
+
return extension === void 0 ? void 0 : EXTENSION_FORMATS[extension];
|
|
309
|
+
}
|
|
310
|
+
function _isBinaryFormat(format) {
|
|
311
|
+
return format === "bytes" || format === "wasm";
|
|
312
|
+
}
|
|
313
|
+
function _toBase64(bytes) {
|
|
314
|
+
return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
|
|
315
|
+
}
|
|
316
|
+
function _isModuleFormat(format) {
|
|
317
|
+
return format == null || format === "module" || format === "module-typescript";
|
|
318
|
+
}
|
|
64
319
|
function _stripQuery(specifier) {
|
|
65
320
|
const qIndex = specifier.indexOf("?");
|
|
66
321
|
return qIndex === -1 ? specifier : specifier.slice(0, qIndex);
|
|
67
322
|
}
|
|
68
|
-
function
|
|
323
|
+
function _bareURL(url) {
|
|
324
|
+
const index = url.search(/[?#]/);
|
|
325
|
+
return index === -1 ? url : url.slice(0, index);
|
|
326
|
+
}
|
|
327
|
+
function _appendQuery(url, param) {
|
|
328
|
+
const hashIndex = url.startsWith(VIRTUAL_SCHEME) ? -1 : url.indexOf("#");
|
|
329
|
+
const base = hashIndex === -1 ? url : url.slice(0, hashIndex);
|
|
330
|
+
const hash = hashIndex === -1 ? "" : url.slice(hashIndex);
|
|
331
|
+
return base + (base.includes("?") ? "&" : "?") + param + hash;
|
|
332
|
+
}
|
|
333
|
+
function _resolvePathSpecifier(specifier, parentURL) {
|
|
334
|
+
try {
|
|
335
|
+
if (/^\.{0,2}\//.test(specifier) || specifier.startsWith("file:")) return new URL(specifier, parentURL).href;
|
|
336
|
+
if (isAbsolute(specifier)) {
|
|
337
|
+
const path = _stripQuery(specifier);
|
|
338
|
+
return pathToFileURL(path).href + specifier.slice(path.length);
|
|
339
|
+
}
|
|
340
|
+
} catch {}
|
|
341
|
+
}
|
|
342
|
+
function _defaultBaseURL() {
|
|
69
343
|
return pathToFileURL(process.cwd() + "/").href;
|
|
70
344
|
}
|
|
71
|
-
export { createVirtualHooks, expandVirtualInvalidation, resolveVirtualModules, stripVirtualTypeScript, virtualModuleFormat };
|
|
345
|
+
export { createVirtualHooks, decodeVirtualModules, encodeVirtualModules, expandVirtualInvalidation, findVirtualPathKey, normalizeVirtualModules, resolveVirtualModules, stripVirtualTypeScript, unsupportedVirtualJSXError, virtualKeyURL, virtualModuleCacheId, virtualModuleCode, virtualModuleCodeSource, virtualModuleFormat, warnVirtualPathCollisions };
|
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";
|
|
@@ -45,6 +45,11 @@ export declare class RunnerManager implements EnvRunner, AsyncDisposable {
|
|
|
45
45
|
timeout?: number;
|
|
46
46
|
}): Promise<T>;
|
|
47
47
|
reloadModule(timeout?: number): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Set or remove (`null`) virtual modules of the active runner (waiting for it
|
|
50
|
+
* during a reload); the next `fetch()` reloads the entry automatically.
|
|
51
|
+
*/
|
|
52
|
+
updateVirtualModules(changes: VirtualModuleUpdates, timeout?: number): Promise<void>;
|
|
48
53
|
/** Invalidate a virtual module; the next `fetch()` reloads the entry automatically. */
|
|
49
54
|
invalidateModule(specifier: string, timeout?: number): Promise<void>;
|
|
50
55
|
close(): Promise<void>;
|
|
@@ -56,7 +61,8 @@ export declare class RunnerManager implements EnvRunner, AsyncDisposable {
|
|
|
56
61
|
private _internalListener;
|
|
57
62
|
private _attach;
|
|
58
63
|
private _detach;
|
|
59
|
-
|
|
64
|
+
/** The active runner, waiting for one during a reload. */
|
|
65
|
+
protected _waitForRunner(timeout?: number): Promise<EnvRunner | undefined>;
|
|
60
66
|
private _flushQueue;
|
|
61
67
|
}
|
|
62
68
|
type RunnerName = "node-worker" | "node-process" | "bun-process" | "deno-process" | "self" | "miniflare" | "vercel" | "netlify";
|
|
@@ -96,6 +102,7 @@ export declare class EnvServer extends RunnerManager {
|
|
|
96
102
|
private _reloadTimeout;
|
|
97
103
|
private _reloadListeners;
|
|
98
104
|
private _startPromise;
|
|
105
|
+
private _virtual;
|
|
99
106
|
runner: Awaited<ReturnType<typeof loadRunner>> | null;
|
|
100
107
|
/** Register a listener called when the runner is reloaded due to a file change. */
|
|
101
108
|
onReload(listener: () => void): void;
|
|
@@ -109,6 +116,12 @@ export declare class EnvServer extends RunnerManager {
|
|
|
109
116
|
start(): Promise<this>;
|
|
110
117
|
/** Replace the active runner; without an argument, create one from the server options. */
|
|
111
118
|
reload(runner?: EnvRunner): Promise<void>;
|
|
119
|
+
/**
|
|
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.
|
|
123
|
+
*/
|
|
124
|
+
updateVirtualModules(changes: VirtualModuleUpdates, timeout?: number): Promise<void>;
|
|
112
125
|
close(): Promise<void>;
|
|
113
126
|
/** Auto-start on first fetch so an explicit `start()` call is optional. */
|
|
114
127
|
protected _fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
|
|
@@ -149,4 +162,4 @@ export declare const RESERVED_RUNTIME_OPTIONS: {
|
|
|
149
162
|
};
|
|
150
163
|
/** Callers add `fetch` (bound lazily so reloads swap it) and the crossws plugin. */
|
|
151
164
|
export declare function toServerOptions(entry: AppEntry): Omit<ServerOptions, "fetch">;
|
|
152
|
-
export { type AppEntry, type AppEntryIPC, type AppEntryIPCContext, BaseEnvRunner, DenoProcessEnvRunner, type EnvRunnerData as DenoProcessEnvRunnerData, type EnvRunner, type EnvRunnerData, type EnvServerOptions, type FetchHandler, type LoadRunnerOptions, MiniflareEnvRunner, type MiniflareEnvRunnerOptions, type MiniflareExportInfo, NetlifyEnvRunner, type NodeUpgradeContext, type RPCOptions, type RunnerMessageListener, type RunnerName, type RunnerRPCHooks, type RuntimeDep, type TransformResult, type UpgradeContext, type UpgradeHandler, VercelEnvRunner, type VirtualModuleSource, type VirtualModules, type WorkerAddress, type WorkerHooks, resolveRuntimeDep };
|
|
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") {
|