@databricks/appkit 0.64.0 → 0.66.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 +19 -0
- package/dist/appkit/package.js +1 -1
- package/dist/beta.d.ts +9 -1
- package/dist/beta.js +6 -1
- package/dist/cli/commands/doctor/resolve-targets.js +1 -1
- package/dist/cli/commands/generate-types.js +12 -3
- package/dist/cli/commands/generate-types.js.map +1 -1
- package/dist/cli/commands/plugin/create/scaffold.js +2 -2
- package/dist/cli/commands/plugin/create/scaffold.js.map +1 -1
- package/dist/core/appkit.js +1 -1
- package/dist/database/contract/index.js +3 -0
- package/dist/database/contract/registry.d.ts +26 -0
- package/dist/database/contract/registry.d.ts.map +1 -0
- package/dist/database/contract/wire.d.ts +8 -0
- package/dist/database/contract/wire.d.ts.map +1 -0
- package/dist/database/contract/wire.js +37 -0
- package/dist/database/contract/wire.js.map +1 -0
- package/dist/database/errors.js +71 -0
- package/dist/database/errors.js.map +1 -0
- package/dist/database/runtime/data-path.js +39 -0
- package/dist/database/runtime/data-path.js.map +1 -0
- package/dist/database/runtime/engine/drizzle-data-path.js +194 -0
- package/dist/database/runtime/engine/drizzle-data-path.js.map +1 -0
- package/dist/database/runtime/engine/translate.js +176 -0
- package/dist/database/runtime/engine/translate.js.map +1 -0
- package/dist/database/schema-builder/columns.d.ts +39 -0
- package/dist/database/schema-builder/columns.d.ts.map +1 -0
- package/dist/database/schema-builder/columns.js +162 -0
- package/dist/database/schema-builder/columns.js.map +1 -0
- package/dist/database/schema-builder/define-schema.d.ts +12 -0
- package/dist/database/schema-builder/define-schema.d.ts.map +1 -0
- package/dist/database/schema-builder/define-schema.js +207 -0
- package/dist/database/schema-builder/define-schema.js.map +1 -0
- package/dist/database/schema-builder/engine/relations.js +37 -0
- package/dist/database/schema-builder/engine/relations.js.map +1 -0
- package/dist/database/schema-builder/engine/tables.js +124 -0
- package/dist/database/schema-builder/engine/tables.js.map +1 -0
- package/dist/database/schema-builder/fk.d.ts +9 -0
- package/dist/database/schema-builder/fk.d.ts.map +1 -0
- package/dist/database/schema-builder/fk.js +71 -0
- package/dist/database/schema-builder/fk.js.map +1 -0
- package/dist/database/schema-builder/index.js +6 -0
- package/dist/database/schema-builder/relations.js +51 -0
- package/dist/database/schema-builder/relations.js.map +1 -0
- package/dist/database/schema-builder/types.d.ts +121 -0
- package/dist/database/schema-builder/types.d.ts.map +1 -0
- package/dist/database/schema-builder/types.js +43 -0
- package/dist/database/schema-builder/types.js.map +1 -0
- package/dist/database/schema-builder/validators.js +53 -0
- package/dist/database/schema-builder/validators.js.map +1 -0
- package/dist/index.d.ts +13 -12
- package/dist/index.js +2 -2
- package/dist/plugin/plugin.d.ts +2 -2
- package/dist/plugins/agents/agents.d.ts +3 -3
- package/dist/plugins/agents/agents.d.ts.map +1 -1
- package/dist/plugins/agents/agents.js +3 -1
- package/dist/plugins/agents/agents.js.map +1 -1
- package/dist/plugins/ai-search/ai-search.d.ts +1 -1
- package/dist/plugins/ai-search/ai-search.d.ts.map +1 -1
- package/dist/plugins/ai-search/ai-search.js +3 -1
- package/dist/plugins/ai-search/ai-search.js.map +1 -1
- package/dist/plugins/analytics/analytics.d.ts +2 -2
- package/dist/plugins/analytics/analytics.d.ts.map +1 -1
- package/dist/plugins/analytics/analytics.js +3 -1
- package/dist/plugins/analytics/analytics.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/database/database.d.ts +37 -0
- package/dist/plugins/database/database.d.ts.map +1 -0
- package/dist/plugins/database/database.js +68 -0
- package/dist/plugins/database/database.js.map +1 -0
- package/dist/plugins/database/defaults.js +12 -0
- package/dist/plugins/database/defaults.js.map +1 -0
- package/dist/plugins/database/entity-client.js +148 -0
- package/dist/plugins/database/entity-client.js.map +1 -0
- package/dist/plugins/database/entity-types.d.ts +76 -0
- package/dist/plugins/database/entity-types.d.ts.map +1 -0
- package/dist/plugins/database/index.d.ts +3 -0
- package/dist/plugins/database/index.js +3 -0
- package/dist/plugins/database/lifecycle.js +107 -0
- package/dist/plugins/database/lifecycle.js.map +1 -0
- package/dist/plugins/database/manifest.js +84 -0
- package/dist/plugins/database/manifest.js.map +1 -0
- package/dist/plugins/database/types.d.ts +10 -0
- package/dist/plugins/database/types.d.ts.map +1 -0
- package/dist/plugins/files/plugin.d.ts +5 -4
- package/dist/plugins/files/plugin.d.ts.map +1 -1
- package/dist/plugins/files/plugin.js +2 -1
- package/dist/plugins/files/plugin.js.map +1 -1
- package/dist/plugins/genie/genie.d.ts +2 -2
- package/dist/plugins/genie/genie.d.ts.map +1 -1
- package/dist/plugins/genie/genie.js +3 -1
- package/dist/plugins/genie/genie.js.map +1 -1
- package/dist/plugins/jobs/plugin.d.ts +3 -2
- package/dist/plugins/jobs/plugin.d.ts.map +1 -1
- package/dist/plugins/jobs/plugin.js +2 -1
- package/dist/plugins/jobs/plugin.js.map +1 -1
- package/dist/plugins/lakebase/lakebase.d.ts +4 -4
- package/dist/plugins/lakebase/lakebase.d.ts.map +1 -1
- package/dist/plugins/lakebase/lakebase.js +3 -1
- package/dist/plugins/lakebase/lakebase.js.map +1 -1
- package/dist/plugins/server/index.d.ts +1 -1
- package/dist/plugins/server/index.d.ts.map +1 -1
- package/dist/plugins/server/index.js +3 -1
- package/dist/plugins/server/index.js.map +1 -1
- package/dist/plugins/serving/serving.d.ts +1 -0
- package/dist/plugins/serving/serving.d.ts.map +1 -1
- package/dist/plugins/serving/serving.js +2 -1
- package/dist/plugins/serving/serving.js.map +1 -1
- package/dist/plugins/ui-variants/index.js +3 -1
- package/dist/plugins/ui-variants/index.js.map +1 -1
- package/dist/registry/index.d.ts +1 -1
- package/dist/registry/index.js +1 -1
- package/dist/registry/manifest-loader.d.ts +20 -1
- package/dist/registry/manifest-loader.d.ts.map +1 -1
- package/dist/registry/manifest-loader.js +23 -1
- package/dist/registry/manifest-loader.js.map +1 -1
- package/dist/schemas/manifest.d.ts +1 -1
- package/dist/shared/src/index.d.ts +1 -1
- package/dist/shared/src/naming.js +13 -1
- package/dist/shared/src/naming.js.map +1 -1
- package/dist/shared/src/schemas/manifest.d.ts +1 -1
- package/dist/shared/src/schemas/manifest.js +463 -0
- package/dist/shared/src/schemas/manifest.js.map +1 -0
- package/dist/testing/fixtures.d.ts +1 -1
- package/dist/type-generator/database/generate.d.ts +15 -0
- package/dist/type-generator/database/generate.d.ts.map +1 -0
- package/dist/type-generator/database/generate.js +104 -0
- package/dist/type-generator/database/generate.js.map +1 -0
- package/dist/type-generator/database/index.js +3 -0
- package/dist/type-generator/database/walk-schema.js +65 -0
- package/dist/type-generator/database/walk-schema.js.map +1 -0
- package/dist/type-generator/index.d.ts +148 -0
- package/dist/type-generator/index.d.ts.map +1 -0
- package/dist/type-generator/index.js +3 -1
- package/dist/type-generator/index.js.map +1 -1
- package/dist/type-generator/mv-registry/types.d.ts +99 -0
- package/dist/type-generator/mv-registry/types.d.ts.map +1 -0
- package/dist/type-generator/preflight.d.ts +16 -0
- package/dist/type-generator/preflight.d.ts.map +1 -0
- package/dist/type-generator/serving/generator.d.ts +20 -0
- package/dist/type-generator/serving/generator.d.ts.map +1 -0
- package/dist/type-generator/types.d.ts +72 -0
- package/dist/type-generator/types.d.ts.map +1 -0
- package/dist/type-generator/vite-plugin.d.ts +1 -0
- package/dist/type-generator/vite-plugin.d.ts.map +1 -1
- package/dist/type-generator/vite-plugin.js +41 -8
- package/dist/type-generator/vite-plugin.js.map +1 -1
- package/dist/type-generator/warehouse-status.d.ts +1 -0
- package/docs/api/appkit/Function.bigid.md +10 -0
- package/docs/api/appkit/Function.bigint.md +10 -0
- package/docs/api/appkit/Function.boolean.md +10 -0
- package/docs/api/appkit/Function.database.md +56 -0
- package/docs/api/appkit/Function.defineManifest.md +32 -0
- package/docs/api/appkit/Function.defineSchema.md +17 -0
- package/docs/api/appkit/Function.enumColumn.md +17 -0
- package/docs/api/appkit/Function.fk.md +18 -0
- package/docs/api/appkit/Function.id.md +10 -0
- package/docs/api/appkit/Function.integer.md +10 -0
- package/docs/api/appkit/Function.jsonb.md +10 -0
- package/docs/api/appkit/Function.text.md +10 -0
- package/docs/api/appkit/Function.timestamp.md +19 -0
- package/docs/api/appkit/Function.uuid.md +10 -0
- package/docs/api/appkit/Function.varchar.md +16 -0
- package/docs/api/appkit/Interface.DatabaseRegistry.md +3 -0
- package/docs/api/appkit/Interface.Schema.md +28 -0
- package/docs/api/appkit/TypeAlias.DatabaseExports.md +35 -0
- package/docs/api/appkit/TypeAlias.IDatabaseConfig.md +25 -0
- package/docs/api/appkit/Variable.agents.md +1 -1
- package/docs/api/appkit.md +19 -0
- package/llms.txt +19 -0
- package/package.json +4 -3
- package/sbom.cdx.json +1 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { assertFinalizedSchema } from "../../database/schema-builder/define-schema.js";
|
|
2
|
+
import { walkSchema } from "./walk-schema.js";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import fs$1 from "node:fs";
|
|
7
|
+
|
|
8
|
+
//#region src/type-generator/database/generate.ts
|
|
9
|
+
/** Safe diagnostic raised when database declarations cannot be generated. */
|
|
10
|
+
var DatabaseTypegenError = class extends Error {
|
|
11
|
+
constructor(message = "Database schema generation failed") {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "DatabaseTypegenError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const DATABASE_TYPES_FILE = "database.d.ts";
|
|
17
|
+
function schemaLabel(schemaFile) {
|
|
18
|
+
return path.relative(process.cwd(), schemaFile) || schemaFile;
|
|
19
|
+
}
|
|
20
|
+
function importFailureReason(error) {
|
|
21
|
+
if (error instanceof SyntaxError) return "could not be parsed";
|
|
22
|
+
let code;
|
|
23
|
+
try {
|
|
24
|
+
code = error && typeof error === "object" ? Reflect.get(error, "code") : void 0;
|
|
25
|
+
} catch {
|
|
26
|
+
code = void 0;
|
|
27
|
+
}
|
|
28
|
+
if (code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND") return "contains an unresolved import";
|
|
29
|
+
return "threw while loading";
|
|
30
|
+
}
|
|
31
|
+
/** Empty augmentation used when no valid database schema is available. */
|
|
32
|
+
const NEUTRAL_DATABASE_TYPES = `// Auto-generated by AppKit - DO NOT EDIT
|
|
33
|
+
import "@databricks/appkit";
|
|
34
|
+
|
|
35
|
+
declare module "@databricks/appkit" {
|
|
36
|
+
interface DatabaseRegistry {}
|
|
37
|
+
}
|
|
38
|
+
`;
|
|
39
|
+
/** Render one `DatabaseRegistry` augmentation from a finalized schema. */
|
|
40
|
+
function render(schema) {
|
|
41
|
+
return `// Auto-generated by AppKit - DO NOT EDIT
|
|
42
|
+
import "@databricks/appkit";
|
|
43
|
+
|
|
44
|
+
declare module "@databricks/appkit" {
|
|
45
|
+
type DatabaseLogicalFilter<T> = T & {
|
|
46
|
+
and?: readonly DatabaseLogicalFilter<T>[];
|
|
47
|
+
or?: readonly DatabaseLogicalFilter<T>[];
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
interface DatabaseRegistry {
|
|
51
|
+
${walkSchema(schema).map((entry) => ` ${JSON.stringify(entry.name)}: {
|
|
52
|
+
row: ${entry.row};
|
|
53
|
+
publicRow: ${entry.publicRow};
|
|
54
|
+
insert: ${entry.insert};
|
|
55
|
+
update: ${entry.update};
|
|
56
|
+
filters: ${entry.filters};
|
|
57
|
+
includes: ${entry.includes};
|
|
58
|
+
hasPrimaryKey: ${entry.hasPrimaryKey};
|
|
59
|
+
};`).join("\n")}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
`;
|
|
63
|
+
}
|
|
64
|
+
/** Atomically replace generated output only when its content changes. */
|
|
65
|
+
async function writeIfChanged(outFile, content) {
|
|
66
|
+
if (fs$1.existsSync(outFile) && await fs.readFile(outFile, "utf8") === content) return;
|
|
67
|
+
await fs.mkdir(path.dirname(outFile), { recursive: true });
|
|
68
|
+
const temporary = path.join(path.dirname(outFile), `.${path.basename(outFile)}.${randomUUID()}.tmp`);
|
|
69
|
+
try {
|
|
70
|
+
await fs.writeFile(temporary, content, "utf8");
|
|
71
|
+
await fs.rename(temporary, outFile);
|
|
72
|
+
} finally {
|
|
73
|
+
await fs.rm(temporary, { force: true });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** Disable Jiti's module cache so watch-mode runs observe schema edits. */
|
|
77
|
+
async function importSchema(schemaFile) {
|
|
78
|
+
const { createJiti } = await import("jiti");
|
|
79
|
+
return createJiti(import.meta.url, { moduleCache: false }).import(schemaFile);
|
|
80
|
+
}
|
|
81
|
+
/** Generate current registry types or neutralize them when loading fails. */
|
|
82
|
+
async function generateDatabaseTypes(options) {
|
|
83
|
+
if (!fs$1.existsSync(options.schemaFile)) {
|
|
84
|
+
await writeIfChanged(options.outFile, NEUTRAL_DATABASE_TYPES);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
const module = await importSchema(options.schemaFile);
|
|
89
|
+
if (!("schema" in module)) throw new DatabaseTypegenError(`Database schema module "${schemaLabel(options.schemaFile)}" must export a named "schema"`);
|
|
90
|
+
try {
|
|
91
|
+
assertFinalizedSchema(module.schema);
|
|
92
|
+
} catch {
|
|
93
|
+
throw new DatabaseTypegenError(`Database schema "${schemaLabel(options.schemaFile)}" is not a finalized AppKit schema`);
|
|
94
|
+
}
|
|
95
|
+
await writeIfChanged(options.outFile, render(module.schema));
|
|
96
|
+
} catch (error) {
|
|
97
|
+
await writeIfChanged(options.outFile, NEUTRAL_DATABASE_TYPES);
|
|
98
|
+
throw error instanceof DatabaseTypegenError ? error : new DatabaseTypegenError(`Database schema "${schemaLabel(options.schemaFile)}" ${importFailureReason(error)}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
//#endregion
|
|
103
|
+
export { DATABASE_TYPES_FILE, DatabaseTypegenError, generateDatabaseTypes };
|
|
104
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","names":["fsSync"],"sources":["../../../src/type-generator/database/generate.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport fsSync from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { Schema } from \"../../database/schema-builder\";\nimport { assertFinalizedSchema } from \"../../database/schema-builder/define-schema\";\nimport { walkSchema } from \"./walk-schema\";\n\n/** Safe diagnostic raised when database declarations cannot be generated. */\nexport class DatabaseTypegenError extends Error {\n constructor(message = \"Database schema generation failed\") {\n super(message);\n this.name = \"DatabaseTypegenError\";\n }\n}\n\ninterface GenerateDatabaseTypesOptions {\n readonly schemaFile: string;\n readonly outFile: string;\n}\n\nexport const DATABASE_TYPES_FILE = \"database.d.ts\";\n\nfunction schemaLabel(schemaFile: string): string {\n return path.relative(process.cwd(), schemaFile) || schemaFile;\n}\n\nfunction importFailureReason(error: unknown): string {\n if (error instanceof SyntaxError) return \"could not be parsed\";\n let code: unknown;\n try {\n code =\n error && typeof error === \"object\"\n ? Reflect.get(error, \"code\")\n : undefined;\n } catch {\n code = undefined;\n }\n if (code === \"MODULE_NOT_FOUND\" || code === \"ERR_MODULE_NOT_FOUND\") {\n return \"contains an unresolved import\";\n }\n return \"threw while loading\";\n}\n\n/** Empty augmentation used when no valid database schema is available. */\nexport const NEUTRAL_DATABASE_TYPES = `// Auto-generated by AppKit - DO NOT EDIT\nimport \"@databricks/appkit\";\n\ndeclare module \"@databricks/appkit\" {\n interface DatabaseRegistry {}\n}\n`;\n\n/** Render one `DatabaseRegistry` augmentation from a finalized schema. */\nfunction render(schema: Schema): string {\n const entries = walkSchema(schema)\n .map(\n (entry) => ` ${JSON.stringify(entry.name)}: {\n row: ${entry.row};\n publicRow: ${entry.publicRow};\n insert: ${entry.insert};\n update: ${entry.update};\n filters: ${entry.filters};\n includes: ${entry.includes};\n hasPrimaryKey: ${entry.hasPrimaryKey};\n };`,\n )\n .join(\"\\n\");\n return `// Auto-generated by AppKit - DO NOT EDIT\nimport \"@databricks/appkit\";\n\ndeclare module \"@databricks/appkit\" {\n type DatabaseLogicalFilter<T> = T & {\n and?: readonly DatabaseLogicalFilter<T>[];\n or?: readonly DatabaseLogicalFilter<T>[];\n };\n\n interface DatabaseRegistry {\n${entries}\n }\n}\n`;\n}\n\n/** Atomically replace generated output only when its content changes. */\nasync function writeIfChanged(outFile: string, content: string): Promise<void> {\n if (\n fsSync.existsSync(outFile) &&\n (await fs.readFile(outFile, \"utf8\")) === content\n )\n return;\n await fs.mkdir(path.dirname(outFile), { recursive: true });\n const temporary = path.join(\n path.dirname(outFile),\n `.${path.basename(outFile)}.${randomUUID()}.tmp`,\n );\n try {\n await fs.writeFile(temporary, content, \"utf8\");\n await fs.rename(temporary, outFile);\n } finally {\n await fs.rm(temporary, { force: true });\n }\n}\n\n/** Disable Jiti's module cache so watch-mode runs observe schema edits. */\nasync function importSchema(schemaFile: string): Promise<unknown> {\n const { createJiti } = await import(\"jiti\");\n return createJiti(import.meta.url, { moduleCache: false }).import(schemaFile);\n}\n\n/** Generate current registry types or neutralize them when loading fails. */\nexport async function generateDatabaseTypes(\n options: GenerateDatabaseTypesOptions,\n): Promise<void> {\n if (!fsSync.existsSync(options.schemaFile)) {\n await writeIfChanged(options.outFile, NEUTRAL_DATABASE_TYPES);\n return;\n }\n try {\n const module = (await importSchema(options.schemaFile)) as {\n schema?: Schema;\n };\n if (!(\"schema\" in module)) {\n throw new DatabaseTypegenError(\n `Database schema module \"${schemaLabel(options.schemaFile)}\" must export a named \"schema\"`,\n );\n }\n try {\n assertFinalizedSchema(module.schema);\n } catch {\n throw new DatabaseTypegenError(\n `Database schema \"${schemaLabel(options.schemaFile)}\" is not a finalized AppKit schema`,\n );\n }\n await writeIfChanged(options.outFile, render(module.schema));\n } catch (error) {\n // A failed run must not leave stale entities visible to TypeScript.\n await writeIfChanged(options.outFile, NEUTRAL_DATABASE_TYPES);\n throw error instanceof DatabaseTypegenError\n ? error\n : new DatabaseTypegenError(\n `Database schema \"${schemaLabel(options.schemaFile)}\" ${importFailureReason(error)}`,\n );\n }\n}\n"],"mappings":";;;;;;;;;AAUA,IAAa,uBAAb,cAA0C,MAAM;CAC9C,YAAY,UAAU,qCAAqC;AACzD,QAAM,QAAQ;AACd,OAAK,OAAO;;;AAShB,MAAa,sBAAsB;AAEnC,SAAS,YAAY,YAA4B;AAC/C,QAAO,KAAK,SAAS,QAAQ,KAAK,EAAE,WAAW,IAAI;;AAGrD,SAAS,oBAAoB,OAAwB;AACnD,KAAI,iBAAiB,YAAa,QAAO;CACzC,IAAI;AACJ,KAAI;AACF,SACE,SAAS,OAAO,UAAU,WACtB,QAAQ,IAAI,OAAO,OAAO,GAC1B;SACA;AACN,SAAO;;AAET,KAAI,SAAS,sBAAsB,SAAS,uBAC1C,QAAO;AAET,QAAO;;;AAIT,MAAa,yBAAyB;;;;;;;;AAStC,SAAS,OAAO,QAAwB;AActC,QAAO;;;;;;;;;;EAbS,WAAW,OAAO,CAC/B,KACE,UAAU,OAAO,KAAK,UAAU,MAAM,KAAK,CAAC;aACtC,MAAM,IAAI;mBACJ,MAAM,UAAU;gBACnB,MAAM,OAAO;gBACb,MAAM,OAAO;iBACZ,MAAM,QAAQ;kBACb,MAAM,SAAS;uBACV,MAAM,cAAc;QAEtC,CACA,KAAK,KAAK,CAWL;;;;;;AAOV,eAAe,eAAe,SAAiB,SAAgC;AAC7E,KACEA,KAAO,WAAW,QAAQ,IACzB,MAAM,GAAG,SAAS,SAAS,OAAO,KAAM,QAEzC;AACF,OAAM,GAAG,MAAM,KAAK,QAAQ,QAAQ,EAAE,EAAE,WAAW,MAAM,CAAC;CAC1D,MAAM,YAAY,KAAK,KACrB,KAAK,QAAQ,QAAQ,EACrB,IAAI,KAAK,SAAS,QAAQ,CAAC,GAAG,YAAY,CAAC,MAC5C;AACD,KAAI;AACF,QAAM,GAAG,UAAU,WAAW,SAAS,OAAO;AAC9C,QAAM,GAAG,OAAO,WAAW,QAAQ;WAC3B;AACR,QAAM,GAAG,GAAG,WAAW,EAAE,OAAO,MAAM,CAAC;;;;AAK3C,eAAe,aAAa,YAAsC;CAChE,MAAM,EAAE,eAAe,MAAM,OAAO;AACpC,QAAO,WAAW,OAAO,KAAK,KAAK,EAAE,aAAa,OAAO,CAAC,CAAC,OAAO,WAAW;;;AAI/E,eAAsB,sBACpB,SACe;AACf,KAAI,CAACA,KAAO,WAAW,QAAQ,WAAW,EAAE;AAC1C,QAAM,eAAe,QAAQ,SAAS,uBAAuB;AAC7D;;AAEF,KAAI;EACF,MAAM,SAAU,MAAM,aAAa,QAAQ,WAAW;AAGtD,MAAI,EAAE,YAAY,QAChB,OAAM,IAAI,qBACR,2BAA2B,YAAY,QAAQ,WAAW,CAAC,gCAC5D;AAEH,MAAI;AACF,yBAAsB,OAAO,OAAO;UAC9B;AACN,SAAM,IAAI,qBACR,oBAAoB,YAAY,QAAQ,WAAW,CAAC,oCACrD;;AAEH,QAAM,eAAe,QAAQ,SAAS,OAAO,OAAO,OAAO,CAAC;UACrD,OAAO;AAEd,QAAM,eAAe,QAAQ,SAAS,uBAAuB;AAC7D,QAAM,iBAAiB,uBACnB,QACA,IAAI,qBACF,oBAAoB,YAAY,QAAQ,WAAW,CAAC,IAAI,oBAAoB,MAAM,GACnF"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { filterOperatorsForKind } from "../../database/schema-builder/types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/type-generator/database/walk-schema.ts
|
|
4
|
+
/** Keep generated scalars aligned with the schema's canonical runtime values. */
|
|
5
|
+
function tsType(meta) {
|
|
6
|
+
switch (meta.kind) {
|
|
7
|
+
case "string":
|
|
8
|
+
case "uuid":
|
|
9
|
+
case "date": return "string";
|
|
10
|
+
case "number": return "number";
|
|
11
|
+
case "bigint": return "bigint";
|
|
12
|
+
case "boolean": return "boolean";
|
|
13
|
+
case "json": return "unknown";
|
|
14
|
+
case "enum": return meta.enumValues?.map((value) => JSON.stringify(value)).join(" | ") || "string";
|
|
15
|
+
default: return "unknown";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function objectFacet(lines, empty = "{}") {
|
|
19
|
+
return lines.length ? `{\n${lines.join("\n")}\n }` : empty;
|
|
20
|
+
}
|
|
21
|
+
function property(meta, optional = false) {
|
|
22
|
+
const nullable = meta.notNull ? "" : " | null";
|
|
23
|
+
return ` ${JSON.stringify(meta.columnName)}${optional ? "?" : ""}: ${tsType(meta)}${nullable};`;
|
|
24
|
+
}
|
|
25
|
+
function rowType(table, publicOnly) {
|
|
26
|
+
return objectFacet(Object.values(table.$columns).filter((c) => !publicOnly || !c.isPrivate).map((c) => property(c)), "Record<string, never>");
|
|
27
|
+
}
|
|
28
|
+
function insertType(table) {
|
|
29
|
+
return objectFacet(Object.values(table.$columns).filter((c) => !c.serverGenerated).map((c) => property(c, !c.notNull || c.hasDefault)), "Record<string, never>");
|
|
30
|
+
}
|
|
31
|
+
function updateType(table) {
|
|
32
|
+
return objectFacet(Object.values(table.$columns).filter((c) => !c.serverGenerated && !c.primaryKey).map((c) => property(c, true)), "Record<string, never>");
|
|
33
|
+
}
|
|
34
|
+
/** Reuse the canonical operator matrix when rendering `where()` types. */
|
|
35
|
+
function filtersType(table) {
|
|
36
|
+
return `DatabaseLogicalFilter<${objectFacet(Object.values(table.$columns).flatMap((column) => {
|
|
37
|
+
const operators = filterOperatorsForKind(column.kind);
|
|
38
|
+
if (operators.length === 0) return [];
|
|
39
|
+
const value = tsType(column);
|
|
40
|
+
const fields = operators.map((operator) => `${operator}?: ${operator === "in" ? `readonly (${value})[]` : value};`);
|
|
41
|
+
if (!column.notNull) fields.push("is?: null;");
|
|
42
|
+
return [` ${JSON.stringify(column.columnName)}?: ${value} | readonly (${value})[] | { ${fields.join(" ")} };`];
|
|
43
|
+
}))}>`;
|
|
44
|
+
}
|
|
45
|
+
/** Preserve finalized relation identity and cardinality in include types. */
|
|
46
|
+
function includesType(table) {
|
|
47
|
+
return objectFacet(table.$relations.map((relation) => ` ${JSON.stringify(relation.name)}: { to: ${JSON.stringify(relation.targetTable)}; many: ${relation.cardinality === "toMany"} };`));
|
|
48
|
+
}
|
|
49
|
+
/** Preserve schema table identity as the generated registry key. */
|
|
50
|
+
function walkSchema(schema) {
|
|
51
|
+
return Object.entries(schema.$tables).map(([name, table]) => ({
|
|
52
|
+
name,
|
|
53
|
+
row: rowType(table, false),
|
|
54
|
+
publicRow: rowType(table, true),
|
|
55
|
+
insert: insertType(table),
|
|
56
|
+
update: updateType(table),
|
|
57
|
+
filters: filtersType(table),
|
|
58
|
+
includes: includesType(table),
|
|
59
|
+
hasPrimaryKey: Object.values(table.$columns).some((column) => column.primaryKey)
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
export { walkSchema };
|
|
65
|
+
//# sourceMappingURL=walk-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walk-schema.js","names":[],"sources":["../../../src/type-generator/database/walk-schema.ts"],"sourcesContent":["import type {\n AppKitTable,\n ColumnMeta,\n Schema,\n} from \"../../database/schema-builder\";\nimport { filterOperatorsForKind } from \"../../database/schema-builder/types\";\n\n/** Render-ready type facets for one database registry entry. */\ninterface RegistryEntry {\n readonly name: string;\n readonly row: string;\n readonly publicRow: string;\n readonly insert: string;\n readonly update: string;\n readonly filters: string;\n readonly includes: string;\n readonly hasPrimaryKey: boolean;\n}\n\n/** Keep generated scalars aligned with the schema's canonical runtime values. */\nfunction tsType(meta: ColumnMeta): string {\n switch (meta.kind) {\n case \"string\":\n case \"uuid\":\n case \"date\":\n return \"string\";\n case \"number\":\n return \"number\";\n case \"bigint\":\n return \"bigint\";\n case \"boolean\":\n return \"boolean\";\n case \"json\":\n return \"unknown\";\n case \"enum\":\n return (\n meta.enumValues?.map((value) => JSON.stringify(value)).join(\" | \") ||\n \"string\"\n );\n default:\n return \"unknown\";\n }\n}\n\nfunction objectFacet(lines: string[], empty = \"{}\"): string {\n return lines.length ? `{\\n${lines.join(\"\\n\")}\\n }` : empty;\n}\n\nfunction property(meta: ColumnMeta, optional = false): string {\n const nullable = meta.notNull ? \"\" : \" | null\";\n return ` ${JSON.stringify(meta.columnName)}${optional ? \"?\" : \"\"}: ${tsType(meta)}${nullable};`;\n}\n\n// Trusted facets retain private columns; only public rows project them out.\nfunction rowType(table: AppKitTable, publicOnly: boolean): string {\n return objectFacet(\n Object.values(table.$columns)\n .filter((c) => !publicOnly || !c.isPrivate)\n .map((c) => property(c)),\n \"Record<string, never>\",\n );\n}\n\n// Write facets mirror trusted validators; updates additionally omit primary keys.\nfunction insertType(table: AppKitTable): string {\n return objectFacet(\n Object.values(table.$columns)\n .filter((c) => !c.serverGenerated)\n .map((c) => property(c, !c.notNull || c.hasDefault)),\n \"Record<string, never>\",\n );\n}\n\nfunction updateType(table: AppKitTable): string {\n return objectFacet(\n Object.values(table.$columns)\n .filter((c) => !c.serverGenerated && !c.primaryKey)\n .map((c) => property(c, true)),\n \"Record<string, never>\",\n );\n}\n\n/** Reuse the canonical operator matrix when rendering `where()` types. */\nfunction filtersType(table: AppKitTable): string {\n const direct = objectFacet(\n Object.values(table.$columns).flatMap((column) => {\n const operators = filterOperatorsForKind(column.kind);\n if (operators.length === 0) return [];\n const value = tsType(column);\n const fields = operators.map(\n (operator) =>\n `${operator}?: ${operator === \"in\" ? `readonly (${value})[]` : value};`,\n );\n if (!column.notNull) fields.push(\"is?: null;\");\n return [\n ` ${JSON.stringify(column.columnName)}?: ${value} | readonly (${value})[] | { ${fields.join(\" \")} };`,\n ];\n }),\n );\n return `DatabaseLogicalFilter<${direct}>`;\n}\n\n/** Preserve finalized relation identity and cardinality in include types. */\nfunction includesType(table: AppKitTable): string {\n return objectFacet(\n table.$relations.map(\n (relation) =>\n ` ${JSON.stringify(relation.name)}: { to: ${JSON.stringify(relation.targetTable)}; many: ${relation.cardinality === \"toMany\"} };`,\n ),\n );\n}\n\n/** Preserve schema table identity as the generated registry key. */\nexport function walkSchema(schema: Schema): RegistryEntry[] {\n return Object.entries(schema.$tables).map(([name, table]) => ({\n name,\n row: rowType(table, false),\n publicRow: rowType(table, true),\n insert: insertType(table),\n update: updateType(table),\n filters: filtersType(table),\n includes: includesType(table),\n hasPrimaryKey: Object.values(table.$columns).some(\n (column) => column.primaryKey,\n ),\n }));\n}\n"],"mappings":";;;;AAoBA,SAAS,OAAO,MAA0B;AACxC,SAAQ,KAAK,MAAb;EACE,KAAK;EACL,KAAK;EACL,KAAK,OACH,QAAO;EACT,KAAK,SACH,QAAO;EACT,KAAK,SACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,KAAK,OACH,QACE,KAAK,YAAY,KAAK,UAAU,KAAK,UAAU,MAAM,CAAC,CAAC,KAAK,MAAM,IAClE;EAEJ,QACE,QAAO;;;AAIb,SAAS,YAAY,OAAiB,QAAQ,MAAc;AAC1D,QAAO,MAAM,SAAS,MAAM,MAAM,KAAK,KAAK,CAAC,WAAW;;AAG1D,SAAS,SAAS,MAAkB,WAAW,OAAe;CAC5D,MAAM,WAAW,KAAK,UAAU,KAAK;AACrC,QAAO,SAAS,KAAK,UAAU,KAAK,WAAW,GAAG,WAAW,MAAM,GAAG,IAAI,OAAO,KAAK,GAAG,SAAS;;AAIpG,SAAS,QAAQ,OAAoB,YAA6B;AAChE,QAAO,YACL,OAAO,OAAO,MAAM,SAAS,CAC1B,QAAQ,MAAM,CAAC,cAAc,CAAC,EAAE,UAAU,CAC1C,KAAK,MAAM,SAAS,EAAE,CAAC,EAC1B,wBACD;;AAIH,SAAS,WAAW,OAA4B;AAC9C,QAAO,YACL,OAAO,OAAO,MAAM,SAAS,CAC1B,QAAQ,MAAM,CAAC,EAAE,gBAAgB,CACjC,KAAK,MAAM,SAAS,GAAG,CAAC,EAAE,WAAW,EAAE,WAAW,CAAC,EACtD,wBACD;;AAGH,SAAS,WAAW,OAA4B;AAC9C,QAAO,YACL,OAAO,OAAO,MAAM,SAAS,CAC1B,QAAQ,MAAM,CAAC,EAAE,mBAAmB,CAAC,EAAE,WAAW,CAClD,KAAK,MAAM,SAAS,GAAG,KAAK,CAAC,EAChC,wBACD;;;AAIH,SAAS,YAAY,OAA4B;AAgB/C,QAAO,yBAfQ,YACb,OAAO,OAAO,MAAM,SAAS,CAAC,SAAS,WAAW;EAChD,MAAM,YAAY,uBAAuB,OAAO,KAAK;AACrD,MAAI,UAAU,WAAW,EAAG,QAAO,EAAE;EACrC,MAAM,QAAQ,OAAO,OAAO;EAC5B,MAAM,SAAS,UAAU,KACtB,aACC,GAAG,SAAS,KAAK,aAAa,OAAO,aAAa,MAAM,OAAO,MAAM,GACxE;AACD,MAAI,CAAC,OAAO,QAAS,QAAO,KAAK,aAAa;AAC9C,SAAO,CACL,SAAS,KAAK,UAAU,OAAO,WAAW,CAAC,KAAK,MAAM,eAAe,MAAM,UAAU,OAAO,KAAK,IAAI,CAAC,KACvG;GACD,CACH,CACsC;;;AAIzC,SAAS,aAAa,OAA4B;AAChD,QAAO,YACL,MAAM,WAAW,KACd,aACC,SAAS,KAAK,UAAU,SAAS,KAAK,CAAC,UAAU,KAAK,UAAU,SAAS,YAAY,CAAC,UAAU,SAAS,gBAAgB,SAAS,KACrI,CACF;;;AAIH,SAAgB,WAAW,QAAiC;AAC1D,QAAO,OAAO,QAAQ,OAAO,QAAQ,CAAC,KAAK,CAAC,MAAM,YAAY;EAC5D;EACA,KAAK,QAAQ,OAAO,MAAM;EAC1B,WAAW,QAAQ,OAAO,KAAK;EAC/B,QAAQ,WAAW,MAAM;EACzB,QAAQ,WAAW,MAAM;EACzB,SAAS,YAAY,MAAM;EAC3B,UAAU,aAAa,MAAM;EAC7B,eAAe,OAAO,OAAO,MAAM,SAAS,CAAC,MAC1C,WAAW,OAAO,WACpB;EACF,EAAE"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { QueryFatalError, QuerySyntaxError } from "./types.js";
|
|
2
|
+
import { DescribeFetcher, MetricColumnMetadata, MetricLane, MetricSchema, MetricSyncFailure, MetricSyncResult } from "./mv-registry/types.js";
|
|
3
|
+
import { PreflightMode } from "./preflight.js";
|
|
4
|
+
import { generateServingTypes as generateServingTypes$1 } from "./serving/generator.js";
|
|
5
|
+
import { DATABASE_TYPES_FILE, DatabaseTypegenError, generateDatabaseTypes } from "./database/generate.js";
|
|
6
|
+
|
|
7
|
+
//#region src/type-generator/index.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Thrown when one or more queries fail `DESCRIBE QUERY` against a *reachable*
|
|
10
|
+
* warehouse — i.e. genuine SQL errors (bad table, syntax, incompatible type),
|
|
11
|
+
* as opposed to a connectivity failure (warehouse unreachable), which degrades
|
|
12
|
+
* silently. Whether this is fatal is the caller's decision: the Vite plugin and
|
|
13
|
+
* CLI fail the build in production and warn-only in development.
|
|
14
|
+
*/
|
|
15
|
+
declare class TypegenSyntaxError extends Error {
|
|
16
|
+
readonly queries: QuerySyntaxError[];
|
|
17
|
+
readonly fatalQueries: QueryFatalError[];
|
|
18
|
+
constructor(queries: QuerySyntaxError[], warehouseId?: string, fatalQueries?: QueryFatalError[]);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Thrown when DESCRIBE QUERY could not be requested because of a non-SQL fatal
|
|
22
|
+
* setup/request problem, such as missing permissions, invalid warehouse IDs, or
|
|
23
|
+
* malformed SDK configuration. Like TypegenSyntaxError, this is thrown only
|
|
24
|
+
* after the declaration file has been written with `result: unknown` entries.
|
|
25
|
+
*/
|
|
26
|
+
declare class TypegenFatalError extends Error {
|
|
27
|
+
readonly queries: QueryFatalError[];
|
|
28
|
+
constructor(queries: QueryFatalError[], warehouseId?: string);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Entry point for generating type declarations from all imported files
|
|
32
|
+
* @param options - the options for the generation
|
|
33
|
+
* @param options.entryPoint - the entry point file
|
|
34
|
+
* @param options.outFile - the output file
|
|
35
|
+
* @param options.noCache - skip the typegen cache entirely: every query is
|
|
36
|
+
* re-described, and the metric path ignores its cached schemas (every
|
|
37
|
+
* configured key becomes describe-needed) and overwrites the cache's
|
|
38
|
+
* `metrics` section with this pass's results.
|
|
39
|
+
* @param options.mode - preflight policy (see {@link PreflightMode}), default
|
|
40
|
+
* `"non-blocking"`. For queries, `"non-blocking"` never touches the
|
|
41
|
+
* warehouse. For metric views it makes one status-only probe and DESCRIBEs
|
|
42
|
+
* only when the warehouse is already RUNNING, otherwise emits permissive
|
|
43
|
+
* degraded types immediately. `"blocking"` waits for / starts the warehouse
|
|
44
|
+
* first, failing the build only for a deleted/deleting one.
|
|
45
|
+
* @param options.metricViewsFolder - folder that holds `definitions.json`
|
|
46
|
+
* (`<root>/config/metric-views`). Optional and independent of `queryFolder`:
|
|
47
|
+
* metric-view types generate whenever this folder holds a config, even if the
|
|
48
|
+
* app has no `config/queries`. When omitted it defaults to a sibling
|
|
49
|
+
* `metric-views` directory of `queryFolder` (so query-only callers keep
|
|
50
|
+
* working); when neither is given, the metric path is skipped.
|
|
51
|
+
* @param options.mvOutFile - optional output file for the MetricRegistry
|
|
52
|
+
* augmentation. Defaults to a sibling `metric-views.ts` file under the same
|
|
53
|
+
* directory as `outFile`. Skipped entirely if `definitions.json` is absent.
|
|
54
|
+
* @param options.metricFetcher - optional DescribeFetcher used by
|
|
55
|
+
* {@link syncMetrics} (tests inject a mock; production lazily builds a
|
|
56
|
+
* default WorkspaceClient-backed one). An injected fetcher always runs: it
|
|
57
|
+
* hits no warehouse, so it bypasses both the non-blocking gate and the
|
|
58
|
+
* blocking preflight.
|
|
59
|
+
*/
|
|
60
|
+
declare function generateFromEntryPoint(options: {
|
|
61
|
+
outFile: string;
|
|
62
|
+
queryFolder?: string;
|
|
63
|
+
metricViewsFolder?: string;
|
|
64
|
+
warehouseId: string;
|
|
65
|
+
noCache?: boolean;
|
|
66
|
+
mode?: PreflightMode;
|
|
67
|
+
mvOutFile?: string;
|
|
68
|
+
metricFetcher?: DescribeFetcher;
|
|
69
|
+
}): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Result of a {@link syncMetricViewsTypes} run, returned to the caller (the CLI
|
|
72
|
+
* directly, or {@link generateFromEntryPoint} which delegates to it) so it can
|
|
73
|
+
* report what happened and decide its exit code.
|
|
74
|
+
*/
|
|
75
|
+
interface SyncMetricViewsTypesResult {
|
|
76
|
+
metricOutFile?: string;
|
|
77
|
+
schemas: MetricSchema[];
|
|
78
|
+
failures: MetricSyncFailure[];
|
|
79
|
+
/**
|
|
80
|
+
* `true` when no `definitions.json` was found in the metric-views folder, so
|
|
81
|
+
* nothing was synced.
|
|
82
|
+
*/
|
|
83
|
+
noConfig: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Per-key fatal preflight errors (empty except in the `blocking`-mode
|
|
86
|
+
* deleted/deleting-warehouse and deterministic-preflight-failure cases).
|
|
87
|
+
* {@link generateFromEntryPoint} surfaces these by throwing
|
|
88
|
+
* {@link TypegenFatalError}; when the run also degraded, `suppressDegradedWrite`
|
|
89
|
+
* means no artifact was written and the committed types stand. A `"describe-now"`
|
|
90
|
+
* run sets no blocking preflight, so for that mode this is always empty.
|
|
91
|
+
* ONLY contains deterministic failures (404/400).
|
|
92
|
+
*/
|
|
93
|
+
fatalErrors: Array<{
|
|
94
|
+
name: string;
|
|
95
|
+
message: string;
|
|
96
|
+
}>;
|
|
97
|
+
/**
|
|
98
|
+
* `true` when an environmental failure occurred in blocking mode (auth, connectivity,
|
|
99
|
+
* DELETED/DELETING, wait-timeout, or other unrecognized failures). Used by
|
|
100
|
+
* {@link generateFromEntryPoint} to decide whether to apply the has-types gate.
|
|
101
|
+
* Does not directly cause a throw — the gate decides that. Always false in
|
|
102
|
+
* non-blocking or describe-now mode.
|
|
103
|
+
*/
|
|
104
|
+
hadEnvironmentalFailure?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Coarse cause label for the environmental failure, one of "auth" (401/403),
|
|
107
|
+
* "unreachable" (connectivity), or "unavailable" (other). Only set when
|
|
108
|
+
* hadEnvironmentalFailure is true; used by the warning message.
|
|
109
|
+
*/
|
|
110
|
+
environmentalCause?: "auth" | "unreachable" | "unavailable";
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Unified metric-view type-generation pipeline behind {@link
|
|
114
|
+
* generateFromEntryPoint}'s metric section (which forwards its
|
|
115
|
+
* `"non-blocking"`/`"blocking"` mode). Also directly callable with the default
|
|
116
|
+
* `"describe-now"` mode for a focused, always-converge metric refresh.
|
|
117
|
+
*
|
|
118
|
+
*
|
|
119
|
+
* @param options.metricViewsFolder - folder that holds `definitions.json` (`<root>/config/metric-views`).
|
|
120
|
+
* @param options.warehouseId - SQL warehouse used for `DESCRIBE TABLE EXTENDED`.
|
|
121
|
+
* @param options.metricOutFile - output path for the MetricRegistry `.ts` (the
|
|
122
|
+
* generated source carries both the `declare module` augmentation and the
|
|
123
|
+
* runtime `metricViewsMetadata` const).
|
|
124
|
+
* @param options.cache - cache toggle, default ON. Only `cache === false` disables it (so `undefined`/`true` keep caching).
|
|
125
|
+
* @param options.metricFetcher - optional injected {@link DescribeFetcher}
|
|
126
|
+
* @param options.mode - preflight/gate policy, default `"describe-now"`. When set to `"blocking"`,
|
|
127
|
+
* metric-view `.ts` writes are suppressed if any metric is degraded (to preserve committed files).
|
|
128
|
+
* @param options.suppressDegradedWrite - when true (only in `mode === "blocking"` context), skip
|
|
129
|
+
* the metricOutFile write if any metric schema has `degraded === true`. Used to prevent
|
|
130
|
+
* overwriting committed type files with degraded types in blocking mode.
|
|
131
|
+
*/
|
|
132
|
+
declare function syncMetricViewsTypes(options: {
|
|
133
|
+
metricViewsFolder: string;
|
|
134
|
+
warehouseId: string;
|
|
135
|
+
metricOutFile: string;
|
|
136
|
+
cache?: boolean;
|
|
137
|
+
metricFetcher?: DescribeFetcher;
|
|
138
|
+
mode?: "describe-now" | "non-blocking" | "blocking";
|
|
139
|
+
suppressDegradedWrite?: boolean;
|
|
140
|
+
}): Promise<SyncMetricViewsTypesResult>;
|
|
141
|
+
declare const generateServingTypes: typeof generateServingTypes$1;
|
|
142
|
+
declare const TYPES_DIR = "appkit-types";
|
|
143
|
+
declare const ANALYTICS_TYPES_FILE = "analytics.d.ts";
|
|
144
|
+
declare const SERVING_TYPES_FILE = "serving.d.ts";
|
|
145
|
+
declare const METRIC_TYPES_FILE = "metric-views.d.ts";
|
|
146
|
+
//#endregion
|
|
147
|
+
export { ANALYTICS_TYPES_FILE, DATABASE_TYPES_FILE, DatabaseTypegenError, METRIC_TYPES_FILE, type MetricColumnMetadata, type MetricLane, type MetricSchema, type MetricSyncFailure, type MetricSyncResult, SERVING_TYPES_FILE, SyncMetricViewsTypesResult, TYPES_DIR, TypegenFatalError, TypegenSyntaxError, generateDatabaseTypes, generateFromEntryPoint, generateServingTypes, syncMetricViewsTypes };
|
|
148
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/type-generator/index.ts"],"mappings":";;;;;;;;;;AA6KA;;;;cAAa,kBAAA,SAA2B,KAAA;EAAA,SAC7B,OAAA,EAAS,gBAAA;EAAA,SACT,YAAA,EAAc,eAAA;cAGrB,OAAA,EAAS,gBAAA,IACT,WAAA,WACA,YAAA,GAAc,eAAA;AAAA;;;;;;;cA8BL,iBAAA,SAA0B,KAAA;EAAA,SAC5B,OAAA,EAAS,eAAA;cAEN,OAAA,EAAS,eAAA,IAAmB,WAAA;AAAA;;;;;AAH1C;;;;;;;;;;;;;;;;;AAiHA;;;;;;;;;iBAAsB,sBAAA,CAAuB,OAAA;EAC3C,OAAA;EACA,WAAA;EACA,iBAAA;EACA,WAAA;EACA,OAAA;EACA,IAAA,GAAO,aAAA;EACP,SAAA;EACA,aAAA,GAAgB,eAAA;AAAA,IACjB,OAAA;;;;AAkLD;;UAAiB,0BAAA;EACf,aAAA;EACA,OAAA,EAAS,YAAA;EACT,QAAA,EAAU,iBAAA;EAeQ;;;;EAVlB,QAAA;EALA;;;;;;;;;EAeA,WAAA,EAAa,KAAA;IAAQ,IAAA;IAAc,OAAA;EAAA;;;;;;;;EAQnC,uBAAA;EAgCA;;;;;EA1BA,kBAAA;AAAA;;;;;AAsWF;;;;;AAaA;;;;;AACA;;;;;AACA;iBA9VsB,oBAAA,CAAqB,OAAA;EACzC,iBAAA;EACA,WAAA;EACA,aAAA;EACA,KAAA;EACA,aAAA,GAAgB,eAAA;EAChB,IAAA;EACA,qBAAA;AAAA,IACE,OAAA,CAAQ,0BAAA;AAAA,cAuUC,oBAAA,SAAoB,sBAAA;AAAA,cAapB,SAAA;AAAA,cACA,oBAAA;AAAA,cACA,kBAAA;AAAA,cACA,iBAAA"}
|
|
@@ -12,6 +12,8 @@ import { decidePreflight } from "./preflight.js";
|
|
|
12
12
|
import { getWarehouseState, startWarehouse, waitUntilRunning } from "./warehouse-status.js";
|
|
13
13
|
import { generateQueriesFromDescribe } from "./query-registry.js";
|
|
14
14
|
import { generateServingTypes as generateServingTypes$1 } from "./serving/generator.js";
|
|
15
|
+
import { DATABASE_TYPES_FILE, DatabaseTypegenError, generateDatabaseTypes } from "./database/generate.js";
|
|
16
|
+
import "./database/index.js";
|
|
15
17
|
import pc from "picocolors";
|
|
16
18
|
import fs from "node:fs/promises";
|
|
17
19
|
import path from "node:path";
|
|
@@ -469,5 +471,5 @@ const SERVING_TYPES_FILE = "serving.d.ts";
|
|
|
469
471
|
const METRIC_TYPES_FILE = "metric-views.d.ts";
|
|
470
472
|
|
|
471
473
|
//#endregion
|
|
472
|
-
export { ANALYTICS_TYPES_FILE, SERVING_TYPES_FILE, TYPES_DIR, TypegenFatalError, TypegenSyntaxError, generateFromEntryPoint, generateServingTypes };
|
|
474
|
+
export { ANALYTICS_TYPES_FILE, DATABASE_TYPES_FILE, DatabaseTypegenError, METRIC_TYPES_FILE, SERVING_TYPES_FILE, TYPES_DIR, TypegenFatalError, TypegenSyntaxError, generateDatabaseTypes, generateFromEntryPoint, generateServingTypes, syncMetricViewsTypes };
|
|
473
475
|
//# sourceMappingURL=index.js.map
|
|
@@ -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\";\n\nimport dotenv from \"dotenv\";\nimport pc from \"picocolors\";\n\nimport { METRIC_METADATA_FILE } from \"../../../shared/src/schemas/metric-metadata-bundle\";\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 {\n buildMetricMetadataBundle,\n generateMetricTypeDeclarations,\n} 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.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 const bundlePath = path.join(metricViewsFolder, METRIC_METADATA_FILE);\n await fs.writeFile(\n bundlePath,\n `${JSON.stringify(buildMetricMetadataBundle(schemas), null, 2)}\\n`,\n \"utf-8\",\n );\n logger.debug(\"Wrote metric metadata bundle to %s\", bundlePath);\n }\n // Sweep the `metric-views.ts` an interim version left behind, which would\n // otherwise duplicate the augmentation the `.d.ts` emits. Skipped unless the\n // replacement was actually written, so a degraded blocking pass leaves an\n // app's only committed metric types in place.\n if (metricOutFile.endsWith(\".d.ts\") && existsSync(metricOutFile)) {\n const staleTs = `${metricOutFile.slice(0, -\".d.ts\".length)}.ts`;\n try {\n await fs.unlink(staleTs);\n logger.debug(\"Removed stale generated types at %s\", staleTs);\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.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\";\n\nimport dotenv from \"dotenv\";\nimport pc from \"picocolors\";\n\nimport { METRIC_METADATA_FILE } from \"../../../shared/src/schemas/metric-metadata-bundle\";\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 {\n buildMetricMetadataBundle,\n generateMetricTypeDeclarations,\n} 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.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 const bundlePath = path.join(metricViewsFolder, METRIC_METADATA_FILE);\n await fs.writeFile(\n bundlePath,\n `${JSON.stringify(buildMetricMetadataBundle(schemas), null, 2)}\\n`,\n \"utf-8\",\n );\n logger.debug(\"Wrote metric metadata bundle to %s\", bundlePath);\n }\n // Sweep the `metric-views.ts` an interim version left behind, which would\n // otherwise duplicate the augmentation the `.d.ts` emits. Skipped unless the\n // replacement was actually written, so a degraded blocking pass leaves an\n // app's only committed metric types in place.\n if (metricOutFile.endsWith(\".d.ts\") && existsSync(metricOutFile)) {\n const staleTs = `${metricOutFile.slice(0, -\".d.ts\".length)}.ts`;\n try {\n await fs.unlink(staleTs);\n logger.debug(\"Removed stale generated types at %s\", staleTs);\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.d.ts\";\nexport {\n DATABASE_TYPES_FILE,\n DatabaseTypegenError,\n generateDatabaseTypes,\n} from \"./database\";\n"],"mappings":"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"}
|