workerd's built-in internet service cannot read the Windows system trust store, so npm installs inside the publish worker failed TLS verification. Declare the internet service explicitly with the runtime's bundled Mozilla roots, surface workerd stderr in startup failures, and fail the bundle when a dependency install warning is present instead of publishing a broken bundle.
Unified · Split
| @@ -8,6 +8,7 @@ import { createServer as createNetServer, type Server as NetServer } from "node: · 7 unmodified lines | ||
| 8 | 8 | import type { Readable } from "node:stream"; |
| 9 | 9 | import { tmpdir } from "node:os"; |
| 10 | 10 | import { dirname, join } from "node:path"; |
| 11 | +import { rootCertificates } from "node:tls"; | |
| 11 | 12 | import { fileURLToPath } from "node:url"; |
| 12 | 13 | |
| 13 | 14 | import { |
| @@ -303,9 +304,19 @@ const buildConfig = (input: { · 289 unmodified lines | ||
| 303 | 304 | return `( name = ${capnpString(name)}, ${field} = embed ${capnpString(name)} )`; |
| 304 | 305 | }) |
| 305 | 306 | .join(",\n "); |
| 307 | + // workerd's built-in `internet` service relies on the platform TLS trust | |
| 308 | + // store, which kj/tls cannot read on Windows ("unable to get local issuer | |
| 309 | + // certificate"). Declaring our own internet service with the runtime's | |
| 310 | + // bundled Mozilla roots gives every platform the same trust behavior. | |
| 311 | + const trustedCertificates = rootCertificates.map(capnpString).join(",\n "); | |
| 306 | 312 | const services = [ |
| 307 | 313 | '( name = "main", worker = .mainWorker )', |
| 308 | 314 | '( name = "blocked", worker = .blockedWorker )', |
| 315 | + ...(input.globalOutbound === "internet" | |
| 316 | + ? [ | |
| 317 | + `( name = "internet", network = ( allow = ["public"], tlsOptions = ( trustedCertificates = [\n ${trustedCertificates}\n ] ) ) )`, | |
| 318 | + ] | |
| 319 | + : []), | |
| 309 | 320 | ...(input.hostPort === null |
| 310 | 321 | ? [] |
| 311 | 322 | : [`( name = "host", external = ( address = "127.0.0.1:${input.hostPort}", http = () ) )`]), |
| @@ -504,7 +515,10 @@ export const createWorkerdModuleRunner = ( · 192 unmodified lines | ||
| 504 | 515 | }); |
| 505 | 516 | proc.stderr.setEncoding("utf8"); |
| 506 | 517 | proc.stdout.resume(); |
| 507 | − proc.stderr.resume(); | |
| 518 | + let stderrTail = ""; | |
| 519 | + proc.stderr.on("data", (chunk: string) => { | |
| 520 | + stderrTail = (stderrTail + chunk).slice(-4000); | |
| 521 | + }); | |
| 508 | 522 | proc.on("exit", (code, signal) => { |
| 509 | 523 | lastExitAt = Date.now(); |
| 510 | 524 | if (processState?.proc === proc) { |
| @@ -522,7 +536,10 @@ export const createWorkerdModuleRunner = ( · 11 unmodified lines | ||
| 522 | 536 | await waitReady(listenPort, token, startupTimeoutMs); |
| 523 | 537 | } catch (cause) { |
| 524 | 538 | await disposeState(true); |
| 525 | − throw cause; | |
| 539 | + const detail = stderrTail.trim(); | |
| 540 | + throw detail.length === 0 | |
| 541 | + ? cause | |
| 542 | + : new Error(`${normalizeError(cause).message}\nworkerd stderr:\n${detail}`); | |
| 526 | 543 | } |
| 527 | 544 | })(); |
| 528 | 545 | |
| @@ -81,6 +81,10 @@ export default { · 80 unmodified lines | ||
| 81 | 81 | if (code === null) { |
| 82 | 82 | return json({ ok: false, message: "worker-bundler did not return JavaScript for " + result.mainModule }); |
| 83 | 83 | } |
| 84 | + const installFailure = (result.warnings ?? []).find((warning) => /failed to install/i.test(String(warning))); | |
| 85 | + if (installFailure) { | |
| 86 | + return json({ ok: false, message: String(installFailure) }); | |
| 87 | + } | |
| 84 | 88 | for (const [path, module] of Object.entries(result.modules)) { |
| 85 | 89 | const source = moduleCode(module) ?? ""; |
| 86 | 90 | if (/\\.node(?:$|[?#])|node-gyp|prebuild-install|node-pre-gyp/.test(path) || /\\.node(?:$|[?#])|node-gyp|prebuild-install|node-pre-gyp/.test(source)) { |