@databricks/appkit 0.23.0 → 0.25.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 (88) hide show
  1. package/CLAUDE.md +9 -1
  2. package/dist/appkit/package.js +1 -1
  3. package/dist/cache/index.js.map +1 -1
  4. package/dist/cli/commands/docs.js +7 -1
  5. package/dist/cli/commands/docs.js.map +1 -1
  6. package/dist/cli/commands/generate-types.js +20 -10
  7. package/dist/cli/commands/generate-types.js.map +1 -1
  8. package/dist/cli/commands/lint.js +3 -1
  9. package/dist/cli/commands/lint.js.map +1 -1
  10. package/dist/cli/commands/plugin/add-resource/add-resource.js +73 -8
  11. package/dist/cli/commands/plugin/add-resource/add-resource.js.map +1 -1
  12. package/dist/cli/commands/plugin/create/create.js +164 -20
  13. package/dist/cli/commands/plugin/create/create.js.map +1 -1
  14. package/dist/cli/commands/plugin/create/resource-defaults.js +5 -1
  15. package/dist/cli/commands/plugin/create/resource-defaults.js.map +1 -1
  16. package/dist/cli/commands/plugin/index.js +7 -1
  17. package/dist/cli/commands/plugin/index.js.map +1 -1
  18. package/dist/cli/commands/plugin/list/list.js +7 -1
  19. package/dist/cli/commands/plugin/list/list.js.map +1 -1
  20. package/dist/cli/commands/plugin/sync/sync.js +27 -14
  21. package/dist/cli/commands/plugin/sync/sync.js.map +1 -1
  22. package/dist/cli/commands/plugin/validate/validate.js +39 -9
  23. package/dist/cli/commands/plugin/validate/validate.js.map +1 -1
  24. package/dist/cli/commands/setup.js +6 -5
  25. package/dist/cli/commands/setup.js.map +1 -1
  26. package/dist/connectors/index.js +1 -0
  27. package/dist/connectors/lakebase/index.js.map +1 -1
  28. package/dist/connectors/lakebase-v1/client.js.map +1 -1
  29. package/dist/connectors/vector-search/client.js +9 -0
  30. package/dist/connectors/vector-search/client.js.map +1 -0
  31. package/dist/connectors/vector-search/index.js +3 -0
  32. package/dist/context/execution-context.js +1 -7
  33. package/dist/context/execution-context.js.map +1 -1
  34. package/dist/context/index.js +1 -1
  35. package/dist/context/index.js.map +1 -1
  36. package/dist/index.d.ts +2 -1
  37. package/dist/index.js +2 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/plugin/dev-reader.js.map +1 -1
  40. package/dist/plugins/files/plugin.d.ts +46 -15
  41. package/dist/plugins/files/plugin.d.ts.map +1 -1
  42. package/dist/plugins/files/plugin.js +182 -103
  43. package/dist/plugins/files/plugin.js.map +1 -1
  44. package/dist/plugins/files/policy.d.ts +45 -0
  45. package/dist/plugins/files/policy.d.ts.map +1 -0
  46. package/dist/plugins/files/policy.js +63 -0
  47. package/dist/plugins/files/policy.js.map +1 -0
  48. package/dist/plugins/files/types.d.ts +16 -8
  49. package/dist/plugins/files/types.d.ts.map +1 -1
  50. package/dist/plugins/server/vite-dev-server.js.map +1 -1
  51. package/dist/plugins/serving/serving.d.ts.map +1 -1
  52. package/dist/plugins/serving/serving.js +22 -8
  53. package/dist/plugins/serving/serving.js.map +1 -1
  54. package/dist/plugins/serving/types.d.ts +11 -10
  55. package/dist/plugins/serving/types.d.ts.map +1 -1
  56. package/dist/type-generator/index.js +13 -1
  57. package/dist/type-generator/index.js.map +1 -1
  58. package/dist/type-generator/migration.js +155 -0
  59. package/dist/type-generator/migration.js.map +1 -0
  60. package/dist/type-generator/serving/generator.js +22 -1
  61. package/dist/type-generator/serving/generator.js.map +1 -1
  62. package/dist/type-generator/serving/vite-plugin.d.ts +1 -1
  63. package/dist/type-generator/serving/vite-plugin.js +2 -2
  64. package/dist/type-generator/serving/vite-plugin.js.map +1 -1
  65. package/dist/type-generator/vite-plugin.d.ts.map +1 -1
  66. package/dist/type-generator/vite-plugin.js +3 -4
  67. package/dist/type-generator/vite-plugin.js.map +1 -1
  68. package/docs/api/appkit/Class.PolicyDeniedError.md +52 -0
  69. package/docs/api/appkit/Interface.FilePolicyUser.md +23 -0
  70. package/docs/api/appkit/Interface.FileResource.md +36 -0
  71. package/docs/api/appkit/TypeAlias.FileAction.md +18 -0
  72. package/docs/api/appkit/TypeAlias.FilePolicy.md +20 -0
  73. package/docs/api/appkit/TypeAlias.ServingFactory.md +9 -5
  74. package/docs/api/appkit/Variable.READ_ACTIONS.md +8 -0
  75. package/docs/api/appkit/Variable.WRITE_ACTIONS.md +8 -0
  76. package/docs/api/appkit.md +19 -12
  77. package/docs/development/type-generation.md +6 -5
  78. package/docs/faq.md +8 -8
  79. package/docs/plugins/analytics.md +1 -1
  80. package/docs/plugins/custom-plugins.md +4 -0
  81. package/docs/plugins/execution-context.md +0 -1
  82. package/docs/plugins/files.md +150 -2
  83. package/docs/plugins/{serving.md → model-serving.md} +1 -1
  84. package/docs/plugins/plugin-management.md +22 -6
  85. package/docs/plugins/vector-search.md +247 -0
  86. package/llms.txt +9 -1
  87. package/package.json +1 -1
  88. package/sbom.cdx.json +1 -1
@@ -37,22 +37,23 @@ interface ServingEndpointMethods<TRequest extends Record<string, unknown> = Reco
37
37
  type ServingEndpointHandle<TRequest extends Record<string, unknown> = Record<string, unknown>, TResponse = unknown> = ServingEndpointMethods<TRequest, TResponse> & {
38
38
  asUser: (req: express$1.Request) => ServingEndpointMethods<TRequest, TResponse>;
39
39
  };
40
+ /** True when T is a union of 2+ members; false for a single literal type. */
41
+ type IsUnion<T, C = T> = T extends C ? ([C] extends [T] ? false : true) : never;
40
42
  /**
41
43
  * Factory function returned by `AppKit.serving`.
42
44
  *
43
- * This is a conditional type that adapts based on whether `ServingEndpointRegistry`
44
- * has been populated via module augmentation (generated by `appKitServingTypesPlugin()`):
45
+ * Adapts based on the `ServingEndpointRegistry` state:
45
46
  *
46
- * - **Registry empty (default):** `(alias?: string) => ServingEndpointHandle` —
47
- * accepts any alias string with untyped request/response.
48
- * - **Registry populated:** `<K>(alias: K) => ServingEndpointHandle<...>`
49
- * restricts `alias` to known endpoint keys and infers typed request/response
50
- * from the registry entry.
47
+ * - **Empty (default):** `(alias?: string) => ServingEndpointHandle` — any string, untyped.
48
+ * - **Single key:** alias optional `serving()` returns the typed handle for the only endpoint.
49
+ * - **Multiple keys:** alias required must specify which endpoint.
51
50
  *
52
- * Run `appKitServingTypesPlugin()` in your Vite config to generate the registry
53
- * augmentation and enable full type safety.
51
+ * Run `npx appkit generate-types` or start the dev server to generate the registry.
54
52
  */
55
- type ServingFactory = keyof ServingEndpointRegistry extends never ? (alias?: string) => ServingEndpointHandle : <K extends keyof ServingEndpointRegistry>(alias: K) => ServingEndpointHandle<ServingEndpointRegistry[K]["request"], ServingEndpointRegistry[K]["response"]>;
53
+ type ServingFactory = keyof ServingEndpointRegistry extends never ? (alias?: string) => ServingEndpointHandle : true extends IsUnion<keyof ServingEndpointRegistry> ? <K extends keyof ServingEndpointRegistry>(alias: K) => ServingEndpointHandle<ServingEndpointRegistry[K]["request"], ServingEndpointRegistry[K]["response"]> : {
54
+ <K extends keyof ServingEndpointRegistry>(alias: K): ServingEndpointHandle<ServingEndpointRegistry[K]["request"], ServingEndpointRegistry[K]["response"]>;
55
+ (): ServingEndpointHandle<ServingEndpointRegistry[keyof ServingEndpointRegistry]["request"], ServingEndpointRegistry[keyof ServingEndpointRegistry]["response"]>;
56
+ };
56
57
  //#endregion
57
58
  export { EndpointConfig, IServingConfig, ServingEndpointEntry, ServingEndpointHandle, ServingEndpointMethods, ServingEndpointRegistry, ServingFactory };
