@cloudflare/vite-plugin 1.54.11 → 1.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -16,7 +16,7 @@ import net from "node:net";
16
16
  import http from "node:http";
17
17
  import events, { EventEmitter } from "node:events";
18
18
  import { Buffer as Buffer$1 } from "node:buffer";
19
- import crypto$1, { createCipheriv, createDecipheriv, createHash, randomBytes, randomUUID, webcrypto } from "node:crypto";
19
+ import crypto2, { createCipheriv, createDecipheriv, createHash, randomBytes, randomUUID, webcrypto } from "node:crypto";
20
20
  import { AsyncLocalStorage } from "node:async_hooks";
21
21
  import * as fsp from "node:fs/promises";
22
22
  import fs, { constants as constants$1 } from "node:fs/promises";
@@ -2614,8 +2614,8 @@ function formatConfigSnippet(snippet, configPath, formatted = true) {
2614
2614
  __name$6(formatConfigSnippet, "formatConfigSnippet");
2615
2615
 
2616
2616
  //#endregion
2617
- //#region ../workers-utils/dist/chunk-YZWVNJ5I.mjs
2618
- var DEFAULT_COMPAT_DATE = "2026-09-16";
2617
+ //#region ../workers-utils/dist/chunk-S6ZAI5DC.mjs
2618
+ var DEFAULT_COMPAT_DATE = "2026-09-18";
2619
2619
  function assertNever(_value) {}
2620
2620
  __name$6(assertNever, "assertNever");
2621
2621
  function mapWorkerMetadataBindings(bindings) {
@@ -15573,7 +15573,7 @@ var require_snapshot_utils = /* @__PURE__ */ __commonJS$2({ "../../node_modules/
15573
15573
  match: new Set(matchHeaders$1.map((header) => caseSensitive ? header : header.toLowerCase()))
15574
15574
  };
15575
15575
  }
15576
- const crypto$4 = runtimeFeatures$5.has("crypto") ? __require$2("node:crypto") : null;
15576
+ const crypto$3 = runtimeFeatures$5.has("crypto") ? __require$2("node:crypto") : null;
15577
15577
  /**
15578
15578
  * @callback HashIdFunction
15579
15579
  * @param {string} value - The value to hash
@@ -15583,7 +15583,7 @@ var require_snapshot_utils = /* @__PURE__ */ __commonJS$2({ "../../node_modules/
15583
15583
  * Generates a hash for a given value
15584
15584
  * @type {HashIdFunction}
15585
15585
  */
15586
- const hashId$1 = crypto$4?.hash ? (value) => crypto$4.hash("sha256", value, "base64url") : (value) => Buffer.from(value).toString("base64url");
15586
+ const hashId$1 = crypto$3?.hash ? (value) => crypto$3.hash("sha256", value, "base64url") : (value) => Buffer.from(value).toString("base64url");
15587
15587
  /**
15588
15588
  * @typedef {(url: string) => boolean} IsUrlExcluded Checks if a URL matches any of the exclude patterns
15589
15589
  */
@@ -21153,10 +21153,10 @@ var require_subresource_integrity = /* @__PURE__ */ __commonJS$2({ "../../node_m
21153
21153
  ["sha512", 2]
21154
21154
  ]);
21155
21155
  /** @type {import('node:crypto')} */
21156
- let crypto$3;
21156
+ let crypto$2;
21157
21157
  if (runtimeFeatures$3.has("crypto")) {
21158
- crypto$3 = __require$2("node:crypto");
21159
- const cryptoHashes = crypto$3.getHashes();
21158
+ crypto$2 = __require$2("node:crypto");
21159
+ const cryptoHashes = crypto$2.getHashes();
21160
21160
  if (cryptoHashes.length === 0) validSRIHashAlgorithmTokenSet.clear();
21161
21161
  for (const algorithm of validSRIHashAlgorithmTokenSet.keys()) if (cryptoHashes.includes(algorithm) === false) validSRIHashAlgorithmTokenSet.delete(algorithm);
21162
21162
  } else validSRIHashAlgorithmTokenSet.clear();
@@ -21254,7 +21254,7 @@ var require_subresource_integrity = /* @__PURE__ */ __commonJS$2({ "../../node_m
21254
21254
  * @returns {string}
21255
21255
  */
21256
21256
  const applyAlgorithmToBytes = (algorithm, bytes) => {
21257
- return crypto$3.hash(algorithm, bytes, "base64");
21257
+ return crypto$2.hash(algorithm, bytes, "base64");
21258
21258
  };
21259
21259
  /**
21260
21260
  * Compares two base64 strings, allowing for base64url
@@ -23838,7 +23838,7 @@ var require_connection = /* @__PURE__ */ __commonJS$2({ "../../node_modules/.pnp
23838
23838
  const { WebsocketFrameSend: WebsocketFrameSend$4 } = require_frame();
23839
23839
  const assert$3 = __require$2("node:assert");
23840
23840
  const { runtimeFeatures } = require_runtime_features();
23841
- const crypto$2 = runtimeFeatures.has("crypto") ? __require$2("node:crypto") : null;
23841
+ const crypto$1 = runtimeFeatures.has("crypto") ? __require$2("node:crypto") : null;
23842
23842
  let warningEmitted = false;
23843
23843
  /**
23844
23844
  * @see https://websockets.spec.whatwg.org/#concept-websocket-establish
@@ -23862,7 +23862,7 @@ var require_connection = /* @__PURE__ */ __commonJS$2({ "../../node_modules/.pnp
23862
23862
  useURLCredentials: true
23863
23863
  });
23864
23864
  if (options.headers) request$2.headersList = getHeadersList(new Headers$2(options.headers));
23865
- const keyValue = crypto$2.randomBytes(16).toString("base64");
23865
+ const keyValue = crypto$1.randomBytes(16).toString("base64");
23866
23866
  request$2.headersList.append("sec-websocket-key", keyValue, true);
23867
23867
  request$2.headersList.append("sec-websocket-version", "13", true);
23868
23868
  for (const protocol of protocols) request$2.headersList.append("sec-websocket-protocol", protocol, true);
@@ -23898,7 +23898,7 @@ var require_connection = /* @__PURE__ */ __commonJS$2({ "../../node_modules/.pnp
23898
23898
  failWebsocketConnection$3(handler, 1002, "Server did not set Connection header to \"upgrade\".");
23899
23899
  return;
23900
23900
  }
23901
- if (response.headersList.get("Sec-WebSocket-Accept") !== crypto$2.hash("sha1", keyValue + uid, "base64")) {
23901
+ if (response.headersList.get("Sec-WebSocket-Accept") !== crypto$1.hash("sha1", keyValue + uid, "base64")) {
23902
23902
  failWebsocketConnection$3(handler, 1002, "Incorrect hash received in Sec-WebSocket-Accept header.");
23903
23903
  return;
23904
23904
  }
@@ -44103,19 +44103,34 @@ var validateObservability = /* @__PURE__ */ __name$6((diagnostics, field, value)
44103
44103
  key: "traces.enabled",
44104
44104
  value: val.traces?.enabled,
44105
44105
  type: "boolean"
44106
+ },
44107
+ {
44108
+ key: "issues.enabled",
44109
+ value: val.issues?.enabled,
44110
+ type: "boolean"
44106
44111
  }
44107
44112
  ]) && isValid;
44108
44113
  isValid = validateOptionalProperty(diagnostics, field, "head_sampling_rate", val.head_sampling_rate, "number") && isValid;
44109
44114
  isValid = validateOptionalProperty(diagnostics, field, "redact_query_string", val.redact_query_string, "boolean") && isValid;
44115
+ const issuesIsObject = val.issues === void 0 || val.issues !== null && typeof val.issues === "object" && !Array.isArray(val.issues);
44116
+ if (!issuesIsObject) {
44117
+ diagnostics.errors.push(`"${field}.issues" should be an object but got ${JSON.stringify(val.issues)}.`);
44118
+ isValid = false;
44119
+ }
44110
44120
  isValid = validateOptionalProperty(diagnostics, field, "logs", val.logs, "object") && isValid;
44111
44121
  isValid = validateOptionalProperty(diagnostics, field, "traces", val.traces, "object") && isValid;
44112
44122
  isValid = validateAdditionalProperties(diagnostics, field, Object.keys(val), [
44113
44123
  "enabled",
44114
44124
  "head_sampling_rate",
44115
44125
  "redact_query_string",
44126
+ "issues",
44116
44127
  "logs",
44117
44128
  "traces"
44118
44129
  ]) && isValid;
44130
+ if (val.issues !== void 0 && issuesIsObject) {
44131
+ isValid = validateOptionalProperty(diagnostics, `${field}.issues`, "enabled", val.issues.enabled, "boolean") && isValid;
44132
+ isValid = validateAdditionalProperties(diagnostics, `${field}.issues`, Object.keys(val.issues), ["enabled"]) && isValid;
44133
+ }
44119
44134
  if (typeof val.logs === "object") {
44120
44135
  isValid = validateOptionalProperty(diagnostics, field, "logs.enabled", val.logs.enabled, "boolean") && isValid;
44121
44136
  isValid = validateOptionalProperty(diagnostics, field, "logs.head_sampling_rate", val.logs.head_sampling_rate, "number") && isValid;
@@ -44342,6 +44357,7 @@ var BINDING_LOCAL_SUPPORT = {
44342
44357
  pipeline: "local-and-remote",
44343
44358
  service: "local-and-remote",
44344
44359
  queue: "local-and-remote",
44360
+ flagship: "local-and-remote",
44345
44361
  vectorize: "remote",
44346
44362
  mtls_certificate: "remote",
44347
44363
  dispatch_namespace: "remote",
@@ -44350,7 +44366,6 @@ var BINDING_LOCAL_SUPPORT = {
44350
44366
  ai_search_namespace: "DO-NOT-USE-this-resource-will-never-have-a-local-simulator",
44351
44367
  media: "DO-NOT-USE-this-resource-will-never-have-a-local-simulator",
44352
44368
  artifacts: "DO-NOT-USE-this-resource-will-never-have-a-local-simulator",
44353
- flagship: "DO-NOT-USE-this-resource-will-never-have-a-local-simulator",
44354
44369
  vpc_service: "DO-NOT-USE-this-resource-will-never-have-a-local-simulator",
44355
44370
  vpc_network: "DO-NOT-USE-this-resource-will-never-have-a-local-simulator",
44356
44371
  agent_memory: "DO-NOT-USE-this-resource-will-never-have-a-local-simulator"
@@ -45065,12 +45080,12 @@ function printBindings(bindings, options) {
45065
45080
  mode: getMode({ isSimulatedLocally: true })
45066
45081
  };
45067
45082
  }));
45068
- if (flagship.length > 0) output.push(...flagship.map(({ binding, app_id }) => {
45083
+ if (flagship.length > 0) output.push(...flagship.map(({ binding, app_id, remote }) => {
45069
45084
  return {
45070
45085
  name: binding,
45071
45086
  type: getBindingTypeFriendlyName("flagship"),
45072
45087
  value: app_id,
45073
- mode: getMode({ isSimulatedLocally: !context.remoteBindingsDisabled ? false : void 0 })
45088
+ mode: getMode({ isSimulatedLocally: context.remoteBindingsDisabled || !remote })
45074
45089
  };
45075
45090
  }));
45076
45091
  if (services.length > 0) output.push(...services.map(({ binding, service, entrypoint, remote, dev }) => {
@@ -48153,10 +48168,14 @@ async function assertWranglerVersion() {
48153
48168
  * delegate uses to force the experimental Build Output Specification on by default.
48154
48169
  */
48155
48170
  const FORCE_BUILD_OUTPUT_ENV_VAR = "CLOUDFLARE_VITE_FORCE_BUILD_OUTPUT";
48171
+ const PREVIEW_BUILD_ENV_VAR = "CLOUDFLARE_VITE_PREVIEW_BUILD";
48156
48172
  /** Whether `cf-vite build` has forced the Build Output Specification on. */
48157
48173
  function isForcedBuildOutput() {
48158
48174
  return process.env[FORCE_BUILD_OUTPUT_ENV_VAR] === "true";
48159
48175
  }
48176
+ function isPreviewBuild() {
48177
+ return process.env[PREVIEW_BUILD_ENV_VAR] === "true";
48178
+ }
48160
48179
 
48161
48180
  //#endregion
48162
48181
  //#region ../../node_modules/.pnpm/@remix-run+node-fetch-server@0.8.0/node_modules/@remix-run/node-fetch-server/dist/node-fetch-server.js
@@ -55609,14 +55628,14 @@ const ContainerObservabilityBaseSchema = strictObject({
55609
55628
  enabled: boolean().optional(),
55610
55629
  logs: strictObject({ enabled: boolean().optional() }).optional()
55611
55630
  });
55612
- const ContainerObservabilitySchema = union([ContainerObservabilityBaseSchema.extend({ targetInstancePercentage: number().min(0).max(100).optional() }), ContainerObservabilityBaseSchema.extend({ targetInstanceCount: number().int().nonnegative().optional() })]);
55631
+ const StandardContainerObservabilitySchema = union([ContainerObservabilityBaseSchema.extend({ targetInstancePercentage: number().min(0).max(100).optional() }), ContainerObservabilityBaseSchema.extend({ targetInstanceCount: number().int().nonnegative().optional() })]);
55613
55632
  const BaseContainerSchema = strictObject({
55614
55633
  type: literal$1("container"),
55615
55634
  name: string().min(1),
55616
- observability: ContainerObservabilitySchema.optional(),
55617
55635
  unsafe: record(string(), unknown()).optional()
55618
55636
  });
55619
55637
  const StandardContainerBaseSchema = BaseContainerSchema.extend({
55638
+ observability: StandardContainerObservabilitySchema.optional(),
55620
55639
  maxInstances: number().int().nonnegative().default(20),
55621
55640
  instanceType: union([_enum([
55622
55641
  "basic",
@@ -55663,7 +55682,10 @@ const StandardContainerBaseSchema = BaseContainerSchema.extend({
55663
55682
  activeGracePeriod: number().nonnegative().optional()
55664
55683
  }).optional()
55665
55684
  });
55666
- const DurableObjectContainerBaseSchema = BaseContainerSchema.extend({ schedulingPolicy: literal$1("durable-object") });
55685
+ const DurableObjectContainerBaseSchema = BaseContainerSchema.extend({
55686
+ schedulingPolicy: literal$1("durable-object"),
55687
+ observability: ContainerObservabilityBaseSchema.optional()
55688
+ });
55667
55689
  /**
55668
55690
  * Input Container schema — validates user-authored `cloudflare.config.ts`
55669
55691
  * Container exports. Dockerfiles are built by the consuming build tool.
@@ -55728,6 +55750,7 @@ const ObservabilitySchema = strictObject({
55728
55750
  enabled: boolean().optional(),
55729
55751
  headSamplingRate: number().optional(),
55730
55752
  redactQueryString: boolean().optional(),
55753
+ issues: strictObject({ enabled: boolean().optional() }).optional(),
55731
55754
  logs: strictObject({
55732
55755
  enabled: boolean().optional(),
55733
55756
  headSamplingRate: number().optional(),
@@ -55850,9 +55873,12 @@ const InputSettingsSchema = strictObject({
55850
55873
  });
55851
55874
  /**
55852
55875
  * Output settings schema — the shape of the top-level `config.json` in the
55853
- * Build Output Specification. Adds the `mode` the build was produced in.
55876
+ * Build Output Specification. Adds the build mode and Preview intent.
55854
55877
  */
55855
- const OutputSettingsSchema = InputSettingsSchema.extend({ mode: string().optional() });
55878
+ const OutputSettingsSchema = InputSettingsSchema.extend({
55879
+ isPreview: boolean().optional(),
55880
+ mode: string().optional()
55881
+ });
55856
55882
  const DEFAULT_EXPORT_NAME = "default";
55857
55883
  const SETTINGS_EXPORT_NAME = "settings";
55858
55884
  const SUPPORTED_EXPORT_TYPES = new Set([
@@ -56098,10 +56124,14 @@ function convertToWranglerConfig(workerConfig, settingsConfig, containerExports
56098
56124
  return result;
56099
56125
  }
56100
56126
  function convertContainer(container) {
56101
- if (container.schedulingPolicy === "durable-object") throw new UserError$1("Durable Object-managed Containers are not currently supported by `convertToWranglerConfig()`.", { telemetryMessage: false });
56102
56127
  const converted = { name: container.name };
56103
56128
  if (container.observability !== void 0) converted.observability = convertContainerObservability(container.observability);
56104
56129
  if (container.unsafe !== void 0) converted.unsafe = container.unsafe;
56130
+ if (container.schedulingPolicy === "durable-object") {
56131
+ converted.scheduling_policy = "durable_object";
56132
+ if (container.images !== void 0) converted.images = Object.fromEntries(Object.entries(container.images).map(([name, image]) => [name, convertDurableObjectContainerImage(image)]));
56133
+ return converted;
56134
+ }
56105
56135
  converted.image = "dockerfile" in container.image ? container.image.dockerfile : container.image.reference;
56106
56136
  converted.max_instances = container.maxInstances;
56107
56137
  if ("dockerfile" in container.image) {
@@ -56130,6 +56160,13 @@ function convertContainer(container) {
56130
56160
  }
56131
56161
  return converted;
56132
56162
  }
56163
+ function convertDurableObjectContainerImage(image) {
56164
+ if ("reference" in image) return { image: image.reference };
56165
+ const converted = { dockerfile: image.dockerfile };
56166
+ if (image.buildContext !== void 0) converted.build_context = image.buildContext;
56167
+ if (image.buildVars !== void 0) converted.build_vars = image.buildVars;
56168
+ return converted;
56169
+ }
56133
56170
  function convertContainerObservability(observability) {
56134
56171
  const converted = {};
56135
56172
  if (observability.enabled !== void 0) converted.enabled = observability.enabled;
@@ -56175,6 +56212,7 @@ function convertObservability(observability) {
56175
56212
  if (observability.enabled !== void 0) out.enabled = observability.enabled;
56176
56213
  if (observability.headSamplingRate !== void 0) out.head_sampling_rate = observability.headSamplingRate;
56177
56214
  if (observability.redactQueryString !== void 0) out.redact_query_string = observability.redactQueryString;
56215
+ if (observability.issues !== void 0) out.issues = { enabled: observability.issues.enabled };
56178
56216
  if (observability.logs !== void 0) {
56179
56217
  const logs = {};
56180
56218
  const { logs: src$1 } = observability;
@@ -56793,6 +56831,15 @@ const CROSS_WORKER_BINDING_TYPES = new Set([
56793
56831
  "worker",
56794
56832
  "workflow"
56795
56833
  ]);
56834
+ /**
56835
+ * Selects the Container exports from a validated project configuration.
56836
+ *
56837
+ * @param config - Validated exports keyed by their JavaScript export names.
56838
+ * @returns Container configurations keyed by those same export names.
56839
+ */
56840
+ function getContainerConfigExports(config$2) {
56841
+ return Object.fromEntries(Object.entries(config$2).filter((entry) => entry[1]?.type === "container"));
56842
+ }
56796
56843
  function isRecord(value) {
56797
56844
  return typeof value === "object" && value !== null;
56798
56845
  }
@@ -57424,20 +57471,31 @@ async function writeWorkerConfig({ root, config: config$2, manifest, directoryNa
57424
57471
  await fsp.writeFile(getWorkerConfigPath(root, directoryName), JSON.stringify(outputConfig));
57425
57472
  }
57426
57473
  /**
57474
+ * Write an output Container `config.json` to the Build Output Specification
57475
+ * tree.
57476
+ *
57477
+ * Local Dockerfiles must already have been built and represented by a
57478
+ * `localReference` in the output config.
57479
+ */
57480
+ async function writeContainerConfig({ root, config: config$2, directoryName }) {
57481
+ await fsp.mkdir(getContainerDir(root, directoryName), { recursive: true });
57482
+ await fsp.writeFile(getContainerConfigPath(root, directoryName), JSON.stringify(config$2));
57483
+ }
57484
+ /**
57427
57485
  * Write the top-level `config.json` to the Build Output Specification tree.
57428
57486
  *
57429
- * Holds the project-level settings shared by every Worker: those declared by
57430
- * the `settings` export, including the `mode`, which is supplied at build time
57431
- * rather than declared. Always written, even when there are no declared
57432
- * settings and no mode: the result then degrades to `{ "type": "settings" }`.
57487
+ * Holds the project settings shared by every Worker, plus the build mode and
57488
+ * whether the build is for a Preview. Always written, even without declared
57489
+ * settings or a mode.
57433
57490
  *
57434
57491
  * `mode` is omitted when undefined, which is the case for Wrangler builds that
57435
57492
  * selected no mode (Vite always resolves one).
57436
57493
  */
57437
- async function writeSettingsConfig(root, settings, mode) {
57494
+ async function writeSettingsConfig(root, settings, mode, isPreview = false) {
57438
57495
  const outputConfig = {
57439
57496
  ...settings,
57440
57497
  type: "settings",
57498
+ ...isPreview ? { isPreview: true } : {},
57441
57499
  ...mode !== void 0 ? { mode } : {}
57442
57500
  };
57443
57501
  const configPath = getSettingsConfigPath(root);
@@ -57629,10 +57687,10 @@ function parseJson(contents, configPath) {
57629
57687
  * single-element array.
57630
57688
  */
57631
57689
  async function readBuildOutputWorkers(root) {
57632
- const { workers, settings } = await readBuildOutput(root);
57690
+ const { workers, settings, containers } = await readBuildOutput(root);
57633
57691
  const worker = workers[DEFAULT_WORKER_DIRECTORY_NAME];
57634
57692
  const { manifest,...inputShape } = worker.config;
57635
- const { config: config$2, diagnostics } = normalizeAndValidateConfig(convertToWranglerConfig(inputShape, settings), void 0, void 0, {}, true);
57693
+ const { config: config$2, diagnostics } = normalizeAndValidateConfig(convertToWranglerConfig(inputShape, settings, Object.values(containers).map(({ config: config$3 }) => convertOutputContainerToInput(config$3))), void 0, void 0, { enableContainers: false }, true);
57636
57694
  if (diagnostics.hasWarnings()) console.warn(diagnostics.renderWarnings());
57637
57695
  if (diagnostics.hasErrors()) throw new Error(diagnostics.renderErrors());
57638
57696
  if (worker.assetsDir) config$2.assets = {
@@ -57654,6 +57712,19 @@ async function readBuildOutputWorkers(root) {
57654
57712
  bundle
57655
57713
  }];
57656
57714
  }
57715
+ function convertOutputContainerToInput(config$2) {
57716
+ if (config$2.schedulingPolicy === "durable-object") {
57717
+ const { images: _images,...container } = config$2;
57718
+ return container;
57719
+ }
57720
+ return {
57721
+ ...config$2,
57722
+ image: convertOutputContainerImage(config$2.image)
57723
+ };
57724
+ }
57725
+ function convertOutputContainerImage(image) {
57726
+ return { reference: "reference" in image ? image.reference : image.localReference };
57727
+ }
57657
57728
 
57658
57729
  //#endregion
57659
57730
  //#region src/deploy-config.ts
@@ -64381,8 +64452,8 @@ function readWorkerConfig(configPath, env$3) {
64381
64452
  * validation pipeline as on-disk Wrangler config files, then apply the
64382
64453
  * Vite-specific sanitization.
64383
64454
  */
64384
- function readWorkerConfigFromRaw(rawConfig) {
64385
- const { config: config$2, diagnostics } = normalizeAndValidateConfig(rawConfig, void 0, void 0, {}, true);
64455
+ function readWorkerConfigFromRaw(rawConfig, configPath) {
64456
+ const { config: config$2, diagnostics } = normalizeAndValidateConfig(rawConfig, configPath, configPath, {}, true);
64386
64457
  if (diagnostics.hasWarnings()) console.warn(diagnostics.renderWarnings());
64387
64458
  if (diagnostics.hasErrors()) throw new Error(diagnostics.renderErrors());
64388
64459
  return processNormalizedWorkerConfig(config$2);
@@ -64581,7 +64652,7 @@ function resolveWorkerConfig(options) {
64581
64652
  let workerConfig;
64582
64653
  let raw;
64583
64654
  let nonApplicable;
64584
- if (options.rawConfigOverride) ({raw, config: workerConfig, nonApplicable} = readWorkerConfigFromRaw(options.rawConfigOverride));
64655
+ if (options.rawConfigOverride) ({raw, config: workerConfig, nonApplicable} = readWorkerConfigFromRaw(options.rawConfigOverride, options.rawConfigPath));
64585
64656
  else if (options.configPath) ({raw, config: workerConfig, nonApplicable} = readWorkerConfigFromFile(options.configPath, options.env, { visitedConfigPaths: options.visitedConfigPaths }));
64586
64657
  else {
64587
64658
  workerConfig = { ...wrangler.unstable_defaultWranglerConfig };
@@ -64670,7 +64741,8 @@ async function resolvePluginConfig(pluginConfig, userConfig, viteEnv) {
64670
64741
  env: cloudflareEnv,
64671
64742
  configCustomizer: resolvedNewConfig ? void 0 : pluginConfig.config,
64672
64743
  visitedConfigPaths: configPaths,
64673
- rawConfigOverride
64744
+ rawConfigOverride,
64745
+ rawConfigPath: resolvedNewConfig ? configPath : void 0
64674
64746
  });
64675
64747
  const environmentNameToWorkerMap = /* @__PURE__ */ new Map();
64676
64748
  const environmentNameToChildEnvironmentNamesMap = /* @__PURE__ */ new Map();
@@ -64801,11 +64873,14 @@ const EXPERIMENTAL_CONFIG_PKG = "@cloudflare/vite-plugin/experimental-config";
64801
64873
  async function loadNewConfig(options) {
64802
64874
  const configPath = path.resolve(options.root, NEW_CONFIG_FILENAME);
64803
64875
  if (!fs$1.existsSync(configPath)) throw new Error(`\`experimental.newConfig\` is enabled but no \`${NEW_CONFIG_FILENAME}\` was found at ${configPath}.`);
64804
- const { result, dependencies } = await loadAndValidateConfig(configPath, { mode: options.mode });
64876
+ const { result, dependencies } = await loadAndValidateConfig(configPath, {
64877
+ isPreview: isPreviewBuild(),
64878
+ mode: options.mode
64879
+ });
64805
64880
  if (!result.success) throw new Error(`Invalid \`${NEW_CONFIG_FILENAME}\`:\n${result.error.message}`);
64806
64881
  const worker = result.data.default?.type === "worker" ? result.data.default : void 0;
64807
64882
  if (worker === void 0) throw new Error(`\`${NEW_CONFIG_FILENAME}\` must have a default worker export.`);
64808
- const rawConfig = convertToWranglerConfig(worker, result.data.settings?.type === "settings" ? result.data.settings : void 0);
64883
+ const rawConfig = convertToWranglerConfig(worker, result.data.settings?.type === "settings" ? result.data.settings : void 0, Object.values(getContainerConfigExports(result.data)));
64809
64884
  if (options.command === "serve" && options.types.generate) await writeWorkerConfigurationDts({
64810
64885
  root: options.root,
64811
64886
  configPath,
@@ -66719,7 +66794,7 @@ const buildOutputPlugin = createPlugin("build-output", (ctx) => {
66719
66794
  if (ctx.resolvedPluginConfig.type === "preview") return;
66720
66795
  const settingsExport = ctx.resolvedPluginConfig.parsedNewConfig?.settings;
66721
66796
  const settings = settingsExport?.type === "settings" ? settingsExport : void 0;
66722
- await writeSettingsConfig(ctx.resolvedViteConfig.root, settings, ctx.resolvedViteConfig.mode);
66797
+ await writeSettingsConfig(ctx.resolvedViteConfig.root, settings, ctx.resolvedViteConfig.mode, isPreviewBuild());
66723
66798
  }
66724
66799
  });
66725
66800
  /**
@@ -68918,392 +68993,6 @@ function removeAssetsField(entryWorkerBuildDirectory) {
68918
68993
  fs$1.writeFileSync(entryWorkerConfigPath, JSON.stringify(workerConfig));
68919
68994
  }
68920
68995
 
68921
- //#endregion
68922
- //#region src/dev-vars.ts
68923
- /**
68924
- * Gets any variables with which to augment the Worker config in preview mode.
68925
- *
68926
- * Calls `unstable_getVarsForDev` with the current Cloudflare environment to get local dev variables from the .dev.vars/.env/process.env.
68927
- * When `secrets` is defined in the Worker config, only declared secrets are loaded.
68928
- */
68929
- function getLocalDevVarsForPreview(config$2, cloudflareEnv) {
68930
- const dotDevDotVars = wrangler.unstable_getVarsForDev(config$2.configPath, void 0, {}, cloudflareEnv, false, config$2.secrets);
68931
- const dotDevDotVarsEntries = Array.from(Object.entries(dotDevDotVars));
68932
- if (dotDevDotVarsEntries.length > 0) return dotDevDotVarsEntries.map(([key, { value }]) => {
68933
- return `${key}=${quoteForDotenv(value?.toString() ?? "")}\n`;
68934
- }).join("");
68935
- }
68936
- /**
68937
- * Quote a value so that wrangler reads it back unchanged when it parses
68938
- * `dist/<env>/.dev.vars` at preview time.
68939
- *
68940
- * Strategy: To quote the string pick the first quote character that does not
68941
- * appear in the value (single → backtick → double). Throw if a value cannot
68942
- * be losslessly serialized rather than silently corrupting it.
68943
- *
68944
- * @internal exported for tests
68945
- */
68946
- function quoteForDotenv(value) {
68947
- if (!value.includes("'")) return `'${value}'`;
68948
- if (!value.includes("`")) return `\`${value}\``;
68949
- if (!value.includes("\"") && !/[\\\n\r]/.test(value)) return `"${value}"`;
68950
- throw new Error("Unable to serialize value to .dev.vars: contains every supported quote character or unsafe escape sequence.");
68951
- }
68952
- /**
68953
- * Returns `true` if the `changedFile` matches a `.dev.vars` or `.env` file.
68954
- */
68955
- function hasLocalDevVarsFileChanged({ configPaths, cloudflareEnv }, changedFilePath) {
68956
- return [...configPaths].some((configPath) => {
68957
- const configDir = path.dirname(configPath);
68958
- return [
68959
- ".dev.vars",
68960
- ".env",
68961
- ...cloudflareEnv ? [`.dev.vars.${cloudflareEnv}`, `.env.${cloudflareEnv}`] : []
68962
- ].some((localDevFile) => changedFilePath === path.join(configDir, localDevFile));
68963
- });
68964
- }
68965
-
68966
- //#endregion
68967
- //#region src/vite-config.ts
68968
- const NODE_BUILTIN_SET = new Set([...builtinModules, ...builtinModules.filter((m) => !m.startsWith("node:")).map((m) => `node:${m}`)]);
68969
- function isOnlyNodeBuiltins(external) {
68970
- if (!Array.isArray(external)) return false;
68971
- return external.every((entry) => typeof entry === "string" && NODE_BUILTIN_SET.has(entry));
68972
- }
68973
- /**
68974
- * Throws an error if Worker environments include configuration options that are incompatible with the plugin.
68975
- */
68976
- function validateWorkerEnvironmentOptions(resolvedPluginConfig, resolvedViteConfig) {
68977
- const disallowedEnvironmentOptionsMap = /* @__PURE__ */ new Map();
68978
- for (const environmentName of resolvedPluginConfig.environmentNameToWorkerMap.keys()) {
68979
- const environmentOptions = resolvedViteConfig.environments[environmentName];
68980
- assert2(environmentOptions, `Missing environment config for "${environmentName}"`);
68981
- const { resolve: resolve$4 } = environmentOptions;
68982
- const disallowedEnvironmentOptions = {};
68983
- if ((resolve$4.external === true || Array.isArray(resolve$4.external) && resolve$4.external.length > 0) && !isOnlyNodeBuiltins(resolve$4.external)) disallowedEnvironmentOptions.resolveExternal = resolve$4.external;
68984
- if (Object.keys(disallowedEnvironmentOptions).length) disallowedEnvironmentOptionsMap.set(environmentName, disallowedEnvironmentOptions);
68985
- }
68986
- if (disallowedEnvironmentOptionsMap.size) {
68987
- const errorMessage = `The following environment options are incompatible with the Cloudflare Vite plugin:\n${[...disallowedEnvironmentOptionsMap].map(([environmentName, disallowedEnvironmentOptions]) => disallowedEnvironmentOptions.resolveExternal && ` - "${environmentName}" environment: \`resolve.external\`: ${JSON.stringify(disallowedEnvironmentOptions.resolveExternal)}\n`).join("")}To resolve this issue, avoid setting \`resolve.external\` in your Cloudflare Worker environments.\n`;
68988
- throw new Error(errorMessage);
68989
- }
68990
- }
68991
-
68992
- //#endregion
68993
- //#region src/plugins/wrangler-watch-ignore.ts
68994
- const WRANGLER_STATE_WATCH_IGNORE = "**/.wrangler/**";
68995
- /**
68996
- * Keep Miniflare persistence writes out of Vite's file watcher without
68997
- * replacing any ignore patterns the user already configured.
68998
- *
68999
- * @param userIgnored - Existing `server.watch.ignored` patterns, if any
69000
- * @returns Ignore patterns that always include `.wrangler` directories
69001
- */
69002
- function withWranglerStateIgnored(userIgnored) {
69003
- if (userIgnored == null) return WRANGLER_STATE_WATCH_IGNORE;
69004
- if (Array.isArray(userIgnored)) return [...userIgnored, WRANGLER_STATE_WATCH_IGNORE];
69005
- return [userIgnored, WRANGLER_STATE_WATCH_IGNORE];
69006
- }
69007
- /**
69008
- * Merge `.wrangler` into Vite's `server.watch` config.
69009
- *
69010
- * Returns `null` when the user disabled watching with `server.watch: null`,
69011
- * so this plugin does not turn the watcher back on.
69012
- *
69013
- * @param userWatch - The user's `server.watch` value from Vite config
69014
- * @returns `null` when watching is disabled, otherwise a watch object whose
69015
- * `ignored` list includes `.wrangler` directories
69016
- */
69017
- function getServerWatchConfig(userWatch) {
69018
- if (userWatch === null) return null;
69019
- return {
69020
- ...userWatch,
69021
- ignored: withWranglerStateIgnored(userWatch?.ignored)
69022
- };
69023
- }
69024
-
69025
- //#endregion
69026
- //#region src/plugins/config.ts
69027
- /**
69028
- * Plugin to handle configuration and config file watching
69029
- */
69030
- const configPlugin = createPlugin("config", (ctx) => {
69031
- return {
69032
- config(userConfig, env$3) {
69033
- if (ctx.resolvedPluginConfig.type === "preview") return { appType: "custom" };
69034
- if (!ctx.hasShownWorkerConfigWarnings) {
69035
- ctx.setHasShownWorkerConfigWarnings(true);
69036
- const workerConfigWarnings = getWarningForWorkersConfigs(ctx.resolvedPluginConfig.rawConfigs);
69037
- if (workerConfigWarnings) console.warn(workerConfigWarnings);
69038
- }
69039
- return {
69040
- appType: "custom",
69041
- server: {
69042
- allowedHosts: getAllowedHosts(ctx.getTunnelHostnames(), userConfig.server?.allowedHosts),
69043
- watch: getServerWatchConfig(userConfig.server?.watch),
69044
- fs: { deny: [...[
69045
- ".env",
69046
- ".env.*",
69047
- ".npmrc",
69048
- ".yarnrc",
69049
- ".yarnrc.yml",
69050
- "*.{crt,pem,key,pfx,p12,p8,jks}",
69051
- "**/.git/**",
69052
- ".dev.vars",
69053
- ".dev.vars.*",
69054
- "**/.wrangler/**"
69055
- ], ...Array.from(ctx.resolvedPluginConfig.configPaths, (configPath) => normalizePath(configPath))] }
69056
- },
69057
- environments: getEnvironmentsConfig(ctx, userConfig, env$3.mode),
69058
- builder: { buildApp: userConfig.builder?.buildApp ?? createBuildApp(ctx.resolvedPluginConfig) }
69059
- };
69060
- },
69061
- async configResolved(resolvedViteConfig) {
69062
- ctx.setResolvedViteConfig(resolvedViteConfig);
69063
- if (ctx.resolvedPluginConfig.type === "preview") return;
69064
- validateWorkerEnvironmentOptions(ctx.resolvedPluginConfig, ctx.resolvedViteConfig);
69065
- if (ctx.resolvedPluginConfig.experimental.newConfig?.cfBuildOutput) {
69066
- forceBuildOutputDirs(ctx.resolvedPluginConfig, ctx.resolvedViteConfig);
69067
- if (ctx.resolvedViteConfig.command === "build") await cleanBuildOutputDir(ctx.resolvedViteConfig.root);
69068
- }
69069
- },
69070
- buildStart() {
69071
- ctx.setHasShownWorkerConfigWarnings(false);
69072
- },
69073
- configureServer(viteDevServer) {
69074
- let restartInFlight = false;
69075
- const configChangedHandler = async (changedFilePath) => {
69076
- assertIsNotPreview(ctx);
69077
- if (restartInFlight) return;
69078
- if (ctx.resolvedPluginConfig.configPaths.has(changedFilePath) || hasLocalDevVarsFileChanged(ctx.resolvedPluginConfig, changedFilePath) || hasAssetsConfigChanged(ctx.resolvedPluginConfig, ctx.resolvedViteConfig, changedFilePath)) {
69079
- debuglog("Config changed: " + changedFilePath);
69080
- restartInFlight = true;
69081
- debuglog("Restarting dev server and aborting previous setup");
69082
- try {
69083
- await viteDevServer.restart();
69084
- } finally {
69085
- restartInFlight = false;
69086
- }
69087
- }
69088
- };
69089
- viteDevServer.watcher.on("change", configChangedHandler);
69090
- },
69091
- buildApp: {
69092
- order: "post",
69093
- async handler(builder) {
69094
- if (ctx.resolvedPluginConfig.type !== "workers") return;
69095
- const workerEnvironments = [...ctx.resolvedPluginConfig.environmentNameToWorkerMap.entries()].filter(([_, worker]) => !resolveDevOnly(worker.devOnly)).map(([environmentName]) => {
69096
- const environment = builder.environments[environmentName];
69097
- assert2(environment, `"${environmentName}" environment not found`);
69098
- return environment;
69099
- });
69100
- await Promise.all(workerEnvironments.filter((environment) => !environment.isBuilt).map((environment) => builder.build(environment)));
69101
- const { entryWorkerEnvironmentName } = ctx.resolvedPluginConfig;
69102
- const entryWorkerEnvironment = builder.environments[entryWorkerEnvironmentName];
69103
- assert2(entryWorkerEnvironment, `No "${entryWorkerEnvironmentName}" environment`);
69104
- const cfBuildOutput = ctx.resolvedPluginConfig.experimental.newConfig?.cfBuildOutput === true;
69105
- if (entryWorkerEnvironment.isBuilt) {
69106
- if (!builder.environments.client?.isBuilt && !cfBuildOutput) removeAssetsField(path.resolve(builder.config.root, entryWorkerEnvironment.config.build.outDir));
69107
- } else {
69108
- const clientEnvironment = builder.environments.client;
69109
- assert2(clientEnvironment, "No \"client\" environment");
69110
- if (!clientEnvironment.isBuilt) throw new Error("If `assetsOnly` is set to `true`, the client environment must be built");
69111
- if (cfBuildOutput) {
69112
- const entryWorkerNewConfig = ctx.getWorkerNewConfig(entryWorkerEnvironmentName);
69113
- assert2(entryWorkerNewConfig, `No config found for "${entryWorkerEnvironmentName}" environment`);
69114
- await writeWorkerConfig({
69115
- root: builder.config.root,
69116
- config: entryWorkerNewConfig
69117
- });
69118
- } else {
69119
- const entryWorkerConfig = ctx.getWorkerConfig(entryWorkerEnvironmentName);
69120
- assert2(entryWorkerConfig, `No config found for "${entryWorkerEnvironmentName}" environment`);
69121
- const outputConfig = {
69122
- ...entryWorkerConfig,
69123
- main: void 0,
69124
- assets: {
69125
- ...entryWorkerConfig.assets,
69126
- directory: ".",
69127
- binding: void 0
69128
- }
69129
- };
69130
- if (outputConfig.unsafe && Object.keys(outputConfig.unsafe).length === 0) outputConfig.unsafe = void 0;
69131
- fs$1.writeFileSync(path.resolve(builder.config.root, clientEnvironment.config.build.outDir, "wrangler.json"), JSON.stringify(outputConfig));
69132
- writeDeployConfig(ctx.resolvedPluginConfig, ctx.resolvedViteConfig, true);
69133
- }
69134
- }
69135
- }
69136
- }
69137
- };
69138
- });
69139
- /**
69140
- * Generates the environment configuration for all Worker environments.
69141
- */
69142
- function getEnvironmentsConfig(ctx, userConfig, mode) {
69143
- assertIsNotPreview(ctx);
69144
- if (!ctx.resolvedPluginConfig.environmentNameToWorkerMap.size) return;
69145
- return {
69146
- ...Object.fromEntries([...ctx.resolvedPluginConfig.environmentNameToWorkerMap].flatMap(([environmentName, worker]) => {
69147
- const childEnvironmentNames = ctx.resolvedPluginConfig.environmentNameToChildEnvironmentNamesMap.get(environmentName) ?? [];
69148
- const sharedOptions = {
69149
- workerConfig: worker.config,
69150
- userConfig,
69151
- mode,
69152
- hasNodeJsCompat: ctx.getNodeJsCompat(environmentName) !== void 0
69153
- };
69154
- const isEntryWorker = environmentName === ctx.resolvedPluginConfig.prerenderWorkerEnvironmentName || ctx.resolvedPluginConfig.type === "workers" && environmentName === ctx.resolvedPluginConfig.entryWorkerEnvironmentName;
69155
- return [[environmentName, createCloudflareEnvironmentOptions({
69156
- ...sharedOptions,
69157
- environmentName,
69158
- isEntryWorker,
69159
- isParentEnvironment: true
69160
- })], ...childEnvironmentNames.map((childEnvironmentName) => [childEnvironmentName, createCloudflareEnvironmentOptions({
69161
- ...sharedOptions,
69162
- environmentName: childEnvironmentName,
69163
- isEntryWorker: false,
69164
- isParentEnvironment: false
69165
- })])];
69166
- })),
69167
- client: {
69168
- build: { outDir: getOutputDirectory(userConfig, "client") },
69169
- optimizeDeps: { exclude: [...cloudflareBuiltInModules] }
69170
- }
69171
- };
69172
- }
69173
- /**
69174
- * When the Build Output Specification is enabled,
69175
- * force every Worker environment's and the client environment's `build.outDir`
69176
- * to the spec-mandated location.
69177
- *
69178
- * Runs after Vite's merge in `configResolved`, so it overrides any
69179
- * user-supplied `build.outDir`
69180
- */
69181
- function forceBuildOutputDirs(resolvedPluginConfig, resolvedViteConfig) {
69182
- if (!resolvedPluginConfig.experimental.newConfig?.cfBuildOutput) return;
69183
- const { root } = resolvedViteConfig;
69184
- if (resolvedPluginConfig.type === "workers") {
69185
- const entryName = resolvedPluginConfig.entryWorkerEnvironmentName;
69186
- const entryEnvironment = resolvedViteConfig.environments[entryName];
69187
- if (entryEnvironment) entryEnvironment.build.outDir = getWorkerBundleDir(root);
69188
- }
69189
- const clientEnvironment = resolvedViteConfig.environments.client;
69190
- if (clientEnvironment) clientEnvironment.build.outDir = getWorkerAssetsDir(root);
69191
- }
69192
- function getAllowedHosts(tunnelHostnames, userAllowedHosts) {
69193
- if (tunnelHostnames.length === 0 || userAllowedHosts === true) return userAllowedHosts;
69194
- return Array.from(new Set([...userAllowedHosts ?? [], ...tunnelHostnames]));
69195
- }
69196
-
69197
- //#endregion
69198
- //#region src/debug.ts
69199
- var import_picocolors$3 = /* @__PURE__ */ __toESM$1(require_picocolors(), 1);
69200
- const DEBUG_PATH = "/__debug";
69201
- const DEFAULT_INSPECTOR_PORT = 9229;
69202
- /**
69203
- * Gets the inspector port option that should be passed to Miniflare based on the user's plugin config
69204
- */
69205
- async function getInputInspectorPort(ctx, viteServer) {
69206
- if (ctx.resolvedPluginConfig.inspectorPort === void 0 || ctx.resolvedPluginConfig.inspectorPort === 0) {
69207
- const resolvedInspectorPort = await ctx.getResolvedInspectorPort();
69208
- if (resolvedInspectorPort !== null) return resolvedInspectorPort;
69209
- }
69210
- const inputInspectorPort = ctx.resolvedPluginConfig.inspectorPort ?? await getFirstAvailablePort(DEFAULT_INSPECTOR_PORT);
69211
- if (ctx.resolvedPluginConfig.inspectorPort === void 0 && inputInspectorPort !== DEFAULT_INSPECTOR_PORT) viteServer.config.logger.warn(import_picocolors$3.default.dim(`Default inspector port ${DEFAULT_INSPECTOR_PORT} not available, using ${inputInspectorPort} instead\n`));
69212
- return inputInspectorPort;
69213
- }
69214
- function getFirstAvailablePort(start) {
69215
- return getPorts({ port: portNumbers(start, 65535) });
69216
- }
69217
- /**
69218
- * Modifies the URL printing logic to also include a URL that developers can use to open DevTools to debug their Worker(s)
69219
- */
69220
- function addDebugToVitePrintUrls(server) {
69221
- const originalPrintUrls = server.printUrls;
69222
- server.printUrls = () => {
69223
- originalPrintUrls();
69224
- const localUrl = server.resolvedUrls?.local[0];
69225
- if (localUrl) {
69226
- const { protocol, hostname: hostname$1, port } = new URL(localUrl);
69227
- const colorDebugUrl = (url$2) => import_picocolors$3.default.dim(import_picocolors$3.default.yellow(url$2.replace(/:(\d+)\//, (_, portNumber) => `:${import_picocolors$3.default.bold(portNumber)}/`)));
69228
- server.config.logger.info(` ${import_picocolors$3.default.green("➜")} ${import_picocolors$3.default.bold("Debug")}: ${colorDebugUrl(`${protocol}//${hostname$1}:${port}${DEBUG_PATH}`)}`);
69229
- }
69230
- };
69231
- }
69232
- /**
69233
- * Generate HTML that comprises a single script that:
69234
- * - redirects the page to the DevTools for debugging the first available Worker
69235
- * - opens tabs to the DevTools for all the remaining workers if any
69236
- *
69237
- * Note: this works based on the Miniflare inspector proxy logic (where Workers are available via
69238
- * their names)
69239
- */
69240
- function getDebugPathHtml(workerNames, inspectorPort) {
69241
- assert2(workerNames.length >= 1, "no workers present to debug");
69242
- const workerDevtoolsUrls = workerNames.map((workerName) => {
69243
- const localhost = `localhost:${inspectorPort}/${workerName}`;
69244
- return `https://devtools.devprod.cloudflare.dev/js_app?${new URLSearchParams({
69245
- theme: "systemPreferred",
69246
- debugger: "true",
69247
- ws: localhost,
69248
- domain: workerName
69249
- })}`;
69250
- });
69251
- return `
69252
- <script>
69253
- const workerUrls = ${JSON.stringify(workerDevtoolsUrls)};
69254
- const [firstUrl, ...rest] = workerUrls;
69255
- for (const workerUrl of rest) {
69256
- // open new tabs for the devtools of the various workers
69257
- window.open(workerUrl);
69258
- }
69259
- // redirect the current tab to the devtools of the first worker
69260
- window.location.replace(firstUrl);
69261
- <\/script>
69262
- `;
69263
- }
69264
-
69265
- //#endregion
69266
- //#region src/plugins/debug.ts
69267
- /**
69268
- * Plugin to provide a `/__debug` path for debugging Workers
69269
- */
69270
- const debugPlugin = createPlugin("debug", (ctx) => {
69271
- return {
69272
- enforce: "pre",
69273
- configureServer(viteDevServer) {
69274
- assertIsNotPreview(ctx);
69275
- if (!!process.env.VSCODE_INSPECTOR_OPTIONS) return;
69276
- if (ctx.resolvedPluginConfig.type === "workers" && ctx.resolvedPluginConfig.inspectorPort !== false) addDebugToVitePrintUrls(viteDevServer);
69277
- const workerNames = ctx.resolvedPluginConfig.type === "workers" ? [...ctx.resolvedPluginConfig.environmentNameToWorkerMap.values()].map((worker) => worker.config.name) : [];
69278
- viteDevServer.middlewares.use(DEBUG_PATH, async (_, res, next) => {
69279
- const resolvedInspectorPort = await ctx.getResolvedInspectorPort();
69280
- if (resolvedInspectorPort) {
69281
- const html = getDebugPathHtml(workerNames, resolvedInspectorPort);
69282
- res.setHeader("Content-Type", "text/html");
69283
- res.end(html);
69284
- } else next();
69285
- });
69286
- },
69287
- async configurePreviewServer(vitePreviewServer) {
69288
- assertIsPreview(ctx);
69289
- if (!!process.env.VSCODE_INSPECTOR_OPTIONS) return;
69290
- if (ctx.resolvedPluginConfig.workers.length >= 1 && ctx.resolvedPluginConfig.inspectorPort !== false) addDebugToVitePrintUrls(vitePreviewServer);
69291
- const workerNames = ctx.resolvedPluginConfig.workers.map((worker) => {
69292
- assert2(worker.config.name, "Expected the Worker to have a name");
69293
- return worker.config.name;
69294
- });
69295
- vitePreviewServer.middlewares.use(DEBUG_PATH, async (_, res, next) => {
69296
- const resolvedInspectorPort = await ctx.getResolvedInspectorPort();
69297
- if (resolvedInspectorPort) {
69298
- const html = getDebugPathHtml(workerNames, resolvedInspectorPort);
69299
- res.setHeader("Content-Type", "text/html");
69300
- res.end(html);
69301
- } else next();
69302
- });
69303
- }
69304
- };
69305
- });
69306
-
69307
68996
  //#endregion
69308
68997
  //#region ../cli/dist/colors.mjs
69309
68998
  const { white, gray, dim, hidden, bold, cyanBright, bgCyan } = source_default;
@@ -72007,11 +71696,11 @@ var getQueryString = /* @__PURE__ */ __name$2((params) => {
72007
71696
  }, "getQueryString");
72008
71697
  var getUrl = /* @__PURE__ */ __name$2((config$2, options) => {
72009
71698
  const encoder$1 = config$2.ENCODE_PATH || encodeURI;
72010
- const path$2 = options.url.replace("{api-version}", config$2.VERSION).replace(/{(.*?)}/g, (substring, group) => {
71699
+ const path3 = options.url.replace("{api-version}", config$2.VERSION).replace(/{(.*?)}/g, (substring, group) => {
72011
71700
  if (options.path?.hasOwnProperty(group)) return encoder$1(String(options.path[group]));
72012
71701
  return substring;
72013
71702
  });
72014
- const url$2 = `${config$2.BASE}${path$2}`;
71703
+ const url$2 = `${config$2.BASE}${path3}`;
72015
71704
  if (options.query) return `${url$2}${getQueryString(options.query)}`;
72016
71705
  return url$2;
72017
71706
  }, "getUrl");
@@ -73995,7 +73684,7 @@ async function checkExposedPorts(dockerPath, options) {
73995
73684
  if (await dockerImageInspect(dockerPath, {
73996
73685
  imageTag: options.image_tag,
73997
73686
  formatString: "{{ len .Config.ExposedPorts }}"
73998
- }) === "0") throw new UserError(`The container "${options.class_name}" does not expose any ports. In your Dockerfile, please expose any ports you intend to connect to.
73687
+ }) === "0") throw new UserError(`The container "${options.image_name === void 0 ? options.class_name : `${options.class_name}.${options.image_name}`}" does not expose any ports. In your Dockerfile, please expose any ports you intend to connect to.
73999
73688
  For additional information please see: https://developers.cloudflare.com/containers/local-dev/#exposing-ports.
74000
73689
  `, { telemetryMessage: false });
74001
73690
  }
@@ -74025,31 +73714,6 @@ function resolveDockerHost(dockerPath) {
74025
73714
  return process.platform === "win32" ? "//./pipe/docker_engine" : "unix:///var/run/docker.sock";
74026
73715
  }
74027
73716
  __name$2(resolveDockerHost, "resolveDockerHost");
74028
- async function getImageRepoTags(dockerPath, imageTag) {
74029
- try {
74030
- return (await dockerImageInspect(dockerPath, {
74031
- imageTag,
74032
- formatString: "{{ range .RepoTags }}{{ . }}\n{{ end }}"
74033
- })).split("\n").filter((tag) => tag.trim() !== "");
74034
- } catch {
74035
- return [];
74036
- }
74037
- }
74038
- __name$2(getImageRepoTags, "getImageRepoTags");
74039
- async function cleanupDuplicateImageTags(dockerPath, imageTag) {
74040
- try {
74041
- const repoTags = await getImageRepoTags(dockerPath, imageTag);
74042
- const currentBuildId = getImageTag(imageTag);
74043
- const tagsToRemove = repoTags.filter((tag) => tag.startsWith("cloudflare-dev") && getImageTag(tag) !== currentBuildId);
74044
- if (tagsToRemove.length > 0) runDockerCmdWithOutput(dockerPath, ["rmi", ...tagsToRemove]);
74045
- } catch {}
74046
- }
74047
- __name$2(cleanupDuplicateImageTags, "cleanupDuplicateImageTags");
74048
- function getImageTag(imageTag) {
74049
- const tagSeparatorIndex = imageTag.lastIndexOf(":");
74050
- return tagSeparatorIndex === -1 ? void 0 : imageTag.slice(tagSeparatorIndex + 1);
74051
- }
74052
- __name$2(getImageTag, "getImageTag");
74053
73717
  var DEFAULT_CONTAINER_EGRESS_INTERCEPTOR_IMAGE = "cloudflare/proxy-everything:3cb1195@sha256:0ef6716c52430096900b150d84a3302057d6cd2319dae7987128c85d0733e3c8";
74054
73718
  function getEgressInterceptorPlatform() {
74055
73719
  return process.env.MINIFLARE_CONTAINER_EGRESS_IMAGE_PLATFORM;
@@ -74088,6 +73752,7 @@ async function pullImage(dockerPath, options, logger2, complianceConfig) {
74088
73752
  options.image_uri,
74089
73753
  options.image_tag
74090
73754
  ]);
73755
+ return { aborted: aborted$2 };
74091
73756
  });
74092
73757
  return {
74093
73758
  abort: /* @__PURE__ */ __name$2(() => {
@@ -74138,17 +73803,25 @@ async function prepareContainerImagesForDev(args) {
74138
73803
  pull.abort();
74139
73804
  }, "abort")
74140
73805
  });
74141
- await pull.ready;
73806
+ const pullResult = await pull.ready;
73807
+ aborted$2 ||= pullResult.aborted;
74142
73808
  onContainerImagePreparationEnd({ containerOptions: options });
74143
73809
  }
74144
- if (!aborted$2) {
74145
- await cleanupDuplicateImageTags(dockerPath, options.image_tag);
74146
- await checkExposedPorts(dockerPath, options);
74147
- }
73810
+ if (aborted$2) break;
73811
+ await checkExposedPorts(dockerPath, options);
74148
73812
  }
74149
73813
  if (!aborted$2) await pullEgressInterceptorImage(dockerPath);
73814
+ return { aborted: aborted$2 };
74150
73815
  }
74151
73816
  __name$2(prepareContainerImagesForDev, "prepareContainerImagesForDev");
73817
+ function isCloudflareRegistryImage(image, complianceConfig) {
73818
+ let url$2;
73819
+ try {
73820
+ url$2 = new URL(`http://${image}`);
73821
+ } catch {}
73822
+ return url$2 === void 0 || !url$2.host.match(/[:.]/) && url$2.hostname !== "localhost" || url$2.hostname === getCloudflareContainerRegistry(complianceConfig);
73823
+ }
73824
+ __name$2(isCloudflareRegistryImage, "isCloudflareRegistryImage");
74152
73825
  function resolveImageName(accountId, image, complianceConfig) {
74153
73826
  let url$2;
74154
73827
  try {
@@ -74348,6 +74021,11 @@ async function getContainerAccount(accountId, complianceConfig) {
74348
74021
  return await fetchResult$1(complianceConfig ?? {}, `/accounts/${accountId}/containers/me`);
74349
74022
  }
74350
74023
  __name$2(getContainerAccount, "getContainerAccount");
74024
+ var MAX_DOCKER_REPOSITORY_NAME_LENGTH = 255;
74025
+ function normalizeContainerImageRepositoryName(value) {
74026
+ return value.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/[._-]+/g, (separators) => separators === "." || separators === "_" || separators === "__" || /^-+$/.test(separators) ? separators : "-").replace(/^[._-]+|[._-]+$/g, "").slice(0, MAX_DOCKER_REPOSITORY_NAME_LENGTH).replace(/[._-]+$/g, "") || "container";
74027
+ }
74028
+ __name$2(normalizeContainerImageRepositoryName, "normalizeContainerImageRepositoryName");
74351
74029
  function isDockerfileContainerConfig(container) {
74352
74030
  return "dockerfile" in container;
74353
74031
  }
@@ -74590,7 +74268,7 @@ async function buildAndMaybePush(args, pathToDocker, push, containerConfig, veri
74590
74268
  }
74591
74269
  __name$2(buildAndMaybePush, "buildAndMaybePush");
74592
74270
  async function buildContainerImage(containerConfig, pathToDocker, verifyDockerIsRunning) {
74593
- const localTag = `${getContainerImageRepositoryName(containerConfig)}:wrangler-${crypto$1.randomUUID()}`;
74271
+ const localTag = `${getContainerImageRepositoryName(containerConfig)}:wrangler-${crypto2.randomUUID()}`;
74594
74272
  logger$1.log("Building image", localTag);
74595
74273
  try {
74596
74274
  await (await startContainerBuild({
@@ -74728,6 +74406,157 @@ async function dockerBuild(dockerPath, options) {
74728
74406
  };
74729
74407
  }
74730
74408
  __name$2(dockerBuild, "dockerBuild");
74409
+ var BUILD_OUTPUT_IMAGE_NAMESPACE = "cloudflare-build";
74410
+ var DOCKER_REPOSITORY_NAME_LENGTH = 255;
74411
+ async function buildAndWriteContainerOutput(options) {
74412
+ const containers = Object.entries(options.containers);
74413
+ await cleanupPreviousBuildOutputImageTags({
74414
+ root: options.root,
74415
+ pathToDocker: options.pathToDocker
74416
+ });
74417
+ const buildId = createBuildId();
74418
+ const localTags = /* @__PURE__ */ new Set();
74419
+ try {
74420
+ const dockerfileCount = countDockerfiles(containers);
74421
+ if (dockerfileCount > 0) await verifyDockerInstalled({
74422
+ dockerPath: options.pathToDocker,
74423
+ operation: "building the project",
74424
+ imageNoun: dockerfileCount === 1 ? "the configured image" : "the configured images"
74425
+ });
74426
+ const outputConfigs = [];
74427
+ try {
74428
+ for (const [directoryName, config$2] of containers) outputConfigs.push({
74429
+ root: options.root,
74430
+ directoryName,
74431
+ config: await buildContainerOutputConfig({
74432
+ config: config$2,
74433
+ root: options.root,
74434
+ pathToDocker: options.pathToDocker,
74435
+ buildId,
74436
+ localTags
74437
+ })
74438
+ });
74439
+ } catch (error$2) {
74440
+ throwBuildError(error$2);
74441
+ }
74442
+ for (const outputConfig of outputConfigs) await writeContainerConfig(outputConfig);
74443
+ } catch (error$2) {
74444
+ await Promise.allSettled([cleanBuildOutputDir(options.root), cleanupBuiltImages(Array.from(localTags, (localTag) => ({ localTag })), options.pathToDocker)]);
74445
+ throw error$2;
74446
+ }
74447
+ }
74448
+ __name$2(buildAndWriteContainerOutput, "buildAndWriteContainerOutput");
74449
+ async function buildContainerOutputConfig(options) {
74450
+ if (options.config.schedulingPolicy === "durable-object") {
74451
+ if (options.config.images === void 0) return {
74452
+ ...options.config,
74453
+ images: void 0
74454
+ };
74455
+ const images = {};
74456
+ for (const [imageName, image2] of Object.entries(options.config.images)) images[imageName] = await buildContainerImage2({
74457
+ image: image2,
74458
+ repositoryName: `${options.config.name}-${imageName}`,
74459
+ root: options.root,
74460
+ pathToDocker: options.pathToDocker,
74461
+ buildId: options.buildId,
74462
+ localTags: options.localTags
74463
+ });
74464
+ return {
74465
+ ...options.config,
74466
+ images
74467
+ };
74468
+ }
74469
+ const image = await buildContainerImage2({
74470
+ image: options.config.image,
74471
+ repositoryName: options.config.name,
74472
+ root: options.root,
74473
+ pathToDocker: options.pathToDocker,
74474
+ buildId: options.buildId,
74475
+ localTags: options.localTags
74476
+ });
74477
+ return {
74478
+ ...options.config,
74479
+ image
74480
+ };
74481
+ }
74482
+ __name$2(buildContainerOutputConfig, "buildContainerOutputConfig");
74483
+ async function buildContainerImage2(options) {
74484
+ if ("reference" in options.image) return { reference: options.image.reference };
74485
+ const pathToDockerfile = path__default.resolve(options.root, options.image.dockerfile);
74486
+ const localTag = createBuildOutputImageTag({
74487
+ root: options.root,
74488
+ repositoryName: options.repositoryName,
74489
+ buildId: options.buildId
74490
+ });
74491
+ if (options.localTags.has(localTag)) throw new UserError(`Container image name ${JSON.stringify(options.repositoryName)} conflicts with another image after Docker name normalization.`, { telemetryMessage: "container build output image name conflict" });
74492
+ await (await startContainerBuild({
74493
+ build: {
74494
+ tag: localTag,
74495
+ pathToDockerfile,
74496
+ buildContext: options.image.buildContext === void 0 ? path__default.dirname(pathToDockerfile) : path__default.resolve(options.root, options.image.buildContext),
74497
+ args: options.image.buildVars,
74498
+ platform: "linux/amd64"
74499
+ },
74500
+ pathToDocker: options.pathToDocker,
74501
+ verifyDockerIsRunning: false
74502
+ })).ready;
74503
+ options.localTags.add(localTag);
74504
+ return { localReference: localTag };
74505
+ }
74506
+ __name$2(buildContainerImage2, "buildContainerImage");
74507
+ function createBuildOutputImageTag(options) {
74508
+ const repositoryPrefix = getBuildOutputImageRepositoryPrefix(options.root);
74509
+ const maxNameLength = DOCKER_REPOSITORY_NAME_LENGTH - repositoryPrefix.length;
74510
+ return `${repositoryPrefix}${normalizeContainerImageRepositoryName(options.repositoryName).slice(0, maxNameLength).replace(/[._-]+$/g, "")}:${options.buildId}`;
74511
+ }
74512
+ __name$2(createBuildOutputImageTag, "createBuildOutputImageTag");
74513
+ async function cleanupPreviousBuildOutputImageTags(options) {
74514
+ const repositoryPrefix = getBuildOutputImageRepositoryPrefix(options.root);
74515
+ let existingTags;
74516
+ try {
74517
+ const output = runDockerCmdWithOutput(options.pathToDocker, [
74518
+ "image",
74519
+ "ls",
74520
+ "--filter",
74521
+ `reference=${repositoryPrefix}*`,
74522
+ "--format",
74523
+ "{{.Repository}}:{{.Tag}}"
74524
+ ]);
74525
+ existingTags = output === "" ? [] : output.split(/\r?\n/);
74526
+ } catch {
74527
+ return;
74528
+ }
74529
+ if (existingTags.length > 0) await cleanupBuiltImages(existingTags.map((localTag) => ({ localTag })), options.pathToDocker);
74530
+ }
74531
+ __name$2(cleanupPreviousBuildOutputImageTags, "cleanupPreviousBuildOutputImageTags");
74532
+ function getBuildOutputImageRepositoryPrefix(root) {
74533
+ return `${BUILD_OUTPUT_IMAGE_NAMESPACE}/${shortHash(path__default.resolve(root))}/`;
74534
+ }
74535
+ __name$2(getBuildOutputImageRepositoryPrefix, "getBuildOutputImageRepositoryPrefix");
74536
+ function shortHash(value) {
74537
+ return crypto2.createHash("sha256").update(value).digest("hex").slice(0, 12);
74538
+ }
74539
+ __name$2(shortHash, "shortHash");
74540
+ function createBuildId() {
74541
+ return crypto2.randomUUID().replaceAll("-", "").slice(0, 12);
74542
+ }
74543
+ __name$2(createBuildId, "createBuildId");
74544
+ function countDockerfiles(containers) {
74545
+ let count = 0;
74546
+ for (const [, config$2] of containers) if (config$2.schedulingPolicy === "durable-object") count += Object.values(config$2.images ?? {}).filter((image) => "dockerfile" in image).length;
74547
+ else if ("dockerfile" in config$2.image) count++;
74548
+ return count;
74549
+ }
74550
+ __name$2(countDockerfiles, "countDockerfiles");
74551
+ function throwBuildError(error$2) {
74552
+ if (error$2 instanceof UserError) throw error$2;
74553
+ if (error$2 instanceof Error) throw new UserError(error$2.message, {
74554
+ cause: error$2,
74555
+ telemetryMessage: "container build image operation failed"
74556
+ });
74557
+ throw new UserError("An unknown error occurred", { telemetryMessage: "container build unknown error" });
74558
+ }
74559
+ __name$2(throwBuildError, "throwBuildError");
74731
74560
  var Diff = class {
74732
74561
  static {
74733
74562
  __name$2(this, "Diff");
@@ -74892,10 +74721,10 @@ var Diff = class {
74892
74721
  print(options = { contextLines: 3 }) {
74893
74722
  log$1(this.toString(options));
74894
74723
  }
74895
- #addToPath(path$2, added, removed, oldPosInc) {
74896
- const last = path$2.lastComponent;
74724
+ #addToPath(path3, added, removed, oldPosInc) {
74725
+ const last = path3.lastComponent;
74897
74726
  if (last && last.added === added && last.removed === removed) return {
74898
- oldPos: path$2.oldPos + oldPosInc,
74727
+ oldPos: path3.oldPos + oldPosInc,
74899
74728
  lastComponent: {
74900
74729
  count: last.count + 1,
74901
74730
  added,
@@ -74904,7 +74733,7 @@ var Diff = class {
74904
74733
  }
74905
74734
  };
74906
74735
  return {
74907
- oldPos: path$2.oldPos + oldPosInc,
74736
+ oldPos: path3.oldPos + oldPosInc,
74908
74737
  lastComponent: {
74909
74738
  count: 1,
74910
74739
  added,
@@ -75424,6 +75253,495 @@ var configRolloutStepsToAPI = /* @__PURE__ */ __name$2((rolloutSteps) => {
75424
75253
  return { steps: output };
75425
75254
  }
75426
75255
  }, "configRolloutStepsToAPI");
75256
+ var MAX_NAMED_IMAGE_SLUG_LENGTH = 255 - MF_DEV_CONTAINER_PREFIX.length - 1;
75257
+ var IMAGE_ID_HASH_LENGTH = 12;
75258
+ function getNamedContainerImageTag(className, imageName, containerBuildId) {
75259
+ const identity = `${className}\0${imageName}`;
75260
+ const classSlug = getDockerRepositorySlug(className);
75261
+ const imageSlug = getDockerRepositorySlug(imageName);
75262
+ const identityHash = createHash("sha256").update(identity).digest("hex").slice(0, IMAGE_ID_HASH_LENGTH);
75263
+ return getDevContainerImageName(`${`${classSlug || "container"}-${imageSlug || "image"}`.slice(0, MAX_NAMED_IMAGE_SLUG_LENGTH - identityHash.length - 1).replace(/-+$/g, "")}-${identityHash}`, containerBuildId);
75264
+ }
75265
+ __name$2(getNamedContainerImageTag, "getNamedContainerImageTag");
75266
+ function getDockerRepositorySlug(value) {
75267
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
75268
+ }
75269
+ __name$2(getDockerRepositorySlug, "getDockerRepositorySlug");
75270
+ function requireContainerBuildId(containerBuildId) {
75271
+ if (containerBuildId === void 0) throw new Error("Build ID should be set when a Container image requires preparation");
75272
+ return containerBuildId;
75273
+ }
75274
+ __name$2(requireContainerBuildId, "requireContainerBuildId");
75275
+ function createContainerDevPlan(options) {
75276
+ const { containers, exports: exports$1, containerBuildId, configPath } = options;
75277
+ if (!containers?.length) return;
75278
+ const containerOptions = [];
75279
+ const containerRuntimeOptions = /* @__PURE__ */ new Map();
75280
+ const durableObjectContainerAppsByName = new Map(getResolvedDurableObjectContainerApps(containers, exports$1).map((container) => [container.name, container]));
75281
+ for (const container of containers) {
75282
+ if (isDurableObjectContainerApp(container)) {
75283
+ const resolvedContainer = durableObjectContainerAppsByName.get(container.name);
75284
+ if (resolvedContainer === void 0) throw new Error(`Expected Durable Object-managed Container "${container.name}" to be resolved`);
75285
+ const className2 = resolvedContainer.class_name;
75286
+ const namedOptions = [];
75287
+ const baseDir$1 = configPath ? path__default.dirname(configPath) : process.cwd();
75288
+ for (const [imageName, image] of Object.entries(container.images ?? {})) {
75289
+ const imageTag2 = getNamedContainerImageTag(className2, imageName, requireContainerBuildId(containerBuildId));
75290
+ if (image.dockerfile !== void 0) {
75291
+ const dockerfile = path__default.resolve(baseDir$1, image.dockerfile);
75292
+ namedOptions.push({
75293
+ dockerfile,
75294
+ image_build_context: image.build_context === void 0 ? path__default.dirname(dockerfile) : path__default.resolve(baseDir$1, image.build_context),
75295
+ image_vars: image.build_vars,
75296
+ class_name: className2,
75297
+ image_name: imageName,
75298
+ image_tag: imageTag2
75299
+ });
75300
+ } else namedOptions.push({
75301
+ image_uri: image.image,
75302
+ class_name: className2,
75303
+ image_name: imageName,
75304
+ image_tag: imageTag2
75305
+ });
75306
+ }
75307
+ containerOptions.push(...namedOptions);
75308
+ const images = namedOptions.map(({ image_name, image_tag }) => ({
75309
+ name: image_name,
75310
+ image: image_tag
75311
+ }));
75312
+ containerRuntimeOptions.set(className2, images.length === 0 ? {} : { images });
75313
+ continue;
75314
+ }
75315
+ if (container.image === void 0) continue;
75316
+ const className = resolveContainerClassName(container, exports$1);
75317
+ if (className === void 0) continue;
75318
+ const imageTag = getDevContainerImageName(className, requireContainerBuildId(containerBuildId));
75319
+ containerRuntimeOptions.set(className, { imageName: imageTag });
75320
+ if (isDockerfile(container.image, configPath)) containerOptions.push({
75321
+ dockerfile: container.image,
75322
+ image_build_context: container.image_build_context ?? path__default.dirname(container.image),
75323
+ image_vars: container.image_vars,
75324
+ class_name: className,
75325
+ image_tag: imageTag
75326
+ });
75327
+ else containerOptions.push({
75328
+ image_uri: container.image,
75329
+ class_name: className,
75330
+ image_tag: imageTag
75331
+ });
75332
+ }
75333
+ return containerOptions.length === 0 && containerRuntimeOptions.size === 0 ? void 0 : {
75334
+ containerOptions,
75335
+ containerRuntimeOptions
75336
+ };
75337
+ }
75338
+ __name$2(createContainerDevPlan, "createContainerDevPlan");
75339
+
75340
+ //#endregion
75341
+ //#region src/build-output-containers.ts
75342
+ /**
75343
+ * Build and write the Container portion of the Build Output Specification.
75344
+ *
75345
+ * @param resolvedPluginConfig - The resolved Cloudflare Vite plugin config.
75346
+ * @param root - The Vite project root and Build Output root.
75347
+ */
75348
+ async function buildOutputContainers(resolvedPluginConfig, root) {
75349
+ if (resolvedPluginConfig.type === "preview" || resolvedPluginConfig.experimental.newConfig?.cfBuildOutput !== true) return;
75350
+ const config$2 = resolvedPluginConfig.parsedNewConfig;
75351
+ if (config$2 === void 0) return;
75352
+ await buildAndWriteContainerOutput({
75353
+ containers: getContainerConfigExports(config$2),
75354
+ root,
75355
+ pathToDocker: getDockerPath$1()
75356
+ });
75357
+ }
75358
+
75359
+ //#endregion
75360
+ //#region src/dev-vars.ts
75361
+ /**
75362
+ * Gets any variables with which to augment the Worker config in preview mode.
75363
+ *
75364
+ * Calls `unstable_getVarsForDev` with the current Cloudflare environment to get local dev variables from the .dev.vars/.env/process.env.
75365
+ * When `secrets` is defined in the Worker config, only declared secrets are loaded.
75366
+ */
75367
+ function getLocalDevVarsForPreview(config$2, cloudflareEnv) {
75368
+ const dotDevDotVars = wrangler.unstable_getVarsForDev(config$2.configPath, void 0, {}, cloudflareEnv, false, config$2.secrets);
75369
+ const dotDevDotVarsEntries = Array.from(Object.entries(dotDevDotVars));
75370
+ if (dotDevDotVarsEntries.length > 0) return dotDevDotVarsEntries.map(([key, { value }]) => {
75371
+ return `${key}=${quoteForDotenv(value?.toString() ?? "")}\n`;
75372
+ }).join("");
75373
+ }
75374
+ /**
75375
+ * Quote a value so that wrangler reads it back unchanged when it parses
75376
+ * `dist/<env>/.dev.vars` at preview time.
75377
+ *
75378
+ * Strategy: To quote the string pick the first quote character that does not
75379
+ * appear in the value (single → backtick → double). Throw if a value cannot
75380
+ * be losslessly serialized rather than silently corrupting it.
75381
+ *
75382
+ * @internal exported for tests
75383
+ */
75384
+ function quoteForDotenv(value) {
75385
+ if (!value.includes("'")) return `'${value}'`;
75386
+ if (!value.includes("`")) return `\`${value}\``;
75387
+ if (!value.includes("\"") && !/[\\\n\r]/.test(value)) return `"${value}"`;
75388
+ throw new Error("Unable to serialize value to .dev.vars: contains every supported quote character or unsafe escape sequence.");
75389
+ }
75390
+ /**
75391
+ * Returns `true` if the `changedFile` matches a `.dev.vars` or `.env` file.
75392
+ */
75393
+ function hasLocalDevVarsFileChanged({ configPaths, cloudflareEnv }, changedFilePath) {
75394
+ return [...configPaths].some((configPath) => {
75395
+ const configDir = path.dirname(configPath);
75396
+ return [
75397
+ ".dev.vars",
75398
+ ".env",
75399
+ ...cloudflareEnv ? [`.dev.vars.${cloudflareEnv}`, `.env.${cloudflareEnv}`] : []
75400
+ ].some((localDevFile) => changedFilePath === path.join(configDir, localDevFile));
75401
+ });
75402
+ }
75403
+
75404
+ //#endregion
75405
+ //#region src/vite-config.ts
75406
+ const NODE_BUILTIN_SET = new Set([...builtinModules, ...builtinModules.filter((m) => !m.startsWith("node:")).map((m) => `node:${m}`)]);
75407
+ function isOnlyNodeBuiltins(external) {
75408
+ if (!Array.isArray(external)) return false;
75409
+ return external.every((entry) => typeof entry === "string" && NODE_BUILTIN_SET.has(entry));
75410
+ }
75411
+ /**
75412
+ * Throws an error if Worker environments include configuration options that are incompatible with the plugin.
75413
+ */
75414
+ function validateWorkerEnvironmentOptions(resolvedPluginConfig, resolvedViteConfig) {
75415
+ const disallowedEnvironmentOptionsMap = /* @__PURE__ */ new Map();
75416
+ for (const environmentName of resolvedPluginConfig.environmentNameToWorkerMap.keys()) {
75417
+ const environmentOptions = resolvedViteConfig.environments[environmentName];
75418
+ assert2(environmentOptions, `Missing environment config for "${environmentName}"`);
75419
+ const { resolve: resolve$4 } = environmentOptions;
75420
+ const disallowedEnvironmentOptions = {};
75421
+ if ((resolve$4.external === true || Array.isArray(resolve$4.external) && resolve$4.external.length > 0) && !isOnlyNodeBuiltins(resolve$4.external)) disallowedEnvironmentOptions.resolveExternal = resolve$4.external;
75422
+ if (Object.keys(disallowedEnvironmentOptions).length) disallowedEnvironmentOptionsMap.set(environmentName, disallowedEnvironmentOptions);
75423
+ }
75424
+ if (disallowedEnvironmentOptionsMap.size) {
75425
+ const errorMessage = `The following environment options are incompatible with the Cloudflare Vite plugin:\n${[...disallowedEnvironmentOptionsMap].map(([environmentName, disallowedEnvironmentOptions]) => disallowedEnvironmentOptions.resolveExternal && ` - "${environmentName}" environment: \`resolve.external\`: ${JSON.stringify(disallowedEnvironmentOptions.resolveExternal)}\n`).join("")}To resolve this issue, avoid setting \`resolve.external\` in your Cloudflare Worker environments.\n`;
75426
+ throw new Error(errorMessage);
75427
+ }
75428
+ }
75429
+
75430
+ //#endregion
75431
+ //#region src/plugins/wrangler-watch-ignore.ts
75432
+ const WRANGLER_STATE_WATCH_IGNORE = "**/.wrangler/**";
75433
+ /**
75434
+ * Keep Miniflare persistence writes out of Vite's file watcher without
75435
+ * replacing any ignore patterns the user already configured.
75436
+ *
75437
+ * @param userIgnored - Existing `server.watch.ignored` patterns, if any
75438
+ * @returns Ignore patterns that always include `.wrangler` directories
75439
+ */
75440
+ function withWranglerStateIgnored(userIgnored) {
75441
+ if (userIgnored == null) return WRANGLER_STATE_WATCH_IGNORE;
75442
+ if (Array.isArray(userIgnored)) return [...userIgnored, WRANGLER_STATE_WATCH_IGNORE];
75443
+ return [userIgnored, WRANGLER_STATE_WATCH_IGNORE];
75444
+ }
75445
+ /**
75446
+ * Merge `.wrangler` into Vite's `server.watch` config.
75447
+ *
75448
+ * Returns `null` when the user disabled watching with `server.watch: null`,
75449
+ * so this plugin does not turn the watcher back on.
75450
+ *
75451
+ * @param userWatch - The user's `server.watch` value from Vite config
75452
+ * @returns `null` when watching is disabled, otherwise a watch object whose
75453
+ * `ignored` list includes `.wrangler` directories
75454
+ */
75455
+ function getServerWatchConfig(userWatch) {
75456
+ if (userWatch === null) return null;
75457
+ return {
75458
+ ...userWatch,
75459
+ ignored: withWranglerStateIgnored(userWatch?.ignored)
75460
+ };
75461
+ }
75462
+
75463
+ //#endregion
75464
+ //#region src/plugins/config.ts
75465
+ /**
75466
+ * Plugin to handle configuration and config file watching
75467
+ */
75468
+ const configPlugin = createPlugin("config", (ctx) => {
75469
+ return {
75470
+ config(userConfig, env$3) {
75471
+ if (ctx.resolvedPluginConfig.type === "preview") return { appType: "custom" };
75472
+ if (!ctx.hasShownWorkerConfigWarnings) {
75473
+ ctx.setHasShownWorkerConfigWarnings(true);
75474
+ const workerConfigWarnings = getWarningForWorkersConfigs(ctx.resolvedPluginConfig.rawConfigs);
75475
+ if (workerConfigWarnings) console.warn(workerConfigWarnings);
75476
+ }
75477
+ return {
75478
+ appType: "custom",
75479
+ server: {
75480
+ allowedHosts: getAllowedHosts(ctx.getTunnelHostnames(), userConfig.server?.allowedHosts),
75481
+ watch: getServerWatchConfig(userConfig.server?.watch),
75482
+ fs: { deny: [...[
75483
+ ".env",
75484
+ ".env.*",
75485
+ ".npmrc",
75486
+ ".yarnrc",
75487
+ ".yarnrc.yml",
75488
+ "*.{crt,pem,key,pfx,p12,p8,jks}",
75489
+ "**/.git/**",
75490
+ ".dev.vars",
75491
+ ".dev.vars.*",
75492
+ "**/.wrangler/**"
75493
+ ], ...Array.from(ctx.resolvedPluginConfig.configPaths, (configPath) => normalizePath(configPath))] }
75494
+ },
75495
+ environments: getEnvironmentsConfig(ctx, userConfig, env$3.mode),
75496
+ builder: { buildApp: userConfig.builder?.buildApp ?? createBuildApp(ctx.resolvedPluginConfig) }
75497
+ };
75498
+ },
75499
+ async configResolved(resolvedViteConfig) {
75500
+ ctx.setResolvedViteConfig(resolvedViteConfig);
75501
+ if (ctx.resolvedPluginConfig.type === "preview") return;
75502
+ validateWorkerEnvironmentOptions(ctx.resolvedPluginConfig, ctx.resolvedViteConfig);
75503
+ if (ctx.resolvedPluginConfig.experimental.newConfig?.cfBuildOutput) {
75504
+ forceBuildOutputDirs(ctx.resolvedPluginConfig, ctx.resolvedViteConfig);
75505
+ if (ctx.resolvedViteConfig.command === "build") await cleanBuildOutputDir(ctx.resolvedViteConfig.root);
75506
+ }
75507
+ },
75508
+ buildStart() {
75509
+ ctx.setHasShownWorkerConfigWarnings(false);
75510
+ },
75511
+ configureServer(viteDevServer) {
75512
+ let restartInFlight = false;
75513
+ const configChangedHandler = async (changedFilePath) => {
75514
+ assertIsNotPreview(ctx);
75515
+ if (restartInFlight) return;
75516
+ if (ctx.resolvedPluginConfig.configPaths.has(changedFilePath) || hasLocalDevVarsFileChanged(ctx.resolvedPluginConfig, changedFilePath) || hasAssetsConfigChanged(ctx.resolvedPluginConfig, ctx.resolvedViteConfig, changedFilePath)) {
75517
+ debuglog("Config changed: " + changedFilePath);
75518
+ restartInFlight = true;
75519
+ debuglog("Restarting dev server and aborting previous setup");
75520
+ try {
75521
+ await viteDevServer.restart();
75522
+ } finally {
75523
+ restartInFlight = false;
75524
+ }
75525
+ }
75526
+ };
75527
+ viteDevServer.watcher.on("change", configChangedHandler);
75528
+ },
75529
+ buildApp: {
75530
+ order: "post",
75531
+ async handler(builder) {
75532
+ if (ctx.resolvedPluginConfig.type !== "workers") return;
75533
+ const workerEnvironments = [...ctx.resolvedPluginConfig.environmentNameToWorkerMap.entries()].filter(([_, worker]) => !resolveDevOnly(worker.devOnly)).map(([environmentName]) => {
75534
+ const environment = builder.environments[environmentName];
75535
+ assert2(environment, `"${environmentName}" environment not found`);
75536
+ return environment;
75537
+ });
75538
+ await Promise.all(workerEnvironments.filter((environment) => !environment.isBuilt).map((environment) => builder.build(environment)));
75539
+ const { entryWorkerEnvironmentName } = ctx.resolvedPluginConfig;
75540
+ const entryWorkerEnvironment = builder.environments[entryWorkerEnvironmentName];
75541
+ assert2(entryWorkerEnvironment, `No "${entryWorkerEnvironmentName}" environment`);
75542
+ const cfBuildOutput = ctx.resolvedPluginConfig.experimental.newConfig?.cfBuildOutput === true;
75543
+ if (entryWorkerEnvironment.isBuilt) {
75544
+ if (!builder.environments.client?.isBuilt && !cfBuildOutput) removeAssetsField(path.resolve(builder.config.root, entryWorkerEnvironment.config.build.outDir));
75545
+ } else {
75546
+ const clientEnvironment = builder.environments.client;
75547
+ assert2(clientEnvironment, "No \"client\" environment");
75548
+ if (!clientEnvironment.isBuilt) throw new Error("If `assetsOnly` is set to `true`, the client environment must be built");
75549
+ if (cfBuildOutput) {
75550
+ const entryWorkerNewConfig = ctx.getWorkerNewConfig(entryWorkerEnvironmentName);
75551
+ assert2(entryWorkerNewConfig, `No config found for "${entryWorkerEnvironmentName}" environment`);
75552
+ await writeWorkerConfig({
75553
+ root: builder.config.root,
75554
+ config: entryWorkerNewConfig
75555
+ });
75556
+ } else {
75557
+ const entryWorkerConfig = ctx.getWorkerConfig(entryWorkerEnvironmentName);
75558
+ assert2(entryWorkerConfig, `No config found for "${entryWorkerEnvironmentName}" environment`);
75559
+ const outputConfig = {
75560
+ ...entryWorkerConfig,
75561
+ main: void 0,
75562
+ assets: {
75563
+ ...entryWorkerConfig.assets,
75564
+ directory: ".",
75565
+ binding: void 0
75566
+ }
75567
+ };
75568
+ if (outputConfig.unsafe && Object.keys(outputConfig.unsafe).length === 0) outputConfig.unsafe = void 0;
75569
+ fs$1.writeFileSync(path.resolve(builder.config.root, clientEnvironment.config.build.outDir, "wrangler.json"), JSON.stringify(outputConfig));
75570
+ writeDeployConfig(ctx.resolvedPluginConfig, ctx.resolvedViteConfig, true);
75571
+ }
75572
+ }
75573
+ await buildOutputContainers(ctx.resolvedPluginConfig, builder.config.root);
75574
+ }
75575
+ }
75576
+ };
75577
+ });
75578
+ /**
75579
+ * Generates the environment configuration for all Worker environments.
75580
+ */
75581
+ function getEnvironmentsConfig(ctx, userConfig, mode) {
75582
+ assertIsNotPreview(ctx);
75583
+ if (!ctx.resolvedPluginConfig.environmentNameToWorkerMap.size) return;
75584
+ return {
75585
+ ...Object.fromEntries([...ctx.resolvedPluginConfig.environmentNameToWorkerMap].flatMap(([environmentName, worker]) => {
75586
+ const childEnvironmentNames = ctx.resolvedPluginConfig.environmentNameToChildEnvironmentNamesMap.get(environmentName) ?? [];
75587
+ const sharedOptions = {
75588
+ workerConfig: worker.config,
75589
+ userConfig,
75590
+ mode,
75591
+ hasNodeJsCompat: ctx.getNodeJsCompat(environmentName) !== void 0
75592
+ };
75593
+ const isEntryWorker = environmentName === ctx.resolvedPluginConfig.prerenderWorkerEnvironmentName || ctx.resolvedPluginConfig.type === "workers" && environmentName === ctx.resolvedPluginConfig.entryWorkerEnvironmentName;
75594
+ return [[environmentName, createCloudflareEnvironmentOptions({
75595
+ ...sharedOptions,
75596
+ environmentName,
75597
+ isEntryWorker,
75598
+ isParentEnvironment: true
75599
+ })], ...childEnvironmentNames.map((childEnvironmentName) => [childEnvironmentName, createCloudflareEnvironmentOptions({
75600
+ ...sharedOptions,
75601
+ environmentName: childEnvironmentName,
75602
+ isEntryWorker: false,
75603
+ isParentEnvironment: false
75604
+ })])];
75605
+ })),
75606
+ client: {
75607
+ build: { outDir: getOutputDirectory(userConfig, "client") },
75608
+ optimizeDeps: { exclude: [...cloudflareBuiltInModules] }
75609
+ }
75610
+ };
75611
+ }
75612
+ /**
75613
+ * When the Build Output Specification is enabled,
75614
+ * force every Worker environment's and the client environment's `build.outDir`
75615
+ * to the spec-mandated location.
75616
+ *
75617
+ * Runs after Vite's merge in `configResolved`, so it overrides any
75618
+ * user-supplied `build.outDir`
75619
+ */
75620
+ function forceBuildOutputDirs(resolvedPluginConfig, resolvedViteConfig) {
75621
+ if (!resolvedPluginConfig.experimental.newConfig?.cfBuildOutput) return;
75622
+ const { root } = resolvedViteConfig;
75623
+ if (resolvedPluginConfig.type === "workers") {
75624
+ const entryName = resolvedPluginConfig.entryWorkerEnvironmentName;
75625
+ const entryEnvironment = resolvedViteConfig.environments[entryName];
75626
+ if (entryEnvironment) entryEnvironment.build.outDir = getWorkerBundleDir(root);
75627
+ }
75628
+ const clientEnvironment = resolvedViteConfig.environments.client;
75629
+ if (clientEnvironment) clientEnvironment.build.outDir = getWorkerAssetsDir(root);
75630
+ }
75631
+ function getAllowedHosts(tunnelHostnames, userAllowedHosts) {
75632
+ if (tunnelHostnames.length === 0 || userAllowedHosts === true) return userAllowedHosts;
75633
+ return Array.from(new Set([...userAllowedHosts ?? [], ...tunnelHostnames]));
75634
+ }
75635
+
75636
+ //#endregion
75637
+ //#region src/debug.ts
75638
+ var import_picocolors$3 = /* @__PURE__ */ __toESM$1(require_picocolors(), 1);
75639
+ const DEBUG_PATH = "/__debug";
75640
+ const DEFAULT_INSPECTOR_PORT = 9229;
75641
+ /**
75642
+ * Gets the inspector port option that should be passed to Miniflare based on the user's plugin config
75643
+ */
75644
+ async function getInputInspectorPort(ctx, viteServer) {
75645
+ if (ctx.resolvedPluginConfig.inspectorPort === void 0 || ctx.resolvedPluginConfig.inspectorPort === 0) {
75646
+ const resolvedInspectorPort = await ctx.getResolvedInspectorPort();
75647
+ if (resolvedInspectorPort !== null) return resolvedInspectorPort;
75648
+ }
75649
+ const inputInspectorPort = ctx.resolvedPluginConfig.inspectorPort ?? await getFirstAvailablePort(DEFAULT_INSPECTOR_PORT);
75650
+ if (ctx.resolvedPluginConfig.inspectorPort === void 0 && inputInspectorPort !== DEFAULT_INSPECTOR_PORT) viteServer.config.logger.warn(import_picocolors$3.default.dim(`Default inspector port ${DEFAULT_INSPECTOR_PORT} not available, using ${inputInspectorPort} instead\n`));
75651
+ return inputInspectorPort;
75652
+ }
75653
+ function getFirstAvailablePort(start) {
75654
+ return getPorts({ port: portNumbers(start, 65535) });
75655
+ }
75656
+ /**
75657
+ * Modifies the URL printing logic to also include a URL that developers can use to open DevTools to debug their Worker(s)
75658
+ */
75659
+ function addDebugToVitePrintUrls(server) {
75660
+ const originalPrintUrls = server.printUrls;
75661
+ server.printUrls = () => {
75662
+ originalPrintUrls();
75663
+ const localUrl = server.resolvedUrls?.local[0];
75664
+ if (localUrl) {
75665
+ const { protocol, hostname: hostname$1, port } = new URL(localUrl);
75666
+ const colorDebugUrl = (url$2) => import_picocolors$3.default.dim(import_picocolors$3.default.yellow(url$2.replace(/:(\d+)\//, (_, portNumber) => `:${import_picocolors$3.default.bold(portNumber)}/`)));
75667
+ server.config.logger.info(` ${import_picocolors$3.default.green("➜")} ${import_picocolors$3.default.bold("Debug")}: ${colorDebugUrl(`${protocol}//${hostname$1}:${port}${DEBUG_PATH}`)}`);
75668
+ }
75669
+ };
75670
+ }
75671
+ /**
75672
+ * Generate HTML that comprises a single script that:
75673
+ * - redirects the page to the DevTools for debugging the first available Worker
75674
+ * - opens tabs to the DevTools for all the remaining workers if any
75675
+ *
75676
+ * Note: this works based on the Miniflare inspector proxy logic (where Workers are available via
75677
+ * their names)
75678
+ */
75679
+ function getDebugPathHtml(workerNames, inspectorPort) {
75680
+ assert2(workerNames.length >= 1, "no workers present to debug");
75681
+ const workerDevtoolsUrls = workerNames.map((workerName) => {
75682
+ const localhost = `localhost:${inspectorPort}/${workerName}`;
75683
+ return `https://devtools.devprod.cloudflare.dev/js_app?${new URLSearchParams({
75684
+ theme: "systemPreferred",
75685
+ debugger: "true",
75686
+ ws: localhost,
75687
+ domain: workerName
75688
+ })}`;
75689
+ });
75690
+ return `
75691
+ <script>
75692
+ const workerUrls = ${JSON.stringify(workerDevtoolsUrls)};
75693
+ const [firstUrl, ...rest] = workerUrls;
75694
+ for (const workerUrl of rest) {
75695
+ // open new tabs for the devtools of the various workers
75696
+ window.open(workerUrl);
75697
+ }
75698
+ // redirect the current tab to the devtools of the first worker
75699
+ window.location.replace(firstUrl);
75700
+ <\/script>
75701
+ `;
75702
+ }
75703
+
75704
+ //#endregion
75705
+ //#region src/plugins/debug.ts
75706
+ /**
75707
+ * Plugin to provide a `/__debug` path for debugging Workers
75708
+ */
75709
+ const debugPlugin = createPlugin("debug", (ctx) => {
75710
+ return {
75711
+ enforce: "pre",
75712
+ configureServer(viteDevServer) {
75713
+ assertIsNotPreview(ctx);
75714
+ if (!!process.env.VSCODE_INSPECTOR_OPTIONS) return;
75715
+ if (ctx.resolvedPluginConfig.type === "workers" && ctx.resolvedPluginConfig.inspectorPort !== false) addDebugToVitePrintUrls(viteDevServer);
75716
+ const workerNames = ctx.resolvedPluginConfig.type === "workers" ? [...ctx.resolvedPluginConfig.environmentNameToWorkerMap.values()].map((worker) => worker.config.name) : [];
75717
+ viteDevServer.middlewares.use(DEBUG_PATH, async (_, res, next) => {
75718
+ const resolvedInspectorPort = await ctx.getResolvedInspectorPort();
75719
+ if (resolvedInspectorPort) {
75720
+ const html = getDebugPathHtml(workerNames, resolvedInspectorPort);
75721
+ res.setHeader("Content-Type", "text/html");
75722
+ res.end(html);
75723
+ } else next();
75724
+ });
75725
+ },
75726
+ async configurePreviewServer(vitePreviewServer) {
75727
+ assertIsPreview(ctx);
75728
+ if (!!process.env.VSCODE_INSPECTOR_OPTIONS) return;
75729
+ if (ctx.resolvedPluginConfig.workers.length >= 1 && ctx.resolvedPluginConfig.inspectorPort !== false) addDebugToVitePrintUrls(vitePreviewServer);
75730
+ const workerNames = ctx.resolvedPluginConfig.workers.map((worker) => {
75731
+ assert2(worker.config.name, "Expected the Worker to have a name");
75732
+ return worker.config.name;
75733
+ });
75734
+ vitePreviewServer.middlewares.use(DEBUG_PATH, async (_, res, next) => {
75735
+ const resolvedInspectorPort = await ctx.getResolvedInspectorPort();
75736
+ if (resolvedInspectorPort) {
75737
+ const html = getDebugPathHtml(workerNames, resolvedInspectorPort);
75738
+ res.setHeader("Content-Type", "text/html");
75739
+ res.end(html);
75740
+ } else next();
75741
+ });
75742
+ }
75743
+ };
75744
+ });
75427
75745
 
75428
75746
  //#endregion
75429
75747
  //#region ../workers-shared/asset-worker/src/utils/rules-engine.ts
@@ -75458,6 +75776,54 @@ function configureContainerPull(accountId, apiToken, complianceConfig) {
75458
75776
  configureOpenAPIForContainerPull(accountId, apiToken, getCloudflareApiBaseUrl(complianceConfig ?? COMPLIANCE_REGION_CONFIG_UNKNOWN));
75459
75777
  }
75460
75778
  /**
75779
+ * Qualifies managed-registry image references with the selected Cloudflare
75780
+ * account before Docker pulls them.
75781
+ *
75782
+ * @param containerOptions - Planned Container images.
75783
+ * @param accountId - Cloudflare account ID that owns managed-registry images.
75784
+ * @param complianceConfig - Compliance configuration used to select the managed registry.
75785
+ * @returns Container options with managed-registry image references qualified.
75786
+ */
75787
+ function normalizeContainerImageUris(containerOptions, accountId, complianceConfig) {
75788
+ return containerOptions.map((option) => "image_uri" in option && isCloudflareRegistryImage(option.image_uri, complianceConfig) ? {
75789
+ ...option,
75790
+ image_uri: resolveImageName(accountId, option.image_uri, complianceConfig)
75791
+ } : option);
75792
+ }
75793
+ /**
75794
+ * Prepares each Worker's Container images with that Worker's registry account
75795
+ * and compliance settings.
75796
+ *
75797
+ * @param args - Planned images, Docker executable, and Vite logger.
75798
+ * @returns No value.
75799
+ */
75800
+ async function prepareContainerImagesForVite(args) {
75801
+ const optionsByWorkerConfig = /* @__PURE__ */ new Map();
75802
+ for (const { containerOptions, workerConfig } of args.containerTagToOptionsMap.values()) {
75803
+ const options = optionsByWorkerConfig.get(workerConfig) ?? [];
75804
+ options.push(containerOptions);
75805
+ optionsByWorkerConfig.set(workerConfig, options);
75806
+ }
75807
+ for (const [workerConfig, plannedOptions] of optionsByWorkerConfig) {
75808
+ let containerOptions = plannedOptions;
75809
+ if (containerOptions.some((options) => "image_uri" in options && isCloudflareRegistryImage(options.image_uri, workerConfig))) {
75810
+ const apiToken = process.env.CLOUDFLARE_API_TOKEN;
75811
+ const accountId = workerConfig.account_id ?? process.env.CLOUDFLARE_ACCOUNT_ID;
75812
+ if (!apiToken || !accountId) throw new UserError("To use images from the Cloudflare-managed registry with the Vite plugin, set the CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID environment variables.\nThe API token requires Containers:Edit and Workers Scripts:Edit permissions.\nAlternatively, use a Dockerfile that references the image via FROM.", { telemetryMessage: false });
75813
+ configureContainerPull(accountId, apiToken, workerConfig);
75814
+ containerOptions = normalizeContainerImageUris(containerOptions, accountId, workerConfig);
75815
+ }
75816
+ await prepareContainerImagesForDev({
75817
+ dockerPath: args.dockerPath,
75818
+ containerOptions,
75819
+ onContainerImagePreparationStart: () => {},
75820
+ onContainerImagePreparationEnd: () => {},
75821
+ logger: args.logger,
75822
+ complianceConfig: workerConfig
75823
+ });
75824
+ }
75825
+ }
75826
+ /**
75461
75827
  * Returns the path to the Docker executable as defined by the
75462
75828
  * `WRANGLER_DOCKER_BIN` environment variable, or the default value
75463
75829
  * `"docker"`
@@ -75465,36 +75831,6 @@ function configureContainerPull(accountId, apiToken, complianceConfig) {
75465
75831
  function getDockerPath() {
75466
75832
  return process.env["WRANGLER_DOCKER_BIN"] || "docker";
75467
75833
  }
75468
- /**
75469
- * @returns Container options suitable for building or pulling images,
75470
- * with image tag set to well-known dev format, or undefined if
75471
- * containers are not enabled or not configured. Containers that are
75472
- * configured but resolve to no Durable Object class are dropped, so the
75473
- * result may also be an empty array. Both mean there is nothing to build
75474
- * or pull, and callers treat them alike.
75475
- */
75476
- function getContainerOptions(options) {
75477
- const { containersConfig, exports: exports$1, containerBuildId, configPath } = options;
75478
- if (!containersConfig?.length) return;
75479
- return containersConfig.map((container) => {
75480
- if (isDurableObjectContainerApp(container) || container.image === void 0) return;
75481
- const className = resolveContainerClassName(container, exports$1);
75482
- if (className === void 0) return;
75483
- const image_tag = getDevContainerImageName(className, containerBuildId);
75484
- if (isDockerfile(container.image, configPath)) return {
75485
- dockerfile: container.image,
75486
- image_build_context: container.image_build_context ?? path__default.dirname(container.image),
75487
- image_vars: container.image_vars,
75488
- class_name: className,
75489
- image_tag
75490
- };
75491
- else return {
75492
- image_uri: container.image,
75493
- class_name: className,
75494
- image_tag
75495
- };
75496
- }).filter((container) => container !== void 0);
75497
- }
75498
75834
 
75499
75835
  //#endregion
75500
75836
  //#region ../workers-auth/dist/chunk-O6YSETKJ.mjs
@@ -81056,7 +81392,7 @@ __name(createWorkerUploadForm, "createWorkerUploadForm");
81056
81392
  //#endregion
81057
81393
  //#region ../remote-bindings/dist/index.mjs
81058
81394
  var import_undici = require_undici();
81059
- var version$2 = "0.0.26";
81395
+ var version$2 = "0.0.28";
81060
81396
  var NoDefaultValueProvided = class extends UserError {
81061
81397
  constructor() {
81062
81398
  super("This command cannot be run in a non-interactive context", { telemetryMessage: "remote bindings prompt default missing" });
@@ -81340,7 +81676,7 @@ async function getOrRegisterWorkersDevSubdomain(complianceConfig, account, abort
81340
81676
  } catch (error$2) {
81341
81677
  if (!(error$2 instanceof APIError) || error$2.code !== 10007) throw error$2;
81342
81678
  }
81343
- const subdomain = crypto$1.randomBytes(4).toString("hex");
81679
+ const subdomain = crypto2.randomBytes(4).toString("hex");
81344
81680
  return (await fetchResult(complianceConfig, account, resource, {
81345
81681
  method: "PUT",
81346
81682
  body: JSON.stringify({ subdomain })
@@ -84835,14 +85171,13 @@ async function getDevMiniflareOptions(ctx, viteDevServer) {
84835
85171
  if (worker.config.containers?.length && worker.config.dev.enable_containers) {
84836
85172
  containerEngine = resolveDockerHost(getDockerPath());
84837
85173
  containerBuildId = generateContainerBuildId();
84838
- const options = getContainerOptions({
84839
- containersConfig: worker.config.containers,
84840
- exports: worker.config.exports,
84841
- containerBuildId,
84842
- configPath: worker.config.configPath
84843
- });
84844
- for (const option of options ?? []) containerTagToOptionsMap.set(option.image_tag, option);
84845
85174
  }
85175
+ const containerPlan = worker.config.dev.enable_containers ? createContainerDevPlan({
85176
+ containers: worker.config.containers,
85177
+ exports: worker.config.exports,
85178
+ containerBuildId,
85179
+ configPath: worker.config.configPath
85180
+ }) : void 0;
84846
85181
  const miniflareWorkerOptions = wrangler.unstable_getMiniflareWorkerOptions({
84847
85182
  ...worker.config,
84848
85183
  assets: void 0
@@ -84850,6 +85185,10 @@ async function getDevMiniflareOptions(ctx, viteDevServer) {
84850
85185
  remoteProxyConnectionString: remoteProxySessionData?.session?.remoteProxyConnectionString,
84851
85186
  containerBuildId
84852
85187
  });
85188
+ for (const option of containerPlan?.containerOptions ?? []) containerTagToOptionsMap.set(option.image_tag, {
85189
+ containerOptions: option,
85190
+ workerConfig: worker.config
85191
+ });
84853
85192
  const { externalWorkers: externalWorkers$1 } = miniflareWorkerOptions;
84854
85193
  const workerOptions = miniflareWorkerOptions.workerOptions;
84855
85194
  if (workerOptions.browserRendering && getBrowserRenderingHeadfulFromEnv()) workerOptions.browserRendering.headful = true;
@@ -85054,18 +85393,21 @@ async function getPreviewMiniflareOptions(ctx, vitePreviewServer) {
85054
85393
  if (workerConfig.containers?.length && workerConfig.dev.enable_containers) {
85055
85394
  containerEngine = resolveDockerHost(getDockerPath());
85056
85395
  containerBuildId = generateContainerBuildId();
85057
- const options = getContainerOptions({
85058
- containersConfig: workerConfig.containers,
85059
- exports: workerConfig.exports,
85060
- containerBuildId,
85061
- configPath: workerConfig.configPath
85062
- });
85063
- for (const option of options ?? []) containerTagToOptionsMap.set(option.image_tag, option);
85064
85396
  }
85397
+ const containerPlan = workerConfig.dev.enable_containers ? createContainerDevPlan({
85398
+ containers: workerConfig.containers,
85399
+ exports: workerConfig.exports,
85400
+ containerBuildId,
85401
+ configPath: workerConfig.configPath
85402
+ }) : void 0;
85065
85403
  const miniflareWorkerOptions = wrangler.unstable_getMiniflareWorkerOptions(workerConfig, void 0, {
85066
85404
  remoteProxyConnectionString: remoteProxySessionData?.session?.remoteProxyConnectionString,
85067
85405
  containerBuildId
85068
85406
  });
85407
+ for (const option of containerPlan?.containerOptions ?? []) containerTagToOptionsMap.set(option.image_tag, {
85408
+ containerOptions: option,
85409
+ workerConfig
85410
+ });
85069
85411
  const { externalWorkers } = miniflareWorkerOptions;
85070
85412
  const { modulesRules,...sourcelessWorkerOptions } = miniflareWorkerOptions.workerOptions;
85071
85413
  const workerOptions = sourcelessWorkerOptions;
@@ -85338,19 +85680,10 @@ const devPlugin = createPlugin("dev", (ctx) => {
85338
85680
  }
85339
85681
  if (containerTagToOptionsMap.size) {
85340
85682
  viteDevServer.config.logger.info(import_picocolors$2.default.dim(import_picocolors$2.default.yellow("∷ Building container images for local development...\n")));
85341
- if ([...containerTagToOptionsMap.values()].some((opts) => "image_uri" in opts && new URL(`http://${opts.image_uri}`).hostname === getCloudflareContainerRegistry(ctx.entryWorkerConfig))) {
85342
- const apiToken = process.env.CLOUDFLARE_API_TOKEN;
85343
- const accountId = ctx.entryWorkerConfig?.account_id ?? process.env.CLOUDFLARE_ACCOUNT_ID;
85344
- if (!apiToken || !accountId) throw new UserError("To use images from the Cloudflare-managed registry with the Vite plugin, set the CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID environment variables.\nThe API token requires Containers:Edit and Workers Scripts:Edit permissions.\nAlternatively, use a Dockerfile that references the image via FROM.", { telemetryMessage: false });
85345
- configureContainerPull(accountId, apiToken, ctx.entryWorkerConfig);
85346
- }
85347
- await prepareContainerImagesForDev({
85683
+ await prepareContainerImagesForVite({
85348
85684
  dockerPath: getDockerPath(),
85349
- containerOptions: [...containerTagToOptionsMap.values()],
85350
- onContainerImagePreparationStart: () => {},
85351
- onContainerImagePreparationEnd: () => {},
85352
- logger: viteDevServer.config.logger,
85353
- complianceConfig: ctx.entryWorkerConfig
85685
+ containerTagToOptionsMap,
85686
+ logger: viteDevServer.config.logger
85354
85687
  });
85355
85688
  containerImageTags = new Set(containerTagToOptionsMap.keys());
85356
85689
  viteDevServer.config.logger.info(import_picocolors$2.default.dim(import_picocolors$2.default.yellow("\n⚡️ Containers successfully built. To rebuild your containers during development, restart the Vite dev server (r + enter).")));
@@ -85560,6 +85893,7 @@ const outputConfigPlugin = createPlugin("output-config", (ctx) => {
85560
85893
  function getOutputConfig({ inputWorkerConfig, workerOutputDirectory, resolvedViteConfig, entryFileName, includeAssets }) {
85561
85894
  const sourceConfigDirectory = inputWorkerConfig.configPath ? path.dirname(inputWorkerConfig.configPath) : resolvedViteConfig.root;
85562
85895
  const outputDirectory = path.resolve(resolvedViteConfig.root, workerOutputDirectory);
85896
+ const rebasePathToOutput = (sourcePath) => vite.normalizePath(path.relative(outputDirectory, path.resolve(sourceConfigDirectory, sourcePath)) || ".");
85563
85897
  return {
85564
85898
  ...inputWorkerConfig,
85565
85899
  main: entryFileName,
@@ -85572,10 +85906,23 @@ function getOutputConfig({ inputWorkerConfig, workerOutputDirectory, resolvedVit
85572
85906
  ...inputWorkerConfig.assets,
85573
85907
  directory: getAssetsDirectory(workerOutputDirectory, resolvedViteConfig)
85574
85908
  } : void 0,
85909
+ containers: inputWorkerConfig.containers?.map((container) => {
85910
+ if (container.images === void 0) return container;
85911
+ return {
85912
+ ...container,
85913
+ images: Object.fromEntries(Object.entries(container.images).map(([name, image]) => {
85914
+ if (image.dockerfile === void 0) return [name, image];
85915
+ return [name, {
85916
+ dockerfile: rebasePathToOutput(image.dockerfile),
85917
+ ...image.build_context === void 0 ? {} : { build_context: rebasePathToOutput(image.build_context) },
85918
+ ...image.build_vars === void 0 ? {} : { build_vars: image.build_vars }
85919
+ }];
85920
+ }))
85921
+ };
85922
+ }),
85575
85923
  d1_databases: inputWorkerConfig.d1_databases.map((database) => {
85576
85924
  const sourceMigrationsDir = database.migrations_dir ?? "migrations";
85577
- const sourceMigrationsPath = path.resolve(sourceConfigDirectory, sourceMigrationsDir);
85578
- const outputMigrationsDir = vite.normalizePath(path.relative(outputDirectory, sourceMigrationsPath) || ".");
85925
+ const outputMigrationsDir = rebasePathToOutput(sourceMigrationsDir);
85579
85926
  return {
85580
85927
  ...database,
85581
85928
  migrations_dir: outputMigrationsDir,
@@ -85674,19 +86021,10 @@ const previewPlugin = createPlugin("preview", (ctx) => {
85674
86021
  if (containerTagToOptionsMap.size) {
85675
86022
  const dockerPath = getDockerPath();
85676
86023
  vitePreviewServer.config.logger.info(import_picocolors$1.default.dim(import_picocolors$1.default.yellow("∷ Building container images for local preview...\n")));
85677
- if ([...containerTagToOptionsMap.values()].some((opts) => "image_uri" in opts && new URL(`http://${opts.image_uri}`).hostname === getCloudflareContainerRegistry(ctx.allWorkerConfigs[0]))) {
85678
- const apiToken = process.env.CLOUDFLARE_API_TOKEN;
85679
- const accountId = ctx.allWorkerConfigs[0]?.account_id ?? process.env.CLOUDFLARE_ACCOUNT_ID;
85680
- if (!apiToken || !accountId) throw new UserError("To use images from the Cloudflare-managed registry with the Vite plugin, set the CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID environment variables.\nThe API token requires Containers:Edit and Workers Scripts:Edit permissions.\nAlternatively, use a Dockerfile that references the image via FROM.", { telemetryMessage: false });
85681
- configureContainerPull(accountId, apiToken, ctx.allWorkerConfigs[0]);
85682
- }
85683
- await prepareContainerImagesForDev({
85684
- dockerPath: getDockerPath(),
85685
- containerOptions: [...containerTagToOptionsMap.values()],
85686
- onContainerImagePreparationStart: () => {},
85687
- onContainerImagePreparationEnd: () => {},
85688
- logger: vitePreviewServer.config.logger,
85689
- complianceConfig: ctx.allWorkerConfigs[0]
86024
+ await prepareContainerImagesForVite({
86025
+ dockerPath,
86026
+ containerTagToOptionsMap,
86027
+ logger: vitePreviewServer.config.logger
85690
86028
  });
85691
86029
  const containerImageTags = new Set(containerTagToOptionsMap.keys());
85692
86030
  vitePreviewServer.config.logger.info(import_picocolors$1.default.dim(import_picocolors$1.default.yellow("\n⚡️ Containers successfully built.\n")));