@databricks/appkit 0.22.0 → 0.23.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.
Files changed (87) hide show
  1. package/CLAUDE.md +10 -0
  2. package/NOTICE.md +1 -0
  3. package/dist/appkit/package.js +1 -1
  4. package/dist/cli/commands/generate-types.js +15 -13
  5. package/dist/cli/commands/generate-types.js.map +1 -1
  6. package/dist/connectors/serving/client.js +47 -0
  7. package/dist/connectors/serving/client.js.map +1 -0
  8. package/dist/index.d.ts +6 -1
  9. package/dist/index.js +4 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/plugin/execution-result.d.ts +26 -0
  12. package/dist/plugin/execution-result.d.ts.map +1 -0
  13. package/dist/plugin/index.d.ts +1 -0
  14. package/dist/plugin/interceptors/retry.js +1 -1
  15. package/dist/plugin/interceptors/retry.js.map +1 -1
  16. package/dist/plugin/plugin.d.ts +7 -4
  17. package/dist/plugin/plugin.d.ts.map +1 -1
  18. package/dist/plugin/plugin.js +36 -5
  19. package/dist/plugin/plugin.js.map +1 -1
  20. package/dist/plugins/analytics/analytics.d.ts.map +1 -1
  21. package/dist/plugins/analytics/analytics.js +2 -3
  22. package/dist/plugins/analytics/analytics.js.map +1 -1
  23. package/dist/plugins/files/plugin.d.ts +1 -0
  24. package/dist/plugins/files/plugin.d.ts.map +1 -1
  25. package/dist/plugins/files/plugin.js +36 -59
  26. package/dist/plugins/files/plugin.js.map +1 -1
  27. package/dist/plugins/index.d.ts +4 -1
  28. package/dist/plugins/index.js +2 -0
  29. package/dist/plugins/server/index.d.ts +1 -1
  30. package/dist/plugins/server/vite-dev-server.js +6 -1
  31. package/dist/plugins/server/vite-dev-server.js.map +1 -1
  32. package/dist/plugins/serving/defaults.js +10 -0
  33. package/dist/plugins/serving/defaults.js.map +1 -0
  34. package/dist/plugins/serving/index.d.ts +2 -0
  35. package/dist/plugins/serving/index.js +3 -0
  36. package/dist/plugins/serving/manifest.js +53 -0
  37. package/dist/plugins/serving/manifest.js.map +1 -0
  38. package/dist/plugins/serving/schema-filter.js +52 -0
  39. package/dist/plugins/serving/schema-filter.js.map +1 -0
  40. package/dist/plugins/serving/serving.d.ts +38 -0
  41. package/dist/plugins/serving/serving.d.ts.map +1 -0
  42. package/dist/plugins/serving/serving.js +213 -0
  43. package/dist/plugins/serving/serving.js.map +1 -0
  44. package/dist/plugins/serving/types.d.ts +58 -0
  45. package/dist/plugins/serving/types.d.ts.map +1 -0
  46. package/dist/shared/src/execute.d.ts +1 -1
  47. package/dist/stream/stream-manager.js +1 -0
  48. package/dist/stream/stream-manager.js.map +1 -1
  49. package/dist/stream/types.js +2 -1
  50. package/dist/stream/types.js.map +1 -1
  51. package/dist/type-generator/cache.js +1 -1
  52. package/dist/type-generator/cache.js.map +1 -1
  53. package/dist/type-generator/index.js +3 -1
  54. package/dist/type-generator/index.js.map +1 -1
  55. package/dist/type-generator/query-registry.js +77 -4
  56. package/dist/type-generator/query-registry.js.map +1 -1
  57. package/dist/type-generator/serving/cache.js +38 -0
  58. package/dist/type-generator/serving/cache.js.map +1 -0
  59. package/dist/type-generator/serving/converter.js +108 -0
  60. package/dist/type-generator/serving/converter.js.map +1 -0
  61. package/dist/type-generator/serving/fetcher.js +54 -0
  62. package/dist/type-generator/serving/fetcher.js.map +1 -0
  63. package/dist/type-generator/serving/generator.js +185 -0
  64. package/dist/type-generator/serving/generator.js.map +1 -0
  65. package/dist/type-generator/serving/server-file-extractor.d.ts +22 -0
  66. package/dist/type-generator/serving/server-file-extractor.d.ts.map +1 -0
  67. package/dist/type-generator/serving/server-file-extractor.js +131 -0
  68. package/dist/type-generator/serving/server-file-extractor.js.map +1 -0
  69. package/dist/type-generator/serving/vite-plugin.d.ts +24 -0
  70. package/dist/type-generator/serving/vite-plugin.d.ts.map +1 -0
  71. package/dist/type-generator/serving/vite-plugin.js +60 -0
  72. package/dist/type-generator/serving/vite-plugin.js.map +1 -0
  73. package/docs/api/appkit/Class.Plugin.md +8 -3
  74. package/docs/api/appkit/Function.appKitServingTypesPlugin.md +24 -0
  75. package/docs/api/appkit/Function.extractServingEndpoints.md +22 -0
  76. package/docs/api/appkit/Function.findServerFile.md +20 -0
  77. package/docs/api/appkit/Interface.EndpointConfig.md +23 -0
  78. package/docs/api/appkit/Interface.ServingEndpointEntry.md +30 -0
  79. package/docs/api/appkit/Interface.ServingEndpointRegistry.md +3 -0
  80. package/docs/api/appkit/TypeAlias.ExecutionResult.md +36 -0
  81. package/docs/api/appkit/TypeAlias.ServingFactory.md +15 -0
  82. package/docs/api/appkit.md +39 -31
  83. package/docs/faq.md +66 -0
  84. package/docs/plugins/serving.md +223 -0
  85. package/llms.txt +10 -0
  86. package/package.json +2 -2
  87. package/sbom.cdx.json +1 -1
