@stacksjs/ts-cloud 0.8.2 → 0.9.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/aws/index.js +3 -1
- package/dist/bin/cli.js +575 -565
- package/dist/bin/dashboard-server.js +334 -324
- package/dist/cdn/cloudflare-plan.d.ts +61 -0
- package/dist/cdn/cloudflare-rules.d.ts +77 -0
- package/dist/cdn/cloudflare-settings.d.ts +100 -0
- package/dist/cdn/cloudflare.d.ts +108 -0
- package/dist/cdn/cloudflare.test.d.ts +1 -0
- package/dist/cdn/index.d.ts +17 -0
- package/dist/cdn/origin-tls.d.ts +36 -0
- package/dist/{chunk-v0vpamnf.js → chunk-5nr95wpj.js} +3 -3
- package/dist/{chunk-gttvakpv.js → chunk-atbgd8wz.js} +163 -37
- package/dist/{chunk-x6137d59.js → chunk-hg7rhrgd.js} +27 -4
- package/dist/{chunk-wxhrr03r.js → chunk-hygwq9y2.js} +2 -2
- package/dist/{chunk-kpeb5rcs.js → chunk-ws47smxr.js} +2 -2
- package/dist/deploy/index.js +4 -4
- package/dist/dns/cloudflare.d.ts +119 -1
- package/dist/dns/index.d.ts +4 -1
- package/dist/dns/index.js +3 -1
- package/dist/dns/types.d.ts +43 -0
- package/dist/drivers/index.js +1 -1
- package/dist/drivers/shared/rpx-gateway.d.ts +11 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +369 -4
- package/dist/ui/account/automation.html +3 -3
- package/dist/ui/account/security.html +2 -2
- package/dist/ui/applications/compose.html +4 -4
- package/dist/ui/applications/new.html +2 -2
- package/dist/ui/data/backups.html +4 -4
- package/dist/ui/data/services.html +4 -4
- package/dist/ui/data/volumes.html +4 -4
- package/dist/ui/index.html +4 -4
- package/dist/ui/infrastructure/topology.html +4 -4
- package/dist/ui/integrations.html +2 -2
- package/dist/ui/operations/alerts.html +4 -4
- package/dist/ui/operations/configuration.html +4 -4
- package/dist/ui/operations/jobs.html +4 -4
- package/dist/ui/operations/maintenance.html +4 -4
- package/dist/ui/operations/observability.html +4 -4
- package/dist/ui/operations/previews.html +4 -4
- package/dist/ui/operations/queue.html +4 -4
- package/dist/ui/operations/regions.html +4 -4
- package/dist/ui/operations/releases.html +4 -4
- package/dist/ui/operations/spend.html +4 -4
- package/dist/ui/operations/workloads.html +4 -4
- package/dist/ui/security.html +2 -2
- package/dist/ui/server/actions.html +4 -4
- package/dist/ui/server/activity.html +2 -2
- package/dist/ui/server/capacity.html +4 -4
- package/dist/ui/server/database.html +4 -4
- package/dist/ui/server/deployments.html +4 -4
- package/dist/ui/server/diagnostics.html +2 -2
- package/dist/ui/server/firewall.html +4 -4
- package/dist/ui/server/fleet.html +4 -4
- package/dist/ui/server/logs.html +3 -3
- package/dist/ui/server/metrics.html +4 -4
- package/dist/ui/server/security.html +2 -2
- package/dist/ui/server/services.html +2 -2
- package/dist/ui/server/sites.html +4 -4
- package/dist/ui/server/ssh-keys.html +4 -4
- package/dist/ui/server/team.html +4 -4
- package/dist/ui/server/terminal.html +2 -2
- package/dist/ui/serverless/alarms.html +4 -4
- package/dist/ui/serverless/assets.html +2 -2
- package/dist/ui/serverless/data.html +4 -4
- package/dist/ui/serverless/deployments.html +2 -2
- package/dist/ui/serverless/firewall.html +2 -2
- package/dist/ui/serverless/functions.html +4 -4
- package/dist/ui/serverless/logs.html +4 -4
- package/dist/ui/serverless/metrics.html +2 -2
- package/dist/ui/serverless/queues.html +4 -4
- package/dist/ui/serverless/secrets.html +4 -4
- package/dist/ui/serverless/traces.html +3 -3
- package/dist/ui/serverless.html +4 -4
- package/package.json +3 -3
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn a `CloudConfig` into a concrete Cloudflare CDN reconcile plan.
|
|
3
|
+
*
|
|
4
|
+
* Kept separate from {@link import('./cloudflare').reconcileCloudflareCdn} so
|
|
5
|
+
* the "what did the operator ask for" question is answerable — and testable —
|
|
6
|
+
* without a zone, a token, or a network. The reconciler takes explicit values;
|
|
7
|
+
* this is the only place that reads config and environment.
|
|
8
|
+
*/
|
|
9
|
+
import type { CloudConfig } from '@ts-cloud/core';
|
|
10
|
+
import type { CloudflareCacheRuleSettings } from './cloudflare-rules';
|
|
11
|
+
import type { CloudflareZoneSettings } from './cloudflare-settings';
|
|
12
|
+
import type { CloudflareOriginGuard } from './cloudflare';
|
|
13
|
+
export interface CloudflareCdnPlan {
|
|
14
|
+
/** Zone apex the records live in. */
|
|
15
|
+
zone: string;
|
|
16
|
+
/** Hostnames to front. */
|
|
17
|
+
hosts: string[];
|
|
18
|
+
/** API token. */
|
|
19
|
+
apiToken: string;
|
|
20
|
+
zoneId?: string;
|
|
21
|
+
accountId?: string;
|
|
22
|
+
proxied: boolean;
|
|
23
|
+
settings: CloudflareZoneSettings;
|
|
24
|
+
cache?: CloudflareCacheRuleSettings;
|
|
25
|
+
originGuard?: CloudflareOriginGuard;
|
|
26
|
+
purge: boolean;
|
|
27
|
+
skipOriginProbe: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface ResolveCloudflareCdnPlanResult {
|
|
30
|
+
/** The plan, or `null` when this config does not ask for a Cloudflare CDN. */
|
|
31
|
+
plan: CloudflareCdnPlan | null;
|
|
32
|
+
/**
|
|
33
|
+
* Reasons a requested CDN could not be planned. Non-empty with a `null` plan
|
|
34
|
+
* means "you asked for this and it is misconfigured", which is worth surfacing
|
|
35
|
+
* — as distinct from "you never asked", which yields no plan and no errors.
|
|
36
|
+
*/
|
|
37
|
+
errors: string[];
|
|
38
|
+
}
|
|
39
|
+
/** Environment lookup, injectable so tests never touch `process.env`. */
|
|
40
|
+
export type EnvLookup = (key: string) => string | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Derive the zone apex for a hostname.
|
|
43
|
+
*
|
|
44
|
+
* A configured `infrastructure.dns.domain` wins whenever the host sits inside
|
|
45
|
+
* it, because that is the only input that knows about multi-label suffixes; the
|
|
46
|
+
* last-two-labels fallback is a guess that is right for `example.com` and wrong
|
|
47
|
+
* for `example.co.uk`. The provider corrects the guess anyway once it resolves
|
|
48
|
+
* the zone by id, so this only has to be good enough to route the lookup.
|
|
49
|
+
*/
|
|
50
|
+
export declare function resolveZoneApex(host: string, configuredDomain?: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* Resolve the Cloudflare CDN plan for a config, or `null` when none applies.
|
|
53
|
+
*
|
|
54
|
+
* A plan is produced only when `infrastructure.compute.proxy.cdn.provider` is
|
|
55
|
+
* `'cloudflare'`. Everything else — hosts, zone, credentials — is filled in from
|
|
56
|
+
* the config with environment fallbacks, so the common case is a two-line CDN
|
|
57
|
+
* block plus a token in the environment.
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveCloudflareCdnPlan(config: CloudConfig, options?: {
|
|
60
|
+
env?: EnvLookup;
|
|
61
|
+
}): ResolveCloudflareCdnPlanResult;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Ruleset rules ts-cloud generates: cache rules for a static site and
|
|
3
|
+
* the request-header transform that carries the origin-lockdown secret.
|
|
4
|
+
*
|
|
5
|
+
* Everything here is pure — rules in, rules out — so the expression syntax can
|
|
6
|
+
* be tested without a zone. Writing them is {@link
|
|
7
|
+
* import('../dns/cloudflare').CloudflareProvider.putManagedPhaseRules}'s job.
|
|
8
|
+
*/
|
|
9
|
+
import type { CloudflareRule } from '../dns/cloudflare';
|
|
10
|
+
/**
|
|
11
|
+
* File extensions treated as immutable build output.
|
|
12
|
+
*
|
|
13
|
+
* These are the extensions a bundler fingerprints (`app.4f2a9c.js`), so a given
|
|
14
|
+
* URL's bytes never change and the edge can hold them effectively forever. HTML
|
|
15
|
+
* is deliberately absent: it carries the references to the fingerprinted files,
|
|
16
|
+
* so caching it as long would pin visitors to a stale deploy.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_STATIC_ASSET_EXTENSIONS: readonly string[];
|
|
19
|
+
export interface CloudflareCacheRuleSettings {
|
|
20
|
+
/** Generate cache rules at all. @default true */
|
|
21
|
+
enabled?: boolean;
|
|
22
|
+
/** Extensions treated as immutable, fingerprinted assets. */
|
|
23
|
+
assetExtensions?: readonly string[];
|
|
24
|
+
/** Edge TTL for those assets, in seconds. @default 2592000 (30 days) */
|
|
25
|
+
assetEdgeTtl?: number;
|
|
26
|
+
/** Browser TTL for those assets, in seconds. @default 31536000 (1 year) */
|
|
27
|
+
assetBrowserTtl?: number;
|
|
28
|
+
/** Edge TTL for HTML documents, in seconds. @default 3600 (1 hour) */
|
|
29
|
+
documentEdgeTtl?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Browser TTL for HTML documents, in seconds. @default 0
|
|
32
|
+
*
|
|
33
|
+
* Zero means the browser revalidates every navigation while the edge still
|
|
34
|
+
* absorbs the traffic — a deploy then reaches visitors as soon as the edge
|
|
35
|
+
* cache is purged, without a stale copy pinned in their local cache.
|
|
36
|
+
*/
|
|
37
|
+
documentBrowserTtl?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Path prefixes that must never be cached (`/api`, `/admin`, …). Matched with
|
|
40
|
+
* `starts_with`, so `/api` also covers `/api/anything`.
|
|
41
|
+
*/
|
|
42
|
+
bypassPaths?: readonly string[];
|
|
43
|
+
}
|
|
44
|
+
/** `http.host in {…}` for the hosts a rule applies to. */
|
|
45
|
+
export declare function hostCondition(hosts: readonly string[]): string;
|
|
46
|
+
/**
|
|
47
|
+
* Cache rules for a static site fronted by Cloudflare.
|
|
48
|
+
*
|
|
49
|
+
* The three rules are **mutually exclusive by construction** — each carries the
|
|
50
|
+
* negation of the ones before it — rather than relying on evaluation order.
|
|
51
|
+
* That is deliberate. Cloudflare applies every matching rule in this phase and
|
|
52
|
+
* lets a later one override an earlier one's settings, so a plain catch-all for
|
|
53
|
+
* documents (`http.host eq …`) silently matches `.js` and `.css` too and undoes
|
|
54
|
+
* the asset rule: fingerprinted files come back with the HTML's short browser
|
|
55
|
+
* TTL, which is the opposite of the intent and invisible unless you inspect the
|
|
56
|
+
* response headers.
|
|
57
|
+
*
|
|
58
|
+
* Every rule is also scoped to `hosts`. The zone may serve names that have
|
|
59
|
+
* nothing to do with this site, and a catch-all matching them would quietly
|
|
60
|
+
* start caching someone else's dynamic responses.
|
|
61
|
+
*/
|
|
62
|
+
export declare function buildStaticSiteCacheRules(hosts: readonly string[], settings?: CloudflareCacheRuleSettings): CloudflareRule[];
|
|
63
|
+
/**
|
|
64
|
+
* A request-header transform that stamps the origin-lockdown secret onto every
|
|
65
|
+
* request Cloudflare forwards to the box.
|
|
66
|
+
*
|
|
67
|
+
* This is the Cloudflare half of ts-cloud's origin guard: rpx rejects any
|
|
68
|
+
* request to these hosts that arrives without the header
|
|
69
|
+
* (`RpxGatewayConfig.originGuard`), so a client that discovers the origin IP
|
|
70
|
+
* and connects to it directly cannot bypass the edge. Without the pair, the
|
|
71
|
+
* origin stays openly reachable and the CDN is advisory.
|
|
72
|
+
*
|
|
73
|
+
* It goes in the LATE transform phase so the header survives any earlier
|
|
74
|
+
* rewrite and is not visible to rules the zone owner writes in the normal
|
|
75
|
+
* transform phase.
|
|
76
|
+
*/
|
|
77
|
+
export declare function buildOriginGuardRule(hosts: readonly string[], header: string, value: string): CloudflareRule[];
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare zone settings: the friendly config shape ts-cloud exposes, and its
|
|
3
|
+
* translation into the `{ setting_id: value }` pairs the API takes.
|
|
4
|
+
*
|
|
5
|
+
* Cloudflare's own ids are terse and inconsistently shaped (`ssl` is an enum
|
|
6
|
+
* string, `brotli` is `'on'|'off'`, HSTS is a nested object under
|
|
7
|
+
* `security_header`), so the config speaks in booleans and plain names and this
|
|
8
|
+
* module owns every mapping. Keeping the translation in one small, pure function
|
|
9
|
+
* is also what makes it testable without touching the network.
|
|
10
|
+
*/
|
|
11
|
+
/** HSTS (`Strict-Transport-Security`) configuration. */
|
|
12
|
+
export interface CloudflareHstsSettings {
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
/** Lifetime in seconds. Cloudflare requires one when enabled. @default 31536000 (1 year) */
|
|
15
|
+
maxAge?: number;
|
|
16
|
+
/** Apply to every subdomain. @default false */
|
|
17
|
+
includeSubdomains?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Add the zone to the browser preload lists.
|
|
20
|
+
*
|
|
21
|
+
* Left off by default on purpose: preloading is effectively irreversible on a
|
|
22
|
+
* human timescale, so it should be a deliberate choice rather than something a
|
|
23
|
+
* deploy turns on.
|
|
24
|
+
* @default false
|
|
25
|
+
*/
|
|
26
|
+
preload?: boolean;
|
|
27
|
+
/** Send `X-Content-Type-Options: nosniff`. @default true */
|
|
28
|
+
noSniff?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Zone-level settings ts-cloud can reconcile.
|
|
32
|
+
*
|
|
33
|
+
* Every field is optional and anything left undefined is NOT touched — the zone
|
|
34
|
+
* may hold settings that have nothing to do with this deploy, and a reconcile
|
|
35
|
+
* has no business resetting them to a default it invented.
|
|
36
|
+
*/
|
|
37
|
+
export interface CloudflareZoneSettings {
|
|
38
|
+
/**
|
|
39
|
+
* How Cloudflare talks to the origin.
|
|
40
|
+
*
|
|
41
|
+
* - `strict` — Full (strict): HTTPS to the origin, certificate verified. The
|
|
42
|
+
* only mode that actually protects the Cloudflare→origin hop, and the right
|
|
43
|
+
* answer whenever the origin holds a real certificate.
|
|
44
|
+
* - `full` — HTTPS to the origin, certificate NOT verified. Accepts a
|
|
45
|
+
* self-signed cert; use only while an origin is still getting its first one.
|
|
46
|
+
* - `flexible` — plaintext to the origin. Cloudflare serves HTTPS to visitors
|
|
47
|
+
* while the origin hop travels unencrypted, and on an origin that redirects
|
|
48
|
+
* HTTP to HTTPS it also produces a redirect loop.
|
|
49
|
+
* - `off` — no HTTPS at all.
|
|
50
|
+
*/
|
|
51
|
+
ssl?: 'off' | 'flexible' | 'full' | 'strict';
|
|
52
|
+
/** Redirect every HTTP request to HTTPS at the edge. */
|
|
53
|
+
alwaysUseHttps?: boolean;
|
|
54
|
+
/** Rewrite http:// references in HTML to https://. */
|
|
55
|
+
automaticHttpsRewrites?: boolean;
|
|
56
|
+
/** Minimum TLS version accepted from visitors. */
|
|
57
|
+
minTlsVersion?: '1.0' | '1.1' | '1.2' | '1.3';
|
|
58
|
+
/** Offer TLS 1.3 to visitors. */
|
|
59
|
+
tls13?: boolean;
|
|
60
|
+
/** Brotli-compress responses to visitors that accept it. */
|
|
61
|
+
brotli?: boolean;
|
|
62
|
+
/** Offer HTTP/3 (QUIC). */
|
|
63
|
+
http3?: boolean;
|
|
64
|
+
/** TLS 1.3 0-RTT resumption. */
|
|
65
|
+
zeroRtt?: boolean;
|
|
66
|
+
/** Send 103 Early Hints for `Link` headers. */
|
|
67
|
+
earlyHints?: boolean;
|
|
68
|
+
/** HSTS. */
|
|
69
|
+
hsts?: CloudflareHstsSettings;
|
|
70
|
+
/** Default browser cache TTL in seconds. `0` means "respect the origin". */
|
|
71
|
+
browserCacheTtl?: number;
|
|
72
|
+
/** Serve a cached copy when the origin is unreachable. */
|
|
73
|
+
alwaysOnline?: boolean;
|
|
74
|
+
/** Proxy WebSocket upgrades. */
|
|
75
|
+
websockets?: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Raw escape hatch: setting ids passed straight through, merged last.
|
|
78
|
+
*
|
|
79
|
+
* The mapped fields above cover what a static site needs; this exists so a
|
|
80
|
+
* zone-specific toggle doesn't require a ts-cloud release to reach.
|
|
81
|
+
*/
|
|
82
|
+
raw?: Record<string, unknown>;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Translate {@link CloudflareZoneSettings} into Cloudflare setting ids.
|
|
86
|
+
*
|
|
87
|
+
* Undefined inputs produce no entry at all, which is what keeps a reconcile
|
|
88
|
+
* additive: the caller only ever writes settings the config actually names.
|
|
89
|
+
*/
|
|
90
|
+
export declare function toCloudflareZoneSettings(settings: CloudflareZoneSettings): Record<string, unknown>;
|
|
91
|
+
/**
|
|
92
|
+
* Sensible zone settings for a static site served from a ts-cloud box behind
|
|
93
|
+
* Cloudflare's proxy.
|
|
94
|
+
*
|
|
95
|
+
* `ssl: 'strict'` is deliberate: the box terminates TLS with a real Let's
|
|
96
|
+
* Encrypt certificate (rpx + tlsx), so there is no reason to accept an
|
|
97
|
+
* unverified origin — and `flexible` would additionally loop, because the
|
|
98
|
+
* gateway redirects plain HTTP to HTTPS.
|
|
99
|
+
*/
|
|
100
|
+
export declare const STATIC_SITE_ZONE_SETTINGS: CloudflareZoneSettings;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Put Cloudflare's proxy CDN in front of a ts-cloud origin.
|
|
3
|
+
*
|
|
4
|
+
* Cloudflare's CDN is not a separate resource you point at an origin the way
|
|
5
|
+
* CloudFront is — it *is* the DNS record. A proxied ("orange cloud") A/AAAA
|
|
6
|
+
* record makes the public answer a Cloudflare anycast address, and Cloudflare
|
|
7
|
+
* forwards to the address stored in the record. Two consequences shape this
|
|
8
|
+
* module:
|
|
9
|
+
*
|
|
10
|
+
* - there is no origin hostname to invent. CloudFront needs a dedicated
|
|
11
|
+
* `origin.example.com` because a distribution whose origin is one of its own
|
|
12
|
+
* aliases resolves back into itself; Cloudflare reads the origin address out
|
|
13
|
+
* of the record it is proxying, so the apex fronts itself without looping.
|
|
14
|
+
* - the CDN and the DNS record cannot drift apart, because they are one object.
|
|
15
|
+
* That is why proxy state has to be preserved across upserts rather than
|
|
16
|
+
* re-derived (see `CloudflareProvider.upsertRecord`).
|
|
17
|
+
*
|
|
18
|
+
* The reconcile is ordered so it is safe to run on a zone that has never been
|
|
19
|
+
* touched AND on one already serving traffic: addresses first (so the origin is
|
|
20
|
+
* reachable), then zone settings, then rules, then a purge — never the reverse,
|
|
21
|
+
* which would briefly enforce `Full (strict)` against an origin the deploy has
|
|
22
|
+
* not yet pointed anywhere.
|
|
23
|
+
*/
|
|
24
|
+
import type { CloudflareProvider } from '../dns/cloudflare';
|
|
25
|
+
import type { CloudflareCacheRuleSettings } from './cloudflare-rules';
|
|
26
|
+
import type { CloudflareZoneSettings } from './cloudflare-settings';
|
|
27
|
+
export interface CloudflareOriginGuard {
|
|
28
|
+
/** Header Cloudflare stamps on the origin hop. */
|
|
29
|
+
header: string;
|
|
30
|
+
/** Shared secret value; rpx rejects fronted hosts that arrive without it. */
|
|
31
|
+
value: string;
|
|
32
|
+
}
|
|
33
|
+
export interface ReconcileCloudflareCdnOptions {
|
|
34
|
+
provider: CloudflareProvider;
|
|
35
|
+
/** Zone apex (`example.com`). */
|
|
36
|
+
zone: string;
|
|
37
|
+
/** Public hostnames served through the proxy. */
|
|
38
|
+
hosts: string[];
|
|
39
|
+
/** Origin IPv4 the proxied records point at. */
|
|
40
|
+
ipv4: string;
|
|
41
|
+
/** Origin IPv6, when the box has one. */
|
|
42
|
+
ipv6?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Serve these hosts through Cloudflare's proxy. @default true
|
|
45
|
+
*
|
|
46
|
+
* Set `false` for a DNS-only zone — records still reconcile, the CDN-specific
|
|
47
|
+
* work (settings, cache rules, purge) is skipped.
|
|
48
|
+
*/
|
|
49
|
+
proxied?: boolean;
|
|
50
|
+
/** Zone settings to apply. Defaults to {@link STATIC_SITE_ZONE_SETTINGS}. */
|
|
51
|
+
settings?: CloudflareZoneSettings;
|
|
52
|
+
/** Cache-rule tuning. */
|
|
53
|
+
cache?: CloudflareCacheRuleSettings;
|
|
54
|
+
/** Origin lockdown. Omit to leave the origin reachable directly. */
|
|
55
|
+
originGuard?: CloudflareOriginGuard;
|
|
56
|
+
/** Purge the edge cache once everything else is in place. @default true */
|
|
57
|
+
purge?: boolean;
|
|
58
|
+
/** Record TTL for unproxied records, in seconds. @default 300 */
|
|
59
|
+
ttl?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Skip the origin TLS probe and proxy immediately.
|
|
62
|
+
*
|
|
63
|
+
* The probe only exists to avoid stranding a box that has no certificate yet;
|
|
64
|
+
* a caller that knows the origin is ready can skip the round trip.
|
|
65
|
+
* @default false
|
|
66
|
+
*/
|
|
67
|
+
skipOriginProbe?: boolean;
|
|
68
|
+
}
|
|
69
|
+
export interface CloudflareCdnReport {
|
|
70
|
+
/** Records confirmed written, with the proxy state each ended up in. */
|
|
71
|
+
records: Array<{
|
|
72
|
+
host: string;
|
|
73
|
+
type: 'A' | 'AAAA';
|
|
74
|
+
content: string;
|
|
75
|
+
proxied: boolean;
|
|
76
|
+
}>;
|
|
77
|
+
/** Zone settings actually changed. */
|
|
78
|
+
settingsChanged: Array<{
|
|
79
|
+
id: string;
|
|
80
|
+
from: unknown;
|
|
81
|
+
to: unknown;
|
|
82
|
+
}>;
|
|
83
|
+
/** Cache rules written. */
|
|
84
|
+
cacheRules: number;
|
|
85
|
+
/** Whether the origin-guard transform rule was written. */
|
|
86
|
+
originGuard: boolean;
|
|
87
|
+
/** Whether the edge cache was purged. */
|
|
88
|
+
purged: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Hosts published DNS-only because the origin could not yet serve a trusted
|
|
91
|
+
* certificate for them. Re-run the deploy once the certificate is issued.
|
|
92
|
+
*/
|
|
93
|
+
deferredProxy: Array<{
|
|
94
|
+
host: string;
|
|
95
|
+
reason: string;
|
|
96
|
+
}>;
|
|
97
|
+
warnings: string[];
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Reconcile a Cloudflare zone so `hosts` are served from the edge and forwarded
|
|
101
|
+
* to a ts-cloud origin.
|
|
102
|
+
*
|
|
103
|
+
* Warnings are collected rather than thrown throughout. This runs at the tail of
|
|
104
|
+
* a deploy that has already shipped code to a box: a plan-gated zone setting or
|
|
105
|
+
* a rules permission the token lacks is worth reporting loudly, but it is not
|
|
106
|
+
* worth reporting as "the deploy failed" when the site is up and serving.
|
|
107
|
+
*/
|
|
108
|
+
export declare function reconcileCloudflareCdn(options: ReconcileCloudflareCdnOptions): Promise<CloudflareCdnReport>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDN integrations that sit in front of a ts-cloud origin.
|
|
3
|
+
*
|
|
4
|
+
* AWS/CloudFront fronting lives with the AWS stack (see
|
|
5
|
+
* `drivers/shared/cloudfront-origin`); this module holds the Cloudflare path,
|
|
6
|
+
* where the CDN and the DNS record are the same object.
|
|
7
|
+
*/
|
|
8
|
+
export type { CloudflareCdnReport, CloudflareOriginGuard, ReconcileCloudflareCdnOptions, } from './cloudflare';
|
|
9
|
+
export { reconcileCloudflareCdn } from './cloudflare';
|
|
10
|
+
export type { CloudflareCdnPlan, EnvLookup, ResolveCloudflareCdnPlanResult, } from './cloudflare-plan';
|
|
11
|
+
export { resolveCloudflareCdnPlan, resolveZoneApex } from './cloudflare-plan';
|
|
12
|
+
export type { CloudflareCacheRuleSettings } from './cloudflare-rules';
|
|
13
|
+
export { buildOriginGuardRule, buildStaticSiteCacheRules, DEFAULT_STATIC_ASSET_EXTENSIONS, hostCondition, } from './cloudflare-rules';
|
|
14
|
+
export type { CloudflareHstsSettings, CloudflareZoneSettings } from './cloudflare-settings';
|
|
15
|
+
export { STATIC_SITE_ZONE_SETTINGS, toCloudflareZoneSettings } from './cloudflare-settings';
|
|
16
|
+
export type { OriginTlsProbeResult, ProbeOriginTlsOptions } from './origin-tls';
|
|
17
|
+
export { probeOriginTls } from './origin-tls';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface OriginTlsProbeResult {
|
|
2
|
+
/** A TLS handshake completed at all. */
|
|
3
|
+
reachable: boolean;
|
|
4
|
+
/**
|
|
5
|
+
* The certificate is valid for this hostname and chains to a public root —
|
|
6
|
+
* i.e. `Full (strict)` will work.
|
|
7
|
+
*/
|
|
8
|
+
trusted: boolean;
|
|
9
|
+
/** Why the probe is not `trusted`, when it isn't. */
|
|
10
|
+
reason?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ProbeOriginTlsOptions {
|
|
13
|
+
/** Origin address to connect to (IPv4 or IPv6). */
|
|
14
|
+
address: string;
|
|
15
|
+
/** Hostname to present as SNI and verify the certificate against. */
|
|
16
|
+
serverName: string;
|
|
17
|
+
/** @default 443 */
|
|
18
|
+
port?: number;
|
|
19
|
+
/** @default 5000 */
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Connect to `address:port` with `serverName` as SNI and report what the origin
|
|
24
|
+
* presents.
|
|
25
|
+
*
|
|
26
|
+
* Verification is deliberately not enforced at the socket (`rejectUnauthorized:
|
|
27
|
+
* false`) so an untrusted certificate produces a *description* rather than a
|
|
28
|
+
* thrown error — "reachable but the cert doesn't cover this name" and "nothing
|
|
29
|
+
* is listening" call for different advice, and collapsing them into one failure
|
|
30
|
+
* loses that. Node still performs the checks and reports them through
|
|
31
|
+
* `authorized` / `authorizationError`.
|
|
32
|
+
*
|
|
33
|
+
* Never throws: a probe is a hint used to pick a safe ordering, and a probe
|
|
34
|
+
* that fails for its own reasons must not fail a deploy.
|
|
35
|
+
*/
|
|
36
|
+
export declare function probeOriginTls(options: ProbeOriginTlsOptions): Promise<OriginTlsProbeResult>;
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
Route53Provider,
|
|
8
8
|
UnifiedDnsValidator,
|
|
9
9
|
createDnsProvider
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-atbgd8wz.js";
|
|
11
11
|
import {
|
|
12
12
|
Route53Client
|
|
13
13
|
} from "./chunk-32e7ya18.js";
|
|
@@ -815,7 +815,7 @@ async function deployStaticSiteWithExternalDnsFull(config) {
|
|
|
815
815
|
}
|
|
816
816
|
}
|
|
817
817
|
onProgress?.("upload", "Uploading files to S3...");
|
|
818
|
-
const { uploadStaticFiles } = await import("./chunk-
|
|
818
|
+
const { uploadStaticFiles } = await import("./chunk-ws47smxr.js");
|
|
819
819
|
const uploadResult = await uploadStaticFiles({
|
|
820
820
|
sourceDir,
|
|
821
821
|
bucket: infraResult.bucket,
|
|
@@ -835,7 +835,7 @@ async function deployStaticSiteWithExternalDnsFull(config) {
|
|
|
835
835
|
}
|
|
836
836
|
if (infraResult.distributionId && uploadResult.uploaded > 0) {
|
|
837
837
|
onProgress?.("invalidate", "Invalidating CloudFront cache...");
|
|
838
|
-
const { invalidateCache } = await import("./chunk-
|
|
838
|
+
const { invalidateCache } = await import("./chunk-ws47smxr.js");
|
|
839
839
|
await invalidateCache(infraResult.distributionId);
|
|
840
840
|
}
|
|
841
841
|
onProgress?.("complete", "Deployment complete!");
|