@cloudflare/workers-utils 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,13 +1,16 @@
1
1
  export { assertNever, constructWranglerConfig, getTodaysCompatDate, isCompatDate, mapWorkerMetadataBindings } from './chunk-BLWXWFJK.mjs';
2
2
  export { MetricsRegistry } from './chunk-O4YGOZSW.mjs';
3
- import { isDirectory, UserError, isRedirectedRawConfig, dedent, configFileName, formatConfigSnippet, FatalError, readFileSync, parseTOML, modify, applyEdits, format, dist_default, parseJSONC } from './chunk-A4F3D336.mjs';
4
- export { APIError, CommandLineArgsError, DeprecationError, FatalError, JsonFriendlyFatalError, MissingConfigError, ParseError, UserError, configFileName, configFormat, createFatalError, experimental_readRawConfig, findWranglerConfig, formatConfigSnippet, indexLocation, isDirectory, parseByteSize, parseHumanDuration, parseJSON, parseJSONC, parseNonHyphenedUuid, parsePackageJSON, parseTOML, readFileSync, readFileSyncToBuffer, removeDir, removeDirSync, resolveWranglerConfigPath, searchLocation } from './chunk-A4F3D336.mjs';
3
+ import { isDirectory, UserError, isRedirectedRawConfig, dedent, configFileName, formatConfigSnippet, FatalError, removeDirSync, readFileSync, parseTOML, modify, applyEdits, format, dist_default, parseJSONC } from './chunk-XXCQEG76.mjs';
4
+ export { APIError, CommandLineArgsError, DeprecationError, FatalError, JsonFriendlyFatalError, MissingConfigError, ParseError, UserError, configFileName, configFormat, createFatalError, experimental_readRawConfig, findWranglerConfig, formatConfigSnippet, indexLocation, isDirectory, parseByteSize, parseHumanDuration, parseJSON, parseJSONC, parseNonHyphenedUuid, parsePackageJSON, parseTOML, readFileSync, readFileSyncToBuffer, removeDir, removeDirSync, resolveWranglerConfigPath, searchLocation } from './chunk-XXCQEG76.mjs';
5
5
  export { ENVIRONMENT_TAG_PREFIX, INHERIT_SYMBOL, JSON_CONFIG_FORMATS, PATH_TO_DEPLOY_CONFIG, SERVICE_TAG_PREFIX } from './chunk-OZQVB3L3.mjs';
6
6
  import { __commonJS, __name, __require, __export, __toESM, __reExport } from './chunk-DCOBXSFB.mjs';
7
- import fs, { writeFileSync } from 'node:fs';
7
+ import fs, { accessSync, constants, writeFileSync, renameSync, existsSync, unlinkSync, mkdirSync, chmodSync } from 'node:fs';
8
8
  import assert from 'node:assert';
9
- import path3 from 'node:path';
10
- import os from 'node:os';
9
+ import path3, { join, dirname } from 'node:path';
10
+ import os, { arch } from 'node:os';
11
+ import { execFileSync, spawn } from 'node:child_process';
12
+ import { createHash } from 'node:crypto';
13
+ import { fetch } from 'undici';
11
14
 
12
15
  // ../../node_modules/.pnpm/xdg-app-paths@8.3.0/node_modules/xdg-app-paths/dist/cjs/lib/XDGAppPaths.js
