@stacksjs/ts-cloud 0.2.23 → 0.2.25

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.js CHANGED
@@ -81449,204 +81449,6 @@ import { homedir as homedir7 } from "node:os";
81449
81449
  import { join as join13 } from "node:path";
81450
81450
  import { execSync } from "node:child_process";
81451
81451
 
81452
- // src/drivers/shared/caddyfile.ts
81453
- function isOnDemandDomain(domain) {
81454
- return domain === "*" || domain.includes("*");
81455
- }
81456
- function isStaticApp(app) {
81457
- return typeof app.root === "string" && app.root.length > 0;
81458
- }
81459
- function isProxyApp(app) {
81460
- return typeof app.port === "number";
81461
- }
81462
- function staticSiteServerRoot(name) {
81463
- return `/var/www/${name}`;
81464
- }
81465
- function normalizeOnDemandTls(onDemandTls) {
81466
- if (!onDemandTls)
81467
- return;
81468
- if (onDemandTls === true)
81469
- return {};
81470
- return onDemandTls;
81471
- }
81472
- function wrapInHandle(body, path, indent) {
81473
- const inner = body.split(`
81474
- `).map((line) => ` ${line}`).join(`
81475
- `);
81476
- return `${indent}handle ${path} {
81477
- ${inner}
81478
- ${indent}}`;
81479
- }
81480
- function renderUpstreamBlock(app, indent) {
81481
- const host = app.upstreamHost || "localhost";
81482
- const upstream = `${host}:${app.port}`;
81483
- const directives = app.reverseProxyDirectives ?? [];
81484
- const proxyLine = directives.length === 0 ? `${indent}reverse_proxy ${upstream}` : [
81485
- `${indent}reverse_proxy ${upstream} {`,
81486
- ...directives.map((d) => `${indent} ${d}`),
81487
- `${indent}}`
81488
- ].join(`
81489
- `);
81490
- const isCatchAll = !app.path || app.path === "/";
81491
- if (isCatchAll)
81492
- return proxyLine;
81493
- return wrapInHandle(proxyLine, app.path, indent);
81494
- }
81495
- function renderStaticBlock(app, indent) {
81496
- const root = app.root;
81497
- const lines = [`${indent}root * ${root}`];
81498
- if (app.cache?.enabled) {
81499
- const maxAge = app.cache.maxAge ?? 3600;
81500
- lines.push(`${indent}header Cache-Control "public, max-age=${maxAge}"`);
81501
- }
81502
- if (app.spa) {
81503
- lines.push(`${indent}try_files {path} /index.html`);
81504
- } else if (app.pathRewriteStyle === "flat") {
81505
- lines.push(`${indent}try_files {path} {path}.html {path}/index.html`);
81506
- } else {
81507
- lines.push(`${indent}try_files {path} {path}/index.html {path}.html`);
81508
- }
81509
- lines.push(`${indent}file_server`);
81510
- const body = lines.join(`
81511
- `);
81512
- const isCatchAll = !app.path || app.path === "/";
81513
- if (isCatchAll)
81514
- return body;
81515
- return wrapInHandle(body, app.path, indent);
81516
- }
81517
- function renderAppBlock(app, indent) {
81518
- return isStaticApp(app) ? renderStaticBlock(app, indent) : renderUpstreamBlock(app, indent);
81519
- }
81520
- function groupAppsByDomains(apps) {
81521
- const groups = new Map;
81522
- for (const app of apps) {
81523
- const domains = [...app.domains].filter(Boolean);
81524
- if (domains.length === 0)
81525
- continue;
81526
- const key = [...domains].sort().join(" ");
81527
- const group = groups.get(key) ?? { domains, apps: [] };
81528
- group.apps.push(app);
81529
- groups.set(key, group);
81530
- }
81531
- return [...groups.values()];
81532
- }
81533
- function sortAppsByPath(apps) {
81534
- return [...apps].sort((a, b) => {
81535
- const aCatchAll = !a.path || a.path === "/";
81536
- const bCatchAll = !b.path || b.path === "/";
81537
- if (aCatchAll && !bCatchAll)
81538
- return 1;
81539
- if (!aCatchAll && bCatchAll)
81540
- return -1;
81541
- return (b.path?.length ?? 0) - (a.path?.length ?? 0);
81542
- });
81543
- }
81544
- function buildCaddyfileFromProxy(proxy) {
81545
- if (proxy.raw && proxy.raw.trim())
81546
- return proxy.raw.trim();
81547
- const apps = (proxy.apps ?? []).filter((app) => app.domains.length > 0 && (isProxyApp(app) || isStaticApp(app)));
81548
- if (apps.length === 0)
81549
- return;
81550
- const onDemand = normalizeOnDemandTls(proxy.onDemandTls);
81551
- const hasOnDemandDomain = apps.some((app) => app.domains.some(isOnDemandDomain));
81552
- const globalLines = [];
81553
- if (proxy.email)
81554
- globalLines.push(`email ${proxy.email}`);
81555
- if (proxy.staging)
81556
- globalLines.push("acme_ca https://acme-staging-v02.api.letsencrypt.org/directory");
81557
- if (onDemand) {
81558
- if (onDemand.ask || onDemand.interval || onDemand.burst != null) {
81559
- const inner = [];
81560
- if (onDemand.ask)
81561
- inner.push(` ask ${onDemand.ask}`);
81562
- if (onDemand.interval || onDemand.burst != null) {
81563
- const rl = [];
81564
- if (onDemand.interval)
81565
- rl.push(` interval ${onDemand.interval}`);
81566
- if (onDemand.burst != null)
81567
- rl.push(` burst ${onDemand.burst}`);
81568
- inner.push(` rate_limit {
81569
- ${rl.join(`
81570
- `)}
81571
- }`);
81572
- }
81573
- globalLines.push(`on_demand_tls {
81574
- ${inner.join(`
81575
- `)}
81576
- }`);
81577
- } else {
81578
- globalLines.push(`on_demand_tls {
81579
- }`);
81580
- }
81581
- }
81582
- for (const directive of proxy.globalDirectives ?? [])
81583
- globalLines.push(directive);
81584
- const blocks = [];
81585
- if (globalLines.length > 0)
81586
- blocks.push(`{
81587
- ${globalLines.map((line) => ` ${line}`).join(`
81588
- `)}
81589
- }`);
81590
- for (const group of groupAppsByDomains(apps)) {
81591
- const sorted = sortAppsByPath(group.apps);
81592
- const body = sorted.map((app) => renderAppBlock(app, " ")).join(`
81593
- `);
81594
- const needsOnDemand = onDemand && group.domains.some(isOnDemandDomain);
81595
- const tlsBlock = needsOnDemand ? `
81596
- tls {
81597
- on_demand
81598
- }` : "";
81599
- blocks.push(`${group.domains.join(", ")} {
81600
- ${body}${tlsBlock}
81601
- }`);
81602
- }
81603
- if (hasOnDemandDomain && !onDemand) {
81604
- blocks.unshift(`# WARNING: wildcard/catch-all domain present but on_demand_tls is not enabled.
81605
- ` + "# Caddy cannot provision TLS for these hosts. Set compute.proxy.onDemandTls.");
81606
- }
81607
- return blocks.join(`
81608
-
81609
- `);
81610
- }
81611
- function proxyConfigFromSites(sites) {
81612
- const apps = [];
81613
- for (const [name, site] of Object.entries(sites)) {
81614
- if (typeof site.domain !== "string" || !site.domain)
81615
- continue;
81616
- if (typeof site.port === "number" && site.deploy !== "bucket") {
81617
- apps.push({
81618
- name,
81619
- domains: [site.domain],
81620
- port: site.port,
81621
- path: site.path
81622
- });
81623
- } else if (resolveSiteKind(site) === "server-static") {
81624
- apps.push({
81625
- name,
81626
- domains: [site.domain],
81627
- root: staticSiteServerRoot(name),
81628
- path: site.path,
81629
- spa: site.spa,
81630
- pathRewriteStyle: site.pathRewriteStyle,
81631
- cache: site.cache
81632
- });
81633
- }
81634
- }
81635
- return { apps };
81636
- }
81637
- function resolveCaddyfile(sites, proxy) {
81638
- if (proxy) {
81639
- if (proxy.raw && proxy.raw.trim())
81640
- return proxy.raw.trim();
81641
- const resolved = proxy.apps && proxy.apps.length > 0 ? proxy : { ...proxy, apps: proxyConfigFromSites(sites).apps };
81642
- return buildCaddyfileFromProxy(resolved);
81643
- }
81644
- return buildCaddyfileFromProxy(proxyConfigFromSites(sites));
81645
- }
81646
- function buildCaddyfile(sites) {
81647
- return buildCaddyfileFromProxy(proxyConfigFromSites(sites));
81648
- }
81649
-
81650
81452
  // src/drivers/hetzner/client.ts
