miniflare 0.0.0-e51304ca0 → 0.0.0-e5dbedd78

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/dist/src/index.js CHANGED
@@ -628,7 +628,7 @@ __export(src_exports, {
628
628
  deserialiseRegExps: () => deserialiseRegExps,
629
629
  deserialiseSiteRegExps: () => deserialiseSiteRegExps,
630
630
  encodeSitesKey: () => encodeSitesKey,
631
- fetch: () => fetch3,
631
+ fetch: () => fetch4,
632
632
  formatZodError: () => formatZodError,
633
633
  getAccessibleHosts: () => getAccessibleHosts,
634
634
  getAssetsBindingsNames: () => getAssetsBindingsNames,
@@ -770,7 +770,7 @@ function zod_worker_default() {
770
770
  }
771
771
 
772
772
  // src/index.ts
773
- var import_ws3 = require("ws");
773
+ var import_ws5 = require("ws");
774
774
  var import_zod23 = require("zod");
775
775
 
776
776
  // src/cf.ts
@@ -2359,7 +2359,7 @@ function headersFromIncomingRequest(req) {
2359
2359
  );
2360
2360
  return new undici.Headers(Object.fromEntries(entries));
2361
2361
  }
2362
- async function fetch3(input, init2) {
2362
+ async function fetch4(input, init2) {
2363
2363
  const requestInit = init2;
2364
2364
  const request = new Request(input, requestInit);
2365
2365
  if (request.method === "GET" && request.headers.get("upgrade") === "websocket") {
@@ -2558,8 +2558,8 @@ function valueOrFile(value, filePath) {
2558
2558
  var import_os = require("os");
2559
2559
  function getAccessibleHosts(ipv4Only = false) {
2560
2560
  const hosts = [];
2561
- Object.values((0, import_os.networkInterfaces)()).forEach((net2) => {
2562
- net2?.forEach(({ family, address }) => {
2561
+ Object.values((0, import_os.networkInterfaces)()).forEach((net3) => {
2562
+ net3?.forEach(({ family, address }) => {
2563
2563
  if (family === "IPv4" || family === 4) {
2564
2564
  hosts.push(address);
2565
2565
  } else if (!ipv4Only) {
@@ -11884,6 +11884,7 @@ var CoreOptionsSchemaInput = import_zod12.z.intersection(
11884
11884
  rootPath: UnusableStringSchema.optional(),
11885
11885
  compatibilityDate: import_zod12.z.string().optional(),
11886
11886
  compatibilityFlags: import_zod12.z.string().array().optional(),
11887
+ unsafeInspectorProxy: import_zod12.z.boolean().optional(),
11887
11888
  routes: import_zod12.z.string().array().optional(),
11888
11889
  bindings: import_zod12.z.record(JsonSchema).optional(),
11889
11890
  wasmBindings: import_zod12.z.record(import_zod12.z.union([PathSchema, import_zod12.z.instanceof(Uint8Array)])).optional(),
@@ -11902,8 +11903,7 @@ var CoreOptionsSchemaInput = import_zod12.z.intersection(
11902
11903
  (If there are assets but we're not using vitest, the miniflare entry worker can point directly to
11903
11904
  Router Worker)
11904
11905
  */
11905
- hasAssetsAndIsVitest: import_zod12.z.boolean().optional(),
11906
- unsafeEnableAssetsRpc: import_zod12.z.boolean().optional()
11906
+ hasAssetsAndIsVitest: import_zod12.z.boolean().optional()
11907
11907
  })
11908
11908
  );
11909
11909
  var CoreOptionsSchema = CoreOptionsSchemaInput.transform((value) => {
@@ -11916,7 +11916,7 @@ var CoreOptionsSchema = CoreOptionsSchemaInput.transform((value) => {
11916
11916
  );
11917
11917
  }
11918
11918
  value.fetchMock = void 0;
11919
- value.outboundService = (req) => fetch3(req, { dispatcher: fetchMock });
11919
+ value.outboundService = (req) => fetch4(req, { dispatcher: fetchMock });
11920
11920
  }
11921
11921
  return value;
11922
11922
  });
@@ -11943,8 +11943,7 @@ var CoreSharedOptionsSchema = import_zod12.z.object({
11943
11943
  unsafeProxySharedSecret: import_zod12.z.string().optional(),
11944
11944
  unsafeModuleFallbackService: ServiceFetchSchema.optional(),
11945
11945
  // Keep blobs when deleting/overwriting keys, required for stacked storage
11946
- unsafeStickyBlobs: import_zod12.z.boolean().optional(),
11947
- unsafeEnableAssetsRpc: import_zod12.z.boolean().optional()
11946
+ unsafeStickyBlobs: import_zod12.z.boolean().optional()
11948
11947
  });
11949
11948
  var CORE_PLUGIN_NAME2 = "core";
11950
11949
  var LIVE_RELOAD_SCRIPT_TEMPLATE = (port) => `<script defer type="application/javascript">
@@ -11970,7 +11969,7 @@ var SCRIPT_CUSTOM_SERVICE = `addEventListener("fetch", (event) => {
11970
11969
  request.headers.set("${CoreHeaders.ORIGINAL_URL}", request.url);
11971
11970
  event.respondWith(${CoreBindings.SERVICE_LOOPBACK}.fetch(request));
11972
11971
  })`;
11973
- function getCustomServiceDesignator(refererName, workerIndex, kind, name, service, hasAssetsAndIsVitest = false, unsafeEnableAssetsRpc = false) {
11972
+ function getCustomServiceDesignator(refererName, workerIndex, kind, name, service, hasAssetsAndIsVitest = false) {
11974
11973
  let serviceName;
11975
11974
  let entrypoint;
11976
11975
  if (typeof service === "function") {
@@ -11987,7 +11986,7 @@ function getCustomServiceDesignator(refererName, workerIndex, kind, name, servic
11987
11986
  serviceName = getBuiltinServiceName(workerIndex, kind, name);
11988
11987
  }
11989
11988
  } else if (service === kCurrentWorker) {
11990
- serviceName = hasAssetsAndIsVitest ? unsafeEnableAssetsRpc ? `${RPC_PROXY_SERVICE_NAME}:${refererName}` : `${ROUTER_SERVICE_NAME}:${refererName}` : getUserServiceName(refererName);
11989
+ serviceName = hasAssetsAndIsVitest ? `${RPC_PROXY_SERVICE_NAME}:${refererName}` : getUserServiceName(refererName);
11991
11990
  } else {
11992
11991
  serviceName = getUserServiceName(service);
11993
11992
  }
@@ -12108,8 +12107,7 @@ var CORE_PLUGIN = {
12108
12107
  "#" /* UNKNOWN */,
12109
12108
  name,
12110
12109
  service,
12111
- options.hasAssetsAndIsVitest,
12112
- options.unsafeEnableAssetsRpc
12110
+ options.hasAssetsAndIsVitest
12113
12111
  )
12114
12112
  };
12115
12113
  })
@@ -12297,7 +12295,10 @@ Ensure ${stringName} doesn't include unbundled \`import\`s.`
12297
12295
  worker: {
12298
12296
  ...workerScript,
12299
12297
  compatibilityDate,
12300
- compatibilityFlags: options.compatibilityFlags,
12298
+ compatibilityFlags: options.compatibilityFlags?.filter(
12299
+ // This can be removed once https://github.com/cloudflare/workerd/pull/3745 lands.
12300
+ (flag) => !flag.startsWith("assets_")
12301
+ ),
12301
12302
  bindings: workerBindings,
12302
12303
  durableObjectNamespaces: classNamesEntries.map(
12303
12304
  ([
@@ -12332,8 +12333,7 @@ Ensure ${stringName} doesn't include unbundled \`import\`s.`
12332
12333
  "$" /* KNOWN */,
12333
12334
  CUSTOM_SERVICE_KNOWN_OUTBOUND,
12334
12335
  options.outboundService,
12335
- options.hasAssetsAndIsVitest,
12336
- options.unsafeEnableAssetsRpc
12336
+ options.hasAssetsAndIsVitest
12337
12337
  ),
12338
12338
  cacheApiOutbound: { name: getCacheServiceName(workerIndex) },
12339
12339
  moduleFallback: options.unsafeUseModuleFallbackService && sharedOptions.unsafeModuleFallbackService !== void 0 ? `localhost:${loopbackPort}` : void 0
@@ -12515,8 +12515,13 @@ var AssetsOptionsSchema = import_zod13.z.object({
12515
12515
  directory: PathSchema,
12516
12516
  binding: import_zod13.z.string().optional(),
12517
12517
  routerConfig: RouterConfigSchema.optional(),
12518
- assetConfig: AssetConfigSchema.optional()
12519
- }).optional()
12518
+ assetConfig: AssetConfigSchema.omit({
12519
+ compatibility_date: true,
12520
+ compatibility_flags: true
12521
+ }).optional()
12522
+ }).optional(),
12523
+ compatibilityDate: import_zod13.z.string().optional(),
12524
+ compatibilityFlags: import_zod13.z.string().array().optional()
12520
12525
  });
12521
12526
 
12522
12527
  // src/plugins/assets/index.ts
@@ -12544,7 +12549,7 @@ var ASSETS_PLUGIN = {
12544
12549
  [options.assets.binding]: new ProxyNodeBinding()
12545
12550
  };
12546
12551
  },
12547
- async getServices({ options, unsafeEnableAssetsRpc }) {
12552
+ async getServices({ options }) {
12548
12553
  if (!options.assets) {
12549
12554
  return [];
12550
12555
  }
@@ -12591,6 +12596,8 @@ var ASSETS_PLUGIN = {
12591
12596
  );
12592
12597
  }
12593
12598
  const assetConfig = {
12599
+ compatibility_date: options.compatibilityDate,
12600
+ compatibility_flags: options.compatibilityFlags,
12594
12601
  ...options.assets.assetConfig,
12595
12602
  redirects: parsedRedirects,
12596
12603
  headers: parsedHeaders
@@ -12679,36 +12686,39 @@ var ASSETS_PLUGIN = {
12679
12686
  ]
12680
12687
  }
12681
12688
  };
12682
- const services = [
12689
+ const assetsProxyService = {
12690
+ name: `${RPC_PROXY_SERVICE_NAME}:${id}`,
12691
+ worker: {
12692
+ compatibilityDate: "2024-08-01",
12693
+ modules: [
12694
+ {
12695
+ name: "assets-proxy-worker.mjs",
12696
+ esModule: rpc_proxy_worker_default()
12697
+ }
12698
+ ],
12699
+ bindings: [
12700
+ {
12701
+ name: "ROUTER_WORKER",
12702
+ service: {
12703
+ name: `${ROUTER_SERVICE_NAME}:${id}`
12704
+ }
12705
+ },
12706
+ {
12707
+ name: "USER_WORKER",
12708
+ service: {
12709
+ name: getUserServiceName(id)
12710
+ }
12711
+ }
12712
+ ]
12713
+ }
12714
+ };
12715
+ return [
12683
12716
  storageService,
12684
12717
  namespaceService,
12685
12718
  assetService,
12686
- routerService
12719
+ routerService,
12720
+ assetsProxyService
12687
12721
  ];
12688
- if (unsafeEnableAssetsRpc) {
12689
- const assetsProxyService = {
12690
- name: `${RPC_PROXY_SERVICE_NAME}:${id}`,
12691
- worker: {
12692
- compatibilityDate: "2024-08-01",
12693
- modules: [
12694
- {
12695
- name: "assets-proxy-worker.mjs",
12696
- esModule: rpc_proxy_worker_default()
12697
- }
12698
- ],
12699
- bindings: [
12700
- {
12701
- name: "ROUTER_WORKER",
12702
- service: {
12703
- name: `${ROUTER_SERVICE_NAME}:${id}`
12704
- }
12705
- }
12706
- ]
12707
- }
12708
- };
12709
- services.push(assetsProxyService);
12710
- }
12711
- return services;
12712
12722
  }
12713
12723
  };
12714
12724
  var buildAssetManifest = async (dir) => {
@@ -12962,9 +12972,13 @@ var HYPERDRIVE_PLUGIN_NAME = "hyperdrive";
12962
12972
  function hasPostgresProtocol(url20) {
12963
12973
  return url20.protocol === "postgresql:" || url20.protocol === "postgres:";
12964
12974
  }
12975
+ function hasMysqlProtocol(url20) {
12976
+ return url20.protocol === "mysql:";
12977
+ }
12965
12978
  function getPort(url20) {
12966
12979
  if (url20.port !== "") return url20.port;
12967
12980
  if (hasPostgresProtocol(url20)) return "5432";
12981
+ if (hasMysqlProtocol(url20)) return "3306";
12968
12982
  import_node_assert3.default.fail(`Expected known protocol, got ${url20.protocol}`);
12969
12983
  }
12970
12984
  var HyperdriveSchema = import_zod15.z.union([import_zod15.z.string().url(), import_zod15.z.instanceof(URL)]).transform((url20, ctx) => {
@@ -12972,12 +12986,12 @@ var HyperdriveSchema = import_zod15.z.union([import_zod15.z.string().url(), impo
12972
12986
  if (url20.protocol === "") {
12973
12987
  ctx.addIssue({
12974
12988
  code: import_zod15.z.ZodIssueCode.custom,
12975
- message: "You must specify the database protocol - e.g. 'postgresql'."
12989
+ message: "You must specify the database protocol - e.g. 'postgresql'/'mysql'."
12976
12990
  });
12977
- } else if (!hasPostgresProtocol(url20)) {
12991
+ } else if (!hasPostgresProtocol(url20) && !hasMysqlProtocol(url20)) {
12978
12992
  ctx.addIssue({
12979
12993
  code: import_zod15.z.ZodIssueCode.custom,
12980
- message: "Only PostgreSQL or PostgreSQL compatible databases are currently supported."
12994
+ message: "Only PostgreSQL-compatible or MySQL-compatible databases are currently supported."
12981
12995
  });
12982
12996
  }
12983
12997
  if (url20.host === "") {
@@ -13850,6 +13864,415 @@ var PLUGINS = {
13850
13864
  };
13851
13865
  var PLUGIN_ENTRIES = Object.entries(PLUGINS);
13852
13866
 
13867
+ // src/plugins/core/inspector-proxy/inspector-proxy-controller.ts
13868
+ var import_node_crypto2 = __toESM(require("node:crypto"));
13869
+ var import_node_http = require("node:http");
13870
+
13871
+ // ../../node_modules/.pnpm/get-port@7.1.0/node_modules/get-port/index.js
13872
+ var import_node_net = __toESM(require("node:net"), 1);
13873
+ var import_node_os = __toESM(require("node:os"), 1);
13874
+ var Locked = class extends Error {
13875
+ constructor(port) {
13876
+ super(`${port} is locked`);
13877
+ }
13878
+ };
13879
+ var lockedPorts = {
13880
+ old: /* @__PURE__ */ new Set(),
13881
+ young: /* @__PURE__ */ new Set()
13882
+ };
13883
+ var releaseOldLockedPortsIntervalMs = 1e3 * 15;
13884
+ var timeout;
13885
+ var getLocalHosts = () => {
13886
+ const interfaces = import_node_os.default.networkInterfaces();
13887
+ const results = /* @__PURE__ */ new Set([void 0, "0.0.0.0"]);
13888
+ for (const _interface of Object.values(interfaces)) {
13889
+ for (const config of _interface) {
13890
+ results.add(config.address);
13891
+ }
13892
+ }
13893
+ return results;
13894
+ };
13895
+ var checkAvailablePort = (options) => new Promise((resolve2, reject) => {
13896
+ const server = import_node_net.default.createServer();
13897
+ server.unref();
13898
+ server.on("error", reject);
13899
+ server.listen(options, () => {
13900
+ const { port } = server.address();
13901
+ server.close(() => {
13902
+ resolve2(port);
13903
+ });
13904
+ });
13905
+ });
13906
+ var getAvailablePort = async (options, hosts) => {
13907
+ if (options.host || options.port === 0) {
13908
+ return checkAvailablePort(options);
13909
+ }
13910
+ for (const host of hosts) {
13911
+ try {
13912
+ await checkAvailablePort({ port: options.port, host });
13913
+ } catch (error) {
13914
+ if (!["EADDRNOTAVAIL", "EINVAL"].includes(error.code)) {
13915
+ throw error;
13916
+ }
13917
+ }
13918
+ }
13919
+ return options.port;
13920
+ };
13921
+ var portCheckSequence = function* (ports) {
13922
+ if (ports) {
13923
+ yield* ports;
13924
+ }
13925
+ yield 0;
13926
+ };
13927
+ async function getPorts(options) {
13928
+ let ports;
13929
+ let exclude = /* @__PURE__ */ new Set();
13930
+ if (options) {
13931
+ if (options.port) {
13932
+ ports = typeof options.port === "number" ? [options.port] : options.port;
13933
+ }
13934
+ if (options.exclude) {
13935
+ const excludeIterable = options.exclude;
13936
+ if (typeof excludeIterable[Symbol.iterator] !== "function") {
13937
+ throw new TypeError("The `exclude` option must be an iterable.");
13938
+ }
13939
+ for (const element of excludeIterable) {
13940
+ if (typeof element !== "number") {
13941
+ throw new TypeError("Each item in the `exclude` option must be a number corresponding to the port you want excluded.");
13942
+ }
13943
+ if (!Number.isSafeInteger(element)) {
13944
+ throw new TypeError(`Number ${element} in the exclude option is not a safe integer and can't be used`);
13945
+ }
13946
+ }
13947
+ exclude = new Set(excludeIterable);
13948
+ }
13949
+ }
13950
+ if (timeout === void 0) {
13951
+ timeout = setTimeout(() => {
13952
+ timeout = void 0;
13953
+ lockedPorts.old = lockedPorts.young;
13954
+ lockedPorts.young = /* @__PURE__ */ new Set();
13955
+ }, releaseOldLockedPortsIntervalMs);
13956
+ if (timeout.unref) {
13957
+ timeout.unref();
13958
+ }
13959
+ }
13960
+ const hosts = getLocalHosts();
13961
+ for (const port of portCheckSequence(ports)) {
13962
+ try {
13963
+ if (exclude.has(port)) {
13964
+ continue;
13965
+ }
13966
+ let availablePort = await getAvailablePort({ ...options, port }, hosts);
13967
+ while (lockedPorts.old.has(availablePort) || lockedPorts.young.has(availablePort)) {
13968
+ if (port !== 0) {
13969
+ throw new Locked(port);
13970
+ }
13971
+ availablePort = await getAvailablePort({ ...options, port }, hosts);
13972
+ }
13973
+ lockedPorts.young.add(availablePort);
13974
+ return availablePort;
13975
+ } catch (error) {
13976
+ if (!["EADDRINUSE", "EACCES"].includes(error.code) && !(error instanceof Locked)) {
13977
+ throw error;
13978
+ }
13979
+ }
13980
+ }
13981
+ throw new Error("No available ports found");
13982
+ }
13983
+
13984
+ // src/plugins/core/inspector-proxy/inspector-proxy-controller.ts
13985
+ var import_ws4 = __toESM(require("ws"));
13986
+
13987
+ // package.json
13988
+ var version = "0.0.0-e5dbedd78";
13989
+
13990
+ // src/plugins/core/inspector-proxy/inspector-proxy.ts
13991
+ var import_node_assert4 = __toESM(require("node:assert"));
13992
+ var import_ws3 = __toESM(require("ws"));
13993
+
13994
+ // src/plugins/core/inspector-proxy/devtools.ts
13995
+ function isDevToolsEvent(event, name) {
13996
+ return typeof event === "object" && event !== null && "method" in event && event.method === name;
13997
+ }
13998
+
13999
+ // src/plugins/core/inspector-proxy/inspector-proxy.ts
14000
+ var InspectorProxy = class {
14001
+ #workerName;
14002
+ #runtimeWs;
14003
+ #devtoolsWs;
14004
+ #devtoolsHaveFileSystemAccess = false;
14005
+ constructor(workerName, runtimeWs) {
14006
+ this.#workerName = workerName;
14007
+ this.#runtimeWs = runtimeWs;
14008
+ this.#runtimeWs.once("open", () => this.#handleRuntimeWebSocketOpen());
14009
+ }
14010
+ get workerName() {
14011
+ return this.#workerName;
14012
+ }
14013
+ get path() {
14014
+ return `/${this.#workerName}`;
14015
+ }
14016
+ onDevtoolsConnected(devtoolsWs, devtoolsHaveFileSystemAccess) {
14017
+ if (this.#devtoolsWs) {
14018
+ devtoolsWs.close(
14019
+ 1013,
14020
+ "Too many clients; only one can be connected at a time"
14021
+ );
14022
+ return;
14023
+ }
14024
+ this.#devtoolsWs = devtoolsWs;
14025
+ this.#devtoolsHaveFileSystemAccess = devtoolsHaveFileSystemAccess;
14026
+ (0, import_node_assert4.default)(this.#devtoolsWs?.readyState === import_ws3.default.OPEN);
14027
+ this.#devtoolsWs.on("error", console.error);
14028
+ this.#devtoolsWs.once("close", () => {
14029
+ if (this.#runtimeWs?.OPEN) {
14030
+ this.#sendMessageToRuntime({
14031
+ method: "Debugger.disable",
14032
+ id: this.#nextCounter()
14033
+ });
14034
+ }
14035
+ this.#devtoolsWs = void 0;
14036
+ });
14037
+ this.#devtoolsWs.on("message", (data) => {
14038
+ const message = JSON.parse(data.toString());
14039
+ (0, import_node_assert4.default)(this.#runtimeWs?.OPEN);
14040
+ this.#sendMessageToRuntime(message);
14041
+ });
14042
+ }
14043
+ #runtimeMessageCounter = 1e8;
14044
+ #nextCounter() {
14045
+ return ++this.#runtimeMessageCounter;
14046
+ }
14047
+ #runtimeKeepAliveInterval;
14048
+ #handleRuntimeWebSocketOpen() {
14049
+ (0, import_node_assert4.default)(this.#runtimeWs?.OPEN);
14050
+ this.#runtimeWs.on("message", (data) => {
14051
+ const message = JSON.parse(data.toString());
14052
+ if (!this.#devtoolsWs) {
14053
+ return;
14054
+ }
14055
+ if (isDevToolsEvent(message, "Debugger.scriptParsed")) {
14056
+ return this.#handleRuntimeScriptParsed(message);
14057
+ }
14058
+ return this.#sendMessageToDevtools(message);
14059
+ });
14060
+ clearInterval(this.#runtimeKeepAliveInterval);
14061
+ this.#runtimeKeepAliveInterval = setInterval(() => {
14062
+ if (this.#runtimeWs?.OPEN) {
14063
+ this.#sendMessageToRuntime({
14064
+ method: "Runtime.getIsolateId",
14065
+ id: this.#nextCounter()
14066
+ });
14067
+ }
14068
+ }, 1e4);
14069
+ }
14070
+ #handleRuntimeScriptParsed(message) {
14071
+ if (!this.#devtoolsHaveFileSystemAccess && message.params.sourceMapURL !== void 0 && // Don't try to find a sourcemap for e.g. node-internal: scripts
14072
+ message.params.url.startsWith("file:")) {
14073
+ const url20 = new URL(message.params.sourceMapURL, message.params.url);
14074
+ if (url20.protocol === "file:") {
14075
+ message.params.sourceMapURL = url20.href.replace(
14076
+ "file:",
14077
+ "wrangler-file:"
14078
+ );
14079
+ }
14080
+ }
14081
+ return this.#sendMessageToDevtools(message);
14082
+ }
14083
+ #sendMessageToDevtools(message) {
14084
+ (0, import_node_assert4.default)(this.#devtoolsWs);
14085
+ if (!this.#devtoolsWs.OPEN) {
14086
+ this.#devtoolsWs.once(
14087
+ "open",
14088
+ () => this.#devtoolsWs?.send(JSON.stringify(message))
14089
+ );
14090
+ return;
14091
+ }
14092
+ this.#devtoolsWs.send(JSON.stringify(message));
14093
+ }
14094
+ #sendMessageToRuntime(message) {
14095
+ (0, import_node_assert4.default)(this.#runtimeWs?.OPEN);
14096
+ this.#runtimeWs.send(JSON.stringify(message));
14097
+ }
14098
+ async dispose() {
14099
+ clearInterval(this.#runtimeKeepAliveInterval);
14100
+ this.#devtoolsWs?.close();
14101
+ }
14102
+ };
14103
+
14104
+ // src/plugins/core/inspector-proxy/inspector-proxy-controller.ts
14105
+ var InspectorProxyController = class {
14106
+ constructor(userInspectorPort, log, workerNamesToProxy) {
14107
+ this.log = log;
14108
+ this.workerNamesToProxy = workerNamesToProxy;
14109
+ this.#inspectorPort = userInspectorPort !== 0 ? userInspectorPort : getPorts();
14110
+ this.#server = this.#initializeServer();
14111
+ this.#runtimeConnectionEstablished = new DeferredPromise();
14112
+ }
14113
+ #runtimeConnectionEstablished;
14114
+ #proxies = [];
14115
+ #server;
14116
+ #inspectorPort;
14117
+ async #initializeServer() {
14118
+ const server = (0, import_node_http.createServer)(async (req, res) => {
14119
+ const maybeJson = await this.#handleDevToolsJsonRequest(
14120
+ req.headers.host ?? "localhost",
14121
+ req.url ?? "/"
14122
+ );
14123
+ if (maybeJson !== null) {
14124
+ res.setHeader("Content-Type", "application/json");
14125
+ res.end(JSON.stringify(maybeJson));
14126
+ return;
14127
+ }
14128
+ res.statusCode = 404;
14129
+ res.end(null);
14130
+ });
14131
+ this.#initializeWebSocketServer(server);
14132
+ server.listen(await this.#inspectorPort);
14133
+ return server;
14134
+ }
14135
+ #initializeWebSocketServer(server) {
14136
+ const devtoolsWebSocketServer = new import_ws4.WebSocketServer({ server });
14137
+ devtoolsWebSocketServer.on("connection", (devtoolsWs, upgradeRequest) => {
14138
+ const validationError = this.#validateDevToolsWebSocketUpgradeRequest(upgradeRequest);
14139
+ if (validationError !== null) {
14140
+ devtoolsWs.close();
14141
+ return;
14142
+ }
14143
+ const proxy = this.#proxies.find(
14144
+ ({ path: path30 }) => upgradeRequest.url === path30
14145
+ );
14146
+ if (!proxy) {
14147
+ this.log.warn(
14148
+ `Warning: An inspector connection was requested for the ${upgradeRequest.url} path but no such inspector exists`
14149
+ );
14150
+ devtoolsWs.close();
14151
+ return;
14152
+ }
14153
+ proxy.onDevtoolsConnected(
14154
+ devtoolsWs,
14155
+ this.#checkIfDevtoolsHaveFileSystemAccess(upgradeRequest)
14156
+ );
14157
+ });
14158
+ }
14159
+ #validateDevToolsWebSocketUpgradeRequest(req) {
14160
+ const hostHeader = req.headers.host;
14161
+ if (hostHeader == null) return { statusText: null, status: 400 };
14162
+ try {
14163
+ const host = new URL(`http://${hostHeader}`);
14164
+ if (!ALLOWED_HOST_HOSTNAMES.includes(host.hostname)) {
14165
+ return { statusText: "Disallowed `Host` header", status: 401 };
14166
+ }
14167
+ } catch {
14168
+ return { statusText: "Expected `Host` header", status: 400 };
14169
+ }
14170
+ let originHeader = req.headers.origin;
14171
+ if (!originHeader && !req.headers["user-agent"]) {
14172
+ originHeader = "http://localhost";
14173
+ }
14174
+ if (!originHeader) {
14175
+ return { statusText: "Expected `Origin` header", status: 400 };
14176
+ }
14177
+ try {
14178
+ const origin = new URL(originHeader);
14179
+ const allowed = ALLOWED_ORIGIN_HOSTNAMES.some((rule) => {
14180
+ if (typeof rule === "string") return origin.hostname === rule;
14181
+ else return rule.test(origin.hostname);
14182
+ });
14183
+ if (!allowed) {
14184
+ return { statusText: "Disallowed `Origin` header", status: 401 };
14185
+ }
14186
+ } catch {
14187
+ return { statusText: "Expected `Origin` header", status: 400 };
14188
+ }
14189
+ return null;
14190
+ }
14191
+ #checkIfDevtoolsHaveFileSystemAccess(req) {
14192
+ const userAgent = req.headers["user-agent"] ?? "";
14193
+ const hasFileSystemAccess = !/mozilla/i.test(userAgent);
14194
+ return hasFileSystemAccess;
14195
+ }
14196
+ #inspectorId = import_node_crypto2.default.randomUUID();
14197
+ async #handleDevToolsJsonRequest(host, path30) {
14198
+ if (path30 === "/json/version") {
14199
+ return {
14200
+ Browser: `miniflare/v${version}`,
14201
+ // TODO: (someday): The DevTools protocol should match that of workerd.
14202
+ // This could be exposed by the preview API.
14203
+ "Protocol-Version": "1.3"
14204
+ };
14205
+ }
14206
+ if (path30 === "/json" || path30 === "/json/list") {
14207
+ return this.#proxies.map(({ workerName }) => {
14208
+ const localHost = `${host}/${workerName}`;
14209
+ const devtoolsFrontendUrl = `https://devtools.devprod.cloudflare.dev/js_app?theme=systemPreferred&debugger=true&ws=${localHost}`;
14210
+ return {
14211
+ id: `${this.#inspectorId}-${workerName}`,
14212
+ type: "node",
14213
+ // TODO: can we specify different type?
14214
+ description: "workers",
14215
+ webSocketDebuggerUrl: `ws://${localHost}`,
14216
+ devtoolsFrontendUrl,
14217
+ devtoolsFrontendUrlCompat: devtoolsFrontendUrl,
14218
+ // Below are fields that are visible in the DevTools UI.
14219
+ title: workerName.length === 0 || this.#proxies.length === 1 ? `Cloudflare Worker` : `Cloudflare Worker: ${workerName}`,
14220
+ faviconUrl: "https://workers.cloudflare.com/favicon.ico"
14221
+ // url: "http://" + localHost, // looks unnecessary
14222
+ };
14223
+ });
14224
+ }
14225
+ return null;
14226
+ }
14227
+ async getInspectorURL() {
14228
+ return getWebsocketURL(await this.#inspectorPort);
14229
+ }
14230
+ async updateConnection(runtimeInspectorPort) {
14231
+ const workerdInspectorJson = await fetch(
14232
+ `http://127.0.0.1:${runtimeInspectorPort}/json`
14233
+ ).then((resp) => resp.json());
14234
+ this.#proxies = workerdInspectorJson.map(({ id }) => {
14235
+ if (!id.startsWith("core:user:")) {
14236
+ return;
14237
+ }
14238
+ const workerName = id.replace(/^core:user:/, "");
14239
+ if (!this.workerNamesToProxy.has(workerName)) {
14240
+ return;
14241
+ }
14242
+ return new InspectorProxy(
14243
+ workerName,
14244
+ new import_ws4.default(`ws://127.0.0.1:${runtimeInspectorPort}/${id}`)
14245
+ );
14246
+ }).filter(Boolean);
14247
+ this.#runtimeConnectionEstablished.resolve();
14248
+ }
14249
+ async #waitForReady() {
14250
+ await this.#runtimeConnectionEstablished;
14251
+ }
14252
+ get ready() {
14253
+ return this.#waitForReady();
14254
+ }
14255
+ async dispose() {
14256
+ await Promise.all(this.#proxies.map((proxy) => proxy.dispose()));
14257
+ const server = await this.#server;
14258
+ return new Promise((resolve2, reject) => {
14259
+ server.close((err) => err ? reject(err) : resolve2());
14260
+ });
14261
+ }
14262
+ };
14263
+ function getWebsocketURL(port) {
14264
+ return new URL(`ws://127.0.0.1:${port}`);
14265
+ }
14266
+ var ALLOWED_HOST_HOSTNAMES = ["127.0.0.1", "[::1]", "localhost"];
14267
+ var ALLOWED_ORIGIN_HOSTNAMES = [
14268
+ "devtools.devprod.cloudflare.dev",
14269
+ "cloudflare-devtools.pages.dev",
14270
+ /^[a-z0-9]+\.cloudflare-devtools\.pages\.dev$/,
14271
+ "127.0.0.1",
14272
+ "[::1]",
14273
+ "localhost"
14274
+ ];
14275
+
13853
14276
  // src/shared/mime-types.ts
