@ts-cloud/core 0.2.20 → 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.
Files changed (2) hide show
  1. package/dist/types.d.ts +234 -2
  2. package/package.json +2 -2
package/dist/types.d.ts CHANGED
@@ -689,6 +689,40 @@ export interface ResourceConditions {
689
689
  */
690
690
  condition?: (config: CloudConfig, env: EnvironmentType) => boolean;
691
691
  }
692
+ /**
693
+ * Where a {@link SiteConfig} is deployed.
694
+ *
695
+ * - `'bucket'` — the built `root` is uploaded to object storage (AWS S3 /
696
+ * Hetzner Object Storage) and served via a CDN (CloudFront on AWS). This is
697
+ * the classic static-site path.
698
+ * - `'server'` — the site lives on the environment's compute server (EC2 /
699
+ * Hetzner VM) behind the Caddy reverse proxy. A `'server'` site resolves to
700
+ * one of two kinds depending on whether it declares a `start` command:
701
+ * - `start` present → a **dynamic app** run as a systemd service and proxied
702
+ * by Caddy (`reverse_proxy`).
703
+ * - no `start` (but a static `root`) → a **static site built and served on the
704
+ * server** by Caddy `file_server`, optionally fronted by a CDN.
705
+ *
706
+ * @see resolveSiteDeployTarget for the default inference rules.
707
+ */
708
+ export type SiteDeployTarget = 'bucket' | 'server';
709
+ /**
710
+ * Per-site caching hint, applicable to either origin (bucket or server).
711
+ *
712
+ * For a `server`-served static site the values are emitted as `Cache-Control`
713
+ * headers inside the Caddy `file_server` block. `cdn` expresses the intent to
714
+ * place a CDN in front of the origin — on AWS this reuses the existing
715
+ * CloudFront machinery; on Hetzner (no native CDN) it's advisory only (put
716
+ * CloudFront / Cloudflare / bunny in front of the box yourself).
717
+ */
718
+ export interface SiteCacheConfig {
719
+ /** Emit cache-control headers / enable CDN caching for this site. */
720
+ enabled?: boolean;
721
+ /** `max-age` (seconds) used in the emitted `Cache-Control` header. */
722
+ maxAge?: number;
723
+ /** Front this origin with a CDN (CloudFront on AWS; advisory on Hetzner). */
724
+ cdn?: boolean;
725
+ }
692
726
  export interface SiteConfig {
693
727
  /**
694
728
  * Directory to deploy.
@@ -721,10 +755,50 @@ export interface SiteConfig {
721
755
  * content type and the URL rewrite function is disabled.
722
756
  */
723
757
  installScript?: string;
758
+ /**
759
+ * Explicit deployment target for this site.
760
+ *
761
+ * When omitted, the target is **inferred** for backward compatibility:
762
+ * - if `start` is present → `'server'`;
763
+ * - otherwise → `'bucket'`.
764
+ *
765
+ * An explicit value always wins over the inference. Combined with `start`,
766
+ * this resolves to one of three kinds (see {@link SiteDeployTarget}):
767
+ * - `'bucket'` → upload built `root` to object storage + CDN;
768
+ * - `'server'` + `start` → dynamic app as a systemd service behind Caddy;
769
+ * - `'server'` + no `start` (static `root`) → static site built and served
770
+ * **on the server** via Caddy `file_server`, with optional CDN caching.
771
+ *
772
+ * Set `deploy: 'server'` (without `start`) to build/serve docs or a blog on an
773
+ * existing compute box instead of a bucket. Set `deploy: 'bucket'` on a site
774
+ * that also declares `start` to force the classic static path.
775
+ */
776
+ deploy?: SiteDeployTarget;
777
+ /**
778
+ * Per-site caching hint, used for both bucket and server origins.
779
+ * For a server-served static site, emits `Cache-Control` headers in the Caddy
780
+ * `file_server` block; `cdn` expresses "front this origin with a CDN".
781
+ */
782
+ cache?: SiteCacheConfig;
783
+ /**
784
+ * Whether this site serves a single-page application (client-side routing).
785
+ * For a `server`-served static site, unmatched paths fall back to
786
+ * `index.html` (Caddy `try_files {path} /index.html`). Mirrors
787
+ * {@link StorageItemConfig.spa} for the bucket path.
788
+ */
789
+ spa?: boolean;
790
+ /**
791
+ * URL rewrite style for a `server`-served static site's extensionless URLs,
792
+ * mirroring {@link StorageItemConfig.pathRewriteStyle}:
793
+ * - `'directory'` (default): `/guide/get-started` → `/guide/get-started/index.html`
794
+ * - `'flat'`: `/guide/get-started` → `/guide/get-started.html`
795
+ */
796
+ pathRewriteStyle?: 'directory' | 'flat';
724
797
  /**
725
798
  * Command the systemd service runs (becomes ExecStart).
726
- * Presence of this field is the discriminator: set => SSR (deploy to EC2),
727
- * unset => static (deploy to S3+CloudFront).
799
+ * Presence of this field, in the absence of an explicit `deploy`, is the
800
+ * discriminator: set => `server` (deploy to compute as a systemd service),
801
+ * unset => `bucket` (deploy to object storage + CDN). See {@link deploy}.
728
802
  *
729
803
  * Example: 'bun run server.ts'
730
804
  */
@@ -739,6 +813,16 @@ export interface SiteConfig {
739
813
  * (`/var/www/<site>/.env`). Available as process.env.* inside the running app.
740
814
  */
741
815
  env?: Record<string, string>;
816
+ /**
817
+ * SSR only. Commands run on the server inside the app directory after the
818
+ * release tarball is extracted and the `.env` is written, but before the
819
+ * systemd service (re)starts. Use this to install runtime dependencies and/or
820
+ * produce build artifacts on the machine itself — so the release tarball can
821
+ * ship source only (no `node_modules`) and stays small.
822
+ *
823
+ * Example: ['bun install --frozen-lockfile', 'bun run build']
824
+ */
825
+ preStart?: string[];
742
826
  }
743
827
  export interface VpcConfig {
744
828
  cidr?: string;
@@ -1214,6 +1298,144 @@ export interface ContainerItemConfig {
1214
1298
  targetMemoryUtilization?: number;
1215
1299
  };
1216
1300
  }
1301
+ /**
1302
+ * A single upstream application fronted by the Caddy reverse proxy.
1303
+ *
1304
+ * Each app maps one or more request domains to a local upstream port, so a
1305
+ * single server can host several apps (e.g. a registry, a web app and a
1306
+ * tunnel server) behind one Caddy instance with host-based routing.
1307
+ */
1308
+ export interface CaddyAppConfig {
1309
+ /**
1310
+ * Optional human-readable name for the app (used only for Caddyfile
1311
+ * comments / readability). Defaults to the first domain.
1312
+ */
1313
+ name?: string;
1314
+ /**
1315
+ * Domains routed to this upstream. Multiple domains share one site block.
1316
+ *
1317
+ * - Explicit hostnames (e.g. `app.example.com`) get automatic Let's Encrypt
1318
+ * TLS via the HTTP-01 challenge.
1319
+ * - Wildcards (`*.tunnel.example.com`) or a bare `*` require on-demand TLS
1320
+ * (see `proxy.onDemandTls`) because Caddy can't pre-provision certs for an
1321
+ * unbounded set of hostnames.
1322
+ */
1323
+ domains: string[];
1324
+ /**
1325
+ * Local upstream port the app listens on (Caddy proxies to
1326
+ * `localhost:<port>`).
1327
+ *
1328
+ * Required for a reverse-proxy (dynamic) app. Omit for a static
1329
+ * (`file_server`) site, which is selected by setting {@link root} instead.
1330
+ */
1331
+ port?: number;
1332
+ /**
1333
+ * Serve a static site from this directory via Caddy `file_server` (the box is
1334
+ * the origin) instead of reverse-proxying to a port. Mutually exclusive with
1335
+ * {@link port}; when set, this app emits a `root * <root>` + `file_server`
1336
+ * block rather than a `reverse_proxy`.
1337
+ */
1338
+ root?: string;
1339
+ /**
1340
+ * Static `file_server` only. When true, unmatched paths fall back to
1341
+ * `index.html` (`try_files {path} /index.html`) for client-side SPA routing.
1342
+ */
1343
+ spa?: boolean;
1344
+ /**
1345
+ * Static `file_server` only. URL rewrite style for extensionless requests,
1346
+ * mirroring {@link StorageItemConfig.pathRewriteStyle}:
1347
+ * - `'directory'` (default): `/guide/get-started` → `/guide/get-started/index.html`
1348
+ * - `'flat'`: `/guide/get-started` → `/guide/get-started.html`
1349
+ */
1350
+ pathRewriteStyle?: 'directory' | 'flat';
1351
+ /**
1352
+ * Static `file_server` only. When set, emits a `Cache-Control` header for
1353
+ * served assets. `maxAge` (seconds) controls `max-age`; defaults to 3600 when
1354
+ * enabled without an explicit value.
1355
+ */
1356
+ cache?: SiteCacheConfig;
1357
+ /**
1358
+ * Upstream host. Defaults to `localhost`. Set this to proxy to another
1359
+ * machine / container on the private network.
1360
+ * @default 'localhost'
1361
+ */
1362
+ upstreamHost?: string;
1363
+ /**
1364
+ * Optional path prefix. When set, only requests matching this path are
1365
+ * routed to the upstream; several apps can then share a domain. Omit (or use
1366
+ * `/`) for a catch-all.
1367
+ */
1368
+ path?: string;
1369
+ /**
1370
+ * Extra raw directives placed inside this app's `reverse_proxy` block
1371
+ * (e.g. `header_up Host {host}`, `lb_policy round_robin`). Advanced escape
1372
+ * hatch — emitted verbatim.
1373
+ */
1374
+ reverseProxyDirectives?: string[];
1375
+ }
1376
+ /**
1377
+ * On-demand TLS configuration. Caddy obtains a certificate at the moment of
1378
+ * the first TLS handshake for a hostname, rather than ahead of time. Essential
1379
+ * for wildcard/tunnel domains where the full hostname set isn't known up front.
1380
+ *
1381
+ * @see https://caddyserver.com/docs/automatic-https#on-demand-tls
1382
+ */
1383
+ export interface CaddyOnDemandTlsConfig {
1384
+ /**
1385
+ * URL Caddy queries before issuing a certificate for an unknown host. Caddy
1386
+ * issues a cert only on a 2xx response, which prevents unbounded issuance.
1387
+ * Strongly recommended (Caddy refuses to start on-demand TLS without it in
1388
+ * most production setups).
1389
+ *
1390
+ * Example: `http://localhost:9007/check-domain`
1391
+ */
1392
+ ask?: string;
1393
+ /**
1394
+ * Rate limit: max certificate issuances allowed within `interval`.
1395
+ * @deprecated by Caddy upstream but still emitted when set.
1396
+ */
1397
+ burst?: number;
1398
+ /** Interval (Caddy duration, e.g. `1m`, `2h`) for the `burst` rate limit. */
1399
+ interval?: string;
1400
+ }
1401
+ /**
1402
+ * Typed reverse-proxy front for a compute server. Generates a `/etc/caddy/Caddyfile`
1403
+ * that performs host-based routing to one or more upstream apps with automatic
1404
+ * HTTPS (Let's Encrypt HTTP-01) and optional on-demand TLS.
1405
+ */
1406
+ export interface CaddyProxyConfig {
1407
+ /**
1408
+ * Apps fronted by the proxy. Each maps domain(s) → an upstream port.
1409
+ * When omitted, ts-cloud derives apps from `sites` that declare a `domain`
1410
+ * and `port`, so single-app deploys keep working without extra config.
1411
+ */
1412
+ apps?: CaddyAppConfig[];
1413
+ /**
1414
+ * Email used for the Let's Encrypt ACME account (recommended — receives
1415
+ * expiry warnings). Emitted in the Caddyfile global options block.
1416
+ */
1417
+ email?: string;
1418
+ /**
1419
+ * Enable on-demand TLS for wildcard/tunnel domains. Either `true` (use the
1420
+ * `ask` endpoint from this object) or a full config object.
1421
+ */
1422
+ onDemandTls?: boolean | CaddyOnDemandTlsConfig;
1423
+ /**
1424
+ * Use the Let's Encrypt **staging** CA (higher rate limits, untrusted certs)
1425
+ * — handy while iterating on a new deploy so you don't burn prod rate limits.
1426
+ */
1427
+ staging?: boolean;
1428
+ /**
1429
+ * Extra global directives placed in the Caddyfile global options block
1430
+ * (e.g. `admin off`, `servers { protocols h1 h2 h3 } `). Emitted verbatim.
1431
+ */
1432
+ globalDirectives?: string[];
1433
+ /**
1434
+ * Provide a fully pre-rendered Caddyfile, bypassing generation entirely.
1435
+ * Mutually exclusive with `apps` (raw wins). Escape hatch for advanced setups.
1436
+ */
1437
+ raw?: string;
1438
+ }
1217
1439
  export interface ComputeConfig {
1218
1440
  /**
1219
1441
  * Compute mode: 'server' for EC2, 'serverless' for Fargate/Lambda
@@ -1453,6 +1675,16 @@ export interface ComputeConfig {
1453
1675
  * Set to `true` only if you need traditional SSH access.
1454
1676
  */
1455
1677
  allowSsh?: boolean;
1678
+ /**
1679
+ * Reverse-proxy front (Caddy) for this server: host-based routing to one or
1680
+ * more upstream apps with automatic HTTPS and optional on-demand TLS.
1681
+ *
1682
+ * Lets a single server host multiple apps (e.g. a registry + web app +
1683
+ * tunnel server) behind :80/:443. When omitted, ts-cloud falls back to
1684
+ * deriving a proxy from `sites` that declare a `domain` + `port`, so
1685
+ * single-app deploys keep working unchanged.
1686
+ */
1687
+ proxy?: CaddyProxyConfig;
1456
1688
  }
1457
1689
  export interface DatabaseItemConfig {
1458
1690
  engine?: 'dynamodb' | 'postgres' | 'mysql';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ts-cloud/core",
3
- "version": "0.2.20",
3
+ "version": "0.2.22",
4
4
  "type": "module",
5
5
  "description": "Core CloudFormation generation library for ts-cloud",
6
6
  "author": "Chris Breuer <chris@stacksjs.com>",
@@ -31,7 +31,7 @@
31
31
  "typecheck": "tsc --noEmit"
32
32
  },
33
33
  "dependencies": {
34
- "@ts-cloud/aws-types": "0.2.20"
34
+ "@ts-cloud/aws-types": "0.2.22"
35
35
  },
36
36
  "devDependencies": {
37
37
  "typescript": "^5.9.3"