@dbx-tools/appkit-autopg 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # @dbx-tools/appkit-autopg
2
+
3
+ `autopg()` is a one-line helper that fills in every Lakebase Postgres
4
+ env var the AppKit `lakebase` plugin needs from whatever fragments your
5
+ deployment actually carries. Run it once before `createApp(...)` and stop
6
+ hand-rolling connection strings:
7
+
8
+ ```ts
9
+ import { createApp, lakebase, server } from "@databricks/appkit";
10
+ import { autopg } from "@dbx-tools/appkit-autopg";
11
+
12
+ await autopg();
13
+ await createApp({ plugins: [server(), lakebase()] });
14
+ ```
15
+
16
+ ## Why a top-level helper instead of an AppKit plugin
17
+
18
+ AppKit's `static phase` field orders plugin `setup()` _invocation_, not
19
+ async _completion_. `lakebase.setup()` synchronously throws on a missing
20
+ `PGHOST` after its first `await`, so a sibling plugin that performs REST
21
+ discovery during `setup()` races and loses every time. Awaiting
22
+ `autopg()` before `createApp(...)` sidesteps the race - by the time any
23
+ plugin runs, `process.env` is fully populated.
24
+
25
+ ## What it accepts
26
+
27
+ `autopg()` looks at, in priority order:
28
+
29
+ 1. Explicit `autopg({ project, branch, endpoint, database, host, port, sslMode, autoCreate })`
30
+ 2. Env vars - the same ones `lakebase` already reads:
31
+ - `LAKEBASE_PROJECT`, `LAKEBASE_BRANCH`, `LAKEBASE_ENDPOINT`
32
+ - `PGHOST`, `PGDATABASE`, `PGPORT`, `PGSSLMODE`
33
+ 3. Whatever the address parser can recover from
34
+ `LAKEBASE_ENDPOINT` / `config.endpoint`
35
+
36
+ The address input is permissive - any of these work:
37
+
38
+ ```bash
39
+ # Canonical resource path
40
+ LAKEBASE_ENDPOINT="projects/dbx-tools/branches/main/endpoints/primary"
41
+
42
+ # Full Postgres URI (auth, host, db, sslmode all extracted)
43
+ LAKEBASE_ENDPOINT="postgresql://me%40databricks.com@ep-foo.database.azuredatabricks.net/databricks_postgres?sslmode=require"
44
+
45
+ # Bare Lakebase hostname (resolver reverse-looks-up the project)
46
+ LAKEBASE_ENDPOINT="ep-steep-forest-e199v43w.database.eastus2.azuredatabricks.net"
47
+
48
+ # Bare project id (resolver picks the default branch + endpoint + db)
49
+ LAKEBASE_ENDPOINT="dbx-tools"
50
+ ```
51
+
52
+ ## Three resolution modes
53
+
54
+ After parsing, the resolver fills gaps in this order:
55
+
56
+ 1. **Reverse-lookup** - given just a host, scan
57
+ `projects` -> `branches` -> `endpoints` for a matching
58
+ `status.hosts.host` and recover the owning resource path.
59
+ 2. **Pick default** - given a `project` (and optionally a `branch`),
60
+ prefer the server-marked default child (`status.default`,
61
+ `ENDPOINT_TYPE_READ_WRITE`, `databricks_postgres`) and fall back to
62
+ "the only one" when a listing returns a single result.
63
+ 3. **Auto-create** - when no projects exist at all, create one whose id
64
+ defaults to a slugified `projectUtils.name()` (override with
65
+ `autoCreate: "my-id"` or disable with `autoCreate: false`). The
66
+ create call is idempotent: an `ALREADY_EXISTS` response from a
67
+ concurrent boot is treated as success. Then poll the default
68
+ endpoint until `current_state` is `READY` or `IDLE`.
69
+
70
+ ## Options
71
+
72
+ ```ts
73
+ await autopg({
74
+ // Skip writing process.env (just inspect the returned record).
75
+ exportEnv: false,
76
+ // Pin individual fields - any of these short-circuit the resolver.
77
+ project: "dbx-tools",
78
+ branch: "main",
79
+ endpoint: "projects/dbx-tools/branches/main/endpoints/primary",
80
+ database: "databricks_postgres",
81
+ // Auto-create behavior.
82
+ autoCreate: false, // throw if no project exists
83
+ // autoCreate: "my-custom-id", // create with this id
84
+ });
85
+ ```
86
+
87
+ `autopg()` returns a `Resolved` record (`project`, `branch`, `endpoint`,
88
+ `database`, `host`, `port`, `sslMode`). When `exportEnv: true` (the
89
+ default) it also writes the same values to `process.env`, only filling
90
+ gaps - existing values are preserved.
91
+
92
+ ## Address parser
93
+
94
+ The address parser is exported as well if you want it without the
95
+ resolver wrapper:
96
+
97
+ ```ts
98
+ import { parseAddress } from "@dbx-tools/appkit-autopg";
99
+
100
+ parseAddress("postgresql://user@ep-foo.database.azuredatabricks.net/dbpg");
101
+ // { user, host, database, port?, sslMode?, project?, branch?, endpointId? }
102
+ ```
103
+
104
+ ## Required permissions
105
+
106
+ The Databricks user / SP behind `getWorkspaceClient()` needs
107
+ `postgres.projects.{list,create}`, `postgres.branches.list`,
108
+ `postgres.endpoints.{list,get}`, and `postgres.databases.list` on the
109
+ account.
110
+
111
+ ## License
112
+
113
+ Apache-2.0
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `@dbx-tools/appkit-autopg` - top-level Lakebase Postgres auto-
3
+ * discovery helper. Resolves project / branch / endpoint / database /
4
+ * host from env vars and the Databricks REST API, then writes the
5
+ * standard `PGHOST` / `PGDATABASE` / `LAKEBASE_ENDPOINT` env vars so
6
+ * the AppKit `lakebase` plugin can connect without manual wiring.
7
+ *
8
+ * ```ts
9
+ * import { autopg } from "@dbx-tools/appkit-autopg";
10
+ * import { createApp, lakebase, server } from "@databricks/appkit";
11
+ *
12
+ * await autopg();
13
+ * await createApp({ plugins: [lakebase(), server()] });
14
+ * ```
15
+ */
16
+ export * from "./src/address.js";
17
+ export * from "./src/autopg.js";
18
+ export * from "./src/resolver.js";
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `@dbx-tools/appkit-autopg` - top-level Lakebase Postgres auto-
3
+ * discovery helper. Resolves project / branch / endpoint / database /
4
+ * host from env vars and the Databricks REST API, then writes the
5
+ * standard `PGHOST` / `PGDATABASE` / `LAKEBASE_ENDPOINT` env vars so
6
+ * the AppKit `lakebase` plugin can connect without manual wiring.
7
+ *
8
+ * ```ts
9
+ * import { autopg } from "@dbx-tools/appkit-autopg";
10
+ * import { createApp, lakebase, server } from "@databricks/appkit";
11
+ *
12
+ * await autopg();
13
+ * await createApp({ plugins: [lakebase(), server()] });
14
+ * ```
15
+ */
16
+ export * from "./src/address.js";
17
+ export * from "./src/autopg.js";
18
+ export * from "./src/resolver.js";
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Flexible address parser for Lakebase Postgres connection inputs.
3
+ *
4
+ * Accepts whatever shape a user is likely to paste into
5
+ * `LAKEBASE_ENDPOINT` (or the matching config field) and extracts
6
+ * every recognizable piece. Whatever it can't recover is left for the
7
+ * REST resolver to discover.
8
+ *
9
+ * Recognized formats:
10
+ *
11
+ * - **Postgres URI** -
12
+ * `postgresql://user@host:port/db?sslmode=require` (also `postgres://`).
13
+ * Yields `user`, `host`, `port`, `database`, `sslMode`.
14
+ *
15
+ * - **Canonical endpoint resource path** -
16
+ * `projects/{p}/branches/{b}/endpoints/{e}` -
17
+ * yields `project`, `branch`, `endpointId`, and the original string as
18
+ * `endpoint` (already in lakebase's expected form).
19
+ *
20
+ * - **Database resource path** -
21
+ * `projects/{p}/branches/{b}/databases/{d}` -
22
+ * yields `project`, `branch`. The database leaf isn't surfaced because
23
+ * it's a resource id, not the Postgres database name; the resolver
24
+ * will look up the real `status.postgres_database` value via REST.
25
+ *
26
+ * - **Branch resource path** -
27
+ * `projects/{p}/branches/{b}` - yields `project`, `branch`.
28
+ *
29
+ * - **Project resource path** -
30
+ * `projects/{p}` - yields `project`.
31
+ *
32
+ * - **Bare hostname** -
33
+ * `ep-steep-forest-e199v43w.database.eastus2.azuredatabricks.net` -
34
+ * yields `host` only; the resolver reverse-looks up the owning
35
+ * endpoint to recover the resource path.
36
+ *
37
+ * - **Bare project id** -
38
+ * `dbx-tools-demo` (1-63 chars, lowercase letters/digits/hyphens) -
39
+ * yields `project`.
40
+ *
41
+ * Returns an empty object for inputs it doesn't recognize.
42
+ */
43
+ import type { SslMode } from "./resolver.js";
44
+ export interface ParsedAddress {
45
+ /** Lakebase project id. */
46
+ project?: string;
47
+ /** Branch id within the project. */
48
+ branch?: string;
49
+ /** Endpoint leaf id (last segment of an endpoint resource path). */
50
+ endpointId?: string;
51
+ /** Canonical endpoint resource path; only set for matching inputs. */
52
+ endpoint?: string;
53
+ /** Postgres database name (PGDATABASE) when parsed from a URI path. */
54
+ database?: string;
55
+ /** Postgres hostname. */
56
+ host?: string;
57
+ /** Postgres port. */
58
+ port?: number;
59
+ /** Postgres user (URI-decoded if encoded). */
60
+ user?: string;
61
+ /** Postgres TLS mode. */
62
+ sslMode?: SslMode;
63
+ }
64
+ /**
65
+ * Parse a Lakebase connection input into whatever pieces it carries.
66
+ * See module docstring for the supported formats. Returns `{}` for
67
+ * `undefined`, empty strings, and unrecognized inputs.
68
+ */
69
+ export declare function parseAddress(input: string | undefined | null): ParsedAddress;
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Flexible address parser for Lakebase Postgres connection inputs.
3
+ *
4
+ * Accepts whatever shape a user is likely to paste into
5
+ * `LAKEBASE_ENDPOINT` (or the matching config field) and extracts
6
+ * every recognizable piece. Whatever it can't recover is left for the
7
+ * REST resolver to discover.
8
+ *
9
+ * Recognized formats:
10
+ *
11
+ * - **Postgres URI** -
12
+ * `postgresql://user@host:port/db?sslmode=require` (also `postgres://`).
13
+ * Yields `user`, `host`, `port`, `database`, `sslMode`.
14
+ *
15
+ * - **Canonical endpoint resource path** -
16
+ * `projects/{p}/branches/{b}/endpoints/{e}` -
17
+ * yields `project`, `branch`, `endpointId`, and the original string as
18
+ * `endpoint` (already in lakebase's expected form).
19
+ *
20
+ * - **Database resource path** -
21
+ * `projects/{p}/branches/{b}/databases/{d}` -
22
+ * yields `project`, `branch`. The database leaf isn't surfaced because
23
+ * it's a resource id, not the Postgres database name; the resolver
24
+ * will look up the real `status.postgres_database` value via REST.
25
+ *
26
+ * - **Branch resource path** -
27
+ * `projects/{p}/branches/{b}` - yields `project`, `branch`.
28
+ *
29
+ * - **Project resource path** -
30
+ * `projects/{p}` - yields `project`.
31
+ *
32
+ * - **Bare hostname** -
33
+ * `ep-steep-forest-e199v43w.database.eastus2.azuredatabricks.net` -
34
+ * yields `host` only; the resolver reverse-looks up the owning
35
+ * endpoint to recover the resource path.
36
+ *
37
+ * - **Bare project id** -
38
+ * `dbx-tools-demo` (1-63 chars, lowercase letters/digits/hyphens) -
39
+ * yields `project`.
40
+ *
41
+ * Returns an empty object for inputs it doesn't recognize.
42
+ */
43
+ const URL_SCHEME_RE = /^(postgres|postgresql):\/\//i;
44
+ const RESOURCE_ENDPOINT_RE = /^projects\/([^/]+)\/branches\/([^/]+)\/endpoints\/([^/]+)$/;
45
+ const RESOURCE_DATABASE_RE = /^projects\/([^/]+)\/branches\/([^/]+)\/databases\/([^/]+)$/;
46
+ const RESOURCE_BRANCH_RE = /^projects\/([^/]+)\/branches\/([^/]+)$/;
47
+ const RESOURCE_PROJECT_RE = /^projects\/([^/]+)$/;
48
+ const PROJECT_ID_RE = /^[a-z][a-z0-9-]{0,61}[a-z0-9]$|^[a-z]$/;
49
+ const HOSTNAME_HINT_RE = /^[a-z0-9][a-z0-9-]*(\.[a-z0-9][a-z0-9-]*)+$/i;
50
+ /**
51
+ * Parse a Lakebase connection input into whatever pieces it carries.
52
+ * See module docstring for the supported formats. Returns `{}` for
53
+ * `undefined`, empty strings, and unrecognized inputs.
54
+ */
55
+ export function parseAddress(input) {
56
+ if (!input)
57
+ return {};
58
+ const s = input.trim();
59
+ if (!s)
60
+ return {};
61
+ if (URL_SCHEME_RE.test(s))
62
+ return parseUri(s);
63
+ if (s.startsWith("projects/"))
64
+ return parseResourcePath(s);
65
+ // Resource ids never contain dots; a dotted input must be a hostname.
66
+ if (HOSTNAME_HINT_RE.test(s) && s.includes("."))
67
+ return { host: s };
68
+ if (PROJECT_ID_RE.test(s))
69
+ return { project: s };
70
+ return {};
71
+ }
72
+ function parseUri(s) {
73
+ let url;
74
+ try {
75
+ url = new URL(s);
76
+ }
77
+ catch {
78
+ return {};
79
+ }
80
+ const result = {};
81
+ if (url.hostname)
82
+ result.host = url.hostname;
83
+ if (url.port) {
84
+ const port = Number.parseInt(url.port, 10);
85
+ if (!Number.isNaN(port))
86
+ result.port = port;
87
+ }
88
+ if (url.username) {
89
+ try {
90
+ result.user = decodeURIComponent(url.username);
91
+ }
92
+ catch {
93
+ // Malformed percent-escape; keep the raw form.
94
+ result.user = url.username;
95
+ }
96
+ }
97
+ const db = url.pathname.replace(/^\//, "");
98
+ if (db)
99
+ result.database = decodeURIComponent(db);
100
+ // Postgres clients accept `sslmode`; URL params are case-sensitive but
101
+ // we tolerate either since users paste both.
102
+ const sslmodeRaw = url.searchParams.get("sslmode") ?? url.searchParams.get("sslMode");
103
+ const sslmode = sslmodeRaw?.toLowerCase();
104
+ if (sslmode === "require" || sslmode === "disable" || sslmode === "prefer") {
105
+ result.sslMode = sslmode;
106
+ }
107
+ return result;
108
+ }
109
+ function parseResourcePath(s) {
110
+ const ep = RESOURCE_ENDPOINT_RE.exec(s);
111
+ if (ep) {
112
+ return {
113
+ project: ep[1],
114
+ branch: ep[2],
115
+ endpointId: ep[3],
116
+ endpoint: s,
117
+ };
118
+ }
119
+ // databases/{d} is a resource id (often kebab-case), not the actual
120
+ // Postgres database name. Surface project + branch only; the resolver
121
+ // will fetch the real `postgres_database` value.
122
+ const db = RESOURCE_DATABASE_RE.exec(s);
123
+ if (db)
124
+ return { project: db[1], branch: db[2] };
125
+ const br = RESOURCE_BRANCH_RE.exec(s);
126
+ if (br)
127
+ return { project: br[1], branch: br[2] };
128
+ const pr = RESOURCE_PROJECT_RE.exec(s);
129
+ if (pr)
130
+ return { project: pr[1] };
131
+ return {};
132
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Top-level Lakebase auto-discovery helper.
3
+ *
4
+ * Read this once at process startup BEFORE `createApp(...)` so the
5
+ * `lakebase` plugin (and anyone else who reads `process.env` during
6
+ * `setup()`) sees a fully-populated environment:
7
+ *
8
+ * ```ts
9
+ * import { autopg } from "@dbx-tools/appkit-autopg";
10
+ * import { createApp, lakebase, server } from "@databricks/appkit";
11
+ *
12
+ * await autopg(); // resolves + writes process.env
13
+ * await createApp({ plugins: [lakebase(), server()] });
14
+ * ```
15
+ *
16
+ * `autopg` is intentionally NOT an AppKit plugin. AppKit's `static phase`
17
+ * field only orders plugin `setup()` invocation, not async completion -
18
+ * `lakebase.setup()` calls `parsePoolConfig` synchronously after its
19
+ * first `await` and would throw on `PGHOST` before any sibling plugin's
20
+ * REST resolution could finish. Awaiting `autopg()` upfront sidesteps
21
+ * the race entirely.
22
+ *
23
+ * Inputs flow in this priority order:
24
+ * 1. Explicit `config.<field>` argument
25
+ * 2. Matching env var (`LAKEBASE_PROJECT`, `LAKEBASE_BRANCH`,
26
+ * `LAKEBASE_ENDPOINT`, `PGHOST`, `PGDATABASE`, `PGPORT`, `PGSSLMODE`)
27
+ * 3. Derived from the Lakebase Autoscaling REST API under
28
+ * `/api/2.0/postgres/` via the Databricks workspace client
29
+ *
30
+ * Resolved values are written back to `process.env` (only filling gaps;
31
+ * existing values are preserved) so the downstream `lakebase` plugin
32
+ * picks them up. Pass `{ exportEnv: false }` to keep `process.env`
33
+ * untouched and just inspect the returned record.
34
+ */
35
+ import { type Resolved, type ResolverInputs } from "./resolver.js";
36
+ /** Options accepted by {@link autopg}. */
37
+ export interface AutopgOptions extends ResolverInputs {
38
+ /**
39
+ * When `true` (the default), resolved values are written to
40
+ * `process.env` so the `lakebase` plugin sees them at startup.
41
+ * Set to `false` to leave `process.env` untouched and just receive
42
+ * the resolved record back.
43
+ */
44
+ exportEnv?: boolean;
45
+ }
46
+ /**
47
+ * Resolve Lakebase Postgres connection info from config + env (and the
48
+ * Databricks REST API when needed), write the resolved values to
49
+ * `process.env`, and return the fully-populated record.
50
+ *
51
+ * Always safe to call: when env already provides every field, it
52
+ * returns immediately without any network traffic.
53
+ *
54
+ * @throws when a `project` is set (directly or via env) but the
55
+ * Databricks API returns no branches / endpoints / databases to
56
+ * choose from. The error message lists the available candidates so
57
+ * the caller can pin the right one via env or config.
58
+ */
59
+ export declare function autopg(opts?: AutopgOptions): Promise<Resolved>;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Top-level Lakebase auto-discovery helper.
3
+ *
4
+ * Read this once at process startup BEFORE `createApp(...)` so the
5
+ * `lakebase` plugin (and anyone else who reads `process.env` during
6
+ * `setup()`) sees a fully-populated environment:
7
+ *
8
+ * ```ts
9
+ * import { autopg } from "@dbx-tools/appkit-autopg";
10
+ * import { createApp, lakebase, server } from "@databricks/appkit";
11
+ *
12
+ * await autopg(); // resolves + writes process.env
13
+ * await createApp({ plugins: [lakebase(), server()] });
14
+ * ```
15
+ *
16
+ * `autopg` is intentionally NOT an AppKit plugin. AppKit's `static phase`
17
+ * field only orders plugin `setup()` invocation, not async completion -
18
+ * `lakebase.setup()` calls `parsePoolConfig` synchronously after its
19
+ * first `await` and would throw on `PGHOST` before any sibling plugin's
20
+ * REST resolution could finish. Awaiting `autopg()` upfront sidesteps
21
+ * the race entirely.
22
+ *
23
+ * Inputs flow in this priority order:
24
+ * 1. Explicit `config.<field>` argument
25
+ * 2. Matching env var (`LAKEBASE_PROJECT`, `LAKEBASE_BRANCH`,
26
+ * `LAKEBASE_ENDPOINT`, `PGHOST`, `PGDATABASE`, `PGPORT`, `PGSSLMODE`)
27
+ * 3. Derived from the Lakebase Autoscaling REST API under
28
+ * `/api/2.0/postgres/` via the Databricks workspace client
29
+ *
30
+ * Resolved values are written back to `process.env` (only filling gaps;
31
+ * existing values are preserved) so the downstream `lakebase` plugin
32
+ * picks them up. Pass `{ exportEnv: false }` to keep `process.env`
33
+ * untouched and just inspect the returned record.
34
+ */
35
+ import { logUtils } from "@dbx-tools/appkit-shared";
36
+ import { applyToEnv, resolveConnection, } from "./resolver.js";
37
+ /**
38
+ * Resolve Lakebase Postgres connection info from config + env (and the
39
+ * Databricks REST API when needed), write the resolved values to
40
+ * `process.env`, and return the fully-populated record.
41
+ *
42
+ * Always safe to call: when env already provides every field, it
43
+ * returns immediately without any network traffic.
44
+ *
45
+ * @throws when a `project` is set (directly or via env) but the
46
+ * Databricks API returns no branches / endpoints / databases to
47
+ * choose from. The error message lists the available candidates so
48
+ * the caller can pin the right one via env or config.
49
+ */
50
+ export async function autopg(opts = {}) {
51
+ const { exportEnv = true, ...inputs } = opts;
52
+ const log = logUtils.logger("autopg");
53
+ const resolved = await resolveConnection(inputs, log);
54
+ if (exportEnv) {
55
+ applyToEnv(resolved);
56
+ log.info("env updated", redactForLog(resolved));
57
+ }
58
+ else {
59
+ log.info("resolved (env untouched)", redactForLog(resolved));
60
+ }
61
+ return resolved;
62
+ }
63
+ /** Strip resolved record to log-safe primitive fields. */
64
+ function redactForLog(resolved) {
65
+ return {
66
+ project: resolved.project,
67
+ branch: resolved.branch,
68
+ endpoint: resolved.endpoint,
69
+ database: resolved.database,
70
+ host: resolved.host,
71
+ port: resolved.port,
72
+ sslMode: resolved.sslMode,
73
+ };
74
+ }
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Lakebase Postgres connection resolver.
3
+ *
4
+ * Reads the same env vars the `lakebase` plugin consumes (`PGHOST`,
5
+ * `PGDATABASE`, `PGPORT`, `PGSSLMODE`, `LAKEBASE_ENDPOINT`) and fills in
6
+ * whichever pieces are missing using the Lakebase Autoscaling REST API
7
+ * under `/api/2.0/postgres/` via the Databricks workspace client.
8
+ *
9
+ * `LAKEBASE_ENDPOINT` (and `config.endpoint`) accept anything
10
+ * {@link parseAddress} understands - canonical resource paths, Postgres
11
+ * URIs, bare hostnames, or bare project ids. The resolver layers
12
+ * whatever pieces fall out of parsing under explicit config / env
13
+ * values, then fills the remaining gaps via the API:
14
+ *
15
+ * 1. Reverse-lookup: when a host is known but no resource path is,
16
+ * scan projects -> branches -> endpoints for a matching
17
+ * `status.hosts.host` and recover the owning project/branch/endpoint.
18
+ * 2. Pick: when a project is known but child resources aren't, prefer
19
+ * the server-side default (`status.default`, `ENDPOINT_TYPE_READ_WRITE`,
20
+ * `databricks_postgres`) and fall back to "the only one" when a
21
+ * listing returns a single result.
22
+ * 3. Auto-create: when no projects exist at all, create one whose
23
+ * id defaults to `projectUtils.name()` slugified (override
24
+ * with `config.autoCreate: "my-id"` or disable with
25
+ * `config.autoCreate: false`). The create call is idempotent - an
26
+ * `ALREADY_EXISTS` response from a concurrent boot is treated as
27
+ * success. Then poll the default endpoint until it reports
28
+ * `current_state` `READY` or `IDLE`.
29
+ *
30
+ * The {@link autopg} helper then writes the resolved values back to
31
+ * `process.env` so the downstream `lakebase` plugin picks them up.
32
+ *
33
+ * @see https://docs.databricks.com/api/workspace/postgres
34
+ */
35
+ import { type logUtils } from "@dbx-tools/appkit-shared";
36
+ /** Postgres TLS mode passed through to `pg`. */
37
+ export type SslMode = "require" | "disable" | "prefer";
38
+ /**
39
+ * User-supplied inputs (config or env) before any API resolution. Every
40
+ * field is optional - the resolver tries to fill in missing pieces from
41
+ * the Lakebase API when it has enough context (typically a `project`).
42
+ */
43
+ export interface ResolverInputs {
44
+ /** Lakebase project id, e.g. `my-app`. Triggers API discovery when set. */
45
+ project?: string;
46
+ /** Branch id within the project. Defaults to the server-marked default. */
47
+ branch?: string;
48
+ /**
49
+ * Lakebase address - accepts a canonical endpoint/branch/project
50
+ * resource path, a Postgres URI (`postgresql://user@host/db?...`),
51
+ * a bare Lakebase hostname, or a bare project id. Whatever pieces it
52
+ * carries seed the resolver before REST lookups happen. Reads from
53
+ * `LAKEBASE_ENDPOINT` when not set.
54
+ */
55
+ endpoint?: string;
56
+ /** Postgres database name (e.g. `databricks_postgres`). */
57
+ database?: string;
58
+ /** Postgres hostname; auto-derived from the endpoint when missing. */
59
+ host?: string;
60
+ /** Postgres port. Defaults to 5432. */
61
+ port?: number;
62
+ /** TLS mode. Defaults to `require`. */
63
+ sslMode?: SslMode;
64
+ /**
65
+ * What to do when no project exists in the workspace at all.
66
+ * - `undefined` (default): derive a project id from
67
+ * {@link projectUtils.name} (the host repo's `package.json`
68
+ * name) slugified to Lakebase id constraints, then create it.
69
+ * - `string`: create a new project with this exact id.
70
+ * - `false`: skip creation and throw with a clear error message.
71
+ */
72
+ autoCreate?: string | false;
73
+ }
74
+ /** Fully-resolved connection. `port` and `sslMode` always have a value. */
75
+ export interface Resolved {
76
+ project?: string;
77
+ branch?: string;
78
+ endpoint?: string;
79
+ database?: string;
80
+ host?: string;
81
+ port: number;
82
+ sslMode: SslMode;
83
+ }
84
+ /**
85
+ * Pull resolver inputs from `process.env`, parse the address blob, and
86
+ * layer explicit config on top with this precedence:
87
+ *
88
+ * `config.<field>` > matching env var > whatever {@link parseAddress}
89
+ * recovered from the `endpoint` / `LAKEBASE_ENDPOINT` blob.
90
+ */
91
+ export declare function readInputs(config: ResolverInputs): ResolverInputs;
92
+ /**
93
+ * Resolve a fully-populated Postgres connection record from config + env.
94
+ *
95
+ * Returns immediately without network traffic when env already supplies
96
+ * `endpoint`, `host`, and `database`. Otherwise issues REST calls and
97
+ * may auto-create a project (see module docstring).
98
+ */
99
+ export declare function resolveConnection(config: ResolverInputs, log: logUtils.Logger): Promise<Resolved>;
100
+ /**
101
+ * Write resolved values back to `process.env` so the `lakebase` plugin
102
+ * (which reads env directly) picks them up during its own `setup()`.
103
+ * Existing env values are preserved; only missing keys are filled in,
104
+ * which keeps explicit overrides authoritative.
105
+ */
106
+ export declare function applyToEnv(resolved: Resolved): void;
107
+ /** Parse `projects/{p}/branches/{b}/endpoints/{e}` into its components. */
108
+ export declare function parseEndpointName(endpoint: string): {
109
+ project: string;
110
+ branch: string;
111
+ endpointId: string;
112
+ } | null;
113
+ /** Parse `projects/{p}/branches/{b}/databases/{d}` into its components. */
114
+ export declare function parseDatabaseName(database: string): {
115
+ project: string;
116
+ branch: string;
117
+ databaseId: string;
118
+ } | null;