UNPKG

alepha

Version:

Easy-to-use modern TypeScript framework for building many kind of applications.

1,263 lines 57.4 kB
import { Alepha, Static, TSchema } from "alepha"; import { AlephaCliUtils, AppEntry, BuildCloudflareTask, PackageManagerUtils } from "alepha/cli"; import { Asker, EnvUtils, Runner, RunnerMethod } from "alepha/command"; import { ConsoleColorProvider } from "alepha/logger"; import { FileSystemProvider, ShellProvider } from "alepha/system"; import { DateTimeProvider } from "alepha/datetime"; //#region ../../src/cli/platform-lib/providers/PlatformCacheProvider.d.ts interface PlatformCache { [adapter: string]: { lastLoginCheck: number; accountId?: string; }; } /** * Caches cloud provider login state to avoid slow auth checks. * * Stored in node_modules/.alepha/platform.json (gitignored, project-scoped). * TTL: 4 hours. */ declare class PlatformCacheProvider { protected static readonly TTL_MS: number; protected readonly fs: FileSystemProvider; protected readonly dateTime: DateTimeProvider; protected cachePath(root: string): string; isLoginFresh(root: string, adapter: string): Promise<boolean>; getAccountId(root: string, adapter: string): Promise<string | undefined>; recordLogin(root: string, adapter: string, accountId?: string): Promise<void>; protected readCache(root: string): Promise<PlatformCache>; protected writeCache(root: string, cache: PlatformCache): Promise<void>; } //#endregion //#region ../../src/cli/platform-lib/schemas/cloudflare.d.ts declare const cloudflareAccountSchema: import("zod").ZodObject<{ id: import("zod").ZodString; name: import("zod").ZodString; }, import("zod/v4/core").$strip>; type CloudflareAccount = Static<typeof cloudflareAccountSchema>; declare const cloudflareD1Schema: import("zod").ZodObject<{ uuid: import("zod").ZodString; name: import("zod").ZodString; }, import("zod/v4/core").$strip>; type CloudflareD1 = Static<typeof cloudflareD1Schema>; declare const cloudflareKVSchema: import("zod").ZodObject<{ id: import("zod").ZodString; title: import("zod").ZodString; }, import("zod/v4/core").$strip>; type CloudflareKV = Static<typeof cloudflareKVSchema>; declare const cloudflareR2Schema: import("zod").ZodObject<{ name: import("zod").ZodString; creation_date: import("zod").ZodOptional<import("zod").ZodString>; }, import("zod/v4/core").$strip>; type CloudflareR2 = Static<typeof cloudflareR2Schema>; declare const cloudflareR2ListSchema: import("zod").ZodObject<{ buckets: import("zod").ZodArray<import("zod").ZodObject<{ name: import("zod").ZodString; creation_date: import("zod").ZodOptional<import("zod").ZodString>; }, import("zod/v4/core").$strip>>; }, import("zod/v4/core").$strip>; declare const cloudflareQueueSchema: import("zod").ZodObject<{ queue_id: import("zod").ZodString; queue_name: import("zod").ZodString; }, import("zod/v4/core").$strip>; type CloudflareQueue = Static<typeof cloudflareQueueSchema>; declare const cloudflareQueueConsumerSchema: import("zod").ZodObject<{ consumer_id: import("zod").ZodString; service: import("zod").ZodString; environment: import("zod").ZodOptional<import("zod").ZodString>; }, import("zod/v4/core").$strip>; type CloudflareQueueConsumer = Static<typeof cloudflareQueueConsumerSchema>; declare const cloudflareHyperdriveOriginSchema: import("zod").ZodObject<{ host: import("zod").ZodString; }, import("zod/v4/core").$strip>; declare const cloudflareHyperdriveSchema: import("zod").ZodObject<{ id: import("zod").ZodString; name: import("zod").ZodString; origin: import("zod").ZodObject<{ host: import("zod").ZodString; }, import("zod/v4/core").$strip>; }, import("zod/v4/core").$strip>; type CloudflareHyperdrive = Static<typeof cloudflareHyperdriveSchema>; declare const cloudflareWorkerSchema: import("zod").ZodObject<{ id: import("zod").ZodString; created_on: import("zod").ZodString; modified_on: import("zod").ZodString; }, import("zod/v4/core").$strip>; type CloudflareWorker = Static<typeof cloudflareWorkerSchema>; declare const cloudflareDeploymentVersionSchema: import("zod").ZodObject<{ version_id: import("zod").ZodString; percentage: import("zod").ZodNumber; }, import("zod/v4/core").$strip>; declare const cloudflareDeploymentSchema: import("zod").ZodObject<{ id: import("zod").ZodString; versions: import("zod").ZodArray<import("zod").ZodObject<{ version_id: import("zod").ZodString; percentage: import("zod").ZodNumber; }, import("zod/v4/core").$strip>>; created_on: import("zod").ZodString; }, import("zod/v4/core").$strip>; type CloudflareDeployment = Static<typeof cloudflareDeploymentSchema>; declare const cloudflareDeploymentListSchema: import("zod").ZodObject<{ deployments: import("zod").ZodArray<import("zod").ZodObject<{ id: import("zod").ZodString; versions: import("zod").ZodArray<import("zod").ZodObject<{ version_id: import("zod").ZodString; percentage: import("zod").ZodNumber; }, import("zod/v4/core").$strip>>; created_on: import("zod").ZodString; }, import("zod/v4/core").$strip>>; }, import("zod/v4/core").$strip>; declare const cloudflareVersionSchema: import("zod").ZodObject<{ id: import("zod").ZodString; metadata: import("zod").ZodObject<{ created_on: import("zod").ZodString; }, import("zod/v4/core").$strip>; annotations: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodString>>; }, import("zod/v4/core").$strip>; type CloudflareVersion = Static<typeof cloudflareVersionSchema>; declare const cloudflareVersionListSchema: import("zod").ZodObject<{ items: import("zod").ZodArray<import("zod").ZodObject<{ id: import("zod").ZodString; metadata: import("zod").ZodObject<{ created_on: import("zod").ZodString; }, import("zod/v4/core").$strip>; annotations: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodString>>; }, import("zod/v4/core").$strip>>; }, import("zod/v4/core").$strip>; declare const cloudflareSecretSchema: import("zod").ZodObject<{ name: import("zod").ZodString; type: import("zod").ZodString; }, import("zod/v4/core").$strip>; type CloudflareSecret = Static<typeof cloudflareSecretSchema>; declare const createD1BodySchema: import("zod").ZodObject<{ name: import("zod").ZodString; primary_location_hint: import("zod").ZodOptional<import("zod").ZodString>; jurisdiction: import("zod").ZodOptional<import("zod").ZodString>; }, import("zod/v4/core").$strip>; declare const createKVBodySchema: import("zod").ZodObject<{ title: import("zod").ZodString; }, import("zod/v4/core").$strip>; declare const createR2BodySchema: import("zod").ZodObject<{ name: import("zod").ZodString; }, import("zod/v4/core").$strip>; declare const cloudflareR2TokenSchema: import("zod").ZodObject<{ id: import("zod").ZodString; accessKeyId: import("zod").ZodString; secretAccessKey: import("zod").ZodString; }, import("zod/v4/core").$strip>; type CloudflareR2Token = Static<typeof cloudflareR2TokenSchema>; declare const createR2TokenBodySchema: import("zod").ZodObject<{ name: import("zod").ZodString; policies: import("zod").ZodArray<import("zod").ZodObject<{ effect: import("zod").ZodString; permissions: import("zod").ZodArray<import("zod").ZodString>; buckets: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>; }, import("zod/v4/core").$strip>>; }, import("zod/v4/core").$strip>; declare const createQueueBodySchema: import("zod").ZodObject<{ queue_name: import("zod").ZodString; }, import("zod/v4/core").$strip>; declare const createHyperdriveOriginSchema: import("zod").ZodObject<{ scheme: import("zod").ZodString; host: import("zod").ZodString; port: import("zod").ZodNumber; database: import("zod").ZodString; user: import("zod").ZodString; password: import("zod").ZodString; }, import("zod/v4/core").$strip>; declare const createHyperdriveBodySchema: import("zod").ZodObject<{ name: import("zod").ZodString; origin: import("zod").ZodObject<{ scheme: import("zod").ZodString; host: import("zod").ZodString; port: import("zod").ZodNumber; database: import("zod").ZodString; user: import("zod").ZodString; password: import("zod").ZodString; }, import("zod/v4/core").$strip>; }, import("zod/v4/core").$strip>; declare const putSecretBodySchema: import("zod").ZodObject<{ name: import("zod").ZodString; text: import("zod").ZodString; type: import("zod").ZodString; }, import("zod/v4/core").$strip>; declare const cloudflareApiErrorSchema: import("zod").ZodObject<{ code: import("zod").ZodNumber; message: import("zod").ZodString; }, import("zod/v4/core").$strip>; type CloudflareApiError = Static<typeof cloudflareApiErrorSchema>; //#endregion //#region ../../src/cli/platform-lib/services/WranglerApi.d.ts /** * Wraps wrangler CLI commands that are kept as shell-outs. * * Only used for operations where wrangler provides value * beyond a raw API call: OAuth login, worker deploy (bundling/upload), * D1 migrations, and secret bulk push. */ declare class WranglerApi { protected readonly log: import("alepha/logger").Logger; protected readonly shell: ShellProvider; protected readonly utils: AlephaCliUtils; protected readonly pm: PackageManagerUtils; protected runShell(command: string, options?: Parameters<ShellProvider["run"]>[1]): Promise<string>; /** * Ensure wrangler is installed in the project. */ ensureInstalled(root: string): Promise<void>; /** * Check if the user is authenticated. Returns the whoami output. */ whoami(): Promise<string>; /** * Open the browser-based OAuth login flow. */ login(): Promise<void>; /** * Get the current auth token from wrangler (auto-refreshes if expired). */ getAuthToken(): Promise<string>; /** * Deploy a worker via wrangler (handles bundling and upload). * * Returns the workers.dev URL if found in the output. */ deploy(workerName: string, configPath: string, root?: string): Promise<string | undefined>; /** * Apply D1 migrations remotely. */ d1MigrationsApply(dbName: string, configPath: string, root?: string): Promise<void>; } //#endregion //#region ../../src/cli/platform-lib/services/CloudflareApi.d.ts /** * Thin wrapper over the Cloudflare REST API. * * Uses `wrangler auth token` to obtain credentials, * then calls `fetch()` directly for all CRUD operations. */ declare class CloudflareApi { protected static readonly BASE = "https://api.cloudflare.com/client/v4"; protected readonly log: import("alepha/logger").Logger; protected readonly alepha: Alepha; protected readonly wrangler: WranglerApi; protected token?: string; protected accountId?: string; protected jurisdiction?: "eu" | "fedramp"; /** * Set the Cloudflare data jurisdiction for R2 and D1 resources. * * R2 buckets and D1 databases created under a jurisdiction live in a * separate namespace — every R2 API call (list/create/delete) must include * the `cf-r2-jurisdiction` header, and D1 create must include the field * in the request body. Omit / pass `undefined` for the default (global). */ setJurisdiction(jurisdiction?: "eu" | "fedramp"): void; /** * Override the Cloudflare account ID (from platform config). * * When unset, `resolveAccountId` falls back to `CLOUDFLARE_ACCOUNT_ID` env * var or the token's single account. */ setAccountId(accountId?: string): void; /** * Obtain the current auth token from wrangler. */ resolveToken(): Promise<string>; /** * Resolve the Cloudflare account ID. * * Calls /accounts and picks the first one. Cached after first call. */ resolveAccountId(): Promise<string>; listD1(): Promise<CloudflareD1[]>; createD1(name: string, location?: string): Promise<CloudflareD1>; deleteD1(databaseId: string): Promise<void>; listKV(): Promise<CloudflareKV[]>; createKV(title: string): Promise<CloudflareKV>; deleteKV(namespaceId: string): Promise<void>; listR2(): Promise<CloudflareR2[]>; createR2(name: string): Promise<void>; deleteR2(name: string): Promise<void>; /** * Mint a bucket-scoped R2 API token (S3 access key + secret) using the * current bearer token. Used by teardown to wipe a bucket over the S3 * protocol without requiring users to pre-create R2 access keys. * * The returned token should be revoked with `deleteR2Token` as soon as the * wipe is done. */ createR2Token(name: string, bucket: string): Promise<CloudflareR2Token>; deleteR2Token(tokenId: string): Promise<void>; listQueues(): Promise<CloudflareQueue[]>; createQueue(name: string): Promise<CloudflareQueue>; deleteQueue(queueId: string): Promise<void>; listQueueConsumers(queueId: string): Promise<CloudflareQueueConsumer[]>; deleteQueueConsumer(queueId: string, consumerService: string): Promise<void>; listHyperdrive(): Promise<CloudflareHyperdrive[]>; createHyperdrive(name: string, connectionString: string): Promise<CloudflareHyperdrive>; deleteHyperdrive(configId: string): Promise<void>; getWorker(scriptName: string): Promise<CloudflareWorker | undefined>; deleteWorker(scriptName: string): Promise<void>; listDeployments(scriptName: string): Promise<CloudflareDeployment[]>; listVersions(scriptName: string): Promise<CloudflareVersion[]>; listSecrets(scriptName: string): Promise<CloudflareSecret[]>; putSecret(scriptName: string, name: string, value: string): Promise<void>; /** * Fetch the current worker bindings via the script-settings endpoint. * Used to merge new secrets into the existing binding set in one PATCH * (avoids the per-secret `putSecret` calls, each of which creates a * Cloudflare deployment — pushing 7 secrets meant 7 deployment rows). * * Secret bindings come back with `name` + `type` but no `text` (they're * write-only on Cloudflare's side); to preserve them across a PATCH we * forward each one as `{ type, name }` and Cloudflare keeps the stored * value. */ getWorkerSettings(scriptName: string): Promise<{ bindings: Array<{ type: string; name: string; text?: string; }>; }>; /** * Replace the worker's binding set in one call (= one Cloudflare * deployment, regardless of how many secrets are being updated). * * The endpoint expects multipart FormData with a `settings` field whose * value is a JSON-encoded `{ bindings: [...] }` — the `fetch` helper * above is JSON-only, so this one bypasses it and calls `globalThis.fetch` * directly. Mirrors what `wrangler secret bulk` does internally. */ patchWorkerBindings(scriptName: string, bindings: Array<{ type: string; name: string; text?: string; }>): Promise<void>; protected fetch<T = unknown>(path: string, options?: { method?: string; body?: unknown; bodySchema?: TSchema; schema?: TSchema; query?: Record<string, string>; }): Promise<T>; /** * Paginate a page-based list endpoint (`result_info.total_pages`). * * Cloudflare defaults to `per_page=20`; we push it to 1000 (max on most * list endpoints) and loop if more pages exist. Each page is validated * against the item schema. */ protected paginate<T>(path: string, itemSchema: TSchema, perPage?: number): Promise<T[]>; /** * Paginate a cursor-based list endpoint where `result` is an object * containing both the items array and a `cursor` field (R2 buckets, * Workers versions). Returns the flattened item array. */ protected paginateCursor<T>(path: string, itemsKey: string, itemSchema: TSchema, perPage?: number): Promise<T[]>; /** * Parse a postgres:// connection string into Hyperdrive origin fields. */ protected parseConnectionString(connectionString: string): { scheme: string; host: string; port: number; database: string; user: string; password: string; }; } //#endregion //#region ../../src/cli/platform-lib/atoms/platformOptions.d.ts /** * Platform deployment configuration atom. * * Filled from the `platform` section of `alepha.config.ts`. * Read by `PlatformCommand` to resolve environments and adapters. */ declare const platformOptions: import("alepha").Atom<import("zod").ZodOptional<import("zod").ZodObject<{ name: import("zod").ZodOptional<import("zod").ZodString>; default: import("zod").ZodOptional<import("zod").ZodString>; tenancy: import("zod").ZodOptional<import("zod").ZodEnum<{ none: "none"; optional: "optional"; required: "required"; }>>; secrets: import("zod").ZodOptional<import("zod").ZodObject<{ keys: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>; store: import("zod").ZodOptional<import("zod").ZodEnum<{ github: "github"; }>>; environmentPattern: import("zod").ZodOptional<import("zod").ZodString>; }, import("zod/v4/core").$strip>>; environments: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodObject<{ adapter: import("zod").ZodEnum<{ cloudflare: "cloudflare"; vercel: "vercel"; }>; domain: import("zod").ZodOptional<import("zod").ZodString>; zone: import("zod").ZodOptional<import("zod").ZodString>; services: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodObject<{ binding: import("zod").ZodString; service: import("zod").ZodString; }, import("zod/v4/core").$strip>>>; jurisdiction: import("zod").ZodOptional<import("zod").ZodEnum<{ eu: "eu"; fedramp: "fedramp"; }>>; accountId: import("zod").ZodOptional<import("zod").ZodString>; }, import("zod/v4/core").$strip>>; }, import("zod/v4/core").$strip>>, "alepha.cli.platform.options">; /** * Type for platform options. */ type PlatformOptions = Static<typeof platformOptions.schema>; /** * Configuration for a single named environment. */ interface EnvironmentConfig { adapter: "cloudflare" | "vercel"; domain?: string; zone?: string; vars?: Record<string, string>; jurisdiction?: "eu" | "fedramp"; accountId?: string; services?: Array<{ binding: string; service: string; }>; } //#endregion //#region ../../src/cli/platform-lib/services/NamingService.d.ts /** * Multi-tenancy mode for an app's deployments. * * - `none` (default): single-instance app. Passing `--tenant` is an * error — guards a non-tenanted app (e.g. the SaaS console) from * accidentally getting a tenant prefix. * - `required`: every deploy targets a tenant. Omitting `--tenant` is an * error — kills the "forgot the flag → deployed to the apex" footgun. * - `optional`: both work — a base instance (no `--tenant`) and per-tenant * instances coexist (distinct names + hosts). */ type Tenancy = "none" | "optional" | "required"; /** * Validate a `--tenant` value against the app's declared `tenancy` and * return the effective tenant (or `undefined` for a base/non-tenanted * deploy). Throws on any matrix violation so a misconfigured deploy fails * fast instead of landing on the wrong resource names / host. */ declare function resolveTenant(tenancy: Tenancy | undefined, tenant: string | undefined): string | undefined; /** * Resolve the host a deploy is served on. * * - `override` (V2 custom domains, e.g. `reservation.club-b14.fr`) wins * outright when supplied — not wired to a flag today, but the single * seam a future `--domain` / Rocket `config.hostname` plugs into. * - else a tenant becomes the leftmost DNS label: `b14` + `alepha.club` * → `b14.alepha.club`. * - else the base domain is used as-is. */ declare function tenantDomain(base: string | undefined, tenant: string | undefined, override?: string): string | undefined; /** * Generates deterministic resource names for cloud deployments. * * Pattern: `<tenant>-<project>-<env>` (tenant segment omitted when the * deploy isn't tenanted). * * All segments are slugified (lowercase, alphanumeric + dashes, max 63 * chars). One app per workspace — see `alepha platform`. */ declare class NamingService { forContext(project: string, env: string, tenant?: string): NamingContext; slugify(name: string): string; } declare class NamingContext { protected readonly prefix: string; constructor(prefix: string); worker(): string; d1(): string; hyperdrive(): string; r2(): string; kv(): string; queue(): string; } //#endregion //#region ../../src/cli/platform-lib/adapters/PlatformAdapter.d.ts /** * Options for {@link PlatformAdapter.exportDb}. */ interface ExportDbOptions { /** * Destination file for the local snapshot. Adapter-specific default — * Cloudflare/D1 writes the dev SQLite at * `node_modules/.alepha/sqlite.db`. */ output?: string; /** * Keep the intermediate `.sql` dump instead of deleting it after import. */ keepSql?: boolean; } interface DetectedResources { hasDatabase: boolean; hasBucket: boolean; hasKV: boolean; hasQueue: boolean; hasCron: boolean; } /** * One workspace = one app. Used to be a per-app definition in a * monorepo-aware orchestrator; flattened into `PlatformContext` after * the `apps:` field was removed from platform options. */ interface PlatformContext { /** * Slugified project name (from package.json or platform config). */ project: string; /** * Environment key (e.g., "production", "staging", "tmp-bug001"). */ env: string; /** * Environment configuration from alepha.config.ts. */ envConfig: EnvironmentConfig; /** * Workspace root path. */ root: string; /** * Resolved entry points for the workspace. Stub (`{ root, server: "" }`) * in pre-built / manifest mode since no source booting happens. */ entry: AppEntry; /** * Cloud resources the workspace uses — discovered at build time, read * from `dist/manifest.json` at deploy time. */ resources: DetectedResources; /** * Resource name generator bound to this project+env(+tenant). */ naming: NamingContext; /** * Active tenant slug for this deploy (apps with `tenancy: optional | * required`), or `undefined` for a base / non-tenanted deploy. Shapes * the served host (`<tenant>.<domain>`); resource names already fold it * in via {@link naming}. */ tenant?: string; /** * Pre-built mode. When true, the adapter's `build()` should skip the * Vite bundle steps and only regenerate the deploy config * (wrangler.jsonc, Dockerfile, etc.) so it reflects current bindings + * per-tenant overrides. */ prebuilt?: boolean; } /** * @deprecated Same as `PlatformContext` since the `apps:` collapse — * kept as a type alias so existing adapter signatures still compile. */ type AppContext = PlatformContext; interface ResourceState { name: string; exists: boolean; id?: string; detail?: string; } interface WorkerState extends ResourceState { version?: string; tag?: string; createdAt?: string; } interface SecretState { name: string; deployed: boolean; } interface PlatformState { workers: WorkerState[]; databases: ResourceState[]; buckets: ResourceState[]; kvNamespaces: ResourceState[]; queues: ResourceState[]; secrets: SecretState[]; } /** * Abstract platform adapter. * * Each cloud provider (Cloudflare, AKS, docker-compose) implements this. * The PlatformOrchestrator calls these methods in the correct order. */ declare abstract class PlatformAdapter { /** * Ensure the user is authenticated with the cloud provider. * May use cached credentials to avoid slow checks. */ abstract authenticate(ctx: PlatformContext, run: RunnerMethod): Promise<void>; /** * Build artifacts for a single app. */ abstract build(ctx: AppContext, run: RunnerMethod): Promise<void>; /** * Deploy a single app (upload + activate atomically, e.g., wrangler deploy). * Returns the live URL if the platform provides one. */ abstract deploy(ctx: AppContext, run: RunnerMethod): Promise<string | undefined>; /** * Create/ensure cloud resources exist (DB, buckets, queues). * Not all adapters provision -- AKS defers to Helm. */ provision(_ctx: PlatformContext, _run: RunnerMethod): Promise<void>; /** * Run database migrations. */ migrate(_ctx: PlatformContext, _run: RunnerMethod): Promise<void>; /** * Export the deployed database to a local file — the remote → local dev * snapshot workflow. Adapter/dialect specific; the default refuses. */ exportDb(_ctx: PlatformContext, _run: RunnerMethod, _options?: ExportDbOptions): Promise<void>; /** * Push runtime secrets to the deployed worker(s). * * Reads secrets from `.env.{env}` files (parsed, not from process.env), * filters out vars already handled by bindings (DATABASE_URL, R2, etc.), * and pushes the rest via the platform's secret management. */ secrets(_ctx: PlatformContext, _run: RunnerMethod): Promise<void>; /** * Detect existing resources and their state. * Used by `plan` and `status` commands. */ abstract inspect(ctx: PlatformContext, run: RunnerMethod): Promise<PlatformState>; /** * Tear down all resources for an environment. */ abstract teardown(ctx: PlatformContext, run: RunnerMethod): Promise<void>; } //#endregion //#region ../../src/cli/platform-lib/adapters/CloudflareAdapter.d.ts /** * Cloudflare Workers adapter. * * Uses the Cloudflare REST API (via CloudflareApi) for resource provisioning * and teardown, and wrangler CLI (via WranglerApi) for login, deploy, * D1 migrations, and secret bulk push. */ declare class CloudflareAdapter extends PlatformAdapter { protected readonly log: import("alepha/logger").Logger; protected readonly fs: FileSystemProvider; protected readonly shell: ShellProvider; protected readonly cache: PlatformCacheProvider; protected readonly alepha: Alepha; protected readonly envUtils: EnvUtils; protected readonly api: CloudflareApi; protected readonly wrangler: WranglerApi; protected readonly runner: Runner; protected readonly buildTask: BuildCloudflareTask; protected readonly options: Readonly<{ name?: string | undefined; default?: string | undefined; tenancy?: "none" | "optional" | "required" | undefined; secrets?: { keys?: string[] | undefined; store?: "github" | undefined; environmentPattern?: string | undefined; } | undefined; environments: Record<string, { adapter: "cloudflare" | "vercel"; domain?: string | undefined; zone?: string | undefined; services?: { binding: string; service: string; }[] | undefined; jurisdiction?: "eu" | "fedramp" | undefined; accountId?: string | undefined; }>; } | undefined>; protected provisionedD1Id?: string; protected provisionedHyperdriveId?: string; protected provisionedKVIds: Map<string, string>; /** * Check if the user's DATABASE_URL points to an external Postgres database. * If so, we use Hyperdrive instead of D1. * * Reads from `.env.{env}` first, falls back to `process.env`. */ protected isPostgres(ctx: PlatformContext): Promise<boolean>; /** * Propagate the environment's data-jurisdiction setting to the API client. * * Must be invoked at the top of every entry point (authenticate, build, * deploy, secrets, provision, migrate, inspect, teardown) because * CloudflareApi is a singleton reused across env invocations. */ protected configureApi(ctx: PlatformContext): void; protected runShell(command: string, options?: Parameters<ShellProvider["run"]>[1]): Promise<string>; authenticate(ctx: PlatformContext, run: RunnerMethod): Promise<void>; build(ctx: AppContext, run: RunnerMethod): Promise<void>; /** * Library-embed of `alepha build -t cloudflare --prebuilt`. Loads the * pre-built `dist/manifest.json`, sets the per-tenant env vars on * `process.env` for the duration of the call (the task's enhance* * methods read them directly), then runs `BuildCloudflareTask` * against a synthetic context. * * `ctx.alepha` is intentionally null — in manifest mode the task * reads resources/crons/containers from `ctx.manifest` and never * dereferences `ctx.alepha`. Same for `entry` and `hasClient`: * prebuilt mode skips the bundle tasks; only the wrangler.jsonc / * worker-entrypoint emission runs. */ protected runBuildInProcess(root: string, env: Record<string, string>): Promise<void>; deploy(ctx: AppContext, run: RunnerMethod): Promise<string | undefined>; /** * Vars that are handled by wrangler bindings or build config. * These should not be pushed as secrets. */ static readonly EXCLUDED_SECRET_KEYS: Set<string>; /** * Read the build manifest's `env` list (every key the app declares via * `$env`) from `dist/manifest.json`. Used as the default worker-secret * allowlist. Returns `undefined` when the manifest is absent or predates * the `env` field, so the caller falls back to the `.env` file keys. */ protected readManifestEnvKeys(root: string): Promise<string[] | undefined>; secrets(ctx: PlatformContext, run: RunnerMethod): Promise<void>; /** * Public base URL for this deploy, derived from the configured domain * (honoring tenant subdomains). Returns undefined when no domain is set or * the host is a wildcard — there's no single resolvable origin to point at. */ protected publicUrl(ctx: PlatformContext): string | undefined; /** * Plain-text binding used to fingerprint the deployed secret set so the * next `up` can skip the PATCH when nothing has changed. */ static readonly SECRETS_HASH_BINDING = "ALEPHA_SECRETS_HASH"; provision(ctx: PlatformContext, run: RunnerMethod): Promise<void>; migrate(ctx: PlatformContext, run: RunnerMethod): Promise<void>; exportDb(ctx: PlatformContext, run: RunnerMethod, options?: ExportDbOptions): Promise<void>; protected migrateD1(ctx: PlatformContext, run: RunnerMethod): Promise<void>; protected migratePostgres(ctx: PlatformContext, run: RunnerMethod): Promise<void>; inspect(ctx: PlatformContext, run: RunnerMethod): Promise<PlatformState>; teardown(ctx: PlatformContext, run: RunnerMethod): Promise<void>; protected ensureD1(name: string): Promise<string>; protected ensureHyperdrive(name: string, connectionString: string): Promise<string>; protected ensureR2(name: string): Promise<void>; /** Whether a Cloudflare error message indicates the bucket is already gone. */ protected isMissingBucketError(msg: string): boolean; /** * Resolve S3 credentials for wiping an R2 bucket over the S3 protocol. * * Prefers the account's R2 S3 credentials from the environment * (`S3_ACCESS_KEY_ID` / `S3_SECRET_ACCESS_KEY`) — these are already * provisioned for the deploy (artifact registry) and are account-scoped, * so they can empty any bucket without minting anything. Returns `null` * when not configured, letting the caller fall back to token minting. */ protected resolveR2Credentials(): { accessKeyId: string; secretAccessKey: string; } | null; /** * Delete an R2 bucket, emptying it first only when necessary. * * Cloudflare's REST `DELETE /r2/buckets/:name` succeeds on an empty bucket * but rejects a non-empty one. So we attempt the delete directly (the * common teardown case — no objects, no creds needed), and only on failure * empty the bucket over the S3 protocol and retry. A missing bucket is a * no-op, so teardown is idempotent. */ protected deleteR2Bucket(name: string, ctx: PlatformContext): Promise<void>; /** * Empty an R2 bucket via the S3-compatible API. * * Cloudflare's REST API has no object-level endpoints — objects must be * listed and deleted over the S3 protocol. We use the account's R2 S3 * credentials (`S3_ACCESS_KEY_ID` / `S3_SECRET_ACCESS_KEY`) when present; * otherwise we fall back to minting a short-lived bucket-scoped token via * the CF API (requires a user-scoped `CLOUDFLARE_API_TOKEN`) and revoke it * after. When neither is available the wipe is skipped with a warning — * the caller still attempts the delete, which succeeds for empty buckets. * * Also aborts any pending multipart uploads — those count as bucket * contents from R2's perspective and would otherwise block the delete. */ protected wipeR2Bucket(bucketName: string, ctx: PlatformContext): Promise<void>; protected ensureKV(name: string): Promise<string>; protected ensureQueue(name: string): Promise<void>; /** * Get the currently active deployment for a worker. */ protected getActiveDeployment(workerName: string): Promise<{ versionId: string; tag?: string; createdAt?: string; } | undefined>; } //#endregion //#region ../../src/cli/platform-lib/schemas/vercel.d.ts declare const vercelProjectSchema: import("zod").ZodObject<{ id: import("zod").ZodString; name: import("zod").ZodString; accountId: import("zod").ZodString; }, import("zod/v4/core").$strip>; type VercelProject = Static<typeof vercelProjectSchema>; declare const createProjectBodySchema: import("zod").ZodObject<{ name: import("zod").ZodString; framework: import("zod").ZodOptional<import("zod").ZodNull>; }, import("zod/v4/core").$strip>; declare const vercelDeploymentSchema: import("zod").ZodObject<{ uid: import("zod").ZodString; name: import("zod").ZodString; url: import("zod").ZodString; state: import("zod").ZodOptional<import("zod").ZodString>; readyState: import("zod").ZodOptional<import("zod").ZodString>; created: import("zod").ZodOptional<import("zod").ZodNumber>; target: import("zod").ZodOptional<import("zod").ZodString>; alias: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>; }, import("zod/v4/core").$strip>; type VercelDeployment = Static<typeof vercelDeploymentSchema>; declare const vercelEnvVarSchema: import("zod").ZodObject<{ id: import("zod").ZodString; key: import("zod").ZodString; value: import("zod").ZodOptional<import("zod").ZodString>; type: import("zod").ZodString; target: import("zod").ZodArray<import("zod").ZodString>; }, import("zod/v4/core").$strip>; type VercelEnvVar = Static<typeof vercelEnvVarSchema>; declare const createEnvVarBodySchema: import("zod").ZodObject<{ key: import("zod").ZodString; value: import("zod").ZodString; type: import("zod").ZodString; target: import("zod").ZodArray<import("zod").ZodString>; }, import("zod/v4/core").$strip>; //#endregion //#region ../../src/cli/platform-lib/services/VercelCli.d.ts /** * Wraps Vercel CLI commands and token management. * * Used for operations where the Vercel CLI provides value: * OAuth login, prebuilt deploy, and auth token extraction. */ declare class VercelCli { protected readonly log: import("alepha/logger").Logger; protected readonly shell: ShellProvider; protected readonly fs: FileSystemProvider; protected readonly utils: AlephaCliUtils; protected readonly pm: PackageManagerUtils; protected runShell(command: string, options?: Parameters<ShellProvider["run"]>[1]): Promise<string>; /** * Ensure vercel CLI is installed in the project. */ ensureInstalled(root: string): Promise<void>; /** * Get the Vercel auth token. * * Priority: * 1. VERCEL_TOKEN environment variable (CI/CD) * 2. Vercel CLI auth.json file (local dev) */ getAuthToken(): Promise<string>; /** * Validate the current auth token. */ whoami(): Promise<string>; /** * Open the browser-based login flow. */ login(): Promise<void>; /** * Deploy a prebuilt .vercel/output/ directory. * * Returns the deployment URL. */ deploy(distDir: string, options: { prod?: boolean; token?: string; }): Promise<string | undefined>; /** * Resolve the path to Vercel CLI auth.json. */ protected getAuthFilePath(): string; } //#endregion //#region ../../src/cli/platform-lib/services/VercelApi.d.ts /** * Thin wrapper over the Vercel REST API. * * Uses the auth token from VercelCli for all requests. */ declare class VercelApi { protected static readonly BASE = "https://api.vercel.com"; protected readonly log: import("alepha/logger").Logger; protected readonly alepha: Alepha; protected readonly vercelCli: VercelCli; protected token?: string; /** * Obtain the current auth token from the Vercel CLI. */ resolveToken(): Promise<string>; listProjects(): Promise<VercelProject[]>; getProject(nameOrId: string): Promise<VercelProject | undefined>; createProject(name: string): Promise<VercelProject>; updateProject(nameOrId: string, settings: { framework?: null; }): Promise<void>; deleteProject(nameOrId: string): Promise<void>; listDeployments(projectId: string, options?: { limit?: number; target?: string; }): Promise<VercelDeployment[]>; listEnvVars(projectId: string): Promise<VercelEnvVar[]>; upsertEnvVars(projectId: string, vars: Array<{ key: string; value: string; target: string[]; }>): Promise<void>; deleteEnvVar(projectId: string, envVarId: string): Promise<void>; protected fetch<T = unknown>(path: string, options?: { method?: string; body?: unknown; bodySchema?: TSchema; schema?: TSchema; query?: Record<string, string>; }): Promise<T>; } //#endregion //#region ../../src/cli/platform-lib/adapters/VercelAdapter.d.ts /** * Vercel platform adapter. * * Uses the Vercel CLI for login and deploy (--prebuilt), * and the Vercel REST API for project management, env vars, and inspection. * * v1 scope: deploy pipeline only. No DB/storage/KV provisioning. */ declare class VercelAdapter extends PlatformAdapter { protected readonly log: import("alepha/logger").Logger; protected readonly fs: FileSystemProvider; protected readonly shell: ShellProvider; protected readonly utils: AlephaCliUtils; protected readonly cache: PlatformCacheProvider; protected readonly alepha: Alepha; protected readonly envUtils: EnvUtils; protected readonly api: VercelApi; protected readonly vercelCli: VercelCli; /** * Vars that should not be pushed as env vars. * These are either handled by the build or are internal. */ static readonly EXCLUDED_SECRET_KEYS: Set<string>; protected runShell(command: string, options?: Parameters<ShellProvider["run"]>[1]): Promise<string>; authenticate(ctx: PlatformContext, run: RunnerMethod): Promise<void>; build(ctx: AppContext, run: RunnerMethod): Promise<void>; deploy(ctx: AppContext, run: RunnerMethod): Promise<string | undefined>; secrets(ctx: PlatformContext, run: RunnerMethod): Promise<void>; inspect(ctx: PlatformContext, run: RunnerMethod): Promise<PlatformState>; teardown(ctx: PlatformContext, run: RunnerMethod): Promise<void>; } //#endregion //#region ../../src/cli/platform-lib/providers/SecretStoreProvider.d.ts /** * A secret stored in a remote secret store. */ interface RemoteSecret { name: string; updatedAt?: string; } /** * Abstract provider for managing secrets in an external store. * * Implementations: GitHubSecretStore, MemorySecretStore */ declare abstract class SecretStoreProvider { /** * Verify the backing store is reachable and authenticated. */ abstract ensureAvailable(): Promise<void>; /** * Ensure the target environment exists in the store, creating it if needed. */ abstract ensureEnvironment(environment: string): Promise<void>; /** * List all secrets in a given environment. */ abstract list(environment: string): Promise<RemoteSecret[]>; /** * Set (create or update) a secret in a given environment. */ abstract set(environment: string, key: string, value: string): Promise<void>; /** * Delete a secret from a given environment. */ abstract delete(environment: string, key: string): Promise<void>; } //#endregion //#region ../../src/cli/platform-lib/providers/GitHubSecretStore.d.ts /** * GitHub Actions secret store backed by the `gh` CLI. * * Requires the GitHub CLI (`gh`) to be installed and authenticated. * Pushes secrets into GitHub Actions environments. */ declare class GitHubSecretStore implements SecretStoreProvider { protected readonly log: import("alepha/logger").Logger; protected readonly shell: ShellProvider; protected readonly fs: FileSystemProvider; /** * Verify that `gh` is installed and authenticated. */ ensureAvailable(): Promise<void>; /** * Create the GitHub Actions environment if it doesn't exist. */ ensureEnvironment(environment: string): Promise<void>; /** * List all secrets in a GitHub Actions environment. */ list(environment: string): Promise<RemoteSecret[]>; /** * Set a secret in a GitHub Actions environment. * * Writes a dotenv-formatted file and uses `gh secret set --env-file` to * avoid shell pipe issues with NodeShellProvider escaping the `|` character. */ set(environment: string, key: string, value: string): Promise<void>; /** * Delete a secret from a GitHub Actions environment. */ delete(environment: string, key: string): Promise<void>; } //#endregion //#region ../../src/cli/platform-lib/providers/MemorySecretStore.d.ts interface MemorySecretStoreCall { method: "ensureAvailable" | "ensureEnvironment" | "list" | "set" | "delete"; environment?: string; key?: string; value?: string; } /** * In-memory implementation of SecretStoreProvider for testing. * Records all operations and stores secrets in a nested Map. */ declare class MemorySecretStore implements SecretStoreProvider { /** * Secrets keyed by environment, then by key. */ secrets: Map<string, Map<string, string>>; /** * All recorded operations. */ calls: MemorySecretStoreCall[]; /** * When set, ensureAvailable() will throw with this message. */ availableError: string | null; ensureAvailable(): Promise<void>; ensureEnvironment(environment: string): Promise<void>; list(environment: string): Promise<RemoteSecret[]>; set(environment: string, key: string, value: string): Promise<void>; delete(environment: string, key: string): Promise<void>; /** * Check if set() was called for a given environment and key. */ wasSet(environment: string, key: string): boolean; /** * Check if delete() was called for a given environment and key. */ wasDeleted(environment: string, key: string): boolean; /** * Get all set() calls for a given environment. */ getSetCalls(environment: string): Array<{ key: string; value: string; }>; /** * Reset all state. */ reset(): void; } //#endregion //#region ../../src/cli/platform-lib/schemas/platform.d.ts declare const platformStatusWorkerSchema: import("zod").ZodObject<{ name: import("zod").ZodString; exists: import("zod").ZodBoolean; id: import("zod").ZodOptional<import("zod").ZodString>; detail: import("zod").ZodOptional<import("zod").ZodString>; version: import("zod").ZodOptional<import("zod").ZodString>; tag: import("zod").ZodOptional<import("zod").ZodString>; createdAt: import("zod").ZodOptional<import("zod").ZodString>; }, import("zod/v4/core").$strip>; declare const platformStatusResourceSchema: import("zod").ZodObject<{ name: import("zod").ZodString; exists: import("zod").ZodBoolean; id: import("zod").ZodOptional<import("zod").ZodString>; detail: import("zod").ZodOptional<import("zod").ZodString>; }, import("zod/v4/core").$strip>; declare const platformStatusSecretSchema: import("zod").ZodObject<{ name: import("zod").ZodString; deployed: import("zod").ZodBoolean; }, import("zod/v4/core").$strip>; declare const platformStatusSchema: import("zod").ZodObject<{ project: import("zod").ZodString; env: import("zod").ZodString; adapter: import("zod").ZodString; workers: import("zod").ZodArray<import("zod").ZodObject<{ name: import("zod").ZodString; exists: import("zod").ZodBoolean; id: import("zod").ZodOptional<import("zod").ZodString>; detail: import("zod").ZodOptional<import("zod").ZodString>; version: import("zod").ZodOptional<import("zod").ZodString>; tag: import("zod").ZodOptional<import("zod").ZodString>; createdAt: import("zod").ZodOptional<import("zod").ZodString>; }, import("zod/v4/core").$strip>>; databases: import("zod").ZodArray<import("zod").ZodObject<{ name: import("zod").ZodString; exists: import("zod").ZodBoolean; id: import("zod").ZodOptional<import("zod").ZodString>; detail: import("zod").ZodOptional<import("zod").ZodString>; }, import("zod/v4/core").$strip>>; buckets: import("zod").ZodArray<import("zod").ZodObject<{ name: import("zod").ZodString; exists: import("zod").ZodBoolean; id: import("zod").ZodOptional<import("zod").ZodString>; detail: import("zod").ZodOptional<import("zod").ZodString>; }, import("zod/v4/core").$strip>>; kvNamespaces: import("zod").ZodArray<import("zod").ZodObject<{ name: import("zod").ZodString; exists: import("zod").ZodBoolean; id: import("zod").ZodOptional<import("zod").ZodString>; detail: import("zod").ZodOptional<import("zod").ZodString>; }, import("zod/v4/core").$strip>>; queues: import("zod").ZodArray<import("zod").ZodObject<{ name: import("zod").ZodString; exists: import("zod").ZodBoolean; id: import("zod").ZodOptional<import("zod").ZodString>; detail: import("zod").ZodOptional<import("zod").ZodString>; }, import("zod/v4/core").$strip>>; secrets: import("zod").ZodArray<import("zod").ZodObject<{ name: import("zod").ZodString; deployed: import("zod").ZodBoolean; }, import("zod/v4/core").$strip>>; }, import("zod/v4/core").$strip>; type PlatformStatusOutput = Static<typeof platformStatusSchema>; declare const platformPlanAppResourcesSchema: import("zod").ZodObject<{ hasDatabase: import("zod").ZodBoolean; hasBucket: import("zod").ZodBoolean; hasKV: import("zod").ZodBoolean; hasQueue: import("zod").ZodBoolean; hasCron: import("zod").ZodBoolean; }, import("zod/v4/core").$strip>; declare const platformPlanAppSchema: import("zod").ZodObject<{ name: import("zod").ZodString; path: import("zod").ZodString; resources: import("zod").ZodObject<{ hasDatabase: import("zod").ZodBoolean; hasBucket: import("zod").ZodBoolean; hasKV: import("zod").ZodBoolean; hasQueue: import("zod").ZodBoolean; hasCron: import("zod").ZodBoolean; }, import("zod/v4/core").$strip>; }, import("zod/v4/core").$strip>; declare const platformPlanEnvironmentSchema: import("zod").ZodObject<{ adapter: import("zod").ZodString; domain: import("zod").ZodOptional<import("zod").ZodString>; zone: import("zod").ZodOptional<import("zod").ZodString>; }, import("zod/v4/core").$strip>; declare const platformPlanResourceSchema: import("zod").ZodObject<{ label: import("zod").ZodString; value: import("zod").ZodString; }, import("zod/v4/core").$strip>; declare const platformPlanSchema: import("zod").ZodObject<{ project: import("zod").ZodString; env: import("zod").ZodString; mode: import("zod").ZodEnum<{ monorepo: "monorepo"; standalone: "standalone"; }>; apps: import("zod").ZodArray<import("zod").ZodObject<{ name: import("zod").ZodString; path: import("zod").ZodString; resources: import("zod").ZodObject<{ hasDatabase: import("zod").ZodBoolean; hasBucket: import("zod").ZodBoolean; hasKV: import("zod").ZodBoolean; hasQueue: import("zod").ZodBoolean; hasCron: import("zod").ZodBoolean; }, import("zod/v4/core").$strip>; }, import("zod/v4/core").$strip>>; environments: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodObject<{ adapter: import("zod").ZodString; domain: import("zod").ZodOptional<import("zod").ZodString>; zone: import("zod").ZodOptional<import("zod").ZodString>; }, import("zod/v4/core").$strip>>; resources: import("zod").ZodArray<import("zod").ZodObject<{ label: import("zod").ZodString; value: import("zod").ZodString; }, import("zod/v4/core").$strip>>; secretCount: import("zod").ZodNumber; }, import("zod/v4/core").$strip>; type PlatformPlanOutput = Static<typeof platformPlanSchema>; //#endregion //#region ../../src/cli/platform-lib/services/PlatformInspector.d.ts interface ResolvedPlatformConfig { project: string; defaultEnv: string; /** App tenancy mode (undefined ⇒ "none"). */ tenancy?: Tenancy; environments: Record<string, EnvironmentConfig>; } /** * Reads platform config and resolves project topology. * * Validates project name and environment configuration. Does NOT * introspect app code for resources — that happens at deploy time via * ViteBuildProvider. * * Each app self-declares its platform topology via its own * `alepha.config.ts`. Run `alepha platform <op>` from the app's * directory; no monorepo-root orchestration here. */ declare class PlatformInspector { protected readonly log: import("alepha/logger").Logger; protected readonly alepha: Alepha; protected readonly fs: FileSystemProvider; protected readonly asker: Asker; protected readonly options: Readonly<{ name?: string | undefined; default?: string | undefined; tenancy?: "none" | "optional" | "required" | undefined; secrets?: { keys?: string[] | undefined; store?: "github" | undefined; environmentPattern?: string | undefined; } | undefined; environments: Record<string, { adapter: "cloudflare" | "vercel"; domain?: string | undefined; zone?: string | undefined; services?: { binding: string; service: string; }[] | undefined; jurisdiction?: "eu" | "fedramp" | undefined; accountId?: string | undefined; }>; } | undefined>; protected readonly naming: NamingService; /** * Resolve and validate the full platform configuration. * * Source priority: * 1. `platformOptions` atom (set by `alepha.config.ts` during the * configure hook) — local dev / from-source deploys. * 2. `dist/manifest.json` (written by `alepha build`) — pre-built * deploys via Alepha Rocket or any `--prebuilt` c