@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
package/cli/clean.ts CHANGED
@@ -1,31 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  import TypeScriptCompiler from "../src/TypeScriptCompiler";
3
+ import { parseArgs, hasFlag, printHelp } from "./cli-utils";
3
4
 
4
- module.exports = async function run(args: string[]) {
5
- if (args.includes("--help")) {
6
- console.log(`
7
- Description
8
- Removes all generated files.
5
+ module.exports = async function clean(args: string[]) {
6
+ const parsed = parseArgs(args);
9
7
 
10
- Usage
11
- $ flink clean <dir>
8
+ if (hasFlag(parsed, "help")) {
9
+ printHelp({
10
+ description: "Removes all generated files.",
11
+ usage: "$ flink clean <dir>",
12
+ options: [{ name: "help", description: "Displays this message" }],
13
+ });
14
+ return;
15
+ }
12
16
 
13
- <dir> represents the directory of the Flink application.
14
- If no directory is provided, the current directory will be used.
15
-
16
- Options
17
- --help Displays this message
18
- `);
19
-
20
- process.exit(0);
21
- }
22
-
23
- let dir = "./";
24
- if (args[0] && !args[0].startsWith("--")) {
25
- dir = args[0];
26
- }
27
-
28
- const cleanedFolder = await TypeScriptCompiler.clean(dir);
29
-
30
- console.log(`Cleaned directory ${cleanedFolder}`);
17
+ const cleanedFolder = await TypeScriptCompiler.clean(parsed.dir);
18
+ console.log(`Cleaned directory ${cleanedFolder}`);
31
19
  };
