env-runner 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,27 +7,9 @@ export interface NetlifyEnvRunnerOptions {
7
7
  hooks?: WorkerHooks;
8
8
  data?: EnvRunnerData;
9
9
  /**
10
- * Module specifier for the `@netlify/runtime` package, used inside the
11
- * worker to call `startRuntime()` (full `globalThis.Netlify` + `caches`
12
- * setup). `env-runner` does not depend on `@netlify/runtime`.
13
- *
14
- * Unlike the other runtime-dependency options, this one takes a **specifier
15
- * only**: the runtime has to be instantiated inside the worker thread, and a
16
- * live module instance cannot cross that boundary.
17
- *
18
- * ```ts
19
- * new NetlifyEnvRunner({
20
- * name: "app",
21
- * netlifyRuntime: import.meta.resolve("@netlify/runtime"),
22
- * data: { entry },
23
- * });
24
- * ```
25
- *
26
- * A bare specifier (e.g. `"@netlify/runtime"`) is resolved from the current
27
- * working directory. When omitted, the worker tries
28
- * `import("@netlify/runtime")` and falls back to a lightweight
29
- * `globalThis.Netlify` shim (env access only) if it isn't installed. Pass
30
- * `false` to always use the shim.
10
+ * `@netlify/runtime` specifier (resolved from cwd), imported inside the worker
11
+ * since a module instance can't cross into it. Omitted: imported optionally,
12
+ * else an env-only `globalThis.Netlify` shim. `false` forces the shim.
31
13
  */
32
14
  netlifyRuntime?: string | URL | false;
33
15
  }
@@ -1,5 +1,4 @@
1
- import { BaseEnvRunner } from "./common-base-runner.mjs";
2
- import { hostEnv } from "./common-host-env.mjs";
1
+ import { BaseEnvRunner, hostEnv } from "./common-base-runner.mjs";
3
2
  import { fileURLToPath } from "node:url";
4
3
  import { existsSync } from "node:fs";
5
4
  import { Worker } from "node:worker_threads";
@@ -168,9 +168,16 @@ var RunnerManager = class {
168
168
  await this._runner.reloadModule(timeout);
169
169
  this._moduleInvalidated = false;
170
170
  }
171
+ async updateVirtualModules(changes, timeout) {
172
+ const runner = await this._waitForRunner();
173
+ if (!runner?.updateVirtualModules) throw new Error("Active runner does not support updateVirtualModules()");
174
+ await runner.updateVirtualModules(changes, timeout);
175
+ this._moduleInvalidated = true;
176
+ }
171
177
  async invalidateModule(specifier, timeout) {
172
- if (!this._runner?.invalidateModule) throw new Error("Active runner does not support invalidateModule()");
173
- await this._runner.invalidateModule(specifier, timeout);
178
+ const runner = await this._waitForRunner();
179
+ if (!runner?.invalidateModule) throw new Error("Active runner does not support invalidateModule()");
180
+ await runner.invalidateModule(specifier, timeout);
174
181
  this._moduleInvalidated = true;
175
182
  }
