@stacksjs/ts-cloud 0.2.21 → 0.2.22

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.
@@ -2,6 +2,7 @@
2
2
  * Deployment Modules
3
3
  * High-level deployment functions for common AWS architectures
4
4
  */
5
+ export * from './site-target';
5
6
  export * from './static-site';
6
7
  export * from './static-site-external-dns';
7
8
  export * from './static-site-helper';
@@ -0,0 +1,41 @@
1
+ import type { CloudConfig, SiteConfig, SiteDeployTarget } from '@ts-cloud/core';
2
+ /**
3
+ * The three resolved deployment kinds for a site:
4
+ * - `'bucket'` — upload built `root` to object storage + CDN.
5
+ * - `'server-app'` — `server` + `start`: dynamic app as a systemd service
6
+ * behind the Caddy reverse proxy.
7
+ * - `'server-static'` — `server` + no `start` (has static `root`): a static
8
+ * site built and served on the box via Caddy `file_server`.
9
+ */
10
+ export type SiteDeployKind = 'bucket' | 'server-app' | 'server-static';
11
+ /**
12
+ * Resolve the explicit-or-inferred {@link SiteDeployTarget} for a site.
13
+ *
14
+ * Inference (backward compatible):
15
+ * 1. an explicit `site.deploy` always wins;
16
+ * 2. else if `start` is present → `'server'`;
17
+ * 3. else → `'bucket'`.
18
+ */
19
+ export declare function resolveSiteDeployTarget(site: SiteConfig): SiteDeployTarget;
20
+ /**
21
+ * Resolve the fine-grained {@link SiteDeployKind} for a site, combining the
22
+ * {@link resolveSiteDeployTarget} target with the presence of `start`.
23
+ *
24
+ * - `bucket` → `'bucket'`
25
+ * - `server` + `start` → `'server-app'`
26
+ * - `server` + no `start` → `'server-static'`
27
+ */
28
+ export declare function resolveSiteKind(site: SiteConfig): SiteDeployKind;
29
+ export interface DeploymentValidationResult {
30
+ errors: string[];
31
+ warnings: string[];
32
+ }
33
+ /**
34
+ * Validate the per-site deployment configuration up front, turning what used to
35
+ * be silent runtime failures (e.g. a `start` site with no compute server) into
36
+ * an explicit, actionable contract.
37
+ *
38
+ * Never throws — returns structured `{ errors, warnings }`. Callers should abort
39
+ * on any error and print warnings while continuing.
40
+ */
41
+ export declare function validateDeploymentConfig(config: CloudConfig): DeploymentValidationResult;
@@ -117,6 +117,11 @@ export declare class HetznerClient {
117
117
  firewall: HetznerFirewall;
118
118
  actions: HetznerAction[];
119
119
  }>;
