import * as yup$1 from "yup"; //#region src/deployments.d.ts declare const DEPLOYMENT_ENV_VAR_KEY_REGEX: RegExp; declare const DEPLOYMENT_SOURCE_ID_REGEX: RegExp; declare const MAX_DEPLOYMENT_SOURCE_ID_LENGTH = 63; declare const DEPLOYMENT_RUNTIMES: readonly ["fly", "gcp"]; type DeploymentRuntime = typeof DEPLOYMENT_RUNTIMES[number]; declare const DEFAULT_DEPLOYMENT_RUNTIME = "fly"; declare const DEPLOYMENT_VERSIONS: { readonly "gcp-beta-1": "gcp"; }; type DeploymentVersion = keyof typeof DEPLOYMENT_VERSIONS; declare const DEPLOYMENT_VERSION_TOKENS: DeploymentVersion[]; declare function isDeploymentRuntime(value: unknown): value is DeploymentRuntime; declare function isDeploymentVersion(value: unknown): value is DeploymentVersion; /** * The runtime a deploy file's `version` export selects. Absent (or null) is the * default runtime; an unknown token is null, and the caller refuses it. */ declare function deploymentRuntimeForVersion(version: string | null | undefined): DeploymentRuntime | null; /** * How many file entries a manifest may carry. * * High enough that the listing is the WHOLE tree for essentially any real * source: node_modules and build output are excluded before packaging, and what * survives is source, which is thousands of files at the top end rather than * tens of thousands. The dashboard shows every entry, so this is the number that * decides whether it is showing everything. * * The cost is a JSON column: ~90 bytes an entry, so ~180 KB before Postgres * compresses it — and paths in one tree share nearly all their prefixes, which * TOAST squashes hard. Worth it to be able to say "these are the files" rather * than "these are some of them". */ declare const MAX_SOURCE_MANIFEST_ENTRIES = 2000; declare const MAX_SOURCE_MANIFEST_PATH_LENGTH = 1024; type DeploymentSourceManifest = { /** Every file packaged, including the ones `entries` had no room for. */file_count: number; /** Their total size before compression. */ total_bytes: number; /** What was actually uploaded, after tar + gzip. */ compressed_bytes: number; /** * The largest files, biggest first, capped at MAX_SOURCE_MANIFEST_ENTRIES. * * Largest-first rather than a truncated alphabetical walk, because the cap * then only ever drops files too small to be anyone's problem — the question * this answers is which files are big. */ entries: { path: string; bytes: number; }[]; }; /** * The manifest for a packaged tree, capped. `paths` and `sizes` come from the * packager, which already holds every entry it wrote. */ declare function buildSourceManifest(options: { files: { path: string; bytes: number; }[]; compressedBytes: number; }): DeploymentSourceManifest; /** * Parses a stored manifest, or null when there is none / it is not one. * * Tolerant on purpose: this is a debugging aid read out of a JSON column, and a * row written by an older client (or hand-edited) must degrade to "no manifest" * rather than break the deployment it belongs to. */ declare function parseSourceManifest(value: unknown): DeploymentSourceManifest | null; /** * The manifest entries belonging to one service, and whether the listing is * complete for it. * * `rootDirectory` is the service's own subtree of the shared upload; null or * "." means the whole tree. Paths are posix and relative to the upload root. * * `prefix` is that root as a path prefix ("web/", or "" for the whole tree). * Returned rather than left for the caller to re-derive: entries keep their * full paths, so anything that displays them relative to the service — the * dashboard's folder tree — needs exactly the prefix this filtered on, and a * second copy of this normalisation is a second place for it to drift. */ declare function sourceManifestEntriesForService(manifest: DeploymentSourceManifest, rootDirectory: string | null): { entries: { path: string; bytes: number; }[]; truncated: boolean; prefix: string; }; declare const DEPLOYMENT_CONNECTION_VALUE_REGEX: RegExp; /** * The managed Hexclave service's slot on the deployments board. A service in * the deploy file's `deploy` export must never shadow it — `service("hexclave")` * doesn't exist (the `hexclave` context object replaces it), but the id stays * reserved so connection values like "hexclave.projectId" are unambiguous. */ declare const HEXCLAVE_SERVICE_ID = "hexclave"; declare const HEXCLAVE_OUTPUT_KEYS: readonly ["projectId", "apiUrl", "jwksUrl", "publishableClientKey", "secretServerKey"]; declare const SERVICE_OUTPUT_KEYS: readonly ["url", "hostname"]; /** * Splits a connection reference into its parts. One parser so the CLI, the * backend and the runtime cannot disagree about what `api.url:9090` * means. Returns null when the value is not a reference at all. */ declare function parseConnectionValue(value: string): { serviceId: string; outputKey: string; port: number | null; } | null; /** * Whether a reference actually requires its target to have DEPLOYED. The * answer depends on the RUNTIME, because it is a fact about where addresses * come from: * * On "fly", a service's private hostname is a pure function of its identity * (Fly's 6PN DNS publishes ".internal" the moment the app exists), so * `hostname` and a private `url` with a named port resolve before the target * ever runs. Only a PUBLIC url (the platform URL, which exists once the service * is up) and a bare `url()` (which has to read the target's ports) wait. * `targetIsPublic` is null when the caller cannot answer — a reference into a * source this deploy file does not contain, or one naming a port the target * does not declare — in which case the conservative answer is that it waits. * Getting this wrong in the other direction would serialize independent * deploys, cascade false "skipped" results when the target fails, and reject * mutually-wired services as circular. * * On "gcp", every SERVICE output waits — `url` and `hostname` alike, public or * private, named port or not. Both are the target's runtime ADDRESS, and GCP * publishes none for a service that does not exist yet: a private service is * reached at its VM's internal IP (assigned when the instance is created), a * public one at its platform URL, and a serverless one at the URI its revision * got. The cost is that mutually-wired services are a circular dependency * there, reported as one, instead of silently resolving. * * `hexclave.*` outputs are not service outputs and never wait — they come from * the managed service, which always exists. */ declare function connectionRequiresTargetDeployed(runtime: DeploymentRuntime, outputKey: string, port: number | null, targetIsPublic: boolean | null): boolean; /** Formats a connection reference. The inverse of parseConnectionValue. */ declare function formatConnectionValue(serviceId: string, outputKey: string, port?: number | null): string; type HexclaveOutputKey = typeof HEXCLAVE_OUTPUT_KEYS[number]; type ServiceOutputKey = typeof SERVICE_OUTPUT_KEYS[number]; type DeploymentEnvVarDefinition = { type?: "secret" | "connection" | undefined; value?: string | undefined; key?: string | undefined; }; /** * The builder machine for one deployment source. * * A property of the DEPLOYMENT rather than of any service: one `hexclave deploy` * uploads one tree and builds every service of it on ONE machine, so there is * exactly one builder to size and a per-service field could only ever be a * request that some other service's request overrode. * * Absent, or `memory` absent within it, means the deployment picks its own size * — the floor a build of that shape needs (see DEFAULT_BUILDER_MEMORY, and the * larger floor an auto-detected build gets). */ type DeploymentBuilderDefinition = { memory?: DeploymentMemorySize | undefined; }; type DeploymentServiceDefinition = { type: DeploymentServiceType; public?: boolean | undefined; ports: DeploymentPorts; min_instances?: number | undefined; max_instances?: number | undefined; memory?: DeploymentMemorySize | undefined; root_directory?: string | undefined; dockerfile_path?: string | undefined; image?: string | undefined; persistent_volumes?: Record | undefined; build_command?: string | undefined; start_command?: string | undefined; env: Record; }; declare function deploymentServiceIsBuilt(definition: Pick): boolean; declare function deploymentServiceUsesGeneratedDockerfile(definition: Pick): boolean; type DeploymentPortDefinition = { protocol: "http" | "tcp"; }; type DeploymentPorts = Record; type DeploymentVolumeDefinition = { path: string; size_gb: number; }; type DeploymentServiceType = "server" | "serverless"; declare const DEPLOYMENT_SERVICE_TYPES: readonly ["server", "serverless"]; /** A port's explicitly configured protocol. */ declare function portProtocol(port: DeploymentPortDefinition): "http" | "tcp"; /** One declared port, with the record's key parsed. */ type DeploymentPortEntry = { port: number; protocol: "http" | "tcp"; }; /** * The ports as a list, ascending by port number. Every * consumer that has to compare, count or iterate ports goes through this, so the * key parsing happens in exactly one place — and so the order is * the same everywhere (object key order would otherwise put "80" after "8080" * for one caller and not another). * * Keys that are not port numbers are dropped rather than thrown on: this runs on * stored rows, and a hand-edited one must not take down a listing. Every write * path validates the shape. */ declare function deploymentPortEntries(ports: DeploymentPorts): DeploymentPortEntry[]; declare const DEPLOYMENT_PORT_KEY_REGEX: RegExp; /** * The port number that additionally answers on the standard 80/443, or null when * the service has no single obvious one. * * For a PUBLIC service it is the LOWEST-numbered HTTP port. Lowest rather than * first-encountered: deploymentPortEntries sorts numerically, so the holder is a * property of the port set and not of JSON key ordering. That determinism is the * whole point — the holder is the port the service's bare URL names and the only * one a custom domain can front, so an arbitrary pick would silently move both. * * For a PRIVATE service it is the sole HTTP port, because a private service gets * public IPs the moment a custom domain is attached and that domain terminates * TLS on 443. Null when there are several, which is what makes such a service * ineligible to hold a domain at all. * * KEPT IN SYNC WITH standardPortsHolderFor in apps/marshal/src/services.ts. */ declare function standardPortsHolderPort(ports: DeploymentPorts, isPublic: boolean): number | null; /** * Whether this port is the one that owns the service's standard 80/443, and so * the one whose URL carries no `:port` suffix. * * VERIFIED AGAINST REAL FLY: every other port of a public service is reachable * on its own number over BOTH IPv4 and IPv6 — a shared IPv4 forwards any port, * so long as the traffic carries SNI or a Host header for the proxy to route on. * The difference between the holder and the rest is the URL shape and which port * a custom domain can front, NOT reachability. */ declare function deploymentPortOwnsStandardPorts(ports: DeploymentPorts, isPublic: boolean, port: number): boolean; /** * Declared ports that collide with the external listeners the standard-ports * holder reserves, as a sorted list (empty when there is no conflict). * * The holder does not only answer on its own number — it also claims external 80 * and 443, which is what makes the platform URL and any custom domain * certificate work. Those are listeners on the WHOLE app: the runtime emits one * entry per declared port, so a *different* port that is itself numbered 80 or * 443 asks for an external listener the holder has already taken. Two entries * claiming one external port is a config the runtime cannot serve — it is * rejected outright, or routes one of them somewhere the author did not ask for. * * The cheap example is a public service with * `{ 80: { protocol: "http" }, 443: { protocol: "http" } }`: 80 is the * holder, claims 80 and 443, and the declared 443 claims 443 again. This is * refused rather than resolved by precedence, because every way of resolving it * silently drops or retargets a port the author explicitly declared. */ declare function reservedStandardPortConflicts(ports: DeploymentPorts, isPublic: boolean): number[]; /** * The port a bare `url()` refers to, or null when the service leaves it * ambiguous (several HTTP ports) or impossible (none). Callers phrase their own * error — the CLI wants a config-file diagnostic, the backend an HTTP status — * which is why this returns null rather than throwing. */ declare function soleHttpDeploymentPort(ports: DeploymentPorts): number | null; /** One port's definition, or null when the service does not declare it. */ declare function deploymentPortEntry(ports: DeploymentPorts, port: number): DeploymentPortEntry | null; declare const MAX_PORTS_PER_SERVICE = 10; declare const MAX_INSTANCES_PER_SERVICE = 10; declare const MIN_VOLUME_SIZE_GB = 1; declare const MAX_VOLUME_SIZE_GB = 500; declare const MAX_PERSISTENT_VOLUMES_PER_SERVICE = 1; declare const FREE_PLAN_MAX_VOLUMES_PER_PROJECT = 1; declare const FREE_PLAN_MAX_VOLUME_SIZE_GB = 10; declare const DEPLOYMENT_VOLUME_ID_REGEX: RegExp; declare const MAX_VOLUME_ID_LENGTH = 26; declare const DEPLOYMENT_MEMORY_SIZES: readonly ["512MB", "1GB", "2GB", "4GB", "8GB", "16GB", "32GB"]; type DeploymentMemorySize = typeof DEPLOYMENT_MEMORY_SIZES[number]; declare const FLY_SERVER_MEMORY_SIZES: readonly ["512MB", "1GB", "2GB", "4GB", "8GB"]; declare const FLY_SERVERLESS_MEMORY_SIZES: readonly ["512MB", "1GB", "2GB", "4GB", "8GB"]; declare const GCP_SERVER_MEMORY_SIZES: readonly ["1GB", "2GB", "4GB", "8GB"]; declare const GCP_SERVERLESS_MEMORY_SIZES: readonly ["512MB", "1GB", "2GB", "4GB", "8GB"]; declare const SERVER_MEMORY_SIZES: readonly ["512MB", "1GB", "2GB", "4GB", "8GB"]; declare const SERVERLESS_MEMORY_SIZES: readonly ["512MB", "1GB", "2GB", "4GB", "8GB"]; declare const BUILDER_MEMORY_SIZES: readonly ["8GB", "16GB", "32GB"]; declare const DEFAULT_SERVER_MEMORY = "512MB"; declare const DEFAULT_SERVERLESS_MEMORY = "512MB"; declare const GCP_DEFAULT_SERVER_MEMORY = "1GB"; declare const DEFAULT_BUILDER_MEMORY = "8GB"; /** The rungs a service of this type may ask for on this runtime. */ declare function deploymentMemorySizesForType(type: DeploymentServiceType, runtime?: DeploymentRuntime): readonly DeploymentMemorySize[]; /** What a service of this type runs at on this runtime when it declares no `memory`. */ declare function defaultDeploymentMemoryForType(type: DeploymentServiceType, runtime?: DeploymentRuntime): DeploymentMemorySize; /** A size token as a whole number of megabytes. */ declare function deploymentMemoryToMb(size: DeploymentMemorySize): number; /** * The size token for a stored megabyte count, or null when no rung matches. * * Null rather than a throw or a rounded neighbour: the input is a database * column, and a value written by a future version (or edited by hand) must * degrade to "unset" — which every reader already handles — rather than claim * to be a size the deployment is not running. */ declare function deploymentMemoryFromMb(megabytes: number): DeploymentMemorySize | null; /** * The canonical token for something an author wrote, or null. * * Case-insensitive and space-tolerant on PURPOSE, and used only to phrase a * "did you mean" — never to accept the input. "4gb" and "4 GB" are refused like * any other non-canonical spelling; recognising them is what lets the error say * which token to write instead of listing seven and leaving the reader to * diff them by eye. Binary suffixes are recognised for the same reason: "4Gi" * is what someone arriving from a container platform will type first. */ declare function suggestDeploymentMemorySize(raw: string): DeploymentMemorySize | null; /** * The CPU that comes with a memory size, and whether it is a whole core. * * Derived rather than declared — see the note on the ladder above — but NOT * hidden: on the smaller server sizes it is a burstable fraction of a core, and * a 4GB server that turns out to have one shared core is a surprise worth * spending a line of UI on rather than one to meet under load. Every surface * that shows a size shows this beside it. * * `shared` means the vCPU is a burstable slice: it can reach a full core in * bursts and is throttled to `count` sustained. A dedicated CPU is `count` * cores, always. * * This is the DISPLAY copy of the mapping. The runtime derives its own machine * shapes from the same ladder at the point it calls a provider — that boundary * re-derives rather than trusting a number off the wire, exactly as it * re-validates every other part of a spec. */ declare function deploymentCpuForMemory(type: DeploymentServiceType, memory: DeploymentMemorySize, runtime?: DeploymentRuntime): { count: number; shared: boolean; }; /** * The most memory one project may hold in ALWAYS-ON services at once. * * Hexclave's own capacity guard, not a per-project quota: nothing meters * deployment compute, so the plan ladder bounds what one service may ask for * and this bounds how many of them may ask at once. Without it a paid project * can stand up an arbitrary number of top-rung servers, each of which is a * machine somebody pays for. * * Only always-on services count (effective `min_instances` of 1 or more). A * service that scales to zero holds no machine while it is idle, and how far it * may scale UP is already bounded by MAX_INSTANCES_PER_SERVICE. */ declare const MAX_PROJECT_ALWAYS_ON_MEMORY_MB: number; declare const MAX_DEPLOYMENT_COMMAND_LENGTH = 2048; /** * Whether `value` is usable as a build or start command. * * Control characters are refused rather than escaped. A build command becomes a * line of a generated Dockerfile and a start command becomes an argv entry in a * machine config, and in both places a newline or a NUL is a structural * character of the thing being generated rather than data — so the rule is * stated here, once, and the generators may then assume it. */ declare function isValidDeploymentCommand(value: string): boolean; declare const MAX_DEPLOYMENT_IMAGE_REF_LENGTH = 512; declare const DEFAULT_DEPLOYMENT_IMAGE_REGISTRY = "docker.io"; declare const DEFAULT_DEPLOYMENT_IMAGE_NAMESPACE = "library"; /** * An image reference in its parts, always fully qualified: `postgres:16` parses * to the registry `docker.io` and the repository `library/postgres`, because * that is what actually gets pulled and a stored definition should say so. * * Exactly one of `tag` and `digest` is set. A tag is a POINTER the publisher can * move; a digest is the content hash and cannot be moved. Both spellings are * accepted from authors and neither is resolved here — the reference reaches the * runtime as written, and an author who needs fixed bytes writes the digest. */ type DeploymentImageRef = { registry: string; repository: string; tag: string | null; digest: string | null; canonical: string; }; /** * Parses and normalizes an image reference. * * Returns the failure MESSAGE rather than just null (unlike * parseConnectionValue) because every caller wants to say the same thing and * the useful part is always *which* rule was broken: the CLI prints it as a * deploy-file diagnostic, the schema as a validation error, and the backend as * a 400. One message, phrased once. */ declare function parseDeploymentImageRef(value: string): { ok: true; ref: DeploymentImageRef; } | { ok: false; message: string; }; declare const deploymentEnvVarSchema: yup$1.ObjectSchema<{ type: "secret" | "connection" | undefined; value: string | undefined; key: string | undefined; default_value: undefined; }, yup$1.AnyObject, { type: undefined; value: undefined; key: undefined; default_value: undefined; }, "">; declare const deploymentSecretDefaultsSchema: yup$1.MixedSchema, yup$1.AnyObject, undefined, "">; declare const MAX_DEPLOYMENT_CI_ENV_BYTES: number; declare const deploymentCiEnvSchema: yup$1.MixedSchema, yup$1.AnyObject, undefined, "">; declare const deploymentServiceDefinitionSchema: yup$1.ObjectSchema<{ type: "server" | "serverless"; public: boolean | undefined; ports: Record; min_instances: number | undefined; max_instances: number | undefined; memory: "512MB" | "1GB" | "2GB" | "4GB" | "8GB" | "16GB" | "32GB" | undefined; root_directory: string | undefined; persistent_volumes: Record | undefined; dockerfile_path: string | undefined; image: string | undefined; build_command: string | undefined; start_command: string | undefined; dev_command: undefined; env: Record; }, yup$1.AnyObject, { type: undefined; public: undefined; ports: undefined; min_instances: undefined; max_instances: undefined; memory: undefined; root_directory: undefined; persistent_volumes: undefined; dockerfile_path: undefined; image: undefined; build_command: undefined; start_command: undefined; dev_command: undefined; env: undefined; }, "">; /** * The `builder` a deploy file declares, alongside its services. * * Deliberately its own schema rather than a field of the service one: the * builder is one machine per DEPLOYMENT, and the sync route stores it on the * deployment source rather than on any service row. */ declare const deploymentBuilderDefinitionSchema: yup$1.ObjectSchema<{ memory: "8GB" | "16GB" | "32GB" | undefined; }, yup$1.AnyObject, { memory: undefined; }, "">; //#endregion export { BUILDER_MEMORY_SIZES, DEFAULT_BUILDER_MEMORY, DEFAULT_DEPLOYMENT_IMAGE_NAMESPACE, DEFAULT_DEPLOYMENT_IMAGE_REGISTRY, DEFAULT_DEPLOYMENT_RUNTIME, DEFAULT_SERVERLESS_MEMORY, DEFAULT_SERVER_MEMORY, DEPLOYMENT_CONNECTION_VALUE_REGEX, DEPLOYMENT_ENV_VAR_KEY_REGEX, DEPLOYMENT_MEMORY_SIZES, DEPLOYMENT_PORT_KEY_REGEX, DEPLOYMENT_RUNTIMES, DEPLOYMENT_SERVICE_TYPES, DEPLOYMENT_SOURCE_ID_REGEX, DEPLOYMENT_VERSIONS, DEPLOYMENT_VERSION_TOKENS, DEPLOYMENT_VOLUME_ID_REGEX, DeploymentBuilderDefinition, DeploymentEnvVarDefinition, DeploymentImageRef, DeploymentMemorySize, DeploymentPortDefinition, DeploymentPortEntry, DeploymentPorts, DeploymentRuntime, DeploymentServiceDefinition, DeploymentServiceType, DeploymentSourceManifest, DeploymentVersion, DeploymentVolumeDefinition, FLY_SERVERLESS_MEMORY_SIZES, FLY_SERVER_MEMORY_SIZES, FREE_PLAN_MAX_VOLUMES_PER_PROJECT, FREE_PLAN_MAX_VOLUME_SIZE_GB, GCP_DEFAULT_SERVER_MEMORY, GCP_SERVERLESS_MEMORY_SIZES, GCP_SERVER_MEMORY_SIZES, HEXCLAVE_OUTPUT_KEYS, HEXCLAVE_SERVICE_ID, HexclaveOutputKey, MAX_DEPLOYMENT_CI_ENV_BYTES, MAX_DEPLOYMENT_COMMAND_LENGTH, MAX_DEPLOYMENT_IMAGE_REF_LENGTH, MAX_DEPLOYMENT_SOURCE_ID_LENGTH, MAX_INSTANCES_PER_SERVICE, MAX_PERSISTENT_VOLUMES_PER_SERVICE, MAX_PORTS_PER_SERVICE, MAX_PROJECT_ALWAYS_ON_MEMORY_MB, MAX_SOURCE_MANIFEST_ENTRIES, MAX_SOURCE_MANIFEST_PATH_LENGTH, MAX_VOLUME_ID_LENGTH, MAX_VOLUME_SIZE_GB, MIN_VOLUME_SIZE_GB, SERVERLESS_MEMORY_SIZES, SERVER_MEMORY_SIZES, SERVICE_OUTPUT_KEYS, ServiceOutputKey, buildSourceManifest, connectionRequiresTargetDeployed, defaultDeploymentMemoryForType, deploymentBuilderDefinitionSchema, deploymentCiEnvSchema, deploymentCpuForMemory, deploymentEnvVarSchema, deploymentMemoryFromMb, deploymentMemorySizesForType, deploymentMemoryToMb, deploymentPortEntries, deploymentPortEntry, deploymentPortOwnsStandardPorts, deploymentRuntimeForVersion, deploymentSecretDefaultsSchema, deploymentServiceDefinitionSchema, deploymentServiceIsBuilt, deploymentServiceUsesGeneratedDockerfile, formatConnectionValue, isDeploymentRuntime, isDeploymentVersion, isValidDeploymentCommand, parseConnectionValue, parseDeploymentImageRef, parseSourceManifest, portProtocol, reservedStandardPortConflicts, soleHttpDeploymentPort, sourceManifestEntriesForService, standardPortsHolderPort, suggestDeploymentMemorySize }; //# sourceMappingURL=deployments.d.ts.map