@cloudflare/vitest-pool-workers 0.15.0 → 0.15.2

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.
@@ -1,11 +1,13 @@
1
1
  import { createRequire } from "node:module";
2
2
  import assert from "node:assert";
3
- import crypto from "node:crypto";
4
- import fs, { existsSync, readFileSync, statSync } from "node:fs";
3
+ import crypto, { createHash } from "node:crypto";
4
+ import fs, { accessSync, chmodSync, constants, existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
5
5
  import path, { dirname, isAbsolute, join, resolve } from "node:path";
6
6
  import { fileURLToPath, pathToFileURL } from "node:url";
7
7
  import util from "node:util";
8
- import os from "node:os";
8
+ import os, { arch } from "node:os";
9
+ import { execFileSync, spawn } from "node:child_process";
10
+ import { fetch } from "undici";
9
11
  import net from "node:net";
10
12
  import { Log, LogLevel, Miniflare, ModuleRuleTypeSchema, PLUGINS, Response, compileModuleRules, formatZodError, getNodeCompat, getRootPath, kCurrentWorker, kUnsafeEphemeralUniqueKey, maybeApply, mergeWorkerOptions, parseWithRootPath, structuredSerializableReducers, structuredSerializableRevivers, testRegExps } from "miniflare";
11
13
  import { experimental_readRawConfig } from "wrangler";
@@ -459,7 +461,7 @@ var MetricsRegistry = class {
459
461
  };
460
462
 
461
463
  //#endregion
462
- //#region ../workers-utils/dist/chunk-A4F3D336.mjs
464
+ //#region ../workers-utils/dist/chunk-XXCQEG76.mjs
463
465
  var UserError = class extends Error {
464
466
  static {
465
467
  __name(this, "UserError");
@@ -468,7 +470,7 @@ var UserError = class extends Error {
468
470
  constructor(message, options) {
469
471
  super(message, options);
470
472
  Object.setPrototypeOf(this, new.target.prototype);
471
- this.telemetryMessage = options?.telemetryMessage === true ? message : options?.telemetryMessage;
473
+ this.telemetryMessage = typeof options?.telemetryMessage === "string" ? options.telemetryMessage : options?.telemetryMessage ? message : void 0;
472
474
  }
473
475
  };
474
476
  var DeprecationError = class extends UserError {
@@ -481,13 +483,14 @@ ${message}`, options);
481
483
  }
482
484
  };
483
485
  var FatalError = class extends UserError {
484
- constructor(message, code, options) {
485
- super(message, options);
486
- this.code = code;
487
- }
488
486
  static {
489
487
  __name(this, "FatalError");
490
488
  }
489
+ code;
490
+ constructor(message, options) {
491
+ super(message, options);
492
+ this.code = options.code;
493
+ }
491
494
  };
492
495
  var CommandLineArgsError = class extends UserError {
493
496
  static {
@@ -495,10 +498,6 @@ var CommandLineArgsError = class extends UserError {
495
498
  }
496
499
  };
497
500
  var JsonFriendlyFatalError = class extends FatalError {
498
- constructor(message, code, options) {
499
- super(message, code, options);
500
- this.code = code;
501
- }
502
501
  static {
503
502
  __name(this, "JsonFriendlyFatalError");
504
503
  }
@@ -513,9 +512,9 @@ var MissingConfigError = class extends Error {
513
512
  this.telemetryMessage = `Missing config value for ${key}`;
514
513
  }
515
514
  };
516
- function createFatalError(message, isJson, code, telemetryMessage) {
517
- if (isJson) return new JsonFriendlyFatalError(JSON.stringify(message), code, telemetryMessage);
518
- else return new FatalError(`${message}`, code, telemetryMessage);
515
+ function createFatalError(message, isJson, options) {
516
+ if (isJson) return new JsonFriendlyFatalError(JSON.stringify(message), options);
517
+ return new FatalError(`${message}`, options);
519
518
  }
520
519
  __name(createFatalError, "createFatalError");
521
520
  function createScanner(text, ignoreTrivia = false) {
@@ -2535,7 +2534,8 @@ function readFileSyncToBuffer(file2) {
2535
2534
  const { message } = err;
2536
2535
  throw new ParseError({
2537
2536
  text: `Could not read file: ${file2}`,
2538
- notes: [{ text: message.replace(file2, resolve(file2)) }]
2537
+ notes: [{ text: message.replace(file2, resolve(file2)) }],
2538
+ telemetryMessage: false
2539
2539
  });
2540
2540
  }
2541
2541
  }
@@ -2827,7 +2827,10 @@ function findRedirectedWranglerConfig(cwd, userConfigPath) {
2827
2827
  const deployConfig = parseJSONC(deployConfigFile, deployConfigPath);
2828
2828
  redirectedConfigPath = deployConfig.configPath && path.resolve(path.dirname(deployConfigPath), deployConfig.configPath);
2829
2829
  } catch (e) {
2830
- throw new UserError(`Failed to parse the deploy configuration file at ${path.relative(".", deployConfigPath)}`, { cause: e });
2830
+ throw new UserError(`Failed to parse the deploy configuration file at ${path.relative(".", deployConfigPath)}`, {
2831
+ cause: e,
2832
+ telemetryMessage: false
2833
+ });
2831
2834
  }
2832
2835
  if (!redirectedConfigPath) throw new UserError(esm_default`
2833
2836
  A deploy configuration file was found at "${path.relative(".", deployConfigPath)}".
@@ -2836,17 +2839,17 @@ function findRedirectedWranglerConfig(cwd, userConfigPath) {
2836
2839
  \`\`\`
2837
2840
  ${deployConfigFile}
2838
2841
  \`\`\`
2839
- `);
2842
+ `, { telemetryMessage: false });
2840
2843
  if (!existsSync(redirectedConfigPath)) throw new UserError(esm_default`
2841
2844
  There is a deploy configuration at "${path.relative(".", deployConfigPath)}".
2842
2845
  But the redirected configuration path it points to, "${path.relative(".", redirectedConfigPath)}", does not exist.
2843
- `);
2846
+ `, { telemetryMessage: false });
2844
2847
  if (userConfigPath) {
2845
2848
  if (path.join(path.dirname(userConfigPath), PATH_TO_DEPLOY_CONFIG) !== deployConfigPath) throw new UserError(esm_default`
2846
2849
  Found both a user configuration file at "${path.relative(".", userConfigPath)}"
2847
2850
  and a deploy configuration file at "${path.relative(".", deployConfigPath)}".
2848
2851
  But these do not share the same base path so it is not clear which should be used.
2849
- `);
2852
+ `, { telemetryMessage: false });
2850
2853
  }
2851
2854
  return {
2852
2855
  configPath: redirectedConfigPath,
@@ -3412,6 +3415,113 @@ var require_mod_cjs3 = __commonJS({ "../../node_modules/.pnpm/xdg-app-paths@8.3.
3412
3415
  var node_js_1 = require_node3();
3413
3416
  module$1.exports = XDGAppPaths_js_1.Adapt(node_js_1.adapter).XDGAppPaths;
3414
3417
  } });
3418
+ var require_command_exists = __commonJS({ "../../node_modules/.pnpm/command-exists@1.2.9/node_modules/command-exists/lib/command-exists.js"(exports$1, module$1) {
3419
+ var exec = __require("child_process").exec;
3420
+ var execSync = __require("child_process").execSync;
3421
+ var fs2 = __require("fs");
3422
+ var path4 = __require("path");
3423
+ var access = fs2.access;
3424
+ var accessSync2 = fs2.accessSync;
3425
+ var constants2 = fs2.constants || fs2;
3426
+ var isUsingWindows = process.platform == "win32";
3427
+ var fileNotExists = /* @__PURE__ */ __name(function(commandName, callback) {
3428
+ access(commandName, constants2.F_OK, function(err) {
3429
+ callback(!err);
3430
+ });
3431
+ }, "fileNotExists");
3432
+ var fileNotExistsSync = /* @__PURE__ */ __name(function(commandName) {
3433
+ try {
3434
+ accessSync2(commandName, constants2.F_OK);
3435
+ return false;
3436
+ } catch (e2) {
3437
+ return true;
3438
+ }
3439
+ }, "fileNotExistsSync");
3440
+ var localExecutable = /* @__PURE__ */ __name(function(commandName, callback) {
3441
+ access(commandName, constants2.F_OK | constants2.X_OK, function(err) {
3442
+ callback(null, !err);
3443
+ });
3444
+ }, "localExecutable");
3445
+ var localExecutableSync = /* @__PURE__ */ __name(function(commandName) {
3446
+ try {
3447
+ accessSync2(commandName, constants2.F_OK | constants2.X_OK);
3448
+ return true;
3449
+ } catch (e2) {
3450
+ return false;
3451
+ }
3452
+ }, "localExecutableSync");
3453
+ var commandExistsUnix = /* @__PURE__ */ __name(function(commandName, cleanedCommandName, callback) {
3454
+ fileNotExists(commandName, function(isFile$1) {
3455
+ if (!isFile$1) {
3456
+ exec("command -v " + cleanedCommandName + " 2>/dev/null && { echo >&1 " + cleanedCommandName + "; exit 0; }", function(error, stdout, stderr) {
3457
+ callback(null, !!stdout);
3458
+ });
3459
+ return;
3460
+ }
3461
+ localExecutable(commandName, callback);
3462
+ });
3463
+ }, "commandExistsUnix");
3464
+ var commandExistsWindows = /* @__PURE__ */ __name(function(commandName, cleanedCommandName, callback) {
3465
+ if (!/^(?!(?:.*\s|.*\.|\W+)$)(?:[a-zA-Z]:)?(?:(?:[^<>:"\|\?\*\n])+(?:\/\/|\/|\\\\|\\)?)+$/m.test(commandName)) {
3466
+ callback(null, false);
3467
+ return;
3468
+ }
3469
+ exec("where " + cleanedCommandName, function(error) {
3470
+ if (error !== null) callback(null, false);
3471
+ else callback(null, true);
3472
+ });
3473
+ }, "commandExistsWindows");
3474
+ var commandExistsUnixSync = /* @__PURE__ */ __name(function(commandName, cleanedCommandName) {
3475
+ if (fileNotExistsSync(commandName)) try {
3476
+ return !!execSync("command -v " + cleanedCommandName + " 2>/dev/null && { echo >&1 " + cleanedCommandName + "; exit 0; }");
3477
+ } catch (error) {
3478
+ return false;
3479
+ }
3480
+ return localExecutableSync(commandName);
3481
+ }, "commandExistsUnixSync");
3482
+ var commandExistsWindowsSync = /* @__PURE__ */ __name(function(commandName, cleanedCommandName, callback) {
3483
+ if (!/^(?!(?:.*\s|.*\.|\W+)$)(?:[a-zA-Z]:)?(?:(?:[^<>:"\|\?\*\n])+(?:\/\/|\/|\\\\|\\)?)+$/m.test(commandName)) return false;
3484
+ try {
3485
+ return !!execSync("where " + cleanedCommandName, { stdio: [] });
3486
+ } catch (error) {
3487
+ return false;
3488
+ }
3489
+ }, "commandExistsWindowsSync");
3490
+ var cleanInput = /* @__PURE__ */ __name(function(s) {
3491
+ if (/[^A-Za-z0-9_\/:=-]/.test(s)) {
3492
+ s = "'" + s.replace(/'/g, "'\\''") + "'";
3493
+ s = s.replace(/^(?:'')+/g, "").replace(/\\'''/g, "\\'");
3494
+ }
3495
+ return s;
3496
+ }, "cleanInput");
3497
+ if (isUsingWindows) cleanInput = /* @__PURE__ */ __name(function(s) {
3498
+ if (/[\\]/.test(s)) {
3499
+ var dirname2 = "\"" + path4.dirname(s) + "\"";
3500
+ var basename = "\"" + path4.basename(s) + "\"";
3501
+ return dirname2 + ":" + basename;
3502
+ }
3503
+ return "\"" + s + "\"";
3504
+ }, "cleanInput");
3505
+ module$1.exports = /* @__PURE__ */ __name(function commandExists(commandName, callback) {
3506
+ var cleanedCommandName = cleanInput(commandName);
3507
+ if (!callback && typeof Promise !== "undefined") return new Promise(function(resolve$2, reject) {
3508
+ commandExists(commandName, function(error, output) {
3509
+ if (output) resolve$2(commandName);
3510
+ else reject(error);
3511
+ });
3512
+ });
3513
+ if (isUsingWindows) commandExistsWindows(commandName, cleanedCommandName, callback);
3514
+ else commandExistsUnix(commandName, cleanedCommandName, callback);
3515
+ }, "commandExists");
3516
+ module$1.exports.sync = function(commandName) {
3517
+ var cleanedCommandName = cleanInput(commandName);
3518
+ if (isUsingWindows) return commandExistsWindowsSync(commandName, cleanedCommandName);
3519
+ else return commandExistsUnixSync(commandName, cleanedCommandName);
3520
+ };
3521
+ } });
3522
+ var require_command_exists2 = __commonJS({ "../../node_modules/.pnpm/command-exists@1.2.9/node_modules/command-exists/index.js"(exports$1, module$1) {
3523
+ module$1.exports = require_command_exists();
3524
+ } });
3415
3525
  var defaultWranglerConfig = {
3416
3526
  configPath: void 0,
3417
3527
  userConfigPath: void 0,
@@ -7090,7 +7200,7 @@ function getBooleanEnvironmentVariableFactory(options) {
7090
7200
  switch (process.env[options.variableName]?.toLowerCase()) {
7091
7201
  case "true": return true;
7092
7202
  case "false": return false;
7093
- default: throw new UserError(`Expected ${options.variableName} to be "true" or "false", but got ${JSON.stringify(process.env[options.variableName])}`);
7203
+ default: throw new UserError(`Expected ${options.variableName} to be "true" or "false", but got ${JSON.stringify(process.env[options.variableName])}`, { telemetryMessage: false });
7094
7204
  }
7095
7205
  };
7096
7206
  }
@@ -7116,7 +7226,7 @@ function getProcessEnv(variableName, choices) {
7116
7226
  }
7117
7227
  __name(getProcessEnv, "getProcessEnv");
7118
7228
  function assertOneOf(choices, value) {
7119
- if (Array.isArray(choices) && !choices.includes(value)) throw new UserError(`Expected ${JSON.stringify(value)} to be one of ${JSON.stringify(choices)}`);
7229
+ if (Array.isArray(choices) && !choices.includes(value)) throw new UserError(`Expected ${JSON.stringify(value)} to be one of ${JSON.stringify(choices)}`, { telemetryMessage: false });
7120
7230
  }
7121
7231
  __name(assertOneOf, "assertOneOf");
7122
7232
  var getC3CommandFromEnv = getEnvironmentVariableFactory({
@@ -7140,7 +7250,7 @@ var getCloudflareComplianceRegion = /* @__PURE__ */ __name((complianceConfig) =>
7140
7250
  The compliance region has been set to different values in two places:
7141
7251
  - \`CLOUDFLARE_COMPLIANCE_REGION\` environment variable: \`${complianceRegionFromEnv}\`
7142
7252
  - \`compliance_region\` configuration property: \`${complianceConfig.compliance_region}\`
7143
- `);
7253
+ `, { telemetryMessage: false });
7144
7254
  return complianceRegionFromEnv || complianceConfig?.compliance_region || "public";
7145
7255
  }, "getCloudflareComplianceRegion");
7146
7256
  var getCloudflareApiBaseUrlFromEnv = getEnvironmentVariableFactory({
@@ -7678,7 +7788,7 @@ function applyPythonConfig(config, args) {
7678
7788
  type: "PythonModule",
7679
7789
  globs: ["**/*.py"]
7680
7790
  });
7681
- if (!config.compatibility_flags.includes("python_workers")) throw new UserError("The `python_workers` compatibility flag is required to use Python.");
7791
+ if (!config.compatibility_flags.includes("python_workers")) throw new UserError("The `python_workers` compatibility flag is required to use Python.", { telemetryMessage: false });
7682
7792
  }
7683
7793
  }
7684
7794
  __name(applyPythonConfig, "applyPythonConfig");
@@ -9677,7 +9787,7 @@ function isDockerfile(imagePath, configPath) {
9677
9787
  const baseDir = configPath ? path.dirname(configPath) : process.cwd();
9678
9788
  const maybeDockerfile = path.resolve(baseDir, imagePath);
9679
9789
  if (fs.existsSync(maybeDockerfile)) {
9680
- if (isDirectory$1(maybeDockerfile)) throw new UserError(`${imagePath} is a directory, you should specify a path to the Dockerfile`);
9790
+ if (isDirectory$1(maybeDockerfile)) throw new UserError(`${imagePath} is a directory, you should specify a path to the Dockerfile`, { telemetryMessage: false });
9681
9791
  return true;
9682
9792
  }
9683
9793
  const errorPrefix = `The image "${imagePath}" does not appear to be a valid path to a Dockerfile, or a valid image registry path:
@@ -9685,12 +9795,12 @@ function isDockerfile(imagePath, configPath) {
9685
9795
  try {
9686
9796
  new URL(`https://${imagePath}`);
9687
9797
  } catch (e2) {
9688
- if (e2 instanceof Error) throw new UserError(errorPrefix + e2.message);
9798
+ if (e2 instanceof Error) throw new UserError(errorPrefix + e2.message, { telemetryMessage: false });
9689
9799
  throw e2;
9690
9800
  }
9691
9801
  const imageParts = imagePath.split("/");
9692
- if (!imageParts[imageParts.length - 1]?.includes(":")) throw new UserError(errorPrefix + `If this is an image registry path, it needs to include at least a tag ':' (e.g: docker.io/httpd:1)`);
9693
- if (imagePath.includes("://")) throw new UserError(errorPrefix + `Image reference should not include the protocol part (e.g: docker.io/httpd:1, not https://docker.io/httpd:1)`);
9802
+ if (!imageParts[imageParts.length - 1]?.includes(":")) throw new UserError(errorPrefix + `If this is an image registry path, it needs to include at least a tag ':' (e.g: docker.io/httpd:1)`, { telemetryMessage: false });
9803
+ if (imagePath.includes("://")) throw new UserError(errorPrefix + `Image reference should not include the protocol part (e.g: docker.io/httpd:1, not https://docker.io/httpd:1)`, { telemetryMessage: false });
9694
9804
  return false;
9695
9805
  }
9696
9806
  __name(isDockerfile, "isDockerfile");
@@ -9727,7 +9837,7 @@ var supportedPagesConfigFields = [
9727
9837
  ];
9728
9838
  function validatePagesConfig(config, envNames, projectName) {
9729
9839
  if (!config.pages_build_output_dir) throw new FatalError(`Attempting to validate Pages configuration file, but "pages_build_output_dir" field was not found.
9730
- "pages_build_output_dir" is required for Pages projects.`);
9840
+ "pages_build_output_dir" is required for Pages projects.`, { telemetryMessage: false });
9731
9841
  const diagnostics = new Diagnostics(`Running configuration file validation for Pages:`);
9732
9842
  validateMainField(config, diagnostics);
9733
9843
  validateProjectName(projectName, diagnostics);
@@ -9775,6 +9885,569 @@ Pages requires Durable Object bindings to specify the name of the Worker where t
9775
9885
  }
9776
9886
  }
9777
9887
  __name(validateDurableObjectBinding2, "validateDurableObjectBinding");
9888
+ var import_command_exists = __toESM(require_command_exists2());
9889
+ var UPDATE_SERVICE_URL = "https://update.argotunnel.com";
9890
+ var CLOUDFLARED_VERSION_PATTERN = /^\d{4}\.\d+\.\d+$/;
9891
+ function sha256Hex(buffer) {
9892
+ return createHash("sha256").update(buffer).digest("hex");
9893
+ }
9894
+ __name(sha256Hex, "sha256Hex");
9895
+ function getGoArch() {
9896
+ const nodeArch = arch();
9897
+ switch (nodeArch) {
9898
+ case "x64": return "amd64";
9899
+ case "arm64": return "arm64";
9900
+ case "arm": return "arm";
9901
+ default: throw new UserError(`Unsupported architecture for cloudflared: ${nodeArch}
9902
+
9903
+ cloudflared supports: x64 (amd64), arm64, arm
9904
+
9905
+ You can manually install cloudflared and set the CLOUDFLARED_PATH environment variable.
9906
+ Download instructions: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`, { telemetryMessage: "tunnel cloudflared unsupported architecture" });
9907
+ }
9908
+ }
9909
+ __name(getGoArch, "getGoArch");
9910
+ function getGoOS() {
9911
+ switch (process.platform) {
9912
+ case "darwin": return "darwin";
9913
+ case "linux": return "linux";
9914
+ case "win32": return "windows";
9915
+ default: throw new UserError(`Unsupported platform for cloudflared: ${process.platform}
9916
+
9917
+ cloudflared supports: darwin (macOS), linux, win32 (Windows)
9918
+
9919
+ You can manually install cloudflared and set the CLOUDFLARED_PATH environment variable.
9920
+ Download instructions: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`, { telemetryMessage: "tunnel cloudflared unsupported platform" });
9921
+ }
9922
+ }
9923
+ __name(getGoOS, "getGoOS");
9924
+ var GITHUB_RELEASE_BASE = "https://github.com/cloudflare/cloudflared/releases/download";
9925
+ function getAssetFilename(goOS, goArch) {
9926
+ if (goOS === "windows") return `cloudflared-${goOS}-${goArch}.exe`;
9927
+ if (goOS === "darwin") return `cloudflared-${goOS}-${goArch}.tgz`;
9928
+ return `cloudflared-${goOS}-${goArch}`;
9929
+ }
9930
+ __name(getAssetFilename, "getAssetFilename");
9931
+ async function queryUpdateService(goOS, goArch, options) {
9932
+ const { logger } = options ?? {};
9933
+ const url = new URL(UPDATE_SERVICE_URL);
9934
+ url.searchParams.set("os", goOS);
9935
+ url.searchParams.set("arch", goArch);
9936
+ logger?.debug(`Checking for latest cloudflared: ${url.toString()}`);
9937
+ let response;
9938
+ try {
9939
+ response = await fetch(url.toString(), { headers: { "User-Agent": "wrangler" } });
9940
+ } catch (e2) {
9941
+ logger?.debug(`Failed to reach update service: ${e2 instanceof Error ? e2.message : String(e2)}`);
9942
+ return null;
9943
+ }
9944
+ if (!response.ok) {
9945
+ logger?.debug(`Update service returned ${response.status} for ${goOS}/${goArch}`);
9946
+ return null;
9947
+ }
9948
+ let data;
9949
+ try {
9950
+ data = await response.json();
9951
+ } catch (e2) {
9952
+ logger?.debug(`Update service returned non-JSON response: ${e2 instanceof Error ? e2.message : String(e2)}`);
9953
+ return null;
9954
+ }
9955
+ if (typeof data.version === "string" && !CLOUDFLARED_VERSION_PATTERN.test(data.version)) throw new Error(`[cloudflared] Invalid cloudflared version returned by update service: ${data.version}`);
9956
+ if (data.error || !data.url || !data.version) return data.version ? data : null;
9957
+ return data;
9958
+ }
9959
+ __name(queryUpdateService, "queryUpdateService");
9960
+ async function getLatestVersionInfo(options) {
9961
+ const { logger } = options ?? {};
9962
+ const goOS = getGoOS();
9963
+ const goArch = getGoArch();
9964
+ const primary = await queryUpdateService(goOS, goArch, { logger });
9965
+ if (primary && primary.url && primary.version) return primary;
9966
+ logger?.debug(`Update worker had no result for ${goOS}/${goArch}, falling back to GitHub release URL`);
9967
+ const fallback = await queryUpdateService("linux", "amd64", { logger });
9968
+ if (!fallback?.version) throw new UserError(`[cloudflared] Failed to determine the latest cloudflared version.
9969
+
9970
+ The update service did not return results for ${goOS}/${goArch},
9971
+ and the fallback query also failed.
9972
+
9973
+ You can manually install cloudflared from:
9974
+ https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`, { telemetryMessage: "tunnel cloudflared version lookup failed" });
9975
+ const version = fallback.version;
9976
+ const filename = getAssetFilename(goOS, goArch);
9977
+ return {
9978
+ url: `${GITHUB_RELEASE_BASE}/${version}/${filename}`,
9979
+ version,
9980
+ checksum: "",
9981
+ compressed: filename.endsWith(".tgz"),
9982
+ shouldUpdate: true,
9983
+ userMessage: "",
9984
+ error: ""
9985
+ };
9986
+ }
9987
+ __name(getLatestVersionInfo, "getLatestVersionInfo");
9988
+ function getCacheDir(version) {
9989
+ return join(getGlobalWranglerConfigPath(), "cloudflared", version);
9990
+ }
9991
+ __name(getCacheDir, "getCacheDir");
9992
+ function getCloudflaredBinPath(version) {
9993
+ const binName = process.platform === "win32" ? "cloudflared.exe" : "cloudflared";
9994
+ return join(getCacheDir(version), binName);
9995
+ }
9996
+ __name(getCloudflaredBinPath, "getCloudflaredBinPath");
9997
+ function isBinaryExecutable(binPath) {
9998
+ try {
9999
+ accessSync(binPath, constants.X_OK);
10000
+ return true;
10001
+ } catch {
10002
+ return false;
10003
+ }
10004
+ }
10005
+ __name(isBinaryExecutable, "isBinaryExecutable");
10006
+ function validateBinary(binPath, options) {
10007
+ const { logger } = options ?? {};
10008
+ try {
10009
+ const output = execFileSync(binPath, ["--version"], {
10010
+ stdio: [
10011
+ "pipe",
10012
+ "pipe",
10013
+ "pipe"
10014
+ ],
10015
+ timeout: 1e4,
10016
+ encoding: "utf8"
10017
+ }).trim();
10018
+ logger?.debug(`cloudflared version: ${output}`);
10019
+ } catch {
10020
+ let errorMessage = `[cloudflared] Failed to validate cloudflared binary at ${binPath}
10021
+
10022
+ `;
10023
+ errorMessage += `This usually means:
10024
+ `;
10025
+ errorMessage += ` - The binary is corrupted or incomplete
10026
+ `;
10027
+ errorMessage += ` - You're missing required system libraries
10028
+ `;
10029
+ if (process.platform === "linux") {
10030
+ errorMessage += `
10031
+ On Linux, make sure you have the required dependencies:
10032
+ `;
10033
+ errorMessage += ` - glibc (GNU C Library)
10034
+ `;
10035
+ errorMessage += ` - For Debian/Ubuntu: sudo apt-get install libc6
10036
+ `;
10037
+ }
10038
+ const cacheDir = join(getGlobalWranglerConfigPath(), "cloudflared");
10039
+ errorMessage += `
10040
+ You can try:
10041
+ `;
10042
+ errorMessage += ` 1. Deleting the cache directory: rm -rf ${cacheDir}
10043
+ `;
10044
+ errorMessage += ` 2. Running the command again to re-download
10045
+ `;
10046
+ errorMessage += ` 3. Manually installing cloudflared: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/
10047
+ `;
10048
+ errorMessage += ` 4. Setting CLOUDFLARED_PATH to point to your cloudflared binary`;
10049
+ throw new UserError(errorMessage, { telemetryMessage: "tunnel cloudflared validation failed" });
10050
+ }
10051
+ }
10052
+ __name(validateBinary, "validateBinary");
10053
+ function redactCloudflaredArgsForLogging(args) {
10054
+ const redacted = [...args];
10055
+ for (let i = 0; i < redacted.length; i++) {
10056
+ const arg = redacted[i];
10057
+ if (arg === "--token" && i + 1 < redacted.length) redacted[i + 1] = "[REDACTED]";
10058
+ if (arg.startsWith("--token=")) redacted[i] = "--token=[REDACTED]";
10059
+ }
10060
+ return redacted;
10061
+ }
10062
+ __name(redactCloudflaredArgsForLogging, "redactCloudflaredArgsForLogging");
10063
+ function tryGetCloudflaredFromPath(options) {
10064
+ const { logger } = options ?? {};
10065
+ if (!(0, import_command_exists.sync)("cloudflared")) return null;
10066
+ try {
10067
+ validateBinary("cloudflared", { logger });
10068
+ return "cloudflared";
10069
+ } catch (e2) {
10070
+ logger?.debug("cloudflared found in PATH but failed validation", e2);
10071
+ return null;
10072
+ }
10073
+ }
10074
+ __name(tryGetCloudflaredFromPath, "tryGetCloudflaredFromPath");
10075
+ function getInstalledVersion(binPath) {
10076
+ try {
10077
+ const match = execFileSync(binPath, ["--version"], {
10078
+ stdio: [
10079
+ "pipe",
10080
+ "pipe",
10081
+ "pipe"
10082
+ ],
10083
+ timeout: 1e4,
10084
+ encoding: "utf8"
10085
+ }).match(/(\d+\.\d+\.\d+)/);
10086
+ return match ? match[1] : null;
10087
+ } catch {
10088
+ return null;
10089
+ }
10090
+ }
10091
+ __name(getInstalledVersion, "getInstalledVersion");
10092
+ function isVersionOutdated(installed, latest) {
10093
+ const parse$2 = /* @__PURE__ */ __name((v) => v.split(".").map(Number), "parse");
10094
+ const [iYear, iMonth, iPatch] = parse$2(installed);
10095
+ const [lYear, lMonth, lPatch] = parse$2(latest);
10096
+ if (iYear !== lYear) return iYear < lYear;
10097
+ if (iMonth !== lMonth) return iMonth < lMonth;
10098
+ return iPatch < lPatch;
10099
+ }
10100
+ __name(isVersionOutdated, "isVersionOutdated");
10101
+ async function warnIfOutdated(binPath, options) {
10102
+ const { logger } = options ?? {};
10103
+ try {
10104
+ const installed = getInstalledVersion(binPath);
10105
+ if (!installed) return;
10106
+ const latestVersion = (await queryUpdateService(getGoOS(), getGoArch(), { logger }) ?? await queryUpdateService("linux", "amd64", { logger }))?.version;
10107
+ if (!latestVersion) return;
10108
+ if (isVersionOutdated(installed, latestVersion)) logger?.warn(`Your cloudflared (${installed}) is outdated. Latest version is ${latestVersion}.
10109
+ Update: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`);
10110
+ } catch (e2) {
10111
+ logger?.debug("Failed to check cloudflared version", e2);
10112
+ }
10113
+ }
10114
+ __name(warnIfOutdated, "warnIfOutdated");
10115
+ function writeFileAtomic(filePath, contents) {
10116
+ const tmpPath = join(dirname(filePath), `.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`);
10117
+ try {
10118
+ writeFileSync(tmpPath, contents);
10119
+ renameSync(tmpPath, filePath);
10120
+ } finally {
10121
+ try {
10122
+ if (existsSync(tmpPath)) unlinkSync(tmpPath);
10123
+ } catch {}
10124
+ }
10125
+ }
10126
+ __name(writeFileAtomic, "writeFileAtomic");
10127
+ async function downloadCloudflared(versionInfo, binPath, options) {
10128
+ const { logger } = options ?? {};
10129
+ const { url, version, checksum, compressed } = versionInfo;
10130
+ logger?.log(`Downloading cloudflared ${version}...`);
10131
+ logger?.debug(`Download URL: ${url}`);
10132
+ const cacheDir = dirname(binPath);
10133
+ mkdirSync(cacheDir, { recursive: true });
10134
+ let response;
10135
+ try {
10136
+ response = await fetch(url, { headers: { "User-Agent": "wrangler" } });
10137
+ } catch (e2) {
10138
+ throw new UserError(`[cloudflared] Failed to download cloudflared from ${url}
10139
+
10140
+ Network error: ${e2 instanceof Error ? e2.message : String(e2)}
10141
+
10142
+ Please check your internet connection and try again.
10143
+ If you're behind a proxy, make sure it's configured correctly.`, { telemetryMessage: "tunnel cloudflared download network failed" });
10144
+ }
10145
+ if (!response.ok) throw new UserError(`[cloudflared] Failed to download cloudflared from ${url}
10146
+
10147
+ HTTP ${response.status}: ${response.statusText}
10148
+
10149
+ You can manually download cloudflared from:
10150
+ https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`, { telemetryMessage: "tunnel cloudflared download response failed" });
10151
+ try {
10152
+ if (compressed) await downloadAndExtractTarball(response, checksum, binPath, cacheDir);
10153
+ else await downloadBinary(response, checksum, binPath);
10154
+ } catch (e2) {
10155
+ try {
10156
+ if (existsSync(binPath)) unlinkSync(binPath);
10157
+ } catch {}
10158
+ if (e2 instanceof UserError) throw e2;
10159
+ throw new UserError(`[cloudflared] Failed to save cloudflared binary
10160
+
10161
+ Error: ${e2 instanceof Error ? e2.message : String(e2)}
10162
+
10163
+ Please ensure you have write permissions to: ${cacheDir}`, { telemetryMessage: "tunnel cloudflared save failed" });
10164
+ }
10165
+ if (process.platform !== "win32") chmodSync(binPath, 493);
10166
+ logger?.log(`cloudflared ${version} installed`);
10167
+ logger?.debug(`Binary location: ${binPath}`);
10168
+ }
10169
+ __name(downloadCloudflared, "downloadCloudflared");
10170
+ async function downloadAndExtractTarball(response, expectedChecksum, binPath, cacheDir) {
10171
+ const tempTarPath = join(cacheDir, "cloudflared.tgz");
10172
+ const buffer = Buffer.from(await response.arrayBuffer());
10173
+ if (expectedChecksum) {
10174
+ const actualSha256 = sha256Hex(buffer);
10175
+ if (actualSha256 !== expectedChecksum) throw new UserError(`[cloudflared] SHA256 mismatch for downloaded cloudflared tarball.
10176
+
10177
+ Expected: ${expectedChecksum}
10178
+ Actual: ${actualSha256}`, { telemetryMessage: "tunnel cloudflared tarball checksum mismatch" });
10179
+ }
10180
+ writeFileSync(tempTarPath, buffer);
10181
+ try {
10182
+ execFileSync("tar", [
10183
+ "-xzf",
10184
+ tempTarPath,
10185
+ "-C",
10186
+ cacheDir
10187
+ ], { stdio: "ignore" });
10188
+ const extractedPath = join(cacheDir, "cloudflared");
10189
+ if (extractedPath !== binPath && existsSync(extractedPath)) renameSync(extractedPath, binPath);
10190
+ } finally {
10191
+ try {
10192
+ if (existsSync(tempTarPath)) unlinkSync(tempTarPath);
10193
+ } catch {}
10194
+ }
10195
+ }
10196
+ __name(downloadAndExtractTarball, "downloadAndExtractTarball");
10197
+ async function downloadBinary(response, expectedChecksum, binPath) {
10198
+ const buffer = Buffer.from(await response.arrayBuffer());
10199
+ if (expectedChecksum) {
10200
+ const actualSha256 = sha256Hex(buffer);
10201
+ if (actualSha256 !== expectedChecksum) throw new UserError(`[cloudflared] SHA256 mismatch for downloaded cloudflared binary.
10202
+
10203
+ Expected: ${expectedChecksum}
10204
+ Actual: ${actualSha256}`, { telemetryMessage: "tunnel cloudflared binary checksum mismatch" });
10205
+ }
10206
+ writeFileAtomic(binPath, buffer);
10207
+ }
10208
+ __name(downloadBinary, "downloadBinary");
10209
+ async function getCloudflaredPath(options) {
10210
+ const logger = options?.logger;
10211
+ const envPath = getCloudflaredPathFromEnv();
10212
+ if (envPath) {
10213
+ if (!existsSync(envPath)) throw new UserError(`CLOUDFLARED_PATH is set to "${envPath}" but the file does not exist.
10214
+
10215
+ Please ensure the path points to a valid cloudflared binary.`, { telemetryMessage: "tunnel cloudflared env path missing" });
10216
+ logger?.debug(`Using cloudflared from CLOUDFLARED_PATH: ${envPath}`);
10217
+ return envPath;
10218
+ }
10219
+ const pathBin = tryGetCloudflaredFromPath({ logger });
10220
+ if (pathBin) {
10221
+ logger?.debug("Using cloudflared from PATH");
10222
+ if (!options?.skipVersionCheck) await warnIfOutdated(pathBin, { logger });
10223
+ return pathBin;
10224
+ }
10225
+ const versionInfo = await getLatestVersionInfo({ logger });
10226
+ const binPath = getCloudflaredBinPath(versionInfo.version);
10227
+ let needsDownload = !isBinaryExecutable(binPath);
10228
+ if (!needsDownload) try {
10229
+ validateBinary(binPath, { logger });
10230
+ logger?.debug(`Using cached cloudflared ${versionInfo.version}: ${binPath}`);
10231
+ return binPath;
10232
+ } catch (e2) {
10233
+ logger?.debug("Cached cloudflared failed validation; re-downloading", e2);
10234
+ needsDownload = true;
10235
+ }
10236
+ if (!(await options?.confirmDownload?.(`cloudflared (${versionInfo.version}) is needed but not installed. Download to ${binPath}?`) ?? true)) throw new UserError(`cloudflared is required to run this command.
10237
+
10238
+ You can install it manually from:
10239
+ https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/
10240
+
10241
+ Then either add it to your PATH or set CLOUDFLARED_PATH.`, { telemetryMessage: "tunnel cloudflared download declined" });
10242
+ if (existsSync(binPath)) {
10243
+ const cacheDir = removeCloudflaredCache(versionInfo.version);
10244
+ if (cacheDir) logger?.log(`Removed cloudflared cache: ${cacheDir}`);
10245
+ }
10246
+ await downloadCloudflared(versionInfo, binPath, { logger });
10247
+ validateBinary(binPath, { logger });
10248
+ return binPath;
10249
+ }
10250
+ __name(getCloudflaredPath, "getCloudflaredPath");
10251
+ async function spawnCloudflared(args, options) {
10252
+ const logger = options?.logger;
10253
+ const binPath = await getCloudflaredPath({
10254
+ skipVersionCheck: options?.skipVersionCheck,
10255
+ confirmDownload: options?.confirmDownload,
10256
+ logger
10257
+ });
10258
+ logger?.debug(`Spawning cloudflared: ${binPath} ${redactCloudflaredArgsForLogging(args).join(" ")}`);
10259
+ return spawn(binPath, args, {
10260
+ stdio: options?.stdio ?? "inherit",
10261
+ env: options?.env ? {
10262
+ ...process.env,
10263
+ ...options.env
10264
+ } : void 0
10265
+ });
10266
+ }
10267
+ __name(spawnCloudflared, "spawnCloudflared");
10268
+ function removeCloudflaredCache(version) {
10269
+ const cacheDir = version ? getCacheDir(version) : join(getGlobalWranglerConfigPath(), "cloudflared");
10270
+ if (existsSync(cacheDir)) {
10271
+ removeDirSync(cacheDir);
10272
+ return cacheDir;
10273
+ }
10274
+ return null;
10275
+ }
10276
+ __name(removeCloudflaredCache, "removeCloudflaredCache");
10277
+ var TUNNEL_STARTUP_TIMEOUT_MS = 3e4;
10278
+ var TUNNEL_FORCE_KILL_TIMEOUT_MS = 5e3;
10279
+ var DEFAULT_TUNNEL_EXPIRY_MS = 3600 * 1e3;
10280
+ var DEFAULT_TUNNEL_EXTENSION_MS = 3600 * 1e3;
10281
+ var DEFAULT_TUNNEL_MAX_REMAINING_MS = 10800 * 1e3;
10282
+ var DEFAULT_TUNNEL_REMINDER_INTERVAL_MS = 600 * 1e3;
10283
+ var QUICK_TUNNEL_URL_REGEX = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/;
10284
+ function startTunnel(options) {
10285
+ let disposed = false;
10286
+ let reminderInterval;
10287
+ let expiryTimeout;
10288
+ let expiresAt = 0;
10289
+ let cloudflaredProcess;
10290
+ const logger = options.logger;
10291
+ const timeoutMs = options.timeoutMs ?? TUNNEL_STARTUP_TIMEOUT_MS;
10292
+ const reminderIntervalMs = options.reminderIntervalMs ?? DEFAULT_TUNNEL_REMINDER_INTERVAL_MS;
10293
+ const defaultExpiryMs = options.expiryMs ?? DEFAULT_TUNNEL_EXPIRY_MS;
10294
+ const timeFormatter = new Intl.DateTimeFormat(void 0, { timeStyle: "short" });
10295
+ const readyPromise = spawnCloudflared([
10296
+ "tunnel",
10297
+ "--no-autoupdate",
10298
+ "--url",
10299
+ options.origin.href
10300
+ ], {
10301
+ stdio: "pipe",
10302
+ skipVersionCheck: true,
10303
+ logger
10304
+ }).then((process2) => {
10305
+ cloudflaredProcess = process2;
10306
+ if (disposed) terminateCloudflared(process2);
10307
+ return process2;
10308
+ }).then((process2) => waitForQuickTunnelReady(process2, timeoutMs, {
10309
+ logger,
10310
+ origin: options.origin
10311
+ })).then((result) => {
10312
+ expiresAt = Date.now() + defaultExpiryMs;
10313
+ scheduleExpiryTimeout();
10314
+ scheduleReminder(result.publicUrl.origin);
10315
+ return result;
10316
+ });
10317
+ function disposeTunnel() {
10318
+ disposed = true;
10319
+ clearTunnelTimers();
10320
+ if (cloudflaredProcess) terminateCloudflared(cloudflaredProcess);
10321
+ }
10322
+ __name(disposeTunnel, "disposeTunnel");
10323
+ function clearTunnelTimers() {
10324
+ if (expiryTimeout) {
10325
+ clearTimeout(expiryTimeout);
10326
+ expiryTimeout = void 0;
10327
+ }
10328
+ if (reminderInterval) {
10329
+ clearInterval(reminderInterval);
10330
+ reminderInterval = void 0;
10331
+ }
10332
+ }
10333
+ __name(clearTunnelTimers, "clearTunnelTimers");
10334
+ function scheduleReminder(publicURL) {
10335
+ if (reminderIntervalMs > 0) {
10336
+ reminderInterval = setInterval(() => {
10337
+ if (disposed) return;
10338
+ const remainingMs = expiresAt - Date.now();
10339
+ if (remainingMs <= 0) return;
10340
+ logger?.log(`The tunnel is still open at ${publicURL}. It expires in ${formatTunnelDuration(remainingMs)}. ${options.extendHint ?? ""}`);
10341
+ }, reminderIntervalMs);
10342
+ reminderInterval.unref?.();
10343
+ }
10344
+ }
10345
+ __name(scheduleReminder, "scheduleReminder");
10346
+ function scheduleExpiryTimeout() {
10347
+ if (disposed) return;
10348
+ if (expiryTimeout) clearTimeout(expiryTimeout);
10349
+ expiryTimeout = setTimeout(() => {
10350
+ if (disposed) return;
10351
+ logger?.log("Tunnel expired. Closing tunnel.");
10352
+ disposeTunnel();
10353
+ }, Math.max(0, expiresAt - Date.now()));
10354
+ expiryTimeout.unref();
10355
+ }
10356
+ __name(scheduleExpiryTimeout, "scheduleExpiryTimeout");
10357
+ function extendExpiry(ms = DEFAULT_TUNNEL_EXTENSION_MS) {
10358
+ if (disposed || !expiryTimeout || ms <= 0) return;
10359
+ const now = Date.now();
10360
+ const previousExpiresAt = expiresAt;
10361
+ expiresAt = Math.min(now + DEFAULT_TUNNEL_MAX_REMAINING_MS, Math.max(expiresAt, now) + ms);
10362
+ const extendedByMs = expiresAt - previousExpiresAt;
10363
+ if (extendedByMs < ms) {
10364
+ logger?.log(`Tunnel expiry extended to the ${formatTunnelDuration(DEFAULT_TUNNEL_MAX_REMAINING_MS)} limit. It now expires at ${timeFormatter.format(new Date(expiresAt))}.`);
10365
+ scheduleExpiryTimeout();
10366
+ return;
10367
+ }
10368
+ logger?.log(`Tunnel expiry extended by ${formatTunnelDuration(extendedByMs)}. It now expires at ${timeFormatter.format(new Date(expiresAt))}.`);
10369
+ scheduleExpiryTimeout();
10370
+ }
10371
+ __name(extendExpiry, "extendExpiry");
10372
+ return {
10373
+ ready: /* @__PURE__ */ __name(() => readyPromise, "ready"),
10374
+ dispose: disposeTunnel,
10375
+ extendExpiry
10376
+ };
10377
+ }
10378
+ __name(startTunnel, "startTunnel");
10379
+ function formatTunnelDuration(durationMs) {
10380
+ const totalMinutes = Math.max(1, Math.ceil(durationMs / 6e4));
10381
+ const hours = Math.floor(totalMinutes / 60);
10382
+ const minutes = totalMinutes % 60;
10383
+ if (hours === 0) return `${minutes}m`;
10384
+ if (minutes === 0) return `${hours}h`;
10385
+ return `${hours}h ${minutes}m`;
10386
+ }
10387
+ __name(formatTunnelDuration, "formatTunnelDuration");
10388
+ function terminateCloudflared(cloudflared) {
10389
+ if (cloudflared.killed) return;
10390
+ cloudflared.unref();
10391
+ cloudflared.kill("SIGTERM");
10392
+ setTimeout(() => {
10393
+ if (!cloudflared.killed) cloudflared.kill("SIGKILL");
10394
+ }, TUNNEL_FORCE_KILL_TIMEOUT_MS).unref();
10395
+ }
10396
+ __name(terminateCloudflared, "terminateCloudflared");
10397
+ function waitForQuickTunnelReady(cloudflared, timeoutMs, options) {
10398
+ return new Promise((resolve$2, reject) => {
10399
+ let resolved = false;
10400
+ let stderrOutput = "";
10401
+ const logger = options?.logger;
10402
+ const origin = options?.origin;
10403
+ const timeoutId = setTimeout(() => {
10404
+ if (!resolved) {
10405
+ resolved = true;
10406
+ terminateCloudflared(cloudflared);
10407
+ reject(createTunnelStartupError(`Timed out waiting for cloudflared to start (${timeoutMs / 1e3}s).`, stderrOutput, origin));
10408
+ }
10409
+ }, timeoutMs);
10410
+ timeoutId.unref();
10411
+ if (cloudflared.stderr) cloudflared.stderr.on("data", (data) => {
10412
+ const chunk = data.toString();
10413
+ stderrOutput += chunk;
10414
+ logger?.debug("[cloudflared]", chunk.trimEnd());
10415
+ const match = QUICK_TUNNEL_URL_REGEX.exec(stderrOutput);
10416
+ if (match && !resolved) {
10417
+ resolved = true;
10418
+ clearTimeout(timeoutId);
10419
+ resolve$2({ publicUrl: new URL(match[0]) });
10420
+ }
10421
+ });
10422
+ cloudflared.on("error", (error) => {
10423
+ if (!resolved) {
10424
+ resolved = true;
10425
+ clearTimeout(timeoutId);
10426
+ reject(/* @__PURE__ */ new Error(`Failed to start cloudflared: ${error.message}`));
10427
+ }
10428
+ });
10429
+ cloudflared.on("exit", (code, signal) => {
10430
+ if (!resolved) {
10431
+ resolved = true;
10432
+ clearTimeout(timeoutId);
10433
+ reject(createTunnelStartupError(`cloudflared ${signal ? `terminated by signal ${signal}` : `exited with code ${code}`} before the tunnel was ready.`, stderrOutput, origin));
10434
+ }
10435
+ });
10436
+ });
10437
+ }
10438
+ __name(waitForQuickTunnelReady, "waitForQuickTunnelReady");
10439
+ function createTunnelStartupError(message, stderrOutput, origin) {
10440
+ const isQuickTunnelRateLimited = stderrOutput.includes("429 Too Many Requests");
10441
+ const errorMessage = `${message}
10442
+ cloudflared output:
10443
+ ${stderrOutput || "(no output)"}
10444
+
10445
+ The local dev server started at ${origin.href}.
10446
+ ` + (isQuickTunnelRateLimited ? "Cloudflare Quick Tunnel creation was rate limited. Try again in a few minutes, or use a named tunnel if you need more reliable access." : `Check the cloudflared output above for more details, and verify that ${origin.href} is reachable from this machine if this keeps happening.`);
10447
+ if (isQuickTunnelRateLimited) return new UserError(errorMessage, { telemetryMessage: false });
10448
+ return new Error(errorMessage);
10449
+ }
10450
+ __name(createTunnelStartupError, "createTunnelStartupError");
9778
10451
 
9779
10452
  //#endregion
9780
10453
  //#region ../../node_modules/.pnpm/devalue@5.6.3/node_modules/devalue/src/utils.js