env-runner 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +203 -26
- package/dist/_chunks/common-base-runner.d.mts +58 -51
- package/dist/_chunks/common-base-runner.mjs +127 -33
- package/dist/_chunks/common-process-data.mjs +13 -0
- package/dist/_chunks/common-runtime-deps.d.mts +7 -30
- package/dist/_chunks/common-worker-utils.mjs +464 -73
- package/dist/_chunks/deno-process-runner.mjs +11 -37
- package/dist/_chunks/lexer.mjs +2 -0
- package/dist/_chunks/libs/cjs-module-lexer.mjs +6 -1
- package/dist/_chunks/miniflare-runner.d.mts +56 -107
- package/dist/_chunks/miniflare-runner.mjs +834 -154
- package/dist/_chunks/netlify-runner.d.mts +3 -21
- package/dist/_chunks/node-worker-runner.mjs +1 -2
- package/dist/_chunks/server.mjs +20 -5
- package/dist/_chunks/types.d.mts +36 -9
- package/dist/_chunks/vercel-runner.mjs +1 -0
- package/dist/_chunks/virtual-loader.mjs +303 -29
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +31 -69
- package/dist/runners/bun-process/runner.mjs +4 -8
- package/dist/runners/bun-process/worker.mjs +8 -8
- package/dist/runners/deno-process/worker.mjs +17 -39
- package/dist/runners/node-process/runner.mjs +2 -6
- package/dist/runners/node-process/worker.mjs +8 -8
- package/dist/runners/node-worker/worker.mjs +6 -7
- package/dist/runners/self/runner.d.mts +2 -1
- package/dist/runners/self/runner.mjs +8 -0
- package/dist/runners/vercel/queue-dev.d.mts +9 -45
- package/dist/vite.d.mts +2 -12
- package/package.json +13 -13
- package/dist/_chunks/common-host-env.mjs +0 -11
|
@@ -1,22 +1,24 @@
|
|
|
1
|
-
import { expandVirtualInvalidation, stripVirtualTypeScript, virtualModuleFormat } from "./virtual-loader.mjs";
|
|
1
|
+
import { encodeVirtualModules, expandVirtualInvalidation, stripVirtualTypeScript, unsupportedVirtualJSXError, virtualModuleCode, virtualModuleCodeSource, virtualModuleFormat } from "./virtual-loader.mjs";
|
|
2
2
|
import { BaseEnvRunner } from "./common-base-runner.mjs";
|
|
3
|
-
import { isVirtualSpecifier } from "./common-worker-utils.mjs";
|
|
4
3
|
import { resolveRuntimeDep } from "./common-runtime-deps.mjs";
|
|
5
4
|
import { init, parse } from "./libs/cjs-module-lexer.mjs";
|
|
6
5
|
import { init as init$1, parse as parse$1 } from "./libs/es-module-lexer.mjs";
|
|
7
6
|
import { createRequire } from "node:module";
|
|
8
7
|
import { proxyUpgrade } from "httpxy";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
8
|
+
import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
9
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
10
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
12
11
|
import { resolveModulePath } from "exsolve";
|
|
13
12
|
import { tmpdir } from "node:os";
|
|
14
|
-
|
|
13
|
+
import { parseEnv } from "node:util";
|
|
14
|
+
const IPC_PATH$1 = "/__env_runner_ipc";
|
|
15
15
|
const IPC_BINDING = "__ENV_RUNNER_IPC";
|
|
16
16
|
const UNSAFE_EVAL_BINDING = "__ENV_RUNNER_UNSAFE_EVAL__";
|
|
17
17
|
function generateWrapper(entryPath, opts) {
|
|
18
|
+
const processShim = opts?.nodeCompat === false ? "" : `import __process from "node:process";
|
|
19
|
+
if (!globalThis.process) { globalThis.process = __process; }`;
|
|
18
20
|
const staticReExport = opts?.dynamicOnly ? "" : `export * from ${JSON.stringify(entryPath)};`;
|
|
19
|
-
const explicitExports = opts?.dynamicOnly && opts.exports?.length ? opts.exports.map((name) => `export { ${name} } from ${JSON.stringify(entryPath)};`).join("\n") : "";
|
|
21
|
+
const explicitExports = typeof opts?.exports === "string" ? `export * from ${JSON.stringify(opts.exports)};` : opts?.dynamicOnly && opts.exports?.length ? opts.exports.map((name) => `export { ${name} } from ${JSON.stringify(entryPath)};`).join("\n") : "";
|
|
20
22
|
const fetchBody = opts?.captureErrors ?? true ? `try {
|
|
21
23
|
return await __server.fetch(request, env, ctx);
|
|
22
24
|
} catch (e) {
|
|
@@ -31,12 +33,11 @@ function generateWrapper(entryPath, opts) {
|
|
|
31
33
|
headers: { "Content-Type": "application/json", "X-Env-Runner-Error": "1" },
|
|
32
34
|
});
|
|
33
35
|
}` : `return __server.fetch(request, env, ctx);`;
|
|
34
|
-
return
|
|
35
|
-
if (!globalThis.process) { globalThis.process = __process; }
|
|
36
|
+
return `${processShim}
|
|
36
37
|
${staticReExport}
|
|
37
38
|
${explicitExports}
|
|
38
39
|
|
|
39
|
-
const __IPC_PATH = "${IPC_PATH}";
|
|
40
|
+
const __IPC_PATH = "${IPC_PATH$1}";
|
|
40
41
|
const __IPC_BINDING = "${IPC_BINDING}";
|
|
41
42
|
const __UNSAFE_EVAL_BINDING = "${UNSAFE_EVAL_BINDING}";
|
|
42
43
|
const __entryPath = ${JSON.stringify(entryPath)};
|
|
@@ -44,7 +45,10 @@ let __userEntry;
|
|
|
44
45
|
let __server;
|
|
45
46
|
let __ipcInitialized = false;
|
|
46
47
|
let __serverWs;
|
|
47
|
-
|
|
48
|
+
// Raw env of the latest user request. Kept after the request ends: requests
|
|
49
|
+
// overlap and streamed bodies outlive fetch(), and unlike \`__serverWs\` the
|
|
50
|
+
// IPC binding works from any request context.
|
|
51
|
+
let __ipcEnv;
|
|
48
52
|
|
|
49
53
|
const __userEnvs = new WeakMap();
|
|
50
54
|
|
|
@@ -130,9 +134,16 @@ async function __loadEntry(env, path) {
|
|
|
130
134
|
return mod.default || mod;
|
|
131
135
|
}
|
|
132
136
|
|
|
137
|
+
// Where an entry load error was thrown (first stack frame), so one thrown by a
|
|
138
|
+
// virtual module names its key (\`#config:2:7\`). workerd's messages don't.
|
|
139
|
+
function __errorLocation(e) {
|
|
140
|
+
const location = /\\n\\s+at (?:async )?(?:.* \\()?([^\\n()]+:\\d+:\\d+)\\)?/.exec(String(e?.stack))?.[1];
|
|
141
|
+
return location ? " (at " + location + ")" : "";
|
|
142
|
+
}
|
|
143
|
+
|
|
133
144
|
function __sendMessage(message) {
|
|
134
145
|
const payload = JSON.stringify(message);
|
|
135
|
-
const env =
|
|
146
|
+
const env = __ipcEnv;
|
|
136
147
|
if (env && env[__IPC_BINDING]) {
|
|
137
148
|
env[__IPC_BINDING].fetch("http://localhost/__ipc", {
|
|
138
149
|
method: "POST",
|
|
@@ -204,8 +215,9 @@ export default {
|
|
|
204
215
|
async fetch(request, env, ctx) {
|
|
205
216
|
const url = new URL(request.url);
|
|
206
217
|
|
|
207
|
-
//
|
|
208
|
-
|
|
218
|
+
// IPC: a plain request loads the entry (204, or a 500 with the error), then
|
|
219
|
+
// the WebSocket upgrade opens the channel.
|
|
220
|
+
if (url.pathname === __IPC_PATH) {
|
|
209
221
|
try {
|
|
210
222
|
if (!__userEntry) {
|
|
211
223
|
const entry = await __loadEntry(env, __entryPath);
|
|
@@ -213,7 +225,11 @@ export default {
|
|
|
213
225
|
__userEntry = entry;
|
|
214
226
|
}
|
|
215
227
|
} catch (e) {
|
|
216
|
-
|
|
228
|
+
const message = "Failed to load entry: " + String(e) + __errorLocation(e);
|
|
229
|
+
return new Response(message, { status: 500 });
|
|
230
|
+
}
|
|
231
|
+
if (request.headers.get("upgrade") !== "websocket") {
|
|
232
|
+
return new Response(null, { status: 204 });
|
|
217
233
|
}
|
|
218
234
|
|
|
219
235
|
const pair = new WebSocketPair();
|
|
@@ -241,22 +257,80 @@ export default {
|
|
|
241
257
|
return new Response("Worker not initialized", { status: 503 });
|
|
242
258
|
}
|
|
243
259
|
|
|
260
|
+
__ipcEnv = env;
|
|
261
|
+
|
|
244
262
|
// Handle WebSocket upgrade via crossws cloudflare adapter
|
|
245
263
|
if (__userEntry.websocket && request.headers.get("upgrade") === "websocket") {
|
|
246
264
|
const adapter = await __initCrossws(env, __userEntry.websocket);
|
|
247
265
|
return adapter.handleUpgrade(request, __userEnv(env), ctx);
|
|
248
266
|
}
|
|
249
267
|
|
|
250
|
-
|
|
251
|
-
try {
|
|
252
|
-
${fetchBody}
|
|
253
|
-
} finally {
|
|
254
|
-
__currentEnv = undefined;
|
|
255
|
-
}
|
|
268
|
+
${fetchBody}
|
|
256
269
|
}
|
|
257
270
|
};
|
|
258
271
|
`;
|
|
259
272
|
}
|
|
273
|
+
function expandDotenv(parsed, processEnv) {
|
|
274
|
+
const running = {};
|
|
275
|
+
for (const [key, raw] of Object.entries(parsed)) running[key] = processEnv[key] = (processEnv[key] && processEnv[key] !== raw ? processEnv[key] : expandDotenvValue(raw, processEnv, running)).replace(/\\\$/g, "$");
|
|
276
|
+
return processEnv;
|
|
277
|
+
}
|
|
278
|
+
function expandDotenvValue(value, processEnv, running) {
|
|
279
|
+
const env = {
|
|
280
|
+
...running,
|
|
281
|
+
...processEnv
|
|
282
|
+
};
|
|
283
|
+
const regex = /(?<!\\)\$\{([^{}]+)\}|(?<!\\)\$([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
284
|
+
const seen = /* @__PURE__ */ new Set();
|
|
285
|
+
let result = value;
|
|
286
|
+
let match;
|
|
287
|
+
while ((match = regex.exec(result)) !== null) {
|
|
288
|
+
seen.add(result);
|
|
289
|
+
const [template, braced, unbraced] = match;
|
|
290
|
+
const expression = braced || unbraced;
|
|
291
|
+
const splitter = expression.match(/(:\+|\+|:-|-)/)?.[0];
|
|
292
|
+
const parts = splitter ? expression.split(splitter) : [expression];
|
|
293
|
+
const key = parts.shift();
|
|
294
|
+
let replacement;
|
|
295
|
+
if (splitter === ":+" || splitter === "+") replacement = env[key] ? parts.join(splitter) : "";
|
|
296
|
+
else {
|
|
297
|
+
const found = env[key];
|
|
298
|
+
replacement = found && !seen.has(found) ? found : parts.join(splitter ?? "");
|
|
299
|
+
}
|
|
300
|
+
result = result.replace(template, replacement);
|
|
301
|
+
if (result === running[key]) break;
|
|
302
|
+
regex.lastIndex = 0;
|
|
303
|
+
}
|
|
304
|
+
return result;
|
|
305
|
+
}
|
|
306
|
+
function readDotenvFile(path) {
|
|
307
|
+
return existsSync(path) ? parseEnv(readFileSync(path, "utf8")) : void 0;
|
|
308
|
+
}
|
|
309
|
+
function booleanEnv(name, defaultValue) {
|
|
310
|
+
const value = process.env[name]?.toLowerCase();
|
|
311
|
+
return value === "true" ? true : value === "false" ? false : defaultValue;
|
|
312
|
+
}
|
|
313
|
+
function loadDevVars(opts) {
|
|
314
|
+
const { configDir, env, envFiles } = opts;
|
|
315
|
+
if (!envFiles?.length) {
|
|
316
|
+
const devVars = resolve(configDir, ".dev.vars");
|
|
317
|
+
const loaded = env !== void 0 && readDotenvFile(`${devVars}.${env}`) || readDotenvFile(devVars);
|
|
318
|
+
if (loaded) return loaded;
|
|
319
|
+
}
|
|
320
|
+
if (!booleanEnv("CLOUDFLARE_LOAD_DEV_VARS_FROM_DOT_ENV", true)) return;
|
|
321
|
+
const files = envFiles ?? [
|
|
322
|
+
".env",
|
|
323
|
+
".env.local",
|
|
324
|
+
...env ? [`.env.${env}`, `.env.${env}.local`] : []
|
|
325
|
+
];
|
|
326
|
+
const parsed = {};
|
|
327
|
+
for (const file of files) Object.assign(parsed, readDotenvFile(resolve(configDir, file)));
|
|
328
|
+
const processEnv = {};
|
|
329
|
+
if (opts.hasSecrets || booleanEnv("CLOUDFLARE_INCLUDE_PROCESS_ENV", false)) {
|
|
330
|
+
for (const [key, value] of Object.entries(process.env)) if (typeof value === "string") processEnv[key] = value;
|
|
331
|
+
}
|
|
332
|
+
return expandDotenv(parsed, processEnv);
|
|
333
|
+
}
|
|
260
334
|
const WRANGLER_CONFIG_FILENAMES = [
|
|
261
335
|
"wrangler.json",
|
|
262
336
|
"wrangler.jsonc",
|
|
@@ -274,45 +348,143 @@ const WRANGLER_OPTION_DENYLIST = /* @__PURE__ */ new Set([
|
|
|
274
348
|
"unsafeModuleFallbackService",
|
|
275
349
|
"unsafeUseModuleFallbackService"
|
|
276
350
|
]);
|
|
351
|
+
const WRANGLER_OPTION_DROPLIST = /* @__PURE__ */ new Set([
|
|
352
|
+
"assets",
|
|
353
|
+
"serviceBindings",
|
|
354
|
+
"workflows",
|
|
355
|
+
"queueConsumers",
|
|
356
|
+
"tails",
|
|
357
|
+
"streamingTails"
|
|
358
|
+
]);
|
|
359
|
+
const WRANGLER_DROPPED_OPTION_NAMES = {
|
|
360
|
+
assets: "assets",
|
|
361
|
+
serviceBindings: "services",
|
|
362
|
+
workflows: "workflows",
|
|
363
|
+
queueConsumers: "queues.consumers",
|
|
364
|
+
tails: "tail_consumers",
|
|
365
|
+
streamingTails: "streaming_tail_consumers",
|
|
366
|
+
durableObjects: "durable_objects"
|
|
367
|
+
};
|
|
368
|
+
function addDropped(dropped, name, details) {
|
|
369
|
+
let set = dropped.get(name);
|
|
370
|
+
if (!set) {
|
|
371
|
+
set = /* @__PURE__ */ new Set();
|
|
372
|
+
dropped.set(name, set);
|
|
373
|
+
}
|
|
374
|
+
for (const detail of details) set.add(detail);
|
|
375
|
+
}
|
|
376
|
+
function warnDroppedWranglerOptions(dropped) {
|
|
377
|
+
if (dropped.size === 0) return;
|
|
378
|
+
const list = [...dropped].map(([name, details]) => details.size > 0 ? `${name} (${[...details].join(", ")})` : name).join(", ");
|
|
379
|
+
console.warn(`[env-runner] wrangler config options not supported by the miniflare dev runner were ignored: ${list}; pass them via miniflareOptions to opt in.`);
|
|
380
|
+
}
|
|
277
381
|
function isPlainObject(value) {
|
|
278
382
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
279
383
|
}
|
|
280
384
|
function isInlineWranglerConfig(opt) {
|
|
281
385
|
return isPlainObject(opt);
|
|
282
386
|
}
|
|
283
|
-
async function loadWranglerConfig(
|
|
284
|
-
|
|
387
|
+
async function loadWranglerConfig(opts) {
|
|
388
|
+
const { wrangler: opt, env, entryPath, wranglerModule, envFiles } = opts;
|
|
389
|
+
if (!opt) return {};
|
|
285
390
|
const inline = isInlineWranglerConfig(opt) ? opt : void 0;
|
|
286
391
|
let configPath;
|
|
287
|
-
|
|
288
|
-
|
|
392
|
+
const explicitPath = typeof opt === "string" ? opt : opts.configPath;
|
|
393
|
+
if (explicitPath) {
|
|
394
|
+
configPath = resolve(explicitPath);
|
|
289
395
|
if (!existsSync(configPath)) {
|
|
290
396
|
console.warn(`[env-runner] wrangler config requested but not found at "${configPath}"`);
|
|
291
|
-
return;
|
|
397
|
+
if (!inline) return {};
|
|
398
|
+
configPath = void 0;
|
|
292
399
|
}
|
|
293
|
-
} else
|
|
400
|
+
} else {
|
|
294
401
|
configPath = findWranglerConfig(entryPath);
|
|
295
|
-
if (!configPath) {
|
|
296
|
-
console.warn("[env-runner] wrangler config requested but none found
|
|
297
|
-
return;
|
|
402
|
+
if (!configPath && !inline) {
|
|
403
|
+
console.warn("[env-runner] wrangler config requested but none found (searched the entry's directory, then from the cwd up to the filesystem root)");
|
|
404
|
+
return {};
|
|
298
405
|
}
|
|
299
|
-
}
|
|
406
|
+
}
|
|
300
407
|
const wrangler = await resolveRuntimeDep({
|
|
301
408
|
name: "wrangler",
|
|
302
409
|
option: "wranglerModule",
|
|
303
410
|
value: wranglerModule
|
|
304
411
|
});
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
412
|
+
const dropped = /* @__PURE__ */ new Map();
|
|
413
|
+
if (!wrangler?.unstable_readConfig || !wrangler.unstable_getMiniflareWorkerOptions) {
|
|
414
|
+
const unsupported = /* @__PURE__ */ new Map();
|
|
415
|
+
const file = configPath ? readWranglerConfigMinimal(configPath, env) : void 0;
|
|
416
|
+
const inlineConfig = inline ? applyWranglerEnv(inline, env) : void 0;
|
|
417
|
+
let options = mergeWranglerMiniflareOptions(file && (mapWranglerConfigToMiniflare(file.config, dropped, unsupported) ?? {}), inlineConfig && mapWranglerConfigToMiniflare(inlineConfig, dropped, unsupported));
|
|
418
|
+
if (file || inlineConfig) options = applyMinimalDevVars(options, [file?.config, inlineConfig], {
|
|
419
|
+
configDir: configPath ? dirname(configPath) : process.cwd(),
|
|
420
|
+
env,
|
|
421
|
+
envFiles
|
|
422
|
+
});
|
|
423
|
+
filterLocalDurableObjects(options, dropped, (inline && wranglerWorkerName(inline, env)) ?? file?.workerName);
|
|
424
|
+
warnDroppedWranglerOptions(dropped);
|
|
425
|
+
warnUnsupportedMinimalBindings(unsupported);
|
|
426
|
+
return {
|
|
427
|
+
options,
|
|
428
|
+
configFile: file ? configPath : void 0
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
let fileLoaded = false;
|
|
432
|
+
let fileOptions;
|
|
433
|
+
let fileSecrets;
|
|
434
|
+
let fileWorkerName;
|
|
435
|
+
if (configPath) try {
|
|
436
|
+
const config = wrangler.unstable_readConfig({
|
|
308
437
|
config: configPath,
|
|
309
438
|
env
|
|
310
|
-
}, { hideWarnings:
|
|
439
|
+
}, { hideWarnings: !claimWranglerWarnings(configPath, env) });
|
|
440
|
+
fileOptions = pickWranglerMiniflareOptions(wrangler.unstable_getMiniflareWorkerOptions(config, env, { envFiles }).workerOptions, dropped);
|
|
441
|
+
fileWorkerName = config?.name;
|
|
442
|
+
fileSecrets = config?.secrets;
|
|
443
|
+
fileLoaded = true;
|
|
311
444
|
} catch (error) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
445
|
+
warnWranglerLoadError(`"${configPath}"`, error);
|
|
446
|
+
}
|
|
447
|
+
let inlineOptions;
|
|
448
|
+
if (inline) try {
|
|
449
|
+
const inlineEnv = env && isPlainObject(inline.env) && inline.env[env] ? env : void 0;
|
|
450
|
+
const { env: _env, ...inlineTopLevel } = inline;
|
|
451
|
+
const inlineConfig = readInlineWranglerConfig(wrangler, inlineEnv ? inline : inlineTopLevel, inlineEnv);
|
|
452
|
+
inlineConfig.userConfigPath = configPath;
|
|
453
|
+
if (fileLoaded && inlineConfig.secrets === void 0) inlineConfig.secrets = fileSecrets;
|
|
454
|
+
inlineOptions = pickWranglerMiniflareOptions(wrangler.unstable_getMiniflareWorkerOptions(inlineConfig, env, { envFiles }).workerOptions, dropped);
|
|
455
|
+
if (inlineOptions) {
|
|
456
|
+
delete inlineOptions.rootPath;
|
|
457
|
+
if (fileLoaded) filterInlineDevVarBindings(inlineOptions, fileOptions, inlineConfig);
|
|
458
|
+
}
|
|
459
|
+
} catch (error) {
|
|
460
|
+
warnWranglerLoadError("(inline)", error);
|
|
315
461
|
}
|
|
462
|
+
const options = mergeWranglerMiniflareOptions(fileOptions, inlineOptions);
|
|
463
|
+
filterLocalDurableObjects(options, dropped, (inline && wranglerWorkerName(inline, env)) ?? fileWorkerName);
|
|
464
|
+
warnDroppedWranglerOptions(dropped);
|
|
465
|
+
return {
|
|
466
|
+
options,
|
|
467
|
+
configFile: fileLoaded ? configPath : void 0
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
const _shownWranglerWarnings = /* @__PURE__ */ new Set();
|
|
471
|
+
function claimWranglerWarnings(configPath, env) {
|
|
472
|
+
let version = "";
|
|
473
|
+
try {
|
|
474
|
+
const stat = statSync(configPath);
|
|
475
|
+
version = `${stat.mtimeMs}:${stat.size}`;
|
|
476
|
+
} catch {}
|
|
477
|
+
const key = JSON.stringify([
|
|
478
|
+
resolve(configPath),
|
|
479
|
+
env ?? null,
|
|
480
|
+
version
|
|
481
|
+
]);
|
|
482
|
+
if (_shownWranglerWarnings.has(key)) return false;
|
|
483
|
+
_shownWranglerWarnings.add(key);
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
function warnWranglerLoadError(desc, error) {
|
|
487
|
+
console.warn(`[env-runner] failed to load wrangler config ${desc}: ${error.message}`);
|
|
316
488
|
}
|
|
317
489
|
function mergeWranglerMiniflareOptions(base, override) {
|
|
318
490
|
if (!base) return override;
|
|
@@ -329,6 +501,12 @@ function mergeWranglerMiniflareOptions(base, override) {
|
|
|
329
501
|
}
|
|
330
502
|
return out;
|
|
331
503
|
}
|
|
504
|
+
function filterInlineDevVarBindings(inlineOptions, fileOptions, inlineConfig) {
|
|
505
|
+
if (!isPlainObject(inlineOptions.bindings)) return;
|
|
506
|
+
const fileBindings = isPlainObject(fileOptions?.bindings) ? fileOptions.bindings : {};
|
|
507
|
+
const declared = /* @__PURE__ */ new Set([...Object.keys(inlineConfig.vars ?? {}), ...inlineConfig.secrets?.required ?? []]);
|
|
508
|
+
inlineOptions.bindings = Object.fromEntries(Object.entries(inlineOptions.bindings).filter(([key]) => declared.has(key) || Object.hasOwn(fileBindings, key)));
|
|
509
|
+
}
|
|
332
510
|
function readInlineWranglerConfig(wrangler, inline, env) {
|
|
333
511
|
const dir = mkdtempSync(join(tmpdir(), "env-runner-wrangler-"));
|
|
334
512
|
const file = join(dir, "wrangler.json");
|
|
@@ -345,29 +523,173 @@ function readInlineWranglerConfig(wrangler, inline, env) {
|
|
|
345
523
|
});
|
|
346
524
|
}
|
|
347
525
|
}
|
|
526
|
+
const _announcedWranglerConfigs = /* @__PURE__ */ new Set();
|
|
348
527
|
function findWranglerConfig(entryPath) {
|
|
349
|
-
const
|
|
528
|
+
const cwd = process.cwd();
|
|
529
|
+
let start = cwd;
|
|
350
530
|
if (entryPath) {
|
|
351
|
-
const
|
|
352
|
-
|
|
531
|
+
const entryDir = dirname(resolve(entryPath));
|
|
532
|
+
const rel = relative(cwd, entryDir);
|
|
533
|
+
if (rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel)) start = entryDir;
|
|
534
|
+
else {
|
|
535
|
+
const local = findWranglerConfigInDir(entryDir);
|
|
536
|
+
if (local) return local;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
for (let dir = start;; dir = dirname(dir)) {
|
|
540
|
+
const found = findWranglerConfigInDir(dir);
|
|
541
|
+
if (found) {
|
|
542
|
+
if (dir !== cwd && dir !== start && !_announcedWranglerConfigs.has(found)) {
|
|
543
|
+
_announcedWranglerConfigs.add(found);
|
|
544
|
+
console.info(`[env-runner] using wrangler config from a parent directory: ${found}`);
|
|
545
|
+
}
|
|
546
|
+
return found;
|
|
547
|
+
}
|
|
548
|
+
if (dirname(dir) === dir) return;
|
|
353
549
|
}
|
|
354
|
-
|
|
355
|
-
|
|
550
|
+
}
|
|
551
|
+
function findWranglerConfigInDir(dir) {
|
|
552
|
+
for (const name of WRANGLER_CONFIG_FILENAMES) {
|
|
356
553
|
const candidate = join(dir, name);
|
|
357
554
|
if (existsSync(candidate)) return candidate;
|
|
358
555
|
}
|
|
359
556
|
}
|
|
360
|
-
function pickWranglerMiniflareOptions(workerOptions) {
|
|
557
|
+
function pickWranglerMiniflareOptions(workerOptions, dropped) {
|
|
361
558
|
const out = {};
|
|
362
559
|
for (const [key, value] of Object.entries(workerOptions)) {
|
|
363
560
|
if (value === void 0 || WRANGLER_OPTION_DENYLIST.has(key)) continue;
|
|
561
|
+
if (WRANGLER_OPTION_DROPLIST.has(key)) {
|
|
562
|
+
if (!isEmptyOption(value)) addDropped(dropped, WRANGLER_DROPPED_OPTION_NAMES[key], describeDroppedOption(value));
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
if (isEmptyPickedOption(key, value)) continue;
|
|
364
566
|
out[key] = value;
|
|
365
567
|
}
|
|
366
568
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
367
569
|
}
|
|
570
|
+
function isEmptyPickedOption(key, value) {
|
|
571
|
+
const isEmptyArray = (v) => Array.isArray(v) && v.length === 0;
|
|
572
|
+
if (isEmptyArray(value) || isPlainObject(value) && Object.keys(value).length === 0) return true;
|
|
573
|
+
return key !== "bindings" && isPlainObject(value) && Object.values(value).every(isEmptyArray);
|
|
574
|
+
}
|
|
575
|
+
function isEmptyOption(value) {
|
|
576
|
+
return !value || Array.isArray(value) && value.length === 0 || isPlainObject(value) && Object.keys(value).length === 0;
|
|
577
|
+
}
|
|
578
|
+
function describeDroppedOption(value) {
|
|
579
|
+
if (Array.isArray(value)) return value.map((entry) => entry?.name).filter((name) => typeof name === "string");
|
|
580
|
+
if (isPlainObject(value)) {
|
|
581
|
+
if ("directory" in value) return typeof value.binding === "string" ? [value.binding] : [];
|
|
582
|
+
return Object.keys(value);
|
|
583
|
+
}
|
|
584
|
+
return [];
|
|
585
|
+
}
|
|
586
|
+
function filterLocalDurableObjects(options, dropped, workerName) {
|
|
587
|
+
const value = options?.durableObjects;
|
|
588
|
+
if (!options || !isPlainObject(value)) return;
|
|
589
|
+
const out = {};
|
|
590
|
+
for (const [name, binding] of Object.entries(value)) if (typeof binding === "string" || isPlainObject(binding) && !binding.scriptName) out[name] = binding;
|
|
591
|
+
else if (isPlainObject(binding) && workerName && binding.scriptName === workerName) {
|
|
592
|
+
const { scriptName: _scriptName, ...local } = binding;
|
|
593
|
+
out[name] = local;
|
|
594
|
+
} else {
|
|
595
|
+
const scriptName = isPlainObject(binding) ? binding.scriptName : void 0;
|
|
596
|
+
addDropped(dropped, "durable_objects", [`${name} → script "${String(scriptName)}"`]);
|
|
597
|
+
}
|
|
598
|
+
if (Object.keys(out).length > 0) options.durableObjects = out;
|
|
599
|
+
else delete options.durableObjects;
|
|
600
|
+
}
|
|
601
|
+
const WRANGLER_NON_INHERITABLE_KEYS = /* @__PURE__ */ new Set([
|
|
602
|
+
"vars",
|
|
603
|
+
"secrets",
|
|
604
|
+
"define",
|
|
605
|
+
"durable_objects",
|
|
606
|
+
"workflows",
|
|
607
|
+
"kv_namespaces",
|
|
608
|
+
"cloudchamber",
|
|
609
|
+
"containers",
|
|
610
|
+
"send_email",
|
|
611
|
+
"queues",
|
|
612
|
+
"connect",
|
|
613
|
+
"r2_buckets",
|
|
614
|
+
"d1_databases",
|
|
615
|
+
"vectorize",
|
|
616
|
+
"ai_search_namespaces",
|
|
617
|
+
"ai_search",
|
|
618
|
+
"websearch",
|
|
619
|
+
"agent_memory",
|
|
620
|
+
"hyperdrive",
|
|
621
|
+
"services",
|
|
622
|
+
"analytics_engine_datasets",
|
|
623
|
+
"dispatch_namespaces",
|
|
624
|
+
"mtls_certificates",
|
|
625
|
+
"tail_consumers",
|
|
626
|
+
"streaming_tail_consumers",
|
|
627
|
+
"unsafe",
|
|
628
|
+
"browser",
|
|
629
|
+
"ai",
|
|
630
|
+
"images",
|
|
631
|
+
"stream",
|
|
632
|
+
"media",
|
|
633
|
+
"pipelines",
|
|
634
|
+
"secrets_store_secrets",
|
|
635
|
+
"artifacts",
|
|
636
|
+
"unsafe_hello_world",
|
|
637
|
+
"flagship",
|
|
638
|
+
"worker_loaders",
|
|
639
|
+
"ratelimits",
|
|
640
|
+
"vpc_services",
|
|
641
|
+
"vpc_networks",
|
|
642
|
+
"version_metadata"
|
|
643
|
+
]);
|
|
644
|
+
const MINIMAL_UNSUPPORTED_BINDING_KEYS = [
|
|
645
|
+
"hyperdrive",
|
|
646
|
+
"analytics_engine_datasets",
|
|
647
|
+
"ai",
|
|
648
|
+
"ai_search_namespaces",
|
|
649
|
+
"ai_search",
|
|
650
|
+
"websearch",
|
|
651
|
+
"agent_memory",
|
|
652
|
+
"version_metadata",
|
|
653
|
+
"ratelimits",
|
|
654
|
+
"send_email",
|
|
655
|
+
"secrets_store_secrets",
|
|
656
|
+
"vectorize",
|
|
657
|
+
"browser",
|
|
658
|
+
"images",
|
|
659
|
+
"stream",
|
|
660
|
+
"media",
|
|
661
|
+
"pipelines",
|
|
662
|
+
"dispatch_namespaces",
|
|
663
|
+
"mtls_certificates",
|
|
664
|
+
"worker_loaders",
|
|
665
|
+
"vpc_services",
|
|
666
|
+
"vpc_networks",
|
|
667
|
+
"artifacts",
|
|
668
|
+
"flagship",
|
|
669
|
+
"containers",
|
|
670
|
+
"unsafe",
|
|
671
|
+
"unsafe_hello_world",
|
|
672
|
+
"logfwdr",
|
|
673
|
+
"wasm_modules",
|
|
674
|
+
"text_blobs",
|
|
675
|
+
"data_blobs"
|
|
676
|
+
];
|
|
677
|
+
const MINIMAL_BINDING_OPTIONS = [
|
|
678
|
+
"kvNamespaces",
|
|
679
|
+
"r2Buckets",
|
|
680
|
+
"d1Databases",
|
|
681
|
+
"queueProducers",
|
|
682
|
+
"durableObjects"
|
|
683
|
+
];
|
|
684
|
+
function warnUnsupportedMinimalBindings(unsupported) {
|
|
685
|
+
if (unsupported.size === 0) return;
|
|
686
|
+
const list = [...unsupported].map(([name, details]) => details.size > 0 ? `${name} (${[...details].join(", ")})` : name).join(", ");
|
|
687
|
+
console.warn(`[env-runner] wrangler config bindings not supported by the built-in minimal wrangler config reader were ignored: ${list}; install 'wrangler' (or pass it as \`wranglerModule\`) to use them.`);
|
|
688
|
+
}
|
|
368
689
|
function readWranglerConfigMinimal(configPath, env) {
|
|
369
|
-
|
|
370
|
-
|
|
690
|
+
const ext = extname(configPath).toLowerCase();
|
|
691
|
+
if (ext !== ".json" && ext !== ".jsonc") {
|
|
692
|
+
console.warn(`[env-runner] reading "${basename(configPath)}" requires the 'wrangler' package; the built-in reader supports JSON/JSONC only (install 'wrangler' for TOML).`);
|
|
371
693
|
return;
|
|
372
694
|
}
|
|
373
695
|
let raw;
|
|
@@ -378,45 +700,203 @@ function readWranglerConfigMinimal(configPath, env) {
|
|
|
378
700
|
}
|
|
379
701
|
let config;
|
|
380
702
|
try {
|
|
381
|
-
config =
|
|
703
|
+
config = parseJSONC(raw);
|
|
382
704
|
} catch (error) {
|
|
383
705
|
console.warn(`[env-runner] failed to parse wrangler config "${configPath}": ${error.message}`);
|
|
384
706
|
return;
|
|
385
707
|
}
|
|
386
|
-
|
|
708
|
+
if (!isPlainObject(config)) {
|
|
709
|
+
warnWranglerLoadError(`"${configPath}"`, /* @__PURE__ */ new Error("config must be an object"));
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
let envConfig;
|
|
713
|
+
try {
|
|
714
|
+
envConfig = applyWranglerEnv(config, env, configPath);
|
|
715
|
+
} catch (error) {
|
|
716
|
+
warnWranglerLoadError(`"${configPath}"`, error);
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
return {
|
|
720
|
+
config: envConfig,
|
|
721
|
+
workerName: wranglerWorkerName(config, env)
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
function applyWranglerEnv(config, env, configPath) {
|
|
725
|
+
const { env: envs, ...topLevel } = config;
|
|
726
|
+
if (!env) return topLevel;
|
|
727
|
+
const rawEnv = isPlainObject(envs) ? envs[env] : void 0;
|
|
728
|
+
const warnings = [];
|
|
729
|
+
let out;
|
|
730
|
+
if (isPlainObject(rawEnv)) {
|
|
731
|
+
out = {};
|
|
732
|
+
for (const [key, value] of Object.entries(topLevel)) if (!WRANGLER_NON_INHERITABLE_KEYS.has(key)) out[key] = value;
|
|
733
|
+
else if (rawEnv[key] === void 0) warnings.push(`"${key}" exists at the top level, but is not inherited by "env.${env}"`);
|
|
734
|
+
Object.assign(out, rawEnv);
|
|
735
|
+
} else {
|
|
736
|
+
if (configPath && isPlainObject(envs)) throw new Error(`No environment found in configuration with name "${env}". The available configured environment names are: ${JSON.stringify(Object.keys(envs))}`);
|
|
737
|
+
warnings.push(`No environment found in configuration with name "${env}"`);
|
|
738
|
+
out = topLevel;
|
|
739
|
+
}
|
|
740
|
+
if (configPath && warnings.length > 0 && claimWranglerWarnings(configPath, env)) console.warn(`[env-runner] wrangler config "${configPath}": ${warnings.join("; ")}.`);
|
|
741
|
+
return out;
|
|
387
742
|
}
|
|
388
|
-
function
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
} : config;
|
|
743
|
+
function wranglerWorkerName(config, env) {
|
|
744
|
+
const envName = env ? config.env?.[env]?.name : void 0;
|
|
745
|
+
if (typeof envName === "string") return envName;
|
|
746
|
+
if (typeof config.name !== "string" || !config.name) return void 0;
|
|
747
|
+
return env ? `${config.name}-${env}` : config.name;
|
|
393
748
|
}
|
|
394
|
-
function mapWranglerConfigToMiniflare(config) {
|
|
749
|
+
function mapWranglerConfigToMiniflare(config, dropped, unsupported) {
|
|
395
750
|
const out = {};
|
|
751
|
+
for (const [name, value, field] of [
|
|
752
|
+
[
|
|
753
|
+
"services",
|
|
754
|
+
config.services,
|
|
755
|
+
"binding"
|
|
756
|
+
],
|
|
757
|
+
[
|
|
758
|
+
"assets",
|
|
759
|
+
config.assets,
|
|
760
|
+
"binding"
|
|
761
|
+
],
|
|
762
|
+
[
|
|
763
|
+
"queues.consumers",
|
|
764
|
+
config.queues?.consumers,
|
|
765
|
+
"queue"
|
|
766
|
+
],
|
|
767
|
+
[
|
|
768
|
+
"workflows",
|
|
769
|
+
config.workflows,
|
|
770
|
+
"binding"
|
|
771
|
+
],
|
|
772
|
+
[
|
|
773
|
+
"tail_consumers",
|
|
774
|
+
config.tail_consumers,
|
|
775
|
+
"service"
|
|
776
|
+
],
|
|
777
|
+
[
|
|
778
|
+
"streaming_tail_consumers",
|
|
779
|
+
config.streaming_tail_consumers,
|
|
780
|
+
"service"
|
|
781
|
+
]
|
|
782
|
+
]) if (!isEmptyOption(value)) addDropped(dropped, name, (Array.isArray(value) ? value : [value]).map((e) => e?.[field]).filter((d) => typeof d === "string"));
|
|
783
|
+
for (const key of MINIMAL_UNSUPPORTED_BINDING_KEYS) {
|
|
784
|
+
const value = config[key];
|
|
785
|
+
const bindings = isPlainObject(value) && Array.isArray(value.bindings) ? value.bindings : value;
|
|
786
|
+
if (!isEmptyOption(bindings)) addDropped(unsupported, key, describeBindingNames(bindings));
|
|
787
|
+
}
|
|
396
788
|
if (typeof config.compatibility_date === "string") out.compatibilityDate = config.compatibility_date;
|
|
397
789
|
if (Array.isArray(config.compatibility_flags)) out.compatibilityFlags = config.compatibility_flags;
|
|
398
790
|
if (config.vars && typeof config.vars === "object") out.bindings = { ...config.vars };
|
|
399
|
-
const kv = mapBindingArray(config.kv_namespaces, "binding", (n) => n.id
|
|
791
|
+
const kv = mapBindingArray(config.kv_namespaces, "binding", (n) => firstString(n.preview_id, n.id, n.binding));
|
|
400
792
|
if (kv) out.kvNamespaces = kv;
|
|
401
|
-
const r2 = mapBindingArray(config.r2_buckets, "binding", (n) => n.bucket_name
|
|
793
|
+
const r2 = mapBindingArray(config.r2_buckets, "binding", (n) => firstString(n.preview_bucket_name, n.bucket_name, n.binding));
|
|
402
794
|
if (r2) out.r2Buckets = r2;
|
|
403
|
-
const d1 = mapBindingArray(config.d1_databases, "binding", (n) => n.
|
|
795
|
+
const d1 = mapBindingArray(config.d1_databases, "binding", (n) => firstString(n.preview_database_id, n.database_id, n.binding));
|
|
404
796
|
if (d1) out.d1Databases = d1;
|
|
405
797
|
const queues = mapBindingArray(config.queues?.producers, "binding", (n) => n.queue);
|
|
406
798
|
if (queues) out.queueProducers = queues;
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
799
|
+
const useSQLite = durableObjectClassStorage(config);
|
|
800
|
+
const doBindings = Array.isArray(config.durable_objects?.bindings) ? config.durable_objects.bindings : [];
|
|
801
|
+
const dos = {};
|
|
802
|
+
for (const b of doBindings) {
|
|
803
|
+
if (!b?.name || !b?.class_name) continue;
|
|
804
|
+
dos[b.name] = {
|
|
805
|
+
className: b.class_name,
|
|
806
|
+
...b.script_name ? { scriptName: b.script_name } : {},
|
|
807
|
+
...useSQLite.has(b.class_name) ? { useSQLite: useSQLite.get(b.class_name) } : {}
|
|
808
|
+
};
|
|
417
809
|
}
|
|
810
|
+
if (Object.keys(dos).length > 0) out.durableObjects = dos;
|
|
811
|
+
const unbound = [...useSQLite].filter(([className]) => !doBindings.some((b) => b?.class_name === className)).map(([className, sqlite]) => ({
|
|
812
|
+
className,
|
|
813
|
+
useSQLite: sqlite
|
|
814
|
+
}));
|
|
815
|
+
if (unbound.length > 0) out.additionalUnboundDurableObjects = unbound;
|
|
418
816
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
419
817
|
}
|
|
818
|
+
function durableObjectClassStorage(config) {
|
|
819
|
+
const classes = /* @__PURE__ */ new Map();
|
|
820
|
+
for (const migration of Array.isArray(config.migrations) ? config.migrations : []) {
|
|
821
|
+
for (const name of migration?.deleted_classes ?? []) classes.delete(name);
|
|
822
|
+
for (const { from, to } of migration?.renamed_classes ?? []) {
|
|
823
|
+
const sqlite = classes.get(from);
|
|
824
|
+
if (sqlite !== void 0) {
|
|
825
|
+
classes.delete(from);
|
|
826
|
+
classes.set(to, sqlite);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
for (const name of migration?.new_classes ?? []) classes.set(name, false);
|
|
830
|
+
for (const name of migration?.new_sqlite_classes ?? []) classes.set(name, true);
|
|
831
|
+
}
|
|
832
|
+
if (isPlainObject(config.exports)) for (const [name, entry] of Object.entries(config.exports)) {
|
|
833
|
+
const state = entry?.state;
|
|
834
|
+
if (entry?.type === "durable-object" && (state === void 0 || state === "created" || state === "expecting-transfer")) classes.set(name, entry.storage === "sqlite");
|
|
835
|
+
}
|
|
836
|
+
return classes;
|
|
837
|
+
}
|
|
838
|
+
function describeBindingNames(value) {
|
|
839
|
+
if (isPlainObject(value) && typeof value.binding !== "string") return Object.keys(value);
|
|
840
|
+
return (Array.isArray(value) ? value : [value]).map((e) => e?.binding ?? e?.name ?? e?.class_name).filter((name) => typeof name === "string");
|
|
841
|
+
}
|
|
842
|
+
function applyMinimalDevVars(options, configs, opts) {
|
|
843
|
+
const secrets = configs.map((c) => c?.secrets).filter(isPlainObject);
|
|
844
|
+
const loaded = loadDevVars({
|
|
845
|
+
...opts,
|
|
846
|
+
hasSecrets: secrets.length > 0
|
|
847
|
+
});
|
|
848
|
+
if (!loaded) return options;
|
|
849
|
+
const out = { ...options };
|
|
850
|
+
const bindings = isPlainObject(out.bindings) ? { ...out.bindings } : {};
|
|
851
|
+
const taken = new Set(MINIMAL_BINDING_OPTIONS.flatMap((key) => isPlainObject(out[key]) ? Object.keys(out[key]) : []));
|
|
852
|
+
const required = new Set(secrets.flatMap((s) => Array.isArray(s.required) ? s.required : []));
|
|
853
|
+
let changed = false;
|
|
854
|
+
for (const [key, value] of Object.entries(loaded)) {
|
|
855
|
+
if (taken.has(key) || secrets.length > 0 && !Object.hasOwn(bindings, key) && !required.has(key)) continue;
|
|
856
|
+
bindings[key] = value;
|
|
857
|
+
changed = true;
|
|
858
|
+
}
|
|
859
|
+
const missing = [...required].filter((key) => !Object.hasOwn(loaded, key));
|
|
860
|
+
if (missing.length > 0) console.warn(`[env-runner] missing required wrangler secrets: ${missing.join(", ")}. Add them to .dev.vars, .env, or set as environment variables.`);
|
|
861
|
+
if (!changed) return options;
|
|
862
|
+
out.bindings = bindings;
|
|
863
|
+
return out;
|
|
864
|
+
}
|
|
865
|
+
function firstString(...values) {
|
|
866
|
+
return values.find((value) => typeof value === "string");
|
|
867
|
+
}
|
|
868
|
+
function parseJSONC(text) {
|
|
869
|
+
let out = "";
|
|
870
|
+
let comma = -1;
|
|
871
|
+
const src = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
|
|
872
|
+
for (let i = 0; i < src.length; i++) {
|
|
873
|
+
const ch = src[i];
|
|
874
|
+
if (ch === "/" && (src[i + 1] === "/" || src[i + 1] === "*")) {
|
|
875
|
+
const line = src[i + 1] === "/";
|
|
876
|
+
let end = line ? src.indexOf("\n", i) : src.indexOf("*/", i + 2);
|
|
877
|
+
end = end === -1 ? src.length : line ? end : end + 2;
|
|
878
|
+
out += src.slice(i, end).replace(/[^\n]/g, " ");
|
|
879
|
+
i = end - 1;
|
|
880
|
+
continue;
|
|
881
|
+
}
|
|
882
|
+
if (/\s/.test(ch)) {
|
|
883
|
+
out += ch;
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
if ((ch === "}" || ch === "]") && comma !== -1) out = `${out.slice(0, comma)} ${out.slice(comma + 1)}`;
|
|
887
|
+
comma = -1;
|
|
888
|
+
if (ch === "\"") {
|
|
889
|
+
let end = i + 1;
|
|
890
|
+
while (end < src.length && src[end] !== "\"") end += src[end] === "\\" ? 2 : 1;
|
|
891
|
+
out += src.slice(i, end + 1);
|
|
892
|
+
i = end;
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
895
|
+
if (ch === ",") comma = out.length;
|
|
896
|
+
out += ch;
|
|
897
|
+
}
|
|
898
|
+
return JSON.parse(out);
|
|
899
|
+
}
|
|
420
900
|
function mapBindingArray(arr, keyField, value) {
|
|
421
901
|
if (!Array.isArray(arr)) return;
|
|
422
902
|
const out = {};
|
|
@@ -426,6 +906,7 @@ function mapBindingArray(arr, keyField, value) {
|
|
|
426
906
|
}
|
|
427
907
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
428
908
|
}
|
|
909
|
+
const IPC_PATH = "/__env_runner_ipc";
|
|
429
910
|
const _miniflareCache = /* @__PURE__ */ new Map();
|
|
430
911
|
var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
431
912
|
#miniflare;
|
|
@@ -433,7 +914,6 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
433
914
|
#transformRequest;
|
|
434
915
|
#reloadCounter = 0;
|
|
435
916
|
#virtual;
|
|
436
|
-
#virtualVersions = /* @__PURE__ */ new Map();
|
|
437
917
|
#cacheEntry;
|
|
438
918
|
#ws;
|
|
439
919
|
#persistent;
|
|
@@ -443,6 +923,9 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
443
923
|
#exportConditions;
|
|
444
924
|
#wrangler;
|
|
445
925
|
#wranglerEnv;
|
|
926
|
+
#wranglerConfigPath;
|
|
927
|
+
#wranglerEnvFiles;
|
|
928
|
+
#compatibilityDate;
|
|
446
929
|
#wranglerModule;
|
|
447
930
|
#miniflareModule;
|
|
448
931
|
constructor(opts) {
|
|
@@ -460,6 +943,9 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
460
943
|
this.#wrangler = opts.wrangler ?? false;
|
|
461
944
|
this.#wranglerEnv = opts.wranglerEnv ?? process.env.CLOUDFLARE_ENV;
|
|
462
945
|
this.#wranglerModule = opts.wranglerModule;
|
|
946
|
+
this.#wranglerConfigPath = opts.wranglerConfigPath;
|
|
947
|
+
this.#wranglerEnvFiles = opts.wranglerEnvFiles;
|
|
948
|
+
this.#compatibilityDate = opts.compatibilityDate;
|
|
463
949
|
this._initWithVirtualData(() => this.#init());
|
|
464
950
|
}
|
|
465
951
|
static async disposeAll() {
|
|
@@ -484,8 +970,12 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
484
970
|
for (let i = 0; i < 5 && !this._address && !this.closed; i++) await new Promise((r) => setTimeout(r, 100 * Math.pow(2, i)));
|
|
485
971
|
if (!this.#miniflare || this.closed) return new Response("miniflare env runner is unavailable", { status: 503 });
|
|
486
972
|
const resolved = this._resolveFetchInput(input);
|
|
487
|
-
const request = typeof resolved === "string" || resolved instanceof URL ? new Request(resolved,
|
|
973
|
+
const request = typeof resolved === "string" || resolved instanceof URL ? new Request(resolved, {
|
|
488
974
|
...init,
|
|
975
|
+
redirect: "manual"
|
|
976
|
+
}) : new Request(new Request(resolved.url, resolved), {
|
|
977
|
+
...init,
|
|
978
|
+
redirect: "manual",
|
|
489
979
|
referrer: init?.referrer ?? resolved.referrer,
|
|
490
980
|
referrerPolicy: init?.referrerPolicy ?? resolved.referrerPolicy
|
|
491
981
|
});
|
|
@@ -512,6 +1002,7 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
512
1002
|
}));
|
|
513
1003
|
}
|
|
514
1004
|
async reloadModule(timeout = 5e3) {
|
|
1005
|
+
await this._virtualUpdates;
|
|
515
1006
|
if (!this.#ws) throw new Error("Miniflare env runner should be initialized before reloading.");
|
|
516
1007
|
if (!this._data?.entry) return;
|
|
517
1008
|
this.#reloadCounter++;
|
|
@@ -525,15 +1016,27 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
525
1016
|
send: (message) => this.#ws.send(JSON.stringify(message))
|
|
526
1017
|
});
|
|
527
1018
|
}
|
|
528
|
-
async
|
|
1019
|
+
async _applyVirtualUpdates(changes) {
|
|
529
1020
|
const virtual = this.#virtual;
|
|
530
|
-
if (!virtual
|
|
531
|
-
|
|
532
|
-
|
|
1021
|
+
if (!virtual) throw new Error("Miniflare env runner should be initialized before updating modules.");
|
|
1022
|
+
const prepared = {};
|
|
1023
|
+
for (const [key, module] of Object.entries(changes)) if (module !== null) prepared[key] = await this.#prepareVirtualModule(key, module);
|
|
1024
|
+
await init$1;
|
|
1025
|
+
const merged = {
|
|
1026
|
+
...virtual.sources,
|
|
1027
|
+
...prepared
|
|
1028
|
+
};
|
|
1029
|
+
const importers = virtualImporters(merged, createVirtualKeyResolver(Object.keys(merged)));
|
|
1030
|
+
const invalidated = expandVirtualInvalidation(merged, Object.keys(changes), importers);
|
|
1031
|
+
for (const key of Object.keys(changes)) if (Object.hasOwn(prepared, key)) {
|
|
1032
|
+
virtual.sources[key] = prepared[key];
|
|
1033
|
+
virtual.removed.delete(key);
|
|
1034
|
+
} else if (Object.hasOwn(virtual.sources, key)) {
|
|
1035
|
+
delete virtual.sources[key];
|
|
1036
|
+
virtual.removed.add(key);
|
|
533
1037
|
}
|
|
534
|
-
const
|
|
535
|
-
|
|
536
|
-
for (const key of expandVirtualInvalidation(virtual, specifier)) this.#virtualVersions.set(key, (this.#virtualVersions.get(key) ?? 0) + 1);
|
|
1038
|
+
for (const key of invalidated) virtual.versions.set(key, ++virtual.version);
|
|
1039
|
+
refreshVirtualKeyResolvers(virtual);
|
|
537
1040
|
if (this.#cacheKey && _miniflareCache.get(this.#cacheKey) === this.#cacheEntry) _miniflareCache.delete(this.#cacheKey);
|
|
538
1041
|
}
|
|
539
1042
|
_hasRuntime() {
|
|
@@ -591,38 +1094,60 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
591
1094
|
} catch {}
|
|
592
1095
|
}
|
|
593
1096
|
async #prepareVirtualModules() {
|
|
594
|
-
const virtual = this._data?.virtual;
|
|
595
|
-
if (!virtual || Object.keys(virtual).length === 0) return;
|
|
1097
|
+
const virtual = this._data?.virtual ?? {};
|
|
596
1098
|
const out = {};
|
|
597
|
-
for (const [
|
|
1099
|
+
for (const [key, module] of Object.entries(virtual)) out[key] = await this.#prepareVirtualModule(key, module);
|
|
598
1100
|
return out;
|
|
599
1101
|
}
|
|
600
|
-
async #
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
1102
|
+
async #prepareVirtualModule(key, module) {
|
|
1103
|
+
const format = virtualModuleFormat(key, module);
|
|
1104
|
+
const source = typeof module === "string" ? module : module.source;
|
|
1105
|
+
switch (format) {
|
|
1106
|
+
case "module-typescript":
|
|
1107
|
+
case "commonjs-typescript": return {
|
|
1108
|
+
source: stripVirtualTypeScript(key, source, await _getStripTypeScriptTypes(), {
|
|
1109
|
+
requirement: "on the host (workerd does not parse TypeScript)",
|
|
1110
|
+
remedy: "upgrade Node.js"
|
|
1111
|
+
}),
|
|
1112
|
+
format: format === "module-typescript" ? "module" : "commonjs"
|
|
1113
|
+
};
|
|
1114
|
+
case "jsx":
|
|
1115
|
+
case "tsx": throw unsupportedVirtualJSXError(key, format, "workerd");
|
|
1116
|
+
}
|
|
1117
|
+
return {
|
|
1118
|
+
source,
|
|
1119
|
+
format
|
|
1120
|
+
};
|
|
606
1121
|
}
|
|
607
1122
|
async #initAsync() {
|
|
608
|
-
const
|
|
1123
|
+
const miniflare = await this.#resolveMiniflare();
|
|
1124
|
+
const supportedCompatibilityDate = await resolveSupportedCompatibilityDate(miniflare);
|
|
609
1125
|
const entryPath = this._data?.entry;
|
|
610
|
-
const
|
|
1126
|
+
const initialVirtual = await this.#prepareVirtualModules();
|
|
1127
|
+
const virtual = createMiniflareVirtualModules({ ...initialVirtual });
|
|
611
1128
|
this.#virtual = virtual;
|
|
612
|
-
const wranglerOptions = await loadWranglerConfig(
|
|
1129
|
+
const { options: wranglerOptions, configFile: wranglerConfigFile } = await loadWranglerConfig({
|
|
1130
|
+
wrangler: this.#wrangler,
|
|
1131
|
+
env: this.#wranglerEnv,
|
|
1132
|
+
entryPath,
|
|
1133
|
+
configPath: this.#wranglerConfigPath,
|
|
1134
|
+
wranglerModule: this.#wranglerModule,
|
|
1135
|
+
envFiles: this.#wranglerEnvFiles
|
|
1136
|
+
});
|
|
613
1137
|
const userFlags = this.#miniflareOptions.compatibilityFlags || [];
|
|
614
1138
|
const wranglerFlags = wranglerOptions?.compatibilityFlags || [];
|
|
615
1139
|
const userDirectSockets = this.#miniflareOptions.unsafeDirectSockets || [];
|
|
616
1140
|
const options = {
|
|
617
|
-
compatibilityDate: supportedCompatibilityDate,
|
|
618
1141
|
modules: true,
|
|
1142
|
+
...this.#wrangler && !hasUserPersistOptions(this.#miniflareOptions) ? { defaultPersistRoot: wranglerPersistRoot(wranglerConfigFile, typeof this.#wrangler === "string" ? this.#wrangler : this.#wranglerConfigPath) } : void 0,
|
|
619
1143
|
...wranglerOptions,
|
|
620
1144
|
...this.#miniflareOptions,
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
])
|
|
1145
|
+
compatibilityDate: resolveCompatibilityDate([
|
|
1146
|
+
this.#miniflareOptions.compatibilityDate,
|
|
1147
|
+
this.#compatibilityDate === "latest" ? supportedCompatibilityDate : this.#compatibilityDate,
|
|
1148
|
+
wranglerOptions?.compatibilityDate
|
|
1149
|
+
], supportedCompatibilityDate),
|
|
1150
|
+
compatibilityFlags: resolveCompatibilityFlags(wranglerFlags, userFlags),
|
|
626
1151
|
unsafeDirectSockets: [{
|
|
627
1152
|
host: "127.0.0.1",
|
|
628
1153
|
port: 0
|
|
@@ -635,28 +1160,42 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
635
1160
|
...uValue
|
|
636
1161
|
};
|
|
637
1162
|
}
|
|
1163
|
+
const ipc = { runner: this };
|
|
638
1164
|
if (entryPath && !options.script && !options.scriptPath) {
|
|
639
|
-
const
|
|
640
|
-
const
|
|
1165
|
+
const entryKey = virtual.keyOf(entryPath) ?? virtual.keyOf(resolve(entryPath));
|
|
1166
|
+
const entryIsVirtual = entryKey !== void 0;
|
|
1167
|
+
const resolvedEntry = entryIsVirtual ? virtualKeyPath(entryKey) ?? entryKey : resolve(entryPath);
|
|
641
1168
|
const entryBase = isAbsolute(resolvedEntry) ? resolvedEntry : resolve("__env_runner_virtual_entry__.mjs");
|
|
642
1169
|
const entryDir = dirname(entryBase);
|
|
643
|
-
const entrySource = entryIsVirtual ? virtual[
|
|
644
|
-
const detectedExports = this.#exports === false || this.#exports ===
|
|
645
|
-
if (
|
|
646
|
-
|
|
647
|
-
const
|
|
1170
|
+
const entrySource = entryIsVirtual ? virtualModuleCodeSource(entryKey, virtual.sources[entryKey]) : _tryReadFile(resolvedEntry);
|
|
1171
|
+
const detectedExports = this.#exports === false || typeof this.#exports === "string" ? [] : detectExportedClasses(entrySource, typeof this.#exports === "object" ? this.#exports : {});
|
|
1172
|
+
if (detectedExports.length > 0) {
|
|
1173
|
+
const existingDOs = isPlainObject(options.durableObjects) ? options.durableObjects : {};
|
|
1174
|
+
const boundClasses = new Set(Object.values(existingDOs).map((b) => typeof b === "string" ? b : isPlainObject(b) && !b.scriptName ? b.className : void 0));
|
|
1175
|
+
const autoDOs = { ...existingDOs };
|
|
648
1176
|
for (const name of detectedExports) {
|
|
649
1177
|
const bindingName = toScreamingSnakeCase(name);
|
|
650
|
-
if (!autoDOs[bindingName]) autoDOs[bindingName] = name;
|
|
1178
|
+
if (!autoDOs[bindingName] && !boundClasses.has(name)) autoDOs[bindingName] = name;
|
|
651
1179
|
}
|
|
652
1180
|
options.durableObjects = autoDOs;
|
|
653
1181
|
}
|
|
654
|
-
|
|
1182
|
+
const script = generateWrapper(entryIsVirtual ? toWorkerdPath(resolvedEntry) : resolvedEntry, {
|
|
655
1183
|
dynamicOnly: true,
|
|
656
1184
|
captureErrors: this.#captureErrors,
|
|
657
|
-
exports: detectedExports
|
|
1185
|
+
exports: typeof this.#exports === "string" ? this.#exports : detectedExports,
|
|
1186
|
+
nodeCompat: !options.compatibilityFlags.includes("no_nodejs_compat")
|
|
658
1187
|
});
|
|
659
|
-
|
|
1188
|
+
const scriptPath = entryDir + "/__env_runner_wrapper.mjs";
|
|
1189
|
+
const skipLocator = typeof this.#exports === "string" || entryIsVirtual && detectedExports.length > 0;
|
|
1190
|
+
if (skipLocator) options.modules = [{
|
|
1191
|
+
type: "ESModule",
|
|
1192
|
+
path: scriptPath,
|
|
1193
|
+
contents: script
|
|
1194
|
+
}];
|
|
1195
|
+
else {
|
|
1196
|
+
options.script = script;
|
|
1197
|
+
options.scriptPath = scriptPath;
|
|
1198
|
+
}
|
|
660
1199
|
if (!options.modulesRoot) options.modulesRoot = "/";
|
|
661
1200
|
options.unsafeEvalBinding = UNSAFE_EVAL_BINDING;
|
|
662
1201
|
options.serviceBindings = {
|
|
@@ -664,12 +1203,12 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
664
1203
|
[IPC_BINDING]: async (request) => {
|
|
665
1204
|
try {
|
|
666
1205
|
const message = await request.json();
|
|
667
|
-
|
|
1206
|
+
ipc.runner._handleMessage(message);
|
|
668
1207
|
} catch {}
|
|
669
1208
|
return new Response(null, { status: 204 });
|
|
670
1209
|
}
|
|
671
1210
|
};
|
|
672
|
-
if (this.#transformRequest && !options.modulesRules) options.modulesRules = [{
|
|
1211
|
+
if (this.#transformRequest && !options.modulesRules && !miniflare.convertV4MiniflareOptions && !skipLocator) options.modulesRules = [{
|
|
673
1212
|
type: "ESModule",
|
|
674
1213
|
include: [
|
|
675
1214
|
"**/*.ts",
|
|
@@ -681,10 +1220,23 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
681
1220
|
if (!options.unsafeModuleFallbackService) {
|
|
682
1221
|
const _require = createRequire(entryBase);
|
|
683
1222
|
const _virtual = virtual;
|
|
684
|
-
const _virtualVersions = this.#virtualVersions;
|
|
685
1223
|
const _transformRequest = this.#transformRequest;
|
|
686
1224
|
const _exportConditions = this.#exportConditions;
|
|
687
|
-
const _applyVirtualVersions = (code) => applyVirtualVersions(code,
|
|
1225
|
+
const _applyVirtualVersions = (code, modulePath) => applyVirtualVersions(code, _virtual.versions, (specifier) => _virtual.versionedKeyOf(specifier, modulePath));
|
|
1226
|
+
const _serveVirtual = (module, modulePath) => {
|
|
1227
|
+
const { source } = module;
|
|
1228
|
+
switch (module.format) {
|
|
1229
|
+
case "module": return { esModule: _applyVirtualVersions(source, modulePath) };
|
|
1230
|
+
case "commonjs": return {
|
|
1231
|
+
commonJsModule: source,
|
|
1232
|
+
namedExports: commonJSExports(source).filter((e) => e !== "default")
|
|
1233
|
+
};
|
|
1234
|
+
case "json": return { json: source };
|
|
1235
|
+
case "text": return { text: source };
|
|
1236
|
+
case "bytes": return { esModule: virtualModuleCode("bytes", source) };
|
|
1237
|
+
case "wasm": return { wasm: Array.from(source) };
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
688
1240
|
options.unsafeUseModuleFallbackService = true;
|
|
689
1241
|
const modulePathMap = /* @__PURE__ */ new Map();
|
|
690
1242
|
const _lexersReady = Promise.all([ensureCjsLexer(), init$1]);
|
|
@@ -697,24 +1249,30 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
697
1249
|
if (!specifier) return new Response(null, { status: 404 });
|
|
698
1250
|
const cleanSpecifier = specifier.split("?")[0] || specifier;
|
|
699
1251
|
const cleanRaw = rawSpecifier?.split("?")[0];
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
1252
|
+
const bareSpecifier = cleanSpecifier.startsWith("/") ? cleanSpecifier.slice(1) : cleanSpecifier;
|
|
1253
|
+
const referrerKey = referrer.startsWith("/") ? referrer.slice(1) : referrer;
|
|
1254
|
+
const referrerPath = modulePathMap.get(referrerKey);
|
|
1255
|
+
const virtualKey = [
|
|
1256
|
+
cleanRaw,
|
|
1257
|
+
cleanSpecifier,
|
|
1258
|
+
bareSpecifier
|
|
1259
|
+
].find((key) => key !== void 0 && Object.hasOwn(_virtual.sources, key)) ?? (cleanRaw ? _virtual.keyOf(cleanRaw, referrerPath) : void 0) ?? _virtual.keyOf(cleanSpecifier);
|
|
1260
|
+
if (virtualKey !== void 0) {
|
|
1261
|
+
const query = specifier.includes("?") ? specifier.slice(specifier.indexOf("?")) : "";
|
|
1262
|
+
const keyPath = virtualKeyPath(virtualKey);
|
|
1263
|
+
if (keyPath && cleanRaw?.startsWith("file:") && cleanSpecifier !== keyPath && cleanSpecifier.includes("file:")) {
|
|
1264
|
+
const location = Buffer.from(toWorkerdPath(keyPath) + query, "utf8").toString("latin1");
|
|
1265
|
+
return new Response(null, {
|
|
1266
|
+
status: 301,
|
|
1267
|
+
headers: { location }
|
|
716
1268
|
});
|
|
717
1269
|
}
|
|
1270
|
+
const name = bareSpecifier + query;
|
|
1271
|
+
if (keyPath) modulePathMap.set(name, keyPath);
|
|
1272
|
+
return Response.json({
|
|
1273
|
+
name,
|
|
1274
|
+
..._serveVirtual(_virtual.sources[virtualKey], keyPath)
|
|
1275
|
+
});
|
|
718
1276
|
}
|
|
719
1277
|
let resolvedPath;
|
|
720
1278
|
const fileUrlRaw = cleanRaw || cleanSpecifier;
|
|
@@ -723,9 +1281,8 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
723
1281
|
} catch {
|
|
724
1282
|
return new Response(null, { status: 404 });
|
|
725
1283
|
}
|
|
726
|
-
else if (cleanRaw && !cleanRaw.startsWith(".") && !cleanRaw.startsWith("/")) {
|
|
727
|
-
const
|
|
728
|
-
const referrerReal = modulePathMap.get(referrerKey);
|
|
1284
|
+
else if (cleanRaw && !cleanRaw.startsWith(".") && !cleanRaw.startsWith("/") && !isAbsolute(cleanRaw)) {
|
|
1285
|
+
const referrerReal = referrerPath;
|
|
729
1286
|
const contextRequire = referrerReal ? createRequire(referrerReal) : _require;
|
|
730
1287
|
if (cleanRaw.startsWith("cloudflare:")) return new Response(null, { status: 404 });
|
|
731
1288
|
if (cleanRaw.startsWith("node:")) {
|
|
@@ -749,11 +1306,11 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
749
1306
|
});
|
|
750
1307
|
}
|
|
751
1308
|
} else {
|
|
752
|
-
const
|
|
753
|
-
const referrerReal = modulePathMap.get(referrerKey) || (referrer.startsWith("/") ? referrer : "/" + referrer);
|
|
1309
|
+
const referrerReal = referrerPath || (referrer.startsWith("/") ? referrer : "/" + referrer);
|
|
754
1310
|
const referrerDir = dirname(referrerReal);
|
|
755
1311
|
const raw = cleanRaw || cleanSpecifier;
|
|
756
1312
|
if (raw.startsWith(".")) resolvedPath = resolve(referrerDir, raw);
|
|
1313
|
+
else if (cleanRaw && isAbsolute(cleanRaw) && !cleanRaw.startsWith("/")) resolvedPath = cleanRaw;
|
|
757
1314
|
else if (cleanSpecifier.startsWith("/")) resolvedPath = cleanSpecifier;
|
|
758
1315
|
else try {
|
|
759
1316
|
resolvedPath = _require.resolve(raw);
|
|
@@ -769,7 +1326,7 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
769
1326
|
modulePathMap.set(name, resolvedPath);
|
|
770
1327
|
return Response.json({
|
|
771
1328
|
name,
|
|
772
|
-
esModule: _applyVirtualVersions(result.code)
|
|
1329
|
+
esModule: _applyVirtualVersions(result.code, resolvedPath)
|
|
773
1330
|
});
|
|
774
1331
|
}
|
|
775
1332
|
} catch {}
|
|
@@ -778,7 +1335,7 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
778
1335
|
modulePathMap.set(name, resolvedPath);
|
|
779
1336
|
if (resolvedPath.endsWith(".mjs") || !resolvedPath.endsWith(".cjs") && /\b(import\s|import\(|export\s|export\{|import\.meta\b)/.test(contents)) return Response.json({
|
|
780
1337
|
name,
|
|
781
|
-
esModule: _applyVirtualVersions(contents)
|
|
1338
|
+
esModule: _applyVirtualVersions(contents, resolvedPath)
|
|
782
1339
|
});
|
|
783
1340
|
const cjsSuffix = "?__cjs";
|
|
784
1341
|
if (specifier.endsWith(cjsSuffix)) return Response.json({
|
|
@@ -800,31 +1357,35 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
|
|
|
800
1357
|
this.#cacheKey = computeCacheKey(entryPath, {
|
|
801
1358
|
...this.#miniflareOptions,
|
|
802
1359
|
_exportConditions: this.#exportConditions,
|
|
803
|
-
|
|
1360
|
+
_exports: this.#exports,
|
|
1361
|
+
_virtual: encodeVirtualModules(initialVirtual)
|
|
804
1362
|
});
|
|
805
1363
|
const cached = _miniflareCache.get(this.#cacheKey);
|
|
806
1364
|
if (cached) {
|
|
807
1365
|
this.#miniflare = cached.mf;
|
|
808
1366
|
cached.refCount++;
|
|
809
1367
|
this.#cacheEntry = cached;
|
|
1368
|
+
cached.ipc.runner = this;
|
|
810
1369
|
this.#virtual = cached.virtual;
|
|
811
|
-
this.#virtualVersions = cached.versions;
|
|
812
1370
|
}
|
|
813
1371
|
}
|
|
814
1372
|
if (!this.#miniflare) {
|
|
815
|
-
this.#miniflare = new Miniflare(options);
|
|
1373
|
+
this.#miniflare = new miniflare.Miniflare(toMiniflareOptions(miniflare, options));
|
|
816
1374
|
await this.#miniflare.ready;
|
|
817
1375
|
if (this.#persistent && this.#cacheKey) {
|
|
818
1376
|
this.#cacheEntry = {
|
|
819
1377
|
mf: this.#miniflare,
|
|
820
1378
|
refCount: 1,
|
|
821
1379
|
virtual,
|
|
822
|
-
|
|
1380
|
+
ipc
|
|
823
1381
|
};
|
|
824
1382
|
_miniflareCache.set(this.#cacheKey, this.#cacheEntry);
|
|
825
1383
|
}
|
|
826
1384
|
}
|
|
827
|
-
const
|
|
1385
|
+
const loadRes = await this.#miniflare.dispatchFetch("http://localhost" + IPC_PATH);
|
|
1386
|
+
const loadBody = await loadRes.text().catch(() => "");
|
|
1387
|
+
if (!loadRes.ok) throw new Error(`Failed to establish WebSocket IPC channel (${loadRes.status}: ${loadBody})`);
|
|
1388
|
+
const initRes = await this.#miniflare.dispatchFetch("http://localhost" + IPC_PATH, { headers: { upgrade: "websocket" } });
|
|
828
1389
|
const ws = initRes.webSocket;
|
|
829
1390
|
if (!ws) {
|
|
830
1391
|
const body = await initRes.text().catch(() => "");
|
|
@@ -853,6 +1414,55 @@ function detectExportedClasses(entrySource, explicit) {
|
|
|
853
1414
|
}
|
|
854
1415
|
return [...names];
|
|
855
1416
|
}
|
|
1417
|
+
function toMiniflareOptions(miniflare, options) {
|
|
1418
|
+
if (!miniflare.convertV4MiniflareOptions) return options;
|
|
1419
|
+
const { defaultPersistRoot, ...rest } = options;
|
|
1420
|
+
return miniflare.convertV4MiniflareOptions({
|
|
1421
|
+
resourcePersistencePath: defaultPersistRoot,
|
|
1422
|
+
...rest
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
async function resolveSupportedCompatibilityDate(miniflare) {
|
|
1426
|
+
if (typeof miniflare.supportedCompatibilityDate === "string") return miniflare.supportedCompatibilityDate;
|
|
1427
|
+
try {
|
|
1428
|
+
const from = [process.cwd() + "/", import.meta.url];
|
|
1429
|
+
const miniflarePath = resolveModulePath("miniflare", {
|
|
1430
|
+
from,
|
|
1431
|
+
try: true
|
|
1432
|
+
});
|
|
1433
|
+
const workerdPath = resolveModulePath("workerd", { from: miniflarePath || from });
|
|
1434
|
+
const { compatibilityDate } = await import(pathToFileURL(workerdPath).href);
|
|
1435
|
+
if (!_isDateString(compatibilityDate)) return;
|
|
1436
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1437
|
+
return compatibilityDate > today ? today : compatibilityDate;
|
|
1438
|
+
} catch {
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
function resolveCompatibilityDate(candidates, supported) {
|
|
1443
|
+
const date = candidates.find((d) => typeof d === "string" && d) ?? supported;
|
|
1444
|
+
if (date && supported && _isDateString(date) && _isDateString(supported) && date > supported) {
|
|
1445
|
+
console.warn(`[env-runner] compatibility date "${date}" is newer than the installed workerd supports; falling back to "${supported}".`);
|
|
1446
|
+
return supported;
|
|
1447
|
+
}
|
|
1448
|
+
return date;
|
|
1449
|
+
}
|
|
1450
|
+
function resolveCompatibilityFlags(wranglerFlags, userFlags) {
|
|
1451
|
+
const opposite = (flag) => flag === "nodejs_compat" ? "no_nodejs_compat" : flag === "no_nodejs_compat" ? "nodejs_compat" : void 0;
|
|
1452
|
+
const flags = [...wranglerFlags.filter((flag) => !userFlags.includes(opposite(flag))), ...userFlags];
|
|
1453
|
+
const defaults = flags.includes("no_nodejs_compat") ? [] : ["nodejs_compat"];
|
|
1454
|
+
return [.../* @__PURE__ */ new Set([...defaults, ...flags])];
|
|
1455
|
+
}
|
|
1456
|
+
function _isDateString(value) {
|
|
1457
|
+
return /^\d{4}-\d{2}-\d{2}$/.test(value);
|
|
1458
|
+
}
|
|
1459
|
+
function hasUserPersistOptions(options) {
|
|
1460
|
+
return Object.keys(options).some((key) => key === "defaultPersistRoot" || key === "resourcePersistencePath" || key.endsWith("Persist"));
|
|
1461
|
+
}
|
|
1462
|
+
function wranglerPersistRoot(configFile, explicitPath) {
|
|
1463
|
+
const file = configFile ?? (explicitPath ? resolve(explicitPath) : void 0);
|
|
1464
|
+
return join(file ? dirname(file) : process.cwd(), ".wrangler/state/v3");
|
|
1465
|
+
}
|
|
856
1466
|
function _tryReadFile(path) {
|
|
857
1467
|
try {
|
|
858
1468
|
return readFileSync(path, "utf8");
|
|
@@ -868,26 +1478,93 @@ function computeCacheKey(entryPath, opts) {
|
|
|
868
1478
|
for (const [k, v] of Object.entries(opts)) if (typeof v !== "function") serializableOpts[k] = v;
|
|
869
1479
|
return `${resolve(entryPath)}::${JSON.stringify(serializableOpts)}`;
|
|
870
1480
|
}
|
|
871
|
-
|
|
872
|
-
|
|
1481
|
+
const _isWin = process.platform === "win32";
|
|
1482
|
+
function virtualKeyPath(key) {
|
|
1483
|
+
if (_isWin && /^\/[A-Za-z]:\//.test(key)) key = key.slice(1);
|
|
1484
|
+
if (key.startsWith("file:")) try {
|
|
1485
|
+
return fileURLToPath(key);
|
|
1486
|
+
} catch {
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
return isAbsolute(key) ? resolve(key) : void 0;
|
|
1490
|
+
}
|
|
1491
|
+
function toWorkerdPath(path) {
|
|
1492
|
+
return _isWin && /^[A-Za-z]:[\\/]/.test(path) ? "/" + path.replaceAll("\\", "/") : path;
|
|
1493
|
+
}
|
|
1494
|
+
function createMiniflareVirtualModules(sources) {
|
|
1495
|
+
return refreshVirtualKeyResolvers({
|
|
1496
|
+
sources,
|
|
1497
|
+
removed: /* @__PURE__ */ new Set(),
|
|
1498
|
+
versions: /* @__PURE__ */ new Map(),
|
|
1499
|
+
version: 0
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
function refreshVirtualKeyResolvers(virtual) {
|
|
1503
|
+
const keys = Object.keys(virtual.sources);
|
|
1504
|
+
virtual.keyOf = createVirtualKeyResolver(keys);
|
|
1505
|
+
virtual.versionedKeyOf = virtual.removed.size > 0 ? createVirtualKeyResolver([...keys, ...virtual.removed]) : virtual.keyOf;
|
|
1506
|
+
return virtual;
|
|
1507
|
+
}
|
|
1508
|
+
function createVirtualKeyResolver(keys) {
|
|
1509
|
+
const verbatim = new Set(keys);
|
|
1510
|
+
const pathKeys = /* @__PURE__ */ new Map();
|
|
1511
|
+
for (const key of verbatim) {
|
|
1512
|
+
const path = virtualKeyPath(key);
|
|
1513
|
+
if (path !== void 0) pathKeys.set(path, key);
|
|
1514
|
+
}
|
|
1515
|
+
return (specifier, importerPath) => {
|
|
1516
|
+
const clean = specifier.split("?")[0];
|
|
1517
|
+
if (verbatim.has(clean)) return clean;
|
|
1518
|
+
if (pathKeys.size === 0) return;
|
|
1519
|
+
const path = /^\.\.?\//.test(clean) ? importerPath && resolve(dirname(importerPath), clean) : virtualKeyPath(clean);
|
|
1520
|
+
return path ? pathKeys.get(path) : void 0;
|
|
1521
|
+
};
|
|
1522
|
+
}
|
|
1523
|
+
function virtualImporters(virtual, keyOf) {
|
|
1524
|
+
const importers = /* @__PURE__ */ new Map();
|
|
1525
|
+
for (const [importer, { source, format }] of Object.entries(virtual)) {
|
|
1526
|
+
if (format !== "module") continue;
|
|
1527
|
+
const importerPath = virtualKeyPath(importer);
|
|
1528
|
+
for (const { specifier } of versionableImports(source) ?? []) {
|
|
1529
|
+
const key = keyOf(specifier, importerPath);
|
|
1530
|
+
if (key !== void 0 && key !== importer) {
|
|
1531
|
+
let set = importers.get(key);
|
|
1532
|
+
if (!set) importers.set(key, set = /* @__PURE__ */ new Set());
|
|
1533
|
+
set.add(importer);
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
return importers;
|
|
1538
|
+
}
|
|
1539
|
+
function versionableImports(code) {
|
|
873
1540
|
let imports;
|
|
874
1541
|
try {
|
|
875
1542
|
[imports] = parse$1(code);
|
|
876
1543
|
} catch {
|
|
877
|
-
return
|
|
1544
|
+
return;
|
|
878
1545
|
}
|
|
1546
|
+
const result = [];
|
|
1547
|
+
for (const imp of imports) {
|
|
1548
|
+
if (typeof imp.specifier !== "string" || imp.type === "dynamic" && imp.glob) continue;
|
|
1549
|
+
result.push({
|
|
1550
|
+
specifier: imp.specifier,
|
|
1551
|
+
end: imp.type === "dynamic" ? imp.end - 1 : imp.end
|
|
1552
|
+
});
|
|
1553
|
+
}
|
|
1554
|
+
return result;
|
|
1555
|
+
}
|
|
1556
|
+
function applyVirtualVersions(code, versions, keyOf) {
|
|
1557
|
+
if (versions.size === 0) return code;
|
|
1558
|
+
const imports = versionableImports(code);
|
|
1559
|
+
if (!imports) return code;
|
|
879
1560
|
let out = "";
|
|
880
1561
|
let last = 0;
|
|
881
|
-
for (const
|
|
882
|
-
|
|
883
|
-
const
|
|
884
|
-
if (dynamic && imp.glob) continue;
|
|
885
|
-
const specifier = imp.specifier;
|
|
886
|
-
const version = specifier === void 0 ? void 0 : versions.get(specifier);
|
|
1562
|
+
for (const { specifier, end } of imports) {
|
|
1563
|
+
const key = keyOf(specifier);
|
|
1564
|
+
const version = key === void 0 ? void 0 : versions.get(key);
|
|
887
1565
|
if (!version) continue;
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
last = imp.end;
|
|
1566
|
+
out += code.slice(last, end) + (specifier.includes("?") ? "&" : "?") + `v=${version}`;
|
|
1567
|
+
last = end;
|
|
891
1568
|
}
|
|
892
1569
|
return out + code.slice(last);
|
|
893
1570
|
}
|
|
@@ -901,12 +1578,15 @@ function ensureCjsLexer() {
|
|
|
901
1578
|
if (!_cjsLexerReady) _cjsLexerReady = init();
|
|
902
1579
|
return _cjsLexerReady;
|
|
903
1580
|
}
|
|
904
|
-
function
|
|
905
|
-
let namedExports = [];
|
|
1581
|
+
function commonJSExports(contents) {
|
|
906
1582
|
try {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
1583
|
+
return parse(contents).exports;
|
|
1584
|
+
} catch {
|
|
1585
|
+
return [];
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
function createCjsEsmShim(cjsSpecifier, contents) {
|
|
1589
|
+
const namedExports = commonJSExports(contents).filter((e) => e !== "default" && e !== "__esModule");
|
|
910
1590
|
let shim = `import __cjs_mod__ from ${JSON.stringify(cjsSpecifier)};\nexport default __cjs_mod__;\n`;
|
|
911
1591
|
for (const name of namedExports) shim += `export var ${name} = __cjs_mod__["${name}"];\n`;
|
|
912
1592
|
return shim;
|