@flink-app/flink 2.0.0-alpha.82 → 2.0.0-alpha.84
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 +8 -0
- package/cli/cli-utils.ts +1 -0
- package/dist/cli/cli-utils.js +14 -11
- package/dist/src/FlinkApp.d.ts +9 -0
- package/dist/src/FlinkApp.js +60 -31
- package/dist/src/FlinkContext.d.ts +20 -0
- package/dist/src/FlinkService.d.ts +38 -0
- package/dist/src/FlinkService.js +46 -0
- package/dist/src/TypeScriptCompiler.d.ts +6 -0
- package/dist/src/TypeScriptCompiler.js +50 -5
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/utils/loadFlinkConfig.d.ts +2 -0
- package/package.json +1 -1
- package/src/FlinkApp.ts +30 -0
- package/src/FlinkContext.ts +21 -0
- package/src/FlinkService.ts +49 -0
- package/src/TypeScriptCompiler.ts +68 -3
- package/src/index.ts +1 -0
- package/src/utils/loadFlinkConfig.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @flink-app/flink
|
|
2
2
|
|
|
3
|
+
## 2.0.0-alpha.84
|
|
4
|
+
|
|
5
|
+
## 2.0.0-alpha.83
|
|
6
|
+
|
|
7
|
+
### Minor Changes
|
|
8
|
+
|
|
9
|
+
- Add FlinkService as optional business logic layer with auto-discovery, ctx injection, async onInit(), and mockCtx() test helper
|
|
10
|
+
|
|
3
11
|
## 2.0.0-alpha.82
|
|
4
12
|
|
|
5
13
|
## 2.0.0-alpha.81
|
package/cli/cli-utils.ts
CHANGED
|
@@ -116,6 +116,7 @@ export async function compile(opts: CompileOptions): Promise<void> {
|
|
|
116
116
|
await compiler.parseTools();
|
|
117
117
|
await compiler.parseAgents();
|
|
118
118
|
await compiler.parseJobs();
|
|
119
|
+
await compiler.parseServices();
|
|
119
120
|
await compiler.parseAllExtensionDirs();
|
|
120
121
|
await compiler.parseHandlers();
|
|
121
122
|
await compiler.generateStartScript(entry);
|
package/dist/cli/cli-utils.js
CHANGED
|
@@ -125,20 +125,23 @@ function compile(opts) {
|
|
|
125
125
|
return [4 /*yield*/, compiler.parseJobs()];
|
|
126
126
|
case 5:
|
|
127
127
|
_c.sent();
|
|
128
|
-
return [4 /*yield*/, compiler.
|
|
128
|
+
return [4 /*yield*/, compiler.parseServices()];
|
|
129
129
|
case 6:
|
|
130
130
|
_c.sent();
|
|
131
|
-
return [4 /*yield*/, compiler.
|
|
131
|
+
return [4 /*yield*/, compiler.parseAllExtensionDirs()];
|
|
132
132
|
case 7:
|
|
133
133
|
_c.sent();
|
|
134
|
-
return [4 /*yield*/, compiler.
|
|
134
|
+
return [4 /*yield*/, compiler.parseHandlers()];
|
|
135
135
|
case 8:
|
|
136
136
|
_c.sent();
|
|
137
|
-
return [4 /*yield*/, compiler.
|
|
137
|
+
return [4 /*yield*/, compiler.generateStartScript(entry)];
|
|
138
138
|
case 9:
|
|
139
139
|
_c.sent();
|
|
140
|
-
return [4 /*yield*/, compiler.
|
|
140
|
+
return [4 /*yield*/, compiler.generateAllSchemas()];
|
|
141
141
|
case 10:
|
|
142
|
+
_c.sent();
|
|
143
|
+
return [4 /*yield*/, compiler.saveAllModifiedFiles()];
|
|
144
|
+
case 11:
|
|
142
145
|
_c.sent();
|
|
143
146
|
if (typeCheck) {
|
|
144
147
|
stepStart = Date.now();
|
|
@@ -148,14 +151,14 @@ function compile(opts) {
|
|
|
148
151
|
time("Type checking (getPreEmitDiagnostics)", stepStart);
|
|
149
152
|
}
|
|
150
153
|
stepStart = Date.now();
|
|
151
|
-
if (!(process.env.FLINK_USE_TSC === "true")) return [3 /*break*/,
|
|
154
|
+
if (!(process.env.FLINK_USE_TSC === "true")) return [3 /*break*/, 12];
|
|
152
155
|
compiler.emitWithTsc();
|
|
153
|
-
return [3 /*break*/,
|
|
154
|
-
case
|
|
155
|
-
case 12:
|
|
156
|
-
_c.sent();
|
|
157
|
-
_c.label = 13;
|
|
156
|
+
return [3 /*break*/, 14];
|
|
157
|
+
case 12: return [4 /*yield*/, compiler.emit()];
|
|
158
158
|
case 13:
|
|
159
|
+
_c.sent();
|
|
160
|
+
_c.label = 14;
|
|
161
|
+
case 14:
|
|
159
162
|
time("Transpilation (".concat(process.env.FLINK_USE_TSC === "true" ? "tsc" : "swc", ")"), stepStart);
|
|
160
163
|
if (timingLogs) {
|
|
161
164
|
console.log("Compilation done in ".concat(Date.now() - start, "ms"));
|
package/dist/src/FlinkApp.d.ts
CHANGED
|
@@ -55,6 +55,14 @@ export declare const autoRegisteredTools: FlinkToolFile[];
|
|
|
55
55
|
* are picked up by TypeScript compiler
|
|
56
56
|
*/
|
|
57
57
|
export declare const autoRegisteredAgents: FlinkAgentFile<any, any>[];
|
|
58
|
+
/**
|
|
59
|
+
* This will be populated at compile time when the apps services
|
|
60
|
+
* are picked up by TypeScript compiler
|
|
61
|
+
*/
|
|
62
|
+
export declare const autoRegisteredServices: {
|
|
63
|
+
serviceInstanceName: string;
|
|
64
|
+
Service: any;
|
|
65
|
+
}[];
|
|
58
66
|
export interface FlinkOptions {
|
|
59
67
|
/**
|
|
60
68
|
* Name of application, will only show in logs and in HTTP header.
|
|
@@ -280,6 +288,7 @@ export declare class FlinkApp<C extends FlinkContext> {
|
|
|
280
288
|
private expressServer;
|
|
281
289
|
private onError?;
|
|
282
290
|
private repos;
|
|
291
|
+
private services;
|
|
283
292
|
private llmAdapters;
|
|
284
293
|
private tools;
|
|
285
294
|
private agents;
|
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.autoRegisteredAgents = exports.autoRegisteredTools = 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"));
|
|
@@ -110,6 +110,11 @@ exports.autoRegisteredTools = [];
|
|
|
110
110
|
* are picked up by TypeScript compiler
|
|
111
111
|
*/
|
|
112
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 = [];
|
|
113
118
|
var FlinkApp = /** @class */ (function () {
|
|
114
119
|
function FlinkApp(opts) {
|
|
115
120
|
var _a;
|
|
@@ -120,6 +125,7 @@ var FlinkApp = /** @class */ (function () {
|
|
|
120
125
|
this.routingConfigured = false;
|
|
121
126
|
this.disableHttpServer = false;
|
|
122
127
|
this.repos = {};
|
|
128
|
+
this.services = {};
|
|
123
129
|
this.llmAdapters = new Map();
|
|
124
130
|
this.tools = {};
|
|
125
131
|
this.agents = {}; // FlinkAgent<C> instances
|
|
@@ -1123,37 +1129,60 @@ var FlinkApp = /** @class */ (function () {
|
|
|
1123
1129
|
*/
|
|
1124
1130
|
FlinkApp.prototype.buildContext = function () {
|
|
1125
1131
|
return __awaiter(this, void 0, void 0, function () {
|
|
1126
|
-
var _i, autoRegisteredRepos_1, _a, collectionName, repoInstanceName, Repo, repoInstance, pluginCtx, _b, _c, repo;
|
|
1127
|
-
return __generator(this, function (
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1132
|
+
var _i, autoRegisteredRepos_1, _a, collectionName, repoInstanceName, Repo, repoInstance, pluginCtx, _b, autoRegisteredServices_1, _c, serviceInstanceName, Service, serviceInstance, _d, _e, repo, _f, _g, service, servicesWithInit;
|
|
1133
|
+
return __generator(this, function (_h) {
|
|
1134
|
+
switch (_h.label) {
|
|
1135
|
+
case 0:
|
|
1136
|
+
if (this.dbOpts) {
|
|
1137
|
+
for (_i = 0, autoRegisteredRepos_1 = exports.autoRegisteredRepos; _i < autoRegisteredRepos_1.length; _i++) {
|
|
1138
|
+
_a = autoRegisteredRepos_1[_i], collectionName = _a.collectionName, repoInstanceName = _a.repoInstanceName, Repo = _a.Repo;
|
|
1139
|
+
repoInstance = new Repo(collectionName, this.db, this.dbClient);
|
|
1140
|
+
this.repos[repoInstanceName] = repoInstance;
|
|
1141
|
+
initLog.info("Registered repo ".concat(repoInstanceName));
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
else if (exports.autoRegisteredRepos.length > 0) {
|
|
1145
|
+
FlinkLog_1.log.warn("No db configured but found repo(s)");
|
|
1146
|
+
}
|
|
1147
|
+
pluginCtx = this.plugins.reduce(function (out, plugin) {
|
|
1148
|
+
if (out[plugin.id]) {
|
|
1149
|
+
throw new Error("Plugin ".concat(plugin.id, " is already registered"));
|
|
1150
|
+
}
|
|
1151
|
+
out[plugin.id] = plugin.ctx;
|
|
1152
|
+
return out;
|
|
1153
|
+
}, {});
|
|
1154
|
+
// Instantiate services (ctx not yet available - constructors must not access it)
|
|
1155
|
+
for (_b = 0, autoRegisteredServices_1 = exports.autoRegisteredServices; _b < autoRegisteredServices_1.length; _b++) {
|
|
1156
|
+
_c = autoRegisteredServices_1[_b], serviceInstanceName = _c.serviceInstanceName, Service = _c.Service;
|
|
1157
|
+
serviceInstance = new Service();
|
|
1158
|
+
this.services[serviceInstanceName] = serviceInstance;
|
|
1159
|
+
initLog.info("Registered service ".concat(serviceInstanceName));
|
|
1160
|
+
}
|
|
1161
|
+
this._ctx = {
|
|
1162
|
+
repos: this.repos,
|
|
1163
|
+
plugins: pluginCtx,
|
|
1164
|
+
auth: this.auth,
|
|
1165
|
+
agents: this.agents,
|
|
1166
|
+
services: this.services,
|
|
1167
|
+
};
|
|
1168
|
+
// Inject context into repos
|
|
1169
|
+
for (_d = 0, _e = Object.values(this.repos); _d < _e.length; _d++) {
|
|
1170
|
+
repo = _e[_d];
|
|
1171
|
+
repo.ctx = this.ctx;
|
|
1172
|
+
}
|
|
1173
|
+
// Inject context into services, then call onInit() in parallel
|
|
1174
|
+
for (_f = 0, _g = Object.values(this.services); _f < _g.length; _f++) {
|
|
1175
|
+
service = _g[_f];
|
|
1176
|
+
service.ctx = this.ctx;
|
|
1177
|
+
}
|
|
1178
|
+
servicesWithInit = Object.values(this.services).filter(function (s) { return typeof s.onInit === "function"; });
|
|
1179
|
+
if (!(servicesWithInit.length > 0)) return [3 /*break*/, 2];
|
|
1180
|
+
return [4 /*yield*/, Promise.all(servicesWithInit.map(function (s) { return s.onInit(); }))];
|
|
1181
|
+
case 1:
|
|
1182
|
+
_h.sent();
|
|
1183
|
+
_h.label = 2;
|
|
1184
|
+
case 2: return [2 /*return*/];
|
|
1155
1185
|
}
|
|
1156
|
-
return [2 /*return*/];
|
|
1157
1186
|
});
|
|
1158
1187
|
});
|
|
1159
1188
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FlinkAuthPlugin } from "./auth/FlinkAuthPlugin";
|
|
2
2
|
import { FlinkRepo } from "./FlinkRepo";
|
|
3
3
|
import { FlinkAgent } from "./ai/FlinkAgent";
|
|
4
|
+
import { FlinkService } from "./FlinkService";
|
|
4
5
|
export interface FlinkContext<P = any> {
|
|
5
6
|
repos: {
|
|
6
7
|
[x: string]: FlinkRepo<any, any>;
|
|
@@ -30,4 +31,23 @@ export interface FlinkContext<P = any> {
|
|
|
30
31
|
agents?: {
|
|
31
32
|
[x: string]: FlinkAgent<any>;
|
|
32
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* Optional services namespace for shared business logic.
|
|
36
|
+
*
|
|
37
|
+
* Define services directly in your context interface:
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* interface AppCtx extends FlinkContext<PluginCtx> {
|
|
41
|
+
* repos: {
|
|
42
|
+
* carRepo: CarRepo;
|
|
43
|
+
* };
|
|
44
|
+
* services: {
|
|
45
|
+
* carService: CarService;
|
|
46
|
+
* orderService: OrderService;
|
|
47
|
+
* };
|
|
48
|
+
* }
|
|
49
|
+
*/
|
|
50
|
+
services?: {
|
|
51
|
+
[x: string]: FlinkService<any>;
|
|
52
|
+
};
|
|
33
53
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { FlinkContext } from "./FlinkContext";
|
|
2
|
+
/**
|
|
3
|
+
* Base class for Flink services - optional business logic layer.
|
|
4
|
+
*
|
|
5
|
+
* Services provide a place for shared business logic that can be used by
|
|
6
|
+
* handlers, jobs, agents, and other services via `ctx.services`.
|
|
7
|
+
*
|
|
8
|
+
* Context (`this.ctx`) is not available in the constructor - use `onInit()`
|
|
9
|
+
* for any setup that requires context.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* class CarService extends FlinkService<AppCtx> {
|
|
14
|
+
* async onInit() {
|
|
15
|
+
* // Called after all services are instantiated and ctx is fully wired
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* async getWithOwner(carId: string) {
|
|
19
|
+
* const car = await this.ctx.repos.carRepo.getById(carId);
|
|
20
|
+
* if (!car) throw notFound("Car not found");
|
|
21
|
+
* return car;
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare abstract class FlinkService<C extends FlinkContext> {
|
|
27
|
+
private _ctx?;
|
|
28
|
+
set ctx(ctx: FlinkContext);
|
|
29
|
+
get ctx(): C;
|
|
30
|
+
/**
|
|
31
|
+
* Optional async initialization hook called after all services are
|
|
32
|
+
* instantiated and ctx is fully wired (repos, plugins, agents, services all available).
|
|
33
|
+
*
|
|
34
|
+
* All service onInit() methods run in parallel via Promise.all.
|
|
35
|
+
* Do not depend on another service's onInit() having completed.
|
|
36
|
+
*/
|
|
37
|
+
onInit?(): Promise<void>;
|
|
38
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FlinkService = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Base class for Flink services - optional business logic layer.
|
|
6
|
+
*
|
|
7
|
+
* Services provide a place for shared business logic that can be used by
|
|
8
|
+
* handlers, jobs, agents, and other services via `ctx.services`.
|
|
9
|
+
*
|
|
10
|
+
* Context (`this.ctx`) is not available in the constructor - use `onInit()`
|
|
11
|
+
* for any setup that requires context.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* class CarService extends FlinkService<AppCtx> {
|
|
16
|
+
* async onInit() {
|
|
17
|
+
* // Called after all services are instantiated and ctx is fully wired
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* async getWithOwner(carId: string) {
|
|
21
|
+
* const car = await this.ctx.repos.carRepo.getById(carId);
|
|
22
|
+
* if (!car) throw notFound("Car not found");
|
|
23
|
+
* return car;
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
var FlinkService = /** @class */ (function () {
|
|
29
|
+
function FlinkService() {
|
|
30
|
+
}
|
|
31
|
+
Object.defineProperty(FlinkService.prototype, "ctx", {
|
|
32
|
+
get: function () {
|
|
33
|
+
if (!this._ctx) {
|
|
34
|
+
throw new Error("FlinkService: ctx is not available in constructor. Use onInit() for setup logic.");
|
|
35
|
+
}
|
|
36
|
+
return this._ctx;
|
|
37
|
+
},
|
|
38
|
+
set: function (ctx) {
|
|
39
|
+
this._ctx = ctx;
|
|
40
|
+
},
|
|
41
|
+
enumerable: false,
|
|
42
|
+
configurable: true
|
|
43
|
+
});
|
|
44
|
+
return FlinkService;
|
|
45
|
+
}());
|
|
46
|
+
exports.FlinkService = FlinkService;
|
|
@@ -21,6 +21,7 @@ declare class TypeScriptCompiler {
|
|
|
21
21
|
private toolFiles;
|
|
22
22
|
private agentFiles;
|
|
23
23
|
private jobFiles;
|
|
24
|
+
private serviceFiles;
|
|
24
25
|
/**
|
|
25
26
|
* Tool ID registry for agent validation (built during segmentation)
|
|
26
27
|
*/
|
|
@@ -29,6 +30,7 @@ declare class TypeScriptCompiler {
|
|
|
29
30
|
* Compiler plugins loaded from flink.config.js
|
|
30
31
|
*/
|
|
31
32
|
private compilerPlugins;
|
|
33
|
+
private disableServices;
|
|
32
34
|
/**
|
|
33
35
|
* Extension files collected during segmentation, keyed by generatedFile name
|
|
34
36
|
*/
|
|
@@ -215,6 +217,10 @@ declare class TypeScriptCompiler {
|
|
|
215
217
|
* Scans project for jobs so they can be registered during start.
|
|
216
218
|
*/
|
|
217
219
|
parseJobs(): Promise<SourceFile>;
|
|
220
|
+
/**
|
|
221
|
+
* Scans project for services so they can be registered during start.
|
|
222
|
+
*/
|
|
223
|
+
parseServices(): Promise<SourceFile>;
|
|
218
224
|
/**
|
|
219
225
|
* Generates a .flink/generatedXxx.ts file for a single compiler plugin extension.
|
|
220
226
|
* Mirrors the same namespace-import + spread pattern used by parseJobs.
|
|
@@ -95,7 +95,7 @@ var perfLog = FlinkLogFactory_1.FlinkLogFactory.createLogger("flink.perf");
|
|
|
95
95
|
var initLog = FlinkLogFactory_1.FlinkLogFactory.createLogger("flink.init");
|
|
96
96
|
var TypeScriptCompiler = /** @class */ (function () {
|
|
97
97
|
function TypeScriptCompiler(cwd) {
|
|
98
|
-
var _a, _b, _c;
|
|
98
|
+
var _a, _b, _c, _d;
|
|
99
99
|
/**
|
|
100
100
|
* Handler schemas collected during parseHandlers, to be generated later
|
|
101
101
|
*/
|
|
@@ -112,6 +112,7 @@ var TypeScriptCompiler = /** @class */ (function () {
|
|
|
112
112
|
this.toolFiles = [];
|
|
113
113
|
this.agentFiles = [];
|
|
114
114
|
this.jobFiles = [];
|
|
115
|
+
this.serviceFiles = [];
|
|
115
116
|
/**
|
|
116
117
|
* Tool ID registry for agent validation (built during segmentation)
|
|
117
118
|
*/
|
|
@@ -120,6 +121,7 @@ var TypeScriptCompiler = /** @class */ (function () {
|
|
|
120
121
|
* Compiler plugins loaded from flink.config.js
|
|
121
122
|
*/
|
|
122
123
|
this.compilerPlugins = [];
|
|
124
|
+
this.disableServices = false;
|
|
123
125
|
/**
|
|
124
126
|
* Extension files collected during segmentation, keyed by generatedFile name
|
|
125
127
|
*/
|
|
@@ -198,9 +200,10 @@ var TypeScriptCompiler = /** @class */ (function () {
|
|
|
198
200
|
var loadTime = Date.now() - loadStartTime;
|
|
199
201
|
var fileCount = this.project.getSourceFiles().length;
|
|
200
202
|
perfLog.debug("\u2713 All source files loaded in ".concat(loadTime, "ms (").concat(fileCount, " files)"));
|
|
201
|
-
// Load
|
|
203
|
+
// Load config from flink.config.js
|
|
202
204
|
var flinkCfg = (0, loadFlinkConfig_1.loadFlinkConfig)(this.cwd);
|
|
203
205
|
this.compilerPlugins = (_c = flinkCfg === null || flinkCfg === void 0 ? void 0 : flinkCfg.compilerPlugins) !== null && _c !== void 0 ? _c : [];
|
|
206
|
+
this.disableServices = (_d = flinkCfg === null || flinkCfg === void 0 ? void 0 : flinkCfg.disableServices) !== null && _d !== void 0 ? _d : false;
|
|
204
207
|
if (this.compilerPlugins.length > 0) {
|
|
205
208
|
initLog.info("Compiler plugins loaded (".concat(this.compilerPlugins.length, "):"), this.compilerPlugins.map(function (p) { return "".concat(p.package, " \u2192 ").concat(p.scanDir); }).join(", "));
|
|
206
209
|
}
|
|
@@ -444,7 +447,7 @@ var TypeScriptCompiler = /** @class */ (function () {
|
|
|
444
447
|
var _a;
|
|
445
448
|
var startTime = Date.now();
|
|
446
449
|
var allFiles = this.project.getSourceFiles();
|
|
447
|
-
var handlerTime = 0, repoTime = 0, toolTime = 0, agentTime = 0, jobTime = 0;
|
|
450
|
+
var handlerTime = 0, repoTime = 0, toolTime = 0, agentTime = 0, jobTime = 0, serviceTime = 0;
|
|
448
451
|
for (var _i = 0, allFiles_1 = allFiles; _i < allFiles_1.length; _i++) {
|
|
449
452
|
var sf = allFiles_1[_i];
|
|
450
453
|
var filePath = sf.getFilePath();
|
|
@@ -491,6 +494,12 @@ var TypeScriptCompiler = /** @class */ (function () {
|
|
|
491
494
|
jobTime += Date.now() - fileStartTime;
|
|
492
495
|
continue;
|
|
493
496
|
}
|
|
497
|
+
// Services: simple path check (opt-out via flink.config.js disableServices)
|
|
498
|
+
if (!this.disableServices && filePath.includes("src/services/")) {
|
|
499
|
+
this.serviceFiles.push(sf);
|
|
500
|
+
serviceTime += Date.now() - fileStartTime;
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
494
503
|
// Extension dirs from compiler plugins
|
|
495
504
|
for (var _b = 0, _c = this.compilerPlugins; _b < _c.length; _b++) {
|
|
496
505
|
var ext = _c[_b];
|
|
@@ -506,7 +515,7 @@ var TypeScriptCompiler = /** @class */ (function () {
|
|
|
506
515
|
}
|
|
507
516
|
var segmentTime = Date.now() - startTime;
|
|
508
517
|
perfLog.debug("\u2713 File segmentation completed in ".concat(segmentTime, "ms ") +
|
|
509
|
-
"(".concat(this.handlerFiles.length, " handlers, ").concat(this.repoFiles.length, " repos, ").concat(this.toolFiles.length, " tools, ").concat(this.agentFiles.length, " agents, ").concat(this.jobFiles.length, " jobs)"));
|
|
518
|
+
"(".concat(this.handlerFiles.length, " handlers, ").concat(this.repoFiles.length, " repos, ").concat(this.toolFiles.length, " tools, ").concat(this.agentFiles.length, " agents, ").concat(this.jobFiles.length, " jobs, ").concat(this.serviceFiles.length, " services)"));
|
|
510
519
|
};
|
|
511
520
|
/**
|
|
512
521
|
* Detects if the project is using ESM (ECMAScript Modules)
|
|
@@ -1172,7 +1181,7 @@ var TypeScriptCompiler = /** @class */ (function () {
|
|
|
1172
1181
|
extensionImports = this.compilerPlugins
|
|
1173
1182
|
.map(function (ext) { return "import \"./".concat(ext.generatedFile).concat(_this.isEsm ? ".js" : "", "\";"); })
|
|
1174
1183
|
.join("\n");
|
|
1175
|
-
sf = this.createSourceFile(["start.ts"], "// Generated ".concat(new Date(), "\nimport \"./generatedHandlers").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedRepos").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedTools").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedAgents").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedJobs").concat(this.isEsm ? ".js" : "", "\";\n").concat(extensionImports ? extensionImports + "\n" : "", "import \"..").concat(appEntryScript.replace(/\.ts/g, "")).concat(this.isEsm ? ".js" : "", "\";\nexport default {}; // Export an empty object to make it a module\n"));
|
|
1184
|
+
sf = this.createSourceFile(["start.ts"], "// Generated ".concat(new Date(), "\nimport \"./generatedHandlers").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedRepos").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedTools").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedAgents").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedJobs").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedServices").concat(this.isEsm ? ".js" : "", "\";\n").concat(extensionImports ? extensionImports + "\n" : "", "import \"..").concat(appEntryScript.replace(/\.ts/g, "")).concat(this.isEsm ? ".js" : "", "\";\nexport default {}; // Export an empty object to make it a module\n"));
|
|
1176
1185
|
// Defer save until batch save at end (performance optimization)
|
|
1177
1186
|
return [2 /*return*/, sf];
|
|
1178
1187
|
}
|
|
@@ -1518,6 +1527,42 @@ var TypeScriptCompiler = /** @class */ (function () {
|
|
|
1518
1527
|
});
|
|
1519
1528
|
});
|
|
1520
1529
|
};
|
|
1530
|
+
/**
|
|
1531
|
+
* Scans project for services so they can be registered during start.
|
|
1532
|
+
*/
|
|
1533
|
+
TypeScriptCompiler.prototype.parseServices = function () {
|
|
1534
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1535
|
+
var startTime, generatedFile, servicesArr, imports, serviceElements, _i, _a, sf, serviceParseTime;
|
|
1536
|
+
return __generator(this, function (_b) {
|
|
1537
|
+
if (this.disableServices) {
|
|
1538
|
+
initLog.info("Services disabled via flink.config.js (disableServices: true)");
|
|
1539
|
+
}
|
|
1540
|
+
startTime = Date.now();
|
|
1541
|
+
generatedFile = this.createSourceFile(["generatedServices.ts"], "// Generated ".concat(new Date(), "\nimport { autoRegisteredServices } from \"@flink-app/flink\";\nexport const services: any[] = [];\nautoRegisteredServices.push(...services);\n "));
|
|
1542
|
+
servicesArr = generatedFile.getVariableDeclarationOrThrow("services").getFirstDescendantByKindOrThrow(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
|
|
1543
|
+
imports = [];
|
|
1544
|
+
serviceElements = [];
|
|
1545
|
+
for (_i = 0, _a = this.serviceFiles; _i < _a.length; _i++) {
|
|
1546
|
+
sf = _a[_i];
|
|
1547
|
+
console.log("Detected service ".concat(sf.getBaseName()));
|
|
1548
|
+
imports.push({
|
|
1549
|
+
defaultImport: sf.getBaseNameWithoutExtension(),
|
|
1550
|
+
moduleSpecifier: this.getModuleSpecifier(generatedFile, sf),
|
|
1551
|
+
});
|
|
1552
|
+
serviceElements.push("{serviceInstanceName: \"".concat((0, utils_1.getRepoInstanceName)(sf.getBaseName()), "\", Service: ").concat(sf.getBaseNameWithoutExtension(), "}"));
|
|
1553
|
+
}
|
|
1554
|
+
servicesArr.addElements(serviceElements);
|
|
1555
|
+
generatedFile.addImportDeclarations(imports);
|
|
1556
|
+
// Cleanup: forget service file nodes to reduce memory overhead
|
|
1557
|
+
this.serviceFiles.forEach(function (sf) {
|
|
1558
|
+
sf.getClasses().forEach(function (cls) { return cls.forget(); });
|
|
1559
|
+
});
|
|
1560
|
+
serviceParseTime = Date.now() - startTime;
|
|
1561
|
+
perfLog.info("\u2713 Service parsing completed in ".concat(serviceParseTime, "ms (").concat(serviceElements.length, " services)"));
|
|
1562
|
+
return [2 /*return*/, generatedFile];
|
|
1563
|
+
});
|
|
1564
|
+
});
|
|
1565
|
+
};
|
|
1521
1566
|
/**
|
|
1522
1567
|
* Generates a .flink/generatedXxx.ts file for a single compiler plugin extension.
|
|
1523
1568
|
* Mirrors the same namespace-import + spread pattern used by parseJobs.
|
package/dist/src/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from "./FlinkRequestContext";
|
|
|
10
10
|
export * from "./FlinkErrors";
|
|
11
11
|
export * from "./FlinkPlugin";
|
|
12
12
|
export * from "./FlinkJob";
|
|
13
|
+
export * from "./FlinkService";
|
|
13
14
|
export { LeaderElection } from "./LeaderElection";
|
|
14
15
|
export type { LeaderElectionOptions } from "./LeaderElection";
|
|
15
16
|
export * from "./auth/FlinkAuthUser";
|
package/dist/src/index.js
CHANGED
|
@@ -27,6 +27,7 @@ __exportStar(require("./FlinkRequestContext"), exports);
|
|
|
27
27
|
__exportStar(require("./FlinkErrors"), exports);
|
|
28
28
|
__exportStar(require("./FlinkPlugin"), exports);
|
|
29
29
|
__exportStar(require("./FlinkJob"), exports);
|
|
30
|
+
__exportStar(require("./FlinkService"), exports);
|
|
30
31
|
var LeaderElection_1 = require("./LeaderElection");
|
|
31
32
|
Object.defineProperty(exports, "LeaderElection", { enumerable: true, get: function () { return LeaderElection_1.LeaderElection; } });
|
|
32
33
|
__exportStar(require("./auth/FlinkAuthUser"), exports);
|
|
@@ -26,6 +26,8 @@ export interface FlinkConfig {
|
|
|
26
26
|
logging?: LoggingConfig;
|
|
27
27
|
/** Compiler plugins that extend auto-discovery to custom directories */
|
|
28
28
|
compilerPlugins?: FlinkCompilerPlugin[];
|
|
29
|
+
/** Disable auto-discovery of services from src/services/. Default: false */
|
|
30
|
+
disableServices?: boolean;
|
|
29
31
|
}
|
|
30
32
|
/**
|
|
31
33
|
* Load Flink configuration from flink.config.js in current working directory
|
package/package.json
CHANGED
package/src/FlinkApp.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { log } from "./FlinkLog";
|
|
|
23
23
|
import { FlinkLogFactory } from "./FlinkLogFactory";
|
|
24
24
|
import { FlinkPlugin } from "./FlinkPlugin";
|
|
25
25
|
import { FlinkRepo } from "./FlinkRepo";
|
|
26
|
+
import { FlinkService } from "./FlinkService";
|
|
26
27
|
import { FlinkResponse } from "./FlinkResponse";
|
|
27
28
|
import { requestContext } from "./FlinkRequestContext";
|
|
28
29
|
import { StreamWriterFactory } from "./handlers/StreamWriterFactory";
|
|
@@ -88,6 +89,15 @@ export const autoRegisteredTools: FlinkToolFile[] = [];
|
|
|
88
89
|
*/
|
|
89
90
|
export const autoRegisteredAgents: FlinkAgentFile<any, any>[] = [];
|
|
90
91
|
|
|
92
|
+
/**
|
|
93
|
+
* This will be populated at compile time when the apps services
|
|
94
|
+
* are picked up by TypeScript compiler
|
|
95
|
+
*/
|
|
96
|
+
export const autoRegisteredServices: {
|
|
97
|
+
serviceInstanceName: string;
|
|
98
|
+
Service: any;
|
|
99
|
+
}[] = [];
|
|
100
|
+
|
|
91
101
|
export interface FlinkOptions {
|
|
92
102
|
/**
|
|
93
103
|
* Name of application, will only show in logs and in HTTP header.
|
|
@@ -338,6 +348,7 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
338
348
|
private onError?: FlinkOptions["onError"];
|
|
339
349
|
|
|
340
350
|
private repos: { [x: string]: FlinkRepo<C, any> } = {};
|
|
351
|
+
private services: { [x: string]: FlinkService<C> } = {};
|
|
341
352
|
|
|
342
353
|
private llmAdapters: Map<string, LLMAdapter> = new Map();
|
|
343
354
|
private tools: { [x: string]: ToolExecutor<C> } = {};
|
|
@@ -1443,16 +1454,35 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
1443
1454
|
return out;
|
|
1444
1455
|
}, {});
|
|
1445
1456
|
|
|
1457
|
+
// Instantiate services (ctx not yet available - constructors must not access it)
|
|
1458
|
+
for (const { serviceInstanceName, Service } of autoRegisteredServices) {
|
|
1459
|
+
const serviceInstance: FlinkService<C> = new Service();
|
|
1460
|
+
this.services[serviceInstanceName] = serviceInstance;
|
|
1461
|
+
initLog.info(`Registered service ${serviceInstanceName}`);
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1446
1464
|
this._ctx = {
|
|
1447
1465
|
repos: this.repos,
|
|
1448
1466
|
plugins: pluginCtx,
|
|
1449
1467
|
auth: this.auth,
|
|
1450
1468
|
agents: this.agents,
|
|
1469
|
+
services: this.services,
|
|
1451
1470
|
} as C;
|
|
1452
1471
|
|
|
1472
|
+
// Inject context into repos
|
|
1453
1473
|
for (const repo of Object.values(this.repos)) {
|
|
1454
1474
|
repo.ctx = this.ctx;
|
|
1455
1475
|
}
|
|
1476
|
+
|
|
1477
|
+
// Inject context into services, then call onInit() in parallel
|
|
1478
|
+
for (const service of Object.values(this.services)) {
|
|
1479
|
+
service.ctx = this.ctx;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
const servicesWithInit = Object.values(this.services).filter((s) => typeof s.onInit === "function");
|
|
1483
|
+
if (servicesWithInit.length > 0) {
|
|
1484
|
+
await Promise.all(servicesWithInit.map((s) => s.onInit!()));
|
|
1485
|
+
}
|
|
1456
1486
|
}
|
|
1457
1487
|
|
|
1458
1488
|
/**
|
package/src/FlinkContext.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FlinkAuthPlugin } from "./auth/FlinkAuthPlugin";
|
|
2
2
|
import { FlinkRepo } from "./FlinkRepo";
|
|
3
3
|
import { FlinkAgent } from "./ai/FlinkAgent";
|
|
4
|
+
import { FlinkService } from "./FlinkService";
|
|
4
5
|
|
|
5
6
|
export interface FlinkContext<P = any> {
|
|
6
7
|
repos: {
|
|
@@ -34,4 +35,24 @@ export interface FlinkContext<P = any> {
|
|
|
34
35
|
agents?: {
|
|
35
36
|
[x: string]: FlinkAgent<any>;
|
|
36
37
|
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Optional services namespace for shared business logic.
|
|
41
|
+
*
|
|
42
|
+
* Define services directly in your context interface:
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* interface AppCtx extends FlinkContext<PluginCtx> {
|
|
46
|
+
* repos: {
|
|
47
|
+
* carRepo: CarRepo;
|
|
48
|
+
* };
|
|
49
|
+
* services: {
|
|
50
|
+
* carService: CarService;
|
|
51
|
+
* orderService: OrderService;
|
|
52
|
+
* };
|
|
53
|
+
* }
|
|
54
|
+
*/
|
|
55
|
+
services?: {
|
|
56
|
+
[x: string]: FlinkService<any>;
|
|
57
|
+
};
|
|
37
58
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { FlinkContext } from "./FlinkContext";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base class for Flink services - optional business logic layer.
|
|
5
|
+
*
|
|
6
|
+
* Services provide a place for shared business logic that can be used by
|
|
7
|
+
* handlers, jobs, agents, and other services via `ctx.services`.
|
|
8
|
+
*
|
|
9
|
+
* Context (`this.ctx`) is not available in the constructor - use `onInit()`
|
|
10
|
+
* for any setup that requires context.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* class CarService extends FlinkService<AppCtx> {
|
|
15
|
+
* async onInit() {
|
|
16
|
+
* // Called after all services are instantiated and ctx is fully wired
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* async getWithOwner(carId: string) {
|
|
20
|
+
* const car = await this.ctx.repos.carRepo.getById(carId);
|
|
21
|
+
* if (!car) throw notFound("Car not found");
|
|
22
|
+
* return car;
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export abstract class FlinkService<C extends FlinkContext> {
|
|
28
|
+
private _ctx?: C;
|
|
29
|
+
|
|
30
|
+
set ctx(ctx: FlinkContext) {
|
|
31
|
+
this._ctx = ctx as C;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get ctx(): C {
|
|
35
|
+
if (!this._ctx) {
|
|
36
|
+
throw new Error("FlinkService: ctx is not available in constructor. Use onInit() for setup logic.");
|
|
37
|
+
}
|
|
38
|
+
return this._ctx;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Optional async initialization hook called after all services are
|
|
43
|
+
* instantiated and ctx is fully wired (repos, plugins, agents, services all available).
|
|
44
|
+
*
|
|
45
|
+
* All service onInit() methods run in parallel via Promise.all.
|
|
46
|
+
* Do not depend on another service's onInit() having completed.
|
|
47
|
+
*/
|
|
48
|
+
onInit?(): Promise<void>;
|
|
49
|
+
}
|
|
@@ -45,6 +45,7 @@ class TypeScriptCompiler {
|
|
|
45
45
|
private toolFiles: SourceFile[] = [];
|
|
46
46
|
private agentFiles: SourceFile[] = [];
|
|
47
47
|
private jobFiles: SourceFile[] = [];
|
|
48
|
+
private serviceFiles: SourceFile[] = [];
|
|
48
49
|
|
|
49
50
|
/**
|
|
50
51
|
* Tool ID registry for agent validation (built during segmentation)
|
|
@@ -55,6 +56,7 @@ class TypeScriptCompiler {
|
|
|
55
56
|
* Compiler plugins loaded from flink.config.js
|
|
56
57
|
*/
|
|
57
58
|
private compilerPlugins: FlinkCompilerPlugin[] = [];
|
|
59
|
+
private disableServices = false;
|
|
58
60
|
|
|
59
61
|
/**
|
|
60
62
|
* Extension files collected during segmentation, keyed by generatedFile name
|
|
@@ -227,9 +229,10 @@ class TypeScriptCompiler {
|
|
|
227
229
|
const fileCount = this.project.getSourceFiles().length;
|
|
228
230
|
perfLog.debug(`✓ All source files loaded in ${loadTime}ms (${fileCount} files)`);
|
|
229
231
|
|
|
230
|
-
// Load
|
|
232
|
+
// Load config from flink.config.js
|
|
231
233
|
const flinkCfg = loadFlinkConfig(this.cwd);
|
|
232
234
|
this.compilerPlugins = flinkCfg?.compilerPlugins ?? [];
|
|
235
|
+
this.disableServices = flinkCfg?.disableServices ?? false;
|
|
233
236
|
|
|
234
237
|
if (this.compilerPlugins.length > 0) {
|
|
235
238
|
initLog.info(
|
|
@@ -429,7 +432,8 @@ class TypeScriptCompiler {
|
|
|
429
432
|
repoTime = 0,
|
|
430
433
|
toolTime = 0,
|
|
431
434
|
agentTime = 0,
|
|
432
|
-
jobTime = 0
|
|
435
|
+
jobTime = 0,
|
|
436
|
+
serviceTime = 0;
|
|
433
437
|
|
|
434
438
|
for (const sf of allFiles) {
|
|
435
439
|
const filePath = sf.getFilePath();
|
|
@@ -484,6 +488,13 @@ class TypeScriptCompiler {
|
|
|
484
488
|
continue;
|
|
485
489
|
}
|
|
486
490
|
|
|
491
|
+
// Services: simple path check (opt-out via flink.config.js disableServices)
|
|
492
|
+
if (!this.disableServices && filePath.includes("src/services/")) {
|
|
493
|
+
this.serviceFiles.push(sf);
|
|
494
|
+
serviceTime += Date.now() - fileStartTime;
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
|
|
487
498
|
// Extension dirs from compiler plugins
|
|
488
499
|
for (const ext of this.compilerPlugins) {
|
|
489
500
|
if (filePath.includes(ext.scanDir)) {
|
|
@@ -500,7 +511,7 @@ class TypeScriptCompiler {
|
|
|
500
511
|
const segmentTime = Date.now() - startTime;
|
|
501
512
|
perfLog.debug(
|
|
502
513
|
`✓ File segmentation completed in ${segmentTime}ms ` +
|
|
503
|
-
`(${this.handlerFiles.length} handlers, ${this.repoFiles.length} repos, ${this.toolFiles.length} tools, ${this.agentFiles.length} agents, ${this.jobFiles.length} jobs)`
|
|
514
|
+
`(${this.handlerFiles.length} handlers, ${this.repoFiles.length} repos, ${this.toolFiles.length} tools, ${this.agentFiles.length} agents, ${this.jobFiles.length} jobs, ${this.serviceFiles.length} services)`
|
|
504
515
|
);
|
|
505
516
|
}
|
|
506
517
|
|
|
@@ -1205,6 +1216,7 @@ import "./generatedRepos${this.isEsm ? ".js" : ""}";
|
|
|
1205
1216
|
import "./generatedTools${this.isEsm ? ".js" : ""}";
|
|
1206
1217
|
import "./generatedAgents${this.isEsm ? ".js" : ""}";
|
|
1207
1218
|
import "./generatedJobs${this.isEsm ? ".js" : ""}";
|
|
1219
|
+
import "./generatedServices${this.isEsm ? ".js" : ""}";
|
|
1208
1220
|
${extensionImports ? extensionImports + "\n" : ""}import "..${appEntryScript.replace(/\.ts/g, "")}${this.isEsm ? ".js" : ""}";
|
|
1209
1221
|
export default {}; // Export an empty object to make it a module
|
|
1210
1222
|
`
|
|
@@ -1604,6 +1616,59 @@ autoRegisteredJobs.push(...jobs);
|
|
|
1604
1616
|
return generatedFile;
|
|
1605
1617
|
}
|
|
1606
1618
|
|
|
1619
|
+
/**
|
|
1620
|
+
* Scans project for services so they can be registered during start.
|
|
1621
|
+
*/
|
|
1622
|
+
async parseServices() {
|
|
1623
|
+
if (this.disableServices) {
|
|
1624
|
+
initLog.info("Services disabled via flink.config.js (disableServices: true)");
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
const startTime = Date.now();
|
|
1628
|
+
|
|
1629
|
+
const generatedFile = this.createSourceFile(
|
|
1630
|
+
["generatedServices.ts"],
|
|
1631
|
+
`// Generated ${new Date()}
|
|
1632
|
+
import { autoRegisteredServices } from "@flink-app/flink";
|
|
1633
|
+
export const services: any[] = [];
|
|
1634
|
+
autoRegisteredServices.push(...services);
|
|
1635
|
+
`
|
|
1636
|
+
);
|
|
1637
|
+
|
|
1638
|
+
const servicesArr = generatedFile.getVariableDeclarationOrThrow("services").getFirstDescendantByKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
|
1639
|
+
|
|
1640
|
+
const imports: OptionalKind<ImportDeclarationStructure>[] = [];
|
|
1641
|
+
|
|
1642
|
+
const serviceElements: string[] = [];
|
|
1643
|
+
|
|
1644
|
+
for (const sf of this.serviceFiles) {
|
|
1645
|
+
console.log(`Detected service ${sf.getBaseName()}`);
|
|
1646
|
+
|
|
1647
|
+
imports.push({
|
|
1648
|
+
defaultImport: sf.getBaseNameWithoutExtension(),
|
|
1649
|
+
moduleSpecifier: this.getModuleSpecifier(generatedFile, sf),
|
|
1650
|
+
});
|
|
1651
|
+
|
|
1652
|
+
serviceElements.push(
|
|
1653
|
+
`{serviceInstanceName: "${getRepoInstanceName(sf.getBaseName())}", Service: ${sf.getBaseNameWithoutExtension()}}`
|
|
1654
|
+
);
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
servicesArr.addElements(serviceElements);
|
|
1658
|
+
|
|
1659
|
+
generatedFile.addImportDeclarations(imports);
|
|
1660
|
+
|
|
1661
|
+
// Cleanup: forget service file nodes to reduce memory overhead
|
|
1662
|
+
this.serviceFiles.forEach((sf) => {
|
|
1663
|
+
sf.getClasses().forEach((cls) => cls.forget());
|
|
1664
|
+
});
|
|
1665
|
+
|
|
1666
|
+
const serviceParseTime = Date.now() - startTime;
|
|
1667
|
+
perfLog.info(`✓ Service parsing completed in ${serviceParseTime}ms (${serviceElements.length} services)`);
|
|
1668
|
+
|
|
1669
|
+
return generatedFile;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1607
1672
|
/**
|
|
1608
1673
|
* Generates a .flink/generatedXxx.ts file for a single compiler plugin extension.
|
|
1609
1674
|
* Mirrors the same namespace-import + spread pattern used by parseJobs.
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from "./FlinkRequestContext";
|
|
|
10
10
|
export * from "./FlinkErrors";
|
|
11
11
|
export * from "./FlinkPlugin";
|
|
12
12
|
export * from "./FlinkJob";
|
|
13
|
+
export * from "./FlinkService";
|
|
13
14
|
export { LeaderElection } from "./LeaderElection";
|
|
14
15
|
export type { LeaderElectionOptions } from "./LeaderElection";
|
|
15
16
|
export * from "./auth/FlinkAuthUser";
|
|
@@ -30,6 +30,8 @@ export interface FlinkConfig {
|
|
|
30
30
|
logging?: LoggingConfig;
|
|
31
31
|
/** Compiler plugins that extend auto-discovery to custom directories */
|
|
32
32
|
compilerPlugins?: FlinkCompilerPlugin[];
|
|
33
|
+
/** Disable auto-discovery of services from src/services/. Default: false */
|
|
34
|
+
disableServices?: boolean;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
/**
|