81651
81453
  var DEFAULT_API_URL = "https://api.hetzner.cloud/v1";
81652
81454
 
@@ -81798,7 +81600,8 @@ function generateUbuntuAppCloudInit(options = {}) {
81798
81600
  runtimeVersion = "latest",
81799
81601
  systemPackages = [],
81800
81602
  database,
81801
- caddyfile
81603
+ caddyfile,
81604
+ rpxProvision
81802
81605
  } = options;
81803
81606
  const packages = new Set(systemPackages);
81804
81607
  if (database === "sqlite")
@@ -81890,6 +81693,13 @@ CADDY_CONFIG_EOF
81890
81693
  systemctl daemon-reload
81891
81694
  systemctl enable caddy
81892
81695
  systemctl start caddy
81696
+ `;
81697
+ }
81698
+ if (rpxProvision && rpxProvision.length > 0) {
81699
+ const body = rpxProvision[0] === "set -euo pipefail" ? rpxProvision.slice(1) : rpxProvision;
81700
+ script += `
81701
+ ${body.join(`
81702
+ `)}
81893
81703
  `;
81894
81704
  }
81895
81705
  script += `
@@ -81914,6 +81724,129 @@ runcmd:
81914
81724
  `;
81915
81725
  }
81916
81726
 
81727
+ // src/drivers/shared/rpx-gateway.ts
81728
+ var DEFAULT_RPX_CERTS_DIR = "/etc/rpx/certs";
81729
+ function normalizeRoutePath(path) {
81730
+ if (!path || path === "/")
81731
+ return;
81732
+ let p = `/${path}`.replace(/\/+/g, "/").replace(/\/+$/, "");
81733
+ if (!p.startsWith("/"))
81734
+ p = `/${p}`;
81735
+ return p === "" || p === "/" ? undefined : p;
81736
+ }
81737
+ function deriveRouteId(to, path) {
81738
+ const base = path ? `${to}${path}` : to;
81739
+ const cleaned = base.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 128);
81740
+ return cleaned.length > 0 ? cleaned : "rpx";
81741
+ }
81742
+ function buildRpxConfig(sites, options) {
81743
+ const wwwRoot = (options.wwwRoot ?? "/var/www").replace(/\/+$/, "");
81744
+ const certsDir = options.proxy.certsDir ?? DEFAULT_RPX_CERTS_DIR;
81745
+ const proxies = [];
81746
+ const domains = new Set;
81747
+ for (const [name, site] of Object.entries(sites)) {
81748
+ if (!site || !site.domain)
81749
+ continue;
81750
+ const kind = resolveSiteKind(site);
81751
+ if (kind === "bucket")
81752
+ continue;
81753
+ const path = normalizeRoutePath(site.path);
81754
+ const id = deriveRouteId(site.domain, path);
81755
+ if (kind === "server-app") {
81756
+ if (typeof site.port !== "number")
81757
+ continue;
81758
+ proxies.push({ to: site.domain, path, from: `localhost:${site.port}`, id });
81759
+ } else {
81760
+ proxies.push({
81761
+ to: site.domain,
81762
+ path,
81763
+ static: `${wwwRoot}/${name}`,
81764
+ cleanUrls: site.pathRewriteStyle !== "flat",
81765
+ spa: site.spa ?? false,
81766
+ id
81767
+ });
81768
+ }
81769
+ domains.add(site.domain);
81770
+ }
81771
+ proxies.sort((a, b) => {
81772
+ if (a.to !== b.to)
81773
+ return a.to.localeCompare(b.to);
81774
+ return (b.path?.length ?? 0) - (a.path?.length ?? 0);
81775
+ });
81776
+ const config6 = {
81777
+ proxies,
81778
+ productionCerts: { certsDir },
81779
+ https: true,
81780
+ hostsManagement: false,
81781
+ cleanup: { hosts: false, certs: false }
81782
+ };
81783
+ if (options.proxy.onDemandTls && domains.size > 0) {
81784
+ config6.onDemandTls = {
81785
+ enabled: true,
81786
+ allowedSuffixes: [...domains],
81787
+ email: options.proxy.onDemandTlsEmail,
81788
+ certsDir
81789
+ };
81790
+ }
81791
+ return config6;
81792
+ }
81793
+ function renderRpxLauncher(config6) {
81794
+ const json = JSON.stringify(config6, null, 2);
81795
+ return `// Generated by ts-cloud — rpx reverse-proxy gateway.
81796
+ // Routes are derived from the \`sites\` model on every \`buddy deploy\`.
81797
+ import { startProxies } from '@stacksjs/rpx'
81798
+
81799
+ const config = ${json} as const
81800
+
81801
+ await startProxies(config as any)
81802
+ `;
81803
+ }
81804
+ var RPX_DIR = "/etc/rpx";
81805
+ var RPX_LAUNCHER_PATH = "/etc/rpx/gateway.ts";
81806
+ var RPX_SERVICE_NAME = "rpx-gateway.service";
81807
+ function writeFileHeredoc(path, content, delimiter) {
81808
+ return [
81809
+ `cat > ${path} <<'${delimiter}'`,
81810
+ content,
81811
+ delimiter
81812
+ ];
81813
+ }
81814
+ function buildRpxProvisionScript(options) {
81815
+ const { config: config6, proxy } = options;
81816
+ const bunBin = options.bunBin ?? "/usr/local/bin/bun";
81817
+ const version2 = proxy.version ?? "latest";
81818
+ const certsDir = config6.productionCerts.certsDir;
81819
+ const launcher = renderRpxLauncher(config6);
81820
+ return [
81821
+ "set -euo pipefail",
81822
+ `mkdir -p ${RPX_DIR} ${certsDir}`,
81823
+ `${bunBin} add -g @stacksjs/rpx@${version2}`,
81824
+ ...writeFileHeredoc(RPX_LAUNCHER_PATH, launcher, "TS_CLOUD_RPX_EOF"),
81825
+ ...writeFileHeredoc(`/etc/systemd/system/${RPX_SERVICE_NAME}`, [
81826
+ "[Unit]",
81827
+ "Description=rpx reverse-proxy gateway (managed by ts-cloud)",
81828
+ "After=network.target network-online.target",
81829
+ "Wants=network-online.target",
81830
+ "",
81831
+ "[Service]",
81832
+ "Type=simple",
81833
+ `ExecStart=${bunBin} ${RPX_LAUNCHER_PATH}`,
81834
+ `Environment=BUN_INSTALL=/root/.bun`,
81835
+ "Restart=always",
81836
+ "RestartSec=5",
81837
+ "LimitNOFILE=1048576",
81838
+ "AmbientCapabilities=CAP_NET_BIND_SERVICE",
81839
+ "",
81840
+ "[Install]",
81841
+ "WantedBy=multi-user.target"
81842
+ ].join(`
81843
+ `), "TS_CLOUD_RPX_UNIT_EOF"),
81844
+ "systemctl daemon-reload",
81845
+ `systemctl enable ${RPX_SERVICE_NAME}`,
81846
+ `systemctl restart ${RPX_SERVICE_NAME}`
81847
+ ];
81848
+ }
81849
+
81917
81850
  // src/drivers/hetzner/firewall-rules.ts
81918
81851
  function buildHetznerFirewallRules(config6) {
81919
81852
  const openPorts = new Set([80, 443, ...config6.sitePorts]);
@@ -82049,14 +81982,18 @@ class HetznerDriver {
82049
81982
  return this.outputsFromState(rehydrated, alreadyRunning);
82050
81983
  }
82051
81984
  const sites = config6.sites || {};
82052
- const caddyfile = resolveCaddyfile(sites, compute.proxy);
82053
- const sitePorts = caddyfile ? [] : this.collectUpstreamPorts(sites, compute.proxy);
81985
+ const sitePorts = this.collectUpstreamPorts(sites);
81986
+ const rpxProvision = compute.proxy?.engine === "rpx" ? buildRpxProvisionScript({
81987
+ proxy: compute.proxy,
81988
+ config: buildRpxConfig(sites, { proxy: compute.proxy }),
81989
+ bunBin: compute.runtime === "node" || compute.runtime === "deno" ? undefined : "/usr/local/bin/bun"
81990
+ }) : undefined;
82054
81991
  const bootstrap = generateUbuntuAppCloudInit({
82055
81992
  runtime: compute.runtime || "bun",
82056
81993
  runtimeVersion: compute.runtimeVersion || "latest",
82057
81994
  systemPackages: compute.systemPackages,
82058
81995
  database: config6.infrastructure?.database,
82059
- caddyfile
81996
+ rpxProvision
82060
81997
  });
82061
81998
  const userData = wrapCloudInitUserData(bootstrap);
82062
81999
  const serverType = resolveHetznerServerType(compute.size);
@@ -82222,12 +82159,8 @@ class HetznerDriver {
82222
82159
  const { firewall } = await this.client.createFirewall({ name, labels, rules });
82223
82160
  return { firewall };
82224
82161
  }
82225
- collectUpstreamPorts(sites, proxy) {
82162
+ collectUpstreamPorts(sites) {
82226
82163
  const ports = new Set;
82227
- for (const app of proxy?.apps ?? []) {
82228
- if (typeof app.port === "number")
82229
- ports.add(app.port);
82230
- }
82231
82164
  for (const site of Object.values(sites)) {
82232
82165
  if (typeof site.port === "number")
82233
82166
  ports.add(site.port);
@@ -82548,6 +82481,48 @@ async function deployAllComputeSites(options) {
82548
82481
  }
82549
82482
  }
82550
82483
  }
82484
+ const reloaded = await reloadRpxGateway(options);
82485
+ if (!reloaded)
82486
+ return false;
82487
+ return true;
82488
+ }
82489
+ async function reloadRpxGateway(options) {
82490
+ const { config: config6, environment, driver, logger: logger4 = noopLogger } = options;
82491
+ const proxy = config6.infrastructure?.compute?.proxy;
82492
+ if (proxy?.engine !== "rpx")
82493
+ return true;
82494
+ const sites = config6.sites || {};
82495
+ const rpxConfig = buildRpxConfig(sites, { proxy });
82496
+ if (rpxConfig.proxies.length === 0) {
82497
+ logger4.warn("rpx gateway: no server sites with a domain to route — skipping gateway reload.");
82498
+ return true;
82499
+ }
82500
+ const targets = await driver.findComputeTargets({
82501
+ slug: config6.project.slug,
82502
+ environment,
82503
+ role: "app"
82504
+ });
82505
+ if (targets.length === 0) {
82506
+ logger4.warn("rpx gateway: no compute targets found — skipping gateway reload.");
82507
+ return true;
82508
+ }
82509
+ logger4.step(`Reloading rpx gateway with ${rpxConfig.proxies.length} route(s)...`);
82510
+ const script = buildRpxProvisionScript({ proxy, config: rpxConfig });
82511
+ const result = await driver.runRemoteDeploy({
82512
+ targets,
82513
+ commands: script,
82514
+ comment: `ts-cloud rpx gateway reload ${config6.project.slug}`,
82515
+ tags: {
82516
+ Project: config6.project.slug,
82517
+ Environment: environment,
82518
+ Role: "app"
82519
+ }
82520
+ });
82521
+ if (!result.success) {
82522
+ logger4.error(`rpx gateway reload failed: ${result.error || "unknown error"}`);
82523
+ return false;
82524
+ }
82525
+ logger4.success(`rpx gateway reloaded on ${result.instanceCount} target(s)`);
82551
82526
  return true;
82552
82527
  }
82553
82528
  // src/index.ts
@@ -82581,7 +82556,6 @@ export {
82581
82556
  suggestFlags,
82582
82557
  suggestCommand,
82583
82558
  storageAdvancedManager,
82584
- staticSiteServerRoot,
82585
82559
  staticSiteManager,
82586
82560
  stackDependencyManager,
82587
82561
  signRequestAsync,
@@ -82613,14 +82587,12 @@ export {
82613
82587
  resolveDeployBucketName,
82614
82588
  resolveCredentials,
82615
82589
  resolveCloudProvider,
82616
- resolveCaddyfile,
82617
82590
  requiresReplacement,
82618
82591
  replicaManager,
82619
82592
  remapKey,
82620
82593
  regionPairManager,
82621
82594
  quickHash,
82622
82595
  queueManagementManager,
82623
- proxyConfigFromSites,
82624
82596
  providerEndpoint,
82625
82597
  progressiveDeploymentManager,
82626
82598
  processInChunks,
@@ -82655,7 +82627,6 @@ export {
82655
82627
  keyMatchesFilters,
82656
82628
  isWebCryptoAvailable,
82657
82629
  isValidRegion,
82658
- isOnDemandDomain,
82659
82630
  isNodeCryptoAvailable,
82660
82631
  isLocalDevelopment,
82661
82632
  isLikelyTypo,
@@ -82808,8 +82779,6 @@ export {
82808
82779
  buildSiteDeployScript,
82809
82780
  buildOptimizationManager,
82810
82781
  buildCloudFormationTemplate,
82811
- buildCaddyfileFromProxy,
82812
- buildCaddyfile,
82813
82782
  bounceComplaintHandler,
82814
82783
  blueGreenManager,
82815
82784
  batchProcessingManager,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/ts-cloud",
3
3
  "type": "module",
4
- "version": "0.2.23",
4
+ "version": "0.2.25",
5
5
  "description": "A lightweight, performant infrastructure-as-code library and CLI for deploying both server-based (EC2) and serverless applications.",
6
6
  "author": "Chris Breuer <chris@stacksjs.com>",
7
7
  "license": "MIT",
@@ -89,8 +89,8 @@
89
89
  "test": "bun test"
90
90
  },
91
91
  "dependencies": {
92
- "@ts-cloud/aws-types": "0.2.23",
93
- "@ts-cloud/core": "0.2.23",
92
+ "@ts-cloud/aws-types": "0.2.25",
93
+ "@ts-cloud/core": "0.2.25",
94
94
  "@stacksjs/ts-xml": "^0.1.0"
95
95
  },
96
96
  "devDependencies": {
@@ -1,46 +0,0 @@
1
- import type { CaddyAppConfig, CaddyProxyConfig, SiteConfig } from '@ts-cloud/core';
2
- /** A domain is "on-demand" (needs lazy TLS) if it's a wildcard or bare catch-all. */
3
- export declare function isOnDemandDomain(domain: string): boolean;
4
- /**
5
- * The on-server install path a static site's `root` is shipped to. Mirrors the
6
- * release layout used by the systemd app deploy (`/var/www/<name>`), so a box
7
- * can host both proxied apps and file-served static sites side by side.
8
- */
9
- export declare function staticSiteServerRoot(name: string): string;
10
- /**
11
- * Build a complete Caddyfile from a typed {@link CaddyProxyConfig}.
12
- *
13
- * Produces:
14
- * - a global options block (ACME email, on-demand TLS `ask`, staging CA, extras);
15
- * - one site block per unique domain set, each performing host-based routing to
16
- * its upstream app(s);
17
- * - `tls { on_demand }` inside any block whose domains are wildcards/catch-all.
18
- *
19
- * Returns `undefined` when there's nothing to route (no apps, no raw).
20
- */
21
- export declare function buildCaddyfileFromProxy(proxy: CaddyProxyConfig): string | undefined;
22
- /**
23
- * Derive a {@link CaddyProxyConfig} from the legacy `sites` map: every site
24
- * that declares a `domain` + `port` becomes a Caddy app. Keeps single-app /
25
- * sites-driven deploys working without an explicit `compute.proxy` block.
26
- */
27
- export declare function proxyConfigFromSites(sites: Record<string, SiteConfig>): CaddyProxyConfig & {
28
- apps: CaddyAppConfig[];
29
- };
30
- /**
31
- * Resolve the final Caddyfile for a deploy. Prefers the typed `compute.proxy`
32
- * config (merging in `sites`-derived apps when `proxy.apps` is omitted), and
33
- * falls back to deriving everything from `sites`.
34
- *
35
- * Returns `undefined` when there's nothing to route.
36
- */
37
- export declare function resolveCaddyfile(sites: Record<string, SiteConfig>, proxy?: CaddyProxyConfig): string | undefined;
38
- /**
39
- * Build a Caddyfile from site configs. Sites sharing a domain are grouped;
40
- * explicit paths are ordered before catch-all routes.
41
- *
42
- * @deprecated Prefer {@link resolveCaddyfile} / {@link buildCaddyfileFromProxy},
43
- * which support multi-app host routing and on-demand TLS. Retained for
44
- * backward compatibility.
45
- */
46
- export declare function buildCaddyfile(sites: Record<string, SiteConfig>): string | undefined;