@@ -0,0 +1,185 @@
1
+ import { createLogger } from "../../logging/logger.js";
2
+ import { CACHE_VERSION, hashSchema, loadServingCache, saveServingCache } from "./cache.js";
3
+ import { convertRequestSchema, convertResponseSchema, deriveChunkType, extractRequestKeys } from "./converter.js";
4
+ import { fetchOpenApiSchema } from "./fetcher.js";
5
+ import { WorkspaceClient } from "@databricks/sdk-experimental";
6
+ import fs from "node:fs/promises";
7
+ import pc from "picocolors";
8
+
9
+ //#region src/type-generator/serving/generator.ts
10
+ const logger = createLogger("type-generator:serving");
11
+ const GENERIC_REQUEST = "Record<string, unknown>";
12
+ const GENERIC_RESPONSE = "unknown";
13
+ const GENERIC_CHUNK = "unknown";
14
+ /**
15
+ * Generates TypeScript type declarations for serving endpoints
16
+ * by fetching their OpenAPI schemas and converting to TypeScript.
17
+ */
18
+ async function generateServingTypes(options) {
19
+ const { outFile, noCache } = options;
20
+ const endpoints = options.endpoints ?? resolveDefaultEndpoints();
21
+ if (Object.keys(endpoints).length === 0) {
22
+ logger.debug("No serving endpoints configured, skipping type generation");
23
+ return;
24
+ }
25
+ const startTime = performance.now();
26
+ const cache = noCache ? {
27
+ version: CACHE_VERSION,
28
+ endpoints: {}
29
+ } : await loadServingCache();
30
+ let client;
31
+ let updated = false;
32
+ const registryEntries = [];
33
+ const logEntries = [];
34
+ for (const [alias, config] of Object.entries(endpoints)) {
35
+ client ??= new WorkspaceClient({});
36
+ const result = await processEndpoint(alias, config, client, cache);
37
+ if (result.cacheUpdated) updated = true;
38
+ registryEntries.push(result.entry);
39
+ logEntries.push(result.log);
40
+ }
41
+ printLogTable(logEntries, startTime);
42
+ const output = generateTypeDeclarations(registryEntries);
43
+ await fs.writeFile(outFile, output, "utf-8");
44
+ if (registryEntries.length === 0) logger.debug("Wrote empty serving types to %s (no endpoints resolved)", outFile);
45
+ else logger.debug("Wrote serving types to %s", outFile);
46
+ if (updated) await saveServingCache(cache);
47
+ }
48
+ function genericEntry(alias) {
49
+ return buildRegistryEntry(alias, GENERIC_REQUEST, GENERIC_RESPONSE, GENERIC_CHUNK);
50
+ }
51
+ async function processEndpoint(alias, config, client, cache) {
52
+ const endpointName = process.env[config.env];
53
+ if (!endpointName) return {
54
+ entry: genericEntry(alias),
55
+ log: {
56
+ alias,
57
+ status: "MISS",
58
+ error: `env ${config.env} not set`
59
+ },
60
+ cacheUpdated: false
61
+ };
62
+ const result = await fetchOpenApiSchema(client, endpointName, config.servedModel);
63
+ if (!result) return {
64
+ entry: genericEntry(alias),
65
+ log: {
66
+ alias,
67
+ status: "MISS",
68
+ error: "schema fetch failed"
69
+ },
70
+ cacheUpdated: false
71
+ };
72
+ const { spec, pathKey } = result;
73
+ const hash = hashSchema(JSON.stringify(spec));
74
+ const cached = cache.endpoints[alias];
75
+ if (cached && cached.hash === hash) return {
76
+ entry: buildRegistryEntry(alias, cached.requestType, cached.responseType, cached.chunkType),
77
+ log: {
78
+ alias,
79
+ status: "HIT"
80
+ },
81
+ cacheUpdated: false
82
+ };
83
+ const operation = spec.paths[pathKey]?.post;
84
+ if (!operation) return {
85
+ entry: genericEntry(alias),
86
+ log: {
87
+ alias,
88
+ status: "MISS",
89
+ error: "no POST operation"
90
+ },
91
+ cacheUpdated: false
92
+ };
93
+ try {
94
+ const requestType = convertRequestSchema(operation);
95
+ const responseType = convertResponseSchema(operation);
96
+ const chunkType = deriveChunkType(operation);
97
+ const requestKeys = extractRequestKeys(operation);
98
+ cache.endpoints[alias] = {
99
+ hash,
100
+ requestType,
101
+ responseType,
102
+ chunkType,
103
+ requestKeys
104
+ };
105
+ return {
106
+ entry: buildRegistryEntry(alias, requestType, responseType, chunkType),
107
+ log: {
108
+ alias,
109
+ status: "MISS"
110
+ },
111
+ cacheUpdated: true
112
+ };
113
+ } catch (convErr) {
114
+ logger.warn("Schema conversion failed for '%s': %s", alias, convErr.message);
115
+ return {
116
+ entry: genericEntry(alias),
117
+ log: {
118
+ alias,
119
+ status: "MISS",
120
+ error: "schema conversion failed"
121
+ },
122
+ cacheUpdated: false
123
+ };
124
+ }
125
+ }
126
+ function printLogTable(logEntries, startTime) {
127
+ if (logEntries.length === 0) return;
128
+ const maxNameLen = Math.max(...logEntries.map((e) => e.alias.length));
129
+ const separator = pc.dim("─".repeat(50));
130
+ console.log("");
131
+ console.log(` ${pc.bold("Typegen Serving")} ${pc.dim(`(${logEntries.length})`)}`);
132
+ console.log(` ${separator}`);
133
+ for (const entry of logEntries) {
134
+ const tag = entry.status === "HIT" ? `cache ${pc.bold(pc.green("HIT "))}` : `cache ${pc.bold(pc.yellow("MISS "))}`;
135
+ const rawName = entry.alias.padEnd(maxNameLen);
136
+ const reason = entry.error ? ` ${pc.dim(entry.error)}` : "";
137
+ console.log(` ${tag} ${rawName}${reason}`);
138
+ }
139
+ const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
140
+ const newCount = logEntries.filter((e) => e.status === "MISS").length;
141
+ const cacheCount = logEntries.filter((e) => e.status === "HIT").length;
142
+ console.log(` ${separator}`);
143
+ console.log(` ${newCount} new, ${cacheCount} from cache. ${pc.dim(`${elapsed}s`)}`);
144
+ console.log("");
145
+ }
146
+ function resolveDefaultEndpoints() {
147
+ if (process.env.DATABRICKS_SERVING_ENDPOINT_NAME) return { default: { env: "DATABRICKS_SERVING_ENDPOINT_NAME" } };
148
+ return {};
149
+ }
150
+ function buildRegistryEntry(alias, requestType, responseType, chunkType) {
151
+ const indent = " ";
152
+ const chunkEntry = chunkType ? chunkType : "unknown";
153
+ return ` ${alias}: {
154
+ ${indent}request: ${indentType(requestType, indent)};
155
+ ${indent}response: ${indentType(responseType, indent)};
156
+ ${indent}chunk: ${indentType(chunkEntry, indent)};
157
+ };`;
158
+ }
159
+ function indentType(typeStr, baseIndent) {
160
+ if (!typeStr.includes("\n")) return typeStr;
161
+ return typeStr.split("\n").map((line, i) => i === 0 ? line : `${baseIndent}${line}`).join("\n");
162
+ }
163
+ function generateTypeDeclarations(entries) {
164
+ return `// Auto-generated by AppKit - DO NOT EDIT
165
+ // Generated from serving endpoint OpenAPI schemas
166
+ import "@databricks/appkit";
167
+ import "@databricks/appkit-ui/react";
168
+
169
+ declare module "@databricks/appkit" {
170
+ interface ServingEndpointRegistry {
171
+ ${entries.join("\n")}
172
+ }
173
+ }
174
+
175
+ declare module "@databricks/appkit-ui/react" {
176
+ interface ServingEndpointRegistry {
177
+ ${entries.join("\n")}
178
+ }
179
+ }
180
+ `;
181
+ }
182
+
183
+ //#endregion
184
+ export { generateServingTypes };
185
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.js","names":[],"sources":["../../../src/type-generator/serving/generator.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport { WorkspaceClient } from \"@databricks/sdk-experimental\";\nimport pc from \"picocolors\";\nimport { createLogger } from \"../../logging/logger\";\nimport type { EndpointConfig } from \"../../plugins/serving/types\";\nimport {\n CACHE_VERSION,\n hashSchema,\n loadServingCache,\n type ServingCache,\n saveServingCache,\n} from \"./cache\";\nimport {\n convertRequestSchema,\n convertResponseSchema,\n deriveChunkType,\n extractRequestKeys,\n} from \"./converter\";\nimport { fetchOpenApiSchema } from \"./fetcher\";\n\nconst logger = createLogger(\"type-generator:serving\");\n\nconst GENERIC_REQUEST = \"Record<string, unknown>\";\nconst GENERIC_RESPONSE = \"unknown\";\nconst GENERIC_CHUNK = \"unknown\";\n\ninterface GenerateServingTypesOptions {\n outFile: string;\n endpoints?: Record<string, EndpointConfig>;\n noCache?: boolean;\n}\n\n/**\n * Generates TypeScript type declarations for serving endpoints\n * by fetching their OpenAPI schemas and converting to TypeScript.\n */\nexport async function generateServingTypes(\n options: GenerateServingTypesOptions,\n): Promise<void> {\n const { outFile, noCache } = options;\n\n // Resolve endpoints from config or env\n const endpoints = options.endpoints ?? resolveDefaultEndpoints();\n if (Object.keys(endpoints).length === 0) {\n logger.debug(\"No serving endpoints configured, skipping type generation\");\n return;\n }\n\n const startTime = performance.now();\n\n const cache = noCache\n ? { version: CACHE_VERSION, endpoints: {} }\n : await loadServingCache();\n\n let client: WorkspaceClient | undefined;\n let updated = false;\n\n const registryEntries: string[] = [];\n const logEntries: Array<{\n alias: string;\n status: \"HIT\" | \"MISS\";\n error?: string;\n }> = [];\n\n for (const [alias, config] of Object.entries(endpoints)) {\n client ??= new WorkspaceClient({});\n const result = await processEndpoint(alias, config, client, cache);\n if (result.cacheUpdated) updated = true;\n registryEntries.push(result.entry);\n logEntries.push(result.log);\n }\n\n printLogTable(logEntries, startTime);\n\n const output = generateTypeDeclarations(registryEntries);\n await fs.writeFile(outFile, output, \"utf-8\");\n\n if (registryEntries.length === 0) {\n logger.debug(\n \"Wrote empty serving types to %s (no endpoints resolved)\",\n outFile,\n );\n } else {\n logger.debug(\"Wrote serving types to %s\", outFile);\n }\n\n if (updated) {\n await saveServingCache(cache as ServingCache);\n }\n}\n\ninterface EndpointResult {\n entry: string;\n log: { alias: string; status: \"HIT\" | \"MISS\"; error?: string };\n cacheUpdated: boolean;\n}\n\nfunction genericEntry(alias: string): string {\n return buildRegistryEntry(\n alias,\n GENERIC_REQUEST,\n GENERIC_RESPONSE,\n GENERIC_CHUNK,\n );\n}\n\nasync function processEndpoint(\n alias: string,\n config: EndpointConfig,\n client: WorkspaceClient,\n cache: { endpoints: Record<string, any> },\n): Promise<EndpointResult> {\n const endpointName = process.env[config.env];\n if (!endpointName) {\n return {\n entry: genericEntry(alias),\n log: { alias, status: \"MISS\", error: `env ${config.env} not set` },\n cacheUpdated: false,\n };\n }\n\n const result = await fetchOpenApiSchema(\n client,\n endpointName,\n config.servedModel,\n );\n if (!result) {\n return {\n entry: genericEntry(alias),\n log: { alias, status: \"MISS\", error: \"schema fetch failed\" },\n cacheUpdated: false,\n };\n }\n\n const { spec, pathKey } = result;\n const hash = hashSchema(JSON.stringify(spec));\n\n // Cache hit\n const cached = cache.endpoints[alias];\n if (cached && cached.hash === hash) {\n return {\n entry: buildRegistryEntry(\n alias,\n cached.requestType,\n cached.responseType,\n cached.chunkType,\n ),\n log: { alias, status: \"HIT\" },\n cacheUpdated: false,\n };\n }\n\n // Cache miss — convert schema to types\n const operation = spec.paths[pathKey]?.post;\n if (!operation) {\n return {\n entry: genericEntry(alias),\n log: { alias, status: \"MISS\", error: \"no POST operation\" },\n cacheUpdated: false,\n };\n }\n\n try {\n const requestType = convertRequestSchema(operation);\n const responseType = convertResponseSchema(operation);\n const chunkType = deriveChunkType(operation);\n const requestKeys = extractRequestKeys(operation);\n\n cache.endpoints[alias] = {\n hash,\n requestType,\n responseType,\n chunkType,\n requestKeys,\n };\n\n return {\n entry: buildRegistryEntry(alias, requestType, responseType, chunkType),\n log: { alias, status: \"MISS\" },\n cacheUpdated: true,\n };\n } catch (convErr) {\n logger.warn(\n \"Schema conversion failed for '%s': %s\",\n alias,\n (convErr as Error).message,\n );\n return {\n entry: genericEntry(alias),\n log: { alias, status: \"MISS\", error: \"schema conversion failed\" },\n cacheUpdated: false,\n };\n }\n}\n\nfunction printLogTable(\n logEntries: Array<{ alias: string; status: \"HIT\" | \"MISS\"; error?: string }>,\n startTime: number,\n): void {\n if (logEntries.length === 0) return;\n\n const maxNameLen = Math.max(...logEntries.map((e) => e.alias.length));\n const separator = pc.dim(\"─\".repeat(50));\n console.log(\"\");\n console.log(\n ` ${pc.bold(\"Typegen Serving\")} ${pc.dim(`(${logEntries.length})`)}`,\n );\n console.log(` ${separator}`);\n for (const entry of logEntries) {\n const tag =\n entry.status === \"HIT\"\n ? `cache ${pc.bold(pc.green(\"HIT \"))}`\n : `cache ${pc.bold(pc.yellow(\"MISS \"))}`;\n const rawName = entry.alias.padEnd(maxNameLen);\n const reason = entry.error ? ` ${pc.dim(entry.error)}` : \"\";\n console.log(` ${tag} ${rawName}${reason}`);\n }\n const elapsed = ((performance.now() - startTime) / 1000).toFixed(2);\n const newCount = logEntries.filter((e) => e.status === \"MISS\").length;\n const cacheCount = logEntries.filter((e) => e.status === \"HIT\").length;\n console.log(` ${separator}`);\n console.log(\n ` ${newCount} new, ${cacheCount} from cache. ${pc.dim(`${elapsed}s`)}`,\n );\n console.log(\"\");\n}\n\nfunction resolveDefaultEndpoints(): Record<string, EndpointConfig> {\n if (process.env.DATABRICKS_SERVING_ENDPOINT_NAME) {\n return { default: { env: \"DATABRICKS_SERVING_ENDPOINT_NAME\" } };\n }\n return {};\n}\n\nfunction buildRegistryEntry(\n alias: string,\n requestType: string,\n responseType: string,\n chunkType: string | null,\n): string {\n const indent = \" \";\n const chunkEntry = chunkType ? chunkType : \"unknown\";\n return ` ${alias}: {\n${indent}request: ${indentType(requestType, indent)};\n${indent}response: ${indentType(responseType, indent)};\n${indent}chunk: ${indentType(chunkEntry, indent)};\n };`;\n}\n\nfunction indentType(typeStr: string, baseIndent: string): string {\n if (!typeStr.includes(\"\\n\")) return typeStr;\n return typeStr\n .split(\"\\n\")\n .map((line, i) => (i === 0 ? line : `${baseIndent}${line}`))\n .join(\"\\n\");\n}\n\nfunction generateTypeDeclarations(entries: string[]): string {\n return `// Auto-generated by AppKit - DO NOT EDIT\n// Generated from serving endpoint OpenAPI schemas\nimport \"@databricks/appkit\";\nimport \"@databricks/appkit-ui/react\";\n\ndeclare module \"@databricks/appkit\" {\n interface ServingEndpointRegistry {\n${entries.join(\"\\n\")}\n }\n}\n\ndeclare module \"@databricks/appkit-ui/react\" {\n interface ServingEndpointRegistry {\n${entries.join(\"\\n\")}\n }\n}\n`;\n}\n"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { EndpointConfig } from "../../plugins/serving/types.js";
2
+
3
+ //#region src/type-generator/serving/server-file-extractor.d.ts
4
+ /**
5
+ * Find the server entry file by checking candidate paths in order.
6
+ *
7
+ * @param basePath - Project root directory to search from
8
+ * @returns Absolute path to the server file, or null if none found
9
+ */
10
+ declare function findServerFile(basePath: string): string | null;
11
+ /**
12
+ * Extract serving endpoint config from a server file by AST-parsing it.
13
+ * Looks for `serving({ endpoints: { alias: { env: "..." }, ... } })` calls
14
+ * and extracts the endpoint alias names and their environment variable mappings.
15
+ *
16
+ * @param serverFilePath - Absolute path to the server entry file
17
+ * @returns Extracted endpoint config, or null if not found or not extractable
18
+ */
19
+ declare function extractServingEndpoints(serverFilePath: string): Record<string, EndpointConfig> | null;
20
+ //#endregion
21
+ export { extractServingEndpoints, findServerFile };
22
+ //# sourceMappingURL=server-file-extractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-file-extractor.d.ts","names":[],"sources":["../../../src/type-generator/serving/server-file-extractor.ts"],"mappings":";;;;;AAqBA;;;;iBAAgB,cAAA,CAAe,QAAA;AAkB/B;;;;;;;;AAAA,iBAAgB,uBAAA,CACd,cAAA,WACC,MAAA,SAAe,cAAA"}
@@ -0,0 +1,131 @@
1
+ import { createLogger } from "../../logging/logger.js";
2
+ import path from "node:path";
3
+ import fs from "node:fs";
4
+ import { Lang, parse } from "@ast-grep/napi";
5
+
6
+ //#region src/type-generator/serving/server-file-extractor.ts
7
+ const logger = createLogger("type-generator:serving:extractor");
8
+ /**
9
+ * Candidate paths for the server entry file, relative to the project root.
10
+ * Checked in order; the first that exists is used.
11
+ * Same convention as plugin sync (sync.ts SERVER_FILE_CANDIDATES).
12
+ */
13
+ const SERVER_FILE_CANDIDATES = ["server/index.ts", "server/server.ts"];
14
+ /**
15
+ * Find the server entry file by checking candidate paths in order.
16
+ *
17
+ * @param basePath - Project root directory to search from
18
+ * @returns Absolute path to the server file, or null if none found
19
+ */
20
+ function findServerFile(basePath) {
21
+ for (const candidate of SERVER_FILE_CANDIDATES) {
22
+ const fullPath = path.join(basePath, candidate);
23
+ if (fs.existsSync(fullPath)) return fullPath;
24
+ }
25
+ return null;
26
+ }
27
+ /**
28
+ * Extract serving endpoint config from a server file by AST-parsing it.
29
+ * Looks for `serving({ endpoints: { alias: { env: "..." }, ... } })` calls
30
+ * and extracts the endpoint alias names and their environment variable mappings.
31
+ *
32
+ * @param serverFilePath - Absolute path to the server entry file
33
+ * @returns Extracted endpoint config, or null if not found or not extractable
34
+ */
35
+ function extractServingEndpoints(serverFilePath) {
36
+ let content;
37
+ try {
38
+ content = fs.readFileSync(serverFilePath, "utf-8");
39
+ } catch {
40
+ logger.debug("Could not read server file: %s", serverFilePath);
41
+ return null;
42
+ }
43
+ const servingCall = findServingCall(parse(serverFilePath.endsWith(".tsx") ? Lang.Tsx : Lang.TypeScript, content).root());
44
+ if (!servingCall) {
45
+ logger.debug("No serving() call found in %s", serverFilePath);
46
+ return null;
47
+ }
48
+ const args = servingCall.field("arguments");
49
+ if (!args) return null;
50
+ const configArg = args.children().find((child) => child.kind() === "object");
51
+ if (!configArg) return null;
52
+ const endpointsPair = findProperty(configArg, "endpoints");
53
+ if (!endpointsPair) return null;
54
+ const endpointsValue = getPropertyValue(endpointsPair);
55
+ if (!endpointsValue || endpointsValue.kind() !== "object") {
56
+ logger.debug("serving() endpoints is not an inline object literal in %s. Pass endpoints explicitly via appKitServingTypesPlugin({ endpoints }) in vite.config.ts.", serverFilePath);
57
+ return null;
58
+ }
59
+ const endpoints = {};
60
+ const pairs = endpointsValue.children().filter((child) => child.kind() === "pair");
61
+ for (const pair of pairs) {
62
+ const entry = extractEndpointEntry(pair);
63
+ if (entry) endpoints[entry.alias] = entry.config;
64
+ }
65
+ if (Object.keys(endpoints).length === 0) return null;
66
+ logger.debug("Extracted %d endpoint(s) from %s: %s", Object.keys(endpoints).length, serverFilePath, Object.keys(endpoints).join(", "));
67
+ return endpoints;
68
+ }
69
+ /**
70
+ * Find the serving() call expression in the AST.
71
+ * Looks for call expressions where the callee identifier is "serving".
72
+ */
73
+ function findServingCall(root) {
74
+ const callExpressions = root.findAll({ rule: { kind: "call_expression" } });
75
+ for (const call of callExpressions) {
76
+ const callee = call.children()[0];
77
+ if (callee?.kind() === "identifier" && callee.text() === "serving") return call;
78
+ }
79
+ return null;
80
+ }
81
+ /**
82
+ * Find a property (pair node) with the given key name in an object expression.
83
+ */
84
+ function findProperty(objectNode, propertyName) {
85
+ const pairs = objectNode.children().filter((child) => child.kind() === "pair");
86
+ for (const pair of pairs) {
87
+ const key = pair.children()[0];
88
+ if (!key) continue;
89
+ if ((key.kind() === "property_identifier" ? key.text() : key.kind() === "string" ? key.text().replace(/^['"]|['"]$/g, "") : null) === propertyName) return pair;
90
+ }
91
+ return null;
92
+ }
93
+ /**
94
+ * Get the value node from a pair (property: value).
95
+ * The value is typically the last meaningful child after the colon.
96
+ */
97
+ function getPropertyValue(pairNode) {
98
+ const children = pairNode.children();
99
+ return children.length >= 3 ? children[children.length - 1] : null;
100
+ }
101
+ /**
102
+ * Extract a single endpoint entry from a pair node like:
103
+ * `demo: { env: "DATABRICKS_SERVING_ENDPOINT_NAME", servedModel: "my-model" }`
104
+ */
105
+ function extractEndpointEntry(pair) {
106
+ const children = pair.children();
107
+ if (children.length < 3) return null;
108
+ const keyNode = children[0];
109
+ const alias = keyNode.kind() === "property_identifier" ? keyNode.text() : keyNode.kind() === "string" ? keyNode.text().replace(/^['"]|['"]$/g, "") : null;
110
+ if (!alias) return null;
111
+ const valueNode = children[children.length - 1];
112
+ if (valueNode.kind() !== "object") return null;
113
+ const envPair = findProperty(valueNode, "env");
114
+ if (!envPair) return null;
115
+ const envValue = getPropertyValue(envPair);
116
+ if (!envValue || envValue.kind() !== "string") return null;
117
+ const config = { env: envValue.text().replace(/^['"]|['"]$/g, "") };
118
+ const servedModelPair = findProperty(valueNode, "servedModel");
119
+ if (servedModelPair) {
120
+ const servedModelValue = getPropertyValue(servedModelPair);
121
+ if (servedModelValue?.kind() === "string") config.servedModel = servedModelValue.text().replace(/^['"]|['"]$/g, "");
122
+ }
123
+ return {
124
+ alias,
125
+ config
126
+ };
127
+ }
128
+
129
+ //#endregion
130
+ export { extractServingEndpoints, findServerFile };
131
+ //# sourceMappingURL=server-file-extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-file-extractor.js","names":[],"sources":["../../../src/type-generator/serving/server-file-extractor.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Lang, parse, type SgNode } from \"@ast-grep/napi\";\nimport { createLogger } from \"../../logging/logger\";\nimport type { EndpointConfig } from \"../../plugins/serving/types\";\n\nconst logger = createLogger(\"type-generator:serving:extractor\");\n\n/**\n * Candidate paths for the server entry file, relative to the project root.\n * Checked in order; the first that exists is used.\n * Same convention as plugin sync (sync.ts SERVER_FILE_CANDIDATES).\n */\nconst SERVER_FILE_CANDIDATES = [\"server/index.ts\", \"server/server.ts\"];\n\n/**\n * Find the server entry file by checking candidate paths in order.\n *\n * @param basePath - Project root directory to search from\n * @returns Absolute path to the server file, or null if none found\n */\nexport function findServerFile(basePath: string): string | null {\n for (const candidate of SERVER_FILE_CANDIDATES) {\n const fullPath = path.join(basePath, candidate);\n if (fs.existsSync(fullPath)) {\n return fullPath;\n }\n }\n return null;\n}\n\n/**\n * Extract serving endpoint config from a server file by AST-parsing it.\n * Looks for `serving({ endpoints: { alias: { env: \"...\" }, ... } })` calls\n * and extracts the endpoint alias names and their environment variable mappings.\n *\n * @param serverFilePath - Absolute path to the server entry file\n * @returns Extracted endpoint config, or null if not found or not extractable\n */\nexport function extractServingEndpoints(\n serverFilePath: string,\n): Record<string, EndpointConfig> | null {\n let content: string;\n try {\n content = fs.readFileSync(serverFilePath, \"utf-8\");\n } catch {\n logger.debug(\"Could not read server file: %s\", serverFilePath);\n return null;\n }\n\n const lang = serverFilePath.endsWith(\".tsx\") ? Lang.Tsx : Lang.TypeScript;\n const ast = parse(lang, content);\n const root = ast.root();\n\n // Find serving(...) call expressions\n const servingCall = findServingCall(root);\n if (!servingCall) {\n logger.debug(\"No serving() call found in %s\", serverFilePath);\n return null;\n }\n\n // Get the first argument (the config object)\n const args = servingCall.field(\"arguments\");\n if (!args) {\n return null;\n }\n\n const configArg = args.children().find((child) => child.kind() === \"object\");\n if (!configArg) {\n // serving() called with no args or non-object arg\n return null;\n }\n\n // Find the \"endpoints\" property in the config object\n const endpointsPair = findProperty(configArg, \"endpoints\");\n if (!endpointsPair) {\n // Config object has no \"endpoints\" property (e.g. serving({ timeout: 5000 }))\n return null;\n }\n\n // Get the value of the endpoints property\n const endpointsValue = getPropertyValue(endpointsPair);\n if (!endpointsValue || endpointsValue.kind() !== \"object\") {\n // endpoints is a variable reference, not an inline object\n logger.debug(\n \"serving() endpoints is not an inline object literal in %s. \" +\n \"Pass endpoints explicitly via appKitServingTypesPlugin({ endpoints }) in vite.config.ts.\",\n serverFilePath,\n );\n return null;\n }\n\n // Extract each endpoint entry\n const endpoints: Record<string, EndpointConfig> = {};\n const pairs = endpointsValue\n .children()\n .filter((child) => child.kind() === \"pair\");\n\n for (const pair of pairs) {\n const entry = extractEndpointEntry(pair);\n if (entry) {\n endpoints[entry.alias] = entry.config;\n }\n }\n\n if (Object.keys(endpoints).length === 0) {\n return null;\n }\n\n logger.debug(\n \"Extracted %d endpoint(s) from %s: %s\",\n Object.keys(endpoints).length,\n serverFilePath,\n Object.keys(endpoints).join(\", \"),\n );\n\n return endpoints;\n}\n\n/**\n * Find the serving() call expression in the AST.\n * Looks for call expressions where the callee identifier is \"serving\".\n */\nfunction findServingCall(root: SgNode): SgNode | null {\n const callExpressions = root.findAll({\n rule: { kind: \"call_expression\" },\n });\n\n for (const call of callExpressions) {\n const callee = call.children()[0];\n if (callee?.kind() === \"identifier\" && callee.text() === \"serving\") {\n return call;\n }\n }\n\n return null;\n}\n\n/**\n * Find a property (pair node) with the given key name in an object expression.\n */\nfunction findProperty(objectNode: SgNode, propertyName: string): SgNode | null {\n const pairs = objectNode\n .children()\n .filter((child) => child.kind() === \"pair\");\n\n for (const pair of pairs) {\n const key = pair.children()[0];\n if (!key) continue;\n\n const keyText =\n key.kind() === \"property_identifier\"\n ? key.text()\n : key.kind() === \"string\"\n ? key.text().replace(/^['\"]|['\"]$/g, \"\")\n : null;\n\n if (keyText === propertyName) {\n return pair;\n }\n }\n\n return null;\n}\n\n/**\n * Get the value node from a pair (property: value).\n * The value is typically the last meaningful child after the colon.\n */\nfunction getPropertyValue(pairNode: SgNode): SgNode | null {\n const children = pairNode.children();\n // pair children: [key, \":\", value]\n return children.length >= 3 ? children[children.length - 1] : null;\n}\n\n/**\n * Extract a single endpoint entry from a pair node like:\n * `demo: { env: \"DATABRICKS_SERVING_ENDPOINT_NAME\", servedModel: \"my-model\" }`\n */\nfunction extractEndpointEntry(\n pair: SgNode,\n): { alias: string; config: EndpointConfig } | null {\n const children = pair.children();\n if (children.length < 3) return null;\n\n // Get alias name (the key)\n const keyNode = children[0];\n const alias =\n keyNode.kind() === \"property_identifier\"\n ? keyNode.text()\n : keyNode.kind() === \"string\"\n ? keyNode.text().replace(/^['\"]|['\"]$/g, \"\")\n : null;\n\n if (!alias) return null;\n\n // Get the value (should be an object like { env: \"...\" })\n const valueNode = children[children.length - 1];\n if (valueNode.kind() !== \"object\") return null;\n\n // Extract env field\n const envPair = findProperty(valueNode, \"env\");\n if (!envPair) return null;\n\n const envValue = getPropertyValue(envPair);\n if (!envValue || envValue.kind() !== \"string\") return null;\n\n const env = envValue.text().replace(/^['\"]|['\"]$/g, \"\");\n\n // Extract optional servedModel field\n const config: EndpointConfig = { env };\n const servedModelPair = findProperty(valueNode, \"servedModel\");\n if (servedModelPair) {\n const servedModelValue = getPropertyValue(servedModelPair);\n if (servedModelValue?.kind() === \"string\") {\n config.servedModel = servedModelValue.text().replace(/^['\"]|['\"]$/g, \"\");\n }\n }\n\n return { alias, config };\n}\n"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { EndpointConfig } from "../../plugins/serving/types.js";
2
+ import { Plugin } from "vite";
3
+
4
+ //#region src/type-generator/serving/vite-plugin.d.ts
5
+ interface AppKitServingTypesPluginOptions {
6
+ /** Path to the output .d.ts file (relative to client root). Default: "src/appKitServingTypes.d.ts" */
7
+ outFile?: string;
8
+ /** Endpoint config override. If omitted, auto-discovers from the server file or falls back to DATABRICKS_SERVING_ENDPOINT_NAME env var. */
9
+ endpoints?: Record<string, EndpointConfig>;
10
+ }
11
+ /**
12
+ * Vite plugin to generate TypeScript types for AppKit serving endpoints.
13
+ * Fetches OpenAPI schemas from Databricks and generates a .d.ts with
14
+ * ServingEndpointRegistry module augmentation.
15
+ *
16
+ * Endpoint discovery order:
17
+ * 1. Explicit `endpoints` option (override)
18
+ * 2. AST extraction from server file (server/index.ts or server/server.ts)
19
+ * 3. DATABRICKS_SERVING_ENDPOINT_NAME env var (single default endpoint)
20
+ */
21
+ declare function appKitServingTypesPlugin(options?: AppKitServingTypesPluginOptions): Plugin;
22
+ //#endregion
23
+ export { appKitServingTypesPlugin };
24
+ //# sourceMappingURL=vite-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-plugin.d.ts","names":[],"sources":["../../../src/type-generator/serving/vite-plugin.ts"],"mappings":";;;;UAYU,+BAAA;;EAER,OAAA;EAFuC;EAIvC,SAAA,GAAY,MAAA,SAAe,cAAA;AAAA;;;;;;;AAa7B;;;;iBAAgB,wBAAA,CACd,OAAA,GAAU,+BAAA,GACT,MAAA"}
@@ -0,0 +1,60 @@
1
+ import { createLogger } from "../../logging/logger.js";
2
+ import { generateServingTypes } from "../index.js";
3
+ import { extractServingEndpoints, findServerFile } from "./server-file-extractor.js";
4
+ import path from "node:path";
5
+
6
+ //#region src/type-generator/serving/vite-plugin.ts
7
+ const logger = createLogger("type-generator:serving:vite-plugin");
8
+ /**
9
+ * Vite plugin to generate TypeScript types for AppKit serving endpoints.
10
+ * Fetches OpenAPI schemas from Databricks and generates a .d.ts with
11
+ * ServingEndpointRegistry module augmentation.
12
+ *
13
+ * Endpoint discovery order:
14
+ * 1. Explicit `endpoints` option (override)
15
+ * 2. AST extraction from server file (server/index.ts or server/server.ts)
16
+ * 3. DATABRICKS_SERVING_ENDPOINT_NAME env var (single default endpoint)
17
+ */
18
+ function appKitServingTypesPlugin(options) {
19
+ let outFile;
20
+ let projectRoot;
21
+ async function generate() {
22
+ try {
23
+ let endpoints = options?.endpoints;
24
+ if (!endpoints) {
25
+ const serverFile = findServerFile(projectRoot);
26
+ if (serverFile) endpoints = extractServingEndpoints(serverFile) ?? void 0;
27
+ }
28
+ await generateServingTypes({
29
+ outFile,
30
+ endpoints,
31
+ noCache: false
32
+ });
33
+ } catch (error) {
34
+ if (process.env.NODE_ENV === "production") throw error;
35
+ logger.error("Error generating serving types: %O", error);
36
+ }
37
+ }
38
+ return {
39
+ name: "appkit-serving-types",
40
+ apply() {
41
+ if (options?.endpoints && Object.keys(options.endpoints).length > 0) return true;
42
+ if (process.env.DATABRICKS_SERVING_ENDPOINT_NAME) return true;
43
+ if (findServerFile(process.cwd())) return true;
44
+ if (findServerFile(path.resolve(process.cwd(), ".."))) return true;
45
+ logger.debug("No serving endpoints configured. Skipping type generation.");
46
+ return false;
47
+ },
48
+ configResolved(config) {
49
+ projectRoot = path.resolve(config.root, "..");
50
+ outFile = path.resolve(config.root, options?.outFile ?? "src/appKitServingTypes.d.ts");
51
+ },
52
+ async buildStart() {
53
+ await generate();
54
+ }
55
+ };
56
+ }
57
+
58
+ //#endregion
59
+ export { appKitServingTypesPlugin };
60
+ //# sourceMappingURL=vite-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-plugin.js","names":[],"sources":["../../../src/type-generator/serving/vite-plugin.ts"],"sourcesContent":["import path from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport { createLogger } from \"../../logging/logger\";\nimport type { EndpointConfig } from \"../../plugins/serving/types\";\nimport { generateServingTypes } from \"../index\";\nimport {\n extractServingEndpoints,\n findServerFile,\n} from \"./server-file-extractor\";\n\nconst logger = createLogger(\"type-generator:serving:vite-plugin\");\n\ninterface AppKitServingTypesPluginOptions {\n /** Path to the output .d.ts file (relative to client root). Default: \"src/appKitServingTypes.d.ts\" */\n outFile?: string;\n /** Endpoint config override. If omitted, auto-discovers from the server file or falls back to DATABRICKS_SERVING_ENDPOINT_NAME env var. */\n endpoints?: Record<string, EndpointConfig>;\n}\n\n/**\n * Vite plugin to generate TypeScript types for AppKit serving endpoints.\n * Fetches OpenAPI schemas from Databricks and generates a .d.ts with\n * ServingEndpointRegistry module augmentation.\n *\n * Endpoint discovery order:\n * 1. Explicit `endpoints` option (override)\n * 2. AST extraction from server file (server/index.ts or server/server.ts)\n * 3. DATABRICKS_SERVING_ENDPOINT_NAME env var (single default endpoint)\n */\nexport function appKitServingTypesPlugin(\n options?: AppKitServingTypesPluginOptions,\n): Plugin {\n let outFile: string;\n let projectRoot: string;\n\n async function generate() {\n try {\n // Resolve endpoints: explicit option > server file AST > env var fallback (handled by generator)\n let endpoints = options?.endpoints;\n if (!endpoints) {\n const serverFile = findServerFile(projectRoot);\n if (serverFile) {\n endpoints = extractServingEndpoints(serverFile) ?? undefined;\n }\n }\n\n await generateServingTypes({\n outFile,\n endpoints,\n noCache: false,\n });\n } catch (error) {\n if (process.env.NODE_ENV === \"production\") {\n throw error;\n }\n logger.error(\"Error generating serving types: %O\", error);\n }\n }\n\n return {\n name: \"appkit-serving-types\",\n\n apply() {\n // Fast checks — no AST parsing here\n if (options?.endpoints && Object.keys(options.endpoints).length > 0) {\n return true;\n }\n\n if (process.env.DATABRICKS_SERVING_ENDPOINT_NAME) {\n return true;\n }\n\n // Check if a server file exists (may contain serving() config)\n // Use process.cwd() for apply() since configResolved hasn't run yet\n if (findServerFile(process.cwd())) {\n return true;\n }\n\n // Also check parent dir (for when cwd is client/)\n const parentDir = path.resolve(process.cwd(), \"..\");\n if (findServerFile(parentDir)) {\n return true;\n }\n\n logger.debug(\n \"No serving endpoints configured. Skipping type generation.\",\n );\n return false;\n },\n\n configResolved(config) {\n // Resolve project root: go up one level from Vite root (client dir)\n // This handles both:\n // - pnpm dev: process.cwd() is app root, config.root is client/\n // - pnpm build: process.cwd() is client/ (cd client && vite build), config.root is client/\n projectRoot = path.resolve(config.root, \"..\");\n outFile = path.resolve(\n config.root,\n options?.outFile ?? \"src/appKitServingTypes.d.ts\",\n );\n },\n\n async buildStart() {\n await generate();\n },\n\n // No configureServer / watcher — schemas change on endpoint redeploy, not on file edit\n };\n}\n"],"mappings":";;;;;;AAUA,MAAM,SAAS,aAAa,qCAAqC;;;;;;;;;;;AAmBjE,SAAgB,yBACd,SACQ;CACR,IAAI;CACJ,IAAI;CAEJ,eAAe,WAAW;AACxB,MAAI;GAEF,IAAI,YAAY,SAAS;AACzB,OAAI,CAAC,WAAW;IACd,MAAM,aAAa,eAAe,YAAY;AAC9C,QAAI,WACF,aAAY,wBAAwB,WAAW,IAAI;;AAIvD,SAAM,qBAAqB;IACzB;IACA;IACA,SAAS;IACV,CAAC;WACK,OAAO;AACd,OAAI,QAAQ,IAAI,aAAa,aAC3B,OAAM;AAER,UAAO,MAAM,sCAAsC,MAAM;;;AAI7D,QAAO;EACL,MAAM;EAEN,QAAQ;AAEN,OAAI,SAAS,aAAa,OAAO,KAAK,QAAQ,UAAU,CAAC,SAAS,EAChE,QAAO;AAGT,OAAI,QAAQ,IAAI,iCACd,QAAO;AAKT,OAAI,eAAe,QAAQ,KAAK,CAAC,CAC/B,QAAO;AAKT,OAAI,eADc,KAAK,QAAQ,QAAQ,KAAK,EAAE,KAAK,CACtB,CAC3B,QAAO;AAGT,UAAO,MACL,6DACD;AACD,UAAO;;EAGT,eAAe,QAAQ;AAKrB,iBAAc,KAAK,QAAQ,OAAO,MAAM,KAAK;AAC7C,aAAU,KAAK,QACb,OAAO,MACP,SAAS,WAAW,8BACrB;;EAGH,MAAM,aAAa;AACjB,SAAM,UAAU;;EAInB"}
@@ -319,13 +319,18 @@ BasePlugin.clientConfig
319
319
  protected execute<T>(
320
320
  fn: (signal?: AbortSignal) => Promise<T>,
321
321
  options: PluginExecutionSettings,
322
- userKey?: string): Promise<T | undefined>;
322
+ userKey?: string): Promise<ExecutionResult<T>>;
323
323
 
324
324
  ```
325
325
 
326
326
  Execute a function with the plugin's interceptor chain.
327
327
 
328
- All errors are caught and `undefined` is returned (production-safe). Route handlers should check for `undefined` and respond with an appropriate error status.
328
+ Returns an [ExecutionResult](./docs/api/appkit/TypeAlias.ExecutionResult.md) discriminated union:
329
+
330
+ * `{ ok: true, data: T }` on success
331
+ * `{ ok: false, status: number, message: string }` on failure
332
+
333
+ Errors are never thrown — the method is production-safe.
329
334
 
330
335
  #### Type Parameters[​](#type-parameters-1 "Direct link to Type Parameters")
331
336
 
@@ -343,7 +348,7 @@ All errors are caught and `undefined` is returned (production-safe). Route handl
343
348
 
344
349
  #### Returns[​](#returns-4 "Direct link to Returns")
345
350
 
346
- `Promise`<`T` | `undefined`>
351
+ `Promise`<[`ExecutionResult`](./docs/api/appkit/TypeAlias.ExecutionResult.md)<`T`>>
347
352
 
348
353
  ***
349
354
 
@@ -0,0 +1,24 @@
1
+ # Function: appKitServingTypesPlugin()
2
+
3
+ ```ts
4
+ function appKitServingTypesPlugin(options?: AppKitServingTypesPluginOptions): Plugin$1;
5
+
6
+ ```
7
+
8
+ Vite plugin to generate TypeScript types for AppKit serving endpoints. Fetches OpenAPI schemas from Databricks and generates a .d.ts with ServingEndpointRegistry module augmentation.
9
+
10
+ Endpoint discovery order:
11
+
12
+ 1. Explicit `endpoints` option (override)
13
+ 2. AST extraction from server file (server/index.ts or server/server.ts)
14
+ 3. DATABRICKS\_SERVING\_ENDPOINT\_NAME env var (single default endpoint)
15
+
16
+ ## Parameters[​](#parameters "Direct link to Parameters")
17
+
18
+ | Parameter | Type |
19
+ | ---------- | --------------------------------- |
20
+ | `options?` | `AppKitServingTypesPluginOptions` |
21
+
22
+ ## Returns[​](#returns "Direct link to Returns")
23
+
24
+ `Plugin$1`
@@ -0,0 +1,22 @@
1
+ # Function: extractServingEndpoints()
2
+
3
+ ```ts
4
+ function extractServingEndpoints(serverFilePath: string):
5
+ | Record<string, EndpointConfig>
6
+ | null;
7
+
8
+ ```
9
+
10
+ Extract serving endpoint config from a server file by AST-parsing it. Looks for `serving({ endpoints: { alias: { env: "..." }, ... } })` calls and extracts the endpoint alias names and their environment variable mappings.
11
+
12
+ ## Parameters[​](#parameters "Direct link to Parameters")
13
+
14
+ | Parameter | Type | Description |
15
+ | ---------------- | -------- | -------------------------------------- |
16
+ | `serverFilePath` | `string` | Absolute path to the server entry file |
17
+
18
+ ## Returns[​](#returns "Direct link to Returns")
19
+
20
+ \| `Record`<`string`, [`EndpointConfig`](./docs/api/appkit/Interface.EndpointConfig.md)> | `null`
21
+
22
+ Extracted endpoint config, or null if not found or not extractable
@@ -0,0 +1,20 @@
1
+ # Function: findServerFile()
2
+
3
+ ```ts
4
+ function findServerFile(basePath: string): string | null;
5
+
6
+ ```
7
+
8
+ Find the server entry file by checking candidate paths in order.
9
+
10
+ ## Parameters[​](#parameters "Direct link to Parameters")
11
+
12
+ | Parameter | Type | Description |
13
+ | ---------- | -------- | ------------------------------------- |
14
+ | `basePath` | `string` | Project root directory to search from |
15
+
16
+ ## Returns[​](#returns "Direct link to Returns")
17
+
18
+ `string` | `null`
19
+
20
+ Absolute path to the server file, or null if none found
@@ -0,0 +1,23 @@
1
+ # Interface: EndpointConfig
2
+
3
+ ## Properties[​](#properties "Direct link to Properties")
4
+
5
+ ### env[​](#env "Direct link to env")
6
+
7
+ ```ts
8
+ env: string;
9
+
10
+ ```
11
+
12
+ Environment variable holding the endpoint name.
13
+
14
+ ***
15
+
16
+ ### servedModel?[​](#servedmodel "Direct link to servedModel?")
17
+
18
+ ```ts
19
+ optional servedModel: string;
20
+
21
+ ```
22
+
23
+ Target a specific served model (bypasses traffic routing).
@@ -0,0 +1,30 @@
1
+ # Interface: ServingEndpointEntry
2
+
3
+ Shape of a single registry entry.
4
+
5
+ ## Properties[​](#properties "Direct link to Properties")
6
+
7
+ ### chunk[​](#chunk "Direct link to chunk")
8
+
9
+ ```ts
10
+ chunk: unknown;
11
+
12
+ ```
13
+
14
+ ***
15
+
16
+ ### request[​](#request "Direct link to request")
17
+
18
+ ```ts
19
+ request: Record<string, unknown>;
20
+
21
+ ```
22
+
23
+ ***
24
+
25
+ ### response[​](#response "Direct link to response")
26
+
27
+ ```ts
28
+ response: unknown;
29
+
30
+ ```
@@ -0,0 +1,3 @@
1
+ # Interface: ServingEndpointRegistry
2
+
3
+ Registry interface for serving endpoint type generation. Empty by default — augmented by the Vite type generator's `.d.ts` output via module augmentation. When populated, provides autocomplete for alias names and typed request/response/chunk per endpoint.