@databricks/appkit 0.51.0 → 0.53.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/shared/src/schemas/manifest.d.ts +2 -2
- package/dist/stream/arrow-stream-processor.js.map +1 -1
- package/dist/type-generator/errors.js +59 -1
- package/dist/type-generator/errors.js.map +1 -1
- package/dist/type-generator/index.js +105 -22
- 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 +60 -21
- 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/types.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/docs/development/type-generation.md +17 -2
- package/llms.txt +3 -0
- package/package.json +1 -1
- package/sbom.cdx.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statement-result.js","names":[],"sources":["../../src/type-generator/statement-result.ts"],"sourcesContent":["import type { WorkspaceClient } from \"@databricks/sdk-experimental\";\nimport { createLogger } from \"../logging/logger\";\nimport { getErrorMessage } from \"./errors\";\nimport type { DatabricksStatementExecutionResponse } from \"./types\";\n\nconst logger = createLogger(\"type-generator:statement-result\");\n\n/**\n * Normalize a Statement Execution response so downstream parsers can always\n * read rows from `result.data_array`, regardless of the wire format the\n * warehouse chose.\n *\n * `@databricks/sdk-experimental`'s `executeStatement` defaults to an\n * `ARROW_STREAM` disposition. With an `INLINE` disposition the single\n * DESCRIBE row is returned as a base64-encoded Arrow IPC stream in\n * `result.attachment` and `result.data_array` is left undefined. The metric\n * and query type generators only ever read `result.data_array`, so without\n * this normalization an Arrow response reads as \"returned no rows\" — the\n * registry ships empty and the runtime fail-closed gate 503s every affected\n * metric/query. (A warehouse configured to return `JSON_ARRAY` populates\n * `data_array` directly and needs no decoding — that path, and every mocked\n * test, flows through here unchanged.)\n */\nexport async function normalizeResultRows(\n response: DatabricksStatementExecutionResponse,\n): Promise<DatabricksStatementExecutionResponse> {\n // Truncation guard, above the passthrough so it runs on either transport. A\n // result exceeding INLINE's size limit is paginated (`next_chunk_*` set) and\n // we hold only the first chunk; emitting types from it would cache partial\n // types. A deliberate throw — unlike the best-effort decode below — that both\n // callers catch per-entry as a loud per-key/per-query failure.\n if (\n response.result?.next_chunk_index != null ||\n response.result?.next_chunk_internal_link != null\n ) {\n throw new Error(\n \"DESCRIBE result is multi-chunk (truncated); refusing to emit partial types — see next_chunk_index\",\n );\n }\n\n // Passthrough: rows already materialized (JSON_ARRAY warehouses + every\n // mocked test). `data_array` being an empty array still counts as present —\n // that is a genuine \"no rows\" answer we must not overwrite with a decode.\n if (response.result?.data_array !== undefined) {\n return response;\n }\n\n const attachment = response.result?.attachment;\n if (attachment === undefined) {\n // No rows, no attachment: let the downstream \"no rows\" degrade path fire.\n return response;\n }\n\n try {\n // Lazy import: only pull apache-arrow into the process when an attachment\n // genuinely needs decoding.\n const { tableFromIPC } = await import(\"apache-arrow\");\n const bytes = Buffer.from(attachment, \"base64\");\n const table = tableFromIPC(bytes);\n // Extract each cell in SCHEMA/FIELD order. Spreading a StructRow\n // (`[...row]`) drives apache-arrow's StructRowIterator, which walks the\n // struct's children by positional index and yields `[fieldName, value]`\n // pairs in field order. We deliberately do NOT use `Object.values(row)`\n // nor `row.toArray()`: both funnel through `Object.values` (toArray() is\n // literally `Object.values(this.toJSON())` in apache-arrow@21), and\n // `Object.values` re-sorts integer-like keys ascending per the ECMAScript\n // spec — so an integer-named DESCRIBE column would scramble the positional\n // `[col_name, data_type, comment]` order. The iterator is immune to that.\n const dataArray: (string | null)[][] = table\n .toArray()\n .map((row) =>\n [...(row as Iterable<[unknown, unknown]>)].map(([, value]) =>\n value == null ? null : String(value),\n ),\n );\n\n return {\n ...response,\n result: {\n ...response.result,\n data_array: dataArray,\n },\n };\n } catch (err) {\n // Best-effort: a corrupt/partial Arrow payload — or a missing apache-arrow\n // module — must not crash the pass. Warn so a Reyden user whose decode failed\n // gets a breadcrumb instead of mysteriously-empty types, then return the\n // response unchanged: it routes into the deterministic \"no rows\" degrade.\n logger.warn(\n \"failed to decode ARROW_STREAM DESCRIBE attachment (%s); emitting no rows — metric/query types may degrade\",\n getErrorMessage(err),\n );\n return response;\n }\n}\n\n/** Result format the typegen requests for a DESCRIBE. */\ntype DescribeFormat = \"JSON_ARRAY\" | \"ARROW_STREAM\";\n\n/**\n * Per-path memo of the result format a warehouse accepts for a DESCRIBE shape.\n * Create one per describe path (metric / query) and reuse it across that path's\n * statements: a typegen run targets a single warehouse, so the working format\n * is discovered once and every later DESCRIBE skips the probe. NOT shared\n * across paths — `DESCRIBE QUERY` and `DESCRIBE … AS JSON` can differ (Reyden\n * fails `JSON_ARRAY` only for the single-cell `AS JSON` result).\n */\nexport interface DescribeFormatMemo {\n format?: \"JSON_ARRAY\" | \"ARROW_STREAM\";\n}\n\n/**\n * True when a failure means the warehouse REJECTED the requested result format\n * (so another format is worth trying), not that it ran the statement and hit a\n * real error. There is no single structured signal for this, so we combine two:\n *\n * - `errorCode` — a statement that actually RAN and failed carries a SQL error\n * code (`TABLE_OR_VIEW_NOT_FOUND`, `PARSE_SYNTAX_ERROR`, …); a request-shape\n * rejection comes back as `INVALID_PARAMETER_VALUE`. Any other code means the\n * statement ran, so it is never a format rejection — whatever its message\n * text happens to contain. This gate matters because DESCRIBE runs over\n * user-supplied SQL and analysis errors echo the offending SQL back: a source\n * with columns named `disposition`/`format` would otherwise trip the message\n * match below and get its real diagnostic masked by a pointless retry.\n * - the message signatures — Reyden's `merge_json_arrays` (its `JSON_ARRAY`\n * assembly fails on a `… AS JSON` single-cell result) and standard DBSQL's\n * rejection of `ARROW_STREAM` under an `INLINE` disposition.\n *\n * A genuine SQL error, connectivity failure, or not-ready warehouse does NOT\n * match — those are returned/propagated for the caller's normal handling, never\n * re-tried. `errorCode` is optional: a thrown SDK error carries no reliable\n * structured code, so the catch-path caller passes message only.\n */\nfunction isFormatRejection(\n message: string | undefined,\n errorCode?: string,\n): boolean {\n if (!message) return false;\n // It ran and produced a SQL error — never a format rejection, whatever the\n // message text happens to contain.\n if (errorCode && errorCode !== \"INVALID_PARAMETER_VALUE\") return false;\n const m = message.toLowerCase();\n return (\n m.includes(\"merge_json_arrays\") ||\n m.includes(\"must be json_array\") ||\n (m.includes(\"disposition\") && m.includes(\"format\"))\n );\n}\n\n/**\n * Run a DESCRIBE and return a response whose rows are readable via\n * `result.data_array`, adapting to the warehouse's result-format capability.\n *\n * No single format is portable: standard DBSQL (PRO/CLASSIC) serves\n * `INLINE`+`JSON_ARRAY` and rejects `INLINE`+`ARROW_STREAM`; the Reyden engine\n * is the inverse — it rejects `JSON_ARRAY` on a `… AS JSON` result\n * (`merge_json_arrays`) and only returns rows as an `INLINE`+`ARROW_STREAM`\n * attachment. We try `JSON_ARRAY` first (the documented default) and ONLY when\n * the warehouse rejects that format ({@link isFormatRejection}) fall back to\n * `ARROW_STREAM` (decoded by {@link normalizeResultRows}); the accepted format\n * is memoized so the rest of the run skips the probe. Any other outcome —\n * success, SQL error, degrade, connectivity failure — is returned or propagated\n * unchanged, exactly as a single executeStatement would.\n */\nexport async function describeAdaptive(\n client: WorkspaceClient,\n statement: string,\n warehouseId: string,\n memo: DescribeFormatMemo,\n): Promise<DatabricksStatementExecutionResponse> {\n const formats: DescribeFormat[] = memo.format\n ? [memo.format]\n : [\"JSON_ARRAY\", \"ARROW_STREAM\"];\n let lastResponse: DatabricksStatementExecutionResponse | undefined;\n let lastError: unknown;\n for (const format of formats) {\n try {\n const response = (await client.statementExecution.executeStatement({\n statement,\n warehouse_id: warehouseId,\n // Synchronous wait: without it the call can return PENDING/RUNNING with\n // no rows, which downstream misreads as a no-result degrade.\n wait_timeout: \"30s\",\n format,\n disposition: \"INLINE\",\n })) as DatabricksStatementExecutionResponse;\n const normalized = await normalizeResultRows(response);\n if (\n normalized.status?.state === \"FAILED\" &&\n isFormatRejection(\n normalized.status.error?.message,\n normalized.status.error?.error_code,\n )\n ) {\n lastResponse = normalized;\n continue; // warehouse rejected this format — try the next\n }\n if (normalized.status?.state === \"SUCCEEDED\") {\n memo.format = format;\n }\n return normalized;\n } catch (error) {\n if (isFormatRejection(getErrorMessage(error))) {\n lastError = error;\n continue; // format rejected via a thrown error — try the next\n }\n throw error;\n }\n }\n // Every attempted format was rejected. Surface the last outcome so the caller\n // degrades / reports as usual.\n if (lastResponse !== undefined) return lastResponse;\n throw lastError;\n}\n"],"mappings":";;;;AAKA,MAAM,SAAS,aAAa,kCAAkC;;;;;;;;;;;;;;;;;AAkB9D,eAAsB,oBACpB,UAC+C;AAM/C,KACE,SAAS,QAAQ,oBAAoB,QACrC,SAAS,QAAQ,4BAA4B,KAE7C,OAAM,IAAI,MACR,oGACD;AAMH,KAAI,SAAS,QAAQ,eAAe,OAClC,QAAO;CAGT,MAAM,aAAa,SAAS,QAAQ;AACpC,KAAI,eAAe,OAEjB,QAAO;AAGT,KAAI;EAGF,MAAM,EAAE,iBAAiB,MAAM,OAAO;EAYtC,MAAM,YAVQ,aADA,OAAO,KAAK,YAAY,SAAS,CACd,CAW9B,SAAS,CACT,KAAK,QACJ,CAAC,GAAI,IAAqC,CAAC,KAAK,GAAG,WACjD,SAAS,OAAO,OAAO,OAAO,MAAM,CACrC,CACF;AAEH,SAAO;GACL,GAAG;GACH,QAAQ;IACN,GAAG,SAAS;IACZ,YAAY;IACb;GACF;UACM,KAAK;AAKZ,SAAO,KACL,6GACA,gBAAgB,IAAI,CACrB;AACD,SAAO;;;;;;;;;;;;;;;;;;;;;;;;;AAyCX,SAAS,kBACP,SACA,WACS;AACT,KAAI,CAAC,QAAS,QAAO;AAGrB,KAAI,aAAa,cAAc,0BAA2B,QAAO;CACjE,MAAM,IAAI,QAAQ,aAAa;AAC/B,QACE,EAAE,SAAS,oBAAoB,IAC/B,EAAE,SAAS,qBAAqB,IAC/B,EAAE,SAAS,cAAc,IAAI,EAAE,SAAS,SAAS;;;;;;;;;;;;;;;;;AAmBtD,eAAsB,iBACpB,QACA,WACA,aACA,MAC+C;CAC/C,MAAM,UAA4B,KAAK,SACnC,CAAC,KAAK,OAAO,GACb,CAAC,cAAc,eAAe;CAClC,IAAI;CACJ,IAAI;AACJ,MAAK,MAAM,UAAU,QACnB,KAAI;EAUF,MAAM,aAAa,MAAM,oBATP,MAAM,OAAO,mBAAmB,iBAAiB;GACjE;GACA,cAAc;GAGd,cAAc;GACd;GACA,aAAa;GACd,CAAC,CACoD;AACtD,MACE,WAAW,QAAQ,UAAU,YAC7B,kBACE,WAAW,OAAO,OAAO,SACzB,WAAW,OAAO,OAAO,WAC1B,EACD;AACA,kBAAe;AACf;;AAEF,MAAI,WAAW,QAAQ,UAAU,YAC/B,MAAK,SAAS;AAEhB,SAAO;UACA,OAAO;AACd,MAAI,kBAAkB,gBAAgB,MAAM,CAAC,EAAE;AAC7C,eAAY;AACZ;;AAEF,QAAM;;AAKV,KAAI,iBAAiB,OAAW,QAAO;AACvC,OAAM"}
|
|
1
|
+
{"version":3,"file":"statement-result.js","names":[],"sources":["../../src/type-generator/statement-result.ts"],"sourcesContent":["import { createLogger } from \"../logging/logger\";\nimport type { WorkspaceClient } from \"../workspace-client\";\nimport { getErrorMessage } from \"./errors\";\nimport type { DatabricksStatementExecutionResponse } from \"./types\";\n\nconst logger = createLogger(\"type-generator:statement-result\");\n\n/**\n * Normalize a Statement Execution response so downstream parsers can always\n * read rows from `result.data_array`, regardless of the wire format the\n * warehouse chose.\n *\n * `@databricks/sdk-experimental`'s `executeStatement` defaults to an\n * `ARROW_STREAM` disposition. With an `INLINE` disposition the single\n * DESCRIBE row is returned as a base64-encoded Arrow IPC stream in\n * `result.attachment` and `result.data_array` is left undefined. The metric\n * and query type generators only ever read `result.data_array`, so without\n * this normalization an Arrow response reads as \"returned no rows\" — the\n * registry ships empty and the runtime fail-closed gate 503s every affected\n * metric/query. (A warehouse configured to return `JSON_ARRAY` populates\n * `data_array` directly and needs no decoding — that path, and every mocked\n * test, flows through here unchanged.)\n */\nexport async function normalizeResultRows(\n response: DatabricksStatementExecutionResponse,\n): Promise<DatabricksStatementExecutionResponse> {\n // Truncation guard, above the passthrough so it runs on either transport. A\n // result exceeding INLINE's size limit is paginated (`next_chunk_*` set) and\n // we hold only the first chunk; emitting types from it would cache partial\n // types. A deliberate throw — unlike the best-effort decode below — that both\n // callers catch per-entry as a loud per-key/per-query failure.\n if (\n response.result?.next_chunk_index != null ||\n response.result?.next_chunk_internal_link != null\n ) {\n throw new Error(\n \"DESCRIBE result is multi-chunk (truncated); refusing to emit partial types — see next_chunk_index\",\n );\n }\n\n // Passthrough: rows already materialized (JSON_ARRAY warehouses + every\n // mocked test). `data_array` being an empty array still counts as present —\n // that is a genuine \"no rows\" answer we must not overwrite with a decode.\n if (response.result?.data_array !== undefined) {\n return response;\n }\n\n const attachment = response.result?.attachment;\n if (attachment === undefined) {\n // No rows, no attachment: let the downstream \"no rows\" degrade path fire.\n return response;\n }\n\n try {\n // Lazy import: only pull apache-arrow into the process when an attachment\n // genuinely needs decoding.\n const { tableFromIPC } = await import(\"apache-arrow\");\n const bytes = Buffer.from(attachment, \"base64\");\n const table = tableFromIPC(bytes);\n // Extract each cell in SCHEMA/FIELD order. Spreading a StructRow\n // (`[...row]`) drives apache-arrow's StructRowIterator, which walks the\n // struct's children by positional index and yields `[fieldName, value]`\n // pairs in field order. We deliberately do NOT use `Object.values(row)`\n // nor `row.toArray()`: both funnel through `Object.values` (toArray() is\n // literally `Object.values(this.toJSON())` in apache-arrow@21), and\n // `Object.values` re-sorts integer-like keys ascending per the ECMAScript\n // spec — so an integer-named DESCRIBE column would scramble the positional\n // `[col_name, data_type, comment]` order. The iterator is immune to that.\n const dataArray: (string | null)[][] = table\n .toArray()\n .map((row) =>\n [...(row as Iterable<[unknown, unknown]>)].map(([, value]) =>\n value == null ? null : String(value),\n ),\n );\n\n return {\n ...response,\n result: {\n ...response.result,\n data_array: dataArray,\n },\n };\n } catch (err) {\n // Best-effort: a corrupt/partial Arrow payload — or a missing apache-arrow\n // module — must not crash the pass. Warn so a Reyden user whose decode failed\n // gets a breadcrumb instead of mysteriously-empty types, then return the\n // response unchanged: it routes into the deterministic \"no rows\" degrade.\n logger.warn(\n \"failed to decode ARROW_STREAM DESCRIBE attachment (%s); emitting no rows — metric/query types may degrade\",\n getErrorMessage(err),\n );\n return response;\n }\n}\n\n/** Result format the typegen requests for a DESCRIBE. */\ntype DescribeFormat = \"JSON_ARRAY\" | \"ARROW_STREAM\";\n\n/**\n * Per-path memo of the result format a warehouse accepts for a DESCRIBE shape.\n * Create one per describe path (metric / query) and reuse it across that path's\n * statements: a typegen run targets a single warehouse, so the working format\n * is discovered once and every later DESCRIBE skips the probe. NOT shared\n * across paths — `DESCRIBE QUERY` and `DESCRIBE … AS JSON` can differ (Reyden\n * fails `JSON_ARRAY` only for the single-cell `AS JSON` result).\n */\nexport interface DescribeFormatMemo {\n format?: \"JSON_ARRAY\" | \"ARROW_STREAM\";\n}\n\n/**\n * True when a failure means the warehouse REJECTED the requested result format\n * (so another format is worth trying), not that it ran the statement and hit a\n * real error. There is no single structured signal for this, so we combine two:\n *\n * - `errorCode` — a statement that actually RAN and failed carries a SQL error\n * code (`TABLE_OR_VIEW_NOT_FOUND`, `PARSE_SYNTAX_ERROR`, …); a request-shape\n * rejection comes back as `INVALID_PARAMETER_VALUE`. Any other code means the\n * statement ran, so it is never a format rejection — whatever its message\n * text happens to contain. This gate matters because DESCRIBE runs over\n * user-supplied SQL and analysis errors echo the offending SQL back: a source\n * with columns named `disposition`/`format` would otherwise trip the message\n * match below and get its real diagnostic masked by a pointless retry.\n * - the message signatures — Reyden's `merge_json_arrays` (its `JSON_ARRAY`\n * assembly fails on a `… AS JSON` single-cell result) and standard DBSQL's\n * rejection of `ARROW_STREAM` under an `INLINE` disposition.\n *\n * A genuine SQL error, connectivity failure, or not-ready warehouse does NOT\n * match — those are returned/propagated for the caller's normal handling, never\n * re-tried. `errorCode` is optional: a thrown SDK error carries no reliable\n * structured code, so the catch-path caller passes message only.\n */\nfunction isFormatRejection(\n message: string | undefined,\n errorCode?: string,\n): boolean {\n if (!message) return false;\n // It ran and produced a SQL error — never a format rejection, whatever the\n // message text happens to contain.\n if (errorCode && errorCode !== \"INVALID_PARAMETER_VALUE\") return false;\n const m = message.toLowerCase();\n return (\n m.includes(\"merge_json_arrays\") ||\n m.includes(\"must be json_array\") ||\n (m.includes(\"disposition\") && m.includes(\"format\"))\n );\n}\n\n/**\n * Run a DESCRIBE and return a response whose rows are readable via\n * `result.data_array`, adapting to the warehouse's result-format capability.\n *\n * No single format is portable: standard DBSQL (PRO/CLASSIC) serves\n * `INLINE`+`JSON_ARRAY` and rejects `INLINE`+`ARROW_STREAM`; the Reyden engine\n * is the inverse — it rejects `JSON_ARRAY` on a `… AS JSON` result\n * (`merge_json_arrays`) and only returns rows as an `INLINE`+`ARROW_STREAM`\n * attachment. We try `JSON_ARRAY` first (the documented default) and ONLY when\n * the warehouse rejects that format ({@link isFormatRejection}) fall back to\n * `ARROW_STREAM` (decoded by {@link normalizeResultRows}); the accepted format\n * is memoized so the rest of the run skips the probe. Any other outcome —\n * success, SQL error, degrade, connectivity failure — is returned or propagated\n * unchanged, exactly as a single executeStatement would.\n */\nexport async function describeAdaptive(\n client: WorkspaceClient,\n statement: string,\n warehouseId: string,\n memo: DescribeFormatMemo,\n): Promise<DatabricksStatementExecutionResponse> {\n const formats: DescribeFormat[] = memo.format\n ? [memo.format]\n : [\"JSON_ARRAY\", \"ARROW_STREAM\"];\n let lastResponse: DatabricksStatementExecutionResponse | undefined;\n let lastError: unknown;\n for (const format of formats) {\n try {\n const response = (await client.statementExecution.executeStatement({\n statement,\n warehouse_id: warehouseId,\n // Synchronous wait: without it the call can return PENDING/RUNNING with\n // no rows, which downstream misreads as a no-result degrade.\n wait_timeout: \"30s\",\n format,\n disposition: \"INLINE\",\n })) as DatabricksStatementExecutionResponse;\n const normalized = await normalizeResultRows(response);\n if (\n normalized.status?.state === \"FAILED\" &&\n isFormatRejection(\n normalized.status.error?.message,\n normalized.status.error?.error_code,\n )\n ) {\n lastResponse = normalized;\n continue; // warehouse rejected this format — try the next\n }\n if (normalized.status?.state === \"SUCCEEDED\") {\n memo.format = format;\n }\n return normalized;\n } catch (error) {\n if (isFormatRejection(getErrorMessage(error))) {\n lastError = error;\n continue; // format rejected via a thrown error — try the next\n }\n throw error;\n }\n }\n // Every attempted format was rejected. Surface the last outcome so the caller\n // degrades / reports as usual.\n if (lastResponse !== undefined) return lastResponse;\n throw lastError;\n}\n"],"mappings":";;;;AAKA,MAAM,SAAS,aAAa,kCAAkC;;;;;;;;;;;;;;;;;AAkB9D,eAAsB,oBACpB,UAC+C;AAM/C,KACE,SAAS,QAAQ,oBAAoB,QACrC,SAAS,QAAQ,4BAA4B,KAE7C,OAAM,IAAI,MACR,oGACD;AAMH,KAAI,SAAS,QAAQ,eAAe,OAClC,QAAO;CAGT,MAAM,aAAa,SAAS,QAAQ;AACpC,KAAI,eAAe,OAEjB,QAAO;AAGT,KAAI;EAGF,MAAM,EAAE,iBAAiB,MAAM,OAAO;EAYtC,MAAM,YAVQ,aADA,OAAO,KAAK,YAAY,SAAS,CACd,CAW9B,SAAS,CACT,KAAK,QACJ,CAAC,GAAI,IAAqC,CAAC,KAAK,GAAG,WACjD,SAAS,OAAO,OAAO,OAAO,MAAM,CACrC,CACF;AAEH,SAAO;GACL,GAAG;GACH,QAAQ;IACN,GAAG,SAAS;IACZ,YAAY;IACb;GACF;UACM,KAAK;AAKZ,SAAO,KACL,6GACA,gBAAgB,IAAI,CACrB;AACD,SAAO;;;;;;;;;;;;;;;;;;;;;;;;;AAyCX,SAAS,kBACP,SACA,WACS;AACT,KAAI,CAAC,QAAS,QAAO;AAGrB,KAAI,aAAa,cAAc,0BAA2B,QAAO;CACjE,MAAM,IAAI,QAAQ,aAAa;AAC/B,QACE,EAAE,SAAS,oBAAoB,IAC/B,EAAE,SAAS,qBAAqB,IAC/B,EAAE,SAAS,cAAc,IAAI,EAAE,SAAS,SAAS;;;;;;;;;;;;;;;;;AAmBtD,eAAsB,iBACpB,QACA,WACA,aACA,MAC+C;CAC/C,MAAM,UAA4B,KAAK,SACnC,CAAC,KAAK,OAAO,GACb,CAAC,cAAc,eAAe;CAClC,IAAI;CACJ,IAAI;AACJ,MAAK,MAAM,UAAU,QACnB,KAAI;EAUF,MAAM,aAAa,MAAM,oBATP,MAAM,OAAO,mBAAmB,iBAAiB;GACjE;GACA,cAAc;GAGd,cAAc;GACd;GACA,aAAa;GACd,CAAC,CACoD;AACtD,MACE,WAAW,QAAQ,UAAU,YAC7B,kBACE,WAAW,OAAO,OAAO,SACzB,WAAW,OAAO,OAAO,WAC1B,EACD;AACA,kBAAe;AACf;;AAEF,MAAI,WAAW,QAAQ,UAAU,YAC/B,MAAK,SAAS;AAEhB,SAAO;UACA,OAAO;AACd,MAAI,kBAAkB,gBAAgB,MAAM,CAAC,EAAE;AAC7C,eAAY;AACZ;;AAEF,QAAM;;AAKV,KAAI,iBAAiB,OAAW,QAAO;AACvC,OAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","names":[],"sources":["../../src/type-generator/types.ts"],"sourcesContent":["/**\n * Databricks statement execution response interface for DESCRIBE QUERY /\n * DESCRIBE TABLE EXTENDED.\n *\n * Two result shapes matter here:\n * - `result.data_array` — rows already materialized as JSON arrays. Present\n * when the warehouse returns `JSON_ARRAY` (and what every mocked test\n * builds).\n * - `result.attachment` — a base64-encoded Arrow IPC stream. Present when the\n * statement runs with `format: \"ARROW_STREAM\"` + `disposition: \"INLINE\"`,\n * which is the SDK's default disposition. The single row lands here and\n * `data_array` is left undefined. {@link normalizeResultRows} decodes this\n * back into `data_array` so downstream parsers stay shape-agnostic.\n *\n * @property statement_id - the id of the statement\n * @property status - the status of the statement\n * @property manifest - result metadata; `manifest.format` echoes the wire\n * format (`ARROW_STREAM`, `JSON_ARRAY`, ...) the warehouse chose.\n * @property result - the result; either `data_array` (rows as\n * `[col_name, data_type, comment]` arrays) or `attachment` (base64 Arrow IPC)\n */\nexport interface DatabricksStatementExecutionResponse {\n statement_id: string;\n status: {\n state: string;\n error?: { error_code?: string; message?: string };\n };\n manifest?: {\n format?: string;\n };\n result?: {\n data_array?: (string | null)[][];\n /** Base64-encoded Arrow IPC stream (ARROW_STREAM + INLINE disposition). */\n attachment?: string;\n /**\n * Set when the result spans multiple chunks (rows exceeded INLINE's size\n * limit). Its presence means this response holds only the FIRST chunk;\n * {@link normalizeResultRows} throws rather than emit truncated types.\n */\n next_chunk_index?: number;\n /** Companion to {@link next_chunk_index}: link to fetch the next chunk. */\n next_chunk_internal_link?: string;\n };\n}\n\n/**\n * Map of SQL types to their corresponding marker types\n * Used to convert SQL types to their corresponding marker types\n */\nexport const sqlTypeToMarker: Record<string, string> = {\n // string\n STRING: \"SQLStringMarker\",\n BINARY: \"SQLBinaryMarker\",\n // boolean\n BOOLEAN: \"SQLBooleanMarker\",\n // numeric\n NUMERIC: \"SQLNumberMarker\",\n INT: \"SQLNumberMarker\",\n BIGINT: \"SQLNumberMarker\",\n TINYINT: \"SQLNumberMarker\",\n SMALLINT: \"SQLNumberMarker\",\n FLOAT: \"SQLNumberMarker\",\n DOUBLE: \"SQLNumberMarker\",\n DECIMAL: \"SQLNumberMarker\",\n // date/time\n DATE: \"SQLDateMarker\",\n TIMESTAMP: \"SQLTimestampMarker\",\n TIMESTAMP_NTZ: \"SQLTimestampMarker\",\n};\n\n/**\n * Map of SQL types to their corresponding helper function names\n * Used to generate JSDoc hints for parameters\n */\nexport const sqlTypeToHelper: Record<string, string> = {\n // string\n STRING: \"sql.string()\",\n BINARY: \"sql.binary()\",\n // boolean\n BOOLEAN: \"sql.boolean()\",\n // numeric — route each SQL type to its closest typed helper. INT/BIGINT\n // are critical for LIMIT/OFFSET; FLOAT/DOUBLE preserve precision intent;\n // NUMERIC/DECIMAL route to sql.numeric() for exact-decimal columns.\n NUMERIC: \"sql.numeric()\",\n DECIMAL: \"sql.numeric()\",\n BIGINT: \"sql.bigint()\",\n INT: \"sql.int()\",\n TINYINT: \"sql.int()\",\n SMALLINT: \"sql.int()\",\n FLOAT: \"sql.float()\",\n DOUBLE: \"sql.double()\",\n // date/time\n DATE: \"sql.date()\",\n TIMESTAMP: \"sql.timestamp()\",\n TIMESTAMP_NTZ: \"sql.timestamp()\",\n};\n\n/**\n * Query schema interface\n * @property name - the name of the query\n * @property type - the type of the query (string, number, boolean, object, array, etc.)\n */\nexport interface QuerySchema {\n name: string;\n type: string;\n}\n\n/**\n * A genuine SQL error: `DESCRIBE QUERY` ran against a *reachable* warehouse and\n * the warehouse reported the statement as FAILED (bad table, syntax error,\n * incompatible type, …). Distinct from a connectivity failure (warehouse\n * unreachable), which is non-fatal and never recorded here.\n * @property name - the query name\n * @property message - the SQL error message reported by the warehouse\n */\nexport interface QuerySyntaxError {\n name: string;\n message: string;\n}\n\n/**\n * A non-SQL fatal error while attempting to describe a query: authentication,\n * authorization, invalid warehouse/configuration, malformed SDK request, or\n * any other setup problem that should not be treated as an offline warehouse.\n * @property name - the query name\n * @property message - the fatal error message\n */\nexport interface QueryFatalError {\n name: string;\n message: string;\n}\n\n/**\n * Result of describing a folder of queries.\n * @property schemas - one schema per query, in original file order. Queries that\n * could not be described carry `result: unknown` so output stays valid.\n * @property syntaxErrors - queries whose DESCRIBE failed against a reachable\n * warehouse (genuine SQL errors). Connectivity failures are deliberately NOT\n * included: they degrade silently (reuse last-known-good type or emit\n * `unknown`) so a transient outage never fails a build.\n * @property fatalErrors - non-SQL fatal describe request failures. These still
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../../src/type-generator/types.ts"],"sourcesContent":["/**\n * Databricks statement execution response interface for DESCRIBE QUERY /\n * DESCRIBE TABLE EXTENDED.\n *\n * Two result shapes matter here:\n * - `result.data_array` — rows already materialized as JSON arrays. Present\n * when the warehouse returns `JSON_ARRAY` (and what every mocked test\n * builds).\n * - `result.attachment` — a base64-encoded Arrow IPC stream. Present when the\n * statement runs with `format: \"ARROW_STREAM\"` + `disposition: \"INLINE\"`,\n * which is the SDK's default disposition. The single row lands here and\n * `data_array` is left undefined. {@link normalizeResultRows} decodes this\n * back into `data_array` so downstream parsers stay shape-agnostic.\n *\n * @property statement_id - the id of the statement\n * @property status - the status of the statement\n * @property manifest - result metadata; `manifest.format` echoes the wire\n * format (`ARROW_STREAM`, `JSON_ARRAY`, ...) the warehouse chose.\n * @property result - the result; either `data_array` (rows as\n * `[col_name, data_type, comment]` arrays) or `attachment` (base64 Arrow IPC)\n */\nexport interface DatabricksStatementExecutionResponse {\n statement_id: string;\n status: {\n state: string;\n error?: { error_code?: string; message?: string };\n };\n manifest?: {\n format?: string;\n };\n result?: {\n data_array?: (string | null)[][];\n /** Base64-encoded Arrow IPC stream (ARROW_STREAM + INLINE disposition). */\n attachment?: string;\n /**\n * Set when the result spans multiple chunks (rows exceeded INLINE's size\n * limit). Its presence means this response holds only the FIRST chunk;\n * {@link normalizeResultRows} throws rather than emit truncated types.\n */\n next_chunk_index?: number;\n /** Companion to {@link next_chunk_index}: link to fetch the next chunk. */\n next_chunk_internal_link?: string;\n };\n}\n\n/**\n * Map of SQL types to their corresponding marker types\n * Used to convert SQL types to their corresponding marker types\n */\nexport const sqlTypeToMarker: Record<string, string> = {\n // string\n STRING: \"SQLStringMarker\",\n BINARY: \"SQLBinaryMarker\",\n // boolean\n BOOLEAN: \"SQLBooleanMarker\",\n // numeric\n NUMERIC: \"SQLNumberMarker\",\n INT: \"SQLNumberMarker\",\n BIGINT: \"SQLNumberMarker\",\n TINYINT: \"SQLNumberMarker\",\n SMALLINT: \"SQLNumberMarker\",\n FLOAT: \"SQLNumberMarker\",\n DOUBLE: \"SQLNumberMarker\",\n DECIMAL: \"SQLNumberMarker\",\n // date/time\n DATE: \"SQLDateMarker\",\n TIMESTAMP: \"SQLTimestampMarker\",\n TIMESTAMP_NTZ: \"SQLTimestampMarker\",\n};\n\n/**\n * Map of SQL types to their corresponding helper function names\n * Used to generate JSDoc hints for parameters\n */\nexport const sqlTypeToHelper: Record<string, string> = {\n // string\n STRING: \"sql.string()\",\n BINARY: \"sql.binary()\",\n // boolean\n BOOLEAN: \"sql.boolean()\",\n // numeric — route each SQL type to its closest typed helper. INT/BIGINT\n // are critical for LIMIT/OFFSET; FLOAT/DOUBLE preserve precision intent;\n // NUMERIC/DECIMAL route to sql.numeric() for exact-decimal columns.\n NUMERIC: \"sql.numeric()\",\n DECIMAL: \"sql.numeric()\",\n BIGINT: \"sql.bigint()\",\n INT: \"sql.int()\",\n TINYINT: \"sql.int()\",\n SMALLINT: \"sql.int()\",\n FLOAT: \"sql.float()\",\n DOUBLE: \"sql.double()\",\n // date/time\n DATE: \"sql.date()\",\n TIMESTAMP: \"sql.timestamp()\",\n TIMESTAMP_NTZ: \"sql.timestamp()\",\n};\n\n/**\n * Query schema interface\n * @property name - the name of the query\n * @property type - the type of the query (string, number, boolean, object, array, etc.)\n * @property degraded - true when the schema could not be resolved and `type`\n * is an unknown fallback. Absent when `type` came from DESCRIBE or a matching\n * last-known-good cache entry.\n */\nexport interface QuerySchema {\n name: string;\n type: string;\n degraded?: boolean;\n}\n\n/**\n * A genuine SQL error: `DESCRIBE QUERY` ran against a *reachable* warehouse and\n * the warehouse reported the statement as FAILED (bad table, syntax error,\n * incompatible type, …). Distinct from a connectivity failure (warehouse\n * unreachable), which is non-fatal and never recorded here.\n * @property name - the query name\n * @property message - the SQL error message reported by the warehouse\n */\nexport interface QuerySyntaxError {\n name: string;\n message: string;\n}\n\n/**\n * A non-SQL fatal error while attempting to describe a query: authentication,\n * authorization, invalid warehouse/configuration, malformed SDK request, or\n * any other setup problem that should not be treated as an offline warehouse.\n * @property name - the query name\n * @property message - the fatal error message\n */\nexport interface QueryFatalError {\n name: string;\n message: string;\n}\n\n/**\n * Result of describing a folder of queries.\n * @property schemas - one schema per query, in original file order. Queries that\n * could not be described carry `result: unknown` so output stays valid.\n * @property syntaxErrors - queries whose DESCRIBE failed against a reachable\n * warehouse (genuine SQL errors). Connectivity failures are deliberately NOT\n * included: they degrade silently (reuse last-known-good type or emit\n * `unknown`) so a transient outage never fails a build.\n * @property fatalErrors - deterministic non-SQL fatal describe request failures\n * (404/400). These still produce `result: unknown` schemas so callers can write\n * declarations before surfacing the error.\n * @property hadEnvironmentalFailure - `true` when an environmental failure occurred\n * in blocking mode (auth, connectivity, timeouts, or other unrecognized failures).\n * Used by {@link generateFromEntryPoint} to decide whether to apply the has-types\n * gate. Always false in non-blocking mode.\n * @property environmentalCause - coarse cause label for the environmental failure,\n * one of \"auth\" (401/403), \"unreachable\" (connectivity), or \"unavailable\" (other).\n * Only set when hadEnvironmentalFailure is true; used by the warning message.\n */\nexport interface QueryGenerationResult {\n schemas: QuerySchema[];\n syntaxErrors: QuerySyntaxError[];\n fatalErrors: QueryFatalError[];\n hadEnvironmentalFailure?: boolean;\n environmentalCause?: \"auth\" | \"unreachable\" | \"unavailable\";\n}\n"],"mappings":";;;;;AAiDA,MAAa,kBAA0C;CAErD,QAAQ;CACR,QAAQ;CAER,SAAS;CAET,SAAS;CACT,KAAK;CACL,QAAQ;CACR,SAAS;CACT,UAAU;CACV,OAAO;CACP,QAAQ;CACR,SAAS;CAET,MAAM;CACN,WAAW;CACX,eAAe;CAChB;;;;;AAMD,MAAa,kBAA0C;CAErD,QAAQ;CACR,QAAQ;CAER,SAAS;CAIT,SAAS;CACT,SAAS;CACT,QAAQ;CACR,KAAK;CACL,SAAS;CACT,UAAU;CACV,OAAO;CACP,QAAQ;CAER,MAAM;CACN,WAAW;CACX,eAAe;CAChB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite-plugin.d.ts","names":[],"sources":["../../src/type-generator/vite-plugin.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"vite-plugin.d.ts","names":[],"sources":["../../src/type-generator/vite-plugin.ts"],"mappings":";;;;;AAEmC;UA+BzB,wBAAA;EAER,OAAA;EAFgC;;;;EAOhC,SAAA;EAMY;AASd;;;;EATE,YAAA;AAAA;;;;;;;iBASc,iBAAA,CAAkB,OAAA,GAAU,wBAAA,GAA2B,MAAA"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createLogger } from "../logging/logger.js";
|
|
2
|
+
import { createWorkspaceClient } from "../workspace-client/factory.js";
|
|
3
|
+
import "../workspace-client/index.js";
|
|
2
4
|
import { METRIC_CONFIG_FILE } from "../shared/src/schemas/metric-fqn.js";
|
|
3
5
|
import { getWarehouseState, startWarehouse, waitUntilRunning } from "./warehouse-status.js";
|
|
4
6
|
import { ANALYTICS_TYPES_FILE, TYPES_DIR, TypegenFatalError, TypegenSyntaxError, generateFromEntryPoint } from "./index.js";
|
|
5
|
-
import { WorkspaceClient } from "@databricks/sdk-experimental";
|
|
6
7
|
import path from "node:path";
|
|
7
8
|
import { existsSync } from "node:fs";
|
|
8
9
|
|
|
@@ -142,7 +143,7 @@ function appKitTypesPlugin(options) {
|
|
|
142
143
|
const { signal } = controller;
|
|
143
144
|
(async () => {
|
|
144
145
|
try {
|
|
145
|
-
const client =
|
|
146
|
+
const client = createWorkspaceClient();
|
|
146
147
|
const state = await getWarehouseState(client, warehouseId);
|
|
147
148
|
if (state === "DELETED" || state === "DELETING") return;
|
|
148
149
|
let startedByUs = false;
|
|
@@ -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
|
|