@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
@@ -0,0 +1,699 @@
1
+ import Logger from "node-color-log";
2
+
3
+ type LogLevel = "trace" | "debug" | "info" | "warn" | "error";
4
+
5
+ const LOG_LEVELS: Record<LogLevel, number> = {
6
+ trace: 0,
7
+ debug: 1,
8
+ info: 2,
9
+ warn: 3,
10
+ error: 4,
11
+ };
12
+
13
+ /**
14
+ * Component-specific logger with independent log level control
15
+ */
16
+ export class ComponentLogger {
17
+ private componentName: string;
18
+ private componentLevel: LogLevel | null = null;
19
+ private namedLogger: any;
20
+
21
+ constructor(componentName: string) {
22
+ this.componentName = componentName;
23
+ this.namedLogger = Logger.createNamedLogger(componentName);
24
+ }
25
+
26
+ /**
27
+ * Set log level for this specific component.
28
+ * Overrides the global log level.
29
+ */
30
+ setLevel(level: LogLevel) {
31
+ this.componentLevel = level;
32
+ }
33
+
34
+ /**
35
+ * Clear component-specific level and fall back to global level
36
+ */
37
+ clearLevel() {
38
+ this.componentLevel = null;
39
+ }
40
+
41
+ /**
42
+ * Check if a message should be logged based on component and global levels
43
+ */
44
+ private shouldLog(messageLevel: LogLevel): boolean {
45
+ const effectiveLevel = this.componentLevel ?? FlinkLogFactory.getGlobalLevel();
46
+ return LOG_LEVELS[messageLevel] >= LOG_LEVELS[effectiveLevel];
47
+ }
48
+
49
+ trace(...args: any[]) {
50
+ if (this.shouldLog("trace")) {
51
+ // node-color-log doesn't support trace, use console.log for stdout
52
+ if (FlinkLogFactory.getShowTimestamps()) {
53
+ const timestamp = new Date().toISOString();
54
+ console.log(`${timestamp} [${this.componentName}] [TRACE]`, ...args);
55
+ } else {
56
+ console.log(`[${this.componentName}] [TRACE]`, ...args);
57
+ }
58
+ }
59
+ }
60
+
61
+ debug(...args: any[]) {
62
+ if (this.shouldLog("debug")) {
63
+ this.namedLogger.debug(...args);
64
+ }
65
+ }
66
+
67
+ info(...args: any[]) {
68
+ if (this.shouldLog("info")) {
69
+ this.namedLogger.info(...args);
70
+ }
71
+ }
72
+
73
+ warn(...args: any[]) {
74
+ if (this.shouldLog("warn")) {
75
+ // Use console.warn to write to stderr (Unix/POSIX best practice)
76
+ if (FlinkLogFactory.getShowTimestamps()) {
77
+ const timestamp = new Date().toISOString();
78
+ console.warn(`${timestamp} [${this.componentName}] [WARN]`, ...args);
79
+ } else {
80
+ console.warn(`[${this.componentName}] [WARN]`, ...args);
81
+ }
82
+ }
83
+ }
84
+
85
+ error(...args: any[]) {
86
+ if (this.shouldLog("error")) {
87
+ // Use console.error to write to stderr (Unix/POSIX best practice)
88
+ if (FlinkLogFactory.getShowTimestamps()) {
89
+ const timestamp = new Date().toISOString();
90
+ console.error(`${timestamp} [${this.componentName}] [ERROR]`, ...args);
91
+ } else {
92
+ console.error(`[${this.componentName}] [ERROR]`, ...args);
93
+ }
94
+ }
95
+ }
96
+
97
+ json(...args: any[]) {
98
+ if (this.shouldLog("debug")) {
99
+ for (const o of args) {
100
+ console.log(JSON.stringify(o, null, 2));
101
+ }
102
+ }
103
+ }
104
+
105
+ bgColorLog(color: string, ...args: any[]) {
106
+ if (this.shouldLog("debug")) {
107
+ this.namedLogger.bgColorLog(color, ...args);
108
+ }
109
+ }
110
+
111
+ fontColorLog(color: string, ...args: any[]) {
112
+ if (this.shouldLog("info")) {
113
+ this.namedLogger.fontColorLog(color, ...args);
114
+ }
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Configuration for logging system
120
+ */
121
+ export interface LoggingConfig {
122
+ /**
123
+ * Global log level (default: "info")
124
+ */
125
+ global?: LogLevel;
126
+
127
+ /**
128
+ * Show timestamps in log messages (default: true)
129
+ */
130
+ showTimestamps?: boolean;
131
+
132
+ /**
133
+ * Component-specific log levels with wildcard support
134
+ *
135
+ * Supports:
136
+ * - Exact match: "flink.ai.openai" → matches only "flink.ai.openai"
137
+ * - Prefix match: "flink.ai." → matches "flink.ai.openai", "flink.ai.anthropic", etc.
138
+ * - Single-level wildcard: "flink.ai.*" → matches "flink.ai.openai" but NOT "flink.ai.openai.v4"
139
+ * - Multi-level wildcard: "flink.ai.**" → matches any depth under "flink.ai"
140
+ *
141
+ * @example
142
+ * {
143
+ * "flink.ai.openai": "trace", // exact match
144
+ * "flink.ai.*": "debug", // single-level wildcard
145
+ * "flink.database.**": "warn", // multi-level wildcard
146
+ * "flink.handlers.": "info" // prefix match (trailing dot)
147
+ * }
148
+ */
149
+ components?: Record<string, LogLevel>;
150
+
151
+ /**
152
+ * @internal
153
+ * Internal use only - hierarchical configurations parsed from components map
154
+ */
155
+ hierarchical?: Array<{
156
+ prefix: string;
157
+ level: LogLevel;
158
+ specificity: number;
159
+ }>;
160
+
161
+ /**
162
+ * @internal
163
+ * Internal use only - wildcard configurations parsed from components map
164
+ */
165
+ wildcards?: Array<{
166
+ pattern: string;
167
+ level: LogLevel;
168
+ specificity: number;
169
+ }>;
170
+ }
171
+
172
+ /**
173
+ * Factory for creating component-specific loggers with hierarchical level control
174
+ */
175
+ export class FlinkLogFactory {
176
+ private static globalLevel: LogLevel = "info";
177
+ private static loggers: Map<string, ComponentLogger> = new Map();
178
+ private static initialized = false;
179
+ private static showTimestamps: boolean = true;
180
+
181
+ /**
182
+ * Store component configurations from environment/config for lazy application
183
+ */
184
+ private static componentConfigs: Record<string, LogLevel> = {};
185
+
186
+ /**
187
+ * Hierarchical prefix configurations (Java-style)
188
+ * Sorted by specificity (most specific first) for efficient matching
189
+ */
190
+ private static hierarchicalConfigs: Array<{
191
+ prefix: string;
192
+ level: LogLevel;
193
+ specificity: number;
194
+ }> = [];
195
+
196
+ /**
197
+ * Wildcard pattern configurations (optional, advanced)
198
+ * Sorted by specificity (most specific first) for efficient matching
199
+ */
200
+ private static wildcardConfigs: Array<{
201
+ pattern: string;
202
+ level: LogLevel;
203
+ specificity: number;
204
+ }> = [];
205
+
206
+ /**
207
+ * Set the global log level (affects all loggers without component-specific levels)
208
+ */
209
+ static setGlobalLevel(level: LogLevel) {
210
+ FlinkLogFactory.globalLevel = level;
211
+ // node-color-log doesn't support trace, so use debug as the effective level
212
+ const effectiveLevel = level === "trace" ? "debug" : level;
213
+ Logger.setLevel(effectiveLevel);
214
+ }
215
+
216
+ /**
217
+ * Get the current global log level
218
+ */
219
+ static getGlobalLevel(): LogLevel {
220
+ return FlinkLogFactory.globalLevel;
221
+ }
222
+
223
+ /**
224
+ * Get all registered loggers (useful for debugging)
225
+ */
226
+ static getLoggers(): Map<string, ComponentLogger> {
227
+ return FlinkLogFactory.loggers;
228
+ }
229
+
230
+ /**
231
+ * Enable or disable timestamps in log messages
232
+ */
233
+ static setShowTimestamps(show: boolean) {
234
+ FlinkLogFactory.showTimestamps = show;
235
+ }
236
+
237
+ /**
238
+ * Check if timestamps are enabled
239
+ */
240
+ static getShowTimestamps(): boolean {
241
+ return FlinkLogFactory.showTimestamps;
242
+ }
243
+
244
+ /**
245
+ * Clear all component-specific log levels (fall back to global)
246
+ */
247
+ static resetComponentLevels() {
248
+ FlinkLogFactory.componentConfigs = {};
249
+ for (const logger of Array.from(FlinkLogFactory.loggers.values())) {
250
+ logger.clearLevel();
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Clear all hierarchical prefix configurations
256
+ */
257
+ static resetHierarchicalLevels() {
258
+ FlinkLogFactory.hierarchicalConfigs = [];
259
+ }
260
+
261
+ /**
262
+ * Clear all wildcard pattern configurations
263
+ */
264
+ static resetWildcardLevels() {
265
+ FlinkLogFactory.wildcardConfigs = [];
266
+ }
267
+
268
+ /**
269
+ * Set log level for a specific component by name (exact match)
270
+ */
271
+ static setComponentLevel(componentName: string, level: LogLevel | null) {
272
+ const normalized = FlinkLogFactory.normalize(componentName);
273
+
274
+ if (level === null) {
275
+ // Remove exact match configuration
276
+ delete FlinkLogFactory.componentConfigs[normalized];
277
+ const logger = FlinkLogFactory.loggers.get(normalized);
278
+ if (logger) {
279
+ logger.clearLevel();
280
+ }
281
+ } else {
282
+ // Set exact match configuration
283
+ FlinkLogFactory.componentConfigs[normalized] = level;
284
+ const logger = FlinkLogFactory.loggers.get(normalized);
285
+ if (logger) {
286
+ logger.setLevel(level);
287
+ }
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Set hierarchical prefix-based log level (Java-style)
293
+ * @param prefix Logger name prefix (e.g., "flink.ai" matches all "flink.ai.*")
294
+ * @param level Log level to apply
295
+ *
296
+ * @example
297
+ * FlinkLogFactory.setHierarchicalLevel("flink.ai", "debug");
298
+ * // Now all loggers starting with "flink.ai." will use debug level
299
+ * // This includes the exact match "flink.ai" AND all children "flink.ai.*"
300
+ */
301
+ static setHierarchicalLevel(prefix: string, level: LogLevel) {
302
+ const normalized = FlinkLogFactory.normalize(prefix);
303
+ // Ensure prefix ends with dot for consistent matching
304
+ const prefixWithDot = normalized.endsWith(".") ? normalized : normalized + ".";
305
+ const specificity = FlinkLogFactory.calculatePrefixSpecificity(normalized);
306
+
307
+ // Remove existing config for this prefix if it exists
308
+ FlinkLogFactory.hierarchicalConfigs = FlinkLogFactory.hierarchicalConfigs.filter(
309
+ (c) => c.prefix !== prefixWithDot
310
+ );
311
+
312
+ // Add new config
313
+ FlinkLogFactory.hierarchicalConfigs.push({
314
+ prefix: prefixWithDot,
315
+ level,
316
+ specificity,
317
+ });
318
+
319
+ // Also set exact match for the prefix itself (without trailing dot)
320
+ // This ensures "flink.ai.openai" matches when using setHierarchicalLevel("flink.ai.openai", "debug")
321
+ FlinkLogFactory.componentConfigs[normalized] = level;
322
+
323
+ // Sort by specificity (most specific first) for efficient matching
324
+ FlinkLogFactory.hierarchicalConfigs.sort((a, b) => b.specificity - a.specificity);
325
+ }
326
+
327
+ /**
328
+ * Set wildcard pattern-based log level (advanced)
329
+ * @param pattern Wildcard pattern (e.g., "flink.ai.*" or "flink.ai.**")
330
+ * @param level Log level to apply
331
+ *
332
+ * @example
333
+ * FlinkLogFactory.setWildcardLevel("flink.ai.*", "debug");
334
+ * // Matches flink.ai.openai but NOT flink.ai.openai.v4
335
+ *
336
+ * FlinkLogFactory.setWildcardLevel("flink.ai.**", "trace");
337
+ * // Matches any depth under flink.ai.
338
+ */
339
+ static setWildcardLevel(pattern: string, level: LogLevel) {
340
+ const normalized = FlinkLogFactory.normalize(pattern);
341
+ const specificity = FlinkLogFactory.calculateWildcardSpecificity(normalized);
342
+
343
+ // Remove existing config for this pattern if it exists
344
+ FlinkLogFactory.wildcardConfigs = FlinkLogFactory.wildcardConfigs.filter(
345
+ (c) => c.pattern !== normalized
346
+ );
347
+
348
+ // Add new config
349
+ FlinkLogFactory.wildcardConfigs.push({
350
+ pattern: normalized,
351
+ level,
352
+ specificity,
353
+ });
354
+
355
+ // Sort by specificity (most specific first) for efficient matching
356
+ FlinkLogFactory.wildcardConfigs.sort((a, b) => b.specificity - a.specificity);
357
+ }
358
+
359
+ /**
360
+ * Get the effective log level for a component (resolved through hierarchy)
361
+ * @param componentName Component name to check
362
+ * @returns Resolved log level or null if only global level applies
363
+ */
364
+ static getEffectiveLevel(componentName: string): LogLevel | null {
365
+ return FlinkLogFactory.resolveComponentLevel(componentName);
366
+ }
367
+
368
+ /**
369
+ * Initialize logging from environment variables and/or configuration file
370
+ *
371
+ * Priority (highest to lowest):
372
+ * 1. Config file (flink.config.js)
373
+ * 2. Environment variable (LOG_LEVEL=debug)
374
+ *
375
+ * @param fileConfig Optional logging configuration from flink.config.js
376
+ *
377
+ * @example Environment Variable
378
+ * ```bash
379
+ * LOG_LEVEL=debug pnpm run dev
380
+ * ```
381
+ *
382
+ * @example Config File (flink.config.js)
383
+ * ```javascript
384
+ * module.exports = {
385
+ * logging: {
386
+ * global: "info",
387
+ * showTimestamps: true,
388
+ * components: {
389
+ * "flink.ai.openai": "trace", // exact match
390
+ * "flink.ai.*": "debug", // single-level wildcard
391
+ * "flink.database.**": "warn", // multi-level wildcard
392
+ * "flink.handlers.": "info" // prefix match
393
+ * }
394
+ * }
395
+ * };
396
+ * ```
397
+ */
398
+ static configure(fileConfig?: LoggingConfig) {
399
+ if (FlinkLogFactory.initialized) {
400
+ return;
401
+ }
402
+
403
+ // 1. Parse environment variables (lowest priority)
404
+ const envConfig = FlinkLogFactory.parseEnvironment();
405
+
406
+ // 2. Parse file config components map (highest priority)
407
+ let fileComponentsParsed: {
408
+ exact: Record<string, LogLevel>;
409
+ hierarchical: Array<{ prefix: string; level: LogLevel; specificity: number }>;
410
+ wildcards: Array<{ pattern: string; level: LogLevel; specificity: number }>;
411
+ } = { exact: {}, hierarchical: [], wildcards: [] };
412
+ if (fileConfig?.components) {
413
+ fileComponentsParsed = FlinkLogFactory.parseComponentsMap(fileConfig.components);
414
+ }
415
+
416
+ // 3. Merge configurations
417
+ const finalConfig = {
418
+ global: fileConfig?.global || envConfig.global || "info",
419
+ showTimestamps: fileConfig?.showTimestamps ?? true, // default: true
420
+ exact: {
421
+ ...fileComponentsParsed.exact,
422
+ },
423
+ hierarchical: [...fileComponentsParsed.hierarchical],
424
+ wildcards: [...fileComponentsParsed.wildcards],
425
+ };
426
+
427
+ // 4. Apply global level
428
+ FlinkLogFactory.setGlobalLevel(finalConfig.global);
429
+
430
+ // 5. Apply timestamp setting
431
+ FlinkLogFactory.setShowTimestamps(finalConfig.showTimestamps);
432
+
433
+ // 6. Store exact component configs
434
+ FlinkLogFactory.componentConfigs = finalConfig.exact;
435
+
436
+ // 7. Store hierarchical configs (sorted by specificity)
437
+ FlinkLogFactory.hierarchicalConfigs = finalConfig.hierarchical.sort(
438
+ (a, b) => b.specificity - a.specificity
439
+ );
440
+
441
+ // 8. Store wildcard configs (sorted by specificity)
442
+ FlinkLogFactory.wildcardConfigs = finalConfig.wildcards.sort(
443
+ (a, b) => b.specificity - a.specificity
444
+ );
445
+
446
+ FlinkLogFactory.initialized = true;
447
+ }
448
+
449
+ /**
450
+ * Parse logging configuration from environment variables
451
+ * Supports only LOG_LEVEL for global level setting
452
+ */
453
+ private static parseEnvironment(): LoggingConfig {
454
+ const config: LoggingConfig = {};
455
+
456
+ if (process.env.LOG_LEVEL) {
457
+ const level = process.env.LOG_LEVEL.toLowerCase();
458
+ if (FlinkLogFactory.isValidLogLevel(level)) {
459
+ config.global = level as LogLevel;
460
+ }
461
+ }
462
+
463
+ return config;
464
+ }
465
+
466
+ /**
467
+ * Normalize logger name to lowercase (Java-style convention)
468
+ * @param name Logger name or pattern
469
+ * @returns Normalized lowercase name
470
+ */
471
+ private static normalize(name: string): string {
472
+ return name.toLowerCase();
473
+ }
474
+
475
+ /**
476
+ * Determine if a name should be treated as a hierarchical prefix
477
+ * @param name Normalized logger name
478
+ * @returns True if it should be treated as a prefix
479
+ */
480
+ private static shouldTreatAsPrefix(name: string): boolean {
481
+ // Default: if name contains dots, treat as hierarchical prefix
482
+ // This covers the common case where "flink.ai" should match "flink.ai.*"
483
+ return name.includes(".");
484
+ }
485
+
486
+ /**
487
+ * Calculate specificity for prefix matching
488
+ * More specific prefixes (more segments) have higher specificity
489
+ * @param prefix Prefix string (may or may not end with dot)
490
+ * @returns Specificity score (number of segments)
491
+ */
492
+ private static calculatePrefixSpecificity(prefix: string): number {
493
+ // Remove trailing dot if present
494
+ const normalized = prefix.endsWith(".") ? prefix.slice(0, -1) : prefix;
495
+ // Count dot-separated segments
496
+ return normalized.split(".").filter((s) => s.length > 0).length;
497
+ }
498
+
499
+ /**
500
+ * Calculate specificity for wildcard patterns
501
+ * More specific patterns (more non-wildcard segments) have higher specificity
502
+ * For same segment count, * (single-level) is more specific than ** (multi-level)
503
+ * @param pattern Wildcard pattern
504
+ * @returns Specificity score (base + decimal for wildcard type)
505
+ */
506
+ private static calculateWildcardSpecificity(pattern: string): number {
507
+ const segments = pattern.split(".");
508
+ const nonWildcardCount = segments.filter((seg) => seg !== "*" && seg !== "**" && seg.length > 0).length;
509
+
510
+ // Base specificity: number of non-wildcard segments
511
+ let specificity = nonWildcardCount * 10;
512
+
513
+ // Add fractional specificity based on wildcard type
514
+ // * (single-level) = more specific than ** (multi-level)
515
+ if (pattern.includes("*")) {
516
+ if (pattern.includes("**")) {
517
+ specificity += 1; // ** is less specific
518
+ } else {
519
+ specificity += 5; // * is more specific
520
+ }
521
+ }
522
+
523
+ return specificity;
524
+ }
525
+
526
+ /**
527
+ * Resolve component log level through hierarchical matching
528
+ * Order of precedence (highest to lowest):
529
+ * 1. Exact match
530
+ * 2. Most specific prefix match
531
+ * 3. Less specific prefix match
532
+ * 4. Wildcard pattern match
533
+ * 5. null (falls back to global level)
534
+ *
535
+ * @param componentName Component name to resolve
536
+ * @returns Resolved log level or null
537
+ */
538
+ private static resolveComponentLevel(componentName: string): LogLevel | null {
539
+ const normalized = FlinkLogFactory.normalize(componentName);
540
+
541
+ // 1. Try exact match first (O(1) - fastest, highest priority)
542
+ if (FlinkLogFactory.componentConfigs[normalized]) {
543
+ return FlinkLogFactory.componentConfigs[normalized];
544
+ }
545
+
546
+ // 2. Try hierarchical prefix matches (O(n) where n = number of prefixes)
547
+ // Already sorted by specificity (most specific first)
548
+ for (const config of FlinkLogFactory.hierarchicalConfigs) {
549
+ if (normalized.startsWith(config.prefix)) {
550
+ return config.level;
551
+ }
552
+ }
553
+
554
+ // 3. Try wildcard patterns (O(n) where n = number of patterns)
555
+ // Already sorted by specificity (most specific first)
556
+ for (const config of FlinkLogFactory.wildcardConfigs) {
557
+ if (FlinkLogFactory.matchWildcard(normalized, config.pattern)) {
558
+ return config.level;
559
+ }
560
+ }
561
+
562
+ // 4. No match - fall back to global level
563
+ return null;
564
+ }
565
+
566
+ /**
567
+ * Simple wildcard matching for logger patterns
568
+ * Supports:
569
+ * - * matches single segment (flink.ai.* matches flink.ai.openai)
570
+ * - ** matches any depth (flink.ai.** matches flink.ai.openai.v4)
571
+ * - Partial segment match (flink.ai.open* matches flink.ai.openai)
572
+ *
573
+ * @param name Logger name to match
574
+ * @param pattern Wildcard pattern
575
+ * @returns True if pattern matches
576
+ */
577
+ private static matchWildcard(name: string, pattern: string): boolean {
578
+ // Special handling for ** (multi-level wildcard)
579
+ // Need to process dots BEFORE replacing **
580
+
581
+ // Step 1: Escape dots first (before any other processing)
582
+ let regexPattern = pattern.replace(/\./g, "\\.");
583
+
584
+ // Step 2: Replace ** with placeholder (matches anything including dots)
585
+ regexPattern = regexPattern.replace(/\*\*/g, "___DOUBLE_STAR___");
586
+
587
+ // Step 3: Replace single * with single-segment match (non-dot chars)
588
+ regexPattern = regexPattern.replace(/\*/g, "[^\\.]+");
589
+
590
+ // Step 4: Replace ** placeholder with multi-segment match
591
+ regexPattern = regexPattern.replace(/___DOUBLE_STAR___/g, ".*");
592
+
593
+ const regex = new RegExp(`^${regexPattern}$`);
594
+ return regex.test(name);
595
+ }
596
+
597
+ /**
598
+ * Parse components map and classify entries into exact, prefix, or wildcard matches
599
+ * @param components Components map from config
600
+ * @returns Classified configuration
601
+ */
602
+ private static parseComponentsMap(components: Record<string, LogLevel>): {
603
+ exact: Record<string, LogLevel>;
604
+ hierarchical: Array<{ prefix: string; level: LogLevel; specificity: number }>;
605
+ wildcards: Array<{ pattern: string; level: LogLevel; specificity: number }>;
606
+ } {
607
+ const exact: Record<string, LogLevel> = {};
608
+ const hierarchical: Array<{ prefix: string; level: LogLevel; specificity: number }> = [];
609
+ const wildcards: Array<{ pattern: string; level: LogLevel; specificity: number }> = [];
610
+
611
+ for (const [key, level] of Object.entries(components)) {
612
+ const normalized = FlinkLogFactory.normalize(key);
613
+
614
+ if (normalized.includes("*")) {
615
+ // Wildcard pattern (e.g., "flink.ai.*" or "flink.ai.**")
616
+ wildcards.push({
617
+ pattern: normalized,
618
+ level,
619
+ specificity: FlinkLogFactory.calculateWildcardSpecificity(normalized),
620
+ });
621
+ } else if (normalized.endsWith(".")) {
622
+ // Prefix match (e.g., "flink.ai.")
623
+ hierarchical.push({
624
+ prefix: normalized,
625
+ level,
626
+ specificity: FlinkLogFactory.calculatePrefixSpecificity(normalized),
627
+ });
628
+ } else if (normalized.includes(".")) {
629
+ // Ambiguous: could be exact or prefix
630
+ // Store as BOTH exact and hierarchical
631
+ exact[normalized] = level;
632
+ hierarchical.push({
633
+ prefix: normalized + ".",
634
+ level,
635
+ specificity: FlinkLogFactory.calculatePrefixSpecificity(normalized),
636
+ });
637
+ } else {
638
+ // Simple name (no dots) - exact match only
639
+ exact[normalized] = level;
640
+ }
641
+ }
642
+
643
+ // Sort by specificity (most specific first)
644
+ hierarchical.sort((a, b) => b.specificity - a.specificity);
645
+ wildcards.sort((a, b) => b.specificity - a.specificity);
646
+
647
+ return { exact, hierarchical, wildcards };
648
+ }
649
+
650
+ /**
651
+ * Check if a string is a valid log level
652
+ */
653
+ private static isValidLogLevel(level: string): boolean {
654
+ return ["trace", "debug", "info", "warn", "error"].includes(level);
655
+ }
656
+
657
+ /**
658
+ * Create or retrieve a component-specific logger
659
+ * Automatically applies environment/config settings when logger is created
660
+ * Supports hierarchical prefix matching (Java-style)
661
+ *
662
+ * @param componentName Logger name (lowercase dot notation recommended, e.g., "flink.ai.openai")
663
+ * @returns ComponentLogger instance
664
+ *
665
+ * @example
666
+ * ```typescript
667
+ * // Java-style hierarchical naming (recommended)
668
+ * const log = FlinkLogFactory.createLogger("flink.ai.openai");
669
+ * const dbLog = FlinkLogFactory.createLogger("flink.database.mongodb");
670
+ *
671
+ * // Case insensitive - these create the same logger
672
+ * const log1 = FlinkLogFactory.createLogger("flink.ai.openai");
673
+ * const log2 = FlinkLogFactory.createLogger("Flink.AI.OpenAI"); // Same instance
674
+ * ```
675
+ */
676
+ static createLogger(componentName: string): ComponentLogger {
677
+ // Initialize from environment and config file if not already done
678
+ if (!FlinkLogFactory.initialized) {
679
+ const { loadFlinkConfig } = require("./utils/loadFlinkConfig");
680
+ const flinkConfig = loadFlinkConfig();
681
+ FlinkLogFactory.configure(flinkConfig?.logging);
682
+ }
683
+
684
+ // Normalize to lowercase for consistent lookup
685
+ const normalized = FlinkLogFactory.normalize(componentName);
686
+
687
+ if (!FlinkLogFactory.loggers.has(normalized)) {
688
+ const logger = new ComponentLogger(normalized);
689
+ FlinkLogFactory.loggers.set(normalized, logger);
690
+
691
+ // Resolve level through hierarchical matching
692
+ const resolvedLevel = FlinkLogFactory.resolveComponentLevel(normalized);
693
+ if (resolvedLevel) {
694
+ logger.setLevel(resolvedLevel);
695
+ }
696
+ }
697
+ return FlinkLogFactory.loggers.get(normalized)!;
698
+ }
699
+ }