import type { CollectionConfig } from "@rebasepro/types"; /** * Why is a dependency's binary missing — never installed, or installed with its * build script blocked? * * These need opposite advice, and getting it wrong is not a cosmetic miss. pnpm * 10+ refuses to run a dependency's lifecycle scripts unless it is allowlisted * (`pnpm.onlyBuiltDependencies`, or `allowBuilds` in `pnpm-workspace.yaml`). * `@ariga/atlas` downloads its platform binary in `preinstall`, so a blocked * script leaves a state that looks like a successful install: the package is on * disk with its `install.js` and `package.json`, `node_modules/.bin` is empty, * the install exits 0, and the only signal is `ERR_PNPM_IGNORED_BUILDS` several * screens up. * * Telling somebody in that state to install the package again sends them round * the same loop forever — the add succeeds, the script is blocked again, * nothing changes. Verified by doing it: `pnpm add @ariga/atlas` into a bare * project yields exactly this, three "Failed to create bin … ENOENT" warnings * and no binary. * * Resolution is attempted from the user's project first and this package * second, matching the order {@link resolveLocalBin} searches — the driver may * be installed a level up from where the command runs. */ export declare function diagnoseMissingBin(packageName: string): "not-installed" | "build-script-blocked"; export declare function resolveLocalBin(binName: string): string | null; export declare function getTableIncludesFromCollections(allCollections: CollectionConfig[]): Promise; export declare function getTableIncludes(collectionsPath: string): Promise; /** * Load a project's collections the way the Atlas-facing commands need them. * * Deliberately forgiving — a file that fails to import is skipped rather than * fatal — because the callers use this to *narrow* what Atlas may touch, and a * hard failure here would block a push over an unrelated broken file. Callers * that cannot tolerate a partial answer (the table excludes, which fail closed) * check the result themselves. */ export declare function loadCollectionsForCli(collectionsPath: string): Promise; export declare function getDevDatabaseUrl(databaseUrl: string): string; export declare function ensureDevDatabaseExists(databaseUrl: string, devDatabaseUrl: string): Promise; /** * The generated SQL for the project's `search` blocks, if it has any. * * @param drizzleDir directory holding the generated SQL. Defaults to `drizzle` * under the working directory. */ export declare function readSearchDdl(drizzleDir?: string): string; /** * Bring the search column, its index and their helpers up to date. * * Runs *after* Atlas, not before: the statements are `ALTER TABLE ... ADD * COLUMN`, so the table has to exist. Atlas is told to ignore these objects * entirely (`getSearchExcludes`) — it cannot manage them, and left to itself it * would plan a `DROP COLUMN` for every one, since they are absent from the * desired state it was given. * * A no-op when no collection declared `search`. A failure is *not* swallowed: * silently pushing a schema whose search column never appeared is how a * collection ends up with search configured, no error anywhere, and no results. */ export declare function applySearchDdl(databaseUrl: string, drizzleDir?: string): Promise; /** * Glob patterns keeping Atlas away from the search objects. * * Returns an empty list — and so changes nothing — for a project with no * `search` block, which is every project that has not opted in. */ export declare function getSearchExcludes(collectionsPath: string): Promise; /** * Give the dev database the search helper functions before Atlas plans. * * Excluding the search column keeps Atlas from *diffing* it, but not from * materialising the inspected schema — column and all — in the dev database to * analyse the plan against. That replay is where a push against an * already-searchable database died with `function public.rebase_search_text * (jsonb) does not exist`: the column came across, the function it calls did * not, because Atlas will not carry a function at all. * * Only the extensions and functions, never the tables: the dev database holds * whatever Atlas puts there and nothing of ours. * * Best-effort by design. Failing here would block a push for a project whose * collections merely failed to import, and if the functions really are needed * and really are missing, Atlas says so a moment later in its own words. */ export declare function seedDevDatabaseSearchHelpers(devDatabaseUrl: string, collectionsPath: string): Promise; /** * Query the live database for every user table/view outside the system * catalogs. Separated from {@link getTableExcludes} so its failure mode can * be handled explicitly (fail closed) and so tests can inject a stub. */ export declare function queryExistingTables(databaseUrl: string): Promise; /** * Raised when the exclude list could not be built. `db push` MUST abort on * this rather than continue: the exclude list is the only thing shielding * non-collection (user/system) tables from the auto-approved declarative * apply. A partial list — the old fail-open behaviour — meant a transient * introspection hiccup dropped every table not present in `schema.sql`. */ export declare class ExcludeIntrospectionError extends Error { readonly cause?: unknown | undefined; constructor(message: string, cause?: unknown | undefined); } /** * Build the `--exclude` list that protects tables Rebase doesn't manage from * the declarative apply. Anything not backing a collection (or its M2M * junctions) is excluded so Atlas never drops it. * * Fails **closed**: if the database can't be introspected we cannot know * which tables to protect, so we throw {@link ExcludeIntrospectionError} * instead of returning a near-empty list and letting the caller drop * everything. * * `deps` is injectable for tests; production uses the real pg-backed queries. */ export declare function getTableExcludes(databaseUrl: string, collectionsPath: string, deps?: { queryExistingTables?: (databaseUrl: string) => Promise; getIncludes?: (collectionsPath: string) => Promise; }): Promise; /** * Ask a yes/no question on an interactive terminal. * * The `isTTY` guard is the contract, not an optimisation: non-interactive * shells (CI, pipes, agents) can never answer, and `readline` on a * non-TTY stdin resolves with whatever the pipe happens to contain — or never * resolves at all. Returning false there is what makes an unattended * `db push` abort instead of auto-confirming a destructive change. Callers * should already have gone through {@link decidePushSafety}, which decides * whether interactive confirmation is even possible; this is the backstop. * * Lives here rather than in cli.ts so it is reachable from a test — cli.ts uses * `import.meta` and cannot be imported by the jest runner. */ export declare function promptConfirm(question: string): Promise;