@flink-app/flink 1.0.0 → 2.0.0-alpha.49
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 +12 -0
- package/cli/build.ts +8 -1
- package/cli/run.ts +8 -1
- package/dist/cli/build.js +8 -1
- package/dist/cli/run.js +8 -1
- package/dist/src/FlinkApp.d.ts +33 -0
- package/dist/src/FlinkApp.js +247 -27
- package/dist/src/FlinkContext.d.ts +21 -0
- package/dist/src/FlinkHttpHandler.d.ts +90 -1
- package/dist/src/TypeScriptCompiler.d.ts +42 -0
- package/dist/src/TypeScriptCompiler.js +366 -8
- package/dist/src/TypeScriptUtils.js +4 -0
- package/dist/src/ai/AgentRunner.d.ts +39 -0
- package/dist/src/ai/AgentRunner.js +625 -0
- package/dist/src/ai/FlinkAgent.d.ts +446 -0
- package/dist/src/ai/FlinkAgent.js +633 -0
- package/dist/src/ai/FlinkTool.d.ts +37 -0
- package/dist/src/ai/FlinkTool.js +2 -0
- package/dist/src/ai/LLMAdapter.d.ts +119 -0
- package/dist/src/ai/LLMAdapter.js +2 -0
- package/dist/src/ai/SubAgentExecutor.d.ts +36 -0
- package/dist/src/ai/SubAgentExecutor.js +220 -0
- package/dist/src/ai/ToolExecutor.d.ts +35 -0
- package/dist/src/ai/ToolExecutor.js +237 -0
- package/dist/src/ai/index.d.ts +5 -0
- package/dist/src/ai/index.js +21 -0
- package/dist/src/handlers/StreamWriterFactory.d.ts +20 -0
- package/dist/src/handlers/StreamWriterFactory.js +83 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +4 -0
- package/dist/src/utils.d.ts +30 -0
- package/dist/src/utils.js +52 -0
- package/package.json +14 -2
- package/readme.md +425 -0
- package/spec/AgentDuplicateDetection.spec.ts +112 -0
- package/spec/AgentRunner.spec.ts +527 -0
- package/spec/ConversationHooks.spec.ts +290 -0
- package/spec/FlinkAgent.spec.ts +310 -0
- package/spec/FlinkApp.onError.spec.ts +1 -2
- package/spec/StreamingIntegration.spec.ts +138 -0
- package/spec/SubAgentSupport.spec.ts +941 -0
- package/spec/ToolExecutor.spec.ts +360 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar2.js +59 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema.js +53 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema2.js +53 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema3.js +53 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema2.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile2.js +58 -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 +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOnboardingSession.js +76 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOrderWithComplexTypes.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchProductWithIntersection.js +59 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchUserWithUnion.js +59 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostCar.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogin.js +56 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogout.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PutCar.js +55 -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 +1012 -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 +26 -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/FlinkResponse.js +2 -0
- package/spec/mock-project/dist/src/ai/AgentExecutor.js +279 -0
- package/spec/mock-project/dist/src/ai/AgentRunner.js +625 -0
- package/spec/mock-project/dist/src/ai/FlinkAgent.js +633 -0
- package/spec/mock-project/dist/src/ai/FlinkTool.js +2 -0
- package/spec/mock-project/dist/src/ai/LLMAdapter.js +2 -0
- package/spec/mock-project/dist/src/ai/SubAgentExecutor.js +220 -0
- package/spec/mock-project/dist/src/ai/ToolExecutor.js +237 -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/StreamWriterFactory.js +83 -0
- package/spec/mock-project/dist/src/index.js +17 -69
- package/spec/mock-project/dist/src/mock-data-generator.js +9 -0
- package/spec/mock-project/dist/src/utils.js +290 -0
- package/spec/mock-project/tsconfig.json +6 -1
- package/spec/testHelpers.ts +49 -0
- package/spec/utils.caseConversion.spec.ts +80 -0
- package/spec/utils.spec.ts +13 -13
- package/src/FlinkApp.ts +251 -7
- package/src/FlinkContext.ts +22 -0
- package/src/FlinkHttpHandler.ts +100 -2
- package/src/TypeScriptCompiler.ts +420 -9
- package/src/TypeScriptUtils.ts +5 -0
- package/src/ai/AgentRunner.ts +549 -0
- package/src/ai/FlinkAgent.ts +770 -0
- package/src/ai/FlinkTool.ts +40 -0
- package/src/ai/LLMAdapter.ts +96 -0
- package/src/ai/SubAgentExecutor.ts +199 -0
- package/src/ai/ToolExecutor.ts +193 -0
- package/src/ai/index.ts +5 -0
- package/src/handlers/StreamWriterFactory.ts +84 -0
- package/src/index.ts +4 -0
- package/src/utils.ts +52 -0
- package/tsconfig.json +6 -1
|
@@ -0,0 +1,1012 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
24
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
+
function step(op) {
|
|
27
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
+
switch (op[0]) {
|
|
32
|
+
case 0: case 1: t = op; break;
|
|
33
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
+
default:
|
|
37
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
+
if (t[2]) _.ops.pop();
|
|
42
|
+
_.trys.pop(); continue;
|
|
43
|
+
}
|
|
44
|
+
op = body.call(thisArg, _);
|
|
45
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
50
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
51
|
+
};
|
|
52
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
+
exports.FlinkApp = exports.autoRegisteredAgents = exports.autoRegisteredTools = exports.autoRegisteredJobs = exports.autoRegisteredRepos = exports.autoRegisteredHandlers = exports.expressFn = void 0;
|
|
54
|
+
var ajv_1 = __importDefault(require("ajv"));
|
|
55
|
+
var ajv_formats_1 = __importDefault(require("ajv-formats"));
|
|
56
|
+
var body_parser_1 = __importDefault(require("body-parser"));
|
|
57
|
+
var cors_1 = __importDefault(require("cors"));
|
|
58
|
+
var express_1 = __importDefault(require("express"));
|
|
59
|
+
var mongodb_1 = require("mongodb");
|
|
60
|
+
var morgan_1 = __importDefault(require("morgan"));
|
|
61
|
+
var ms_1 = __importDefault(require("ms"));
|
|
62
|
+
var toad_scheduler_1 = require("toad-scheduler");
|
|
63
|
+
var uuid_1 = require("uuid");
|
|
64
|
+
var FlinkErrors_1 = require("./FlinkErrors");
|
|
65
|
+
var FlinkHttpHandler_1 = require("./FlinkHttpHandler");
|
|
66
|
+
var FlinkLog_1 = require("./FlinkLog");
|
|
67
|
+
var StreamWriterFactory_1 = require("./handlers/StreamWriterFactory");
|
|
68
|
+
var mock_data_generator_1 = __importDefault(require("./mock-data-generator"));
|
|
69
|
+
var utils_1 = require("./utils");
|
|
70
|
+
var ajv = new ajv_1.default();
|
|
71
|
+
(0, ajv_formats_1.default)(ajv);
|
|
72
|
+
var defaultCorsOptions = {
|
|
73
|
+
allowedHeaders: "",
|
|
74
|
+
credentials: true,
|
|
75
|
+
origin: [/.*/],
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Re-export express factory function so sub packages
|
|
79
|
+
* do not need its own express
|
|
80
|
+
*/
|
|
81
|
+
exports.expressFn = express_1.default;
|
|
82
|
+
/**
|
|
83
|
+
* This will be populated at compile time when the apps handlers
|
|
84
|
+
* are picked up by TypeScript compiler
|
|
85
|
+
*/
|
|
86
|
+
exports.autoRegisteredHandlers = [];
|
|
87
|
+
/**
|
|
88
|
+
* This will be populated at compile time when the apps repos
|
|
89
|
+
* are picked up by TypeScript compiler
|
|
90
|
+
*/
|
|
91
|
+
exports.autoRegisteredRepos = [];
|
|
92
|
+
/**
|
|
93
|
+
* This will be populated at compile time when the apps jobs
|
|
94
|
+
* are picked up by TypeScript compiler
|
|
95
|
+
*/
|
|
96
|
+
exports.autoRegisteredJobs = [];
|
|
97
|
+
/**
|
|
98
|
+
* This will be populated at compile time when the apps tools
|
|
99
|
+
* are picked up by TypeScript compiler
|
|
100
|
+
*/
|
|
101
|
+
exports.autoRegisteredTools = [];
|
|
102
|
+
/**
|
|
103
|
+
* This will be populated at compile time when the apps agents
|
|
104
|
+
* are picked up by TypeScript compiler
|
|
105
|
+
*/
|
|
106
|
+
exports.autoRegisteredAgents = [];
|
|
107
|
+
var FlinkApp = /** @class */ (function () {
|
|
108
|
+
function FlinkApp(opts) {
|
|
109
|
+
var _a;
|
|
110
|
+
this.handlers = [];
|
|
111
|
+
this.started = false;
|
|
112
|
+
this.debug = false;
|
|
113
|
+
this.plugins = [];
|
|
114
|
+
this.routingConfigured = false;
|
|
115
|
+
this.disableHttpServer = false;
|
|
116
|
+
this.repos = {};
|
|
117
|
+
this.llmAdapters = new Map();
|
|
118
|
+
this.tools = {};
|
|
119
|
+
this.agents = {}; // FlinkAgent<C> instances
|
|
120
|
+
/**
|
|
121
|
+
* Internal cache used to track registered handlers and potentially any overlapping routes
|
|
122
|
+
*/
|
|
123
|
+
this.handlerRouteCache = new Map();
|
|
124
|
+
this.name = opts.name;
|
|
125
|
+
this.port = opts.port || 3333;
|
|
126
|
+
this.dbOpts = opts.db;
|
|
127
|
+
this.debug = !!opts.debug;
|
|
128
|
+
this.onDbConnection = opts.onDbConnection;
|
|
129
|
+
this.plugins = opts.plugins || [];
|
|
130
|
+
this.corsOpts = __assign(__assign({}, defaultCorsOptions), opts.cors);
|
|
131
|
+
this.rawContentTypes = Array.isArray(opts.rawContentTypes)
|
|
132
|
+
? opts.rawContentTypes
|
|
133
|
+
: typeof opts.rawContentTypes === "string"
|
|
134
|
+
? [opts.rawContentTypes]
|
|
135
|
+
: undefined;
|
|
136
|
+
this.auth = opts.auth;
|
|
137
|
+
this.jsonOptions = opts.jsonOptions || { limit: "1mb" };
|
|
138
|
+
this.schedulingOptions = opts.scheduling;
|
|
139
|
+
this.disableHttpServer = !!opts.disableHttpServer;
|
|
140
|
+
this.accessLog = __assign({ enabled: true, format: "dev" }, opts.accessLog);
|
|
141
|
+
this.onError = opts.onError;
|
|
142
|
+
// Register LLM adapters if configured
|
|
143
|
+
if ((_a = opts.ai) === null || _a === void 0 ? void 0 : _a.llms) {
|
|
144
|
+
// Convert plain object to Map for internal use
|
|
145
|
+
this.llmAdapters = new Map(Object.entries(opts.ai.llms));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
Object.defineProperty(FlinkApp.prototype, "ctx", {
|
|
149
|
+
get: function () {
|
|
150
|
+
if (!this._ctx) {
|
|
151
|
+
throw new Error("Context is not yet initialized");
|
|
152
|
+
}
|
|
153
|
+
return this._ctx;
|
|
154
|
+
},
|
|
155
|
+
enumerable: false,
|
|
156
|
+
configurable: true
|
|
157
|
+
});
|
|
158
|
+
FlinkApp.prototype.start = function () {
|
|
159
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
160
|
+
var startTime, offsetTime, _i, _a, type, _b, _c, plugin, db;
|
|
161
|
+
var _this = this;
|
|
162
|
+
var _d;
|
|
163
|
+
return __generator(this, function (_e) {
|
|
164
|
+
switch (_e.label) {
|
|
165
|
+
case 0:
|
|
166
|
+
startTime = Date.now();
|
|
167
|
+
offsetTime = 0;
|
|
168
|
+
return [4 /*yield*/, this.initDb()];
|
|
169
|
+
case 1:
|
|
170
|
+
_e.sent();
|
|
171
|
+
if (this.debug) {
|
|
172
|
+
offsetTime = Date.now();
|
|
173
|
+
FlinkLog_1.log.bgColorLog("cyan", "Init db took ".concat(offsetTime - startTime, " ms"));
|
|
174
|
+
}
|
|
175
|
+
// Build initial context (without agents - they'll be added later)
|
|
176
|
+
return [4 /*yield*/, this.buildContext()];
|
|
177
|
+
case 2:
|
|
178
|
+
// Build initial context (without agents - they'll be added later)
|
|
179
|
+
_e.sent();
|
|
180
|
+
if (this.debug) {
|
|
181
|
+
FlinkLog_1.log.bgColorLog("cyan", "Build context took ".concat(Date.now() - offsetTime, " ms"));
|
|
182
|
+
offsetTime = Date.now();
|
|
183
|
+
}
|
|
184
|
+
// Register tools (needs context for ToolExecutor)
|
|
185
|
+
return [4 /*yield*/, this.registerAutoRegisterableTools()];
|
|
186
|
+
case 3:
|
|
187
|
+
// Register tools (needs context for ToolExecutor)
|
|
188
|
+
_e.sent();
|
|
189
|
+
if (this.debug) {
|
|
190
|
+
FlinkLog_1.log.bgColorLog("cyan", "Register tools took ".concat(Date.now() - offsetTime, " ms"));
|
|
191
|
+
offsetTime = Date.now();
|
|
192
|
+
}
|
|
193
|
+
// Register agents (creates agent instances)
|
|
194
|
+
return [4 /*yield*/, this.registerAutoRegisterableAgents()];
|
|
195
|
+
case 4:
|
|
196
|
+
// Register agents (creates agent instances)
|
|
197
|
+
_e.sent();
|
|
198
|
+
if (this.debug) {
|
|
199
|
+
FlinkLog_1.log.bgColorLog("cyan", "Register agents took ".concat(Date.now() - offsetTime, " ms"));
|
|
200
|
+
offsetTime = Date.now();
|
|
201
|
+
}
|
|
202
|
+
// Initialize agents now that context and tools are ready
|
|
203
|
+
return [4 /*yield*/, this.initializeAgents()];
|
|
204
|
+
case 5:
|
|
205
|
+
// Initialize agents now that context and tools are ready
|
|
206
|
+
_e.sent();
|
|
207
|
+
if (this.debug) {
|
|
208
|
+
FlinkLog_1.log.bgColorLog("cyan", "Initialize agents took ".concat(Date.now() - offsetTime, " ms"));
|
|
209
|
+
offsetTime = Date.now();
|
|
210
|
+
}
|
|
211
|
+
if (this.isSchedulingEnabled) {
|
|
212
|
+
this.scheduler = new toad_scheduler_1.ToadScheduler();
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
FlinkLog_1.log.info("🚫 Scheduling is disabled");
|
|
216
|
+
}
|
|
217
|
+
if (!this.disableHttpServer) {
|
|
218
|
+
this.expressApp = (0, express_1.default)();
|
|
219
|
+
this.expressApp.use((0, cors_1.default)(this.corsOpts));
|
|
220
|
+
if (this.rawContentTypes) {
|
|
221
|
+
for (_i = 0, _a = this.rawContentTypes; _i < _a.length; _i++) {
|
|
222
|
+
type = _a[_i];
|
|
223
|
+
this.expressApp.use(express_1.default.raw({ type: type }));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
this.expressApp.use(body_parser_1.default.json(this.jsonOptions));
|
|
227
|
+
if (this.accessLog.enabled) {
|
|
228
|
+
this.expressApp.use((0, morgan_1.default)(this.accessLog.format));
|
|
229
|
+
}
|
|
230
|
+
this.expressApp.use(function (req, res, next) {
|
|
231
|
+
req.reqId = (0, uuid_1.v4)();
|
|
232
|
+
next();
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
_b = 0, _c = this.plugins;
|
|
236
|
+
_e.label = 6;
|
|
237
|
+
case 6:
|
|
238
|
+
if (!(_b < _c.length)) return [3 /*break*/, 12];
|
|
239
|
+
plugin = _c[_b];
|
|
240
|
+
db = void 0;
|
|
241
|
+
if (!plugin.db) return [3 /*break*/, 8];
|
|
242
|
+
return [4 /*yield*/, this.initPluginDb(plugin)];
|
|
243
|
+
case 7:
|
|
244
|
+
db = _e.sent();
|
|
245
|
+
_e.label = 8;
|
|
246
|
+
case 8:
|
|
247
|
+
if (!plugin.init) return [3 /*break*/, 10];
|
|
248
|
+
return [4 /*yield*/, plugin.init(this, db)];
|
|
249
|
+
case 9:
|
|
250
|
+
_e.sent();
|
|
251
|
+
_e.label = 10;
|
|
252
|
+
case 10:
|
|
253
|
+
FlinkLog_1.log.info("Initialized plugin '".concat(plugin.id, "'"));
|
|
254
|
+
_e.label = 11;
|
|
255
|
+
case 11:
|
|
256
|
+
_b++;
|
|
257
|
+
return [3 /*break*/, 6];
|
|
258
|
+
case 12: return [4 /*yield*/, this.registerAutoRegisterableHandlers()];
|
|
259
|
+
case 13:
|
|
260
|
+
_e.sent();
|
|
261
|
+
if (this.debug) {
|
|
262
|
+
FlinkLog_1.log.bgColorLog("cyan", "Register handlers took ".concat(Date.now() - offsetTime, " ms"));
|
|
263
|
+
offsetTime = Date.now();
|
|
264
|
+
}
|
|
265
|
+
if (!this.isSchedulingEnabled) return [3 /*break*/, 15];
|
|
266
|
+
return [4 /*yield*/, this.registerAutoRegisterableJobs()];
|
|
267
|
+
case 14:
|
|
268
|
+
_e.sent();
|
|
269
|
+
if (this.debug) {
|
|
270
|
+
FlinkLog_1.log.bgColorLog("cyan", "Register jobs took ".concat(Date.now() - offsetTime, " ms"));
|
|
271
|
+
offsetTime = Date.now();
|
|
272
|
+
}
|
|
273
|
+
_e.label = 15;
|
|
274
|
+
case 15:
|
|
275
|
+
// Register 404 with slight delay to allow all manually added routes to be added
|
|
276
|
+
// TODO: Is there a better solution to force this handler to always run last?
|
|
277
|
+
setTimeout(function () {
|
|
278
|
+
if (!_this.disableHttpServer) {
|
|
279
|
+
_this.expressApp.use(function (req, res, next) {
|
|
280
|
+
res.status(404).json((0, FlinkErrors_1.notFound)());
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
_this.routingConfigured = true;
|
|
284
|
+
});
|
|
285
|
+
if (this.disableHttpServer) {
|
|
286
|
+
FlinkLog_1.log.info("🚧 HTTP server is disabled, but flink app is running");
|
|
287
|
+
this.started = true;
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
this.expressServer = (_d = this.expressApp) === null || _d === void 0 ? void 0 : _d.listen(this.port, function () {
|
|
291
|
+
FlinkLog_1.log.fontColorLog("magenta", "\u26A1\uFE0F HTTP server '".concat(_this.name, "' is running and waiting for connections on ").concat(_this.port));
|
|
292
|
+
_this.started = true;
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
return [2 /*return*/, this];
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
};
|
|
300
|
+
FlinkApp.prototype.stop = function () {
|
|
301
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
302
|
+
var _this = this;
|
|
303
|
+
return __generator(this, function (_a) {
|
|
304
|
+
switch (_a.label) {
|
|
305
|
+
case 0:
|
|
306
|
+
FlinkLog_1.log.info("🛑 Stopping Flink app...");
|
|
307
|
+
if (!this.scheduler) return [3 /*break*/, 2];
|
|
308
|
+
return [4 /*yield*/, this.scheduler.stop()];
|
|
309
|
+
case 1:
|
|
310
|
+
_a.sent();
|
|
311
|
+
_a.label = 2;
|
|
312
|
+
case 2:
|
|
313
|
+
if (this.expressServer) {
|
|
314
|
+
return [2 /*return*/, new Promise(function (resolve, reject) {
|
|
315
|
+
var int = setTimeout(function () {
|
|
316
|
+
reject("Failed to stop HTTP server in time");
|
|
317
|
+
}, 2000);
|
|
318
|
+
_this.expressServer.close(function () {
|
|
319
|
+
clearInterval(int);
|
|
320
|
+
FlinkLog_1.log.info("HTTP server stopped");
|
|
321
|
+
resolve();
|
|
322
|
+
});
|
|
323
|
+
})];
|
|
324
|
+
}
|
|
325
|
+
return [2 /*return*/];
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
};
|
|
330
|
+
/**
|
|
331
|
+
* Manually registers a handler.
|
|
332
|
+
*
|
|
333
|
+
* Typescript compiler will scan handler function and set schemas
|
|
334
|
+
* which are derived from handler function type arguments.
|
|
335
|
+
*/
|
|
336
|
+
FlinkApp.prototype.addHandler = function (handler, routePropsOverride) {
|
|
337
|
+
var _a, _b, _c, _d, _e, _f;
|
|
338
|
+
if (this.routingConfigured) {
|
|
339
|
+
throw new Error("Cannot add handler after routes has been registered, make sure to invoke earlier");
|
|
340
|
+
}
|
|
341
|
+
var routeProps = __assign(__assign({}, (handler.Route || {})), routePropsOverride);
|
|
342
|
+
if (!routeProps.method) {
|
|
343
|
+
FlinkLog_1.log.error("Failed to register handler '".concat(handler.__file, "': Missing 'method' in route props, either set it or name handler file with HTTP method as prefix"));
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
if (!routeProps.path) {
|
|
347
|
+
FlinkLog_1.log.error("Failed to register handler '".concat(handler.__file, "': Missing 'path' in route props"));
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
var dup = this.handlers.find(function (h) { return h.routeProps.path === routeProps.path && h.routeProps.method === routeProps.method; });
|
|
351
|
+
var methodAndPath = "".concat(routeProps.method.toUpperCase(), " ").concat(routeProps.path);
|
|
352
|
+
if (dup) {
|
|
353
|
+
// TODO: Not sure if there is a case where you'd want to overwrite a route?
|
|
354
|
+
FlinkLog_1.log.warn("".concat(methodAndPath, " overlaps existing route"));
|
|
355
|
+
}
|
|
356
|
+
var handlerConfig = {
|
|
357
|
+
routeProps: __assign(__assign({}, routeProps), { method: routeProps.method, path: routeProps.path }),
|
|
358
|
+
schema: {
|
|
359
|
+
reqSchema: (_a = handler.__schemas) === null || _a === void 0 ? void 0 : _a.reqSchema,
|
|
360
|
+
resSchema: (_b = handler.__schemas) === null || _b === void 0 ? void 0 : _b.resSchema,
|
|
361
|
+
},
|
|
362
|
+
queryMetadata: handler.__query || [],
|
|
363
|
+
paramsMetadata: handler.__params || [],
|
|
364
|
+
};
|
|
365
|
+
if (((_c = handler.__schemas) === null || _c === void 0 ? void 0 : _c.reqSchema) && !((_d = handlerConfig.schema) === null || _d === void 0 ? void 0 : _d.reqSchema)) {
|
|
366
|
+
FlinkLog_1.log.warn("Expected request schema ".concat(handler.__schemas.reqSchema, " for handler ").concat(methodAndPath, " but no such schema was found"));
|
|
367
|
+
}
|
|
368
|
+
if (((_e = handler.__schemas) === null || _e === void 0 ? void 0 : _e.resSchema) && !((_f = handlerConfig.schema) === null || _f === void 0 ? void 0 : _f.resSchema)) {
|
|
369
|
+
FlinkLog_1.log.warn("Expected response schema ".concat(handler.__schemas.resSchema, " for handler ").concat(methodAndPath, " but no such schema was found"));
|
|
370
|
+
}
|
|
371
|
+
this.registerHandler(handlerConfig, handler.default);
|
|
372
|
+
};
|
|
373
|
+
FlinkApp.prototype.registerHandler = function (handlerConfig, handler) {
|
|
374
|
+
var _this = this;
|
|
375
|
+
this.handlers.push(handlerConfig);
|
|
376
|
+
var routeProps = handlerConfig.routeProps, _a = handlerConfig.schema, schema = _a === void 0 ? {} : _a;
|
|
377
|
+
var method = routeProps.method, streamFormat = routeProps.streamFormat;
|
|
378
|
+
if (!method) {
|
|
379
|
+
FlinkLog_1.log.error("Route ".concat(routeProps.path, " is missing http method"));
|
|
380
|
+
}
|
|
381
|
+
if (method) {
|
|
382
|
+
var methodAndRoute_1 = "".concat(method.toUpperCase(), " ").concat(routeProps.path);
|
|
383
|
+
if (this.disableHttpServer) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
var validateReq_1;
|
|
387
|
+
var validateRes_1;
|
|
388
|
+
// Determine validation mode (default to Validate if not specified)
|
|
389
|
+
var validationMode = routeProps.validation || FlinkHttpHandler_1.ValidationMode.Validate;
|
|
390
|
+
// Compile request schema if validation mode requires it
|
|
391
|
+
if (schema.reqSchema && validationMode !== FlinkHttpHandler_1.ValidationMode.SkipValidation && validationMode !== FlinkHttpHandler_1.ValidationMode.ValidateResponse) {
|
|
392
|
+
validateReq_1 = ajv.compile(schema.reqSchema);
|
|
393
|
+
}
|
|
394
|
+
// Skip response validation for streaming handlers (responses are stream chunks, not final JSON)
|
|
395
|
+
if (!streamFormat && schema.resSchema && validationMode !== FlinkHttpHandler_1.ValidationMode.SkipValidation && validationMode !== FlinkHttpHandler_1.ValidationMode.ValidateRequest) {
|
|
396
|
+
validateRes_1 = ajv.compile(schema.resSchema);
|
|
397
|
+
}
|
|
398
|
+
this.expressApp[method](routeProps.path, function (req, res) { return __awaiter(_this, void 0, void 0, function () {
|
|
399
|
+
var valid, formattedErrors, data, normalizedQuery, _i, _a, _b, key, value, stream, handlerRes, err_1, errorResponse, result, valid, formattedErrors;
|
|
400
|
+
return __generator(this, function (_c) {
|
|
401
|
+
switch (_c.label) {
|
|
402
|
+
case 0:
|
|
403
|
+
if (!routeProps.permissions) return [3 /*break*/, 2];
|
|
404
|
+
return [4 /*yield*/, this.authenticate(req, routeProps.permissions)];
|
|
405
|
+
case 1:
|
|
406
|
+
if (!(_c.sent())) {
|
|
407
|
+
return [2 /*return*/, res.status(401).json((0, FlinkErrors_1.unauthorized)())];
|
|
408
|
+
}
|
|
409
|
+
_c.label = 2;
|
|
410
|
+
case 2:
|
|
411
|
+
if (validateReq_1) {
|
|
412
|
+
valid = validateReq_1(req.body);
|
|
413
|
+
if (!valid) {
|
|
414
|
+
formattedErrors = (0, utils_1.formatValidationErrors)(validateReq_1.errors, req.body);
|
|
415
|
+
FlinkLog_1.log.warn("[".concat(req.reqId, "] ").concat(methodAndRoute_1, ": Bad request\n").concat(formattedErrors));
|
|
416
|
+
return [2 /*return*/, res.status(400).json({
|
|
417
|
+
status: 400,
|
|
418
|
+
error: {
|
|
419
|
+
id: (0, uuid_1.v4)(),
|
|
420
|
+
title: "Bad request",
|
|
421
|
+
detail: formattedErrors,
|
|
422
|
+
},
|
|
423
|
+
})];
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
// Skip mock API for streaming handlers
|
|
427
|
+
if (routeProps.mockApi && schema.resSchema && !streamFormat) {
|
|
428
|
+
FlinkLog_1.log.warn("Mock response for ".concat(req.method.toUpperCase(), " ").concat(req.path));
|
|
429
|
+
data = (0, mock_data_generator_1.default)(schema.resSchema);
|
|
430
|
+
res.status(200).json({
|
|
431
|
+
status: 200,
|
|
432
|
+
data: data,
|
|
433
|
+
});
|
|
434
|
+
return [2 /*return*/];
|
|
435
|
+
}
|
|
436
|
+
// Normalize query parameters to predictable string or string[] types
|
|
437
|
+
// Express query parser can produce numbers, booleans, objects, etc.
|
|
438
|
+
// We normalize everything to strings or string arrays for consistency
|
|
439
|
+
if (req.query && typeof req.query === "object") {
|
|
440
|
+
normalizedQuery = {};
|
|
441
|
+
for (_i = 0, _a = Object.entries(req.query); _i < _a.length; _i++) {
|
|
442
|
+
_b = _a[_i], key = _b[0], value = _b[1];
|
|
443
|
+
if (Array.isArray(value)) {
|
|
444
|
+
// Handle array values (e.g., ?tag=a&tag=b)
|
|
445
|
+
normalizedQuery[key] = value.map(function (v) { return String(v); });
|
|
446
|
+
}
|
|
447
|
+
else if (value !== undefined && value !== null) {
|
|
448
|
+
// Convert single values to strings
|
|
449
|
+
normalizedQuery[key] = String(value);
|
|
450
|
+
}
|
|
451
|
+
// Skip undefined/null values - they won't appear in the normalized query
|
|
452
|
+
}
|
|
453
|
+
req.query = normalizedQuery;
|
|
454
|
+
}
|
|
455
|
+
stream = streamFormat ? StreamWriterFactory_1.StreamWriterFactory.create(res, streamFormat) : undefined;
|
|
456
|
+
_c.label = 3;
|
|
457
|
+
case 3:
|
|
458
|
+
_c.trys.push([3, 5, , 6]);
|
|
459
|
+
return [4 /*yield*/, handler({
|
|
460
|
+
req: req,
|
|
461
|
+
ctx: this.ctx,
|
|
462
|
+
origin: routeProps.origin,
|
|
463
|
+
stream: stream,
|
|
464
|
+
})];
|
|
465
|
+
case 4:
|
|
466
|
+
// 👇 This is where the actual handler gets invoked
|
|
467
|
+
handlerRes = _c.sent();
|
|
468
|
+
return [3 /*break*/, 6];
|
|
469
|
+
case 5:
|
|
470
|
+
err_1 = _c.sent();
|
|
471
|
+
// Handle errors for streaming handlers
|
|
472
|
+
if (streamFormat && stream) {
|
|
473
|
+
FlinkLog_1.log.error("Streaming handler error on ".concat(req.method.toUpperCase(), " ").concat(req.path, ": ").concat(err_1.message), {
|
|
474
|
+
error: err_1,
|
|
475
|
+
path: req.path,
|
|
476
|
+
method: req.method,
|
|
477
|
+
});
|
|
478
|
+
stream.error(err_1);
|
|
479
|
+
return [2 /*return*/];
|
|
480
|
+
}
|
|
481
|
+
errorResponse = void 0;
|
|
482
|
+
// duck typing to check if it is a FlinkError
|
|
483
|
+
if (typeof err_1.status === "number" && err_1.status >= 400 && err_1.status < 600 && err_1.error) {
|
|
484
|
+
errorResponse = {
|
|
485
|
+
status: err_1.status,
|
|
486
|
+
error: {
|
|
487
|
+
id: err_1.error.id || (0, uuid_1.v4)(),
|
|
488
|
+
title: err_1.error.title || "Unhandled error: ".concat(err_1.error.code || err_1.status),
|
|
489
|
+
detail: err_1.error.detail,
|
|
490
|
+
code: err_1.error.code,
|
|
491
|
+
},
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
FlinkLog_1.log.warn("Handler '".concat(methodAndRoute_1, "' threw unhandled exception ").concat(err_1));
|
|
496
|
+
console.error(err_1);
|
|
497
|
+
errorResponse = (0, FlinkErrors_1.internalServerError)(err_1);
|
|
498
|
+
}
|
|
499
|
+
// Invoke onError callback if provided
|
|
500
|
+
if (this.onError) {
|
|
501
|
+
try {
|
|
502
|
+
result = this.onError(errorResponse, {
|
|
503
|
+
req: req,
|
|
504
|
+
method: method,
|
|
505
|
+
path: routeProps.path,
|
|
506
|
+
reqId: req.reqId,
|
|
507
|
+
});
|
|
508
|
+
// Handle async callbacks - don't wait for them
|
|
509
|
+
if (result instanceof Promise) {
|
|
510
|
+
result.catch(function (callbackErr) {
|
|
511
|
+
FlinkLog_1.log.error("onError callback rejected with: ".concat(callbackErr));
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
catch (callbackErr) {
|
|
516
|
+
FlinkLog_1.log.error("onError callback threw an exception: ".concat(callbackErr));
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return [2 /*return*/, res.status(errorResponse.status || 500).json(errorResponse)];
|
|
520
|
+
case 6:
|
|
521
|
+
// Skip response handling for streaming handlers (stream controls response lifecycle)
|
|
522
|
+
if (streamFormat) {
|
|
523
|
+
return [2 /*return*/];
|
|
524
|
+
}
|
|
525
|
+
// Ensure handlerRes is defined for non-streaming handlers
|
|
526
|
+
if (!handlerRes) {
|
|
527
|
+
return [2 /*return*/, res.status(204).send()];
|
|
528
|
+
}
|
|
529
|
+
if (validateRes_1 && !(0, utils_1.isError)(handlerRes)) {
|
|
530
|
+
valid = validateRes_1(JSON.parse(JSON.stringify(handlerRes.data)));
|
|
531
|
+
if (!valid) {
|
|
532
|
+
formattedErrors = (0, utils_1.formatValidationErrors)(validateRes_1.errors, handlerRes.data);
|
|
533
|
+
FlinkLog_1.log.warn("[".concat(req.reqId, "] ").concat(methodAndRoute_1, ": Bad response\n").concat(formattedErrors));
|
|
534
|
+
return [2 /*return*/, res.status(500).json({
|
|
535
|
+
status: 500,
|
|
536
|
+
error: {
|
|
537
|
+
id: (0, uuid_1.v4)(),
|
|
538
|
+
title: "Bad response",
|
|
539
|
+
detail: formattedErrors,
|
|
540
|
+
},
|
|
541
|
+
})];
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
res.set(handlerRes.headers);
|
|
545
|
+
res.status(handlerRes.status || 200).json(handlerRes);
|
|
546
|
+
return [2 /*return*/];
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
}); });
|
|
550
|
+
if (this.handlerRouteCache.has(methodAndRoute_1)) {
|
|
551
|
+
FlinkLog_1.log.error("Cannot register handler ".concat(methodAndRoute_1, " - route already registered"));
|
|
552
|
+
return process.exit(1); // TODO: Do we need to exit?
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
this.handlerRouteCache.set(methodAndRoute_1, JSON.stringify(routeProps));
|
|
556
|
+
FlinkLog_1.log.info("Registered ".concat(streamFormat ? 'streaming ' : '', "route ").concat(methodAndRoute_1).concat(streamFormat ? " (".concat(streamFormat, ")") : ''));
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
/**
|
|
561
|
+
* Register handlers found within the `/src/handlers`
|
|
562
|
+
* directory in Flink App.
|
|
563
|
+
*
|
|
564
|
+
* Will not register any handlers added programmatically.
|
|
565
|
+
*/
|
|
566
|
+
FlinkApp.prototype.registerAutoRegisterableHandlers = function () {
|
|
567
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
568
|
+
var _i, _a, _b, handler, assumedHttpMethod, pathParams, _c, _d, param;
|
|
569
|
+
var _e, _f, _g;
|
|
570
|
+
return __generator(this, function (_h) {
|
|
571
|
+
for (_i = 0, _a = exports.autoRegisteredHandlers.sort(function (a, b) { var _a, _b; return (((_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); }); _i < _a.length; _i++) {
|
|
572
|
+
_b = _a[_i], handler = _b.handler, assumedHttpMethod = _b.assumedHttpMethod;
|
|
573
|
+
if (!handler.Route) {
|
|
574
|
+
FlinkLog_1.log.error("Missing Props in handler ".concat(handler.__file));
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
if (!handler.default) {
|
|
578
|
+
FlinkLog_1.log.error("Missing exported handler function in handler ".concat(handler.__file));
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
if (!!((_e = handler.__params) === null || _e === void 0 ? void 0 : _e.length)) {
|
|
582
|
+
pathParams = (0, utils_1.getPathParams)(handler.Route.path);
|
|
583
|
+
for (_c = 0, _d = handler.__params; _c < _d.length; _c++) {
|
|
584
|
+
param = _d[_c];
|
|
585
|
+
if (!pathParams.includes(param.name)) {
|
|
586
|
+
FlinkLog_1.log.error("Handler ".concat(handler.__file, " has param ").concat(param.name, " but it is not present in the path '").concat(handler.Route.path, "'"));
|
|
587
|
+
throw new Error("Invalid/missing handler path param");
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (pathParams.length !== handler.__params.length) {
|
|
591
|
+
FlinkLog_1.log.warn("Handler ".concat(handler.__file, " has ").concat(handler.__params.length, " typed params but the path '").concat(handler.Route.path, "' has ").concat(pathParams.length, " params"));
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
this.registerHandler({
|
|
595
|
+
routeProps: __assign(__assign({}, handler.Route), { method: handler.Route.method || assumedHttpMethod, origin: this.name }),
|
|
596
|
+
schema: {
|
|
597
|
+
reqSchema: (_f = handler.__schemas) === null || _f === void 0 ? void 0 : _f.reqSchema,
|
|
598
|
+
resSchema: (_g = handler.__schemas) === null || _g === void 0 ? void 0 : _g.resSchema,
|
|
599
|
+
},
|
|
600
|
+
queryMetadata: handler.__query || [],
|
|
601
|
+
paramsMetadata: handler.__params || [],
|
|
602
|
+
}, handler.default);
|
|
603
|
+
}
|
|
604
|
+
return [2 /*return*/];
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
};
|
|
608
|
+
FlinkApp.prototype.registerAutoRegisterableJobs = function () {
|
|
609
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
610
|
+
var _loop_1, this_1, _i, autoRegisteredJobs_1, _a, jobProps, jobFn, __file;
|
|
611
|
+
var _this = this;
|
|
612
|
+
return __generator(this, function (_b) {
|
|
613
|
+
if (!this.scheduler) {
|
|
614
|
+
throw new Error("Scheduler not initialized"); // should never happen
|
|
615
|
+
}
|
|
616
|
+
_loop_1 = function (jobProps, jobFn, __file) {
|
|
617
|
+
if (jobProps.cron && jobProps.interval) {
|
|
618
|
+
FlinkLog_1.log.error("Cannot register job ".concat(jobProps.id, " - both cron and interval are set in ").concat(__file));
|
|
619
|
+
return "continue";
|
|
620
|
+
}
|
|
621
|
+
if (jobProps.cron && jobProps.afterDelay) {
|
|
622
|
+
FlinkLog_1.log.error("Cannot register job ".concat(jobProps.id, " - both cron and afterDelay are set in ").concat(__file));
|
|
623
|
+
return "continue";
|
|
624
|
+
}
|
|
625
|
+
if (jobProps.interval && jobProps.afterDelay) {
|
|
626
|
+
FlinkLog_1.log.error("Cannot register job ".concat(jobProps.id, " - both interval and afterDelay are set in ").concat(__file));
|
|
627
|
+
return "continue";
|
|
628
|
+
}
|
|
629
|
+
if (this_1.scheduler.existsById(jobProps.id)) {
|
|
630
|
+
FlinkLog_1.log.error("Job with id ".concat(jobProps.id, " is already registered, found duplicate in ").concat(__file));
|
|
631
|
+
return "continue";
|
|
632
|
+
}
|
|
633
|
+
FlinkLog_1.log.debug("Registering job ".concat(jobProps.id, ": ").concat(JSON.stringify(jobProps), " from ").concat(__file));
|
|
634
|
+
var task = new toad_scheduler_1.AsyncTask(jobProps.id, function () { return __awaiter(_this, void 0, void 0, function () {
|
|
635
|
+
return __generator(this, function (_a) {
|
|
636
|
+
switch (_a.label) {
|
|
637
|
+
case 0: return [4 /*yield*/, jobFn({ ctx: this.ctx })];
|
|
638
|
+
case 1:
|
|
639
|
+
_a.sent();
|
|
640
|
+
FlinkLog_1.log.debug("Job ".concat(jobProps.id, " completed"));
|
|
641
|
+
if (jobProps.afterDelay) {
|
|
642
|
+
// afterDelay runs only once, so we remove the job
|
|
643
|
+
this.scheduler.removeById(jobProps.id);
|
|
644
|
+
}
|
|
645
|
+
return [2 /*return*/];
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
}); }, function (err) {
|
|
649
|
+
FlinkLog_1.log.error("Job ".concat(jobProps.id, " threw unhandled exception ").concat(err));
|
|
650
|
+
console.error(err);
|
|
651
|
+
});
|
|
652
|
+
if (jobProps.cron) {
|
|
653
|
+
var job = new toad_scheduler_1.CronJob({ timezone: jobProps.timezone, cronExpression: jobProps.cron }, task, {
|
|
654
|
+
id: jobProps.id,
|
|
655
|
+
preventOverrun: jobProps.singleton,
|
|
656
|
+
});
|
|
657
|
+
this_1.scheduler.addCronJob(job);
|
|
658
|
+
}
|
|
659
|
+
else if (jobProps.interval) {
|
|
660
|
+
var job = new toad_scheduler_1.SimpleIntervalJob({
|
|
661
|
+
milliseconds: (0, ms_1.default)(jobProps.interval),
|
|
662
|
+
runImmediately: false, // TODO: Expose to props?
|
|
663
|
+
}, task, {
|
|
664
|
+
id: jobProps.id,
|
|
665
|
+
preventOverrun: jobProps.singleton,
|
|
666
|
+
});
|
|
667
|
+
this_1.scheduler.addSimpleIntervalJob(job);
|
|
668
|
+
}
|
|
669
|
+
else if (jobProps.afterDelay !== undefined) {
|
|
670
|
+
var job = new toad_scheduler_1.SimpleIntervalJob({
|
|
671
|
+
milliseconds: (0, ms_1.default)(jobProps.afterDelay),
|
|
672
|
+
runImmediately: false,
|
|
673
|
+
}, task, {
|
|
674
|
+
id: jobProps.id,
|
|
675
|
+
preventOverrun: jobProps.singleton,
|
|
676
|
+
});
|
|
677
|
+
this_1.scheduler.addSimpleIntervalJob(job);
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
FlinkLog_1.log.error("Cannot register job ".concat(jobProps.id, " - no cron, interval or once set in ").concat(__file));
|
|
681
|
+
return "continue";
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
this_1 = this;
|
|
685
|
+
for (_i = 0, autoRegisteredJobs_1 = exports.autoRegisteredJobs; _i < autoRegisteredJobs_1.length; _i++) {
|
|
686
|
+
_a = autoRegisteredJobs_1[_i], jobProps = _a.Job, jobFn = _a.default, __file = _a.__file;
|
|
687
|
+
_loop_1(jobProps, jobFn, __file);
|
|
688
|
+
}
|
|
689
|
+
return [2 /*return*/];
|
|
690
|
+
});
|
|
691
|
+
});
|
|
692
|
+
};
|
|
693
|
+
FlinkApp.prototype.addRepo = function (instanceName, repoInstance) {
|
|
694
|
+
this.repos[instanceName] = repoInstance;
|
|
695
|
+
// TODO: Find out if we need to set ctx here or wanted not to if plugin has its own context
|
|
696
|
+
// repoInstance.ctx = this.ctx;
|
|
697
|
+
};
|
|
698
|
+
FlinkApp.prototype.registerAutoRegisterableTools = function () {
|
|
699
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
700
|
+
var ToolExecutor, getRepoInstanceName, _i, autoRegisteredTools_1, toolFile, toolId, toolInstanceName, toolExecutor;
|
|
701
|
+
return __generator(this, function (_a) {
|
|
702
|
+
ToolExecutor = require("./ai/ToolExecutor").ToolExecutor;
|
|
703
|
+
getRepoInstanceName = require("./utils").getRepoInstanceName;
|
|
704
|
+
for (_i = 0, autoRegisteredTools_1 = exports.autoRegisteredTools; _i < autoRegisteredTools_1.length; _i++) {
|
|
705
|
+
toolFile = autoRegisteredTools_1[_i];
|
|
706
|
+
if (!toolFile.Tool) {
|
|
707
|
+
FlinkLog_1.log.error("Missing FlinkToolProps export in tool ".concat(toolFile.__file));
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
if (!toolFile.default) {
|
|
711
|
+
FlinkLog_1.log.error("Missing exported tool function in tool ".concat(toolFile.__file));
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
toolId = toolFile.Tool.id;
|
|
715
|
+
if (!toolId) {
|
|
716
|
+
FlinkLog_1.log.error("Tool ".concat(toolFile.__file, " missing 'id' property"));
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
toolInstanceName = getRepoInstanceName(toolId);
|
|
720
|
+
toolExecutor = new ToolExecutor(toolFile.Tool, toolFile.default, this.ctx);
|
|
721
|
+
this.tools[toolInstanceName] = toolExecutor;
|
|
722
|
+
FlinkLog_1.log.info("Registered tool ".concat(toolInstanceName, " (").concat(toolId, ")"));
|
|
723
|
+
}
|
|
724
|
+
return [2 /*return*/];
|
|
725
|
+
});
|
|
726
|
+
});
|
|
727
|
+
};
|
|
728
|
+
FlinkApp.prototype.registerAutoRegisterableAgents = function () {
|
|
729
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
730
|
+
var _a, getRepoInstanceName, toKebabCase, SubAgentExecutor, _loop_2, this_2, _i, autoRegisteredAgents_1, agentFile, _b, autoRegisteredAgents_2, agentFile, AgentClass, agentInstance, _c, _d, agentRef, subAgentInstanceName;
|
|
731
|
+
return __generator(this, function (_e) {
|
|
732
|
+
_a = require("./utils"), getRepoInstanceName = _a.getRepoInstanceName, toKebabCase = _a.toKebabCase;
|
|
733
|
+
SubAgentExecutor = require("./ai/SubAgentExecutor").SubAgentExecutor;
|
|
734
|
+
_loop_2 = function (agentFile) {
|
|
735
|
+
// agentFile now exports a class, not a config object
|
|
736
|
+
var AgentClass = agentFile.default;
|
|
737
|
+
if (!AgentClass) {
|
|
738
|
+
FlinkLog_1.log.error("Missing default export in agent ".concat(agentFile.__file));
|
|
739
|
+
return "continue";
|
|
740
|
+
}
|
|
741
|
+
// Instantiate agent (similar to repo instantiation)
|
|
742
|
+
var agentInstance = new AgentClass();
|
|
743
|
+
// Derive instance name from class name (camelCase)
|
|
744
|
+
var agentInstanceName = getRepoInstanceName(AgentClass.name);
|
|
745
|
+
// Get agent ID (kebab-case) - either explicit or derived
|
|
746
|
+
var agentId = agentInstance.id || toKebabCase(AgentClass.name);
|
|
747
|
+
// Check for duplicate instance name
|
|
748
|
+
if (this_2.agents[agentInstanceName]) {
|
|
749
|
+
var existingAgent = this_2.agents[agentInstanceName];
|
|
750
|
+
throw new Error("Duplicate agent instance name: \"".concat(agentInstanceName, "\". ") +
|
|
751
|
+
"Agent class \"".concat(AgentClass.name, "\" conflicts with existing agent \"").concat(existingAgent.constructor.name, "\". ") +
|
|
752
|
+
"Instance names are derived by lowercasing the first letter of the class name. " +
|
|
753
|
+
"Rename one of the classes or use a unique explicit 'id' property.");
|
|
754
|
+
}
|
|
755
|
+
// Check for duplicate agent ID
|
|
756
|
+
var existingAgentWithSameId = Object.values(this_2.agents).find(function (agent) {
|
|
757
|
+
var existingId = agent.id || toKebabCase(agent.constructor.name);
|
|
758
|
+
return existingId === agentId;
|
|
759
|
+
});
|
|
760
|
+
if (existingAgentWithSameId) {
|
|
761
|
+
throw new Error("Duplicate agent ID: \"".concat(agentId, "\". ") +
|
|
762
|
+
"Agent class \"".concat(AgentClass.name, "\" conflicts with existing agent \"").concat(existingAgentWithSameId.constructor.name, "\". ") +
|
|
763
|
+
"Agent IDs are derived from class names using kebab-case (e.g., CarAgent \u2192 car-agent). " +
|
|
764
|
+
"Use an explicit 'id' property to resolve this conflict:\n" +
|
|
765
|
+
" id = \"my-unique-id\";");
|
|
766
|
+
}
|
|
767
|
+
// Validate tools exist
|
|
768
|
+
for (var _f = 0, _g = agentInstance.tools; _f < _g.length; _f++) {
|
|
769
|
+
var toolRef = _g[_f];
|
|
770
|
+
// Handle both string IDs and tool file references
|
|
771
|
+
var toolId = typeof toolRef === "string"
|
|
772
|
+
? toolRef
|
|
773
|
+
: toolRef.Tool.id; // Extract ID from FlinkToolFile
|
|
774
|
+
var tool = this_2.tools[toolId];
|
|
775
|
+
if (!tool) {
|
|
776
|
+
FlinkLog_1.log.error("Agent ".concat(AgentClass.name, " references tool ").concat(toolId, " which is not registered"));
|
|
777
|
+
throw new Error("Invalid tool reference in agent ".concat(AgentClass.name));
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
// Validate and register sub-agents
|
|
781
|
+
if (agentInstance.agents && agentInstance.agents.length > 0) {
|
|
782
|
+
for (var _h = 0, _j = agentInstance.agents; _h < _j.length; _h++) {
|
|
783
|
+
var agentRef = _j[_h];
|
|
784
|
+
// Get instance name directly from class reference or string
|
|
785
|
+
var subAgentInstanceName = typeof agentRef === "string" ? agentRef : getRepoInstanceName(agentRef.name);
|
|
786
|
+
// Validate that sub-agent will exist (will be registered in this loop)
|
|
787
|
+
// For now, just log - actual validation happens at runtime
|
|
788
|
+
FlinkLog_1.log.debug("Agent ".concat(AgentClass.name, " references sub-agent ").concat(subAgentInstanceName));
|
|
789
|
+
// Create a SubAgentExecutor as a special tool
|
|
790
|
+
var subAgentToolName = "ask_".concat(subAgentInstanceName);
|
|
791
|
+
var subAgentExecutor = new SubAgentExecutor(subAgentInstanceName, this_2.ctx);
|
|
792
|
+
// Register as a tool so it appears in the agent's tool list
|
|
793
|
+
this_2.tools[subAgentToolName] = subAgentExecutor;
|
|
794
|
+
FlinkLog_1.log.debug("Created sub-agent tool ".concat(subAgentToolName, " for ").concat(subAgentInstanceName));
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
// Register agent (duplicate checks already performed above)
|
|
798
|
+
this_2.agents[agentInstanceName] = agentInstance;
|
|
799
|
+
FlinkLog_1.log.info("Registered agent ".concat(agentInstanceName, " (").concat(AgentClass.name, ") with ID: ").concat(agentId));
|
|
800
|
+
};
|
|
801
|
+
this_2 = this;
|
|
802
|
+
for (_i = 0, autoRegisteredAgents_1 = exports.autoRegisteredAgents; _i < autoRegisteredAgents_1.length; _i++) {
|
|
803
|
+
agentFile = autoRegisteredAgents_1[_i];
|
|
804
|
+
_loop_2(agentFile);
|
|
805
|
+
}
|
|
806
|
+
// Second pass: validate all sub-agent references
|
|
807
|
+
for (_b = 0, autoRegisteredAgents_2 = exports.autoRegisteredAgents; _b < autoRegisteredAgents_2.length; _b++) {
|
|
808
|
+
agentFile = autoRegisteredAgents_2[_b];
|
|
809
|
+
AgentClass = agentFile.default;
|
|
810
|
+
if (!AgentClass) {
|
|
811
|
+
continue;
|
|
812
|
+
}
|
|
813
|
+
agentInstance = new AgentClass();
|
|
814
|
+
if (agentInstance.agents && agentInstance.agents.length > 0) {
|
|
815
|
+
for (_c = 0, _d = agentInstance.agents; _c < _d.length; _c++) {
|
|
816
|
+
agentRef = _d[_c];
|
|
817
|
+
subAgentInstanceName = typeof agentRef === "string" ? agentRef : getRepoInstanceName(agentRef.name);
|
|
818
|
+
if (!this.agents[subAgentInstanceName]) {
|
|
819
|
+
FlinkLog_1.log.error("Agent ".concat(AgentClass.name, " references sub-agent ").concat(subAgentInstanceName, " which is not registered"));
|
|
820
|
+
throw new Error("Invalid sub-agent reference in agent ".concat(AgentClass.name, ": ").concat(subAgentInstanceName));
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return [2 /*return*/];
|
|
826
|
+
});
|
|
827
|
+
});
|
|
828
|
+
};
|
|
829
|
+
/**
|
|
830
|
+
* Constructs the app context. Will inject context in all components
|
|
831
|
+
* except for handlers which are handled in later stage.
|
|
832
|
+
*/
|
|
833
|
+
FlinkApp.prototype.buildContext = function () {
|
|
834
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
835
|
+
var _i, autoRegisteredRepos_1, _a, collectionName, repoInstanceName, Repo, repoInstance, pluginCtx, _b, _c, repo;
|
|
836
|
+
return __generator(this, function (_d) {
|
|
837
|
+
if (this.dbOpts) {
|
|
838
|
+
for (_i = 0, autoRegisteredRepos_1 = exports.autoRegisteredRepos; _i < autoRegisteredRepos_1.length; _i++) {
|
|
839
|
+
_a = autoRegisteredRepos_1[_i], collectionName = _a.collectionName, repoInstanceName = _a.repoInstanceName, Repo = _a.Repo;
|
|
840
|
+
repoInstance = new Repo(collectionName, this.db, this.dbClient);
|
|
841
|
+
this.repos[repoInstanceName] = repoInstance;
|
|
842
|
+
FlinkLog_1.log.info("Registered repo ".concat(repoInstanceName));
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
else if (exports.autoRegisteredRepos.length > 0) {
|
|
846
|
+
FlinkLog_1.log.warn("No db configured but found repo(s)");
|
|
847
|
+
}
|
|
848
|
+
pluginCtx = this.plugins.reduce(function (out, plugin) {
|
|
849
|
+
if (out[plugin.id]) {
|
|
850
|
+
throw new Error("Plugin ".concat(plugin.id, " is already registered"));
|
|
851
|
+
}
|
|
852
|
+
out[plugin.id] = plugin.ctx;
|
|
853
|
+
return out;
|
|
854
|
+
}, {});
|
|
855
|
+
this._ctx = {
|
|
856
|
+
repos: this.repos,
|
|
857
|
+
plugins: pluginCtx,
|
|
858
|
+
auth: this.auth,
|
|
859
|
+
agents: this.agents,
|
|
860
|
+
};
|
|
861
|
+
for (_b = 0, _c = Object.values(this.repos); _b < _c.length; _b++) {
|
|
862
|
+
repo = _c[_b];
|
|
863
|
+
repo.ctx = this.ctx;
|
|
864
|
+
}
|
|
865
|
+
return [2 /*return*/];
|
|
866
|
+
});
|
|
867
|
+
});
|
|
868
|
+
};
|
|
869
|
+
/**
|
|
870
|
+
* Initialize agents after they've been registered and context is ready
|
|
871
|
+
* Must be called after registerAutoRegisterableAgents()
|
|
872
|
+
*/
|
|
873
|
+
FlinkApp.prototype.initializeAgents = function () {
|
|
874
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
875
|
+
var _i, _a, agent;
|
|
876
|
+
return __generator(this, function (_b) {
|
|
877
|
+
// Inject context and initialize agents
|
|
878
|
+
for (_i = 0, _a = Object.values(this.agents); _i < _a.length; _i++) {
|
|
879
|
+
agent = _a[_i];
|
|
880
|
+
agent.ctx = this.ctx;
|
|
881
|
+
agent.__init(this.llmAdapters, this.tools);
|
|
882
|
+
}
|
|
883
|
+
return [2 /*return*/];
|
|
884
|
+
});
|
|
885
|
+
});
|
|
886
|
+
};
|
|
887
|
+
/**
|
|
888
|
+
* Connects to database.
|
|
889
|
+
*/
|
|
890
|
+
FlinkApp.prototype.initDb = function () {
|
|
891
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
892
|
+
var client, err_2;
|
|
893
|
+
return __generator(this, function (_a) {
|
|
894
|
+
switch (_a.label) {
|
|
895
|
+
case 0:
|
|
896
|
+
if (!this.dbOpts) return [3 /*break*/, 6];
|
|
897
|
+
_a.label = 1;
|
|
898
|
+
case 1:
|
|
899
|
+
_a.trys.push([1, 3, , 4]);
|
|
900
|
+
FlinkLog_1.log.debug("Connecting to db");
|
|
901
|
+
return [4 /*yield*/, mongodb_1.MongoClient.connect(this.dbOpts.uri, this.getMongoConnectionOptions())];
|
|
902
|
+
case 2:
|
|
903
|
+
client = _a.sent();
|
|
904
|
+
this.db = client.db();
|
|
905
|
+
this.dbClient = client;
|
|
906
|
+
return [3 /*break*/, 4];
|
|
907
|
+
case 3:
|
|
908
|
+
err_2 = _a.sent();
|
|
909
|
+
FlinkLog_1.log.error("Failed to connect to db: " + err_2);
|
|
910
|
+
process.exit(1);
|
|
911
|
+
return [3 /*break*/, 4];
|
|
912
|
+
case 4:
|
|
913
|
+
if (!this.onDbConnection) return [3 /*break*/, 6];
|
|
914
|
+
return [4 /*yield*/, this.onDbConnection(this.db)];
|
|
915
|
+
case 5:
|
|
916
|
+
_a.sent();
|
|
917
|
+
_a.label = 6;
|
|
918
|
+
case 6: return [2 /*return*/];
|
|
919
|
+
}
|
|
920
|
+
});
|
|
921
|
+
});
|
|
922
|
+
};
|
|
923
|
+
/**
|
|
924
|
+
* Connects plugin to database.
|
|
925
|
+
*/
|
|
926
|
+
FlinkApp.prototype.initPluginDb = function (plugin) {
|
|
927
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
928
|
+
var client, err_3;
|
|
929
|
+
return __generator(this, function (_a) {
|
|
930
|
+
switch (_a.label) {
|
|
931
|
+
case 0:
|
|
932
|
+
if (!plugin.db) {
|
|
933
|
+
return [2 /*return*/];
|
|
934
|
+
}
|
|
935
|
+
if (!plugin.db) return [3 /*break*/, 5];
|
|
936
|
+
if (!plugin.db.useHostDb) return [3 /*break*/, 1];
|
|
937
|
+
if (!this.db) {
|
|
938
|
+
FlinkLog_1.log.error("Plugin '".concat(this.name, " configured to use host app db, but no db exists in FlinkApp'"));
|
|
939
|
+
}
|
|
940
|
+
else {
|
|
941
|
+
return [2 /*return*/, this.db];
|
|
942
|
+
}
|
|
943
|
+
return [3 /*break*/, 5];
|
|
944
|
+
case 1:
|
|
945
|
+
if (!plugin.db.uri) return [3 /*break*/, 5];
|
|
946
|
+
_a.label = 2;
|
|
947
|
+
case 2:
|
|
948
|
+
_a.trys.push([2, 4, , 5]);
|
|
949
|
+
FlinkLog_1.log.debug("Connecting to '".concat(plugin.id, "' db"));
|
|
950
|
+
return [4 /*yield*/, mongodb_1.MongoClient.connect(plugin.db.uri, this.getMongoConnectionOptions())];
|
|
951
|
+
case 3:
|
|
952
|
+
client = _a.sent();
|
|
953
|
+
return [2 /*return*/, client.db()];
|
|
954
|
+
case 4:
|
|
955
|
+
err_3 = _a.sent();
|
|
956
|
+
FlinkLog_1.log.error("Failed to connect to db defined in plugin '".concat(plugin.id, "': ") + err_3);
|
|
957
|
+
return [3 /*break*/, 5];
|
|
958
|
+
case 5: return [2 /*return*/];
|
|
959
|
+
}
|
|
960
|
+
});
|
|
961
|
+
});
|
|
962
|
+
};
|
|
963
|
+
FlinkApp.prototype.authenticate = function (req, permissions) {
|
|
964
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
965
|
+
return __generator(this, function (_a) {
|
|
966
|
+
switch (_a.label) {
|
|
967
|
+
case 0:
|
|
968
|
+
if (!this.auth) {
|
|
969
|
+
throw new Error("Attempting to authenticate request (".concat(req.method, " ").concat(req.path, ") but no authPlugin is set"));
|
|
970
|
+
}
|
|
971
|
+
return [4 /*yield*/, this.auth.authenticateRequest(req, permissions)];
|
|
972
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
973
|
+
}
|
|
974
|
+
});
|
|
975
|
+
});
|
|
976
|
+
};
|
|
977
|
+
FlinkApp.prototype.getRegisteredRoutes = function () {
|
|
978
|
+
return Array.from(this.handlerRouteCache.values());
|
|
979
|
+
};
|
|
980
|
+
Object.defineProperty(FlinkApp.prototype, "isSchedulingEnabled", {
|
|
981
|
+
get: function () {
|
|
982
|
+
var _a;
|
|
983
|
+
return ((_a = this.schedulingOptions) === null || _a === void 0 ? void 0 : _a.enabled) !== false;
|
|
984
|
+
},
|
|
985
|
+
enumerable: false,
|
|
986
|
+
configurable: true
|
|
987
|
+
});
|
|
988
|
+
FlinkApp.prototype.getMongoConnectionOptions = function () {
|
|
989
|
+
if (!this.dbOpts) {
|
|
990
|
+
throw new Error("No db configured");
|
|
991
|
+
}
|
|
992
|
+
var driverVersion = require("mongodb/package.json").version;
|
|
993
|
+
if (driverVersion.startsWith("3")) {
|
|
994
|
+
FlinkLog_1.log.debug("Using legacy mongodb connection options as mongo client is version ".concat(driverVersion));
|
|
995
|
+
return {
|
|
996
|
+
useNewUrlParser: true,
|
|
997
|
+
useUnifiedTopology: true,
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
FlinkLog_1.log.debug("Using modern MongoDB client options (driver version ".concat(driverVersion, ")"));
|
|
1001
|
+
return {
|
|
1002
|
+
serverApi: {
|
|
1003
|
+
version: mongodb_1.ServerApiVersion.v1,
|
|
1004
|
+
strict: process.env.FLINK_MONGO_SERVER_API_DISABLE_STRICT !== "true",
|
|
1005
|
+
deprecationErrors: process.env.FLINK_MONGO_SERVER_API_DISABLE_DEPRECATION !== "true",
|
|
1006
|
+
},
|
|
1007
|
+
connectTimeoutMS: 10 * 1000,
|
|
1008
|
+
};
|
|
1009
|
+
};
|
|
1010
|
+
return FlinkApp;
|
|
1011
|
+
}());
|
|
1012
|
+
exports.FlinkApp = FlinkApp;
|