58
59
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/plugins/serving/types.ts"],"mappings":";;;;;UAEiB,cAAA;;EAEf,GAAA;;EAEA,WAAA;AAAA;AAAA,UAGe,cAAA,SAAuB,gBAAA;EALtC;EAOA,SAAA,GAAY,MAAA,SAAe,cAAA;EAFZ;EAIf,OAAA;;EAEA,UAAA;AAAA;;;;;;UASe,uBAAA;;UAGA,oBAAA;EACf,OAAA,EAAS,MAAA;EACT,QAAA;EACA,KAAA;AAAA;;UAIe,sBAAA,kBACE,MAAA,oBAA0B,MAAA;EAG3C,MAAA,GAAS,IAAA,EAAM,QAAA,KAAa,OAAA,CAAQ,SAAA;AAAA;;KAI1B,qBAAA,kBACO,MAAA,oBAA0B,MAAA,0CAEzC,sBAAA,CAAuB,QAAA,EAAU,SAAA;EACnC,MAAA,GACE,GAAA,EAFsB,SAAA,CAEC,OAAA,KACpB,sBAAA,CAAuB,QAAA,EAAU,SAAA;AAAA;;;;;;AAdxC;;;;;;;;;;KAgCY,cAAA,SAAuB,uBAAA,kBAC9B,KAAA,cAAmB,qBAAA,oBACH,uBAAA,EACf,KAAA,EAAO,CAAA,KACJ,qBAAA,CACH,uBAAA,CAAwB,CAAA,cACxB,uBAAA,CAAwB,CAAA"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/plugins/serving/types.ts"],"mappings":";;;;;UAEiB,cAAA;;EAEf,GAAA;;EAEA,WAAA;AAAA;AAAA,UAGe,cAAA,SAAuB,gBAAA;EALtC;EAOA,SAAA,GAAY,MAAA,SAAe,cAAA;EAFZ;EAIf,OAAA;;EAEA,UAAA;AAAA;;;;;;UASe,uBAAA;;UAGA,oBAAA;EACf,OAAA,EAAS,MAAA;EACT,QAAA;EACA,KAAA;AAAA;;UAIe,sBAAA,kBACE,MAAA,oBAA0B,MAAA;EAG3C,MAAA,GAAS,IAAA,EAAM,QAAA,KAAa,OAAA,CAAQ,SAAA;AAAA;;KAI1B,qBAAA,kBACO,MAAA,oBAA0B,MAAA,0CAEzC,sBAAA,CAAuB,QAAA,EAAU,SAAA;EACnC,MAAA,GACE,GAAA,EAFsB,SAAA,CAEC,OAAA,KACpB,sBAAA,CAAuB,QAAA,EAAU,SAAA;AAAA;;KAInC,OAAA,QAAe,CAAA,IAAK,CAAA,SAAU,CAAA,KAAM,CAAA,WAAY,CAAA;;;;AAlBrD;;;;;;;;KA+BY,cAAA,SAAuB,uBAAA,kBAE9B,KAAA,cAAmB,qBAAA,gBACP,OAAA,OAAc,uBAAA,qBAER,uBAAA,EACf,KAAA,EAAO,CAAA,KACJ,qBAAA,CACH,uBAAA,CAAwB,CAAA,cACxB,uBAAA,CAAwB,CAAA;EAAA,iBAIP,uBAAA,EACf,KAAA,EAAO,CAAA,GACN,qBAAA,CACD,uBAAA,CAAwB,CAAA,cACxB,uBAAA,CAAwB,CAAA;EAAA,IAEtB,qBAAA,CACF,uBAAA,OAA8B,uBAAA,cAC9B,uBAAA,OAA8B,uBAAA;AAAA"}
@@ -1,7 +1,9 @@
1
1
  import { createLogger } from "../logging/logger.js";
2
+ import { migrateProjectConfig, removeOldGeneratedTypes, resolveProjectRoot } from "./migration.js";
2
3
  import { generateQueriesFromDescribe } from "./query-registry.js";
3
4
  import { generateServingTypes as generateServingTypes$1 } from "./serving/generator.js";
4
5
  import fs from "node:fs/promises";
6
+ import path from "node:path";
5
7
  import dotenv from "dotenv";
6
8
 
7
9
  //#region src/type-generator/index.ts
@@ -36,6 +38,7 @@ declare module "@databricks/appkit-ui/react" {
36
38
  */
37
39
  async function generateFromEntryPoint(options) {
38
40
  const { outFile, queryFolder, warehouseId, noCache } = options;
41
+ const projectRoot = resolveProjectRoot(outFile);
39
42
  logger.debug("Starting type generation...");
40
43
  let queryRegistry = [];
41
44
  if (queryFolder) queryRegistry = await generateQueriesFromDescribe(queryFolder, warehouseId, { noCache });
@@ -50,11 +53,20 @@ async function generateFromEntryPoint(options) {
50
53
  ].join("\n"));
51
54
  }
52
55
  const typeDeclarations = generateTypeDeclarations(queryRegistry);
56
+ await fs.mkdir(path.dirname(outFile), { recursive: true });
53
57
  await fs.writeFile(outFile, typeDeclarations, "utf-8");
58
+ await removeOldGeneratedTypes(projectRoot, "appKitTypes.d.ts");
59
+ await migrateProjectConfig(projectRoot);
54
60
  logger.debug("Type generation complete!");
55
61
  }
56
62
  const generateServingTypes = generateServingTypes$1;
63
+ /** Directory name for generated AppKit type declaration files. */
64
+ const TYPES_DIR = "appkit-types";
65
+ /** Default filename for analytics query type declarations. */
66
+ const ANALYTICS_TYPES_FILE = "analytics.d.ts";
67
+ /** Default filename for serving endpoint type declarations. */
68
+ const SERVING_TYPES_FILE = "serving.d.ts";
57
69
 
58
70
  //#endregion
