@cloudflare/workers-utils 0.37.0 → 0.38.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,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-V6JSHOVJ.mjs';
4
- export { APIError, ENVIRONMENT_TAG_PREFIX, INHERIT_SYMBOL, JSON_CONFIG_FORMATS, PATH_TO_DEPLOY_CONFIG, ParseError, SERVICE_TAG_PREFIX, configFileName, configFormat, experimental_readRawConfig, findWranglerConfig, formatConfigSnippet, getContainerDurableObjectClassNames, getContainerNameToClassNameMap, getDurableObjectExports, hasDurableObjectExports, indexLocation, isRedirectedConfig, parseByteSize, parseHumanDuration, parseJSON, parseJSONC, parseNonHyphenedUuid, parsePackageJSON, parseTOML, partitionExports, readFileSync, readFileSyncToBuffer, resolveContainerClassName, resolveWranglerConfigPath, searchLocation } from './chunk-V6JSHOVJ.mjs';
5
- import { assertNever } from './chunk-CDB2RWUW.mjs';
6
- export { DEFAULT_COMPAT_DATE, assertNever, constructWranglerConfig, mapWorkerMetadataBindings } from './chunk-CDB2RWUW.mjs';
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-T5JQYOMX.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, getDurableObjectClassNameToUseSQLiteMap, getDurableObjectContainerApps, getDurableObjectExports, hasDurableObjectExports, indexLocation, isDurableObjectContainerApp, isRedirectedConfig, parseByteSize, parseHumanDuration, parseJSON, parseJSONC, parseNonHyphenedUuid, parsePackageJSON, parseTOML, partitionExports, readFileSync, readFileSyncToBuffer, resolveContainerClassName, resolveWranglerConfigPath, searchLocation, validateDurableObjectContainerApplications } from './chunk-T5JQYOMX.mjs';
5
+ import { assertNever } from './chunk-KBTXSRPS.mjs';
6
+ export { DEFAULT_COMPAT_DATE, assertNever, constructWranglerConfig, mapWorkerMetadataBindings } from './chunk-KBTXSRPS.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, getCloudflareApiBaseUrl, getTraceHeader, getCloudflaredPathFromEnv, getOutputFilePathFromEnv, getOutputFileDirectoryFromEnv, getCIOverrideName, getWranglerCacheDirFromEnv } from './chunk-XUGGRRBO.mjs';
9
- export { COMPLIANCE_REGION_CONFIG_PUBLIC, COMPLIANCE_REGION_CONFIG_UNKNOWN, getBrowserRenderingHeadfulFromEnv, getBuildConditionsFromEnv, getBuildPlatformFromEnv, getC3CommandFromEnv, getCIGeneratePreviewAlias, getCIMatchTag, getCIOverrideName, getCIOverrideNetworkModeHost, getCfFetchEnabledFromEnv, getCfFetchPathFromEnv, getCloudflareApiBaseUrl, getCloudflareApiEnvironmentFromEnv, getCloudflareComplianceRegion, getCloudflareEnv, getCloudflareIncludeProcessEnvFromEnv, getCloudflareLoadDevVarsFromDotEnv, getCloudflaredPathFromEnv, getComplianceRegionSubdomain, getD1ExtraLocationChoices, getDisableConfigWatching, getLocalExplorerEnabledFromEnv, getLocalObservabilityEnabledFromEnv, getNoSkillsUpdatePromptsFromEnv, getOpenNextDeployFromEnv, getOutputFileDirectoryFromEnv, getOutputFilePathFromEnv, getRegistryPath, getSanitizeLogs, getSubdomainMixedStateCheckDisabled, getTraceHeader, getWorkersCIBranchName, getWranglerCacheDirFromEnv, getWranglerHideBanner, getWranglerSendErrorReportsFromEnv, getWranglerSendMetricsFromEnv, isDoNotTrackEnabled } from './chunk-XUGGRRBO.mjs';
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';
@@ -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(envName, rawEnv.name ?? rawConfig?.name, configPath),
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
- validateOptionalProperty(
19954
- diagnostics,
19955
- field,
19956
- "class_name",
19957
- containerAppOptional.class_name,
19958
- "string"
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.`
@@ -20992,6 +21116,7 @@ var validateBindingsHaveUniqueNames = /* @__PURE__ */ __name((diagnostics, confi
20992
21116
  ])
20993
21117
  );
20994
21118
  bindingsGroupedByType["Secret"] = config2.secrets?.required ?? [];
21119
+ bindingsGroupedByType["Container images"] = [CONTAINER_IMAGES_BINDING];
20995
21120
  const bindingsGroupedByName = {};
20996
21121
  for (const bindingType in bindingsGroupedByType) {
20997
21122
  const bindingNames = bindingsGroupedByType[bindingType];
@@ -24439,7 +24564,9 @@ ${allTailConsumers.map(({ service, streaming }) => {
24439
24564
  }
24440
24565
  log(
24441
24566
  `${containersTitle}
24442
- ${containers.map((c) => `- ${c.name} (${c.image})`).join("\n")}`
24567
+ ${containers.map(
24568
+ (container) => container.scheduling_policy === "durable_object" ? `- ${container.class_name} (durable_object)` : `- ${container.name} (${container.image})`
24569
+ ).join("\n")}`
24443
24570
  );
24444
24571
  log("");
24445
24572
  }