@databricks/appkit 0.51.0 → 0.52.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/CLAUDE.md +3 -0
- package/dist/agents/databricks.d.ts +2 -3
- package/dist/agents/databricks.d.ts.map +1 -1
- package/dist/agents/databricks.js +5 -4
- package/dist/agents/databricks.js.map +1 -1
- package/dist/agents/supervisor-api.d.ts.map +1 -1
- package/dist/agents/supervisor-api.js +3 -1
- package/dist/agents/supervisor-api.js.map +1 -1
- package/dist/appkit/package.js +1 -1
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +4 -2
- package/dist/cache/index.js.map +1 -1
- package/dist/connectors/files/client.js +2 -1
- package/dist/connectors/files/client.js.map +1 -1
- package/dist/connectors/genie/client.d.ts +2 -4
- package/dist/connectors/genie/client.js +2 -3
- package/dist/connectors/genie/client.js.map +1 -1
- package/dist/connectors/jobs/client.d.ts +2 -2
- package/dist/connectors/jobs/client.js +2 -1
- package/dist/connectors/jobs/client.js.map +1 -1
- package/dist/connectors/serving/client.d.ts +1 -1
- package/dist/connectors/serving/client.js +2 -1
- package/dist/connectors/serving/client.js.map +1 -1
- package/dist/connectors/sql-warehouse/client.js +2 -1
- package/dist/connectors/sql-warehouse/client.js.map +1 -1
- package/dist/connectors/sql-warehouse/defaults.js.map +1 -1
- package/dist/connectors/sql-warehouse/warehouse-status-emitter.js.map +1 -1
- package/dist/connectors/vector-search/client.js.map +1 -1
- package/dist/context/client-options.js.map +1 -1
- package/dist/context/execution-context.d.ts +1 -1
- package/dist/context/service-context.d.ts +2 -1
- package/dist/context/service-context.d.ts.map +1 -1
- package/dist/context/service-context.js +8 -5
- package/dist/context/service-context.js.map +1 -1
- package/dist/core/appkit.d.ts +2 -1
- package/dist/core/appkit.d.ts.map +1 -1
- package/dist/core/appkit.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +4 -1
- package/dist/internal-telemetry/reporter.js.map +1 -1
- package/dist/plugins/analytics/analytics.d.ts.map +1 -1
- package/dist/plugins/analytics/analytics.js.map +1 -1
- package/dist/plugins/analytics/query.js.map +1 -1
- package/dist/plugins/analytics/result-delivery.js.map +1 -1
- package/dist/plugins/files/plugin.js +4 -3
- package/dist/plugins/files/plugin.js.map +1 -1
- package/dist/plugins/files/types.d.ts +2 -1
- package/dist/plugins/files/types.d.ts.map +1 -1
- package/dist/plugins/jobs/plugin.js.map +1 -1
- package/dist/plugins/jobs/types.d.ts +2 -1
- package/dist/plugins/jobs/types.d.ts.map +1 -1
- package/dist/plugins/lakebase/lakebase.d.ts.map +1 -1
- package/dist/plugins/lakebase/lakebase.js +5 -4
- package/dist/plugins/lakebase/lakebase.js.map +1 -1
- package/dist/stream/arrow-stream-processor.js.map +1 -1
- package/dist/type-generator/index.js +3 -2
- package/dist/type-generator/index.js.map +1 -1
- package/dist/type-generator/mv-registry/describe.js.map +1 -1
- package/dist/type-generator/query-registry.js +3 -2
- package/dist/type-generator/query-registry.js.map +1 -1
- package/dist/type-generator/serving/fetcher.js +2 -1
- package/dist/type-generator/serving/fetcher.js.map +1 -1
- package/dist/type-generator/serving/generator.js +3 -2
- package/dist/type-generator/serving/generator.js.map +1 -1
- package/dist/type-generator/statement-result.js.map +1 -1
- package/dist/type-generator/vite-plugin.d.ts.map +1 -1
- package/dist/type-generator/vite-plugin.js +3 -2
- package/dist/type-generator/vite-plugin.js.map +1 -1
- package/dist/type-generator/warehouse-status.js.map +1 -1
- package/dist/workspace-client/client.js +57 -0
- package/dist/workspace-client/client.js.map +1 -0
- package/dist/workspace-client/errors.d.ts +2 -0
- package/dist/workspace-client/errors.js +3 -0
- package/dist/workspace-client/factory.d.ts +19 -0
- package/dist/workspace-client/factory.d.ts.map +1 -0
- package/dist/workspace-client/factory.js +24 -0
- package/dist/workspace-client/factory.js.map +1 -0
- package/dist/workspace-client/index.d.ts +4 -0
- package/dist/workspace-client/index.js +5 -0
- package/dist/workspace-client/legacy.d.ts +29 -0
- package/dist/workspace-client/legacy.d.ts.map +1 -0
- package/dist/workspace-client/legacy.js +23 -0
- package/dist/workspace-client/legacy.js.map +1 -0
- package/dist/workspace-client/types.d.ts +49 -0
- package/dist/workspace-client/types.d.ts.map +1 -0
- package/docs/api/appkit/Class.DatabricksAdapter.md +2 -3
- package/docs/api/appkit/Function.createApp.md +9 -9
- package/docs/api/appkit/Function.createWorkspaceClient.md +28 -0
- package/docs/api/appkit/Interface.WorkspaceClient.md +119 -0
- package/docs/api/appkit/Interface.WorkspaceClientOptions.md +47 -0
- package/docs/api/appkit.md +3 -0
- package/llms.txt +3 -0
- package/package.json +1 -1
- package/sbom.cdx.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite-plugin.js","names":[],"sources":["../../src/type-generator/vite-plugin.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { WorkspaceClient } from \"@databricks/sdk-experimental\";\nimport type { Plugin } from \"vite\";\nimport { METRIC_CONFIG_FILE } from \"../../../shared/src/schemas/metric-fqn\";\nimport { createLogger } from \"../logging/logger\";\nimport {\n ANALYTICS_TYPES_FILE,\n generateFromEntryPoint,\n TYPES_DIR,\n TypegenFatalError,\n TypegenSyntaxError,\n} from \"./index\";\nimport type { PreflightMode } from \"./preflight\";\nimport {\n getWarehouseState,\n startWarehouse,\n waitUntilRunning,\n} from \"./warehouse-status\";\n\nconst logger = createLogger(\"type-generator:vite-plugin\");\n\n/**\n * How long the DEV background watcher waits for a STARTING warehouse to reach\n * RUNNING before giving up. Short relative to the CLI's preflight budget: this\n * is a best-effort \"regenerate once the warehouse warms up\" convenience, not a\n * gate, so we'd rather stop polling than hold a detached task open for minutes.\n */\nconst DEV_WAREHOUSE_WATCH_MAX_MS = 60_000;\n\n/**\n * Options for the AppKit types plugin.\n */\ninterface AppKitTypesPluginOptions {\n /* Path to the output d.ts file (relative to client folder). */\n outFile?: string;\n /**\n * Path to the metric registry d.ts file (relative to client folder).\n * Defaults to a sibling of `outFile`, computed by the generator.\n */\n mvOutFile?: string;\n /**\n * Folders to watch for changes. Defaults to `config/queries` and\n * `config/metric-views`. When overridden, include a `queries` folder and/or a\n * `metric-views` folder — they are resolved by their trailing path segment.\n */\n watchFolders?: string[];\n}\n\n/**\n * Vite plugin to generate types for AppKit queries.\n * Calls generateFromEntryPoint under the hood.\n * @param options - Options to override default values.\n * @returns Vite plugin to generate types for AppKit queries.\n */\nexport function appKitTypesPlugin(options?: AppKitTypesPluginOptions): Plugin {\n let outFile: string;\n let mvOutFile: string | undefined;\n let watchFolders: string[];\n // The queries + metric-views config folders, resolved in `configResolved`.\n // Passed explicitly into generateFromEntryPoint so neither is inferred from\n // `watchFolders` ordering (which used to assume queries was `watchFolders[0]`).\n let queryFolder: string | undefined;\n let metricViewsFolder: string | undefined;\n\n // Single-flight state for runGenerate(). `inFlight` is the promise of the\n // currently-running drain (null when idle); `queued` records that a trigger\n // arrived while a run was active so exactly ONE trailing run fires afterwards\n // (latest-wins — coalesces any number of overlapping triggers into a single\n // rerun). `queued` is read/cleared synchronously inside the drain loop so a\n // trigger landing in any window is caught before the drain exits.\n //\n // `pendingMode` is the mode the next generate should run in (latest-wins, like\n // `queued`): the foreground build runs non-blocking in dev (instant degrade)\n // while the background warehouse watch runs blocking (real DESCRIBEs). A\n // blocking watch trigger that lands while a non-blocking foreground run is in\n // flight therefore still describes when its trailing run fires.\n let inFlight: Promise<void> | null = null;\n let queued = false;\n let pendingMode: PreflightMode = \"non-blocking\";\n\n // The currently-armed DEV background warehouse watch, if any. Aborting it\n // stops a pending waitUntilRunning (server shutdown, or a newer arm replacing\n // an older one).\n let watchController: AbortController | null = null;\n\n /**\n * Generate types once in the given preflight {@link PreflightMode}. Never\n * throws in dev (logs instead); in production it rethrows so the build fails.\n * This is the un-guarded core — callers should go through {@link runGenerate}\n * so concurrent triggers can't race-write the .d.ts.\n *\n * @param mode - preflight policy for this run. The foreground build passes a\n * NODE_ENV-derived mode (blocking in production, non-blocking in dev so it\n * degrades instantly); the background warehouse watch passes \"blocking\" so\n * its regenerate actually DESCRIBEs and lands real (non-degraded) types.\n */\n async function generateOnce(mode: PreflightMode) {\n try {\n const warehouseId = process.env.DATABRICKS_WAREHOUSE_ID || \"\";\n\n if (!warehouseId) {\n logger.debug(\"Warehouse ID not found. Skipping type generation.\");\n return;\n }\n\n await generateFromEntryPoint({\n outFile,\n queryFolder,\n metricViewsFolder,\n warehouseId,\n noCache: false,\n mode,\n mvOutFile,\n });\n } catch (error) {\n // TypegenSyntaxError / TypegenFatalError carry a complete, actionable\n // report in their message. Their stack frames and attached query arrays\n // point into appkit internals and only add noise, so surface just the\n // message — both when failing the prod build and when logging in dev.\n const isTypegenError =\n error instanceof TypegenSyntaxError ||\n error instanceof TypegenFatalError;\n\n // throw in production to fail the build\n if (process.env.NODE_ENV === \"production\") {\n if (isTypegenError) error.stack = error.message;\n throw error;\n }\n\n if (isTypegenError) {\n logger.error(\"%s\", error.message);\n } else {\n logger.error(\"Error generating types: %O\", error);\n }\n }\n }\n\n /**\n * Single-flight wrapper around {@link generateOnce}. The initial build, the\n * .sql watcher, and the DEV warehouse watch all route through here so they can\n * never run typegen concurrently (which would race-write the .d.ts).\n *\n * If a run is already in flight, this does NOT start a second one — it records\n * the requested mode and sets a trailing flag so exactly one more run fires\n * after the current finishes, coalescing any number of overlapping triggers\n * (latest-wins, including the mode: a blocking watch trigger that arrives mid\n * non-blocking foreground run still describes when its trailing run fires).\n *\n * @param mode - preflight policy for this run. Recorded into `pendingMode`,\n * which the drain reads for each generate (latest trigger wins).\n * @returns A promise that resolves when this trigger's work (including any\n * trailing run it scheduled) has completed.\n */\n function runGenerate(mode: PreflightMode): Promise<void> {\n pendingMode = mode;\n\n if (inFlight) {\n // A run is active: remember that another trigger arrived and ride out the\n // current run. One trailing run then covers all coalesced triggers and\n // runs in the latest requested mode (recorded above).\n queued = true;\n return inFlight;\n }\n\n // Drain in a loop rather than recursing after a single queued-check: a\n // trigger can land in the window between generateOnce() resolving and the\n // check, so we re-test `queued` until it's clear. Critically, `inFlight` is\n // cleared synchronously in the SAME tick as the final `queued === false`\n // observation — never deferred to a .finally microtask — so there's no\n // window where a trigger sees `inFlight` set but the drain has already\n // decided to exit. The guard stays held for the whole drain, so concurrent\n // triggers only ever set the flag; they never start a parallel generate.\n const drain = async (): Promise<void> => {\n while (true) {\n queued = false;\n // Snapshot the mode synchronously alongside clearing `queued` so a\n // trigger landing during this generate is observed (via `queued`) on the\n // next loop with its own mode, not silently dropped.\n const runMode = pendingMode;\n await generateOnce(runMode);\n // Synchronous check + clear, atomic w.r.t. other (synchronous) callers.\n if (!queued) {\n inFlight = null;\n return;\n }\n }\n };\n\n inFlight = drain();\n return inFlight;\n }\n\n /**\n * DEV-only: get the warehouse to RUNNING in the background and regenerate with\n * real (non-degraded) types once it is — without blocking dev startup. The\n * foreground build only ever degrades in dev (instant `unknown`/cached types),\n * so this is what lands actual DESCRIBE results in the editor for EVERY\n * reachable warehouse state, not just one that happens to already be warm.\n *\n * Post-probe behaviour by state:\n * - RUNNING → describe right away (the dev foreground degraded, so a running\n * warehouse would otherwise never get real types). `waitUntilRunning`\n * returns immediately for an already-running warehouse, then the blocking\n * regenerate fires.\n * - STARTING → it's already coming up; just wait for RUNNING, then describe.\n * - STOPPED / STOPPING → kick off a start, wait for RUNNING, then describe.\n * - DELETED / DELETING → return (a deleted warehouse can't be started, and\n * blocking typegen would treat it as fatal); leave the degraded types.\n *\n * No-op in production or without a warehouse id. Replaces any previously-armed\n * watch (aborting it first). Fully self-contained: it never throws into the\n * caller and never re-arms itself. The whole lifecycle is abortable via the\n * shared {@link watchController} — its signal is threaded into\n * `waitUntilRunning`, so a dev-server shutdown cancels a pending wait — and the\n * regenerate routes through {@link runGenerate} so it can't race-write the\n * .d.ts with the foreground degrade or a `.sql` re-trigger.\n *\n * The regenerate runs in \"blocking\" mode (not the foreground's non-blocking)\n * so it actually DESCRIBEs the now-RUNNING warehouse and lands real types —\n * the whole point of warming the warehouse in the background.\n */\n function armWarehouseWatch(): void {\n if (process.env.NODE_ENV === \"production\") return;\n\n const warehouseId = process.env.DATABRICKS_WAREHOUSE_ID || \"\";\n if (!warehouseId) return;\n\n // Supersede any in-flight watch so we never run two concurrently.\n watchController?.abort();\n const controller = new AbortController();\n watchController = controller;\n const { signal } = controller;\n\n void (async () => {\n try {\n const client = new WorkspaceClient({});\n const state = await getWarehouseState(client, warehouseId);\n\n // A deleted/deleting warehouse can't be started and blocking typegen\n // would treat it as fatal — leave the degraded types and stop. Every\n // other state (including RUNNING) proceeds to wait-then-describe so the\n // dev editor gets real types, not just the foreground's degraded ones.\n if (state === \"DELETED\" || state === \"DELETING\") {\n return;\n }\n\n // Stopped/stopping won't reach RUNNING on its own — nudge it. RUNNING and\n // STARTING need no start (RUNNING is already up; STARTING is coming up),\n // so don't issue a redundant one. A failed start is non-fatal: give up\n // silently rather than throw out of the detached task (the developer\n // still has degraded/cached types).\n let startedByUs = false;\n if (state === \"STOPPED\" || state === \"STOPPING\") {\n try {\n logger.debug(\"Warehouse is %s; starting it.\", state);\n await startWarehouse(client, warehouseId);\n startedByUs = true;\n } catch {\n return;\n }\n }\n\n // Wait for RUNNING. For an already-RUNNING warehouse this returns on the\n // first poll; for STARTING/STOPPED it polls (abortably) until the\n // warehouse warms up, a terminal state, or the deadline.\n const final = await waitUntilRunning(client, warehouseId, {\n maxMs: DEV_WAREHOUSE_WATCH_MAX_MS,\n signal,\n // We just issued the start, so the first poll(s) often still report\n // STOPPED/STOPPING before the start propagates. Poll through those\n // instead of bailing, or the regenerate would never fire. When we\n // didn't start it (RUNNING/STARTING branch), keep the default terminal\n // states.\n treatStoppedAsTransient: startedByUs,\n });\n\n if (final === \"RUNNING\" && !signal.aborted) {\n logger.debug(\"Warehouse is RUNNING; regenerating types.\");\n // Blocking: the warehouse is RUNNING now, so describe it and emit real\n // (non-degraded) types — unlike the foreground dev run, which degraded.\n // Routed through the single-flight guard so it coalesces with the\n // foreground degrade / any `.sql` re-trigger instead of racing them.\n await runGenerate(\"blocking\");\n }\n } catch {\n // Detached background task: any failure (timeout, abort, connectivity,\n // auth) is non-fatal — the developer still has degraded/cached types.\n }\n })();\n }\n\n return {\n name: \"appkit-types\",\n\n apply() {\n const warehouseId = process.env.DATABRICKS_WAREHOUSE_ID || \"\";\n\n if (!warehouseId) {\n logger.debug(\"Warehouse ID not found. Skipping type generation.\");\n return false;\n }\n\n // Run when either config surface exists. Metric-view types are\n // independent of `.sql` queries, so a metric-only project (a\n // `config/metric-views/` with no `config/queries/`) must still activate\n // the plugin.\n const hasQueries = existsSync(\n path.join(process.cwd(), \"config\", \"queries\"),\n );\n const hasMetricViews = existsSync(\n path.join(process.cwd(), \"config\", \"metric-views\"),\n );\n if (!hasQueries && !hasMetricViews) {\n return false;\n }\n\n return true;\n },\n\n configResolved(config) {\n const projectRoot = path.resolve(config.root, \"..\");\n outFile = path.resolve(\n projectRoot,\n options?.outFile ?? `shared/${TYPES_DIR}/${ANALYTICS_TYPES_FILE}`,\n );\n // The metric out-path resolves against projectRoot only when explicitly\n // provided; an unset option passes through as undefined so the generator\n // computes its sibling-of-outFile default. In the all-defaults case the\n // final path is identical (the default outFile above lives in\n // shared/<TYPES_DIR>/), and a customized outFile now keeps its metric\n // sibling next to it instead of pinning it under shared/.\n mvOutFile =\n options?.mvOutFile !== undefined\n ? path.resolve(projectRoot, options.mvOutFile)\n : undefined;\n\n const defaultQueryFolder = path.join(process.cwd(), \"config\", \"queries\");\n const defaultMetricViewsFolder = path.join(\n process.cwd(),\n \"config\",\n \"metric-views\",\n );\n watchFolders = options?.watchFolders ?? [\n defaultQueryFolder,\n defaultMetricViewsFolder,\n ];\n\n // Resolve the two config folders explicitly rather than assuming a\n // position in `watchFolders`. With a custom `watchFolders`, match by the\n // trailing segment; otherwise use the computed defaults.\n if (options?.watchFolders) {\n queryFolder = watchFolders.find((f) => path.basename(f) === \"queries\");\n metricViewsFolder = watchFolders.find(\n (f) => path.basename(f) === \"metric-views\",\n );\n } else {\n queryFolder = defaultQueryFolder;\n metricViewsFolder = defaultMetricViewsFolder;\n }\n },\n\n buildStart() {\n // Production: block the build on this generate (and surface failures).\n // The watch is a dev-only no-op, so just run typegen.\n if (process.env.NODE_ENV === \"production\") {\n return runGenerate(\"blocking\");\n }\n\n // Dev: don't block startup waiting on typegen. The foreground generate runs\n // non-blocking — it skips the warehouse entirely and writes degraded\n // (cached/`unknown`) types instantly. Then arm the warehouse watch so the\n // warehouse gets a one-shot BLOCKING regenerate (real types) in the\n // background for EVERY reachable state: RUNNING describes right away, while\n // STARTING/STOPPED are waited (and started) until they reach RUNNING.\n void runGenerate(\"non-blocking\");\n armWarehouseWatch();\n },\n\n configureServer(server) {\n server.watcher.add(watchFolders);\n\n server.watcher.on(\"change\", (changedFile) => {\n const isWatchedFile = watchFolders.some((folder) =>\n changedFile.startsWith(folder),\n );\n\n // The metric config is `definitions.json` — a far more generic name\n // than the old `metric-views.json`. Match it by DIRECTORY, not bare\n // basename: only a `definitions.json` sitting directly in the\n // metric-views folder is the config (a `definitions.json` elsewhere in\n // a watched tree must not trigger a regenerate).\n const isMetricConfig =\n metricViewsFolder !== undefined &&\n path.basename(changedFile) === METRIC_CONFIG_FILE &&\n path.dirname(path.resolve(changedFile)) ===\n path.resolve(metricViewsFolder);\n\n if (isWatchedFile && (changedFile.endsWith(\".sql\") || isMetricConfig)) {\n // Route through the single-flight runner (was fire-and-forget\n // generate(), which could race the initial build / watch). This is a\n // dev-only hook, so degrade instantly (non-blocking), then re-arm the\n // warehouse watch so the edited query or metric-view source is\n // re-described in the background against the running warehouse (or\n // once a still-starting one warms up), landing fresh\n // blocking-described types.\n void runGenerate(\"non-blocking\");\n armWarehouseWatch();\n }\n });\n\n // Tear down any pending warehouse watch when the dev server closes so a\n // long backoff can't keep the process alive after shutdown.\n server.httpServer?.once(\"close\", () => {\n watchController?.abort();\n });\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vite-plugin.js","names":[],"sources":["../../src/type-generator/vite-plugin.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport { METRIC_CONFIG_FILE } from \"../../../shared/src/schemas/metric-fqn\";\nimport { createLogger } from \"../logging/logger\";\nimport { createWorkspaceClient } from \"../workspace-client\";\nimport {\n ANALYTICS_TYPES_FILE,\n generateFromEntryPoint,\n TYPES_DIR,\n TypegenFatalError,\n TypegenSyntaxError,\n} from \"./index\";\nimport type { PreflightMode } from \"./preflight\";\nimport {\n getWarehouseState,\n startWarehouse,\n waitUntilRunning,\n} from \"./warehouse-status\";\n\nconst logger = createLogger(\"type-generator:vite-plugin\");\n\n/**\n * How long the DEV background watcher waits for a STARTING warehouse to reach\n * RUNNING before giving up. Short relative to the CLI's preflight budget: this\n * is a best-effort \"regenerate once the warehouse warms up\" convenience, not a\n * gate, so we'd rather stop polling than hold a detached task open for minutes.\n */\nconst DEV_WAREHOUSE_WATCH_MAX_MS = 60_000;\n\n/**\n * Options for the AppKit types plugin.\n */\ninterface AppKitTypesPluginOptions {\n /* Path to the output d.ts file (relative to client folder). */\n outFile?: string;\n /**\n * Path to the metric registry d.ts file (relative to client folder).\n * Defaults to a sibling of `outFile`, computed by the generator.\n */\n mvOutFile?: string;\n /**\n * Folders to watch for changes. Defaults to `config/queries` and\n * `config/metric-views`. When overridden, include a `queries` folder and/or a\n * `metric-views` folder — they are resolved by their trailing path segment.\n */\n watchFolders?: string[];\n}\n\n/**\n * Vite plugin to generate types for AppKit queries.\n * Calls generateFromEntryPoint under the hood.\n * @param options - Options to override default values.\n * @returns Vite plugin to generate types for AppKit queries.\n */\nexport function appKitTypesPlugin(options?: AppKitTypesPluginOptions): Plugin {\n let outFile: string;\n let mvOutFile: string | undefined;\n let watchFolders: string[];\n // The queries + metric-views config folders, resolved in `configResolved`.\n // Passed explicitly into generateFromEntryPoint so neither is inferred from\n // `watchFolders` ordering (which used to assume queries was `watchFolders[0]`).\n let queryFolder: string | undefined;\n let metricViewsFolder: string | undefined;\n\n // Single-flight state for runGenerate(). `inFlight` is the promise of the\n // currently-running drain (null when idle); `queued` records that a trigger\n // arrived while a run was active so exactly ONE trailing run fires afterwards\n // (latest-wins — coalesces any number of overlapping triggers into a single\n // rerun). `queued` is read/cleared synchronously inside the drain loop so a\n // trigger landing in any window is caught before the drain exits.\n //\n // `pendingMode` is the mode the next generate should run in (latest-wins, like\n // `queued`): the foreground build runs non-blocking in dev (instant degrade)\n // while the background warehouse watch runs blocking (real DESCRIBEs). A\n // blocking watch trigger that lands while a non-blocking foreground run is in\n // flight therefore still describes when its trailing run fires.\n let inFlight: Promise<void> | null = null;\n let queued = false;\n let pendingMode: PreflightMode = \"non-blocking\";\n\n // The currently-armed DEV background warehouse watch, if any. Aborting it\n // stops a pending waitUntilRunning (server shutdown, or a newer arm replacing\n // an older one).\n let watchController: AbortController | null = null;\n\n /**\n * Generate types once in the given preflight {@link PreflightMode}. Never\n * throws in dev (logs instead); in production it rethrows so the build fails.\n * This is the un-guarded core — callers should go through {@link runGenerate}\n * so concurrent triggers can't race-write the .d.ts.\n *\n * @param mode - preflight policy for this run. The foreground build passes a\n * NODE_ENV-derived mode (blocking in production, non-blocking in dev so it\n * degrades instantly); the background warehouse watch passes \"blocking\" so\n * its regenerate actually DESCRIBEs and lands real (non-degraded) types.\n */\n async function generateOnce(mode: PreflightMode) {\n try {\n const warehouseId = process.env.DATABRICKS_WAREHOUSE_ID || \"\";\n\n if (!warehouseId) {\n logger.debug(\"Warehouse ID not found. Skipping type generation.\");\n return;\n }\n\n await generateFromEntryPoint({\n outFile,\n queryFolder,\n metricViewsFolder,\n warehouseId,\n noCache: false,\n mode,\n mvOutFile,\n });\n } catch (error) {\n // TypegenSyntaxError / TypegenFatalError carry a complete, actionable\n // report in their message. Their stack frames and attached query arrays\n // point into appkit internals and only add noise, so surface just the\n // message — both when failing the prod build and when logging in dev.\n const isTypegenError =\n error instanceof TypegenSyntaxError ||\n error instanceof TypegenFatalError;\n\n // throw in production to fail the build\n if (process.env.NODE_ENV === \"production\") {\n if (isTypegenError) error.stack = error.message;\n throw error;\n }\n\n if (isTypegenError) {\n logger.error(\"%s\", error.message);\n } else {\n logger.error(\"Error generating types: %O\", error);\n }\n }\n }\n\n /**\n * Single-flight wrapper around {@link generateOnce}. The initial build, the\n * .sql watcher, and the DEV warehouse watch all route through here so they can\n * never run typegen concurrently (which would race-write the .d.ts).\n *\n * If a run is already in flight, this does NOT start a second one — it records\n * the requested mode and sets a trailing flag so exactly one more run fires\n * after the current finishes, coalescing any number of overlapping triggers\n * (latest-wins, including the mode: a blocking watch trigger that arrives mid\n * non-blocking foreground run still describes when its trailing run fires).\n *\n * @param mode - preflight policy for this run. Recorded into `pendingMode`,\n * which the drain reads for each generate (latest trigger wins).\n * @returns A promise that resolves when this trigger's work (including any\n * trailing run it scheduled) has completed.\n */\n function runGenerate(mode: PreflightMode): Promise<void> {\n pendingMode = mode;\n\n if (inFlight) {\n // A run is active: remember that another trigger arrived and ride out the\n // current run. One trailing run then covers all coalesced triggers and\n // runs in the latest requested mode (recorded above).\n queued = true;\n return inFlight;\n }\n\n // Drain in a loop rather than recursing after a single queued-check: a\n // trigger can land in the window between generateOnce() resolving and the\n // check, so we re-test `queued` until it's clear. Critically, `inFlight` is\n // cleared synchronously in the SAME tick as the final `queued === false`\n // observation — never deferred to a .finally microtask — so there's no\n // window where a trigger sees `inFlight` set but the drain has already\n // decided to exit. The guard stays held for the whole drain, so concurrent\n // triggers only ever set the flag; they never start a parallel generate.\n const drain = async (): Promise<void> => {\n while (true) {\n queued = false;\n // Snapshot the mode synchronously alongside clearing `queued` so a\n // trigger landing during this generate is observed (via `queued`) on the\n // next loop with its own mode, not silently dropped.\n const runMode = pendingMode;\n await generateOnce(runMode);\n // Synchronous check + clear, atomic w.r.t. other (synchronous) callers.\n if (!queued) {\n inFlight = null;\n return;\n }\n }\n };\n\n inFlight = drain();\n return inFlight;\n }\n\n /**\n * DEV-only: get the warehouse to RUNNING in the background and regenerate with\n * real (non-degraded) types once it is — without blocking dev startup. The\n * foreground build only ever degrades in dev (instant `unknown`/cached types),\n * so this is what lands actual DESCRIBE results in the editor for EVERY\n * reachable warehouse state, not just one that happens to already be warm.\n *\n * Post-probe behaviour by state:\n * - RUNNING → describe right away (the dev foreground degraded, so a running\n * warehouse would otherwise never get real types). `waitUntilRunning`\n * returns immediately for an already-running warehouse, then the blocking\n * regenerate fires.\n * - STARTING → it's already coming up; just wait for RUNNING, then describe.\n * - STOPPED / STOPPING → kick off a start, wait for RUNNING, then describe.\n * - DELETED / DELETING → return (a deleted warehouse can't be started, and\n * blocking typegen would treat it as fatal); leave the degraded types.\n *\n * No-op in production or without a warehouse id. Replaces any previously-armed\n * watch (aborting it first). Fully self-contained: it never throws into the\n * caller and never re-arms itself. The whole lifecycle is abortable via the\n * shared {@link watchController} — its signal is threaded into\n * `waitUntilRunning`, so a dev-server shutdown cancels a pending wait — and the\n * regenerate routes through {@link runGenerate} so it can't race-write the\n * .d.ts with the foreground degrade or a `.sql` re-trigger.\n *\n * The regenerate runs in \"blocking\" mode (not the foreground's non-blocking)\n * so it actually DESCRIBEs the now-RUNNING warehouse and lands real types —\n * the whole point of warming the warehouse in the background.\n */\n function armWarehouseWatch(): void {\n if (process.env.NODE_ENV === \"production\") return;\n\n const warehouseId = process.env.DATABRICKS_WAREHOUSE_ID || \"\";\n if (!warehouseId) return;\n\n // Supersede any in-flight watch so we never run two concurrently.\n watchController?.abort();\n const controller = new AbortController();\n watchController = controller;\n const { signal } = controller;\n\n void (async () => {\n try {\n const client = createWorkspaceClient();\n const state = await getWarehouseState(client, warehouseId);\n\n // A deleted/deleting warehouse can't be started and blocking typegen\n // would treat it as fatal — leave the degraded types and stop. Every\n // other state (including RUNNING) proceeds to wait-then-describe so the\n // dev editor gets real types, not just the foreground's degraded ones.\n if (state === \"DELETED\" || state === \"DELETING\") {\n return;\n }\n\n // Stopped/stopping won't reach RUNNING on its own — nudge it. RUNNING and\n // STARTING need no start (RUNNING is already up; STARTING is coming up),\n // so don't issue a redundant one. A failed start is non-fatal: give up\n // silently rather than throw out of the detached task (the developer\n // still has degraded/cached types).\n let startedByUs = false;\n if (state === \"STOPPED\" || state === \"STOPPING\") {\n try {\n logger.debug(\"Warehouse is %s; starting it.\", state);\n await startWarehouse(client, warehouseId);\n startedByUs = true;\n } catch {\n return;\n }\n }\n\n // Wait for RUNNING. For an already-RUNNING warehouse this returns on the\n // first poll; for STARTING/STOPPED it polls (abortably) until the\n // warehouse warms up, a terminal state, or the deadline.\n const final = await waitUntilRunning(client, warehouseId, {\n maxMs: DEV_WAREHOUSE_WATCH_MAX_MS,\n signal,\n // We just issued the start, so the first poll(s) often still report\n // STOPPED/STOPPING before the start propagates. Poll through those\n // instead of bailing, or the regenerate would never fire. When we\n // didn't start it (RUNNING/STARTING branch), keep the default terminal\n // states.\n treatStoppedAsTransient: startedByUs,\n });\n\n if (final === \"RUNNING\" && !signal.aborted) {\n logger.debug(\"Warehouse is RUNNING; regenerating types.\");\n // Blocking: the warehouse is RUNNING now, so describe it and emit real\n // (non-degraded) types — unlike the foreground dev run, which degraded.\n // Routed through the single-flight guard so it coalesces with the\n // foreground degrade / any `.sql` re-trigger instead of racing them.\n await runGenerate(\"blocking\");\n }\n } catch {\n // Detached background task: any failure (timeout, abort, connectivity,\n // auth) is non-fatal — the developer still has degraded/cached types.\n }\n })();\n }\n\n return {\n name: \"appkit-types\",\n\n apply() {\n const warehouseId = process.env.DATABRICKS_WAREHOUSE_ID || \"\";\n\n if (!warehouseId) {\n logger.debug(\"Warehouse ID not found. Skipping type generation.\");\n return false;\n }\n\n // Run when either config surface exists. Metric-view types are\n // independent of `.sql` queries, so a metric-only project (a\n // `config/metric-views/` with no `config/queries/`) must still activate\n // the plugin.\n const hasQueries = existsSync(\n path.join(process.cwd(), \"config\", \"queries\"),\n );\n const hasMetricViews = existsSync(\n path.join(process.cwd(), \"config\", \"metric-views\"),\n );\n if (!hasQueries && !hasMetricViews) {\n return false;\n }\n\n return true;\n },\n\n configResolved(config) {\n const projectRoot = path.resolve(config.root, \"..\");\n outFile = path.resolve(\n projectRoot,\n options?.outFile ?? `shared/${TYPES_DIR}/${ANALYTICS_TYPES_FILE}`,\n );\n // The metric out-path resolves against projectRoot only when explicitly\n // provided; an unset option passes through as undefined so the generator\n // computes its sibling-of-outFile default. In the all-defaults case the\n // final path is identical (the default outFile above lives in\n // shared/<TYPES_DIR>/), and a customized outFile now keeps its metric\n // sibling next to it instead of pinning it under shared/.\n mvOutFile =\n options?.mvOutFile !== undefined\n ? path.resolve(projectRoot, options.mvOutFile)\n : undefined;\n\n const defaultQueryFolder = path.join(process.cwd(), \"config\", \"queries\");\n const defaultMetricViewsFolder = path.join(\n process.cwd(),\n \"config\",\n \"metric-views\",\n );\n watchFolders = options?.watchFolders ?? [\n defaultQueryFolder,\n defaultMetricViewsFolder,\n ];\n\n // Resolve the two config folders explicitly rather than assuming a\n // position in `watchFolders`. With a custom `watchFolders`, match by the\n // trailing segment; otherwise use the computed defaults.\n if (options?.watchFolders) {\n queryFolder = watchFolders.find((f) => path.basename(f) === \"queries\");\n metricViewsFolder = watchFolders.find(\n (f) => path.basename(f) === \"metric-views\",\n );\n } else {\n queryFolder = defaultQueryFolder;\n metricViewsFolder = defaultMetricViewsFolder;\n }\n },\n\n buildStart() {\n // Production: block the build on this generate (and surface failures).\n // The watch is a dev-only no-op, so just run typegen.\n if (process.env.NODE_ENV === \"production\") {\n return runGenerate(\"blocking\");\n }\n\n // Dev: don't block startup waiting on typegen. The foreground generate runs\n // non-blocking — it skips the warehouse entirely and writes degraded\n // (cached/`unknown`) types instantly. Then arm the warehouse watch so the\n // warehouse gets a one-shot BLOCKING regenerate (real types) in the\n // background for EVERY reachable state: RUNNING describes right away, while\n // STARTING/STOPPED are waited (and started) until they reach RUNNING.\n void runGenerate(\"non-blocking\");\n armWarehouseWatch();\n },\n\n configureServer(server) {\n server.watcher.add(watchFolders);\n\n server.watcher.on(\"change\", (changedFile) => {\n const isWatchedFile = watchFolders.some((folder) =>\n changedFile.startsWith(folder),\n );\n\n // The metric config is `definitions.json` — a far more generic name\n // than the old `metric-views.json`. Match it by DIRECTORY, not bare\n // basename: only a `definitions.json` sitting directly in the\n // metric-views folder is the config (a `definitions.json` elsewhere in\n // a watched tree must not trigger a regenerate).\n const isMetricConfig =\n metricViewsFolder !== undefined &&\n path.basename(changedFile) === METRIC_CONFIG_FILE &&\n path.dirname(path.resolve(changedFile)) ===\n path.resolve(metricViewsFolder);\n\n if (isWatchedFile && (changedFile.endsWith(\".sql\") || isMetricConfig)) {\n // Route through the single-flight runner (was fire-and-forget\n // generate(), which could race the initial build / watch). This is a\n // dev-only hook, so degrade instantly (non-blocking), then re-arm the\n // warehouse watch so the edited query or metric-view source is\n // re-described in the background against the running warehouse (or\n // once a still-starting one warms up), landing fresh\n // blocking-described types.\n void runGenerate(\"non-blocking\");\n armWarehouseWatch();\n }\n });\n\n // Tear down any pending warehouse watch when the dev server closes so a\n // long backoff can't keep the process alive after shutdown.\n server.httpServer?.once(\"close\", () => {\n watchController?.abort();\n });\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"warehouse-status.js","names":[],"sources":["../../src/type-generator/warehouse-status.ts"],"sourcesContent":["import type { WorkspaceClient } from \"
|
|
1
|
+
{"version":3,"file":"warehouse-status.js","names":[],"sources":["../../src/type-generator/warehouse-status.ts"],"sourcesContent":["import type { WorkspaceClient } from \"../workspace-client\";\n\n/**\n * Lifecycle states a SQL warehouse can report. Mirrors the Databricks SDK\n * `State` union; redeclared here so callers of this module don't need to reach\n * into the SDK's deep type paths.\n */\nexport type WarehouseState =\n | \"RUNNING\"\n | \"STARTING\"\n | \"STOPPED\"\n | \"STOPPING\"\n | \"DELETING\"\n | \"DELETED\";\n\n/** Backoff bounds for {@link waitUntilRunning}. */\nconst INITIAL_POLL_MS = 1000;\nconst MAX_POLL_MS = 15000;\n\n/** States from which the warehouse will not transition to RUNNING on its own. */\nconst NOT_COMING_UP: ReadonlySet<WarehouseState> = new Set<WarehouseState>([\n \"STOPPED\",\n \"STOPPING\",\n \"DELETED\",\n \"DELETING\",\n]);\n\n/**\n * Terminal states even when {@link waitUntilRunning} is told to treat\n * STOPPED/STOPPING as transient: a deleted (or deleting) warehouse genuinely\n * can't reach RUNNING, so we still resolve with the observed state.\n */\nconst NEVER_COMING_UP: ReadonlySet<WarehouseState> = new Set<WarehouseState>([\n \"DELETED\",\n \"DELETING\",\n]);\n\n/**\n * Sleep for `ms`, resolving early if `signal` aborts. The pending timer is\n * always cleared (on resolve and on abort) so a long backoff can't keep the\n * event loop alive after the caller has bailed.\n */\nfunction delay(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal?.aborted) {\n resolve();\n return;\n }\n\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n\n function onAbort() {\n clearTimeout(timer);\n resolve();\n }\n\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/**\n * Fetch the current lifecycle state of a SQL warehouse.\n *\n * Errors from the SDK (auth, bad warehouse id, connectivity) are intentionally\n * NOT caught — the caller decides how to classify and react to them.\n */\nexport async function getWarehouseState(\n client: WorkspaceClient,\n warehouseId: string,\n): Promise<WarehouseState> {\n const response = await client.warehouses.get({ id: warehouseId });\n return response.state as WarehouseState;\n}\n\n/**\n * Initiate a start of a stopped/stopping SQL warehouse.\n *\n * Only KICKS OFF the start: the SDK's `start()` returns a Waiter, but we\n * deliberately do not `.wait()` on it. Blocking on the full cold-start isn't our\n * job here — {@link waitUntilRunning} is the poller that watches the warehouse\n * the rest of the way to RUNNING. We just nudge it out of the stopped state.\n *\n * Errors from the SDK (auth, bad warehouse id, connectivity) are intentionally\n * NOT caught — the caller decides how to classify and react to them.\n */\nexport async function startWarehouse(\n client: WorkspaceClient,\n warehouseId: string,\n): Promise<void> {\n await client.warehouses.start({ id: warehouseId });\n}\n\n/**\n * Poll a warehouse until it reaches RUNNING, settles into a state it won't\n * leave on its own, or a deadline elapses.\n *\n * Polling uses exponential backoff: the first wait is ~{@link INITIAL_POLL_MS},\n * doubling on each subsequent poll up to a ~{@link MAX_POLL_MS} cap.\n *\n * Resolution:\n * - Resolves `\"RUNNING\"` as soon as the warehouse is running.\n * - Resolves with the observed state if it reaches a not-coming-up state\n * (`STOPPED`/`STOPPING`/`DELETED`/`DELETING`) — the caller decides what to do.\n *\n * Set `opts.treatStoppedAsTransient` when the caller has just issued a start and\n * a still-`STOPPED`/`STOPPING` reading is expected to be a stale pre-start blip\n * rather than a settled state. With it on, those two states are polled through\n * (like `STARTING`) until RUNNING, a genuinely terminal `DELETED`/`DELETING`, or\n * the deadline — so an immediate post-start STOPPED reading no longer bails the\n * wait. Off (default), STOPPED/STOPPING remain terminal and resolve as before.\n *\n * Pass `opts.signal` to abort an in-progress wait (e.g. a dev server shutting\n * down): the next deadline/abort check throws an `AbortError`, and a pending\n * backoff sleep resolves immediately rather than holding the process open.\n *\n * @throws Error if `maxMs` elapses before the warehouse reaches RUNNING.\n * @throws Error (`name === \"AbortError\"`) if `opts.signal` is or becomes aborted.\n */\nexport async function waitUntilRunning(\n client: WorkspaceClient,\n warehouseId: string,\n opts: {\n maxMs: number;\n pollMs?: number;\n signal?: AbortSignal;\n treatStoppedAsTransient?: boolean;\n },\n): Promise<WarehouseState> {\n const { maxMs, signal, treatStoppedAsTransient } = opts;\n const start = Date.now();\n let pollMs = opts.pollMs ?? INITIAL_POLL_MS;\n\n // Which states end the wait early. When we've just issued a start, STOPPED and\n // STOPPING are expected stale readings, so only DELETED/DELETING stay terminal.\n const terminalStates = treatStoppedAsTransient\n ? NEVER_COMING_UP\n : NOT_COMING_UP;\n\n while (true) {\n throwIfAborted(signal);\n\n const state = await getWarehouseState(client, warehouseId);\n if (state === \"RUNNING\") return \"RUNNING\";\n if (terminalStates.has(state)) return state;\n\n if (Date.now() - start >= maxMs) {\n throw new Error(\n `Warehouse ${warehouseId} did not reach RUNNING within ${maxMs}ms (last state: ${state})`,\n );\n }\n\n await delay(pollMs, signal);\n throwIfAborted(signal);\n\n // Re-check the deadline after sleeping so we don't issue another poll past\n // the budget purely because we napped through it.\n if (Date.now() - start >= maxMs) {\n throw new Error(\n `Warehouse ${warehouseId} did not reach RUNNING within ${maxMs}ms (last state: ${state})`,\n );\n }\n\n pollMs = Math.min(pollMs * 2, MAX_POLL_MS);\n }\n}\n\n/** Throw a DOMException-style AbortError if the signal has been aborted. */\nfunction throwIfAborted(signal?: AbortSignal): void {\n if (!signal?.aborted) return;\n const error = new Error(\"The warehouse wait was aborted.\");\n error.name = \"AbortError\";\n throw error;\n}\n"],"mappings":";;AAgBA,MAAM,kBAAkB;AACxB,MAAM,cAAc;;AAGpB,MAAM,gBAA6C,IAAI,IAAoB;CACzE;CACA;CACA;CACA;CACD,CAAC;;;;;;AAOF,MAAM,kBAA+C,IAAI,IAAoB,CAC3E,WACA,WACD,CAAC;;;;;;AAOF,SAAS,MAAM,IAAY,QAAqC;AAC9D,QAAO,IAAI,SAAS,YAAY;AAC9B,MAAI,QAAQ,SAAS;AACnB,YAAS;AACT;;EAGF,MAAM,QAAQ,iBAAiB;AAC7B,WAAQ,oBAAoB,SAAS,QAAQ;AAC7C,YAAS;KACR,GAAG;EAEN,SAAS,UAAU;AACjB,gBAAa,MAAM;AACnB,YAAS;;AAGX,UAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM,CAAC;GAC1D;;;;;;;;AASJ,eAAsB,kBACpB,QACA,aACyB;AAEzB,SADiB,MAAM,OAAO,WAAW,IAAI,EAAE,IAAI,aAAa,CAAC,EACjD;;;;;;;;;;;;;AAclB,eAAsB,eACpB,QACA,aACe;AACf,OAAM,OAAO,WAAW,MAAM,EAAE,IAAI,aAAa,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BpD,eAAsB,iBACpB,QACA,aACA,MAMyB;CACzB,MAAM,EAAE,OAAO,QAAQ,4BAA4B;CACnD,MAAM,QAAQ,KAAK,KAAK;CACxB,IAAI,SAAS,KAAK,UAAU;CAI5B,MAAM,iBAAiB,0BACnB,kBACA;AAEJ,QAAO,MAAM;AACX,iBAAe,OAAO;EAEtB,MAAM,QAAQ,MAAM,kBAAkB,QAAQ,YAAY;AAC1D,MAAI,UAAU,UAAW,QAAO;AAChC,MAAI,eAAe,IAAI,MAAM,CAAE,QAAO;AAEtC,MAAI,KAAK,KAAK,GAAG,SAAS,MACxB,OAAM,IAAI,MACR,aAAa,YAAY,gCAAgC,MAAM,kBAAkB,MAAM,GACxF;AAGH,QAAM,MAAM,QAAQ,OAAO;AAC3B,iBAAe,OAAO;AAItB,MAAI,KAAK,KAAK,GAAG,SAAS,MACxB,OAAM,IAAI,MACR,aAAa,YAAY,gCAAgC,MAAM,kBAAkB,MAAM,GACxF;AAGH,WAAS,KAAK,IAAI,SAAS,GAAG,YAAY;;;;AAK9C,SAAS,eAAe,QAA4B;AAClD,KAAI,CAAC,QAAQ,QAAS;CACtB,MAAM,wBAAQ,IAAI,MAAM,kCAAkC;AAC1D,OAAM,OAAO;AACb,OAAM"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { buildLegacyWorkspaceClient } from "./legacy.js";
|
|
2
|
+
|
|
3
|
+
//#region src/workspace-client/client.ts
|
|
4
|
+
/**
|
|
5
|
+
* `AppKitWorkspaceClient` — the facade implementation. Construct via
|
|
6
|
+
* `createWorkspaceClient(...)`; this class is internal.
|
|
7
|
+
*
|
|
8
|
+
* Every service accessor delegates to a single lazily-constructed legacy SDK
|
|
9
|
+
* client. This is the seam: to migrate a service to the modular SDK, replace
|
|
10
|
+
* its getter here with a modular client instance (and update its connector +
|
|
11
|
+
* the accessor type in `types.ts`). No other AppKit module touches the SDK.
|
|
12
|
+
*/
|
|
13
|
+
var AppKitWorkspaceClient = class {
|
|
14
|
+
#opts;
|
|
15
|
+
#legacy;
|
|
16
|
+
constructor(opts) {
|
|
17
|
+
this.#opts = opts;
|
|
18
|
+
}
|
|
19
|
+
get files() {
|
|
20
|
+
return this.#getLegacy().files;
|
|
21
|
+
}
|
|
22
|
+
get warehouses() {
|
|
23
|
+
return this.#getLegacy().warehouses;
|
|
24
|
+
}
|
|
25
|
+
get genie() {
|
|
26
|
+
return this.#getLegacy().genie;
|
|
27
|
+
}
|
|
28
|
+
get jobs() {
|
|
29
|
+
return this.#getLegacy().jobs;
|
|
30
|
+
}
|
|
31
|
+
get statementExecution() {
|
|
32
|
+
return this.#getLegacy().statementExecution;
|
|
33
|
+
}
|
|
34
|
+
get servingEndpoints() {
|
|
35
|
+
return this.#getLegacy().servingEndpoints;
|
|
36
|
+
}
|
|
37
|
+
get currentUser() {
|
|
38
|
+
return this.#getLegacy().currentUser;
|
|
39
|
+
}
|
|
40
|
+
get config() {
|
|
41
|
+
return this.#getLegacy().config;
|
|
42
|
+
}
|
|
43
|
+
get apiClient() {
|
|
44
|
+
return this.#getLegacy().apiClient;
|
|
45
|
+
}
|
|
46
|
+
toLegacyWorkspaceClient() {
|
|
47
|
+
return this.#getLegacy();
|
|
48
|
+
}
|
|
49
|
+
#getLegacy() {
|
|
50
|
+
if (!this.#legacy) this.#legacy = buildLegacyWorkspaceClient(this.#opts);
|
|
51
|
+
return this.#legacy;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
//#endregion
|
|
56
|
+
export { AppKitWorkspaceClient };
|
|
57
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","names":["#opts","#getLegacy","#legacy"],"sources":["../../src/workspace-client/client.ts"],"sourcesContent":["/**\n * `AppKitWorkspaceClient` — the facade implementation. Construct via\n * `createWorkspaceClient(...)`; this class is internal.\n *\n * Every service accessor delegates to a single lazily-constructed legacy SDK\n * client. This is the seam: to migrate a service to the modular SDK, replace\n * its getter here with a modular client instance (and update its connector +\n * the accessor type in `types.ts`). No other AppKit module touches the SDK.\n */\nimport {\n buildLegacyWorkspaceClient,\n type LegacyWorkspaceClient,\n type WorkspaceClientOptions,\n} from \"./legacy\";\nimport type { WorkspaceClient } from \"./types\";\n\nexport class AppKitWorkspaceClient implements WorkspaceClient {\n readonly #opts: WorkspaceClientOptions;\n #legacy?: LegacyWorkspaceClient;\n\n constructor(opts: WorkspaceClientOptions) {\n this.#opts = opts;\n }\n\n get files() {\n return this.#getLegacy().files;\n }\n\n get warehouses() {\n return this.#getLegacy().warehouses;\n }\n\n get genie() {\n return this.#getLegacy().genie;\n }\n\n get jobs() {\n return this.#getLegacy().jobs;\n }\n\n get statementExecution() {\n return this.#getLegacy().statementExecution;\n }\n\n get servingEndpoints() {\n return this.#getLegacy().servingEndpoints;\n }\n\n get currentUser() {\n return this.#getLegacy().currentUser;\n }\n\n get config() {\n return this.#getLegacy().config;\n }\n\n get apiClient() {\n return this.#getLegacy().apiClient;\n }\n\n toLegacyWorkspaceClient(): LegacyWorkspaceClient {\n return this.#getLegacy();\n }\n\n #getLegacy(): LegacyWorkspaceClient {\n if (!this.#legacy) {\n this.#legacy = buildLegacyWorkspaceClient(this.#opts);\n }\n return this.#legacy;\n }\n}\n"],"mappings":";;;;;;;;;;;;AAgBA,IAAa,wBAAb,MAA8D;CAC5D,CAASA;CACT;CAEA,YAAY,MAA8B;AACxC,QAAKA,OAAQ;;CAGf,IAAI,QAAQ;AACV,SAAO,MAAKC,WAAY,CAAC;;CAG3B,IAAI,aAAa;AACf,SAAO,MAAKA,WAAY,CAAC;;CAG3B,IAAI,QAAQ;AACV,SAAO,MAAKA,WAAY,CAAC;;CAG3B,IAAI,OAAO;AACT,SAAO,MAAKA,WAAY,CAAC;;CAG3B,IAAI,qBAAqB;AACvB,SAAO,MAAKA,WAAY,CAAC;;CAG3B,IAAI,mBAAmB;AACrB,SAAO,MAAKA,WAAY,CAAC;;CAG3B,IAAI,cAAc;AAChB,SAAO,MAAKA,WAAY,CAAC;;CAG3B,IAAI,SAAS;AACX,SAAO,MAAKA,WAAY,CAAC;;CAG3B,IAAI,YAAY;AACd,SAAO,MAAKA,WAAY,CAAC;;CAG3B,0BAAiD;AAC/C,SAAO,MAAKA,WAAY;;CAG1B,aAAoC;AAClC,MAAI,CAAC,MAAKC,OACR,OAAKA,SAAU,2BAA2B,MAAKF,KAAM;AAEvD,SAAO,MAAKE"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { WorkspaceClientOptions } from "./legacy.js";
|
|
2
|
+
import { WorkspaceClient } from "./types.js";
|
|
3
|
+
|
|
4
|
+
//#region src/workspace-client/factory.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Construct an AppKit workspace client.
|
|
7
|
+
*
|
|
8
|
+
* Auth resolution:
|
|
9
|
+
* - If `opts.token` is set, uses PAT credentials.
|
|
10
|
+
* - Otherwise walks the SDK default auth chain (env vars + ~/.databrickscfg).
|
|
11
|
+
*
|
|
12
|
+
* Host resolution:
|
|
13
|
+
* - Explicit `opts.host` → use it.
|
|
14
|
+
* - Otherwise resolved by the SDK from `DATABRICKS_HOST` / profile.
|
|
15
|
+
*/
|
|
16
|
+
declare function createWorkspaceClient(opts?: WorkspaceClientOptions): WorkspaceClient;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { createWorkspaceClient };
|
|
19
|
+
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.d.ts","names":[],"sources":["../../src/workspace-client/factory.ts"],"mappings":";;;;;;AAkBA;;;;;;;;;iBAAgB,qBAAA,CACd,IAAA,GAAM,sBAAA,GACL,eAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { AppKitWorkspaceClient } from "./client.js";
|
|
2
|
+
|
|
3
|
+
//#region src/workspace-client/factory.ts
|
|
4
|
+
/**
|
|
5
|
+
* Public factory for constructing a wrapper instance.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Construct an AppKit workspace client.
|
|
9
|
+
*
|
|
10
|
+
* Auth resolution:
|
|
11
|
+
* - If `opts.token` is set, uses PAT credentials.
|
|
12
|
+
* - Otherwise walks the SDK default auth chain (env vars + ~/.databrickscfg).
|
|
13
|
+
*
|
|
14
|
+
* Host resolution:
|
|
15
|
+
* - Explicit `opts.host` → use it.
|
|
16
|
+
* - Otherwise resolved by the SDK from `DATABRICKS_HOST` / profile.
|
|
17
|
+
*/
|
|
18
|
+
function createWorkspaceClient(opts = {}) {
|
|
19
|
+
return new AppKitWorkspaceClient(opts);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
export { createWorkspaceClient };
|
|
24
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","names":[],"sources":["../../src/workspace-client/factory.ts"],"sourcesContent":["/**\n * Public factory for constructing a wrapper instance.\n */\nimport { AppKitWorkspaceClient } from \"./client\";\nimport type { WorkspaceClientOptions } from \"./legacy\";\nimport type { WorkspaceClient } from \"./types\";\n\n/**\n * Construct an AppKit workspace client.\n *\n * Auth resolution:\n * - If `opts.token` is set, uses PAT credentials.\n * - Otherwise walks the SDK default auth chain (env vars + ~/.databrickscfg).\n *\n * Host resolution:\n * - Explicit `opts.host` → use it.\n * - Otherwise resolved by the SDK from `DATABRICKS_HOST` / profile.\n */\nexport function createWorkspaceClient(\n opts: WorkspaceClientOptions = {},\n): WorkspaceClient {\n return new AppKitWorkspaceClient(opts);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAkBA,SAAgB,sBACd,OAA+B,EAAE,EAChB;AACjB,QAAO,IAAI,sBAAsB,KAAK"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ClientOptions, WorkspaceClient } from "@databricks/sdk-experimental";
|
|
2
|
+
import "@databricks/sdk-experimental/dist/apis/dashboards";
|
|
3
|
+
import "@databricks/sdk-experimental/dist/wait";
|
|
4
|
+
|
|
5
|
+
//#region src/workspace-client/legacy.d.ts
|
|
6
|
+
/** The concrete legacy SDK client type. */
|
|
7
|
+
type LegacyWorkspaceClient = WorkspaceClient;
|
|
8
|
+
/**
|
|
9
|
+
* Options used to construct the wrapper. Mirrors the subset of the old SDK's
|
|
10
|
+
* `Config` + `ClientOptions` that AppKit relies on today; we deliberately do
|
|
11
|
+
* NOT re-expose every old-SDK config knob.
|
|
12
|
+
*/
|
|
13
|
+
interface WorkspaceClientOptions {
|
|
14
|
+
/** Databricks host, e.g. https://my-workspace.cloud.databricks.com. Defaults to DATABRICKS_HOST / profile resolution. */
|
|
15
|
+
host?: string;
|
|
16
|
+
/** Bearer token. When set, `authType` defaults to "pat". */
|
|
17
|
+
token?: string;
|
|
18
|
+
/** Authentication strategy passed to the legacy client. */
|
|
19
|
+
authType?: "pat";
|
|
20
|
+
/**
|
|
21
|
+
* SDK client options (product / productVersion / userAgentExtra) used to
|
|
22
|
+
* stamp the outbound User-Agent. Produced by `getClientOptions()`; omitted
|
|
23
|
+
* by build-time callers that don't stamp a User-Agent.
|
|
24
|
+
*/
|
|
25
|
+
clientOptions?: ClientOptions;
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { LegacyWorkspaceClient, WorkspaceClientOptions };
|
|
29
|
+
//# sourceMappingURL=legacy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legacy.d.ts","names":[],"sources":["../../src/workspace-client/legacy.ts"],"mappings":";;;;;;KAqBY,qBAAA,GAAwB,eAAA;;;;;;UAOnB,sBAAA;;EAEf,IAAA;;EAEA,KAAA;;EAEA,QAAA;;;;;;EAMA,aAAA,GAAgB,aAAA;AAAA"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as SDK from "@databricks/sdk-experimental";
|
|
2
|
+
|
|
3
|
+
//#region src/workspace-client/legacy.ts
|
|
4
|
+
const { WorkspaceClient: SdkWorkspaceClientCtor } = SDK;
|
|
5
|
+
/**
|
|
6
|
+
* Construct a legacy `WorkspaceClient` from wrapper options.
|
|
7
|
+
*
|
|
8
|
+
* Centralised so the wrapper facade, the `.toLegacyWorkspaceClient()` escape
|
|
9
|
+
* hatch, and any per-request OBO client all build it the same way.
|
|
10
|
+
*/
|
|
11
|
+
function buildLegacyWorkspaceClient(opts) {
|
|
12
|
+
return new SdkWorkspaceClientCtor(opts.token !== void 0 ? {
|
|
13
|
+
host: opts.host,
|
|
14
|
+
token: opts.token,
|
|
15
|
+
authType: opts.authType ?? "pat"
|
|
16
|
+
} : opts.host ? { host: opts.host } : {}, opts.clientOptions);
|
|
17
|
+
}
|
|
18
|
+
const { ConfigError, Context, TimeUnits } = SDK;
|
|
19
|
+
const Time = SDK.Time ?? SDK.default.Time;
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
export { ConfigError, Context, Time, TimeUnits, buildLegacyWorkspaceClient };
|
|
23
|
+
//# sourceMappingURL=legacy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legacy.js","names":[],"sources":["../../src/workspace-client/legacy.ts"],"sourcesContent":["/**\n * The single module in AppKit allowed to import `@databricks/sdk-experimental`\n * directly (enforced by the Biome `noRestrictedImports` boundary rule). Every\n * other AppKit module reaches the SDK through the wrapper's re-exports and the\n * {@link WorkspaceClient} facade.\n *\n * Isolating the SDK here is what makes the incremental migration to the modular\n * Databricks SDK a localized change: to migrate a service, swap its getter in\n * `client.ts` from the legacy delegate to the modular client — nothing else in\n * the codebase imports the SDK, so the blast radius is one connector + its test.\n */\n\nimport type {\n ClientOptions,\n WorkspaceClient as SdkWorkspaceClient,\n} from \"@databricks/sdk-experimental\";\nimport * as SDK from \"@databricks/sdk-experimental\";\n\nconst { WorkspaceClient: SdkWorkspaceClientCtor } = SDK;\n\n/** The concrete legacy SDK client type. */\nexport type LegacyWorkspaceClient = SdkWorkspaceClient;\n\n/**\n * Options used to construct the wrapper. Mirrors the subset of the old SDK's\n * `Config` + `ClientOptions` that AppKit relies on today; we deliberately do\n * NOT re-expose every old-SDK config knob.\n */\nexport interface WorkspaceClientOptions {\n /** Databricks host, e.g. https://my-workspace.cloud.databricks.com. Defaults to DATABRICKS_HOST / profile resolution. */\n host?: string;\n /** Bearer token. When set, `authType` defaults to \"pat\". */\n token?: string;\n /** Authentication strategy passed to the legacy client. */\n authType?: \"pat\";\n /**\n * SDK client options (product / productVersion / userAgentExtra) used to\n * stamp the outbound User-Agent. Produced by `getClientOptions()`; omitted\n * by build-time callers that don't stamp a User-Agent.\n */\n clientOptions?: ClientOptions;\n}\n\n/**\n * Construct a legacy `WorkspaceClient` from wrapper options.\n *\n * Centralised so the wrapper facade, the `.toLegacyWorkspaceClient()` escape\n * hatch, and any per-request OBO client all build it the same way.\n */\nexport function buildLegacyWorkspaceClient(\n opts: WorkspaceClientOptions,\n): LegacyWorkspaceClient {\n // Check `token !== undefined`, NOT truthiness: an explicitly-passed token\n // must stick to the PAT path even when it's an empty string. Falling through\n // to the default-auth branch on an empty OBO token would silently\n // authenticate as the service principal instead of failing — a privilege\n // escalation in the OBO path. An invalid/empty token should blow up loudly.\n const cfg =\n opts.token !== undefined\n ? { host: opts.host, token: opts.token, authType: opts.authType ?? \"pat\" }\n : opts.host\n ? { host: opts.host }\n : {};\n return new SdkWorkspaceClientCtor(cfg, opts.clientOptions);\n}\n\n// ── SDK type re-exports ──────────────────────────────────────────────\nexport type {\n CancellationToken,\n ClientOptions,\n} from \"@databricks/sdk-experimental\";\n// ── SDK value re-exports ─────────────────────────────────────────────\n//\n// AppKit modules import these from the wrapper instead of the SDK so the\n// boundary rule holds. `Context` bridges AbortSignal → CancellationToken\n// (serving/jobs/sql-warehouse); `Time`/`TimeUnits` drive genie polling;\n// `ConfigError` is matched in service-context's auth-failure handling.\n//\n// These are sourced off the namespace import rather than `export { ... } from`\n// because the SDK is CommonJS: its `Time` export is emitted as a getter that\n// calls `__importDefault(...)`, which defeats Node's static named-export\n// detection — a direct `export { Time }` throws \"does not provide an export\n// named 'Time'\" at ESM link time. `Time` is only reachable via the module\n// object, so we fall back to `SDK.default.Time` (matching the original genie\n// connector's `SDK.Time ?? SDK.default.Time` guard).\nexport const { ConfigError, Context, TimeUnits } = SDK;\nexport const Time =\n SDK.Time ?? (SDK as unknown as { default: typeof SDK }).default.Time;\n\n// Deep-import types used by the genie connector's waiter idiom. Not exposed\n// on the SDK's top-level index, so re-exported here to keep the genie\n// connector off a direct `@databricks/sdk-experimental/dist/**` import.\nexport type { GenieMessage } from \"@databricks/sdk-experimental/dist/apis/dashboards\";\nexport type { Waiter } from \"@databricks/sdk-experimental/dist/wait\";\n"],"mappings":";;;AAkBA,MAAM,EAAE,iBAAiB,2BAA2B;;;;;;;AA+BpD,SAAgB,2BACd,MACuB;AAYvB,QAAO,IAAI,uBALT,KAAK,UAAU,SACX;EAAE,MAAM,KAAK;EAAM,OAAO,KAAK;EAAO,UAAU,KAAK,YAAY;EAAO,GACxE,KAAK,OACH,EAAE,MAAM,KAAK,MAAM,GACnB,EAAE,EAC6B,KAAK,cAAc;;AAsB5D,MAAa,EAAE,aAAa,SAAS,cAAc;AACnD,MAAa,OACX,IAAI,QAAS,IAA2C,QAAQ"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { LegacyWorkspaceClient } from "./legacy.js";
|
|
2
|
+
import { files, jobs } from "@databricks/sdk-experimental";
|
|
3
|
+
|
|
4
|
+
//#region src/workspace-client/types.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* AppKit's workspace client facade. Mirrors the multi-client shape of the
|
|
7
|
+
* modular Databricks SDK: each service is its own accessor, so services can be
|
|
8
|
+
* migrated one at a time behind this stable interface.
|
|
9
|
+
*
|
|
10
|
+
* Accessors are legacy-typed for now (delegated to the underlying legacy SDK
|
|
11
|
+
* client); see the module docblock.
|
|
12
|
+
*/
|
|
13
|
+
interface WorkspaceClient$1 {
|
|
14
|
+
/** UC Volumes / Files API. */
|
|
15
|
+
readonly files: LegacyWorkspaceClient["files"];
|
|
16
|
+
/** SQL Warehouses. */
|
|
17
|
+
readonly warehouses: LegacyWorkspaceClient["warehouses"];
|
|
18
|
+
/** Genie / dashboards. */
|
|
19
|
+
readonly genie: LegacyWorkspaceClient["genie"];
|
|
20
|
+
/** Jobs. */
|
|
21
|
+
readonly jobs: LegacyWorkspaceClient["jobs"];
|
|
22
|
+
/** Statement Execution. */
|
|
23
|
+
readonly statementExecution: LegacyWorkspaceClient["statementExecution"];
|
|
24
|
+
/** Serving Endpoints. */
|
|
25
|
+
readonly servingEndpoints: LegacyWorkspaceClient["servingEndpoints"];
|
|
26
|
+
/** Current user. */
|
|
27
|
+
readonly currentUser: LegacyWorkspaceClient["currentUser"];
|
|
28
|
+
/**
|
|
29
|
+
* SDK `Config` — exposes `host` and `authenticate(headers)`. Used by the
|
|
30
|
+
* files-upload path and agents auth-header stamping, which bypass the typed
|
|
31
|
+
* services.
|
|
32
|
+
*/
|
|
33
|
+
readonly config: LegacyWorkspaceClient["config"];
|
|
34
|
+
/**
|
|
35
|
+
* Low-level HTTP transport (`apiClient.request(...)`). Used for endpoints
|
|
36
|
+
* without a typed service method: SCIM header probe, warehouse listing,
|
|
37
|
+
* serving SSE streaming, vector search, internal telemetry.
|
|
38
|
+
*/
|
|
39
|
+
readonly apiClient: LegacyWorkspaceClient["apiClient"];
|
|
40
|
+
/**
|
|
41
|
+
* Returns the underlying legacy `@databricks/sdk-experimental`
|
|
42
|
+
* `WorkspaceClient`, for handoff to code still typed against the old SDK
|
|
43
|
+
* (`@databricks/lakebase`). Transitional.
|
|
44
|
+
*/
|
|
45
|
+
toLegacyWorkspaceClient(): LegacyWorkspaceClient;
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { WorkspaceClient$1 as WorkspaceClient, type files, type jobs };
|
|
49
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/workspace-client/types.ts"],"mappings":";;;;;;;;;;;;UAkCiB,iBAAA;EAWA;EAAA,SATN,KAAA,EAAO,qBAAA;EAYa;EAAA,SATpB,UAAA,EAAY,qBAAA;EAYM;EAAA,SATlB,KAAA,EAAO,qBAAA;EAYM;EAAA,SATb,IAAA,EAAM,qBAAA;EAgBE;EAAA,SAbR,kBAAA,EAAoB,qBAAA;EAoBT;EAAA,SAjBX,gBAAA,EAAkB,qBAAA;EAwBA;EAAA,SArBlB,WAAA,EAAa,qBAAA;EAqB0B;;;;;EAAA,SAdvC,MAAA,EAAQ,qBAAA;;;;;;WAOR,SAAA,EAAW,qBAAA;;;;;;EAOpB,uBAAA,IAA2B,qBAAA;AAAA"}
|
|
@@ -9,12 +9,11 @@ Handles both structured `tool_calls` responses and text-based tool call fallback
|
|
|
9
9
|
## Examples[](#examples "Direct link to Examples")
|
|
10
10
|
|
|
11
11
|
```ts
|
|
12
|
-
import { createApp, createAgent, agents } from "@databricks/appkit";
|
|
12
|
+
import { createApp, createAgent, agents, createWorkspaceClient } from "@databricks/appkit";
|
|
13
13
|
import { DatabricksAdapter } from "@databricks/appkit/beta";
|
|
14
|
-
import { WorkspaceClient } from "@databricks/sdk-experimental";
|
|
15
14
|
|
|
16
15
|
const adapter = DatabricksAdapter.fromServingEndpoint({
|
|
17
|
-
workspaceClient:
|
|
16
|
+
workspaceClient: createWorkspaceClient(),
|
|
18
17
|
endpointName: "my-endpoint",
|
|
19
18
|
});
|
|
20
19
|
|
|
@@ -24,15 +24,15 @@ Initializes telemetry, cache, and service context, then registers plugins in pha
|
|
|
24
24
|
|
|
25
25
|
## Parameters[](#parameters "Direct link to Parameters")
|
|
26
26
|
|
|
27
|
-
| Parameter | Type
|
|
28
|
-
| ---------------------------------- |
|
|
29
|
-
| `config` | { `cache?`: [`CacheConfig`](./docs/api/appkit/Interface.CacheConfig.md); `client?`: `WorkspaceClient
|
|
30
|
-
| `config.cache?` | [`CacheConfig`](./docs/api/appkit/Interface.CacheConfig.md)
|
|
31
|
-
| `config.client?` | `WorkspaceClient` |
|
|
32
|
-
| `config.disableInternalTelemetry?` | `boolean`
|
|
33
|
-
| `config.onPluginsReady?` | (`appkit`: `PluginMap`<`T`>) => `void` \| `Promise`<`void`>
|
|
34
|
-
| `config.plugins?` | `T`
|
|
35
|
-
| `config.telemetry?` | [`TelemetryConfig`](./docs/api/appkit/Interface.TelemetryConfig.md)
|
|
27
|
+
| Parameter | Type |
|
|
28
|
+
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
29
|
+
| `config` | { `cache?`: [`CacheConfig`](./docs/api/appkit/Interface.CacheConfig.md); `client?`: [`WorkspaceClient`](./docs/api/appkit/Interface.WorkspaceClient.md); `disableInternalTelemetry?`: `boolean`; `onPluginsReady?`: (`appkit`: `PluginMap`<`T`>) => `void` \| `Promise`<`void`>; `plugins?`: `T`; `telemetry?`: [`TelemetryConfig`](./docs/api/appkit/Interface.TelemetryConfig.md); } |
|
|
30
|
+
| `config.cache?` | [`CacheConfig`](./docs/api/appkit/Interface.CacheConfig.md) |
|
|
31
|
+
| `config.client?` | [`WorkspaceClient`](./docs/api/appkit/Interface.WorkspaceClient.md) |
|
|
32
|
+
| `config.disableInternalTelemetry?` | `boolean` |
|
|
33
|
+
| `config.onPluginsReady?` | (`appkit`: `PluginMap`<`T`>) => `void` \| `Promise`<`void`> |
|
|
34
|
+
| `config.plugins?` | `T` |
|
|
35
|
+
| `config.telemetry?` | [`TelemetryConfig`](./docs/api/appkit/Interface.TelemetryConfig.md) |
|
|
36
36
|
|
|
37
37
|
## Returns[](#returns "Direct link to Returns")
|
|
38
38
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Function: createWorkspaceClient()
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
function createWorkspaceClient(opts: WorkspaceClientOptions): WorkspaceClient;
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
|
|
8
|
+
Construct an AppKit workspace client.
|
|
9
|
+
|
|
10
|
+
Auth resolution:
|
|
11
|
+
|
|
12
|
+
* If `opts.token` is set, uses PAT credentials.
|
|
13
|
+
* Otherwise walks the SDK default auth chain (env vars + \~/.databrickscfg).
|
|
14
|
+
|
|
15
|
+
Host resolution:
|
|
16
|
+
|
|
17
|
+
* Explicit `opts.host` → use it.
|
|
18
|
+
* Otherwise resolved by the SDK from `DATABRICKS_HOST` / profile.
|
|
19
|
+
|
|
20
|
+
## Parameters[](#parameters "Direct link to Parameters")
|
|
21
|
+
|
|
22
|
+
| Parameter | Type |
|
|
23
|
+
| --------- | --------------------------------------------------------------------------------------- |
|
|
24
|
+
| `opts` | [`WorkspaceClientOptions`](./docs/api/appkit/Interface.WorkspaceClientOptions.md) |
|
|
25
|
+
|
|
26
|
+
## Returns[](#returns "Direct link to Returns")
|
|
27
|
+
|
|
28
|
+
[`WorkspaceClient`](./docs/api/appkit/Interface.WorkspaceClient.md)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Interface: WorkspaceClient
|
|
2
|
+
|
|
3
|
+
AppKit's workspace client facade. Mirrors the multi-client shape of the modular Databricks SDK: each service is its own accessor, so services can be migrated one at a time behind this stable interface.
|
|
4
|
+
|
|
5
|
+
Accessors are legacy-typed for now (delegated to the underlying legacy SDK client); see the module docblock.
|
|
6
|
+
|
|
7
|
+
## Properties[](#properties "Direct link to Properties")
|
|
8
|
+
|
|
9
|
+
### apiClient[](#apiclient "Direct link to apiClient")
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
readonly apiClient: ApiClient;
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Low-level HTTP transport (`apiClient.request(...)`). Used for endpoints without a typed service method: SCIM header probe, warehouse listing, serving SSE streaming, vector search, internal telemetry.
|
|
17
|
+
|
|
18
|
+
***
|
|
19
|
+
|
|
20
|
+
### config[](#config "Direct link to config")
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
readonly config: Config;
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
SDK `Config` — exposes `host` and `authenticate(headers)`. Used by the files-upload path and agents auth-header stamping, which bypass the typed services.
|
|
28
|
+
|
|
29
|
+
***
|
|
30
|
+
|
|
31
|
+
### currentUser[](#currentuser "Direct link to currentUser")
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
readonly currentUser: CurrentUserService;
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Current user.
|
|
39
|
+
|
|
40
|
+
***
|
|
41
|
+
|
|
42
|
+
### files[](#files "Direct link to files")
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
readonly files: FilesService;
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
UC Volumes / Files API.
|
|
50
|
+
|
|
51
|
+
***
|
|
52
|
+
|
|
53
|
+
### genie[](#genie "Direct link to genie")
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
readonly genie: GenieService;
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Genie / dashboards.
|
|
61
|
+
|
|
62
|
+
***
|
|
63
|
+
|
|
64
|
+
### jobs[](#jobs "Direct link to jobs")
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
readonly jobs: JobsService;
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Jobs.
|
|
72
|
+
|
|
73
|
+
***
|
|
74
|
+
|
|
75
|
+
### servingEndpoints[](#servingendpoints "Direct link to servingEndpoints")
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
readonly servingEndpoints: ServingEndpointsService;
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Serving Endpoints.
|
|
83
|
+
|
|
84
|
+
***
|
|
85
|
+
|
|
86
|
+
### statementExecution[](#statementexecution "Direct link to statementExecution")
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
readonly statementExecution: StatementExecutionService;
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Statement Execution.
|
|
94
|
+
|
|
95
|
+
***
|
|
96
|
+
|
|
97
|
+
### warehouses[](#warehouses "Direct link to warehouses")
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
readonly warehouses: WarehousesService;
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
SQL Warehouses.
|
|
105
|
+
|
|
106
|
+
## Methods[](#methods "Direct link to Methods")
|
|
107
|
+
|
|
108
|
+
### toLegacyWorkspaceClient()[](#tolegacyworkspaceclient "Direct link to toLegacyWorkspaceClient()")
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
toLegacyWorkspaceClient(): WorkspaceClient;
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Returns the underlying legacy `@databricks/sdk-experimental` `WorkspaceClient`, for handoff to code still typed against the old SDK (`@databricks/lakebase`). Transitional.
|
|
116
|
+
|
|
117
|
+
#### Returns[](#returns "Direct link to Returns")
|
|
118
|
+
|
|
119
|
+
`WorkspaceClient`
|