@cloudflare/workers-utils 0.36.0 → 0.38.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-V6JSHOVJ.mjs → chunk-IQDQ5WRD.mjs} +10 -1
- package/dist/{chunk-XUGGRRBO.mjs → chunk-IYSONYTF.mjs} +10 -1
- package/dist/{chunk-2I4Q5QJP.mjs → chunk-S3ANQ67D.mjs} +1 -1
- package/dist/{compliance-j3-tqeao.d.mts → compliance-Dta138UP.d.mts} +75 -4
- package/dist/compliance.d.mts +1 -1
- package/dist/compliance.mjs +1 -1
- package/dist/index.d.mts +27 -3
- package/dist/index.mjs +1447 -1044
- package/dist/metafile-esm.json +1 -1
- package/dist/test-helpers/index.d.mts +1 -1
- package/dist/test-helpers/index.mjs +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
export { MetricsRegistry } from './chunk-Z62CVNHJ.mjs';
|
|
2
2
|
export { _forceColour, formatZodError } from './chunk-7AX24RFK.mjs';
|
|
3
|
-
import { isRedirectedRawConfig, configFileName, formatConfigSnippet, getDurableObjectExports, getContainerNameToClassNameMap, parsePackageJSON, readFileSync, ParseError, parseJSON, APIError, dir, parseTOML, modify, applyEdits, format, dist_default, parseJSONC } from './chunk-
|
|
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-
|
|
5
|
-
import { assertNever } from './chunk-
|
|
6
|
-
export { DEFAULT_COMPAT_DATE, assertNever, constructWranglerConfig, mapWorkerMetadataBindings } from './chunk-
|
|
3
|
+
import { isRedirectedRawConfig, configFileName, formatConfigSnippet, getDurableObjectExports, getContainerNameToClassNameMap, parsePackageJSON, readFileSync, ParseError, parseJSON, APIError, CONTAINER_IMAGES_BINDING, dir, parseTOML, modify, applyEdits, format, dist_default, parseJSONC } from './chunk-IQDQ5WRD.mjs';
|
|
4
|
+
export { APIError, CONTAINER_IMAGES_BINDING, ENVIRONMENT_TAG_PREFIX, INHERIT_SYMBOL, JSON_CONFIG_FORMATS, PATH_TO_DEPLOY_CONFIG, ParseError, SERVICE_TAG_PREFIX, configFileName, configFormat, experimental_readRawConfig, findWranglerConfig, formatConfigSnippet, getContainerDurableObjectClassNames, getContainerNameToClassNameMap, getDurableObjectContainerApps, getDurableObjectExports, hasDurableObjectExports, indexLocation, isDurableObjectContainerApp, isRedirectedConfig, parseByteSize, parseHumanDuration, parseJSON, parseJSONC, parseNonHyphenedUuid, parsePackageJSON, parseTOML, partitionExports, readFileSync, readFileSyncToBuffer, resolveContainerClassName, resolveWranglerConfigPath, searchLocation } from './chunk-IQDQ5WRD.mjs';
|
|
5
|
+
import { assertNever } from './chunk-S3ANQ67D.mjs';
|
|
6
|
+
export { DEFAULT_COMPAT_DATE, assertNever, constructWranglerConfig, mapWorkerMetadataBindings } from './chunk-S3ANQ67D.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,
|
|
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-
|
|
8
|
+
import { getCloudflareEnv, getCloudflareContainerRegistry, getCloudflareApiBaseUrl, getTraceHeader, getCloudflaredPathFromEnv, getOutputFilePathFromEnv, getOutputFileDirectoryFromEnv, getCIOverrideName, getWranglerCacheDirFromEnv } from './chunk-IYSONYTF.mjs';
|
|
9
|
+
export { COMPLIANCE_REGION_CONFIG_PUBLIC, COMPLIANCE_REGION_CONFIG_UNKNOWN, getBrowserRenderingHeadfulFromEnv, getBuildConditionsFromEnv, getBuildPlatformFromEnv, getC3CommandFromEnv, getCIGeneratePreviewAlias, getCIMatchTag, getCIOverrideName, getCIOverrideNetworkModeHost, getCfFetchEnabledFromEnv, getCfFetchPathFromEnv, getCloudflareApiBaseUrl, getCloudflareApiEnvironmentFromEnv, getCloudflareComplianceRegion, getCloudflareContainerRegistry, 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-IYSONYTF.mjs';
|
|
10
10
|
import { dedent } from './chunk-MCNIGVAF.mjs';
|
|
11
11
|
export { getBooleanEnvironmentVariableFactory, getDockerPath, getEnvironmentVariableFactory } from './chunk-3IYLA2JH.mjs';
|
|
12
12
|
import { UserError, FatalError } from './chunk-WO2IAW6M.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';
|
|
@@ -18451,7 +18451,16 @@ function normalizeAndValidateEnvironment(diagnostics, configPath, rawEnv, isDisp
|
|
|
18451
18451
|
// `name` is inheritable, so a named environment that doesn't redeclare it
|
|
18452
18452
|
// still runs under the top level Worker name — fall back to it so the
|
|
18453
18453
|
// generated container name isn't built from `undefined`.
|
|
18454
|
-
validateContainerApp(
|
|
18454
|
+
validateContainerApp(
|
|
18455
|
+
envName,
|
|
18456
|
+
rawEnv.name ?? rawConfig?.name,
|
|
18457
|
+
configPath,
|
|
18458
|
+
{
|
|
18459
|
+
complianceConfig: {
|
|
18460
|
+
compliance_region: rawEnv.compliance_region ?? topLevelEnv?.compliance_region
|
|
18461
|
+
}
|
|
18462
|
+
}
|
|
18463
|
+
),
|
|
18455
18464
|
void 0
|
|
18456
18465
|
),
|
|
18457
18466
|
send_email: notInheritable(
|
|
@@ -19903,6 +19912,15 @@ function validatePreviewsContainers(envName, configPath) {
|
|
|
19903
19912
|
});
|
|
19904
19913
|
return (diagnostics, field, value, config2) => {
|
|
19905
19914
|
if (Array.isArray(value)) {
|
|
19915
|
+
const durableObjectPolicyFields = [...value.entries()].filter(
|
|
19916
|
+
([, entry]) => entry && typeof entry === "object" && entry.scheduling_policy === "durable_object"
|
|
19917
|
+
).map(([index]) => `"${field}[${index}].scheduling_policy"`);
|
|
19918
|
+
if (durableObjectPolicyFields.length > 0) {
|
|
19919
|
+
diagnostics.errors.push(
|
|
19920
|
+
`${durableObjectPolicyFields.join(", ")} cannot be "durable_object". Durable Object-managed Containers are configured only in the top-level "containers" array.`
|
|
19921
|
+
);
|
|
19922
|
+
return false;
|
|
19923
|
+
}
|
|
19906
19924
|
const nameFields = [...value.entries()].filter(
|
|
19907
19925
|
([, entry]) => entry && typeof entry === "object" && "name" in entry
|
|
19908
19926
|
).map(([index]) => `"${field}[${index}].name"`);
|
|
@@ -19937,6 +19955,85 @@ function validatePreviewsContainers(envName, configPath) {
|
|
|
19937
19955
|
};
|
|
19938
19956
|
}
|
|
19939
19957
|
__name(validatePreviewsContainers, "validatePreviewsContainers");
|
|
19958
|
+
function validateDurableObjectContainerImages(diagnostics, field, images, complianceConfig) {
|
|
19959
|
+
if (images === void 0) {
|
|
19960
|
+
return true;
|
|
19961
|
+
}
|
|
19962
|
+
if (typeof images !== "object" || images === null || Array.isArray(images) || Object.keys(images).length === 0) {
|
|
19963
|
+
diagnostics.errors.push(
|
|
19964
|
+
`"${field}" must be a non-empty object when present.`
|
|
19965
|
+
);
|
|
19966
|
+
return false;
|
|
19967
|
+
}
|
|
19968
|
+
let valid = true;
|
|
19969
|
+
const entries = Object.entries(images);
|
|
19970
|
+
if (entries.length > 100) {
|
|
19971
|
+
diagnostics.errors.push(`"${field}" must contain at most 100 images.`);
|
|
19972
|
+
valid = false;
|
|
19973
|
+
}
|
|
19974
|
+
for (const [imageName, imageValue] of entries) {
|
|
19975
|
+
const imageField = `${field}.${imageName}`;
|
|
19976
|
+
if (imageName.length === 0 || imageName.length > 128) {
|
|
19977
|
+
diagnostics.errors.push(
|
|
19978
|
+
`"${field}" image names must be between 1 and 128 characters.`
|
|
19979
|
+
);
|
|
19980
|
+
valid = false;
|
|
19981
|
+
}
|
|
19982
|
+
if (typeof imageValue !== "object" || imageValue === null || Array.isArray(imageValue)) {
|
|
19983
|
+
diagnostics.errors.push(
|
|
19984
|
+
`"${imageField}" must be an object with either a "dockerfile" or "image" field.`
|
|
19985
|
+
);
|
|
19986
|
+
valid = false;
|
|
19987
|
+
continue;
|
|
19988
|
+
}
|
|
19989
|
+
const image = imageValue;
|
|
19990
|
+
const hasDockerfile = image.dockerfile !== void 0;
|
|
19991
|
+
const hasImage = image.image !== void 0;
|
|
19992
|
+
if (hasDockerfile === hasImage) {
|
|
19993
|
+
diagnostics.errors.push(
|
|
19994
|
+
`"${imageField}" must specify exactly one of "dockerfile" or "image".`
|
|
19995
|
+
);
|
|
19996
|
+
valid = false;
|
|
19997
|
+
}
|
|
19998
|
+
if (hasDockerfile && (typeof image.dockerfile !== "string" || image.dockerfile.length === 0)) {
|
|
19999
|
+
diagnostics.errors.push(
|
|
20000
|
+
`"${imageField}.dockerfile" must be a non-empty string.`
|
|
20001
|
+
);
|
|
20002
|
+
valid = false;
|
|
20003
|
+
}
|
|
20004
|
+
if (hasImage && (typeof image.image !== "string" || image.image.length === 0)) {
|
|
20005
|
+
diagnostics.errors.push(
|
|
20006
|
+
`"${imageField}.image" must be a non-empty string.`
|
|
20007
|
+
);
|
|
20008
|
+
valid = false;
|
|
20009
|
+
}
|
|
20010
|
+
if (typeof image.image === "string" && image.image.length > 0) {
|
|
20011
|
+
const registry2 = getCloudflareContainerRegistry(complianceConfig);
|
|
20012
|
+
const prefix = `${registry2}/`;
|
|
20013
|
+
const reference = image.image.slice(prefix.length);
|
|
20014
|
+
const digestReference = reference.match(
|
|
20015
|
+
/^[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*(?:\/[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*)+@sha256:[a-f0-9]{64}$/
|
|
20016
|
+
);
|
|
20017
|
+
if (!image.image.startsWith(prefix) || digestReference?.[0] !== reference) {
|
|
20018
|
+
diagnostics.errors.push(
|
|
20019
|
+
`"${imageField}.image" must be a digest-pinned image in the managed registry, in the form "${registry2}/<account-id>/<repository>@sha256:<64 lowercase hex characters>".`
|
|
20020
|
+
);
|
|
20021
|
+
valid = false;
|
|
20022
|
+
}
|
|
20023
|
+
}
|
|
20024
|
+
const unsupportedFields = Object.keys(image).filter(
|
|
20025
|
+
(property) => property !== "dockerfile" && property !== "image"
|
|
20026
|
+
);
|
|
20027
|
+
if (unsupportedFields.length > 0) {
|
|
20028
|
+
diagnostics.errors.push(
|
|
20029
|
+
`Unexpected fields found in ${imageField} field: ${unsupportedFields.map((property) => `"${property}"`).join(", ")}`
|
|
20030
|
+
);
|
|
20031
|
+
valid = false;
|
|
20032
|
+
}
|
|
20033
|
+
}
|
|
20034
|
+
return valid;
|
|
20035
|
+
}
|
|
20036
|
+
__name(validateDurableObjectContainerImages, "validateDurableObjectContainerImages");
|
|
19940
20037
|
function validateContainerApp(envName, topLevelName, configPath, options = {}) {
|
|
19941
20038
|
const { generateDefaultName = true } = options;
|
|
19942
20039
|
return (diagnostics, field, value, config2) => {
|
|
@@ -19950,13 +20047,23 @@ function validateContainerApp(envName, topLevelName, configPath, options = {}) {
|
|
|
19950
20047
|
return false;
|
|
19951
20048
|
}
|
|
19952
20049
|
for (const containerAppOptional of value) {
|
|
19953
|
-
|
|
19954
|
-
|
|
19955
|
-
|
|
19956
|
-
|
|
19957
|
-
|
|
19958
|
-
|
|
19959
|
-
|
|
20050
|
+
const isDurableObjectManaged = containerAppOptional.scheduling_policy === "durable_object";
|
|
20051
|
+
const hasValidDurableObjectClassName = typeof containerAppOptional.class_name === "string" && containerAppOptional.class_name.length > 0;
|
|
20052
|
+
if (isDurableObjectManaged) {
|
|
20053
|
+
if (!hasValidDurableObjectClassName) {
|
|
20054
|
+
diagnostics.errors.push(
|
|
20055
|
+
`"containers.class_name" must be a non-empty string when "containers.scheduling_policy" is "durable_object".`
|
|
20056
|
+
);
|
|
20057
|
+
}
|
|
20058
|
+
} else {
|
|
20059
|
+
validateOptionalProperty(
|
|
20060
|
+
diagnostics,
|
|
20061
|
+
field,
|
|
20062
|
+
"class_name",
|
|
20063
|
+
containerAppOptional.class_name,
|
|
20064
|
+
"string"
|
|
20065
|
+
);
|
|
20066
|
+
}
|
|
19960
20067
|
validateOptionalProperty(
|
|
19961
20068
|
diagnostics,
|
|
19962
20069
|
field,
|
|
@@ -19964,7 +20071,7 @@ function validateContainerApp(envName, topLevelName, configPath, options = {}) {
|
|
|
19964
20071
|
containerAppOptional.name,
|
|
19965
20072
|
"string"
|
|
19966
20073
|
);
|
|
19967
|
-
if (generateDefaultName && !containerAppOptional.name) {
|
|
20074
|
+
if (generateDefaultName && !containerAppOptional.name && (!isDurableObjectManaged || hasValidDurableObjectClassName)) {
|
|
19968
20075
|
if (containerAppOptional.class_name === void 0) {
|
|
19969
20076
|
diagnostics.errors.push(
|
|
19970
20077
|
`"containers.name" is required when "containers.class_name" is not defined, because there is no class name to derive a default name from. Either name this container and reference it from a Durable Object's \`exports\` entry, or set "containers.class_name".`
|
|
@@ -19979,6 +20086,23 @@ function validateContainerApp(envName, topLevelName, configPath, options = {}) {
|
|
|
19979
20086
|
containerAppOptional.name = name.toLowerCase().replace(/ /g, "-");
|
|
19980
20087
|
}
|
|
19981
20088
|
}
|
|
20089
|
+
if (isDurableObjectManaged) {
|
|
20090
|
+
validateDurableObjectContainerImages(
|
|
20091
|
+
diagnostics,
|
|
20092
|
+
`${field}.images`,
|
|
20093
|
+
containerAppOptional.images,
|
|
20094
|
+
options.complianceConfig
|
|
20095
|
+
);
|
|
20096
|
+
const unsupportedFields = Object.keys(containerAppOptional).filter(
|
|
20097
|
+
(key) => !["name", "class_name", "scheduling_policy", "images"].includes(key)
|
|
20098
|
+
);
|
|
20099
|
+
if (unsupportedFields.length > 0) {
|
|
20100
|
+
diagnostics.errors.push(
|
|
20101
|
+
`Unsupported fields for Durable Object-managed Containers in ${field}: ${unsupportedFields.map((key) => `"${key}"`).join(",")}. Only "name", "class_name", "scheduling_policy", and "images" are supported.`
|
|
20102
|
+
);
|
|
20103
|
+
}
|
|
20104
|
+
continue;
|
|
20105
|
+
}
|
|
19982
20106
|
if (!containerAppOptional.configuration?.image && !containerAppOptional.image) {
|
|
19983
20107
|
diagnostics.errors.push(
|
|
19984
20108
|
`"containers.image" field must be defined for each container app. This should be the path to your Dockerfile or an image URI pointing to the Cloudflare registry.`
|
|
@@ -20117,6 +20241,12 @@ function validateContainerApp(envName, topLevelName, configPath, options = {}) {
|
|
|
20117
20241
|
containerAppOptional.image_vars,
|
|
20118
20242
|
"object"
|
|
20119
20243
|
);
|
|
20244
|
+
validateContainerObservability(
|
|
20245
|
+
diagnostics,
|
|
20246
|
+
`${field}.observability`,
|
|
20247
|
+
containerAppOptional.observability,
|
|
20248
|
+
config2
|
|
20249
|
+
);
|
|
20120
20250
|
validateOptionalProperty(
|
|
20121
20251
|
diagnostics,
|
|
20122
20252
|
field,
|
|
@@ -20160,6 +20290,7 @@ function validateContainerApp(envName, topLevelName, configPath, options = {}) {
|
|
|
20160
20290
|
"image",
|
|
20161
20291
|
"image_build_context",
|
|
20162
20292
|
"image_vars",
|
|
20293
|
+
"observability",
|
|
20163
20294
|
"class_name",
|
|
20164
20295
|
"scheduling_policy",
|
|
20165
20296
|
"instance_type",
|
|
@@ -20985,6 +21116,7 @@ var validateBindingsHaveUniqueNames = /* @__PURE__ */ __name((diagnostics, confi
|
|
|
20985
21116
|
])
|
|
20986
21117
|
);
|
|
20987
21118
|
bindingsGroupedByType["Secret"] = config2.secrets?.required ?? [];
|
|
21119
|
+
bindingsGroupedByType["Container images"] = [CONTAINER_IMAGES_BINDING];
|
|
20988
21120
|
const bindingsGroupedByName = {};
|
|
20989
21121
|
for (const bindingType in bindingsGroupedByType) {
|
|
20990
21122
|
const bindingNames = bindingsGroupedByType[bindingType];
|
|
@@ -22400,6 +22532,133 @@ var validateExports = /* @__PURE__ */ __name((diagnostics, field, value) => {
|
|
|
22400
22532
|
}
|
|
22401
22533
|
return valid;
|
|
22402
22534
|
}, "validateExports");
|
|
22535
|
+
var CONTAINER_OBSERVABILITY_TARGET_INSTANCE_PERCENTAGE_MIN = 1;
|
|
22536
|
+
var CONTAINER_OBSERVABILITY_TARGET_INSTANCE_PERCENTAGE_MAX = 99;
|
|
22537
|
+
var CONTAINER_OBSERVABILITY_TARGET_INSTANCE_COUNT_MIN = 1;
|
|
22538
|
+
function isContainerObservabilityEnabled(observability) {
|
|
22539
|
+
return observability?.logs?.enabled === true || observability?.enabled === true;
|
|
22540
|
+
}
|
|
22541
|
+
__name(isContainerObservabilityEnabled, "isContainerObservabilityEnabled");
|
|
22542
|
+
function hasConflictingContainerObservabilityEnabledValues(observability) {
|
|
22543
|
+
return typeof observability.enabled === "boolean" && typeof observability.logs?.enabled === "boolean" && observability.enabled !== observability.logs.enabled;
|
|
22544
|
+
}
|
|
22545
|
+
__name(hasConflictingContainerObservabilityEnabledValues, "hasConflictingContainerObservabilityEnabledValues");
|
|
22546
|
+
var validateContainerObservability = /* @__PURE__ */ __name((diagnostics, field, value) => {
|
|
22547
|
+
if (value === void 0) {
|
|
22548
|
+
return true;
|
|
22549
|
+
}
|
|
22550
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
22551
|
+
diagnostics.errors.push(
|
|
22552
|
+
`"${field}" should be an object but got ${JSON.stringify(value)}.`
|
|
22553
|
+
);
|
|
22554
|
+
return false;
|
|
22555
|
+
}
|
|
22556
|
+
const val = value;
|
|
22557
|
+
let isValid = true;
|
|
22558
|
+
isValid = validateOptionalProperty(
|
|
22559
|
+
diagnostics,
|
|
22560
|
+
field,
|
|
22561
|
+
"enabled",
|
|
22562
|
+
val.enabled,
|
|
22563
|
+
"boolean"
|
|
22564
|
+
) && isValid;
|
|
22565
|
+
if (val.logs !== void 0) {
|
|
22566
|
+
if (typeof val.logs !== "object" || val.logs === null || Array.isArray(val.logs)) {
|
|
22567
|
+
diagnostics.errors.push(
|
|
22568
|
+
`Expected "${field}.logs" to be of type object but got ${JSON.stringify(
|
|
22569
|
+
val.logs
|
|
22570
|
+
)}.`
|
|
22571
|
+
);
|
|
22572
|
+
isValid = false;
|
|
22573
|
+
} else {
|
|
22574
|
+
isValid = validateOptionalProperty(
|
|
22575
|
+
diagnostics,
|
|
22576
|
+
field,
|
|
22577
|
+
"logs.enabled",
|
|
22578
|
+
val.logs.enabled,
|
|
22579
|
+
"boolean"
|
|
22580
|
+
) && isValid;
|
|
22581
|
+
isValid = validateAdditionalProperties(
|
|
22582
|
+
diagnostics,
|
|
22583
|
+
`${field}.logs`,
|
|
22584
|
+
Object.keys(val.logs),
|
|
22585
|
+
["enabled"]
|
|
22586
|
+
) && isValid;
|
|
22587
|
+
}
|
|
22588
|
+
}
|
|
22589
|
+
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)) {
|
|
22590
|
+
isValid = validateAtLeastOnePropertyRequired(diagnostics, field, [
|
|
22591
|
+
{
|
|
22592
|
+
key: "enabled",
|
|
22593
|
+
value: val.enabled,
|
|
22594
|
+
type: "boolean"
|
|
22595
|
+
},
|
|
22596
|
+
{
|
|
22597
|
+
key: "logs.enabled",
|
|
22598
|
+
value: val.logs?.enabled,
|
|
22599
|
+
type: "boolean"
|
|
22600
|
+
}
|
|
22601
|
+
]) && isValid;
|
|
22602
|
+
}
|
|
22603
|
+
isValid = validateOptionalProperty(
|
|
22604
|
+
diagnostics,
|
|
22605
|
+
field,
|
|
22606
|
+
"target_instance_percentage",
|
|
22607
|
+
val.target_instance_percentage,
|
|
22608
|
+
"number"
|
|
22609
|
+
) && isValid;
|
|
22610
|
+
isValid = validateOptionalProperty(
|
|
22611
|
+
diagnostics,
|
|
22612
|
+
field,
|
|
22613
|
+
"target_instance_count",
|
|
22614
|
+
val.target_instance_count,
|
|
22615
|
+
"number"
|
|
22616
|
+
) && isValid;
|
|
22617
|
+
isValid = validateAdditionalProperties(diagnostics, field, Object.keys(val), [
|
|
22618
|
+
"enabled",
|
|
22619
|
+
"logs",
|
|
22620
|
+
"target_instance_percentage",
|
|
22621
|
+
"target_instance_count"
|
|
22622
|
+
]) && isValid;
|
|
22623
|
+
if (hasConflictingContainerObservabilityEnabledValues(val)) {
|
|
22624
|
+
diagnostics.errors.push(
|
|
22625
|
+
`"${field}.enabled" and "${field}.logs.enabled" cannot be set to different values.`
|
|
22626
|
+
);
|
|
22627
|
+
isValid = false;
|
|
22628
|
+
}
|
|
22629
|
+
if (val.target_instance_percentage !== void 0 && val.target_instance_count !== void 0) {
|
|
22630
|
+
diagnostics.errors.push(
|
|
22631
|
+
`"${field}.target_instance_percentage" and "${field}.target_instance_count" cannot both be set.`
|
|
22632
|
+
);
|
|
22633
|
+
isValid = false;
|
|
22634
|
+
}
|
|
22635
|
+
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)) {
|
|
22636
|
+
diagnostics.errors.push(
|
|
22637
|
+
`"${field}.target_instance_percentage" must be an integer between ${CONTAINER_OBSERVABILITY_TARGET_INSTANCE_PERCENTAGE_MIN} and ${CONTAINER_OBSERVABILITY_TARGET_INSTANCE_PERCENTAGE_MAX} inclusive.`
|
|
22638
|
+
);
|
|
22639
|
+
isValid = false;
|
|
22640
|
+
}
|
|
22641
|
+
if (typeof val.target_instance_count === "number" && (!Number.isInteger(val.target_instance_count) || val.target_instance_count < CONTAINER_OBSERVABILITY_TARGET_INSTANCE_COUNT_MIN)) {
|
|
22642
|
+
diagnostics.errors.push(
|
|
22643
|
+
`"${field}.target_instance_count" must be a positive integer.`
|
|
22644
|
+
);
|
|
22645
|
+
isValid = false;
|
|
22646
|
+
}
|
|
22647
|
+
const observabilityEnabled = isContainerObservabilityEnabled(val);
|
|
22648
|
+
if (val.target_instance_percentage !== void 0 && !observabilityEnabled) {
|
|
22649
|
+
diagnostics.errors.push(
|
|
22650
|
+
`"${field}.target_instance_percentage" requires "${field}.enabled" or "${field}.logs.enabled" to be true because container observability overrides root observability.`
|
|
22651
|
+
);
|
|
22652
|
+
isValid = false;
|
|
22653
|
+
}
|
|
22654
|
+
if (val.target_instance_count !== void 0 && !observabilityEnabled) {
|
|
22655
|
+
diagnostics.errors.push(
|
|
22656
|
+
`"${field}.target_instance_count" requires "${field}.enabled" or "${field}.logs.enabled" to be true because container observability overrides root observability.`
|
|
22657
|
+
);
|
|
22658
|
+
isValid = false;
|
|
22659
|
+
}
|
|
22660
|
+
return isValid;
|
|
22661
|
+
}, "validateContainerObservability");
|
|
22403
22662
|
var validateObservability = /* @__PURE__ */ __name((diagnostics, field, value) => {
|
|
22404
22663
|
if (value === void 0) {
|
|
22405
22664
|
return true;
|
|
@@ -24305,7 +24564,9 @@ ${allTailConsumers.map(({ service, streaming }) => {
|
|
|
24305
24564
|
}
|
|
24306
24565
|
log(
|
|
24307
24566
|
`${containersTitle}
|
|
24308
|
-
${containers.map(
|
|
24567
|
+
${containers.map(
|
|
24568
|
+
(container) => container.scheduling_policy === "durable_object" ? `- ${container.class_name} (durable_object)` : `- ${container.name} (${container.image})`
|
|
24569
|
+
).join("\n")}`
|
|
24309
24570
|
);
|
|
24310
24571
|
log("");
|
|
24311
24572
|
}
|
|
@@ -24783,1168 +25044,1310 @@ function getWranglerTmpDir(projectRoot, prefix, cleanup = true) {
|
|
|
24783
25044
|
}
|
|
24784
25045
|
__name(getWranglerTmpDir, "getWranglerTmpDir");
|
|
24785
25046
|
|
|
24786
|
-
// src/
|
|
24787
|
-
|
|
24788
|
-
|
|
24789
|
-
|
|
24790
|
-
|
|
24791
|
-
|
|
25047
|
+
// src/cfetch/errors.ts
|
|
25048
|
+
function buildDetailedError(message, ...extra) {
|
|
25049
|
+
return new ParseError({
|
|
25050
|
+
text: message,
|
|
25051
|
+
notes: extra.map((text) => ({ text })),
|
|
25052
|
+
telemetryMessage: false
|
|
25053
|
+
});
|
|
24792
25054
|
}
|
|
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
|
-
);
|
|
25055
|
+
__name(buildDetailedError, "buildDetailedError");
|
|
25056
|
+
function maybeThrowFriendlyError(error51) {
|
|
25057
|
+
if (error51.message === "workers.api.error.email_verification_required") {
|
|
25058
|
+
throw buildDetailedError(
|
|
25059
|
+
"Please verify your account's email address and try again.",
|
|
25060
|
+
"Check your email for a verification link, or login to https://dash.cloudflare.com and request a new one."
|
|
25061
|
+
);
|
|
24813
25062
|
}
|
|
24814
25063
|
}
|
|
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)
|
|
25064
|
+
__name(maybeThrowFriendlyError, "maybeThrowFriendlyError");
|
|
24829
25065
|
|
|
24830
|
-
|
|
24831
|
-
|
|
24832
|
-
|
|
24833
|
-
|
|
24834
|
-
|
|
25066
|
+
// src/cfetch/index.ts
|
|
25067
|
+
function logHeaders(headers, logger) {
|
|
25068
|
+
const clone2 = cloneHeaders(headers);
|
|
25069
|
+
clone2.delete("Authorization");
|
|
25070
|
+
logger.debugWithSanitization?.(
|
|
25071
|
+
"HEADERS:",
|
|
25072
|
+
JSON.stringify(Object.fromEntries(clone2), null, 2)
|
|
25073
|
+
);
|
|
24835
25074
|
}
|
|
24836
|
-
__name(
|
|
24837
|
-
|
|
24838
|
-
|
|
24839
|
-
|
|
24840
|
-
|
|
25075
|
+
__name(logHeaders, "logHeaders");
|
|
25076
|
+
async function performApiFetchBase(complianceConfig, resource, init = {}, userAgent, logger, queryParams, abortSignal, credentials) {
|
|
25077
|
+
assert2(credentials, "credentials are required for performApiFetch");
|
|
25078
|
+
const method = init.method ?? "GET";
|
|
25079
|
+
assert2(
|
|
25080
|
+
resource.startsWith("/"),
|
|
25081
|
+
`CF API fetch - resource path must start with a "/" but got "${resource}"`
|
|
25082
|
+
);
|
|
25083
|
+
const headers = cloneHeaders(new Headers(init.headers));
|
|
25084
|
+
addAuthorizationHeader(headers, credentials);
|
|
25085
|
+
headers.set("User-Agent", userAgent);
|
|
25086
|
+
maybeAddTraceHeader(headers);
|
|
25087
|
+
const queryString = queryParams ? `?${queryParams.toString()}` : "";
|
|
25088
|
+
logger.debug(
|
|
25089
|
+
`-- START CF API REQUEST: ${method} ${getCloudflareApiBaseUrl(complianceConfig)}${resource}`
|
|
25090
|
+
);
|
|
25091
|
+
logger.debugWithSanitization?.("QUERY STRING:", queryString);
|
|
25092
|
+
logHeaders(headers, logger);
|
|
25093
|
+
logger.debugWithSanitization?.("INIT:", JSON.stringify({ ...init }, null, 2));
|
|
25094
|
+
if (init.body instanceof FormData) {
|
|
25095
|
+
logger.debugWithSanitization?.(
|
|
25096
|
+
"BODY:",
|
|
25097
|
+
await new Response(init.body).text(),
|
|
25098
|
+
null,
|
|
25099
|
+
2
|
|
25100
|
+
);
|
|
24841
25101
|
}
|
|
24842
|
-
|
|
24843
|
-
|
|
24844
|
-
|
|
24845
|
-
|
|
25102
|
+
logger.debug("-- END CF API REQUEST");
|
|
25103
|
+
return await fetch(
|
|
25104
|
+
`${getCloudflareApiBaseUrl(complianceConfig)}${resource}${queryString}`,
|
|
25105
|
+
{
|
|
25106
|
+
method,
|
|
25107
|
+
...init,
|
|
25108
|
+
headers,
|
|
25109
|
+
signal: abortSignal
|
|
25110
|
+
}
|
|
25111
|
+
);
|
|
24846
25112
|
}
|
|
24847
|
-
__name(
|
|
24848
|
-
async function
|
|
24849
|
-
const
|
|
24850
|
-
const
|
|
24851
|
-
|
|
24852
|
-
|
|
24853
|
-
|
|
24854
|
-
|
|
24855
|
-
|
|
24856
|
-
|
|
24857
|
-
|
|
24858
|
-
|
|
24859
|
-
|
|
24860
|
-
|
|
24861
|
-
|
|
24862
|
-
|
|
24863
|
-
|
|
25113
|
+
__name(performApiFetchBase, "performApiFetchBase");
|
|
25114
|
+
async function fetchInternalBase(complianceConfig, resource, init = {}, userAgent, logger, queryParams, abortSignal, credentials) {
|
|
25115
|
+
const method = init.method ?? "GET";
|
|
25116
|
+
const response = await performApiFetchBase(
|
|
25117
|
+
complianceConfig,
|
|
25118
|
+
resource,
|
|
25119
|
+
init,
|
|
25120
|
+
userAgent,
|
|
25121
|
+
logger,
|
|
25122
|
+
queryParams,
|
|
25123
|
+
abortSignal,
|
|
25124
|
+
credentials
|
|
25125
|
+
);
|
|
25126
|
+
const jsonText = await response.text();
|
|
25127
|
+
logger.debug(
|
|
25128
|
+
"-- START CF API RESPONSE:",
|
|
25129
|
+
response.statusText,
|
|
25130
|
+
response.status
|
|
25131
|
+
);
|
|
25132
|
+
logHeaders(response.headers, logger);
|
|
25133
|
+
logger.debugWithSanitization?.("RESPONSE:", jsonText);
|
|
25134
|
+
logger.debug("-- END CF API RESPONSE");
|
|
25135
|
+
const retryAfterMs = parseRetryAfterMs(response.headers);
|
|
25136
|
+
if (!jsonText && (response.status === 204 || response.status === 205)) {
|
|
25137
|
+
return {
|
|
25138
|
+
response: {
|
|
25139
|
+
result: {},
|
|
25140
|
+
success: true,
|
|
25141
|
+
errors: [],
|
|
25142
|
+
messages: []
|
|
25143
|
+
},
|
|
25144
|
+
status: response.status,
|
|
25145
|
+
retryAfterMs
|
|
25146
|
+
};
|
|
24864
25147
|
}
|
|
24865
|
-
if (
|
|
24866
|
-
|
|
24867
|
-
|
|
25148
|
+
if (isWAFBlockResponse(response.headers)) {
|
|
25149
|
+
throwWAFBlockError(
|
|
25150
|
+
response.headers,
|
|
25151
|
+
method,
|
|
25152
|
+
resource,
|
|
25153
|
+
response.status,
|
|
25154
|
+
response.statusText,
|
|
25155
|
+
retryAfterMs
|
|
24868
25156
|
);
|
|
24869
|
-
return null;
|
|
24870
25157
|
}
|
|
24871
|
-
let data;
|
|
24872
25158
|
try {
|
|
24873
|
-
|
|
24874
|
-
|
|
24875
|
-
|
|
24876
|
-
|
|
24877
|
-
|
|
24878
|
-
|
|
25159
|
+
const json2 = parseJSON(jsonText);
|
|
25160
|
+
return { response: json2, status: response.status, retryAfterMs };
|
|
25161
|
+
} catch {
|
|
25162
|
+
const rayId = extractWAFBlockRayId(response.headers);
|
|
25163
|
+
throw new APIError({
|
|
25164
|
+
text: "Received a malformed response from the API",
|
|
25165
|
+
notes: [
|
|
25166
|
+
{
|
|
25167
|
+
text: truncate(jsonText, 100)
|
|
25168
|
+
},
|
|
25169
|
+
{
|
|
25170
|
+
text: `${method} ${resource} -> ${response.status} ${response.statusText}`
|
|
25171
|
+
},
|
|
25172
|
+
...rayId ? [{ text: `Cloudflare Ray ID: ${rayId}` }] : []
|
|
25173
|
+
],
|
|
25174
|
+
status: response.status,
|
|
25175
|
+
retryAfterMs,
|
|
25176
|
+
telemetryMessage: false
|
|
25177
|
+
});
|
|
24879
25178
|
}
|
|
24880
|
-
|
|
24881
|
-
|
|
24882
|
-
|
|
25179
|
+
}
|
|
25180
|
+
__name(fetchInternalBase, "fetchInternalBase");
|
|
25181
|
+
async function fetchResultBase(complianceConfig, resource, init = {}, userAgent, logger, queryParams, abortSignal, credentials) {
|
|
25182
|
+
const {
|
|
25183
|
+
response: json2,
|
|
25184
|
+
status,
|
|
25185
|
+
retryAfterMs
|
|
25186
|
+
} = await fetchInternalBase(
|
|
25187
|
+
complianceConfig,
|
|
25188
|
+
resource,
|
|
25189
|
+
init,
|
|
25190
|
+
userAgent,
|
|
25191
|
+
logger,
|
|
25192
|
+
queryParams,
|
|
25193
|
+
abortSignal,
|
|
25194
|
+
credentials
|
|
25195
|
+
);
|
|
25196
|
+
if (json2.success) {
|
|
25197
|
+
return json2.result;
|
|
25198
|
+
} else {
|
|
25199
|
+
throwFetchError(resource, json2, status, retryAfterMs);
|
|
25200
|
+
}
|
|
25201
|
+
}
|
|
25202
|
+
__name(fetchResultBase, "fetchResultBase");
|
|
25203
|
+
async function fetchListResultBase(complianceConfig, resource, init = {}, userAgent, logger, queryParams, credentials) {
|
|
25204
|
+
const results = [];
|
|
25205
|
+
let getMoreResults = true;
|
|
25206
|
+
let cursor;
|
|
25207
|
+
while (getMoreResults) {
|
|
25208
|
+
if (cursor) {
|
|
25209
|
+
queryParams = new URLSearchParams$1(queryParams);
|
|
25210
|
+
queryParams.set("cursor", cursor);
|
|
25211
|
+
}
|
|
25212
|
+
const {
|
|
25213
|
+
response: json2,
|
|
25214
|
+
status,
|
|
25215
|
+
retryAfterMs
|
|
25216
|
+
} = await fetchInternalBase(
|
|
25217
|
+
complianceConfig,
|
|
25218
|
+
resource,
|
|
25219
|
+
init,
|
|
25220
|
+
userAgent,
|
|
25221
|
+
logger,
|
|
25222
|
+
queryParams,
|
|
25223
|
+
void 0,
|
|
25224
|
+
credentials
|
|
24883
25225
|
);
|
|
25226
|
+
if (json2.success) {
|
|
25227
|
+
results.push(...json2.result);
|
|
25228
|
+
if (hasCursor(json2.result_info)) {
|
|
25229
|
+
cursor = json2.result_info?.cursor;
|
|
25230
|
+
} else {
|
|
25231
|
+
getMoreResults = false;
|
|
25232
|
+
}
|
|
25233
|
+
} else {
|
|
25234
|
+
throwFetchError(resource, json2, status, retryAfterMs);
|
|
25235
|
+
}
|
|
24884
25236
|
}
|
|
24885
|
-
|
|
24886
|
-
|
|
25237
|
+
return results;
|
|
25238
|
+
}
|
|
25239
|
+
__name(fetchListResultBase, "fetchListResultBase");
|
|
25240
|
+
function truncate(text, maxLength) {
|
|
25241
|
+
const { length } = text;
|
|
25242
|
+
if (length <= maxLength) {
|
|
25243
|
+
return text;
|
|
24887
25244
|
}
|
|
24888
|
-
return
|
|
25245
|
+
return `${text.substring(0, maxLength)}... (length = ${length})`;
|
|
24889
25246
|
}
|
|
24890
|
-
__name(
|
|
24891
|
-
|
|
24892
|
-
|
|
24893
|
-
|
|
24894
|
-
|
|
24895
|
-
|
|
24896
|
-
if (
|
|
24897
|
-
return
|
|
25247
|
+
__name(truncate, "truncate");
|
|
25248
|
+
function isWAFBlockResponse(headers) {
|
|
25249
|
+
return headers.get("cf-mitigated") === "challenge";
|
|
25250
|
+
}
|
|
25251
|
+
__name(isWAFBlockResponse, "isWAFBlockResponse");
|
|
25252
|
+
function parseRetryAfterValue(retryAfter) {
|
|
25253
|
+
if (!retryAfter) {
|
|
25254
|
+
return void 0;
|
|
24898
25255
|
}
|
|
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
|
-
);
|
|
25256
|
+
if (/^\d+$/.test(retryAfter.trim())) {
|
|
25257
|
+
return Number(retryAfter) * 1e3;
|
|
24914
25258
|
}
|
|
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
|
-
};
|
|
25259
|
+
const retryAfterDate = new Date(retryAfter);
|
|
25260
|
+
if (!Number.isNaN(retryAfterDate.getTime())) {
|
|
25261
|
+
return Math.max(0, retryAfterDate.getTime() - Date.now());
|
|
25262
|
+
}
|
|
25263
|
+
return void 0;
|
|
24929
25264
|
}
|
|
24930
|
-
__name(
|
|
24931
|
-
function
|
|
24932
|
-
return
|
|
25265
|
+
__name(parseRetryAfterValue, "parseRetryAfterValue");
|
|
25266
|
+
function parseRetryAfterMs(headers) {
|
|
25267
|
+
return parseRetryAfterValue(headers.get("Retry-After"));
|
|
24933
25268
|
}
|
|
24934
|
-
__name(
|
|
24935
|
-
function
|
|
24936
|
-
|
|
24937
|
-
return join(getCacheDir(version2), binName);
|
|
25269
|
+
__name(parseRetryAfterMs, "parseRetryAfterMs");
|
|
25270
|
+
function extractWAFBlockRayId(headers) {
|
|
25271
|
+
return headers.get("cf-ray") ?? void 0;
|
|
24938
25272
|
}
|
|
24939
|
-
__name(
|
|
24940
|
-
function
|
|
24941
|
-
|
|
24942
|
-
|
|
24943
|
-
|
|
24944
|
-
} catch {
|
|
24945
|
-
return false;
|
|
24946
|
-
}
|
|
25273
|
+
__name(extractWAFBlockRayId, "extractWAFBlockRayId");
|
|
25274
|
+
function extractAccountTag(resource) {
|
|
25275
|
+
const re = new RegExp("/accounts/([a-zA-Z0-9]+)/?");
|
|
25276
|
+
const matches = re.exec(resource);
|
|
25277
|
+
return matches?.[1];
|
|
24947
25278
|
}
|
|
24948
|
-
__name(
|
|
24949
|
-
function
|
|
24950
|
-
const
|
|
24951
|
-
|
|
24952
|
-
|
|
24953
|
-
|
|
24954
|
-
|
|
24955
|
-
|
|
24956
|
-
|
|
24957
|
-
|
|
24958
|
-
|
|
24959
|
-
|
|
25279
|
+
__name(extractAccountTag, "extractAccountTag");
|
|
25280
|
+
function hasMorePages(result_info) {
|
|
25281
|
+
const page = result_info?.page;
|
|
25282
|
+
const per_page = result_info?.per_page;
|
|
25283
|
+
const total = result_info?.total_count;
|
|
25284
|
+
return page !== void 0 && per_page !== void 0 && total !== void 0 && page * per_page < total;
|
|
25285
|
+
}
|
|
25286
|
+
__name(hasMorePages, "hasMorePages");
|
|
25287
|
+
function renderError(err, level = 0) {
|
|
25288
|
+
const indent = " ".repeat(level);
|
|
25289
|
+
const message = err.message ?? "";
|
|
25290
|
+
const chainedMessages = "error_chain" in err ? err.error_chain?.map(
|
|
25291
|
+
(chainedError) => `
|
|
24960
25292
|
|
|
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
|
-
}
|
|
25293
|
+
${indent}- ${renderError(chainedError, level + 1)}`
|
|
25294
|
+
).join("\n") ?? "" : "";
|
|
25295
|
+
return (err.code ? `${message} [code: ${err.code}]` : message) + (err.documentation_url ? `
|
|
25296
|
+
${indent}To learn more about this error, visit: ${err.documentation_url}` : "") + chainedMessages;
|
|
24992
25297
|
}
|
|
24993
|
-
__name(
|
|
24994
|
-
function
|
|
24995
|
-
|
|
24996
|
-
|
|
24997
|
-
|
|
24998
|
-
|
|
24999
|
-
|
|
25000
|
-
}
|
|
25001
|
-
|
|
25002
|
-
|
|
25298
|
+
__name(renderError, "renderError");
|
|
25299
|
+
function addAuthorizationHeader(headers, auth, overrideExisting = false) {
|
|
25300
|
+
if (!headers.has("Authorization") || overrideExisting) {
|
|
25301
|
+
if ("apiToken" in auth) {
|
|
25302
|
+
const authorizationHeader = `Bearer ${auth.apiToken}`;
|
|
25303
|
+
validateAuthorizationHeaderValue(authorizationHeader);
|
|
25304
|
+
headers.set("Authorization", authorizationHeader);
|
|
25305
|
+
} else {
|
|
25306
|
+
headers.set("X-Auth-Key", auth.authKey);
|
|
25307
|
+
headers.set("X-Auth-Email", auth.authEmail);
|
|
25003
25308
|
}
|
|
25004
25309
|
}
|
|
25005
|
-
return redacted;
|
|
25006
25310
|
}
|
|
25007
|
-
__name(
|
|
25008
|
-
function
|
|
25009
|
-
const
|
|
25010
|
-
|
|
25011
|
-
|
|
25012
|
-
|
|
25013
|
-
|
|
25014
|
-
|
|
25015
|
-
|
|
25016
|
-
|
|
25017
|
-
|
|
25018
|
-
|
|
25311
|
+
__name(addAuthorizationHeader, "addAuthorizationHeader");
|
|
25312
|
+
function validateAuthorizationHeaderValue(value) {
|
|
25313
|
+
for (const character of value) {
|
|
25314
|
+
const codePoint = character.codePointAt(0);
|
|
25315
|
+
if (codePoint === void 0 || codePoint > 255) {
|
|
25316
|
+
throw new UserError(
|
|
25317
|
+
`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.`,
|
|
25318
|
+
{
|
|
25319
|
+
telemetryMessage: "cfetch auth invalid authorization header"
|
|
25320
|
+
}
|
|
25321
|
+
);
|
|
25322
|
+
}
|
|
25019
25323
|
}
|
|
25020
25324
|
}
|
|
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;
|
|
25325
|
+
__name(validateAuthorizationHeaderValue, "validateAuthorizationHeaderValue");
|
|
25326
|
+
function formatAuthorizationHeaderCharacter(character, codePoint) {
|
|
25327
|
+
if (codePoint === void 0) {
|
|
25328
|
+
return '"\\u{unknown}"';
|
|
25033
25329
|
}
|
|
25330
|
+
const codePointLabel = `U+${codePoint.toString(16).toUpperCase().padStart(4, "0")}`;
|
|
25331
|
+
const characterLabel = isPrintableCharacter(character) ? `"${character}"` : `"${escapeCharacter(character)}"`;
|
|
25332
|
+
return `${characterLabel} (${codePointLabel})`;
|
|
25034
25333
|
}
|
|
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;
|
|
25334
|
+
__name(formatAuthorizationHeaderCharacter, "formatAuthorizationHeaderCharacter");
|
|
25335
|
+
function isPrintableCharacter(character) {
|
|
25336
|
+
return !/[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(character);
|
|
25047
25337
|
}
|
|
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
|
-
);
|
|
25338
|
+
__name(isPrintableCharacter, "isPrintableCharacter");
|
|
25339
|
+
function escapeCharacter(character) {
|
|
25340
|
+
return Array.from(character).map((c) => {
|
|
25341
|
+
const codePoint = c.codePointAt(0);
|
|
25342
|
+
if (codePoint === void 0) {
|
|
25343
|
+
return "";
|
|
25066
25344
|
}
|
|
25067
|
-
|
|
25068
|
-
|
|
25069
|
-
}
|
|
25345
|
+
return codePoint <= 65535 ? `\\u${codePoint.toString(16).toUpperCase().padStart(4, "0")}` : `\\u{${codePoint.toString(16).toUpperCase()}}`;
|
|
25346
|
+
}).join("");
|
|
25070
25347
|
}
|
|
25071
|
-
__name(
|
|
25072
|
-
function
|
|
25073
|
-
const
|
|
25074
|
-
const
|
|
25075
|
-
|
|
25076
|
-
|
|
25077
|
-
|
|
25078
|
-
|
|
25079
|
-
|
|
25080
|
-
|
|
25081
|
-
|
|
25082
|
-
|
|
25083
|
-
|
|
25084
|
-
|
|
25085
|
-
|
|
25086
|
-
|
|
25348
|
+
__name(escapeCharacter, "escapeCharacter");
|
|
25349
|
+
function throwFetchError(resource, response, status, retryAfterMs) {
|
|
25350
|
+
const errors = response.errors ?? [];
|
|
25351
|
+
for (const error52 of errors) {
|
|
25352
|
+
maybeThrowFriendlyError(error52);
|
|
25353
|
+
}
|
|
25354
|
+
const notes = [
|
|
25355
|
+
...errors.map((err) => ({ text: renderError(err) })),
|
|
25356
|
+
...response.messages?.map((msg) => ({
|
|
25357
|
+
text: typeof msg === "string" ? msg : msg.message ?? String(msg)
|
|
25358
|
+
})) ?? []
|
|
25359
|
+
];
|
|
25360
|
+
if (notes.length === 0) {
|
|
25361
|
+
const raw = response;
|
|
25362
|
+
const fallbackMessage = typeof raw.error === "string" ? `${raw.error}${raw.code ? ` [code: ${raw.code}]` : ""}` : void 0;
|
|
25363
|
+
if (fallbackMessage) {
|
|
25364
|
+
notes.push({ text: fallbackMessage });
|
|
25087
25365
|
}
|
|
25088
25366
|
}
|
|
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" }
|
|
25367
|
+
if (retryAfterMs !== void 0) {
|
|
25368
|
+
notes.push({
|
|
25369
|
+
text: `The API responded with a "Retry-After" header indicating you should wait ${Math.ceil(retryAfterMs / 1e3)} second(s) before retrying.`
|
|
25102
25370
|
});
|
|
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
25371
|
}
|
|
25114
|
-
|
|
25115
|
-
|
|
25116
|
-
|
|
25117
|
-
|
|
25118
|
-
|
|
25119
|
-
|
|
25120
|
-
|
|
25121
|
-
|
|
25122
|
-
|
|
25123
|
-
|
|
25372
|
+
const error51 = new APIError({
|
|
25373
|
+
text: `A request to the Cloudflare API (${resource}) failed.`,
|
|
25374
|
+
notes,
|
|
25375
|
+
status,
|
|
25376
|
+
// hoist the parsed `Retry-After` header (if any) so consumers such as
|
|
25377
|
+
// `retryOnAPIFailure()` can back off for the amount of time the API
|
|
25378
|
+
// asked us to wait, e.g. when rate limited (HTTP 429).
|
|
25379
|
+
retryAfterMs,
|
|
25380
|
+
telemetryMessage: false
|
|
25381
|
+
});
|
|
25382
|
+
const code = errors[0]?.code;
|
|
25383
|
+
if (code) {
|
|
25384
|
+
error51.code = code;
|
|
25124
25385
|
}
|
|
25125
|
-
|
|
25126
|
-
|
|
25127
|
-
|
|
25128
|
-
|
|
25129
|
-
|
|
25130
|
-
|
|
25131
|
-
|
|
25132
|
-
|
|
25133
|
-
|
|
25134
|
-
|
|
25135
|
-
|
|
25136
|
-
|
|
25137
|
-
|
|
25138
|
-
|
|
25139
|
-
|
|
25140
|
-
|
|
25141
|
-
|
|
25142
|
-
|
|
25143
|
-
|
|
25144
|
-
|
|
25145
|
-
|
|
25146
|
-
|
|
25147
|
-
|
|
25386
|
+
const meta3 = errors[0]?.meta;
|
|
25387
|
+
if (meta3) {
|
|
25388
|
+
error51.meta = meta3;
|
|
25389
|
+
}
|
|
25390
|
+
error51.accountTag = extractAccountTag(resource);
|
|
25391
|
+
throw error51;
|
|
25392
|
+
}
|
|
25393
|
+
__name(throwFetchError, "throwFetchError");
|
|
25394
|
+
function throwWAFBlockError(headers, method, resource, status, statusText, retryAfterMs) {
|
|
25395
|
+
const rayId = extractWAFBlockRayId(headers);
|
|
25396
|
+
throw new APIError({
|
|
25397
|
+
text: "The Cloudflare API responded with a WAF block page instead of the expected JSON response",
|
|
25398
|
+
notes: [
|
|
25399
|
+
{
|
|
25400
|
+
text: "Cloudflare's firewall (WAF) blocked this API request. This is usually a false positive."
|
|
25401
|
+
},
|
|
25402
|
+
...rayId ? [{ text: `Cloudflare Ray ID: ${rayId}` }] : [],
|
|
25403
|
+
{
|
|
25404
|
+
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."
|
|
25405
|
+
},
|
|
25406
|
+
{
|
|
25407
|
+
text: `${method} ${resource} -> ${status} ${statusText}`
|
|
25408
|
+
}
|
|
25409
|
+
],
|
|
25410
|
+
status,
|
|
25411
|
+
retryAfterMs,
|
|
25412
|
+
telemetryMessage: false
|
|
25413
|
+
});
|
|
25414
|
+
}
|
|
25415
|
+
__name(throwWAFBlockError, "throwWAFBlockError");
|
|
25416
|
+
async function fetchKVGetValueBase(complianceConfig, accountId, namespaceId, key, userAgent, logger, credentials) {
|
|
25417
|
+
const headers = new Headers();
|
|
25418
|
+
addAuthorizationHeader(headers, credentials);
|
|
25419
|
+
headers.set("User-Agent", userAgent);
|
|
25420
|
+
maybeAddTraceHeader(headers);
|
|
25421
|
+
const resource = `${getCloudflareApiBaseUrl(complianceConfig)}/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${key}`;
|
|
25422
|
+
logger.debug(`-- START CF API REQUEST: GET ${resource}`);
|
|
25423
|
+
logger.debug("-- END CF API REQUEST");
|
|
25424
|
+
const response = await fetch(resource, {
|
|
25425
|
+
method: "GET",
|
|
25426
|
+
headers
|
|
25427
|
+
});
|
|
25428
|
+
if (response.ok) {
|
|
25429
|
+
return await response.arrayBuffer();
|
|
25430
|
+
} else {
|
|
25431
|
+
throw new Error(
|
|
25432
|
+
`Failed to fetch ${resource} - ${response.status}: ${response.statusText}`
|
|
25148
25433
|
);
|
|
25149
25434
|
}
|
|
25150
|
-
|
|
25151
|
-
|
|
25435
|
+
}
|
|
25436
|
+
__name(fetchKVGetValueBase, "fetchKVGetValueBase");
|
|
25437
|
+
function hasCursor(result_info) {
|
|
25438
|
+
const cursor = result_info?.cursor;
|
|
25439
|
+
return cursor !== void 0 && cursor !== null && cursor !== "";
|
|
25440
|
+
}
|
|
25441
|
+
__name(hasCursor, "hasCursor");
|
|
25442
|
+
function maybeAddTraceHeader(headers) {
|
|
25443
|
+
const traceHeader = getTraceHeader();
|
|
25444
|
+
if (traceHeader) {
|
|
25445
|
+
headers.set("Cf-Trace-Id", traceHeader);
|
|
25152
25446
|
}
|
|
25153
|
-
logger?.log(`cloudflared ${version2} installed`);
|
|
25154
|
-
logger?.debug(`Binary location: ${binPath}`);
|
|
25155
25447
|
}
|
|
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.
|
|
25448
|
+
__name(maybeAddTraceHeader, "maybeAddTraceHeader");
|
|
25449
|
+
function cloneHeaders(headers) {
|
|
25450
|
+
return new Headers(headers);
|
|
25451
|
+
}
|
|
25452
|
+
__name(cloneHeaders, "cloneHeaders");
|
|
25175
25453
|
|
|
25176
|
-
|
|
25177
|
-
|
|
25178
|
-
|
|
25179
|
-
|
|
25180
|
-
|
|
25181
|
-
|
|
25182
|
-
}
|
|
25183
|
-
}
|
|
25184
|
-
} finally {
|
|
25185
|
-
try {
|
|
25186
|
-
if (existsSync(tempTarPath)) {
|
|
25187
|
-
unlinkSync(tempTarPath);
|
|
25188
|
-
}
|
|
25189
|
-
} catch {
|
|
25190
|
-
}
|
|
25191
|
-
}
|
|
25454
|
+
// src/cloudflared.ts
|
|
25455
|
+
var import_command_exists = __toESM(require_command_exists2());
|
|
25456
|
+
var UPDATE_SERVICE_URL = "https://update.argotunnel.com";
|
|
25457
|
+
var CLOUDFLARED_VERSION_PATTERN = /^\d{4}\.\d+\.\d+$/;
|
|
25458
|
+
function sha256Hex(buffer) {
|
|
25459
|
+
return createHash("sha256").update(buffer).digest("hex");
|
|
25192
25460
|
}
|
|
25193
|
-
__name(
|
|
25194
|
-
|
|
25195
|
-
const
|
|
25196
|
-
|
|
25197
|
-
|
|
25198
|
-
|
|
25461
|
+
__name(sha256Hex, "sha256Hex");
|
|
25462
|
+
function getGoArch() {
|
|
25463
|
+
const nodeArch = arch();
|
|
25464
|
+
switch (nodeArch) {
|
|
25465
|
+
case "x64":
|
|
25466
|
+
return "amd64";
|
|
25467
|
+
case "arm64":
|
|
25468
|
+
return "arm64";
|
|
25469
|
+
case "arm":
|
|
25470
|
+
return "arm";
|
|
25471
|
+
default:
|
|
25199
25472
|
throw new UserError(
|
|
25200
|
-
`
|
|
25473
|
+
`Unsupported architecture for cloudflared: ${nodeArch}
|
|
25201
25474
|
|
|
25202
|
-
|
|
25203
|
-
|
|
25204
|
-
|
|
25475
|
+
cloudflared supports: x64 (amd64), arm64, arm
|
|
25476
|
+
|
|
25477
|
+
You can manually install cloudflared and set the CLOUDFLARED_PATH environment variable.
|
|
25478
|
+
Download instructions: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`,
|
|
25479
|
+
{ telemetryMessage: "tunnel cloudflared unsupported architecture" }
|
|
25205
25480
|
);
|
|
25206
|
-
}
|
|
25207
25481
|
}
|
|
25208
|
-
writeFileAtomic(binPath, buffer);
|
|
25209
25482
|
}
|
|
25210
|
-
__name(
|
|
25211
|
-
|
|
25212
|
-
|
|
25213
|
-
|
|
25214
|
-
|
|
25215
|
-
|
|
25483
|
+
__name(getGoArch, "getGoArch");
|
|
25484
|
+
function getGoOS() {
|
|
25485
|
+
switch (process.platform) {
|
|
25486
|
+
case "darwin":
|
|
25487
|
+
return "darwin";
|
|
25488
|
+
case "linux":
|
|
25489
|
+
return "linux";
|
|
25490
|
+
case "win32":
|
|
25491
|
+
return "windows";
|
|
25492
|
+
default:
|
|
25216
25493
|
throw new UserError(
|
|
25217
|
-
`
|
|
25494
|
+
`Unsupported platform for cloudflared: ${process.platform}
|
|
25218
25495
|
|
|
25219
|
-
|
|
25220
|
-
|
|
25496
|
+
cloudflared supports: darwin (macOS), linux, win32 (Windows)
|
|
25497
|
+
|
|
25498
|
+
You can manually install cloudflared and set the CLOUDFLARED_PATH environment variable.
|
|
25499
|
+
Download instructions: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`,
|
|
25500
|
+
{ telemetryMessage: "tunnel cloudflared unsupported platform" }
|
|
25221
25501
|
);
|
|
25222
|
-
}
|
|
25223
|
-
logger?.debug(`Using cloudflared from CLOUDFLARED_PATH: ${envPath}`);
|
|
25224
|
-
return envPath;
|
|
25225
25502
|
}
|
|
25226
|
-
|
|
25227
|
-
|
|
25228
|
-
|
|
25229
|
-
|
|
25230
|
-
|
|
25231
|
-
}
|
|
25232
|
-
return pathBin;
|
|
25503
|
+
}
|
|
25504
|
+
__name(getGoOS, "getGoOS");
|
|
25505
|
+
var GITHUB_RELEASE_BASE = "https://github.com/cloudflare/cloudflared/releases/download";
|
|
25506
|
+
function getAssetFilename(goOS, goArch) {
|
|
25507
|
+
if (goOS === "windows") {
|
|
25508
|
+
return `cloudflared-${goOS}-${goArch}.exe`;
|
|
25233
25509
|
}
|
|
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
|
-
}
|
|
25510
|
+
if (goOS === "darwin") {
|
|
25511
|
+
return `cloudflared-${goOS}-${goArch}.tgz`;
|
|
25248
25512
|
}
|
|
25249
|
-
|
|
25250
|
-
|
|
25251
|
-
|
|
25252
|
-
|
|
25253
|
-
|
|
25254
|
-
|
|
25255
|
-
|
|
25256
|
-
|
|
25257
|
-
|
|
25258
|
-
|
|
25259
|
-
|
|
25260
|
-
|
|
25513
|
+
return `cloudflared-${goOS}-${goArch}`;
|
|
25514
|
+
}
|
|
25515
|
+
__name(getAssetFilename, "getAssetFilename");
|
|
25516
|
+
async function queryUpdateService(goOS, goArch, options) {
|
|
25517
|
+
const { logger } = options ?? {};
|
|
25518
|
+
const url2 = new URL(UPDATE_SERVICE_URL);
|
|
25519
|
+
url2.searchParams.set("os", goOS);
|
|
25520
|
+
url2.searchParams.set("arch", goArch);
|
|
25521
|
+
logger?.debug(`Checking for latest cloudflared: ${url2.toString()}`);
|
|
25522
|
+
let response;
|
|
25523
|
+
try {
|
|
25524
|
+
response = await fetch(url2.toString(), {
|
|
25525
|
+
headers: { "User-Agent": "wrangler" }
|
|
25526
|
+
});
|
|
25527
|
+
} catch (e2) {
|
|
25528
|
+
logger?.debug(
|
|
25529
|
+
`Failed to reach update service: ${e2 instanceof Error ? e2.message : String(e2)}`
|
|
25261
25530
|
);
|
|
25531
|
+
return null;
|
|
25262
25532
|
}
|
|
25263
|
-
if (
|
|
25264
|
-
|
|
25265
|
-
|
|
25266
|
-
|
|
25267
|
-
|
|
25533
|
+
if (!response.ok) {
|
|
25534
|
+
logger?.debug(
|
|
25535
|
+
`Update service returned ${response.status} for ${goOS}/${goArch}`
|
|
25536
|
+
);
|
|
25537
|
+
return null;
|
|
25538
|
+
}
|
|
25539
|
+
let data;
|
|
25540
|
+
try {
|
|
25541
|
+
data = await response.json();
|
|
25542
|
+
} catch (e2) {
|
|
25543
|
+
logger?.debug(
|
|
25544
|
+
`Update service returned non-JSON response: ${e2 instanceof Error ? e2.message : String(e2)}`
|
|
25545
|
+
);
|
|
25546
|
+
return null;
|
|
25547
|
+
}
|
|
25548
|
+
if (typeof data.version === "string" && !CLOUDFLARED_VERSION_PATTERN.test(data.version)) {
|
|
25549
|
+
throw new Error(
|
|
25550
|
+
`[cloudflared] Invalid cloudflared version returned by update service: ${data.version}`
|
|
25551
|
+
);
|
|
25552
|
+
}
|
|
25553
|
+
if (data.error || !data.url || !data.version) {
|
|
25554
|
+
return data.version ? data : null;
|
|
25555
|
+
}
|
|
25556
|
+
return data;
|
|
25557
|
+
}
|
|
25558
|
+
__name(queryUpdateService, "queryUpdateService");
|
|
25559
|
+
async function getLatestVersionInfo(options) {
|
|
25560
|
+
const { logger } = options ?? {};
|
|
25561
|
+
const goOS = getGoOS();
|
|
25562
|
+
const goArch = getGoArch();
|
|
25563
|
+
const primary = await queryUpdateService(goOS, goArch, { logger });
|
|
25564
|
+
if (primary && primary.url && primary.version) {
|
|
25565
|
+
return primary;
|
|
25268
25566
|
}
|
|
25269
|
-
await downloadCloudflared(versionInfo, binPath, { logger });
|
|
25270
|
-
validateBinary(binPath, { logger });
|
|
25271
|
-
return binPath;
|
|
25272
|
-
}
|
|
25273
|
-
__name(getCloudflaredPath, "getCloudflaredPath");
|
|
25274
|
-
async function spawnCloudflared(args, options) {
|
|
25275
|
-
const logger = options?.logger;
|
|
25276
|
-
const binPath = await getCloudflaredPath({
|
|
25277
|
-
skipVersionCheck: options?.skipVersionCheck,
|
|
25278
|
-
confirmDownload: options?.confirmDownload,
|
|
25279
|
-
logger
|
|
25280
|
-
});
|
|
25281
25567
|
logger?.debug(
|
|
25282
|
-
`
|
|
25568
|
+
`Update worker had no result for ${goOS}/${goArch}, falling back to GitHub release URL`
|
|
25283
25569
|
);
|
|
25284
|
-
const
|
|
25285
|
-
|
|
25286
|
-
|
|
25287
|
-
|
|
25288
|
-
|
|
25570
|
+
const fallback = await queryUpdateService("linux", "amd64", { logger });
|
|
25571
|
+
if (!fallback?.version) {
|
|
25572
|
+
throw new UserError(
|
|
25573
|
+
`[cloudflared] Failed to determine the latest cloudflared version.
|
|
25574
|
+
|
|
25575
|
+
The update service did not return results for ${goOS}/${goArch},
|
|
25576
|
+
and the fallback query also failed.
|
|
25577
|
+
|
|
25578
|
+
You can manually install cloudflared from:
|
|
25579
|
+
https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`,
|
|
25580
|
+
{ telemetryMessage: "tunnel cloudflared version lookup failed" }
|
|
25581
|
+
);
|
|
25582
|
+
}
|
|
25583
|
+
const version2 = fallback.version;
|
|
25584
|
+
const filename = getAssetFilename(goOS, goArch);
|
|
25585
|
+
const url2 = `${GITHUB_RELEASE_BASE}/${version2}/${filename}`;
|
|
25586
|
+
const compressed = filename.endsWith(".tgz");
|
|
25587
|
+
return {
|
|
25588
|
+
url: url2,
|
|
25589
|
+
version: version2,
|
|
25590
|
+
checksum: "",
|
|
25591
|
+
// no checksum available for fallback URLs
|
|
25592
|
+
compressed,
|
|
25593
|
+
shouldUpdate: true,
|
|
25594
|
+
userMessage: "",
|
|
25595
|
+
error: ""
|
|
25596
|
+
};
|
|
25289
25597
|
}
|
|
25290
|
-
__name(
|
|
25291
|
-
function
|
|
25292
|
-
|
|
25293
|
-
|
|
25294
|
-
|
|
25295
|
-
|
|
25598
|
+
__name(getLatestVersionInfo, "getLatestVersionInfo");
|
|
25599
|
+
function getCacheDir(version2) {
|
|
25600
|
+
return join(getGlobalConfigPath(), "cloudflared", version2);
|
|
25601
|
+
}
|
|
25602
|
+
__name(getCacheDir, "getCacheDir");
|
|
25603
|
+
function getCloudflaredBinPath(version2) {
|
|
25604
|
+
const binName = process.platform === "win32" ? "cloudflared.exe" : "cloudflared";
|
|
25605
|
+
return join(getCacheDir(version2), binName);
|
|
25606
|
+
}
|
|
25607
|
+
__name(getCloudflaredBinPath, "getCloudflaredBinPath");
|
|
25608
|
+
function isBinaryExecutable(binPath) {
|
|
25609
|
+
try {
|
|
25610
|
+
accessSync(binPath, constants.X_OK);
|
|
25611
|
+
return true;
|
|
25612
|
+
} catch {
|
|
25613
|
+
return false;
|
|
25296
25614
|
}
|
|
25297
|
-
return null;
|
|
25298
25615
|
}
|
|
25299
|
-
__name(
|
|
25616
|
+
__name(isBinaryExecutable, "isBinaryExecutable");
|
|
25617
|
+
function validateBinary(binPath, options) {
|
|
25618
|
+
const { logger } = options ?? {};
|
|
25619
|
+
try {
|
|
25620
|
+
const output = execFileSync(binPath, ["--version"], {
|
|
25621
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
25622
|
+
timeout: 1e4,
|
|
25623
|
+
encoding: "utf8"
|
|
25624
|
+
}).trim();
|
|
25625
|
+
logger?.debug(`cloudflared version: ${output}`);
|
|
25626
|
+
} catch {
|
|
25627
|
+
let errorMessage = `[cloudflared] Failed to validate cloudflared binary at ${binPath}
|
|
25300
25628
|
|
|
25301
|
-
|
|
25302
|
-
|
|
25303
|
-
|
|
25304
|
-
|
|
25305
|
-
|
|
25306
|
-
|
|
25307
|
-
|
|
25308
|
-
|
|
25309
|
-
|
|
25310
|
-
|
|
25311
|
-
|
|
25312
|
-
|
|
25313
|
-
|
|
25314
|
-
|
|
25315
|
-
|
|
25316
|
-
const timeoutMs = options.timeoutMs ?? TUNNEL_STARTUP_TIMEOUT_MS;
|
|
25317
|
-
const reminderIntervalMs = options.reminderIntervalMs ?? DEFAULT_TUNNEL_REMINDER_INTERVAL_MS;
|
|
25318
|
-
const defaultExpiryMs = options.expiryMs ?? DEFAULT_TUNNEL_EXPIRY_MS;
|
|
25319
|
-
const isNamedTunnel = options.token !== void 0;
|
|
25320
|
-
const timeFormatter = new Intl.DateTimeFormat(void 0, {
|
|
25321
|
-
timeStyle: "short"
|
|
25322
|
-
});
|
|
25323
|
-
const cloudflaredArgs = isNamedTunnel ? ["tunnel", "--no-autoupdate", "run"] : ["tunnel", "--no-autoupdate", "--url", options.origin.href];
|
|
25324
|
-
const cloudflaredPromise = spawnCloudflared(cloudflaredArgs, {
|
|
25325
|
-
stdio: "pipe",
|
|
25326
|
-
env: options.token ? { TUNNEL_TOKEN: options.token } : void 0,
|
|
25327
|
-
skipVersionCheck: true,
|
|
25328
|
-
logger
|
|
25329
|
-
}).then((process4) => {
|
|
25330
|
-
cloudflaredProcess = process4;
|
|
25331
|
-
if (disposed) {
|
|
25332
|
-
terminateCloudflared(process4);
|
|
25333
|
-
}
|
|
25334
|
-
return process4;
|
|
25335
|
-
});
|
|
25336
|
-
const readyPromise = cloudflaredPromise.then((process4) => {
|
|
25337
|
-
if (isNamedTunnel) {
|
|
25338
|
-
return { mode: "named" };
|
|
25629
|
+
`;
|
|
25630
|
+
errorMessage += `This usually means:
|
|
25631
|
+
`;
|
|
25632
|
+
errorMessage += ` - The binary is corrupted or incomplete
|
|
25633
|
+
`;
|
|
25634
|
+
errorMessage += ` - You're missing required system libraries
|
|
25635
|
+
`;
|
|
25636
|
+
if (process.platform === "linux") {
|
|
25637
|
+
errorMessage += `
|
|
25638
|
+
On Linux, make sure you have the required dependencies:
|
|
25639
|
+
`;
|
|
25640
|
+
errorMessage += ` - glibc (GNU C Library)
|
|
25641
|
+
`;
|
|
25642
|
+
errorMessage += ` - For Debian/Ubuntu: sudo apt-get install libc6
|
|
25643
|
+
`;
|
|
25339
25644
|
}
|
|
25340
|
-
|
|
25341
|
-
|
|
25342
|
-
|
|
25645
|
+
const cacheDir = join(getGlobalConfigPath(), "cloudflared");
|
|
25646
|
+
errorMessage += `
|
|
25647
|
+
You can try:
|
|
25648
|
+
`;
|
|
25649
|
+
errorMessage += ` 1. Deleting the cache directory: rm -rf ${cacheDir}
|
|
25650
|
+
`;
|
|
25651
|
+
errorMessage += ` 2. Running the command again to re-download
|
|
25652
|
+
`;
|
|
25653
|
+
errorMessage += ` 3. Manually installing cloudflared: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/
|
|
25654
|
+
`;
|
|
25655
|
+
errorMessage += ` 4. Setting CLOUDFLARED_PATH to point to your cloudflared binary`;
|
|
25656
|
+
throw new UserError(errorMessage, {
|
|
25657
|
+
telemetryMessage: "tunnel cloudflared validation failed"
|
|
25343
25658
|
});
|
|
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
25659
|
}
|
|
25359
|
-
|
|
25360
|
-
|
|
25361
|
-
|
|
25362
|
-
|
|
25363
|
-
|
|
25660
|
+
}
|
|
25661
|
+
__name(validateBinary, "validateBinary");
|
|
25662
|
+
function redactCloudflaredArgsForLogging(args) {
|
|
25663
|
+
const redacted = [...args];
|
|
25664
|
+
for (let i = 0; i < redacted.length; i++) {
|
|
25665
|
+
const arg = redacted[i];
|
|
25666
|
+
if (arg === "--token" && i + 1 < redacted.length) {
|
|
25667
|
+
redacted[i + 1] = "[REDACTED]";
|
|
25364
25668
|
}
|
|
25365
|
-
if (
|
|
25366
|
-
|
|
25367
|
-
reminderInterval = void 0;
|
|
25669
|
+
if (arg.startsWith("--token=")) {
|
|
25670
|
+
redacted[i] = "--token=[REDACTED]";
|
|
25368
25671
|
}
|
|
25369
25672
|
}
|
|
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
|
-
}
|
|
25673
|
+
return redacted;
|
|
25674
|
+
}
|
|
25675
|
+
__name(redactCloudflaredArgsForLogging, "redactCloudflaredArgsForLogging");
|
|
25676
|
+
function tryGetCloudflaredFromPath(options) {
|
|
25677
|
+
const { logger } = options ?? {};
|
|
25678
|
+
if (!(0, import_command_exists.sync)("cloudflared")) {
|
|
25679
|
+
return null;
|
|
25387
25680
|
}
|
|
25388
|
-
|
|
25389
|
-
|
|
25390
|
-
|
|
25681
|
+
try {
|
|
25682
|
+
validateBinary("cloudflared", { logger });
|
|
25683
|
+
return "cloudflared";
|
|
25684
|
+
} catch (e2) {
|
|
25685
|
+
logger?.debug("cloudflared found in PATH but failed validation", e2);
|
|
25686
|
+
return null;
|
|
25687
|
+
}
|
|
25688
|
+
}
|
|
25689
|
+
__name(tryGetCloudflaredFromPath, "tryGetCloudflaredFromPath");
|
|
25690
|
+
function getInstalledVersion(binPath) {
|
|
25691
|
+
try {
|
|
25692
|
+
const output = execFileSync(binPath, ["--version"], {
|
|
25693
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
25694
|
+
timeout: 1e4,
|
|
25695
|
+
encoding: "utf8"
|
|
25696
|
+
});
|
|
25697
|
+
const match = output.match(/(\d+\.\d+\.\d+)/);
|
|
25698
|
+
return match ? match[1] : null;
|
|
25699
|
+
} catch {
|
|
25700
|
+
return null;
|
|
25701
|
+
}
|
|
25702
|
+
}
|
|
25703
|
+
__name(getInstalledVersion, "getInstalledVersion");
|
|
25704
|
+
function isVersionOutdated(installed, latest) {
|
|
25705
|
+
const parse3 = /* @__PURE__ */ __name((v) => v.split(".").map(Number), "parse");
|
|
25706
|
+
const [iYear, iMonth, iPatch] = parse3(installed);
|
|
25707
|
+
const [lYear, lMonth, lPatch] = parse3(latest);
|
|
25708
|
+
if (iYear !== lYear) {
|
|
25709
|
+
return iYear < lYear;
|
|
25710
|
+
}
|
|
25711
|
+
if (iMonth !== lMonth) {
|
|
25712
|
+
return iMonth < lMonth;
|
|
25713
|
+
}
|
|
25714
|
+
return iPatch < lPatch;
|
|
25715
|
+
}
|
|
25716
|
+
__name(isVersionOutdated, "isVersionOutdated");
|
|
25717
|
+
async function warnIfOutdated(binPath, options) {
|
|
25718
|
+
const { logger } = options ?? {};
|
|
25719
|
+
try {
|
|
25720
|
+
const installed = getInstalledVersion(binPath);
|
|
25721
|
+
if (!installed) {
|
|
25391
25722
|
return;
|
|
25392
25723
|
}
|
|
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) {
|
|
25724
|
+
const latest = await queryUpdateService(getGoOS(), getGoArch(), { logger }) ?? await queryUpdateService("linux", "amd64", { logger });
|
|
25725
|
+
const latestVersion = latest?.version;
|
|
25726
|
+
if (!latestVersion) {
|
|
25411
25727
|
return;
|
|
25412
25728
|
}
|
|
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))}.`
|
|
25729
|
+
if (isVersionOutdated(installed, latestVersion)) {
|
|
25730
|
+
logger?.warn(
|
|
25731
|
+
`Your cloudflared (${installed}) is outdated. Latest version is ${latestVersion}.
|
|
25732
|
+
Update: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`
|
|
25423
25733
|
);
|
|
25424
|
-
scheduleExpiryTimeout();
|
|
25425
|
-
return;
|
|
25426
25734
|
}
|
|
25427
|
-
|
|
25428
|
-
|
|
25429
|
-
);
|
|
25430
|
-
scheduleExpiryTimeout();
|
|
25735
|
+
} catch (e2) {
|
|
25736
|
+
logger?.debug("Failed to check cloudflared version", e2);
|
|
25431
25737
|
}
|
|
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
25738
|
}
|
|
25440
|
-
__name(
|
|
25441
|
-
function
|
|
25442
|
-
const
|
|
25443
|
-
const
|
|
25444
|
-
|
|
25445
|
-
|
|
25446
|
-
|
|
25447
|
-
|
|
25448
|
-
|
|
25449
|
-
|
|
25739
|
+
__name(warnIfOutdated, "warnIfOutdated");
|
|
25740
|
+
function writeFileAtomic(filePath, contents) {
|
|
25741
|
+
const dir2 = dirname(filePath);
|
|
25742
|
+
const tmpPath = join(
|
|
25743
|
+
dir2,
|
|
25744
|
+
`.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`
|
|
25745
|
+
);
|
|
25746
|
+
try {
|
|
25747
|
+
writeFileSync(tmpPath, contents);
|
|
25748
|
+
renameSync(tmpPath, filePath);
|
|
25749
|
+
} finally {
|
|
25750
|
+
try {
|
|
25751
|
+
if (existsSync(tmpPath)) {
|
|
25752
|
+
unlinkSync(tmpPath);
|
|
25753
|
+
}
|
|
25754
|
+
} catch {
|
|
25755
|
+
}
|
|
25450
25756
|
}
|
|
25451
|
-
return `${hours}h ${minutes}m`;
|
|
25452
25757
|
}
|
|
25453
|
-
__name(
|
|
25454
|
-
function
|
|
25455
|
-
|
|
25456
|
-
|
|
25758
|
+
__name(writeFileAtomic, "writeFileAtomic");
|
|
25759
|
+
async function downloadCloudflared(versionInfo, binPath, options) {
|
|
25760
|
+
const { logger } = options ?? {};
|
|
25761
|
+
const { url: url2, version: version2, checksum, compressed } = versionInfo;
|
|
25762
|
+
logger?.log(`Downloading cloudflared ${version2}...`);
|
|
25763
|
+
logger?.debug(`Download URL: ${url2}`);
|
|
25764
|
+
const cacheDir = dirname(binPath);
|
|
25765
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
25766
|
+
let response;
|
|
25767
|
+
try {
|
|
25768
|
+
response = await fetch(url2, {
|
|
25769
|
+
headers: { "User-Agent": "wrangler" }
|
|
25770
|
+
});
|
|
25771
|
+
} catch (e2) {
|
|
25772
|
+
throw new UserError(
|
|
25773
|
+
`[cloudflared] Failed to download cloudflared from ${url2}
|
|
25774
|
+
|
|
25775
|
+
Network error: ${e2 instanceof Error ? e2.message : String(e2)}
|
|
25776
|
+
|
|
25777
|
+
Please check your internet connection and try again.
|
|
25778
|
+
If you're behind a proxy, make sure it's configured correctly.`,
|
|
25779
|
+
{ telemetryMessage: "tunnel cloudflared download network failed" }
|
|
25780
|
+
);
|
|
25457
25781
|
}
|
|
25458
|
-
|
|
25459
|
-
|
|
25460
|
-
|
|
25461
|
-
|
|
25462
|
-
|
|
25782
|
+
if (!response.ok) {
|
|
25783
|
+
throw new UserError(
|
|
25784
|
+
`[cloudflared] Failed to download cloudflared from ${url2}
|
|
25785
|
+
|
|
25786
|
+
HTTP ${response.status}: ${response.statusText}
|
|
25787
|
+
|
|
25788
|
+
You can manually download cloudflared from:
|
|
25789
|
+
https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/`,
|
|
25790
|
+
{ telemetryMessage: "tunnel cloudflared download response failed" }
|
|
25791
|
+
);
|
|
25792
|
+
}
|
|
25793
|
+
try {
|
|
25794
|
+
if (compressed) {
|
|
25795
|
+
await downloadAndExtractTarball(response, checksum, binPath, cacheDir);
|
|
25796
|
+
} else {
|
|
25797
|
+
await downloadBinary(response, checksum, binPath);
|
|
25463
25798
|
}
|
|
25464
|
-
}
|
|
25465
|
-
|
|
25799
|
+
} catch (e2) {
|
|
25800
|
+
try {
|
|
25801
|
+
if (existsSync(binPath)) {
|
|
25802
|
+
unlinkSync(binPath);
|
|
25803
|
+
}
|
|
25804
|
+
} catch {
|
|
25805
|
+
}
|
|
25806
|
+
if (e2 instanceof UserError) {
|
|
25807
|
+
throw e2;
|
|
25808
|
+
}
|
|
25809
|
+
throw new UserError(
|
|
25810
|
+
`[cloudflared] Failed to save cloudflared binary
|
|
25811
|
+
|
|
25812
|
+
Error: ${e2 instanceof Error ? e2.message : String(e2)}
|
|
25813
|
+
|
|
25814
|
+
Please ensure you have write permissions to: ${cacheDir}`,
|
|
25815
|
+
{ telemetryMessage: "tunnel cloudflared save failed" }
|
|
25816
|
+
);
|
|
25817
|
+
}
|
|
25818
|
+
if (process.platform !== "win32") {
|
|
25819
|
+
chmodSync(binPath, 493);
|
|
25820
|
+
}
|
|
25821
|
+
logger?.log(`cloudflared ${version2} installed`);
|
|
25822
|
+
logger?.debug(`Binary location: ${binPath}`);
|
|
25466
25823
|
}
|
|
25467
|
-
__name(
|
|
25468
|
-
function
|
|
25469
|
-
|
|
25470
|
-
|
|
25471
|
-
|
|
25472
|
-
|
|
25473
|
-
|
|
25474
|
-
|
|
25475
|
-
|
|
25476
|
-
|
|
25477
|
-
|
|
25478
|
-
|
|
25479
|
-
|
|
25480
|
-
|
|
25481
|
-
|
|
25482
|
-
|
|
25483
|
-
|
|
25824
|
+
__name(downloadCloudflared, "downloadCloudflared");
|
|
25825
|
+
async function downloadAndExtractTarball(response, expectedChecksum, binPath, cacheDir) {
|
|
25826
|
+
const tempTarPath = join(cacheDir, "cloudflared.tgz");
|
|
25827
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
25828
|
+
writeFileSync(tempTarPath, buffer);
|
|
25829
|
+
try {
|
|
25830
|
+
execFileSync("tar", ["-xzf", tempTarPath, "-C", cacheDir], {
|
|
25831
|
+
stdio: "ignore"
|
|
25832
|
+
});
|
|
25833
|
+
const extractedPath = join(cacheDir, "cloudflared");
|
|
25834
|
+
if (extractedPath !== binPath && existsSync(extractedPath)) {
|
|
25835
|
+
renameSync(extractedPath, binPath);
|
|
25836
|
+
}
|
|
25837
|
+
if (expectedChecksum) {
|
|
25838
|
+
const extractedBinary = readFileSync$1(binPath);
|
|
25839
|
+
const actualSha256 = sha256Hex(extractedBinary);
|
|
25840
|
+
if (actualSha256 !== expectedChecksum) {
|
|
25841
|
+
throw new UserError(
|
|
25842
|
+
`[cloudflared] SHA256 mismatch for downloaded cloudflared binary.
|
|
25843
|
+
|
|
25844
|
+
Expected: ${expectedChecksum}
|
|
25845
|
+
Actual: ${actualSha256}`,
|
|
25846
|
+
{
|
|
25847
|
+
telemetryMessage: "tunnel cloudflared extracted binary checksum mismatch"
|
|
25848
|
+
}
|
|
25484
25849
|
);
|
|
25485
25850
|
}
|
|
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
25851
|
}
|
|
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
|
-
);
|
|
25852
|
+
} finally {
|
|
25853
|
+
try {
|
|
25854
|
+
if (existsSync(tempTarPath)) {
|
|
25855
|
+
unlinkSync(tempTarPath);
|
|
25520
25856
|
}
|
|
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 });
|
|
25857
|
+
} catch {
|
|
25858
|
+
}
|
|
25537
25859
|
}
|
|
25538
|
-
return new Error(errorMessage);
|
|
25539
25860
|
}
|
|
25540
|
-
__name(
|
|
25861
|
+
__name(downloadAndExtractTarball, "downloadAndExtractTarball");
|
|
25862
|
+
async function downloadBinary(response, expectedChecksum, binPath) {
|
|
25863
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
25864
|
+
if (expectedChecksum) {
|
|
25865
|
+
const actualSha256 = sha256Hex(buffer);
|
|
25866
|
+
if (actualSha256 !== expectedChecksum) {
|
|
25867
|
+
throw new UserError(
|
|
25868
|
+
`[cloudflared] SHA256 mismatch for downloaded cloudflared binary.
|
|
25541
25869
|
|
|
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
|
-
);
|
|
25870
|
+
Expected: ${expectedChecksum}
|
|
25871
|
+
Actual: ${actualSha256}`,
|
|
25872
|
+
{ telemetryMessage: "tunnel cloudflared binary checksum mismatch" }
|
|
25873
|
+
);
|
|
25874
|
+
}
|
|
25557
25875
|
}
|
|
25876
|
+
writeFileAtomic(binPath, buffer);
|
|
25558
25877
|
}
|
|
25559
|
-
__name(
|
|
25878
|
+
__name(downloadBinary, "downloadBinary");
|
|
25879
|
+
async function getCloudflaredPath(options) {
|
|
25880
|
+
const logger = options?.logger;
|
|
25881
|
+
const envPath = getCloudflaredPathFromEnv();
|
|
25882
|
+
if (envPath) {
|
|
25883
|
+
if (!existsSync(envPath)) {
|
|
25884
|
+
throw new UserError(
|
|
25885
|
+
`CLOUDFLARED_PATH is set to "${envPath}" but the file does not exist.
|
|
25560
25886
|
|
|
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
|
-
);
|
|
25887
|
+
Please ensure the path points to a valid cloudflared binary.`,
|
|
25888
|
+
{ telemetryMessage: "tunnel cloudflared env path missing" }
|
|
25889
|
+
);
|
|
25890
|
+
}
|
|
25891
|
+
logger?.debug(`Using cloudflared from CLOUDFLARED_PATH: ${envPath}`);
|
|
25892
|
+
return envPath;
|
|
25596
25893
|
}
|
|
25597
|
-
|
|
25598
|
-
|
|
25599
|
-
|
|
25600
|
-
{
|
|
25601
|
-
|
|
25602
|
-
...init,
|
|
25603
|
-
headers,
|
|
25604
|
-
signal: abortSignal
|
|
25894
|
+
const pathBin = tryGetCloudflaredFromPath({ logger });
|
|
25895
|
+
if (pathBin) {
|
|
25896
|
+
logger?.debug("Using cloudflared from PATH");
|
|
25897
|
+
if (!options?.skipVersionCheck) {
|
|
25898
|
+
await warnIfOutdated(pathBin, { logger });
|
|
25605
25899
|
}
|
|
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
|
-
};
|
|
25900
|
+
return pathBin;
|
|
25642
25901
|
}
|
|
25643
|
-
|
|
25644
|
-
|
|
25645
|
-
|
|
25646
|
-
|
|
25647
|
-
|
|
25648
|
-
|
|
25649
|
-
|
|
25650
|
-
|
|
25902
|
+
const versionInfo = await getLatestVersionInfo({ logger });
|
|
25903
|
+
const binPath = getCloudflaredBinPath(versionInfo.version);
|
|
25904
|
+
let needsDownload = !isBinaryExecutable(binPath);
|
|
25905
|
+
if (!needsDownload) {
|
|
25906
|
+
try {
|
|
25907
|
+
validateBinary(binPath, { logger });
|
|
25908
|
+
logger?.debug(
|
|
25909
|
+
`Using cached cloudflared ${versionInfo.version}: ${binPath}`
|
|
25910
|
+
);
|
|
25911
|
+
return binPath;
|
|
25912
|
+
} catch (e2) {
|
|
25913
|
+
logger?.debug("Cached cloudflared failed validation; re-downloading", e2);
|
|
25914
|
+
needsDownload = true;
|
|
25915
|
+
}
|
|
25916
|
+
}
|
|
25917
|
+
const shouldDownload = await options?.confirmDownload?.(
|
|
25918
|
+
`cloudflared (${versionInfo.version}) is needed but not installed. Download to ${binPath}?`
|
|
25919
|
+
) ?? true;
|
|
25920
|
+
if (!shouldDownload) {
|
|
25921
|
+
throw new UserError(
|
|
25922
|
+
`cloudflared is required to run this command.
|
|
25923
|
+
|
|
25924
|
+
You can install it manually from:
|
|
25925
|
+
https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/
|
|
25926
|
+
|
|
25927
|
+
Then either add it to your PATH or set CLOUDFLARED_PATH.`,
|
|
25928
|
+
{ telemetryMessage: "tunnel cloudflared download declined" }
|
|
25651
25929
|
);
|
|
25652
25930
|
}
|
|
25653
|
-
|
|
25654
|
-
const
|
|
25655
|
-
|
|
25656
|
-
|
|
25657
|
-
|
|
25658
|
-
|
|
25659
|
-
|
|
25660
|
-
|
|
25661
|
-
|
|
25662
|
-
|
|
25663
|
-
|
|
25664
|
-
|
|
25665
|
-
|
|
25666
|
-
|
|
25667
|
-
|
|
25668
|
-
|
|
25669
|
-
|
|
25670
|
-
|
|
25671
|
-
|
|
25672
|
-
})
|
|
25931
|
+
if (existsSync(binPath)) {
|
|
25932
|
+
const cacheDir = removeCloudflaredCache(versionInfo.version);
|
|
25933
|
+
if (cacheDir) {
|
|
25934
|
+
logger?.log(`Removed cloudflared cache: ${cacheDir}`);
|
|
25935
|
+
}
|
|
25936
|
+
}
|
|
25937
|
+
await downloadCloudflared(versionInfo, binPath, { logger });
|
|
25938
|
+
validateBinary(binPath, { logger });
|
|
25939
|
+
return binPath;
|
|
25940
|
+
}
|
|
25941
|
+
__name(getCloudflaredPath, "getCloudflaredPath");
|
|
25942
|
+
async function spawnCloudflared(args, options) {
|
|
25943
|
+
const logger = options?.logger;
|
|
25944
|
+
const binPath = await getCloudflaredPath({
|
|
25945
|
+
skipVersionCheck: options?.skipVersionCheck,
|
|
25946
|
+
confirmDownload: options?.confirmDownload,
|
|
25947
|
+
logger
|
|
25948
|
+
});
|
|
25949
|
+
logger?.debug(
|
|
25950
|
+
`Spawning cloudflared: ${binPath} ${redactCloudflaredArgsForLogging(args).join(" ")}`
|
|
25951
|
+
);
|
|
25952
|
+
const cloudflared = spawn(binPath, args, {
|
|
25953
|
+
stdio: options?.stdio ?? "inherit",
|
|
25954
|
+
env: options?.env ? { ...process.env, ...options.env } : void 0
|
|
25955
|
+
});
|
|
25956
|
+
return cloudflared;
|
|
25957
|
+
}
|
|
25958
|
+
__name(spawnCloudflared, "spawnCloudflared");
|
|
25959
|
+
function removeCloudflaredCache(version2) {
|
|
25960
|
+
const cacheDir = version2 ? getCacheDir(version2) : join(getGlobalConfigPath(), "cloudflared");
|
|
25961
|
+
if (existsSync(cacheDir)) {
|
|
25962
|
+
removeDirSync(cacheDir);
|
|
25963
|
+
return cacheDir;
|
|
25673
25964
|
}
|
|
25965
|
+
return null;
|
|
25674
25966
|
}
|
|
25675
|
-
__name(
|
|
25676
|
-
|
|
25967
|
+
__name(removeCloudflaredCache, "removeCloudflaredCache");
|
|
25968
|
+
|
|
25969
|
+
// src/tunnel.ts
|
|
25970
|
+
var TUNNEL_STARTUP_TIMEOUT_MS = 3e4;
|
|
25971
|
+
var TUNNEL_API_TIMEOUT_MS = 6e4;
|
|
25972
|
+
var TUNNEL_FORCE_KILL_TIMEOUT_MS = 5e3;
|
|
25973
|
+
var DEFAULT_TUNNEL_EXPIRY_MS = 60 * 60 * 1e3;
|
|
25974
|
+
var DEFAULT_TUNNEL_EXTENSION_MS = 60 * 60 * 1e3;
|
|
25975
|
+
var DEFAULT_TUNNEL_MAX_REMAINING_MS = 3 * 60 * 60 * 1e3;
|
|
25976
|
+
var DEFAULT_TUNNEL_REMINDER_INTERVAL_MS = 10 * 60 * 1e3;
|
|
25977
|
+
var QUICK_TUNNEL_URL_REGEX = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/;
|
|
25978
|
+
var LOCAL_TUNNEL_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
25979
|
+
"localhost",
|
|
25980
|
+
"127.0.0.1",
|
|
25981
|
+
"::1",
|
|
25982
|
+
"0.0.0.0",
|
|
25983
|
+
"::"
|
|
25984
|
+
]);
|
|
25985
|
+
async function resolveNamedTunnel(name, origin, options) {
|
|
25677
25986
|
const {
|
|
25678
|
-
|
|
25679
|
-
|
|
25680
|
-
|
|
25681
|
-
|
|
25987
|
+
accountId,
|
|
25988
|
+
apiToken,
|
|
25989
|
+
complianceRegion,
|
|
25990
|
+
logger,
|
|
25991
|
+
userAgent,
|
|
25992
|
+
abortSignal
|
|
25993
|
+
} = options;
|
|
25994
|
+
const complianceConfig = { compliance_region: complianceRegion };
|
|
25995
|
+
const resource = `/accounts/${accountId}/cfd_tunnel`;
|
|
25996
|
+
const tunnels = await fetchResultBase(
|
|
25682
25997
|
complianceConfig,
|
|
25683
25998
|
resource,
|
|
25684
|
-
|
|
25999
|
+
void 0,
|
|
25685
26000
|
userAgent,
|
|
25686
26001
|
logger,
|
|
25687
|
-
|
|
25688
|
-
abortSignal,
|
|
25689
|
-
|
|
26002
|
+
new URLSearchParams({ name, is_deleted: "false" }),
|
|
26003
|
+
createApiAbortSignal(TUNNEL_API_TIMEOUT_MS, abortSignal),
|
|
26004
|
+
apiToken
|
|
25690
26005
|
);
|
|
25691
|
-
|
|
25692
|
-
|
|
25693
|
-
|
|
25694
|
-
|
|
26006
|
+
const tunnel = tunnels.find((item) => item.name === name);
|
|
26007
|
+
if (!tunnel) {
|
|
26008
|
+
throw new UserError(
|
|
26009
|
+
`No Cloudflare Tunnel named "${name}" was found in this account. Use "wrangler tunnel list" to see available tunnels.`,
|
|
26010
|
+
{ telemetryMessage: "tunnel resolve named missing tunnel" }
|
|
26011
|
+
);
|
|
25695
26012
|
}
|
|
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
|
|
26013
|
+
const tunnelId = tunnel.id;
|
|
26014
|
+
if (!tunnelId) {
|
|
26015
|
+
throw new FatalError(
|
|
26016
|
+
`Tunnel "${name}" was found but has no ID. This is unexpected.`,
|
|
26017
|
+
{ telemetryMessage: "tunnel resolve named missing tunnel id" }
|
|
25720
26018
|
);
|
|
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
26019
|
}
|
|
25732
|
-
|
|
25733
|
-
|
|
25734
|
-
|
|
25735
|
-
|
|
25736
|
-
|
|
25737
|
-
|
|
25738
|
-
|
|
26020
|
+
const configuration = await fetchResultBase(
|
|
26021
|
+
complianceConfig,
|
|
26022
|
+
`${resource}/${tunnelId}/configurations`,
|
|
26023
|
+
void 0,
|
|
26024
|
+
userAgent,
|
|
26025
|
+
logger,
|
|
26026
|
+
void 0,
|
|
26027
|
+
createApiAbortSignal(TUNNEL_API_TIMEOUT_MS, abortSignal),
|
|
26028
|
+
apiToken
|
|
26029
|
+
);
|
|
26030
|
+
const ingress = configuration.config?.ingress ?? [];
|
|
26031
|
+
const hostnames = getMatchingIngressHostnames(origin, ingress);
|
|
26032
|
+
if (hostnames.length === 0) {
|
|
26033
|
+
throw new UserError(
|
|
26034
|
+
createMissingIngressMessage(name, origin, {
|
|
26035
|
+
dashboardUrl: `https://dash.cloudflare.com/${accountId}/tunnels/${tunnelId}`,
|
|
26036
|
+
ingress
|
|
26037
|
+
}),
|
|
26038
|
+
{ telemetryMessage: "tunnel resolve named ingress mismatch" }
|
|
26039
|
+
);
|
|
25739
26040
|
}
|
|
25740
|
-
|
|
25741
|
-
|
|
25742
|
-
|
|
25743
|
-
|
|
25744
|
-
|
|
26041
|
+
const token = await fetchResultBase(
|
|
26042
|
+
complianceConfig,
|
|
26043
|
+
`${resource}/${tunnelId}/token`,
|
|
26044
|
+
void 0,
|
|
26045
|
+
userAgent,
|
|
26046
|
+
logger,
|
|
26047
|
+
void 0,
|
|
26048
|
+
createApiAbortSignal(TUNNEL_API_TIMEOUT_MS, abortSignal),
|
|
26049
|
+
apiToken
|
|
26050
|
+
);
|
|
26051
|
+
return { hostnames, token: String(token) };
|
|
25745
26052
|
}
|
|
25746
|
-
__name(
|
|
25747
|
-
function
|
|
25748
|
-
|
|
25749
|
-
|
|
26053
|
+
__name(resolveNamedTunnel, "resolveNamedTunnel");
|
|
26054
|
+
function getMatchingIngressHostnames(origin, ingressConfig) {
|
|
26055
|
+
const hostnames = /* @__PURE__ */ new Set();
|
|
26056
|
+
const originUrl = normalizeURL(origin);
|
|
26057
|
+
for (const ingress of ingressConfig) {
|
|
26058
|
+
try {
|
|
26059
|
+
const serviceUrl = normalizeURL(ingress.service);
|
|
26060
|
+
if (ingress.hostname && serviceUrl.toString() === originUrl.toString()) {
|
|
26061
|
+
hostnames.add(ingress.hostname);
|
|
26062
|
+
}
|
|
26063
|
+
} catch {
|
|
26064
|
+
}
|
|
25750
26065
|
}
|
|
25751
|
-
|
|
25752
|
-
|
|
26066
|
+
return [...hostnames];
|
|
26067
|
+
}
|
|
26068
|
+
__name(getMatchingIngressHostnames, "getMatchingIngressHostnames");
|
|
26069
|
+
function normalizeURL(url2) {
|
|
26070
|
+
const normalizedUrl = new URL(url2);
|
|
26071
|
+
if (LOCAL_TUNNEL_HOSTNAMES.has(normalizedUrl.hostname)) {
|
|
26072
|
+
normalizedUrl.hostname = "localhost";
|
|
25753
26073
|
}
|
|
25754
|
-
|
|
25755
|
-
|
|
25756
|
-
|
|
26074
|
+
if (!normalizedUrl.port) {
|
|
26075
|
+
switch (normalizedUrl.protocol) {
|
|
26076
|
+
case "http:":
|
|
26077
|
+
normalizedUrl.port = "80";
|
|
26078
|
+
break;
|
|
26079
|
+
case "https:":
|
|
26080
|
+
normalizedUrl.port = "443";
|
|
26081
|
+
break;
|
|
26082
|
+
}
|
|
25757
26083
|
}
|
|
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];
|
|
26084
|
+
return normalizedUrl;
|
|
25773
26085
|
}
|
|
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;
|
|
26086
|
+
__name(normalizeURL, "normalizeURL");
|
|
26087
|
+
function createApiAbortSignal(timeoutMs, abortSignal) {
|
|
26088
|
+
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
26089
|
+
return abortSignal ? AbortSignal.any([abortSignal, timeoutSignal]) : timeoutSignal;
|
|
25780
26090
|
}
|
|
25781
|
-
__name(
|
|
25782
|
-
function
|
|
25783
|
-
|
|
25784
|
-
|
|
25785
|
-
|
|
25786
|
-
|
|
25787
|
-
|
|
25788
|
-
${
|
|
25789
|
-
|
|
25790
|
-
|
|
25791
|
-
|
|
26091
|
+
__name(createApiAbortSignal, "createApiAbortSignal");
|
|
26092
|
+
function createMissingIngressMessage(name, origin, {
|
|
26093
|
+
dashboardUrl,
|
|
26094
|
+
ingress
|
|
26095
|
+
}) {
|
|
26096
|
+
if (ingress.length === 0) {
|
|
26097
|
+
return [
|
|
26098
|
+
`Tunnel "${name}" has no routes configured.`,
|
|
26099
|
+
"",
|
|
26100
|
+
`Add a route for ${origin} in the Cloudflare dashboard:`,
|
|
26101
|
+
dashboardUrl,
|
|
26102
|
+
""
|
|
26103
|
+
].join("\n");
|
|
26104
|
+
}
|
|
26105
|
+
return [
|
|
26106
|
+
`Tunnel "${name}" has no route for ${origin}`,
|
|
26107
|
+
"",
|
|
26108
|
+
"Resolved routes:",
|
|
26109
|
+
...ingress.map(
|
|
26110
|
+
({ hostname: hostname3, service }) => ` - ${hostname3 ?? "(no hostname)"} -> ${service}`
|
|
26111
|
+
),
|
|
26112
|
+
"",
|
|
26113
|
+
"Update your local server settings or the tunnel routes in the Cloudflare dashboard:",
|
|
26114
|
+
dashboardUrl,
|
|
26115
|
+
""
|
|
26116
|
+
].join("\n");
|
|
25792
26117
|
}
|
|
25793
|
-
__name(
|
|
25794
|
-
function
|
|
25795
|
-
|
|
25796
|
-
|
|
25797
|
-
|
|
25798
|
-
|
|
25799
|
-
|
|
25800
|
-
|
|
25801
|
-
|
|
25802
|
-
|
|
26118
|
+
__name(createMissingIngressMessage, "createMissingIngressMessage");
|
|
26119
|
+
function startTunnel(options) {
|
|
26120
|
+
let disposed = false;
|
|
26121
|
+
let reminderInterval;
|
|
26122
|
+
let expiryTimeout;
|
|
26123
|
+
let expiresAt = 0;
|
|
26124
|
+
let cloudflaredProcess;
|
|
26125
|
+
const logger = options.logger;
|
|
26126
|
+
const timeoutMs = options.timeoutMs ?? TUNNEL_STARTUP_TIMEOUT_MS;
|
|
26127
|
+
const reminderIntervalMs = options.reminderIntervalMs ?? DEFAULT_TUNNEL_REMINDER_INTERVAL_MS;
|
|
26128
|
+
const defaultExpiryMs = options.expiryMs ?? DEFAULT_TUNNEL_EXPIRY_MS;
|
|
26129
|
+
const isNamedTunnel = options.token !== void 0;
|
|
26130
|
+
const timeFormatter = new Intl.DateTimeFormat(void 0, {
|
|
26131
|
+
timeStyle: "short"
|
|
26132
|
+
});
|
|
26133
|
+
const cloudflaredArgs = isNamedTunnel ? ["tunnel", "--no-autoupdate", "run"] : ["tunnel", "--no-autoupdate", "--url", options.origin.href];
|
|
26134
|
+
const cloudflaredPromise = spawnCloudflared(cloudflaredArgs, {
|
|
26135
|
+
stdio: "pipe",
|
|
26136
|
+
env: options.token ? { TUNNEL_TOKEN: options.token } : void 0,
|
|
26137
|
+
skipVersionCheck: true,
|
|
26138
|
+
logger
|
|
26139
|
+
}).then((process4) => {
|
|
26140
|
+
cloudflaredProcess = process4;
|
|
26141
|
+
if (disposed) {
|
|
26142
|
+
terminateCloudflared(process4);
|
|
26143
|
+
}
|
|
26144
|
+
return process4;
|
|
26145
|
+
});
|
|
26146
|
+
const readyPromise = cloudflaredPromise.then((process4) => {
|
|
26147
|
+
if (isNamedTunnel) {
|
|
26148
|
+
return { mode: "named" };
|
|
26149
|
+
}
|
|
26150
|
+
return waitForQuickTunnelReady(process4, timeoutMs, {
|
|
26151
|
+
logger,
|
|
26152
|
+
origin: options.origin
|
|
26153
|
+
});
|
|
26154
|
+
}).then((result) => {
|
|
26155
|
+
expiresAt = Date.now() + defaultExpiryMs;
|
|
26156
|
+
scheduleExpiryTimeout();
|
|
26157
|
+
scheduleReminder(
|
|
26158
|
+
result.mode === "quick" ? result.publicUrl.origin : void 0
|
|
26159
|
+
);
|
|
26160
|
+
return result;
|
|
26161
|
+
});
|
|
26162
|
+
function disposeTunnel() {
|
|
26163
|
+
disposed = true;
|
|
26164
|
+
clearTunnelTimers();
|
|
26165
|
+
if (cloudflaredProcess) {
|
|
26166
|
+
terminateCloudflared(cloudflaredProcess);
|
|
25803
26167
|
}
|
|
25804
26168
|
}
|
|
25805
|
-
|
|
25806
|
-
|
|
25807
|
-
|
|
25808
|
-
|
|
25809
|
-
|
|
25810
|
-
|
|
25811
|
-
|
|
25812
|
-
|
|
25813
|
-
|
|
25814
|
-
|
|
26169
|
+
__name(disposeTunnel, "disposeTunnel");
|
|
26170
|
+
function clearTunnelTimers() {
|
|
26171
|
+
if (expiryTimeout) {
|
|
26172
|
+
clearTimeout(expiryTimeout);
|
|
26173
|
+
expiryTimeout = void 0;
|
|
26174
|
+
}
|
|
26175
|
+
if (reminderInterval) {
|
|
26176
|
+
clearInterval(reminderInterval);
|
|
26177
|
+
reminderInterval = void 0;
|
|
26178
|
+
}
|
|
26179
|
+
}
|
|
26180
|
+
__name(clearTunnelTimers, "clearTunnelTimers");
|
|
26181
|
+
function scheduleReminder(publicURL) {
|
|
26182
|
+
if (reminderIntervalMs > 0) {
|
|
26183
|
+
reminderInterval = setInterval(() => {
|
|
26184
|
+
if (disposed) {
|
|
26185
|
+
return;
|
|
26186
|
+
}
|
|
26187
|
+
const remainingMs = expiresAt - Date.now();
|
|
26188
|
+
if (remainingMs <= 0) {
|
|
26189
|
+
return;
|
|
26190
|
+
}
|
|
26191
|
+
logger?.log(
|
|
26192
|
+
`${publicURL ? `Tunnel still open, expires in ${formatTunnelDuration(remainingMs)}: ${publicURL}` : `The tunnel is still open. It expires in ${formatTunnelDuration(remainingMs)}.`}${options.extendHint ? ` ${options.extendHint}` : ""}`
|
|
26193
|
+
);
|
|
26194
|
+
}, reminderIntervalMs);
|
|
26195
|
+
reminderInterval.unref?.();
|
|
26196
|
+
}
|
|
26197
|
+
}
|
|
26198
|
+
__name(scheduleReminder, "scheduleReminder");
|
|
26199
|
+
function scheduleExpiryTimeout() {
|
|
26200
|
+
if (disposed) {
|
|
26201
|
+
return;
|
|
26202
|
+
}
|
|
26203
|
+
if (expiryTimeout) {
|
|
26204
|
+
clearTimeout(expiryTimeout);
|
|
26205
|
+
}
|
|
26206
|
+
expiryTimeout = setTimeout(
|
|
26207
|
+
() => {
|
|
26208
|
+
if (disposed) {
|
|
26209
|
+
return;
|
|
25815
26210
|
}
|
|
26211
|
+
logger?.log("Tunnel expired. Closing tunnel.");
|
|
26212
|
+
disposeTunnel();
|
|
26213
|
+
},
|
|
26214
|
+
Math.max(0, expiresAt - Date.now())
|
|
26215
|
+
);
|
|
26216
|
+
expiryTimeout.unref();
|
|
26217
|
+
}
|
|
26218
|
+
__name(scheduleExpiryTimeout, "scheduleExpiryTimeout");
|
|
26219
|
+
function extendExpiry(ms = DEFAULT_TUNNEL_EXTENSION_MS) {
|
|
26220
|
+
if (disposed || !expiryTimeout || ms <= 0) {
|
|
26221
|
+
return;
|
|
26222
|
+
}
|
|
26223
|
+
const now = Date.now();
|
|
26224
|
+
const previousExpiresAt = expiresAt;
|
|
26225
|
+
expiresAt = Math.min(
|
|
26226
|
+
now + DEFAULT_TUNNEL_MAX_REMAINING_MS,
|
|
26227
|
+
Math.max(expiresAt, now) + ms
|
|
26228
|
+
);
|
|
26229
|
+
const extendedByMs = expiresAt - previousExpiresAt;
|
|
26230
|
+
if (extendedByMs < ms) {
|
|
26231
|
+
logger?.log(
|
|
26232
|
+
`Tunnel expiry extended to the ${formatTunnelDuration(DEFAULT_TUNNEL_MAX_REMAINING_MS)} limit. It now expires at ${timeFormatter.format(new Date(expiresAt))}.`
|
|
25816
26233
|
);
|
|
26234
|
+
scheduleExpiryTimeout();
|
|
26235
|
+
return;
|
|
25817
26236
|
}
|
|
26237
|
+
logger?.log(
|
|
26238
|
+
`Tunnel expiry extended by ${formatTunnelDuration(extendedByMs)}. It now expires at ${timeFormatter.format(new Date(expiresAt))}.`
|
|
26239
|
+
);
|
|
26240
|
+
scheduleExpiryTimeout();
|
|
25818
26241
|
}
|
|
26242
|
+
__name(extendExpiry, "extendExpiry");
|
|
26243
|
+
return {
|
|
26244
|
+
ready: /* @__PURE__ */ __name(() => readyPromise, "ready"),
|
|
26245
|
+
isOpen: /* @__PURE__ */ __name(() => !disposed, "isOpen"),
|
|
26246
|
+
dispose: disposeTunnel,
|
|
26247
|
+
extendExpiry
|
|
26248
|
+
};
|
|
25819
26249
|
}
|
|
25820
|
-
__name(
|
|
25821
|
-
function
|
|
25822
|
-
|
|
25823
|
-
|
|
26250
|
+
__name(startTunnel, "startTunnel");
|
|
26251
|
+
function formatTunnelDuration(durationMs) {
|
|
26252
|
+
const totalMinutes = Math.max(1, Math.ceil(durationMs / 6e4));
|
|
26253
|
+
const hours = Math.floor(totalMinutes / 60);
|
|
26254
|
+
const minutes = totalMinutes % 60;
|
|
26255
|
+
if (hours === 0) {
|
|
26256
|
+
return `${minutes}m`;
|
|
25824
26257
|
}
|
|
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);
|
|
26258
|
+
if (minutes === 0) {
|
|
26259
|
+
return `${hours}h`;
|
|
26260
|
+
}
|
|
26261
|
+
return `${hours}h ${minutes}m`;
|
|
25832
26262
|
}
|
|
25833
|
-
__name(
|
|
25834
|
-
function
|
|
25835
|
-
|
|
25836
|
-
|
|
25837
|
-
|
|
25838
|
-
|
|
26263
|
+
__name(formatTunnelDuration, "formatTunnelDuration");
|
|
26264
|
+
function terminateCloudflared(cloudflared) {
|
|
26265
|
+
if (cloudflared.killed) {
|
|
26266
|
+
return;
|
|
26267
|
+
}
|
|
26268
|
+
cloudflared.unref();
|
|
26269
|
+
cloudflared.kill("SIGTERM");
|
|
26270
|
+
const forceKillTimer = setTimeout(() => {
|
|
26271
|
+
if (!cloudflared.killed) {
|
|
26272
|
+
cloudflared.kill("SIGKILL");
|
|
25839
26273
|
}
|
|
25840
|
-
|
|
25841
|
-
|
|
26274
|
+
}, TUNNEL_FORCE_KILL_TIMEOUT_MS);
|
|
26275
|
+
forceKillTimer.unref();
|
|
25842
26276
|
}
|
|
25843
|
-
__name(
|
|
25844
|
-
function
|
|
25845
|
-
|
|
25846
|
-
|
|
25847
|
-
|
|
25848
|
-
|
|
25849
|
-
|
|
25850
|
-
|
|
25851
|
-
|
|
25852
|
-
|
|
25853
|
-
|
|
25854
|
-
|
|
25855
|
-
|
|
25856
|
-
|
|
25857
|
-
|
|
25858
|
-
|
|
25859
|
-
|
|
26277
|
+
__name(terminateCloudflared, "terminateCloudflared");
|
|
26278
|
+
function waitForQuickTunnelReady(cloudflared, timeoutMs, options) {
|
|
26279
|
+
return new Promise((resolve3, reject) => {
|
|
26280
|
+
let resolved = false;
|
|
26281
|
+
let stderrOutput = "";
|
|
26282
|
+
const logger = options?.logger;
|
|
26283
|
+
const origin = options?.origin;
|
|
26284
|
+
const timeoutId = setTimeout(() => {
|
|
26285
|
+
if (!resolved) {
|
|
26286
|
+
resolved = true;
|
|
26287
|
+
terminateCloudflared(cloudflared);
|
|
26288
|
+
reject(
|
|
26289
|
+
createTunnelStartupError(
|
|
26290
|
+
`Timed out waiting for cloudflared to start (${timeoutMs / 1e3}s).`,
|
|
26291
|
+
stderrOutput,
|
|
26292
|
+
origin
|
|
26293
|
+
)
|
|
26294
|
+
);
|
|
26295
|
+
}
|
|
26296
|
+
}, timeoutMs);
|
|
26297
|
+
timeoutId.unref();
|
|
26298
|
+
if (cloudflared.stderr) {
|
|
26299
|
+
cloudflared.stderr.on("data", (data) => {
|
|
26300
|
+
const chunk = data.toString();
|
|
26301
|
+
stderrOutput += chunk;
|
|
26302
|
+
logger?.debug("[cloudflared]", chunk.trimEnd());
|
|
26303
|
+
const match = QUICK_TUNNEL_URL_REGEX.exec(stderrOutput);
|
|
26304
|
+
if (match && !resolved) {
|
|
26305
|
+
resolved = true;
|
|
26306
|
+
clearTimeout(timeoutId);
|
|
26307
|
+
resolve3({ mode: "quick", publicUrl: new URL(match[0]) });
|
|
26308
|
+
}
|
|
26309
|
+
});
|
|
25860
26310
|
}
|
|
25861
|
-
|
|
25862
|
-
|
|
25863
|
-
|
|
25864
|
-
|
|
26311
|
+
cloudflared.on("error", (error51) => {
|
|
26312
|
+
if (!resolved) {
|
|
26313
|
+
resolved = true;
|
|
26314
|
+
clearTimeout(timeoutId);
|
|
26315
|
+
reject(new Error(`Failed to start cloudflared: ${error51.message}`));
|
|
26316
|
+
}
|
|
25865
26317
|
});
|
|
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}`
|
|
26318
|
+
cloudflared.on("exit", (code, signal) => {
|
|
26319
|
+
if (!resolved) {
|
|
26320
|
+
resolved = true;
|
|
26321
|
+
clearTimeout(timeoutId);
|
|
26322
|
+
const reason = signal ? `terminated by signal ${signal}` : `exited with code ${code}`;
|
|
26323
|
+
reject(
|
|
26324
|
+
createTunnelStartupError(
|
|
26325
|
+
`cloudflared ${reason} before the tunnel was ready.`,
|
|
26326
|
+
stderrOutput,
|
|
26327
|
+
origin
|
|
26328
|
+
)
|
|
26329
|
+
);
|
|
25903
26330
|
}
|
|
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
|
|
26331
|
+
});
|
|
25922
26332
|
});
|
|
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
26333
|
}
|
|
25936
|
-
__name(
|
|
25937
|
-
function
|
|
25938
|
-
const
|
|
25939
|
-
|
|
25940
|
-
|
|
26334
|
+
__name(waitForQuickTunnelReady, "waitForQuickTunnelReady");
|
|
26335
|
+
function createTunnelStartupError(message, stderrOutput, origin) {
|
|
26336
|
+
const isQuickTunnelRateLimited = stderrOutput.includes(
|
|
26337
|
+
"429 Too Many Requests"
|
|
26338
|
+
);
|
|
26339
|
+
const errorMessage = `${message}
|
|
26340
|
+
cloudflared output:
|
|
26341
|
+
${stderrOutput || "(no output)"}
|
|
26342
|
+
|
|
26343
|
+
The local dev server started at ${origin.href}
|
|
26344
|
+
` + (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.`);
|
|
26345
|
+
if (isQuickTunnelRateLimited) {
|
|
26346
|
+
return new UserError(errorMessage, { telemetryMessage: false });
|
|
25941
26347
|
}
|
|
26348
|
+
return new Error(errorMessage);
|
|
25942
26349
|
}
|
|
25943
|
-
__name(
|
|
25944
|
-
function cloneHeaders(headers) {
|
|
25945
|
-
return new Headers(headers);
|
|
25946
|
-
}
|
|
25947
|
-
__name(cloneHeaders, "cloneHeaders");
|
|
26350
|
+
__name(createTunnelStartupError, "createTunnelStartupError");
|
|
25948
26351
|
|
|
25949
26352
|
// src/update-check.ts
|
|
25950
26353
|
var import_update_check = __toESM(require_update_check());
|
|
@@ -26277,4 +26680,4 @@ safe-buffer/index.js:
|
|
|
26277
26680
|
(*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> *)
|
|
26278
26681
|
*/
|
|
26279
26682
|
|
|
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 };
|
|
26683
|
+
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 };
|