import { type ManagedCompatibility, type RebaseAppConfig, type RebaseBackendAppConfig, type RebaseProjectManifest } from "@rebasepro/types"; /** Runtime range written into new manifests. */ export declare const CURRENT_RUNTIME_RANGE = "^1"; /** Conventional locations, matching what `rebase init` scaffolds. */ export declare const DEFAULT_CONFIG_DIR = "config"; export declare const DEFAULT_FUNCTIONS_DIR = "backend/functions"; export declare const DEFAULT_CRONS_DIR = "backend/crons"; export declare const DEFAULT_SCHEMA_FILE = "backend/src/schema.generated.ts"; export interface ManifestValidationIssue { /** Dotted path to the offending value, e.g. `apps.web.output`. */ path: string; message: string; } export interface LoadedManifest { manifest: RebaseProjectManifest; /** Where it came from — a real file, or inferred from the directory layout. */ source: "file" | "synthesized"; /** Absolute path to `rebase.json`, when one exists. */ filePath?: string; } export declare class ManifestError extends Error { readonly issues: ManifestValidationIssue[]; constructor(message: string, issues?: ManifestValidationIssue[]); } /** * Validate a parsed manifest, collecting every problem. */ export declare function validateManifest(raw: unknown): { manifest?: RebaseProjectManifest; issues: ManifestValidationIssue[]; }; /** * Infer a manifest from a directory that does not have one. * * This mirrors exactly what the template scaffolds, which is what makes adopting * the manifest a no-op for existing projects: the synthesized result is what * they would have written by hand. * * The backend's `runtime` is inferred from whether the repository declares a * **Dockerfile** — the thing that actually builds an image — and from nothing * else. It used to be inferred from the presence of `backend/src/index.ts`, * which every scaffolded project had whether or not it wanted its own server, so * projects predating the manifest silently landed on the custom runtime and paid * for it (see `docs/cloud-deploy-workspace-vendoring.md`). */ export declare function synthesizeManifest(projectRoot: string): RebaseProjectManifest; export declare function manifestPath(projectRoot: string): string; export declare function manifestExists(projectRoot: string): boolean; /** * Read the manifest, falling back to a synthesized one. * * A malformed manifest throws — unlike a missing one. Silently ignoring a file * the developer wrote, and building something else instead, is the worst * available behaviour. */ export declare function loadManifest(projectRoot: string): LoadedManifest; /** * Write a manifest, with a trailing newline so it plays well with other tools. * * **Every key on disk survives.** This used to emit exactly `$schema`, `rebase` * and `apps`, so a rewrite deleted the rest of the file — and two commands with * no visible relationship to either key rewrite it: `rebase eject` and * `rebase apps init --force`. A repository that had committed * `"telemetry": false` lost its opt-out, and a multi-bucket project lost its * whole `storage` block, in a commit whose stated change was `runtime: custom`. * * So: the caller's manifest wins for what it models, the file supplies the rest. * `storage` and `telemetry` fall back to the file because the callers that * synthesize a manifest (`apps init --force`) cannot know them — they are * authored, not inferred — and unknown top-level keys are copied verbatim * rather than listed, since a hand-listed set loses the next key too. */ export declare function writeManifest(projectRoot: string, manifest: RebaseProjectManifest): string; /** Find the single backend app, if this repository declares one. */ export declare function findBackendApp(manifest: RebaseProjectManifest): { name: string; app: RebaseBackendAppConfig; } | undefined; /** Apps that produce build output, in the order they should be built. */ export declare function buildableApps(manifest: RebaseProjectManifest): { name: string; app: RebaseAppConfig; }[]; /** * Decide whether a project can run on the managed runtime, and say why not. * * "Not eligible" is never a dead end — it selects the custom-runtime path, which * still deploys. The reasons exist so the answer is actionable rather than a * verdict. */ export declare function assessManagedCompatibility(manifest: RebaseProjectManifest): ManagedCompatibility; /** * Resolve a backend app's directories against the conventions it omits. * * `hasCollections` replaces the old `mode: "cms" | "baas"` field. Where the * collections come from was never an independent choice: either they are * declared in code and the bundle ships them, or they are not and the runtime * introspects the live database at boot. Declaring it separately only created * the contradictory state — code-first declared, no collections anywhere. * * `hasConfig` is deliberately a **separate** question. A headless project has no * `config/collections`, but it may still ship a config package — that is where * the `storageAuthorize` hook lives, and storage is not under row-level * security, so without one the server refuses to boot with storage enabled. * Collapsing the two would leave a headless project nowhere to put it. */ export declare function resolveBackendPaths(app: RebaseBackendAppConfig, projectRoot: string): { config: string; functions: string; crons: string; schema: string; usersCollection: string; /** Whether a config package exists — hooks, storage authorization. */ hasConfig: boolean; /** Whether collections are declared in code, under `/collections`. */ hasCollections: boolean; };