@cloudflare/vitest-plugin 1.1.8 → 1.1.9

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.
@@ -325,7 +325,7 @@ function createFatalError$1(message, isJson, options) {
325
325
  __name$3(createFatalError$1, "createFatalError");
326
326
 
327
327
  //#endregion
328
- //#region ../workers-utils/dist/chunk-R5MIV2EH.mjs
328
+ //#region ../workers-utils/dist/chunk-T5JQYOMX.mjs
329
329
  function hasKnownExportType(entry) {
330
330
  const type = entry?.type;
331
331
  return type === "durable-object" || type === "worker";
@@ -413,9 +413,9 @@ function getDurableObjectClassNameToUseSQLiteMap(migrations, exports$1) {
413
413
  return durableObjectClassNameToUseSQLiteMap;
414
414
  }
415
415
  __name$3(getDurableObjectClassNameToUseSQLiteMap, "getDurableObjectClassNameToUseSQLiteMap");
416
- function validateDurableObjectContainerApplications(config$1) {
416
+ function validateDurableObjectContainerApplications(config$1, containers = getDurableObjectContainerApps(config$1.containers)) {
417
417
  const allDOs = getDurableObjectClassNameToUseSQLiteMap(config$1.migrations, config$1.exports);
418
- for (const container of getDurableObjectContainerApps(config$1.containers)) {
418
+ for (const container of containers) {
419
419
  const maybeBoundDO = config$1.durable_objects.bindings.find((durableObject) => durableObject.class_name === container.class_name);
420
420
  const useSQLite = allDOs.get(container.class_name);
421
421
  if (useSQLite === void 0 && maybeBoundDO === void 0) throw new UserError$1(`The container class_name ${container.class_name} does not match any durable object class_name defined in your Wrangler config file. Note that the durable object must be defined in the same script as the container.`, { telemetryMessage: "no DO defined that matches container class_name" });
@@ -42165,7 +42165,7 @@ function normalizeAndValidateEnvironment(diagnostics, configPath, rawEnv, isDisp
42165
42165
  warnIfDurableObjectsHaveNoLifecycleConfig(diagnostics, environment.durable_objects, environment.migrations, environment.exports, configPath);
42166
42166
  errorIfMigrationsAndExportsBothSet(diagnostics, environment.migrations, environment.exports);
42167
42167
  const containersDeclaredElsewhere = rawConfig !== void 0 ? rawConfig.containers !== void 0 : Object.values("env" in rawEnv ? rawEnv.env ?? {} : {}).some((rawNamedEnv) => rawNamedEnv?.containers !== void 0);
42168
- validateContainerExportLinks(diagnostics, environment.containers, environment.exports, containersDeclaredElsewhere);
42168
+ validateContainerExportLinks$1(diagnostics, environment.containers, environment.exports, containersDeclaredElsewhere);
42169
42169
  if (envName !== "top level") validateAdditionalProperties(diagnostics, "env." + envName, Object.keys(rawEnv), Object.keys(environment));
42170
42170
  return environment;
42171
42171
  }
@@ -44375,7 +44375,7 @@ function errorIfMigrationsAndExportsBothSet(diagnostics, migrations, exports$1)
44375
44375
  if (migrations.length > 0 && exports$1 !== void 0 && Object.values(exports$1).some((entry) => entry.type === "durable-object")) diagnostics.errors.push(`\`migrations\` and \`exports\` are mutually exclusive. Choose one or the other to declare your Durable Object lifecycle, but not both.`);
44376
44376
  }
44377
44377
  __name$3(errorIfMigrationsAndExportsBothSet, "errorIfMigrationsAndExportsBothSet");
44378
- function validateContainerExportLinks(diagnostics, containers, exports$1, containersDeclaredElsewhere) {
44378
+ function validateContainerExportLinks$1(diagnostics, containers, exports$1, containersDeclaredElsewhere) {
44379
44379
  if (containers !== void 0 && !Array.isArray(containers)) return;
44380
44380
  if (containers === void 0 && containersDeclaredElsewhere) return;
44381
44381
  const containersByName = /* @__PURE__ */ new Map();
@@ -44435,7 +44435,7 @@ function validateContainerExportLinks(diagnostics, containers, exports$1, contai
44435
44435
  if (referencedContainerName === void 0 && exportEntry !== void 0 && "storage" in exportEntry && exportEntry.storage === "legacy-kv") diagnostics.errors.push(`The container "${container.name}" sets "class_name" to "${container.class_name}", but "exports.${container.class_name}.storage" is "legacy-kv". Containers are not supported on Durable Objects using the "legacy-kv" storage backend.`);
44436
44436
  }
44437
44437
  }
44438
- __name$3(validateContainerExportLinks, "validateContainerExportLinks");
44438
+ __name$3(validateContainerExportLinks$1, "validateContainerExportLinks");
44439
44439
  var validatePythonModules = /* @__PURE__ */ __name$3((diagnostics, field, value, topLevelEnv) => {
44440
44440
  if (value === void 0) return true;
44441
44441
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
@@ -60542,7 +60542,7 @@ __name$1(createWorkerUploadForm, "createWorkerUploadForm");
60542
60542
  //#endregion
60543
60543
  //#region ../remote-bindings/dist/index.mjs
60544
60544
  var import_undici = require_undici();
60545
- var version = "0.0.23";
60545
+ var version = "0.0.24";
60546
60546
  var NoDefaultValueProvided = class extends UserError$1 {
60547
60547
  constructor() {
60548
60548
  super("This command cannot be run in a non-interactive context", { telemetryMessage: "remote bindings prompt default missing" });
@@ -61709,6 +61709,136 @@ function validateSingletonBindings(env$2, ctx) {
61709
61709
  });
61710
61710
  }
61711
61711
  const EnvSchema = z$1.record(z$1.string(), BindingSchema).superRefine(validateSingletonBindings).optional();
61712
+ const ContainerImageDockerfileSchema = z$1.strictObject({
61713
+ dockerfile: z$1.string().min(1),
61714
+ buildContext: z$1.string().optional(),
61715
+ buildVars: z$1.record(z$1.string(), z$1.string()).optional()
61716
+ });
61717
+ const ContainerImageReferenceSchema = z$1.strictObject({ reference: z$1.string().min(1) });
61718
+ const ContainerImageLocalReferenceSchema = z$1.strictObject({ localReference: z$1.string().min(1) });
61719
+ const InputContainerImageSchema = z$1.union([ContainerImageDockerfileSchema, ContainerImageReferenceSchema]);
61720
+ const OutputContainerImageSchema = z$1.union([ContainerImageReferenceSchema, ContainerImageLocalReferenceSchema]);
61721
+ const VALID_ROLLOUT_STEP_PERCENTAGES = new Set([
61722
+ 5,
61723
+ 10,
61724
+ 20,
61725
+ 25,
61726
+ 50,
61727
+ 100
61728
+ ]);
61729
+ const ContainerRolloutStepPercentageSchema = z$1.union([z$1.number(), z$1.array(z$1.number())]).superRefine((stepPercentage, ctx) => {
61730
+ if (typeof stepPercentage === "number") {
61731
+ if (!VALID_ROLLOUT_STEP_PERCENTAGES.has(stepPercentage)) ctx.addIssue({
61732
+ code: "custom",
61733
+ message: "A rollout step percentage must be one of 5, 10, 20, 25, 50, or 100"
61734
+ });
61735
+ return;
61736
+ }
61737
+ if (stepPercentage.length === 0) {
61738
+ ctx.addIssue({
61739
+ code: "custom",
61740
+ message: "A rollout must contain at least one step percentage"
61741
+ });
61742
+ return;
61743
+ }
61744
+ for (const [index, step] of stepPercentage.entries()) {
61745
+ if (step < 10 || step > 100) ctx.addIssue({
61746
+ code: "custom",
61747
+ path: [index],
61748
+ message: "Rollout step percentages must be between 10 and 100"
61749
+ });
61750
+ const previousStep = stepPercentage[index - 1];
61751
+ if (previousStep !== void 0 && step < previousStep) ctx.addIssue({
61752
+ code: "custom",
61753
+ path: [index],
61754
+ message: "Rollout step percentages must be in ascending order"
61755
+ });
61756
+ }
61757
+ const lastIndex = stepPercentage.length - 1;
61758
+ if (stepPercentage[lastIndex] !== 100) ctx.addIssue({
61759
+ code: "custom",
61760
+ path: [lastIndex],
61761
+ message: "The final rollout step percentage must be 100"
61762
+ });
61763
+ });
61764
+ function validateContainerRelationships(container, ctx) {
61765
+ const stepPercentage = container.rollout?.stepPercentage;
61766
+ if (Array.isArray(stepPercentage) && container.maxInstances !== void 0 && stepPercentage.length > container.maxInstances) ctx.addIssue({
61767
+ code: "custom",
61768
+ path: ["rollout", "stepPercentage"],
61769
+ message: "A rollout cannot contain more steps than the maximum number of instances"
61770
+ });
61771
+ }
61772
+ const ContainerObservabilityBaseSchema = z$1.strictObject({
61773
+ enabled: z$1.boolean().optional(),
61774
+ logs: z$1.strictObject({ enabled: z$1.boolean().optional() }).optional()
61775
+ });
61776
+ const ContainerObservabilitySchema = z$1.union([ContainerObservabilityBaseSchema.extend({ targetInstancePercentage: z$1.number().min(0).max(100).optional() }), ContainerObservabilityBaseSchema.extend({ targetInstanceCount: z$1.number().int().nonnegative().optional() })]);
61777
+ const BaseContainerSchema = z$1.strictObject({
61778
+ type: z$1.literal("container"),
61779
+ name: z$1.string().min(1),
61780
+ observability: ContainerObservabilitySchema.optional(),
61781
+ unsafe: z$1.record(z$1.string(), z$1.unknown()).optional()
61782
+ });
61783
+ const StandardContainerBaseSchema = BaseContainerSchema.extend({
61784
+ maxInstances: z$1.number().int().nonnegative().default(20),
61785
+ instanceType: z$1.union([z$1.enum([
61786
+ "basic",
61787
+ "lite",
61788
+ "standard-1",
61789
+ "standard-2",
61790
+ "standard-3",
61791
+ "standard-4"
61792
+ ]), z$1.strictObject({
61793
+ vcpu: z$1.number().min(.0625).optional(),
61794
+ memoryMib: z$1.number().nonnegative().optional(),
61795
+ diskMb: z$1.number().nonnegative().optional()
61796
+ })]).optional(),
61797
+ schedulingPolicy: z$1.enum(["default", "regional"]).optional(),
61798
+ ssh: z$1.strictObject({
61799
+ enabled: z$1.boolean(),
61800
+ port: z$1.number().int().min(1).max(65535).optional()
61801
+ }).optional(),
61802
+ authorizedKeys: z$1.array(z$1.strictObject({
61803
+ name: z$1.string(),
61804
+ publicKey: z$1.string()
61805
+ })).optional(),
61806
+ constraints: z$1.strictObject({
61807
+ regions: z$1.array(z$1.enum([
61808
+ "ENAM",
61809
+ "WNAM",
61810
+ "EEUR",
61811
+ "WEUR",
61812
+ "APAC",
61813
+ "SAM",
61814
+ "ME",
61815
+ "OC",
61816
+ "AFR"
61817
+ ])).optional(),
61818
+ jurisdiction: z$1.enum(["eu", "fedramp"]).optional()
61819
+ }).optional(),
61820
+ rollout: z$1.strictObject({
61821
+ kind: z$1.enum([
61822
+ "full-auto",
61823
+ "none",
61824
+ "full-manual"
61825
+ ]).optional(),
61826
+ stepPercentage: ContainerRolloutStepPercentageSchema.optional(),
61827
+ activeGracePeriod: z$1.number().nonnegative().optional()
61828
+ }).optional()
61829
+ });
61830
+ const DurableObjectContainerBaseSchema = BaseContainerSchema.extend({ schedulingPolicy: z$1.literal("durable-object") });
61831
+ /**
61832
+ * Input Container schema — validates user-authored `cloudflare.config.ts`
61833
+ * Container exports. Dockerfiles are built by the consuming build tool.
61834
+ */
61835
+ const InputContainerSchema = z$1.union([StandardContainerBaseSchema.extend({ image: InputContainerImageSchema }).superRefine(validateContainerRelationships), DurableObjectContainerBaseSchema.extend({ images: z$1.record(z$1.string(), InputContainerImageSchema).optional() })]);
61836
+ /**
61837
+ * Output Container schema — validates Container configs in the Build Output
61838
+ * Specification, after any Dockerfile has been built into a local or remote
61839
+ * image reference.
61840
+ */
61841
+ const OutputContainerSchema = z$1.union([StandardContainerBaseSchema.extend({ image: OutputContainerImageSchema }).superRefine(validateContainerRelationships), DurableObjectContainerBaseSchema.extend({ images: z$1.record(z$1.string(), OutputContainerImageSchema).optional() })]);
61712
61842
  const DurableObjectCreatedExportSchema = z$1.strictObject({
61713
61843
  type: z$1.literal("durable-object"),
61714
61844
  state: z$1.literal("created").optional(),
@@ -61887,8 +62017,13 @@ const InputSettingsSchema = z$1.strictObject({
61887
62017
  * Build Output Specification. Adds the `mode` the build was produced in.
61888
62018
  */
61889
62019
  const OutputSettingsSchema = InputSettingsSchema.extend({ mode: z$1.string().optional() });
62020
+ const DEFAULT_EXPORT_NAME = "default";
61890
62021
  const SETTINGS_EXPORT_NAME = "settings";
61891
- const SUPPORTED_EXPORT_TYPES = new Set(["worker", "settings"]);
62022
+ const SUPPORTED_EXPORT_TYPES = new Set([
62023
+ "container",
62024
+ "worker",
62025
+ "settings"
62026
+ ]);
61892
62027
  function invalidConfigExportMessage(exportName) {
61893
62028
  return `The \`${exportName}\` export is not a supported export type. Move constants, helper functions, and other unsupported exports to a separate module.`;
61894
62029
  }
@@ -61905,6 +62040,15 @@ const ConfigExportsTypeSchema = z$1.record(z$1.string(), z$1.unknown()).check((c
61905
62040
  });
61906
62041
  continue;
61907
62042
  }
62043
+ if (key === DEFAULT_EXPORT_NAME && type !== "worker") {
62044
+ ctx.issues.push({
62045
+ code: "custom",
62046
+ input: value,
62047
+ path: [key],
62048
+ message: `The \`${DEFAULT_EXPORT_NAME}\` export is reserved for a \`worker\` config; found a \`${type}\` config.`
62049
+ });
62050
+ continue;
62051
+ }
61908
62052
  const isSettingsName = key === SETTINGS_EXPORT_NAME;
61909
62053
  const isSettingsType = type === "settings";
61910
62054
  if (isSettingsType && !isSettingsName) ctx.issues.push({
@@ -62053,18 +62197,26 @@ function createFatalError(message, isJson, options) {
62053
62197
  return new FatalError(`${message}`, options);
62054
62198
  }
62055
62199
  __name(createFatalError, "createFatalError");
62200
+ const ROLLOUT_KIND_MAP = {
62201
+ "full-auto": "full_auto",
62202
+ "full-manual": "full_manual",
62203
+ none: "none"
62204
+ };
62056
62205
  /**
62057
62206
  * Convert a parsed `@cloudflare/config` config into a Wrangler `RawConfig`.
62058
62207
  *
62059
- * The caller is responsible for unwrapping any function/promise wrapper around
62060
- * the config and validating it against `InputWorkerSchema` before passing it in.
62208
+ * The caller is responsible for unwrapping any function/promise wrappers and
62209
+ * validating the configs against their corresponding input schemas before
62210
+ * passing them in.
62061
62211
  *
62062
62212
  * @param workerConfig The parsed (post-validation) Worker config.
62063
62213
  * @param settingsConfig The optional parsed settings config, whose fields
62064
62214
  * are merged onto the result.
62215
+ * @param containerExports The parsed Container exports to include in the
62216
+ * result.
62065
62217
  * @returns The corresponding Wrangler `RawConfig`.
62066
62218
  */
62067
- function convertToWranglerConfig(workerConfig, settingsConfig) {
62219
+ function convertToWranglerConfig(workerConfig, settingsConfig, containerExports = []) {
62068
62220
  const result = {};
62069
62221
  convertTopLevel(workerConfig, result);
62070
62222
  convertBindingsAndAssets(workerConfig, result);
@@ -62073,8 +62225,50 @@ function convertToWranglerConfig(workerConfig, settingsConfig) {
62073
62225
  convertTriggers(workerConfig, result);
62074
62226
  convertTailConsumers(workerConfig, result);
62075
62227
  if (settingsConfig !== void 0) convertSettings(settingsConfig, result);
62228
+ if (containerExports.length > 0) result.containers = containerExports.map((container) => convertContainer(container));
62076
62229
  return result;
62077
62230
  }
62231
+ function convertContainer(container) {
62232
+ if (container.schedulingPolicy === "durable-object") throw new UserError("Durable Object-managed Containers are not currently supported by `convertToWranglerConfig()`.", { telemetryMessage: false });
62233
+ const converted = { name: container.name };
62234
+ if (container.observability !== void 0) converted.observability = convertContainerObservability(container.observability);
62235
+ if (container.unsafe !== void 0) converted.unsafe = container.unsafe;
62236
+ converted.image = "dockerfile" in container.image ? container.image.dockerfile : container.image.reference;
62237
+ converted.max_instances = container.maxInstances;
62238
+ if ("dockerfile" in container.image) {
62239
+ if (container.image.buildContext !== void 0) converted.image_build_context = container.image.buildContext;
62240
+ if (container.image.buildVars !== void 0) converted.image_vars = container.image.buildVars;
62241
+ }
62242
+ if (container.instanceType !== void 0) if (typeof container.instanceType === "string") converted.instance_type = container.instanceType;
62243
+ else {
62244
+ const instanceType = {};
62245
+ if (container.instanceType.vcpu !== void 0) instanceType.vcpu = container.instanceType.vcpu;
62246
+ if (container.instanceType.memoryMib !== void 0) instanceType.memory_mib = container.instanceType.memoryMib;
62247
+ if (container.instanceType.diskMb !== void 0) instanceType.disk_mb = container.instanceType.diskMb;
62248
+ converted.instance_type = instanceType;
62249
+ }
62250
+ if (container.schedulingPolicy !== void 0) converted.scheduling_policy = container.schedulingPolicy;
62251
+ if (container.ssh !== void 0) converted.ssh = container.ssh;
62252
+ if (container.authorizedKeys !== void 0) converted.authorized_keys = container.authorizedKeys.map(({ name, publicKey }) => ({
62253
+ name,
62254
+ public_key: publicKey
62255
+ }));
62256
+ if (container.constraints !== void 0) converted.constraints = container.constraints;
62257
+ if (container.rollout !== void 0) {
62258
+ if (container.rollout.kind !== void 0) converted.rollout_kind = ROLLOUT_KIND_MAP[container.rollout.kind];
62259
+ if (container.rollout.stepPercentage !== void 0) converted.rollout_step_percentage = container.rollout.stepPercentage;
62260
+ if (container.rollout.activeGracePeriod !== void 0) converted.rollout_active_grace_period = container.rollout.activeGracePeriod;
62261
+ }
62262
+ return converted;
62263
+ }
62264
+ function convertContainerObservability(observability) {
62265
+ const converted = {};
62266
+ if (observability.enabled !== void 0) converted.enabled = observability.enabled;
62267
+ if (observability.logs !== void 0) converted.logs = observability.logs;
62268
+ if ("targetInstancePercentage" in observability && observability.targetInstancePercentage !== void 0) converted.target_instance_percentage = observability.targetInstancePercentage;
62269
+ if ("targetInstanceCount" in observability && observability.targetInstanceCount !== void 0) converted.target_instance_count = observability.targetInstanceCount;
62270
+ return converted;
62271
+ }
62078
62272
  /**
62079
62273
  * Merge a parsed settings config's fields (`account_id`, `compliance_region`)
62080
62274
  * onto an existing Wrangler `RawConfig`.
@@ -62742,6 +62936,9 @@ function isRecord(value) {
62742
62936
  function isConfigReference(value) {
62743
62937
  return typeof value === "function" || isRecord(value);
62744
62938
  }
62939
+ function normalizeConfigReference(reference, resolved, expectedType) {
62940
+ return isRecord(resolved) && resolved.type === expectedType && typeof resolved.name === "string" ? resolved.name : reference;
62941
+ }
62745
62942
  function createDefinitionResolver(ctx) {
62746
62943
  const resolvedDefinitions = /* @__PURE__ */ new Map();
62747
62944
  return (input) => {
@@ -62762,7 +62959,7 @@ async function normalizeWorkerReferences(resolved, resolveDefinition) {
62762
62959
  const target = await resolveDefinition(binding.worker);
62763
62960
  env$2[bindingName] = {
62764
62961
  ...binding,
62765
- worker: isRecord(target) ? target.name : void 0
62962
+ worker: normalizeConfigReference(binding.worker, target, "worker")
62766
62963
  };
62767
62964
  }
62768
62965
  return {
@@ -62770,22 +62967,151 @@ async function normalizeWorkerReferences(resolved, resolveDefinition) {
62770
62967
  env: env$2
62771
62968
  };
62772
62969
  }
62970
+ async function normalizeContainerReferences(resolved, resolveDefinition, resolvedExports) {
62971
+ const issues = [];
62972
+ if (!isRecord(resolved) || !isRecord(resolved.exports)) return {
62973
+ value: resolved,
62974
+ issues
62975
+ };
62976
+ const workerExports = { ...resolved.exports };
62977
+ for (const [exportName, workerExport] of Object.entries(workerExports)) {
62978
+ if (!isRecord(workerExport) || workerExport.type !== "durable-object" || workerExport.container === void 0) continue;
62979
+ const reference = workerExport.container;
62980
+ if (typeof reference === "string") {
62981
+ issues.push({
62982
+ code: "custom",
62983
+ input: reference,
62984
+ path: [
62985
+ "exports",
62986
+ exportName,
62987
+ "container"
62988
+ ],
62989
+ message: "Container provided as a string. Reference an exported Container definition instead."
62990
+ });
62991
+ workerExports[exportName] = {
62992
+ ...workerExport,
62993
+ container: void 0
62994
+ };
62995
+ continue;
62996
+ }
62997
+ const target = await resolveDefinition(reference);
62998
+ if (isRecord(target) && target.type === "container" && typeof target.name === "string" && !Object.values(resolvedExports).includes(target)) {
62999
+ issues.push({
63000
+ code: "custom",
63001
+ input: reference,
63002
+ path: [
63003
+ "exports",
63004
+ exportName,
63005
+ "container"
63006
+ ],
63007
+ message: `The referenced Container "${target.name}" is not exported.`
63008
+ });
63009
+ workerExports[exportName] = {
63010
+ ...workerExport,
63011
+ container: void 0
63012
+ };
63013
+ continue;
63014
+ }
63015
+ workerExports[exportName] = {
63016
+ ...workerExport,
63017
+ container: normalizeConfigReference(reference, target, "container")
63018
+ };
63019
+ }
63020
+ return {
63021
+ value: {
63022
+ ...resolved,
63023
+ exports: workerExports
63024
+ },
63025
+ issues
63026
+ };
63027
+ }
63028
+ async function normalizeConfigReferences(resolved, resolveDefinition, resolvedExports) {
63029
+ return normalizeContainerReferences(await normalizeWorkerReferences(resolved, resolveDefinition), resolveDefinition, resolvedExports);
63030
+ }
62773
63031
  function prefixIssues(issues, prefix) {
62774
63032
  return issues.map((issue$1) => ({
62775
63033
  ...issue$1,
62776
63034
  path: [prefix, ...issue$1.path]
62777
63035
  }));
62778
63036
  }
63037
+ function validateUniqueResourceNames(configExports) {
63038
+ const issues = [];
63039
+ const exportNameByResourceName = {
63040
+ container: /* @__PURE__ */ new Map(),
63041
+ worker: /* @__PURE__ */ new Map()
63042
+ };
63043
+ for (const [exportName, config$1] of Object.entries(configExports)) {
63044
+ if (config$1.type !== "container" && config$1.type !== "worker") continue;
63045
+ const resourceType = config$1.type === "container" ? "Container" : "Worker";
63046
+ const exportNames = exportNameByResourceName[config$1.type];
63047
+ const previousExportName = exportNames.get(config$1.name);
63048
+ if (previousExportName !== void 0) {
63049
+ issues.push({
63050
+ code: "custom",
63051
+ input: config$1.name,
63052
+ path: [exportName, "name"],
63053
+ message: `The ${resourceType} name "${config$1.name}" is also used by the "${previousExportName}" export. ${resourceType} names must be unique.`
63054
+ });
63055
+ continue;
63056
+ }
63057
+ exportNames.set(config$1.name, exportName);
63058
+ }
63059
+ return issues;
63060
+ }
63061
+ function validateContainerExportLinks(configExports) {
63062
+ const issues = [];
63063
+ const containerNames = /* @__PURE__ */ new Set();
63064
+ for (const config$1 of Object.values(configExports)) if (config$1.type === "container") containerNames.add(config$1.name);
63065
+ const firstReferenceByContainerName = /* @__PURE__ */ new Map();
63066
+ for (const [workerExportName, config$1] of Object.entries(configExports)) {
63067
+ if (config$1.type !== "worker") continue;
63068
+ for (const [durableObjectExportName, workerExport] of Object.entries(config$1.exports ?? {})) {
63069
+ if (workerExport.type !== "durable-object" || !("container" in workerExport) || workerExport.container === void 0) continue;
63070
+ const containerName = workerExport.container;
63071
+ const path$3 = [
63072
+ workerExportName,
63073
+ "exports",
63074
+ durableObjectExportName,
63075
+ "container"
63076
+ ];
63077
+ if (!containerNames.has(containerName)) {
63078
+ issues.push({
63079
+ code: "custom",
63080
+ input: containerName,
63081
+ path: path$3,
63082
+ message: `The Container "${containerName}" is not exported from this configuration.`
63083
+ });
63084
+ continue;
63085
+ }
63086
+ const firstReference = firstReferenceByContainerName.get(containerName);
63087
+ if (firstReference !== void 0) {
63088
+ issues.push({
63089
+ code: "custom",
63090
+ input: containerName,
63091
+ path: path$3,
63092
+ message: `The Container "${containerName}" is already referenced by "${firstReference.workerExportName}.exports.${firstReference.durableObjectExportName}". A Container can only be linked to one Durable Object.`
63093
+ });
63094
+ continue;
63095
+ }
63096
+ firstReferenceByContainerName.set(containerName, {
63097
+ workerExportName,
63098
+ durableObjectExportName
63099
+ });
63100
+ }
63101
+ }
63102
+ return issues;
63103
+ }
62779
63104
  /**
62780
63105
  * Resolve and validate loaded `cloudflare.config.ts` exports.
62781
63106
  *
62782
- * Config inputs are resolved once by identity. Top-level Worker exports are
62783
- * also parsed once by identity; unexported references are resolved only far
62784
- * enough to replace the reference with the Worker's name.
63107
+ * Config inputs are resolved once by identity. Top-level Worker and Container
63108
+ * exports are also parsed once by identity. References are resolved only far
63109
+ * enough to replace them with resource names, then Container links are
63110
+ * validated across the exported resources.
62785
63111
  */
62786
63112
  async function resolveAndValidateConfigExports(exports$2, ctx) {
62787
63113
  const resolveDefinition = createDefinitionResolver(ctx);
62788
- const parsedWorkers = /* @__PURE__ */ new Map();
63114
+ const parsedResources = /* @__PURE__ */ new Map();
62789
63115
  const resolvedExports = {};
62790
63116
  for (const [name, input] of Object.entries(exports$2)) resolvedExports[name] = await resolveDefinition(input);
62791
63117
  const typeResult = ConfigExportsTypeSchema.safeParse(resolvedExports);
@@ -62799,17 +63125,20 @@ async function resolveAndValidateConfigExports(exports$2, ctx) {
62799
63125
  for (const [name, input] of Object.entries(exports$2)) {
62800
63126
  const resolved = resolvedExports[name];
62801
63127
  if (!isRecord(resolved)) continue;
62802
- if (resolved.type === "worker") {
62803
- let result = parsedWorkers.get(input);
62804
- if (!result) {
62805
- result = InputWorkerSchema.safeParse(await normalizeWorkerReferences(resolved, resolveDefinition));
62806
- parsedWorkers.set(input, result);
62807
- if (!result.success) issues.push(...prefixIssues(result.error.issues, name));
62808
- }
62809
- if (result.success) data[name] = result.data;
62810
- continue;
62811
- }
62812
- }
63128
+ if (resolved.type !== "worker" && resolved.type !== "container") continue;
63129
+ let result = parsedResources.get(input);
63130
+ if (!result) {
63131
+ if (resolved.type === "worker") {
63132
+ const normalized = await normalizeConfigReferences(resolved, resolveDefinition, resolvedExports);
63133
+ issues.push(...prefixIssues(normalized.issues, name));
63134
+ result = InputWorkerSchema.safeParse(normalized.value);
63135
+ } else result = InputContainerSchema.safeParse(resolved);
63136
+ parsedResources.set(input, result);
63137
+ if (!result.success) issues.push(...prefixIssues(result.error.issues, name));
63138
+ }
63139
+ if (result.success) data[name] = result.data;
63140
+ }
63141
+ issues.push(...validateUniqueResourceNames(data), ...validateContainerExportLinks(data));
62813
63142
  return issues.length > 0 ? {
62814
63143
  success: false,
62815
63144
  error: new z$1.ZodError(issues)