@flink-app/flink 0.14.3 → 2.0.0-alpha.100

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 (280) hide show
  1. package/CHANGELOG.md +1051 -0
  2. package/SCHEMA_EXTRACTION_ANALYSIS.md +494 -0
  3. package/SIMPLE_AST_FEASIBILITY.md +570 -0
  4. package/bin/flink.ts +13 -2
  5. package/cli/build.ts +24 -44
  6. package/cli/clean.ts +13 -25
  7. package/cli/cli-utils.ts +190 -17
  8. package/cli/dev.ts +252 -0
  9. package/cli/loadEnvFiles.ts +116 -0
  10. package/cli/run.ts +45 -62
  11. package/dist/bin/flink.js +61 -2
  12. package/dist/cli/build.js +20 -25
  13. package/dist/cli/clean.js +12 -10
  14. package/dist/cli/cli-utils.d.ts +34 -3
  15. package/dist/cli/cli-utils.js +193 -12
  16. package/dist/cli/dev.d.ts +2 -0
  17. package/dist/cli/dev.js +279 -0
  18. package/dist/cli/loadEnvFiles.d.ts +30 -0
  19. package/dist/cli/loadEnvFiles.js +113 -0
  20. package/dist/cli/run.js +47 -46
  21. package/dist/src/DependencyTracker.d.ts +44 -0
  22. package/dist/src/DependencyTracker.js +239 -0
  23. package/dist/src/FlinkApp.d.ts +163 -10
  24. package/dist/src/FlinkApp.js +847 -184
  25. package/dist/src/FlinkContext.d.ts +41 -0
  26. package/dist/src/FlinkErrors.d.ts +19 -6
  27. package/dist/src/FlinkErrors.js +36 -42
  28. package/dist/src/FlinkHttpHandler.d.ts +219 -26
  29. package/dist/src/FlinkHttpHandler.js +37 -1
  30. package/dist/src/FlinkJob.d.ts +10 -0
  31. package/dist/src/FlinkLog.d.ts +82 -18
  32. package/dist/src/FlinkLog.js +165 -13
  33. package/dist/src/FlinkLogFactory.d.ts +288 -0
  34. package/dist/src/FlinkLogFactory.js +619 -0
  35. package/dist/src/FlinkRepo.d.ts +10 -2
  36. package/dist/src/FlinkRepo.js +11 -1
  37. package/dist/src/FlinkRequestContext.d.ts +63 -0
  38. package/dist/src/FlinkRequestContext.js +74 -0
  39. package/dist/src/FlinkResponse.d.ts +6 -0
  40. package/dist/src/FlinkService.d.ts +38 -0
  41. package/dist/src/FlinkService.js +46 -0
  42. package/dist/src/LeaderElection.d.ts +45 -0
  43. package/dist/src/LeaderElection.js +269 -0
  44. package/dist/src/SchemaCache.d.ts +84 -0
  45. package/dist/src/SchemaCache.js +289 -0
  46. package/dist/src/TypeScriptCompiler.d.ts +161 -51
  47. package/dist/src/TypeScriptCompiler.js +1253 -617
  48. package/dist/src/TypeScriptUtils.js +4 -0
  49. package/dist/src/ai/AgentRunner.d.ts +39 -0
  50. package/dist/src/ai/AgentRunner.js +760 -0
  51. package/dist/src/ai/ConversationAgent.d.ts +279 -0
  52. package/dist/src/ai/ConversationAgent.js +404 -0
  53. package/dist/src/ai/ConversationFlinkAgent.d.ts +278 -0
  54. package/dist/src/ai/ConversationFlinkAgent.js +404 -0
  55. package/dist/src/ai/FlinkAgent.d.ts +690 -0
  56. package/dist/src/ai/FlinkAgent.js +729 -0
  57. package/dist/src/ai/FlinkTool.d.ts +135 -0
  58. package/dist/src/ai/FlinkTool.js +2 -0
  59. package/dist/src/ai/InMemoryConversationAgent.d.ts +121 -0
  60. package/dist/src/ai/InMemoryConversationAgent.js +209 -0
  61. package/dist/src/ai/LLMAdapter.d.ts +148 -0
  62. package/dist/src/ai/LLMAdapter.js +2 -0
  63. package/dist/src/ai/PersistentFlinkAgent.d.ts +278 -0
  64. package/dist/src/ai/PersistentFlinkAgent.js +403 -0
  65. package/dist/src/ai/SubAgentExecutor.d.ts +38 -0
  66. package/dist/src/ai/SubAgentExecutor.js +223 -0
  67. package/dist/src/ai/ToolExecutor.d.ts +64 -0
  68. package/dist/src/ai/ToolExecutor.js +497 -0
  69. package/dist/src/ai/agentInstructions.d.ts +68 -0
  70. package/dist/src/ai/agentInstructions.js +286 -0
  71. package/dist/src/ai/index.d.ts +8 -0
  72. package/dist/src/ai/index.js +26 -0
  73. package/dist/src/ai/instructionFileLoader.d.ts +44 -0
  74. package/dist/src/ai/instructionFileLoader.js +179 -0
  75. package/dist/src/auth/FlinkAuthPlugin.d.ts +1 -1
  76. package/dist/src/handlers/StreamWriterFactory.d.ts +20 -0
  77. package/dist/src/handlers/StreamWriterFactory.js +83 -0
  78. package/dist/src/index.d.ts +14 -0
  79. package/dist/src/index.js +17 -0
  80. package/dist/src/loadPluginSchemas.d.ts +45 -0
  81. package/dist/src/loadPluginSchemas.js +143 -0
  82. package/dist/src/schema-extraction/ComplexTypeDetection.d.ts +40 -0
  83. package/dist/src/schema-extraction/ComplexTypeDetection.js +75 -0
  84. package/dist/src/schema-extraction/TypeScriptSourceParser.d.ts +321 -0
  85. package/dist/src/schema-extraction/TypeScriptSourceParser.js +925 -0
  86. package/dist/src/schema-extraction/TypeScriptSourceParser.spec.d.ts +1 -0
  87. package/dist/src/schema-extraction/TypeScriptSourceParser.spec.js +233 -0
  88. package/dist/src/schema-extraction/TypeScriptTokenizer.d.ts +57 -0
  89. package/dist/src/schema-extraction/TypeScriptTokenizer.js +177 -0
  90. package/dist/src/schema-extraction/index.d.ts +2 -0
  91. package/dist/src/schema-extraction/index.js +20 -0
  92. package/dist/src/schema-extraction/types.d.ts +31 -0
  93. package/dist/src/schema-extraction/types.js +2 -0
  94. package/dist/src/utils/loadFlinkConfig.d.ts +53 -0
  95. package/dist/src/utils/loadFlinkConfig.js +77 -0
  96. package/dist/src/utils.d.ts +30 -0
  97. package/dist/src/utils.js +52 -0
  98. package/dist/src/workers/SchemaGeneratorWorker.d.ts +1 -0
  99. package/dist/src/workers/SchemaGeneratorWorker.js +49 -0
  100. package/dist/src/workers/WorkerPool.d.ts +60 -0
  101. package/dist/src/workers/WorkerPool.js +306 -0
  102. package/examples/logging-hierarchical-example.ts +125 -0
  103. package/package.json +29 -4
  104. package/readme.md +499 -0
  105. package/spec/AgentDescendantDetection.spec.ts +335 -0
  106. package/spec/AgentDuplicateDetection.spec.ts +112 -0
  107. package/spec/AgentObserver.spec.ts +266 -0
  108. package/spec/AgentRunner.spec.ts +1062 -0
  109. package/spec/AsyncLocalStorageContext.spec.ts +223 -0
  110. package/spec/ConversationHooks.spec.ts +257 -0
  111. package/spec/FlinkAgent.spec.ts +681 -0
  112. package/spec/FlinkApp.htmlResponse.spec.ts +260 -0
  113. package/spec/FlinkApp.onError.invocation.spec.ts +151 -0
  114. package/spec/FlinkApp.onError.spec.ts +1 -2
  115. package/spec/FlinkApp.query.spec.ts +107 -0
  116. package/spec/FlinkApp.routeOrdering.spec.ts +61 -0
  117. package/spec/FlinkApp.undefinedResponse.spec.ts +123 -0
  118. package/spec/FlinkApp.validationMode.spec.ts +155 -0
  119. package/spec/FlinkJob.spec.ts +171 -0
  120. package/spec/FlinkLogFactory.spec.ts +337 -0
  121. package/spec/FlinkRepo.spec.ts +1 -1
  122. package/spec/LeaderElection.spec.ts +174 -0
  123. package/spec/StreamingIntegration.spec.ts +139 -0
  124. package/spec/ToolExecutor.spec.ts +465 -0
  125. package/spec/TypeScriptCompiler.spec.ts +1 -1
  126. package/spec/TypeScriptSourceParser.spec.ts +1215 -0
  127. package/spec/TypeScriptTokenizer.spec.ts +366 -0
  128. package/spec/ai/ContextCompaction.spec.ts +405 -0
  129. package/spec/ai/ConversationAgent.spec.ts +520 -0
  130. package/spec/ai/InMemoryConversationAgent.spec.ts +144 -0
  131. package/spec/ai/agentInstructions.spec.ts +358 -0
  132. package/spec/fixtures/agent-instructions/TestAgent.ts +24 -0
  133. package/spec/fixtures/agent-instructions/simple.md +3 -0
  134. package/spec/fixtures/agent-instructions/template.md +18 -0
  135. package/spec/fixtures/agent-instructions/yaml-format.yaml +9 -0
  136. package/spec/mock-project/dist/.tsbuildinfo +1 -0
  137. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar.js +56 -0
  138. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar2.js +58 -0
  139. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema.js +52 -0
  140. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema2.js +52 -0
  141. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema3.js +52 -0
  142. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema.js +54 -0
  143. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema2.js +54 -0
  144. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile.js +57 -0
  145. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile2.js +57 -0
  146. package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler.js +53 -0
  147. package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler2.js +55 -0
  148. package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchCar.js +57 -0
  149. package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOnboardingSession.js +75 -0
  150. package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOrderWithComplexTypes.js +57 -0
  151. package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchProductWithIntersection.js +58 -0
  152. package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchUserWithUnion.js +58 -0
  153. package/spec/mock-project/dist/spec/mock-project/src/handlers/PostCar.js +54 -0
  154. package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogin.js +55 -0
  155. package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogout.js +54 -0
  156. package/spec/mock-project/dist/spec/mock-project/src/handlers/PutCar.js +54 -0
  157. package/spec/mock-project/dist/spec/mock-project/src/index.js +83 -0
  158. package/spec/mock-project/dist/spec/mock-project/src/repos/CarRepo.js +26 -0
  159. package/spec/mock-project/dist/spec/mock-project/src/schemas/Car.js +2 -0
  160. package/spec/mock-project/dist/spec/mock-project/src/schemas/DefaultExportSchema.js +2 -0
  161. package/spec/mock-project/dist/spec/mock-project/src/schemas/FileWithTwoSchemas.js +2 -0
  162. package/spec/mock-project/dist/src/FlinkApp.js +1000 -0
  163. package/spec/mock-project/dist/src/FlinkContext.js +2 -0
  164. package/spec/mock-project/dist/src/FlinkErrors.js +143 -0
  165. package/spec/mock-project/dist/src/FlinkHttpHandler.js +47 -0
  166. package/spec/mock-project/dist/src/FlinkJob.js +2 -0
  167. package/spec/mock-project/dist/src/FlinkLog.js +119 -0
  168. package/spec/mock-project/dist/src/FlinkLogFactory.js +617 -0
  169. package/spec/mock-project/dist/src/FlinkPlugin.js +2 -0
  170. package/spec/mock-project/dist/src/FlinkRepo.js +224 -0
  171. package/spec/mock-project/dist/src/FlinkRequestContext.js +74 -0
  172. package/spec/mock-project/dist/src/FlinkResponse.js +2 -0
  173. package/spec/mock-project/dist/src/ai/AgentExecutor.js +279 -0
  174. package/spec/mock-project/dist/src/ai/AgentRunner.js +632 -0
  175. package/spec/mock-project/dist/src/ai/ConversationAgent.js +402 -0
  176. package/spec/mock-project/dist/src/ai/ConversationFlinkAgent.js +422 -0
  177. package/spec/mock-project/dist/src/ai/FlinkAgent.js +699 -0
  178. package/spec/mock-project/dist/src/ai/FlinkTool.js +2 -0
  179. package/spec/mock-project/dist/src/ai/InMemoryConversationAgent.js +209 -0
  180. package/spec/mock-project/dist/src/ai/LLMAdapter.js +2 -0
  181. package/spec/mock-project/dist/src/ai/SubAgentExecutor.js +223 -0
  182. package/spec/mock-project/dist/src/ai/ToolExecutor.js +412 -0
  183. package/spec/mock-project/dist/src/ai/agentInstructions.js +246 -0
  184. package/spec/mock-project/dist/src/auth/FlinkAuthPlugin.js +2 -0
  185. package/spec/mock-project/dist/src/auth/FlinkAuthUser.js +2 -0
  186. package/spec/mock-project/dist/src/handlers/GetCar.js +26 -52
  187. package/spec/mock-project/dist/src/handlers/GetCar.js.map +1 -0
  188. package/spec/mock-project/dist/src/handlers/GetCar2.js +32 -54
  189. package/spec/mock-project/dist/src/handlers/GetCar2.js.map +1 -0
  190. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema.js +26 -48
  191. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema.js.map +1 -0
  192. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema2.js +28 -48
  193. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema2.js.map +1 -0
  194. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema3.js +29 -48
  195. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema3.js.map +1 -0
  196. package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema.js +26 -50
  197. package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema.js.map +1 -0
  198. package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema2.js +28 -50
  199. package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema2.js.map +1 -0
  200. package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile.js +27 -53
  201. package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile.js.map +1 -0
  202. package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile2.js +29 -53
  203. package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile2.js.map +1 -0
  204. package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler.js +16 -49
  205. package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler.js.map +1 -0
  206. package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler2.js +25 -50
  207. package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler2.js.map +1 -0
  208. package/spec/mock-project/dist/src/handlers/PatchCar.js +27 -53
  209. package/spec/mock-project/dist/src/handlers/PatchCar.js.map +1 -0
  210. package/spec/mock-project/dist/src/handlers/PatchOnboardingSession.js +44 -70
  211. package/spec/mock-project/dist/src/handlers/PatchOnboardingSession.js.map +1 -0
  212. package/spec/mock-project/dist/src/handlers/PatchOrderWithComplexTypes.js +27 -53
  213. package/spec/mock-project/dist/src/handlers/PatchOrderWithComplexTypes.js.map +1 -0
  214. package/spec/mock-project/dist/src/handlers/PatchProductWithIntersection.js +28 -54
  215. package/spec/mock-project/dist/src/handlers/PatchProductWithIntersection.js.map +1 -0
  216. package/spec/mock-project/dist/src/handlers/PatchUserWithUnion.js +28 -54
  217. package/spec/mock-project/dist/src/handlers/PatchUserWithUnion.js.map +1 -0
  218. package/spec/mock-project/dist/src/handlers/PostCar.js +24 -50
  219. package/spec/mock-project/dist/src/handlers/PostCar.js.map +1 -0
  220. package/spec/mock-project/dist/src/handlers/PostLogin.js +25 -51
  221. package/spec/mock-project/dist/src/handlers/PostLogin.js.map +1 -0
  222. package/spec/mock-project/dist/src/handlers/PostLogout.js +24 -50
  223. package/spec/mock-project/dist/src/handlers/PostLogout.js.map +1 -0
  224. package/spec/mock-project/dist/src/handlers/PutCar.js +24 -50
  225. package/spec/mock-project/dist/src/handlers/PutCar.js.map +1 -0
  226. package/spec/mock-project/dist/src/handlers/StreamWriterFactory.js +83 -0
  227. package/spec/mock-project/dist/src/index.js +52 -76
  228. package/spec/mock-project/dist/src/index.js.map +1 -0
  229. package/spec/mock-project/dist/src/mock-data-generator.js +9 -0
  230. package/spec/mock-project/dist/src/repos/CarRepo.js +12 -24
  231. package/spec/mock-project/dist/src/repos/CarRepo.js.map +1 -0
  232. package/spec/mock-project/dist/src/schemas/Car.js +3 -1
  233. package/spec/mock-project/dist/src/schemas/Car.js.map +1 -0
  234. package/spec/mock-project/dist/src/schemas/DefaultExportSchema.js +3 -1
  235. package/spec/mock-project/dist/src/schemas/DefaultExportSchema.js.map +1 -0
  236. package/spec/mock-project/dist/src/schemas/FileWithTwoSchemas.js +3 -1
  237. package/spec/mock-project/dist/src/schemas/FileWithTwoSchemas.js.map +1 -0
  238. package/spec/mock-project/dist/src/utils.js +290 -0
  239. package/spec/mock-project/tsconfig.json +6 -1
  240. package/spec/schema-generation-nested-objects.spec.ts +97 -0
  241. package/spec/testHelpers.ts +49 -0
  242. package/spec/utils.caseConversion.spec.ts +78 -0
  243. package/spec/utils.spec.ts +13 -13
  244. package/src/DependencyTracker.ts +166 -0
  245. package/src/FlinkApp.ts +919 -155
  246. package/src/FlinkContext.ts +43 -0
  247. package/src/FlinkErrors.ts +32 -12
  248. package/src/FlinkHttpHandler.ts +246 -28
  249. package/src/FlinkJob.ts +11 -0
  250. package/src/FlinkLog.ts +119 -12
  251. package/src/FlinkLogFactory.ts +699 -0
  252. package/src/FlinkRepo.ts +10 -3
  253. package/src/FlinkRequestContext.ts +95 -0
  254. package/src/FlinkResponse.ts +6 -0
  255. package/src/FlinkService.ts +49 -0
  256. package/src/LeaderElection.ts +203 -0
  257. package/src/SchemaCache.ts +232 -0
  258. package/src/TypeScriptCompiler.ts +1347 -610
  259. package/src/TypeScriptUtils.ts +5 -0
  260. package/src/ai/AgentRunner.ts +646 -0
  261. package/src/ai/ConversationAgent.ts +413 -0
  262. package/src/ai/FlinkAgent.ts +1069 -0
  263. package/src/ai/FlinkTool.ts +165 -0
  264. package/src/ai/InMemoryConversationAgent.ts +149 -0
  265. package/src/ai/LLMAdapter.ts +126 -0
  266. package/src/ai/ToolExecutor.ts +485 -0
  267. package/src/ai/agentInstructions.ts +245 -0
  268. package/src/ai/index.ts +8 -0
  269. package/src/ai/instructionFileLoader.ts +156 -0
  270. package/src/auth/FlinkAuthPlugin.ts +2 -1
  271. package/src/handlers/StreamWriterFactory.ts +84 -0
  272. package/src/index.ts +14 -0
  273. package/src/loadPluginSchemas.ts +141 -0
  274. package/src/schema-extraction/TypeScriptSourceParser.ts +1058 -0
  275. package/src/schema-extraction/TypeScriptTokenizer.ts +205 -0
  276. package/src/schema-extraction/index.ts +2 -0
  277. package/src/schema-extraction/types.ts +34 -0
  278. package/src/utils/loadFlinkConfig.ts +89 -0
  279. package/src/utils.ts +52 -0
  280. package/tsconfig.json +6 -1