59
- export { generateFromEntryPoint, generateServingTypes };
71
+ export { ANALYTICS_TYPES_FILE, SERVING_TYPES_FILE, TYPES_DIR, generateFromEntryPoint, generateServingTypes };
60
72
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["generateServingTypesImpl"],"sources":["../../src/type-generator/index.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport dotenv from \"dotenv\";\nimport { createLogger } from \"../logging/logger\";\nimport { generateQueriesFromDescribe } from \"./query-registry\";\nimport { generateServingTypes as generateServingTypesImpl } from \"./serving/generator\";\nimport type { QuerySchema } from \"./types\";\n\ndotenv.config();\n\nconst logger = createLogger(\"type-generator\");\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 * 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.querySchemaFile - optional path to query schema file (e.g. config/queries/schema.ts)\n */\nexport async function generateFromEntryPoint(options: {\n outFile: string;\n queryFolder?: string;\n warehouseId: string;\n noCache?: boolean;\n}) {\n const { outFile, queryFolder, warehouseId, noCache } = options;\n\n logger.debug(\"Starting type generation...\");\n\n let queryRegistry: QuerySchema[] = [];\n if (queryFolder)\n queryRegistry = await generateQueriesFromDescribe(\n queryFolder,\n warehouseId,\n {\n noCache,\n },\n );\n\n const failedQueries = queryRegistry.filter((q) =>\n q.type.includes(\"result: unknown\"),\n );\n if (failedQueries.length > 0) {\n const names = failedQueries.map((q) => q.name).join(\", \");\n throw new Error(\n [\n `Type generation failed: ${failedQueries.length} ${failedQueries.length === 1 ? \"query\" : \"queries\"} could not be described: ${names}.`,\n `DESCRIBE QUERY failed for these queries — see the error codes above for details.`,\n `Common causes: SQL syntax errors, missing tables/views, or warehouse format incompatibilities.`,\n `To debug: run the failing query directly in a SQL editor against warehouse ${warehouseId}.`,\n ].join(\"\\n\"),\n );\n }\n\n const typeDeclarations = generateTypeDeclarations(queryRegistry);\n\n await fs.writeFile(outFile, typeDeclarations, \"utf-8\");\n\n logger.debug(\"Type generation complete!\");\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"],"mappings":";;;;;;;AAOA,OAAO,QAAQ;AAEf,MAAM,SAAS,aAAa,iBAAiB;;;;;;;AAQ7C,SAAS,yBAAyB,eAA8B,EAAE,EAAU;CAC1E,MAAM,eAAe,aAClB,KAAK,EAAE,MAAM,WAAW;AAKvB,SAAO,OAAO,KAAK,IAJE,KAClB,MAAM,KAAK,CACX,KAAK,MAAM,MAAO,MAAM,IAAI,OAAO,OAAO,OAAQ,CAClD,KAAK,KAAK;GAEb,CACD,KAAK,MAAM;AAId,QAAO;;;;;;6BAFc,eAAe,KAAK,aAAa,SAAS,GAQvB;;;;;;;;;;;AAY1C,eAAsB,uBAAuB,SAK1C;CACD,MAAM,EAAE,SAAS,aAAa,aAAa,YAAY;AAEvD,QAAO,MAAM,8BAA8B;CAE3C,IAAI,gBAA+B,EAAE;AACrC,KAAI,YACF,iBAAgB,MAAM,4BACpB,aACA,aACA,EACE,SACD,CACF;CAEH,MAAM,gBAAgB,cAAc,QAAQ,MAC1C,EAAE,KAAK,SAAS,kBAAkB,CACnC;AACD,KAAI,cAAc,SAAS,GAAG;EAC5B,MAAM,QAAQ,cAAc,KAAK,MAAM,EAAE,KAAK,CAAC,KAAK,KAAK;AACzD,QAAM,IAAI,MACR;GACE,2BAA2B,cAAc,OAAO,GAAG,cAAc,WAAW,IAAI,UAAU,UAAU,2BAA2B,MAAM;GACrI;GACA;GACA,8EAA8E,YAAY;GAC3F,CAAC,KAAK,KAAK,CACb;;CAGH,MAAM,mBAAmB,yBAAyB,cAAc;AAEhE,OAAM,GAAG,UAAU,SAAS,kBAAkB,QAAQ;AAEtD,QAAO,MAAM,4BAA4B;;AAM3C,MAAa,uBAAuBA"}
1
+ {"version":3,"file":"index.js","names":["generateServingTypesImpl"],"sources":["../../src/type-generator/index.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport dotenv from \"dotenv\";\nimport { createLogger } from \"../logging/logger\";\nimport {\n migrateProjectConfig,\n removeOldGeneratedTypes,\n resolveProjectRoot,\n} from \"./migration\";\nimport { generateQueriesFromDescribe } from \"./query-registry\";\nimport { generateServingTypes as generateServingTypesImpl } from \"./serving/generator\";\nimport type { QuerySchema } from \"./types\";\n\ndotenv.config();\n\nconst logger = createLogger(\"type-generator\");\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 * 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.querySchemaFile - optional path to query schema file (e.g. config/queries/schema.ts)\n */\nexport async function generateFromEntryPoint(options: {\n outFile: string;\n queryFolder?: string;\n warehouseId: string;\n noCache?: boolean;\n}) {\n const { outFile, queryFolder, warehouseId, noCache } = options;\n const projectRoot = resolveProjectRoot(outFile);\n\n logger.debug(\"Starting type generation...\");\n\n let queryRegistry: QuerySchema[] = [];\n if (queryFolder)\n queryRegistry = await generateQueriesFromDescribe(\n queryFolder,\n warehouseId,\n {\n noCache,\n },\n );\n\n const failedQueries = queryRegistry.filter((q) =>\n q.type.includes(\"result: unknown\"),\n );\n if (failedQueries.length > 0) {\n const names = failedQueries.map((q) => q.name).join(\", \");\n throw new Error(\n [\n `Type generation failed: ${failedQueries.length} ${failedQueries.length === 1 ? \"query\" : \"queries\"} could not be described: ${names}.`,\n `DESCRIBE QUERY failed for these queries — see the error codes above for details.`,\n `Common causes: SQL syntax errors, missing tables/views, or warehouse format incompatibilities.`,\n `To debug: run the failing query directly in a SQL editor against warehouse ${warehouseId}.`,\n ].join(\"\\n\"),\n );\n }\n\n const typeDeclarations = generateTypeDeclarations(queryRegistry);\n\n await fs.mkdir(path.dirname(outFile), { recursive: true });\n await fs.writeFile(outFile, typeDeclarations, \"utf-8\");\n\n // One-time migration: remove old generated file and patch project configs\n await removeOldGeneratedTypes(projectRoot, \"appKitTypes.d.ts\");\n await migrateProjectConfig(projectRoot);\n\n logger.debug(\"Type generation complete!\");\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/** Directory name for generated AppKit type declaration files. */\nexport const TYPES_DIR = \"appkit-types\";\n/** Default filename for analytics query type declarations. */\nexport const ANALYTICS_TYPES_FILE = \"analytics.d.ts\";\n/** Default filename for serving endpoint type declarations. */\nexport const SERVING_TYPES_FILE = \"serving.d.ts\";\n"],"mappings":";;;;;;;;;AAaA,OAAO,QAAQ;AAEf,MAAM,SAAS,aAAa,iBAAiB;;;;;;;AAQ7C,SAAS,yBAAyB,eAA8B,EAAE,EAAU;CAC1E,MAAM,eAAe,aAClB,KAAK,EAAE,MAAM,WAAW;AAKvB,SAAO,OAAO,KAAK,IAJE,KAClB,MAAM,KAAK,CACX,KAAK,MAAM,MAAO,MAAM,IAAI,OAAO,OAAO,OAAQ,CAClD,KAAK,KAAK;GAEb,CACD,KAAK,MAAM;AAId,QAAO;;;;;;6BAFc,eAAe,KAAK,aAAa,SAAS,GAQvB;;;;;;;;;;;AAY1C,eAAsB,uBAAuB,SAK1C;CACD,MAAM,EAAE,SAAS,aAAa,aAAa,YAAY;CACvD,MAAM,cAAc,mBAAmB,QAAQ;AAE/C,QAAO,MAAM,8BAA8B;CAE3C,IAAI,gBAA+B,EAAE;AACrC,KAAI,YACF,iBAAgB,MAAM,4BACpB,aACA,aACA,EACE,SACD,CACF;CAEH,MAAM,gBAAgB,cAAc,QAAQ,MAC1C,EAAE,KAAK,SAAS,kBAAkB,CACnC;AACD,KAAI,cAAc,SAAS,GAAG;EAC5B,MAAM,QAAQ,cAAc,KAAK,MAAM,EAAE,KAAK,CAAC,KAAK,KAAK;AACzD,QAAM,IAAI,MACR;GACE,2BAA2B,cAAc,OAAO,GAAG,cAAc,WAAW,IAAI,UAAU,UAAU,2BAA2B,MAAM;GACrI;GACA;GACA,8EAA8E,YAAY;GAC3F,CAAC,KAAK,KAAK,CACb;;CAGH,MAAM,mBAAmB,yBAAyB,cAAc;AAEhE,OAAM,GAAG,MAAM,KAAK,QAAQ,QAAQ,EAAE,EAAE,WAAW,MAAM,CAAC;AAC1D,OAAM,GAAG,UAAU,SAAS,kBAAkB,QAAQ;AAGtD,OAAM,wBAAwB,aAAa,mBAAmB;AAC9D,OAAM,qBAAqB,YAAY;AAEvC,QAAO,MAAM,4BAA4B;;AAM3C,MAAa,uBAAuBA;;AAGpC,MAAa,YAAY;;AAEzB,MAAa,uBAAuB;;AAEpC,MAAa,qBAAqB"}
@@ -0,0 +1,155 @@
1
+ import { createLogger } from "../logging/logger.js";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import fs$1 from "node:fs";
5
+ import pc from "picocolors";
6
+
7
+ //#region src/type-generator/migration.ts
8
+ const logger = createLogger("type-generator:migration");
9
+ /**
10
+ * Derive project root from an outFile path.
11
+ * outFile is always `<projectRoot>/shared/appkit-types/<file>` — both the Vite plugins
12
+ * and the CLI construct it this way, so going up two levels is safe.
13
+ *
14
+ * Validates that the resolved root contains a package.json — if not, logs a warning
15
+ * so custom outFile paths don't silently operate on the wrong directory.
16
+ */
17
+ function resolveProjectRoot(outFile) {
18
+ const root = path.resolve(path.dirname(outFile), "..", "..");
19
+ if (!fs$1.existsSync(path.join(root, "package.json"))) logger.warn("Resolved project root %s has no package.json — migration may target the wrong directory. Check your outFile path: %s", root, outFile);
20
+ return root;
21
+ }
22
+ /**
23
+ * Remove old generated types from client/src/ (pre-shared/ location).
24
+ * Best-effort: silently ignores missing files.
25
+ */
26
+ async function removeOldGeneratedTypes(projectRoot, filename) {
27
+ const oldFile = path.join(projectRoot, "client", "src", filename);
28
+ try {
29
+ await fs.unlink(oldFile);
30
+ logger.debug("Removed old types at %s", oldFile);
31
+ } catch {}
32
+ }
33
+ const migratedProjects = /* @__PURE__ */ new Set();
34
+ /**
35
+ * One-time config migration: update tsconfig and package.json for shared/ types output.
36
+ * Idempotent — each sub-migration checks current file state and skips if already migrated.
37
+ * Deduplicates per project root so monorepo builds migrate each app independently.
38
+ * Opt-out: set `"appkit": { "autoMigrate": false }` in package.json.
39
+ */
40
+ async function migrateProjectConfig(projectRoot) {
41
+ const resolved = path.resolve(projectRoot);
42
+ if (migratedProjects.has(resolved)) return;
43
+ migratedProjects.add(resolved);
44
+ if (await isAutoMigrateDisabled(projectRoot)) {
45
+ logger.debug("Auto-migration disabled via package.json appkit.autoMigrate");
46
+ return;
47
+ }
48
+ const results = [];
49
+ results.push(...await migrateTsconfigClient(projectRoot));
50
+ results.push(...await migrateTsconfigServer(projectRoot));
51
+ results.push(...await migratePackageJsonScripts(projectRoot));
52
+ if (results.length > 0) printMigrationSummary(results);
53
+ }
54
+ async function isAutoMigrateDisabled(projectRoot) {
55
+ try {
56
+ const raw = await fs.readFile(path.join(projectRoot, "package.json"), "utf-8");
57
+ return JSON.parse(raw).appkit?.autoMigrate === false;
58
+ } catch {
59
+ return false;
60
+ }
61
+ }
62
+ /** Strip JSONC comments (block and line) so JSON.parse can handle tsconfig files. */
63
+ function stripJsonComments(text) {
64
+ return text.replace(/"(?:[^"\\]|\\.)*"|\/\*[\s\S]*?\*\/|\/\/.*/g, (match) => match.startsWith("\"") ? match : "");
65
+ }
66
+ async function migrateTsconfigClient(projectRoot) {
67
+ const results = [];
68
+ const filePath = path.join(projectRoot, "tsconfig.client.json");
69
+ try {
70
+ const raw = await fs.readFile(filePath, "utf-8");
71
+ const parsed = JSON.parse(stripJsonComments(raw));
72
+ if (!Array.isArray(parsed.include)) return results;
73
+ if (parsed.include.includes("shared/appkit-types")) return results;
74
+ parsed.include.push("shared/appkit-types");
75
+ await fs.writeFile(filePath, `${JSON.stringify(parsed, null, 2)}\n`, "utf-8");
76
+ results.push({
77
+ file: "tsconfig.client.json",
78
+ action: "added \"shared/appkit-types\" to include"
79
+ });
80
+ } catch (err) {
81
+ logger.warn("Failed to migrate tsconfig.client.json: %s", err.message);
82
+ }
83
+ return results;
84
+ }
85
+ async function migrateTsconfigServer(projectRoot) {
86
+ const results = [];
87
+ const filePath = path.join(projectRoot, "tsconfig.server.json");
88
+ try {
89
+ const raw = await fs.readFile(filePath, "utf-8");
90
+ const parsed = JSON.parse(stripJsonComments(raw));
91
+ const opts = parsed.compilerOptions;
92
+ if (!opts || !opts.outDir) return results;
93
+ delete opts.outDir;
94
+ delete opts.declaration;
95
+ delete opts.declarationMap;
96
+ delete opts.sourceMap;
97
+ opts.noEmit = true;
98
+ await fs.writeFile(filePath, `${JSON.stringify(parsed, null, 2)}\n`, "utf-8");
99
+ results.push({
100
+ file: "tsconfig.server.json",
101
+ action: "switched to noEmit mode"
102
+ });
103
+ } catch (err) {
104
+ logger.warn("Failed to migrate tsconfig.server.json: %s", err.message);
105
+ }
106
+ return results;
107
+ }
108
+ const SCRIPT_MIGRATIONS = {
109
+ "build:server": {
110
+ old: "tsdown -c tsdown.server.config.ts",
111
+ new: "tsc -b tsconfig.server.json && tsdown -c tsdown.server.config.ts"
112
+ },
113
+ typecheck: {
114
+ old: "tsc -p ./tsconfig.server.json --noEmit && tsc -p ./tsconfig.client.json --noEmit",
115
+ new: "tsc -b tsconfig.server.json && tsc -b tsconfig.client.json"
116
+ }
117
+ };
118
+ async function migratePackageJsonScripts(projectRoot) {
119
+ const results = [];
120
+ const filePath = path.join(projectRoot, "package.json");
121
+ try {
122
+ const raw = await fs.readFile(filePath, "utf-8");
123
+ const parsed = JSON.parse(raw);
124
+ const scripts = parsed.scripts;
125
+ if (!scripts) return results;
126
+ const updated = [];
127
+ for (const [name, { old, new: replacement }] of Object.entries(SCRIPT_MIGRATIONS)) if (scripts[name] === old) {
128
+ scripts[name] = replacement;
129
+ updated.push(name);
130
+ }
131
+ if (updated.length === 0) return results;
132
+ const indent = raw.match(/^\s+/m)?.[0]?.length === 4 ? 4 : 2;
133
+ await fs.writeFile(filePath, `${JSON.stringify(parsed, null, indent)}\n`, "utf-8");
134
+ results.push({
135
+ file: "package.json",
136
+ action: `updated ${updated.join(" and ")} scripts`
137
+ });
138
+ } catch (err) {
139
+ logger.warn("Failed to migrate package.json scripts: %s", err.message);
140
+ }
141
+ return results;
142
+ }
143
+ function printMigrationSummary(results) {
144
+ const separator = pc.dim("─".repeat(50));
145
+ console.log("");
146
+ console.log(` ${pc.bold("Typegen Migration")}`);
147
+ console.log(` ${separator}`);
148
+ for (const { file, action } of results) console.log(` ${pc.green("✓")} ${file.padEnd(24)} ${pc.dim(action)}`);
149
+ console.log(` ${separator}`);
150
+ console.log("");
151
+ }
152
+
153
+ //#endregion
154
+ export { migrateProjectConfig, removeOldGeneratedTypes, resolveProjectRoot };
155
+ //# sourceMappingURL=migration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration.js","names":["fsSync"],"sources":["../../src/type-generator/migration.ts"],"sourcesContent":["import fsSync from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport pc from \"picocolors\";\nimport { createLogger } from \"../logging/logger\";\n\nconst logger = createLogger(\"type-generator:migration\");\n\n/**\n * Derive project root from an outFile path.\n * outFile is always `<projectRoot>/shared/appkit-types/<file>` — both the Vite plugins\n * and the CLI construct it this way, so going up two levels is safe.\n *\n * Validates that the resolved root contains a package.json — if not, logs a warning\n * so custom outFile paths don't silently operate on the wrong directory.\n */\nexport function resolveProjectRoot(outFile: string): string {\n const root = path.resolve(path.dirname(outFile), \"..\", \"..\");\n if (!fsSync.existsSync(path.join(root, \"package.json\"))) {\n logger.warn(\n \"Resolved project root %s has no package.json — migration may target the wrong directory. \" +\n \"Check your outFile path: %s\",\n root,\n outFile,\n );\n }\n return root;\n}\n\n/**\n * Remove old generated types from client/src/ (pre-shared/ location).\n * Best-effort: silently ignores missing files.\n */\nexport async function removeOldGeneratedTypes(\n projectRoot: string,\n filename: string,\n): Promise<void> {\n const oldFile = path.join(projectRoot, \"client\", \"src\", filename);\n try {\n await fs.unlink(oldFile);\n logger.debug(\"Removed old types at %s\", oldFile);\n } catch {\n // File doesn't exist — nothing to clean up\n }\n}\n\n// ── Project config migration ────────────────────────────────────────────\n\nconst migratedProjects = new Set<string>();\n\n/**\n * One-time config migration: update tsconfig and package.json for shared/ types output.\n * Idempotent — each sub-migration checks current file state and skips if already migrated.\n * Deduplicates per project root so monorepo builds migrate each app independently.\n * Opt-out: set `\"appkit\": { \"autoMigrate\": false }` in package.json.\n */\nexport async function migrateProjectConfig(projectRoot: string): Promise<void> {\n const resolved = path.resolve(projectRoot);\n if (migratedProjects.has(resolved)) return;\n migratedProjects.add(resolved);\n\n if (await isAutoMigrateDisabled(projectRoot)) {\n logger.debug(\"Auto-migration disabled via package.json appkit.autoMigrate\");\n return;\n }\n\n const results: Array<{ file: string; action: string }> = [];\n\n results.push(...(await migrateTsconfigClient(projectRoot)));\n results.push(...(await migrateTsconfigServer(projectRoot)));\n results.push(...(await migratePackageJsonScripts(projectRoot)));\n\n if (results.length > 0) {\n printMigrationSummary(results);\n }\n}\n\n/** Exported for testing only. */\nexport function _resetMigrationState(): void {\n migratedProjects.clear();\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────────\n\nasync function isAutoMigrateDisabled(projectRoot: string): Promise<boolean> {\n try {\n const raw = await fs.readFile(\n path.join(projectRoot, \"package.json\"),\n \"utf-8\",\n );\n const parsed = JSON.parse(raw);\n return parsed.appkit?.autoMigrate === false;\n } catch {\n return false;\n }\n}\n\n/** Strip JSONC comments (block and line) so JSON.parse can handle tsconfig files. */\nfunction stripJsonComments(text: string): string {\n // Match strings (to skip them) or comments (to remove them).\n // Strings must be matched first to avoid stripping comment-like patterns inside string values\n // (e.g. \"server/**/*\" contains /* which looks like a block comment start).\n return text.replace(/\"(?:[^\"\\\\]|\\\\.)*\"|\\/\\*[\\s\\S]*?\\*\\/|\\/\\/.*/g, (match) =>\n match.startsWith('\"') ? match : \"\",\n );\n}\n\ntype MigrationResult = Array<{ file: string; action: string }>;\n\n// ── tsconfig.client.json ────────────────────────────────────────────────\n\nasync function migrateTsconfigClient(\n projectRoot: string,\n): Promise<MigrationResult> {\n const results: MigrationResult = [];\n const filePath = path.join(projectRoot, \"tsconfig.client.json\");\n\n try {\n const raw = await fs.readFile(filePath, \"utf-8\");\n const parsed = JSON.parse(stripJsonComments(raw));\n\n if (!Array.isArray(parsed.include)) return results;\n if (parsed.include.includes(\"shared/appkit-types\")) return results;\n\n parsed.include.push(\"shared/appkit-types\");\n await fs.writeFile(\n filePath,\n `${JSON.stringify(parsed, null, 2)}\\n`,\n \"utf-8\",\n );\n results.push({\n file: \"tsconfig.client.json\",\n action: 'added \"shared/appkit-types\" to include',\n });\n } catch (err) {\n logger.warn(\n \"Failed to migrate tsconfig.client.json: %s\",\n (err as Error).message,\n );\n }\n\n return results;\n}\n\n// ── tsconfig.server.json ────────────────────────────────────────────────\n\nasync function migrateTsconfigServer(\n projectRoot: string,\n): Promise<MigrationResult> {\n const results: MigrationResult = [];\n const filePath = path.join(projectRoot, \"tsconfig.server.json\");\n\n try {\n const raw = await fs.readFile(filePath, \"utf-8\");\n const parsed = JSON.parse(stripJsonComments(raw));\n const opts = parsed.compilerOptions;\n\n if (!opts || !opts.outDir) return results; // already migrated or non-standard\n\n delete opts.outDir;\n delete opts.declaration;\n delete opts.declarationMap;\n delete opts.sourceMap;\n opts.noEmit = true;\n\n await fs.writeFile(\n filePath,\n `${JSON.stringify(parsed, null, 2)}\\n`,\n \"utf-8\",\n );\n results.push({\n file: \"tsconfig.server.json\",\n action: \"switched to noEmit mode\",\n });\n } catch (err) {\n logger.warn(\n \"Failed to migrate tsconfig.server.json: %s\",\n (err as Error).message,\n );\n }\n\n return results;\n}\n\n// ── package.json ────────────────────────────────────────────────────────\n\nconst SCRIPT_MIGRATIONS: Record<string, { old: string; new: string }> = {\n \"build:server\": {\n old: \"tsdown -c tsdown.server.config.ts\",\n new: \"tsc -b tsconfig.server.json && tsdown -c tsdown.server.config.ts\",\n },\n typecheck: {\n old: \"tsc -p ./tsconfig.server.json --noEmit && tsc -p ./tsconfig.client.json --noEmit\",\n new: \"tsc -b tsconfig.server.json && tsc -b tsconfig.client.json\",\n },\n};\n\nasync function migratePackageJsonScripts(\n projectRoot: string,\n): Promise<MigrationResult> {\n const results: MigrationResult = [];\n const filePath = path.join(projectRoot, \"package.json\");\n\n try {\n const raw = await fs.readFile(filePath, \"utf-8\");\n const parsed = JSON.parse(raw);\n const scripts = parsed.scripts;\n if (!scripts) return results;\n\n const updated: string[] = [];\n\n for (const [name, { old, new: replacement }] of Object.entries(\n SCRIPT_MIGRATIONS,\n )) {\n if (scripts[name] === old) {\n scripts[name] = replacement;\n updated.push(name);\n }\n }\n\n if (updated.length === 0) return results;\n\n const indent = raw.match(/^\\s+/m)?.[0]?.length === 4 ? 4 : 2;\n await fs.writeFile(\n filePath,\n `${JSON.stringify(parsed, null, indent)}\\n`,\n \"utf-8\",\n );\n results.push({\n file: \"package.json\",\n action: `updated ${updated.join(\" and \")} scripts`,\n });\n } catch (err) {\n logger.warn(\n \"Failed to migrate package.json scripts: %s\",\n (err as Error).message,\n );\n }\n\n return results;\n}\n\n// ── Summary ─────────────────────────────────────────────────────────────\n\nfunction printMigrationSummary(\n results: Array<{ file: string; action: string }>,\n) {\n const separator = pc.dim(\"─\".repeat(50));\n console.log(\"\");\n console.log(` ${pc.bold(\"Typegen Migration\")}`);\n console.log(` ${separator}`);\n for (const { file, action } of results) {\n console.log(` ${pc.green(\"✓\")} ${file.padEnd(24)} ${pc.dim(action)}`);\n }\n console.log(` ${separator}`);\n console.log(\"\");\n}\n"],"mappings":"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"}
@@ -1,9 +1,12 @@
1
1
  import { createLogger } from "../../logging/logger.js";