package/cli/cli-utils.ts CHANGED
@@ -1,22 +1,195 @@
1
- export function getOption(
2
- args: string[],
3
- name: string,
4
- defaultValue: string | boolean,
5
- opts: { isBoolean?: boolean } = {}
6
- ) {
7
- const option = `--${name}`;
8
- if (args.includes(option)) {
9
- if (opts.isBoolean && args[args.indexOf(option) + 1] !== "false") {
10
- return true;
1
+ import TypeScriptCompiler from "../src/TypeScriptCompiler";
2
+
3
+ // --- Arg Parsing ---
4
+
5
+ export interface ParsedArgs {
6
+ dir: string;
7
+ flags: Set<string>;
8
+ options: Map<string, string>;
9
+ }
10
+
11
+ export function parseArgs(args: string[]): ParsedArgs {
12
+ const flags = new Set<string>();
13
+ const options = new Map<string, string>();
14
+ let dir = "./";
15
+
16
+ let i = 0;
17
+
18
+ // First non-flag arg is the directory
19
+ if (args[0] && !args[0].startsWith("--")) {
20
+ dir = args[0];
21
+ i = 1;
11
22
  }
12
23
 
13
- const value = args[args.indexOf(option) + 1];
24
+ while (i < args.length) {
25
+ const arg = args[i];
26
+ if (arg.startsWith("--")) {
27
+ const name = arg.slice(2);
28
+ const next = args[i + 1];
29
+ if (next && !next.startsWith("--")) {
30
+ options.set(name, next);
31
+ i += 2;
32
+ } else {
33
+ flags.add(name);
34
+ i += 1;
35
+ }
36
+ } else {
37
+ i += 1;
38
+ }
39
+ }
40
+
41
+ return { dir, flags, options };
42
+ }
43
+
44
+ export function hasFlag(parsed: ParsedArgs, name: string): boolean {
45
+ return parsed.flags.has(name);
46
+ }
47
+
48
+ export function getOption(parsed: ParsedArgs, name: string, defaultValue: string): string {
49
+ return parsed.options.get(name) ?? defaultValue;
50
+ }
51
+
52
+ export function normalizeEntry(entry: string): string {
53
+ return entry.startsWith("/") ? entry : "/" + entry;
54
+ }
55
+
56
+ // --- Help ---
57
+
58
+ interface HelpOption {
59
+ name: string;
60
+ description: string;
61
+ }
62
+
63
+ interface HelpConfig {
64
+ description: string;
65
+ usage: string;
66
+ options: HelpOption[];
67
+ }
68
+
69
+ export function printHelp(config: HelpConfig): void {
70
+ const optionLines = config.options
71
+ .map((opt) => ` --${opt.name.padEnd(14)} ${opt.description}`)
72
+ .join("\n");
73
+
74
+ console.log(`
75
+ Description
76
+ ${config.description}
77
+
78
+ Usage
79
+ ${config.usage}
80
+
81
+ <dir> represents the directory of the Flink application.
82
+ If no directory is provided, the current directory will be used.
83
+
84
+ Options
85
+ ${optionLines}
86
+ `);
87
+ }
14
88
 
15
- if (!value || value.startsWith("--")) {
16
- console.log("WARN: Missing value for option", option);
17
- process.exit(1);
89
+ // --- Compile ---
90
+
91
+ export interface CompileOptions {
92
+ dir: string;
93
+ entry?: string;
94
+ typeCheck?: boolean;
95
+ timingLogs?: boolean;
96
+ }
97
+
98
+ export async function compile(opts: CompileOptions): Promise<void> {
99
+ const { dir, entry, typeCheck = true, timingLogs = false } = opts;
100
+
101
+ const start = timingLogs ? Date.now() : 0;
102
+ const time = (label: string, stepStart: number) => {
103
+ if (timingLogs) {
104
+ console.log(`⏱️ ${label}: ${Date.now() - stepStart}ms`);
105
+ }
106
+ };
107
+
108
+ await TypeScriptCompiler.clean(dir);
109
+
110
+ let stepStart = Date.now();
111
+ const compiler = new TypeScriptCompiler(dir);
112
+ time("Compiler initialization", stepStart);
113
+
114
+ stepStart = Date.now();
115
+ await compiler.parseRepos();
116
+ await compiler.parseTools();
117
+ await compiler.parseAgents();
118
+ await compiler.parseJobs();
119
+ await compiler.parseServices();
120
+ await compiler.parseAllExtensionDirs();
121
+ await compiler.parseHandlers();
122
+ await compiler.generateStartScript(entry);
123
+ await compiler.generateAllSchemas();
124
+ await compiler.saveAllModifiedFiles();
125
+
126
+ if (typeCheck) {
127
+ stepStart = Date.now();
128
+ if (!compiler.getPreEmitDiagnostics()) {
129
+ throw new Error("Type checking failed");
130
+ }
131
+ time("Type checking (getPreEmitDiagnostics)", stepStart);
132
+ }
133
+
134
+ stepStart = Date.now();
135
+ if (process.env.FLINK_USE_TSC === "true") {
136
+ compiler.emitWithTsc();
137
+ } else {
138
+ await compiler.emit();
18
139
  }
19
- return value;
20
- }
21
- return defaultValue;
140
+ time(`Transpilation (${process.env.FLINK_USE_TSC === "true" ? "tsc" : "swc"})`, stepStart);
141
+
142
+ if (timingLogs) {
143
+ console.log(`Compilation done in ${Date.now() - start}ms`);
144
+ }
145
+ }
146
+
147
+ // --- Fork ---
148
+
149
+ export interface ForkOptions {
150
+ dir: string;
151
+ args: string[];
152
+ cliFlags: string[];
153
+ stdio?: "inherit" | "pipe" | "ignore";
154
+ }
155
+
156
+ export function forkApp(opts: ForkOptions): ReturnType<typeof require> {
157
+ const { dir, args, cliFlags, stdio } = opts;
158
+
159
+ // Filter out CLI-specific flags and their values from args passed to child process
160
+ const cliSet = new Set(cliFlags.map((f) => `--${f}`));
161
+ const extraArgs: string[] = [];
162
+
163
+ for (let i = 0; i < args.length; i++) {
164
+ if (cliSet.has(args[i])) {
165
+ // Skip this flag and its value (if the next arg is not a flag)
166
+ const next = args[i + 1];
167
+ if (next && !next.startsWith("--")) {
168
+ i++; // skip value too
169
+ }
170
+ continue;
171
+ }
172
+ // Skip the dir argument (first non-flag arg)
173
+ if (i === 0 && !args[i].startsWith("--")) {
174
+ continue;
175
+ }
176
+ extraArgs.push(args[i]);
177
+ }
178
+
179
+ const forkOpts: any = {};
180
+ if (stdio) {
181
+ forkOpts.stdio = stdio;
182
+ }
183
+
184
+ const forkedProcess = require("child_process").fork(
185
+ dir + "/dist/.flink/start.js",
186
+ extraArgs,
187
+ forkOpts
188
+ );
189
+
190
+ forkedProcess.on("exit", (code: any) => {
191
+ process.exit(code);
192
+ });
193
+
194
+ return forkedProcess;
22
195
  }
package/cli/dev.ts ADDED
@@ -0,0 +1,252 @@
1
+ #!/usr/bin/env node
2
+ import chokidar from "chokidar";
3
+ import { spawn, ChildProcess } from "child_process";
4
+ import { resolve, join, dirname } from "path";
5
+ import { existsSync } from "fs";
6
+ import { parseArgs, hasFlag, getOption, normalizeEntry, printHelp, compile, forkApp } from "./cli-utils";
7
+ import { FlinkLogFactory } from "../src/FlinkLogFactory";
8
+ import { loadFlinkConfig } from "../src/utils/loadFlinkConfig";
9
+ import { loadEnvFiles } from "./loadEnvFiles";
10
+
11
+ // Load config BEFORE creating logger
12
+ const flinkConfig = loadFlinkConfig();
13
+ FlinkLogFactory.configure(flinkConfig?.logging);
14
+
15
+ const logger = FlinkLogFactory.createLogger("flink.dev");
16
+
17
+ module.exports = async function dev(args: string[]) {
18
+ const parsed = parseArgs(args);
19
+
20
+ // Load .env files before anything else so all env vars are available at startup.
21
+ // NODE_ENV defaults to "development" when not explicitly set.
22
+ if (!process.env.NODE_ENV) {
23
+ process.env.NODE_ENV = "development";
24
+ }
25
+
26
+ const initLogger = FlinkLogFactory.createLogger("flink.init");
27
+ const { loaded, injected } = loadEnvFiles(parsed.dir, "development");
28
+
29
+ if (loaded.length > 0) {
30
+ initLogger.info(`Loaded env files: ${loaded.join(", ")} (${injected} vars injected)`);
31
+ }
32
+
33
+ if (hasFlag(parsed, "help")) {
34
+ printHelp({
35
+ description:
36
+ "Starts development mode with watch and auto-reload.\n Fast rebuilds with swc (skips type checking by default).\n Runs background tsc --watch for async type error reporting.",
37
+ usage: "$ flink dev <dir>",
38
+ options: [
39
+ { name: "typecheck", description: "Run type checking on every rebuild (slower, disables background tsc)" },
40
+ { name: "no-typecheck", description: "Disable background type checking entirely" },
41
+ { name: "entry", description: 'Entry script for app, default "/src/index.ts"' },
42
+ { name: "help", description: "Displays this message" },
43
+ ],
44
+ });
45
+ return;
46
+ }
47
+
48
+ const dir = parsed.dir;
49
+ const entry = normalizeEntry(getOption(parsed, "entry", "/src/index.ts"));
50
+ const typecheck = hasFlag(parsed, "typecheck");
51
+ const noTypecheck = hasFlag(parsed, "no-typecheck");
52
+
53
+ console.log("🚀 Flink dev mode starting...\n");
54
+
55
+ // Initial build (skip type checking for speed unless --typecheck flag)
56
+ console.log(`📦 Initial build ${typecheck ? "(with type checking)" : "(fast mode, skipping type check)"}...`);
57
+ await compile({ dir, entry, typeCheck: typecheck });
58
+
59
+ let serverProcess: ChildProcess | null = null;
60
+ let tscProcess: ChildProcess | null = null;
61
+ let isRebuilding = false;
62
+ let pendingRebuild = false;
63
+
64
+ // Start server
65
+ serverProcess = startServer(dir);
66
+
67
+ // Start background tsc --watch for async type checking (unless --typecheck or --no-typecheck)
68
+ if (!typecheck && !noTypecheck) {
69
+ tscProcess = startTscWatch(dir);
70
+ }
71
+
72
+ // Watch for changes
73
+ const watcher = chokidar.watch([`${dir}/src/**/*.ts`, `${dir}/src/**/*.json`], {
74
+ ignored: ["**/.flink/**", "**/dist/**", "**/node_modules/**", "**/*.spec.ts", "**/*.test.ts"],
75
+ persistent: true,
76
+ ignoreInitial: true,
77
+ });
78
+
79
+ watcher.on("change", async (path) => {
80
+ if (isRebuilding) {
81
+ pendingRebuild = true;
82
+ return;
83
+ }
84
+
85
+ isRebuilding = true;
86
+ logger.info(`\n📝 File changed: ${path}`);
87
+ logger.info("🔄 Rebuilding...");
88
+
89
+ const rebuildStart = Date.now();
90
+
91
+ try {
92
+ await compile({ dir, entry, typeCheck: typecheck });
93
+
94
+ // Restart server — wait for old process to fully exit before binding port again
95
+ if (serverProcess) {
96
+ await killAndWait(serverProcess);
97
+ serverProcess = null;
98
+ }
99
+ serverProcess = startServer(dir);
100
+
101
+ const rebuildTime = Date.now() - rebuildStart;
102
+ logger.info(`✅ Rebuild complete in ${rebuildTime}ms\n`);
103
+ } catch (error: any) {
104
+ logger.error(`❌ Rebuild failed: ${error.message}`);
105
+ }
106
+
107
+ isRebuilding = false;
108
+
109
+ // If another change happened during rebuild, trigger again
110
+ if (pendingRebuild) {
111
+ pendingRebuild = false;
112
+ watcher.emit("change", path);
113
+ }
114
+ });
115
+
116
+ watcher.on("ready", () => {
117
+ console.log("\n👀 Watching for changes...");
118
+ if (tscProcess) {
119
+ console.log(" TypeScript type checking running in background");
120
+ }
121
+ console.log(" Press Ctrl+C to stop\n");
122
+ });
123
+
124
+ // Cleanup on exit
125
+ process.on("SIGINT", () => {
126
+ console.log("\n\n🛑 Stopping dev mode...");
127
+ if (serverProcess) {
128
+ serverProcess.kill();
129
+ }
130
+ if (tscProcess) {
131
+ tscProcess.kill();
132
+ }
133
+ watcher.close();
134
+ process.exit(0);
135
+ });
136
+ };
137
+
138
+ function killAndWait(proc: ChildProcess): Promise<void> {
139
+ return new Promise((resolve) => {
140
+ if (proc.exitCode !== null) {
141
+ // Already exited
142
+ resolve();
143
+ return;
144
+ }
145
+ proc.once("exit", () => resolve());
146
+ proc.kill();
147
+ // Safety timeout: if the process doesn't exit within 5s, force-kill it
148
+ const timeout = setTimeout(() => {
149
+ proc.kill("SIGKILL");
150
+ }, 5000);
151
+ proc.once("exit", () => clearTimeout(timeout));
152
+ });
153
+ }
154
+
155
+ function startServer(dir: string): ChildProcess {
156
+ console.log("🚀 Starting server...");
157
+
158
+ const { fork } = require("child_process");
159
+ const serverProcess = fork(`${dir}/dist/.flink/start.js`, [], {
160
+ stdio: "inherit",
161
+ });
162
+
163
+ serverProcess.on("exit", (code: number | null) => {
164
+ if (code !== null && code !== 0) {
165
+ logger.error(`❌ Server exited with code ${code}`);
166
+ }
167
+ });
168
+
169
+ return serverProcess;
170
+ }
171
+
172
+ function findTsc(startDir: string): string | null {
173
+ let current = startDir;
174
+ while (true) {
175
+ const candidate = join(current, "node_modules", ".bin", "tsc");
176
+ if (existsSync(candidate)) {
177
+ return candidate;
178
+ }
179
+ const parent = dirname(current);
180
+ if (parent === current) break;
181
+ current = parent;
182
+ }
183
+ return null;
184
+ }
185
+
186
+ function startTscWatch(dir: string): ChildProcess {
187
+ const absoluteDir = resolve(dir);
188
+ const tscPath = findTsc(absoluteDir);
189
+
190
+ if (!tscPath) {
191
+ logger.warn("Background type checking unavailable: typescript not found in node_modules");
192
+ const noop = spawn("node", ["-e", ""], { stdio: "ignore" });
193
+ return noop;
194
+ }
195
+
196
+ const tsBuildInfoPath = join(absoluteDir, ".flink", "tsconfig.tsbuildinfo");
197
+ const tsc = spawn(tscPath, ["--noEmit", "--watch", "--preserveWatchOutput", "--pretty", "--incremental", "--tsBuildInfoFile", tsBuildInfoPath], {
198
+ cwd: absoluteDir,
199
+ stdio: ["ignore", "pipe", "pipe"],
200
+ });
201
+
202
+ let buffer = "";
203
+
204
+ const processOutput = (data: Buffer) => {
205
+ buffer += data.toString();
206
+
207
+ const lines = buffer.split("\n");
208
+ buffer = lines.pop() || "";
209
+
210
+ for (const line of lines) {
211
+ const trimmed = line.replace(/\x1Bc/g, "").trim();
212
+
213
+ if (!trimmed) continue;
214
+
215
+ // Filter out noisy watch-mode status lines
216
+ if (
217
+ trimmed.includes("Starting compilation") ||
218
+ trimmed.includes("File change detected") ||
219
+ trimmed.match(/^\[\d+:\d+:\d+\s+[AP]M\] Watching for file changes/)
220
+ ) {
221
+ continue;
222
+ }
223
+
224
+ if (trimmed.includes("Found 0 errors")) {
225
+ console.log(`[tsc] ✓ No type errors`);
226
+ } else if (trimmed.match(/Found \d+ errors?/)) {
227
+ // Pretty-print the error count summary
228
+ const match = trimmed.match(/Found (\d+) errors?/);
229
+ const count = match?.[1];
230
+ console.log(`[tsc] ❌ Found ${count} type error${count === "1" ? "" : "s"} — see details above`);
231
+ } else {
232
+ // Show everything else: error details, file paths, code context, etc.
233
+ console.log(`[tsc] ${trimmed}`);
234
+ }
235
+ }
236
+ };
237
+
238
+ tsc.stdout?.on("data", processOutput);
239
+ tsc.stderr?.on("data", processOutput);
240
+
241
+ tsc.on("error", (err) => {
242
+ logger.warn(`Background type checking unavailable: ${err.message}`);
243
+ });
244
+
245
+ tsc.on("exit", (code) => {
246
+ if (code !== null && code !== 0) {
247
+ logger.debug(`Background tsc exited with code ${code}`);
248
+ }
249
+ });
250
+
251
+ return tsc;
252
+ }
@@ -0,0 +1,116 @@
1
+ import { readFileSync, existsSync } from "fs";
2
+ import { resolve, join } from "path";
3
+
4
+ /**
5
+ * Parse the contents of a .env file into a key/value map.
6
+ *
7
+ * Rules:
8
+ * - Empty lines and lines starting with # are skipped
9
+ * - "export FOO=bar" is treated the same as "FOO=bar"
10
+ * - Quoted values (single or double) are unquoted, and their content is used as-is
11
+ * - Unquoted values have inline comments stripped: FOO=bar # comment → "bar"
12
+ */
13
+ export function parseEnvContent(content: string): Record<string, string> {
14
+ const result: Record<string, string> = {};
15
+
16
+ for (const rawLine of content.split("\n")) {
17
+ let line = rawLine.trim();
18
+
19
+ // Skip empty lines and full-line comments
20
+ if (!line || line.startsWith("#")) continue;
21
+
22
+ // Strip "export " prefix
23
+ if (line.startsWith("export ")) {
24
+ line = line.slice(7).trim();
25
+ }
26
+
27
+ const eqIdx = line.indexOf("=");
28
+ if (eqIdx === -1) continue;
29
+
30
+ const key = line.slice(0, eqIdx).trim();
31
+ if (!key) continue;
32
+
33
+ let value = line.slice(eqIdx + 1);
34
+
35
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
36
+ // Quoted value — strip quotes, preserve content as-is
37
+ value = value.slice(1, -1);
38
+ } else {
39
+ // Unquoted — strip inline comments (" # ...") and trim
40
+ const commentIdx = value.search(/ #/);
41
+ if (commentIdx !== -1) {
42
+ value = value.slice(0, commentIdx);
43
+ }
44
+ value = value.trim();
45
+ }
46
+
47
+ result[key] = value;
48
+ }
49
+
50
+ return result;
51
+ }
52
+
53
+ function readEnvFile(filepath: string): Record<string, string> | null {
54
+ if (!existsSync(filepath)) return null;
55
+ try {
56
+ return parseEnvContent(readFileSync(filepath, "utf-8"));
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+
62
+ export interface LoadEnvFilesResult {
63
+ /** Relative filenames that were found and loaded, in load order */
64
+ loaded: string[];
65
+ /** Number of variables injected into process.env (excludes already-set vars) */
66
+ injected: number;
67
+ }
68
+
69
+ /**
70
+ * Load .env files in Next.js-compatible order.
71
+ *
72
+ * Priority (highest wins):
73
+ * 1. process.env (already-set variables are never overridden)
74
+ * 2. .env.[environment].local
75
+ * 3. .env.local (skipped when environment === "test")
76
+ * 4. .env.[environment]
77
+ * 5. .env
78
+ *
79
+ * @param dir Root directory of the Flink app (where .env files live)
80
+ * @param environment "development" | "production" | "test"
81
+ */
82
+ export function loadEnvFiles(dir: string, environment: "development" | "production" | "test"): LoadEnvFilesResult {
83
+ const absoluteDir = resolve(dir);
84
+
85
+ // Files listed from lowest to highest priority so that Object.assign
86
+ // naturally lets later files override earlier ones.
87
+ const candidates: string[] = [
88
+ ".env",
89
+ `.env.${environment}`,
90
+ // .env.local is intentionally skipped in test (matches Next.js behaviour)
91
+ ...(environment !== "test" ? [".env.local"] : []),
92
+ `.env.${environment}.local`,
93
+ ];
94
+
95
+ const loaded: string[] = [];
96
+ const merged: Record<string, string> = {};
97
+
98
+ for (const filename of candidates) {
99
+ const parsed = readEnvFile(join(absoluteDir, filename));
100
+ if (parsed !== null) {
101
+ loaded.push(filename);
102
+ Object.assign(merged, parsed);
103
+ }
104
+ }
105
+
106
+ // Inject into process.env — existing values always win
107
+ let injected = 0;
108
+ for (const [key, value] of Object.entries(merged)) {
109
+ if (!(key in process.env)) {
110
+ process.env[key] = value;
111
+ injected++;
112
+ }
113
+ }
114
+
115
+ return { loaded, injected };
116
+ }