diff --git a/ext/node/polyfills/02_init.js b/ext/node/polyfills/02_init.js
index 85f924493a..752f518bf2 100644
--- a/ext/node/polyfills/02_init.js
+++ b/ext/node/polyfills/02_init.js
@@ -16,38 +16,32 @@ function initialize(
   runningOnMainThread,
   workerId,
   maybeWorkerMetadata,
+  warmup = false,
 ) {
-  if (initialized) {
-    throw Error("Node runtime already initialized");
-  }
-  initialized = true;
-  if (usesLocalNodeModulesDir) {
-    requireImpl.setUsesLocalNodeModulesDir();
-  }
-  const nativeModuleExports = requireImpl.nativeModuleExports;
-  nodeGlobals.Buffer = nativeModuleExports["buffer"].Buffer;
-  nodeGlobals.clearImmediate = nativeModuleExports["timers"].clearImmediate;
-  nodeGlobals.clearInterval = nativeModuleExports["timers"].clearInterval;
-  nodeGlobals.clearTimeout = nativeModuleExports["timers"].clearTimeout;
-  nodeGlobals.console = nativeModuleExports["console"];
-  nodeGlobals.global = globalThis;
-  nodeGlobals.process = nativeModuleExports["process"];
-  nodeGlobals.setImmediate = nativeModuleExports["timers"].setImmediate;
-  nodeGlobals.setInterval = nativeModuleExports["timers"].setInterval;
-  nodeGlobals.setTimeout = nativeModuleExports["timers"].setTimeout;
-  nodeGlobals.performance = nativeModuleExports["perf_hooks"].performance;
+  if (!warmup) {
+    if (initialized) {
+      throw Error("Node runtime already initialized");
+    }
+    initialized = true;
+    if (usesLocalNodeModulesDir) {
+      requireImpl.setUsesLocalNodeModulesDir();
+    }
 
-  // FIXME(bartlomieju): not nice to depend on `Deno` namespace here
-  // but it's the only way to get `args` and `version` and this point.
-  internals.__bootstrapNodeProcess(argv0, Deno.args, Deno.version);
-  internals.__initWorkerThreads(
-    runningOnMainThread,
-    workerId,
-    maybeWorkerMetadata,
-  );
-  internals.__setupChildProcessIpcChannel();
-  // `Deno[Deno.internal].requireImpl` will be unreachable after this line.
-  delete internals.requireImpl;
+    // FIXME(bartlomieju): not nice to depend on `Deno` namespace here
+    // but it's the only way to get `args` and `version` and this point.
+    internals.__bootstrapNodeProcess(argv0, Deno.args, Deno.version);
+    internals.__initWorkerThreads(
+      runningOnMainThread,
+      workerId,
+      maybeWorkerMetadata,
+    );
+    internals.__setupChildProcessIpcChannel();
+    // `Deno[Deno.internal].requireImpl` will be unreachable after this line.
+    delete internals.requireImpl;
+  } else {
+    // Warm up the process module
+    internals.__bootstrapNodeProcess(undefined, undefined, undefined, true);
+  }
 }
 
 function loadCjsModule(moduleName, isMain, inspectBrk) {
@@ -63,3 +57,16 @@ internals.node = {
   initialize,
   loadCjsModule,
 };
+
+const nativeModuleExports = requireImpl.nativeModuleExports;
+nodeGlobals.Buffer = nativeModuleExports["buffer"].Buffer;
+nodeGlobals.clearImmediate = nativeModuleExports["timers"].clearImmediate;
+nodeGlobals.clearInterval = nativeModuleExports["timers"].clearInterval;
+nodeGlobals.clearTimeout = nativeModuleExports["timers"].clearTimeout;
+nodeGlobals.console = nativeModuleExports["console"];
+nodeGlobals.global = globalThis;
+nodeGlobals.process = nativeModuleExports["process"];
+nodeGlobals.setImmediate = nativeModuleExports["timers"].setImmediate;
+nodeGlobals.setInterval = nativeModuleExports["timers"].setInterval;
+nodeGlobals.setTimeout = nativeModuleExports["timers"].setTimeout;
+nodeGlobals.performance = nativeModuleExports["perf_hooks"].performance;
diff --git a/ext/node/polyfills/_process/streams.mjs b/ext/node/polyfills/_process/streams.mjs
index 09c53eb9a2..f50e205888 100644
--- a/ext/node/polyfills/_process/streams.mjs
+++ b/ext/node/polyfills/_process/streams.mjs
@@ -16,7 +16,7 @@ import * as io from "ext:deno_io/12_io.js";
 import { guessHandleType } from "ext:deno_node/internal_binding/util.ts";
 
 // https://github.com/nodejs/node/blob/00738314828074243c9a52a228ab4c68b04259ef/lib/internal/bootstrap/switches/is_main_thread.js#L41
-export function createWritableStdioStream(writer, name) {
+export function createWritableStdioStream(writer, name, warmup = false) {
   const stream = new Writable({
     emitClose: false,
     write(buf, enc, cb) {
@@ -73,7 +73,9 @@ export function createWritableStdioStream(writer, name) {
     },
   });
 
-  if (writer?.isTerminal()) {
+  // If we're warming up, create a stdout/stderr stream that assumes a terminal (the most likely case).
+  // If we're wrong at boot time, we'll recreate it.
+  if (warmup || writer?.isTerminal()) {
     // These belong on tty.WriteStream(), but the TTY streams currently have
     // following problems:
     // 1. Using them here introduces a circular dependency.
@@ -123,10 +125,11 @@ export function setReadStream(s) {
 /** https://nodejs.org/api/process.html#process_process_stdin */
 // https://github.com/nodejs/node/blob/v18.12.1/lib/internal/bootstrap/switches/is_main_thread.js#L189
 /** Create process.stdin */
-export const initStdin = () => {
+export const initStdin = (warmup = false) => {
   const fd = io.stdin ? io.STDIN_RID : undefined;
   let stdin;
-  const stdinType = _guessStdinType(fd);
+  // Warmup assumes a TTY for all stdio
+  const stdinType = warmup ? "TTY" : _guessStdinType(fd);
 
   switch (stdinType) {
     case "FILE": {
@@ -142,6 +145,11 @@ export const initStdin = () => {
       break;
     }
     case "TTY": {
+      // If it's a TTY, we know that the stdin we created during warmup is the correct one and
+      // just return null to re-use it.
+      if (!warmup) {
+        return null;
+      }
       stdin = new readStream(fd);
       break;
     }
diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts
index 44207e2f43..70c516c965 100644
--- a/ext/node/polyfills/process.ts
+++ b/ext/node/polyfills/process.ts
@@ -68,7 +68,7 @@ const notImplementedEvents = [
   "worker",
 ];
 
-export const argv: string[] = [];
+export const argv: string[] = ["", ""];
 let globalProcessExitCode: number | undefined = undefined;
 
 /** https://nodejs.org/api/process.html#process_process_exit_code */
@@ -868,69 +868,91 @@ function synchronizeListeners() {
   }
 }
 
+// Overwrites the 1st and 2nd items with getters.
+Object.defineProperty(argv, "0", { get: () => argv0 });
+Object.defineProperty(argv, "1", {
+  get: () => {
+    if (Deno.mainModule?.startsWith("file:")) {
+      return pathFromURL(new URL(Deno.mainModule));
+    } else {
+      return join(Deno.cwd(), "$deno$node.js");
+    }
+  },
+});
+
 // Should be called only once, in `runtime/js/99_main.js` when the runtime is
 // bootstrapped.
 internals.__bootstrapNodeProcess = function (
   argv0Val: string | undefined,
   args: string[],
   denoVersions: Record<string, string>,
+  warmup = false,
 ) {
-  // Overwrites the 1st item with getter.
-  if (typeof argv0Val === "string") {
-    argv0 = argv0Val;
-    Object.defineProperty(argv, "0", {
-      get: () => {
-        return argv0Val;
-      },
-    });
+  if (!warmup) {
+    argv0 = argv0Val || "";
+    // Manually concatenate these arrays to avoid triggering the getter
+    for (let i = 0; i < args.length; i++) {
+      argv[i + 2] = args[i];
+    }
+
+    for (const [key, value] of Object.entries(denoVersions)) {
+      versions[key] = value;
+    }
+
+    core.setNextTickCallback(processTicksAndRejections);
+    core.setMacrotaskCallback(runNextTicks);
+    enableNextTick();
+
+    // Replace stdin if it is not a terminal
+    const newStdin = initStdin();
+    if (newStdin) {
+      stdin = process.stdin = newStdin;
+    }
+
+    // Replace stdout/stderr if they are not terminals
+    if (!io.stdout.isTerminal()) {
+      /** https://nodejs.org/api/process.html#process_process_stdout */
+      stdout = process.stdout = createWritableStdioStream(
+        io.stdout,
+        "stdout",
+      );
+    }
+
+    if (!io.stderr.isTerminal()) {
+      /** https://nodejs.org/api/process.html#process_process_stderr */
+      stderr = process.stderr = createWritableStdioStream(
+        io.stderr,
+        "stderr",
+      );
+    }
+
+    process.setStartTime(Date.now());
+
+    arch = arch_();
+    platform = isWindows ? "win32" : Deno.build.os;
+    pid = Deno.pid;
+
+    // @ts-ignore Remove setStartTime and #startTime is not modifiable
+    delete process.setStartTime;
+    delete internals.__bootstrapNodeProcess;
   } else {
-    Object.defineProperty(argv, "0", { get: () => argv0 });
+    // Warmup, assuming stdin/stdout/stderr are all terminals
+    stdin = process.stdin = initStdin(true);
+
+    /** https://nodejs.org/api/process.html#process_process_stdout */
+    stdout = process.stdout = createWritableStdioStream(
+      io.stdout,
+      "stdout",
+      true,
+    );
+
+    /** https://nodejs.org/api/process.html#process_process_stderr */
+    stderr = process.stderr = createWritableStdioStream(
+      io.stderr,
+      "stderr",
+      true,
+    );
   }
-
-  // Overwrites the 2st item with getter.
-  Object.defineProperty(argv, "1", {
-    get: () => {
-      if (Deno.mainModule?.startsWith("file:")) {
-        return pathFromURL(new URL(Deno.mainModule));
-      } else {
-        return join(Deno.cwd(), "$deno$node.js");
-      }
-    },
-  });
-  for (let i = 0; i < args.length; i++) {
-    argv[i + 2] = args[i];
-  }
-
-  for (const [key, value] of Object.entries(denoVersions)) {
-    versions[key] = value;
-  }
-
-  core.setNextTickCallback(processTicksAndRejections);
-  core.setMacrotaskCallback(runNextTicks);
-  enableNextTick();
-
-  stdin = process.stdin = initStdin();
-  /** https://nodejs.org/api/process.html#process_process_stdout */
-  stdout = process.stdout = createWritableStdioStream(
-    io.stdout,
-    "stdout",
-  );
-
-  /** https://nodejs.org/api/process.html#process_process_stderr */
-  stderr = process.stderr = createWritableStdioStream(
-    io.stderr,
-    "stderr",
-  );
-
-  process.setStartTime(Date.now());
-
-  arch = arch_();
-  platform = isWindows ? "win32" : Deno.build.os;
-  pid = Deno.pid;
-
-  // @ts-ignore Remove setStartTime and #startTime is not modifiable
-  delete process.setStartTime;
-  delete internals.__bootstrapNodeProcess;
 };
 
 export default process;
diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js
index 1a1f781190..a47a7163e1 100644
--- a/runtime/js/99_main.js
+++ b/runtime/js/99_main.js
@@ -628,6 +628,28 @@ const finalDenoNs = {
   bench: () => {},
 };
 
+ObjectDefineProperties(finalDenoNs, {
+  pid: core.propGetterOnly(opPid),
+  // `ppid` should not be memoized.
+  // https://github.com/denoland/deno/issues/23004
+  ppid: core.propGetterOnly(() => op_ppid()),
+  noColor: core.propGetterOnly(() => op_bootstrap_no_color()),
+  args: core.propGetterOnly(opArgs),
+  mainModule: core.propGetterOnly(() => op_main_module()),
+  // TODO(kt3k): Remove this export at v2
+  // See https://github.com/denoland/deno/issues/9294
+  customInspect: {
+    get() {
+      warnOnDeprecatedApi(
+        "Deno.customInspect",
+        new Error().stack,
+        'Use `Symbol.for("Deno.customInspect")` instead.',
+      );
+      return customInspect;
+    },
+  },
+});
+
 const {
   denoVersion,
   tsVersion,
@@ -635,155 +657,130 @@ const {
   target,
 } = op_snapshot_options();
 
-function bootstrapMainRuntime(runtimeOptions) {
-  if (hasBootstrapped) {
-    throw new Error("Worker runtime already bootstrapped");
-  }
-  const nodeBootstrap = globalThis.nodeBootstrap;
-
-  const {
-    0: location_,
-    1: unstableFlag,
-    2: unstableFeatures,
-    3: inspectFlag,
-    5: hasNodeModulesDir,
-    6: argv0,
-    7: shouldDisableDeprecatedApiWarning,
-    8: shouldUseVerboseDeprecatedApiWarning,
-    9: future,
-  } = runtimeOptions;
-
-  removeImportedOps();
-
-  deprecatedApiWarningDisabled = shouldDisableDeprecatedApiWarning;
-  verboseDeprecatedApiWarning = shouldUseVerboseDeprecatedApiWarning;
-  performance.setTimeOrigin(DateNow());
-  globalThis_ = globalThis;
-
-  // Remove bootstrapping data from the global scope
-  delete globalThis.__bootstrap;
-  delete globalThis.bootstrap;
-  delete globalThis.nodeBootstrap;
-  hasBootstrapped = true;
-
-  // If the `--location` flag isn't set, make `globalThis.location` `undefined` and
-  // writable, so that they can mock it themselves if they like. If the flag was
-  // set, define `globalThis.location`, using the provided value.
-  if (location_ == null) {
-    mainRuntimeGlobalProperties.location = {
-      writable: true,
-    };
-  } else {
-    location.setLocationHref(location_);
-  }
-
-  exposeUnstableFeaturesForWindowOrWorkerGlobalScope({
-    unstableFlag,
-    unstableFeatures,
-  });
-  ObjectDefineProperties(globalThis, mainRuntimeGlobalProperties);
-  ObjectDefineProperties(globalThis, {
-    // TODO(bartlomieju): in the future we might want to change the
-    // behavior of setting `name` to actually update the process name.
-    // Empty string matches what browsers do.
-    name: core.propWritable(""),
-    close: core.propWritable(windowClose),
-    closed: core.propGetterOnly(() => windowIsClosing),
-  });
-  ObjectSetPrototypeOf(globalThis, Window.prototype);
-
-  if (inspectFlag) {
-    const consoleFromDeno = globalThis.console;
-    core.wrapConsole(consoleFromDeno, core.v8Console);
-  }
-
-  event.setEventTargetData(globalThis);
-  event.saveGlobalThisReference(globalThis);
-
-  event.defineEventHandler(globalThis, "error");
-  event.defineEventHandler(globalThis, "load");
-  event.defineEventHandler(globalThis, "beforeunload");
-  event.defineEventHandler(globalThis, "unload");
-  event.defineEventHandler(globalThis, "unhandledrejection");
-
-  runtimeStart(
-    denoVersion,
-    v8Version,
-    tsVersion,
-    target,
-  );
-
-  ObjectDefineProperties(finalDenoNs, {
-    pid: core.propGetterOnly(opPid),
-    // `ppid` should not be memoized.
-    // https://github.com/denoland/deno/issues/23004
-    ppid: core.propGetterOnly(() => op_ppid()),
-    noColor: core.propGetterOnly(() => op_bootstrap_no_color()),
-    args: core.propGetterOnly(opArgs),
-    mainModule: core.propGetterOnly(() => op_main_module()),
-    // TODO(kt3k): Remove this export at v2
-    // See https://github.com/denoland/deno/issues/9294
-    customInspect: {
-      get() {
-        warnOnDeprecatedApi(
-          "Deno.customInspect",
-          new Error().stack,
-          'Use `Symbol.for("Deno.customInspect")` instead.',
-        );
-        return customInspect;
-      },
-    },
-  });
-
-  // TODO(bartlomieju): deprecate --unstable
-  if (unstableFlag) {
-    ObjectAssign(finalDenoNs, denoNsUnstable);
-    // TODO(bartlomieju): this is not ideal, but because we use `ObjectAssign`
-    // above any properties that are defined elsewhere using `Object.defineProperty`
-    // are lost.
-    let jupyterNs = undefined;
-    ObjectDefineProperty(finalDenoNs, "jupyter", {
-      get() {
-        if (jupyterNs) {
-          return jupyterNs;
-        }
-        throw new Error(
-          "Deno.jupyter is only available in `deno jupyter` subcommand.",
-        );
-      },
-      set(val) {
-        jupyterNs = val;
-      },
-    });
-  } else {
-    for (let i = 0; i <= unstableFeatures.length; i++) {
-      const id = unstableFeatures[i];
-      ObjectAssign(finalDenoNs, denoNsUnstableById[id]);
+function bootstrapMainRuntime(runtimeOptions, warmup = false) {
+  if (!warmup) {
+    if (hasBootstrapped) {
+      throw new Error("Worker runtime already bootstrapped");
     }
-  }
 
-  if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.unsafeProto)) {
-    // Removes the `__proto__` for security reasons.
-    // https://tc39.es/ecma262/#sec-get-object.prototype.__proto__
-    delete Object.prototype.__proto__;
-  }
+    const {
+      0: location_,
+      1: unstableFlag,
+      2: unstableFeatures,
+      3: inspectFlag,
+      5: hasNodeModulesDir,
+      6: argv0,
+      7: shouldDisableDeprecatedApiWarning,
+      8: shouldUseVerboseDeprecatedApiWarning,
+      9: future,
+    } = runtimeOptions;
 
-  if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.temporal)) {
-    // Removes the `Temporal` API.
-    delete globalThis.Temporal;
-    delete globalThis.Date.prototype.toTemporalInstant;
-  }
+    removeImportedOps();
 
-  // Setup `Deno` global - we're actually overriding already existing global
-  // `Deno` with `Deno` namespace from "./deno.ts".
-  ObjectDefineProperty(globalThis, "Deno", core.propReadOnly(finalDenoNs));
+    deprecatedApiWarningDisabled = shouldDisableDeprecatedApiWarning;
+    verboseDeprecatedApiWarning = shouldUseVerboseDeprecatedApiWarning;
+    performance.setTimeOrigin(DateNow());
+    globalThis_ = globalThis;
 
-  if (nodeBootstrap) {
-    nodeBootstrap(hasNodeModulesDir, argv0, /* runningOnMainThread */ true);
-  }
+    // Remove bootstrapping data from the global scope
+    delete globalThis.__bootstrap;
+    delete globalThis.bootstrap;
+    hasBootstrapped = true;
 
-  if (future) {
-    delete globalThis.window;
+    // If the `--location` flag isn't set, make `globalThis.location` `undefined` and
+    // writable, so that they can mock it themselves if they like. If the flag was
+    // set, define `globalThis.location`, using the provided value.
+    if (location_ == null) {
+      mainRuntimeGlobalProperties.location = {
+        writable: true,
+      };
+    } else {
+      location.setLocationHref(location_);
+    }
+
+    exposeUnstableFeaturesForWindowOrWorkerGlobalScope({
+      unstableFlag,
+      unstableFeatures,
+    });
+    ObjectDefineProperties(globalThis, mainRuntimeGlobalProperties);
+    ObjectDefineProperties(globalThis, {
+      // TODO(bartlomieju): in the future we might want to change the
+      // behavior of setting `name` to actually update the process name.
+      // Empty string matches what browsers do.
+      name: core.propWritable(""),
+      close: core.propWritable(windowClose),
+      closed: core.propGetterOnly(() => windowIsClosing),
+    });
+    ObjectSetPrototypeOf(globalThis, Window.prototype);
+
+    if (inspectFlag) {
+      const consoleFromDeno = globalThis.console;
+      core.wrapConsole(consoleFromDeno, core.v8Console);
+    }
+
+    event.defineEventHandler(globalThis, "error");
+    event.defineEventHandler(globalThis, "load");
+    event.defineEventHandler(globalThis, "beforeunload");
+    event.defineEventHandler(globalThis, "unload");
+
+    runtimeStart(
+      denoVersion,
+      v8Version,
+      tsVersion,
+      target,
+    );
+
+    // TODO(bartlomieju): deprecate --unstable
+    if (unstableFlag) {
+      ObjectAssign(finalDenoNs, denoNsUnstable);
+      // TODO(bartlomieju): this is not ideal, but because we use `ObjectAssign`
+      // above any properties that are defined elsewhere using `Object.defineProperty`
+      // are lost.
+      let jupyterNs = undefined;
+      ObjectDefineProperty(finalDenoNs, "jupyter", {
+        get() {
+          if (jupyterNs) {
+            return jupyterNs;
+          }
+          throw new Error(
+            "Deno.jupyter is only available in `deno jupyter` subcommand.",
+          );
+        },
+        set(val) {
+          jupyterNs = val;
+        },
+      });
+    } else {
+      for (let i = 0; i <= unstableFeatures.length; i++) {
+        const id = unstableFeatures[i];
+        ObjectAssign(finalDenoNs, denoNsUnstableById[id]);
+      }
+    }
+
+    if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.unsafeProto)) {
+      // Removes the `__proto__` for security reasons.
+      // https://tc39.es/ecma262/#sec-get-object.prototype.__proto__
+      delete Object.prototype.__proto__;
+    }
+
+    if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.temporal)) {
+      // Removes the `Temporal` API.
+      delete globalThis.Temporal;
+      delete globalThis.Date.prototype.toTemporalInstant;
+    }
+
+    // Setup `Deno` global - we're actually overriding already existing global
+    // `Deno` with `Deno` namespace from "./deno.ts".
+    ObjectDefineProperty(globalThis, "Deno", core.propReadOnly(finalDenoNs));
+
+    if (nodeBootstrap) {
+      nodeBootstrap(hasNodeModulesDir, argv0, /* runningOnMainThread */ true);
+    }
+    if (future) {
+      delete globalThis.window;
+    }
+  } else {
+    // Warmup
   }
 }
 
@@ -793,146 +790,153 @@ function bootstrapWorkerRuntime(
   internalName,
   workerId,
   maybeWorkerMetadata,
+  warmup = false,
 ) {
-  if (hasBootstrapped) {
-    throw new Error("Worker runtime already bootstrapped");
-  }
-
-  const nodeBootstrap = globalThis.nodeBootstrap;
-
-  const {
-    0: location_,
-    1: unstableFlag,
-    2: unstableFeatures,
-    4: enableTestingFeaturesFlag,
-    5: hasNodeModulesDir,
-    6: argv0,
-    7: shouldDisableDeprecatedApiWarning,
-    8: shouldUseVerboseDeprecatedApiWarning,
-    9: _future,
-  } = runtimeOptions;
-
-  deprecatedApiWarningDisabled = shouldDisableDeprecatedApiWarning;
-  verboseDeprecatedApiWarning = shouldUseVerboseDeprecatedApiWarning;
-  performance.setTimeOrigin(DateNow());
-  globalThis_ = globalThis;
-
-  removeImportedOps();
-
-  // Remove bootstrapping data from the global scope
-  delete globalThis.__bootstrap;
-  delete globalThis.bootstrap;
-  delete globalThis.nodeBootstrap;
-  hasBootstrapped = true;
-
-  exposeUnstableFeaturesForWindowOrWorkerGlobalScope({
-    unstableFlag,
-    unstableFeatures,
-  });
-  ObjectDefineProperties(globalThis, workerRuntimeGlobalProperties);
-  ObjectDefineProperties(globalThis, {
-    name: core.propWritable(name),
-    // TODO(bartlomieju): should be readonly?
-    close: core.propNonEnumerable(workerClose),
-    postMessage: core.propWritable(postMessage),
-  });
-  if (enableTestingFeaturesFlag) {
-    ObjectDefineProperty(
-      globalThis,
-      "importScripts",
-      core.propWritable(importScripts),
-    );
-  }
-  ObjectSetPrototypeOf(globalThis, DedicatedWorkerGlobalScope.prototype);
-
-  const consoleFromDeno = globalThis.console;
-  core.wrapConsole(consoleFromDeno, core.v8Console);
-
-  event.setEventTargetData(globalThis);
-  event.saveGlobalThisReference(globalThis);
-
-  event.defineEventHandler(self, "message");
-  event.defineEventHandler(self, "error", undefined, true);
-  event.defineEventHandler(self, "unhandledrejection");
-
-  // `Deno.exit()` is an alias to `self.close()`. Setting and exit
-  // code using an op in worker context is a no-op.
-  os.setExitHandler((_exitCode) => {
-    workerClose();
-  });
-
-  runtimeStart(
-    denoVersion,
-    v8Version,
-    tsVersion,
-    target,
-    internalName ?? name,
-  );
-
-  location.setLocationHref(location_);
-
-  globalThis.pollForMessages = pollForMessages;
-  globalThis.hasMessageEventListener = hasMessageEventListener;
-
-  // TODO(bartlomieju): deprecate --unstable
-  if (unstableFlag) {
-    ObjectAssign(finalDenoNs, denoNsUnstable);
-  } else {
-    for (let i = 0; i <= unstableFeatures.length; i++) {
-      const id = unstableFeatures[i];
-      ObjectAssign(finalDenoNs, denoNsUnstableById[id]);
+  if (!warmup) {
+    if (hasBootstrapped) {
+      throw new Error("Worker runtime already bootstrapped");
     }
-  }
 
-  if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.unsafeProto)) {
-    // Removes the `__proto__` for security reasons.
-    // https://tc39.es/ecma262/#sec-get-object.prototype.__proto__
-    delete Object.prototype.__proto__;
-  }
+    const {
+      0: location_,
+      1: unstableFlag,
+      2: unstableFeatures,
+      4: enableTestingFeaturesFlag,
+      5: hasNodeModulesDir,
+      6: argv0,
+      7: shouldDisableDeprecatedApiWarning,
+      8: shouldUseVerboseDeprecatedApiWarning,
+      9: _future,
+    } = runtimeOptions;
 
-  if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.temporal)) {
-    // Removes the `Temporal` API.
-    delete globalThis.Temporal;
-    delete globalThis.Date.prototype.toTemporalInstant;
-  }
+    deprecatedApiWarningDisabled = shouldDisableDeprecatedApiWarning;
+    verboseDeprecatedApiWarning = shouldUseVerboseDeprecatedApiWarning;
+    performance.setTimeOrigin(DateNow());
+    globalThis_ = globalThis;
 
-  ObjectDefineProperties(finalDenoNs, {
-    pid: core.propGetterOnly(opPid),
-    noColor: core.propGetterOnly(() => op_bootstrap_no_color()),
-    args: core.propGetterOnly(opArgs),
-    // TODO(kt3k): Remove this export at v2
-    // See https://github.com/denoland/deno/issues/9294
-    customInspect: {
-      get() {
-        warnOnDeprecatedApi(
-          "Deno.customInspect",
-          new Error().stack,
-          'Use `Symbol.for("Deno.customInspect")` instead.',
-        );
-        return customInspect;
-      },
-    },
-  });
-  // Setup `Deno` global - we're actually overriding already
-  // existing global `Deno` with `Deno` namespace from "./deno.ts".
-  ObjectDefineProperty(globalThis, "Deno", core.propReadOnly(finalDenoNs));
+    // Remove bootstrapping data from the global scope
+    delete globalThis.__bootstrap;
+    delete globalThis.bootstrap;
+    hasBootstrapped = true;
 
-  const workerMetadata = maybeWorkerMetadata
-    ? messagePort.deserializeJsMessageData(maybeWorkerMetadata)
-    : undefined;
+    exposeUnstableFeaturesForWindowOrWorkerGlobalScope({
+      unstableFlag,
+      unstableFeatures,
+    });
+    ObjectDefineProperties(globalThis, workerRuntimeGlobalProperties);
+    ObjectDefineProperties(globalThis, {
+      name: core.propWritable(name),
+      // TODO(bartlomieju): should be readonly?
+      close: core.propNonEnumerable(workerClose),
+      postMessage: core.propWritable(postMessage),
+    });
+    if (enableTestingFeaturesFlag) {
+      ObjectDefineProperty(
+        globalThis,
+        "importScripts",
+        core.propWritable(importScripts),
+      );
+    }
+    ObjectSetPrototypeOf(globalThis, DedicatedWorkerGlobalScope.prototype);
 
-  if (nodeBootstrap) {
-    nodeBootstrap(
-      hasNodeModulesDir,
-      argv0,
-      /* runningOnMainThread */ false,
-      workerId,
-      workerMetadata,
+    const consoleFromDeno = globalThis.console;
+    core.wrapConsole(consoleFromDeno, core.v8Console);
+
+    event.defineEventHandler(self, "message");
+    event.defineEventHandler(self, "error", undefined, true);
+
+    // `Deno.exit()` is an alias to `self.close()`. Setting and exit
+    // code using an op in worker context is a no-op.
+    os.setExitHandler((_exitCode) => {
+      workerClose();
+    });
+
+    runtimeStart(
+      denoVersion,
+      v8Version,
+      tsVersion,
+      target,
+      internalName ?? name,
     );
+
+    location.setLocationHref(location_);
+
+    globalThis.pollForMessages = pollForMessages;
+    globalThis.hasMessageEventListener = hasMessageEventListener;
+
+    // TODO(bartlomieju): deprecate --unstable
+    if (unstableFlag) {
+      ObjectAssign(finalDenoNs, denoNsUnstable);
+    } else {
+      for (let i = 0; i <= unstableFeatures.length; i++) {
+        const id = unstableFeatures[i];
+        ObjectAssign(finalDenoNs, denoNsUnstableById[id]);
+      }
+    }
+
+    // Not available in workers
+    delete finalDenoNs.mainModule;
+
+    if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.unsafeProto)) {
+      // Removes the `__proto__` for security reasons.
+      // https://tc39.es/ecma262/#sec-get-object.prototype.__proto__
+      delete Object.prototype.__proto__;
+    }
+
+    if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.temporal)) {
+      // Removes the `Temporal` API.
+      delete globalThis.Temporal;
+      delete globalThis.Date.prototype.toTemporalInstant;
+    }
+
+    // Setup `Deno` global - we're actually overriding already existing global
+    // `Deno` with `Deno` namespace from "./deno.ts".
+    ObjectDefineProperty(globalThis, "Deno", core.propReadOnly(finalDenoNs));
+
+    const workerMetadata = maybeWorkerMetadata
+      ? messagePort.deserializeJsMessageData(maybeWorkerMetadata)
+      : undefined;
+
+    if (nodeBootstrap) {
+      nodeBootstrap(
+        hasNodeModulesDir,
+        argv0,
+        /* runningOnMainThread */ false,
+        workerId,
+        workerMetadata,
+      );
+    }
+  } else {
+    // Warmup
+    return;
   }
 }
 
+const nodeBootstrap = globalThis.nodeBootstrap;
+delete globalThis.nodeBootstrap;
+
 globalThis.bootstrap = {
   mainRuntime: bootstrapMainRuntime,
   workerRuntime: bootstrapWorkerRuntime,
 };
+
+event.setEventTargetData(globalThis);
+event.saveGlobalThisReference(globalThis);
+event.defineEventHandler(globalThis, "unhandledrejection");
+
+// Nothing listens to this, but it warms up the code paths for event dispatch
+(new event.EventTarget()).dispatchEvent(new Event("warmup"));
+
+removeImportedOps();
+
+// Run the warmup path through node and runtime/worker bootstrap functions
+bootstrapMainRuntime(undefined, true);
+bootstrapWorkerRuntime(
+  undefined,
+  undefined,
+  undefined,
+  undefined,
+  undefined,
+  true,
+);
+nodeBootstrap(undefined, undefined, undefined, undefined, undefined, true);