@databricks/appkit 0.53.1 → 0.55.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 +9 -1
- package/dist/appkit/package.js +1 -1
- package/dist/beta.d.ts +3 -1
- package/dist/beta.js +2 -1
- package/dist/cli/commands/generate-types.js +1 -1
- package/dist/cli/commands/generate-types.js.map +1 -1
- package/dist/cli/commands/plugin/create/create.js +12 -14
- package/dist/cli/commands/plugin/create/create.js.map +1 -1
- package/dist/cli/commands/plugin/promote/promote.js +2 -12
- package/dist/cli/commands/plugin/promote/promote.js.map +1 -1
- package/dist/connectors/ai-search/client.js +145 -0
- package/dist/connectors/ai-search/client.js.map +1 -0
- package/dist/connectors/ai-search/index.js +3 -0
- package/dist/connectors/context.js +38 -0
- package/dist/connectors/context.js.map +1 -0
- package/dist/connectors/index.js +2 -1
- package/dist/connectors/serving/client.d.ts.map +1 -1
- package/dist/connectors/serving/client.js +2 -26
- package/dist/connectors/serving/client.js.map +1 -1
- package/dist/core/appkit.d.ts.map +1 -1
- package/dist/core/appkit.js.map +1 -1
- package/dist/naming.js +29 -0
- package/dist/plugin/plugin.d.ts.map +1 -1
- package/dist/plugin/plugin.js +2 -1
- package/dist/plugin/plugin.js.map +1 -1
- package/dist/plugins/ai-search/ai-search.d.ts +63 -0
- package/dist/plugins/ai-search/ai-search.d.ts.map +1 -0
- package/dist/plugins/ai-search/ai-search.js +296 -0
- package/dist/plugins/ai-search/ai-search.js.map +1 -0
- package/dist/plugins/ai-search/defaults.js +14 -0
- package/dist/plugins/ai-search/defaults.js.map +1 -0
- package/dist/plugins/ai-search/index.d.ts +2 -0
- package/dist/plugins/ai-search/index.js +3 -0
- package/dist/plugins/ai-search/manifest.js +73 -0
- package/dist/plugins/ai-search/manifest.js.map +1 -0
- package/dist/plugins/ai-search/types.d.ts +80 -0
- package/dist/plugins/ai-search/types.d.ts.map +1 -0
- package/dist/plugins/analytics/analytics.d.ts.map +1 -1
- package/dist/plugins/analytics/analytics.js +7 -1
- package/dist/plugins/analytics/analytics.js.map +1 -1
- package/dist/plugins/analytics/metric.js +1 -0
- package/dist/plugins/analytics/mv/index.js +1 -0
- package/dist/plugins/analytics/mv/metadata.js +30 -0
- package/dist/plugins/analytics/mv/metadata.js.map +1 -0
- package/dist/plugins/analytics/types.d.ts +7 -0
- package/dist/plugins/analytics/types.d.ts.map +1 -1
- package/dist/plugins/analytics/types.js.map +1 -1
- package/dist/plugins/beta-exports.generated.d.ts +3 -1
- package/dist/plugins/beta-exports.generated.js +2 -0
- package/dist/plugins/server/index.d.ts.map +1 -1
- package/dist/plugins/server/index.js +2 -1
- package/dist/plugins/server/index.js.map +1 -1
- package/dist/plugins/ui-variants/index.js.map +1 -1
- package/dist/plugins/ui-variants/manifest.js +1 -1
- package/dist/registry/resource-registry.d.ts.map +1 -1
- package/dist/registry/resource-registry.js +2 -7
- package/dist/registry/resource-registry.js.map +1 -1
- package/dist/schemas/manifest.d.ts.map +1 -1
- package/dist/schemas/manifest.js +3 -2
- package/dist/schemas/manifest.js.map +1 -1
- package/dist/shared/src/index.d.ts +1 -0
- package/dist/shared/src/metric-metadata.d.ts +24 -0
- package/dist/shared/src/metric-metadata.d.ts.map +1 -0
- package/dist/shared/src/naming.js +13 -0
- package/dist/shared/src/naming.js.map +1 -0
- package/dist/shared/src/plugin.d.ts +6 -4
- package/dist/shared/src/plugin.d.ts.map +1 -1
- package/dist/shared/src/schemas/manifest.d.ts +2 -2
- package/dist/shared/src/schemas/manifest.d.ts.map +1 -1
- package/dist/shared/src/sse/analytics.js +2 -1
- package/dist/shared/src/sse/analytics.js.map +1 -1
- package/dist/type-generator/errors.js +0 -3
- package/dist/type-generator/errors.js.map +1 -1
- package/dist/type-generator/index.js +15 -10
- package/dist/type-generator/index.js.map +1 -1
- package/dist/type-generator/mv-registry/render-types.js +49 -11
- package/dist/type-generator/mv-registry/render-types.js.map +1 -1
- package/dist/type-generator/query-registry.js +1 -3
- package/dist/type-generator/query-registry.js.map +1 -1
- package/dist/type-generator/vite-plugin.d.ts +4 -2
- package/dist/type-generator/vite-plugin.d.ts.map +1 -1
- package/dist/type-generator/vite-plugin.js +1 -0
- package/dist/type-generator/vite-plugin.js.map +1 -1
- package/dist/utils/banner.js +19 -0
- package/dist/utils/banner.js.map +1 -0
- package/docs/api/appkit/Interface.BasePluginConfig.md +1 -0
- package/docs/api/appkit/Interface.IAiSearchConfig.md +71 -0
- package/docs/api/appkit/Interface.IndexConfig.md +110 -0
- package/docs/api/appkit/Interface.RerankerConfig.md +10 -0
- package/docs/api/appkit/Interface.SearchRequest.md +64 -0
- package/docs/api/appkit/Interface.SearchResponse.md +52 -0
- package/docs/api/appkit/Interface.SearchResult.md +25 -0
- package/docs/api/appkit/TypeAlias.SearchFilters.md +6 -0
- package/docs/api/appkit/Variable.aiSearch.md +6 -0
- package/docs/api/appkit.md +8 -0
- package/docs/development/type-generation.md +7 -7
- package/docs/plugins/{vector-search.md → ai-search.md} +70 -27
- package/llms.txt +9 -1
- package/package.json +1 -1
- package/sbom.cdx.json +1 -1
- package/dist/connectors/vector-search/client.js +0 -9
- package/dist/connectors/vector-search/client.js.map +0 -1
- package/dist/connectors/vector-search/index.js +0 -3
|
@@ -162,9 +162,9 @@ declare const pluginManifestSchema: z.ZodObject<{
|
|
|
162
162
|
type: z.ZodLiteral<"sql_warehouse">;
|
|
163
163
|
}, z.core.$strict>, z.ZodObject<{
|
|
164
164
|
permission: z.ZodEnum<{
|
|
165
|
-
CAN_QUERY: "CAN_QUERY";
|
|
166
165
|
CAN_VIEW: "CAN_VIEW";
|
|
167
166
|
CAN_MANAGE: "CAN_MANAGE";
|
|
167
|
+
CAN_QUERY: "CAN_QUERY";
|
|
168
168
|
}>;
|
|
169
169
|
alias: z.ZodString;
|
|
170
170
|
resourceKey: z.ZodString;
|
|
@@ -683,9 +683,9 @@ declare const pluginManifestSchema: z.ZodObject<{
|
|
|
683
683
|
type: z.ZodLiteral<"sql_warehouse">;
|
|
684
684
|
}, z.core.$strict>, z.ZodObject<{
|
|
685
685
|
permission: z.ZodEnum<{
|
|
686
|
-
CAN_QUERY: "CAN_QUERY";
|
|
687
686
|
CAN_VIEW: "CAN_VIEW";
|
|
688
687
|
CAN_MANAGE: "CAN_MANAGE";
|
|
688
|
+
CAN_QUERY: "CAN_QUERY";
|
|
689
689
|
}>;
|
|
690
690
|
alias: z.ZodString;
|
|
691
691
|
resourceKey: z.ZodString;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","names":[],"sources":["../../../../../shared/src/schemas/manifest.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","names":[],"sources":["../../../../../shared/src/schemas/manifest.ts"],"mappings":";;;cAqVa,wBAAA,EAAwB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAsUxB,oBAAA,EAAoB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAsgBrB,kBAAA,GAAqB,CAAA,CAAE,KAAA,QAAa,wBAAA;AAAA,KAOpC,cAAA,GAAiB,CAAA,CAAE,KAAA,QAAa,oBAAA"}
|
|
@@ -25,7 +25,8 @@ const AnalyticsResultMessage = z.object({
|
|
|
25
25
|
type: z.literal("result"),
|
|
26
26
|
data: z.array(z.unknown()).optional(),
|
|
27
27
|
status: z.unknown().optional(),
|
|
28
|
-
statement_id: z.string().optional()
|
|
28
|
+
statement_id: z.string().optional(),
|
|
29
|
+
metadata: z.record(z.string(), z.unknown()).optional()
|
|
29
30
|
});
|
|
30
31
|
function makeResultMessage(data, extras = {}) {
|
|
31
32
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analytics.js","names":[],"sources":["../../../../../shared/src/sse/analytics.ts"],"sourcesContent":["import { z } from \"zod\";\n\n/**\n * Wire protocol for analytics SSE messages emitted by `/api/analytics/query`.\n *\n * The SSE channel carries only the JSON_ARRAY path (warehouse-readiness\n * events + a `result` message of rows). ARROW_STREAM does NOT use SSE —\n * the server streams the raw Arrow IPC bytes back on the query response body\n * (`_handleArrowStreamQuery`) and the client reads them directly\n * (`fetchArrowDirect`), so there is no `arrow` message type here.\n *\n * These schemas are the single source of truth for the JSON contract between\n * the server (`AnalyticsPlugin._handleQueryRoute`) and the client\n * (`useAnalyticsQuery`). Both sides validate with the same schema:\n *\n * - Server uses the typed builder (`makeResultMessage`) to construct messages\n * with compile-time guarantees that all required fields are present.\n * - Client calls `AnalyticsSseMessage.safeParse(JSON.parse(event.data))` to\n * fail loudly on a malformed payload instead of silently treating an\n * undefined field as data.\n */\n\n/** Successful row-shaped result (JSON_ARRAY format, or empty results). */\nexport const AnalyticsResultMessage = z.object({\n type: z.literal(\"result\"),\n // `data` is intentionally `z.array(z.unknown())` rather than a deep\n // row schema. Validating every row's keys for shape costs O(rows × cols)\n // CPU and main-thread blocking time on the *client* (the schema is\n // also reused for `safeParse` in `useAnalyticsQuery`); for large JSON\n // results that pushes hundreds of ms to seconds of TBT into the\n // render pipeline. The server constructs `data` via the typed\n // `makeResultMessage` builder, so the per-row shape is enforced at\n // the source, not at validation time. The TS-level interface below\n // narrows `data` to `Record<string, unknown>[]` for callers.\n data: z.array(z.unknown()).optional(),\n // Status is opaque metadata forwarded from the warehouse — keep it as\n // `unknown` so we don't bake the SDK's detailed shape into the contract.\n status: z.unknown().optional(),\n statement_id: z.string().optional(),\n});\n\n/**\n * TS-level shape of a successful row-shaped result message.\n *\n * **Kept in sync by hand** with `AnalyticsResultMessage` above. The Zod\n * schema is intentionally loose (`z.array(z.unknown())`) to keep client\n * validation cheap; this interface narrows `data` to\n * `Record<string, unknown>[]` so consumers don't have to cast at every
|
|
1
|
+
{"version":3,"file":"analytics.js","names":[],"sources":["../../../../../shared/src/sse/analytics.ts"],"sourcesContent":["import { z } from \"zod\";\nimport type { MetricViewColumnDisplay } from \"../metric-metadata\";\n\n/**\n * Wire protocol for analytics SSE messages emitted by `/api/analytics/query`.\n *\n * The SSE channel carries only the JSON_ARRAY path (warehouse-readiness\n * events + a `result` message of rows). ARROW_STREAM does NOT use SSE —\n * the server streams the raw Arrow IPC bytes back on the query response body\n * (`_handleArrowStreamQuery`) and the client reads them directly\n * (`fetchArrowDirect`), so there is no `arrow` message type here.\n *\n * These schemas are the single source of truth for the JSON contract between\n * the server (`AnalyticsPlugin._handleQueryRoute`) and the client\n * (`useAnalyticsQuery`). Both sides validate with the same schema:\n *\n * - Server uses the typed builder (`makeResultMessage`) to construct messages\n * with compile-time guarantees that all required fields are present.\n * - Client calls `AnalyticsSseMessage.safeParse(JSON.parse(event.data))` to\n * fail loudly on a malformed payload instead of silently treating an\n * undefined field as data.\n */\n\n/** Successful row-shaped result (JSON_ARRAY format, or empty results). */\nexport const AnalyticsResultMessage = z.object({\n type: z.literal(\"result\"),\n // `data` is intentionally `z.array(z.unknown())` rather than a deep\n // row schema. Validating every row's keys for shape costs O(rows × cols)\n // CPU and main-thread blocking time on the *client* (the schema is\n // also reused for `safeParse` in `useAnalyticsQuery`); for large JSON\n // results that pushes hundreds of ms to seconds of TBT into the\n // render pipeline. The server constructs `data` via the typed\n // `makeResultMessage` builder, so the per-row shape is enforced at\n // the source, not at validation time. The TS-level interface below\n // narrows `data` to `Record<string, unknown>[]` for callers.\n data: z.array(z.unknown()).optional(),\n // Status is opaque metadata forwarded from the warehouse — keep it as\n // `unknown` so we don't bake the SDK's detailed shape into the contract.\n status: z.unknown().optional(),\n statement_id: z.string().optional(),\n // Per-column display metadata for a metric-view result; absent for plain\n // `/query` results. Kept loose for the same reason as `data` above.\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\n/**\n * TS-level shape of a successful row-shaped result message.\n *\n * **Kept in sync by hand** with `AnalyticsResultMessage` above. The Zod\n * schema is intentionally loose (`z.array(z.unknown())` for `data`,\n * `z.record(z.string(), z.unknown())` for `metadata`) to keep client\n * validation cheap; this interface narrows `data` to\n * `Record<string, unknown>[]` and `metadata` to\n * `Record<string, MetricViewColumnDisplay>` so consumers don't have to cast\n * at every call site. If you add a field to the Zod schema, add it here too.\n */\nexport interface AnalyticsResultMessage {\n type: \"result\";\n data?: Record<string, unknown>[];\n status?: unknown;\n statement_id?: string;\n metadata?: Record<string, MetricViewColumnDisplay>;\n}\n\n/**\n * Every message the analytics SSE stream may emit. Currently only the\n * row-shaped `result` message (JSON_ARRAY path); `warehouse_status` and\n * `error` events are handled off-schema by the client. ARROW_STREAM never\n * uses SSE.\n */\nexport const AnalyticsSseMessage = AnalyticsResultMessage;\nexport type AnalyticsSseMessage = z.infer<typeof AnalyticsSseMessage>;\n\n// ────────────────────────────────────────────────────────────────────────────\n// Typed builder — call from the server route handler. The compiler enforces\n// that every required field is supplied, and the return type narrows so\n// downstream code (executeStream / SSE writer) keeps full type information.\n// ────────────────────────────────────────────────────────────────────────────\n\nexport function makeResultMessage(\n data: Record<string, unknown>[] | undefined,\n extras: {\n status?: unknown;\n statement_id?: string;\n metadata?: Record<string, MetricViewColumnDisplay>;\n } = {},\n): AnalyticsResultMessage {\n return { type: \"result\", data, ...extras };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAa,yBAAyB,EAAE,OAAO;CAC7C,MAAM,EAAE,QAAQ,SAAS;CAUzB,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,UAAU;CAGrC,QAAQ,EAAE,SAAS,CAAC,UAAU;CAC9B,cAAc,EAAE,QAAQ,CAAC,UAAU;CAGnC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,CAAC,CAAC,UAAU;CACvD,CAAC;AAoCF,SAAgB,kBACd,MACA,SAII,EAAE,EACkB;AACxB,QAAO;EAAE,MAAM;EAAU;EAAM,GAAG;EAAQ"}
|
|
@@ -122,9 +122,6 @@ function classifyBlockingFailure(error) {
|
|
|
122
122
|
if (status === 404 || status === 400) return "deterministic";
|
|
123
123
|
stack.push(...getErrorChildren(current));
|
|
124
124
|
}
|
|
125
|
-
const topLevelStatus = getErrorStatus(error);
|
|
126
|
-
if (topLevelStatus !== void 0 && AUTH_ERROR_STATUSES.has(topLevelStatus)) return "environmental";
|
|
127
|
-
if (isConnectivityError(error)) return "environmental";
|
|
128
125
|
return "environmental";
|
|
129
126
|
}
|
|
130
127
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":[],"sources":["../../src/type-generator/errors.ts"],"sourcesContent":["/**\n * Shared error-introspection utilities for the type generator.\n *\n * Both describe paths — the query registry and the metric-view registry — must\n * classify a thrown failure the same way: a transient connectivity blip (which\n * self-converges and should never fail a build) versus a deterministic failure\n * (auth, bad warehouse id, truncated result, malformed request) that must be\n * surfaced. Keeping `isConnectivityError` (and the message/diagnostic helpers it\n * shares) in one module is the single source of truth for that decision, so the\n * two paths can never drift apart.\n */\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nexport function getErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n if (isObject(error) && typeof error.message === \"string\") {\n return error.message;\n }\n return String(error);\n}\n\nexport function getErrorDiagnostic(error: unknown): string {\n const seen = new Set<unknown>();\n const messages: string[] = [];\n const stack = [error];\n\n while (stack.length > 0) {\n const current = stack.pop();\n if (current === undefined || seen.has(current)) continue;\n seen.add(current);\n\n const message = getErrorMessage(current);\n if (\n message &&\n message !== \"[object Object]\" &&\n !messages.includes(message)\n ) {\n messages.push(message);\n }\n\n const code = getErrorCode(current);\n if (code && !messages.includes(code)) messages.push(code);\n\n stack.push(...getErrorChildren(current));\n }\n\n return messages.length > 0 ? messages.join(\": \") : getErrorMessage(error);\n}\n\nfunction getErrorCode(error: unknown): string | undefined {\n if (!isObject(error)) return undefined;\n const code = error.code ?? error.errno;\n return typeof code === \"string\" ? code : undefined;\n}\n\nfunction getErrorStatus(error: unknown): number | undefined {\n if (!isObject(error)) return undefined;\n const direct = error.status ?? error.statusCode;\n if (typeof direct === \"number\") return direct;\n if (isObject(error.response) && typeof error.response.status === \"number\") {\n return error.response.status;\n }\n return undefined;\n}\n\nfunction getErrorChildren(error: unknown): unknown[] {\n if (!isObject(error)) return [];\n const children: unknown[] = [];\n if (\"cause\" in error) children.push(error.cause);\n if (error instanceof AggregateError) children.push(...error.errors);\n return children;\n}\n\nconst CONNECTIVITY_ERROR_CODES = new Set([\n \"ECONNREFUSED\",\n \"ECONNRESET\",\n \"ENOTFOUND\",\n \"ETIMEDOUT\",\n \"EAI_AGAIN\",\n \"EAI_NODATA\",\n \"EAI_NONAME\",\n \"EHOSTUNREACH\",\n \"ENETUNREACH\",\n \"CERT_HAS_EXPIRED\",\n \"DEPTH_ZERO_SELF_SIGNED_CERT\",\n \"ERR_TLS_CERT_ALTNAME_INVALID\",\n \"SELF_SIGNED_CERT_IN_CHAIN\",\n \"UNABLE_TO_VERIFY_LEAF_SIGNATURE\",\n]);\n\nfunction isConnectivityMessage(message: string): boolean {\n return (\n /\\bconnection (?:refused|reset|timed out)\\b/i.test(message) ||\n /\\bsocket hang up\\b/i.test(message) ||\n /\\bnetwork error\\b/i.test(message) ||\n /\\bcan'?t connect to\\b/i.test(message) ||\n /\\bcertificate has expired\\b/i.test(message) ||\n /\\bunable to verify the first certificate\\b/i.test(message) ||\n /\\bupstream connect error or disconnect\\/reset before headers\\b/i.test(\n message,\n )\n );\n}\n\n/**\n * True when a thrown failure is a transport/connectivity problem (DNS, refused\n * connection, reset, TLS, 502/503/504, undici `UND_ERR_*`) rather than a\n * deterministic error the warehouse returned. Walks `cause`/`AggregateError`\n * chains so a wrapped connectivity error is still recognized. Callers degrade\n * (and retry) on `true`; everything else is surfaced as a build failure.\n */\nexport function isConnectivityError(error: unknown): boolean {\n const seen = new Set<unknown>();\n const stack = [error];\n\n while (stack.length > 0) {\n const current = stack.pop();\n if (current === undefined || seen.has(current)) continue;\n seen.add(current);\n\n const code = getErrorCode(current);\n if (\n code &&\n (CONNECTIVITY_ERROR_CODES.has(code) || code.startsWith(\"UND_ERR_\"))\n ) {\n return true;\n }\n\n const status = getErrorStatus(current);\n if (status === 502 || status === 503 || status === 504) return true;\n\n if (isConnectivityMessage(getErrorMessage(current))) return true;\n\n stack.push(...getErrorChildren(current));\n }\n\n return false;\n}\n\nconst AUTH_ERROR_STATUSES = new Set([401, 403]);\n\n/**\n * Classifies a thrown failure into one of two buckets: deterministic failures\n * that must be surfaced (bad warehouse id, malformed request) or environmental\n * issues (connectivity, auth, deleted warehouse, timeouts) that the has-types\n * gate will handle later.\n *\n * Returns:\n * - \"deterministic\": HTTP 404 (bad warehouse id) or 400 (malformed request).\n * The build must fail.\n * - \"environmental\": Everything else — auth (401/403), connectivity errors,\n * warehouse state changes (DELETED/DELETING), wait-for-RUNNING timeouts,\n * unrecognized failures. Default = environmental.\n *\n * Walks `cause`/`AggregateError` chains when checking for deterministic status,\n * so a wrapped 404 is still recognized as deterministic.\n */\nexport function classifyBlockingFailure(\n error: unknown,\n): \"deterministic\" | \"environmental\" {\n // Deterministic: check first so they're never swallowed by environmental rules.\n // Walk the error chain to find any deterministic status.\n const seen = new Set<unknown>();\n const stack = [error];\n\n while (stack.length > 0) {\n const current = stack.pop();\n if (current === undefined || seen.has(current)) continue;\n seen.add(current);\n\n const status = getErrorStatus(current);\n if (status === 404 || status === 400) {\n return \"deterministic\";\n }\n\n stack.push(...getErrorChildren(current));\n }\n\n // Environmental: auth, connectivity, unrecognized, default.\n const topLevelStatus = getErrorStatus(error);\n if (topLevelStatus !== undefined && AUTH_ERROR_STATUSES.has(topLevelStatus)) {\n return \"environmental\";\n }\n\n if (isConnectivityError(error)) {\n return \"environmental\";\n }\n\n // Default: any unrecognized failure or no status (DELETED/DELETING messages,\n // timeout messages, plain Error objects) → environmental.\n return \"environmental\";\n}\n\n/**\n * Coarse cause label for an environmental failure, used by the `--wait`\n * committed-types warning so the log says *why* generation fell back.\n *\n * Returns:\n * - \"unreachable\": transport/connectivity failure (see {@link isConnectivityError}).\n * - \"auth\": HTTP 401/403, including a status carried on `response.status` or\n * wrapped in a `cause`/`AggregateError` chain.\n * - \"unavailable\": everything else (DELETED/DELETING, wait timeouts, degraded\n * DESCRIBEs).\n */\nexport function classifyEnvironmentalCause(\n error: unknown,\n): \"auth\" | \"unreachable\" | \"unavailable\" {\n if (isConnectivityError(error)) return \"unreachable\";\n\n // Walk the error chain so a wrapped 401/403 is still labeled as auth.\n const seen = new Set<unknown>();\n const stack = [error];\n\n while (stack.length > 0) {\n const current = stack.pop();\n if (current === undefined || seen.has(current)) continue;\n seen.add(current);\n\n const status = getErrorStatus(current);\n if (status !== undefined && AUTH_ERROR_STATUSES.has(status)) return \"auth\";\n\n stack.push(...getErrorChildren(current));\n }\n\n return \"unavailable\";\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../../src/type-generator/errors.ts"],"sourcesContent":["/**\n * Shared error-introspection utilities for the type generator.\n *\n * Both describe paths — the query registry and the metric-view registry — must\n * classify a thrown failure the same way: a transient connectivity blip (which\n * self-converges and should never fail a build) versus a deterministic failure\n * (auth, bad warehouse id, truncated result, malformed request) that must be\n * surfaced. Keeping `isConnectivityError` (and the message/diagnostic helpers it\n * shares) in one module is the single source of truth for that decision, so the\n * two paths can never drift apart.\n */\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nexport function getErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n if (isObject(error) && typeof error.message === \"string\") {\n return error.message;\n }\n return String(error);\n}\n\nexport function getErrorDiagnostic(error: unknown): string {\n const seen = new Set<unknown>();\n const messages: string[] = [];\n const stack = [error];\n\n while (stack.length > 0) {\n const current = stack.pop();\n if (current === undefined || seen.has(current)) continue;\n seen.add(current);\n\n const message = getErrorMessage(current);\n if (\n message &&\n message !== \"[object Object]\" &&\n !messages.includes(message)\n ) {\n messages.push(message);\n }\n\n const code = getErrorCode(current);\n if (code && !messages.includes(code)) messages.push(code);\n\n stack.push(...getErrorChildren(current));\n }\n\n return messages.length > 0 ? messages.join(\": \") : getErrorMessage(error);\n}\n\nfunction getErrorCode(error: unknown): string | undefined {\n if (!isObject(error)) return undefined;\n const code = error.code ?? error.errno;\n return typeof code === \"string\" ? code : undefined;\n}\n\nfunction getErrorStatus(error: unknown): number | undefined {\n if (!isObject(error)) return undefined;\n const direct = error.status ?? error.statusCode;\n if (typeof direct === \"number\") return direct;\n if (isObject(error.response) && typeof error.response.status === \"number\") {\n return error.response.status;\n }\n return undefined;\n}\n\nfunction getErrorChildren(error: unknown): unknown[] {\n if (!isObject(error)) return [];\n const children: unknown[] = [];\n if (\"cause\" in error) children.push(error.cause);\n if (error instanceof AggregateError) children.push(...error.errors);\n return children;\n}\n\nconst CONNECTIVITY_ERROR_CODES = new Set([\n \"ECONNREFUSED\",\n \"ECONNRESET\",\n \"ENOTFOUND\",\n \"ETIMEDOUT\",\n \"EAI_AGAIN\",\n \"EAI_NODATA\",\n \"EAI_NONAME\",\n \"EHOSTUNREACH\",\n \"ENETUNREACH\",\n \"CERT_HAS_EXPIRED\",\n \"DEPTH_ZERO_SELF_SIGNED_CERT\",\n \"ERR_TLS_CERT_ALTNAME_INVALID\",\n \"SELF_SIGNED_CERT_IN_CHAIN\",\n \"UNABLE_TO_VERIFY_LEAF_SIGNATURE\",\n]);\n\nfunction isConnectivityMessage(message: string): boolean {\n return (\n /\\bconnection (?:refused|reset|timed out)\\b/i.test(message) ||\n /\\bsocket hang up\\b/i.test(message) ||\n /\\bnetwork error\\b/i.test(message) ||\n /\\bcan'?t connect to\\b/i.test(message) ||\n /\\bcertificate has expired\\b/i.test(message) ||\n /\\bunable to verify the first certificate\\b/i.test(message) ||\n /\\bupstream connect error or disconnect\\/reset before headers\\b/i.test(\n message,\n )\n );\n}\n\n/**\n * True when a thrown failure is a transport/connectivity problem (DNS, refused\n * connection, reset, TLS, 502/503/504, undici `UND_ERR_*`) rather than a\n * deterministic error the warehouse returned. Walks `cause`/`AggregateError`\n * chains so a wrapped connectivity error is still recognized. Callers degrade\n * (and retry) on `true`; everything else is surfaced as a build failure.\n */\nexport function isConnectivityError(error: unknown): boolean {\n const seen = new Set<unknown>();\n const stack = [error];\n\n while (stack.length > 0) {\n const current = stack.pop();\n if (current === undefined || seen.has(current)) continue;\n seen.add(current);\n\n const code = getErrorCode(current);\n if (\n code &&\n (CONNECTIVITY_ERROR_CODES.has(code) || code.startsWith(\"UND_ERR_\"))\n ) {\n return true;\n }\n\n const status = getErrorStatus(current);\n if (status === 502 || status === 503 || status === 504) return true;\n\n if (isConnectivityMessage(getErrorMessage(current))) return true;\n\n stack.push(...getErrorChildren(current));\n }\n\n return false;\n}\n\nconst AUTH_ERROR_STATUSES = new Set([401, 403]);\n\n/**\n * Classifies a thrown failure into one of two buckets: deterministic failures\n * that must be surfaced (bad warehouse id, malformed request) or environmental\n * issues (connectivity, auth, deleted warehouse, timeouts) that the has-types\n * gate will handle later.\n *\n * Returns:\n * - \"deterministic\": HTTP 404 (bad warehouse id) or 400 (malformed request).\n * The build must fail.\n * - \"environmental\": Everything else — auth (401/403), connectivity errors,\n * warehouse state changes (DELETED/DELETING), wait-for-RUNNING timeouts,\n * unrecognized failures. Default = environmental.\n *\n * Walks `cause`/`AggregateError` chains when checking for deterministic status,\n * so a wrapped 404 is still recognized as deterministic.\n */\nexport function classifyBlockingFailure(\n error: unknown,\n): \"deterministic\" | \"environmental\" {\n const seen = new Set<unknown>();\n const stack = [error];\n\n while (stack.length > 0) {\n const current = stack.pop();\n if (current === undefined || seen.has(current)) continue;\n seen.add(current);\n\n const status = getErrorStatus(current);\n if (status === 404 || status === 400) {\n return \"deterministic\";\n }\n\n stack.push(...getErrorChildren(current));\n }\n\n return \"environmental\";\n}\n\n/**\n * Coarse cause label for an environmental failure, used by the `--wait`\n * committed-types warning so the log says *why* generation fell back.\n *\n * Returns:\n * - \"unreachable\": transport/connectivity failure (see {@link isConnectivityError}).\n * - \"auth\": HTTP 401/403, including a status carried on `response.status` or\n * wrapped in a `cause`/`AggregateError` chain.\n * - \"unavailable\": everything else (DELETED/DELETING, wait timeouts, degraded\n * DESCRIBEs).\n */\nexport function classifyEnvironmentalCause(\n error: unknown,\n): \"auth\" | \"unreachable\" | \"unavailable\" {\n if (isConnectivityError(error)) return \"unreachable\";\n\n // Walk the error chain so a wrapped 401/403 is still labeled as auth.\n const seen = new Set<unknown>();\n const stack = [error];\n\n while (stack.length > 0) {\n const current = stack.pop();\n if (current === undefined || seen.has(current)) continue;\n seen.add(current);\n\n const status = getErrorStatus(current);\n if (status !== undefined && AUTH_ERROR_STATUSES.has(status)) return \"auth\";\n\n stack.push(...getErrorChildren(current));\n }\n\n return \"unavailable\";\n}\n"],"mappings":"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"}
|
|
@@ -27,10 +27,6 @@ const logger = createLogger("type-generator");
|
|
|
27
27
|
* `PREFLIGHT_WAIT_MAX_MS` in query-registry.ts.
|
|
28
28
|
*/
|
|
29
29
|
const MV_PREFLIGHT_WAIT_MAX_MS = 3e5;
|
|
30
|
-
/**
|
|
31
|
-
* Generate a loud warning message for environmental failures with committed types present.
|
|
32
|
-
* @param cause - coarse cause label: "auth" (401/403), "unreachable" (connectivity), or "unavailable" (other)
|
|
33
|
-
*/
|
|
34
30
|
function determineWarningMessage(cause, warehouseId) {
|
|
35
31
|
return `AppKit typegen: using committed types — warehouse ${warehouseId} ${cause === "auth" ? "auth blocked" : cause === "unreachable" ? "warehouse unreachable" : "warehouse unavailable"}; please check warehouse status and retry`;
|
|
36
32
|
}
|
|
@@ -201,7 +197,7 @@ async function probeWarehouseState(getClient, warehouseId) {
|
|
|
201
197
|
* `metric-views` directory of `queryFolder` (so query-only callers keep
|
|
202
198
|
* working); when neither is given, the metric path is skipped.
|
|
203
199
|
* @param options.mvOutFile - optional output file for the MetricRegistry
|
|
204
|
-
* augmentation. Defaults to a sibling `metric-views.
|
|
200
|
+
* augmentation. Defaults to a sibling `metric-views.ts` file under the same
|
|
205
201
|
* directory as `outFile`. Skipped entirely if `definitions.json` is absent.
|
|
206
202
|
* @param options.metricFetcher - optional DescribeFetcher used by
|
|
207
203
|
* {@link syncMetrics} (tests inject a mock; production lazily builds a
|
|
@@ -284,7 +280,7 @@ async function generateFromEntryPoint(options) {
|
|
|
284
280
|
logger.warn(warningMessage);
|
|
285
281
|
} else throw new TypegenFatalError([{
|
|
286
282
|
name: "type-generator",
|
|
287
|
-
message: `Warehouse ${warehouseId} could not provide schemas and the required committed type ${plural(missingCommittedTypes.length, "artifact is", "artifacts are")} missing: ${missingCommittedTypes.join(", ")}. Run 'npx @databricks/appkit generate-types --wait' locally and commit the generated
|
|
283
|
+
message: `Warehouse ${warehouseId} could not provide schemas and the required committed type ${plural(missingCommittedTypes.length, "artifact is", "artifacts are")} missing: ${missingCommittedTypes.join(", ")}. Run 'npx @databricks/appkit generate-types --wait' locally and commit the generated type files.`
|
|
288
284
|
}], warehouseId);
|
|
289
285
|
}
|
|
290
286
|
logger.debug("Type generation complete!");
|
|
@@ -298,14 +294,16 @@ async function generateFromEntryPoint(options) {
|
|
|
298
294
|
*
|
|
299
295
|
* @param options.metricViewsFolder - folder that holds `definitions.json` (`<root>/config/metric-views`).
|
|
300
296
|
* @param options.warehouseId - SQL warehouse used for `DESCRIBE TABLE EXTENDED`.
|
|
301
|
-
* @param options.metricOutFile - output path for the MetricRegistry `.
|
|
297
|
+
* @param options.metricOutFile - output path for the MetricRegistry `.ts` (the
|
|
298
|
+
* generated source carries both the `declare module` augmentation and the
|
|
299
|
+
* runtime `metricViewsMetadata` const).
|
|
302
300
|
* @param options.cache - cache toggle, default ON. Only `cache === false` disables it (so `undefined`/`true` keep caching).
|
|
303
301
|
* @param options.metricFetcher - optional injected {@link DescribeFetcher}
|
|
304
302
|
* @param options.mode - preflight/gate policy, default `"describe-now"`. When set to `"blocking"`,
|
|
305
|
-
* metric-view
|
|
303
|
+
* metric-view `.ts` writes are suppressed if any metric is degraded (to preserve committed files).
|
|
306
304
|
* @param options.suppressDegradedWrite - when true (only in `mode === "blocking"` context), skip
|
|
307
305
|
* the metricOutFile write if any metric schema has `degraded === true`. Used to prevent
|
|
308
|
-
* overwriting committed
|
|
306
|
+
* overwriting committed type files with degraded types in blocking mode.
|
|
309
307
|
*/
|
|
310
308
|
async function syncMetricViewsTypes(options) {
|
|
311
309
|
const { metricViewsFolder, warehouseId, metricOutFile, cache: cacheEnabled, metricFetcher, mode = "describe-now", suppressDegradedWrite } = options;
|
|
@@ -443,6 +441,13 @@ async function syncMetricViewsTypes(options) {
|
|
|
443
441
|
await fs.mkdir(path.dirname(metricOutFile), { recursive: true });
|
|
444
442
|
await fs.writeFile(metricOutFile, generateMetricTypeDeclarations(schemas), "utf-8");
|
|
445
443
|
}
|
|
444
|
+
if (metricOutFile.endsWith(".ts") && !metricOutFile.endsWith(".d.ts") && existsSync(metricOutFile)) {
|
|
445
|
+
const staleDts = `${metricOutFile.slice(0, -3)}.d.ts`;
|
|
446
|
+
try {
|
|
447
|
+
await fs.unlink(staleDts);
|
|
448
|
+
logger.debug("Removed stale generated types at %s", staleDts);
|
|
449
|
+
} catch {}
|
|
450
|
+
}
|
|
446
451
|
logger.debug("Wrote MetricRegistry augmentation for %d metric(s)%s", schemas.length, failures.length > 0 ? ` (${failures.length} failure(s))` : "");
|
|
447
452
|
return {
|
|
448
453
|
metricOutFile,
|
|
@@ -458,7 +463,7 @@ const generateServingTypes = generateServingTypes$1;
|
|
|
458
463
|
const TYPES_DIR = "appkit-types";
|
|
459
464
|
const ANALYTICS_TYPES_FILE = "analytics.d.ts";
|
|
460
465
|
const SERVING_TYPES_FILE = "serving.d.ts";
|
|
461
|
-
const METRIC_TYPES_FILE = "metric-views.
|
|
466
|
+
const METRIC_TYPES_FILE = "metric-views.ts";
|
|
462
467
|
|
|
463
468
|
//#endregion
|
|
464
469
|
export { ANALYTICS_TYPES_FILE, SERVING_TYPES_FILE, TYPES_DIR, TypegenFatalError, TypegenSyntaxError, generateFromEntryPoint, generateServingTypes };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["generateServingTypesImpl"],"sources":["../../src/type-generator/index.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport dotenv from \"dotenv\";\nimport pc from \"picocolors\";\nimport { createLogger } from \"../logging/logger\";\nimport {\n createWorkspaceClient,\n type WorkspaceClient,\n} from \"../workspace-client\";\nimport {\n isRevivableMetricCacheEntry,\n loadCache,\n type MetricCacheEntry,\n metricCacheHash,\n saveCache,\n} from \"./cache\";\nimport {\n classifyBlockingFailure,\n classifyEnvironmentalCause,\n getErrorDiagnostic,\n isConnectivityError,\n} from \"./errors\";\nimport {\n migrateProjectConfig,\n removeOldGeneratedTypes,\n resolveProjectRoot,\n} from \"./migration\";\nimport { readMetricConfig, resolveMetricConfig } from \"./mv-registry/config\";\nimport { createWorkspaceDescribeFetcher } from \"./mv-registry/describe\";\nimport { generateMetricTypeDeclarations } from \"./mv-registry/render-types\";\nimport { emptyMetricSchema, syncMetrics } from \"./mv-registry/sync\";\nimport type {\n DescribeFetcher,\n MetricColumnMetadata,\n MetricLane,\n MetricSchema,\n MetricSyncFailure,\n MetricSyncResult,\n} from \"./mv-registry/types\";\nimport { decidePreflight, type PreflightMode } from \"./preflight\";\nimport { generateQueriesFromDescribe } from \"./query-registry\";\nimport { generateServingTypes as generateServingTypesImpl } from \"./serving/generator\";\nimport type { QueryFatalError, QuerySchema, QuerySyntaxError } from \"./types\";\nimport {\n getWarehouseState,\n startWarehouse,\n type WarehouseState,\n waitUntilRunning,\n} from \"./warehouse-status\";\n\ndotenv.config();\n\nconst logger = createLogger(\"type-generator\");\n\n/**\n * Upper bound (~5 min) on how long the Metric Views path's `blocking`-mode preflight\n * waits for a warehouse to reach RUNNING. Mirrors the query path's (unexported)\n * `PREFLIGHT_WAIT_MAX_MS` in query-registry.ts.\n */\nconst MV_PREFLIGHT_WAIT_MAX_MS = 300_000;\n\n/**\n * Generate a loud warning message for environmental failures with committed types present.\n * @param cause - coarse cause label: \"auth\" (401/403), \"unreachable\" (connectivity), or \"unavailable\" (other)\n */\nfunction determineWarningMessage(\n cause: \"auth\" | \"unreachable\" | \"unavailable\",\n warehouseId: string,\n): string {\n const causeLabel =\n cause === \"auth\"\n ? \"auth blocked\"\n : cause === \"unreachable\"\n ? \"warehouse unreachable\"\n : \"warehouse unavailable\";\n // Use a stable prefix for greppability and CI log matching.\n return `AppKit typegen: using committed types — warehouse ${warehouseId} ${causeLabel}; please check warehouse status and retry`;\n}\n\ntype TypegenFailure = QuerySyntaxError | QueryFatalError;\n\nfunction plural(count: number, singular: string, pluralForm = `${singular}s`) {\n return count === 1 ? singular : pluralForm;\n}\n\nfunction isQueryDegraded(schema: QuerySchema): boolean {\n return schema.degraded === true;\n}\n\nfunction hasAnyDegradedMetrics(schemas: MetricSchema[]): boolean {\n return schemas.some((s) => s.degraded === true);\n}\n\nfunction formatFailureRows(\n label: string,\n queries: TypegenFailure[],\n color: (value: string) => string,\n) {\n if (queries.length === 0) return [];\n\n // Group by message so a shared failure — e.g. a warehouse-level fatal that\n // hits every query identically — prints once instead of repeating per row.\n const byMessage = new Map<string, string[]>();\n for (const { name, message } of queries) {\n const names = byMessage.get(message);\n if (names) names.push(name);\n else byMessage.set(message, [name]);\n }\n\n const maxNameLen = Math.max(...queries.map((query) => query.name.length));\n const tag = color(label.padEnd(7));\n const rows: string[] = [];\n for (const [message, names] of byMessage) {\n if (names.length === 1) {\n rows.push(\n ` ${tag} ${pc.bold(names[0].padEnd(maxNameLen))} ${pc.dim(message)}`,\n );\n continue;\n }\n // Shared message → print it once, then list the affected query names.\n rows.push(\n ` ${tag} ${pc.dim(message)} ${pc.dim(`(${names.length} ${plural(names.length, \"query\", \"queries\")})`)}`,\n );\n rows.push(\n ` ${names.map((name) => pc.bold(name)).join(pc.dim(\", \"))}`,\n );\n }\n return rows;\n}\n\nfunction formatTypegenFailureMessage(options: {\n syntaxErrors: QuerySyntaxError[];\n fatalErrors?: QueryFatalError[];\n warehouseId?: string;\n title: string;\n causes: string[];\n nextStep: string;\n}) {\n const { syntaxErrors, fatalErrors = [], warehouseId, title } = options;\n const total = syntaxErrors.length + fatalErrors.length;\n const separator = pc.dim(\"─\".repeat(60));\n const warehouse = warehouseId\n ? ` against ${pc.dim(`warehouse ${warehouseId}`)}`\n : \"\";\n\n return [\n ` ${pc.bold(pc.red(\"Type generation failed\"))}`,\n ` ${separator}`,\n ` ${title}: ${total} ${plural(total, \"query\", \"queries\")} could not be described${warehouse}.`,\n ` AppKit wrote generated types with ${pc.bold(\"result: unknown\")} for the failed ${plural(total, \"query\", \"queries\")}.`,\n \"\",\n ...formatFailureRows(\"SQL ERR\", syntaxErrors, pc.red),\n ...(syntaxErrors.length > 0 && fatalErrors.length > 0 ? [\"\"] : []),\n ...formatFailureRows(\"FATAL\", fatalErrors, pc.red),\n \"\",\n ` ${pc.bold(\"Common causes\")}`,\n ...options.causes.map((cause) => ` - ${cause}`),\n \"\",\n ` ${pc.bold(\"Next step\")}`,\n ` ${options.nextStep}`,\n ].join(\"\\n\");\n}\n\n/**\n * Thrown when one or more queries fail `DESCRIBE QUERY` against a *reachable*\n * warehouse — i.e. genuine SQL errors (bad table, syntax, incompatible type),\n * as opposed to a connectivity failure (warehouse unreachable), which degrades\n * silently. Whether this is fatal is the caller's decision: the Vite plugin and\n * CLI fail the build in production and warn-only in development.\n */\nexport class TypegenSyntaxError extends Error {\n readonly queries: QuerySyntaxError[];\n readonly fatalQueries: QueryFatalError[];\n\n constructor(\n queries: QuerySyntaxError[],\n warehouseId?: string,\n fatalQueries: QueryFatalError[] = [],\n ) {\n super(\n formatTypegenFailureMessage({\n syntaxErrors: queries,\n fatalErrors: fatalQueries,\n warehouseId,\n title: \"DESCRIBE QUERY failed\",\n causes: [\n \"SQL syntax errors\",\n \"missing tables or views\",\n \"warehouse format incompatibilities\",\n ],\n nextStep: warehouseId\n ? `Run each SQL ERR query directly in a Databricks SQL editor against warehouse ${pc.bold(warehouseId)}.`\n : \"Run each SQL ERR query directly in a Databricks SQL editor.\",\n }),\n );\n this.name = \"TypegenSyntaxError\";\n this.queries = queries;\n this.fatalQueries = fatalQueries;\n }\n}\n\n/**\n * Thrown when DESCRIBE QUERY could not be requested because of a non-SQL fatal\n * setup/request problem, such as missing permissions, invalid warehouse IDs, or\n * malformed SDK configuration. Like TypegenSyntaxError, this is thrown only\n * after the declaration file has been written with `result: unknown` entries.\n */\nexport class TypegenFatalError extends Error {\n readonly queries: QueryFatalError[];\n\n constructor(queries: QueryFatalError[], warehouseId?: string) {\n super(\n formatTypegenFailureMessage({\n syntaxErrors: [],\n fatalErrors: queries,\n warehouseId,\n title: \"DESCRIBE QUERY could not be requested\",\n causes: [\n \"missing warehouse permissions\",\n \"invalid warehouse ID\",\n \"authentication failure\",\n \"SDK configuration errors\",\n ],\n nextStep: warehouseId\n ? `Verify access to warehouse ${pc.bold(warehouseId)} and rerun type generation.`\n : \"Verify warehouse access and rerun type generation.\",\n }),\n );\n this.name = \"TypegenFatalError\";\n this.queries = queries;\n }\n}\n\n/**\n * Generate type declarations for QueryRegistry\n * Create the d.ts file from the plugin routes and query schemas\n * @param querySchemas - the list of query schemas\n * @returns - the type declarations as a string\n */\nfunction generateTypeDeclarations(querySchemas: QuerySchema[] = []): string {\n const queryEntries = querySchemas\n .map(({ name, type }) => {\n const indentedType = type\n .split(\"\\n\")\n .map((line, i) => (i === 0 ? line : ` ${line}`))\n .join(\"\\n\");\n return ` ${name}: ${indentedType}`;\n })\n .join(\";\\n\");\n\n const querySection = queryEntries ? `\\n${queryEntries};\\n ` : \"\";\n\n return `// Auto-generated by AppKit - DO NOT EDIT\n// Generated by 'npx @databricks/appkit generate-types' or Vite plugin during build\nimport \"@databricks/appkit-ui/react\";\nimport type { SQLTypeMarker, SQLStringMarker, SQLNumberMarker, SQLBooleanMarker, SQLBinaryMarker, SQLDateMarker, SQLTimestampMarker } from \"@databricks/appkit-ui/js\";\n\ndeclare module \"@databricks/appkit-ui/react\" {\n interface QueryRegistry {${querySection}}\n}\n`;\n}\n\n/**\n * Status-only probe for the metric-view gate in {@link generateFromEntryPoint}\n *\n * Uses {@link getWarehouseState} (`warehouses.get`) —\n * a read-only GET that can never start the warehouse\n *\n * Takes the lazy client *getter* so the probe also absorbs client construction failure.\n * A connectivity blip returns `undefined`, which the gate reads as transient not-running;\n * a deterministic failure (auth, bad id) is re-thrown so the gate can classify it\n * fatal rather than silently degrading.\n */\nasync function probeWarehouseState(\n getClient: () => WorkspaceClient,\n warehouseId: string,\n): Promise<WarehouseState | undefined> {\n try {\n return await getWarehouseState(getClient(), warehouseId);\n } catch (err) {\n // Connectivity blip → undefined (gate degrades, retries next pass). A\n // deterministic failure (auth, bad warehouse id, client construction) must\n // not masquerade as not-running — re-throw so the gate pins it fatal, the\n // same split the query path's preflight makes.\n if (isConnectivityError(err)) return undefined;\n throw err;\n }\n}\n\n/**\n * Entry point for generating type declarations from all imported files\n * @param options - the options for the generation\n * @param options.entryPoint - the entry point file\n * @param options.outFile - the output file\n * @param options.noCache - skip the typegen cache entirely: every query is\n * re-described, and the metric path ignores its cached schemas (every\n * configured key becomes describe-needed) and overwrites the cache's\n * `metrics` section with this pass's results.\n * @param options.mode - preflight policy (see {@link PreflightMode}), default\n * `\"non-blocking\"`. For queries, `\"non-blocking\"` never touches the\n * warehouse. For metric views it makes one status-only probe and DESCRIBEs\n * only when the warehouse is already RUNNING, otherwise emits permissive\n * degraded types immediately. `\"blocking\"` waits for / starts the warehouse\n * first, failing the build only for a deleted/deleting one.\n * @param options.metricViewsFolder - folder that holds `definitions.json`\n * (`<root>/config/metric-views`). Optional and independent of `queryFolder`:\n * metric-view types generate whenever this folder holds a config, even if the\n * app has no `config/queries`. When omitted it defaults to a sibling\n * `metric-views` directory of `queryFolder` (so query-only callers keep\n * working); when neither is given, the metric path is skipped.\n * @param options.mvOutFile - optional output file for the MetricRegistry\n * augmentation. Defaults to a sibling `metric-views.d.ts` file under the same\n * directory as `outFile`. Skipped entirely if `definitions.json` is absent.\n * @param options.metricFetcher - optional DescribeFetcher used by\n * {@link syncMetrics} (tests inject a mock; production lazily builds a\n * default WorkspaceClient-backed one). An injected fetcher always runs: it\n * hits no warehouse, so it bypasses both the non-blocking gate and the\n * blocking preflight.\n */\nexport async function generateFromEntryPoint(options: {\n outFile: string;\n queryFolder?: string;\n metricViewsFolder?: string;\n warehouseId: string;\n noCache?: boolean;\n mode?: PreflightMode;\n mvOutFile?: string;\n metricFetcher?: DescribeFetcher;\n}) {\n const {\n outFile,\n queryFolder,\n warehouseId,\n noCache,\n mode = \"non-blocking\",\n mvOutFile,\n metricFetcher,\n } = options;\n\n // Metric config lives in `config/metric-views/`, a sibling of the queries\n // folder. Prefer the explicit option; otherwise derive the sibling of\n // `queryFolder` so callers that pass only `queryFolder` keep emitting metric\n // types. Undefined when neither is given → the metric path stays dormant.\n const metricViewsFolder =\n options.metricViewsFolder ??\n (queryFolder ? path.resolve(queryFolder, \"..\", \"metric-views\") : undefined);\n const resolvedMvFile =\n mvOutFile ?? path.join(path.dirname(outFile), METRIC_TYPES_FILE);\n\n const projectRoot = resolveProjectRoot(outFile);\n\n logger.debug(\"Starting type generation...\");\n\n let queryRegistry: QuerySchema[] = [];\n let syntaxErrors: QuerySyntaxError[] = [];\n // Deterministic fatal errors only (404/400).\n let fatalErrors: QueryFatalError[] = [];\n let queryHadEnvironmentalFailure = false;\n let metricsHadEnvironmentalFailure = false;\n // Track the coarse cause of the environmental failure for the warning message.\n let environmentalCause: \"auth\" | \"unreachable\" | \"unavailable\" | undefined;\n\n if (queryFolder) {\n const result = await generateQueriesFromDescribe(queryFolder, warehouseId, {\n noCache,\n mode,\n });\n queryRegistry = result.schemas;\n syntaxErrors = result.syntaxErrors ?? [];\n fatalErrors = result.fatalErrors ?? [];\n queryHadEnvironmentalFailure = result.hadEnvironmentalFailure ?? false;\n environmentalCause =\n environmentalCause ?? result.environmentalCause ?? undefined;\n }\n\n const typeDeclarations = generateTypeDeclarations(queryRegistry);\n\n // In blocking mode, never overwrite committed types with a schema explicitly\n // marked degraded. Leave the committed .d.ts as the fallback of record.\n // Non-blocking mode always writes.\n const hasAnyDegradedQuery = queryRegistry.some(isQueryDegraded);\n if (mode === \"blocking\" && hasAnyDegradedQuery) {\n // A degraded schema always participates in the committed-types gate. Keep\n // this invariant next to write suppression so a new producer cannot update\n // one decision without the other.\n queryHadEnvironmentalFailure = true;\n environmentalCause = environmentalCause ?? \"unavailable\";\n }\n const shouldWriteQueries = mode !== \"blocking\" || !hasAnyDegradedQuery;\n\n if (shouldWriteQueries) {\n await fs.mkdir(path.dirname(outFile), { recursive: true });\n await fs.writeFile(outFile, typeDeclarations, \"utf-8\");\n }\n\n // Metric-view types: emit whenever a metric-views folder is resolved (gated\n // on the metric config's own dir, NOT the queries folder — an app can declare\n // metric views without any `.sql` queries). `syncMetricViewsTypes` still\n // returns `noConfig` when the folder holds no `definitions.json`.\n if (metricViewsFolder) {\n let mvResult: SyncMetricViewsTypesResult;\n try {\n mvResult = await syncMetricViewsTypes({\n metricViewsFolder,\n warehouseId,\n metricOutFile: resolvedMvFile,\n cache: !noCache,\n metricFetcher,\n mode,\n // In blocking mode, never overwrite committed metric types with a\n // degraded result — including on runs that go on to throw, so a failing\n // build leaves the committed .d.ts intact. Non-blocking always writes.\n suppressDegradedWrite: mode === \"blocking\",\n });\n } catch (configError) {\n // syncMetricViewsTypes only throws for a malformed definitions.json — re-throw as a message-only TypegenFatalError.\n throw new TypegenFatalError(\n [\n {\n name: \"config/metric-views/definitions.json\",\n message: getErrorDiagnostic(configError),\n },\n ],\n warehouseId,\n );\n }\n\n // Deleted/deleting-warehouse fatal preflight (blocking mode only);\n // empty (no-op) when definitions.json is absent or in non-blocking mode.\n // Only deterministic fatals are recorded in fatalErrors.\n for (const fe of mvResult.fatalErrors) {\n fatalErrors.push(fe);\n }\n\n metricsHadEnvironmentalFailure =\n (mvResult.hadEnvironmentalFailure ?? false) ||\n (mode === \"blocking\" && hasAnyDegradedMetrics(mvResult.schemas));\n environmentalCause =\n environmentalCause ?? mvResult.environmentalCause ?? undefined;\n\n // Blocking (`--wait` / prod Vite) escalates only deterministic per-key\n // DESCRIBE failures. Transient connectivity failures are already recorded\n // as environmental by syncMetricViewsTypes and fall through to the\n // committed-types gate below.\n if (mode === \"blocking\") {\n for (const failure of mvResult.failures) {\n if (failure.transient) continue;\n fatalErrors.push({\n name: failure.key,\n message: `metric view ${failure.key} (${failure.source}) could not be described: ${failure.reason}`,\n });\n }\n }\n }\n\n await removeOldGeneratedTypes(projectRoot, \"appKitTypes.d.ts\");\n await migrateProjectConfig(projectRoot);\n\n // Deterministic failures (SQL syntax errors or 404/400 HTTP) always crash regardless of mode.\n if (syntaxErrors.length > 0) {\n throw new TypegenSyntaxError(syntaxErrors, warehouseId, fatalErrors);\n }\n if (fatalErrors.length > 0) {\n throw new TypegenFatalError(fatalErrors, warehouseId);\n }\n\n if (\n mode === \"blocking\" &&\n (queryHadEnvironmentalFailure || metricsHadEnvironmentalFailure)\n ) {\n const missingCommittedTypes: string[] = [];\n if (queryHadEnvironmentalFailure && !existsSync(outFile)) {\n missingCommittedTypes.push(path.basename(outFile));\n }\n if (metricsHadEnvironmentalFailure && !existsSync(resolvedMvFile)) {\n missingCommittedTypes.push(path.basename(resolvedMvFile));\n }\n\n if (missingCommittedTypes.length === 0) {\n // Committed types present: emit loud warning and exit 0.\n const warningMessage = determineWarningMessage(\n environmentalCause ?? \"unavailable\",\n warehouseId,\n );\n logger.warn(warningMessage);\n } else {\n throw new TypegenFatalError(\n [\n {\n name: \"type-generator\",\n message: `Warehouse ${warehouseId} could not provide schemas and the required committed type ${plural(missingCommittedTypes.length, \"artifact is\", \"artifacts are\")} missing: ${missingCommittedTypes.join(\", \")}. Run 'npx @databricks/appkit generate-types --wait' locally and commit the generated .d.ts files.`,\n },\n ],\n warehouseId,\n );\n }\n }\n\n logger.debug(\"Type generation complete!\");\n}\n\n/**\n * Result of a {@link syncMetricViewsTypes} run, returned to the caller (the CLI\n * directly, or {@link generateFromEntryPoint} which delegates to it) so it can\n * report what happened and decide its exit code.\n */\nexport interface SyncMetricViewsTypesResult {\n metricOutFile?: string;\n schemas: MetricSchema[];\n failures: MetricSyncFailure[];\n /**\n * `true` when no `definitions.json` was found in the metric-views folder, so\n * nothing was synced.\n */\n noConfig: boolean;\n /**\n * Per-key fatal preflight errors (empty except in the `blocking`-mode\n * deleted/deleting-warehouse and deterministic-preflight-failure cases).\n * {@link generateFromEntryPoint} surfaces these by throwing\n * {@link TypegenFatalError}; when the run also degraded, `suppressDegradedWrite`\n * means no artifact was written and the committed types stand. A `\"describe-now\"`\n * run sets no blocking preflight, so for that mode this is always empty.\n * ONLY contains deterministic failures (404/400).\n */\n fatalErrors: Array<{ name: string; message: string }>;\n /**\n * `true` when an environmental failure occurred in blocking mode (auth, connectivity,\n * DELETED/DELETING, wait-timeout, or other unrecognized failures). Used by\n * {@link generateFromEntryPoint} to decide whether to apply the has-types gate.\n * Does not directly cause a throw — the gate decides that. Always false in\n * non-blocking or describe-now mode.\n */\n hadEnvironmentalFailure?: boolean;\n /**\n * Coarse cause label for the environmental failure, one of \"auth\" (401/403),\n * \"unreachable\" (connectivity), or \"unavailable\" (other). Only set when\n * hadEnvironmentalFailure is true; used by the warning message.\n */\n environmentalCause?: \"auth\" | \"unreachable\" | \"unavailable\";\n}\n\n/**\n * Unified metric-view type-generation pipeline behind {@link\n * generateFromEntryPoint}'s metric section (which forwards its\n * `\"non-blocking\"`/`\"blocking\"` mode). Also directly callable with the default\n * `\"describe-now\"` mode for a focused, always-converge metric refresh.\n *\n *\n * @param options.metricViewsFolder - folder that holds `definitions.json` (`<root>/config/metric-views`).\n * @param options.warehouseId - SQL warehouse used for `DESCRIBE TABLE EXTENDED`.\n * @param options.metricOutFile - output path for the MetricRegistry `.d.ts`.\n * @param options.cache - cache toggle, default ON. Only `cache === false` disables it (so `undefined`/`true` keep caching).\n * @param options.metricFetcher - optional injected {@link DescribeFetcher}\n * @param options.mode - preflight/gate policy, default `\"describe-now\"`. When set to `\"blocking\"`,\n * metric-view .d.ts writes are suppressed if any metric is degraded (to preserve committed files).\n * @param options.suppressDegradedWrite - when true (only in `mode === \"blocking\"` context), skip\n * the metricOutFile write if any metric schema has `degraded === true`. Used to prevent\n * overwriting committed .d.ts files with degraded types in blocking mode.\n */\nexport async function syncMetricViewsTypes(options: {\n metricViewsFolder: string;\n warehouseId: string;\n metricOutFile: string;\n cache?: boolean;\n metricFetcher?: DescribeFetcher;\n mode?: \"describe-now\" | \"non-blocking\" | \"blocking\";\n suppressDegradedWrite?: boolean;\n}): Promise<SyncMetricViewsTypesResult> {\n const {\n metricViewsFolder,\n warehouseId,\n metricOutFile,\n cache: cacheEnabled,\n metricFetcher,\n mode = \"describe-now\",\n suppressDegradedWrite,\n } = options;\n\n // Only `cache === false` disables caching; `undefined`/`true` keep it on.\n const noCache = cacheEnabled === false;\n\n const mvConfig = await readMetricConfig(metricViewsFolder);\n if (!mvConfig) {\n // No definitions.json — additive path stays dormant. The CLI turns this\n // into a friendly \"nothing to sync\" message and exits 0;\n // generateFromEntryPoint simply ignores `noConfig`.\n return { schemas: [], failures: [], fatalErrors: [], noConfig: true };\n }\n\n const resolution = resolveMetricConfig(mvConfig);\n\n const fatalErrors: Array<{ name: string; message: string }> = [];\n\n // Load the shared typegen cache and copy its `metrics` section into a null-prototype map.\n const cache = await loadCache();\n const mvCacheSection: Record<string, MetricCacheEntry> = Object.create(null);\n if (!noCache && cache.metrics) {\n for (const key of Object.keys(cache.metrics)) {\n mvCacheSection[key] = cache.metrics[key];\n }\n }\n\n // Partition BEFORE any gate/preflight decision: a hit (a structurally valid,\n // hash-matching, NON-degraded cached entry) is served from cache no matter\n // what the warehouse is doing. The cache only ever holds successful describes\n // (a degraded outcome is never persisted — see the write block below), so the\n // `degraded !== true` guard is normally moot; it also defends against a stale\n // degraded entry left by an older writer, which re-describes instead of\n // serving. Everything else (new, edited, unrevivable, or degraded) is eligible\n // for DESCRIBE, so a fully-warm pass makes zero warehouse calls and constructs\n // zero clients. Mirrors the query path: only a good result is cache-servable.\n const hitSchemas = new Map<string, MetricSchema>();\n const describeNeeded: typeof resolution.entries = [];\n for (const entry of resolution.entries) {\n const prior = mvCacheSection[entry.key];\n if (\n prior !== undefined &&\n isRevivableMetricCacheEntry(prior) &&\n prior.hash === metricCacheHash(entry.source, entry.lane) &&\n prior.schema.degraded !== true\n ) {\n hitSchemas.set(entry.key, prior.schema);\n } else {\n describeNeeded.push(entry);\n }\n }\n\n let mvClient: WorkspaceClient | undefined;\n const getMvClient = (): WorkspaceClient => {\n mvClient ??= createWorkspaceClient();\n return mvClient;\n };\n\n // Blocking-mode preflight: ensure the warehouse is running before the MV DESCRIBE\n // batch (probe → decide → wait / start+wait; only DELETED/DELETING is fatal). Two softenings vs the query preflight: a failed probe and a timed-out wait are NOT fatal here — we fall through to syncMetrics, which classifies a still-not-ready warehouse as degraded rather than failing the build. Skipped for `describe-now`/`non-blocking` (only `mode === \"blocking\"` enters here).\n let preflightFatalMessage: string | undefined;\n let hadEnvironmentalFailure = false;\n let environmentalCause: \"auth\" | \"unreachable\" | \"unavailable\" | undefined;\n if (\n mode === \"blocking\" &&\n metricFetcher === undefined &&\n describeNeeded.length > 0\n ) {\n try {\n const state = await getWarehouseState(getMvClient(), warehouseId);\n const decision = decidePreflight(state, mode);\n if (decision === \"fatal\") {\n preflightFatalMessage = `warehouse ${warehouseId} is ${state}`;\n // State-based DELETED/DELETING is environmental, not deterministic.\n hadEnvironmentalFailure = true;\n environmentalCause = \"unavailable\";\n } else if (decision === \"startWaitProceed\") {\n // treatStoppedAsTransient rides out the stale pre-start STOPPED/STOPPING\n // reading, same as the query preflight.\n await startWarehouse(getMvClient(), warehouseId);\n const settled = await waitUntilRunning(getMvClient(), warehouseId, {\n maxMs: MV_PREFLIGHT_WAIT_MAX_MS,\n treatStoppedAsTransient: true,\n });\n if (settled !== \"RUNNING\") {\n // With treatStoppedAsTransient, a non-RUNNING resolve is exactly\n // DELETED/DELETING — the warehouse was deleted while we waited.\n preflightFatalMessage = `warehouse ${warehouseId} is ${settled}`;\n hadEnvironmentalFailure = true;\n }\n } else if (decision === \"waitThenProceed\") {\n const settled = await waitUntilRunning(getMvClient(), warehouseId, {\n maxMs: MV_PREFLIGHT_WAIT_MAX_MS,\n });\n if (settled === \"DELETED\" || settled === \"DELETING\") {\n // Deleted mid-wait: fatal. Environmental (state-based).\n preflightFatalMessage = `warehouse ${warehouseId} is ${settled}`;\n hadEnvironmentalFailure = true;\n }\n }\n } catch (err) {\n // Connectivity blip: fall through to syncMetrics, whose DESCRIBEs degrade\n // a not-ready / unreachable warehouse rather than throwing.\n if (!isConnectivityError(err)) {\n // Classify: deterministic (404/400) or environmental (auth, etc).\n const classification = classifyBlockingFailure(err);\n if (classification === \"deterministic\") {\n // Keep as fatal preflight for deterministic errors (404/400).\n preflightFatalMessage = `warehouse ${warehouseId}: ${getErrorDiagnostic(err)}`;\n } else {\n // Environmental: set preflightFatalMessage so DESCRIBE is skipped, but\n // mark hadEnvironmentalFailure so the gate handles it later (not added\n // to fatalErrors).\n preflightFatalMessage = `warehouse ${warehouseId}: ${getErrorDiagnostic(err)}`;\n hadEnvironmentalFailure = true;\n environmentalCause = classifyEnvironmentalCause(err);\n }\n }\n }\n }\n\n let gateState: WarehouseState | undefined;\n let describeNow =\n metricFetcher !== undefined ||\n mode !== \"non-blocking\" ||\n describeNeeded.length === 0;\n if (!describeNow) {\n try {\n gateState = await probeWarehouseState(getMvClient, warehouseId);\n } catch (err) {\n preflightFatalMessage = `warehouse ${warehouseId}: ${getErrorDiagnostic(err)}`;\n }\n describeNow = gateState === \"RUNNING\";\n }\n\n let described: MetricSchema[];\n let failures: MetricSyncFailure[] = [];\n if (preflightFatalMessage !== undefined) {\n // Fatal preflight (deleted/deleting warehouse or deterministic error):\n // skip DESCRIBE, emit degraded schemas so both artifacts are still written,\n // and record one fatal error per describe-needed key (cache hits are\n // unaffected) ONLY if it's a deterministic error. Environmental failures\n // degrade silently. The degraded schemas are not cached (see the write\n // block), so a later pass re-probes.\n described = describeNeeded.map(emptyMetricSchema);\n // Only deterministic fatals (404/400) record errors; environmental failures\n // degrade silently for the has-types gate to handle.\n if (!hadEnvironmentalFailure) {\n for (const entry of describeNeeded) {\n fatalErrors.push({ name: entry.key, message: preflightFatalMessage });\n }\n }\n } else if (describeNeeded.length === 0) {\n // Nothing left to describe — every configured key was a cache hit.\n // syncMetrics would be a no-op (and building its fetcher would construct a\n // client for nothing); artifacts regenerate from cache.\n described = [];\n } else if (describeNow) {\n const fetcher =\n metricFetcher ??\n createWorkspaceDescribeFetcher(getMvClient(), warehouseId);\n ({ schemas: described, failures } = await syncMetrics(\n { entries: describeNeeded },\n fetcher,\n ));\n\n // Surface DESCRIBE failures loudly: a misconfigured definitions.json would\n // otherwise silently ship an empty entry that the runtime fail-closed gate\n // 503s in production. syncMetrics is log-free; this caller is the single\n // owner of failure logging.\n if (failures.length > 0) {\n for (const f of failures) {\n logger.warn(\n \"metric sync failed for %s (%s): %s\",\n f.key,\n f.source,\n f.reason,\n );\n }\n }\n\n // A rejected DESCRIBE with a connectivity signal is expected to recover on\n // a later pass. In blocking mode, route it through the same committed-types\n // gate as preflight outages instead of treating it as a configuration error.\n if (mode === \"blocking\" && failures.some((failure) => failure.transient)) {\n hadEnvironmentalFailure = true;\n environmentalCause = environmentalCause ?? \"unreachable\";\n }\n\n // Degraded-but-not-failed keys: the warehouse answered with a non-terminal\n // state (stopped / cold-starting), so their schemas are unknown.\n const failedKeys = new Set(failures.map((f) => f.key));\n const degradedKeys = described\n .filter((s) => s.degraded && !failedKeys.has(s.key))\n .map((s) => s.key);\n if (degradedKeys.length > 0) {\n logger.info(\n \"Warehouse %s did not return schemas for %d metric view(s) (%s) — wrote degraded metric types (permissive); they will refresh once the warehouse is available.\",\n warehouseId,\n degradedKeys.length,\n degradedKeys.join(\", \"),\n );\n hadEnvironmentalFailure = true;\n environmentalCause = environmentalCause ?? \"unavailable\";\n }\n } else {\n // Un-probed DESCRIBEs deliberately skipped, not failures: emit each\n // describe-needed key as a degraded schema so both artifacts exist; cache\n // hits keep serving last-known-good. This is an environmental failure path.\n described = describeNeeded.map(emptyMetricSchema);\n logger.info(\n \"Warehouse %s is not running — wrote degraded metric types (permissive) for %d metric view(s) (%s); they will refresh once the warehouse is available.\",\n warehouseId,\n describeNeeded.length,\n describeNeeded.map((e) => e.key).join(\", \"),\n );\n hadEnvironmentalFailure = true;\n environmentalCause = environmentalCause ?? \"unavailable\";\n }\n\n // Cache only successful schema results for describe-needed keys; remove stale cache for degraded ones.\n for (let i = 0; i < describeNeeded.length; i++) {\n // syncMetrics return one schema per entry in entry order, so described[i] always belongs to describeNeeded[i].\n const entry = describeNeeded[i];\n if (described[i].degraded === true) {\n delete mvCacheSection[entry.key];\n continue;\n }\n mvCacheSection[entry.key] = {\n hash: metricCacheHash(entry.source, entry.lane),\n schema: described[i],\n // Vestigial, mirrors the query path's only cache write (always false): a\n // persisted entry is by construction a good result, so it never needs a\n // re-describe flag. Kept for on-disk shape compatibility with existing\n // version-3 caches (isRevivableMetricCacheEntry gates on a boolean).\n retry: false,\n };\n }\n\n // Prune entries whose key is no longer configured\n const configuredKeys = new Set(resolution.entries.map((e) => e.key));\n let prunedCount = 0;\n for (const key of Object.keys(mvCacheSection)) {\n if (!configuredKeys.has(key)) {\n delete mvCacheSection[key];\n prunedCount++;\n }\n }\n\n // Save when this pass produced outcomes, bypassed the cache, or pruned.\n if (describeNeeded.length > 0 || noCache || prunedCount > 0) {\n cache.metrics = mvCacheSection;\n await saveCache(cache);\n }\n\n // Merge cached hits with fresh results back into config order.\n const describedByKey = new Map<string, MetricSchema>();\n for (const schema of described) {\n describedByKey.set(schema.key, schema);\n }\n const schemas = resolution.entries.map((entry) => {\n const schema = hitSchemas.get(entry.key) ?? describedByKey.get(entry.key);\n if (schema !== undefined) return schema;\n logger.warn(\n \"no schema resolved for metric key %s — emitting degraded types (should not happen)\",\n entry.key,\n );\n return emptyMetricSchema(entry);\n });\n\n // Same anti-clobber rule as the query path: when suppressDegradedWrite is set\n // (blocking mode), skip the write if any metric degraded, preserving the\n // committed metric-views.d.ts. Non-blocking mode always writes.\n const shouldWriteMetrics =\n !suppressDegradedWrite || !hasAnyDegradedMetrics(schemas);\n\n if (shouldWriteMetrics) {\n await fs.mkdir(path.dirname(metricOutFile), { recursive: true });\n await fs.writeFile(\n metricOutFile,\n generateMetricTypeDeclarations(schemas),\n \"utf-8\",\n );\n }\n\n logger.debug(\n \"Wrote MetricRegistry augmentation for %d metric(s)%s\",\n schemas.length,\n failures.length > 0 ? ` (${failures.length} failure(s))` : \"\",\n );\n\n return {\n metricOutFile,\n schemas,\n failures,\n fatalErrors,\n noConfig: false,\n hadEnvironmentalFailure:\n mode === \"blocking\" ? hadEnvironmentalFailure : undefined,\n environmentalCause:\n mode === \"blocking\" && hadEnvironmentalFailure\n ? environmentalCause\n : undefined,\n };\n}\n\n// Rolldown tree-shaking only preserves \"own exports\" (locally defined) — not re-exports.\n// A local binding ensures the serving vite plugin's import keeps this in the dependency graph,\n// mirroring how generateFromEntryPoint (also defined here) is preserved via the analytics vite plugin.\nexport const generateServingTypes = generateServingTypesImpl;\n\n// Re-export the mv-registry types so consumers (CLI, the type-generator\n// .d.ts shim in `packages/shared`) can pick them up from this entry point —\n// the .d.ts shim documents these as part of the package's public surface.\nexport type {\n MetricColumnMetadata,\n MetricLane,\n MetricSchema,\n MetricSyncFailure,\n MetricSyncResult,\n};\n\nexport const TYPES_DIR = \"appkit-types\";\nexport const ANALYTICS_TYPES_FILE = \"analytics.d.ts\";\nexport const SERVING_TYPES_FILE = \"serving.d.ts\";\nexport const METRIC_TYPES_FILE = \"metric-views.d.ts\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["generateServingTypesImpl"],"sources":["../../src/type-generator/index.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport dotenv from \"dotenv\";\nimport pc from \"picocolors\";\nimport { createLogger } from \"../logging/logger\";\nimport {\n createWorkspaceClient,\n type WorkspaceClient,\n} from \"../workspace-client\";\nimport {\n isRevivableMetricCacheEntry,\n loadCache,\n type MetricCacheEntry,\n metricCacheHash,\n saveCache,\n} from \"./cache\";\nimport {\n classifyBlockingFailure,\n classifyEnvironmentalCause,\n getErrorDiagnostic,\n isConnectivityError,\n} from \"./errors\";\nimport {\n migrateProjectConfig,\n removeOldGeneratedTypes,\n resolveProjectRoot,\n} from \"./migration\";\nimport { readMetricConfig, resolveMetricConfig } from \"./mv-registry/config\";\nimport { createWorkspaceDescribeFetcher } from \"./mv-registry/describe\";\nimport { generateMetricTypeDeclarations } from \"./mv-registry/render-types\";\nimport { emptyMetricSchema, syncMetrics } from \"./mv-registry/sync\";\nimport type {\n DescribeFetcher,\n MetricColumnMetadata,\n MetricLane,\n MetricSchema,\n MetricSyncFailure,\n MetricSyncResult,\n} from \"./mv-registry/types\";\nimport { decidePreflight, type PreflightMode } from \"./preflight\";\nimport { generateQueriesFromDescribe } from \"./query-registry\";\nimport { generateServingTypes as generateServingTypesImpl } from \"./serving/generator\";\nimport type { QueryFatalError, QuerySchema, QuerySyntaxError } from \"./types\";\nimport {\n getWarehouseState,\n startWarehouse,\n type WarehouseState,\n waitUntilRunning,\n} from \"./warehouse-status\";\n\ndotenv.config();\n\nconst logger = createLogger(\"type-generator\");\n\n/**\n * Upper bound (~5 min) on how long the Metric Views path's `blocking`-mode preflight\n * waits for a warehouse to reach RUNNING. Mirrors the query path's (unexported)\n * `PREFLIGHT_WAIT_MAX_MS` in query-registry.ts.\n */\nconst MV_PREFLIGHT_WAIT_MAX_MS = 300_000;\n\nfunction determineWarningMessage(\n cause: \"auth\" | \"unreachable\" | \"unavailable\",\n warehouseId: string,\n): string {\n const causeLabel =\n cause === \"auth\"\n ? \"auth blocked\"\n : cause === \"unreachable\"\n ? \"warehouse unreachable\"\n : \"warehouse unavailable\";\n // Use a stable prefix for greppability and CI log matching.\n return `AppKit typegen: using committed types — warehouse ${warehouseId} ${causeLabel}; please check warehouse status and retry`;\n}\n\ntype TypegenFailure = QuerySyntaxError | QueryFatalError;\n\nfunction plural(count: number, singular: string, pluralForm = `${singular}s`) {\n return count === 1 ? singular : pluralForm;\n}\n\nfunction isQueryDegraded(schema: QuerySchema): boolean {\n return schema.degraded === true;\n}\n\nfunction hasAnyDegradedMetrics(schemas: MetricSchema[]): boolean {\n return schemas.some((s) => s.degraded === true);\n}\n\nfunction formatFailureRows(\n label: string,\n queries: TypegenFailure[],\n color: (value: string) => string,\n) {\n if (queries.length === 0) return [];\n\n // Group by message so a shared failure — e.g. a warehouse-level fatal that\n // hits every query identically — prints once instead of repeating per row.\n const byMessage = new Map<string, string[]>();\n for (const { name, message } of queries) {\n const names = byMessage.get(message);\n if (names) names.push(name);\n else byMessage.set(message, [name]);\n }\n\n const maxNameLen = Math.max(...queries.map((query) => query.name.length));\n const tag = color(label.padEnd(7));\n const rows: string[] = [];\n for (const [message, names] of byMessage) {\n if (names.length === 1) {\n rows.push(\n ` ${tag} ${pc.bold(names[0].padEnd(maxNameLen))} ${pc.dim(message)}`,\n );\n continue;\n }\n // Shared message → print it once, then list the affected query names.\n rows.push(\n ` ${tag} ${pc.dim(message)} ${pc.dim(`(${names.length} ${plural(names.length, \"query\", \"queries\")})`)}`,\n );\n rows.push(\n ` ${names.map((name) => pc.bold(name)).join(pc.dim(\", \"))}`,\n );\n }\n return rows;\n}\n\nfunction formatTypegenFailureMessage(options: {\n syntaxErrors: QuerySyntaxError[];\n fatalErrors?: QueryFatalError[];\n warehouseId?: string;\n title: string;\n causes: string[];\n nextStep: string;\n}) {\n const { syntaxErrors, fatalErrors = [], warehouseId, title } = options;\n const total = syntaxErrors.length + fatalErrors.length;\n const separator = pc.dim(\"─\".repeat(60));\n const warehouse = warehouseId\n ? ` against ${pc.dim(`warehouse ${warehouseId}`)}`\n : \"\";\n\n return [\n ` ${pc.bold(pc.red(\"Type generation failed\"))}`,\n ` ${separator}`,\n ` ${title}: ${total} ${plural(total, \"query\", \"queries\")} could not be described${warehouse}.`,\n ` AppKit wrote generated types with ${pc.bold(\"result: unknown\")} for the failed ${plural(total, \"query\", \"queries\")}.`,\n \"\",\n ...formatFailureRows(\"SQL ERR\", syntaxErrors, pc.red),\n ...(syntaxErrors.length > 0 && fatalErrors.length > 0 ? [\"\"] : []),\n ...formatFailureRows(\"FATAL\", fatalErrors, pc.red),\n \"\",\n ` ${pc.bold(\"Common causes\")}`,\n ...options.causes.map((cause) => ` - ${cause}`),\n \"\",\n ` ${pc.bold(\"Next step\")}`,\n ` ${options.nextStep}`,\n ].join(\"\\n\");\n}\n\n/**\n * Thrown when one or more queries fail `DESCRIBE QUERY` against a *reachable*\n * warehouse — i.e. genuine SQL errors (bad table, syntax, incompatible type),\n * as opposed to a connectivity failure (warehouse unreachable), which degrades\n * silently. Whether this is fatal is the caller's decision: the Vite plugin and\n * CLI fail the build in production and warn-only in development.\n */\nexport class TypegenSyntaxError extends Error {\n readonly queries: QuerySyntaxError[];\n readonly fatalQueries: QueryFatalError[];\n\n constructor(\n queries: QuerySyntaxError[],\n warehouseId?: string,\n fatalQueries: QueryFatalError[] = [],\n ) {\n super(\n formatTypegenFailureMessage({\n syntaxErrors: queries,\n fatalErrors: fatalQueries,\n warehouseId,\n title: \"DESCRIBE QUERY failed\",\n causes: [\n \"SQL syntax errors\",\n \"missing tables or views\",\n \"warehouse format incompatibilities\",\n ],\n nextStep: warehouseId\n ? `Run each SQL ERR query directly in a Databricks SQL editor against warehouse ${pc.bold(warehouseId)}.`\n : \"Run each SQL ERR query directly in a Databricks SQL editor.\",\n }),\n );\n this.name = \"TypegenSyntaxError\";\n this.queries = queries;\n this.fatalQueries = fatalQueries;\n }\n}\n\n/**\n * Thrown when DESCRIBE QUERY could not be requested because of a non-SQL fatal\n * setup/request problem, such as missing permissions, invalid warehouse IDs, or\n * malformed SDK configuration. Like TypegenSyntaxError, this is thrown only\n * after the declaration file has been written with `result: unknown` entries.\n */\nexport class TypegenFatalError extends Error {\n readonly queries: QueryFatalError[];\n\n constructor(queries: QueryFatalError[], warehouseId?: string) {\n super(\n formatTypegenFailureMessage({\n syntaxErrors: [],\n fatalErrors: queries,\n warehouseId,\n title: \"DESCRIBE QUERY could not be requested\",\n causes: [\n \"missing warehouse permissions\",\n \"invalid warehouse ID\",\n \"authentication failure\",\n \"SDK configuration errors\",\n ],\n nextStep: warehouseId\n ? `Verify access to warehouse ${pc.bold(warehouseId)} and rerun type generation.`\n : \"Verify warehouse access and rerun type generation.\",\n }),\n );\n this.name = \"TypegenFatalError\";\n this.queries = queries;\n }\n}\n\n/**\n * Generate type declarations for QueryRegistry\n * Create the d.ts file from the plugin routes and query schemas\n * @param querySchemas - the list of query schemas\n * @returns - the type declarations as a string\n */\nfunction generateTypeDeclarations(querySchemas: QuerySchema[] = []): string {\n const queryEntries = querySchemas\n .map(({ name, type }) => {\n const indentedType = type\n .split(\"\\n\")\n .map((line, i) => (i === 0 ? line : ` ${line}`))\n .join(\"\\n\");\n return ` ${name}: ${indentedType}`;\n })\n .join(\";\\n\");\n\n const querySection = queryEntries ? `\\n${queryEntries};\\n ` : \"\";\n\n return `// Auto-generated by AppKit - DO NOT EDIT\n// Generated by 'npx @databricks/appkit generate-types' or Vite plugin during build\nimport \"@databricks/appkit-ui/react\";\nimport type { SQLTypeMarker, SQLStringMarker, SQLNumberMarker, SQLBooleanMarker, SQLBinaryMarker, SQLDateMarker, SQLTimestampMarker } from \"@databricks/appkit-ui/js\";\n\ndeclare module \"@databricks/appkit-ui/react\" {\n interface QueryRegistry {${querySection}}\n}\n`;\n}\n\n/**\n * Status-only probe for the metric-view gate in {@link generateFromEntryPoint}\n *\n * Uses {@link getWarehouseState} (`warehouses.get`) —\n * a read-only GET that can never start the warehouse\n *\n * Takes the lazy client *getter* so the probe also absorbs client construction failure.\n * A connectivity blip returns `undefined`, which the gate reads as transient not-running;\n * a deterministic failure (auth, bad id) is re-thrown so the gate can classify it\n * fatal rather than silently degrading.\n */\nasync function probeWarehouseState(\n getClient: () => WorkspaceClient,\n warehouseId: string,\n): Promise<WarehouseState | undefined> {\n try {\n return await getWarehouseState(getClient(), warehouseId);\n } catch (err) {\n // Connectivity blip → undefined (gate degrades, retries next pass). A\n // deterministic failure (auth, bad warehouse id, client construction) must\n // not masquerade as not-running — re-throw so the gate pins it fatal, the\n // same split the query path's preflight makes.\n if (isConnectivityError(err)) return undefined;\n throw err;\n }\n}\n\n/**\n * Entry point for generating type declarations from all imported files\n * @param options - the options for the generation\n * @param options.entryPoint - the entry point file\n * @param options.outFile - the output file\n * @param options.noCache - skip the typegen cache entirely: every query is\n * re-described, and the metric path ignores its cached schemas (every\n * configured key becomes describe-needed) and overwrites the cache's\n * `metrics` section with this pass's results.\n * @param options.mode - preflight policy (see {@link PreflightMode}), default\n * `\"non-blocking\"`. For queries, `\"non-blocking\"` never touches the\n * warehouse. For metric views it makes one status-only probe and DESCRIBEs\n * only when the warehouse is already RUNNING, otherwise emits permissive\n * degraded types immediately. `\"blocking\"` waits for / starts the warehouse\n * first, failing the build only for a deleted/deleting one.\n * @param options.metricViewsFolder - folder that holds `definitions.json`\n * (`<root>/config/metric-views`). Optional and independent of `queryFolder`:\n * metric-view types generate whenever this folder holds a config, even if the\n * app has no `config/queries`. When omitted it defaults to a sibling\n * `metric-views` directory of `queryFolder` (so query-only callers keep\n * working); when neither is given, the metric path is skipped.\n * @param options.mvOutFile - optional output file for the MetricRegistry\n * augmentation. Defaults to a sibling `metric-views.ts` file under the same\n * directory as `outFile`. Skipped entirely if `definitions.json` is absent.\n * @param options.metricFetcher - optional DescribeFetcher used by\n * {@link syncMetrics} (tests inject a mock; production lazily builds a\n * default WorkspaceClient-backed one). An injected fetcher always runs: it\n * hits no warehouse, so it bypasses both the non-blocking gate and the\n * blocking preflight.\n */\nexport async function generateFromEntryPoint(options: {\n outFile: string;\n queryFolder?: string;\n metricViewsFolder?: string;\n warehouseId: string;\n noCache?: boolean;\n mode?: PreflightMode;\n mvOutFile?: string;\n metricFetcher?: DescribeFetcher;\n}) {\n const {\n outFile,\n queryFolder,\n warehouseId,\n noCache,\n mode = \"non-blocking\",\n mvOutFile,\n metricFetcher,\n } = options;\n\n // Metric config lives in `config/metric-views/`, a sibling of the queries\n // folder. Prefer the explicit option; otherwise derive the sibling of\n // `queryFolder` so callers that pass only `queryFolder` keep emitting metric\n // types. Undefined when neither is given → the metric path stays dormant.\n const metricViewsFolder =\n options.metricViewsFolder ??\n (queryFolder ? path.resolve(queryFolder, \"..\", \"metric-views\") : undefined);\n const resolvedMvFile =\n mvOutFile ?? path.join(path.dirname(outFile), METRIC_TYPES_FILE);\n\n const projectRoot = resolveProjectRoot(outFile);\n\n logger.debug(\"Starting type generation...\");\n\n let queryRegistry: QuerySchema[] = [];\n let syntaxErrors: QuerySyntaxError[] = [];\n // Deterministic fatal errors only (404/400).\n let fatalErrors: QueryFatalError[] = [];\n let queryHadEnvironmentalFailure = false;\n let metricsHadEnvironmentalFailure = false;\n // Track the coarse cause of the environmental failure for the warning message.\n let environmentalCause: \"auth\" | \"unreachable\" | \"unavailable\" | undefined;\n\n if (queryFolder) {\n const result = await generateQueriesFromDescribe(queryFolder, warehouseId, {\n noCache,\n mode,\n });\n queryRegistry = result.schemas;\n syntaxErrors = result.syntaxErrors ?? [];\n fatalErrors = result.fatalErrors ?? [];\n queryHadEnvironmentalFailure = result.hadEnvironmentalFailure ?? false;\n environmentalCause =\n environmentalCause ?? result.environmentalCause ?? undefined;\n }\n\n const typeDeclarations = generateTypeDeclarations(queryRegistry);\n\n // In blocking mode, never overwrite committed types with a schema explicitly\n // marked degraded. Leave the committed .d.ts as the fallback of record.\n // Non-blocking mode always writes.\n const hasAnyDegradedQuery = queryRegistry.some(isQueryDegraded);\n if (mode === \"blocking\" && hasAnyDegradedQuery) {\n // A degraded schema always participates in the committed-types gate. Keep\n // this invariant next to write suppression so a new producer cannot update\n // one decision without the other.\n queryHadEnvironmentalFailure = true;\n environmentalCause = environmentalCause ?? \"unavailable\";\n }\n const shouldWriteQueries = mode !== \"blocking\" || !hasAnyDegradedQuery;\n\n if (shouldWriteQueries) {\n await fs.mkdir(path.dirname(outFile), { recursive: true });\n await fs.writeFile(outFile, typeDeclarations, \"utf-8\");\n }\n\n // Metric-view types: emit whenever a metric-views folder is resolved (gated\n // on the metric config's own dir, NOT the queries folder — an app can declare\n // metric views without any `.sql` queries). `syncMetricViewsTypes` still\n // returns `noConfig` when the folder holds no `definitions.json`.\n if (metricViewsFolder) {\n let mvResult: SyncMetricViewsTypesResult;\n try {\n mvResult = await syncMetricViewsTypes({\n metricViewsFolder,\n warehouseId,\n metricOutFile: resolvedMvFile,\n cache: !noCache,\n metricFetcher,\n mode,\n // In blocking mode, never overwrite committed metric types with a\n // degraded result — including on runs that go on to throw, so a failing\n // build leaves the committed type file intact. Non-blocking always writes.\n suppressDegradedWrite: mode === \"blocking\",\n });\n } catch (configError) {\n // syncMetricViewsTypes only throws for a malformed definitions.json — re-throw as a message-only TypegenFatalError.\n throw new TypegenFatalError(\n [\n {\n name: \"config/metric-views/definitions.json\",\n message: getErrorDiagnostic(configError),\n },\n ],\n warehouseId,\n );\n }\n\n // Deleted/deleting-warehouse fatal preflight (blocking mode only);\n // empty (no-op) when definitions.json is absent or in non-blocking mode.\n // Only deterministic fatals are recorded in fatalErrors.\n for (const fe of mvResult.fatalErrors) {\n fatalErrors.push(fe);\n }\n\n metricsHadEnvironmentalFailure =\n (mvResult.hadEnvironmentalFailure ?? false) ||\n (mode === \"blocking\" && hasAnyDegradedMetrics(mvResult.schemas));\n environmentalCause =\n environmentalCause ?? mvResult.environmentalCause ?? undefined;\n\n // Blocking (`--wait` / prod Vite) escalates only deterministic per-key\n // DESCRIBE failures. Transient connectivity failures are already recorded\n // as environmental by syncMetricViewsTypes and fall through to the\n // committed-types gate below.\n if (mode === \"blocking\") {\n for (const failure of mvResult.failures) {\n if (failure.transient) continue;\n fatalErrors.push({\n name: failure.key,\n message: `metric view ${failure.key} (${failure.source}) could not be described: ${failure.reason}`,\n });\n }\n }\n }\n\n await removeOldGeneratedTypes(projectRoot, \"appKitTypes.d.ts\");\n await migrateProjectConfig(projectRoot);\n\n // Deterministic failures (SQL syntax errors or 404/400 HTTP) always crash regardless of mode.\n if (syntaxErrors.length > 0) {\n throw new TypegenSyntaxError(syntaxErrors, warehouseId, fatalErrors);\n }\n if (fatalErrors.length > 0) {\n throw new TypegenFatalError(fatalErrors, warehouseId);\n }\n\n if (\n mode === \"blocking\" &&\n (queryHadEnvironmentalFailure || metricsHadEnvironmentalFailure)\n ) {\n const missingCommittedTypes: string[] = [];\n if (queryHadEnvironmentalFailure && !existsSync(outFile)) {\n missingCommittedTypes.push(path.basename(outFile));\n }\n if (metricsHadEnvironmentalFailure && !existsSync(resolvedMvFile)) {\n missingCommittedTypes.push(path.basename(resolvedMvFile));\n }\n\n if (missingCommittedTypes.length === 0) {\n // Committed types present: emit loud warning and exit 0.\n const warningMessage = determineWarningMessage(\n environmentalCause ?? \"unavailable\",\n warehouseId,\n );\n logger.warn(warningMessage);\n } else {\n throw new TypegenFatalError(\n [\n {\n name: \"type-generator\",\n message: `Warehouse ${warehouseId} could not provide schemas and the required committed type ${plural(missingCommittedTypes.length, \"artifact is\", \"artifacts are\")} missing: ${missingCommittedTypes.join(\", \")}. Run 'npx @databricks/appkit generate-types --wait' locally and commit the generated type files.`,\n },\n ],\n warehouseId,\n );\n }\n }\n\n logger.debug(\"Type generation complete!\");\n}\n\n/**\n * Result of a {@link syncMetricViewsTypes} run, returned to the caller (the CLI\n * directly, or {@link generateFromEntryPoint} which delegates to it) so it can\n * report what happened and decide its exit code.\n */\nexport interface SyncMetricViewsTypesResult {\n metricOutFile?: string;\n schemas: MetricSchema[];\n failures: MetricSyncFailure[];\n /**\n * `true` when no `definitions.json` was found in the metric-views folder, so\n * nothing was synced.\n */\n noConfig: boolean;\n /**\n * Per-key fatal preflight errors (empty except in the `blocking`-mode\n * deleted/deleting-warehouse and deterministic-preflight-failure cases).\n * {@link generateFromEntryPoint} surfaces these by throwing\n * {@link TypegenFatalError}; when the run also degraded, `suppressDegradedWrite`\n * means no artifact was written and the committed types stand. A `\"describe-now\"`\n * run sets no blocking preflight, so for that mode this is always empty.\n * ONLY contains deterministic failures (404/400).\n */\n fatalErrors: Array<{ name: string; message: string }>;\n /**\n * `true` when an environmental failure occurred in blocking mode (auth, connectivity,\n * DELETED/DELETING, wait-timeout, or other unrecognized failures). Used by\n * {@link generateFromEntryPoint} to decide whether to apply the has-types gate.\n * Does not directly cause a throw — the gate decides that. Always false in\n * non-blocking or describe-now mode.\n */\n hadEnvironmentalFailure?: boolean;\n /**\n * Coarse cause label for the environmental failure, one of \"auth\" (401/403),\n * \"unreachable\" (connectivity), or \"unavailable\" (other). Only set when\n * hadEnvironmentalFailure is true; used by the warning message.\n */\n environmentalCause?: \"auth\" | \"unreachable\" | \"unavailable\";\n}\n\n/**\n * Unified metric-view type-generation pipeline behind {@link\n * generateFromEntryPoint}'s metric section (which forwards its\n * `\"non-blocking\"`/`\"blocking\"` mode). Also directly callable with the default\n * `\"describe-now\"` mode for a focused, always-converge metric refresh.\n *\n *\n * @param options.metricViewsFolder - folder that holds `definitions.json` (`<root>/config/metric-views`).\n * @param options.warehouseId - SQL warehouse used for `DESCRIBE TABLE EXTENDED`.\n * @param options.metricOutFile - output path for the MetricRegistry `.ts` (the\n * generated source carries both the `declare module` augmentation and the\n * runtime `metricViewsMetadata` const).\n * @param options.cache - cache toggle, default ON. Only `cache === false` disables it (so `undefined`/`true` keep caching).\n * @param options.metricFetcher - optional injected {@link DescribeFetcher}\n * @param options.mode - preflight/gate policy, default `\"describe-now\"`. When set to `\"blocking\"`,\n * metric-view `.ts` writes are suppressed if any metric is degraded (to preserve committed files).\n * @param options.suppressDegradedWrite - when true (only in `mode === \"blocking\"` context), skip\n * the metricOutFile write if any metric schema has `degraded === true`. Used to prevent\n * overwriting committed type files with degraded types in blocking mode.\n */\nexport async function syncMetricViewsTypes(options: {\n metricViewsFolder: string;\n warehouseId: string;\n metricOutFile: string;\n cache?: boolean;\n metricFetcher?: DescribeFetcher;\n mode?: \"describe-now\" | \"non-blocking\" | \"blocking\";\n suppressDegradedWrite?: boolean;\n}): Promise<SyncMetricViewsTypesResult> {\n const {\n metricViewsFolder,\n warehouseId,\n metricOutFile,\n cache: cacheEnabled,\n metricFetcher,\n mode = \"describe-now\",\n suppressDegradedWrite,\n } = options;\n\n // Only `cache === false` disables caching; `undefined`/`true` keep it on.\n const noCache = cacheEnabled === false;\n\n const mvConfig = await readMetricConfig(metricViewsFolder);\n if (!mvConfig) {\n // No definitions.json — additive path stays dormant. The CLI turns this\n // into a friendly \"nothing to sync\" message and exits 0;\n // generateFromEntryPoint simply ignores `noConfig`.\n return { schemas: [], failures: [], fatalErrors: [], noConfig: true };\n }\n\n const resolution = resolveMetricConfig(mvConfig);\n\n const fatalErrors: Array<{ name: string; message: string }> = [];\n\n // Load the shared typegen cache and copy its `metrics` section into a null-prototype map.\n const cache = await loadCache();\n const mvCacheSection: Record<string, MetricCacheEntry> = Object.create(null);\n if (!noCache && cache.metrics) {\n for (const key of Object.keys(cache.metrics)) {\n mvCacheSection[key] = cache.metrics[key];\n }\n }\n\n // Partition BEFORE any gate/preflight decision: a hit (a structurally valid,\n // hash-matching, NON-degraded cached entry) is served from cache no matter\n // what the warehouse is doing. The cache only ever holds successful describes\n // (a degraded outcome is never persisted — see the write block below), so the\n // `degraded !== true` guard is normally moot; it also defends against a stale\n // degraded entry left by an older writer, which re-describes instead of\n // serving. Everything else (new, edited, unrevivable, or degraded) is eligible\n // for DESCRIBE, so a fully-warm pass makes zero warehouse calls and constructs\n // zero clients. Mirrors the query path: only a good result is cache-servable.\n const hitSchemas = new Map<string, MetricSchema>();\n const describeNeeded: typeof resolution.entries = [];\n for (const entry of resolution.entries) {\n const prior = mvCacheSection[entry.key];\n if (\n prior !== undefined &&\n isRevivableMetricCacheEntry(prior) &&\n prior.hash === metricCacheHash(entry.source, entry.lane) &&\n prior.schema.degraded !== true\n ) {\n hitSchemas.set(entry.key, prior.schema);\n } else {\n describeNeeded.push(entry);\n }\n }\n\n let mvClient: WorkspaceClient | undefined;\n const getMvClient = (): WorkspaceClient => {\n mvClient ??= createWorkspaceClient();\n return mvClient;\n };\n\n // Blocking-mode preflight: ensure the warehouse is running before the MV DESCRIBE\n // batch (probe → decide → wait / start+wait; only DELETED/DELETING is fatal). Two softenings vs the query preflight: a failed probe and a timed-out wait are NOT fatal here — we fall through to syncMetrics, which classifies a still-not-ready warehouse as degraded rather than failing the build. Skipped for `describe-now`/`non-blocking` (only `mode === \"blocking\"` enters here).\n let preflightFatalMessage: string | undefined;\n let hadEnvironmentalFailure = false;\n let environmentalCause: \"auth\" | \"unreachable\" | \"unavailable\" | undefined;\n if (\n mode === \"blocking\" &&\n metricFetcher === undefined &&\n describeNeeded.length > 0\n ) {\n try {\n const state = await getWarehouseState(getMvClient(), warehouseId);\n const decision = decidePreflight(state, mode);\n if (decision === \"fatal\") {\n preflightFatalMessage = `warehouse ${warehouseId} is ${state}`;\n // State-based DELETED/DELETING is environmental, not deterministic.\n hadEnvironmentalFailure = true;\n environmentalCause = \"unavailable\";\n } else if (decision === \"startWaitProceed\") {\n // treatStoppedAsTransient rides out the stale pre-start STOPPED/STOPPING\n // reading, same as the query preflight.\n await startWarehouse(getMvClient(), warehouseId);\n const settled = await waitUntilRunning(getMvClient(), warehouseId, {\n maxMs: MV_PREFLIGHT_WAIT_MAX_MS,\n treatStoppedAsTransient: true,\n });\n if (settled !== \"RUNNING\") {\n // With treatStoppedAsTransient, a non-RUNNING resolve is exactly\n // DELETED/DELETING — the warehouse was deleted while we waited.\n preflightFatalMessage = `warehouse ${warehouseId} is ${settled}`;\n hadEnvironmentalFailure = true;\n }\n } else if (decision === \"waitThenProceed\") {\n const settled = await waitUntilRunning(getMvClient(), warehouseId, {\n maxMs: MV_PREFLIGHT_WAIT_MAX_MS,\n });\n if (settled === \"DELETED\" || settled === \"DELETING\") {\n // Deleted mid-wait: fatal. Environmental (state-based).\n preflightFatalMessage = `warehouse ${warehouseId} is ${settled}`;\n hadEnvironmentalFailure = true;\n }\n }\n } catch (err) {\n // Connectivity blip: fall through to syncMetrics, whose DESCRIBEs degrade\n // a not-ready / unreachable warehouse rather than throwing.\n if (!isConnectivityError(err)) {\n // Deterministic failures become fatal errors; environmental failures\n // degrade for the committed-types gate.\n const classification = classifyBlockingFailure(err);\n if (classification === \"deterministic\") {\n preflightFatalMessage = `warehouse ${warehouseId}: ${getErrorDiagnostic(err)}`;\n } else {\n preflightFatalMessage = `warehouse ${warehouseId}: ${getErrorDiagnostic(err)}`;\n hadEnvironmentalFailure = true;\n environmentalCause = classifyEnvironmentalCause(err);\n }\n }\n }\n }\n\n let gateState: WarehouseState | undefined;\n let describeNow =\n metricFetcher !== undefined ||\n mode !== \"non-blocking\" ||\n describeNeeded.length === 0;\n if (!describeNow) {\n try {\n gateState = await probeWarehouseState(getMvClient, warehouseId);\n } catch (err) {\n preflightFatalMessage = `warehouse ${warehouseId}: ${getErrorDiagnostic(err)}`;\n }\n describeNow = gateState === \"RUNNING\";\n }\n\n let described: MetricSchema[];\n let failures: MetricSyncFailure[] = [];\n if (preflightFatalMessage !== undefined) {\n // Environmental failures degrade for the committed-types gate; deterministic\n // failures record one fatal error per key. Degraded schemas are not cached.\n described = describeNeeded.map(emptyMetricSchema);\n if (!hadEnvironmentalFailure) {\n for (const entry of describeNeeded) {\n fatalErrors.push({ name: entry.key, message: preflightFatalMessage });\n }\n }\n } else if (describeNeeded.length === 0) {\n // Nothing left to describe — every configured key was a cache hit.\n // syncMetrics would be a no-op (and building its fetcher would construct a\n // client for nothing); artifacts regenerate from cache.\n described = [];\n } else if (describeNow) {\n const fetcher =\n metricFetcher ??\n createWorkspaceDescribeFetcher(getMvClient(), warehouseId);\n ({ schemas: described, failures } = await syncMetrics(\n { entries: describeNeeded },\n fetcher,\n ));\n\n // Surface DESCRIBE failures loudly: a misconfigured definitions.json would\n // otherwise silently ship an empty entry that the runtime fail-closed gate\n // 503s in production. syncMetrics is log-free; this caller is the single\n // owner of failure logging.\n if (failures.length > 0) {\n for (const f of failures) {\n logger.warn(\n \"metric sync failed for %s (%s): %s\",\n f.key,\n f.source,\n f.reason,\n );\n }\n }\n\n // A rejected DESCRIBE with a connectivity signal is expected to recover on\n // a later pass. In blocking mode, route it through the same committed-types\n // gate as preflight outages instead of treating it as a configuration error.\n if (mode === \"blocking\" && failures.some((failure) => failure.transient)) {\n hadEnvironmentalFailure = true;\n environmentalCause = environmentalCause ?? \"unreachable\";\n }\n\n // Degraded-but-not-failed keys: the warehouse answered with a non-terminal\n // state (stopped / cold-starting), so their schemas are unknown.\n const failedKeys = new Set(failures.map((f) => f.key));\n const degradedKeys = described\n .filter((s) => s.degraded && !failedKeys.has(s.key))\n .map((s) => s.key);\n if (degradedKeys.length > 0) {\n logger.info(\n \"Warehouse %s did not return schemas for %d metric view(s) (%s) — wrote degraded metric types (permissive); they will refresh once the warehouse is available.\",\n warehouseId,\n degradedKeys.length,\n degradedKeys.join(\", \"),\n );\n hadEnvironmentalFailure = true;\n environmentalCause = environmentalCause ?? \"unavailable\";\n }\n } else {\n // Un-probed DESCRIBEs emit degraded schemas; cache hits remain last-known-good.\n described = describeNeeded.map(emptyMetricSchema);\n logger.info(\n \"Warehouse %s is not running — wrote degraded metric types (permissive) for %d metric view(s) (%s); they will refresh once the warehouse is available.\",\n warehouseId,\n describeNeeded.length,\n describeNeeded.map((e) => e.key).join(\", \"),\n );\n hadEnvironmentalFailure = true;\n environmentalCause = environmentalCause ?? \"unavailable\";\n }\n\n // Cache only successful schema results for describe-needed keys; remove stale cache for degraded ones.\n for (let i = 0; i < describeNeeded.length; i++) {\n // syncMetrics return one schema per entry in entry order, so described[i] always belongs to describeNeeded[i].\n const entry = describeNeeded[i];\n if (described[i].degraded === true) {\n delete mvCacheSection[entry.key];\n continue;\n }\n mvCacheSection[entry.key] = {\n hash: metricCacheHash(entry.source, entry.lane),\n schema: described[i],\n // Vestigial, mirrors the query path's only cache write (always false): a\n // persisted entry is by construction a good result, so it never needs a\n // re-describe flag. Kept for on-disk shape compatibility with existing\n // version-3 caches (isRevivableMetricCacheEntry gates on a boolean).\n retry: false,\n };\n }\n\n // Prune entries whose key is no longer configured\n const configuredKeys = new Set(resolution.entries.map((e) => e.key));\n let prunedCount = 0;\n for (const key of Object.keys(mvCacheSection)) {\n if (!configuredKeys.has(key)) {\n delete mvCacheSection[key];\n prunedCount++;\n }\n }\n\n // Save when this pass produced outcomes, bypassed the cache, or pruned.\n if (describeNeeded.length > 0 || noCache || prunedCount > 0) {\n cache.metrics = mvCacheSection;\n await saveCache(cache);\n }\n\n // Merge cached hits with fresh results back into config order.\n const describedByKey = new Map<string, MetricSchema>();\n for (const schema of described) {\n describedByKey.set(schema.key, schema);\n }\n const schemas = resolution.entries.map((entry) => {\n const schema = hitSchemas.get(entry.key) ?? describedByKey.get(entry.key);\n if (schema !== undefined) return schema;\n logger.warn(\n \"no schema resolved for metric key %s — emitting degraded types (should not happen)\",\n entry.key,\n );\n return emptyMetricSchema(entry);\n });\n\n // Same anti-clobber rule as the query path: when suppressDegradedWrite is set\n // (blocking mode), skip the write if any metric degraded, preserving the\n // committed metric-views.ts. Non-blocking mode always writes.\n const shouldWriteMetrics =\n !suppressDegradedWrite || !hasAnyDegradedMetrics(schemas);\n\n if (shouldWriteMetrics) {\n await fs.mkdir(path.dirname(metricOutFile), { recursive: true });\n await fs.writeFile(\n metricOutFile,\n generateMetricTypeDeclarations(schemas),\n \"utf-8\",\n );\n }\n // Sweep the ambient `metric-views.d.ts` a pre-`.ts` version left behind,\n // which would otherwise duplicate the augmentation the new `.ts` emits.\n // Skipped unless the replacement was actually written, so a degraded\n // blocking pass leaves an app's only committed metric types in place.\n if (\n metricOutFile.endsWith(\".ts\") &&\n !metricOutFile.endsWith(\".d.ts\") &&\n existsSync(metricOutFile)\n ) {\n const staleDts = `${metricOutFile.slice(0, -\".ts\".length)}.d.ts`;\n try {\n await fs.unlink(staleDts);\n logger.debug(\"Removed stale generated types at %s\", staleDts);\n } catch {\n // No stale sibling — nothing to clean up.\n }\n }\n\n logger.debug(\n \"Wrote MetricRegistry augmentation for %d metric(s)%s\",\n schemas.length,\n failures.length > 0 ? ` (${failures.length} failure(s))` : \"\",\n );\n\n return {\n metricOutFile,\n schemas,\n failures,\n fatalErrors,\n noConfig: false,\n hadEnvironmentalFailure:\n mode === \"blocking\" ? hadEnvironmentalFailure : undefined,\n environmentalCause:\n mode === \"blocking\" && hadEnvironmentalFailure\n ? environmentalCause\n : undefined,\n };\n}\n\n// Rolldown tree-shaking only preserves \"own exports\" (locally defined) — not re-exports.\n// A local binding ensures the serving vite plugin's import keeps this in the dependency graph,\n// mirroring how generateFromEntryPoint (also defined here) is preserved via the analytics vite plugin.\nexport const generateServingTypes = generateServingTypesImpl;\n\n// Re-export the mv-registry types so consumers (CLI, the type-generator\n// .d.ts shim in `packages/shared`) can pick them up from this entry point —\n// the .d.ts shim documents these as part of the package's public surface.\nexport type {\n MetricColumnMetadata,\n MetricLane,\n MetricSchema,\n MetricSyncFailure,\n MetricSyncResult,\n};\n\nexport const TYPES_DIR = \"appkit-types\";\nexport const ANALYTICS_TYPES_FILE = \"analytics.d.ts\";\nexport const SERVING_TYPES_FILE = \"serving.d.ts\";\nexport const METRIC_TYPES_FILE = \"metric-views.ts\";\n"],"mappings":"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"}
|
|
@@ -75,25 +75,54 @@ function renderDegradedMetricEntry(schema) {
|
|
|
75
75
|
};
|
|
76
76
|
}`;
|
|
77
77
|
}
|
|
78
|
+
function metadataFields(col, includeTimeGrain = false) {
|
|
79
|
+
const fields = [["type", JSON.stringify(col.type)]];
|
|
80
|
+
const optionalFields = [
|
|
81
|
+
["display_name", col.displayName],
|
|
82
|
+
["format", col.format],
|
|
83
|
+
["description", col.description]
|
|
84
|
+
];
|
|
85
|
+
for (const [name, value] of optionalFields) if (value) fields.push([name, JSON.stringify(value)]);
|
|
86
|
+
if (includeTimeGrain && col.timeGrains && col.timeGrains.length > 0) {
|
|
87
|
+
const grainTuple = col.timeGrains.map((g) => JSON.stringify(g)).join(", ");
|
|
88
|
+
fields.push(["time_grain", `readonly [${grainTuple}]`]);
|
|
89
|
+
}
|
|
90
|
+
return fields;
|
|
91
|
+
}
|
|
78
92
|
function renderMetadataMap(cols, indent, includeTimeGrain = false) {
|
|
79
93
|
if (cols.length === 0) return "Record<string, never>";
|
|
80
94
|
return `{
|
|
81
95
|
${cols.map((col) => {
|
|
82
|
-
const
|
|
83
|
-
if (col.displayName) fields.push(`display_name: ${JSON.stringify(col.displayName)}`);
|
|
84
|
-
if (col.format) fields.push(`format: ${JSON.stringify(col.format)}`);
|
|
85
|
-
if (col.description) fields.push(`description: ${JSON.stringify(col.description)}`);
|
|
86
|
-
if (includeTimeGrain && col.timeGrains && col.timeGrains.length > 0) {
|
|
87
|
-
const grainTuple = col.timeGrains.map((g) => JSON.stringify(g)).join(", ");
|
|
88
|
-
fields.push(`time_grain: readonly [${grainTuple}]`);
|
|
89
|
-
}
|
|
90
|
-
const fieldsBlock = fields.map((f) => `${indent} ${f}`).join(";\n");
|
|
96
|
+
const fieldsBlock = metadataFields(col, includeTimeGrain).map(([name, value]) => `${indent} ${name}: ${value}`).join(";\n");
|
|
91
97
|
return `${indent}${JSON.stringify(col.name)}: {
|
|
92
98
|
${fieldsBlock};
|
|
93
99
|
${indent}}`;
|
|
94
100
|
}).join(";\n")};
|
|
95
101
|
}`;
|
|
96
102
|
}
|
|
103
|
+
function renderMetadataValueField(col) {
|
|
104
|
+
return `{ ${metadataFields(col).map(([name, value]) => `${name}: ${value}`).join(", ")} }`;
|
|
105
|
+
}
|
|
106
|
+
function renderMetadataValueMap(cols, indent) {
|
|
107
|
+
if (cols.length === 0) return "{}";
|
|
108
|
+
return `{
|
|
109
|
+
${cols.map((col) => `${indent} ${JSON.stringify(col.name)}: ${renderMetadataValueField(col)}`).join(",\n")},
|
|
110
|
+
${indent}}`;
|
|
111
|
+
}
|
|
112
|
+
function renderMetricViewsMetadata(schemas) {
|
|
113
|
+
if (schemas.length === 0) return "export const metricViewsMetadata = {} as const;\n";
|
|
114
|
+
return `export const metricViewsMetadata = {
|
|
115
|
+
${schemas.map((schema) => {
|
|
116
|
+
const measures = renderMetadataValueMap(schema.measures, " ");
|
|
117
|
+
const dimensions = renderMetadataValueMap(schema.dimensions, " ");
|
|
118
|
+
return ` ${JSON.stringify(schema.key)}: {
|
|
119
|
+
measures: ${measures},
|
|
120
|
+
dimensions: ${dimensions},
|
|
121
|
+
}`;
|
|
122
|
+
}).join(",\n")},
|
|
123
|
+
} as const;
|
|
124
|
+
`;
|
|
125
|
+
}
|
|
97
126
|
function renderMetricRegistry(schemas) {
|
|
98
127
|
if (schemas.length === 0) return `declare module "@databricks/appkit-ui/react" {
|
|
99
128
|
interface MetricRegistry {}
|
|
@@ -106,11 +135,20 @@ ${schemas.map(renderMetricEntry).join(";\n")};
|
|
|
106
135
|
}
|
|
107
136
|
`;
|
|
108
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Build the full metric-views.ts file from a list of metric schemas.
|
|
140
|
+
*
|
|
141
|
+
* The header must stay a type-only `import type {} from`: it anchors the module
|
|
142
|
+
* so the augmentation resolves while compiling to zero runtime code, whereas a
|
|
143
|
+
* bare `import "@databricks/appkit-ui/react"` would execute the client package
|
|
144
|
+
* entry on the Node server.
|
|
145
|
+
*/
|
|
109
146
|
function generateMetricTypeDeclarations(schemas) {
|
|
110
147
|
return `// Auto-generated by AppKit - DO NOT EDIT
|
|
111
148
|
// Generated by 'npx @databricks/appkit generate-types' or Vite plugin during build
|
|
112
|
-
import "@databricks/appkit-ui/react";
|
|
113
|
-
${renderMetricRegistry(schemas)}
|
|
149
|
+
import type {} from "@databricks/appkit-ui/react";
|
|
150
|
+
${renderMetricRegistry(schemas)}
|
|
151
|
+
${renderMetricViewsMetadata(schemas)}`;
|
|
114
152
|
}
|
|
115
153
|
|
|
116
154
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-types.js","names":[],"sources":["../../../src/type-generator/mv-registry/render-types.ts"],"sourcesContent":["import type { MetricColumnMetadata, MetricSchema } from \"./types\";\n\n/**\n * @todo unify with query-registry.ts\n * Map a Databricks SQL type to a TypeScript primitive.\n * Centralized here (not imported from query-registry) so this module\n * stays self-contained.\n */\nfunction tsTypeFor(sqlType: string): string {\n const normalized = sqlType\n .toUpperCase()\n .replace(/\\(.*\\)$/, \"\")\n .replace(/<.*>$/, \"\")\n .split(\" \")[0];\n\n switch (normalized) {\n case \"BOOLEAN\":\n return \"boolean\";\n case \"TINYINT\":\n case \"SMALLINT\":\n case \"INT\":\n case \"INTEGER\":\n case \"BIGINT\":\n case \"FLOAT\":\n case \"DOUBLE\":\n case \"DECIMAL\":\n case \"NUMERIC\":\n return \"number\";\n default:\n return \"string\";\n }\n}\n\n// Render a MetricRegistry interface entry from a MetricSchema.\nfunction renderMetricEntry(schema: MetricSchema): string {\n if (schema.degraded) {\n return renderDegradedMetricEntry(schema);\n }\n const indent = \" \";\n const colsBlock = (cols: MetricColumnMetadata[]): string => {\n if (cols.length === 0) return \"Record<string, never>\";\n const fields = cols\n .map((col) => {\n const grainComment = col.timeGrains?.length\n ? ` @timeGrain ${col.timeGrains.join(\"|\")}`\n : \"\";\n return `${indent}/** @sqlType ${col.type.replace(/\\*\\//g, \"* /\")}${grainComment} */\n${indent}${JSON.stringify(col.name)}: ${tsTypeFor(col.type)}`;\n })\n .join(\";\\n\");\n return `{\n${fields};\n }`;\n };\n const unionOf = (keys: string[]): string =>\n keys.length > 0 ? keys.join(\" | \") : \"never\";\n\n const measuresBlock = colsBlock(schema.measures);\n const dimensionsBlock = colsBlock(schema.dimensions);\n const measureUnion = unionOf(\n schema.measures.map((m) => JSON.stringify(m.name)),\n );\n const dimensionUnion = unionOf(\n schema.dimensions.map((d) => JSON.stringify(d.name)),\n );\n\n const timeGrainSet = new Set<string>();\n for (const d of schema.dimensions) {\n for (const g of d.timeGrains ?? []) {\n timeGrainSet.add(g);\n }\n }\n const timeGrainUnion =\n timeGrainSet.size > 0\n ? [...timeGrainSet]\n .sort()\n .map((g) => JSON.stringify(g))\n .join(\" | \")\n : \"never\";\n\n const measureMetadata = renderMetadataMap(schema.measures, indent);\n const dimensionMetadata = renderMetadataMap(schema.dimensions, indent, true);\n\n return ` ${JSON.stringify(schema.key)}: {\n key: ${JSON.stringify(schema.key)};\n source: ${JSON.stringify(schema.source)};\n lane: ${JSON.stringify(schema.lane)};\n measures: ${measuresBlock};\n dimensions: ${dimensionsBlock};\n measureKeys: ${measureUnion};\n dimensionKeys: ${dimensionUnion};\n timeGrains: ${timeGrainUnion};\n metadata: {\n measures: ${measureMetadata};\n dimensions: ${dimensionMetadata};\n };\n }`;\n}\n\n// Render the permissive (\"degraded-open\") entry for a schema the warehouse could not describe.\nfunction renderDegradedMetricEntry(schema: MetricSchema): string {\n return ` /** Degraded: schema unavailable at type-generation time — permissive types until a successful DESCRIBE refreshes them. */\n ${JSON.stringify(schema.key)}: {\n key: ${JSON.stringify(schema.key)};\n source: ${JSON.stringify(schema.source)};\n lane: ${JSON.stringify(schema.lane)};\n measures: Record<string, unknown>;\n dimensions: Record<string, unknown>;\n measureKeys: string;\n dimensionKeys: string;\n timeGrains: string;\n metadata: {\n measures: Record<string, never>;\n dimensions: Record<string, never>;\n };\n }`;\n}\n\n// Render the type-level shape of a column's semantic-metadata map\n// for the `metadata` field of a MetricRegistry entry.\nfunction renderMetadataMap(\n cols: MetricColumnMetadata[],\n indent: string,\n includeTimeGrain = false,\n): string {\n if (cols.length === 0) return \"Record<string, never>\";\n\n const inner = cols\n .map((col) => {\n const fields: string[] = [`type: ${JSON.stringify(col.type)}`];\n if (col.displayName) {\n fields.push(`display_name: ${JSON.stringify(col.displayName)}`);\n }\n if (col.format) {\n fields.push(`format: ${JSON.stringify(col.format)}`);\n }\n if (col.description) {\n fields.push(`description: ${JSON.stringify(col.description)}`);\n }\n if (includeTimeGrain && col.timeGrains && col.timeGrains.length > 0) {\n const grainTuple = col.timeGrains\n .map((g) => JSON.stringify(g))\n .join(\", \");\n fields.push(`time_grain: readonly [${grainTuple}]`);\n }\n const fieldsBlock = fields.map((f) => `${indent} ${f}`).join(\";\\n\");\n return `${indent}${JSON.stringify(col.name)}: {\n${fieldsBlock};\n${indent}}`;\n })\n .join(\";\\n\");\n\n return `{\n${inner};\n }`;\n}\n\n// Render the augmentation block for the appkit-ui MetricRegistry interface.\nfunction renderMetricRegistry(schemas: MetricSchema[]): string {\n if (schemas.length === 0) {\n return `declare module \"@databricks/appkit-ui/react\" {\n interface MetricRegistry {}\n}\n`;\n }\n const entries = schemas.map(renderMetricEntry).join(\";\\n\");\n return `declare module \"@databricks/appkit-ui/react\" {\n interface MetricRegistry {\n${entries};\n }\n}\n`;\n}\n\n// Build the full metric-views.d.ts file from a list of metric schemas.\nexport function generateMetricTypeDeclarations(\n schemas: MetricSchema[],\n): string {\n return `// Auto-generated by AppKit - DO NOT EDIT\n// Generated by 'npx @databricks/appkit generate-types' or Vite plugin during build\nimport \"@databricks/appkit-ui/react\";\n${renderMetricRegistry(schemas)}`;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"render-types.js","names":[],"sources":["../../../src/type-generator/mv-registry/render-types.ts"],"sourcesContent":["import type { MetricColumnMetadata, MetricSchema } from \"./types\";\n\n/**\n * @todo unify with query-registry.ts\n * Map a Databricks SQL type to a TypeScript primitive.\n * Centralized here (not imported from query-registry) so this module\n * stays self-contained.\n */\nfunction tsTypeFor(sqlType: string): string {\n const normalized = sqlType\n .toUpperCase()\n .replace(/\\(.*\\)$/, \"\")\n .replace(/<.*>$/, \"\")\n .split(\" \")[0];\n\n switch (normalized) {\n case \"BOOLEAN\":\n return \"boolean\";\n case \"TINYINT\":\n case \"SMALLINT\":\n case \"INT\":\n case \"INTEGER\":\n case \"BIGINT\":\n case \"FLOAT\":\n case \"DOUBLE\":\n case \"DECIMAL\":\n case \"NUMERIC\":\n return \"number\";\n default:\n return \"string\";\n }\n}\n\n// Render a MetricRegistry interface entry from a MetricSchema.\nfunction renderMetricEntry(schema: MetricSchema): string {\n if (schema.degraded) {\n return renderDegradedMetricEntry(schema);\n }\n const indent = \" \";\n const colsBlock = (cols: MetricColumnMetadata[]): string => {\n if (cols.length === 0) return \"Record<string, never>\";\n const fields = cols\n .map((col) => {\n const grainComment = col.timeGrains?.length\n ? ` @timeGrain ${col.timeGrains.join(\"|\")}`\n : \"\";\n return `${indent}/** @sqlType ${col.type.replace(/\\*\\//g, \"* /\")}${grainComment} */\n${indent}${JSON.stringify(col.name)}: ${tsTypeFor(col.type)}`;\n })\n .join(\";\\n\");\n return `{\n${fields};\n }`;\n };\n const unionOf = (keys: string[]): string =>\n keys.length > 0 ? keys.join(\" | \") : \"never\";\n\n const measuresBlock = colsBlock(schema.measures);\n const dimensionsBlock = colsBlock(schema.dimensions);\n const measureUnion = unionOf(\n schema.measures.map((m) => JSON.stringify(m.name)),\n );\n const dimensionUnion = unionOf(\n schema.dimensions.map((d) => JSON.stringify(d.name)),\n );\n\n const timeGrainSet = new Set<string>();\n for (const d of schema.dimensions) {\n for (const g of d.timeGrains ?? []) {\n timeGrainSet.add(g);\n }\n }\n const timeGrainUnion =\n timeGrainSet.size > 0\n ? [...timeGrainSet]\n .sort()\n .map((g) => JSON.stringify(g))\n .join(\" | \")\n : \"never\";\n\n const measureMetadata = renderMetadataMap(schema.measures, indent);\n const dimensionMetadata = renderMetadataMap(schema.dimensions, indent, true);\n\n return ` ${JSON.stringify(schema.key)}: {\n key: ${JSON.stringify(schema.key)};\n source: ${JSON.stringify(schema.source)};\n lane: ${JSON.stringify(schema.lane)};\n measures: ${measuresBlock};\n dimensions: ${dimensionsBlock};\n measureKeys: ${measureUnion};\n dimensionKeys: ${dimensionUnion};\n timeGrains: ${timeGrainUnion};\n metadata: {\n measures: ${measureMetadata};\n dimensions: ${dimensionMetadata};\n };\n }`;\n}\n\n// Render the permissive (\"degraded-open\") entry for a schema the warehouse could not describe.\nfunction renderDegradedMetricEntry(schema: MetricSchema): string {\n return ` /** Degraded: schema unavailable at type-generation time — permissive types until a successful DESCRIBE refreshes them. */\n ${JSON.stringify(schema.key)}: {\n key: ${JSON.stringify(schema.key)};\n source: ${JSON.stringify(schema.source)};\n lane: ${JSON.stringify(schema.lane)};\n measures: Record<string, unknown>;\n dimensions: Record<string, unknown>;\n measureKeys: string;\n dimensionKeys: string;\n timeGrains: string;\n metadata: {\n measures: Record<string, never>;\n dimensions: Record<string, never>;\n };\n }`;\n}\n\ntype RenderedMetadataField = readonly [name: string, value: string];\n\n// Rendered per-column fields shared by the type-level and runtime metadata.\n// `time_grain` is type-only, so it is included only when requested.\nfunction metadataFields(\n col: MetricColumnMetadata,\n includeTimeGrain = false,\n): RenderedMetadataField[] {\n const fields: RenderedMetadataField[] = [[\"type\", JSON.stringify(col.type)]];\n const optionalFields = [\n [\"display_name\", col.displayName],\n [\"format\", col.format],\n [\"description\", col.description],\n ] as const;\n\n for (const [name, value] of optionalFields) {\n if (value) {\n fields.push([name, JSON.stringify(value)]);\n }\n }\n\n if (includeTimeGrain && col.timeGrains && col.timeGrains.length > 0) {\n const grainTuple = col.timeGrains.map((g) => JSON.stringify(g)).join(\", \");\n fields.push([\"time_grain\", `readonly [${grainTuple}]`]);\n }\n\n return fields;\n}\n\n// Render the type-level shape of a column's semantic-metadata map\n// for the `metadata` field of a MetricRegistry entry.\nfunction renderMetadataMap(\n cols: MetricColumnMetadata[],\n indent: string,\n includeTimeGrain = false,\n): string {\n if (cols.length === 0) return \"Record<string, never>\";\n\n const inner = cols\n .map((col) => {\n const fieldsBlock = metadataFields(col, includeTimeGrain)\n .map(([name, value]) => `${indent} ${name}: ${value}`)\n .join(\";\\n\");\n return `${indent}${JSON.stringify(col.name)}: {\n${fieldsBlock};\n${indent}}`;\n })\n .join(\";\\n\");\n\n return `{\n${inner};\n }`;\n}\n\n// Value-side twin of a `renderMetadataMap` entry, minus `time_grain` (not part\n// of MetricViewColumnDisplay).\nfunction renderMetadataValueField(col: MetricColumnMetadata): string {\n const fields = metadataFields(col).map(\n ([name, value]) => `${name}: ${value}`,\n );\n return `{ ${fields.join(\", \")} }`;\n}\n\n// Render one metric's runtime measures/dimensions map, keyed by column name.\nfunction renderMetadataValueMap(\n cols: MetricColumnMetadata[],\n indent: string,\n): string {\n if (cols.length === 0) return \"{}\";\n const inner = cols\n .map(\n (col) =>\n `${indent} ${JSON.stringify(col.name)}: ${renderMetadataValueField(col)}`,\n )\n .join(\",\\n\");\n return `{\n${inner},\n${indent}}`;\n}\n\n// Render the runtime `metricViewsMetadata` const, emitted `as const` in the\n// same key order as the augmentation.\nfunction renderMetricViewsMetadata(schemas: MetricSchema[]): string {\n if (schemas.length === 0) {\n return \"export const metricViewsMetadata = {} as const;\\n\";\n }\n const entries = schemas\n .map((schema) => {\n const measures = renderMetadataValueMap(schema.measures, \" \");\n const dimensions = renderMetadataValueMap(schema.dimensions, \" \");\n return ` ${JSON.stringify(schema.key)}: {\n measures: ${measures},\n dimensions: ${dimensions},\n }`;\n })\n .join(\",\\n\");\n return `export const metricViewsMetadata = {\n${entries},\n} as const;\n`;\n}\n\n// Render the augmentation block for the appkit-ui MetricRegistry interface.\nfunction renderMetricRegistry(schemas: MetricSchema[]): string {\n if (schemas.length === 0) {\n return `declare module \"@databricks/appkit-ui/react\" {\n interface MetricRegistry {}\n}\n`;\n }\n const entries = schemas.map(renderMetricEntry).join(\";\\n\");\n return `declare module \"@databricks/appkit-ui/react\" {\n interface MetricRegistry {\n${entries};\n }\n}\n`;\n}\n\n/**\n * Build the full metric-views.ts file from a list of metric schemas.\n *\n * The header must stay a type-only `import type {} from`: it anchors the module\n * so the augmentation resolves while compiling to zero runtime code, whereas a\n * bare `import \"@databricks/appkit-ui/react\"` would execute the client package\n * entry on the Node server.\n */\nexport function generateMetricTypeDeclarations(\n schemas: MetricSchema[],\n): string {\n return `// Auto-generated by AppKit - DO NOT EDIT\n// Generated by 'npx @databricks/appkit generate-types' or Vite plugin during build\nimport type {} from \"@databricks/appkit-ui/react\";\n${renderMetricRegistry(schemas)}\n${renderMetricViewsMetadata(schemas)}`;\n}\n"],"mappings":"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"}
|
|
@@ -454,15 +454,13 @@ async function generateQueriesFromDescribe(queryFolder, warehouseId, options = {
|
|
|
454
454
|
} else if (classifyBlockingFailure(err) === "deterministic") {
|
|
455
455
|
decision = "fatal";
|
|
456
456
|
fatalMessage = `warehouse ${warehouseId}: ${getErrorDiagnostic(err)}`;
|
|
457
|
-
isEnvironmental = false;
|
|
458
457
|
} else {
|
|
459
458
|
decision = "degradeAll";
|
|
460
459
|
isEnvironmental = true;
|
|
461
460
|
environmentalCause = classifyEnvironmentalCause(err);
|
|
462
|
-
fatalMessage = `warehouse ${warehouseId}: ${getErrorDiagnostic(err)}`;
|
|
463
461
|
}
|
|
464
462
|
}
|
|
465
|
-
if (mode === "blocking" &&
|
|
463
|
+
if (mode === "blocking" && decision !== "proceed" && isEnvironmental) hadEnvironmentalFailure = true;
|
|
466
464
|
if (decision !== "proceed") {
|
|
467
465
|
const kind = decision === "fatal" && !isEnvironmental ? "fatal" : "connectivity";
|
|
468
466
|
for (const { index, queryName, sql, sqlHash } of uncachedQueries) {
|