13854
14277
  var compressedByCloudflareFL = /* @__PURE__ */ new Set([
13855
14278
  // list copied from https://developers.cloudflare.com/speed/optimization/content/brotli/content-compression/#:~:text=If%20supported%20by%20visitors%E2%80%99%20web%20browsers%2C%20Cloudflare%20will%20return%20Gzip%20or%20Brotli%2Dencoded%20responses%20for%20the%20following%20content%20types%3A
@@ -14589,18 +15012,45 @@ var Miniflare2 = class _Miniflare {
14589
15012
  #liveReloadServer;
14590
15013
  #webSocketServer;
14591
15014
  #webSocketExtraHeaders;
15015
+ #maybeInspectorProxyController;
15016
+ #previousRuntimeInspectorPort;
14592
15017
  constructor(opts) {
14593
15018
  const [sharedOpts, workerOpts] = validateOptions(opts);
14594
15019
  this.#sharedOpts = sharedOpts;
14595
15020
  this.#workerOpts = workerOpts;
15021
+ const workerNamesToProxy = new Set(
15022
+ this.#workerOpts.filter(({ core: { unsafeInspectorProxy } }) => !!unsafeInspectorProxy).map((w) => w.core.name ?? "")
15023
+ );
15024
+ const enableInspectorProxy = workerNamesToProxy.size > 0;
15025
+ if (enableInspectorProxy) {
15026
+ if (this.#sharedOpts.core.inspectorPort === void 0) {
15027
+ throw new MiniflareCoreError(
15028
+ "ERR_MISSING_INSPECTOR_PROXY_PORT",
15029
+ "inspector proxy requested but without an inspectorPort specified"
15030
+ );
15031
+ }
15032
+ }
14596
15033
  if (maybeInstanceRegistry !== void 0) {
14597
15034
  const object = { name: "Miniflare", stack: "" };
14598
15035
  Error.captureStackTrace(object, _Miniflare);
14599
15036
  maybeInstanceRegistry.set(this, object.stack);
14600
15037
  }
14601
15038
  this.#log = this.#sharedOpts.core.log ?? new NoOpLog();
14602
- this.#liveReloadServer = new import_ws3.WebSocketServer({ noServer: true });
14603
- this.#webSocketServer = new import_ws3.WebSocketServer({
15039
+ if (enableInspectorProxy) {
15040
+ if (this.#sharedOpts.core.inspectorPort === void 0) {
15041
+ throw new MiniflareCoreError(
15042
+ "ERR_MISSING_INSPECTOR_PROXY_PORT",
15043
+ "inspector proxy requested but without an inspectorPort specified"
15044
+ );
15045
+ }
15046
+ this.#maybeInspectorProxyController = new InspectorProxyController(
15047
+ this.#sharedOpts.core.inspectorPort,
15048
+ this.#log,
15049
+ workerNamesToProxy
15050
+ );
15051
+ }
15052
+ this.#liveReloadServer = new import_ws5.WebSocketServer({ noServer: true });
15053
+ this.#webSocketServer = new import_ws5.WebSocketServer({
14604
15054
  noServer: true,
14605
15055
  // Disable automatic handling of `Sec-WebSocket-Protocol` header,
14606
15056
  // Cloudflare Workers require users to include this header themselves in
@@ -14911,14 +15361,13 @@ var Miniflare2 = class _Miniflare {
14911
15361
  );
14912
15362
  if (maybeAssetTargetService && !binding.service?.entrypoint) {
14913
15363
  (0, import_assert12.default)(binding.service?.name);
14914
- binding.service.name = this.#sharedOpts.core.unsafeEnableAssetsRpc ? `${RPC_PROXY_SERVICE_NAME}:${targetWorkerName}` : `${ROUTER_SERVICE_NAME}:${targetWorkerName}`;
15364
+ binding.service.name = `${RPC_PROXY_SERVICE_NAME}:${targetWorkerName}`;
14915
15365
  }
14916
15366
  }
14917
15367
  }
14918
15368
  }