176
183
  async close() {
@@ -208,11 +215,11 @@ var RunnerManager = class {
208
215
  runner.onMessage(this._internalListener);
209
216
  for (const listener of this._messageListeners) runner.onMessage(listener);
210
217
  const originalClose = runner.close.bind(runner);
211
- runner.close = async () => {
212
- await originalClose();
218
+ runner.close = async (cause) => {
219
+ await originalClose(cause);
213
220
  if (this._runner === runner) {
214
221
  this._runner = void 0;
215
- for (const fn of this._closeListeners) fn(this);
222
+ for (const fn of this._closeListeners) fn(this, cause);
216
223
  }
217
224
  };
218
225
  if (runner.ready) this._flushQueue();
@@ -263,6 +270,7 @@ var EnvServer = class extends RunnerManager {
263
270
  _reloadTimeout;
264
271
  _reloadListeners = /* @__PURE__ */ new Set();
265
272
  _startPromise;
273
+ _virtual;
266
274
  runner = null;
267
275
  onReload(listener) {
268
276
  this._reloadListeners.add(listener);
@@ -285,6 +293,12 @@ var EnvServer = class extends RunnerManager {
285
293
  this.runner = runner ?? await this._createRunner();
286
294
  await super.reload(this.runner);
287
295
  }
296
+ async updateVirtualModules(changes, timeout) {
297
+ const virtual = this._virtual ??= { ...this._opts.data?.virtual };
298
+ for (const [key, source] of Object.entries(changes)) if (source === null) delete virtual[key];
299
+ else if (source !== void 0) virtual[key] = source;
300
+ if (await this._waitForRunner()) await super.updateVirtualModules(changes, timeout);
301
+ }
288
302
  async close() {
289
303
  this._stopWatching();
290
304
  await super.close();
@@ -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
@@ -1,11 +1,36 @@
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.
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
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>;
33
+ /** Proxy a request to the worker; relative inputs resolve against `http://localhost`. */
9
34
  export type FetchHandler = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
10
35
  /** Callback for receiving messages from the worker. */
11
36
  export type RunnerMessageListener = (data: unknown) => void;
@@ -30,11 +55,7 @@ export interface RunnerRPCHooks {
30
55
  /** Remove a previously registered message listener. */
31
56
  offMessage: (listener: RunnerMessageListener) => void;
32
57
  }
33
- /**
34
- * Address reported by the worker once it is ready.
35
- *
36
- * Either a TCP `host`/`port` pair or a Unix `socketPath`.
37
- */
58
+ /** Address reported by the worker once it is ready. */
38
59
  export type WorkerAddress = {
39
60
  host?: string;
40
61
  port: number;
@@ -74,6 +95,12 @@ export interface EnvRunner extends RunnerRPCHooks, AsyncDisposable {
74
95
  rpc<T = unknown>(name: string, data?: unknown, opts?: RPCOptions): Promise<T>;
75
96
  /** Re-import the entry module without restarting the worker/process. */
76
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>;
77
104
  /**
78
105
  * Invalidate a virtual module so the next `reloadModule()` re-evaluates it.
79
106
  * A factory-valued source is re-run for fresh contents.
@@ -61,6 +61,7 @@ var VercelEnvRunner = class extends NodeWorkerEnvRunner {
61
61
  input = this._resolveFetchInput(input);
62
62
  const headers = new Headers(input instanceof Request ? input.headers : init?.headers);
63
63
  const requestId = generateVercelId();
64
+ await this._waitForAddress();
64
65
  if (this._address && this._address.port != null && !headers.has("x-vercel-deployment-url")) {
65
66
  const host = this._address.host || "127.0.0.1";
66
67
  headers.set("x-vercel-deployment-url", `http://${host}:${this._address.port}`);
@@ -1,71 +1,345 @@
1
- import { pathToFileURL } from "node:url";
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]) => [key, typeof value === "function" ? await value() : 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, versions, parentURL = _defaultParentURL(), forcePlainModule = false) {
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 version = versions?.get(key);
12
- return {
13
- url: VIRTUAL_SCHEME + encodeURIComponent(specifier) + (version ? `?v=${version}` : ""),
14
- shortCircuit: true
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 (context.parentURL?.startsWith(VIRTUAL_SCHEME)) return nextResolve(specifier, {
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
- return nextResolve(specifier, context);
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
- if (url.startsWith(VIRTUAL_SCHEME)) {
25
- const key = _stripQuery(decodeURIComponent(url.slice(8)));
26
- if (Object.hasOwn(virtual, key)) return {
27
- format: forcePlainModule ? "module" : virtualModuleFormat(key),
28
- source: virtual[key],
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
- return nextLoad(url, context);
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 virtualModuleFormat(specifier) {
40
- if (specifier.endsWith(".json")) return "json";
41
- if (specifier.endsWith(".ts") || specifier.endsWith(".mts")) return "module-typescript";
42
- return "module";
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
- return stripTypeScriptTypes(source);
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, source] of Object.entries(virtual)) if (!seen.has(key) && refs.some((ref) => source.includes(ref))) {
58
- seen.add(key);
59
- invalidated.push(key);
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 _defaultParentURL() {
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/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { EnvServer } from "./_chunks/server.mjs";
2
2
  import { resolve } from "node:path";
3
- import { serve } from "srvx";
4
3
  import { parseArgs } from "node:util";
4
+ import { serve } from "srvx";
5
5
  const { values, positionals } = parseArgs({
6
6
  allowPositionals: true,
7
7
  options: {