@cloudflare/workers-utils 0.36.0 → 0.37.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/browser.d.mts +1 -1
- package/dist/browser.mjs +1 -1
- package/dist/{chunk-2I4Q5QJP.mjs → chunk-CDB2RWUW.mjs} +1 -1
- package/dist/{compliance-j3-tqeao.d.mts → compliance-b1X-Ss7e.d.mts} +24 -1
- package/dist/compliance.d.mts +1 -1
- package/dist/index.d.mts +18 -3
- package/dist/index.mjs +1307 -1031
- package/dist/metafile-esm.json +1 -1
- package/dist/test-helpers/index.d.mts +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -2,10 +2,10 @@ export { MetricsRegistry } from './chunk-Z62CVNHJ.mjs';
|
|
|
2
2
|
export { _forceColour, formatZodError } from './chunk-7AX24RFK.mjs';
|
|
3
3
|
import { isRedirectedRawConfig, configFileName, formatConfigSnippet, getDurableObjectExports, getContainerNameToClassNameMap, parsePackageJSON, readFileSync, ParseError, parseJSON, APIError, dir, parseTOML, modify, applyEdits, format, dist_default, parseJSONC } from './chunk-V6JSHOVJ.mjs';
|
|
4
4
|
export { APIError, ENVIRONMENT_TAG_PREFIX, INHERIT_SYMBOL, JSON_CONFIG_FORMATS, PATH_TO_DEPLOY_CONFIG, ParseError, SERVICE_TAG_PREFIX, configFileName, configFormat, experimental_readRawConfig, findWranglerConfig, formatConfigSnippet, getContainerDurableObjectClassNames, getContainerNameToClassNameMap, getDurableObjectExports, hasDurableObjectExports, indexLocation, isRedirectedConfig, parseByteSize, parseHumanDuration, parseJSON, parseJSONC, parseNonHyphenedUuid, parsePackageJSON, parseTOML, partitionExports, readFileSync, readFileSyncToBuffer, resolveContainerClassName, resolveWranglerConfigPath, searchLocation } from './chunk-V6JSHOVJ.mjs';
|
|
5
|
-
import { assertNever } from './chunk-
|
|
6
|
-
export { DEFAULT_COMPAT_DATE, assertNever, constructWranglerConfig, mapWorkerMetadataBindings } from './chunk-
|
|
5
|
+
import { assertNever } from './chunk-CDB2RWUW.mjs';
|
|
6
|
+
export { DEFAULT_COMPAT_DATE, assertNever, constructWranglerConfig, mapWorkerMetadataBindings } from './chunk-CDB2RWUW.mjs';
|
|
7
7
|
export { NODEJS_COMPAT_DEFAULT_ON_DATE, NODEJS_COMPAT_V2_SWITCH_OVER_DATE, getTodaysCompatDate, isCompatDate, isNodejsCompatDefaultOn, resolveNodejsCompat, stripRedundantNodejsCompatFlags } from './chunk-C6O4ION7.mjs';
|
|
8
|
-
import { getCloudflareEnv,
|
|
8
|
+
import { getCloudflareEnv, getCloudflareApiBaseUrl, getTraceHeader, getCloudflaredPathFromEnv, getOutputFilePathFromEnv, getOutputFileDirectoryFromEnv, getCIOverrideName, getWranglerCacheDirFromEnv } from './chunk-XUGGRRBO.mjs';
|
|
9
9
|
export { COMPLIANCE_REGION_CONFIG_PUBLIC, COMPLIANCE_REGION_CONFIG_UNKNOWN, getBrowserRenderingHeadfulFromEnv, getBuildConditionsFromEnv, getBuildPlatformFromEnv, getC3CommandFromEnv, getCIGeneratePreviewAlias, getCIMatchTag, getCIOverrideName, getCIOverrideNetworkModeHost, getCfFetchEnabledFromEnv, getCfFetchPathFromEnv, getCloudflareApiBaseUrl, getCloudflareApiEnvironmentFromEnv, getCloudflareComplianceRegion, getCloudflareEnv, getCloudflareIncludeProcessEnvFromEnv, getCloudflareLoadDevVarsFromDotEnv, getCloudflaredPathFromEnv, getComplianceRegionSubdomain, getD1ExtraLocationChoices, getDisableConfigWatching, getLocalExplorerEnabledFromEnv, getLocalObservabilityEnabledFromEnv, getNoSkillsUpdatePromptsFromEnv, getOpenNextDeployFromEnv, getOutputFileDirectoryFromEnv, getOutputFilePathFromEnv, getRegistryPath, getSanitizeLogs, getSubdomainMixedStateCheckDisabled, getTraceHeader, getWorkersCIBranchName, getWranglerCacheDirFromEnv, getWranglerHideBanner, getWranglerSendErrorReportsFromEnv, getWranglerSendMetricsFromEnv, isDoNotTrackEnabled } from './chunk-XUGGRRBO.mjs';
|
|
10
10
|
import { dedent } from './chunk-MCNIGVAF.mjs';
|
|
11
11
|
export { getBooleanEnvironmentVariableFactory, getDockerPath, getEnvironmentVariableFactory } from './chunk-3IYLA2JH.mjs';
|
|
@@ -22,11 +22,11 @@ import * as path from 'node:path';
|
|
|
22
22
|
import path__default, { join, dirname, resolve, basename } from 'node:path';
|
|
23
23
|
import { stripVTControlCharacters } from 'node:util';
|
|
24
24
|
import chalk from 'chalk';
|
|
25
|
+
import { URLSearchParams as URLSearchParams$1 } from 'node:url';
|
|
26
|
+
import { Headers, FormData, Response, fetch } from 'undici';
|
|
25
27
|
import { execFileSync, spawn } from 'node:child_process';
|
|
26
28
|
import { createHash, randomBytes } from 'node:crypto';
|
|
27
29
|
import { arch } from 'node:os';
|
|
28
|
-
import { fetch, Headers, FormData, Response } from 'undici';
|
|
29
|
-
import { URLSearchParams } from 'node:url';
|
|
30
30
|
import * as timersPromises from 'node:timers/promises';
|
|
31
31
|
import { setTimeout as setTimeout$1 } from 'node:timers/promises';
|
|
32
32
|
import { assert as assert$1 } from 'node:console';
|
|
@@ -20117,6 +20117,12 @@ function validateContainerApp(envName, topLevelName, configPath, options = {}) {
|
|
|
20117
20117
|
containerAppOptional.image_vars,
|
|
20118
20118
|
"object"
|
|
20119
20119
|
);
|
|
20120
|
+
validateContainerObservability(
|
|
20121
|
+
diagnostics,
|
|
20122
|
+
`${field}.observability`,
|
|
20123
|
+
containerAppOptional.observability,
|
|
20124
|
+
config2
|
|
20125
|
+
);
|
|
20120
20126
|
validateOptionalProperty(
|
|
20121
20127
|
diagnostics,
|
|
20122
20128
|
field,
|
|
@@ -20160,6 +20166,7 @@ function validateContainerApp(envName, topLevelName, configPath, options = {}) {
|
|
|
20160
20166
|
"image",
|
|
20161
20167
|
"image_build_context",
|
|
20162
20168
|
"image_vars",
|
|
20169
|
+
"observability",
|
|
20163
20170
|
"class_name",
|
|
20164
20171
|
"scheduling_policy",
|
|
20165
20172
|
"instance_type",
|
|
@@ -22400,6 +22407,133 @@ var validateExports = /* @__PURE__ */ __name((diagnostics, field, value) => {
|
|
|
22400
22407
|
}
|
|
22401
22408
|
return valid;
|
|
22402
22409
|
}, "validateExports");
|
|
22410
|
+
var CONTAINER_OBSERVABILITY_TARGET_INSTANCE_PERCENTAGE_MIN = 1;
|
|
22411
|
+
var CONTAINER_OBSERVABILITY_TARGET_INSTANCE_PERCENTAGE_MAX = 99;
|
|
22412
|
+
var CONTAINER_OBSERVABILITY_TARGET_INSTANCE_COUNT_MIN = 1;
|
|
22413
|
+
function isContainerObservabilityEnabled(observability) {
|
|
22414
|
+
return observability?.logs?.enabled === true || observability?.enabled === true;
|
|
22415
|
+
}
|
|
22416
|
+
__name(isContainerObservabilityEnabled, "isContainerObservabilityEnabled");
|
|
22417
|
+
function hasConflictingContainerObservabilityEnabledValues(observability) {
|
|
22418
|
+
return typeof observability.enabled === "boolean" && typeof observability.logs?.enabled === "boolean" && observability.enabled !== observability.logs.enabled;
|
|
22419
|
+
}
|
|
22420
|
+
__name(hasConflictingContainerObservabilityEnabledValues, "hasConflictingContainerObservabilityEnabledValues");
|
|
22421
|
+
var validateContainerObservability = /* @__PURE__ */ __name((diagnostics, field, value) => {
|
|
22422
|
+
if (value === void 0) {
|
|
22423
|
+
return true;
|
|
22424
|
+
}
|
|
22425
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
22426
|
+
diagnostics.errors.push(
|
|
22427
|
+
`"${field}" should be an object but got ${JSON.stringify(value)}.`
|
|
22428
|
+
);
|
|
22429
|
+
return false;
|
|
22430
|
+
}
|
|
22431
|
+
const val = value;
|
|
22432
|
+
let isValid = true;
|
|
22433
|
+
isValid = validateOptionalProperty(
|
|
22434
|
+
diagnostics,
|
|
22435
|
+
field,
|
|
22436
|
+
"enabled",
|
|
22437
|
+
val.enabled,
|
|
22438
|
+
"boolean"
|
|
22439
|
+
) && isValid;
|
|
22440
|
+
if (val.logs !== void 0) {
|
|
22441
|
+
if (typeof val.logs !== "object" || val.logs === null || Array.isArray(val.logs)) {
|
|
22442
|
+
diagnostics.errors.push(
|
|
22443
|
+
`Expected "${field}.logs" to be of type object but got ${JSON.stringify(
|
|
22444
|
+
val.logs
|
|
22445
|
+
)}.`
|
|
22446
|
+
);
|
|
22447
|
+
isValid = false;
|
|
22448
|
+
} else {
|
|
22449
|
+
isValid = validateOptionalProperty(
|
|
22450
|
+
diagnostics,
|
|
22451
|
+
field,
|
|
22452
|
+
"logs.enabled",
|
|
22453
|
+
val.logs.enabled,
|
|
22454
|
+
"boolean"
|
|
22455
|
+
) && isValid;
|
|
22456
|
+
isValid = validateAdditionalProperties(
|
|
22457
|
+
diagnostics,
|
|
22458
|
+
`${field}.logs`,
|
|
22459
|
+
Object.keys(val.logs),
|
|
22460
|
+
["enabled"]
|
|
22461
|
+
) && isValid;
|
|
22462
|
+
}
|
|
22463
|
+
}
|
|
22464
|
+
if (val.enabled === void 0 && val.target_instance_percentage === void 0 && val.target_instance_count === void 0 && (val.logs === void 0 || typeof val.logs === "object" && val.logs !== null && !Array.isArray(val.logs) && val.logs.enabled === void 0)) {
|
|
22465
|
+
isValid = validateAtLeastOnePropertyRequired(diagnostics, field, [
|
|
22466
|
+
{
|
|
22467
|
+
key: "enabled",
|
|
22468
|
+
value: val.enabled,
|
|
22469
|
+
type: "boolean"
|
|
22470
|
+
},
|
|
22471
|
+
{
|
|
22472
|
+
key: "logs.enabled",
|
|
22473
|
+
value: val.logs?.enabled,
|
|
22474
|
+
type: "boolean"
|
|
22475
|
+
}
|
|
22476
|
+
]) && isValid;
|
|
22477
|
+
}
|
|
22478
|
+
isValid = validateOptionalProperty(
|
|
22479
|
+
diagnostics,
|
|
22480
|
+
field,
|
|
22481
|
+
"target_instance_percentage",
|
|
22482
|
+
val.target_instance_percentage,
|
|
22483
|
+
"number"
|
|
22484
|
+
) && isValid;
|
|
22485
|
+
isValid = validateOptionalProperty(
|
|
22486
|
+
diagnostics,
|
|
22487
|
+
field,
|
|
22488
|
+
"target_instance_count",
|
|
22489
|
+
val.target_instance_count,
|
|
22490
|
+
"number"
|
|
22491
|
+
) && isValid;
|
|
22492
|
+
isValid = validateAdditionalProperties(diagnostics, field, Object.keys(val), [
|
|
22493
|
+
"enabled",
|
|
22494
|
+
"logs",
|
|
22495
|
+
"target_instance_percentage",
|
|
22496
|
+
"target_instance_count"
|
|
22497
|
+
]) && isValid;
|
|
22498
|
+
if (hasConflictingContainerObservabilityEnabledValues(val)) {
|
|
22499
|
+
diagnostics.errors.push(
|
|
22500
|
+
`"${field}.enabled" and "${field}.logs.enabled" cannot be set to different values.`
|
|
22501
|
+
);
|
|
22502
|
+
isValid = false;
|
|
22503
|
+
}
|
|
22504
|
+
if (val.target_instance_percentage !== void 0 && val.target_instance_count !== void 0) {
|
|
22505
|
+
diagnostics.errors.push(
|
|
22506
|
+
`"${field}.target_instance_percentage" and "${field}.target_instance_count" cannot both be set.`
|
|
22507
|
+
);
|
|
22508
|
+
isValid = false;
|
|
22509
|
+
}
|
|
22510
|
+
if (typeof val.target_instance_percentage === "number" && (!Number.isInteger(val.target_instance_percentage) || val.target_instance_percentage < CONTAINER_OBSERVABILITY_TARGET_INSTANCE_PERCENTAGE_MIN || val.target_instance_percentage > CONTAINER_OBSERVABILITY_TARGET_INSTANCE_PERCENTAGE_MAX)) {
|
|
22511
|
+
diagnostics.errors.push(
|
|
22512
|
+
`"${field}.target_instance_percentage" must be an integer between ${CONTAINER_OBSERVABILITY_TARGET_INSTANCE_PERCENTAGE_MIN} and ${CONTAINER_OBSERVABILITY_TARGET_INSTANCE_PERCENTAGE_MAX} inclusive.`
|
|
22513
|
+
);
|
|
22514
|
+
isValid = false;
|
|
22515
|
+
}
|
|
22516
|
+
if (typeof val.target_instance_count === "number" && (!Number.isInteger(val.target_instance_count) || val.target_instance_count < CONTAINER_OBSERVABILITY_TARGET_INSTANCE_COUNT_MIN)) {
|
|
22517
|
+
diagnostics.errors.push(
|
|
22518
|
+
`"${field}.target_instance_count" must be a positive integer.`
|
|
22519
|
+
);
|
|
22520
|
+
isValid = false;
|
|
22521
|
+
}
|
|
22522
|
+
const observabilityEnabled = isContainerObservabilityEnabled(val);
|
|
22523
|
+
if (val.target_instance_percentage !== void 0 && !observabilityEnabled) {
|
|
22524
|
+
diagnostics.errors.push(
|
|
22525
|
+
`"${field}.target_instance_percentage" requires "${field}.enabled" or "${field}.logs.enabled" to be true because container observability overrides root observability.`
|
|
22526
|
+
);
|
|
22527
|
+
isValid = false;
|
|
22528
|
+
}
|
|
22529
|
+
if (val.target_instance_count !== void 0 && !observabilityEnabled) {
|
|
22530
|
+
diagnostics.errors.push(
|
|
22531
|
+
`"${field}.target_instance_count" requires "${field}.enabled" or "${field}.logs.enabled" to be true because container observability overrides root observability.`
|
|
22532
|
+
);
|
|
22533
|
+
isValid = false;
|
|
22534
|
+
}
|
|
22535
|
+
return isValid;
|
|
22536
|
+
}, "validateContainerObservability");
|
|
22403
22537
|
var validateObservability = /* @__PURE__ */ __name((diagnostics, field, value) => {
|
|
22404
22538
|
if (value === void 0) {
|
|
22405
22539
|
return true;
|
|
@@ -24783,1168 +24917,1310 @@ function getWranglerTmpDir(projectRoot, prefix, cleanup = true) {
|
|
|
24783
24917
|
}
|
|
24784
24918
|
__name(getWranglerTmpDir, "getWranglerTmpDir");
|
|
24785
24919
|
|
|
24786
|
-
// src/
|
|
24787
|
-
|
|
24788
|
-
|
|
24789
|
-
|
|
24790
|
-
|
|
24791
|
-
|
|
24920
|
+
// src/cfetch/errors.ts
|
|
24921
|
+
function buildDetailedError(message, ...extra) {
|
|
24922
|
+
return new ParseError({
|
|
24923
|
+
text: message,
|
|
24924
|
+
notes: extra.map((text) => ({ text })),
|
|
24925
|
+
telemetryMessage: false
|
|
24926
|
+
});
|
|
24792
24927
|
}
|
|
24793
|
-
__name(
|
|
24794
|
-
function
|
|
24795
|
-
|
|
24796
|
-
|
|
24797
|
-
|
|
24798
|
-
|
|
24799
|
-
|
|
24800
|
-
return "arm64";
|
|
24801
|
-
case "arm":
|
|
24802
|
-
return "arm";
|
|
24803
|
-
default:
|
|
24804
|
-
throw new UserError(
|
|
24805
|
-
`Unsupported architecture for cloudflared: ${nodeArch}
|
|
24806
|
-
|
|
24807
|
-
cloudflared supports: x64 (amd64), arm64, arm
|
|
24808
|
-
|
|
24809
|
-
You can manually install cloudflared and set the CLOUDFLARED_PATH environment variable.
|
|
24810
|
-
Download instructions: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`,
|
|
24811
|
-
{ telemetryMessage: "tunnel cloudflared unsupported architecture" }
|
|
24812
|
-
);
|
|
24928
|
+
__name(buildDetailedError, "buildDetailedError");
|
|
24929
|
+
function maybeThrowFriendlyError(error51) {
|
|
24930
|
+
if (error51.message === "workers.api.error.email_verification_required") {
|
|
24931
|
+
throw buildDetailedError(
|
|
24932
|
+
"Please verify your account's email address and try again.",
|
|
24933
|
+
"Check your email for a verification link, or login to https://dash.cloudflare.com and request a new one."
|
|
24934
|
+
);
|
|
24813
24935
|
}
|
|
24814
24936
|
}
|
|
24815
|
-
__name(
|
|
24816
|
-
function getGoOS() {
|
|
24817
|
-
switch (process.platform) {
|
|
24818
|
-
case "darwin":
|
|
24819
|
-
return "darwin";
|
|
24820
|
-
case "linux":
|
|
24821
|
-
return "linux";
|
|
24822
|
-
case "win32":
|
|
24823
|
-
return "windows";
|
|
24824
|
-
default:
|
|
24825
|
-
throw new UserError(
|
|
24826
|
-
`Unsupported platform for cloudflared: ${process.platform}
|
|
24827
|
-
|
|
24828
|
-
cloudflared supports: darwin (macOS), linux, win32 (Windows)
|
|
24937
|
+
__name(maybeThrowFriendlyError, "maybeThrowFriendlyError");
|
|
24829
24938
|
|
|
24830
|
-
|
|
24831
|
-
|
|
24832
|
-
|
|
24833
|
-
|
|
24834
|
-
|
|
24939
|
+
// src/cfetch/index.ts
|
|
24940
|
+
function logHeaders(headers, logger) {
|
|
24941
|
+
const clone2 = cloneHeaders(headers);
|
|
24942
|
+
clone2.delete("Authorization");
|
|
24943
|
+
logger.debugWithSanitization?.(
|
|
24944
|
+
"HEADERS:",
|
|
24945
|
+
JSON.stringify(Object.fromEntries(clone2), null, 2)
|
|
24946
|
+
);
|
|
24835
24947
|
}
|
|
24836
|
-
__name(
|
|
24837
|
-
|
|
24838
|
-
|
|
24839
|
-
|
|
24840
|
-
|
|
24841
|
-
|
|
24842
|
-
|
|
24843
|
-
|
|
24948
|
+
__name(logHeaders, "logHeaders");
|
|
24949
|
+
async function performApiFetchBase(complianceConfig, resource, init = {}, userAgent, logger, queryParams, abortSignal, credentials) {
|
|
24950
|
+
assert2(credentials, "credentials are required for performApiFetch");
|
|
24951
|
+
const method = init.method ?? "GET";
|
|
24952
|
+
assert2(
|
|
24953
|
+
resource.startsWith("/"),
|
|
24954
|
+
`CF API fetch - resource path must start with a "/" but got "${resource}"`
|
|
24955
|
+
);
|
|
24956
|
+
const headers = cloneHeaders(new Headers(init.headers));
|
|
24957
|
+
addAuthorizationHeader(headers, credentials);
|
|
24958
|
+
headers.set("User-Agent", userAgent);
|
|
24959
|
+
maybeAddTraceHeader(headers);
|
|
24960
|
+
const queryString = queryParams ? `?${queryParams.toString()}` : "";
|
|
24961
|
+
logger.debug(
|
|
24962
|
+
`-- START CF API REQUEST: ${method} ${getCloudflareApiBaseUrl(complianceConfig)}${resource}`
|
|
24963
|
+
);
|
|
24964
|
+
logger.debugWithSanitization?.("QUERY STRING:", queryString);
|
|
24965
|
+
logHeaders(headers, logger);
|
|
24966
|
+
logger.debugWithSanitization?.("INIT:", JSON.stringify({ ...init }, null, 2));
|
|
24967
|
+
if (init.body instanceof FormData) {
|
|
24968
|
+
logger.debugWithSanitization?.(
|
|
24969
|
+
"BODY:",
|
|
24970
|
+
await new Response(init.body).text(),
|
|
24971
|
+
null,
|
|
24972
|
+
2
|
|
24973
|
+
);
|
|
24844
24974
|
}
|
|
24845
|
-
|
|
24975
|
+
logger.debug("-- END CF API REQUEST");
|
|
24976
|
+
return await fetch(
|
|
24977
|
+
`${getCloudflareApiBaseUrl(complianceConfig)}${resource}${queryString}`,
|
|
24978
|
+
{
|
|
24979
|
+
method,
|
|
24980
|
+
...init,
|
|
24981
|
+
headers,
|
|
24982
|
+
signal: abortSignal
|
|
24983
|
+
}
|
|
24984
|
+
);
|
|
24846
24985
|
}
|
|
24847
|
-
__name(
|
|
24848
|
-
async function
|
|
24849
|
-
const
|
|
24850
|
-
const
|
|
24851
|
-
|
|
24852
|
-
|
|
24853
|
-
|
|
24854
|
-
|
|
24855
|
-
|
|
24856
|
-
|
|
24857
|
-
|
|
24858
|
-
|
|
24859
|
-
|
|
24860
|
-
|
|
24861
|
-
|
|
24862
|
-
|
|
24863
|
-
|
|
24986
|
+
__name(performApiFetchBase, "performApiFetchBase");
|
|
24987
|
+
async function fetchInternalBase(complianceConfig, resource, init = {}, userAgent, logger, queryParams, abortSignal, credentials) {
|
|
24988
|
+
const method = init.method ?? "GET";
|
|
24989
|
+
const response = await performApiFetchBase(
|
|
24990
|
+
complianceConfig,
|
|
24991
|
+
resource,
|
|
24992
|
+
init,
|
|
24993
|
+
userAgent,
|
|
24994
|
+
logger,
|
|
24995
|
+
queryParams,
|
|
24996
|
+
abortSignal,
|
|
24997
|
+
credentials
|
|
24998
|
+
);
|
|
24999
|
+
const jsonText = await response.text();
|
|
25000
|
+
logger.debug(
|
|
25001
|
+
"-- START CF API RESPONSE:",
|
|
25002
|
+
response.statusText,
|
|
25003
|
+
response.status
|
|
25004
|
+
);
|
|
25005
|
+
logHeaders(response.headers, logger);
|
|
25006
|
+
logger.debugWithSanitization?.("RESPONSE:", jsonText);
|
|
25007
|
+
logger.debug("-- END CF API RESPONSE");
|
|
25008
|
+
const retryAfterMs = parseRetryAfterMs(response.headers);
|
|
25009
|
+
if (!jsonText && (response.status === 204 || response.status === 205)) {
|
|
25010
|
+
return {
|
|
25011
|
+
response: {
|
|
25012
|
+
result: {},
|
|
25013
|
+
success: true,
|
|
25014
|
+
errors: [],
|
|
25015
|
+
messages: []
|
|
25016
|
+
},
|
|
25017
|
+
status: response.status,
|
|
25018
|
+
retryAfterMs
|
|
25019
|
+
};
|
|
24864
25020
|
}
|
|
24865
|
-
if (
|
|
24866
|
-
|
|
24867
|
-
|
|
25021
|
+
if (isWAFBlockResponse(response.headers)) {
|
|
25022
|
+
throwWAFBlockError(
|
|
25023
|
+
response.headers,
|
|
25024
|
+
method,
|
|
25025
|
+
resource,
|
|
25026
|
+
response.status,
|
|
25027
|
+
response.statusText,
|
|
25028
|
+
retryAfterMs
|
|
24868
25029
|
);
|
|
24869
|
-
return null;
|
|
24870
25030
|
}
|
|
24871
|
-
let data;
|
|
24872
25031
|
try {
|
|
24873
|
-
|
|
24874
|
-
|
|
24875
|
-
|
|
24876
|
-
|
|
24877
|
-
|
|
24878
|
-
|
|
25032
|
+
const json2 = parseJSON(jsonText);
|
|
25033
|
+
return { response: json2, status: response.status, retryAfterMs };
|
|
25034
|
+
} catch {
|
|
25035
|
+
const rayId = extractWAFBlockRayId(response.headers);
|
|
25036
|
+
throw new APIError({
|
|
25037
|
+
text: "Received a malformed response from the API",
|
|
25038
|
+
notes: [
|
|
25039
|
+
{
|
|
25040
|
+
text: truncate(jsonText, 100)
|
|
25041
|
+
},
|
|
25042
|
+
{
|
|
25043
|
+
text: `${method} ${resource} -> ${response.status} ${response.statusText}`
|
|
25044
|
+
},
|
|
25045
|
+
...rayId ? [{ text: `Cloudflare Ray ID: ${rayId}` }] : []
|
|
25046
|
+
],
|
|
25047
|
+
status: response.status,
|
|
25048
|
+
retryAfterMs,
|
|
25049
|
+
telemetryMessage: false
|
|
25050
|
+
});
|
|
24879
25051
|
}
|
|
24880
|
-
|
|
24881
|
-
|
|
24882
|
-
|
|
25052
|
+
}
|
|
25053
|
+
__name(fetchInternalBase, "fetchInternalBase");
|
|
25054
|
+
async function fetchResultBase(complianceConfig, resource, init = {}, userAgent, logger, queryParams, abortSignal, credentials) {
|
|
25055
|
+
const {
|
|
25056
|
+
response: json2,
|
|
25057
|
+
status,
|
|
25058
|
+
retryAfterMs
|
|
25059
|
+
} = await fetchInternalBase(
|
|
25060
|
+
complianceConfig,
|
|
25061
|
+
resource,
|
|
25062
|
+
init,
|
|
25063
|
+
userAgent,
|
|
25064
|
+
logger,
|
|
25065
|
+
queryParams,
|
|
25066
|
+
abortSignal,
|
|
25067
|
+
credentials
|
|
25068
|
+
);
|
|
25069
|
+
if (json2.success) {
|
|
25070
|
+
return json2.result;
|
|
25071
|
+
} else {
|
|
25072
|
+
throwFetchError(resource, json2, status, retryAfterMs);
|
|
25073
|
+
}
|
|
25074
|
+
}
|
|
25075
|
+
__name(fetchResultBase, "fetchResultBase");
|
|
25076
|
+
async function fetchListResultBase(complianceConfig, resource, init = {}, userAgent, logger, queryParams, credentials) {
|
|
25077
|
+
const results = [];
|
|
25078
|
+
let getMoreResults = true;
|
|
25079
|
+
let cursor;
|
|
25080
|
+
while (getMoreResults) {
|
|
25081
|
+
if (cursor) {
|
|
25082
|
+
queryParams = new URLSearchParams$1(queryParams);
|
|
25083
|
+
queryParams.set("cursor", cursor);
|
|
25084
|
+
}
|
|
25085
|
+
const {
|
|
25086
|
+
response: json2,
|
|
25087
|
+
status,
|
|
25088
|
+
retryAfterMs
|
|
25089
|
+
} = await fetchInternalBase(
|
|
25090
|
+
complianceConfig,
|
|
25091
|
+
resource,
|
|
25092
|
+
init,
|
|
25093
|
+
userAgent,
|
|
25094
|
+
logger,
|
|
25095
|
+
queryParams,
|
|
25096
|
+
void 0,
|
|
25097
|
+
credentials
|
|
24883
25098
|
);
|
|
25099
|
+
if (json2.success) {
|
|
25100
|
+
results.push(...json2.result);
|
|
25101
|
+
if (hasCursor(json2.result_info)) {
|
|
25102
|
+
cursor = json2.result_info?.cursor;
|
|
25103
|
+
} else {
|
|
25104
|
+
getMoreResults = false;
|
|
25105
|
+
}
|
|
25106
|
+
} else {
|
|
25107
|
+
throwFetchError(resource, json2, status, retryAfterMs);
|
|
25108
|
+
}
|
|
24884
25109
|
}
|
|
24885
|
-
|
|
24886
|
-
|
|
25110
|
+
return results;
|
|
25111
|
+
}
|
|
25112
|
+
__name(fetchListResultBase, "fetchListResultBase");
|
|
25113
|
+
function truncate(text, maxLength) {
|
|
25114
|
+
const { length } = text;
|
|
25115
|
+
if (length <= maxLength) {
|
|
25116
|
+
return text;
|
|
24887
25117
|
}
|
|
24888
|
-
return
|
|
25118
|
+
return `${text.substring(0, maxLength)}... (length = ${length})`;
|
|
24889
25119
|
}
|
|
24890
|
-
__name(
|
|
24891
|
-
|
|
24892
|
-
|
|
24893
|
-
|
|
24894
|
-
|
|
24895
|
-
|
|
24896
|
-
if (
|
|
24897
|
-
return
|
|
25120
|
+
__name(truncate, "truncate");
|
|
25121
|
+
function isWAFBlockResponse(headers) {
|
|
25122
|
+
return headers.get("cf-mitigated") === "challenge";
|
|
25123
|
+
}
|
|
25124
|
+
__name(isWAFBlockResponse, "isWAFBlockResponse");
|
|
25125
|
+
function parseRetryAfterValue(retryAfter) {
|
|
25126
|
+
if (!retryAfter) {
|
|
25127
|
+
return void 0;
|
|
24898
25128
|
}
|
|
24899
|
-
|
|
24900
|
-
|
|
24901
|
-
);
|
|
24902
|
-
const fallback = await queryUpdateService("linux", "amd64", { logger });
|
|
24903
|
-
if (!fallback?.version) {
|
|
24904
|
-
throw new UserError(
|
|
24905
|
-
`[cloudflared] Failed to determine the latest cloudflared version.
|
|
24906
|
-
|
|
24907
|
-
The update service did not return results for ${goOS}/${goArch},
|
|
24908
|
-
and the fallback query also failed.
|
|
24909
|
-
|
|
24910
|
-
You can manually install cloudflared from:
|
|
24911
|
-
https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`,
|
|
24912
|
-
{ telemetryMessage: "tunnel cloudflared version lookup failed" }
|
|
24913
|
-
);
|
|
25129
|
+
if (/^\d+$/.test(retryAfter.trim())) {
|
|
25130
|
+
return Number(retryAfter) * 1e3;
|
|
24914
25131
|
}
|
|
24915
|
-
const
|
|
24916
|
-
|
|
24917
|
-
|
|
24918
|
-
|
|
24919
|
-
return
|
|
24920
|
-
url: url2,
|
|
24921
|
-
version: version2,
|
|
24922
|
-
checksum: "",
|
|
24923
|
-
// no checksum available for fallback URLs
|
|
24924
|
-
compressed,
|
|
24925
|
-
shouldUpdate: true,
|
|
24926
|
-
userMessage: "",
|
|
24927
|
-
error: ""
|
|
24928
|
-
};
|
|
25132
|
+
const retryAfterDate = new Date(retryAfter);
|
|
25133
|
+
if (!Number.isNaN(retryAfterDate.getTime())) {
|
|
25134
|
+
return Math.max(0, retryAfterDate.getTime() - Date.now());
|
|
25135
|
+
}
|
|
25136
|
+
return void 0;
|
|
24929
25137
|
}
|
|
24930
|
-
__name(
|
|
24931
|
-
function
|
|
24932
|
-
return
|
|
25138
|
+
__name(parseRetryAfterValue, "parseRetryAfterValue");
|
|
25139
|
+
function parseRetryAfterMs(headers) {
|
|
25140
|
+
return parseRetryAfterValue(headers.get("Retry-After"));
|
|
24933
25141
|
}
|
|
24934
|
-
__name(
|
|
24935
|
-
function
|
|
24936
|
-
|
|
24937
|
-
return join(getCacheDir(version2), binName);
|
|
25142
|
+
__name(parseRetryAfterMs, "parseRetryAfterMs");
|
|
25143
|
+
function extractWAFBlockRayId(headers) {
|
|
25144
|
+
return headers.get("cf-ray") ?? void 0;
|
|
24938
25145
|
}
|
|
24939
|
-
__name(
|
|
24940
|
-
function
|
|
24941
|
-
|
|
24942
|
-
|
|
24943
|
-
|
|
24944
|
-
} catch {
|
|
24945
|
-
return false;
|
|
24946
|
-
}
|
|
25146
|
+
__name(extractWAFBlockRayId, "extractWAFBlockRayId");
|
|
25147
|
+
function extractAccountTag(resource) {
|
|
25148
|
+
const re = new RegExp("/accounts/([a-zA-Z0-9]+)/?");
|
|
25149
|
+
const matches = re.exec(resource);
|
|
25150
|
+
return matches?.[1];
|
|
24947
25151
|
}
|
|
24948
|
-
__name(
|
|
24949
|
-
function
|
|
24950
|
-
const
|
|
24951
|
-
|
|
24952
|
-
|
|
24953
|
-
|
|
24954
|
-
|
|
24955
|
-
|
|
24956
|
-
|
|
24957
|
-
|
|
24958
|
-
|
|
24959
|
-
|
|
25152
|
+
__name(extractAccountTag, "extractAccountTag");
|
|
25153
|
+
function hasMorePages(result_info) {
|
|
25154
|
+
const page = result_info?.page;
|
|
25155
|
+
const per_page = result_info?.per_page;
|
|
25156
|
+
const total = result_info?.total_count;
|
|
25157
|
+
return page !== void 0 && per_page !== void 0 && total !== void 0 && page * per_page < total;
|
|
25158
|
+
}
|
|
25159
|
+
__name(hasMorePages, "hasMorePages");
|
|
25160
|
+
function renderError(err, level = 0) {
|
|
25161
|
+
const indent = " ".repeat(level);
|
|
25162
|
+
const message = err.message ?? "";
|
|
25163
|
+
const chainedMessages = "error_chain" in err ? err.error_chain?.map(
|
|
25164
|
+
(chainedError) => `
|
|
24960
25165
|
|
|
24961
|
-
|
|
24962
|
-
|
|
24963
|
-
|
|
24964
|
-
|
|
24965
|
-
`;
|
|
24966
|
-
errorMessage += ` - You're missing required system libraries
|
|
24967
|
-
`;
|
|
24968
|
-
if (process.platform === "linux") {
|
|
24969
|
-
errorMessage += `
|
|
24970
|
-
On Linux, make sure you have the required dependencies:
|
|
24971
|
-
`;
|
|
24972
|
-
errorMessage += ` - glibc (GNU C Library)
|
|
24973
|
-
`;
|
|
24974
|
-
errorMessage += ` - For Debian/Ubuntu: sudo apt-get install libc6
|
|
24975
|
-
`;
|
|
24976
|
-
}
|
|
24977
|
-
const cacheDir = join(getGlobalConfigPath(), "cloudflared");
|
|
24978
|
-
errorMessage += `
|
|
24979
|
-
You can try:
|
|
24980
|
-
`;
|
|
24981
|
-
errorMessage += ` 1. Deleting the cache directory: rm -rf ${cacheDir}
|
|
24982
|
-
`;
|
|
24983
|
-
errorMessage += ` 2. Running the command again to re-download
|
|
24984
|
-
`;
|
|
24985
|
-
errorMessage += ` 3. Manually installing cloudflared: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/
|
|
24986
|
-
`;
|
|
24987
|
-
errorMessage += ` 4. Setting CLOUDFLARED_PATH to point to your cloudflared binary`;
|
|
24988
|
-
throw new UserError(errorMessage, {
|
|
24989
|
-
telemetryMessage: "tunnel cloudflared validation failed"
|
|
24990
|
-
});
|
|
24991
|
-
}
|
|
25166
|
+
${indent}- ${renderError(chainedError, level + 1)}`
|
|
25167
|
+
).join("\n") ?? "" : "";
|
|
25168
|
+
return (err.code ? `${message} [code: ${err.code}]` : message) + (err.documentation_url ? `
|
|
25169
|
+
${indent}To learn more about this error, visit: ${err.documentation_url}` : "") + chainedMessages;
|
|
24992
25170
|
}
|
|
24993
|
-
__name(
|
|
24994
|
-
function
|
|
24995
|
-
|
|
24996
|
-
|
|
24997
|
-
|
|
24998
|
-
|
|
24999
|
-
|
|
25000
|
-
}
|
|
25001
|
-
|
|
25002
|
-
|
|
25171
|
+
__name(renderError, "renderError");
|
|
25172
|
+
function addAuthorizationHeader(headers, auth, overrideExisting = false) {
|
|
25173
|
+
if (!headers.has("Authorization") || overrideExisting) {
|
|
25174
|
+
if ("apiToken" in auth) {
|
|
25175
|
+
const authorizationHeader = `Bearer ${auth.apiToken}`;
|
|
25176
|
+
validateAuthorizationHeaderValue(authorizationHeader);
|
|
25177
|
+
headers.set("Authorization", authorizationHeader);
|
|
25178
|
+
} else {
|
|
25179
|
+
headers.set("X-Auth-Key", auth.authKey);
|
|
25180
|
+
headers.set("X-Auth-Email", auth.authEmail);
|
|
25003
25181
|
}
|
|
25004
25182
|
}
|
|
25005
|
-
return redacted;
|
|
25006
25183
|
}
|
|
25007
|
-
__name(
|
|
25008
|
-
function
|
|
25009
|
-
const
|
|
25010
|
-
|
|
25011
|
-
|
|
25012
|
-
|
|
25013
|
-
|
|
25014
|
-
|
|
25015
|
-
|
|
25016
|
-
|
|
25017
|
-
|
|
25018
|
-
|
|
25184
|
+
__name(addAuthorizationHeader, "addAuthorizationHeader");
|
|
25185
|
+
function validateAuthorizationHeaderValue(value) {
|
|
25186
|
+
for (const character of value) {
|
|
25187
|
+
const codePoint = character.codePointAt(0);
|
|
25188
|
+
if (codePoint === void 0 || codePoint > 255) {
|
|
25189
|
+
throw new UserError(
|
|
25190
|
+
`The configured Cloudflare API token contains a character that cannot be used in an HTTP Authorization header: ${formatAuthorizationHeaderCharacter(character, codePoint)}. Recreate or copy the token again, making sure it does not include characters such as ellipses.`,
|
|
25191
|
+
{
|
|
25192
|
+
telemetryMessage: "cfetch auth invalid authorization header"
|
|
25193
|
+
}
|
|
25194
|
+
);
|
|
25195
|
+
}
|
|
25019
25196
|
}
|
|
25020
25197
|
}
|
|
25021
|
-
__name(
|
|
25022
|
-
function
|
|
25023
|
-
|
|
25024
|
-
|
|
25025
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
25026
|
-
timeout: 1e4,
|
|
25027
|
-
encoding: "utf8"
|
|
25028
|
-
});
|
|
25029
|
-
const match = output.match(/(\d+\.\d+\.\d+)/);
|
|
25030
|
-
return match ? match[1] : null;
|
|
25031
|
-
} catch {
|
|
25032
|
-
return null;
|
|
25198
|
+
__name(validateAuthorizationHeaderValue, "validateAuthorizationHeaderValue");
|
|
25199
|
+
function formatAuthorizationHeaderCharacter(character, codePoint) {
|
|
25200
|
+
if (codePoint === void 0) {
|
|
25201
|
+
return '"\\u{unknown}"';
|
|
25033
25202
|
}
|
|
25203
|
+
const codePointLabel = `U+${codePoint.toString(16).toUpperCase().padStart(4, "0")}`;
|
|
25204
|
+
const characterLabel = isPrintableCharacter(character) ? `"${character}"` : `"${escapeCharacter(character)}"`;
|
|
25205
|
+
return `${characterLabel} (${codePointLabel})`;
|
|
25034
25206
|
}
|
|
25035
|
-
__name(
|
|
25036
|
-
function
|
|
25037
|
-
|
|
25038
|
-
const [iYear, iMonth, iPatch] = parse3(installed);
|
|
25039
|
-
const [lYear, lMonth, lPatch] = parse3(latest);
|
|
25040
|
-
if (iYear !== lYear) {
|
|
25041
|
-
return iYear < lYear;
|
|
25042
|
-
}
|
|
25043
|
-
if (iMonth !== lMonth) {
|
|
25044
|
-
return iMonth < lMonth;
|
|
25045
|
-
}
|
|
25046
|
-
return iPatch < lPatch;
|
|
25207
|
+
__name(formatAuthorizationHeaderCharacter, "formatAuthorizationHeaderCharacter");
|
|
25208
|
+
function isPrintableCharacter(character) {
|
|
25209
|
+
return !/[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(character);
|
|
25047
25210
|
}
|
|
25048
|
-
__name(
|
|
25049
|
-
|
|
25050
|
-
|
|
25051
|
-
|
|
25052
|
-
|
|
25053
|
-
|
|
25054
|
-
return;
|
|
25055
|
-
}
|
|
25056
|
-
const latest = await queryUpdateService(getGoOS(), getGoArch(), { logger }) ?? await queryUpdateService("linux", "amd64", { logger });
|
|
25057
|
-
const latestVersion = latest?.version;
|
|
25058
|
-
if (!latestVersion) {
|
|
25059
|
-
return;
|
|
25060
|
-
}
|
|
25061
|
-
if (isVersionOutdated(installed, latestVersion)) {
|
|
25062
|
-
logger?.warn(
|
|
25063
|
-
`Your cloudflared (${installed}) is outdated. Latest version is ${latestVersion}.
|
|
25064
|
-
Update: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`
|
|
25065
|
-
);
|
|
25211
|
+
__name(isPrintableCharacter, "isPrintableCharacter");
|
|
25212
|
+
function escapeCharacter(character) {
|
|
25213
|
+
return Array.from(character).map((c) => {
|
|
25214
|
+
const codePoint = c.codePointAt(0);
|
|
25215
|
+
if (codePoint === void 0) {
|
|
25216
|
+
return "";
|
|
25066
25217
|
}
|
|
25067
|
-
|
|
25068
|
-
|
|
25069
|
-
}
|
|
25218
|
+
return codePoint <= 65535 ? `\\u${codePoint.toString(16).toUpperCase().padStart(4, "0")}` : `\\u{${codePoint.toString(16).toUpperCase()}}`;
|
|
25219
|
+
}).join("");
|
|
25070
25220
|
}
|
|
25071
|
-
__name(
|
|
25072
|
-
function
|
|
25073
|
-
const
|
|
25074
|
-
const
|
|
25075
|
-
|
|
25076
|
-
|
|
25077
|
-
|
|
25078
|
-
|
|
25079
|
-
|
|
25080
|
-
|
|
25081
|
-
|
|
25082
|
-
|
|
25083
|
-
|
|
25084
|
-
|
|
25085
|
-
|
|
25086
|
-
|
|
25221
|
+
__name(escapeCharacter, "escapeCharacter");
|
|
25222
|
+
function throwFetchError(resource, response, status, retryAfterMs) {
|
|
25223
|
+
const errors = response.errors ?? [];
|
|
25224
|
+
for (const error52 of errors) {
|
|
25225
|
+
maybeThrowFriendlyError(error52);
|
|
25226
|
+
}
|
|
25227
|
+
const notes = [
|
|
25228
|
+
...errors.map((err) => ({ text: renderError(err) })),
|
|
25229
|
+
...response.messages?.map((msg) => ({
|
|
25230
|
+
text: typeof msg === "string" ? msg : msg.message ?? String(msg)
|
|
25231
|
+
})) ?? []
|
|
25232
|
+
];
|
|
25233
|
+
if (notes.length === 0) {
|
|
25234
|
+
const raw = response;
|
|
25235
|
+
const fallbackMessage = typeof raw.error === "string" ? `${raw.error}${raw.code ? ` [code: ${raw.code}]` : ""}` : void 0;
|
|
25236
|
+
if (fallbackMessage) {
|
|
25237
|
+
notes.push({ text: fallbackMessage });
|
|
25087
25238
|
}
|
|
25088
25239
|
}
|
|
25089
|
-
|
|
25090
|
-
|
|
25091
|
-
|
|
25092
|
-
const { logger } = options ?? {};
|
|
25093
|
-
const { url: url2, version: version2, checksum, compressed } = versionInfo;
|
|
25094
|
-
logger?.log(`Downloading cloudflared ${version2}...`);
|
|
25095
|
-
logger?.debug(`Download URL: ${url2}`);
|
|
25096
|
-
const cacheDir = dirname(binPath);
|
|
25097
|
-
mkdirSync(cacheDir, { recursive: true });
|
|
25098
|
-
let response;
|
|
25099
|
-
try {
|
|
25100
|
-
response = await fetch(url2, {
|
|
25101
|
-
headers: { "User-Agent": "wrangler" }
|
|
25240
|
+
if (retryAfterMs !== void 0) {
|
|
25241
|
+
notes.push({
|
|
25242
|
+
text: `The API responded with a "Retry-After" header indicating you should wait ${Math.ceil(retryAfterMs / 1e3)} second(s) before retrying.`
|
|
25102
25243
|
});
|
|
25103
|
-
} catch (e2) {
|
|
25104
|
-
throw new UserError(
|
|
25105
|
-
`[cloudflared] Failed to download cloudflared from ${url2}
|
|
25106
|
-
|
|
25107
|
-
Network error: ${e2 instanceof Error ? e2.message : String(e2)}
|
|
25108
|
-
|
|
25109
|
-
Please check your internet connection and try again.
|
|
25110
|
-
If you're behind a proxy, make sure it's configured correctly.`,
|
|
25111
|
-
{ telemetryMessage: "tunnel cloudflared download network failed" }
|
|
25112
|
-
);
|
|
25113
25244
|
}
|
|
25114
|
-
|
|
25115
|
-
|
|
25116
|
-
|
|
25117
|
-
|
|
25118
|
-
|
|
25119
|
-
|
|
25120
|
-
|
|
25121
|
-
|
|
25122
|
-
|
|
25123
|
-
|
|
25245
|
+
const error51 = new APIError({
|
|
25246
|
+
text: `A request to the Cloudflare API (${resource}) failed.`,
|
|
25247
|
+
notes,
|
|
25248
|
+
status,
|
|
25249
|
+
// hoist the parsed `Retry-After` header (if any) so consumers such as
|
|
25250
|
+
// `retryOnAPIFailure()` can back off for the amount of time the API
|
|
25251
|
+
// asked us to wait, e.g. when rate limited (HTTP 429).
|
|
25252
|
+
retryAfterMs,
|
|
25253
|
+
telemetryMessage: false
|
|
25254
|
+
});
|
|
25255
|
+
const code = errors[0]?.code;
|
|
25256
|
+
if (code) {
|
|
25257
|
+
error51.code = code;
|
|
25124
25258
|
}
|
|
25125
|
-
|
|
25126
|
-
|
|
25127
|
-
|
|
25128
|
-
|
|
25129
|
-
|
|
25130
|
-
|
|
25131
|
-
|
|
25132
|
-
|
|
25133
|
-
|
|
25134
|
-
|
|
25259
|
+
const meta3 = errors[0]?.meta;
|
|
25260
|
+
if (meta3) {
|
|
25261
|
+
error51.meta = meta3;
|
|
25262
|
+
}
|
|
25263
|
+
error51.accountTag = extractAccountTag(resource);
|
|
25264
|
+
throw error51;
|
|
25265
|
+
}
|
|
25266
|
+
__name(throwFetchError, "throwFetchError");
|
|
25267
|
+
function throwWAFBlockError(headers, method, resource, status, statusText, retryAfterMs) {
|
|
25268
|
+
const rayId = extractWAFBlockRayId(headers);
|
|
25269
|
+
throw new APIError({
|
|
25270
|
+
text: "The Cloudflare API responded with a WAF block page instead of the expected JSON response",
|
|
25271
|
+
notes: [
|
|
25272
|
+
{
|
|
25273
|
+
text: "Cloudflare's firewall (WAF) blocked this API request. This is usually a false positive."
|
|
25274
|
+
},
|
|
25275
|
+
...rayId ? [{ text: `Cloudflare Ray ID: ${rayId}` }] : [],
|
|
25276
|
+
{
|
|
25277
|
+
text: rayId ? "If the issue persists, please open a Cloudflare Support ticket and include the Ray ID above." : "If the issue persists, please open a Cloudflare Support ticket. You can find the Cloudflare Ray ID on the block page in your browser."
|
|
25278
|
+
},
|
|
25279
|
+
{
|
|
25280
|
+
text: `${method} ${resource} -> ${status} ${statusText}`
|
|
25135
25281
|
}
|
|
25136
|
-
|
|
25137
|
-
|
|
25138
|
-
|
|
25139
|
-
|
|
25140
|
-
|
|
25141
|
-
|
|
25142
|
-
|
|
25143
|
-
|
|
25144
|
-
|
|
25145
|
-
|
|
25146
|
-
|
|
25147
|
-
|
|
25282
|
+
],
|
|
25283
|
+
status,
|
|
25284
|
+
retryAfterMs,
|
|
25285
|
+
telemetryMessage: false
|
|
25286
|
+
});
|
|
25287
|
+
}
|
|
25288
|
+
__name(throwWAFBlockError, "throwWAFBlockError");
|
|
25289
|
+
async function fetchKVGetValueBase(complianceConfig, accountId, namespaceId, key, userAgent, logger, credentials) {
|
|
25290
|
+
const headers = new Headers();
|
|
25291
|
+
addAuthorizationHeader(headers, credentials);
|
|
25292
|
+
headers.set("User-Agent", userAgent);
|
|
25293
|
+
maybeAddTraceHeader(headers);
|
|
25294
|
+
const resource = `${getCloudflareApiBaseUrl(complianceConfig)}/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${key}`;
|
|
25295
|
+
logger.debug(`-- START CF API REQUEST: GET ${resource}`);
|
|
25296
|
+
logger.debug("-- END CF API REQUEST");
|
|
25297
|
+
const response = await fetch(resource, {
|
|
25298
|
+
method: "GET",
|
|
25299
|
+
headers
|
|
25300
|
+
});
|
|
25301
|
+
if (response.ok) {
|
|
25302
|
+
return await response.arrayBuffer();
|
|
25303
|
+
} else {
|
|
25304
|
+
throw new Error(
|
|
25305
|
+
`Failed to fetch ${resource} - ${response.status}: ${response.statusText}`
|
|
25148
25306
|
);
|
|
25149
25307
|
}
|
|
25150
|
-
|
|
25151
|
-
|
|
25308
|
+
}
|
|
25309
|
+
__name(fetchKVGetValueBase, "fetchKVGetValueBase");
|
|
25310
|
+
function hasCursor(result_info) {
|
|
25311
|
+
const cursor = result_info?.cursor;
|
|
25312
|
+
return cursor !== void 0 && cursor !== null && cursor !== "";
|
|
25313
|
+
}
|
|
25314
|
+
__name(hasCursor, "hasCursor");
|
|
25315
|
+
function maybeAddTraceHeader(headers) {
|
|
25316
|
+
const traceHeader = getTraceHeader();
|
|
25317
|
+
if (traceHeader) {
|
|
25318
|
+
headers.set("Cf-Trace-Id", traceHeader);
|
|
25152
25319
|
}
|
|
25153
|
-
logger?.log(`cloudflared ${version2} installed`);
|
|
25154
|
-
logger?.debug(`Binary location: ${binPath}`);
|
|
25155
25320
|
}
|
|
25156
|
-
__name(
|
|
25157
|
-
|
|
25158
|
-
|
|
25159
|
-
|
|
25160
|
-
|
|
25161
|
-
try {
|
|
25162
|
-
execFileSync("tar", ["-xzf", tempTarPath, "-C", cacheDir], {
|
|
25163
|
-
stdio: "ignore"
|
|
25164
|
-
});
|
|
25165
|
-
const extractedPath = join(cacheDir, "cloudflared");
|
|
25166
|
-
if (extractedPath !== binPath && existsSync(extractedPath)) {
|
|
25167
|
-
renameSync(extractedPath, binPath);
|
|
25168
|
-
}
|
|
25169
|
-
if (expectedChecksum) {
|
|
25170
|
-
const extractedBinary = readFileSync$1(binPath);
|
|
25171
|
-
const actualSha256 = sha256Hex(extractedBinary);
|
|
25172
|
-
if (actualSha256 !== expectedChecksum) {
|
|
25173
|
-
throw new UserError(
|
|
25174
|
-
`[cloudflared] SHA256 mismatch for downloaded cloudflared binary.
|
|
25321
|
+
__name(maybeAddTraceHeader, "maybeAddTraceHeader");
|
|
25322
|
+
function cloneHeaders(headers) {
|
|
25323
|
+
return new Headers(headers);
|
|
25324
|
+
}
|
|
25325
|
+
__name(cloneHeaders, "cloneHeaders");
|
|
25175
25326
|
|
|
25176
|
-
|
|
25177
|
-
|
|
25178
|
-
|
|
25179
|
-
|
|
25180
|
-
|
|
25181
|
-
|
|
25182
|
-
}
|
|
25183
|
-
}
|
|
25184
|
-
} finally {
|
|
25185
|
-
try {
|
|
25186
|
-
if (existsSync(tempTarPath)) {
|
|
25187
|
-
unlinkSync(tempTarPath);
|
|
25188
|
-
}
|
|
25189
|
-
} catch {
|
|
25190
|
-
}
|
|
25191
|
-
}
|
|
25327
|
+
// src/cloudflared.ts
|
|
25328
|
+
var import_command_exists = __toESM(require_command_exists2());
|
|
25329
|
+
var UPDATE_SERVICE_URL = "https://update.argotunnel.com";
|
|
25330
|
+
var CLOUDFLARED_VERSION_PATTERN = /^\d{4}\.\d+\.\d+$/;
|
|
25331
|
+
function sha256Hex(buffer) {
|
|
25332
|
+
return createHash("sha256").update(buffer).digest("hex");
|
|
25192
25333
|
}
|
|
25193
|
-
__name(
|
|
25194
|
-
|
|
25195
|
-
const
|
|
25196
|
-
|
|
25197
|
-
|
|
25198
|
-
|
|
25334
|
+
__name(sha256Hex, "sha256Hex");
|
|
25335
|
+
function getGoArch() {
|
|
25336
|
+
const nodeArch = arch();
|
|
25337
|
+
switch (nodeArch) {
|
|
25338
|
+
case "x64":
|
|
25339
|
+
return "amd64";
|
|
25340
|
+
case "arm64":
|
|
25341
|
+
return "arm64";
|
|
25342
|
+
case "arm":
|
|
25343
|
+
return "arm";
|
|
25344
|
+
default:
|
|
25199
25345
|
throw new UserError(
|
|
25200
|
-
`
|
|
25346
|
+
`Unsupported architecture for cloudflared: ${nodeArch}
|
|
25201
25347
|
|
|
25202
|
-
|
|
25203
|
-
|
|
25204
|
-
|
|
25348
|
+
cloudflared supports: x64 (amd64), arm64, arm
|
|
25349
|
+
|
|
25350
|
+
You can manually install cloudflared and set the CLOUDFLARED_PATH environment variable.
|
|
25351
|
+
Download instructions: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`,
|
|
25352
|
+
{ telemetryMessage: "tunnel cloudflared unsupported architecture" }
|
|
25205
25353
|
);
|
|
25206
|
-
}
|
|
25207
25354
|
}
|
|
25208
|
-
writeFileAtomic(binPath, buffer);
|
|
25209
25355
|
}
|
|
25210
|
-
__name(
|
|
25211
|
-
|
|
25212
|
-
|
|
25213
|
-
|
|
25214
|
-
|
|
25215
|
-
|
|
25356
|
+
__name(getGoArch, "getGoArch");
|
|
25357
|
+
function getGoOS() {
|
|
25358
|
+
switch (process.platform) {
|
|
25359
|
+
case "darwin":
|
|
25360
|
+
return "darwin";
|
|
25361
|
+
case "linux":
|
|
25362
|
+
return "linux";
|
|
25363
|
+
case "win32":
|
|
25364
|
+
return "windows";
|
|
25365
|
+
default:
|
|
25216
25366
|
throw new UserError(
|
|
25217
|
-
`
|
|
25367
|
+
`Unsupported platform for cloudflared: ${process.platform}
|
|
25218
25368
|
|
|
25219
|
-
|
|
25220
|
-
|
|
25369
|
+
cloudflared supports: darwin (macOS), linux, win32 (Windows)
|
|
25370
|
+
|
|
25371
|
+
You can manually install cloudflared and set the CLOUDFLARED_PATH environment variable.
|
|
25372
|
+
Download instructions: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`,
|
|
25373
|
+
{ telemetryMessage: "tunnel cloudflared unsupported platform" }
|
|
25221
25374
|
);
|
|
25222
|
-
}
|
|
25223
|
-
logger?.debug(`Using cloudflared from CLOUDFLARED_PATH: ${envPath}`);
|
|
25224
|
-
return envPath;
|
|
25225
25375
|
}
|
|
25226
|
-
|
|
25227
|
-
|
|
25228
|
-
|
|
25229
|
-
|
|
25230
|
-
|
|
25231
|
-
}
|
|
25232
|
-
return pathBin;
|
|
25376
|
+
}
|
|
25377
|
+
__name(getGoOS, "getGoOS");
|
|
25378
|
+
var GITHUB_RELEASE_BASE = "https://github.com/cloudflare/cloudflared/releases/download";
|
|
25379
|
+
function getAssetFilename(goOS, goArch) {
|
|
25380
|
+
if (goOS === "windows") {
|
|
25381
|
+
return `cloudflared-${goOS}-${goArch}.exe`;
|
|
25233
25382
|
}
|
|
25234
|
-
|
|
25235
|
-
|
|
25236
|
-
let needsDownload = !isBinaryExecutable(binPath);
|
|
25237
|
-
if (!needsDownload) {
|
|
25238
|
-
try {
|
|
25239
|
-
validateBinary(binPath, { logger });
|
|
25240
|
-
logger?.debug(
|
|
25241
|
-
`Using cached cloudflared ${versionInfo.version}: ${binPath}`
|
|
25242
|
-
);
|
|
25243
|
-
return binPath;
|
|
25244
|
-
} catch (e2) {
|
|
25245
|
-
logger?.debug("Cached cloudflared failed validation; re-downloading", e2);
|
|
25246
|
-
needsDownload = true;
|
|
25247
|
-
}
|
|
25383
|
+
if (goOS === "darwin") {
|
|
25384
|
+
return `cloudflared-${goOS}-${goArch}.tgz`;
|
|
25248
25385
|
}
|
|
25249
|
-
|
|
25250
|
-
|
|
25251
|
-
|
|
25252
|
-
|
|
25253
|
-
|
|
25254
|
-
|
|
25255
|
-
|
|
25256
|
-
|
|
25257
|
-
|
|
25258
|
-
|
|
25259
|
-
|
|
25260
|
-
|
|
25386
|
+
return `cloudflared-${goOS}-${goArch}`;
|
|
25387
|
+
}
|
|
25388
|
+
__name(getAssetFilename, "getAssetFilename");
|
|
25389
|
+
async function queryUpdateService(goOS, goArch, options) {
|
|
25390
|
+
const { logger } = options ?? {};
|
|
25391
|
+
const url2 = new URL(UPDATE_SERVICE_URL);
|
|
25392
|
+
url2.searchParams.set("os", goOS);
|
|
25393
|
+
url2.searchParams.set("arch", goArch);
|
|
25394
|
+
logger?.debug(`Checking for latest cloudflared: ${url2.toString()}`);
|
|
25395
|
+
let response;
|
|
25396
|
+
try {
|
|
25397
|
+
response = await fetch(url2.toString(), {
|
|
25398
|
+
headers: { "User-Agent": "wrangler" }
|
|
25399
|
+
});
|
|
25400
|
+
} catch (e2) {
|
|
25401
|
+
logger?.debug(
|
|
25402
|
+
`Failed to reach update service: ${e2 instanceof Error ? e2.message : String(e2)}`
|
|
25261
25403
|
);
|
|
25404
|
+
return null;
|
|
25262
25405
|
}
|
|
25263
|
-
if (
|
|
25264
|
-
|
|
25265
|
-
|
|
25266
|
-
|
|
25267
|
-
|
|
25406
|
+
if (!response.ok) {
|
|
25407
|
+
logger?.debug(
|
|
25408
|
+
`Update service returned ${response.status} for ${goOS}/${goArch}`
|
|
25409
|
+
);
|
|
25410
|
+
return null;
|
|
25268
25411
|
}
|
|
25269
|
-
|
|
25270
|
-
|
|
25271
|
-
|
|
25412
|
+
let data;
|
|
25413
|
+
try {
|
|
25414
|
+
data = await response.json();
|
|
25415
|
+
} catch (e2) {
|
|
25416
|
+
logger?.debug(
|
|
25417
|
+
`Update service returned non-JSON response: ${e2 instanceof Error ? e2.message : String(e2)}`
|
|
25418
|
+
);
|
|
25419
|
+
return null;
|
|
25420
|
+
}
|
|
25421
|
+
if (typeof data.version === "string" && !CLOUDFLARED_VERSION_PATTERN.test(data.version)) {
|
|
25422
|
+
throw new Error(
|
|
25423
|
+
`[cloudflared] Invalid cloudflared version returned by update service: ${data.version}`
|
|
25424
|
+
);
|
|
25425
|
+
}
|
|
25426
|
+
if (data.error || !data.url || !data.version) {
|
|
25427
|
+
return data.version ? data : null;
|
|
25428
|
+
}
|
|
25429
|
+
return data;
|
|
25272
25430
|
}
|
|
25273
|
-
__name(
|
|
25274
|
-
async function
|
|
25275
|
-
const logger = options
|
|
25276
|
-
const
|
|
25277
|
-
|
|
25278
|
-
|
|
25279
|
-
|
|
25280
|
-
|
|
25431
|
+
__name(queryUpdateService, "queryUpdateService");
|
|
25432
|
+
async function getLatestVersionInfo(options) {
|
|
25433
|
+
const { logger } = options ?? {};
|
|
25434
|
+
const goOS = getGoOS();
|
|
25435
|
+
const goArch = getGoArch();
|
|
25436
|
+
const primary = await queryUpdateService(goOS, goArch, { logger });
|
|
25437
|
+
if (primary && primary.url && primary.version) {
|
|
25438
|
+
return primary;
|
|
25439
|
+
}
|
|
25281
25440
|
logger?.debug(
|
|
25282
|
-
`
|
|
25441
|
+
`Update worker had no result for ${goOS}/${goArch}, falling back to GitHub release URL`
|
|
25283
25442
|
);
|
|
25284
|
-
const
|
|
25285
|
-
|
|
25286
|
-
|
|
25287
|
-
|
|
25288
|
-
|
|
25289
|
-
}
|
|
25290
|
-
|
|
25291
|
-
|
|
25292
|
-
|
|
25293
|
-
|
|
25294
|
-
|
|
25295
|
-
|
|
25443
|
+
const fallback = await queryUpdateService("linux", "amd64", { logger });
|
|
25444
|
+
if (!fallback?.version) {
|
|
25445
|
+
throw new UserError(
|
|
25446
|
+
`[cloudflared] Failed to determine the latest cloudflared version.
|
|
25447
|
+
|
|
25448
|
+
The update service did not return results for ${goOS}/${goArch},
|
|
25449
|
+
and the fallback query also failed.
|
|
25450
|
+
|
|
25451
|
+
You can manually install cloudflared from:
|
|
25452
|
+
https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`,
|
|
25453
|
+
{ telemetryMessage: "tunnel cloudflared version lookup failed" }
|
|
25454
|
+
);
|
|
25296
25455
|
}
|
|
25297
|
-
|
|
25456
|
+
const version2 = fallback.version;
|
|
25457
|
+
const filename = getAssetFilename(goOS, goArch);
|
|
25458
|
+
const url2 = `${GITHUB_RELEASE_BASE}/${version2}/${filename}`;
|
|
25459
|
+
const compressed = filename.endsWith(".tgz");
|
|
25460
|
+
return {
|
|
25461
|
+
url: url2,
|
|
25462
|
+
version: version2,
|
|
25463
|
+
checksum: "",
|
|
25464
|
+
// no checksum available for fallback URLs
|
|
25465
|
+
compressed,
|
|
25466
|
+
shouldUpdate: true,
|
|
25467
|
+
userMessage: "",
|
|
25468
|
+
error: ""
|
|
25469
|
+
};
|
|
25298
25470
|
}
|
|
25299
|
-
__name(
|
|
25300
|
-
|
|
25301
|
-
|
|
25302
|
-
|
|
25303
|
-
|
|
25304
|
-
|
|
25305
|
-
|
|
25306
|
-
|
|
25307
|
-
|
|
25308
|
-
|
|
25309
|
-
function
|
|
25310
|
-
|
|
25311
|
-
|
|
25312
|
-
|
|
25313
|
-
|
|
25314
|
-
|
|
25315
|
-
|
|
25316
|
-
|
|
25317
|
-
|
|
25318
|
-
|
|
25319
|
-
const
|
|
25320
|
-
|
|
25321
|
-
|
|
25322
|
-
|
|
25323
|
-
|
|
25324
|
-
|
|
25325
|
-
|
|
25326
|
-
|
|
25327
|
-
|
|
25328
|
-
|
|
25329
|
-
|
|
25330
|
-
|
|
25331
|
-
|
|
25332
|
-
|
|
25333
|
-
|
|
25334
|
-
|
|
25335
|
-
|
|
25336
|
-
|
|
25337
|
-
if (
|
|
25338
|
-
|
|
25471
|
+
__name(getLatestVersionInfo, "getLatestVersionInfo");
|
|
25472
|
+
function getCacheDir(version2) {
|
|
25473
|
+
return join(getGlobalConfigPath(), "cloudflared", version2);
|
|
25474
|
+
}
|
|
25475
|
+
__name(getCacheDir, "getCacheDir");
|
|
25476
|
+
function getCloudflaredBinPath(version2) {
|
|
25477
|
+
const binName = process.platform === "win32" ? "cloudflared.exe" : "cloudflared";
|
|
25478
|
+
return join(getCacheDir(version2), binName);
|
|
25479
|
+
}
|
|
25480
|
+
__name(getCloudflaredBinPath, "getCloudflaredBinPath");
|
|
25481
|
+
function isBinaryExecutable(binPath) {
|
|
25482
|
+
try {
|
|
25483
|
+
accessSync(binPath, constants.X_OK);
|
|
25484
|
+
return true;
|
|
25485
|
+
} catch {
|
|
25486
|
+
return false;
|
|
25487
|
+
}
|
|
25488
|
+
}
|
|
25489
|
+
__name(isBinaryExecutable, "isBinaryExecutable");
|
|
25490
|
+
function validateBinary(binPath, options) {
|
|
25491
|
+
const { logger } = options ?? {};
|
|
25492
|
+
try {
|
|
25493
|
+
const output = execFileSync(binPath, ["--version"], {
|
|
25494
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
25495
|
+
timeout: 1e4,
|
|
25496
|
+
encoding: "utf8"
|
|
25497
|
+
}).trim();
|
|
25498
|
+
logger?.debug(`cloudflared version: ${output}`);
|
|
25499
|
+
} catch {
|
|
25500
|
+
let errorMessage = `[cloudflared] Failed to validate cloudflared binary at ${binPath}
|
|
25501
|
+
|
|
25502
|
+
`;
|
|
25503
|
+
errorMessage += `This usually means:
|
|
25504
|
+
`;
|
|
25505
|
+
errorMessage += ` - The binary is corrupted or incomplete
|
|
25506
|
+
`;
|
|
25507
|
+
errorMessage += ` - You're missing required system libraries
|
|
25508
|
+
`;
|
|
25509
|
+
if (process.platform === "linux") {
|
|
25510
|
+
errorMessage += `
|
|
25511
|
+
On Linux, make sure you have the required dependencies:
|
|
25512
|
+
`;
|
|
25513
|
+
errorMessage += ` - glibc (GNU C Library)
|
|
25514
|
+
`;
|
|
25515
|
+
errorMessage += ` - For Debian/Ubuntu: sudo apt-get install libc6
|
|
25516
|
+
`;
|
|
25339
25517
|
}
|
|
25340
|
-
|
|
25341
|
-
|
|
25342
|
-
|
|
25518
|
+
const cacheDir = join(getGlobalConfigPath(), "cloudflared");
|
|
25519
|
+
errorMessage += `
|
|
25520
|
+
You can try:
|
|
25521
|
+
`;
|
|
25522
|
+
errorMessage += ` 1. Deleting the cache directory: rm -rf ${cacheDir}
|
|
25523
|
+
`;
|
|
25524
|
+
errorMessage += ` 2. Running the command again to re-download
|
|
25525
|
+
`;
|
|
25526
|
+
errorMessage += ` 3. Manually installing cloudflared: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/
|
|
25527
|
+
`;
|
|
25528
|
+
errorMessage += ` 4. Setting CLOUDFLARED_PATH to point to your cloudflared binary`;
|
|
25529
|
+
throw new UserError(errorMessage, {
|
|
25530
|
+
telemetryMessage: "tunnel cloudflared validation failed"
|
|
25343
25531
|
});
|
|
25344
|
-
}).then((result) => {
|
|
25345
|
-
expiresAt = Date.now() + defaultExpiryMs;
|
|
25346
|
-
scheduleExpiryTimeout();
|
|
25347
|
-
scheduleReminder(
|
|
25348
|
-
result.mode === "quick" ? result.publicUrl.origin : void 0
|
|
25349
|
-
);
|
|
25350
|
-
return result;
|
|
25351
|
-
});
|
|
25352
|
-
function disposeTunnel() {
|
|
25353
|
-
disposed = true;
|
|
25354
|
-
clearTunnelTimers();
|
|
25355
|
-
if (cloudflaredProcess) {
|
|
25356
|
-
terminateCloudflared(cloudflaredProcess);
|
|
25357
|
-
}
|
|
25358
25532
|
}
|
|
25359
|
-
|
|
25360
|
-
|
|
25361
|
-
|
|
25362
|
-
|
|
25363
|
-
|
|
25533
|
+
}
|
|
25534
|
+
__name(validateBinary, "validateBinary");
|
|
25535
|
+
function redactCloudflaredArgsForLogging(args) {
|
|
25536
|
+
const redacted = [...args];
|
|
25537
|
+
for (let i = 0; i < redacted.length; i++) {
|
|
25538
|
+
const arg = redacted[i];
|
|
25539
|
+
if (arg === "--token" && i + 1 < redacted.length) {
|
|
25540
|
+
redacted[i + 1] = "[REDACTED]";
|
|
25364
25541
|
}
|
|
25365
|
-
if (
|
|
25366
|
-
|
|
25367
|
-
reminderInterval = void 0;
|
|
25542
|
+
if (arg.startsWith("--token=")) {
|
|
25543
|
+
redacted[i] = "--token=[REDACTED]";
|
|
25368
25544
|
}
|
|
25369
25545
|
}
|
|
25370
|
-
|
|
25371
|
-
|
|
25372
|
-
|
|
25373
|
-
|
|
25374
|
-
|
|
25375
|
-
|
|
25376
|
-
|
|
25377
|
-
const remainingMs = expiresAt - Date.now();
|
|
25378
|
-
if (remainingMs <= 0) {
|
|
25379
|
-
return;
|
|
25380
|
-
}
|
|
25381
|
-
logger?.log(
|
|
25382
|
-
`${publicURL ? `Tunnel still open, expires in ${formatTunnelDuration(remainingMs)}: ${publicURL}` : `The tunnel is still open. It expires in ${formatTunnelDuration(remainingMs)}.`}${options.extendHint ? ` ${options.extendHint}` : ""}`
|
|
25383
|
-
);
|
|
25384
|
-
}, reminderIntervalMs);
|
|
25385
|
-
reminderInterval.unref?.();
|
|
25386
|
-
}
|
|
25546
|
+
return redacted;
|
|
25547
|
+
}
|
|
25548
|
+
__name(redactCloudflaredArgsForLogging, "redactCloudflaredArgsForLogging");
|
|
25549
|
+
function tryGetCloudflaredFromPath(options) {
|
|
25550
|
+
const { logger } = options ?? {};
|
|
25551
|
+
if (!(0, import_command_exists.sync)("cloudflared")) {
|
|
25552
|
+
return null;
|
|
25387
25553
|
}
|
|
25388
|
-
|
|
25389
|
-
|
|
25390
|
-
|
|
25554
|
+
try {
|
|
25555
|
+
validateBinary("cloudflared", { logger });
|
|
25556
|
+
return "cloudflared";
|
|
25557
|
+
} catch (e2) {
|
|
25558
|
+
logger?.debug("cloudflared found in PATH but failed validation", e2);
|
|
25559
|
+
return null;
|
|
25560
|
+
}
|
|
25561
|
+
}
|
|
25562
|
+
__name(tryGetCloudflaredFromPath, "tryGetCloudflaredFromPath");
|
|
25563
|
+
function getInstalledVersion(binPath) {
|
|
25564
|
+
try {
|
|
25565
|
+
const output = execFileSync(binPath, ["--version"], {
|
|
25566
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
25567
|
+
timeout: 1e4,
|
|
25568
|
+
encoding: "utf8"
|
|
25569
|
+
});
|
|
25570
|
+
const match = output.match(/(\d+\.\d+\.\d+)/);
|
|
25571
|
+
return match ? match[1] : null;
|
|
25572
|
+
} catch {
|
|
25573
|
+
return null;
|
|
25574
|
+
}
|
|
25575
|
+
}
|
|
25576
|
+
__name(getInstalledVersion, "getInstalledVersion");
|
|
25577
|
+
function isVersionOutdated(installed, latest) {
|
|
25578
|
+
const parse3 = /* @__PURE__ */ __name((v) => v.split(".").map(Number), "parse");
|
|
25579
|
+
const [iYear, iMonth, iPatch] = parse3(installed);
|
|
25580
|
+
const [lYear, lMonth, lPatch] = parse3(latest);
|
|
25581
|
+
if (iYear !== lYear) {
|
|
25582
|
+
return iYear < lYear;
|
|
25583
|
+
}
|
|
25584
|
+
if (iMonth !== lMonth) {
|
|
25585
|
+
return iMonth < lMonth;
|
|
25586
|
+
}
|
|
25587
|
+
return iPatch < lPatch;
|
|
25588
|
+
}
|
|
25589
|
+
__name(isVersionOutdated, "isVersionOutdated");
|
|
25590
|
+
async function warnIfOutdated(binPath, options) {
|
|
25591
|
+
const { logger } = options ?? {};
|
|
25592
|
+
try {
|
|
25593
|
+
const installed = getInstalledVersion(binPath);
|
|
25594
|
+
if (!installed) {
|
|
25391
25595
|
return;
|
|
25392
25596
|
}
|
|
25393
|
-
|
|
25394
|
-
|
|
25395
|
-
|
|
25396
|
-
expiryTimeout = setTimeout(
|
|
25397
|
-
() => {
|
|
25398
|
-
if (disposed) {
|
|
25399
|
-
return;
|
|
25400
|
-
}
|
|
25401
|
-
logger?.log("Tunnel expired. Closing tunnel.");
|
|
25402
|
-
disposeTunnel();
|
|
25403
|
-
},
|
|
25404
|
-
Math.max(0, expiresAt - Date.now())
|
|
25405
|
-
);
|
|
25406
|
-
expiryTimeout.unref();
|
|
25407
|
-
}
|
|
25408
|
-
__name(scheduleExpiryTimeout, "scheduleExpiryTimeout");
|
|
25409
|
-
function extendExpiry(ms = DEFAULT_TUNNEL_EXTENSION_MS) {
|
|
25410
|
-
if (disposed || !expiryTimeout || ms <= 0) {
|
|
25597
|
+
const latest = await queryUpdateService(getGoOS(), getGoArch(), { logger }) ?? await queryUpdateService("linux", "amd64", { logger });
|
|
25598
|
+
const latestVersion = latest?.version;
|
|
25599
|
+
if (!latestVersion) {
|
|
25411
25600
|
return;
|
|
25412
25601
|
}
|
|
25413
|
-
|
|
25414
|
-
|
|
25415
|
-
|
|
25416
|
-
|
|
25417
|
-
Math.max(expiresAt, now) + ms
|
|
25418
|
-
);
|
|
25419
|
-
const extendedByMs = expiresAt - previousExpiresAt;
|
|
25420
|
-
if (extendedByMs < ms) {
|
|
25421
|
-
logger?.log(
|
|
25422
|
-
`Tunnel expiry extended to the ${formatTunnelDuration(DEFAULT_TUNNEL_MAX_REMAINING_MS)} limit. It now expires at ${timeFormatter.format(new Date(expiresAt))}.`
|
|
25602
|
+
if (isVersionOutdated(installed, latestVersion)) {
|
|
25603
|
+
logger?.warn(
|
|
25604
|
+
`Your cloudflared (${installed}) is outdated. Latest version is ${latestVersion}.
|
|
25605
|
+
Update: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`
|
|
25423
25606
|
);
|
|
25424
|
-
scheduleExpiryTimeout();
|
|
25425
|
-
return;
|
|
25426
25607
|
}
|
|
25427
|
-
|
|
25428
|
-
|
|
25429
|
-
);
|
|
25430
|
-
scheduleExpiryTimeout();
|
|
25608
|
+
} catch (e2) {
|
|
25609
|
+
logger?.debug("Failed to check cloudflared version", e2);
|
|
25431
25610
|
}
|
|
25432
|
-
__name(extendExpiry, "extendExpiry");
|
|
25433
|
-
return {
|
|
25434
|
-
ready: /* @__PURE__ */ __name(() => readyPromise, "ready"),
|
|
25435
|
-
isOpen: /* @__PURE__ */ __name(() => !disposed, "isOpen"),
|
|
25436
|
-
dispose: disposeTunnel,
|
|
25437
|
-
extendExpiry
|
|
25438
|
-
};
|
|
25439
25611
|
}
|
|
25440
|
-
__name(
|
|
25441
|
-
function
|
|
25442
|
-
const
|
|
25443
|
-
const
|
|
25444
|
-
|
|
25445
|
-
|
|
25446
|
-
|
|
25447
|
-
|
|
25448
|
-
|
|
25449
|
-
|
|
25612
|
+
__name(warnIfOutdated, "warnIfOutdated");
|
|
25613
|
+
function writeFileAtomic(filePath, contents) {
|
|
25614
|
+
const dir2 = dirname(filePath);
|
|
25615
|
+
const tmpPath = join(
|
|
25616
|
+
dir2,
|
|
25617
|
+
`.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`
|
|
25618
|
+
);
|
|
25619
|
+
try {
|
|
25620
|
+
writeFileSync(tmpPath, contents);
|
|
25621
|
+
renameSync(tmpPath, filePath);
|
|
25622
|
+
} finally {
|
|
25623
|
+
try {
|
|
25624
|
+
if (existsSync(tmpPath)) {
|
|
25625
|
+
unlinkSync(tmpPath);
|
|
25626
|
+
}
|
|
25627
|
+
} catch {
|
|
25628
|
+
}
|
|
25450
25629
|
}
|
|
25451
|
-
return `${hours}h ${minutes}m`;
|
|
25452
25630
|
}
|
|
25453
|
-
__name(
|
|
25454
|
-
function
|
|
25455
|
-
|
|
25456
|
-
|
|
25631
|
+
__name(writeFileAtomic, "writeFileAtomic");
|
|
25632
|
+
async function downloadCloudflared(versionInfo, binPath, options) {
|
|
25633
|
+
const { logger } = options ?? {};
|
|
25634
|
+
const { url: url2, version: version2, checksum, compressed } = versionInfo;
|
|
25635
|
+
logger?.log(`Downloading cloudflared ${version2}...`);
|
|
25636
|
+
logger?.debug(`Download URL: ${url2}`);
|
|
25637
|
+
const cacheDir = dirname(binPath);
|
|
25638
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
25639
|
+
let response;
|
|
25640
|
+
try {
|
|
25641
|
+
response = await fetch(url2, {
|
|
25642
|
+
headers: { "User-Agent": "wrangler" }
|
|
25643
|
+
});
|
|
25644
|
+
} catch (e2) {
|
|
25645
|
+
throw new UserError(
|
|
25646
|
+
`[cloudflared] Failed to download cloudflared from ${url2}
|
|
25647
|
+
|
|
25648
|
+
Network error: ${e2 instanceof Error ? e2.message : String(e2)}
|
|
25649
|
+
|
|
25650
|
+
Please check your internet connection and try again.
|
|
25651
|
+
If you're behind a proxy, make sure it's configured correctly.`,
|
|
25652
|
+
{ telemetryMessage: "tunnel cloudflared download network failed" }
|
|
25653
|
+
);
|
|
25457
25654
|
}
|
|
25458
|
-
|
|
25459
|
-
|
|
25460
|
-
|
|
25461
|
-
|
|
25462
|
-
|
|
25655
|
+
if (!response.ok) {
|
|
25656
|
+
throw new UserError(
|
|
25657
|
+
`[cloudflared] Failed to download cloudflared from ${url2}
|
|
25658
|
+
|
|
25659
|
+
HTTP ${response.status}: ${response.statusText}
|
|
25660
|
+
|
|
25661
|
+
You can manually download cloudflared from:
|
|
25662
|
+
https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`,
|
|
25663
|
+
{ telemetryMessage: "tunnel cloudflared download response failed" }
|
|
25664
|
+
);
|
|
25665
|
+
}
|
|
25666
|
+
try {
|
|
25667
|
+
if (compressed) {
|
|
25668
|
+
await downloadAndExtractTarball(response, checksum, binPath, cacheDir);
|
|
25669
|
+
} else {
|
|
25670
|
+
await downloadBinary(response, checksum, binPath);
|
|
25463
25671
|
}
|
|
25464
|
-
}
|
|
25465
|
-
|
|
25672
|
+
} catch (e2) {
|
|
25673
|
+
try {
|
|
25674
|
+
if (existsSync(binPath)) {
|
|
25675
|
+
unlinkSync(binPath);
|
|
25676
|
+
}
|
|
25677
|
+
} catch {
|
|
25678
|
+
}
|
|
25679
|
+
if (e2 instanceof UserError) {
|
|
25680
|
+
throw e2;
|
|
25681
|
+
}
|
|
25682
|
+
throw new UserError(
|
|
25683
|
+
`[cloudflared] Failed to save cloudflared binary
|
|
25684
|
+
|
|
25685
|
+
Error: ${e2 instanceof Error ? e2.message : String(e2)}
|
|
25686
|
+
|
|
25687
|
+
Please ensure you have write permissions to: ${cacheDir}`,
|
|
25688
|
+
{ telemetryMessage: "tunnel cloudflared save failed" }
|
|
25689
|
+
);
|
|
25690
|
+
}
|
|
25691
|
+
if (process.platform !== "win32") {
|
|
25692
|
+
chmodSync(binPath, 493);
|
|
25693
|
+
}
|
|
25694
|
+
logger?.log(`cloudflared ${version2} installed`);
|
|
25695
|
+
logger?.debug(`Binary location: ${binPath}`);
|
|
25466
25696
|
}
|
|
25467
|
-
__name(
|
|
25468
|
-
function
|
|
25469
|
-
|
|
25470
|
-
|
|
25471
|
-
|
|
25472
|
-
|
|
25473
|
-
|
|
25474
|
-
|
|
25475
|
-
|
|
25476
|
-
|
|
25477
|
-
|
|
25478
|
-
|
|
25479
|
-
|
|
25480
|
-
|
|
25481
|
-
|
|
25482
|
-
|
|
25483
|
-
|
|
25697
|
+
__name(downloadCloudflared, "downloadCloudflared");
|
|
25698
|
+
async function downloadAndExtractTarball(response, expectedChecksum, binPath, cacheDir) {
|
|
25699
|
+
const tempTarPath = join(cacheDir, "cloudflared.tgz");
|
|
25700
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
25701
|
+
writeFileSync(tempTarPath, buffer);
|
|
25702
|
+
try {
|
|
25703
|
+
execFileSync("tar", ["-xzf", tempTarPath, "-C", cacheDir], {
|
|
25704
|
+
stdio: "ignore"
|
|
25705
|
+
});
|
|
25706
|
+
const extractedPath = join(cacheDir, "cloudflared");
|
|
25707
|
+
if (extractedPath !== binPath && existsSync(extractedPath)) {
|
|
25708
|
+
renameSync(extractedPath, binPath);
|
|
25709
|
+
}
|
|
25710
|
+
if (expectedChecksum) {
|
|
25711
|
+
const extractedBinary = readFileSync$1(binPath);
|
|
25712
|
+
const actualSha256 = sha256Hex(extractedBinary);
|
|
25713
|
+
if (actualSha256 !== expectedChecksum) {
|
|
25714
|
+
throw new UserError(
|
|
25715
|
+
`[cloudflared] SHA256 mismatch for downloaded cloudflared binary.
|
|
25716
|
+
|
|
25717
|
+
Expected: ${expectedChecksum}
|
|
25718
|
+
Actual: ${actualSha256}`,
|
|
25719
|
+
{
|
|
25720
|
+
telemetryMessage: "tunnel cloudflared extracted binary checksum mismatch"
|
|
25721
|
+
}
|
|
25484
25722
|
);
|
|
25485
25723
|
}
|
|
25486
|
-
}, timeoutMs);
|
|
25487
|
-
timeoutId.unref();
|
|
25488
|
-
if (cloudflared.stderr) {
|
|
25489
|
-
cloudflared.stderr.on("data", (data) => {
|
|
25490
|
-
const chunk = data.toString();
|
|
25491
|
-
stderrOutput += chunk;
|
|
25492
|
-
logger?.debug("[cloudflared]", chunk.trimEnd());
|
|
25493
|
-
const match = QUICK_TUNNEL_URL_REGEX.exec(stderrOutput);
|
|
25494
|
-
if (match && !resolved) {
|
|
25495
|
-
resolved = true;
|
|
25496
|
-
clearTimeout(timeoutId);
|
|
25497
|
-
resolve3({ mode: "quick", publicUrl: new URL(match[0]) });
|
|
25498
|
-
}
|
|
25499
|
-
});
|
|
25500
25724
|
}
|
|
25501
|
-
|
|
25502
|
-
|
|
25503
|
-
|
|
25504
|
-
|
|
25505
|
-
reject(new Error(`Failed to start cloudflared: ${error51.message}`));
|
|
25506
|
-
}
|
|
25507
|
-
});
|
|
25508
|
-
cloudflared.on("exit", (code, signal) => {
|
|
25509
|
-
if (!resolved) {
|
|
25510
|
-
resolved = true;
|
|
25511
|
-
clearTimeout(timeoutId);
|
|
25512
|
-
const reason = signal ? `terminated by signal ${signal}` : `exited with code ${code}`;
|
|
25513
|
-
reject(
|
|
25514
|
-
createTunnelStartupError(
|
|
25515
|
-
`cloudflared ${reason} before the tunnel was ready.`,
|
|
25516
|
-
stderrOutput,
|
|
25517
|
-
origin
|
|
25518
|
-
)
|
|
25519
|
-
);
|
|
25725
|
+
} finally {
|
|
25726
|
+
try {
|
|
25727
|
+
if (existsSync(tempTarPath)) {
|
|
25728
|
+
unlinkSync(tempTarPath);
|
|
25520
25729
|
}
|
|
25521
|
-
}
|
|
25522
|
-
|
|
25523
|
-
}
|
|
25524
|
-
__name(waitForQuickTunnelReady, "waitForQuickTunnelReady");
|
|
25525
|
-
function createTunnelStartupError(message, stderrOutput, origin) {
|
|
25526
|
-
const isQuickTunnelRateLimited = stderrOutput.includes(
|
|
25527
|
-
"429 Too Many Requests"
|
|
25528
|
-
);
|
|
25529
|
-
const errorMessage = `${message}
|
|
25530
|
-
cloudflared output:
|
|
25531
|
-
${stderrOutput || "(no output)"}
|
|
25532
|
-
|
|
25533
|
-
The local dev server started at ${origin.href}
|
|
25534
|
-
` + (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.`);
|
|
25535
|
-
if (isQuickTunnelRateLimited) {
|
|
25536
|
-
return new UserError(errorMessage, { telemetryMessage: false });
|
|
25730
|
+
} catch {
|
|
25731
|
+
}
|
|
25537
25732
|
}
|
|
25538
|
-
return new Error(errorMessage);
|
|
25539
25733
|
}
|
|
25540
|
-
__name(
|
|
25734
|
+
__name(downloadAndExtractTarball, "downloadAndExtractTarball");
|
|
25735
|
+
async function downloadBinary(response, expectedChecksum, binPath) {
|
|
25736
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
25737
|
+
if (expectedChecksum) {
|
|
25738
|
+
const actualSha256 = sha256Hex(buffer);
|
|
25739
|
+
if (actualSha256 !== expectedChecksum) {
|
|
25740
|
+
throw new UserError(
|
|
25741
|
+
`[cloudflared] SHA256 mismatch for downloaded cloudflared binary.
|
|
25541
25742
|
|
|
25542
|
-
|
|
25543
|
-
|
|
25544
|
-
|
|
25545
|
-
|
|
25546
|
-
|
|
25547
|
-
telemetryMessage: false
|
|
25548
|
-
});
|
|
25549
|
-
}
|
|
25550
|
-
__name(buildDetailedError, "buildDetailedError");
|
|
25551
|
-
function maybeThrowFriendlyError(error51) {
|
|
25552
|
-
if (error51.message === "workers.api.error.email_verification_required") {
|
|
25553
|
-
throw buildDetailedError(
|
|
25554
|
-
"Please verify your account's email address and try again.",
|
|
25555
|
-
"Check your email for a verification link, or login to https://dash.cloudflare.com and request a new one."
|
|
25556
|
-
);
|
|
25743
|
+
Expected: ${expectedChecksum}
|
|
25744
|
+
Actual: ${actualSha256}`,
|
|
25745
|
+
{ telemetryMessage: "tunnel cloudflared binary checksum mismatch" }
|
|
25746
|
+
);
|
|
25747
|
+
}
|
|
25557
25748
|
}
|
|
25749
|
+
writeFileAtomic(binPath, buffer);
|
|
25558
25750
|
}
|
|
25559
|
-
__name(
|
|
25751
|
+
__name(downloadBinary, "downloadBinary");
|
|
25752
|
+
async function getCloudflaredPath(options) {
|
|
25753
|
+
const logger = options?.logger;
|
|
25754
|
+
const envPath = getCloudflaredPathFromEnv();
|
|
25755
|
+
if (envPath) {
|
|
25756
|
+
if (!existsSync(envPath)) {
|
|
25757
|
+
throw new UserError(
|
|
25758
|
+
`CLOUDFLARED_PATH is set to "${envPath}" but the file does not exist.
|
|
25560
25759
|
|
|
25561
|
-
|
|
25562
|
-
|
|
25563
|
-
|
|
25564
|
-
|
|
25565
|
-
|
|
25566
|
-
|
|
25567
|
-
JSON.stringify(Object.fromEntries(clone2), null, 2)
|
|
25568
|
-
);
|
|
25569
|
-
}
|
|
25570
|
-
__name(logHeaders, "logHeaders");
|
|
25571
|
-
async function performApiFetchBase(complianceConfig, resource, init = {}, userAgent, logger, queryParams, abortSignal, credentials) {
|
|
25572
|
-
assert2(credentials, "credentials are required for performApiFetch");
|
|
25573
|
-
const method = init.method ?? "GET";
|
|
25574
|
-
assert2(
|
|
25575
|
-
resource.startsWith("/"),
|
|
25576
|
-
`CF API fetch - resource path must start with a "/" but got "${resource}"`
|
|
25577
|
-
);
|
|
25578
|
-
const headers = cloneHeaders(new Headers(init.headers));
|
|
25579
|
-
addAuthorizationHeader(headers, credentials);
|
|
25580
|
-
headers.set("User-Agent", userAgent);
|
|
25581
|
-
maybeAddTraceHeader(headers);
|
|
25582
|
-
const queryString = queryParams ? `?${queryParams.toString()}` : "";
|
|
25583
|
-
logger.debug(
|
|
25584
|
-
`-- START CF API REQUEST: ${method} ${getCloudflareApiBaseUrl(complianceConfig)}${resource}`
|
|
25585
|
-
);
|
|
25586
|
-
logger.debugWithSanitization?.("QUERY STRING:", queryString);
|
|
25587
|
-
logHeaders(headers, logger);
|
|
25588
|
-
logger.debugWithSanitization?.("INIT:", JSON.stringify({ ...init }, null, 2));
|
|
25589
|
-
if (init.body instanceof FormData) {
|
|
25590
|
-
logger.debugWithSanitization?.(
|
|
25591
|
-
"BODY:",
|
|
25592
|
-
await new Response(init.body).text(),
|
|
25593
|
-
null,
|
|
25594
|
-
2
|
|
25595
|
-
);
|
|
25760
|
+
Please ensure the path points to a valid cloudflared binary.`,
|
|
25761
|
+
{ telemetryMessage: "tunnel cloudflared env path missing" }
|
|
25762
|
+
);
|
|
25763
|
+
}
|
|
25764
|
+
logger?.debug(`Using cloudflared from CLOUDFLARED_PATH: ${envPath}`);
|
|
25765
|
+
return envPath;
|
|
25596
25766
|
}
|
|
25597
|
-
|
|
25598
|
-
|
|
25599
|
-
|
|
25600
|
-
{
|
|
25601
|
-
|
|
25602
|
-
...init,
|
|
25603
|
-
headers,
|
|
25604
|
-
signal: abortSignal
|
|
25767
|
+
const pathBin = tryGetCloudflaredFromPath({ logger });
|
|
25768
|
+
if (pathBin) {
|
|
25769
|
+
logger?.debug("Using cloudflared from PATH");
|
|
25770
|
+
if (!options?.skipVersionCheck) {
|
|
25771
|
+
await warnIfOutdated(pathBin, { logger });
|
|
25605
25772
|
}
|
|
25606
|
-
|
|
25607
|
-
}
|
|
25608
|
-
__name(performApiFetchBase, "performApiFetchBase");
|
|
25609
|
-
async function fetchInternalBase(complianceConfig, resource, init = {}, userAgent, logger, queryParams, abortSignal, credentials) {
|
|
25610
|
-
const method = init.method ?? "GET";
|
|
25611
|
-
const response = await performApiFetchBase(
|
|
25612
|
-
complianceConfig,
|
|
25613
|
-
resource,
|
|
25614
|
-
init,
|
|
25615
|
-
userAgent,
|
|
25616
|
-
logger,
|
|
25617
|
-
queryParams,
|
|
25618
|
-
abortSignal,
|
|
25619
|
-
credentials
|
|
25620
|
-
);
|
|
25621
|
-
const jsonText = await response.text();
|
|
25622
|
-
logger.debug(
|
|
25623
|
-
"-- START CF API RESPONSE:",
|
|
25624
|
-
response.statusText,
|
|
25625
|
-
response.status
|
|
25626
|
-
);
|
|
25627
|
-
logHeaders(response.headers, logger);
|
|
25628
|
-
logger.debugWithSanitization?.("RESPONSE:", jsonText);
|
|
25629
|
-
logger.debug("-- END CF API RESPONSE");
|
|
25630
|
-
const retryAfterMs = parseRetryAfterMs(response.headers);
|
|
25631
|
-
if (!jsonText && (response.status === 204 || response.status === 205)) {
|
|
25632
|
-
return {
|
|
25633
|
-
response: {
|
|
25634
|
-
result: {},
|
|
25635
|
-
success: true,
|
|
25636
|
-
errors: [],
|
|
25637
|
-
messages: []
|
|
25638
|
-
},
|
|
25639
|
-
status: response.status,
|
|
25640
|
-
retryAfterMs
|
|
25641
|
-
};
|
|
25773
|
+
return pathBin;
|
|
25642
25774
|
}
|
|
25643
|
-
|
|
25644
|
-
|
|
25645
|
-
|
|
25646
|
-
|
|
25647
|
-
|
|
25648
|
-
|
|
25649
|
-
|
|
25650
|
-
|
|
25775
|
+
const versionInfo = await getLatestVersionInfo({ logger });
|
|
25776
|
+
const binPath = getCloudflaredBinPath(versionInfo.version);
|
|
25777
|
+
let needsDownload = !isBinaryExecutable(binPath);
|
|
25778
|
+
if (!needsDownload) {
|
|
25779
|
+
try {
|
|
25780
|
+
validateBinary(binPath, { logger });
|
|
25781
|
+
logger?.debug(
|
|
25782
|
+
`Using cached cloudflared ${versionInfo.version}: ${binPath}`
|
|
25783
|
+
);
|
|
25784
|
+
return binPath;
|
|
25785
|
+
} catch (e2) {
|
|
25786
|
+
logger?.debug("Cached cloudflared failed validation; re-downloading", e2);
|
|
25787
|
+
needsDownload = true;
|
|
25788
|
+
}
|
|
25789
|
+
}
|
|
25790
|
+
const shouldDownload = await options?.confirmDownload?.(
|
|
25791
|
+
`cloudflared (${versionInfo.version}) is needed but not installed. Download to ${binPath}?`
|
|
25792
|
+
) ?? true;
|
|
25793
|
+
if (!shouldDownload) {
|
|
25794
|
+
throw new UserError(
|
|
25795
|
+
`cloudflared is required to run this command.
|
|
25796
|
+
|
|
25797
|
+
You can install it manually from:
|
|
25798
|
+
https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/
|
|
25799
|
+
|
|
25800
|
+
Then either add it to your PATH or set CLOUDFLARED_PATH.`,
|
|
25801
|
+
{ telemetryMessage: "tunnel cloudflared download declined" }
|
|
25651
25802
|
);
|
|
25652
25803
|
}
|
|
25653
|
-
|
|
25654
|
-
const
|
|
25655
|
-
|
|
25656
|
-
|
|
25657
|
-
|
|
25658
|
-
|
|
25659
|
-
|
|
25660
|
-
|
|
25661
|
-
|
|
25662
|
-
|
|
25663
|
-
|
|
25664
|
-
|
|
25665
|
-
|
|
25666
|
-
|
|
25667
|
-
|
|
25668
|
-
|
|
25669
|
-
|
|
25670
|
-
|
|
25671
|
-
|
|
25672
|
-
})
|
|
25804
|
+
if (existsSync(binPath)) {
|
|
25805
|
+
const cacheDir = removeCloudflaredCache(versionInfo.version);
|
|
25806
|
+
if (cacheDir) {
|
|
25807
|
+
logger?.log(`Removed cloudflared cache: ${cacheDir}`);
|
|
25808
|
+
}
|
|
25809
|
+
}
|
|
25810
|
+
await downloadCloudflared(versionInfo, binPath, { logger });
|
|
25811
|
+
validateBinary(binPath, { logger });
|
|
25812
|
+
return binPath;
|
|
25813
|
+
}
|
|
25814
|
+
__name(getCloudflaredPath, "getCloudflaredPath");
|
|
25815
|
+
async function spawnCloudflared(args, options) {
|
|
25816
|
+
const logger = options?.logger;
|
|
25817
|
+
const binPath = await getCloudflaredPath({
|
|
25818
|
+
skipVersionCheck: options?.skipVersionCheck,
|
|
25819
|
+
confirmDownload: options?.confirmDownload,
|
|
25820
|
+
logger
|
|
25821
|
+
});
|
|
25822
|
+
logger?.debug(
|
|
25823
|
+
`Spawning cloudflared: ${binPath} ${redactCloudflaredArgsForLogging(args).join(" ")}`
|
|
25824
|
+
);
|
|
25825
|
+
const cloudflared = spawn(binPath, args, {
|
|
25826
|
+
stdio: options?.stdio ?? "inherit",
|
|
25827
|
+
env: options?.env ? { ...process.env, ...options.env } : void 0
|
|
25828
|
+
});
|
|
25829
|
+
return cloudflared;
|
|
25830
|
+
}
|
|
25831
|
+
__name(spawnCloudflared, "spawnCloudflared");
|
|
25832
|
+
function removeCloudflaredCache(version2) {
|
|
25833
|
+
const cacheDir = version2 ? getCacheDir(version2) : join(getGlobalConfigPath(), "cloudflared");
|
|
25834
|
+
if (existsSync(cacheDir)) {
|
|
25835
|
+
removeDirSync(cacheDir);
|
|
25836
|
+
return cacheDir;
|
|
25673
25837
|
}
|
|
25838
|
+
return null;
|
|
25674
25839
|
}
|
|
25675
|
-
__name(
|
|
25676
|
-
|
|
25840
|
+
__name(removeCloudflaredCache, "removeCloudflaredCache");
|
|
25841
|
+
|
|
25842
|
+
// src/tunnel.ts
|
|
25843
|
+
var TUNNEL_STARTUP_TIMEOUT_MS = 3e4;
|
|
25844
|
+
var TUNNEL_API_TIMEOUT_MS = 6e4;
|
|
25845
|
+
var TUNNEL_FORCE_KILL_TIMEOUT_MS = 5e3;
|
|
25846
|
+
var DEFAULT_TUNNEL_EXPIRY_MS = 60 * 60 * 1e3;
|
|
25847
|
+
var DEFAULT_TUNNEL_EXTENSION_MS = 60 * 60 * 1e3;
|
|
25848
|
+
var DEFAULT_TUNNEL_MAX_REMAINING_MS = 3 * 60 * 60 * 1e3;
|
|
25849
|
+
var DEFAULT_TUNNEL_REMINDER_INTERVAL_MS = 10 * 60 * 1e3;
|
|
25850
|
+
var QUICK_TUNNEL_URL_REGEX = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/;
|
|
25851
|
+
var LOCAL_TUNNEL_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
25852
|
+
"localhost",
|
|
25853
|
+
"127.0.0.1",
|
|
25854
|
+
"::1",
|
|
25855
|
+
"0.0.0.0",
|
|
25856
|
+
"::"
|
|
25857
|
+
]);
|
|
25858
|
+
async function resolveNamedTunnel(name, origin, options) {
|
|
25677
25859
|
const {
|
|
25678
|
-
|
|
25679
|
-
|
|
25680
|
-
|
|
25681
|
-
|
|
25860
|
+
accountId,
|
|
25861
|
+
apiToken,
|
|
25862
|
+
complianceRegion,
|
|
25863
|
+
logger,
|
|
25864
|
+
userAgent,
|
|
25865
|
+
abortSignal
|
|
25866
|
+
} = options;
|
|
25867
|
+
const complianceConfig = { compliance_region: complianceRegion };
|
|
25868
|
+
const resource = `/accounts/${accountId}/cfd_tunnel`;
|
|
25869
|
+
const tunnels = await fetchResultBase(
|
|
25682
25870
|
complianceConfig,
|
|
25683
25871
|
resource,
|
|
25684
|
-
|
|
25872
|
+
void 0,
|
|
25685
25873
|
userAgent,
|
|
25686
25874
|
logger,
|
|
25687
|
-
|
|
25688
|
-
abortSignal,
|
|
25689
|
-
|
|
25875
|
+
new URLSearchParams({ name, is_deleted: "false" }),
|
|
25876
|
+
createApiAbortSignal(TUNNEL_API_TIMEOUT_MS, abortSignal),
|
|
25877
|
+
apiToken
|
|
25690
25878
|
);
|
|
25691
|
-
|
|
25692
|
-
|
|
25693
|
-
|
|
25694
|
-
|
|
25879
|
+
const tunnel = tunnels.find((item) => item.name === name);
|
|
25880
|
+
if (!tunnel) {
|
|
25881
|
+
throw new UserError(
|
|
25882
|
+
`No Cloudflare Tunnel named "${name}" was found in this account. Use "wrangler tunnel list" to see available tunnels.`,
|
|
25883
|
+
{ telemetryMessage: "tunnel resolve named missing tunnel" }
|
|
25884
|
+
);
|
|
25695
25885
|
}
|
|
25696
|
-
|
|
25697
|
-
|
|
25698
|
-
|
|
25699
|
-
|
|
25700
|
-
|
|
25701
|
-
let cursor;
|
|
25702
|
-
while (getMoreResults) {
|
|
25703
|
-
if (cursor) {
|
|
25704
|
-
queryParams = new URLSearchParams(queryParams);
|
|
25705
|
-
queryParams.set("cursor", cursor);
|
|
25706
|
-
}
|
|
25707
|
-
const {
|
|
25708
|
-
response: json2,
|
|
25709
|
-
status,
|
|
25710
|
-
retryAfterMs
|
|
25711
|
-
} = await fetchInternalBase(
|
|
25712
|
-
complianceConfig,
|
|
25713
|
-
resource,
|
|
25714
|
-
init,
|
|
25715
|
-
userAgent,
|
|
25716
|
-
logger,
|
|
25717
|
-
queryParams,
|
|
25718
|
-
void 0,
|
|
25719
|
-
credentials
|
|
25886
|
+
const tunnelId = tunnel.id;
|
|
25887
|
+
if (!tunnelId) {
|
|
25888
|
+
throw new FatalError(
|
|
25889
|
+
`Tunnel "${name}" was found but has no ID. This is unexpected.`,
|
|
25890
|
+
{ telemetryMessage: "tunnel resolve named missing tunnel id" }
|
|
25720
25891
|
);
|
|
25721
|
-
if (json2.success) {
|
|
25722
|
-
results.push(...json2.result);
|
|
25723
|
-
if (hasCursor(json2.result_info)) {
|
|
25724
|
-
cursor = json2.result_info?.cursor;
|
|
25725
|
-
} else {
|
|
25726
|
-
getMoreResults = false;
|
|
25727
|
-
}
|
|
25728
|
-
} else {
|
|
25729
|
-
throwFetchError(resource, json2, status, retryAfterMs);
|
|
25730
|
-
}
|
|
25731
25892
|
}
|
|
25732
|
-
|
|
25733
|
-
|
|
25734
|
-
|
|
25735
|
-
|
|
25736
|
-
|
|
25737
|
-
|
|
25738
|
-
|
|
25893
|
+
const configuration = await fetchResultBase(
|
|
25894
|
+
complianceConfig,
|
|
25895
|
+
`${resource}/${tunnelId}/configurations`,
|
|
25896
|
+
void 0,
|
|
25897
|
+
userAgent,
|
|
25898
|
+
logger,
|
|
25899
|
+
void 0,
|
|
25900
|
+
createApiAbortSignal(TUNNEL_API_TIMEOUT_MS, abortSignal),
|
|
25901
|
+
apiToken
|
|
25902
|
+
);
|
|
25903
|
+
const ingress = configuration.config?.ingress ?? [];
|
|
25904
|
+
const hostnames = getMatchingIngressHostnames(origin, ingress);
|
|
25905
|
+
if (hostnames.length === 0) {
|
|
25906
|
+
throw new UserError(
|
|
25907
|
+
createMissingIngressMessage(name, origin, {
|
|
25908
|
+
dashboardUrl: `https://dash.cloudflare.com/${accountId}/tunnels/${tunnelId}`,
|
|
25909
|
+
ingress
|
|
25910
|
+
}),
|
|
25911
|
+
{ telemetryMessage: "tunnel resolve named ingress mismatch" }
|
|
25912
|
+
);
|
|
25739
25913
|
}
|
|
25740
|
-
|
|
25741
|
-
|
|
25742
|
-
|
|
25743
|
-
|
|
25744
|
-
|
|
25914
|
+
const token = await fetchResultBase(
|
|
25915
|
+
complianceConfig,
|
|
25916
|
+
`${resource}/${tunnelId}/token`,
|
|
25917
|
+
void 0,
|
|
25918
|
+
userAgent,
|
|
25919
|
+
logger,
|
|
25920
|
+
void 0,
|
|
25921
|
+
createApiAbortSignal(TUNNEL_API_TIMEOUT_MS, abortSignal),
|
|
25922
|
+
apiToken
|
|
25923
|
+
);
|
|
25924
|
+
return { hostnames, token: String(token) };
|
|
25745
25925
|
}
|
|
25746
|
-
__name(
|
|
25747
|
-
function
|
|
25748
|
-
|
|
25749
|
-
|
|
25926
|
+
__name(resolveNamedTunnel, "resolveNamedTunnel");
|
|
25927
|
+
function getMatchingIngressHostnames(origin, ingressConfig) {
|
|
25928
|
+
const hostnames = /* @__PURE__ */ new Set();
|
|
25929
|
+
const originUrl = normalizeURL(origin);
|
|
25930
|
+
for (const ingress of ingressConfig) {
|
|
25931
|
+
try {
|
|
25932
|
+
const serviceUrl = normalizeURL(ingress.service);
|
|
25933
|
+
if (ingress.hostname && serviceUrl.toString() === originUrl.toString()) {
|
|
25934
|
+
hostnames.add(ingress.hostname);
|
|
25935
|
+
}
|
|
25936
|
+
} catch {
|
|
25937
|
+
}
|
|
25750
25938
|
}
|
|
25751
|
-
|
|
25752
|
-
|
|
25939
|
+
return [...hostnames];
|
|
25940
|
+
}
|
|
25941
|
+
__name(getMatchingIngressHostnames, "getMatchingIngressHostnames");
|
|
25942
|
+
function normalizeURL(url2) {
|
|
25943
|
+
const normalizedUrl = new URL(url2);
|
|
25944
|
+
if (LOCAL_TUNNEL_HOSTNAMES.has(normalizedUrl.hostname)) {
|
|
25945
|
+
normalizedUrl.hostname = "localhost";
|
|
25753
25946
|
}
|
|
25754
|
-
|
|
25755
|
-
|
|
25756
|
-
|
|
25947
|
+
if (!normalizedUrl.port) {
|
|
25948
|
+
switch (normalizedUrl.protocol) {
|
|
25949
|
+
case "http:":
|
|
25950
|
+
normalizedUrl.port = "80";
|
|
25951
|
+
break;
|
|
25952
|
+
case "https:":
|
|
25953
|
+
normalizedUrl.port = "443";
|
|
25954
|
+
break;
|
|
25955
|
+
}
|
|
25757
25956
|
}
|
|
25758
|
-
return
|
|
25759
|
-
}
|
|
25760
|
-
__name(parseRetryAfterValue, "parseRetryAfterValue");
|
|
25761
|
-
function parseRetryAfterMs(headers) {
|
|
25762
|
-
return parseRetryAfterValue(headers.get("Retry-After"));
|
|
25763
|
-
}
|
|
25764
|
-
__name(parseRetryAfterMs, "parseRetryAfterMs");
|
|
25765
|
-
function extractWAFBlockRayId(headers) {
|
|
25766
|
-
return headers.get("cf-ray") ?? void 0;
|
|
25767
|
-
}
|
|
25768
|
-
__name(extractWAFBlockRayId, "extractWAFBlockRayId");
|
|
25769
|
-
function extractAccountTag(resource) {
|
|
25770
|
-
const re = new RegExp("/accounts/([a-zA-Z0-9]+)/?");
|
|
25771
|
-
const matches = re.exec(resource);
|
|
25772
|
-
return matches?.[1];
|
|
25957
|
+
return normalizedUrl;
|
|
25773
25958
|
}
|
|
25774
|
-
__name(
|
|
25775
|
-
function
|
|
25776
|
-
const
|
|
25777
|
-
|
|
25778
|
-
const total = result_info?.total_count;
|
|
25779
|
-
return page !== void 0 && per_page !== void 0 && total !== void 0 && page * per_page < total;
|
|
25959
|
+
__name(normalizeURL, "normalizeURL");
|
|
25960
|
+
function createApiAbortSignal(timeoutMs, abortSignal) {
|
|
25961
|
+
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
25962
|
+
return abortSignal ? AbortSignal.any([abortSignal, timeoutSignal]) : timeoutSignal;
|
|
25780
25963
|
}
|
|
25781
|
-
__name(
|
|
25782
|
-
function
|
|
25783
|
-
|
|
25784
|
-
|
|
25785
|
-
|
|
25786
|
-
|
|
25787
|
-
|
|
25788
|
-
${
|
|
25789
|
-
|
|
25790
|
-
|
|
25791
|
-
|
|
25964
|
+
__name(createApiAbortSignal, "createApiAbortSignal");
|
|
25965
|
+
function createMissingIngressMessage(name, origin, {
|
|
25966
|
+
dashboardUrl,
|
|
25967
|
+
ingress
|
|
25968
|
+
}) {
|
|
25969
|
+
if (ingress.length === 0) {
|
|
25970
|
+
return [
|
|
25971
|
+
`Tunnel "${name}" has no routes configured.`,
|
|
25972
|
+
"",
|
|
25973
|
+
`Add a route for ${origin} in the Cloudflare dashboard:`,
|
|
25974
|
+
dashboardUrl,
|
|
25975
|
+
""
|
|
25976
|
+
].join("\n");
|
|
25977
|
+
}
|
|
25978
|
+
return [
|
|
25979
|
+
`Tunnel "${name}" has no route for ${origin}`,
|
|
25980
|
+
"",
|
|
25981
|
+
"Resolved routes:",
|
|
25982
|
+
...ingress.map(
|
|
25983
|
+
({ hostname: hostname3, service }) => ` - ${hostname3 ?? "(no hostname)"} -> ${service}`
|
|
25984
|
+
),
|
|
25985
|
+
"",
|
|
25986
|
+
"Update your local server settings or the tunnel routes in the Cloudflare dashboard:",
|
|
25987
|
+
dashboardUrl,
|
|
25988
|
+
""
|
|
25989
|
+
].join("\n");
|
|
25792
25990
|
}
|
|
25793
|
-
__name(
|
|
25794
|
-
function
|
|
25795
|
-
|
|
25796
|
-
|
|
25797
|
-
|
|
25798
|
-
|
|
25799
|
-
|
|
25800
|
-
|
|
25801
|
-
|
|
25802
|
-
|
|
25991
|
+
__name(createMissingIngressMessage, "createMissingIngressMessage");
|
|
25992
|
+
function startTunnel(options) {
|
|
25993
|
+
let disposed = false;
|
|
25994
|
+
let reminderInterval;
|
|
25995
|
+
let expiryTimeout;
|
|
25996
|
+
let expiresAt = 0;
|
|
25997
|
+
let cloudflaredProcess;
|
|
25998
|
+
const logger = options.logger;
|
|
25999
|
+
const timeoutMs = options.timeoutMs ?? TUNNEL_STARTUP_TIMEOUT_MS;
|
|
26000
|
+
const reminderIntervalMs = options.reminderIntervalMs ?? DEFAULT_TUNNEL_REMINDER_INTERVAL_MS;
|
|
26001
|
+
const defaultExpiryMs = options.expiryMs ?? DEFAULT_TUNNEL_EXPIRY_MS;
|
|
26002
|
+
const isNamedTunnel = options.token !== void 0;
|
|
26003
|
+
const timeFormatter = new Intl.DateTimeFormat(void 0, {
|
|
26004
|
+
timeStyle: "short"
|
|
26005
|
+
});
|
|
26006
|
+
const cloudflaredArgs = isNamedTunnel ? ["tunnel", "--no-autoupdate", "run"] : ["tunnel", "--no-autoupdate", "--url", options.origin.href];
|
|
26007
|
+
const cloudflaredPromise = spawnCloudflared(cloudflaredArgs, {
|
|
26008
|
+
stdio: "pipe",
|
|
26009
|
+
env: options.token ? { TUNNEL_TOKEN: options.token } : void 0,
|
|
26010
|
+
skipVersionCheck: true,
|
|
26011
|
+
logger
|
|
26012
|
+
}).then((process4) => {
|
|
26013
|
+
cloudflaredProcess = process4;
|
|
26014
|
+
if (disposed) {
|
|
26015
|
+
terminateCloudflared(process4);
|
|
26016
|
+
}
|
|
26017
|
+
return process4;
|
|
26018
|
+
});
|
|
26019
|
+
const readyPromise = cloudflaredPromise.then((process4) => {
|
|
26020
|
+
if (isNamedTunnel) {
|
|
26021
|
+
return { mode: "named" };
|
|
26022
|
+
}
|
|
26023
|
+
return waitForQuickTunnelReady(process4, timeoutMs, {
|
|
26024
|
+
logger,
|
|
26025
|
+
origin: options.origin
|
|
26026
|
+
});
|
|
26027
|
+
}).then((result) => {
|
|
26028
|
+
expiresAt = Date.now() + defaultExpiryMs;
|
|
26029
|
+
scheduleExpiryTimeout();
|
|
26030
|
+
scheduleReminder(
|
|
26031
|
+
result.mode === "quick" ? result.publicUrl.origin : void 0
|
|
26032
|
+
);
|
|
26033
|
+
return result;
|
|
26034
|
+
});
|
|
26035
|
+
function disposeTunnel() {
|
|
26036
|
+
disposed = true;
|
|
26037
|
+
clearTunnelTimers();
|
|
26038
|
+
if (cloudflaredProcess) {
|
|
26039
|
+
terminateCloudflared(cloudflaredProcess);
|
|
25803
26040
|
}
|
|
25804
26041
|
}
|
|
25805
|
-
|
|
25806
|
-
|
|
25807
|
-
|
|
25808
|
-
|
|
25809
|
-
|
|
25810
|
-
|
|
25811
|
-
|
|
25812
|
-
|
|
25813
|
-
|
|
25814
|
-
|
|
26042
|
+
__name(disposeTunnel, "disposeTunnel");
|
|
26043
|
+
function clearTunnelTimers() {
|
|
26044
|
+
if (expiryTimeout) {
|
|
26045
|
+
clearTimeout(expiryTimeout);
|
|
26046
|
+
expiryTimeout = void 0;
|
|
26047
|
+
}
|
|
26048
|
+
if (reminderInterval) {
|
|
26049
|
+
clearInterval(reminderInterval);
|
|
26050
|
+
reminderInterval = void 0;
|
|
26051
|
+
}
|
|
26052
|
+
}
|
|
26053
|
+
__name(clearTunnelTimers, "clearTunnelTimers");
|
|
26054
|
+
function scheduleReminder(publicURL) {
|
|
26055
|
+
if (reminderIntervalMs > 0) {
|
|
26056
|
+
reminderInterval = setInterval(() => {
|
|
26057
|
+
if (disposed) {
|
|
26058
|
+
return;
|
|
26059
|
+
}
|
|
26060
|
+
const remainingMs = expiresAt - Date.now();
|
|
26061
|
+
if (remainingMs <= 0) {
|
|
26062
|
+
return;
|
|
26063
|
+
}
|
|
26064
|
+
logger?.log(
|
|
26065
|
+
`${publicURL ? `Tunnel still open, expires in ${formatTunnelDuration(remainingMs)}: ${publicURL}` : `The tunnel is still open. It expires in ${formatTunnelDuration(remainingMs)}.`}${options.extendHint ? ` ${options.extendHint}` : ""}`
|
|
26066
|
+
);
|
|
26067
|
+
}, reminderIntervalMs);
|
|
26068
|
+
reminderInterval.unref?.();
|
|
26069
|
+
}
|
|
26070
|
+
}
|
|
26071
|
+
__name(scheduleReminder, "scheduleReminder");
|
|
26072
|
+
function scheduleExpiryTimeout() {
|
|
26073
|
+
if (disposed) {
|
|
26074
|
+
return;
|
|
26075
|
+
}
|
|
26076
|
+
if (expiryTimeout) {
|
|
26077
|
+
clearTimeout(expiryTimeout);
|
|
26078
|
+
}
|
|
26079
|
+
expiryTimeout = setTimeout(
|
|
26080
|
+
() => {
|
|
26081
|
+
if (disposed) {
|
|
26082
|
+
return;
|
|
25815
26083
|
}
|
|
26084
|
+
logger?.log("Tunnel expired. Closing tunnel.");
|
|
26085
|
+
disposeTunnel();
|
|
26086
|
+
},
|
|
26087
|
+
Math.max(0, expiresAt - Date.now())
|
|
26088
|
+
);
|
|
26089
|
+
expiryTimeout.unref();
|
|
26090
|
+
}
|
|
26091
|
+
__name(scheduleExpiryTimeout, "scheduleExpiryTimeout");
|
|
26092
|
+
function extendExpiry(ms = DEFAULT_TUNNEL_EXTENSION_MS) {
|
|
26093
|
+
if (disposed || !expiryTimeout || ms <= 0) {
|
|
26094
|
+
return;
|
|
26095
|
+
}
|
|
26096
|
+
const now = Date.now();
|
|
26097
|
+
const previousExpiresAt = expiresAt;
|
|
26098
|
+
expiresAt = Math.min(
|
|
26099
|
+
now + DEFAULT_TUNNEL_MAX_REMAINING_MS,
|
|
26100
|
+
Math.max(expiresAt, now) + ms
|
|
26101
|
+
);
|
|
26102
|
+
const extendedByMs = expiresAt - previousExpiresAt;
|
|
26103
|
+
if (extendedByMs < ms) {
|
|
26104
|
+
logger?.log(
|
|
26105
|
+
`Tunnel expiry extended to the ${formatTunnelDuration(DEFAULT_TUNNEL_MAX_REMAINING_MS)} limit. It now expires at ${timeFormatter.format(new Date(expiresAt))}.`
|
|
25816
26106
|
);
|
|
26107
|
+
scheduleExpiryTimeout();
|
|
26108
|
+
return;
|
|
25817
26109
|
}
|
|
26110
|
+
logger?.log(
|
|
26111
|
+
`Tunnel expiry extended by ${formatTunnelDuration(extendedByMs)}. It now expires at ${timeFormatter.format(new Date(expiresAt))}.`
|
|
26112
|
+
);
|
|
26113
|
+
scheduleExpiryTimeout();
|
|
25818
26114
|
}
|
|
26115
|
+
__name(extendExpiry, "extendExpiry");
|
|
26116
|
+
return {
|
|
26117
|
+
ready: /* @__PURE__ */ __name(() => readyPromise, "ready"),
|
|
26118
|
+
isOpen: /* @__PURE__ */ __name(() => !disposed, "isOpen"),
|
|
26119
|
+
dispose: disposeTunnel,
|
|
26120
|
+
extendExpiry
|
|
26121
|
+
};
|
|
25819
26122
|
}
|
|
25820
|
-
__name(
|
|
25821
|
-
function
|
|
25822
|
-
|
|
25823
|
-
|
|
26123
|
+
__name(startTunnel, "startTunnel");
|
|
26124
|
+
function formatTunnelDuration(durationMs) {
|
|
26125
|
+
const totalMinutes = Math.max(1, Math.ceil(durationMs / 6e4));
|
|
26126
|
+
const hours = Math.floor(totalMinutes / 60);
|
|
26127
|
+
const minutes = totalMinutes % 60;
|
|
26128
|
+
if (hours === 0) {
|
|
26129
|
+
return `${minutes}m`;
|
|
25824
26130
|
}
|
|
25825
|
-
|
|
25826
|
-
|
|
25827
|
-
|
|
25828
|
-
}
|
|
25829
|
-
__name(formatAuthorizationHeaderCharacter, "formatAuthorizationHeaderCharacter");
|
|
25830
|
-
function isPrintableCharacter(character) {
|
|
25831
|
-
return !/[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(character);
|
|
26131
|
+
if (minutes === 0) {
|
|
26132
|
+
return `${hours}h`;
|
|
26133
|
+
}
|
|
26134
|
+
return `${hours}h ${minutes}m`;
|
|
25832
26135
|
}
|
|
25833
|
-
__name(
|
|
25834
|
-
function
|
|
25835
|
-
|
|
25836
|
-
|
|
25837
|
-
|
|
25838
|
-
|
|
26136
|
+
__name(formatTunnelDuration, "formatTunnelDuration");
|
|
26137
|
+
function terminateCloudflared(cloudflared) {
|
|
26138
|
+
if (cloudflared.killed) {
|
|
26139
|
+
return;
|
|
26140
|
+
}
|
|
26141
|
+
cloudflared.unref();
|
|
26142
|
+
cloudflared.kill("SIGTERM");
|
|
26143
|
+
const forceKillTimer = setTimeout(() => {
|
|
26144
|
+
if (!cloudflared.killed) {
|
|
26145
|
+
cloudflared.kill("SIGKILL");
|
|
25839
26146
|
}
|
|
25840
|
-
|
|
25841
|
-
|
|
26147
|
+
}, TUNNEL_FORCE_KILL_TIMEOUT_MS);
|
|
26148
|
+
forceKillTimer.unref();
|
|
25842
26149
|
}
|
|
25843
|
-
__name(
|
|
25844
|
-
function
|
|
25845
|
-
|
|
25846
|
-
|
|
25847
|
-
|
|
25848
|
-
|
|
25849
|
-
|
|
25850
|
-
|
|
25851
|
-
|
|
25852
|
-
|
|
25853
|
-
|
|
25854
|
-
|
|
25855
|
-
|
|
25856
|
-
|
|
25857
|
-
|
|
25858
|
-
|
|
25859
|
-
|
|
26150
|
+
__name(terminateCloudflared, "terminateCloudflared");
|
|
26151
|
+
function waitForQuickTunnelReady(cloudflared, timeoutMs, options) {
|
|
26152
|
+
return new Promise((resolve3, reject) => {
|
|
26153
|
+
let resolved = false;
|
|
26154
|
+
let stderrOutput = "";
|
|
26155
|
+
const logger = options?.logger;
|
|
26156
|
+
const origin = options?.origin;
|
|
26157
|
+
const timeoutId = setTimeout(() => {
|
|
26158
|
+
if (!resolved) {
|
|
26159
|
+
resolved = true;
|
|
26160
|
+
terminateCloudflared(cloudflared);
|
|
26161
|
+
reject(
|
|
26162
|
+
createTunnelStartupError(
|
|
26163
|
+
`Timed out waiting for cloudflared to start (${timeoutMs / 1e3}s).`,
|
|
26164
|
+
stderrOutput,
|
|
26165
|
+
origin
|
|
26166
|
+
)
|
|
26167
|
+
);
|
|
26168
|
+
}
|
|
26169
|
+
}, timeoutMs);
|
|
26170
|
+
timeoutId.unref();
|
|
26171
|
+
if (cloudflared.stderr) {
|
|
26172
|
+
cloudflared.stderr.on("data", (data) => {
|
|
26173
|
+
const chunk = data.toString();
|
|
26174
|
+
stderrOutput += chunk;
|
|
26175
|
+
logger?.debug("[cloudflared]", chunk.trimEnd());
|
|
26176
|
+
const match = QUICK_TUNNEL_URL_REGEX.exec(stderrOutput);
|
|
26177
|
+
if (match && !resolved) {
|
|
26178
|
+
resolved = true;
|
|
26179
|
+
clearTimeout(timeoutId);
|
|
26180
|
+
resolve3({ mode: "quick", publicUrl: new URL(match[0]) });
|
|
26181
|
+
}
|
|
26182
|
+
});
|
|
25860
26183
|
}
|
|
25861
|
-
|
|
25862
|
-
|
|
25863
|
-
|
|
25864
|
-
|
|
26184
|
+
cloudflared.on("error", (error51) => {
|
|
26185
|
+
if (!resolved) {
|
|
26186
|
+
resolved = true;
|
|
26187
|
+
clearTimeout(timeoutId);
|
|
26188
|
+
reject(new Error(`Failed to start cloudflared: ${error51.message}`));
|
|
26189
|
+
}
|
|
25865
26190
|
});
|
|
25866
|
-
|
|
25867
|
-
|
|
25868
|
-
|
|
25869
|
-
|
|
25870
|
-
|
|
25871
|
-
|
|
25872
|
-
|
|
25873
|
-
|
|
25874
|
-
|
|
25875
|
-
|
|
25876
|
-
|
|
25877
|
-
|
|
25878
|
-
if (code) {
|
|
25879
|
-
error51.code = code;
|
|
25880
|
-
}
|
|
25881
|
-
const meta3 = errors[0]?.meta;
|
|
25882
|
-
if (meta3) {
|
|
25883
|
-
error51.meta = meta3;
|
|
25884
|
-
}
|
|
25885
|
-
error51.accountTag = extractAccountTag(resource);
|
|
25886
|
-
throw error51;
|
|
25887
|
-
}
|
|
25888
|
-
__name(throwFetchError, "throwFetchError");
|
|
25889
|
-
function throwWAFBlockError(headers, method, resource, status, statusText, retryAfterMs) {
|
|
25890
|
-
const rayId = extractWAFBlockRayId(headers);
|
|
25891
|
-
throw new APIError({
|
|
25892
|
-
text: "The Cloudflare API responded with a WAF block page instead of the expected JSON response",
|
|
25893
|
-
notes: [
|
|
25894
|
-
{
|
|
25895
|
-
text: "Cloudflare's firewall (WAF) blocked this API request. This is usually a false positive."
|
|
25896
|
-
},
|
|
25897
|
-
...rayId ? [{ text: `Cloudflare Ray ID: ${rayId}` }] : [],
|
|
25898
|
-
{
|
|
25899
|
-
text: rayId ? "If the issue persists, please open a Cloudflare Support ticket and include the Ray ID above." : "If the issue persists, please open a Cloudflare Support ticket. You can find the Cloudflare Ray ID on the block page in your browser."
|
|
25900
|
-
},
|
|
25901
|
-
{
|
|
25902
|
-
text: `${method} ${resource} -> ${status} ${statusText}`
|
|
26191
|
+
cloudflared.on("exit", (code, signal) => {
|
|
26192
|
+
if (!resolved) {
|
|
26193
|
+
resolved = true;
|
|
26194
|
+
clearTimeout(timeoutId);
|
|
26195
|
+
const reason = signal ? `terminated by signal ${signal}` : `exited with code ${code}`;
|
|
26196
|
+
reject(
|
|
26197
|
+
createTunnelStartupError(
|
|
26198
|
+
`cloudflared ${reason} before the tunnel was ready.`,
|
|
26199
|
+
stderrOutput,
|
|
26200
|
+
origin
|
|
26201
|
+
)
|
|
26202
|
+
);
|
|
25903
26203
|
}
|
|
25904
|
-
|
|
25905
|
-
status,
|
|
25906
|
-
retryAfterMs,
|
|
25907
|
-
telemetryMessage: false
|
|
25908
|
-
});
|
|
25909
|
-
}
|
|
25910
|
-
__name(throwWAFBlockError, "throwWAFBlockError");
|
|
25911
|
-
async function fetchKVGetValueBase(complianceConfig, accountId, namespaceId, key, userAgent, logger, credentials) {
|
|
25912
|
-
const headers = new Headers();
|
|
25913
|
-
addAuthorizationHeader(headers, credentials);
|
|
25914
|
-
headers.set("User-Agent", userAgent);
|
|
25915
|
-
maybeAddTraceHeader(headers);
|
|
25916
|
-
const resource = `${getCloudflareApiBaseUrl(complianceConfig)}/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${key}`;
|
|
25917
|
-
logger.debug(`-- START CF API REQUEST: GET ${resource}`);
|
|
25918
|
-
logger.debug("-- END CF API REQUEST");
|
|
25919
|
-
const response = await fetch(resource, {
|
|
25920
|
-
method: "GET",
|
|
25921
|
-
headers
|
|
26204
|
+
});
|
|
25922
26205
|
});
|
|
25923
|
-
if (response.ok) {
|
|
25924
|
-
return await response.arrayBuffer();
|
|
25925
|
-
} else {
|
|
25926
|
-
throw new Error(
|
|
25927
|
-
`Failed to fetch ${resource} - ${response.status}: ${response.statusText}`
|
|
25928
|
-
);
|
|
25929
|
-
}
|
|
25930
|
-
}
|
|
25931
|
-
__name(fetchKVGetValueBase, "fetchKVGetValueBase");
|
|
25932
|
-
function hasCursor(result_info) {
|
|
25933
|
-
const cursor = result_info?.cursor;
|
|
25934
|
-
return cursor !== void 0 && cursor !== null && cursor !== "";
|
|
25935
26206
|
}
|
|
25936
|
-
__name(
|
|
25937
|
-
function
|
|
25938
|
-
const
|
|
25939
|
-
|
|
25940
|
-
|
|
26207
|
+
__name(waitForQuickTunnelReady, "waitForQuickTunnelReady");
|
|
26208
|
+
function createTunnelStartupError(message, stderrOutput, origin) {
|
|
26209
|
+
const isQuickTunnelRateLimited = stderrOutput.includes(
|
|
26210
|
+
"429 Too Many Requests"
|
|
26211
|
+
);
|
|
26212
|
+
const errorMessage = `${message}
|
|
26213
|
+
cloudflared output:
|
|
26214
|
+
${stderrOutput || "(no output)"}
|
|
26215
|
+
|
|
26216
|
+
The local dev server started at ${origin.href}
|
|
26217
|
+
` + (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.`);
|
|
26218
|
+
if (isQuickTunnelRateLimited) {
|
|
26219
|
+
return new UserError(errorMessage, { telemetryMessage: false });
|
|
25941
26220
|
}
|
|
26221
|
+
return new Error(errorMessage);
|
|
25942
26222
|
}
|
|
25943
|
-
__name(
|
|
25944
|
-
function cloneHeaders(headers) {
|
|
25945
|
-
return new Headers(headers);
|
|
25946
|
-
}
|
|
25947
|
-
__name(cloneHeaders, "cloneHeaders");
|
|
26223
|
+
__name(createTunnelStartupError, "createTunnelStartupError");
|
|
25948
26224
|
|
|
25949
26225
|
// src/update-check.ts
|
|
25950
26226
|
var import_update_check = __toESM(require_update_check());
|
|
@@ -26277,4 +26553,4 @@ safe-buffer/index.js:
|
|
|
26277
26553
|
(*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> *)
|
|
26278
26554
|
*/
|
|
26279
26555
|
|
|
26280
|
-
export { ARTIFACTS_EVENT_TYPES, BunPackageManager, Diagnostics, LOGGER_LEVELS, NpmPackageManager, NubPackageManager, PatchConfigError, PnpmPackageManager, YarnPackageManager, addAuthorizationHeader, bucketFormatMessage, checkWorkerNameValidity, clearOutputFilePath, convertConfigToBindings, createConfigCache, defaultWranglerConfig, experimental_patchConfig, extractAccountTag, extractBindingsOfType, extractWAFBlockRayId, fetchInternalBase, fetchKVGetValueBase, fetchLatestNpmVersion, fetchListResultBase, fetchResultBase, formatTime, friendlyBindingNames2 as friendlyBindingNames, getBindingLocalSupport, getBindingTypeFriendlyName, getBindings, getHostFromRoute, getHostFromUrl, getInstalledPackageVersion, getPackagePath, getWorkerName, getWorkerNameFromProject, getWranglerHiddenDirPath, getWranglerTmpDir, getZoneFromRoute, hasCursor, hasMorePages, hasProperty, isCI, isDockerfile, isInteractive, isNonInteractiveOrCI, isOptionalProperty, isPackageInstalled, isPagesConfig, isRequiredProperty, isUnsafeBindingType, isValidR2BucketName, isWAFBlockResponse, maybeAddTraceHeader, normalizeAndValidateConfig, openInBrowser, parseRetryAfterMs, parseRetryAfterValue, performApiFetchBase, printBindings, renderError, retryOnAPIFailure, spawnCloudflared, startTunnel, sweepStaleWranglerTmpDirs, throwFetchError, toUrlPath, toValidWorkerName, truncate, validateBindingRemoteSetting, validatePagesConfig, writeOutput };
|
|
26556
|
+
export { ARTIFACTS_EVENT_TYPES, BunPackageManager, Diagnostics, LOGGER_LEVELS, NpmPackageManager, NubPackageManager, PatchConfigError, PnpmPackageManager, YarnPackageManager, addAuthorizationHeader, bucketFormatMessage, checkWorkerNameValidity, clearOutputFilePath, convertConfigToBindings, createConfigCache, defaultWranglerConfig, experimental_patchConfig, extractAccountTag, extractBindingsOfType, extractWAFBlockRayId, fetchInternalBase, fetchKVGetValueBase, fetchLatestNpmVersion, fetchListResultBase, fetchResultBase, formatTime, friendlyBindingNames2 as friendlyBindingNames, getBindingLocalSupport, getBindingTypeFriendlyName, getBindings, getHostFromRoute, getHostFromUrl, getInstalledPackageVersion, getPackagePath, getWorkerName, getWorkerNameFromProject, getWranglerHiddenDirPath, getWranglerTmpDir, getZoneFromRoute, hasCursor, hasMorePages, hasProperty, isCI, isDockerfile, isInteractive, isNonInteractiveOrCI, isOptionalProperty, isPackageInstalled, isPagesConfig, isRequiredProperty, isUnsafeBindingType, isValidR2BucketName, isWAFBlockResponse, maybeAddTraceHeader, normalizeAndValidateConfig, openInBrowser, parseRetryAfterMs, parseRetryAfterValue, performApiFetchBase, printBindings, renderError, resolveNamedTunnel, retryOnAPIFailure, spawnCloudflared, startTunnel, sweepStaleWranglerTmpDirs, throwFetchError, toUrlPath, toValidWorkerName, truncate, validateBindingRemoteSetting, validatePagesConfig, writeOutput };
|