/** * Read a repository well enough to write its CI workflow. * * Every function here guesses. That is deliberate and it is the opposite of * what the engine does, so the distinction is worth stating plainly: * * The engine refuses to guess because its output is a *claim about the * developer's schema*, made behind their back, on every commit. A wrong guess * there is a false finding, and a tool that cries wolf is uninstalled inside a * week. * * A scaffold's output is a file in a pull request. It is read before it runs, * by the one person who knows the answer, and a wrong guess is a line they fix * in the diff. The cost of guessing is a correction; the cost of *not* * guessing is that hand-writing a workflow stays a decision, and almost nobody * makes it. * * Two rules keep that from sliding: * * - Every guess is reported with the evidence it came from, in the terminal * and in the generated file, in words that name a path the reader can go * and look at. * - Where nothing was found, nothing is invented. The migration step is left * present, commented, and marked as the one line to fill in, and the run it * produces comes back inconclusive rather than green. */ export type MigrationTool = "supabase" | "prisma" | "drizzle" | "sql"; /** * Precedence when a repository shows evidence of more than one. * * `supabase/migrations` first because when it exists it is the thing actually * applied to the database — a Drizzle or Prisma schema alongside it is usually * generating types, not building the database. This ordering is a guess like * any other, so it is never silent: everything found is listed, and * `--migrations` overrides it. */ export declare const TOOL_ORDER: readonly MigrationTool[]; export declare const TOOL_LABELS: Record; /** * The host port `supabase db start` binds, out of `[db] port` in config.toml. * * Defaulted rather than assumed. A project that moved the port off 54322 — * common when two Supabase projects share a machine — would otherwise get a * workflow pointing at nothing, and Crossline would fail to connect for a * reason that has nothing to do with anybody's authorization rules. */ export declare function supabaseDbPort(configToml: string): string | null; export interface ToolMatch { tool: MigrationTool; /** * Which command shape the evidence points at. `deploy` applies migration * files that already exist; `push` diffs a schema straight onto the database * for projects that never generate migration files. Getting this wrong is * the difference between a schema and an empty database. */ variant: "deploy" | "push"; /** Paths that were found, as a reader can check them against their tree. */ evidence: string[]; /** Directory the migrations live in, where that is meaningful. */ dir?: string; /** Supabase only: the host port `db start` will bind. */ port?: string; } export type PackageManager = "npm" | "pnpm" | "yarn" | "bun"; export interface Guess { value: T; /** * Where the value came from, as a clause that reads directly after the * value: "Node 22 — from .nvmrc", "Node 22 — the current LTS; nothing in the * repository pinned one". Written out in full rather than as a key, because * a reader checking a guess needs the file it came from, not a label. */ source: string; } export interface CiDetection { /** Every migration tool with evidence in the tree, in precedence order. */ matches: ToolMatch[]; /** The one the workflow will use, or null when nothing was found. */ chosen: ToolMatch | null; /** True when `chosen` came from `--migrations` rather than from the tree. */ forced: boolean; node: Guess; pm: Guess; /** Major version of the package manager, when it could be pinned. */ pmVersion: string | null; /** Yarn 2+ takes `--immutable`; yarn 1 takes `--frozen-lockfile`. */ yarnBerry: boolean; branch: Guess; /** * Set when this repository declares an application to check and no database * at all, which is the shape of a project whose data lives somewhere * Crossline has no driver for. * * The workflow it produces has no Postgres service and no migration step, * because there would be nothing to put in either. That is a real difference * in kind, so the bar for it is high and every part comes from what the * developer wrote down rather than from an absence: `crossline.config.json` * names an API target or a server block, names no `db`, and no migration tool * was found in the tree. A repository with no config file at all is *not* * this — it is a repository that has not been set up yet, and the ordinary * workflow with its FILL THIS IN step is the honest answer there. */ apiOnly: { target: string | null; evidence: string; } | null; } /** * The `out` directory a drizzle config names, so the journal can be looked for * in the right place. * * A regex over the config rather than importing it: importing means executing * whatever the repository's config file does, at scaffold time, which is not a * thing a tool that only wants to read a directory name should ever do. */ export declare function drizzleOutDir(configText: string): string | null; export interface DetectOptions { /** From `--migrations`. Overrides precedence, and is reported as forced. */ migrations?: MigrationTool | "none"; } /** Everything the workflow renderer needs, with the evidence for each part. */ export declare function detectCi(cwd: string, opts?: DetectOptions): CiDetection;