14919
15369
  }
14920
15370
  const unsafeStickyBlobs = sharedOpts.core.unsafeStickyBlobs ?? false;
14921
- const unsafeEnableAssetsRpc = sharedOpts.core.unsafeEnableAssetsRpc ?? false;
14922
15371
  const unsafeEphemeralDurableObjects = workerOpts.core.unsafeEphemeralDurableObjects ?? false;
14923
15372
  const pluginServicesOptionsBase = {
14924
15373
  log: this.#log,
@@ -14933,8 +15382,7 @@ var Miniflare2 = class _Miniflare {
14933
15382
  durableObjectClassNames,
14934
15383
  unsafeEphemeralDurableObjects,
14935
15384
  queueProducers,
14936
- queueConsumers,
14937
- unsafeEnableAssetsRpc
15385
+ queueConsumers
14938
15386
  };
14939
15387
  for (const [key, plugin] of PLUGIN_ENTRIES) {
14940
15388
  const pluginServicesExtensions = await plugin.getServices({
@@ -14982,13 +15430,16 @@ var Miniflare2 = class _Miniflare {
14982
15430
  directSocket.host,
14983
15431
  directSocket.port
14984
15432
  );
15433
+ const service = workerOpts.assets.assets && entrypoint === "default" ? {
15434
+ name: `${RPC_PROXY_SERVICE_NAME}:${workerOpts.core.name}`
15435
+ } : {
15436
+ name: getUserServiceName(workerName),
15437
+ entrypoint: entrypoint === "default" ? void 0 : entrypoint
15438
+ };
14985
15439
  sockets.push({
14986
15440
  name,
14987
15441
  address,
14988
- service: {
14989
- name: getUserServiceName(workerName),
14990
- entrypoint: entrypoint === "default" ? void 0 : entrypoint
14991
- },
15442
+ service,
14992
15443
  http: {
14993
15444
  style: directSocket.proxy ? HttpOptions_Style.PROXY : void 0,
14994
15445
  cfBlobHeader: CoreHeaders.CF_BLOB,
@@ -15001,15 +15452,15 @@ var Miniflare2 = class _Miniflare {
15001
15452
  sharedOptions: sharedOpts.core,
15002
15453
  allWorkerRoutes,
15003
15454
  /*
15004
- * - if Workers + Assets project but NOT Vitest, point to Router Worker or
15005
- * Proxy Worker depending on whether experimental flag `unsafeEnableAssetsRpc`
15006
- * was set
15007
- * - if Vitest with assets, the self binding on the test runner will point to
15008
- * Router Worker
15455
+ * - if Workers + Assets project but NOT Vitest, the fallback Worker (see
15456
+ * `MINIFLARE_USER_FALLBACK`) should point to the (assets) RPC Proxy Worker
15457
+ * - if Vitest with assets, the fallback Worker should point to the Vitest
15458
+ * runner Worker, while the SELF binding on the test runner will point to
15459
+ * the (assets) RPC Proxy Worker
15009
15460
  */
15010
15461
  fallbackWorkerName: this.#workerOpts[0].assets.assets && !this.#workerOpts[0].core.name?.startsWith(
15011
15462
  "vitest-pool-workers-runner-"
15012
- ) ? this.#sharedOpts.core.unsafeEnableAssetsRpc ? `${RPC_PROXY_SERVICE_NAME}:${this.#workerOpts[0].core.name}` : `${ROUTER_SERVICE_NAME}:${this.#workerOpts[0].core.name}` : getUserServiceName(this.#workerOpts[0].core.name),
15463
+ ) ? `${RPC_PROXY_SERVICE_NAME}:${this.#workerOpts[0].core.name}` : getUserServiceName(this.#workerOpts[0].core.name),
15013
15464
  loopbackPort,
15014
15465
  log: this.#log,
15015
15466
  proxyBindings
@@ -15060,14 +15511,19 @@ var Miniflare2 = class _Miniflare {
15060
15511
  configuredHost,
15061
15512
  this.#sharedOpts.core.port
15062
15513
  );
15063
- let inspectorAddress;
15514
+ let runtimeInspectorAddress;
15064
15515
  if (this.#sharedOpts.core.inspectorPort !== void 0) {
15065
- inspectorAddress = this.#getSocketAddress(
15516
+ let runtimeInspectorPort = this.#sharedOpts.core.inspectorPort;
15517
+ if (this.#maybeInspectorProxyController !== void 0) {
15518
+ runtimeInspectorPort = 0;
15519
+ }
15520
+ runtimeInspectorAddress = this.#getSocketAddress(
15066
15521
  kInspectorSocket,
15067
- this.#previousSharedOpts?.core.inspectorPort,
15522
+ this.#previousRuntimeInspectorPort,
15068
15523
  "localhost",
15069
- this.#sharedOpts.core.inspectorPort
15524
+ runtimeInspectorPort
15070
15525
  );
15526
+ this.#previousRuntimeInspectorPort = runtimeInspectorPort;
15071
15527
  }
15072
15528
  const loopbackAddress = `${maybeGetLocallyAccessibleHost(configuredHost) ?? getURLSafeHost(configuredHost)}:${loopbackPort}`;
15073
15529
  const runtimeOpts = {
@@ -15075,7 +15531,7 @@ var Miniflare2 = class _Miniflare {
15075
15531
  entryAddress,
15076
15532
  loopbackAddress,
15077
15533
  requiredSockets,
15078
- inspectorAddress,
15534
+ inspectorAddress: runtimeInspectorAddress,
15079
15535
  verbose: this.#sharedOpts.core.verbose,
15080
15536
  handleRuntimeStdio: this.#sharedOpts.core.handleRuntimeStdio
15081
15537
  };
@@ -15091,6 +15547,17 @@ var Miniflare2 = class _Miniflare {
15091
15547
  );
15092
15548
  }
15093
15549
  this.#socketPorts = maybeSocketPorts;
15550
+ if (this.#maybeInspectorProxyController !== void 0) {
15551
+ const maybePort = this.#socketPorts.get(kInspectorSocket);
15552
+ if (maybePort === void 0) {
15553
+ throw new MiniflareCoreError(
15554
+ "ERR_RUNTIME_FAILURE",
15555
+ "Unable to access the runtime inspector socket."
15556
+ );
15557
+ } else {
15558
+ this.#maybeInspectorProxyController.updateConnection(maybePort);
15559
+ }
15560
+ }
15094
15561
  const entrySocket = config.sockets?.[0];
15095
15562
  const secure = entrySocket !== void 0 && "https" in entrySocket;
15096
15563
  const previousEntryURL = this.#runtimeEntryURL;
@@ -15145,6 +15612,7 @@ var Miniflare2 = class _Miniflare {
15145
15612
  await this.#initPromise;
15146
15613
  await this.#runtimeMutex.drained();
15147
15614
  if (disposing) return new URL("http://[100::]/");
15615
+ await this.#maybeInspectorProxyController?.ready;
15148
15616
  this.#checkDisposed();
15149
15617
  (0, import_assert12.default)(this.#runtimeEntryURL !== void 0);
15150
15618
  return new URL(this.#runtimeEntryURL.toString());
@@ -15160,6 +15628,9 @@ var Miniflare2 = class _Miniflare {
15160
15628
  async getInspectorURL() {
15161
15629
  this.#checkDisposed();
15162
15630
  await this.ready;
15631
+ if (this.#maybeInspectorProxyController !== void 0) {
15632
+ return this.#maybeInspectorProxyController.getInspectorURL();
15633
+ }
15163
15634
  (0, import_assert12.default)(this.#socketPorts !== void 0);
15164
15635
  const maybePort = this.#socketPorts.get(kInspectorSocket);
15165
15636
  if (maybePort === void 0) {
@@ -15238,7 +15709,7 @@ var Miniflare2 = class _Miniflare {
15238
15709
  );
15239
15710
  const forwardInit = forward;
15240
15711
  forwardInit.dispatcher = dispatcher;
15241
- const response = await fetch3(url20, forwardInit);
15712
+ const response = await fetch4(url20, forwardInit);
15242
15713
  const stack = response.headers.get(CoreHeaders.ERROR_STACK);
15243
15714
  if (response.status === 500 && stack !== null) {
15244
15715
  const caught = JsonErrorSchema.parse(await response.json());
@@ -15387,6 +15858,7 @@ var Miniflare2 = class _Miniflare {
15387
15858
  await this.#runtime?.dispose();
15388
15859
  await this.#stopLoopbackServer();
15389
15860
  await import_fs24.default.promises.rm(this.#tmpPath, { force: true, recursive: true });
15861
+ await this.#maybeInspectorProxyController?.dispose();
15390
15862
  maybeInstanceRegistry?.delete(this);
15391
15863
  }
15392
15864
  }