2
+ import { migrateProjectConfig, removeOldGeneratedTypes, resolveProjectRoot } from "../migration.js";
2
3
  import { CACHE_VERSION, hashSchema, loadServingCache, saveServingCache } from "./cache.js";
3
4
  import { convertRequestSchema, convertResponseSchema, deriveChunkType, extractRequestKeys } from "./converter.js";
4
5
  import { fetchOpenApiSchema } from "./fetcher.js";
6
+ import { extractServingEndpoints, findServerFile } from "./server-file-extractor.js";
5
7
  import { WorkspaceClient } from "@databricks/sdk-experimental";
6
8
  import fs from "node:fs/promises";
9
+ import path from "node:path";
7
10
  import pc from "picocolors";
8
11
 
9
12
  //#region src/type-generator/serving/generator.ts
@@ -14,10 +17,15 @@ const GENERIC_CHUNK = "unknown";
14
17
  /**
15
18
  * Generates TypeScript type declarations for serving endpoints
16
19
  * by fetching their OpenAPI schemas and converting to TypeScript.
20
+ *
21
+ * Endpoint discovery order (when `endpoints` is not provided):
22
+ * 1. AST extraction from server file (server/index.ts or server/server.ts)
23
+ * 2. DATABRICKS_SERVING_ENDPOINT_NAME env var (single default endpoint)
17
24
  */