13
16
  var require_XDGAppPaths = __commonJS({
@@ -515,6 +518,160 @@ var require_mod_cjs3 = __commonJS({
515
518
  }
516
519
  });
517
520
 
521
+ // ../../node_modules/.pnpm/command-exists@1.2.9/node_modules/command-exists/lib/command-exists.js
522
+ var require_command_exists = __commonJS({
523
+ "../../node_modules/.pnpm/command-exists@1.2.9/node_modules/command-exists/lib/command-exists.js"(exports, module) {
524
+ var exec = __require("child_process").exec;
525
+ var execSync = __require("child_process").execSync;
526
+ var fs2 = __require("fs");
527
+ var path4 = __require("path");
528
+ var access = fs2.access;
529
+ var accessSync2 = fs2.accessSync;
530
+ var constants2 = fs2.constants || fs2;
531
+ var isUsingWindows = process.platform == "win32";
532
+ var fileNotExists = /* @__PURE__ */ __name(function(commandName, callback) {
533
+ access(
534
+ commandName,
535
+ constants2.F_OK,
536
+ function(err) {
537
+ callback(!err);
538
+ }
539
+ );
540
+ }, "fileNotExists");
541
+ var fileNotExistsSync = /* @__PURE__ */ __name(function(commandName) {
542
+ try {
543
+ accessSync2(commandName, constants2.F_OK);
544
+ return false;
545
+ } catch (e2) {
546
+ return true;
547
+ }
548
+ }, "fileNotExistsSync");
549
+ var localExecutable = /* @__PURE__ */ __name(function(commandName, callback) {
550
+ access(
551
+ commandName,
552
+ constants2.F_OK | constants2.X_OK,
553
+ function(err) {
554
+ callback(null, !err);
555
+ }
556
+ );
557
+ }, "localExecutable");
558
+ var localExecutableSync = /* @__PURE__ */ __name(function(commandName) {
559
+ try {
560
+ accessSync2(commandName, constants2.F_OK | constants2.X_OK);
561
+ return true;
562
+ } catch (e2) {
563
+ return false;
564
+ }
565
+ }, "localExecutableSync");
566
+ var commandExistsUnix = /* @__PURE__ */ __name(function(commandName, cleanedCommandName, callback) {
567
+ fileNotExists(commandName, function(isFile) {
568
+ if (!isFile) {
569
+ exec(
570
+ "command -v " + cleanedCommandName + " 2>/dev/null && { echo >&1 " + cleanedCommandName + "; exit 0; }",
571
+ function(error, stdout, stderr) {
572
+ callback(null, !!stdout);
573
+ }
574
+ );
575
+ return;
576
+ }
577
+ localExecutable(commandName, callback);
578
+ });
579
+ }, "commandExistsUnix");
580
+ var commandExistsWindows = /* @__PURE__ */ __name(function(commandName, cleanedCommandName, callback) {
581
+ if (!/^(?!(?:.*\s|.*\.|\W+)$)(?:[a-zA-Z]:)?(?:(?:[^<>:"\|\?\*\n])+(?:\/\/|\/|\\\\|\\)?)+$/m.test(commandName)) {
582
+ callback(null, false);
583
+ return;
584
+ }
585
+ exec(
586
+ "where " + cleanedCommandName,
587
+ function(error) {
588
+ if (error !== null) {
589
+ callback(null, false);
590
+ } else {
591
+ callback(null, true);
592
+ }
593
+ }
594
+ );
595
+ }, "commandExistsWindows");
596
+ var commandExistsUnixSync = /* @__PURE__ */ __name(function(commandName, cleanedCommandName) {
597
+ if (fileNotExistsSync(commandName)) {
598
+ try {
599
+ var stdout = execSync(
600
+ "command -v " + cleanedCommandName + " 2>/dev/null && { echo >&1 " + cleanedCommandName + "; exit 0; }"
601
+ );
602
+ return !!stdout;
603
+ } catch (error) {
604
+ return false;
605
+ }
606
+ }
607
+ return localExecutableSync(commandName);
608
+ }, "commandExistsUnixSync");
609
+ var commandExistsWindowsSync = /* @__PURE__ */ __name(function(commandName, cleanedCommandName, callback) {
610
+ if (!/^(?!(?:.*\s|.*\.|\W+)$)(?:[a-zA-Z]:)?(?:(?:[^<>:"\|\?\*\n])+(?:\/\/|\/|\\\\|\\)?)+$/m.test(commandName)) {
611
+ return false;
612
+ }
613
+ try {
614
+ var stdout = execSync("where " + cleanedCommandName, { stdio: [] });
615
+ return !!stdout;
616
+ } catch (error) {
617
+ return false;
618
+ }
619
+ }, "commandExistsWindowsSync");
620
+ var cleanInput = /* @__PURE__ */ __name(function(s) {
621
+ if (/[^A-Za-z0-9_\/:=-]/.test(s)) {
622
+ s = "'" + s.replace(/'/g, "'\\''") + "'";
623
+ s = s.replace(/^(?:'')+/g, "").replace(/\\'''/g, "\\'");
624
+ }
625
+ return s;
626
+ }, "cleanInput");
627
+ if (isUsingWindows) {
628
+ cleanInput = /* @__PURE__ */ __name(function(s) {
629
+ var isPathName = /[\\]/.test(s);
630
+ if (isPathName) {
631
+ var dirname2 = '"' + path4.dirname(s) + '"';
632
+ var basename = '"' + path4.basename(s) + '"';
633
+ return dirname2 + ":" + basename;
634
+ }
635
+ return '"' + s + '"';
636
+ }, "cleanInput");
637
+ }
638
+ module.exports = /* @__PURE__ */ __name(function commandExists(commandName, callback) {
639
+ var cleanedCommandName = cleanInput(commandName);
640
+ if (!callback && typeof Promise !== "undefined") {
641
+ return new Promise(function(resolve, reject) {
642
+ commandExists(commandName, function(error, output) {
643
+ if (output) {
644
+ resolve(commandName);
645
+ } else {
646
+ reject(error);
647
+ }
648
+ });
649
+ });
650
+ }
651
+ if (isUsingWindows) {
652
+ commandExistsWindows(commandName, cleanedCommandName, callback);
653
+ } else {
654
+ commandExistsUnix(commandName, cleanedCommandName, callback);
655
+ }
656
+ }, "commandExists");
657
+ module.exports.sync = function(commandName) {
658
+ var cleanedCommandName = cleanInput(commandName);
659
+ if (isUsingWindows) {
660
+ return commandExistsWindowsSync(commandName, cleanedCommandName);
661
+ } else {
662
+ return commandExistsUnixSync(commandName, cleanedCommandName);
663
+ }
664
+ };
665
+ }
666
+ });
667
+
668
+ // ../../node_modules/.pnpm/command-exists@1.2.9/node_modules/command-exists/index.js
669
+ var require_command_exists2 = __commonJS({
670
+ "../../node_modules/.pnpm/command-exists@1.2.9/node_modules/command-exists/index.js"(exports, module) {
671
+ module.exports = require_command_exists();
672
+ }
673
+ });
674
+
518
675
  // src/config/config.ts
519
676
  var defaultWranglerConfig = {
520
677
  /* COMPUTED_FIELDS */
@@ -4608,7 +4765,8 @@ function getBooleanEnvironmentVariableFactory(options) {
4608
4765
  throw new UserError(
4609
4766
  `Expected ${options.variableName} to be "true" or "false", but got ${JSON.stringify(
4610
4767
  process.env[options.variableName]
4611
- )}`
4768
+ )}`,
4769
+ { telemetryMessage: false }
4612
4770
  );
4613
4771
  }
4614
4772
  };
@@ -4646,7 +4804,8 @@ __name(getProcessEnv, "getProcessEnv");
4646
4804
  function assertOneOf(choices, value) {
4647
4805
  if (Array.isArray(choices) && !choices.includes(value)) {
4648
4806
  throw new UserError(
4649
- `Expected ${JSON.stringify(value)} to be one of ${JSON.stringify(choices)}`
4807
+ `Expected ${JSON.stringify(value)} to be one of ${JSON.stringify(choices)}`,
4808
+ { telemetryMessage: false }
4650
4809
  );
4651
4810
  }
4652
4811
  }
@@ -4683,11 +4842,14 @@ var getCloudflareComplianceRegionFromEnv = getEnvironmentVariableFactory({
4683
4842
  var getCloudflareComplianceRegion = /* @__PURE__ */ __name((complianceConfig) => {
4684
4843
  const complianceRegionFromEnv = getCloudflareComplianceRegionFromEnv();
4685
4844
  if (complianceRegionFromEnv !== void 0 && complianceConfig?.compliance_region !== void 0 && complianceRegionFromEnv !== complianceConfig.compliance_region) {
4686
- throw new UserError(dedent`
4845
+ throw new UserError(
4846
+ dedent`
4687
4847
  The compliance region has been set to different values in two places:
4688
4848
  - \`CLOUDFLARE_COMPLIANCE_REGION\` environment variable: \`${complianceRegionFromEnv}\`
4689
4849
  - \`compliance_region\` configuration property: \`${complianceConfig.compliance_region}\`
4690
- `);
4850
+ `,
4851
+ { telemetryMessage: false }
4852
+ );
4691
4853
  }
4692
4854
  return complianceRegionFromEnv || complianceConfig?.compliance_region || "public";
4693
4855
  }, "getCloudflareComplianceRegion");
@@ -5528,7 +5690,8 @@ function applyPythonConfig(config, args) {
5528
5690
  }
5529
5691
  if (!config.compatibility_flags.includes("python_workers")) {
5530
5692
  throw new UserError(
5531
- "The `python_workers` compatibility flag is required to use Python."
5693
+ "The `python_workers` compatibility flag is required to use Python.",
5694
+ { telemetryMessage: false }
5532
5695
  );
5533
5696
  }
5534
5697
  }
@@ -6166,7 +6329,6 @@ function normalizeAndValidateEnvironment(diagnostics, configPath, rawEnv, isDisp
6166
6329
  "error"
6167
6330
  );
6168
6331
  experimental(diagnostics, rawEnv, "unsafe");
6169
- experimental(diagnostics, rawEnv, "secrets");
6170
6332
  const route = normalizeAndValidateRoute(diagnostics, topLevelEnv, rawEnv);
6171
6333
  const account_id = inheritableInWranglerEnvironments(
6172
6334
  diagnostics,
@@ -9245,6 +9407,7 @@ var validatePreviewsConfig = /* @__PURE__ */ __name((envName) => (diagnostics, f
9245
9407
  "secrets_store_secrets",
9246
9408
  "artifacts",
9247
9409
  "unsafe_hello_world",
9410
+ "flagship",
9248
9411
  "worker_loaders",
9249
9412
  "ratelimits",
9250
9413
  "vpc_services",
@@ -9425,6 +9588,12 @@ var validatePreviewsConfig = /* @__PURE__ */ __name((envName) => (diagnostics, f
9425
9588
  previews.unsafe_hello_world,
9426
9589
  void 0
9427
9590
  ) && isValid2;
9591
+ isValid2 = validateBindingArray(envName, validateFlagshipBinding)(
9592
+ diagnostics,
9593
+ `${field}.flagship`,
9594
+ previews.flagship,
9595
+ void 0
9596
+ ) && isValid2;
9428
9597
  isValid2 = validateBindingArray(envName, validateWorkerLoaderBinding)(
9429
9598
  diagnostics,
9430
9599
  `${field}.worker_loaders`,
@@ -9798,7 +9967,8 @@ function isDockerfile(imagePath, configPath) {
9798
9967
  if (fs.existsSync(maybeDockerfile)) {
9799
9968
  if (isDirectory(maybeDockerfile)) {
9800
9969
  throw new UserError(
9801
- `${imagePath} is a directory, you should specify a path to the Dockerfile`
9970
+ `${imagePath} is a directory, you should specify a path to the Dockerfile`,
9971
+ { telemetryMessage: false }
9802
9972
  );
9803
9973
  }
9804
9974
  return true;
@@ -9809,19 +9979,23 @@ function isDockerfile(imagePath, configPath) {
9809
9979
  new URL(`https://${imagePath}`);
9810
9980
  } catch (e2) {
9811
9981
  if (e2 instanceof Error) {
9812
- throw new UserError(errorPrefix + e2.message);
9982
+ throw new UserError(errorPrefix + e2.message, {
9983
+ telemetryMessage: false
9984
+ });
9813
9985
  }
9814
9986
  throw e2;
9815
9987
  }
9816
9988
  const imageParts = imagePath.split("/");
9817
9989
  if (!imageParts[imageParts.length - 1]?.includes(":")) {
9818
9990
  throw new UserError(
9819
- errorPrefix + `If this is an image registry path, it needs to include at least a tag ':' (e.g: docker.io/httpd:1)`
9991
+ errorPrefix + `If this is an image registry path, it needs to include at least a tag ':' (e.g: docker.io/httpd:1)`,
9992
+ { telemetryMessage: false }
9820
9993
  );
9821
9994
  }
9822
9995
  if (imagePath.includes("://")) {
9823
9996
  throw new UserError(
9824
- errorPrefix + `Image reference should not include the protocol part (e.g: docker.io/httpd:1, not https://docker.io/httpd:1)`
9997
+ errorPrefix + `Image reference should not include the protocol part (e.g: docker.io/httpd:1, not https://docker.io/httpd:1)`,
9998
+ { telemetryMessage: false }
9825
9999
  );
9826
10000
  }
9827
10001
  return false;
@@ -9864,8 +10038,11 @@ var supportedPagesConfigFields = [
9864
10038
  ];
9865
10039
  function validatePagesConfig(config, envNames, projectName) {
9866
10040
  if (!config.pages_build_output_dir) {
9867
- throw new FatalError(`Attempting to validate Pages configuration file, but "pages_build_output_dir" field was not found.
9868
- "pages_build_output_dir" is required for Pages projects.`);
10041
+ throw new FatalError(
10042
+ `Attempting to validate Pages configuration file, but "pages_build_output_dir" field was not found.
10043
+ "pages_build_output_dir" is required for Pages projects.`,
10044
+ { telemetryMessage: false }
10045
+ );
9869
10046
  }
9870
10047
  const diagnostics = new Diagnostics(
9871
10048
  `Running configuration file validation for Pages:`
@@ -9956,4 +10133,754 @@ Pages requires Durable Object bindings to specify the name of the Worker where t
9956
10133
  }
9957
10134
  __name(validateDurableObjectBinding2, "validateDurableObjectBinding");
9958
10135
 
9959
- export { COMPLIANCE_REGION_CONFIG_PUBLIC, COMPLIANCE_REGION_CONFIG_UNKNOWN, Diagnostics, PatchConfigError, bucketFormatMessage, defaultWranglerConfig, experimental_patchConfig, friendlyBindingNames, getBindingTypeFriendlyName, getBooleanEnvironmentVariableFactory, getBrowserRenderingHeadfulFromEnv, getBuildConditionsFromEnv, getBuildPlatformFromEnv, getC3CommandFromEnv, getCIGeneratePreviewAlias, getCIMatchTag, getCIOverrideName, getCIOverrideNetworkModeHost, getCfFetchEnabledFromEnv, getCfFetchPathFromEnv, getCloudflareApiBaseUrl, getCloudflareApiEnvironmentFromEnv, getCloudflareComplianceRegion, getCloudflareEnv, getCloudflareIncludeProcessEnvFromEnv, getCloudflareLoadDevVarsFromDotEnv, getCloudflaredPathFromEnv, getComplianceRegionSubdomain, getD1ExtraLocationChoices, getDisableConfigWatching, getDockerPath, getEnvironmentVariableFactory, getGlobalWranglerConfigPath, getLocalExplorerEnabledFromEnv, getOpenNextDeployFromEnv, getOutputFileDirectoryFromEnv, getOutputFilePathFromEnv, getRegistryPath, getSanitizeLogs, getSubdomainMixedStateCheckDisabled, getTraceHeader, getWorkersCIBranchName, getWranglerCacheDirFromEnv, getWranglerHideBanner, getWranglerSendErrorReportsFromEnv, getWranglerSendMetricsFromEnv, hasProperty, isDockerfile, isOptionalProperty, isPagesConfig, isRequiredProperty, isValidR2BucketName, normalizeAndValidateConfig, validatePagesConfig };
10136
+ // src/cloudflared.ts
10137
+ var import_command_exists = __toESM(require_command_exists2());
10138
+ var UPDATE_SERVICE_URL = "https://update.argotunnel.com";
10139
+ var CLOUDFLARED_VERSION_PATTERN = /^\d{4}\.\d+\.\d+$/;
10140
+ function sha256Hex(buffer) {
10141
+ return createHash("sha256").update(buffer).digest("hex");
10142
+ }
10143
+ __name(sha256Hex, "sha256Hex");
10144
+ function getGoArch() {
10145
+ const nodeArch = arch();
10146
+ switch (nodeArch) {
10147
+ case "x64":
10148
+ return "amd64";
10149
+ case "arm64":
10150
+ return "arm64";
10151
+ case "arm":
10152
+ return "arm";
10153
+ default:
10154
+ throw new UserError(
10155
+ `Unsupported architecture for cloudflared: ${nodeArch}
10156
+
10157
+ cloudflared supports: x64 (amd64), arm64, arm
10158
+
10159
+ You can manually install cloudflared and set the CLOUDFLARED_PATH environment variable.
10160
+ Download instructions: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`,
10161
+ { telemetryMessage: "tunnel cloudflared unsupported architecture" }
10162
+ );
10163
+ }
10164
+ }
10165
+ __name(getGoArch, "getGoArch");
10166
+ function getGoOS() {
10167
+ switch (process.platform) {
10168
+ case "darwin":
10169
+ return "darwin";
10170
+ case "linux":
10171
+ return "linux";
10172
+ case "win32":
10173
+ return "windows";
10174
+ default:
10175
+ throw new UserError(
10176
+ `Unsupported platform for cloudflared: ${process.platform}
10177
+
10178
+ cloudflared supports: darwin (macOS), linux, win32 (Windows)
10179
+
10180
+ You can manually install cloudflared and set the CLOUDFLARED_PATH environment variable.
10181
+ Download instructions: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`,
10182
+ { telemetryMessage: "tunnel cloudflared unsupported platform" }
10183
+ );
10184
+ }
10185
+ }
10186
+ __name(getGoOS, "getGoOS");
10187
+ var GITHUB_RELEASE_BASE = "https://github.com/cloudflare/cloudflared/releases/download";
10188
+ function getAssetFilename(goOS, goArch) {
10189
+ if (goOS === "windows") {
10190
+ return `cloudflared-${goOS}-${goArch}.exe`;
10191
+ }
10192
+ if (goOS === "darwin") {
10193
+ return `cloudflared-${goOS}-${goArch}.tgz`;
10194
+ }
10195
+ return `cloudflared-${goOS}-${goArch}`;
10196
+ }
10197
+ __name(getAssetFilename, "getAssetFilename");
10198
+ async function queryUpdateService(goOS, goArch, options) {
10199
+ const { logger } = options ?? {};
10200
+ const url = new URL(UPDATE_SERVICE_URL);
10201
+ url.searchParams.set("os", goOS);
10202
+ url.searchParams.set("arch", goArch);
10203
+ logger?.debug(`Checking for latest cloudflared: ${url.toString()}`);
10204
+ let response;
10205
+ try {
10206
+ response = await fetch(url.toString(), {
10207
+ headers: { "User-Agent": "wrangler" }
10208
+ });
10209
+ } catch (e2) {
10210
+ logger?.debug(
10211
+ `Failed to reach update service: ${e2 instanceof Error ? e2.message : String(e2)}`
10212
+ );
10213
+ return null;
10214
+ }
10215
+ if (!response.ok) {
10216
+ logger?.debug(
10217
+ `Update service returned ${response.status} for ${goOS}/${goArch}`
10218
+ );
10219
+ return null;
10220
+ }
10221
+ let data;
10222
+ try {
10223
+ data = await response.json();
10224
+ } catch (e2) {
10225
+ logger?.debug(
10226
+ `Update service returned non-JSON response: ${e2 instanceof Error ? e2.message : String(e2)}`
10227
+ );
10228
+ return null;
10229
+ }
10230
+ if (typeof data.version === "string" && !CLOUDFLARED_VERSION_PATTERN.test(data.version)) {
10231
+ throw new Error(
10232
+ `[cloudflared] Invalid cloudflared version returned by update service: ${data.version}`
10233
+ );
10234
+ }
10235
+ if (data.error || !data.url || !data.version) {
10236
+ return data.version ? data : null;
10237
+ }
10238
+ return data;
10239
+ }
10240
+ __name(queryUpdateService, "queryUpdateService");
10241
+ async function getLatestVersionInfo(options) {
10242
+ const { logger } = options ?? {};
10243
+ const goOS = getGoOS();
10244
+ const goArch = getGoArch();
10245
+ const primary = await queryUpdateService(goOS, goArch, { logger });
10246
+ if (primary && primary.url && primary.version) {
10247
+ return primary;
10248
+ }
10249
+ logger?.debug(
10250
+ `Update worker had no result for ${goOS}/${goArch}, falling back to GitHub release URL`
10251
+ );
10252
+ const fallback = await queryUpdateService("linux", "amd64", { logger });
10253
+ if (!fallback?.version) {
10254
+ throw new UserError(
10255
+ `[cloudflared] Failed to determine the latest cloudflared version.
10256
+
10257
+ The update service did not return results for ${goOS}/${goArch},
10258
+ and the fallback query also failed.
10259
+
10260
+ You can manually install cloudflared from:
10261
+ https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`,
10262
+ { telemetryMessage: "tunnel cloudflared version lookup failed" }
10263
+ );
10264
+ }
10265
+ const version = fallback.version;
10266
+ const filename = getAssetFilename(goOS, goArch);
10267
+ const url = `${GITHUB_RELEASE_BASE}/${version}/${filename}`;
10268
+ const compressed = filename.endsWith(".tgz");
10269
+ return {
10270
+ url,
10271
+ version,
10272
+ checksum: "",
10273
+ // no checksum available for fallback URLs
10274
+ compressed,
10275
+ shouldUpdate: true,
10276
+ userMessage: "",
10277
+ error: ""
10278
+ };
10279
+ }
10280
+ __name(getLatestVersionInfo, "getLatestVersionInfo");
10281
+ function getCacheDir(version) {
10282
+ return join(getGlobalWranglerConfigPath(), "cloudflared", version);
10283
+ }
10284
+ __name(getCacheDir, "getCacheDir");
10285
+ function getCloudflaredBinPath(version) {
10286
+ const binName = process.platform === "win32" ? "cloudflared.exe" : "cloudflared";
10287
+ return join(getCacheDir(version), binName);
10288
+ }
10289
+ __name(getCloudflaredBinPath, "getCloudflaredBinPath");
10290
+ function isBinaryExecutable(binPath) {
10291
+ try {
10292
+ accessSync(binPath, constants.X_OK);
10293
+ return true;
10294
+ } catch {
10295
+ return false;
10296
+ }
10297
+ }
10298
+ __name(isBinaryExecutable, "isBinaryExecutable");
10299
+ function validateBinary(binPath, options) {
10300
+ const { logger } = options ?? {};
10301
+ try {
10302
+ const output = execFileSync(binPath, ["--version"], {
10303
+ stdio: ["pipe", "pipe", "pipe"],
10304
+ timeout: 1e4,
10305
+ encoding: "utf8"
10306
+ }).trim();
10307
+ logger?.debug(`cloudflared version: ${output}`);
10308
+ } catch {
10309
+ let errorMessage = `[cloudflared] Failed to validate cloudflared binary at ${binPath}
10310
+
10311
+ `;
10312
+ errorMessage += `This usually means:
10313
+ `;
10314
+ errorMessage += ` - The binary is corrupted or incomplete
10315
+ `;
10316
+ errorMessage += ` - You're missing required system libraries
10317
+ `;
10318
+ if (process.platform === "linux") {
10319
+ errorMessage += `
10320
+ On Linux, make sure you have the required dependencies:
10321
+ `;
10322
+ errorMessage += ` - glibc (GNU C Library)
10323
+ `;
10324
+ errorMessage += ` - For Debian/Ubuntu: sudo apt-get install libc6
10325
+ `;
10326
+ }
10327
+ const cacheDir = join(getGlobalWranglerConfigPath(), "cloudflared");
10328
+ errorMessage += `
10329
+ You can try:
10330
+ `;
10331
+ errorMessage += ` 1. Deleting the cache directory: rm -rf ${cacheDir}
10332
+ `;
10333
+ errorMessage += ` 2. Running the command again to re-download
10334
+ `;
10335
+ errorMessage += ` 3. Manually installing cloudflared: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/
10336
+ `;
10337
+ errorMessage += ` 4. Setting CLOUDFLARED_PATH to point to your cloudflared binary`;
10338
+ throw new UserError(errorMessage, {
10339
+ telemetryMessage: "tunnel cloudflared validation failed"
10340
+ });
10341
+ }
10342
+ }
10343
+ __name(validateBinary, "validateBinary");
10344
+ function redactCloudflaredArgsForLogging(args) {
10345
+ const redacted = [...args];
10346
+ for (let i = 0; i < redacted.length; i++) {
10347
+ const arg = redacted[i];
10348
+ if (arg === "--token" && i + 1 < redacted.length) {
10349
+ redacted[i + 1] = "[REDACTED]";
10350
+ }
10351
+ if (arg.startsWith("--token=")) {
10352
+ redacted[i] = "--token=[REDACTED]";
10353
+ }
10354
+ }
10355
+ return redacted;
10356
+ }
10357
+ __name(redactCloudflaredArgsForLogging, "redactCloudflaredArgsForLogging");
10358
+ function tryGetCloudflaredFromPath(options) {
10359
+ const { logger } = options ?? {};
10360
+ if (!(0, import_command_exists.sync)("cloudflared")) {
10361
+ return null;
10362
+ }
10363
+ try {
10364
+ validateBinary("cloudflared", { logger });
10365
+ return "cloudflared";
10366
+ } catch (e2) {
10367
+ logger?.debug("cloudflared found in PATH but failed validation", e2);
10368
+ return null;
10369
+ }
10370
+ }
10371
+ __name(tryGetCloudflaredFromPath, "tryGetCloudflaredFromPath");
10372
+ function getInstalledVersion(binPath) {
10373
+ try {
10374
+ const output = execFileSync(binPath, ["--version"], {
10375
+ stdio: ["pipe", "pipe", "pipe"],
10376
+ timeout: 1e4,
10377
+ encoding: "utf8"
10378
+ });
10379
+ const match = output.match(/(\d+\.\d+\.\d+)/);
10380
+ return match ? match[1] : null;
10381
+ } catch {
10382
+ return null;
10383
+ }
10384
+ }
10385
+ __name(getInstalledVersion, "getInstalledVersion");
10386
+ function isVersionOutdated(installed, latest) {
10387
+ const parse = /* @__PURE__ */ __name((v) => v.split(".").map(Number), "parse");
10388
+ const [iYear, iMonth, iPatch] = parse(installed);
10389
+ const [lYear, lMonth, lPatch] = parse(latest);
10390
+ if (iYear !== lYear) {
10391
+ return iYear < lYear;
10392
+ }
10393
+ if (iMonth !== lMonth) {
10394
+ return iMonth < lMonth;
10395
+ }
10396
+ return iPatch < lPatch;
10397
+ }
10398
+ __name(isVersionOutdated, "isVersionOutdated");
10399
+ async function warnIfOutdated(binPath, options) {
10400
+ const { logger } = options ?? {};
10401
+ try {
10402
+ const installed = getInstalledVersion(binPath);
10403
+ if (!installed) {
10404
+ return;
10405
+ }
10406
+ const latest = await queryUpdateService(getGoOS(), getGoArch(), { logger }) ?? await queryUpdateService("linux", "amd64", { logger });
10407
+ const latestVersion = latest?.version;
10408
+ if (!latestVersion) {
10409
+ return;
10410
+ }
10411
+ if (isVersionOutdated(installed, latestVersion)) {
10412
+ logger?.warn(
10413
+ `Your cloudflared (${installed}) is outdated. Latest version is ${latestVersion}.
10414
+ Update: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`
10415
+ );
10416
+ }
10417
+ } catch (e2) {
10418
+ logger?.debug("Failed to check cloudflared version", e2);
10419
+ }
10420
+ }
10421
+ __name(warnIfOutdated, "warnIfOutdated");
10422
+ function writeFileAtomic(filePath, contents) {
10423
+ const dir = dirname(filePath);
10424
+ const tmpPath = join(
10425
+ dir,
10426
+ `.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`
10427
+ );
10428
+ try {
10429
+ writeFileSync(tmpPath, contents);
10430
+ renameSync(tmpPath, filePath);
10431
+ } finally {
10432
+ try {
10433
+ if (existsSync(tmpPath)) {
10434
+ unlinkSync(tmpPath);
10435
+ }
10436
+ } catch {
10437
+ }
10438
+ }
10439
+ }
10440
+ __name(writeFileAtomic, "writeFileAtomic");
10441
+ async function downloadCloudflared(versionInfo, binPath, options) {
10442
+ const { logger } = options ?? {};
10443
+ const { url, version, checksum, compressed } = versionInfo;
10444
+ logger?.log(`Downloading cloudflared ${version}...`);
10445
+ logger?.debug(`Download URL: ${url}`);
10446
+ const cacheDir = dirname(binPath);
10447
+ mkdirSync(cacheDir, { recursive: true });
10448
+ let response;
10449
+ try {
10450
+ response = await fetch(url, {
10451
+ headers: { "User-Agent": "wrangler" }
10452
+ });
10453
+ } catch (e2) {
10454
+ throw new UserError(
10455
+ `[cloudflared] Failed to download cloudflared from ${url}
10456
+
10457
+ Network error: ${e2 instanceof Error ? e2.message : String(e2)}
10458
+
10459
+ Please check your internet connection and try again.
10460
+ If you're behind a proxy, make sure it's configured correctly.`,
10461
+ { telemetryMessage: "tunnel cloudflared download network failed" }
10462
+ );
10463
+ }
10464
+ if (!response.ok) {
10465
+ throw new UserError(
10466
+ `[cloudflared] Failed to download cloudflared from ${url}
10467
+
10468
+ HTTP ${response.status}: ${response.statusText}
10469
+
10470
+ You can manually download cloudflared from:
10471
+ https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`,
10472
+ { telemetryMessage: "tunnel cloudflared download response failed" }
10473
+ );
10474
+ }
10475
+ try {
10476
+ if (compressed) {
10477
+ await downloadAndExtractTarball(response, checksum, binPath, cacheDir);
10478
+ } else {
10479
+ await downloadBinary(response, checksum, binPath);
10480
+ }
10481
+ } catch (e2) {
10482
+ try {
10483
+ if (existsSync(binPath)) {
10484
+ unlinkSync(binPath);
10485
+ }
10486
+ } catch {
10487
+ }
10488
+ if (e2 instanceof UserError) {
10489
+ throw e2;
10490
+ }
10491
+ throw new UserError(
10492
+ `[cloudflared] Failed to save cloudflared binary
10493
+
10494
+ Error: ${e2 instanceof Error ? e2.message : String(e2)}
10495
+
10496
+ Please ensure you have write permissions to: ${cacheDir}`,
10497
+ { telemetryMessage: "tunnel cloudflared save failed" }
10498
+ );
10499
+ }
10500
+ if (process.platform !== "win32") {
10501
+ chmodSync(binPath, 493);
10502
+ }
10503
+ logger?.log(`cloudflared ${version} installed`);
10504
+ logger?.debug(`Binary location: ${binPath}`);
10505
+ }
10506
+ __name(downloadCloudflared, "downloadCloudflared");
10507
+ async function downloadAndExtractTarball(response, expectedChecksum, binPath, cacheDir) {
10508
+ const tempTarPath = join(cacheDir, "cloudflared.tgz");
10509
+ const buffer = Buffer.from(await response.arrayBuffer());
10510
+ if (expectedChecksum) {
10511
+ const actualSha256 = sha256Hex(buffer);
10512
+ if (actualSha256 !== expectedChecksum) {
10513
+ throw new UserError(
10514
+ `[cloudflared] SHA256 mismatch for downloaded cloudflared tarball.
10515
+
10516
+ Expected: ${expectedChecksum}
10517
+ Actual: ${actualSha256}`,
10518
+ { telemetryMessage: "tunnel cloudflared tarball checksum mismatch" }
10519
+ );
10520
+ }
10521
+ }
10522
+ writeFileSync(tempTarPath, buffer);
10523
+ try {
10524
+ execFileSync("tar", ["-xzf", tempTarPath, "-C", cacheDir], {
10525
+ stdio: "ignore"
10526
+ });
10527
+ const extractedPath = join(cacheDir, "cloudflared");
10528
+ if (extractedPath !== binPath && existsSync(extractedPath)) {
10529
+ renameSync(extractedPath, binPath);
10530
+ }
10531
+ } finally {
10532
+ try {
10533
+ if (existsSync(tempTarPath)) {
10534
+ unlinkSync(tempTarPath);
10535
+ }
10536
+ } catch {
10537
+ }
10538
+ }
10539
+ }
10540
+ __name(downloadAndExtractTarball, "downloadAndExtractTarball");
10541
+ async function downloadBinary(response, expectedChecksum, binPath) {
10542
+ const buffer = Buffer.from(await response.arrayBuffer());
10543
+ if (expectedChecksum) {
10544
+ const actualSha256 = sha256Hex(buffer);
10545
+ if (actualSha256 !== expectedChecksum) {
10546
+ throw new UserError(
10547
+ `[cloudflared] SHA256 mismatch for downloaded cloudflared binary.
10548
+
10549
+ Expected: ${expectedChecksum}
10550
+ Actual: ${actualSha256}`,
10551
+ { telemetryMessage: "tunnel cloudflared binary checksum mismatch" }
10552
+ );
10553
+ }
10554
+ }
10555
+ writeFileAtomic(binPath, buffer);
10556
+ }
10557
+ __name(downloadBinary, "downloadBinary");
10558
+ async function getCloudflaredPath(options) {
10559
+ const logger = options?.logger;
10560
+ const envPath = getCloudflaredPathFromEnv();
10561
+ if (envPath) {
10562
+ if (!existsSync(envPath)) {
10563
+ throw new UserError(
10564
+ `CLOUDFLARED_PATH is set to "${envPath}" but the file does not exist.
10565
+
10566
+ Please ensure the path points to a valid cloudflared binary.`,
10567
+ { telemetryMessage: "tunnel cloudflared env path missing" }
10568
+ );
10569
+ }
10570
+ logger?.debug(`Using cloudflared from CLOUDFLARED_PATH: ${envPath}`);
10571
+ return envPath;
10572
+ }
10573
+ const pathBin = tryGetCloudflaredFromPath({ logger });
10574
+ if (pathBin) {
10575
+ logger?.debug("Using cloudflared from PATH");
10576
+ if (!options?.skipVersionCheck) {
10577
+ await warnIfOutdated(pathBin, { logger });
10578
+ }
10579
+ return pathBin;
10580
+ }
10581
+ const versionInfo = await getLatestVersionInfo({ logger });
10582
+ const binPath = getCloudflaredBinPath(versionInfo.version);
10583
+ let needsDownload = !isBinaryExecutable(binPath);
10584
+ if (!needsDownload) {
10585
+ try {
10586
+ validateBinary(binPath, { logger });
10587
+ logger?.debug(
10588
+ `Using cached cloudflared ${versionInfo.version}: ${binPath}`
10589
+ );
10590
+ return binPath;
10591
+ } catch (e2) {
10592
+ logger?.debug("Cached cloudflared failed validation; re-downloading", e2);
10593
+ needsDownload = true;
10594
+ }
10595
+ }
10596
+ const shouldDownload = await options?.confirmDownload?.(
10597
+ `cloudflared (${versionInfo.version}) is needed but not installed. Download to ${binPath}?`
10598
+ ) ?? true;
10599
+ if (!shouldDownload) {
10600
+ throw new UserError(
10601
+ `cloudflared is required to run this command.
10602
+
10603
+ You can install it manually from:
10604
+ https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/
10605
+
10606
+ Then either add it to your PATH or set CLOUDFLARED_PATH.`,
10607
+ { telemetryMessage: "tunnel cloudflared download declined" }
10608
+ );
10609
+ }
10610
+ if (existsSync(binPath)) {
10611
+ const cacheDir = removeCloudflaredCache(versionInfo.version);
10612
+ if (cacheDir) {
10613
+ logger?.log(`Removed cloudflared cache: ${cacheDir}`);
10614
+ }
10615
+ }
10616
+ await downloadCloudflared(versionInfo, binPath, { logger });
10617
+ validateBinary(binPath, { logger });
10618
+ return binPath;
10619
+ }
10620
+ __name(getCloudflaredPath, "getCloudflaredPath");
10621
+ async function spawnCloudflared(args, options) {
10622
+ const logger = options?.logger;
10623
+ const binPath = await getCloudflaredPath({
10624
+ skipVersionCheck: options?.skipVersionCheck,
10625
+ confirmDownload: options?.confirmDownload,
10626
+ logger
10627
+ });
10628
+ logger?.debug(
10629
+ `Spawning cloudflared: ${binPath} ${redactCloudflaredArgsForLogging(args).join(" ")}`
10630
+ );
10631
+ const cloudflared = spawn(binPath, args, {
10632
+ stdio: options?.stdio ?? "inherit",
10633
+ env: options?.env ? { ...process.env, ...options.env } : void 0
10634
+ });
10635
+ return cloudflared;
10636
+ }
10637
+ __name(spawnCloudflared, "spawnCloudflared");
10638
+ function removeCloudflaredCache(version) {
10639
+ const cacheDir = version ? getCacheDir(version) : join(getGlobalWranglerConfigPath(), "cloudflared");
10640
+ if (existsSync(cacheDir)) {
10641
+ removeDirSync(cacheDir);
10642
+ return cacheDir;
10643
+ }
10644
+ return null;
10645
+ }
10646
+ __name(removeCloudflaredCache, "removeCloudflaredCache");
10647
+
10648
+ // src/tunnel.ts
10649
+ var TUNNEL_STARTUP_TIMEOUT_MS = 3e4;
10650
+ var TUNNEL_FORCE_KILL_TIMEOUT_MS = 5e3;
10651
+ var DEFAULT_TUNNEL_EXPIRY_MS = 60 * 60 * 1e3;
10652
+ var DEFAULT_TUNNEL_EXTENSION_MS = 60 * 60 * 1e3;
10653
+ var DEFAULT_TUNNEL_MAX_REMAINING_MS = 3 * 60 * 60 * 1e3;
10654
+ var DEFAULT_TUNNEL_REMINDER_INTERVAL_MS = 10 * 60 * 1e3;
10655
+ var QUICK_TUNNEL_URL_REGEX = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/;
10656
+ function startTunnel(options) {
10657
+ let disposed = false;
10658
+ let reminderInterval;
10659
+ let expiryTimeout;
10660
+ let expiresAt = 0;
10661
+ let cloudflaredProcess;
10662
+ const logger = options.logger;
10663
+ const timeoutMs = options.timeoutMs ?? TUNNEL_STARTUP_TIMEOUT_MS;
10664
+ const reminderIntervalMs = options.reminderIntervalMs ?? DEFAULT_TUNNEL_REMINDER_INTERVAL_MS;
10665
+ const defaultExpiryMs = options.expiryMs ?? DEFAULT_TUNNEL_EXPIRY_MS;
10666
+ const timeFormatter = new Intl.DateTimeFormat(void 0, {
10667
+ timeStyle: "short"
10668
+ });
10669
+ const cloudflaredArgs = [
10670
+ "tunnel",
10671
+ "--no-autoupdate",
10672
+ "--url",
10673
+ options.origin.href
10674
+ ];
10675
+ const cloudflaredPromise = spawnCloudflared(cloudflaredArgs, {
10676
+ stdio: "pipe",
10677
+ skipVersionCheck: true,
10678
+ logger
10679
+ }).then((process2) => {
10680
+ cloudflaredProcess = process2;
10681
+ if (disposed) {
10682
+ terminateCloudflared(process2);
10683
+ }
10684
+ return process2;
10685
+ });
10686
+ const readyPromise = cloudflaredPromise.then(
10687
+ (process2) => waitForQuickTunnelReady(process2, timeoutMs, {
10688
+ logger,
10689
+ origin: options.origin
10690
+ })
10691
+ ).then((result) => {
10692
+ expiresAt = Date.now() + defaultExpiryMs;
10693
+ scheduleExpiryTimeout();
10694
+ scheduleReminder(result.publicUrl.origin);
10695
+ return result;
10696
+ });
10697
+ function disposeTunnel() {
10698
+ disposed = true;
10699
+ clearTunnelTimers();
10700
+ if (cloudflaredProcess) {
10701
+ terminateCloudflared(cloudflaredProcess);
10702
+ }
10703
+ }
10704
+ __name(disposeTunnel, "disposeTunnel");
10705
+ function clearTunnelTimers() {
10706
+ if (expiryTimeout) {
10707
+ clearTimeout(expiryTimeout);
10708
+ expiryTimeout = void 0;
10709
+ }
10710
+ if (reminderInterval) {
10711
+ clearInterval(reminderInterval);
10712
+ reminderInterval = void 0;
10713
+ }
10714
+ }
10715
+ __name(clearTunnelTimers, "clearTunnelTimers");
10716
+ function scheduleReminder(publicURL) {
10717
+ if (reminderIntervalMs > 0) {
10718
+ reminderInterval = setInterval(() => {
10719
+ if (disposed) {
10720
+ return;
10721
+ }
10722
+ const remainingMs = expiresAt - Date.now();
10723
+ if (remainingMs <= 0) {
10724
+ return;
10725
+ }
10726
+ logger?.log(
10727
+ `The tunnel is still open at ${publicURL}. It expires in ${formatTunnelDuration(remainingMs)}. ${options.extendHint ?? ""}`
10728
+ );
10729
+ }, reminderIntervalMs);
10730
+ reminderInterval.unref?.();
10731
+ }
10732
+ }
10733
+ __name(scheduleReminder, "scheduleReminder");
10734
+ function scheduleExpiryTimeout() {
10735
+ if (disposed) {
10736
+ return;
10737
+ }
10738
+ if (expiryTimeout) {
10739
+ clearTimeout(expiryTimeout);
10740
+ }
10741
+ expiryTimeout = setTimeout(
10742
+ () => {
10743
+ if (disposed) {
10744
+ return;
10745
+ }
10746
+ logger?.log("Tunnel expired. Closing tunnel.");
10747
+ disposeTunnel();
10748
+ },
10749
+ Math.max(0, expiresAt - Date.now())
10750
+ );
10751
+ expiryTimeout.unref();
10752
+ }
10753
+ __name(scheduleExpiryTimeout, "scheduleExpiryTimeout");
10754
+ function extendExpiry(ms = DEFAULT_TUNNEL_EXTENSION_MS) {
10755
+ if (disposed || !expiryTimeout || ms <= 0) {
10756
+ return;
10757
+ }
10758
+ const now = Date.now();
10759
+ const previousExpiresAt = expiresAt;
10760
+ expiresAt = Math.min(
10761
+ now + DEFAULT_TUNNEL_MAX_REMAINING_MS,
10762
+ Math.max(expiresAt, now) + ms
10763
+ );
10764
+ const extendedByMs = expiresAt - previousExpiresAt;
10765
+ if (extendedByMs < ms) {
10766
+ logger?.log(
10767
+ `Tunnel expiry extended to the ${formatTunnelDuration(DEFAULT_TUNNEL_MAX_REMAINING_MS)} limit. It now expires at ${timeFormatter.format(new Date(expiresAt))}.`
10768
+ );
10769
+ scheduleExpiryTimeout();
10770
+ return;
10771
+ }
10772
+ logger?.log(
10773
+ `Tunnel expiry extended by ${formatTunnelDuration(extendedByMs)}. It now expires at ${timeFormatter.format(new Date(expiresAt))}.`
10774
+ );
10775
+ scheduleExpiryTimeout();
10776
+ }
10777
+ __name(extendExpiry, "extendExpiry");
10778
+ return {
10779
+ ready: /* @__PURE__ */ __name(() => readyPromise, "ready"),
10780
+ dispose: disposeTunnel,
10781
+ extendExpiry
10782
+ };
10783
+ }
10784
+ __name(startTunnel, "startTunnel");
10785
+ function formatTunnelDuration(durationMs) {
10786
+ const totalMinutes = Math.max(1, Math.ceil(durationMs / 6e4));
10787
+ const hours = Math.floor(totalMinutes / 60);
10788
+ const minutes = totalMinutes % 60;
10789
+ if (hours === 0) {
10790
+ return `${minutes}m`;
10791
+ }
10792
+ if (minutes === 0) {
10793
+ return `${hours}h`;
10794
+ }
10795
+ return `${hours}h ${minutes}m`;
10796
+ }
10797
+ __name(formatTunnelDuration, "formatTunnelDuration");
10798
+ function terminateCloudflared(cloudflared) {
10799
+ if (cloudflared.killed) {
10800
+ return;
10801
+ }
10802
+ cloudflared.unref();
10803
+ cloudflared.kill("SIGTERM");
10804
+ const forceKillTimer = setTimeout(() => {
10805
+ if (!cloudflared.killed) {
10806
+ cloudflared.kill("SIGKILL");
10807
+ }
10808
+ }, TUNNEL_FORCE_KILL_TIMEOUT_MS);
10809
+ forceKillTimer.unref();
10810
+ }
10811
+ __name(terminateCloudflared, "terminateCloudflared");
10812
+ function waitForQuickTunnelReady(cloudflared, timeoutMs, options) {
10813
+ return new Promise((resolve, reject) => {
10814
+ let resolved = false;
10815
+ let stderrOutput = "";
10816
+ const logger = options?.logger;
10817
+ const origin = options?.origin;
10818
+ const timeoutId = setTimeout(() => {
10819
+ if (!resolved) {
10820
+ resolved = true;
10821
+ terminateCloudflared(cloudflared);
10822
+ reject(
10823
+ createTunnelStartupError(
10824
+ `Timed out waiting for cloudflared to start (${timeoutMs / 1e3}s).`,
10825
+ stderrOutput,
10826
+ origin
10827
+ )
10828
+ );
10829
+ }
10830
+ }, timeoutMs);
10831
+ timeoutId.unref();
10832
+ if (cloudflared.stderr) {
10833
+ cloudflared.stderr.on("data", (data) => {
10834
+ const chunk = data.toString();
10835
+ stderrOutput += chunk;
10836
+ logger?.debug("[cloudflared]", chunk.trimEnd());
10837
+ const match = QUICK_TUNNEL_URL_REGEX.exec(stderrOutput);
10838
+ if (match && !resolved) {
10839
+ resolved = true;
10840
+ clearTimeout(timeoutId);
10841
+ resolve({ publicUrl: new URL(match[0]) });
10842
+ }
10843
+ });
10844
+ }
10845
+ cloudflared.on("error", (error) => {
10846
+ if (!resolved) {
10847
+ resolved = true;
10848
+ clearTimeout(timeoutId);
10849
+ reject(new Error(`Failed to start cloudflared: ${error.message}`));
10850
+ }
10851
+ });
10852
+ cloudflared.on("exit", (code, signal) => {
10853
+ if (!resolved) {
10854
+ resolved = true;
10855
+ clearTimeout(timeoutId);
10856
+ const reason = signal ? `terminated by signal ${signal}` : `exited with code ${code}`;
10857
+ reject(
10858
+ createTunnelStartupError(
10859
+ `cloudflared ${reason} before the tunnel was ready.`,
10860
+ stderrOutput,
10861
+ origin
10862
+ )
10863
+ );
10864
+ }
10865
+ });
10866
+ });
10867
+ }
10868
+ __name(waitForQuickTunnelReady, "waitForQuickTunnelReady");
10869
+ function createTunnelStartupError(message, stderrOutput, origin) {
10870
+ const isQuickTunnelRateLimited = stderrOutput.includes(
10871
+ "429 Too Many Requests"
10872
+ );
10873
+ const errorMessage = `${message}
10874
+ cloudflared output:
10875
+ ${stderrOutput || "(no output)"}
10876
+
10877
+ The local dev server started at ${origin.href}.
10878
+ ` + (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.`);
10879
+ if (isQuickTunnelRateLimited) {
10880
+ return new UserError(errorMessage, { telemetryMessage: false });
10881
+ }
10882
+ return new Error(errorMessage);
10883
+ }
10884
+ __name(createTunnelStartupError, "createTunnelStartupError");
10885
+
10886
+ export { COMPLIANCE_REGION_CONFIG_PUBLIC, COMPLIANCE_REGION_CONFIG_UNKNOWN, Diagnostics, PatchConfigError, bucketFormatMessage, defaultWranglerConfig, experimental_patchConfig, friendlyBindingNames, getBindingTypeFriendlyName, getBooleanEnvironmentVariableFactory, getBrowserRenderingHeadfulFromEnv, getBuildConditionsFromEnv, getBuildPlatformFromEnv, getC3CommandFromEnv, getCIGeneratePreviewAlias, getCIMatchTag, getCIOverrideName, getCIOverrideNetworkModeHost, getCfFetchEnabledFromEnv, getCfFetchPathFromEnv, getCloudflareApiBaseUrl, getCloudflareApiEnvironmentFromEnv, getCloudflareComplianceRegion, getCloudflareEnv, getCloudflareIncludeProcessEnvFromEnv, getCloudflareLoadDevVarsFromDotEnv, getCloudflaredPathFromEnv, getComplianceRegionSubdomain, getD1ExtraLocationChoices, getDisableConfigWatching, getDockerPath, getEnvironmentVariableFactory, getGlobalWranglerConfigPath, getLocalExplorerEnabledFromEnv, getOpenNextDeployFromEnv, getOutputFileDirectoryFromEnv, getOutputFilePathFromEnv, getRegistryPath, getSanitizeLogs, getSubdomainMixedStateCheckDisabled, getTraceHeader, getWorkersCIBranchName, getWranglerCacheDirFromEnv, getWranglerHideBanner, getWranglerSendErrorReportsFromEnv, getWranglerSendMetricsFromEnv, hasProperty, isDockerfile, isOptionalProperty, isPagesConfig, isRequiredProperty, isValidR2BucketName, normalizeAndValidateConfig, spawnCloudflared, startTunnel, validatePagesConfig };