@@ -85,32 +85,80 @@ Object.defineProperty(exports, "__esModule", { value: true });
85
85
  var fs_1 = __importStar(require("fs"));
86
86
  var path_1 = require("path");
87
87
  var tiny_glob_1 = __importDefault(require("tiny-glob"));
88
- var ts_json_schema_generator_1 = require("ts-json-schema-generator");
89
88
  var ts_morph_1 = require("ts-morph");
89
+ var FlinkLogFactory_1 = require("./FlinkLogFactory");
90
90
  var FsUtils_1 = require("./FsUtils");
91
- var TypeScriptUtils_1 = require("./TypeScriptUtils");
91
+ var schema_extraction_1 = require("./schema-extraction");
92
92
  var utils_1 = require("./utils");
93
+ var loadFlinkConfig_1 = require("./utils/loadFlinkConfig");
94
+ var perfLog = FlinkLogFactory_1.FlinkLogFactory.createLogger("flink.perf");
95
+ var initLog = FlinkLogFactory_1.FlinkLogFactory.createLogger("flink.init");
93
96
  var TypeScriptCompiler = /** @class */ (function () {
94
97
  function TypeScriptCompiler(cwd) {
95
- this.cwd = cwd;
98
+ var _a, _b, _c, _d;
96
99
  /**
97
- * Parsed typescript schemas that will be added to intermediate
98
- * schemas.ts file.
99
- *
100
- * This will be written to file in a batch for performance reasons.
100
+ * Handler schemas collected during parseHandlers, to be generated later
101
101
  */
102
- this.parsedTsSchemas = [];
102
+ this.handlerSchemasToGenerate = [];
103
103
  /**
104
- * Imports needed for schemas.ts.
105
- *
106
- * This will be added to file in a batch for performance reasons.
104
+ * Tool schemas collected during parseTools, to be generated later
107
105
  */
108
- this.tsSchemasSymbolsToImports = [];
106
+ this.toolSchemasToGenerate = [];
107
+ /**
108
+ * Pre-segmented source files by type (cached for performance)
109
+ */
110
+ this.handlerFiles = [];
111
+ this.repoFiles = [];
112
+ this.toolFiles = [];
113
+ this.agentFiles = [];
114
+ this.jobFiles = [];
115
+ this.serviceFiles = [];
116
+ /**
117
+ * Tool ID registry for agent validation (built during segmentation)
118
+ */
119
+ this.toolIdRegistry = new Set();
120
+ /**
121
+ * Compiler plugins loaded from flink.config.js
122
+ */
123
+ this.compilerPlugins = [];
124
+ this.disableServices = false;
125
+ /**
126
+ * Extension files collected during segmentation, keyed by generatedFile name
127
+ */
128
+ this.extensionFiles = new Map();
129
+ var path = require("path");
130
+ // Convert to absolute path to ensure consistent path comparisons
131
+ this.cwd = path.resolve(cwd);
109
132
  // Detect if project is using ESM based solely on package.json "type": "module"
110
- this.isEsm = this.isEsmProject(cwd);
133
+ this.isEsm = this.isEsmProject(this.cwd);
134
+ var tsConfigPath = (0, path_1.join)(this.cwd, "tsconfig.json");
135
+ // Read and inherit important settings from tsconfig.json
136
+ var userTsConfig = {};
137
+ try {
138
+ if (fs_1.default.existsSync(tsConfigPath)) {
139
+ var tsConfigContent = fs_1.default.readFileSync(tsConfigPath, "utf8");
140
+ userTsConfig = JSON.parse(tsConfigContent);
141
+ }
142
+ }
143
+ catch (error) {
144
+ console.warn("Warning: Could not read tsconfig.json:", error);
145
+ }
111
146
  var compilerOptions = {
112
147
  noEmit: false, // We need to emit files
113
148
  outDir: (0, path_1.join)(cwd, "dist"),
149
+ // Inherit important performance options from user's tsconfig
150
+ skipLibCheck: (_b = (_a = userTsConfig.compilerOptions) === null || _a === void 0 ? void 0 : _a.skipLibCheck) !== null && _b !== void 0 ? _b : true, // Default to true for performance
151
+ // Performance optimizations for emit phase
152
+ // Since we already run getPreEmitDiagnostics() for type checking,
153
+ // we can use faster transpilation-only options
154
+ isolatedModules: true, // Transpile each file independently (much faster, no cross-file analysis)
155
+ // removeComments: true, // Smaller output, faster emit
156
+ importHelpers: false, // Don't import tslib helpers
157
+ // EXPERIMENTAL: Enable incremental compilation
158
+ // This creates a .tsbuildinfo file to cache type information
159
+ // Should speed up subsequent builds by avoiding full type checking
160
+ // incremental: true,
161
+ // tsBuildInfoFile: join(cwd, "dist", ".tsbuildinfo"),
114
162
  };
115
163
  // Set appropriate module settings based on detected module system
116
164
  if (this.isEsm) {
@@ -124,17 +172,351 @@ var TypeScriptCompiler = /** @class */ (function () {
124
172
  compilerOptions.module = ts_morph_1.ts.ModuleKind.CommonJS;
125
173
  compilerOptions.moduleResolution = ts_morph_1.ts.ModuleResolutionKind.NodeJs;
126
174
  }
127
- var tsConfigPath = (0, path_1.join)(cwd, "tsconfig.json");
128
- console.log("TypeScript config path:", require("path").resolve(tsConfigPath));
129
- console.log("TypeScript version:", ts_morph_1.ts.version);
175
+ initLog.info("TypeScript version:", ts_morph_1.ts.version, "config path", require("path").resolve(tsConfigPath));
176
+ // console.log("Compiler options:", JSON.stringify(compilerOptions, null, 2));
177
+ var projectStartTime = Date.now();
130
178
  this.project = new ts_morph_1.Project({
131
179
  tsConfigFilePath: tsConfigPath,
132
180
  compilerOptions: compilerOptions,
181
+ skipAddingFilesFromTsConfig: true, // Don't auto-load all files - we'll add specific directories we need
182
+ skipFileDependencyResolution: true, // Don't resolve /// <reference> and type dependencies from node_modules
133
183
  });
184
+ var projectTime = Date.now() - projectStartTime;
185
+ perfLog.debug("\u2713 Project initialization completed in ".concat(projectTime, "ms"));
186
+ // Load Flink-specific source paths from tsconfig.json if available
187
+ var additionalSourcePaths = this.getFlinkSourcePaths(tsConfigPath);
188
+ // Load all TypeScript files from src/ directory
189
+ // This is simpler and faster than recursive import resolution
190
+ // We exclude test files and declarations to keep it lean
191
+ var defaultSourcePaths = [
192
+ (0, path_1.join)(cwd, "src/**/*.ts"),
193
+ "!" + (0, path_1.join)(cwd, "src/**/*.spec.ts"),
194
+ "!" + (0, path_1.join)(cwd, "src/**/*.test.ts"),
195
+ "!" + (0, path_1.join)(cwd, "src/**/*.d.ts"),
196
+ ];
197
+ var allSourcePaths = __spreadArray(__spreadArray([], defaultSourcePaths, true), additionalSourcePaths, true);
198
+ var loadStartTime = Date.now();
199
+ this.project.addSourceFilesAtPaths(allSourcePaths);
200
+ var loadTime = Date.now() - loadStartTime;
201
+ var fileCount = this.project.getSourceFiles().length;
202
+ perfLog.debug("\u2713 All source files loaded in ".concat(loadTime, "ms (").concat(fileCount, " files)"));
203
+ // Load config from flink.config.js
204
+ var flinkCfg = (0, loadFlinkConfig_1.loadFlinkConfig)(this.cwd);
205
+ this.compilerPlugins = (_c = flinkCfg === null || flinkCfg === void 0 ? void 0 : flinkCfg.compilerPlugins) !== null && _c !== void 0 ? _c : [];
206
+ this.disableServices = (_d = flinkCfg === null || flinkCfg === void 0 ? void 0 : flinkCfg.disableServices) !== null && _d !== void 0 ? _d : false;
207
+ if (this.compilerPlugins.length > 0) {
208
+ initLog.info("Compiler plugins loaded (".concat(this.compilerPlugins.length, "):"), this.compilerPlugins.map(function (p) { return "".concat(p.package, " \u2192 ").concat(p.scanDir); }).join(", "));
209
+ }
210
+ else {
211
+ initLog.info("No compiler plugins configured");
212
+ }
213
+ // Segment files by type for efficient processing
214
+ this.segmentSourceFiles();
134
215
  console.log("Loaded", this.project.getSourceFiles().length, "source file(s) from", cwd);
135
216
  console.log("Module system:", this.isEsm ? "ESM" : "CommonJS");
136
217
  console.log("Using module:", compilerOptions.module === ts_morph_1.ts.ModuleKind.ESNext ? "ESNext" : "CommonJS");
137
218
  }
219
+ /**
220
+ * Generates a schema $id from a file path and type name using the same algorithm
221
+ * as the schema generator's defineId callback.
222
+ *
223
+ * Examples:
224
+ * - src/schemas/Car.ts, "Car" → "Car"
225
+ * - src/schemas/wrappers/PartialLoginReq.ts, "PartialLoginReq" → "wrappers.PartialLoginReq"
226
+ * - src/schemas/PatchCarSchemas.ts, "PatchCarReq" → "PatchCarSchemas.PatchCarReq"
227
+ */
228
+ TypeScriptCompiler.prototype.filePathToSchemaId = function (absolutePath, typeName) {
229
+ var path = require("path");
230
+ var schemaDir = path.join(this.cwd, "src/schemas");
231
+ // Get path relative to src/schemas/
232
+ var relativePath = path.relative(schemaDir, absolutePath);
233
+ // Remove .ts extension
234
+ var pathWithoutExt = relativePath.replace(/\.ts$/, "");
235
+ // Split by path separator
236
+ var parts = pathWithoutExt.split(path.sep);
237
+ var fileName = parts[parts.length - 1];
238
+ // Root file with matching name: just type name
239
+ if (parts.length === 1 && fileName === typeName) {
240
+ return typeName;
241
+ }
242
+ // Nested file or multiple exports: include path
243
+ var pathPrefix = parts.join(".");
244
+ if (fileName === typeName) {
245
+ return pathPrefix; // Avoid duplication
246
+ }
247
+ return "".concat(pathPrefix, ".").concat(typeName);
248
+ };
249
+ /**
250
+ * Resolves a type name to its schema $id by looking up its import path.
251
+ * Returns undefined if the type is not imported from src/schemas/.
252
+ */
253
+ TypeScriptCompiler.prototype.resolveTypeNameToSchemaId = function (fileText, typeName, handlerFilePath) {
254
+ var path = require("path");
255
+ // Extract import statement for this type
256
+ var importStmt = schema_extraction_1.TypeScriptSourceParser.extractImportForType(fileText, typeName);
257
+ if (!importStmt) {
258
+ perfLog.trace("Type ".concat(typeName, " not found in imports"));
259
+ return undefined;
260
+ }
261
+ // Extract module specifier from import
262
+ // e.g., import { Foo } from "../schemas/Bar" → "../schemas/Bar"
263
+ var moduleMatch = importStmt.match(/from\s+["']([^"']+)["']/);
264
+ if (!moduleMatch) {
265
+ perfLog.trace("Could not extract module specifier from: ".concat(importStmt));
266
+ return undefined;
267
+ }
268
+ var moduleSpecifier = moduleMatch[1];
269
+ // Resolve relative import to absolute path
270
+ var handlerDir = path.dirname(handlerFilePath);
271
+ var importPath = path.resolve(handlerDir, moduleSpecifier);
272
+ // Add .ts extension if not present
273
+ var importPathWithExt = importPath.endsWith(".ts") ? importPath : importPath + ".ts";
274
+ // Check if this is from src/schemas/
275
+ var schemaDir = path.join(this.cwd, "src/schemas");
276
+ if (!importPathWithExt.startsWith(schemaDir)) {
277
+ perfLog.trace("Type ".concat(typeName, " is not from src/schemas/ (from ").concat(importPathWithExt, ")"));
278
+ return undefined;
279
+ }
280
+ // Generate schema $id using the same algorithm
281
+ return this.filePathToSchemaId(importPathWithExt, typeName);
282
+ };
283
+ /**
284
+ * Loads additional source paths from tsconfig.json's flink configuration.
285
+ * Allows projects to specify extra directories to include in compilation.
286
+ *
287
+ * Example tsconfig.json:
288
+ * {
289
+ * "flink": {
290
+ * "sourcePaths": ["src/custom-types/**\/*.ts", "src/utils/**\/*.ts"]
291
+ * }
292
+ * }
293
+ */
294
+ TypeScriptCompiler.prototype.getFlinkSourcePaths = function (tsConfigPath) {
295
+ var _this = this;
296
+ try {
297
+ if (fs_1.default.existsSync(tsConfigPath)) {
298
+ var tsConfigContent = fs_1.default.readFileSync(tsConfigPath, "utf8");
299
+ var tsConfig = JSON.parse(tsConfigContent);
300
+ if (tsConfig.flink && Array.isArray(tsConfig.flink.sourcePaths)) {
301
+ console.log("Found Flink-specific source paths:", tsConfig.flink.sourcePaths);
302
+ return tsConfig.flink.sourcePaths.map(function (path) { return (0, path_1.join)(_this.cwd, path); });
303
+ }
304
+ }
305
+ }
306
+ catch (error) {
307
+ console.warn("Error reading Flink source paths from tsconfig.json:", error);
308
+ }
309
+ return [];
310
+ };
311
+ /**
312
+ * Initializes the schema generator.
313
+ *
314
+ * Note: ts-source-to-json-schema is ESM-only while @flink-app/flink is CommonJS.
315
+ * We use new Function() to preserve the actual import() in compiled output.
316
+ * Without this, TypeScript converts import() to require() which can't load ESM.
317
+ */
318
+ TypeScriptCompiler.prototype.initSchemaGenerator = function () {
319
+ return __awaiter(this, void 0, void 0, function () {
320
+ var dynamicImport, module_1, error_1;
321
+ return __generator(this, function (_a) {
322
+ switch (_a.label) {
323
+ case 0:
324
+ if (this.schemaGenerator !== undefined) {
325
+ return [2 /*return*/]; // Already initialized
326
+ }
327
+ _a.label = 1;
328
+ case 1:
329
+ _a.trys.push([1, 3, , 4]);
330
+ dynamicImport = new Function("specifier", "return import(specifier)");
331
+ return [4 /*yield*/, dynamicImport("@flink-app/ts-source-to-json-schema")];
332
+ case 2:
333
+ module_1 = _a.sent();
334
+ if (typeof module_1.toJsonSchemasFromFile !== "function") {
335
+ throw new Error("toJsonSchemasFromFile function not found. Please update @flink-app/ts-source-to-json-schema to >= 0.2.0");
336
+ }
337
+ this.schemaGenerator = module_1.toJsonSchemasFromFile;
338
+ return [3 /*break*/, 4];
339
+ case 3:
340
+ error_1 = _a.sent();
341
+ console.error("\n❌ Schema generator could not be loaded:\n" +
342
+ " Error: ".concat(error_1.message, "\n") +
343
+ "\n💡 Make sure @flink-app/ts-source-to-json-schema is installed:\n" +
344
+ " npm install @flink-app/ts-source-to-json-schema\n");
345
+ process.exit(1);
346
+ return [3 /*break*/, 4];
347
+ case 4: return [2 /*return*/];
348
+ }
349
+ });
350
+ });
351
+ };
352
+ /**
353
+ * Recursively resolves and adds imported files to the project.
354
+ * This ensures that files imported by handlers, schemas, etc. are available for type resolution.
355
+ *
356
+ * Handles three types of imports:
357
+ * 1. Relative imports (./foo, ../bar) - always resolved
358
+ * 2. Workspace package imports (@mycompany/shared) - resolved if symlinked outside node_modules
359
+ * 3. External packages (lodash, express) - skipped to avoid loading entire dependency trees
360
+ */
361
+ TypeScriptCompiler.prototype.resolveImportedFiles = function () {
362
+ // TODO: Check if this really is needed!
363
+ var processedFiles = new Set();
364
+ var filesToProcess = __spreadArray([], this.project.getSourceFiles(), true);
365
+ var totalImports = 0;
366
+ var skippedImports = 0;
367
+ var resolvedImports = 0;
368
+ while (filesToProcess.length > 0) {
369
+ var sourceFile = filesToProcess.pop();
370
+ var filePath = sourceFile.getFilePath();
371
+ if (processedFiles.has(filePath)) {
372
+ continue;
373
+ }
374
+ processedFiles.add(filePath);
375
+ // Get all import declarations
376
+ var importDeclarations = sourceFile.getImportDeclarations();
377
+ for (var _i = 0, importDeclarations_1 = importDeclarations; _i < importDeclarations_1.length; _i++) {
378
+ var importDecl = importDeclarations_1[_i];
379
+ var moduleSpecifier = importDecl.getModuleSpecifierValue();
380
+ totalImports++;
381
+ // For relative imports (./foo, ../bar), always resolve
382
+ var isRelativeImport = moduleSpecifier.startsWith(".") || moduleSpecifier.startsWith("/");
383
+ // For package imports, check if it might be a workspace package
384
+ // Workspace packages are typically scoped (@company/pkg) or match the workspace pattern
385
+ var isLikelyWorkspacePackage = !isRelativeImport && (moduleSpecifier.startsWith("@" + this.getWorkspaceScope() + "/") || this.isLikelyWorkspaceImport(moduleSpecifier));
386
+ // Skip resolution for external packages (performance optimization)
387
+ // Only resolve relative imports and likely workspace packages
388
+ if (!isRelativeImport && !isLikelyWorkspacePackage) {
389
+ skippedImports++;
390
+ continue;
391
+ }
392
+ // Try to resolve the imported file (expensive operation)
393
+ resolvedImports++;
394
+ var moduleSourceFile = importDecl.getModuleSpecifierSourceFile();
395
+ if (moduleSourceFile) {
396
+ var importedPath = moduleSourceFile.getFilePath();
397
+ // Double-check it's not in node_modules (for workspace packages)
398
+ var isWorkspacePackage = !isRelativeImport && !importedPath.includes("node_modules");
399
+ // Add to processing queue if it's a relative import or workspace package
400
+ if ((isRelativeImport || isWorkspacePackage) && !processedFiles.has(importedPath)) {
401
+ filesToProcess.push(moduleSourceFile);
402
+ }
403
+ }
404
+ }
405
+ }
406
+ console.log("Resolved imports, total files loaded:", processedFiles.size);
407
+ console.log(" Import stats: ".concat(totalImports, " total, ").concat(skippedImports, " skipped, ").concat(resolvedImports, " resolved"));
408
+ };
409
+ /**
410
+ * Gets the workspace scope from package.json (e.g., "@mycompany" from "@mycompany/my-app")
411
+ * Returns empty string if not a scoped package.
412
+ */
413
+ TypeScriptCompiler.prototype.getWorkspaceScope = function () {
414
+ try {
415
+ var packageJson = JSON.parse(fs_1.default.readFileSync((0, path_1.join)(this.cwd, "package.json"), "utf8"));
416
+ var name = packageJson.name || "";
417
+ if (name.startsWith("@")) {
418
+ return name.split("/")[0].substring(1); // Remove @ and get scope
419
+ }
420
+ }
421
+ catch (error) {
422
+ // Ignore errors
423
+ }
424
+ return "";
425
+ };
426
+ /**
427
+ * Checks if an import is likely to be a workspace package.
428
+ * This is a heuristic - we check for common workspace patterns but can't be 100% sure
429
+ * without actually resolving. This is a performance optimization to avoid resolving
430
+ * obvious external packages like "express", "lodash", etc.
431
+ */
432
+ TypeScriptCompiler.prototype.isLikelyWorkspaceImport = function (moduleSpecifier) {
433
+ // If it contains a workspace marker in the name, it might be workspace
434
+ // Common patterns: starts with project name, contains "internal", etc.
435
+ // For now, be conservative and return false - only workspace packages with
436
+ // matching scope will be resolved
437
+ return false;
438
+ };
439
+ /**
440
+ * Segments source files by type for efficient processing.
441
+ * Performs all path checks and AST inspections in a single pass.
442
+ *
443
+ * This runs once during construction and caches results to avoid
444
+ * multiple full-file iterations during parse methods.
445
+ */
446
+ TypeScriptCompiler.prototype.segmentSourceFiles = function () {
447
+ var _a;
448
+ var startTime = Date.now();
449
+ var allFiles = this.project.getSourceFiles();
450
+ var handlerTime = 0, repoTime = 0, toolTime = 0, agentTime = 0, jobTime = 0, serviceTime = 0;
451
+ for (var _i = 0, allFiles_1 = allFiles; _i < allFiles_1.length; _i++) {
452
+ var sf = allFiles_1[_i];
453
+ var filePath = sf.getFilePath();
454
+ var fileStartTime = Date.now();
455
+ // Handlers: simple path check
456
+ if (filePath.includes("src/handlers/")) {
457
+ this.handlerFiles.push(sf);
458
+ handlerTime += Date.now() - fileStartTime;
459
+ continue; // Skip further checks (mutually exclusive)
460
+ }
461
+ // Repos: simple path check
462
+ if (filePath.includes("src/repos/")) {
463
+ this.repoFiles.push(sf);
464
+ repoTime += Date.now() - fileStartTime;
465
+ continue;
466
+ }
467
+ // Tools: path check + fast text-based detection
468
+ // Check if file contains "FlinkToolProps" without parsing AST (much faster)
469
+ if (filePath.includes("src/tools/")) {
470
+ var fileText = sf.getFullText();
471
+ // Fast text check: must have both "export" and ": FlinkToolProps"
472
+ if (fileText.includes("FlinkToolProps") && fileText.includes("export")) {
473
+ this.toolFiles.push(sf);
474
+ // Extract tool ID using regex (faster than AST inspection)
475
+ // Matches: id: "tool-name" or name: "tool-name"
476
+ var idMatch = fileText.match(/(?:id|name):\s*["']([^"']+)["']/);
477
+ if (idMatch) {
478
+ this.toolIdRegistry.add(idMatch[1]);
479
+ }
480
+ }
481
+ toolTime += Date.now() - fileStartTime;
482
+ continue;
483
+ }
484
+ // Agents: convention-based detection (all .ts files in src/agents/)
485
+ // Trust that files in src/agents/ are valid agents - validate at runtime
486
+ if (filePath.includes("src/agents/")) {
487
+ this.agentFiles.push(sf);
488
+ agentTime += Date.now() - fileStartTime;
489
+ continue;
490
+ }
491
+ // Jobs: simple path check
492
+ if (filePath.includes("src/jobs/")) {
493
+ this.jobFiles.push(sf);
494
+ jobTime += Date.now() - fileStartTime;
495
+ continue;
496
+ }
497
+ // Services: simple path check (opt-out via flink.config.js disableServices)
498
+ if (!this.disableServices && filePath.includes("src/services/")) {
499
+ this.serviceFiles.push(sf);
500
+ serviceTime += Date.now() - fileStartTime;
501
+ continue;
502
+ }
503
+ // Extension dirs from compiler plugins
504
+ for (var _b = 0, _c = this.compilerPlugins; _b < _c.length; _b++) {
505
+ var ext = _c[_b];
506
+ if (filePath.includes(ext.scanDir)) {
507
+ if (!ext.detectBy || ext.detectBy(sf.getFullText(), filePath)) {
508
+ var list = (_a = this.extensionFiles.get(ext.generatedFile)) !== null && _a !== void 0 ? _a : [];
509
+ list.push(sf);
510
+ this.extensionFiles.set(ext.generatedFile, list);
511
+ }
512
+ break;
513
+ }
514
+ }
515
+ }
516
+ var segmentTime = Date.now() - startTime;
517
+ perfLog.debug("\u2713 File segmentation completed in ".concat(segmentTime, "ms ") +
518
+ "(".concat(this.handlerFiles.length, " handlers, ").concat(this.repoFiles.length, " repos, ").concat(this.toolFiles.length, " tools, ").concat(this.agentFiles.length, " agents, ").concat(this.jobFiles.length, " jobs, ").concat(this.serviceFiles.length, " services)"));
519
+ };
138
520
  /**
139
521
  * Detects if the project is using ESM (ECMAScript Modules)
140
522
  * by checking type in package.json.
@@ -156,14 +538,29 @@ var TypeScriptCompiler = /** @class */ (function () {
156
538
  };
157
539
  /**
158
540
  * Gets the module specifier for imports, adding .js extension for ESM
541
+ * Uses fast path calculation instead of ts-morph's getRelativePathAsModuleSpecifierTo
542
+ * which triggers expensive language service initialization
159
543
  */
160
544
  TypeScriptCompiler.prototype.getModuleSpecifier = function (fromFile, toFile) {
161
- var moduleSpecifier = fromFile.getRelativePathAsModuleSpecifierTo(toFile);
162
- // Add .js extension for ESM imports (only for relative paths)
163
- if (this.isEsm && !moduleSpecifier.startsWith("@") && !moduleSpecifier.endsWith(".js")) {
164
- moduleSpecifier += ".js";
545
+ var path = require("path");
546
+ // Get directory paths
547
+ var fromDir = path.dirname(fromFile.getFilePath());
548
+ var toPath = toFile.getFilePath();
549
+ // Calculate relative path
550
+ var relativePath = path.relative(fromDir, toPath);
551
+ // Convert to forward slashes (module specifiers use forward slashes)
552
+ relativePath = relativePath.replace(/\\/g, "/");
553
+ // Remove .ts extension
554
+ relativePath = relativePath.replace(/\.ts$/, "");
555
+ // Ensure it starts with ./ or ../
556
+ if (!relativePath.startsWith(".")) {
557
+ relativePath = "./" + relativePath;
558
+ }
559
+ // Add .js extension for ESM imports
560
+ if (this.isEsm) {
561
+ relativePath += ".js";
165
562
  }
166
- return moduleSpecifier;
563
+ return relativePath;
167
564
  };
168
565
  /**
169
566
  * Deletes all generated files.
@@ -210,10 +607,136 @@ var TypeScriptCompiler = /** @class */ (function () {
210
607
  });
211
608
  };
212
609
  /**
213
- * Emits compiled javascript source to dist folder
610
+ * Saves all modified source files in a single batch operation.
611
+ * Call this before emit() to persist all changes to disk.
612
+ */
613
+ TypeScriptCompiler.prototype.saveAllModifiedFiles = function () {
614
+ return __awaiter(this, void 0, void 0, function () {
615
+ var startTime, flinkFiles, unsavedFlinkFiles, saveTime;
616
+ return __generator(this, function (_a) {
617
+ switch (_a.label) {
618
+ case 0:
619
+ startTime = Date.now();
620
+ flinkFiles = this.project.getSourceFiles("**/.flink/**/*.ts");
621
+ unsavedFlinkFiles = flinkFiles.filter(function (sf) { return !sf.isSaved(); });
622
+ return [4 /*yield*/, Promise.all(unsavedFlinkFiles.map(function (sf) { return sf.save(); }))];
623
+ case 1:
624
+ _a.sent();
625
+ saveTime = Date.now() - startTime;
626
+ perfLog.debug("\u2713 Batch save completed in ".concat(saveTime, "ms (").concat(unsavedFlinkFiles.length, " files)"));
627
+ return [2 /*return*/];
628
+ }
629
+ });
630
+ });
631
+ };
632
+ /**
633
+ * Emits compiled javascript source to dist folder using swc (20-50x faster than tsc)
634
+ */
635
+ TypeScriptCompiler.prototype.emitWithSwc = function () {
636
+ return __awaiter(this, void 0, void 0, function () {
637
+ var emitStartTime, swc, fs, path, allSourceFiles, emitTime;
638
+ var _this = this;
639
+ return __generator(this, function (_a) {
640
+ switch (_a.label) {
641
+ case 0:
642
+ emitStartTime = Date.now();
643
+ swc = require("@swc/core");
644
+ fs = require("fs-extra");
645
+ path = require("path");
646
+ allSourceFiles = this.project.getSourceFiles().filter(function (sf) {
647
+ var filePath = sf.getFilePath();
648
+ return !filePath.endsWith(".d.ts") && !filePath.includes("/node_modules/");
649
+ });
650
+ initLog.debug("Starting swc compilation for ".concat(allSourceFiles.length, " source files..."));
651
+ // Transpile all files in parallel using swc
652
+ return [4 /*yield*/, Promise.all(allSourceFiles.map(function (sf) { return __awaiter(_this, void 0, void 0, function () {
653
+ var filePath, code, result, relativePath, outPath;
654
+ return __generator(this, function (_a) {
655
+ switch (_a.label) {
656
+ case 0:
657
+ filePath = sf.getFilePath();
658
+ code = sf.getFullText();
659
+ return [4 /*yield*/, swc.transform(code, {
660
+ filename: filePath,
661
+ jsc: {
662
+ parser: {
663
+ syntax: "typescript",
664
+ decorators: true,
665
+ tsx: false, // Flink doesn't use JSX
666
+ },
667
+ target: "es2017", // Async/await support, modern enough for Node 14+
668
+ transform: {
669
+ legacyDecorator: true,
670
+ decoratorMetadata: true, // Required for reflect-metadata
671
+ },
672
+ keepClassNames: true, // Preserve class names for debugging
673
+ },
674
+ module: {
675
+ type: this.isEsm ? "es6" : "commonjs",
676
+ },
677
+ sourceMaps: true,
678
+ })];
679
+ case 1:
680
+ result = _a.sent();
681
+ relativePath = path.relative(this.cwd, filePath);
682
+ outPath = path.join(this.cwd, "dist", relativePath).replace(/\.ts$/, ".js");
683
+ return [4 /*yield*/, fs.ensureDir(path.dirname(outPath))];
684
+ case 2:
685
+ _a.sent();
686
+ return [4 /*yield*/, fs.writeFile(outPath, result.code)];
687
+ case 3:
688
+ _a.sent();
689
+ if (!result.map) return [3 /*break*/, 5];
690
+ return [4 /*yield*/, fs.writeFile(outPath + ".map", result.map)];
691
+ case 4:
692
+ _a.sent();
693
+ _a.label = 5;
694
+ case 5: return [2 /*return*/];
695
+ }
696
+ });
697
+ }); }))];
698
+ case 1:
699
+ // Transpile all files in parallel using swc
700
+ _a.sent();
701
+ emitTime = Date.now() - emitStartTime;
702
+ initLog.debug("\u2713 Emitted ".concat(allSourceFiles.length, " files with swc in ").concat(emitTime, "ms (").concat((emitTime / allSourceFiles.length).toFixed(1), "ms per file)"));
703
+ return [2 /*return*/];
704
+ }
705
+ });
706
+ });
707
+ };
708
+ /**
709
+ * Emits compiled javascript source to dist folder (defaults to swc for speed, falls back to tsc)
214
710
  */
215
711
  TypeScriptCompiler.prototype.emit = function () {
712
+ return __awaiter(this, void 0, void 0, function () {
713
+ var _a;
714
+ return __generator(this, function (_b) {
715
+ switch (_b.label) {
716
+ case 0:
717
+ _b.trys.push([0, 2, , 3]);
718
+ require.resolve("@swc/core");
719
+ return [4 /*yield*/, this.emitWithSwc()];
720
+ case 1: return [2 /*return*/, _b.sent()];
721
+ case 2:
722
+ _a = _b.sent();
723
+ initLog.debug("@swc/core not found, falling back to TypeScript compiler (slower)");
724
+ return [2 /*return*/, this.emitWithTsc()];
725
+ case 3: return [2 /*return*/];
726
+ }
727
+ });
728
+ });
729
+ };
730
+ /**
731
+ * Emits compiled javascript source to dist folder using TypeScript compiler (slower, kept for fallback)
732
+ */
733
+ TypeScriptCompiler.prototype.emitWithTsc = function () {
734
+ var emitStartTime = Date.now();
735
+ var sourceFileCount = this.project.getSourceFiles().length;
736
+ perfLog.debug("Starting TypeScript compilation for ".concat(sourceFileCount, " source files..."));
216
737
  this.project.emitSync();
738
+ var emitTime = Date.now() - emitStartTime;
739
+ perfLog.debug("\u2713 Emitted ".concat(sourceFileCount, " files with tsc in ").concat(emitTime, "ms (").concat((emitTime / sourceFileCount).toFixed(1), "ms per file)"));
217
740
  };
218
741
  /**
219
742
  * Catch any compilation errors. Will return false if any Errors
@@ -253,28 +776,27 @@ var TypeScriptCompiler = /** @class */ (function () {
253
776
  * type arguments.
254
777
  */
255
778
  TypeScriptCompiler.prototype.parseHandlers = function () {
256
- return __awaiter(this, arguments, void 0, function (excludeDirs) {
257
- var generatedFile, handlersArr, handlers, schemaFilePath, jsonSchemas;
258
- if (excludeDirs === void 0) { excludeDirs = []; }
779
+ return __awaiter(this, void 0, void 0, function () {
780
+ var startTime, generatedFile, handlersArr, handlers, handlerParseTime;
259
781
  return __generator(this, function (_a) {
260
782
  switch (_a.label) {
261
783
  case 0:
784
+ startTime = Date.now();
262
785
  generatedFile = this.createSourceFile(["generatedHandlers.ts"], "// Generated ".concat(new Date(), "\nimport { autoRegisteredHandlers, HttpMethod } from \"@flink-app/flink\";\nexport const handlers = [];\nautoRegisteredHandlers.push(...handlers);\n "));
263
786
  handlersArr = generatedFile.getVariableDeclarationOrThrow("handlers").getFirstDescendantByKindOrThrow(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
264
787
  return [4 /*yield*/, this.parseHandlerDir(generatedFile, handlersArr)];
265
788
  case 1:
266
789
  handlers = _a.sent();
267
790
  generatedFile.addImportDeclarations(handlers.imports);
268
- return [4 /*yield*/, generatedFile.save()];
269
- case 2:
270
- _a.sent();
271
- return [4 /*yield*/, this.createIntermediateSchemaFile()];
272
- case 3:
273
- schemaFilePath = _a.sent();
274
- return [4 /*yield*/, this.generateAndSaveJsonSchemas(handlers.schemasToGenerate, schemaFilePath)];
275
- case 4:
276
- jsonSchemas = _a.sent();
277
- this.appendSchemasToHandlerSourceFiles(handlers.schemasToGenerate, jsonSchemas);
791
+ // Defer save until batch save at end (performance optimization)
792
+ // Store handler schemas for later batch processing
793
+ this.handlerSchemasToGenerate = handlers.schemasToGenerate;
794
+ // Cleanup: forget handler file nodes to reduce memory overhead (ts-morph performance optimization)
795
+ this.handlerFiles.forEach(function (sf) {
796
+ sf.getClasses().forEach(function (cls) { return cls.forget(); });
797
+ });
798
+ handlerParseTime = Date.now() - startTime;
799
+ perfLog.info("\u2713 Handler parsing completed in ".concat(handlerParseTime, "ms"));
278
800
  return [2 /*return*/, generatedFile];
279
801
  }
280
802
  });
@@ -285,129 +807,342 @@ var TypeScriptCompiler = /** @class */ (function () {
285
807
  */
286
808
  TypeScriptCompiler.prototype.parseHandlerDir = function (generatedFile, handlersArr) {
287
809
  return __awaiter(this, void 0, void 0, function () {
288
- var imports, i, schemasToGenerate, _i, _a, sf, isAutoRegister, namespaceImport, assumedHttpMethod, schemaTypes, _b, existingVars;
289
- return __generator(this, function (_c) {
290
- switch (_c.label) {
810
+ var imports, schemasToGenerate, handlerFiles, handlerResults, handlerElements, _i, handlerResults_1, result, sf, isAutoRegister, namespaceImport, assumedHttpMethod, schemaTypes;
811
+ var _this = this;
812
+ return __generator(this, function (_a) {
813
+ switch (_a.label) {
291
814
  case 0:
292
815
  imports = [];
293
- i = 0;
294
816
  schemasToGenerate = [];
295
- _i = 0, _a = this.project.getSourceFiles();
296
- _c.label = 1;
817
+ handlerFiles = this.handlerFiles;
818
+ return [4 /*yield*/, Promise.all(handlerFiles.map(function (sf, index) { return __awaiter(_this, void 0, void 0, function () {
819
+ var startTime, isAutoRegister, namespaceImport, assumedHttpMethod, schemaTypes, _a;
820
+ return __generator(this, function (_b) {
821
+ switch (_b.label) {
822
+ case 0:
823
+ startTime = Date.now();
824
+ isAutoRegister = this.isAutoRegisterableHandler(sf);
825
+ namespaceImport = sf.getBaseNameWithoutExtension().replace(/\./g, "_") + "_" + index;
826
+ assumedHttpMethod = (0, utils_1.getHttpMethodFromHandlerName)(sf.getBaseName());
827
+ if (!isAutoRegister) return [3 /*break*/, 2];
828
+ return [4 /*yield*/, this.extractSchemasFromHandlerFast(sf.getFilePath())];
829
+ case 1:
830
+ _a = _b.sent();
831
+ return [3 /*break*/, 3];
832
+ case 2:
833
+ _a = undefined;
834
+ _b.label = 3;
835
+ case 3:
836
+ schemaTypes = _a;
837
+ perfLog.trace("".concat(sf.getBaseName(), " took ").concat(Date.now() - startTime, "ms"));
838
+ return [2 /*return*/, {
839
+ sf: sf,
840
+ isAutoRegister: isAutoRegister,
841
+ namespaceImport: namespaceImport,
842
+ assumedHttpMethod: assumedHttpMethod,
843
+ schemaTypes: schemaTypes,
844
+ index: index,
845
+ }];
846
+ }
847
+ });
848
+ }); }))];
297
849
  case 1:
298
- if (!(_i < _a.length)) return [3 /*break*/, 6];
299
- sf = _a[_i];
300
- if (!sf.getFilePath().includes("src/handlers/")) {
301
- return [3 /*break*/, 5];
302
- }
303
- isAutoRegister = this.isAutoRegisterableHandler(sf);
304
- console.log("Detected handler ".concat(sf.getBaseName(), " ").concat(!isAutoRegister ? "(requires manual registration)" : ""));
305
- namespaceImport = sf.getBaseNameWithoutExtension().replace(/\./g, "_") + "_" + i;
306
- imports.push({
307
- defaultImport: "* as " + namespaceImport,
308
- moduleSpecifier: this.getModuleSpecifier(generatedFile, sf),
309
- });
310
- assumedHttpMethod = (0, utils_1.getHttpMethodFromHandlerName)(sf.getBaseName());
311
- if (!isAutoRegister) return [3 /*break*/, 3];
312
- return [4 /*yield*/, this.extractSchemasFromHandlerSourceFile(sf)];
313
- case 2:
314
- _b = _c.sent();
315
- return [3 /*break*/, 4];
316
- case 3:
317
- _b = undefined;
318
- _c.label = 4;
319
- case 4:
320
- schemaTypes = _b;
321
- existingVars = sf.getVariableStatements().filter(function (vs) {
322
- var varNames = vs.getDeclarations().map(function (d) { return d.getName(); });
323
- return varNames.some(function (name) { return ["__assumedHttpMethod", "__file", "__query", "__params"].includes(name); });
324
- });
325
- existingVars.forEach(function (v) { return v.remove(); });
326
- // Append schemas and metadata to source file that will be part of emitted dist bundle (javascript)
327
- sf.addVariableStatement({
328
- declarationKind: ts_morph_1.VariableDeclarationKind.Const,
329
- isExported: true,
330
- declarations: [
331
- {
332
- name: "__assumedHttpMethod",
333
- initializer: "\"".concat(assumedHttpMethod || "", "\""),
334
- },
335
- {
336
- name: "__file",
337
- initializer: "\"".concat(sf.getBaseName(), "\""),
338
- },
339
- {
340
- name: "__query",
341
- initializer: "[".concat(((schemaTypes === null || schemaTypes === void 0 ? void 0 : schemaTypes.queryMetadata) || [])
342
- .map(function (_a) {
343
- var description = _a.description, name = _a.name;
344
- return "{description: \"".concat(description, "\", name: \"").concat(name, "\"}");
345
- })
346
- .join(","), "]"),
347
- },
348
- {
349
- name: "__params",
350
- initializer: "[".concat(((schemaTypes === null || schemaTypes === void 0 ? void 0 : schemaTypes.paramsMetadata) || [])
351
- .map(function (_a) {
352
- var description = _a.description, name = _a.name;
353
- return "{description: \"".concat(description, "\", name: \"").concat(name, "\"}");
354
- })
355
- .join(","), "]"),
356
- },
357
- ],
358
- });
359
- if (isAutoRegister) {
360
- handlersArr.insertElement(i, "{handler: ".concat(namespaceImport, ", assumedHttpMethod: ").concat(assumedHttpMethod ? "HttpMethod." + assumedHttpMethod : undefined, "}"));
361
- i++;
362
- // Add schemas to generate list
363
- if (schemaTypes) {
364
- schemasToGenerate.push(__assign(__assign({}, schemaTypes), { sourceFile: sf }));
850
+ handlerResults = _a.sent();
851
+ handlerElements = [];
852
+ for (_i = 0, handlerResults_1 = handlerResults; _i < handlerResults_1.length; _i++) {
853
+ result = handlerResults_1[_i];
854
+ sf = result.sf, isAutoRegister = result.isAutoRegister, namespaceImport = result.namespaceImport, assumedHttpMethod = result.assumedHttpMethod, schemaTypes = result.schemaTypes;
855
+ imports.push({
856
+ defaultImport: "* as " + namespaceImport,
857
+ moduleSpecifier: this.getModuleSpecifier(generatedFile, sf),
858
+ });
859
+ if (isAutoRegister) {
860
+ handlerElements.push("{handler: ".concat(namespaceImport, ", assumedHttpMethod: ").concat(assumedHttpMethod ? "HttpMethod." + assumedHttpMethod : undefined, ", __file: \"").concat(this.getRelativePath(sf), "\"}"));
861
+ // Add schemas to generate list
862
+ if (schemaTypes) {
863
+ schemasToGenerate.push(__assign(__assign({}, schemaTypes), { sourceFile: sf }));
864
+ }
365
865
  }
366
866
  }
367
- _c.label = 5;
368
- case 5:
369
- _i++;
370
- return [3 /*break*/, 1];
371
- case 6: return [2 /*return*/, {
372
- imports: imports,
373
- schemasToGenerate: schemasToGenerate,
374
- }];
867
+ // Add all handler elements in one batch operation
868
+ handlersArr.addElements(handlerElements);
869
+ return [2 /*return*/, {
870
+ imports: imports,
871
+ schemasToGenerate: schemasToGenerate,
872
+ }];
375
873
  }
376
874
  });
377
875
  });
378
876
  };
379
877
  TypeScriptCompiler.prototype.parseRepos = function () {
380
878
  return __awaiter(this, void 0, void 0, function () {
381
- var generatedFile, reposArr, imports, i, _i, _a, sf;
879
+ var startTime, generatedFile, reposArr, imports, repoFiles, repoElements, _i, repoFiles_1, sf, repoParseTime;
880
+ return __generator(this, function (_a) {
881
+ startTime = Date.now();
882
+ generatedFile = this.createSourceFile(["generatedRepos.ts"], "// Generated ".concat(new Date(), "\n import { autoRegisteredRepos } from \"@flink-app/flink\";\n export const repos = [];\n autoRegisteredRepos.push(...repos);\n "));
883
+ reposArr = generatedFile.getVariableDeclarationOrThrow("repos").getFirstDescendantByKindOrThrow(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
884
+ imports = [];
885
+ repoFiles = this.repoFiles;
886
+ repoElements = [];
887
+ for (_i = 0, repoFiles_1 = repoFiles; _i < repoFiles_1.length; _i++) {
888
+ sf = repoFiles_1[_i];
889
+ console.log("Detected repo ".concat(sf.getBaseName()));
890
+ imports.push({
891
+ defaultImport: sf.getBaseNameWithoutExtension(),
892
+ moduleSpecifier: this.getModuleSpecifier(generatedFile, sf),
893
+ });
894
+ repoElements.push("{collectionName: \"".concat((0, utils_1.getCollectionNameForRepo)(sf.getBaseName()), "\", repoInstanceName: \"").concat((0, utils_1.getRepoInstanceName)(sf.getBaseName()), "\", Repo: ").concat(sf.getBaseNameWithoutExtension(), "}"));
895
+ }
896
+ // Add all repo elements in one batch operation
897
+ reposArr.addElements(repoElements);
898
+ generatedFile.addImportDeclarations(imports);
899
+ // Defer save until batch save at end (performance optimization)
900
+ // Cleanup: forget repo file nodes to reduce memory overhead (ts-morph performance optimization)
901
+ this.repoFiles.forEach(function (sf) {
902
+ sf.getClasses().forEach(function (cls) { return cls.forget(); });
903
+ });
904
+ repoParseTime = Date.now() - startTime;
905
+ initLog.info("\u2713 Repo parsing completed in ".concat(repoParseTime, "ms (").concat(repoElements.length, " repos)"));
906
+ return [2 /*return*/, generatedFile];
907
+ });
908
+ });
909
+ };
910
+ /**
911
+ * Scans project for tools and adds those to Flink
912
+ * "singleton" property `autoRegisteredTools` so they can
913
+ * be registered during start.
914
+ *
915
+ * Also extracts input and output schemas from FlinkTool type arguments
916
+ * when manual schemas are not provided.
917
+ */
918
+ TypeScriptCompiler.prototype.parseTools = function () {
919
+ return __awaiter(this, void 0, void 0, function () {
920
+ var startTime, generatedFile, toolsArr, imports, schemasToGenerate, toolFiles, toolResults, _loop_1, index, _i, toolResults_1, result, schemaTypes, toolParseTime, toolElements, _a, toolResults_2, result, sf, namespaceImport, toolPropsExportName, schemaTypes;
382
921
  return __generator(this, function (_b) {
383
922
  switch (_b.label) {
384
923
  case 0:
385
- generatedFile = this.createSourceFile(["generatedRepos.ts"], "// Generated ".concat(new Date(), "\n import { autoRegisteredRepos } from \"@flink-app/flink\";\n export const repos = [];\n autoRegisteredRepos.push(...repos);\n "));
386
- reposArr = generatedFile.getVariableDeclarationOrThrow("repos").getFirstDescendantByKindOrThrow(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
924
+ startTime = Date.now();
925
+ // Initialize schema generator BEFORE schema extraction
926
+ return [4 /*yield*/, this.initSchemaGenerator()];
927
+ case 1:
928
+ // Initialize schema generator BEFORE schema extraction
929
+ _b.sent();
930
+ generatedFile = this.createSourceFile(["generatedTools.ts"], "// Generated ".concat(new Date(), "\nimport { autoRegisteredTools } from \"@flink-app/flink\";\nexport const tools = [];\nautoRegisteredTools.push(...tools);\n "));
931
+ toolsArr = generatedFile.getVariableDeclarationOrThrow("tools").getFirstDescendantByKindOrThrow(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
387
932
  imports = [];
388
- i = 0;
389
- for (_i = 0, _a = this.project.getSourceFiles(); _i < _a.length; _i++) {
390
- sf = _a[_i];
391
- if (!sf.getFilePath().includes("src/repos/")) {
392
- continue;
393
- }
394
- console.log("Detected repo ".concat(sf.getBaseName()));
933
+ schemasToGenerate = [];
934
+ toolFiles = this.toolFiles;
935
+ toolResults = [];
936
+ _loop_1 = function (index) {
937
+ var sf = toolFiles[index];
938
+ (function () {
939
+ // Get file path ONCE and cache it to avoid triggering ts-morph lazy parsing
940
+ var filePath = sf.getFilePath();
941
+ var path = require("path");
942
+ var baseNameWithoutExt = path.basename(filePath, path.extname(filePath));
943
+ var namespaceImport = baseNameWithoutExt.replace(/\./g, "_") + "_" + index;
944
+ // Extract the export name using fast text matching instead of AST parsing
945
+ // Read file directly from disk to avoid ts-morph's lazy parsing
946
+ // Matches: export const Tool: FlinkToolProps or export const MyName: FlinkToolProps
947
+ var fileText = fs_1.default.readFileSync(filePath, "utf8");
948
+ var exportMatch = fileText.match(/export\s+const\s+(\w+)\s*:\s*FlinkToolProps/);
949
+ var toolPropsExportName = exportMatch ? exportMatch[1] : "Tool"; // Default to "Tool" if not found
950
+ toolResults.push({
951
+ sf: sf,
952
+ filePath: filePath, // Cache file path to avoid triggering sf.getFilePath() later
953
+ namespaceImport: namespaceImport,
954
+ toolPropsExportName: toolPropsExportName,
955
+ index: index,
956
+ });
957
+ })();
958
+ };
959
+ for (index = 0; index < toolFiles.length; index++) {
960
+ _loop_1(index);
961
+ }
962
+ _i = 0, toolResults_1 = toolResults;
963
+ _b.label = 2;
964
+ case 2:
965
+ if (!(_i < toolResults_1.length)) return [3 /*break*/, 5];
966
+ result = toolResults_1[_i];
967
+ return [4 /*yield*/, this.extractSchemasFromToolFast(result.filePath)];
968
+ case 3:
969
+ schemaTypes = _b.sent();
970
+ schemasToGenerate.push({
971
+ inputSchemaType: schemaTypes === null || schemaTypes === void 0 ? void 0 : schemaTypes.inputSchemaType,
972
+ outputSchemaType: schemaTypes === null || schemaTypes === void 0 ? void 0 : schemaTypes.outputSchemaType,
973
+ inputTypeHint: schemaTypes === null || schemaTypes === void 0 ? void 0 : schemaTypes.inputTypeHint,
974
+ outputTypeHint: schemaTypes === null || schemaTypes === void 0 ? void 0 : schemaTypes.outputTypeHint,
975
+ sourceFile: result.sf,
976
+ });
977
+ _b.label = 4;
978
+ case 4:
979
+ _i++;
980
+ return [3 /*break*/, 2];
981
+ case 5:
982
+ toolParseTime = Date.now() - startTime;
983
+ initLog.info("\u2713 Tool parsing completed in ".concat(toolParseTime, "ms (").concat(toolFiles.length, " tools)"));
984
+ toolElements = [];
985
+ for (_a = 0, toolResults_2 = toolResults; _a < toolResults_2.length; _a++) {
986
+ result = toolResults_2[_a];
987
+ sf = result.sf, namespaceImport = result.namespaceImport, toolPropsExportName = result.toolPropsExportName, schemaTypes = result.schemaTypes;
395
988
  imports.push({
396
- defaultImport: sf.getBaseNameWithoutExtension(),
989
+ defaultImport: "* as " + namespaceImport,
397
990
  moduleSpecifier: this.getModuleSpecifier(generatedFile, sf),
398
991
  });
399
- reposArr.insertElement(i, "{collectionName: \"".concat((0, utils_1.getCollectionNameForRepo)(sf.getBaseName()), "\", repoInstanceName: \"").concat((0, utils_1.getRepoInstanceName)(sf.getBaseName()), "\", Repo: ").concat(sf.getBaseNameWithoutExtension(), "}"));
400
- i++;
992
+ // Create an object that wraps the namespace and provides normalized access to the tool props
993
+ // This creates a consistent "Tool" property regardless of the source export name
994
+ // Example: {Tool: SearchCarsTool_0.MyCustomToolConfig} works even if export was "MyCustomToolConfig"
995
+ // __file is set here on the registration object (not injected into source files)
996
+ toolElements.push("{...".concat(namespaceImport, ", Tool: ").concat(namespaceImport, ".").concat(toolPropsExportName, ", __file: \"").concat(this.getRelativePath(sf), "\"}"));
997
+ // Add schemas to generate list
998
+ if (schemaTypes) {
999
+ schemasToGenerate.push(__assign(__assign({}, schemaTypes), { sourceFile: sf }));
1000
+ }
401
1001
  }
1002
+ // Add all tool elements in one batch operation
1003
+ toolsArr.addElements(toolElements);
402
1004
  generatedFile.addImportDeclarations(imports);
403
- return [4 /*yield*/, generatedFile.save()];
404
- case 1:
405
- _b.sent();
1005
+ // Defer save until batch save at end (performance optimization)
1006
+ // Store tool schemas for later batch processing
1007
+ this.toolSchemasToGenerate = schemasToGenerate;
1008
+ // Cleanup: forget tool file nodes to reduce memory overhead (ts-morph performance optimization)
1009
+ // Note: We avoid calling methods on sf during iteration since we cached all needed data earlier
1010
+ this.toolFiles.forEach(function (sf) {
1011
+ // Only forget if we haven't already processed this file
1012
+ var classes = sf.getClasses();
1013
+ classes.forEach(function (cls) { return cls.forget(); });
1014
+ });
406
1015
  return [2 /*return*/, generatedFile];
407
1016
  }
408
1017
  });
409
1018
  });
410
1019
  };
1020
+ /**
1021
+ * Scans project for agents and validates tool references.
1022
+ * Agents are classes extending FlinkAgent exported as default.
1023
+ */
1024
+ TypeScriptCompiler.prototype.parseAgents = function () {
1025
+ return __awaiter(this, void 0, void 0, function () {
1026
+ var startTime, generatedFile, agentsArr, imports, registeredToolIds, agentElements, agentParseTime;
1027
+ var _this = this;
1028
+ return __generator(this, function (_a) {
1029
+ startTime = Date.now();
1030
+ generatedFile = this.createSourceFile(["generatedAgents.ts"], "// Generated ".concat(new Date(), "\nimport { autoRegisteredAgents } from \"@flink-app/flink\";\nexport const agents = [];\nautoRegisteredAgents.push(...agents);\n "));
1031
+ agentsArr = generatedFile.getVariableDeclarationOrThrow("agents").getFirstDescendantByKindOrThrow(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
1032
+ imports = [];
1033
+ registeredToolIds = this.toolIdRegistry;
1034
+ agentElements = [];
1035
+ // Use pre-segmented agent files (convention-based: all files in src/agents/ are agents)
1036
+ this.agentFiles.forEach(function (sf, index) {
1037
+ var _a;
1038
+ // Convention-based approach: Trust that all files in src/agents/ are valid agents
1039
+ // Find the first exported class (assume it's the agent)
1040
+ var agentClass = sf.getClasses().find(function (cls) { return cls.isExported() || cls.isDefaultExport(); });
1041
+ // Skip if no exported class found (shouldn't happen, but defensive)
1042
+ if (!agentClass) {
1043
+ perfLog.debug("\u26A0 Skipping ".concat(sf.getBaseName(), " - no exported class found"));
1044
+ return;
1045
+ }
1046
+ // Create unique namespace import (same pattern as handlers and tools)
1047
+ var namespaceImport = sf.getBaseNameWithoutExtension().replace(/\./g, "_") + "_" + index;
1048
+ // Validate tool references exist (read from class properties)
1049
+ var toolsProperty = agentClass.getProperty("tools");
1050
+ if (toolsProperty) {
1051
+ var initializer = toolsProperty.getInitializer();
1052
+ if (initializer && initializer.getKind() === ts_morph_1.SyntaxKind.ArrayLiteralExpression) {
1053
+ var toolsArray = initializer;
1054
+ var toolElements = toolsArray.getElements();
1055
+ var _loop_2 = function (toolElement) {
1056
+ var toolName = void 0;
1057
+ // Handle string literals, method calls, and identifier references (tool imports)
1058
+ if (toolElement.getKind() === ts_morph_1.SyntaxKind.StringLiteral) {
1059
+ // Direct string: "tool-name"
1060
+ toolName = toolElement.getText().replace(/['"]/g, "");
1061
+ }
1062
+ else if (toolElement.getKind() === ts_morph_1.SyntaxKind.CallExpression) {
1063
+ // Method call: this.useTool("tool-name")
1064
+ var args = toolElement.getArguments();
1065
+ if (args.length > 0 && args[0].getKind() === ts_morph_1.SyntaxKind.StringLiteral) {
1066
+ toolName = args[0].getText().replace(/['"]/g, "");
1067
+ }
1068
+ else {
1069
+ console.warn("Agent ".concat(sf.getBaseName(), " has non-string tool reference, skipping validation"));
1070
+ return "continue";
1071
+ }
1072
+ }
1073
+ else if (toolElement.getKind() === ts_morph_1.SyntaxKind.Identifier) {
1074
+ // Tool file reference (imported): SearchCarsByBrandTool
1075
+ // Look up the import to find the actual tool file
1076
+ var importName_1 = toolElement.getText();
1077
+ var importDecl = sf.getImportDeclarations().find(function (imp) {
1078
+ var namedImports = imp.getNamedImports();
1079
+ return namedImports.some(function (ni) { return ni.getName() === importName_1; });
1080
+ });
1081
+ if (!importDecl) {
1082
+ // Try namespace import (* as Foo)
1083
+ var namespaceImport_1 = sf.getImportDeclarations().find(function (imp) {
1084
+ var _a;
1085
+ return ((_a = imp.getNamespaceImport()) === null || _a === void 0 ? void 0 : _a.getText()) === importName_1;
1086
+ });
1087
+ if (namespaceImport_1) {
1088
+ var moduleSpecifier = namespaceImport_1.getModuleSpecifierValue();
1089
+ // Extract tool ID from the tool file path
1090
+ // e.g., "../tools/SearchCarsByBrandTool" -> find in registeredToolIds
1091
+ var toolFileName = (_a = moduleSpecifier.split("/").pop()) === null || _a === void 0 ? void 0 : _a.replace(/\.ts$/, "");
1092
+ var matchingTool = Array.from(registeredToolIds).find(function (id) {
1093
+ // Try to match by searching for the tool ID
1094
+ // This is a heuristic - we'll validate it exists
1095
+ return true; // Skip validation for imported tools for now
1096
+ });
1097
+ return "continue";
1098
+ }
1099
+ console.warn("Agent ".concat(sf.getBaseName(), " references tool \"").concat(importName_1, "\" but it's not imported, skipping validation"));
1100
+ return "continue";
1101
+ }
1102
+ return "continue";
1103
+ }
1104
+ else {
1105
+ console.warn("Agent ".concat(sf.getBaseName(), " has unexpected tool reference format: ").concat(toolElement.getText()));
1106
+ return "continue";
1107
+ }
1108
+ if (!registeredToolIds.has(toolName)) {
1109
+ console.error("Agent ".concat(sf.getBaseName(), " references tool \"").concat(toolName, "\" which does not exist"));
1110
+ throw new Error("Invalid tool reference in agent ".concat(sf.getBaseName()));
1111
+ }
1112
+ };
1113
+ for (var _i = 0, toolElements_1 = toolElements; _i < toolElements_1.length; _i++) {
1114
+ var toolElement = toolElements_1[_i];
1115
+ _loop_2(toolElement);
1116
+ }
1117
+ }
1118
+ }
1119
+ var className = agentClass.getName();
1120
+ if (!className) {
1121
+ console.error("Agent class in ".concat(sf.getBaseName(), " has no name"));
1122
+ return;
1123
+ }
1124
+ imports.push({
1125
+ defaultImport: "* as " + namespaceImport,
1126
+ moduleSpecifier: _this.getModuleSpecifier(generatedFile, sf),
1127
+ });
1128
+ // Register the agent class (access default export via namespace)
1129
+ // __file is set here on the registration object (not injected into source files)
1130
+ agentElements.push("{ default: ".concat(namespaceImport, ".default, __file: \"").concat(_this.getRelativePath(sf), "\" }"));
1131
+ });
1132
+ // Add all agent elements in one batch operation
1133
+ agentsArr.addElements(agentElements);
1134
+ generatedFile.addImportDeclarations(imports);
1135
+ // Defer save until batch save at end (performance optimization)
1136
+ // Cleanup: forget agent file nodes to reduce memory overhead (ts-morph performance optimization)
1137
+ this.agentFiles.forEach(function (sf) {
1138
+ sf.getClasses().forEach(function (cls) { return cls.forget(); });
1139
+ });
1140
+ agentParseTime = Date.now() - startTime;
1141
+ initLog.info("\u2713 Agent parsing completed in ".concat(agentParseTime, "ms (").concat(agentElements.length, " agents)"));
1142
+ return [2 /*return*/, generatedFile];
1143
+ });
1144
+ });
1145
+ };
411
1146
  /**
412
1147
  * Generates a start script that will import references to handlers, repos and the
413
1148
  * actual Flink app to start.
@@ -417,19 +1152,37 @@ var TypeScriptCompiler = /** @class */ (function () {
417
1152
  */
418
1153
  TypeScriptCompiler.prototype.generateStartScript = function () {
419
1154
  return __awaiter(this, arguments, void 0, function (appEntryScript) {
420
- var sf;
1155
+ var path, entryScriptPath, specFiles, extensionImports, sf;
1156
+ var _this = this;
421
1157
  if (appEntryScript === void 0) { appEntryScript = "/src/index.ts"; }
422
1158
  return __generator(this, function (_a) {
423
1159
  switch (_a.label) {
424
1160
  case 0:
425
- if (!this.project.getSourceFile(function (sf) { return sf.getFilePath().endsWith(appEntryScript); })) {
426
- console.error("Cannot find entry script '".concat(appEntryScript, "'"));
1161
+ path = require("path");
1162
+ entryScriptPath = path.resolve(this.cwd, appEntryScript.replace(/^\//, ""));
1163
+ if (!fs_1.default.existsSync(entryScriptPath)) {
1164
+ console.error("Cannot find entry script '".concat(appEntryScript, "' at ").concat(entryScriptPath));
427
1165
  return [2 /*return*/, process.exit(1)];
428
1166
  }
429
- sf = this.createSourceFile(["start.ts"], "// Generated ".concat(new Date(), "\nimport \"./generatedHandlers").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedRepos").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedJobs").concat(this.isEsm ? ".js" : "", "\";\nimport \"..").concat(appEntryScript.replace(/\.ts/g, "")).concat(this.isEsm ? ".js" : "", "\";\nexport default {}; // Export an empty object to make it a module\n"));
430
- return [4 /*yield*/, sf.save()];
1167
+ if (!!this.project.getSourceFile(function (sf) { return sf.getFilePath().endsWith(appEntryScript); })) return [3 /*break*/, 3];
1168
+ this.project.addSourceFileAtPath(entryScriptPath);
1169
+ if (!appEntryScript.includes("/spec/")) return [3 /*break*/, 2];
1170
+ return [4 /*yield*/, (0, tiny_glob_1.default)((0, path_1.join)(this.cwd, "spec/**/*.ts"))];
431
1171
  case 1:
432
- _a.sent();
1172
+ specFiles = _a.sent();
1173
+ this.project.addSourceFilesAtPaths(specFiles);
1174
+ console.log("Added ".concat(specFiles.length, " spec files to compilation"));
1175
+ _a.label = 2;
1176
+ case 2:
1177
+ // Resolve any imports from the entry script
1178
+ this.resolveImportedFiles();
1179
+ _a.label = 3;
1180
+ case 3:
1181
+ extensionImports = this.compilerPlugins
1182
+ .map(function (ext) { return "import \"./".concat(ext.generatedFile).concat(_this.isEsm ? ".js" : "", "\";"); })
1183
+ .join("\n");
1184
+ sf = this.createSourceFile(["start.ts"], "// Generated ".concat(new Date(), "\nimport \"./generatedHandlers").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedRepos").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedTools").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedAgents").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedJobs").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedServices").concat(this.isEsm ? ".js" : "", "\";\n").concat(extensionImports ? extensionImports + "\n" : "", "import \"..").concat(appEntryScript.replace(/\.ts/g, "")).concat(this.isEsm ? ".js" : "", "\";\nexport default {}; // Export an empty object to make it a module\n"));
1185
+ // Defer save until batch save at end (performance optimization)
433
1186
  return [2 /*return*/, sf];
434
1187
  }
435
1188
  });
@@ -441,548 +1194,431 @@ var TypeScriptCompiler = /** @class */ (function () {
441
1194
  });
442
1195
  };
443
1196
  /**
444
- * Parses handlers `Handler<...>` function and its type arguments to extract
445
- * which schemas to use.
446
- *
447
- * There are multiple ways of defining schema types as valid ts and this
448
- * implementation aims to support all of these.
449
- *
450
- * Some examples of different cases (check spec/mock-project/src/handlers for more):
451
- *
452
- * ```
453
- * // Interface reference
454
- * Handler<Ctx, Car>
455
- * // Inline type definition with reference to interface
456
- * Handler<Ctx, {car: Car}>
457
- * // Inline type definition with literal values
458
- * Handler<Ctx, {car: {model: string}}>
459
- * // Array
460
- * Handler<Ctx, Car[]>
461
- * // Array with inline type definition
462
- * Handler<Ctx, {car: Car}[]>
463
- * ```
464
- *
465
- * Return names of req and/or res schema types.
1197
+ * Extracts schema information from a tool file using text-based parsing.
1198
+ * Resolves type names to schema $ids from the schema universe.
466
1199
  */
467
- TypeScriptCompiler.prototype.extractSchemasFromHandlerSourceFile = function (handlerSourceFile) {
1200
+ TypeScriptCompiler.prototype.extractSchemasFromToolFast = function (filePath) {
468
1201
  return __awaiter(this, void 0, void 0, function () {
469
- var defaultExport, handlerTypeRef;
1202
+ var fileText, schemaDetection, typeArgs, baseName, inputTypeName, outputTypeName, inputSchemaType, outputSchemaType, inputTypeHint, outputTypeHint, baseName, unwrappedTypeName, baseName;
470
1203
  return __generator(this, function (_a) {
471
- defaultExport = (0, TypeScriptUtils_1.getDefaultExport)(handlerSourceFile);
472
- if (defaultExport) {
473
- handlerTypeRef = defaultExport.getFirstDescendantByKindOrThrow(ts_morph_1.SyntaxKind.TypeReference);
474
- return [2 /*return*/, this.extractSchemaTypeFromHandler(handlerTypeRef)];
1204
+ fileText = fs_1.default.readFileSync(filePath, "utf8");
1205
+ schemaDetection = schema_extraction_1.TypeScriptSourceParser.detectSchemaType(fileText);
1206
+ if (schemaDetection.shouldSkipTypeScriptExtraction) {
1207
+ return [2 /*return*/, {
1208
+ inputSchemaType: undefined,
1209
+ outputSchemaType: undefined,
1210
+ }];
1211
+ }
1212
+ typeArgs = schema_extraction_1.TypeScriptSourceParser.parseFlinkToolTypeArgs(fileText);
1213
+ if (!typeArgs) {
1214
+ baseName = require("path").basename(filePath, require("path").extname(filePath));
1215
+ perfLog.trace(" Tool ".concat(baseName, ": Could not parse FlinkTool type arguments"));
1216
+ return [2 /*return*/, {
1217
+ inputSchemaType: undefined,
1218
+ outputSchemaType: undefined,
1219
+ }];
1220
+ }
1221
+ inputTypeName = typeArgs.inputType;
1222
+ outputTypeName = typeArgs.outputType;
1223
+ inputSchemaType = undefined;
1224
+ outputSchemaType = undefined;
1225
+ inputTypeHint = undefined;
1226
+ outputTypeHint = undefined;
1227
+ // Determine input type hint
1228
+ if (inputTypeName.toLowerCase() === "void") {
1229
+ inputTypeHint = "void";
1230
+ }
1231
+ else if (inputTypeName.toLowerCase() === "any") {
1232
+ inputTypeHint = "any";
1233
+ }
1234
+ else if (schema_extraction_1.TypeScriptSourceParser.shouldGenerateSchema(inputTypeName)) {
1235
+ inputTypeHint = "named";
1236
+ inputSchemaType = this.resolveTypeNameToSchemaId(fileText, inputTypeName, filePath);
1237
+ if (!inputSchemaType) {
1238
+ baseName = require("path").basename(filePath);
1239
+ perfLog.warn("Tool ".concat(baseName, ": Could not resolve input type \"").concat(inputTypeName, "\" to schema $id. Make sure it's exported from src/schemas/"));
1240
+ }
1241
+ }
1242
+ unwrappedTypeName = schema_extraction_1.TypeScriptSourceParser.unwrapToolResultType(outputTypeName);
1243
+ if (unwrappedTypeName.toLowerCase() === "void") {
1244
+ outputTypeHint = "void";
475
1245
  }
476
- else {
477
- console.warn("Handler ".concat(handlerSourceFile.getBaseName(), " is missing default exported handler function"));
1246
+ else if (unwrappedTypeName.toLowerCase() === "any") {
1247
+ outputTypeHint = "any";
478
1248
  }
479
- return [2 /*return*/];
1249
+ else if (schema_extraction_1.TypeScriptSourceParser.shouldGenerateSchema(unwrappedTypeName)) {
1250
+ outputTypeHint = "named";
1251
+ outputSchemaType = this.resolveTypeNameToSchemaId(fileText, unwrappedTypeName, filePath);
1252
+ if (!outputSchemaType) {
1253
+ baseName = require("path").basename(filePath);
1254
+ perfLog.warn("Tool ".concat(baseName, ": Could not resolve output type \"").concat(unwrappedTypeName, "\" to schema $id. Make sure it's exported from src/schemas/"));
1255
+ }
1256
+ }
1257
+ return [2 /*return*/, {
1258
+ inputSchemaType: inputSchemaType,
1259
+ outputSchemaType: outputSchemaType,
1260
+ inputTypeHint: inputTypeHint,
1261
+ outputTypeHint: outputTypeHint,
1262
+ }];
480
1263
  });
481
1264
  });
482
1265
  };
483
1266
  /**
484
- * Recursively copies an interface and all its dependencies from the same file
1267
+ * Extracts schema information from a handler file using text-based parsing.
1268
+ * Resolves type names to schema $ids from the schema universe.
485
1269
  */
486
- TypeScriptCompiler.prototype.copyInterfaceWithDependencies = function (interfaceDecl, handlerFile) {
487
- var _a, _b, _c;
488
- var interfaceName = ((_a = interfaceDecl.getName) === null || _a === void 0 ? void 0 : _a.call(interfaceDecl)) || ((_b = interfaceDecl.getFirstChildByKind(ts_morph_1.SyntaxKind.Identifier)) === null || _b === void 0 ? void 0 : _b.getText());
489
- if (!interfaceName)
490
- return;
491
- // Check if already copied
492
- var existingInterface = this.parsedTsSchemas.find(function (s) { return s.includes("interface ".concat(interfaceName, " ")) || s.includes("type ".concat(interfaceName, " =")); });
493
- if (existingInterface)
494
- return;
495
- // Copy the interface
496
- this.parsedTsSchemas.push(interfaceDecl.getText());
497
- // Find and recursively copy dependencies from the same file
498
- // First, find direct type references in this interface
499
- var typeRefIdentifiers = interfaceDecl
500
- .getDescendantsOfKind(ts_morph_1.SyntaxKind.TypeReference)
501
- .filter(function (typeRefNode) { return !!typeRefNode.getFirstChildIfKind(ts_morph_1.SyntaxKind.Identifier); })
502
- .map(function (typeRefNode) { return typeRefNode.getFirstChildIfKindOrThrow(ts_morph_1.SyntaxKind.Identifier); });
503
- for (var _i = 0, typeRefIdentifiers_1 = typeRefIdentifiers; _i < typeRefIdentifiers_1.length; _i++) {
504
- var typeRefIdentifier = typeRefIdentifiers_1[_i];
505
- var typeSymbol = typeRefIdentifier.getSymbol();
506
- if (typeSymbol) {
507
- var declaredType = typeSymbol.getDeclaredType();
508
- var declaration = (_c = declaredType.getSymbol()) === null || _c === void 0 ? void 0 : _c.getDeclarations()[0];
509
- if (declaration && declaration.getSourceFile() === handlerFile) {
510
- // Same file - recursively copy this dependency
511
- this.copyInterfaceWithDependencies(declaration, handlerFile);
512
- }
513
- else if (declaration && declaration.getSourceFile() !== handlerFile) {
514
- // Different file - add to imports
515
- var declaredTypeSymbol = declaredType.getSymbol();
516
- if (declaredTypeSymbol) {
517
- this.tsSchemasSymbolsToImports.push(declaredTypeSymbol);
518
- }
519
- }
520
- }
521
- }
522
- };
523
- TypeScriptCompiler.prototype.saveIntermediateTsSchema = function (schema, handlerFile, suffix) {
1270
+ TypeScriptCompiler.prototype.extractSchemasFromHandlerFast = function (filePath) {
524
1271
  return __awaiter(this, void 0, void 0, function () {
525
- var schemaText, handlerFileName, generatedSchemaInterfaceStr, schemaInterfaceName, schemaSymbol, interfaceName, declaration, _i, _a, typeToImport, typeSymbol, declaredTypeSymbol, _b, _c, prop, propDecl, propText, interfaceNameMatches, _d, interfaceNameMatches_1, match, referencedInterfaceName, referencedInterfaceDecl, arrayTypeArg, schemaSymbol, interfaceName, declaration, props, _e, _f, typeToImport, typeSymbol, declaredTypeSymbol, schemaSymbol, declarations, declaration, propertySignatures, _g, _h, prop, propType, typeSymbol, typeDeclaration, elementType, elementSymbol, elementDeclaration, currentPropTypeText, interfaceNameMatches, _j, interfaceNameMatches_2, match, interfaceName, interfaceDecl, typeArgs, _k, typeArgs_1, typeArg, argSymbol, argDeclaration, _l, _m, typeToImport, typeSymbol, declaredTypeSymbol;
526
- return __generator(this, function (_o) {
527
- if (schema.isAny()) {
528
- return [2 /*return*/]; // 'any' indicates that no schema is used
529
- }
530
- schemaText = schema.getText();
531
- if (schemaText === 'void' || schemaText === 'undefined') {
532
- return [2 /*return*/];
533
- }
534
- handlerFileName = handlerFile.getBaseNameWithoutExtension().replace(/\./g, "_");
535
- generatedSchemaInterfaceStr = "";
536
- schemaInterfaceName = "".concat(handlerFileName, "_").concat(suffix);
537
- if (schema.isInterface()) {
538
- schemaSymbol = schema.getSymbolOrThrow();
539
- interfaceName = (0, TypeScriptUtils_1.getInterfaceName)(schemaSymbol);
540
- declaration = schemaSymbol.getDeclarations()[0];
541
- if (declaration.getSourceFile() === handlerFile) {
542
- // Interface is declared within handler file
543
- generatedSchemaInterfaceStr = "export interface ".concat(schemaInterfaceName, " {\n ").concat(schema
544
- .getProperties()
545
- .map(function (p) { return p.getValueDeclarationOrThrow().getText(); })
546
- .join("\n"), "\n }");
547
- for (_i = 0, _a = (0, TypeScriptUtils_1.getTypesToImport)(declaration); _i < _a.length; _i++) {
548
- typeToImport = _a[_i];
549
- typeSymbol = typeToImport.getSymbol();
550
- if (typeSymbol) {
551
- declaredTypeSymbol = typeSymbol.getDeclaredType().getSymbol();
552
- if (declaredTypeSymbol) {
553
- this.tsSchemasSymbolsToImports.push(declaredTypeSymbol);
554
- }
555
- }
556
- }
557
- // Also check for utility types with indexed access patterns like Partial<Foo["bar"]>
558
- for (_b = 0, _c = schema.getProperties(); _b < _c.length; _b++) {
559
- prop = _c[_b];
560
- propDecl = prop.getValueDeclaration();
561
- if (propDecl) {
562
- propText = propDecl.getText();
563
- interfaceNameMatches = propText.match(/\b([A-Z][a-zA-Z0-9]*)\s*\[/g);
564
- if (interfaceNameMatches) {
565
- for (_d = 0, interfaceNameMatches_1 = interfaceNameMatches; _d < interfaceNameMatches_1.length; _d++) {
566
- match = interfaceNameMatches_1[_d];
567
- referencedInterfaceName = match.replace(/\s*\[$/, '').trim();
568
- referencedInterfaceDecl = handlerFile.getInterface(referencedInterfaceName) || handlerFile.getTypeAlias(referencedInterfaceName);
569
- if (referencedInterfaceDecl) {
570
- // Interface is in same file - copy it and all its dependencies recursively
571
- this.copyInterfaceWithDependencies(referencedInterfaceDecl, handlerFile);
572
- }
573
- }
574
- }
575
- }
576
- }
577
- }
578
- else {
579
- // Interface is imported from other file
580
- generatedSchemaInterfaceStr = "export interface ".concat(schemaInterfaceName, " extends ").concat(interfaceName, " {}");
581
- this.tsSchemasSymbolsToImports.push(schemaSymbol);
582
- }
1272
+ var fileText, typeArgs, baseName, reqTypeName, resTypeName, paramsTypeName, queryTypeName, reqSchemaType, resSchemaType, isSkipValidation, baseName, baseName, paramsMetadata, queryMetadata, metadata, metadata;
1273
+ return __generator(this, function (_a) {
1274
+ fileText = fs_1.default.readFileSync(filePath, "utf8");
1275
+ typeArgs = schema_extraction_1.TypeScriptSourceParser.parseHandlerTypeArgs(fileText);
1276
+ if (!typeArgs) {
1277
+ baseName = require("path").basename(filePath, require("path").extname(filePath));
1278
+ perfLog.trace(" Handler ".concat(baseName, ": Could not parse Handler type arguments"));
1279
+ return [2 /*return*/, {
1280
+ reqSchemaType: undefined,
1281
+ resSchemaType: undefined,
1282
+ queryMetadata: [],
1283
+ paramsMetadata: [],
1284
+ }];
583
1285
  }
584
- else if (schema.isArray()) {
585
- arrayTypeArg = schema.getTypeArguments()[0];
586
- schemaSymbol = arrayTypeArg.getSymbolOrThrow();
587
- interfaceName = schemaSymbol.getEscapedName();
588
- declaration = schemaSymbol.getDeclarations()[0];
589
- if (declaration.getSourceFile() !== handlerFile) {
590
- generatedSchemaInterfaceStr = "export interface ".concat(schemaInterfaceName, " extends Array<").concat(interfaceName, "> {}");
591
- this.tsSchemasSymbolsToImports.push(schemaSymbol);
592
- }
593
- else {
594
- if (arrayTypeArg.isInterface()) {
595
- props = arrayTypeArg
596
- .getProperties()
597
- .map(function (p) { return p.getValueDeclarationOrThrow().getText(); })
598
- .join(" ");
599
- generatedSchemaInterfaceStr = "export interface ".concat(schemaInterfaceName, " extends Array<{").concat(props, "}> {}");
600
- }
601
- else {
602
- generatedSchemaInterfaceStr = "export interface ".concat(schemaInterfaceName, " extends Array<").concat(declaration.getText(), "> {}");
603
- }
604
- for (_e = 0, _f = (0, TypeScriptUtils_1.getTypesToImport)(declaration); _e < _f.length; _e++) {
605
- typeToImport = _f[_e];
606
- typeSymbol = typeToImport.getSymbol();
607
- if (typeSymbol) {
608
- declaredTypeSymbol = typeSymbol.getDeclaredType().getSymbol();
609
- if (declaredTypeSymbol) {
610
- this.tsSchemasSymbolsToImports.push(declaredTypeSymbol);
611
- }
612
- }
613
- }
1286
+ reqTypeName = typeArgs.reqType;
1287
+ resTypeName = typeArgs.resType;
1288
+ paramsTypeName = typeArgs.paramsType;
1289
+ queryTypeName = typeArgs.queryType;
1290
+ reqSchemaType = undefined;
1291
+ resSchemaType = undefined;
1292
+ isSkipValidation = /ValidationMode\.SkipValidation/.test(fileText);
1293
+ if (reqTypeName && schema_extraction_1.TypeScriptSourceParser.shouldGenerateSchema(reqTypeName)) {
1294
+ reqSchemaType = this.resolveTypeNameToSchemaId(fileText, reqTypeName, filePath);
1295
+ if (!reqSchemaType && !isSkipValidation) {
1296
+ baseName = require("path").basename(filePath);
1297
+ perfLog.warn("Handler ".concat(baseName, ": Could not resolve request type \"").concat(reqTypeName, "\" to schema $id. Make sure it's exported from src/schemas/"));
614
1298
  }
615
1299
  }
616
- else if (schema.isObject()) {
617
- schemaSymbol = schema.getSymbol();
618
- declarations = schemaSymbol === null || schemaSymbol === void 0 ? void 0 : schemaSymbol.getDeclarations();
619
- declaration = declarations === null || declarations === void 0 ? void 0 : declarations[0];
620
- propertySignatures = schema.getProperties().map(function (prop) {
621
- var propName = prop.getName();
622
- var propType = prop.getTypeAtLocation(handlerFile);
623
- var propTypeText = propType.getText(undefined, ts_morph_1.ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope);
624
- // Check if property is optional
625
- // For utility types (Omit, Pick, etc.), properties may not have value declarations
626
- var valueDeclaration = prop.getValueDeclaration();
627
- var isOptional = false;
628
- if (valueDeclaration) {
629
- // Property has a source declaration (normal case)
630
- isOptional = valueDeclaration.getType().isNullable() || valueDeclaration.compilerNode.questionToken !== undefined;
631
- }
632
- else {
633
- // Property is synthetic (from utility types like Omit, Pick, etc.)
634
- // Check if the property itself is optional by examining the symbol flags
635
- isOptional = !!(prop.getFlags() & ts_morph_1.ts.SymbolFlags.Optional);
636
- }
637
- return "".concat(propName).concat(isOptional ? "?" : "", ": ").concat(propTypeText);
638
- });
639
- // Extract type references for imports from resolved types
640
- for (_g = 0, _h = schema.getProperties(); _g < _h.length; _g++) {
641
- prop = _h[_g];
642
- propType = prop.getTypeAtLocation(handlerFile);
643
- typeSymbol = propType.getSymbol();
644
- if (typeSymbol) {
645
- typeDeclaration = typeSymbol.getDeclarations()[0];
646
- if (typeDeclaration && typeDeclaration.getSourceFile() !== handlerFile) {
647
- this.tsSchemasSymbolsToImports.push(typeSymbol);
648
- }
649
- }
650
- // Also check for array element types
651
- if (propType.isArray()) {
652
- elementType = propType.getArrayElementType();
653
- elementSymbol = elementType === null || elementType === void 0 ? void 0 : elementType.getSymbol();
654
- if (elementSymbol) {
655
- elementDeclaration = elementSymbol.getDeclarations()[0];
656
- if (elementDeclaration && elementDeclaration.getSourceFile() !== handlerFile) {
657
- this.tsSchemasSymbolsToImports.push(elementSymbol);
658
- }
659
- }
660
- }
661
- currentPropTypeText = propType.getText(undefined, ts_morph_1.ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope);
662
- interfaceNameMatches = currentPropTypeText.match(/\b([A-Z][a-zA-Z0-9]*)\s*\[/g);
663
- if (interfaceNameMatches) {
664
- for (_j = 0, interfaceNameMatches_2 = interfaceNameMatches; _j < interfaceNameMatches_2.length; _j++) {
665
- match = interfaceNameMatches_2[_j];
666
- interfaceName = match.replace(/\s*\[$/, '').trim();
667
- interfaceDecl = handlerFile.getInterface(interfaceName) || handlerFile.getTypeAlias(interfaceName);
668
- if (interfaceDecl) {
669
- // Interface is in same file - copy it and all its dependencies recursively
670
- this.copyInterfaceWithDependencies(interfaceDecl, handlerFile);
671
- }
672
- }
673
- }
674
- typeArgs = propType.getTypeArguments();
675
- if (typeArgs && typeArgs.length > 0) {
676
- for (_k = 0, typeArgs_1 = typeArgs; _k < typeArgs_1.length; _k++) {
677
- typeArg = typeArgs_1[_k];
678
- argSymbol = typeArg.getSymbol();
679
- if (argSymbol) {
680
- argDeclaration = argSymbol.getDeclarations()[0];
681
- if (argDeclaration && argDeclaration.getSourceFile() !== handlerFile) {
682
- this.tsSchemasSymbolsToImports.push(argSymbol);
683
- }
684
- }
685
- }
686
- }
687
- }
688
- // If we have a declaration, check if we need to import any types
689
- if (declaration) {
690
- for (_l = 0, _m = (0, TypeScriptUtils_1.getTypesToImport)(declaration); _l < _m.length; _l++) {
691
- typeToImport = _m[_l];
692
- typeSymbol = typeToImport.getSymbol();
693
- if (typeSymbol) {
694
- declaredTypeSymbol = typeSymbol.getDeclaredType().getSymbol();
695
- if (declaredTypeSymbol) {
696
- this.tsSchemasSymbolsToImports.push(declaredTypeSymbol);
697
- }
698
- }
699
- }
1300
+ if (resTypeName && schema_extraction_1.TypeScriptSourceParser.shouldGenerateSchema(resTypeName)) {
1301
+ resSchemaType = this.resolveTypeNameToSchemaId(fileText, resTypeName, filePath);
1302
+ if (!resSchemaType && !isSkipValidation) {
1303
+ baseName = require("path").basename(filePath);
1304
+ perfLog.warn("Handler ".concat(baseName, ": Could not resolve response type \"").concat(resTypeName, "\" to schema $id. Make sure it's exported from src/schemas/"));
700
1305
  }
701
- generatedSchemaInterfaceStr = "export interface ".concat(schemaInterfaceName, " { ").concat(propertySignatures.join(";\n"), " }");
702
1306
  }
703
- else {
704
- console.log("[WARN] Unknown schema type", schema.getText());
1307
+ paramsMetadata = [];
1308
+ queryMetadata = [];
1309
+ if (paramsTypeName && paramsTypeName !== "any" && paramsTypeName !== "void") {
1310
+ metadata = schema_extraction_1.TypeScriptSourceParser.extractPropertyMetadata(fileText, paramsTypeName);
1311
+ if (metadata) {
1312
+ paramsMetadata = metadata;
1313
+ }
705
1314
  }
706
- if (generatedSchemaInterfaceStr) {
707
- this.parsedTsSchemas.push(generatedSchemaInterfaceStr);
708
- return [2 /*return*/, schemaInterfaceName];
1315
+ if (queryTypeName && queryTypeName !== "any" && queryTypeName !== "void") {
1316
+ metadata = schema_extraction_1.TypeScriptSourceParser.extractPropertyMetadata(fileText, queryTypeName);
1317
+ if (metadata) {
1318
+ queryMetadata = metadata;
1319
+ }
709
1320
  }
710
- return [2 /*return*/];
1321
+ return [2 /*return*/, {
1322
+ reqSchemaType: reqSchemaType,
1323
+ resSchemaType: resSchemaType,
1324
+ queryMetadata: queryMetadata,
1325
+ paramsMetadata: paramsMetadata,
1326
+ }];
711
1327
  });
712
1328
  });
713
1329
  };
714
- TypeScriptCompiler.prototype.initJsonSchemaGenerator = function (schemaFilePath) {
715
- var tsconfigPath = (0, path_1.join)(this.cwd, "tsconfig.json");
716
- var conf = {
717
- path: schemaFilePath, // Point to the intermediate schema file
718
- expose: "none", // Do not create shared $ref definitions.
719
- topRef: false, // Removes the wrapper object around the schema.
720
- additionalProperties: false,
721
- jsDoc: "basic",
722
- sortProps: false,
723
- strictTuples: false,
724
- minify: false,
725
- markdownDescription: false,
726
- skipTypeCheck: false,
727
- encodeRefs: false,
728
- extraTags: [],
729
- functions: "fail",
730
- discriminatorType: "json-schema",
731
- tsconfig: tsconfigPath,
732
- };
733
- console.log("Creating TypeScript program for schema generation:");
734
- console.log(" Schema file:", schemaFilePath);
735
- console.log(" tsconfig:", tsconfigPath);
736
- // Create a fresh TypeScript Program that includes the schema file
737
- // This ensures ts-json-schema-generator can find the types we just generated
738
- var program;
739
- try {
740
- program = (0, ts_json_schema_generator_1.createProgram)(conf);
741
- }
742
- catch (error) {
743
- // Format the error in a more developer-friendly way
744
- console.error("\n❌ Schema generation failed due to TypeScript compilation errors:\n");
745
- if (error.diagnostic && error.diagnostic.relatedInformation) {
746
- // Extract and display only the relevant error messages
747
- for (var _i = 0, _a = error.diagnostic.relatedInformation; _i < _a.length; _i++) {
748
- var info = _a[_i];
749
- if (info.file) {
750
- var _b = info.file.getLineAndCharacterOfPosition(info.start), line = _b.line, character = _b.character;
751
- var fileName = info.file.fileName.replace(this.cwd, ".");
752
- var message = typeof info.messageText === "string" ? info.messageText : info.messageText.messageText;
753
- console.error(" ".concat(fileName, ":").concat(line + 1, ":").concat(character + 1));
754
- console.error(" ".concat(message, "\n"));
755
- }
756
- }
757
- }
758
- else if (error.message) {
759
- console.error(" ".concat(error.message, "\n"));
760
- }
761
- console.error("💡 Tip: Fix the TypeScript errors above and try again.\n");
762
- process.exit(1);
1330
+ /**
1331
+ * Check if handler source file is up for auto registration by inspecting
1332
+ * the Route.
1333
+ *
1334
+ * @param sf handler file
1335
+ * @returns
1336
+ */
1337
+ TypeScriptCompiler.prototype.isAutoRegisterableHandler = function (sf) {
1338
+ var route = sf.getVariableDeclaration("Route");
1339
+ if (!route) {
1340
+ return false;
763
1341
  }
764
- console.log(" TypeScript version:", ts_morph_1.ts.version);
765
- console.log(" Program root files:", program.getRootFileNames().length);
766
- var formatter = (0, ts_json_schema_generator_1.createFormatter)(conf);
767
- var parser = (0, ts_json_schema_generator_1.createParser)(program, conf);
768
- var generator = new ts_json_schema_generator_1.SchemaGenerator(program, parser, formatter, conf);
769
- return generator;
770
- };
771
- TypeScriptCompiler.prototype.generateAndSaveJsonSchemas = function (schemas, schemaFilePath) {
1342
+ var routeProps = route.getFirstDescendantByKindOrThrow(ts_morph_1.SyntaxKind.ObjectLiteralExpression);
1343
+ var skipAutoRegProp = routeProps.getProperty("skipAutoRegister");
1344
+ return !skipAutoRegProp || skipAutoRegProp.getText().endsWith("false");
1345
+ };
1346
+ /**
1347
+ * Generates JSON schemas for all handlers and tools.
1348
+ * Should be called after parseHandlers() and parseTools() have completed.
1349
+ *
1350
+ * NEW SIMPLIFIED APPROACH:
1351
+ * 1. Generate schema universe from src/schemas/**\/*.ts (includes wrappers/)
1352
+ * 2. Handlers/tools reference schemas by $id
1353
+ * 3. Create manifest with schema universe and references
1354
+ */
1355
+ TypeScriptCompiler.prototype.generateAllSchemas = function () {
772
1356
  return __awaiter(this, void 0, void 0, function () {
773
- var jsonSchemas, _i, schemas_1, _a, reqSchemaType, resSchemaType, mergedSchemas, filePath;
774
- var _b, _c;
775
- return __generator(this, function (_d) {
776
- switch (_d.label) {
1357
+ var schemaGenStartTime, path, schemaDir, schemaFiles, schemaUniverse, _i, schemaFiles_1, schemaFile, absolutePath, fileSchemas, schemaGenTime;
1358
+ var _this = this;
1359
+ return __generator(this, function (_a) {
1360
+ switch (_a.label) {
777
1361
  case 0:
778
- // Reset schema generator to use the newly created intermediate schema file
779
- this.schemaGenerator = undefined;
780
- jsonSchemas = [];
781
- for (_i = 0, schemas_1 = schemas; _i < schemas_1.length; _i++) {
782
- _a = schemas_1[_i], reqSchemaType = _a.reqSchemaType, resSchemaType = _a.resSchemaType;
783
- if (reqSchemaType) {
784
- jsonSchemas.push({ definitions: (_b = {}, _b[reqSchemaType] = this.generateJsonSchema(reqSchemaType, schemaFilePath), _b) });
1362
+ schemaGenStartTime = Date.now();
1363
+ // Initialize schema generator
1364
+ return [4 /*yield*/, this.initSchemaGenerator()];
1365
+ case 1:
1366
+ // Initialize schema generator
1367
+ _a.sent();
1368
+ if (!this.schemaGenerator) {
1369
+ throw new Error("Schema generator not initialized");
1370
+ }
1371
+ path = require("path");
1372
+ schemaDir = path.join(this.cwd, "src/schemas");
1373
+ // Check if schemas directory exists
1374
+ if (!fs_1.default.existsSync(schemaDir)) {
1375
+ perfLog.warn("No src/schemas/ directory found. Skipping schema generation.");
1376
+ return [2 /*return*/];
1377
+ }
1378
+ return [4 /*yield*/, (0, tiny_glob_1.default)(path.join(schemaDir, "**/*.ts"))];
1379
+ case 2:
1380
+ schemaFiles = _a.sent();
1381
+ perfLog.debug("Found ".concat(schemaFiles.length, " schema files in src/schemas/"));
1382
+ schemaUniverse = {};
1383
+ for (_i = 0, schemaFiles_1 = schemaFiles; _i < schemaFiles_1.length; _i++) {
1384
+ schemaFile = schemaFiles_1[_i];
1385
+ absolutePath = path.join(this.cwd, schemaFile);
1386
+ try {
1387
+ fileSchemas = this.schemaGenerator(absolutePath, {
1388
+ followImports: "local",
1389
+ schemaVersion: "http://json-schema.org/draft-07/schema#",
1390
+ includeJSDoc: true,
1391
+ strictObjects: false,
1392
+ additionalProperties: undefined,
1393
+ defineId: function (typeName, declaration, context) {
1394
+ if (!context)
1395
+ return typeName;
1396
+ // Generate stable $id using same algorithm as resolveTypeNameToSchemaId
1397
+ return _this.filePathToSchemaId(context.absolutePath, typeName);
1398
+ },
1399
+ });
1400
+ // Merge schemas from this file into universe
1401
+ schemaUniverse = __assign(__assign({}, schemaUniverse), fileSchemas);
785
1402
  }
786
- if (resSchemaType) {
787
- jsonSchemas.push({ definitions: (_c = {}, _c[resSchemaType] = this.generateJsonSchema(resSchemaType, schemaFilePath), _c) });
1403
+ catch (error) {
1404
+ perfLog.warn("Failed to generate schemas from ".concat(schemaFile, ": ").concat(error.message));
788
1405
  }
789
1406
  }
790
- mergedSchemas = jsonSchemas.reduce(function (out, schema) {
791
- if (schema.definitions) {
792
- out.definitions = __assign(__assign({}, out.definitions), schema.definitions);
793
- }
794
- return out;
795
- }, {
796
- $schema: "http://json-schema.org/draft-07/schema#",
797
- $ref: "#/definitions/Schemas",
798
- definitions: {},
799
- });
800
- filePath = (0, path_1.join)(this.cwd, ".flink", "schemas", "schemas.json");
801
- return [4 /*yield*/, (0, FsUtils_1.writeJsonFile)(filePath, mergedSchemas)];
802
- case 1:
803
- _d.sent();
804
- this.project.addSourceFileAtPath(filePath);
805
- return [2 /*return*/, mergedSchemas];
1407
+ perfLog.debug("Generated ".concat(Object.keys(schemaUniverse).length, " schemas from ").concat(schemaFiles.length, " files"));
1408
+ perfLog.debug("Sample schema $ids: ".concat(Object.keys(schemaUniverse).slice(0, 10).join(", "), "..."));
1409
+ // Create manifest with schema universe and handler/tool references
1410
+ return [4 /*yield*/, this.generateSchemaManifest(schemaUniverse)];
1411
+ case 3:
1412
+ // Create manifest with schema universe and handler/tool references
1413
+ _a.sent();
1414
+ schemaGenTime = Date.now() - schemaGenStartTime;
1415
+ initLog.info("\u2713 Schema generation completed in ".concat(schemaGenTime, "ms ") +
1416
+ "(".concat(Object.keys(schemaUniverse).length, " schemas, ").concat(this.handlerSchemasToGenerate.length, " handlers, ").concat(this.toolSchemasToGenerate.length, " tools)"));
1417
+ return [2 /*return*/];
806
1418
  }
807
1419
  });
808
1420
  });
809
1421
  };
810
- TypeScriptCompiler.prototype.generateJsonSchema = function (typeName, schemaFilePath) {
811
- if (!this.schemaGenerator) {
812
- this.schemaGenerator = this.initJsonSchemaGenerator(schemaFilePath);
813
- }
814
- return this.schemaGenerator.createSchema(typeName);
1422
+ /**
1423
+ * Computes relative path from project root for a source file.
1424
+ * This is used consistently for manifest keys and __file exports.
1425
+ */
1426
+ TypeScriptCompiler.prototype.getRelativePath = function (sf) {
1427
+ var filePath = sf.getFilePath();
1428
+ return filePath.startsWith(this.cwd) ? filePath.substring(this.cwd.length + 1) : filePath;
815
1429
  };
816
- TypeScriptCompiler.prototype.extractSchemaTypeFromHandler = function (handlerTypeReference) {
1430
+ /**
1431
+ * Generates schema manifest with schema universe and handler/tool references.
1432
+ */
1433
+ TypeScriptCompiler.prototype.generateSchemaManifest = function (schemaUniverse) {
817
1434
  return __awaiter(this, void 0, void 0, function () {
818
- var handlerType, handlerTypeArgs, reqSchema, resSchema, params, query, sf, createReqSchemaPromise, createResSchemaPromise, _a, reqSchemaType, resSchemaType;
819
- return __generator(this, function (_b) {
820
- switch (_b.label) {
1435
+ var manifest, _i, _a, handler, relativePath, _b, _c, tool, relativePath, manifestPath;
1436
+ return __generator(this, function (_d) {
1437
+ switch (_d.label) {
821
1438
  case 0:
822
- handlerType = handlerTypeReference.getTypeName().getText();
823
- handlerTypeArgs = handlerTypeReference.getType().getAliasTypeArguments();
824
- if (handlerType === "Handler") {
825
- // `Handler<Ctx, ReqSchema, ResSchema, Params, Query>`
826
- // 0 = Ctx, 1 = Req schema, 2 = Res schema, 3 = Params, 4 = Query
827
- reqSchema = handlerTypeArgs[1];
828
- resSchema = handlerTypeArgs[2];
829
- params = handlerTypeArgs[3];
830
- query = handlerTypeArgs[4];
831
- }
832
- else if (handlerType === "GetHandler") {
833
- // `GetHandler<Ctx, ResSchema, Params, Query>`
834
- // 0 = Ctx, 1 = Res schema, 2 = Params, 3 = Query
835
- resSchema = handlerTypeArgs[1];
836
- params = handlerTypeArgs[2];
837
- query = handlerTypeArgs[3];
1439
+ manifest = {
1440
+ version: "2.0",
1441
+ generated: new Date().toISOString(),
1442
+ schemas: schemaUniverse,
1443
+ handlers: {},
1444
+ tools: {},
1445
+ };
1446
+ // Map handlers with their schema references
1447
+ for (_i = 0, _a = this.handlerSchemasToGenerate; _i < _a.length; _i++) {
1448
+ handler = _a[_i];
1449
+ relativePath = this.getRelativePath(handler.sourceFile);
1450
+ // Validate schema references exist
1451
+ if (handler.reqSchemaType && !schemaUniverse[handler.reqSchemaType]) {
1452
+ perfLog.warn("Handler ".concat(handler.sourceFile.getBaseName(), " references request schema \"").concat(handler.reqSchemaType, "\" which was not found in schema universe"));
1453
+ }
1454
+ if (handler.resSchemaType && !schemaUniverse[handler.resSchemaType]) {
1455
+ perfLog.warn("Handler ".concat(handler.sourceFile.getBaseName(), " references response schema \"").concat(handler.resSchemaType, "\" which was not found in schema universe"));
1456
+ }
1457
+ manifest.handlers[relativePath] = {
1458
+ reqSchemaName: handler.reqSchemaType,
1459
+ resSchemaName: handler.resSchemaType,
1460
+ queryMetadata: handler.queryMetadata || [],
1461
+ paramsMetadata: handler.paramsMetadata || [],
1462
+ };
838
1463
  }
839
- else {
840
- throw new Error("Unknown handler type ".concat(handlerType, " in ").concat(handlerTypeReference.getSourceFile().getBaseName(), " - should be Handler or GetHandler"));
1464
+ // Map tools with their schema references
1465
+ for (_b = 0, _c = this.toolSchemasToGenerate; _b < _c.length; _b++) {
1466
+ tool = _c[_b];
1467
+ relativePath = this.getRelativePath(tool.sourceFile);
1468
+ // Validate schema references exist
1469
+ if (tool.inputSchemaType && !schemaUniverse[tool.inputSchemaType]) {
1470
+ perfLog.warn("Tool ".concat(tool.sourceFile.getBaseName(), " references input schema \"").concat(tool.inputSchemaType, "\" which was not found in schema universe"));
1471
+ }
1472
+ if (tool.outputSchemaType && !schemaUniverse[tool.outputSchemaType]) {
1473
+ perfLog.warn("Tool ".concat(tool.sourceFile.getBaseName(), " references output schema \"").concat(tool.outputSchemaType, "\" which was not found in schema universe"));
1474
+ }
1475
+ manifest.tools[relativePath] = {
1476
+ inputSchemaName: tool.inputSchemaType,
1477
+ outputSchemaName: tool.outputSchemaType,
1478
+ inputTypeHint: tool.inputTypeHint,
1479
+ outputTypeHint: tool.outputTypeHint,
1480
+ };
841
1481
  }
842
- sf = handlerTypeReference.getSourceFile();
843
- createReqSchemaPromise = reqSchema
844
- ? this.saveIntermediateTsSchema(reqSchema, sf, "".concat(handlerTypeReference.getStartLineNumber(), "_ReqSchema"))
845
- : Promise.resolve("");
846
- createResSchemaPromise = resSchema
847
- ? this.saveIntermediateTsSchema(resSchema, sf, "".concat(handlerTypeReference.getStartLineNumber(), "_ResSchema"))
848
- : Promise.resolve("");
849
- return [4 /*yield*/, Promise.all([createReqSchemaPromise, createResSchemaPromise])];
1482
+ manifestPath = (0, path_1.join)(this.cwd, "dist/.flink/schema-manifest.json");
1483
+ return [4 /*yield*/, (0, FsUtils_1.writeJsonFile)(manifestPath, manifest, { ensureDir: true })];
850
1484
  case 1:
851
- _a = _b.sent(), reqSchemaType = _a[0], resSchemaType = _a[1];
852
- return [2 /*return*/, {
853
- reqSchemaType: reqSchemaType,
854
- resSchemaType: resSchemaType,
855
- queryMetadata: (0, TypeScriptUtils_1.getTypeMetadata)(query),
856
- paramsMetadata: (0, TypeScriptUtils_1.getTypeMetadata)(params),
857
- }];
1485
+ _d.sent();
1486
+ perfLog.debug("Schema manifest written to: ".concat(manifestPath));
1487
+ return [2 /*return*/];
858
1488
  }
859
1489
  });
860
1490
  });
861
1491
  };
862
1492
  /**
863
- * Creates generated source file that contains all
864
- * TypeScript schemas that has been derived from handlers.
1493
+ * Scans project for jobs so they can be registered during start.
865
1494
  */
866
- TypeScriptCompiler.prototype.createIntermediateSchemaFile = function () {
1495
+ TypeScriptCompiler.prototype.parseJobs = function () {
867
1496
  return __awaiter(this, void 0, void 0, function () {
868
- var schemaSourceFile;
1497
+ var startTime, generatedFile, jobsArr, imports, jobElements, jobParseTime;
1498
+ var _this = this;
869
1499
  return __generator(this, function (_a) {
870
- switch (_a.label) {
871
- case 0:
872
- schemaSourceFile = this.createSourceFile(["schemas", "schemas.ts"], "// Generated ".concat(new Date(), "\n").concat(this.parsedTsSchemas.join("\n\n")));
873
- (0, TypeScriptUtils_1.addImports)(schemaSourceFile, this.tsSchemasSymbolsToImports);
874
- return [4 /*yield*/, schemaSourceFile.save()];
875
- case 1:
876
- _a.sent();
877
- // Return the file path so it can be used by ts-json-schema-generator
878
- return [2 /*return*/, schemaSourceFile.getFilePath()];
879
- }
1500
+ startTime = Date.now();
1501
+ generatedFile = this.createSourceFile(["generatedJobs.ts"], "// Generated ".concat(new Date(), "\nimport { autoRegisteredJobs } from \"@flink-app/flink\";\nexport const jobs = [];\nautoRegisteredJobs.push(...jobs);\n "));
1502
+ jobsArr = generatedFile.getVariableDeclarationOrThrow("jobs").getFirstDescendantByKindOrThrow(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
1503
+ imports = [];
1504
+ jobElements = [];
1505
+ // Use pre-segmented job files (no filtering needed)
1506
+ this.jobFiles.forEach(function (sf, i) {
1507
+ console.log("Detected job ".concat(sf.getBaseName()));
1508
+ var namespaceImport = sf.getBaseNameWithoutExtension().replace(/\./g, "_") + "_" + i;
1509
+ imports.push({
1510
+ defaultImport: "* as " + namespaceImport,
1511
+ moduleSpecifier: _this.getModuleSpecifier(generatedFile, sf),
1512
+ });
1513
+ // __file is set on the registration object (not injected into source files)
1514
+ jobElements.push("{...".concat(namespaceImport, ", __file: \"").concat(_this.getRelativePath(sf), "\"}"));
1515
+ });
1516
+ // Add all job elements in one batch operation
1517
+ jobsArr.addElements(jobElements);
1518
+ generatedFile.addImportDeclarations(imports);
1519
+ // Defer save until batch save at end (performance optimization)
1520
+ // Cleanup: forget job file nodes to reduce memory overhead (ts-morph performance optimization)
1521
+ this.jobFiles.forEach(function (sf) {
1522
+ sf.getClasses().forEach(function (cls) { return cls.forget(); });
1523
+ });
1524
+ jobParseTime = Date.now() - startTime;
1525
+ perfLog.info("\u2713 Job parsing completed in ".concat(jobParseTime, "ms (").concat(jobElements.length, " jobs)"));
1526
+ return [2 /*return*/, generatedFile];
880
1527
  });
881
1528
  });
882
1529
  };
883
1530
  /**
884
- * Check if handler source file is up for auto registration by inspecting
885
- * the Route.
886
- *
887
- * @param sf handler file
888
- * @returns
1531
+ * Scans project for services so they can be registered during start.
889
1532
  */
890
- TypeScriptCompiler.prototype.isAutoRegisterableHandler = function (sf) {
891
- var route = sf.getVariableDeclaration("Route");
892
- if (!route) {
893
- return false;
894
- }
895
- var routeProps = route.getFirstDescendantByKindOrThrow(ts_morph_1.SyntaxKind.ObjectLiteralExpression);
896
- var skipAutoRegProp = routeProps.getProperty("skipAutoRegister");
897
- return !skipAutoRegProp || skipAutoRegProp.getText().endsWith("false");
1533
+ TypeScriptCompiler.prototype.parseServices = function () {
1534
+ return __awaiter(this, void 0, void 0, function () {
1535
+ var startTime, generatedFile, servicesArr, imports, serviceElements, _i, _a, sf, serviceParseTime;
1536
+ return __generator(this, function (_b) {
1537
+ if (this.disableServices) {
1538
+ initLog.info("Services disabled via flink.config.js (disableServices: true)");
1539
+ }
1540
+ startTime = Date.now();
1541
+ generatedFile = this.createSourceFile(["generatedServices.ts"], "// Generated ".concat(new Date(), "\nimport { autoRegisteredServices } from \"@flink-app/flink\";\nexport const services: any[] = [];\nautoRegisteredServices.push(...services);\n "));
1542
+ servicesArr = generatedFile.getVariableDeclarationOrThrow("services").getFirstDescendantByKindOrThrow(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
1543
+ imports = [];
1544
+ serviceElements = [];
1545
+ for (_i = 0, _a = this.serviceFiles; _i < _a.length; _i++) {
1546
+ sf = _a[_i];
1547
+ console.log("Detected service ".concat(sf.getBaseName()));
1548
+ imports.push({
1549
+ defaultImport: sf.getBaseNameWithoutExtension(),
1550
+ moduleSpecifier: this.getModuleSpecifier(generatedFile, sf),
1551
+ });
1552
+ serviceElements.push("{serviceInstanceName: \"".concat((0, utils_1.getRepoInstanceName)(sf.getBaseName()), "\", Service: ").concat(sf.getBaseNameWithoutExtension(), "}"));
1553
+ }
1554
+ servicesArr.addElements(serviceElements);
1555
+ generatedFile.addImportDeclarations(imports);
1556
+ // Cleanup: forget service file nodes to reduce memory overhead
1557
+ this.serviceFiles.forEach(function (sf) {
1558
+ sf.getClasses().forEach(function (cls) { return cls.forget(); });
1559
+ });
1560
+ serviceParseTime = Date.now() - startTime;
1561
+ perfLog.info("\u2713 Service parsing completed in ".concat(serviceParseTime, "ms (").concat(serviceElements.length, " services)"));
1562
+ return [2 /*return*/, generatedFile];
1563
+ });
1564
+ });
898
1565
  };
899
1566
  /**
900
- * Appends generated json schemas to handler source files.
901
- *
902
- * @param handlers
903
- * @param jsonSchemas
1567
+ * Generates a .flink/generatedXxx.ts file for a single compiler plugin extension.
1568
+ * Mirrors the same namespace-import + spread pattern used by parseJobs.
904
1569
  */
905
- TypeScriptCompiler.prototype.appendSchemasToHandlerSourceFiles = function (handlers, jsonSchemas) {
906
- var jsonSchemaDefs = jsonSchemas.definitions || {};
907
- for (var _i = 0, handlers_1 = handlers; _i < handlers_1.length; _i++) {
908
- var _a = handlers_1[_i], sourceFile = _a.sourceFile, reqSchemaType = _a.reqSchemaType, resSchemaType = _a.resSchemaType;
909
- if (reqSchemaType && !jsonSchemaDefs[reqSchemaType]) {
910
- console.error("Handler ".concat(sourceFile.getBaseName(), " has request schema\u00A0(").concat(reqSchemaType, ") defined, but no JSON schema has been generated"));
911
- continue;
912
- }
913
- if (resSchemaType && !jsonSchemaDefs[resSchemaType]) {
914
- console.error("Handler ".concat(sourceFile.getBaseName(), " has response schema\u00A0(").concat(resSchemaType, ") defined, but no JSON schema has been generated"));
915
- continue;
916
- }
917
- var reqJsonSchema = JSON.stringify(reqSchemaType ? jsonSchemaDefs[reqSchemaType] : undefined);
918
- var resJsonSchema = JSON.stringify(resSchemaType ? jsonSchemaDefs[resSchemaType] : undefined);
919
- // Remove existing __schemas variable if it exists (to avoid redeclaration errors)
920
- var existingSchemas = sourceFile.getVariableStatements().filter(function (vs) {
921
- var varNames = vs.getDeclarations().map(function (d) { return d.getName(); });
922
- return varNames.includes("__schemas");
923
- });
924
- existingSchemas.forEach(function (v) { return v.remove(); });
925
- sourceFile.addVariableStatement({
926
- declarationKind: ts_morph_1.VariableDeclarationKind.Const,
927
- isExported: true,
928
- declarations: [
929
- {
930
- name: "__schemas",
931
- type: "any",
932
- initializer: "{ reqSchema: ".concat(reqJsonSchema, ", resSchema: ").concat(resJsonSchema, " }"),
933
- },
934
- ],
1570
+ TypeScriptCompiler.prototype.parseExtensionDir = function (ext) {
1571
+ return __awaiter(this, void 0, void 0, function () {
1572
+ var startTime, files, generatedFile, itemsArr, imports, itemElements, elapsed;
1573
+ var _this = this;
1574
+ var _a;
1575
+ return __generator(this, function (_b) {
1576
+ startTime = Date.now();
1577
+ files = (_a = this.extensionFiles.get(ext.generatedFile)) !== null && _a !== void 0 ? _a : [];
1578
+ generatedFile = this.createSourceFile(["".concat(ext.generatedFile, ".ts")], "// Generated ".concat(new Date(), "\nimport { ").concat(ext.registrationVar, " } from \"").concat(ext.package, "\";\nexport const items: any[] = [];\n").concat(ext.registrationVar, ".push(...items);\n"));
1579
+ itemsArr = generatedFile.getVariableDeclarationOrThrow("items").getFirstDescendantByKindOrThrow(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
1580
+ imports = [];
1581
+ itemElements = [];
1582
+ files.forEach(function (sf, i) {
1583
+ var namespaceImport = sf.getBaseNameWithoutExtension().replace(/\./g, "_") + "_" + i;
1584
+ imports.push({
1585
+ defaultImport: "* as " + namespaceImport,
1586
+ moduleSpecifier: _this.getModuleSpecifier(generatedFile, sf),
1587
+ });
1588
+ itemElements.push("{...".concat(namespaceImport, ", __file: \"").concat(_this.getRelativePath(sf), "\"}"));
1589
+ });
1590
+ itemsArr.addElements(itemElements);
1591
+ generatedFile.addImportDeclarations(imports);
1592
+ elapsed = Date.now() - startTime;
1593
+ initLog.info("\u2713 Extension dir \"".concat(ext.scanDir, "\" parsed in ").concat(elapsed, "ms (").concat(files.length, " files)"));
1594
+ return [2 /*return*/, generatedFile];
935
1595
  });
936
- }
1596
+ });
937
1597
  };
938
1598
  /**
939
- * Scans project for jobs so they can be registered during start.
1599
+ * Iterates all compilerPlugins from flink.config.js and generates
1600
+ * a .flink/generatedXxx.ts file for each one.
1601
+ * Call this after parseJobs() and before generateStartScript().
940
1602
  */
941
- TypeScriptCompiler.prototype.parseJobs = function () {
1603
+ TypeScriptCompiler.prototype.parseAllExtensionDirs = function () {
942
1604
  return __awaiter(this, void 0, void 0, function () {
943
- var generatedFile, jobsArr, imports, i, _i, _a, sf, namespaceImport, existingFile;
1605
+ var _i, _a, ext;
944
1606
  return __generator(this, function (_b) {
945
1607
  switch (_b.label) {
946
1608
  case 0:
947
- generatedFile = this.createSourceFile(["generatedJobs.ts"], "// Generated ".concat(new Date(), "\nimport { autoRegisteredJobs } from \"@flink-app/flink\";\nexport const jobs = [];\nautoRegisteredJobs.push(...jobs);\n "));
948
- jobsArr = generatedFile.getVariableDeclarationOrThrow("jobs").getFirstDescendantByKindOrThrow(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
949
- imports = [];
950
- i = 0;
951
- for (_i = 0, _a = this.project.getSourceFiles(); _i < _a.length; _i++) {
952
- sf = _a[_i];
953
- if (!sf.getFilePath().includes("src/jobs/")) {
954
- continue;
955
- }
956
- console.log("Detected job ".concat(sf.getBaseName()));
957
- namespaceImport = sf.getBaseNameWithoutExtension().replace(/\./g, "_") + "_" + i;
958
- imports.push({
959
- defaultImport: "* as " + namespaceImport,
960
- moduleSpecifier: this.getModuleSpecifier(generatedFile, sf),
961
- });
962
- existingFile = sf.getVariableStatements().filter(function (vs) {
963
- var varNames = vs.getDeclarations().map(function (d) { return d.getName(); });
964
- return varNames.includes("__file");
965
- });
966
- existingFile.forEach(function (v) { return v.remove(); });
967
- // Append metadata to source file that will be part of emitted dist bundle (javascript)
968
- sf.addVariableStatement({
969
- declarationKind: ts_morph_1.VariableDeclarationKind.Const,
970
- isExported: true,
971
- declarations: [
972
- {
973
- name: "__file",
974
- initializer: "\"".concat(sf.getBaseName(), "\""),
975
- },
976
- ],
977
- });
978
- jobsArr.insertElement(i, namespaceImport);
979
- i++;
980
- }
981
- generatedFile.addImportDeclarations(imports);
982
- return [4 /*yield*/, generatedFile.save()];
1609
+ _i = 0, _a = this.compilerPlugins;
1610
+ _b.label = 1;
983
1611
  case 1:
1612
+ if (!(_i < _a.length)) return [3 /*break*/, 4];
1613
+ ext = _a[_i];
1614
+ return [4 /*yield*/, this.parseExtensionDir(ext)];
1615
+ case 2:
984
1616
  _b.sent();
985
- return [2 /*return*/, generatedFile];
1617
+ _b.label = 3;
1618
+ case 3:
1619
+ _i++;
1620
+ return [3 /*break*/, 1];
1621
+ case 4: return [2 /*return*/];
986
1622
  }
987
1623
  });
988
1624
  });