18
25
  async function generateServingTypes(options) {
19
26
  const { outFile, noCache } = options;
20
- const endpoints = options.endpoints ?? resolveDefaultEndpoints();
27
+ const projectRoot = resolveProjectRoot(outFile);
28
+ const endpoints = options.endpoints ?? resolveEndpointsFromServerFile() ?? resolveDefaultEndpoints();
21
29
  if (Object.keys(endpoints).length === 0) {
22
30
  logger.debug("No serving endpoints configured, skipping type generation");
23
31
  return;
@@ -40,7 +48,10 @@ async function generateServingTypes(options) {
40
48
  }
41
49
  printLogTable(logEntries, startTime);
42
50
  const output = generateTypeDeclarations(registryEntries);
51
+ await fs.mkdir(path.dirname(outFile), { recursive: true });
43
52
  await fs.writeFile(outFile, output, "utf-8");
53
+ await removeOldGeneratedTypes(projectRoot, "appKitServingTypes.d.ts");
54
+ await migrateProjectConfig(projectRoot);
44
55
  if (registryEntries.length === 0) logger.debug("Wrote empty serving types to %s (no endpoints resolved)", outFile);
45
56
  else logger.debug("Wrote serving types to %s", outFile);
46
57
  if (updated) await saveServingCache(cache);
@@ -143,6 +154,16 @@ function printLogTable(logEntries, startTime) {
143
154
  console.log(` ${newCount} new, ${cacheCount} from cache. ${pc.dim(`${elapsed}s`)}`);
144
155
  console.log("");
145
156
  }
157
+ function resolveEndpointsFromServerFile() {
158
+ try {
159
+ const serverFile = findServerFile(process.cwd());
160
+ if (!serverFile) return void 0;
161
+ return extractServingEndpoints(serverFile) ?? void 0;
162
+ } catch (error) {
163
+ logger.debug("Failed to extract endpoints from server file: %s", error.message);
164
+ return;
165
+ }
166
+ }
146
167
  function resolveDefaultEndpoints() {
147
168
  if (process.env.DATABRICKS_SERVING_ENDPOINT_NAME) return { default: { env: "DATABRICKS_SERVING_ENDPOINT_NAME" } };
148
169
  return {};
@@ -1 +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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"}
1
+ {"version":3,"file":"generator.js","names":[],"sources":["../../../src/type-generator/serving/generator.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { WorkspaceClient } from \"@databricks/sdk-experimental\";\nimport pc from \"picocolors\";\nimport { createLogger } from \"../../logging/logger\";\nimport type { EndpointConfig } from \"../../plugins/serving/types\";\nimport {\n migrateProjectConfig,\n removeOldGeneratedTypes,\n resolveProjectRoot,\n} from \"../migration\";\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\";\nimport {\n extractServingEndpoints,\n findServerFile,\n} from \"./server-file-extractor\";\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 *\n * Endpoint discovery order (when `endpoints` is not provided):\n * 1. AST extraction from server file (server/index.ts or server/server.ts)\n * 2. DATABRICKS_SERVING_ENDPOINT_NAME env var (single default endpoint)\n */\nexport async function generateServingTypes(\n options: GenerateServingTypesOptions,\n): Promise<void> {\n const { outFile, noCache } = options;\n const projectRoot = resolveProjectRoot(outFile);\n\n // Resolve endpoints: explicit > AST extraction from server file > env var fallback\n const endpoints =\n options.endpoints ??\n resolveEndpointsFromServerFile() ??\n 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.mkdir(path.dirname(outFile), { recursive: true });\n await fs.writeFile(outFile, output, \"utf-8\");\n\n // One-time migration: remove old generated file and patch project configs\n await removeOldGeneratedTypes(projectRoot, \"appKitServingTypes.d.ts\");\n await migrateProjectConfig(projectRoot);\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 resolveEndpointsFromServerFile():\n | Record<string, EndpointConfig>\n | undefined {\n try {\n const serverFile = findServerFile(process.cwd());\n if (!serverFile) return undefined;\n return extractServingEndpoints(serverFile) ?? undefined;\n } catch (error) {\n logger.debug(\n \"Failed to extract endpoints from server file: %s\",\n (error as Error).message,\n );\n return undefined;\n }\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":";;;;;;;;;;;;AA8BA,MAAM,SAAS,aAAa,yBAAyB;AAErD,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AACzB,MAAM,gBAAgB;;;;;;;;;AAgBtB,eAAsB,qBACpB,SACe;CACf,MAAM,EAAE,SAAS,YAAY;CAC7B,MAAM,cAAc,mBAAmB,QAAQ;CAG/C,MAAM,YACJ,QAAQ,aACR,gCAAgC,IAChC,yBAAyB;AAC3B,KAAI,OAAO,KAAK,UAAU,CAAC,WAAW,GAAG;AACvC,SAAO,MAAM,4DAA4D;AACzE;;CAGF,MAAM,YAAY,YAAY,KAAK;CAEnC,MAAM,QAAQ,UACV;EAAE,SAAS;EAAe,WAAW,EAAE;EAAE,GACzC,MAAM,kBAAkB;CAE5B,IAAI;CACJ,IAAI,UAAU;CAEd,MAAM,kBAA4B,EAAE;CACpC,MAAM,aAID,EAAE;AAEP,MAAK,MAAM,CAAC,OAAO,WAAW,OAAO,QAAQ,UAAU,EAAE;AACvD,aAAW,IAAI,gBAAgB,EAAE,CAAC;EAClC,MAAM,SAAS,MAAM,gBAAgB,OAAO,QAAQ,QAAQ,MAAM;AAClE,MAAI,OAAO,aAAc,WAAU;AACnC,kBAAgB,KAAK,OAAO,MAAM;AAClC,aAAW,KAAK,OAAO,IAAI;;AAG7B,eAAc,YAAY,UAAU;CAEpC,MAAM,SAAS,yBAAyB,gBAAgB;AACxD,OAAM,GAAG,MAAM,KAAK,QAAQ,QAAQ,EAAE,EAAE,WAAW,MAAM,CAAC;AAC1D,OAAM,GAAG,UAAU,SAAS,QAAQ,QAAQ;AAG5C,OAAM,wBAAwB,aAAa,0BAA0B;AACrE,OAAM,qBAAqB,YAAY;AAEvC,KAAI,gBAAgB,WAAW,EAC7B,QAAO,MACL,2DACA,QACD;KAED,QAAO,MAAM,6BAA6B,QAAQ;AAGpD,KAAI,QACF,OAAM,iBAAiB,MAAsB;;AAUjD,SAAS,aAAa,OAAuB;AAC3C,QAAO,mBACL,OACA,iBACA,kBACA,cACD;;AAGH,eAAe,gBACb,OACA,QACA,QACA,OACyB;CACzB,MAAM,eAAe,QAAQ,IAAI,OAAO;AACxC,KAAI,CAAC,aACH,QAAO;EACL,OAAO,aAAa,MAAM;EAC1B,KAAK;GAAE;GAAO,QAAQ;GAAQ,OAAO,OAAO,OAAO,IAAI;GAAW;EAClE,cAAc;EACf;CAGH,MAAM,SAAS,MAAM,mBACnB,QACA,cACA,OAAO,YACR;AACD,KAAI,CAAC,OACH,QAAO;EACL,OAAO,aAAa,MAAM;EAC1B,KAAK;GAAE;GAAO,QAAQ;GAAQ,OAAO;GAAuB;EAC5D,cAAc;EACf;CAGH,MAAM,EAAE,MAAM,YAAY;CAC1B,MAAM,OAAO,WAAW,KAAK,UAAU,KAAK,CAAC;CAG7C,MAAM,SAAS,MAAM,UAAU;AAC/B,KAAI,UAAU,OAAO,SAAS,KAC5B,QAAO;EACL,OAAO,mBACL,OACA,OAAO,aACP,OAAO,cACP,OAAO,UACR;EACD,KAAK;GAAE;GAAO,QAAQ;GAAO;EAC7B,cAAc;EACf;CAIH,MAAM,YAAY,KAAK,MAAM,UAAU;AACvC,KAAI,CAAC,UACH,QAAO;EACL,OAAO,aAAa,MAAM;EAC1B,KAAK;GAAE;GAAO,QAAQ;GAAQ,OAAO;GAAqB;EAC1D,cAAc;EACf;AAGH,KAAI;EACF,MAAM,cAAc,qBAAqB,UAAU;EACnD,MAAM,eAAe,sBAAsB,UAAU;EACrD,MAAM,YAAY,gBAAgB,UAAU;EAC5C,MAAM,cAAc,mBAAmB,UAAU;AAEjD,QAAM,UAAU,SAAS;GACvB;GACA;GACA;GACA;GACA;GACD;AAED,SAAO;GACL,OAAO,mBAAmB,OAAO,aAAa,cAAc,UAAU;GACtE,KAAK;IAAE;IAAO,QAAQ;IAAQ;GAC9B,cAAc;GACf;UACM,SAAS;AAChB,SAAO,KACL,yCACA,OACC,QAAkB,QACpB;AACD,SAAO;GACL,OAAO,aAAa,MAAM;GAC1B,KAAK;IAAE;IAAO,QAAQ;IAAQ,OAAO;IAA4B;GACjE,cAAc;GACf;;;AAIL,SAAS,cACP,YACA,WACM;AACN,KAAI,WAAW,WAAW,EAAG;CAE7B,MAAM,aAAa,KAAK,IAAI,GAAG,WAAW,KAAK,MAAM,EAAE,MAAM,OAAO,CAAC;CACrE,MAAM,YAAY,GAAG,IAAI,IAAI,OAAO,GAAG,CAAC;AACxC,SAAQ,IAAI,GAAG;AACf,SAAQ,IACN,KAAK,GAAG,KAAK,kBAAkB,CAAC,GAAG,GAAG,IAAI,IAAI,WAAW,OAAO,GAAG,GACpE;AACD,SAAQ,IAAI,KAAK,YAAY;AAC7B,MAAK,MAAM,SAAS,YAAY;EAC9B,MAAM,MACJ,MAAM,WAAW,QACb,SAAS,GAAG,KAAK,GAAG,MAAM,QAAQ,CAAC,KACnC,SAAS,GAAG,KAAK,GAAG,OAAO,QAAQ,CAAC;EAC1C,MAAM,UAAU,MAAM,MAAM,OAAO,WAAW;EAC9C,MAAM,SAAS,MAAM,QAAQ,KAAK,GAAG,IAAI,MAAM,MAAM,KAAK;AAC1D,UAAQ,IAAI,KAAK,IAAI,IAAI,UAAU,SAAS;;CAE9C,MAAM,YAAY,YAAY,KAAK,GAAG,aAAa,KAAM,QAAQ,EAAE;CACnE,MAAM,WAAW,WAAW,QAAQ,MAAM,EAAE,WAAW,OAAO,CAAC;CAC/D,MAAM,aAAa,WAAW,QAAQ,MAAM,EAAE,WAAW,MAAM,CAAC;AAChE,SAAQ,IAAI,KAAK,YAAY;AAC7B,SAAQ,IACN,KAAK,SAAS,QAAQ,WAAW,eAAe,GAAG,IAAI,GAAG,QAAQ,GAAG,GACtE;AACD,SAAQ,IAAI,GAAG;;AAGjB,SAAS,iCAEK;AACZ,KAAI;EACF,MAAM,aAAa,eAAe,QAAQ,KAAK,CAAC;AAChD,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO,wBAAwB,WAAW,IAAI;UACvC,OAAO;AACd,SAAO,MACL,oDACC,MAAgB,QAClB;AACD;;;AAIJ,SAAS,0BAA0D;AACjE,KAAI,QAAQ,IAAI,iCACd,QAAO,EAAE,SAAS,EAAE,KAAK,oCAAoC,EAAE;AAEjE,QAAO,EAAE;;AAGX,SAAS,mBACP,OACA,aACA,cACA,WACQ;CACR,MAAM,SAAS;CACf,MAAM,aAAa,YAAY,YAAY;AAC3C,QAAO,OAAO,MAAM;EACpB,OAAO,WAAW,WAAW,aAAa,OAAO,CAAC;EAClD,OAAO,YAAY,WAAW,cAAc,OAAO,CAAC;EACpD,OAAO,SAAS,WAAW,YAAY,OAAO,CAAC;;;AAIjD,SAAS,WAAW,SAAiB,YAA4B;AAC/D,KAAI,CAAC,QAAQ,SAAS,KAAK,CAAE,QAAO;AACpC,QAAO,QACJ,MAAM,KAAK,CACX,KAAK,MAAM,MAAO,MAAM,IAAI,OAAO,GAAG,aAAa,OAAQ,CAC3D,KAAK,KAAK;;AAGf,SAAS,yBAAyB,SAA2B;AAC3D,QAAO;;;;;;;EAOP,QAAQ,KAAK,KAAK,CAAC;;;;;;EAMnB,QAAQ,KAAK,KAAK,CAAC"}
@@ -3,7 +3,7 @@ import { Plugin } from "vite";
3
3
 
4
4
  //#region src/type-generator/serving/vite-plugin.d.ts
5
5
  interface AppKitServingTypesPluginOptions {
6
- /** Path to the output .d.ts file (relative to client root). Default: "src/appKitServingTypes.d.ts" */
6
+ /** Path to the output .d.ts file (relative to project root). */
7
7
  outFile?: string;
8
8
  /** Endpoint config override. If omitted, auto-discovers from the server file or falls back to DATABRICKS_SERVING_ENDPOINT_NAME env var. */
9
9
  endpoints?: Record<string, EndpointConfig>;
@@ -1,6 +1,6 @@
1
1
  import { createLogger } from "../../logging/logger.js";
2
- import { generateServingTypes } from "../index.js";
3
2
  import { extractServingEndpoints, findServerFile } from "./server-file-extractor.js";
3
+ import { SERVING_TYPES_FILE, TYPES_DIR, generateServingTypes } from "../index.js";
4
4
  import path from "node:path";
5
5
 
6
6
  //#region src/type-generator/serving/vite-plugin.ts
@@ -47,7 +47,7 @@ function appKitServingTypesPlugin(options) {
47
47
  },
48
48
  configResolved(config) {
49
49
  projectRoot = path.resolve(config.root, "..");
50
- outFile = path.resolve(config.root, options?.outFile ?? "src/appKitServingTypes.d.ts");
50
+ outFile = path.resolve(projectRoot, options?.outFile ?? `shared/${TYPES_DIR}/${SERVING_TYPES_FILE}`);
51
51
  },
52
52
  async buildStart() {
53
53
  await generate();
@@ -1 +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"}
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, SERVING_TYPES_FILE, TYPES_DIR } 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 project root). */\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 projectRoot,\n options?.outFile ?? `shared/${TYPES_DIR}/${SERVING_TYPES_FILE}`,\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,aACA,SAAS,WAAW,UAAU,UAAU,GAAG,qBAC5C;;EAGH,MAAM,aAAa;AACjB,SAAM,UAAU;;EAInB"}
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin.d.ts","names":[],"sources":["../../src/type-generator/vite-plugin.ts"],"mappings":";;;;;AAEmC;UASzB,wBAAA;EAER,OAAA;EAAA;EAEA,YAAA;AAAA;;;;;;;iBASc,iBAAA,CAAkB,OAAA,GAAU,wBAAA,GAA2B,MAAA"}
1
+ {"version":3,"file":"vite-plugin.d.ts","names":[],"sources":["../../src/type-generator/vite-plugin.ts"],"mappings":";;;;;AAEmC;UAazB,wBAAA;EAER,OAAA;EAAA;EAEA,YAAA;AAAA;;;;;;;iBASc,iBAAA,CAAkB,OAAA,GAAU,wBAAA,GAA2B,MAAA"}
@@ -1,5 +1,5 @@
1
1
  import { createLogger } from "../logging/logger.js";
2
- import { generateFromEntryPoint } from "./index.js";
2
+ import { ANALYTICS_TYPES_FILE, TYPES_DIR, generateFromEntryPoint } from "./index.js";
3
3
  import path from "node:path";
4
4
  import { existsSync } from "node:fs";
5
5
 
@@ -12,7 +12,6 @@ const logger = createLogger("type-generator:vite-plugin");
12
12
  * @returns Vite plugin to generate types for AppKit queries.
13
13
  */
14
14
  function appKitTypesPlugin(options) {
15
- let root;
16
15
  let outFile;
17
16
  let watchFolders;
18
17
  async function generate() {
@@ -44,8 +43,8 @@ function appKitTypesPlugin(options) {
44
43
  return true;
45
44
  },
46
45
  configResolved(config) {
47
- root = config.root;
48
- outFile = path.resolve(root, options?.outFile ?? "src/appKitTypes.d.ts");
46
+ const projectRoot = path.resolve(config.root, "..");
47
+ outFile = path.resolve(projectRoot, options?.outFile ?? `shared/${TYPES_DIR}/${ANALYTICS_TYPES_FILE}`);
49
48
  watchFolders = options?.watchFolders ?? [path.join(process.cwd(), "config", "queries")];
50
49
  },
51
50
  buildStart() {
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin.js","names":[],"sources":["../../src/type-generator/vite-plugin.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport { createLogger } from \"../logging/logger\";\nimport { generateFromEntryPoint } from \"./index\";\n\nconst logger = createLogger(\"type-generator:vite-plugin\");\n\n/**\n * Options for the AppKit types plugin.\n */\ninterface AppKitTypesPluginOptions {\n /* Path to the output d.ts file (relative to client folder). */\n outFile?: string;\n /** Folders to watch for changes. */\n watchFolders?: string[];\n}\n\n/**\n * Vite plugin to generate types for AppKit queries.\n * Calls generateFromEntryPoint under the hood.\n * @param options - Options to override default values.\n * @returns Vite plugin to generate types for AppKit queries.\n */\nexport function appKitTypesPlugin(options?: AppKitTypesPluginOptions): Plugin {\n let root: string;\n let outFile: string;\n let watchFolders: string[];\n\n async function generate() {\n try {\n const warehouseId = process.env.DATABRICKS_WAREHOUSE_ID || \"\";\n\n if (!warehouseId) {\n logger.debug(\"Warehouse ID not found. Skipping type generation.\");\n return;\n }\n\n await generateFromEntryPoint({\n outFile,\n queryFolder: watchFolders[0],\n warehouseId,\n noCache: false,\n });\n } catch (error) {\n // throw in production to fail the build\n if (process.env.NODE_ENV === \"production\") {\n throw error;\n }\n logger.error(\"Error generating types: %O\", error);\n }\n }\n\n return {\n name: \"appkit-types\",\n\n apply() {\n const warehouseId = process.env.DATABRICKS_WAREHOUSE_ID || \"\";\n\n if (!warehouseId) {\n logger.debug(\"Warehouse ID not found. Skipping type generation.\");\n return false;\n }\n\n if (!existsSync(path.join(process.cwd(), \"config\", \"queries\"))) {\n return false;\n }\n\n return true;\n },\n\n configResolved(config) {\n root = config.root;\n outFile = path.resolve(root, options?.outFile ?? \"src/appKitTypes.d.ts\");\n watchFolders = options?.watchFolders ?? [\n path.join(process.cwd(), \"config\", \"queries\"),\n ];\n },\n\n buildStart() {\n generate();\n },\n\n configureServer(server) {\n server.watcher.add(watchFolders);\n\n server.watcher.on(\"change\", (changedFile) => {\n const isWatchedFile = watchFolders.some((folder) =>\n changedFile.startsWith(folder),\n );\n\n if (isWatchedFile && changedFile.endsWith(\".sql\")) {\n generate();\n }\n });\n },\n };\n}\n"],"mappings":";;;;;;AAMA,MAAM,SAAS,aAAa,6BAA6B;;;;;;;AAkBzD,SAAgB,kBAAkB,SAA4C;CAC5E,IAAI;CACJ,IAAI;CACJ,IAAI;CAEJ,eAAe,WAAW;AACxB,MAAI;GACF,MAAM,cAAc,QAAQ,IAAI,2BAA2B;AAE3D,OAAI,CAAC,aAAa;AAChB,WAAO,MAAM,oDAAoD;AACjE;;AAGF,SAAM,uBAAuB;IAC3B;IACA,aAAa,aAAa;IAC1B;IACA,SAAS;IACV,CAAC;WACK,OAAO;AAEd,OAAI,QAAQ,IAAI,aAAa,aAC3B,OAAM;AAER,UAAO,MAAM,8BAA8B,MAAM;;;AAIrD,QAAO;EACL,MAAM;EAEN,QAAQ;AAGN,OAAI,EAFgB,QAAQ,IAAI,2BAA2B,KAEzC;AAChB,WAAO,MAAM,oDAAoD;AACjE,WAAO;;AAGT,OAAI,CAAC,WAAW,KAAK,KAAK,QAAQ,KAAK,EAAE,UAAU,UAAU,CAAC,CAC5D,QAAO;AAGT,UAAO;;EAGT,eAAe,QAAQ;AACrB,UAAO,OAAO;AACd,aAAU,KAAK,QAAQ,MAAM,SAAS,WAAW,uBAAuB;AACxE,kBAAe,SAAS,gBAAgB,CACtC,KAAK,KAAK,QAAQ,KAAK,EAAE,UAAU,UAAU,CAC9C;;EAGH,aAAa;AACX,aAAU;;EAGZ,gBAAgB,QAAQ;AACtB,UAAO,QAAQ,IAAI,aAAa;AAEhC,UAAO,QAAQ,GAAG,WAAW,gBAAgB;AAK3C,QAJsB,aAAa,MAAM,WACvC,YAAY,WAAW,OAAO,CAC/B,IAEoB,YAAY,SAAS,OAAO,CAC/C,WAAU;KAEZ;;EAEL"}
1
+ {"version":3,"file":"vite-plugin.js","names":[],"sources":["../../src/type-generator/vite-plugin.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport { createLogger } from \"../logging/logger\";\nimport {\n ANALYTICS_TYPES_FILE,\n generateFromEntryPoint,\n TYPES_DIR,\n} from \"./index\";\n\nconst logger = createLogger(\"type-generator:vite-plugin\");\n\n/**\n * Options for the AppKit types plugin.\n */\ninterface AppKitTypesPluginOptions {\n /* Path to the output d.ts file (relative to client folder). */\n outFile?: string;\n /** Folders to watch for changes. */\n watchFolders?: string[];\n}\n\n/**\n * Vite plugin to generate types for AppKit queries.\n * Calls generateFromEntryPoint under the hood.\n * @param options - Options to override default values.\n * @returns Vite plugin to generate types for AppKit queries.\n */\nexport function appKitTypesPlugin(options?: AppKitTypesPluginOptions): Plugin {\n let outFile: string;\n let watchFolders: string[];\n\n async function generate() {\n try {\n const warehouseId = process.env.DATABRICKS_WAREHOUSE_ID || \"\";\n\n if (!warehouseId) {\n logger.debug(\"Warehouse ID not found. Skipping type generation.\");\n return;\n }\n\n await generateFromEntryPoint({\n outFile,\n queryFolder: watchFolders[0],\n warehouseId,\n noCache: false,\n });\n } catch (error) {\n // throw in production to fail the build\n if (process.env.NODE_ENV === \"production\") {\n throw error;\n }\n logger.error(\"Error generating types: %O\", error);\n }\n }\n\n return {\n name: \"appkit-types\",\n\n apply() {\n const warehouseId = process.env.DATABRICKS_WAREHOUSE_ID || \"\";\n\n if (!warehouseId) {\n logger.debug(\"Warehouse ID not found. Skipping type generation.\");\n return false;\n }\n\n if (!existsSync(path.join(process.cwd(), \"config\", \"queries\"))) {\n return false;\n }\n\n return true;\n },\n\n configResolved(config) {\n const projectRoot = path.resolve(config.root, \"..\");\n outFile = path.resolve(\n projectRoot,\n options?.outFile ?? `shared/${TYPES_DIR}/${ANALYTICS_TYPES_FILE}`,\n );\n watchFolders = options?.watchFolders ?? [\n path.join(process.cwd(), \"config\", \"queries\"),\n ];\n },\n\n buildStart() {\n generate();\n },\n\n configureServer(server) {\n server.watcher.add(watchFolders);\n\n server.watcher.on(\"change\", (changedFile) => {\n const isWatchedFile = watchFolders.some((folder) =>\n changedFile.startsWith(folder),\n );\n\n if (isWatchedFile && changedFile.endsWith(\".sql\")) {\n generate();\n }\n });\n },\n };\n}\n"],"mappings":";;;;;;AAUA,MAAM,SAAS,aAAa,6BAA6B;;;;;;;AAkBzD,SAAgB,kBAAkB,SAA4C;CAC5E,IAAI;CACJ,IAAI;CAEJ,eAAe,WAAW;AACxB,MAAI;GACF,MAAM,cAAc,QAAQ,IAAI,2BAA2B;AAE3D,OAAI,CAAC,aAAa;AAChB,WAAO,MAAM,oDAAoD;AACjE;;AAGF,SAAM,uBAAuB;IAC3B;IACA,aAAa,aAAa;IAC1B;IACA,SAAS;IACV,CAAC;WACK,OAAO;AAEd,OAAI,QAAQ,IAAI,aAAa,aAC3B,OAAM;AAER,UAAO,MAAM,8BAA8B,MAAM;;;AAIrD,QAAO;EACL,MAAM;EAEN,QAAQ;AAGN,OAAI,EAFgB,QAAQ,IAAI,2BAA2B,KAEzC;AAChB,WAAO,MAAM,oDAAoD;AACjE,WAAO;;AAGT,OAAI,CAAC,WAAW,KAAK,KAAK,QAAQ,KAAK,EAAE,UAAU,UAAU,CAAC,CAC5D,QAAO;AAGT,UAAO;;EAGT,eAAe,QAAQ;GACrB,MAAM,cAAc,KAAK,QAAQ,OAAO,MAAM,KAAK;AACnD,aAAU,KAAK,QACb,aACA,SAAS,WAAW,UAAU,UAAU,GAAG,uBAC5C;AACD,kBAAe,SAAS,gBAAgB,CACtC,KAAK,KAAK,QAAQ,KAAK,EAAE,UAAU,UAAU,CAC9C;;EAGH,aAAa;AACX,aAAU;;EAGZ,gBAAgB,QAAQ;AACtB,UAAO,QAAQ,IAAI,aAAa;AAEhC,UAAO,QAAQ,GAAG,WAAW,gBAAgB;AAK3C,QAJsB,aAAa,MAAM,WACvC,YAAY,WAAW,OAAO,CAC/B,IAEoB,YAAY,SAAS,OAAO,CAC/C,WAAU;KAEZ;;EAEL"}
@@ -0,0 +1,52 @@
1
+ # Class: PolicyDeniedError
2
+
3
+ Thrown when a policy denies an action.
4
+
5
+ ## Extends[​](#extends "Direct link to Extends")
6
+
7
+ * `Error`
8
+
9
+ ## Constructors[​](#constructors "Direct link to Constructors")
10
+
11
+ ### Constructor[​](#constructor "Direct link to Constructor")
12
+
13
+ ```ts
14
+ new PolicyDeniedError(action: FileAction, volumeKey: string): PolicyDeniedError;
15
+
16
+ ```
17
+
18
+ #### Parameters[​](#parameters "Direct link to Parameters")
19
+
20
+ | Parameter | Type |
21
+ | ----------- | --------------------------------------------------------------- |
22
+ | `action` | [`FileAction`](./docs/api/appkit/TypeAlias.FileAction.md) |
23
+ | `volumeKey` | `string` |
24
+
25
+ #### Returns[​](#returns "Direct link to Returns")
26
+
27
+ `PolicyDeniedError`
28
+
29
+ #### Overrides[​](#overrides "Direct link to Overrides")
30
+
31
+ ```ts
32
+ Error.constructor
33
+
34
+ ```
35
+
36
+ ## Properties[​](#properties "Direct link to Properties")
37
+
38
+ ### action[​](#action "Direct link to action")
39
+
40
+ ```ts
41
+ readonly action: FileAction;
42
+
43
+ ```
44
+
45
+ ***
46
+
47
+ ### volumeKey[​](#volumekey "Direct link to volumeKey")
48
+
49
+ ```ts
50
+ readonly volumeKey: string;
51
+
52
+ ```
@@ -0,0 +1,23 @@
1
+ # Interface: FilePolicyUser
2
+
3
+ Minimal user identity passed to the policy function.
4
+
5
+ ## Properties[​](#properties "Direct link to Properties")
6
+
7
+ ### id[​](#id "Direct link to id")
8
+
9
+ ```ts
10
+ id: string;
11
+
12
+ ```
13
+
14
+ ***
15
+
16
+ ### isServicePrincipal?[​](#isserviceprincipal "Direct link to isServicePrincipal?")
17
+
18
+ ```ts
19
+ optional isServicePrincipal: boolean;
20
+
21
+ ```
22
+
23
+ `true` when the caller is the service principal (direct SDK call, not `asUser`).