import { ConnectionOptions } from '../contracts/IDBSQLClient'; /** * Shape consumed by the napi-binding's `openSession()` (see * `native/kernel/index.d.ts`). Mirrors `ConnectionOptions` in the binding's * `.d.ts`; declared locally to avoid coupling the JS-side adapter to the * auto-generated TS file. * * Discriminated by `authMode`: * - `'Pat'` → `token` is the PAT. * - `'OAuthM2m'` → `oauthClientId` + `oauthClientSecret` drive a * kernel-side client_credentials exchange. * - `'OAuthU2m'` → `oauthRedirectPort` overrides the kernel default; * everything else (client_id, scopes, callback timeout, * token_url_override) uses kernel defaults. * * The `authMode` string literals MUST match the napi-emitted `AuthMode` * variant names verbatim (`'Pat'`, `'OAuthM2m'`, `'OAuthU2m'` — napi-rs's * `#[napi(string_enum)]` without an explicit case option emits the * Rust variant identifier as-is). We duplicate the values here instead * of importing `AuthMode` from `native/kernel/index.d.ts` because that * file declares `AuthMode` as `export const enum`, which is * incompatible with `isolatedModules` and a runtime-coupling hazard. * The Rust source of truth lives at `native/kernel/src/database.rs`. */ /** * Session-level defaults shared across all auth-mode variants. * * Mirrors `ConnectionOptions.catalog` / `.schema` / `.sessionConf` on * the napi binding (kernel `Session::builder().defaults(DefaultOpts)` * and `.session_conf(HashMap)` — the routes that actually populate kernel * `CreateSession.catalog` / `.schema` / `.session_confs`). * * Per-statement overrides do not exist on the kernel surface; both * pyo3 and napi expose catalog / schema / sessionConf only at session * creation. Mirror that here so the adapter doesn't promise a * capability the binding can't honour. */ export interface KernelSessionDefaults { catalog?: string; schema?: string; sessionConf?: Record; /** * Render `INTERVAL` / `DURATION` result columns as strings * (kernel `ResultConfig.intervals_as_string`). The kernel default is * native Arrow `month_interval` / `duration[us]`, but the NodeJS * Thrift driver surfaces intervals as strings — so the kernel path sets * this `true` so its result shape is a byte-compatible drop-in for the * Thrift backend. Omitting it falls back to the kernel's native types. */ intervalsAsString?: boolean; /** * Render complex (`ARRAY` / `MAP` / `STRUCT` / `VARIANT`) result * columns as JSON strings (kernel `ResultConfig.complex_types_as_json`). * Left unset on the kernel path: native Arrow nested types already decode * identically to the Thrift backend through the shared Arrow converter, * so forcing JSON here would *introduce* a divergence rather than * remove one. */ complexTypesAsJson?: boolean; /** * Per-session kernel connection-pool size * (kernel `ConnectionOptions.max_connections`). Validated as a positive * integer within the napi `u32` range by `buildKernelConnectionOptions`. */ maxConnections?: number; /** * Retry/backoff tuning forwarded to the kernel (which owns the retry loop * on the kernel path). These mirror the driver's `ClientConfig` retry knobs — * the same ones the Thrift `HttpRetryPolicy` uses — converted from the * connector's milliseconds to the kernel's whole seconds, so a single * retry config governs both backends. Unset ⇒ kernel default policy. * Map onto the napi `ConnectionOptions.retry{Min,Max}WaitSecs` / * `retryMaxAttempts` / `retryOverallTimeoutSecs` (see `buildKernelRetryOptions`). */ retryMinWaitSecs?: number; retryMaxWaitSecs?: number; /** **Total** attempts (kernel converts to retries-after-first internally). */ retryMaxAttempts?: number; retryOverallTimeoutSecs?: number; } /** * TLS options shared across all auth-mode variants. Mirror the napi * binding's `ConnectionOptions.checkServerCertificate` / `.customCaCert` * (kernel `Session::builder().tls(TlsConfig)`). * * The napi shape takes `customCaCert` as a `Buffer` only; the public * `ConnectionOptions` additionally accepts a PEM string, which * `buildKernelConnectionOptions` normalises to a `Buffer` before crossing * the FFI boundary. */ export interface KernelTlsOptions { /** * Verify the server's TLS certificate. The kernel backend is * **secure-by-default**: omitting this leaves the kernel default of * `true` (full chain + hostname verification). Set `false` only to opt * into the insecure, accept-anything mode (analogous to Thrift's * `rejectUnauthorized: false`); prefer pairing strict checking with * `customCaCert` over disabling verification entirely. */ checkServerCertificate?: boolean; /** * Verify the server certificate's hostname (hostname-vs-SNI), independently * of chain validation. Omit ⇒ kernel default (on). `false` skips only the * hostname check. No-op when `checkServerCertificate` is `false`. Mirrors * the kernel napi `checkServerCertificateHostname` / Python * `tls_verify_hostname`. */ checkServerCertificateHostname?: boolean; /** PEM-encoded CA bytes to add to the trust store. */ customCaCert?: Buffer; /** * PEM-encoded client certificate for mutual TLS (kernel * `TlsConfig::client_cert_pem`). Paired with {@link clientKeyPem} — * `buildKernelTlsOptions` rejects supplying only one before the FFI hop. * The napi shape takes a `Buffer`; the public surface also accepts a * PEM string, normalised here. */ clientCertPem?: Buffer; /** * PEM-encoded private key for the mTLS client certificate (kernel * `TlsConfig::client_key_pem`). Paired with {@link clientCertPem}. */ clientKeyPem?: Buffer; } /** * HTTP options shared across all auth-mode variants. Mirrors the napi * binding's `ConnectionOptions.customHeaders` (kernel * `HttpConfig::custom_headers`). * * Carries the extra request headers the kernel path sends on every request: * the caller's `customHeaders` plus the composed `User-Agent` (the kernel * appends a `User-Agent` entry to its base UA rather than replacing it). * * An **ordered list** of `{ name, value }` pairs — the napi shape * (`Array`), which mirrors the kernel core's * `Vec<(String, String)>` and the Python connector's `http_headers` * `List[Tuple[str, str]]`. Order is preserved and duplicate names are * allowed (e.g. a caller `User-Agent` followed by the connector's, which * the kernel folds last-wins). */ export interface KernelHttpOptions { customHeaders?: Array<{ name: string; value: string; }>; socketTimeoutMs?: number; } /** * HTTP(S) proxy forwarded to the napi binding's `ConnectionOptions.proxy` * (kernel `ProxyConfig`). The public `ConnectionOptions.proxy` is the * Thrift-shaped `{protocol, host, port, auth}`; `buildKernelProxyOptions` * maps it onto the kernel's structured proxy input — `url` composed from * `protocol://host:port`, with `auth.{username,password}` forwarded as * separate basic-auth fields (NOT embedded in the URL, so no percent-encoding * footgun) and the `noProxy` host list forwarded as `bypassHosts`. The same * connection option therefore works identically on both backends. */ export interface KernelProxyOptions { proxy?: { url: string; username?: string; password?: string; bypassHosts?: string; }; } export type KernelNativeConnectionOptions = KernelSessionDefaults & KernelTlsOptions & KernelHttpOptions & KernelProxyOptions & ({ hostName: string; httpPath: string; authMode: 'Pat'; token: string; } | { hostName: string; httpPath: string; authMode: 'OAuthM2m'; oauthClientId: string; oauthClientSecret: string; oauthScopes?: Array; } | { hostName: string; httpPath: string; authMode: 'OAuthU2m'; oauthRedirectPort: number; oauthScopes?: Array; oauthClientId?: string; }); /** * Reject inputs that pass `typeof === 'string' && length > 0` but are * structurally useless as credentials: whitespace-only strings, and the * literal strings `'undefined'` / `'null'` (case-insensitive) that buggy * shell exports (e.g. `export FOO="$UNSET_VAR"`) produce. Surfacing * these here means an OAuth flow's `invalid_client` from the workspace * is always a real credential mismatch, never a malformed-input passthrough. * * Exported so the integration-test env-gate can reuse the same predicate * and stay in lockstep with production (B-3 fix). */ export declare function isBlankOrReserved(s: string): boolean; /** * Normalise the public TLS options into the napi shape. * * - `checkServerCertificate` passes through verbatim (only when set; an * absent value leaves the kernel default, which is secure — verify on). * - `checkServerCertificateHostname` passes through verbatim — the * independent hostname-vs-SNI toggle (kernel applies it only when the * master verify toggle is on). Mirrors Python's `tls_verify_hostname`. * - `customCaCert` accepts a PEM string or `Buffer`; normalised to a * `Buffer` via {@link normalizePemBytes}. * - `clientCertPem` / `clientKeyPem` carry the mutual-TLS client identity. * They must be supplied **together** — supplying only one is rejected * here with an actionable error (rather than waiting for the kernel's * `InvalidArgument` at `openSession`). Each accepts a PEM string or * `Buffer`, normalised the same way. * * Throws `HiveDriverError` when a cert/key is empty, mis-typed, lacks the * expected PEM header, or when only one half of the mTLS pair is set. */ export declare function buildKernelTlsOptions(options: ConnectionOptions): KernelTlsOptions; export declare function buildKernelHttpOptions(options: ConnectionOptions): KernelHttpOptions; /** * Validate the user-supplied `ConnectionOptions` and build the * napi-binding's connection-options shape. * * Supported auth modes: * - PAT: `authType: 'access-token'` (or undefined, which already means * PAT throughout the existing driver — see * `DBSQLClient.createAuthProvider`). * - OAuth M2M: `authType: 'databricks-oauth'` + `oauthClientId` + * `oauthClientSecret`. Kernel handles OIDC discovery, client_credentials * exchange, and re-auth on expiry internally. * - OAuth U2M: `authType: 'databricks-oauth'` + NO `oauthClientId` and * NO `oauthClientSecret`. Kernel runs the PKCE auth-code dance (opens * a browser, listens on localhost:8030, exchanges the code, persists * to `~/.config/databricks-sql-kernel/oauth/{sha256}.json`). * * **Flow selection — DELIBERATE DIVERGENCE FROM THRIFT.** Thrift's * `DBSQLClient.createAuthProvider` (`DBSQLClient.ts:216`) keys off the * *secret* (`oauthClientSecret === undefined ? U2M : M2M`), so a custom * `oauthClientId` with no secret runs U2M with that id. kernel instead keys * off `oauthClientId` *presence* (id present → M2M, absent → U2M). The * trade-off: keying off the id means a caller who set an id but * typoed/forgot the secret gets the actionable M2M "secret is required" * error instead of being silently routed to U2M (which would hide their * intent). The cost is two real behavioural gaps vs Thrift: * 1. `oauthClientId` + no secret → Thrift runs U2M; kernel throws * `AuthenticationError` (M2M secret required). * 2. kernel U2M has NO custom-client-id support — the kernel hardcodes * `client_id = "databricks-cli"`, and kernel rejects any `oauthClientId` * on the U2M arm. Thrift U2M honours a custom `clientId`. * Both are documented limitations of the M0 kernel OAuth surface, not bugs. * * Out of scope on the OAuth paths (rejected with a clear error): * - `azureTenantId` / `useDatabricksOAuthInAzure` → Microsoft Entra * direct flow. The kernel uses workspace-OIDC discovery (which works * against Azure workspaces too — they serve `/oidc/.well-known/...`) * and does not implement the Entra-direct scope-rewrite path. * - `persistence` on M2M → M2M tokens are not cached (re-issuing is * cheap; no refresh token). * - `persistence` on U2M → custom token store is a parity gap; * requires kernel-side `AuthConfig::External` plumbing. The kernel's * auto-disk-cache works for the standard flow today. * * Ambiguity: * - PAT path: rejects when OAuth fields (`oauthClientId` / * `oauthClientSecret`) are simultaneously set. * - OAuth path: rejects when `token` is set alongside OAuth fields. * * Throws: * - `AuthenticationError` for missing/blank required credentials. * - `HiveDriverError` for unsupported auth modes / Azure-direct / * custom persistence / ambiguous combinations. */ /** * Convert the driver's `ClientConfig` retry knobs (milliseconds, total-attempt * count) into the kernel's `ConnectionOptions` retry kwargs (whole seconds). * The kernel owns the retry loop on the kernel path, so forwarding these keeps kernel * and Thrift governed by one retry config. `retryMaxAttempts` is a TOTAL attempt * count on both sides (the kernel converts to retries-after-first internally), * so it passes through directly. Sub-second delays round to the nearest second * (the kernel's granularity); all values are clamped into the napi `u32` range. */ export declare function buildKernelRetryOptions(config: { retryMaxAttempts?: number; retriesTimeout?: number; retryDelayMin?: number; retryDelayMax?: number; }): Pick; /** * Map the public `ConnectionOptions.proxy` (`{protocol, host, port, auth}` — * the same shape the Thrift backend accepts) onto the kernel's structured napi * proxy input. The `url` is composed from `protocol://host:port` (no embedded * credentials); `auth.{username,password}` are forwarded as separate * basic-auth fields (the kernel applies them via reqwest `Proxy::basic_auth`), * avoiding any URL percent-encoding footgun. The `noProxy` host list (a driver * option, not on the published `.d.ts`) is forwarded as `bypassHosts`. The * kernel accepts only `http://` / `https://`; a SOCKS protocol surfaces a clear * kernel error at connect (reqwest SOCKS support is not compiled in). */ export declare function buildKernelProxyOptions(options: ConnectionOptions): KernelProxyOptions; export declare function buildKernelConnectionOptions(options: ConnectionOptions): KernelNativeConnectionOptions;