@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.
- package/CHANGELOG.md +1051 -0
- package/SCHEMA_EXTRACTION_ANALYSIS.md +494 -0
- package/SIMPLE_AST_FEASIBILITY.md +570 -0
- package/bin/flink.ts +13 -2
- package/cli/build.ts +24 -44
- package/cli/clean.ts +13 -25
- package/cli/cli-utils.ts +190 -17
- package/cli/dev.ts +252 -0
- package/cli/loadEnvFiles.ts +116 -0
- package/cli/run.ts +45 -62
- package/dist/bin/flink.js +61 -2
- package/dist/cli/build.js +20 -25
- package/dist/cli/clean.js +12 -10
- package/dist/cli/cli-utils.d.ts +34 -3
- package/dist/cli/cli-utils.js +193 -12
- package/dist/cli/dev.d.ts +2 -0
- package/dist/cli/dev.js +279 -0
- package/dist/cli/loadEnvFiles.d.ts +30 -0
- package/dist/cli/loadEnvFiles.js +113 -0
- package/dist/cli/run.js +47 -46
- package/dist/src/DependencyTracker.d.ts +44 -0
- package/dist/src/DependencyTracker.js +239 -0
- package/dist/src/FlinkApp.d.ts +163 -10
- package/dist/src/FlinkApp.js +847 -184
- package/dist/src/FlinkContext.d.ts +41 -0
- package/dist/src/FlinkErrors.d.ts +19 -6
- package/dist/src/FlinkErrors.js +36 -42
- package/dist/src/FlinkHttpHandler.d.ts +219 -26
- package/dist/src/FlinkHttpHandler.js +37 -1
- package/dist/src/FlinkJob.d.ts +10 -0
- package/dist/src/FlinkLog.d.ts +82 -18
- package/dist/src/FlinkLog.js +165 -13
- package/dist/src/FlinkLogFactory.d.ts +288 -0
- package/dist/src/FlinkLogFactory.js +619 -0
- package/dist/src/FlinkRepo.d.ts +10 -2
- package/dist/src/FlinkRepo.js +11 -1
- package/dist/src/FlinkRequestContext.d.ts +63 -0
- package/dist/src/FlinkRequestContext.js +74 -0
- package/dist/src/FlinkResponse.d.ts +6 -0
- package/dist/src/FlinkService.d.ts +38 -0
- package/dist/src/FlinkService.js +46 -0
- package/dist/src/LeaderElection.d.ts +45 -0
- package/dist/src/LeaderElection.js +269 -0
- package/dist/src/SchemaCache.d.ts +84 -0
- package/dist/src/SchemaCache.js +289 -0
- package/dist/src/TypeScriptCompiler.d.ts +161 -51
- package/dist/src/TypeScriptCompiler.js +1253 -617
- package/dist/src/TypeScriptUtils.js +4 -0
- package/dist/src/ai/AgentRunner.d.ts +39 -0
- package/dist/src/ai/AgentRunner.js +760 -0
- package/dist/src/ai/ConversationAgent.d.ts +279 -0
- package/dist/src/ai/ConversationAgent.js +404 -0
- package/dist/src/ai/ConversationFlinkAgent.d.ts +278 -0
- package/dist/src/ai/ConversationFlinkAgent.js +404 -0
- package/dist/src/ai/FlinkAgent.d.ts +690 -0
- package/dist/src/ai/FlinkAgent.js +729 -0
- package/dist/src/ai/FlinkTool.d.ts +135 -0
- package/dist/src/ai/FlinkTool.js +2 -0
- package/dist/src/ai/InMemoryConversationAgent.d.ts +121 -0
- package/dist/src/ai/InMemoryConversationAgent.js +209 -0
- package/dist/src/ai/LLMAdapter.d.ts +148 -0
- package/dist/src/ai/LLMAdapter.js +2 -0
- package/dist/src/ai/PersistentFlinkAgent.d.ts +278 -0
- package/dist/src/ai/PersistentFlinkAgent.js +403 -0
- package/dist/src/ai/SubAgentExecutor.d.ts +38 -0
- package/dist/src/ai/SubAgentExecutor.js +223 -0
- package/dist/src/ai/ToolExecutor.d.ts +64 -0
- package/dist/src/ai/ToolExecutor.js +497 -0
- package/dist/src/ai/agentInstructions.d.ts +68 -0
- package/dist/src/ai/agentInstructions.js +286 -0
- package/dist/src/ai/index.d.ts +8 -0
- package/dist/src/ai/index.js +26 -0
- package/dist/src/ai/instructionFileLoader.d.ts +44 -0
- package/dist/src/ai/instructionFileLoader.js +179 -0
- package/dist/src/auth/FlinkAuthPlugin.d.ts +1 -1
- package/dist/src/handlers/StreamWriterFactory.d.ts +20 -0
- package/dist/src/handlers/StreamWriterFactory.js +83 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.js +17 -0
- package/dist/src/loadPluginSchemas.d.ts +45 -0
- package/dist/src/loadPluginSchemas.js +143 -0
- package/dist/src/schema-extraction/ComplexTypeDetection.d.ts +40 -0
- package/dist/src/schema-extraction/ComplexTypeDetection.js +75 -0
- package/dist/src/schema-extraction/TypeScriptSourceParser.d.ts +321 -0
- package/dist/src/schema-extraction/TypeScriptSourceParser.js +925 -0
- package/dist/src/schema-extraction/TypeScriptSourceParser.spec.d.ts +1 -0
- package/dist/src/schema-extraction/TypeScriptSourceParser.spec.js +233 -0
- package/dist/src/schema-extraction/TypeScriptTokenizer.d.ts +57 -0
- package/dist/src/schema-extraction/TypeScriptTokenizer.js +177 -0
- package/dist/src/schema-extraction/index.d.ts +2 -0
- package/dist/src/schema-extraction/index.js +20 -0
- package/dist/src/schema-extraction/types.d.ts +31 -0
- package/dist/src/schema-extraction/types.js +2 -0
- package/dist/src/utils/loadFlinkConfig.d.ts +53 -0
- package/dist/src/utils/loadFlinkConfig.js +77 -0
- package/dist/src/utils.d.ts +30 -0
- package/dist/src/utils.js +52 -0
- package/dist/src/workers/SchemaGeneratorWorker.d.ts +1 -0
- package/dist/src/workers/SchemaGeneratorWorker.js +49 -0
- package/dist/src/workers/WorkerPool.d.ts +60 -0
- package/dist/src/workers/WorkerPool.js +306 -0
- package/examples/logging-hierarchical-example.ts +125 -0
- package/package.json +29 -4
- package/readme.md +499 -0
- package/spec/AgentDescendantDetection.spec.ts +335 -0
- package/spec/AgentDuplicateDetection.spec.ts +112 -0
- package/spec/AgentObserver.spec.ts +266 -0
- package/spec/AgentRunner.spec.ts +1062 -0
- package/spec/AsyncLocalStorageContext.spec.ts +223 -0
- package/spec/ConversationHooks.spec.ts +257 -0
- package/spec/FlinkAgent.spec.ts +681 -0
- package/spec/FlinkApp.htmlResponse.spec.ts +260 -0
- package/spec/FlinkApp.onError.invocation.spec.ts +151 -0
- package/spec/FlinkApp.onError.spec.ts +1 -2
- package/spec/FlinkApp.query.spec.ts +107 -0
- package/spec/FlinkApp.routeOrdering.spec.ts +61 -0
- package/spec/FlinkApp.undefinedResponse.spec.ts +123 -0
- package/spec/FlinkApp.validationMode.spec.ts +155 -0
- package/spec/FlinkJob.spec.ts +171 -0
- package/spec/FlinkLogFactory.spec.ts +337 -0
- package/spec/FlinkRepo.spec.ts +1 -1
- package/spec/LeaderElection.spec.ts +174 -0
- package/spec/StreamingIntegration.spec.ts +139 -0
- package/spec/ToolExecutor.spec.ts +465 -0
- package/spec/TypeScriptCompiler.spec.ts +1 -1
- package/spec/TypeScriptSourceParser.spec.ts +1215 -0
- package/spec/TypeScriptTokenizer.spec.ts +366 -0
- package/spec/ai/ContextCompaction.spec.ts +405 -0
- package/spec/ai/ConversationAgent.spec.ts +520 -0
- package/spec/ai/InMemoryConversationAgent.spec.ts +144 -0
- package/spec/ai/agentInstructions.spec.ts +358 -0
- package/spec/fixtures/agent-instructions/TestAgent.ts +24 -0
- package/spec/fixtures/agent-instructions/simple.md +3 -0
- package/spec/fixtures/agent-instructions/template.md +18 -0
- package/spec/fixtures/agent-instructions/yaml-format.yaml +9 -0
- package/spec/mock-project/dist/.tsbuildinfo +1 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar.js +56 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar2.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema.js +52 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema2.js +52 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema3.js +52 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema2.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile2.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler.js +53 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler2.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchCar.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOnboardingSession.js +75 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOrderWithComplexTypes.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchProductWithIntersection.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchUserWithUnion.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostCar.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogin.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogout.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PutCar.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/index.js +83 -0
- package/spec/mock-project/dist/spec/mock-project/src/repos/CarRepo.js +26 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/Car.js +2 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/DefaultExportSchema.js +2 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/FileWithTwoSchemas.js +2 -0
- package/spec/mock-project/dist/src/FlinkApp.js +1000 -0
- package/spec/mock-project/dist/src/FlinkContext.js +2 -0
- package/spec/mock-project/dist/src/FlinkErrors.js +143 -0
- package/spec/mock-project/dist/src/FlinkHttpHandler.js +47 -0
- package/spec/mock-project/dist/src/FlinkJob.js +2 -0
- package/spec/mock-project/dist/src/FlinkLog.js +119 -0
- package/spec/mock-project/dist/src/FlinkLogFactory.js +617 -0
- package/spec/mock-project/dist/src/FlinkPlugin.js +2 -0
- package/spec/mock-project/dist/src/FlinkRepo.js +224 -0
- package/spec/mock-project/dist/src/FlinkRequestContext.js +74 -0
- package/spec/mock-project/dist/src/FlinkResponse.js +2 -0
- package/spec/mock-project/dist/src/ai/AgentExecutor.js +279 -0
- package/spec/mock-project/dist/src/ai/AgentRunner.js +632 -0
- package/spec/mock-project/dist/src/ai/ConversationAgent.js +402 -0
- package/spec/mock-project/dist/src/ai/ConversationFlinkAgent.js +422 -0
- package/spec/mock-project/dist/src/ai/FlinkAgent.js +699 -0
- package/spec/mock-project/dist/src/ai/FlinkTool.js +2 -0
- package/spec/mock-project/dist/src/ai/InMemoryConversationAgent.js +209 -0
- package/spec/mock-project/dist/src/ai/LLMAdapter.js +2 -0
- package/spec/mock-project/dist/src/ai/SubAgentExecutor.js +223 -0
- package/spec/mock-project/dist/src/ai/ToolExecutor.js +412 -0
- package/spec/mock-project/dist/src/ai/agentInstructions.js +246 -0
- package/spec/mock-project/dist/src/auth/FlinkAuthPlugin.js +2 -0
- package/spec/mock-project/dist/src/auth/FlinkAuthUser.js +2 -0
- package/spec/mock-project/dist/src/handlers/GetCar.js +26 -52
- package/spec/mock-project/dist/src/handlers/GetCar.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCar2.js +32 -54
- package/spec/mock-project/dist/src/handlers/GetCar2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema.js +26 -48
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema2.js +28 -48
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema3.js +29 -48
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema3.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema.js +26 -50
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema2.js +28 -50
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile.js +27 -53
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile2.js +29 -53
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler.js +16 -49
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler2.js +25 -50
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchCar.js +27 -53
- package/spec/mock-project/dist/src/handlers/PatchCar.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchOnboardingSession.js +44 -70
- package/spec/mock-project/dist/src/handlers/PatchOnboardingSession.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchOrderWithComplexTypes.js +27 -53
- package/spec/mock-project/dist/src/handlers/PatchOrderWithComplexTypes.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchProductWithIntersection.js +28 -54
- package/spec/mock-project/dist/src/handlers/PatchProductWithIntersection.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchUserWithUnion.js +28 -54
- package/spec/mock-project/dist/src/handlers/PatchUserWithUnion.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PostCar.js +24 -50
- package/spec/mock-project/dist/src/handlers/PostCar.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PostLogin.js +25 -51
- package/spec/mock-project/dist/src/handlers/PostLogin.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PostLogout.js +24 -50
- package/spec/mock-project/dist/src/handlers/PostLogout.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PutCar.js +24 -50
- package/spec/mock-project/dist/src/handlers/PutCar.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/StreamWriterFactory.js +83 -0
- package/spec/mock-project/dist/src/index.js +52 -76
- package/spec/mock-project/dist/src/index.js.map +1 -0
- package/spec/mock-project/dist/src/mock-data-generator.js +9 -0
- package/spec/mock-project/dist/src/repos/CarRepo.js +12 -24
- package/spec/mock-project/dist/src/repos/CarRepo.js.map +1 -0
- package/spec/mock-project/dist/src/schemas/Car.js +3 -1
- package/spec/mock-project/dist/src/schemas/Car.js.map +1 -0
- package/spec/mock-project/dist/src/schemas/DefaultExportSchema.js +3 -1
- package/spec/mock-project/dist/src/schemas/DefaultExportSchema.js.map +1 -0
- package/spec/mock-project/dist/src/schemas/FileWithTwoSchemas.js +3 -1
- package/spec/mock-project/dist/src/schemas/FileWithTwoSchemas.js.map +1 -0
- package/spec/mock-project/dist/src/utils.js +290 -0
- package/spec/mock-project/tsconfig.json +6 -1
- package/spec/schema-generation-nested-objects.spec.ts +97 -0
- package/spec/testHelpers.ts +49 -0
- package/spec/utils.caseConversion.spec.ts +78 -0
- package/spec/utils.spec.ts +13 -13
- package/src/DependencyTracker.ts +166 -0
- package/src/FlinkApp.ts +919 -155
- package/src/FlinkContext.ts +43 -0
- package/src/FlinkErrors.ts +32 -12
- package/src/FlinkHttpHandler.ts +246 -28
- package/src/FlinkJob.ts +11 -0
- package/src/FlinkLog.ts +119 -12
- package/src/FlinkLogFactory.ts +699 -0
- package/src/FlinkRepo.ts +10 -3
- package/src/FlinkRequestContext.ts +95 -0
- package/src/FlinkResponse.ts +6 -0
- package/src/FlinkService.ts +49 -0
- package/src/LeaderElection.ts +203 -0
- package/src/SchemaCache.ts +232 -0
- package/src/TypeScriptCompiler.ts +1347 -610
- package/src/TypeScriptUtils.ts +5 -0
- package/src/ai/AgentRunner.ts +646 -0
- package/src/ai/ConversationAgent.ts +413 -0
- package/src/ai/FlinkAgent.ts +1069 -0
- package/src/ai/FlinkTool.ts +165 -0
- package/src/ai/InMemoryConversationAgent.ts +149 -0
- package/src/ai/LLMAdapter.ts +126 -0
- package/src/ai/ToolExecutor.ts +485 -0
- package/src/ai/agentInstructions.ts +245 -0
- package/src/ai/index.ts +8 -0
- package/src/ai/instructionFileLoader.ts +156 -0
- package/src/auth/FlinkAuthPlugin.ts +2 -1
- package/src/handlers/StreamWriterFactory.ts +84 -0
- package/src/index.ts +14 -0
- package/src/loadPluginSchemas.ts +141 -0
- package/src/schema-extraction/TypeScriptSourceParser.ts +1058 -0
- package/src/schema-extraction/TypeScriptTokenizer.ts +205 -0
- package/src/schema-extraction/index.ts +2 -0
- package/src/schema-extraction/types.ts +34 -0
- package/src/utils/loadFlinkConfig.ts +89 -0
- package/src/utils.ts +52 -0
- 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
|
|
5
|
-
|
|
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
|
-
|
|
11
|
-
|
|
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
|
-
|
|
14
|
-
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
+
}
|