120
+ /**
121
+ * Replace a firewall's rule set in place. Used to keep an existing (reused)
122
+ * firewall's rules in sync with the desired config without recreating it.
123
+ */
124
+ setFirewallRules(firewallId: number, rules: HetznerFirewallRule[]): Promise<HetznerAction[]>;
120
125
  applyFirewallToResources(firewallId: number, applyTo: Array<{
121
126
  type: 'server';
122
127
  server: number;
@@ -7,6 +7,27 @@ export interface HetznerDriverOptions {
7
7
  sshUser?: string;
8
8
  location?: string;
9
9
  client?: HetznerClient;
10
+ /**
11
+ * After the server reports `running`, block until SSH is reachable and
12
+ * cloud-init has finished before returning from provisioning. Disable in
13
+ * tests (or fast-path provisioning) to avoid real network waits.
14
+ * @default true
15
+ */
16
+ waitForBoot?: boolean;
17
+ /**
18
+ * Tunables for the SSH-readiness / cloud-init wait loops. Overridable so
19
+ * tests can use tiny intervals.
20
+ */
21
+ bootWait?: {
22
+ /** Delay between SSH probe attempts (ms). @default 5000 */
23
+ sshIntervalMs?: number;
24
+ /** Max time to wait for SSH to accept connections (ms). @default 300000 */
25
+ sshTimeoutMs?: number;
26
+ /** Delay between `cloud-init status` polls (ms). @default 5000 */
27
+ cloudInitIntervalMs?: number;
28
+ /** Max time to wait for cloud-init to finish (ms). @default 600000 */
29
+ cloudInitTimeoutMs?: number;
30
+ };
10
31
  }
11
32
  export declare class HetznerDriver implements CloudDriver {
12
33
  readonly name: "hetzner";
@@ -16,6 +37,8 @@ export declare class HetznerDriver implements CloudDriver {
16
37
  private sshPublicKeyPath;
17
38
  private sshUser;
18
39
  private location;
40
+ private waitForBoot;
41
+ private bootWait;
19
42
  constructor(options?: HetznerDriverOptions);
20
43
  provisionComputeInfrastructure(options: ProvisionComputeOptions): Promise<ComputeStackOutputs>;
21
44
  getComputeOutputs(options: ProvisionComputeOptions): Promise<ComputeStackOutputs>;
@@ -29,6 +52,39 @@ export declare class HetznerDriver implements CloudDriver {
29
52
  * denied (publickey)" because the server has no authorized keys.
30
53
  */
31
54
  private ensureSshKey;
55
+ /** getServer that returns null instead of throwing when the server is gone. */
56
+ private tryGetServer;
57
+ /**
58
+ * Look up an existing ts-cloud server for this project/environment by labels
59
+ * (falling back to name match). Used for idempotency when local state is
60
+ * missing, so re-running deploy doesn't spin up a duplicate server.
61
+ */
62
+ private findExistingServer;
63
+ /**
64
+ * Idempotent firewall: reuse an existing firewall with the same name rather
65
+ * than creating a duplicate on every deploy. When found, its rules are
66
+ * updated to the desired set so config changes (new ports) still apply.
67
+ */
68
+ private ensureFirewall;
69
+ /**
70
+ * Collect the upstream app ports that must be reachable when no reverse proxy
71
+ * is fronting traffic (raw-port deploys). Drops 80/443 (handled separately).
72
+ */
73
+ private collectUpstreamPorts;
74
+ private sleep;
75
+ /**
76
+ * Probe SSH (a trivial `true` over the connection) with backoff until the box
77
+ * accepts connections. A freshly booted server refuses SSH for a few seconds
78
+ * while sshd starts; without this, the very next deploy command races it and
79
+ * fails with "Connection refused".
80
+ */
81
+ private waitForSshReady;
82
+ /**
83
+ * Block until cloud-init finishes (`cloud-init status --wait`). cloud-init is
84
+ * what installs the runtime + Caddy; deploying before it completes leaves the
85
+ * release pointing at a half-provisioned box (missing `bun`, no Caddy).
86
+ */
87
+ private waitForCloudInit;
32
88
  private outputsFromState;
33
89
  private sshBaseArgs;
34
90
  private scpToHost;
@@ -3,6 +3,6 @@ export { AwsDriver } from './aws/driver';
3
3
  export { HetznerDriver } from './hetzner/driver';
4
4
  export { HetznerClient, resolveHetznerApiToken } from './hetzner/client';
5
5
  export { generateUbuntuAppCloudInit, wrapCloudInitUserData } from './hetzner/cloud-init';
6
- export { buildCaddyfile } from './shared/caddyfile';
7
- export { buildAwsArtifactFetch, buildLocalArtifactFetch, buildSiteDeployScript, resolveExecStart, } from './shared/deploy-script';
6
+ export { buildCaddyfile, buildCaddyfileFromProxy, isOnDemandDomain, proxyConfigFromSites, resolveCaddyfile, staticSiteServerRoot, } from './shared/caddyfile';
7
+ export { buildAwsArtifactFetch, buildLocalArtifactFetch, buildSiteDeployScript, buildStaticSiteDeployScript, resolveExecStart, } from './shared/deploy-script';
8
8
  export { deployAllComputeSites, deploySiteRelease } from './shared/compute-deploy';
@@ -1,6 +1,46 @@
1
- import type { SiteConfig } from '@ts-cloud/core';
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;
2
38
  /**
3
39
  * Build a Caddyfile from site configs. Sites sharing a domain are grouped;
4
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.
5
45
  */
6
46
  export declare function buildCaddyfile(sites: Record<string, SiteConfig>): string | undefined;
@@ -20,6 +20,8 @@ export interface DeployAllSitesOptions {
20
20
  logger?: ComputeDeployLogger;
21
21
  }
22
22
  /**
23
- * Deploy every site that declares a `start` command.
23
+ * Deploy every site that targets the compute server — both dynamic apps
24
+ * (`server` + `start`, run as systemd services) and static sites (`server`
25
+ * without `start`, served by Caddy `file_server`). Bucket sites are skipped.
24
26
  */
25
27
  export declare function deployAllComputeSites(options: DeployAllSitesOptions): Promise<boolean>;
@@ -27,5 +27,25 @@ export interface BuildSiteDeployScriptOptions {
27
27
  * Build the remote shell commands that install/refresh a site on a compute target.
28
28
  */
29
29
  export declare function buildSiteDeployScript(options: BuildSiteDeployScriptOptions): string[];
30
+ export interface BuildStaticSiteDeployScriptOptions {
31
+ siteName: string;
32
+ /** How the remote host obtains the release tarball */
33
+ artifactFetch: string[];
34
+ /** Target directory served by Caddy `file_server`. Default `/var/www/<site>`. */
35
+ appDir?: string;
36
+ /**
37
+ * Commands run inside `appDir` after extraction — e.g. build the docs/blog on
38
+ * the box itself (`bun install`, `bun run docs:build`) when the tarball ships
39
+ * source rather than a pre-built site.
40
+ */
41
+ preStartCommands?: string[];
42
+ }
43
+ /**
44
+ * Build the remote shell commands that install/refresh a STATIC site on a
45
+ * compute target. Unlike {@link buildSiteDeployScript}, there is no systemd
46
+ * service — the extracted files are served directly by Caddy `file_server`
47
+ * (Caddy is reloaded separately when the Caddyfile changes).
48
+ */
49
+ export declare function buildStaticSiteDeployScript(options: BuildStaticSiteDeployScriptOptions): string[];
30
50
  export declare function buildAwsArtifactFetch(bucket: string, key: string, region: string, siteName: string): string[];
31
51
  export declare function buildLocalArtifactFetch(localPath: string, siteName: string): string[];
package/dist/index.d.ts CHANGED
@@ -7,9 +7,9 @@ export type { AWSRequestOptions, AWSClientConfig, AWSError, AWSCredentials as AW
7
7
  export { createObjectStorageClient, providerEndpoint, resolveObjectStorage, } from './object-storage';
8
8
  export type { ObjectStorageConfig, ObjectStorageCredentials, ObjectStorageProvider, ResolvedObjectStorage, } from './object-storage';
9
9
  export * from './ssl';
10
- export { deployStaticSite, deployStaticSiteFull, uploadStaticFiles, invalidateCache, deleteStaticSite, generateStaticSiteTemplate, deployStaticSiteWithExternalDns, deployStaticSiteWithExternalDnsFull, generateExternalDnsStaticSiteTemplate, deploySite, } from './deploy';
11
- export type { StaticSiteConfig, DeployResult, UploadOptions, ExternalDnsStaticSiteConfig, ExternalDnsDeployResult, DeploySiteConfig, DeploySiteResult, StaticSiteDnsProvider, } from './deploy';
12
- export { createCloudDriver, CloudDriverFactory, cloudDrivers, AwsDriver, HetznerDriver, HetznerClient, resolveHetznerApiToken, generateUbuntuAppCloudInit, wrapCloudInitUserData, buildCaddyfile, buildSiteDeployScript, resolveExecStart, deployAllComputeSites, deploySiteRelease, } from './drivers';
10
+ export { deployStaticSite, deployStaticSiteFull, uploadStaticFiles, invalidateCache, deleteStaticSite, generateStaticSiteTemplate, deployStaticSiteWithExternalDns, deployStaticSiteWithExternalDnsFull, generateExternalDnsStaticSiteTemplate, deploySite, resolveSiteDeployTarget, resolveSiteKind, validateDeploymentConfig, } from './deploy';
11
+ export type { StaticSiteConfig, DeployResult, UploadOptions, ExternalDnsStaticSiteConfig, ExternalDnsDeployResult, DeploySiteConfig, DeploySiteResult, StaticSiteDnsProvider, SiteDeployKind, DeploymentValidationResult, } from './deploy';
12
+ export { createCloudDriver, CloudDriverFactory, cloudDrivers, AwsDriver, HetznerDriver, HetznerClient, resolveHetznerApiToken, generateUbuntuAppCloudInit, wrapCloudInitUserData, buildCaddyfile, buildCaddyfileFromProxy, isOnDemandDomain, proxyConfigFromSites, resolveCaddyfile, staticSiteServerRoot, buildSiteDeployScript, buildStaticSiteDeployScript, resolveExecStart, deployAllComputeSites, deploySiteRelease, } from './drivers';
13
13
  export type { CreateCloudDriverOptions } from './drivers/factory';
14
14
  export { createDnsProvider, detectDnsProvider, DnsProviderFactory, dnsProviders, PorkbunProvider, GoDaddyProvider, Route53Provider, UnifiedDnsValidator, createPorkbunValidator, createGoDaddyValidator, createRoute53Validator, } from './dns';
15
15
  export type { DnsProvider, DnsProviderConfig, DnsRecord, DnsRecordType, DnsRecordResult, CreateRecordResult, DeleteRecordResult, ListRecordsResult, } from './dns';