@flink-app/flink 1.0.0 → 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 +991 -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 +823 -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 +157 -18
- 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 +27 -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.routeOrdering.spec.ts +61 -0
- package/spec/FlinkApp.undefinedResponse.spec.ts +123 -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 +895 -154
- package/src/FlinkContext.ts +43 -0
- package/src/FlinkErrors.ts +32 -12
- package/src/FlinkHttpHandler.ts +182 -20
- 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/dist/src/FlinkApp.js
CHANGED
|
@@ -50,7 +50,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
50
50
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
51
51
|
};
|
|
52
52
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
-
exports.FlinkApp = exports.autoRegisteredJobs = exports.autoRegisteredRepos = exports.autoRegisteredHandlers = exports.expressFn = void 0;
|
|
53
|
+
exports.FlinkApp = exports.autoRegisteredServices = exports.autoRegisteredAgents = exports.autoRegisteredTools = exports.autoRegisteredJobs = exports.autoRegisteredRepos = exports.autoRegisteredHandlers = exports.expressFn = void 0;
|
|
54
54
|
var ajv_1 = __importDefault(require("ajv"));
|
|
55
55
|
var ajv_formats_1 = __importDefault(require("ajv-formats"));
|
|
56
56
|
var body_parser_1 = __importDefault(require("body-parser"));
|
|
@@ -63,9 +63,16 @@ var toad_scheduler_1 = require("toad-scheduler");
|
|
|
63
63
|
var uuid_1 = require("uuid");
|
|
64
64
|
var FlinkErrors_1 = require("./FlinkErrors");
|
|
65
65
|
var FlinkHttpHandler_1 = require("./FlinkHttpHandler");
|
|
66
|
+
var LeaderElection_1 = require("./LeaderElection");
|
|
66
67
|
var FlinkLog_1 = require("./FlinkLog");
|
|
68
|
+
var FlinkLogFactory_1 = require("./FlinkLogFactory");
|
|
69
|
+
var FlinkRequestContext_1 = require("./FlinkRequestContext");
|
|
70
|
+
var StreamWriterFactory_1 = require("./handlers/StreamWriterFactory");
|
|
67
71
|
var mock_data_generator_1 = __importDefault(require("./mock-data-generator"));
|
|
68
72
|
var utils_1 = require("./utils");
|
|
73
|
+
var initLog = FlinkLogFactory_1.FlinkLogFactory.createLogger("flink.init");
|
|
74
|
+
var perfLog = FlinkLogFactory_1.FlinkLogFactory.createLogger("flink.perf");
|
|
75
|
+
var schedulerLog = FlinkLogFactory_1.FlinkLogFactory.createLogger("flink.scheduler");
|
|
69
76
|
var ajv = new ajv_1.default();
|
|
70
77
|
(0, ajv_formats_1.default)(ajv);
|
|
71
78
|
var defaultCorsOptions = {
|
|
@@ -93,8 +100,24 @@ exports.autoRegisteredRepos = [];
|
|
|
93
100
|
* are picked up by TypeScript compiler
|
|
94
101
|
*/
|
|
95
102
|
exports.autoRegisteredJobs = [];
|
|
103
|
+
/**
|
|
104
|
+
* This will be populated at compile time when the apps tools
|
|
105
|
+
* are picked up by TypeScript compiler
|
|
106
|
+
*/
|
|
107
|
+
exports.autoRegisteredTools = [];
|
|
108
|
+
/**
|
|
109
|
+
* This will be populated at compile time when the apps agents
|
|
110
|
+
* are picked up by TypeScript compiler
|
|
111
|
+
*/
|
|
112
|
+
exports.autoRegisteredAgents = [];
|
|
113
|
+
/**
|
|
114
|
+
* This will be populated at compile time when the apps services
|
|
115
|
+
* are picked up by TypeScript compiler
|
|
116
|
+
*/
|
|
117
|
+
exports.autoRegisteredServices = [];
|
|
96
118
|
var FlinkApp = /** @class */ (function () {
|
|
97
119
|
function FlinkApp(opts) {
|
|
120
|
+
var _a, _b;
|
|
98
121
|
this.handlers = [];
|
|
99
122
|
this.started = false;
|
|
100
123
|
this.debug = false;
|
|
@@ -102,10 +125,18 @@ var FlinkApp = /** @class */ (function () {
|
|
|
102
125
|
this.routingConfigured = false;
|
|
103
126
|
this.disableHttpServer = false;
|
|
104
127
|
this.repos = {};
|
|
128
|
+
this.services = {};
|
|
129
|
+
this.llmAdapters = new Map();
|
|
130
|
+
this.tools = {};
|
|
131
|
+
this.agents = {}; // FlinkAgent<C> instances
|
|
105
132
|
/**
|
|
106
133
|
* Internal cache used to track registered handlers and potentially any overlapping routes
|
|
107
134
|
*/
|
|
108
135
|
this.handlerRouteCache = new Map();
|
|
136
|
+
// Load config file and initialize logging
|
|
137
|
+
var loadFlinkConfig = require("./utils/loadFlinkConfig").loadFlinkConfig;
|
|
138
|
+
var flinkConfig = loadFlinkConfig();
|
|
139
|
+
FlinkLogFactory_1.FlinkLogFactory.configure(flinkConfig === null || flinkConfig === void 0 ? void 0 : flinkConfig.logging);
|
|
109
140
|
this.name = opts.name;
|
|
110
141
|
this.port = opts.port || 3333;
|
|
111
142
|
this.dbOpts = opts.db;
|
|
@@ -124,6 +155,13 @@ var FlinkApp = /** @class */ (function () {
|
|
|
124
155
|
this.disableHttpServer = !!opts.disableHttpServer;
|
|
125
156
|
this.accessLog = __assign({ enabled: true, format: "dev" }, opts.accessLog);
|
|
126
157
|
this.onError = opts.onError;
|
|
158
|
+
// Register LLM adapters if configured
|
|
159
|
+
if ((_a = opts.ai) === null || _a === void 0 ? void 0 : _a.llms) {
|
|
160
|
+
// Convert plain object to Map for internal use
|
|
161
|
+
this.llmAdapters = new Map(Object.entries(opts.ai.llms));
|
|
162
|
+
}
|
|
163
|
+
// Register global agent observer if configured
|
|
164
|
+
this.agentObserver = (_b = opts.ai) === null || _b === void 0 ? void 0 : _b.observer;
|
|
127
165
|
}
|
|
128
166
|
Object.defineProperty(FlinkApp.prototype, "ctx", {
|
|
129
167
|
get: function () {
|
|
@@ -137,33 +175,43 @@ var FlinkApp = /** @class */ (function () {
|
|
|
137
175
|
});
|
|
138
176
|
FlinkApp.prototype.start = function () {
|
|
139
177
|
return __awaiter(this, void 0, void 0, function () {
|
|
140
|
-
var startTime,
|
|
178
|
+
var startTime, dbStartTime, contextStartTime, toolsStartTime, agentsStartTime, agentInitStartTime, _i, _a, type, pluginsStartTime, _b, _c, plugin, db, handlersStartTime, jobsStartTime, totalStartTime;
|
|
141
179
|
var _this = this;
|
|
142
180
|
var _d;
|
|
143
181
|
return __generator(this, function (_e) {
|
|
144
182
|
switch (_e.label) {
|
|
145
183
|
case 0:
|
|
146
184
|
startTime = Date.now();
|
|
147
|
-
|
|
185
|
+
dbStartTime = Date.now();
|
|
148
186
|
return [4 /*yield*/, this.initDb()];
|
|
149
187
|
case 1:
|
|
150
188
|
_e.sent();
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
FlinkLog_1.log.bgColorLog("cyan", "Init db took ".concat(offsetTime - startTime, " ms"));
|
|
154
|
-
}
|
|
189
|
+
perfLog.debug("Init db took ".concat(Date.now() - dbStartTime, "ms"));
|
|
190
|
+
contextStartTime = Date.now();
|
|
155
191
|
return [4 /*yield*/, this.buildContext()];
|
|
156
192
|
case 2:
|
|
157
193
|
_e.sent();
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
194
|
+
perfLog.debug("Build context took ".concat(Date.now() - contextStartTime, "ms"));
|
|
195
|
+
toolsStartTime = Date.now();
|
|
196
|
+
return [4 /*yield*/, this.registerAutoRegisterableTools()];
|
|
197
|
+
case 3:
|
|
198
|
+
_e.sent();
|
|
199
|
+
perfLog.debug("Register tools took ".concat(Date.now() - toolsStartTime, "ms"));
|
|
200
|
+
agentsStartTime = Date.now();
|
|
201
|
+
return [4 /*yield*/, this.registerAutoRegisterableAgents()];
|
|
202
|
+
case 4:
|
|
203
|
+
_e.sent();
|
|
204
|
+
perfLog.debug("Register agents took ".concat(Date.now() - agentsStartTime, "ms"));
|
|
205
|
+
agentInitStartTime = Date.now();
|
|
206
|
+
return [4 /*yield*/, this.initializeAgents()];
|
|
207
|
+
case 5:
|
|
208
|
+
_e.sent();
|
|
209
|
+
perfLog.debug("Initialize agents took ".concat(Date.now() - agentInitStartTime, "ms"));
|
|
210
|
+
if (this.isSchedulingEnabled && !this.leaderElectionConfig) {
|
|
163
211
|
this.scheduler = new toad_scheduler_1.ToadScheduler();
|
|
164
212
|
}
|
|
165
|
-
else {
|
|
166
|
-
|
|
213
|
+
else if (!this.isSchedulingEnabled) {
|
|
214
|
+
schedulerLog.info("Scheduling is disabled");
|
|
167
215
|
}
|
|
168
216
|
if (!this.disableHttpServer) {
|
|
169
217
|
this.expressApp = (0, express_1.default)();
|
|
@@ -183,46 +231,53 @@ var FlinkApp = /** @class */ (function () {
|
|
|
183
231
|
next();
|
|
184
232
|
});
|
|
185
233
|
}
|
|
234
|
+
pluginsStartTime = Date.now();
|
|
186
235
|
_b = 0, _c = this.plugins;
|
|
187
|
-
_e.label =
|
|
188
|
-
case
|
|
189
|
-
if (!(_b < _c.length)) return [3 /*break*/,
|
|
236
|
+
_e.label = 6;
|
|
237
|
+
case 6:
|
|
238
|
+
if (!(_b < _c.length)) return [3 /*break*/, 12];
|
|
190
239
|
plugin = _c[_b];
|
|
191
240
|
db = void 0;
|
|
192
|
-
if (!plugin.db) return [3 /*break*/,
|
|
241
|
+
if (!plugin.db) return [3 /*break*/, 8];
|
|
193
242
|
return [4 /*yield*/, this.initPluginDb(plugin)];
|
|
194
|
-
case 4:
|
|
195
|
-
db = _e.sent();
|
|
196
|
-
_e.label = 5;
|
|
197
|
-
case 5:
|
|
198
|
-
if (!plugin.init) return [3 /*break*/, 7];
|
|
199
|
-
return [4 /*yield*/, plugin.init(this, db)];
|
|
200
|
-
case 6:
|
|
201
|
-
_e.sent();
|
|
202
|
-
_e.label = 7;
|
|
203
243
|
case 7:
|
|
204
|
-
|
|
244
|
+
db = _e.sent();
|
|
205
245
|
_e.label = 8;
|
|
206
246
|
case 8:
|
|
207
|
-
|
|
208
|
-
return [
|
|
209
|
-
case 9:
|
|
210
|
-
case 10:
|
|
247
|
+
if (!plugin.init) return [3 /*break*/, 10];
|
|
248
|
+
return [4 /*yield*/, plugin.init(this, db)];
|
|
249
|
+
case 9:
|
|
211
250
|
_e.sent();
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
251
|
+
_e.label = 10;
|
|
252
|
+
case 10:
|
|
253
|
+
initLog.info("Initialized plugin '".concat(plugin.id, "'"));
|
|
254
|
+
_e.label = 11;
|
|
255
|
+
case 11:
|
|
256
|
+
_b++;
|
|
257
|
+
return [3 /*break*/, 6];
|
|
258
|
+
case 12:
|
|
259
|
+
if (this.plugins.length > 0) {
|
|
260
|
+
perfLog.debug("Initialize plugins took ".concat(Date.now() - pluginsStartTime, "ms (").concat(this.plugins.length, " plugins)"));
|
|
215
261
|
}
|
|
216
|
-
|
|
262
|
+
handlersStartTime = Date.now();
|
|
263
|
+
return [4 /*yield*/, this.registerAutoRegisterableHandlers()];
|
|
264
|
+
case 13:
|
|
265
|
+
_e.sent();
|
|
266
|
+
perfLog.debug("Register handlers took ".concat(Date.now() - handlersStartTime, "ms"));
|
|
267
|
+
if (!this.isSchedulingEnabled) return [3 /*break*/, 17];
|
|
268
|
+
if (!this.leaderElectionConfig) return [3 /*break*/, 15];
|
|
269
|
+
return [4 /*yield*/, this.startLeaderElection()];
|
|
270
|
+
case 14:
|
|
271
|
+
_e.sent();
|
|
272
|
+
return [3 /*break*/, 17];
|
|
273
|
+
case 15:
|
|
274
|
+
jobsStartTime = Date.now();
|
|
217
275
|
return [4 /*yield*/, this.registerAutoRegisterableJobs()];
|
|
218
|
-
case
|
|
276
|
+
case 16:
|
|
219
277
|
_e.sent();
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
_e.label = 12;
|
|
225
|
-
case 12:
|
|
278
|
+
perfLog.debug("Register jobs took ".concat(Date.now() - jobsStartTime, "ms"));
|
|
279
|
+
_e.label = 17;
|
|
280
|
+
case 17:
|
|
226
281
|
// Register 404 with slight delay to allow all manually added routes to be added
|
|
227
282
|
// TODO: Is there a better solution to force this handler to always run last?
|
|
228
283
|
setTimeout(function () {
|
|
@@ -234,7 +289,7 @@ var FlinkApp = /** @class */ (function () {
|
|
|
234
289
|
_this.routingConfigured = true;
|
|
235
290
|
});
|
|
236
291
|
if (this.disableHttpServer) {
|
|
237
|
-
|
|
292
|
+
initLog.info("🚧 HTTP server is disabled, but flink app is running");
|
|
238
293
|
this.started = true;
|
|
239
294
|
}
|
|
240
295
|
else {
|
|
@@ -243,6 +298,8 @@ var FlinkApp = /** @class */ (function () {
|
|
|
243
298
|
_this.started = true;
|
|
244
299
|
});
|
|
245
300
|
}
|
|
301
|
+
totalStartTime = Date.now() - startTime;
|
|
302
|
+
perfLog.info("\u2713 FlinkApp started in ".concat(totalStartTime, "ms"));
|
|
246
303
|
return [2 /*return*/, this];
|
|
247
304
|
}
|
|
248
305
|
});
|
|
@@ -255,12 +312,24 @@ var FlinkApp = /** @class */ (function () {
|
|
|
255
312
|
switch (_a.label) {
|
|
256
313
|
case 0:
|
|
257
314
|
FlinkLog_1.log.info("🛑 Stopping Flink app...");
|
|
258
|
-
if (!this.
|
|
259
|
-
return [4 /*yield*/, this.
|
|
315
|
+
if (!this.leaderElection) return [3 /*break*/, 2];
|
|
316
|
+
return [4 /*yield*/, this.leaderElection.stop()];
|
|
260
317
|
case 1:
|
|
261
318
|
_a.sent();
|
|
262
319
|
_a.label = 2;
|
|
263
320
|
case 2:
|
|
321
|
+
if (!this.scheduler) return [3 /*break*/, 4];
|
|
322
|
+
return [4 /*yield*/, this.scheduler.stop()];
|
|
323
|
+
case 3:
|
|
324
|
+
_a.sent();
|
|
325
|
+
_a.label = 4;
|
|
326
|
+
case 4:
|
|
327
|
+
if (!this.allInstanceScheduler) return [3 /*break*/, 6];
|
|
328
|
+
return [4 /*yield*/, this.allInstanceScheduler.stop()];
|
|
329
|
+
case 5:
|
|
330
|
+
_a.sent();
|
|
331
|
+
_a.label = 6;
|
|
332
|
+
case 6:
|
|
264
333
|
if (this.expressServer) {
|
|
265
334
|
return [2 /*return*/, new Promise(function (resolve, reject) {
|
|
266
335
|
var int = setTimeout(function () {
|
|
@@ -285,7 +354,6 @@ var FlinkApp = /** @class */ (function () {
|
|
|
285
354
|
* which are derived from handler function type arguments.
|
|
286
355
|
*/
|
|
287
356
|
FlinkApp.prototype.addHandler = function (handler, routePropsOverride) {
|
|
288
|
-
var _a, _b, _c, _d, _e, _f;
|
|
289
357
|
if (this.routingConfigured) {
|
|
290
358
|
throw new Error("Cannot add handler after routes has been registered, make sure to invoke earlier");
|
|
291
359
|
}
|
|
@@ -304,28 +372,37 @@ var FlinkApp = /** @class */ (function () {
|
|
|
304
372
|
// TODO: Not sure if there is a case where you'd want to overwrite a route?
|
|
305
373
|
FlinkLog_1.log.warn("".concat(methodAndPath, " overlaps existing route"));
|
|
306
374
|
}
|
|
375
|
+
// Use direct schemas from routeProps if provided, otherwise fall back to manifest lookup
|
|
376
|
+
var reqSchema = routeProps.reqSchema;
|
|
377
|
+
var resSchema = routeProps.resSchema;
|
|
378
|
+
var queryMetadata = [];
|
|
379
|
+
var paramsMetadata = [];
|
|
380
|
+
if (!reqSchema || !resSchema) {
|
|
381
|
+
var schemaManifest = this.loadSchemaManifest();
|
|
382
|
+
var metadata = handler.__file ? schemaManifest.handlers[handler.__file] : undefined;
|
|
383
|
+
if (!reqSchema)
|
|
384
|
+
reqSchema = this.resolveSchema(metadata === null || metadata === void 0 ? void 0 : metadata.reqSchemaName);
|
|
385
|
+
if (!resSchema)
|
|
386
|
+
resSchema = this.resolveSchema(metadata === null || metadata === void 0 ? void 0 : metadata.resSchemaName);
|
|
387
|
+
queryMetadata = (metadata === null || metadata === void 0 ? void 0 : metadata.queryMetadata) || [];
|
|
388
|
+
paramsMetadata = (metadata === null || metadata === void 0 ? void 0 : metadata.paramsMetadata) || [];
|
|
389
|
+
}
|
|
307
390
|
var handlerConfig = {
|
|
308
391
|
routeProps: __assign(__assign({}, routeProps), { method: routeProps.method, path: routeProps.path }),
|
|
309
392
|
schema: {
|
|
310
|
-
reqSchema:
|
|
311
|
-
resSchema:
|
|
393
|
+
reqSchema: reqSchema,
|
|
394
|
+
resSchema: resSchema,
|
|
312
395
|
},
|
|
313
|
-
queryMetadata:
|
|
314
|
-
paramsMetadata:
|
|
396
|
+
queryMetadata: queryMetadata,
|
|
397
|
+
paramsMetadata: paramsMetadata,
|
|
315
398
|
};
|
|
316
|
-
if (((_c = handler.__schemas) === null || _c === void 0 ? void 0 : _c.reqSchema) && !((_d = handlerConfig.schema) === null || _d === void 0 ? void 0 : _d.reqSchema)) {
|
|
317
|
-
FlinkLog_1.log.warn("Expected request schema ".concat(handler.__schemas.reqSchema, " for handler ").concat(methodAndPath, " but no such schema was found"));
|
|
318
|
-
}
|
|
319
|
-
if (((_e = handler.__schemas) === null || _e === void 0 ? void 0 : _e.resSchema) && !((_f = handlerConfig.schema) === null || _f === void 0 ? void 0 : _f.resSchema)) {
|
|
320
|
-
FlinkLog_1.log.warn("Expected response schema ".concat(handler.__schemas.resSchema, " for handler ").concat(methodAndPath, " but no such schema was found"));
|
|
321
|
-
}
|
|
322
399
|
this.registerHandler(handlerConfig, handler.default);
|
|
323
400
|
};
|
|
324
401
|
FlinkApp.prototype.registerHandler = function (handlerConfig, handler) {
|
|
325
402
|
var _this = this;
|
|
326
403
|
this.handlers.push(handlerConfig);
|
|
327
404
|
var routeProps = handlerConfig.routeProps, _a = handlerConfig.schema, schema = _a === void 0 ? {} : _a;
|
|
328
|
-
var method = routeProps.method;
|
|
405
|
+
var method = routeProps.method, streamFormat = routeProps.streamFormat;
|
|
329
406
|
if (!method) {
|
|
330
407
|
FlinkLog_1.log.error("Route ".concat(routeProps.path, " is missing http method"));
|
|
331
408
|
}
|
|
@@ -336,45 +413,77 @@ var FlinkApp = /** @class */ (function () {
|
|
|
336
413
|
}
|
|
337
414
|
var validateReq_1;
|
|
338
415
|
var validateRes_1;
|
|
416
|
+
// Select AJV instance (use schemaAjv for v2.0 manifests, fallback to global ajv)
|
|
417
|
+
var ajvInstance = this.schemaAjv || ajv;
|
|
339
418
|
// Determine validation mode (default to Validate if not specified)
|
|
340
419
|
var validationMode = routeProps.validation || FlinkHttpHandler_1.ValidationMode.Validate;
|
|
341
420
|
// Compile request schema if validation mode requires it
|
|
342
421
|
if (schema.reqSchema && validationMode !== FlinkHttpHandler_1.ValidationMode.SkipValidation && validationMode !== FlinkHttpHandler_1.ValidationMode.ValidateResponse) {
|
|
343
|
-
|
|
422
|
+
// For v2.0 manifests with $id, use getSchema() if available
|
|
423
|
+
if (schema.reqSchema.$id && this.schemaAjv) {
|
|
424
|
+
validateReq_1 = this.schemaAjv.getSchema(schema.reqSchema.$id);
|
|
425
|
+
if (!validateReq_1) {
|
|
426
|
+
FlinkLog_1.log.warn("Schema ".concat(schema.reqSchema.$id, " not found in AJV registry, compiling inline"));
|
|
427
|
+
validateReq_1 = ajvInstance.compile(schema.reqSchema);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
validateReq_1 = ajvInstance.compile(schema.reqSchema);
|
|
432
|
+
}
|
|
344
433
|
}
|
|
345
|
-
//
|
|
346
|
-
|
|
347
|
-
|
|
434
|
+
// Skip response validation for streaming handlers (responses are stream chunks, not final JSON)
|
|
435
|
+
// Skip response validation for non-JSON response types (html, csv, etc.)
|
|
436
|
+
if (!streamFormat &&
|
|
437
|
+
!routeProps.responseType &&
|
|
438
|
+
schema.resSchema &&
|
|
439
|
+
validationMode !== FlinkHttpHandler_1.ValidationMode.SkipValidation &&
|
|
440
|
+
validationMode !== FlinkHttpHandler_1.ValidationMode.ValidateRequest) {
|
|
441
|
+
// For v2.0 manifests with $id, use getSchema() if available
|
|
442
|
+
if (schema.resSchema.$id && this.schemaAjv) {
|
|
443
|
+
validateRes_1 = this.schemaAjv.getSchema(schema.resSchema.$id);
|
|
444
|
+
if (!validateRes_1) {
|
|
445
|
+
FlinkLog_1.log.warn("Schema ".concat(schema.resSchema.$id, " not found in AJV registry, compiling inline"));
|
|
446
|
+
validateRes_1 = ajvInstance.compile(schema.resSchema);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
validateRes_1 = ajvInstance.compile(schema.resSchema);
|
|
451
|
+
}
|
|
348
452
|
}
|
|
349
453
|
this.expressApp[method](routeProps.path, function (req, res) { return __awaiter(_this, void 0, void 0, function () {
|
|
350
|
-
var valid, formattedErrors, data, normalizedQuery, _i, _a, _b, key, value, handlerRes, err_1, errorResponse,
|
|
351
|
-
|
|
352
|
-
|
|
454
|
+
var valid, formattedErrors, errorResponse, data, normalizedQuery, _i, _a, _b, key, value, stream, handlerRes, flinkReq_1, err_1, errorResponse, detail, errorResponse, valid, formattedErrors, errorResponse;
|
|
455
|
+
var _this = this;
|
|
456
|
+
var _c;
|
|
457
|
+
return __generator(this, function (_d) {
|
|
458
|
+
switch (_d.label) {
|
|
353
459
|
case 0:
|
|
354
460
|
if (!routeProps.permissions) return [3 /*break*/, 2];
|
|
355
461
|
return [4 /*yield*/, this.authenticate(req, routeProps.permissions)];
|
|
356
462
|
case 1:
|
|
357
|
-
if (!(
|
|
463
|
+
if (!(_d.sent())) {
|
|
358
464
|
return [2 /*return*/, res.status(401).json((0, FlinkErrors_1.unauthorized)())];
|
|
359
465
|
}
|
|
360
|
-
|
|
466
|
+
_d.label = 2;
|
|
361
467
|
case 2:
|
|
362
468
|
if (validateReq_1) {
|
|
363
469
|
valid = validateReq_1(req.body);
|
|
364
470
|
if (!valid) {
|
|
365
471
|
formattedErrors = (0, utils_1.formatValidationErrors)(validateReq_1.errors, req.body);
|
|
366
472
|
FlinkLog_1.log.warn("[".concat(req.reqId, "] ").concat(methodAndRoute_1, ": Bad request\n").concat(formattedErrors));
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
473
|
+
errorResponse = {
|
|
474
|
+
status: 400,
|
|
475
|
+
error: {
|
|
476
|
+
id: (0, uuid_1.v4)(),
|
|
477
|
+
title: "Bad request",
|
|
478
|
+
detail: formattedErrors,
|
|
479
|
+
},
|
|
480
|
+
};
|
|
481
|
+
this.invokeOnError(errorResponse, req, method, routeProps.path);
|
|
482
|
+
return [2 /*return*/, res.status(400).json(errorResponse)];
|
|
375
483
|
}
|
|
376
484
|
}
|
|
377
|
-
|
|
485
|
+
// Skip mock API for streaming handlers
|
|
486
|
+
if (routeProps.mockApi && schema.resSchema && !streamFormat) {
|
|
378
487
|
FlinkLog_1.log.warn("Mock response for ".concat(req.method.toUpperCase(), " ").concat(req.path));
|
|
379
488
|
data = (0, mock_data_generator_1.default)(schema.resSchema);
|
|
380
489
|
res.status(200).json({
|
|
@@ -402,20 +511,47 @@ var FlinkApp = /** @class */ (function () {
|
|
|
402
511
|
}
|
|
403
512
|
req.query = normalizedQuery;
|
|
404
513
|
}
|
|
405
|
-
|
|
514
|
+
stream = streamFormat ? StreamWriterFactory_1.StreamWriterFactory.create(res, streamFormat) : undefined;
|
|
515
|
+
_d.label = 3;
|
|
406
516
|
case 3:
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
517
|
+
_d.trys.push([3, 5, , 6]);
|
|
518
|
+
flinkReq_1 = req;
|
|
519
|
+
return [4 /*yield*/, FlinkRequestContext_1.requestContext.run({
|
|
520
|
+
reqId: flinkReq_1.reqId,
|
|
521
|
+
user: flinkReq_1.user,
|
|
522
|
+
userPermissions: flinkReq_1.userPermissions,
|
|
523
|
+
method: method,
|
|
524
|
+
path: routeProps.path,
|
|
525
|
+
timestamp: Date.now(),
|
|
526
|
+
isStreaming: !!streamFormat,
|
|
527
|
+
}, function () { return __awaiter(_this, void 0, void 0, function () {
|
|
528
|
+
return __generator(this, function (_a) {
|
|
529
|
+
switch (_a.label) {
|
|
530
|
+
case 0: return [4 /*yield*/, handler({
|
|
531
|
+
req: flinkReq_1,
|
|
532
|
+
ctx: this.ctx,
|
|
533
|
+
origin: routeProps.origin,
|
|
534
|
+
stream: stream,
|
|
535
|
+
})];
|
|
536
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
}); })];
|
|
413
540
|
case 4:
|
|
414
|
-
|
|
415
|
-
handlerRes = _c.sent();
|
|
541
|
+
handlerRes = _d.sent();
|
|
416
542
|
return [3 /*break*/, 6];
|
|
417
543
|
case 5:
|
|
418
|
-
err_1 =
|
|
544
|
+
err_1 = _d.sent();
|
|
545
|
+
// Handle errors for streaming handlers
|
|
546
|
+
if (streamFormat && stream) {
|
|
547
|
+
FlinkLog_1.log.error("Streaming handler error on ".concat(req.method.toUpperCase(), " ").concat(req.path, ": ").concat(err_1.message), {
|
|
548
|
+
error: err_1,
|
|
549
|
+
path: req.path,
|
|
550
|
+
method: req.method,
|
|
551
|
+
});
|
|
552
|
+
stream.error(err_1);
|
|
553
|
+
return [2 /*return*/];
|
|
554
|
+
}
|
|
419
555
|
errorResponse = void 0;
|
|
420
556
|
// duck typing to check if it is a FlinkError
|
|
421
557
|
if (typeof err_1.status === "number" && err_1.status >= 400 && err_1.status < 600 && err_1.error) {
|
|
@@ -434,44 +570,64 @@ var FlinkApp = /** @class */ (function () {
|
|
|
434
570
|
console.error(err_1);
|
|
435
571
|
errorResponse = (0, FlinkErrors_1.internalServerError)(err_1);
|
|
436
572
|
}
|
|
437
|
-
|
|
438
|
-
if (this.onError) {
|
|
439
|
-
try {
|
|
440
|
-
result = this.onError(errorResponse, {
|
|
441
|
-
req: req,
|
|
442
|
-
method: method,
|
|
443
|
-
path: routeProps.path,
|
|
444
|
-
reqId: req.reqId,
|
|
445
|
-
});
|
|
446
|
-
// Handle async callbacks - don't wait for them
|
|
447
|
-
if (result instanceof Promise) {
|
|
448
|
-
result.catch(function (callbackErr) {
|
|
449
|
-
FlinkLog_1.log.error("onError callback rejected with: ".concat(callbackErr));
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
catch (callbackErr) {
|
|
454
|
-
FlinkLog_1.log.error("onError callback threw an exception: ".concat(callbackErr));
|
|
455
|
-
}
|
|
456
|
-
}
|
|
573
|
+
this.invokeOnError(errorResponse, req, method, routeProps.path);
|
|
457
574
|
return [2 /*return*/, res.status(errorResponse.status || 500).json(errorResponse)];
|
|
458
575
|
case 6:
|
|
576
|
+
// Skip response handling for streaming handlers (stream controls response lifecycle)
|
|
577
|
+
if (streamFormat) {
|
|
578
|
+
return [2 /*return*/];
|
|
579
|
+
}
|
|
580
|
+
// Ensure handlerRes is defined for non-streaming handlers
|
|
581
|
+
if (!handlerRes) {
|
|
582
|
+
return [2 /*return*/, res.status(204).send()];
|
|
583
|
+
}
|
|
459
584
|
if (validateRes_1 && !(0, utils_1.isError)(handlerRes)) {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
585
|
+
if (handlerRes.data === undefined) {
|
|
586
|
+
if (handlerRes.status !== 204) {
|
|
587
|
+
detail = "Response schema is defined but handler returned no data";
|
|
588
|
+
FlinkLog_1.log.warn("[".concat(req.reqId, "] ").concat(methodAndRoute_1, ": Bad response - ").concat(detail));
|
|
589
|
+
errorResponse = {
|
|
590
|
+
status: 500,
|
|
591
|
+
error: { id: (0, uuid_1.v4)(), title: "Bad response", detail: detail },
|
|
592
|
+
};
|
|
593
|
+
this.invokeOnError(errorResponse, req, method, routeProps.path);
|
|
594
|
+
return [2 /*return*/, res.status(500).json(errorResponse)];
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
valid = validateRes_1(JSON.parse(JSON.stringify(handlerRes.data)));
|
|
599
|
+
if (!valid) {
|
|
600
|
+
formattedErrors = (0, utils_1.formatValidationErrors)(validateRes_1.errors, handlerRes.data);
|
|
601
|
+
FlinkLog_1.log.warn("[".concat(req.reqId, "] ").concat(methodAndRoute_1, ": Bad response\n").concat(formattedErrors));
|
|
602
|
+
errorResponse = {
|
|
465
603
|
status: 500,
|
|
466
604
|
error: {
|
|
467
605
|
id: (0, uuid_1.v4)(),
|
|
468
606
|
title: "Bad response",
|
|
469
607
|
detail: formattedErrors,
|
|
470
608
|
},
|
|
471
|
-
}
|
|
609
|
+
};
|
|
610
|
+
this.invokeOnError(errorResponse, req, method, routeProps.path);
|
|
611
|
+
return [2 /*return*/, res.status(500).json(errorResponse)];
|
|
612
|
+
}
|
|
472
613
|
}
|
|
473
614
|
}
|
|
474
615
|
res.set(handlerRes.headers);
|
|
616
|
+
if (routeProps.responseType) {
|
|
617
|
+
return [2 /*return*/, res
|
|
618
|
+
.status(handlerRes.status || 200)
|
|
619
|
+
.type(routeProps.responseType)
|
|
620
|
+
.send(handlerRes.data)];
|
|
621
|
+
}
|
|
622
|
+
if (((_c = handlerRes.error) === null || _c === void 0 ? void 0 : _c.meta) !== undefined) {
|
|
623
|
+
try {
|
|
624
|
+
JSON.stringify(handlerRes.error.meta);
|
|
625
|
+
}
|
|
626
|
+
catch (e) {
|
|
627
|
+
FlinkLog_1.log.warn("[".concat(handlerRes.reqId, "] error.meta stripped from error ").concat(handlerRes.error.id, ": not JSON-serializable (").concat(e.message, ")"));
|
|
628
|
+
delete handlerRes.error.meta;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
475
631
|
res.status(handlerRes.status || 200).json(handlerRes);
|
|
476
632
|
return [2 /*return*/];
|
|
477
633
|
}
|
|
@@ -483,10 +639,175 @@ var FlinkApp = /** @class */ (function () {
|
|
|
483
639
|
}
|
|
484
640
|
else {
|
|
485
641
|
this.handlerRouteCache.set(methodAndRoute_1, JSON.stringify(routeProps));
|
|
486
|
-
|
|
642
|
+
initLog.info("Registered ".concat(streamFormat ? "streaming " : "", "route ").concat(methodAndRoute_1).concat(streamFormat ? " (".concat(streamFormat, ")") : ""));
|
|
487
643
|
}
|
|
488
644
|
}
|
|
489
645
|
};
|
|
646
|
+
/**
|
|
647
|
+
* Load schema manifest from .flink directory.
|
|
648
|
+
* Returns empty structure if manifest doesn't exist (dev mode without build).
|
|
649
|
+
*
|
|
650
|
+
* The manifest contains:
|
|
651
|
+
* - definitions: ALL JSON Schema type definitions (supports $ref resolution)
|
|
652
|
+
* - handlers: Handler metadata with schema names (references to definitions)
|
|
653
|
+
* - tools: Tool metadata with schema names (references to definitions)
|
|
654
|
+
*/
|
|
655
|
+
FlinkApp.prototype.loadSchemaManifest = function () {
|
|
656
|
+
// Return cached manifest if already loaded
|
|
657
|
+
if (this.schemaManifest) {
|
|
658
|
+
return this.schemaManifest;
|
|
659
|
+
}
|
|
660
|
+
var fs = require("fs");
|
|
661
|
+
var path = require("path");
|
|
662
|
+
var manifestPath = path.join(process.cwd(), "dist/.flink/schema-manifest.json");
|
|
663
|
+
if (!fs.existsSync(manifestPath)) {
|
|
664
|
+
FlinkLog_1.log.warn("Schema manifest not found at dist/.flink/schema-manifest.json - handlers/tools may not have validation schemas");
|
|
665
|
+
var emptyManifest = { definitions: {}, handlers: {}, tools: {} };
|
|
666
|
+
this.schemaManifest = emptyManifest;
|
|
667
|
+
return emptyManifest;
|
|
668
|
+
}
|
|
669
|
+
try {
|
|
670
|
+
var manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
671
|
+
// Check version for backward compatibility
|
|
672
|
+
if (manifest.version === "2.0") {
|
|
673
|
+
// New format with schema universe and AJV global registry
|
|
674
|
+
this.schemaManifest = manifest;
|
|
675
|
+
// Initialize AJV and register all schemas
|
|
676
|
+
this.schemaAjv = new ajv_1.default({
|
|
677
|
+
strict: false, // Allow additional properties by default
|
|
678
|
+
allErrors: true, // Return all validation errors, not just first
|
|
679
|
+
});
|
|
680
|
+
(0, ajv_formats_1.default)(this.schemaAjv);
|
|
681
|
+
// Register all schemas in the universe
|
|
682
|
+
var schemas = manifest.schemas || {};
|
|
683
|
+
for (var _i = 0, _a = Object.values(schemas); _i < _a.length; _i++) {
|
|
684
|
+
var schema = _a[_i];
|
|
685
|
+
if (schema && typeof schema === "object" && schema.$id) {
|
|
686
|
+
// Skip schemas with unresolved generic type parameters (T, U, V, etc.)
|
|
687
|
+
if (this.hasUnresolvedTypeParams(schema, Object.keys(schemas))) {
|
|
688
|
+
FlinkLog_1.log.debug("Skipping registration of generic schema: ".concat(schema.$id));
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
this.schemaAjv.addSchema(schema);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
FlinkLog_1.log.debug("Loaded schema manifest v2.0: ".concat(Object.keys(schemas).length, " schemas, ").concat(Object.keys(manifest.handlers).length, " handlers, ").concat(Object.keys(manifest.tools).length, " tools"));
|
|
695
|
+
return manifest;
|
|
696
|
+
}
|
|
697
|
+
else {
|
|
698
|
+
// Old format (v1.0) - still supported for migration
|
|
699
|
+
FlinkLog_1.log.debug("Loaded schema manifest v1.0: ".concat(Object.keys(manifest.definitions || {}).length, " definitions, ").concat(Object.keys(manifest.handlers).length, " handlers, ").concat(Object.keys(manifest.tools).length, " tools"));
|
|
700
|
+
this.schemaManifest = manifest;
|
|
701
|
+
return manifest;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
catch (error) {
|
|
705
|
+
FlinkLog_1.log.error("Failed to parse schema manifest:", error);
|
|
706
|
+
var errorManifest = { definitions: {}, handlers: {}, tools: {} };
|
|
707
|
+
this.schemaManifest = errorManifest;
|
|
708
|
+
return errorManifest;
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
/**
|
|
712
|
+
* Get the AJV instance for validation (v2.0 manifests).
|
|
713
|
+
* Returns undefined for v1.0 manifests.
|
|
714
|
+
*/
|
|
715
|
+
FlinkApp.prototype.getSchemaAjv = function () {
|
|
716
|
+
return this.schemaAjv;
|
|
717
|
+
};
|
|
718
|
+
/**
|
|
719
|
+
* Register plugin schemas into the app's AJV instance.
|
|
720
|
+
* Schema $id values are prefixed with `pluginId::` to avoid collisions
|
|
721
|
+
* with the app's own schemas or other plugins.
|
|
722
|
+
*
|
|
723
|
+
* @param pluginId Unique identifier for the plugin (used as namespace prefix)
|
|
724
|
+
* @param schemas Record of schema name to JSON Schema object (already prefixed)
|
|
725
|
+
*/
|
|
726
|
+
FlinkApp.prototype.registerSchemas = function (pluginId, schemas) {
|
|
727
|
+
// Ensure schema manifest and AJV are initialized
|
|
728
|
+
this.loadSchemaManifest();
|
|
729
|
+
if (!this.schemaAjv) {
|
|
730
|
+
// If no v2.0 manifest exists, create a fresh AJV instance
|
|
731
|
+
this.schemaAjv = new ajv_1.default({
|
|
732
|
+
strict: false,
|
|
733
|
+
allErrors: true,
|
|
734
|
+
});
|
|
735
|
+
(0, ajv_formats_1.default)(this.schemaAjv);
|
|
736
|
+
}
|
|
737
|
+
for (var _i = 0, _a = Object.values(schemas); _i < _a.length; _i++) {
|
|
738
|
+
var schema = _a[_i];
|
|
739
|
+
if (schema && typeof schema === "object" && schema.$id) {
|
|
740
|
+
// Skip if already registered
|
|
741
|
+
if (this.schemaAjv.getSchema(schema.$id)) {
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
744
|
+
try {
|
|
745
|
+
this.schemaAjv.addSchema(schema);
|
|
746
|
+
}
|
|
747
|
+
catch (err) {
|
|
748
|
+
FlinkLog_1.log.warn("Failed to register plugin schema ".concat(schema.$id, ": ").concat(err));
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
FlinkLog_1.log.debug("Registered ".concat(Object.keys(schemas).length, " schemas from plugin '").concat(pluginId, "'"));
|
|
753
|
+
};
|
|
754
|
+
/**
|
|
755
|
+
* Check if a schema has unresolved generic type parameter references.
|
|
756
|
+
* Generic type parameters like T, U, V are single uppercase letters.
|
|
757
|
+
*
|
|
758
|
+
* @param schema JSON schema object
|
|
759
|
+
* @param registeredSchemaIds List of schema IDs in the manifest
|
|
760
|
+
* @returns true if schema references generic type params that don't exist
|
|
761
|
+
*/
|
|
762
|
+
FlinkApp.prototype.hasUnresolvedTypeParams = function (schema, registeredSchemaIds) {
|
|
763
|
+
var schemaIdSet = new Set(registeredSchemaIds);
|
|
764
|
+
var checkRefs = function (obj) {
|
|
765
|
+
if (!obj || typeof obj !== "object") {
|
|
766
|
+
return false;
|
|
767
|
+
}
|
|
768
|
+
if (Array.isArray(obj)) {
|
|
769
|
+
return obj.some(function (item) { return checkRefs(item); });
|
|
770
|
+
}
|
|
771
|
+
for (var _i = 0, _a = Object.entries(obj); _i < _a.length; _i++) {
|
|
772
|
+
var _b = _a[_i], key = _b[0], value = _b[1];
|
|
773
|
+
if (key === "$ref" && typeof value === "string") {
|
|
774
|
+
// Check if ref is a single uppercase letter (generic type param)
|
|
775
|
+
// and not in the registered schema IDs
|
|
776
|
+
if (/^[A-Z]$/.test(value) && !schemaIdSet.has(value)) {
|
|
777
|
+
return true;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
else if (typeof value === "object") {
|
|
781
|
+
if (checkRefs(value)) {
|
|
782
|
+
return true;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
return false;
|
|
787
|
+
};
|
|
788
|
+
return checkRefs(schema);
|
|
789
|
+
};
|
|
790
|
+
/**
|
|
791
|
+
* Resolve schema by name from manifest.
|
|
792
|
+
* Works with both v1.0 (definitions) and v2.0 (schema universe) formats.
|
|
793
|
+
*
|
|
794
|
+
* @param schemaName Schema name or $id
|
|
795
|
+
* @returns JSON schema object or undefined
|
|
796
|
+
*/
|
|
797
|
+
FlinkApp.prototype.resolveSchema = function (schemaName) {
|
|
798
|
+
if (!schemaName)
|
|
799
|
+
return undefined;
|
|
800
|
+
var manifest = this.loadSchemaManifest();
|
|
801
|
+
// v2.0 manifest - return schema from universe
|
|
802
|
+
if (manifest.version === "2.0" && manifest.schemas) {
|
|
803
|
+
return manifest.schemas[schemaName];
|
|
804
|
+
}
|
|
805
|
+
// v1.0 manifest - return from definitions
|
|
806
|
+
if (manifest.definitions) {
|
|
807
|
+
return manifest.definitions[schemaName];
|
|
808
|
+
}
|
|
809
|
+
return undefined;
|
|
810
|
+
};
|
|
490
811
|
/**
|
|
491
812
|
* Register handlers found within the `/src/handlers`
|
|
492
813
|
* directory in Flink App.
|
|
@@ -495,47 +816,77 @@ var FlinkApp = /** @class */ (function () {
|
|
|
495
816
|
*/
|
|
496
817
|
FlinkApp.prototype.registerAutoRegisterableHandlers = function () {
|
|
497
818
|
return __awaiter(this, void 0, void 0, function () {
|
|
498
|
-
var _i, _a, _b, handler, assumedHttpMethod, pathParams, _c, _d, param;
|
|
499
|
-
var _e
|
|
500
|
-
return __generator(this, function (
|
|
501
|
-
|
|
502
|
-
|
|
819
|
+
var schemaManifest, schemaCount, _i, _a, _b, handler, assumedHttpMethod, __file, metadata, reqSchema, resSchema, pathParams, _c, _d, param;
|
|
820
|
+
var _e;
|
|
821
|
+
return __generator(this, function (_f) {
|
|
822
|
+
schemaManifest = this.loadSchemaManifest();
|
|
823
|
+
schemaCount = schemaManifest.version === "2.0" ? Object.keys(schemaManifest.schemas || {}).length : Object.keys(schemaManifest.definitions || {}).length;
|
|
824
|
+
FlinkLog_1.log.debug("Registering ".concat(schemaCount, " schemas with AJV (manifest version: ").concat(schemaManifest.version || "1.0", ")"));
|
|
825
|
+
for (_i = 0, _a = exports.autoRegisteredHandlers.sort(function (a, b) {
|
|
826
|
+
var _a, _b, _c, _d, _e, _f;
|
|
827
|
+
var orderDiff = (((_a = a.handler.Route) === null || _a === void 0 ? void 0 : _a.order) || 0) - (((_b = b.handler.Route) === null || _b === void 0 ? void 0 : _b.order) || 0);
|
|
828
|
+
if (orderDiff !== 0)
|
|
829
|
+
return orderDiff;
|
|
830
|
+
// Static segments must be registered before parameterized ones to avoid
|
|
831
|
+
// Express matching e.g. GET /jobs/by-tags with the /jobs/:id route.
|
|
832
|
+
var aHasParam = ((_d = (_c = a.handler.Route) === null || _c === void 0 ? void 0 : _c.path) === null || _d === void 0 ? void 0 : _d.includes("/:")) ? 1 : 0;
|
|
833
|
+
var bHasParam = ((_f = (_e = b.handler.Route) === null || _e === void 0 ? void 0 : _e.path) === null || _f === void 0 ? void 0 : _f.includes("/:")) ? 1 : 0;
|
|
834
|
+
return aHasParam - bHasParam;
|
|
835
|
+
}); _i < _a.length; _i++) {
|
|
836
|
+
_b = _a[_i], handler = _b.handler, assumedHttpMethod = _b.assumedHttpMethod, __file = _b.__file;
|
|
503
837
|
if (!handler.Route) {
|
|
504
|
-
FlinkLog_1.log.error("Missing Props in handler ".concat(
|
|
838
|
+
FlinkLog_1.log.error("Missing Props in handler ".concat(__file));
|
|
505
839
|
continue;
|
|
506
840
|
}
|
|
507
841
|
if (!handler.default) {
|
|
508
|
-
FlinkLog_1.log.error("Missing exported handler function in handler ".concat(
|
|
842
|
+
FlinkLog_1.log.error("Missing exported handler function in handler ".concat(__file));
|
|
509
843
|
continue;
|
|
510
844
|
}
|
|
511
|
-
|
|
845
|
+
metadata = schemaManifest.handlers[__file || ""];
|
|
846
|
+
reqSchema = handler.Route.reqSchema || this.resolveSchema(metadata === null || metadata === void 0 ? void 0 : metadata.reqSchemaName);
|
|
847
|
+
resSchema = handler.Route.resSchema || this.resolveSchema(metadata === null || metadata === void 0 ? void 0 : metadata.resSchemaName);
|
|
848
|
+
// Validation warnings
|
|
849
|
+
if (!metadata &&
|
|
850
|
+
(handler.Route.validation === FlinkHttpHandler_1.ValidationMode.Validate ||
|
|
851
|
+
handler.Route.validation === FlinkHttpHandler_1.ValidationMode.ValidateRequest ||
|
|
852
|
+
handler.Route.validation === FlinkHttpHandler_1.ValidationMode.ValidateResponse)) {
|
|
853
|
+
FlinkLog_1.log.warn("Handler ".concat(__file, " expects validation but no metadata found in manifest"));
|
|
854
|
+
}
|
|
855
|
+
// Warn if schema name doesn't resolve
|
|
856
|
+
if ((metadata === null || metadata === void 0 ? void 0 : metadata.reqSchemaName) && !reqSchema) {
|
|
857
|
+
FlinkLog_1.log.warn("Handler ".concat(__file, " references reqSchema \"").concat(metadata.reqSchemaName, "\" but not found in schema universe"));
|
|
858
|
+
}
|
|
859
|
+
if ((metadata === null || metadata === void 0 ? void 0 : metadata.resSchemaName) && !resSchema) {
|
|
860
|
+
FlinkLog_1.log.warn("Handler ".concat(__file, " references resSchema \"").concat(metadata.resSchemaName, "\" but not found in schema universe"));
|
|
861
|
+
}
|
|
862
|
+
if (!!((_e = metadata === null || metadata === void 0 ? void 0 : metadata.paramsMetadata) === null || _e === void 0 ? void 0 : _e.length)) {
|
|
512
863
|
pathParams = (0, utils_1.getPathParams)(handler.Route.path);
|
|
513
|
-
for (_c = 0, _d =
|
|
864
|
+
for (_c = 0, _d = metadata.paramsMetadata; _c < _d.length; _c++) {
|
|
514
865
|
param = _d[_c];
|
|
515
866
|
if (!pathParams.includes(param.name)) {
|
|
516
|
-
FlinkLog_1.log.error("Handler ".concat(
|
|
867
|
+
FlinkLog_1.log.error("Handler ".concat(__file, " has param ").concat(param.name, " but it is not present in the path '").concat(handler.Route.path, "'"));
|
|
517
868
|
throw new Error("Invalid/missing handler path param");
|
|
518
869
|
}
|
|
519
870
|
}
|
|
520
|
-
if (pathParams.length !==
|
|
521
|
-
FlinkLog_1.log.warn("Handler ".concat(
|
|
871
|
+
if (pathParams.length !== metadata.paramsMetadata.length) {
|
|
872
|
+
FlinkLog_1.log.warn("Handler ".concat(__file, " has ").concat(metadata.paramsMetadata.length, " typed params but the path '").concat(handler.Route.path, "' has ").concat(pathParams.length, " params"));
|
|
522
873
|
}
|
|
523
874
|
}
|
|
524
875
|
this.registerHandler({
|
|
525
|
-
routeProps: __assign(__assign({}, handler.Route), { method: handler.Route.method || assumedHttpMethod, origin: this.name }),
|
|
876
|
+
routeProps: __assign(__assign({}, handler.Route), { method: handler.Route.method || assumedHttpMethod || (metadata === null || metadata === void 0 ? void 0 : metadata.assumedMethod), origin: this.name }),
|
|
526
877
|
schema: {
|
|
527
|
-
reqSchema:
|
|
528
|
-
resSchema:
|
|
878
|
+
reqSchema: reqSchema,
|
|
879
|
+
resSchema: resSchema,
|
|
529
880
|
},
|
|
530
|
-
queryMetadata:
|
|
531
|
-
paramsMetadata:
|
|
881
|
+
queryMetadata: (metadata === null || metadata === void 0 ? void 0 : metadata.queryMetadata) || [],
|
|
882
|
+
paramsMetadata: (metadata === null || metadata === void 0 ? void 0 : metadata.paramsMetadata) || [],
|
|
532
883
|
}, handler.default);
|
|
533
884
|
}
|
|
534
885
|
return [2 /*return*/];
|
|
535
886
|
});
|
|
536
887
|
});
|
|
537
888
|
};
|
|
538
|
-
FlinkApp.prototype.registerAutoRegisterableJobs = function () {
|
|
889
|
+
FlinkApp.prototype.registerAutoRegisterableJobs = function (filter) {
|
|
539
890
|
return __awaiter(this, void 0, void 0, function () {
|
|
540
891
|
var _loop_1, this_1, _i, autoRegisteredJobs_1, _a, jobProps, jobFn, __file;
|
|
541
892
|
var _this = this;
|
|
@@ -544,30 +895,33 @@ var FlinkApp = /** @class */ (function () {
|
|
|
544
895
|
throw new Error("Scheduler not initialized"); // should never happen
|
|
545
896
|
}
|
|
546
897
|
_loop_1 = function (jobProps, jobFn, __file) {
|
|
898
|
+
if (filter && !filter(jobProps)) {
|
|
899
|
+
return "continue";
|
|
900
|
+
}
|
|
547
901
|
if (jobProps.cron && jobProps.interval) {
|
|
548
|
-
|
|
902
|
+
schedulerLog.error("Cannot register job ".concat(jobProps.id, " - both cron and interval are set in ").concat(__file));
|
|
549
903
|
return "continue";
|
|
550
904
|
}
|
|
551
905
|
if (jobProps.cron && jobProps.afterDelay) {
|
|
552
|
-
|
|
906
|
+
schedulerLog.error("Cannot register job ".concat(jobProps.id, " - both cron and afterDelay are set in ").concat(__file));
|
|
553
907
|
return "continue";
|
|
554
908
|
}
|
|
555
909
|
if (jobProps.interval && jobProps.afterDelay) {
|
|
556
|
-
|
|
910
|
+
schedulerLog.error("Cannot register job ".concat(jobProps.id, " - both interval and afterDelay are set in ").concat(__file));
|
|
557
911
|
return "continue";
|
|
558
912
|
}
|
|
559
913
|
if (this_1.scheduler.existsById(jobProps.id)) {
|
|
560
|
-
|
|
914
|
+
schedulerLog.error("Job with id ".concat(jobProps.id, " is already registered, found duplicate in ").concat(__file));
|
|
561
915
|
return "continue";
|
|
562
916
|
}
|
|
563
|
-
|
|
917
|
+
schedulerLog.debug("Registering job ".concat(jobProps.id, ": ").concat(JSON.stringify(jobProps), " from ").concat(__file));
|
|
564
918
|
var task = new toad_scheduler_1.AsyncTask(jobProps.id, function () { return __awaiter(_this, void 0, void 0, function () {
|
|
565
919
|
return __generator(this, function (_a) {
|
|
566
920
|
switch (_a.label) {
|
|
567
921
|
case 0: return [4 /*yield*/, jobFn({ ctx: this.ctx })];
|
|
568
922
|
case 1:
|
|
569
923
|
_a.sent();
|
|
570
|
-
|
|
924
|
+
schedulerLog.debug("Job ".concat(jobProps.id, " completed"));
|
|
571
925
|
if (jobProps.afterDelay) {
|
|
572
926
|
// afterDelay runs only once, so we remove the job
|
|
573
927
|
this.scheduler.removeById(jobProps.id);
|
|
@@ -576,7 +930,7 @@ var FlinkApp = /** @class */ (function () {
|
|
|
576
930
|
}
|
|
577
931
|
});
|
|
578
932
|
}); }, function (err) {
|
|
579
|
-
|
|
933
|
+
schedulerLog.error("Job ".concat(jobProps.id, " threw unhandled exception ").concat(err));
|
|
580
934
|
console.error(err);
|
|
581
935
|
});
|
|
582
936
|
if (jobProps.cron) {
|
|
@@ -597,17 +951,41 @@ var FlinkApp = /** @class */ (function () {
|
|
|
597
951
|
this_1.scheduler.addSimpleIntervalJob(job);
|
|
598
952
|
}
|
|
599
953
|
else if (jobProps.afterDelay !== undefined) {
|
|
600
|
-
var
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
954
|
+
var delayMs = (0, ms_1.default)(jobProps.afterDelay);
|
|
955
|
+
if (delayMs === 0) {
|
|
956
|
+
setImmediate(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
957
|
+
var err_2;
|
|
958
|
+
return __generator(this, function (_a) {
|
|
959
|
+
switch (_a.label) {
|
|
960
|
+
case 0:
|
|
961
|
+
_a.trys.push([0, 2, , 3]);
|
|
962
|
+
return [4 /*yield*/, jobFn({ ctx: this.ctx })];
|
|
963
|
+
case 1:
|
|
964
|
+
_a.sent();
|
|
965
|
+
return [3 /*break*/, 3];
|
|
966
|
+
case 2:
|
|
967
|
+
err_2 = _a.sent();
|
|
968
|
+
schedulerLog.error("Job ".concat(jobProps.id, " threw unhandled exception ").concat(err_2));
|
|
969
|
+
console.error(err_2);
|
|
970
|
+
return [3 /*break*/, 3];
|
|
971
|
+
case 3: return [2 /*return*/];
|
|
972
|
+
}
|
|
973
|
+
});
|
|
974
|
+
}); });
|
|
975
|
+
}
|
|
976
|
+
else {
|
|
977
|
+
var job = new toad_scheduler_1.SimpleIntervalJob({
|
|
978
|
+
milliseconds: delayMs,
|
|
979
|
+
runImmediately: false,
|
|
980
|
+
}, task, {
|
|
981
|
+
id: jobProps.id,
|
|
982
|
+
preventOverrun: jobProps.singleton,
|
|
983
|
+
});
|
|
984
|
+
this_1.scheduler.addSimpleIntervalJob(job);
|
|
985
|
+
}
|
|
608
986
|
}
|
|
609
987
|
else {
|
|
610
|
-
|
|
988
|
+
schedulerLog.error("Cannot register job ".concat(jobProps.id, " - no cron, interval or once set in ").concat(__file));
|
|
611
989
|
return "continue";
|
|
612
990
|
}
|
|
613
991
|
};
|
|
@@ -625,40 +1003,201 @@ var FlinkApp = /** @class */ (function () {
|
|
|
625
1003
|
// TODO: Find out if we need to set ctx here or wanted not to if plugin has its own context
|
|
626
1004
|
// repoInstance.ctx = this.ctx;
|
|
627
1005
|
};
|
|
1006
|
+
FlinkApp.prototype.registerAutoRegisterableTools = function () {
|
|
1007
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1008
|
+
var ToolExecutor, getRepoInstanceName, schemaManifest, _i, autoRegisteredTools_1, toolFile, toolId, toolInstanceName, metadata, schemas, allSchemas, toolExecutor;
|
|
1009
|
+
return __generator(this, function (_a) {
|
|
1010
|
+
ToolExecutor = require("./ai/ToolExecutor").ToolExecutor;
|
|
1011
|
+
getRepoInstanceName = require("./utils").getRepoInstanceName;
|
|
1012
|
+
schemaManifest = this.loadSchemaManifest();
|
|
1013
|
+
for (_i = 0, autoRegisteredTools_1 = exports.autoRegisteredTools; _i < autoRegisteredTools_1.length; _i++) {
|
|
1014
|
+
toolFile = autoRegisteredTools_1[_i];
|
|
1015
|
+
if (!toolFile.Tool) {
|
|
1016
|
+
FlinkLog_1.log.error("Missing FlinkToolProps export in tool ".concat(toolFile.__file));
|
|
1017
|
+
continue;
|
|
1018
|
+
}
|
|
1019
|
+
if (!toolFile.default) {
|
|
1020
|
+
FlinkLog_1.log.error("Missing exported tool function in tool ".concat(toolFile.__file));
|
|
1021
|
+
continue;
|
|
1022
|
+
}
|
|
1023
|
+
toolId = toolFile.Tool.id;
|
|
1024
|
+
if (!toolId) {
|
|
1025
|
+
FlinkLog_1.log.error("Tool ".concat(toolFile.__file, " missing 'id' property"));
|
|
1026
|
+
continue;
|
|
1027
|
+
}
|
|
1028
|
+
toolInstanceName = getRepoInstanceName(toolId);
|
|
1029
|
+
metadata = schemaManifest.tools[toolFile.__file || ""];
|
|
1030
|
+
schemas = metadata
|
|
1031
|
+
? {
|
|
1032
|
+
inputSchema: this.resolveSchema(metadata.inputSchemaName),
|
|
1033
|
+
outputSchema: this.resolveSchema(metadata.outputSchemaName),
|
|
1034
|
+
inputTypeHint: metadata.inputTypeHint,
|
|
1035
|
+
outputTypeHint: metadata.outputTypeHint,
|
|
1036
|
+
}
|
|
1037
|
+
: undefined;
|
|
1038
|
+
// Warn if schema name doesn't resolve
|
|
1039
|
+
if ((metadata === null || metadata === void 0 ? void 0 : metadata.inputSchemaName) && !(schemas === null || schemas === void 0 ? void 0 : schemas.inputSchema)) {
|
|
1040
|
+
FlinkLog_1.log.warn("Tool ".concat(toolFile.__file, " references inputSchema \"").concat(metadata.inputSchemaName, "\" but not found in schema universe"));
|
|
1041
|
+
}
|
|
1042
|
+
if ((metadata === null || metadata === void 0 ? void 0 : metadata.outputSchemaName) && !(schemas === null || schemas === void 0 ? void 0 : schemas.outputSchema)) {
|
|
1043
|
+
FlinkLog_1.log.warn("Tool ".concat(toolFile.__file, " references outputSchema \"").concat(metadata.outputSchemaName, "\" but not found in schema universe"));
|
|
1044
|
+
}
|
|
1045
|
+
allSchemas = schemaManifest.version === "2.0" ? schemaManifest.schemas : schemaManifest.definitions;
|
|
1046
|
+
toolExecutor = new ToolExecutor(toolFile.Tool, toolFile.default, this.ctx, schemas, // Auto-generated schemas from manifest (resolved from definitions)
|
|
1047
|
+
allSchemas);
|
|
1048
|
+
this.tools[toolInstanceName] = toolExecutor;
|
|
1049
|
+
initLog.info("Registered tool ".concat(toolInstanceName, " (").concat(toolId, ")"));
|
|
1050
|
+
}
|
|
1051
|
+
return [2 /*return*/];
|
|
1052
|
+
});
|
|
1053
|
+
});
|
|
1054
|
+
};
|
|
1055
|
+
FlinkApp.prototype.registerAutoRegisterableAgents = function () {
|
|
1056
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1057
|
+
var _a, getRepoInstanceName, toKebabCase, _loop_2, this_2, _i, autoRegisteredAgents_1, agentFile;
|
|
1058
|
+
return __generator(this, function (_b) {
|
|
1059
|
+
_a = require("./utils"), getRepoInstanceName = _a.getRepoInstanceName, toKebabCase = _a.toKebabCase;
|
|
1060
|
+
_loop_2 = function (agentFile) {
|
|
1061
|
+
// agentFile now exports a class, not a config object
|
|
1062
|
+
var AgentClass = agentFile.default;
|
|
1063
|
+
if (!AgentClass) {
|
|
1064
|
+
FlinkLog_1.log.error("Missing default export in agent ".concat(agentFile.__file));
|
|
1065
|
+
return "continue";
|
|
1066
|
+
}
|
|
1067
|
+
// Instantiate agent (similar to repo instantiation)
|
|
1068
|
+
var agentInstance = new AgentClass();
|
|
1069
|
+
// Derive instance name from class name (camelCase)
|
|
1070
|
+
var agentInstanceName = getRepoInstanceName(AgentClass.name);
|
|
1071
|
+
// Get agent ID (kebab-case) - either explicit or derived
|
|
1072
|
+
var agentId = agentInstance.id;
|
|
1073
|
+
// Check for duplicate instance name
|
|
1074
|
+
if (this_2.agents[agentInstanceName]) {
|
|
1075
|
+
var existingAgent = this_2.agents[agentInstanceName];
|
|
1076
|
+
throw new Error("Duplicate agent instance name: \"".concat(agentInstanceName, "\". ") +
|
|
1077
|
+
"Agent class \"".concat(AgentClass.name, "\" conflicts with existing agent \"").concat(existingAgent.constructor.name, "\". ") +
|
|
1078
|
+
"Instance names are derived by lowercasing the first letter of the class name. " +
|
|
1079
|
+
"Rename one of the classes or use a unique explicit 'id' property.");
|
|
1080
|
+
}
|
|
1081
|
+
// Check for duplicate agent ID
|
|
1082
|
+
var existingAgentWithSameId = Object.values(this_2.agents).find(function (agent) { return agent.id === agentId; });
|
|
1083
|
+
if (existingAgentWithSameId) {
|
|
1084
|
+
throw new Error("Duplicate agent ID: \"".concat(agentId, "\". ") +
|
|
1085
|
+
"Agent class \"".concat(AgentClass.name, "\" conflicts with existing agent \"").concat(existingAgentWithSameId.constructor.name, "\". ") +
|
|
1086
|
+
"Change the 'id' property on one of them to resolve the conflict.");
|
|
1087
|
+
}
|
|
1088
|
+
// Validate tools exist
|
|
1089
|
+
if (agentInstance.tools) {
|
|
1090
|
+
for (var _c = 0, _d = agentInstance.tools; _c < _d.length; _c++) {
|
|
1091
|
+
var toolRef = _d[_c];
|
|
1092
|
+
// Handle string IDs, tool file references, and tool props
|
|
1093
|
+
var toolId = void 0;
|
|
1094
|
+
if (typeof toolRef === "string") {
|
|
1095
|
+
toolId = toolRef;
|
|
1096
|
+
}
|
|
1097
|
+
else if ("Tool" in toolRef) {
|
|
1098
|
+
// FlinkToolFile - extract ID from Tool property
|
|
1099
|
+
toolId = toolRef.Tool.id;
|
|
1100
|
+
}
|
|
1101
|
+
else {
|
|
1102
|
+
// FlinkToolProps - extract ID directly
|
|
1103
|
+
toolId = toolRef.id;
|
|
1104
|
+
}
|
|
1105
|
+
var tool = this_2.tools[toolId];
|
|
1106
|
+
if (!tool) {
|
|
1107
|
+
FlinkLog_1.log.error("Agent ".concat(AgentClass.name, " references tool ").concat(toolId, " which is not registered"));
|
|
1108
|
+
throw new Error("Invalid tool reference in agent ".concat(AgentClass.name));
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
// Register agent (duplicate checks already performed above)
|
|
1113
|
+
this_2.agents[agentInstanceName] = agentInstance;
|
|
1114
|
+
initLog.info("Registered agent ".concat(agentInstanceName, " (").concat(AgentClass.name, ") with ID: ").concat(agentId));
|
|
1115
|
+
};
|
|
1116
|
+
this_2 = this;
|
|
1117
|
+
for (_i = 0, autoRegisteredAgents_1 = exports.autoRegisteredAgents; _i < autoRegisteredAgents_1.length; _i++) {
|
|
1118
|
+
agentFile = autoRegisteredAgents_1[_i];
|
|
1119
|
+
_loop_2(agentFile);
|
|
1120
|
+
}
|
|
1121
|
+
return [2 /*return*/];
|
|
1122
|
+
});
|
|
1123
|
+
});
|
|
1124
|
+
};
|
|
628
1125
|
/**
|
|
629
1126
|
* Constructs the app context. Will inject context in all components
|
|
630
1127
|
* except for handlers which are handled in later stage.
|
|
631
1128
|
*/
|
|
632
1129
|
FlinkApp.prototype.buildContext = function () {
|
|
633
1130
|
return __awaiter(this, void 0, void 0, function () {
|
|
634
|
-
var _i, autoRegisteredRepos_1, _a, collectionName, repoInstanceName, Repo, repoInstance, pluginCtx, _b, _c, repo;
|
|
635
|
-
return __generator(this, function (
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
1131
|
+
var _i, autoRegisteredRepos_1, _a, collectionName, repoInstanceName, Repo, repoInstance, pluginCtx, _b, autoRegisteredServices_1, _c, serviceInstanceName, Service, serviceInstance, _d, _e, repo, _f, _g, service, servicesWithInit;
|
|
1132
|
+
return __generator(this, function (_h) {
|
|
1133
|
+
switch (_h.label) {
|
|
1134
|
+
case 0:
|
|
1135
|
+
if (this.dbOpts) {
|
|
1136
|
+
for (_i = 0, autoRegisteredRepos_1 = exports.autoRegisteredRepos; _i < autoRegisteredRepos_1.length; _i++) {
|
|
1137
|
+
_a = autoRegisteredRepos_1[_i], collectionName = _a.collectionName, repoInstanceName = _a.repoInstanceName, Repo = _a.Repo;
|
|
1138
|
+
repoInstance = new Repo(collectionName, this.db, this.dbClient);
|
|
1139
|
+
this.repos[repoInstanceName] = repoInstance;
|
|
1140
|
+
initLog.info("Registered repo ".concat(repoInstanceName));
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
else if (exports.autoRegisteredRepos.length > 0) {
|
|
1144
|
+
FlinkLog_1.log.warn("No db configured but found repo(s)");
|
|
1145
|
+
}
|
|
1146
|
+
pluginCtx = this.plugins.reduce(function (out, plugin) {
|
|
1147
|
+
if (out[plugin.id]) {
|
|
1148
|
+
throw new Error("Plugin ".concat(plugin.id, " is already registered"));
|
|
1149
|
+
}
|
|
1150
|
+
out[plugin.id] = plugin.ctx;
|
|
1151
|
+
return out;
|
|
1152
|
+
}, {});
|
|
1153
|
+
// Instantiate services (ctx not yet available - constructors must not access it)
|
|
1154
|
+
for (_b = 0, autoRegisteredServices_1 = exports.autoRegisteredServices; _b < autoRegisteredServices_1.length; _b++) {
|
|
1155
|
+
_c = autoRegisteredServices_1[_b], serviceInstanceName = _c.serviceInstanceName, Service = _c.Service;
|
|
1156
|
+
serviceInstance = new Service();
|
|
1157
|
+
this.services[serviceInstanceName] = serviceInstance;
|
|
1158
|
+
initLog.info("Registered service ".concat(serviceInstanceName));
|
|
1159
|
+
}
|
|
1160
|
+
this._ctx = {
|
|
1161
|
+
repos: this.repos,
|
|
1162
|
+
plugins: pluginCtx,
|
|
1163
|
+
auth: this.auth,
|
|
1164
|
+
agents: this.agents,
|
|
1165
|
+
services: this.services,
|
|
1166
|
+
};
|
|
1167
|
+
// Inject context into repos
|
|
1168
|
+
for (_d = 0, _e = Object.values(this.repos); _d < _e.length; _d++) {
|
|
1169
|
+
repo = _e[_d];
|
|
1170
|
+
repo.ctx = this.ctx;
|
|
1171
|
+
}
|
|
1172
|
+
// Inject context into services, then call onInit() in parallel
|
|
1173
|
+
for (_f = 0, _g = Object.values(this.services); _f < _g.length; _f++) {
|
|
1174
|
+
service = _g[_f];
|
|
1175
|
+
service.ctx = this.ctx;
|
|
1176
|
+
}
|
|
1177
|
+
servicesWithInit = Object.values(this.services).filter(function (s) { return typeof s.onInit === "function"; });
|
|
1178
|
+
if (!(servicesWithInit.length > 0)) return [3 /*break*/, 2];
|
|
1179
|
+
return [4 /*yield*/, Promise.all(servicesWithInit.map(function (s) { return s.onInit(); }))];
|
|
1180
|
+
case 1:
|
|
1181
|
+
_h.sent();
|
|
1182
|
+
_h.label = 2;
|
|
1183
|
+
case 2: return [2 /*return*/];
|
|
646
1184
|
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
for (
|
|
660
|
-
|
|
661
|
-
|
|
1185
|
+
});
|
|
1186
|
+
});
|
|
1187
|
+
};
|
|
1188
|
+
/**
|
|
1189
|
+
* Initialize agents after they've been registered and context is ready
|
|
1190
|
+
* Must be called after registerAutoRegisterableAgents()
|
|
1191
|
+
*/
|
|
1192
|
+
FlinkApp.prototype.initializeAgents = function () {
|
|
1193
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1194
|
+
var _i, _a, agent;
|
|
1195
|
+
return __generator(this, function (_b) {
|
|
1196
|
+
// Inject context and initialize agents
|
|
1197
|
+
for (_i = 0, _a = Object.values(this.agents); _i < _a.length; _i++) {
|
|
1198
|
+
agent = _a[_i];
|
|
1199
|
+
agent.ctx = this.ctx;
|
|
1200
|
+
agent.__init(this.llmAdapters, this.tools, this.agentObserver);
|
|
662
1201
|
}
|
|
663
1202
|
return [2 /*return*/];
|
|
664
1203
|
});
|
|
@@ -669,7 +1208,7 @@ var FlinkApp = /** @class */ (function () {
|
|
|
669
1208
|
*/
|
|
670
1209
|
FlinkApp.prototype.initDb = function () {
|
|
671
1210
|
return __awaiter(this, void 0, void 0, function () {
|
|
672
|
-
var client,
|
|
1211
|
+
var client, err_3;
|
|
673
1212
|
return __generator(this, function (_a) {
|
|
674
1213
|
switch (_a.label) {
|
|
675
1214
|
case 0:
|
|
@@ -685,8 +1224,8 @@ var FlinkApp = /** @class */ (function () {
|
|
|
685
1224
|
this.dbClient = client;
|
|
686
1225
|
return [3 /*break*/, 4];
|
|
687
1226
|
case 3:
|
|
688
|
-
|
|
689
|
-
FlinkLog_1.log.error("Failed to connect to db: " +
|
|
1227
|
+
err_3 = _a.sent();
|
|
1228
|
+
FlinkLog_1.log.error("Failed to connect to db: " + err_3);
|
|
690
1229
|
process.exit(1);
|
|
691
1230
|
return [3 /*break*/, 4];
|
|
692
1231
|
case 4:
|
|
@@ -705,7 +1244,7 @@ var FlinkApp = /** @class */ (function () {
|
|
|
705
1244
|
*/
|
|
706
1245
|
FlinkApp.prototype.initPluginDb = function (plugin) {
|
|
707
1246
|
return __awaiter(this, void 0, void 0, function () {
|
|
708
|
-
var client,
|
|
1247
|
+
var client, err_4;
|
|
709
1248
|
return __generator(this, function (_a) {
|
|
710
1249
|
switch (_a.label) {
|
|
711
1250
|
case 0:
|
|
@@ -726,14 +1265,14 @@ var FlinkApp = /** @class */ (function () {
|
|
|
726
1265
|
_a.label = 2;
|
|
727
1266
|
case 2:
|
|
728
1267
|
_a.trys.push([2, 4, , 5]);
|
|
729
|
-
|
|
1268
|
+
initLog.debug("Connecting to '".concat(plugin.id, "' db"));
|
|
730
1269
|
return [4 /*yield*/, mongodb_1.MongoClient.connect(plugin.db.uri, this.getMongoConnectionOptions())];
|
|
731
1270
|
case 3:
|
|
732
1271
|
client = _a.sent();
|
|
733
1272
|
return [2 /*return*/, client.db()];
|
|
734
1273
|
case 4:
|
|
735
|
-
|
|
736
|
-
FlinkLog_1.log.error("Failed to connect to db defined in plugin '".concat(plugin.id, "': ") +
|
|
1274
|
+
err_4 = _a.sent();
|
|
1275
|
+
FlinkLog_1.log.error("Failed to connect to db defined in plugin '".concat(plugin.id, "': ") + err_4);
|
|
737
1276
|
return [3 /*break*/, 5];
|
|
738
1277
|
case 5: return [2 /*return*/];
|
|
739
1278
|
}
|
|
@@ -748,12 +1287,40 @@ var FlinkApp = /** @class */ (function () {
|
|
|
748
1287
|
if (!this.auth) {
|
|
749
1288
|
throw new Error("Attempting to authenticate request (".concat(req.method, " ").concat(req.path, ") but no authPlugin is set"));
|
|
750
1289
|
}
|
|
751
|
-
return [4 /*yield*/, this.auth.authenticateRequest(req, permissions)];
|
|
1290
|
+
return [4 /*yield*/, this.auth.authenticateRequest(req, permissions, this._ctx)];
|
|
752
1291
|
case 1: return [2 /*return*/, _a.sent()];
|
|
753
1292
|
}
|
|
754
1293
|
});
|
|
755
1294
|
});
|
|
756
1295
|
};
|
|
1296
|
+
/**
|
|
1297
|
+
* Invokes the optional onError callback in a fire-and-forget manner.
|
|
1298
|
+
* Any error thrown or rejected by the callback is caught and logged so
|
|
1299
|
+
* it never affects the error response sent to the client.
|
|
1300
|
+
*/
|
|
1301
|
+
FlinkApp.prototype.invokeOnError = function (errorResponse, req, method, path) {
|
|
1302
|
+
if (!this.onError) {
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
try {
|
|
1306
|
+
var result = this.onError(errorResponse, {
|
|
1307
|
+
req: req,
|
|
1308
|
+
method: method,
|
|
1309
|
+
path: path,
|
|
1310
|
+
reqId: req.reqId,
|
|
1311
|
+
ctx: this.ctx,
|
|
1312
|
+
});
|
|
1313
|
+
// Handle async callbacks - don't wait for them
|
|
1314
|
+
if (result instanceof Promise) {
|
|
1315
|
+
result.catch(function (callbackErr) {
|
|
1316
|
+
FlinkLog_1.log.error("onError callback rejected with: ".concat(callbackErr));
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
catch (callbackErr) {
|
|
1321
|
+
FlinkLog_1.log.error("onError callback threw an exception: ".concat(callbackErr));
|
|
1322
|
+
}
|
|
1323
|
+
};
|
|
757
1324
|
FlinkApp.prototype.getRegisteredRoutes = function () {
|
|
758
1325
|
return Array.from(this.handlerRouteCache.values());
|
|
759
1326
|
};
|
|
@@ -765,19 +1332,91 @@ var FlinkApp = /** @class */ (function () {
|
|
|
765
1332
|
enumerable: false,
|
|
766
1333
|
configurable: true
|
|
767
1334
|
});
|
|
1335
|
+
Object.defineProperty(FlinkApp.prototype, "leaderElectionConfig", {
|
|
1336
|
+
get: function () {
|
|
1337
|
+
var _a;
|
|
1338
|
+
var opt = (_a = this.schedulingOptions) === null || _a === void 0 ? void 0 : _a.leaderElection;
|
|
1339
|
+
if (!opt)
|
|
1340
|
+
return undefined;
|
|
1341
|
+
return opt === true ? {} : opt;
|
|
1342
|
+
},
|
|
1343
|
+
enumerable: false,
|
|
1344
|
+
configurable: true
|
|
1345
|
+
});
|
|
1346
|
+
FlinkApp.prototype.startLeaderElection = function () {
|
|
1347
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1348
|
+
var hasAllInstanceJobs, opts;
|
|
1349
|
+
var _this = this;
|
|
1350
|
+
return __generator(this, function (_a) {
|
|
1351
|
+
switch (_a.label) {
|
|
1352
|
+
case 0:
|
|
1353
|
+
if (!!this.db) return [3 /*break*/, 2];
|
|
1354
|
+
schedulerLog.warn("Leader election is enabled but no database is configured. " +
|
|
1355
|
+
"Leader election requires a MongoDB connection to coordinate between instances. " +
|
|
1356
|
+
"Either add a database connection via the `db` option, or remove `scheduling.leaderElection` from your config. " +
|
|
1357
|
+
"Jobs will run on ALL instances without leader election.");
|
|
1358
|
+
// Fall back to running jobs on all instances
|
|
1359
|
+
this.scheduler = new toad_scheduler_1.ToadScheduler();
|
|
1360
|
+
return [4 /*yield*/, this.registerAutoRegisterableJobs()];
|
|
1361
|
+
case 1:
|
|
1362
|
+
_a.sent();
|
|
1363
|
+
return [2 /*return*/];
|
|
1364
|
+
case 2:
|
|
1365
|
+
hasAllInstanceJobs = exports.autoRegisteredJobs.some(function (j) { return j.Job.runOnAllInstances; });
|
|
1366
|
+
if (!hasAllInstanceJobs) return [3 /*break*/, 4];
|
|
1367
|
+
this.allInstanceScheduler = new toad_scheduler_1.ToadScheduler();
|
|
1368
|
+
this.scheduler = this.allInstanceScheduler;
|
|
1369
|
+
return [4 /*yield*/, this.registerAutoRegisterableJobs(function (job) { return !!job.runOnAllInstances; })];
|
|
1370
|
+
case 3:
|
|
1371
|
+
_a.sent();
|
|
1372
|
+
this.scheduler = undefined;
|
|
1373
|
+
_a.label = 4;
|
|
1374
|
+
case 4:
|
|
1375
|
+
opts = this.leaderElectionConfig;
|
|
1376
|
+
this.leaderElection = new LeaderElection_1.LeaderElection(this.db, opts);
|
|
1377
|
+
return [4 /*yield*/, this.leaderElection.start(
|
|
1378
|
+
// onBecameLeader
|
|
1379
|
+
function () { return __awaiter(_this, void 0, void 0, function () {
|
|
1380
|
+
return __generator(this, function (_a) {
|
|
1381
|
+
switch (_a.label) {
|
|
1382
|
+
case 0:
|
|
1383
|
+
schedulerLog.info("This instance is now the leader - starting scheduled jobs");
|
|
1384
|
+
this.scheduler = new toad_scheduler_1.ToadScheduler();
|
|
1385
|
+
return [4 /*yield*/, this.registerAutoRegisterableJobs(function (job) { return !job.runOnAllInstances; })];
|
|
1386
|
+
case 1:
|
|
1387
|
+
_a.sent();
|
|
1388
|
+
return [2 /*return*/];
|
|
1389
|
+
}
|
|
1390
|
+
});
|
|
1391
|
+
}); },
|
|
1392
|
+
// onLostLeadership
|
|
1393
|
+
function () {
|
|
1394
|
+
schedulerLog.info("This instance lost leadership - stopping scheduled jobs");
|
|
1395
|
+
if (_this.scheduler) {
|
|
1396
|
+
_this.scheduler.stop();
|
|
1397
|
+
_this.scheduler = undefined;
|
|
1398
|
+
}
|
|
1399
|
+
})];
|
|
1400
|
+
case 5:
|
|
1401
|
+
_a.sent();
|
|
1402
|
+
return [2 /*return*/];
|
|
1403
|
+
}
|
|
1404
|
+
});
|
|
1405
|
+
});
|
|
1406
|
+
};
|
|
768
1407
|
FlinkApp.prototype.getMongoConnectionOptions = function () {
|
|
769
1408
|
if (!this.dbOpts) {
|
|
770
1409
|
throw new Error("No db configured");
|
|
771
1410
|
}
|
|
772
1411
|
var driverVersion = require("mongodb/package.json").version;
|
|
773
1412
|
if (driverVersion.startsWith("3")) {
|
|
774
|
-
|
|
1413
|
+
initLog.debug("Using legacy mongodb connection options as mongo client is version ".concat(driverVersion));
|
|
775
1414
|
return {
|
|
776
1415
|
useNewUrlParser: true,
|
|
777
1416
|
useUnifiedTopology: true,
|
|
778
1417
|
};
|
|
779
1418
|
}
|
|
780
|
-
|
|
1419
|
+
initLog.debug("Using modern MongoDB client options (driver version ".concat(driverVersion, ")"));
|
|
781
1420
|
return {
|
|
782
1421
|
serverApi: {
|
|
783
1422
|
version: mongodb_1.ServerApiVersion.v1,
|