@ultrst-armada/krnl 1.0.7 → 1.0.9
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/README.md +15 -0
- package/package.json +14 -2
- package/packages/cli/dist/bin/krnl.cjs +5 -15
- package/packages/cli/dist/bin/krnl.js +1 -1
- package/packages/cli/dist/{chunk-GHWYEAPD.js → chunk-OOVZPIMX.js} +4 -14
- package/packages/cli/dist/index.cjs +5 -15
- package/packages/cli/dist/index.js +1 -1
- package/packages/core/dist/index.cjs +590 -102
- package/packages/core/dist/index.d.cts +310 -24
- package/packages/core/dist/index.d.ts +310 -24
- package/packages/core/dist/index.js +582 -102
- package/packages/core/package.json +2 -2
|
@@ -30,6 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
EventBus: () => EventBus,
|
|
34
|
+
FunctionalityFacade: () => FunctionalityFacade,
|
|
33
35
|
FunctionalityOrchestration: () => FunctionalityOrchestration,
|
|
34
36
|
HttpProtocolAdapter: () => HttpProtocolAdapter,
|
|
35
37
|
KrnlPackageReconciler: () => KrnlPackageReconciler,
|
|
@@ -37,6 +39,7 @@ __export(index_exports, {
|
|
|
37
39
|
KrnlWorkerPool: () => KrnlWorkerPool,
|
|
38
40
|
LifecycleManager: () => LifecycleManager,
|
|
39
41
|
MemoryTransport: () => MemoryTransport,
|
|
42
|
+
OrchestrationEventFacade: () => OrchestrationEventFacade,
|
|
40
43
|
P2PProtocolAdapter: () => P2PProtocolAdapter,
|
|
41
44
|
ProtocolResolver: () => ProtocolResolver,
|
|
42
45
|
ServiceDiscovery: () => ServiceDiscovery,
|
|
@@ -47,14 +50,19 @@ __export(index_exports, {
|
|
|
47
50
|
ServiceRegistry: () => ServiceRegistry,
|
|
48
51
|
ServiceRuntime: () => ServiceRuntime,
|
|
49
52
|
ServiceStateStore: () => ServiceStateStore,
|
|
53
|
+
TelemetryController: () => TelemetryController,
|
|
50
54
|
UdpProtocolAdapter: () => UdpProtocolAdapter,
|
|
51
55
|
WebSocketProtocolAdapter: () => WebSocketProtocolAdapter,
|
|
52
56
|
WorkerExecutionBoundary: () => WorkerExecutionBoundary,
|
|
57
|
+
createEventChannels: () => createEventChannels,
|
|
53
58
|
createService: () => createService,
|
|
54
59
|
createServiceDefinition: () => createServiceDefinition,
|
|
55
60
|
createServiceFacade: () => createServiceFacade,
|
|
56
61
|
createServiceProxy: () => createServiceProxy,
|
|
62
|
+
deltaResources: () => deltaResources,
|
|
57
63
|
gutts: () => gutts,
|
|
64
|
+
sampleResources: () => sampleResources,
|
|
65
|
+
telemetryNow: () => telemetryNow,
|
|
58
66
|
validateOrchestrationDAG: () => validateOrchestrationDAG,
|
|
59
67
|
validateServiceDefinition: () => validateServiceDefinition
|
|
60
68
|
});
|
|
@@ -88,35 +96,6 @@ var ServiceStateStore = class {
|
|
|
88
96
|
}
|
|
89
97
|
};
|
|
90
98
|
|
|
91
|
-
// src/events/ServiceEventBus.ts
|
|
92
|
-
var ServiceEventBus = class {
|
|
93
|
-
handlers = /* @__PURE__ */ new Map();
|
|
94
|
-
on(event, handler) {
|
|
95
|
-
if (!this.handlers.has(event)) {
|
|
96
|
-
this.handlers.set(event, /* @__PURE__ */ new Set());
|
|
97
|
-
}
|
|
98
|
-
const set = this.handlers.get(event);
|
|
99
|
-
set.add(handler);
|
|
100
|
-
return () => {
|
|
101
|
-
set.delete(handler);
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
emit(event, payload) {
|
|
105
|
-
const set = this.handlers.get(event);
|
|
106
|
-
if (!set) return;
|
|
107
|
-
for (const handler of Array.from(set)) {
|
|
108
|
-
try {
|
|
109
|
-
handler(payload);
|
|
110
|
-
} catch (err) {
|
|
111
|
-
console.error(`[ServiceEventBus] Handler error for '${event}':`, err);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
clear() {
|
|
116
|
-
this.handlers.clear();
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
|
|
120
99
|
// src/lifecycle/LifecycleManager.ts
|
|
121
100
|
var TRANSITIONS = {
|
|
122
101
|
uninitialized: ["initializing"],
|
|
@@ -153,6 +132,227 @@ var LifecycleManager = class {
|
|
|
153
132
|
}
|
|
154
133
|
};
|
|
155
134
|
|
|
135
|
+
// src/telemetry/TelemetryController.ts
|
|
136
|
+
var import_node_crypto = require("crypto");
|
|
137
|
+
|
|
138
|
+
// src/telemetry/TelemetryResources.ts
|
|
139
|
+
var runtimeProcess = typeof globalThis !== "undefined" ? globalThis.process : void 0;
|
|
140
|
+
var runtimePerformance = typeof globalThis !== "undefined" ? globalThis.performance : void 0;
|
|
141
|
+
function telemetryNow() {
|
|
142
|
+
return runtimePerformance?.now ? runtimePerformance.now() : Date.now();
|
|
143
|
+
}
|
|
144
|
+
function sampleResources() {
|
|
145
|
+
const memory = runtimeProcess?.memoryUsage?.();
|
|
146
|
+
const cpu = runtimeProcess?.cpuUsage?.();
|
|
147
|
+
const eventLoop = runtimePerformance?.eventLoopUtilization?.();
|
|
148
|
+
return {
|
|
149
|
+
cpuUserMicros: cpu?.user,
|
|
150
|
+
cpuSystemMicros: cpu?.system,
|
|
151
|
+
heapUsedBytes: memory?.heapUsed,
|
|
152
|
+
heapTotalBytes: memory?.heapTotal,
|
|
153
|
+
rssBytes: memory?.rss,
|
|
154
|
+
externalBytes: memory?.external,
|
|
155
|
+
arrayBuffersBytes: memory?.arrayBuffers,
|
|
156
|
+
eventLoopUtilization: eventLoop?.utilization,
|
|
157
|
+
scope: memory || cpu || eventLoop ? "process" : "runtime"
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function deltaResources(start, end) {
|
|
161
|
+
const delta = (a, b) => a === void 0 || b === void 0 ? void 0 : b - a;
|
|
162
|
+
return {
|
|
163
|
+
cpuUserMicros: delta(start.cpuUserMicros, end.cpuUserMicros),
|
|
164
|
+
cpuSystemMicros: delta(start.cpuSystemMicros, end.cpuSystemMicros),
|
|
165
|
+
heapUsedBytes: delta(start.heapUsedBytes, end.heapUsedBytes),
|
|
166
|
+
heapTotalBytes: delta(start.heapTotalBytes, end.heapTotalBytes),
|
|
167
|
+
rssBytes: delta(start.rssBytes, end.rssBytes),
|
|
168
|
+
externalBytes: delta(start.externalBytes, end.externalBytes),
|
|
169
|
+
arrayBuffersBytes: delta(start.arrayBuffersBytes, end.arrayBuffersBytes),
|
|
170
|
+
eventLoopUtilization: delta(start.eventLoopUtilization, end.eventLoopUtilization),
|
|
171
|
+
scope: start.scope
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// src/telemetry/TelemetryController.ts
|
|
176
|
+
var id = (prefix) => `${prefix}_${(0, import_node_crypto.randomUUID)()}`;
|
|
177
|
+
function percentiles(values) {
|
|
178
|
+
if (!values.length) return { min: 0, mean: 0, p50: 0, p90: 0, p95: 0, p99: 0, max: 0 };
|
|
179
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
180
|
+
const pick = (p) => sorted[Math.min(sorted.length - 1, Math.floor((sorted.length - 1) * p))];
|
|
181
|
+
return {
|
|
182
|
+
min: sorted[0],
|
|
183
|
+
mean: sorted.reduce((sum, value) => sum + value, 0) / sorted.length,
|
|
184
|
+
p50: pick(0.5),
|
|
185
|
+
p90: pick(0.9),
|
|
186
|
+
p95: pick(0.95),
|
|
187
|
+
p99: pick(0.99),
|
|
188
|
+
max: sorted[sorted.length - 1]
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
var TelemetryController = class {
|
|
192
|
+
constructor(serviceId, options = {}) {
|
|
193
|
+
this.serviceId = serviceId;
|
|
194
|
+
this.mode = options.mode ?? "off";
|
|
195
|
+
this.maxSamples = Math.max(1, options.maxSamples ?? 2048);
|
|
196
|
+
this.maxRecentInvocations = Math.max(1, options.maxRecentInvocations ?? 100);
|
|
197
|
+
}
|
|
198
|
+
serviceId;
|
|
199
|
+
mode;
|
|
200
|
+
maxSamples;
|
|
201
|
+
maxRecentInvocations;
|
|
202
|
+
samples = [];
|
|
203
|
+
active = /* @__PURE__ */ new Map();
|
|
204
|
+
totalInvocationCount = 0;
|
|
205
|
+
totalSuccessCount = 0;
|
|
206
|
+
totalErrorCount = 0;
|
|
207
|
+
lastInvocationAt;
|
|
208
|
+
enable(mode = "standard") {
|
|
209
|
+
this.mode = mode;
|
|
210
|
+
}
|
|
211
|
+
disable() {
|
|
212
|
+
this.mode = "off";
|
|
213
|
+
this.active.clear();
|
|
214
|
+
}
|
|
215
|
+
setMode(mode) {
|
|
216
|
+
this.mode = mode;
|
|
217
|
+
if (mode === "off") this.active.clear();
|
|
218
|
+
}
|
|
219
|
+
getMode() {
|
|
220
|
+
return this.mode;
|
|
221
|
+
}
|
|
222
|
+
status() {
|
|
223
|
+
return {
|
|
224
|
+
enabled: this.mode !== "off",
|
|
225
|
+
mode: this.mode,
|
|
226
|
+
serviceId: this.serviceId,
|
|
227
|
+
invocationCount: this.totalInvocationCount,
|
|
228
|
+
activeInvocations: this.active.size,
|
|
229
|
+
lastInvocationAt: this.lastInvocationAt
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
begin(target, kind, correlationId, parentSpanId) {
|
|
233
|
+
if (this.mode === "off") {
|
|
234
|
+
return {
|
|
235
|
+
invocationId: "",
|
|
236
|
+
traceId: "",
|
|
237
|
+
spanId: "",
|
|
238
|
+
parentSpanId,
|
|
239
|
+
end: () => void 0
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
const invocationId = id("inv");
|
|
243
|
+
const spanId = id("span");
|
|
244
|
+
const traceId = parentSpanId ? this.findTraceId(parentSpanId) ?? id("trace") : id("trace");
|
|
245
|
+
const active = {
|
|
246
|
+
invocationId,
|
|
247
|
+
traceId,
|
|
248
|
+
spanId,
|
|
249
|
+
parentSpanId,
|
|
250
|
+
serviceId: this.serviceId,
|
|
251
|
+
target,
|
|
252
|
+
kind,
|
|
253
|
+
correlationId,
|
|
254
|
+
startedAt: telemetryNow(),
|
|
255
|
+
resourcesStart: this.mode === "standard" ? void 0 : sampleResources()
|
|
256
|
+
};
|
|
257
|
+
this.active.set(invocationId, active);
|
|
258
|
+
return {
|
|
259
|
+
invocationId,
|
|
260
|
+
traceId,
|
|
261
|
+
spanId,
|
|
262
|
+
parentSpanId,
|
|
263
|
+
end: (error) => this.finish(active, error)
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
reset() {
|
|
267
|
+
this.samples.length = 0;
|
|
268
|
+
this.active.clear();
|
|
269
|
+
this.totalInvocationCount = 0;
|
|
270
|
+
this.totalSuccessCount = 0;
|
|
271
|
+
this.totalErrorCount = 0;
|
|
272
|
+
this.lastInvocationAt = void 0;
|
|
273
|
+
}
|
|
274
|
+
snapshot() {
|
|
275
|
+
const invocations = this.samples.slice();
|
|
276
|
+
const durations = invocations.map((item) => item.durationMs);
|
|
277
|
+
const targetMap = /* @__PURE__ */ new Map();
|
|
278
|
+
for (const invocation of invocations) {
|
|
279
|
+
const key = `${invocation.kind}:${invocation.target}`;
|
|
280
|
+
const group = targetMap.get(key) ?? [];
|
|
281
|
+
group.push(invocation);
|
|
282
|
+
targetMap.set(key, group);
|
|
283
|
+
}
|
|
284
|
+
const targets = [...targetMap.values()].map((group) => ({
|
|
285
|
+
target: group[0].target,
|
|
286
|
+
kind: group[0].kind,
|
|
287
|
+
count: group.length,
|
|
288
|
+
successCount: group.filter((item) => item.status === "completed").length,
|
|
289
|
+
errorCount: group.filter((item) => item.status === "failed").length,
|
|
290
|
+
durationMs: percentiles(group.map((item) => item.durationMs)),
|
|
291
|
+
lastInvocationAt: group[group.length - 1]?.completedAt
|
|
292
|
+
}));
|
|
293
|
+
return {
|
|
294
|
+
serviceId: this.serviceId,
|
|
295
|
+
mode: this.mode,
|
|
296
|
+
capturedAt: Date.now(),
|
|
297
|
+
status: this.status(),
|
|
298
|
+
totals: {
|
|
299
|
+
invocationCount: this.totalInvocationCount,
|
|
300
|
+
successCount: this.totalSuccessCount,
|
|
301
|
+
errorCount: this.totalErrorCount,
|
|
302
|
+
durationMs: percentiles(durations)
|
|
303
|
+
},
|
|
304
|
+
targets,
|
|
305
|
+
activeInvocations: this.active.size,
|
|
306
|
+
recentInvocations: invocations.slice(-this.maxRecentInvocations)
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
functionality(name) {
|
|
310
|
+
return this.snapshot().targets.find((target) => target.kind === "functionality" && target.target === name);
|
|
311
|
+
}
|
|
312
|
+
functionalities() {
|
|
313
|
+
return this.snapshot().targets.filter((target) => target.kind === "functionality");
|
|
314
|
+
}
|
|
315
|
+
findTraceId(parentSpanId) {
|
|
316
|
+
for (let index = this.samples.length - 1; index >= 0; index -= 1) {
|
|
317
|
+
if (this.samples[index].spanId === parentSpanId) return this.samples[index].traceId;
|
|
318
|
+
}
|
|
319
|
+
for (const active of this.active.values()) {
|
|
320
|
+
if (active.spanId === parentSpanId) return active.traceId;
|
|
321
|
+
}
|
|
322
|
+
return void 0;
|
|
323
|
+
}
|
|
324
|
+
finish(active, error) {
|
|
325
|
+
if (!this.active.delete(active.invocationId)) return;
|
|
326
|
+
const completedAt = telemetryNow();
|
|
327
|
+
const resourcesEnd = this.mode === "standard" ? void 0 : sampleResources();
|
|
328
|
+
const invocation = {
|
|
329
|
+
invocationId: active.invocationId,
|
|
330
|
+
traceId: active.traceId,
|
|
331
|
+
spanId: active.spanId,
|
|
332
|
+
parentSpanId: active.parentSpanId,
|
|
333
|
+
serviceId: active.serviceId,
|
|
334
|
+
target: active.target,
|
|
335
|
+
kind: active.kind,
|
|
336
|
+
correlationId: active.correlationId,
|
|
337
|
+
startedAt: active.startedAt,
|
|
338
|
+
completedAt,
|
|
339
|
+
durationMs: Math.max(0, completedAt - active.startedAt),
|
|
340
|
+
status: error === void 0 ? "completed" : "failed",
|
|
341
|
+
error: error === void 0 ? void 0 : {
|
|
342
|
+
name: error instanceof Error ? error.name : "Error",
|
|
343
|
+
message: error instanceof Error ? error.message : String(error)
|
|
344
|
+
},
|
|
345
|
+
resources: active.resourcesStart && resourcesEnd ? { start: active.resourcesStart, end: resourcesEnd, delta: deltaResources(active.resourcesStart, resourcesEnd) } : void 0
|
|
346
|
+
};
|
|
347
|
+
this.samples.push(invocation);
|
|
348
|
+
this.totalInvocationCount += 1;
|
|
349
|
+
if (invocation.status === "completed") this.totalSuccessCount += 1;
|
|
350
|
+
else this.totalErrorCount += 1;
|
|
351
|
+
if (this.samples.length > this.maxSamples) this.samples.splice(0, this.samples.length - this.maxSamples);
|
|
352
|
+
this.lastInvocationAt = Date.now();
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
|
|
156
356
|
// src/services/ServiceInstance.ts
|
|
157
357
|
var ServiceInstance = class {
|
|
158
358
|
constructor(definition) {
|
|
@@ -160,8 +360,8 @@ var ServiceInstance = class {
|
|
|
160
360
|
this.id = definition.id;
|
|
161
361
|
this.name = definition.name;
|
|
162
362
|
this.version = definition.version;
|
|
363
|
+
this.telemetry = new TelemetryController(this.id);
|
|
163
364
|
this.lifecycle = new LifecycleManager();
|
|
164
|
-
this.eventBus = new ServiceEventBus();
|
|
165
365
|
if (definition.state) {
|
|
166
366
|
this.stateStore = new ServiceStateStore(definition.state.initial);
|
|
167
367
|
}
|
|
@@ -170,8 +370,8 @@ var ServiceInstance = class {
|
|
|
170
370
|
id;
|
|
171
371
|
name;
|
|
172
372
|
version;
|
|
373
|
+
telemetry;
|
|
173
374
|
stateStore;
|
|
174
|
-
eventBus;
|
|
175
375
|
lifecycle;
|
|
176
376
|
initialized = false;
|
|
177
377
|
disposed = false;
|
|
@@ -235,7 +435,19 @@ var ServiceInstance = class {
|
|
|
235
435
|
if (!operation) {
|
|
236
436
|
throw new Error("[Service:" + this.name + "] Operation '" + op + "' not found.");
|
|
237
437
|
}
|
|
238
|
-
|
|
438
|
+
if (ctx.metadata?.__krnlTelemetryInternal === true) {
|
|
439
|
+
return await operation.handler(input, ctx);
|
|
440
|
+
}
|
|
441
|
+
const parentSpanId = typeof ctx.metadata?.__krnlTelemetryParentSpanId === "string" ? ctx.metadata.__krnlTelemetryParentSpanId : void 0;
|
|
442
|
+
const handle = this.telemetry.begin(op, "service", ctx.correlationId, parentSpanId);
|
|
443
|
+
try {
|
|
444
|
+
const output = await operation.handler(input, ctx);
|
|
445
|
+
handle.end();
|
|
446
|
+
return output;
|
|
447
|
+
} catch (error) {
|
|
448
|
+
handle.end(error);
|
|
449
|
+
throw error;
|
|
450
|
+
}
|
|
239
451
|
}
|
|
240
452
|
getState() {
|
|
241
453
|
return this.stateStore?.getState() ?? Object.freeze({});
|
|
@@ -247,12 +459,6 @@ var ServiceInstance = class {
|
|
|
247
459
|
}
|
|
248
460
|
this.stateStore.setState(mutation);
|
|
249
461
|
}
|
|
250
|
-
on(event, handler) {
|
|
251
|
-
return this.eventBus.on(event, handler);
|
|
252
|
-
}
|
|
253
|
-
emit(event, payload) {
|
|
254
|
-
this.eventBus.emit(event, payload);
|
|
255
|
-
}
|
|
256
462
|
};
|
|
257
463
|
|
|
258
464
|
// src/services/ServiceProxy.ts
|
|
@@ -273,6 +479,7 @@ var ServiceProxy = class {
|
|
|
273
479
|
this.id = definition.id;
|
|
274
480
|
this.name = definition.name;
|
|
275
481
|
this.version = definition.version;
|
|
482
|
+
this.telemetry = new TelemetryController(this.id);
|
|
276
483
|
this.stateCache = { ...definition.state?.initial ?? {} };
|
|
277
484
|
}
|
|
278
485
|
definition;
|
|
@@ -281,6 +488,7 @@ var ServiceProxy = class {
|
|
|
281
488
|
id;
|
|
282
489
|
name;
|
|
283
490
|
version;
|
|
491
|
+
telemetry;
|
|
284
492
|
stateCache;
|
|
285
493
|
eventHandlers = /* @__PURE__ */ new Map();
|
|
286
494
|
lifecycle = new LifecycleManager();
|
|
@@ -338,11 +546,21 @@ var ServiceProxy = class {
|
|
|
338
546
|
payload: input,
|
|
339
547
|
timestamp: Date.now()
|
|
340
548
|
};
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
549
|
+
const parentSpanId = typeof ctx.metadata?.__krnlTelemetryParentSpanId === "string" ? ctx.metadata.__krnlTelemetryParentSpanId : void 0;
|
|
550
|
+
const handle = this.telemetry.begin(op, "service", ctx.correlationId, parentSpanId);
|
|
551
|
+
try {
|
|
552
|
+
const response = await this.adapter.send(this.endpoint, envelope);
|
|
553
|
+
if (response.error) {
|
|
554
|
+
const error = new Error("[ServiceProxy:" + this.name + "] Remote error: " + response.error);
|
|
555
|
+
handle.end(error);
|
|
556
|
+
throw error;
|
|
557
|
+
}
|
|
558
|
+
handle.end();
|
|
559
|
+
return response.payload;
|
|
560
|
+
} catch (error) {
|
|
561
|
+
handle.end(error);
|
|
562
|
+
throw error;
|
|
344
563
|
}
|
|
345
|
-
return response.payload;
|
|
346
564
|
}
|
|
347
565
|
getState() {
|
|
348
566
|
return Object.freeze({ ...this.stateCache });
|
|
@@ -402,12 +620,6 @@ function validateServiceDefinition(definition) {
|
|
|
402
620
|
throw new Error("[Service:" + definition.name + "] Operation '" + key + "' must define a handler.");
|
|
403
621
|
}
|
|
404
622
|
}
|
|
405
|
-
for (const [key, event] of Object.entries(definition.events ?? {})) {
|
|
406
|
-
if (!event || typeof event !== "object") {
|
|
407
|
-
throw new Error("[Service:" + definition.name + "] Invalid event '" + key + "'.");
|
|
408
|
-
}
|
|
409
|
-
assertNonEmptyString(event.name, "events." + key + ".name");
|
|
410
|
-
}
|
|
411
623
|
for (const [key, schema] of Object.entries(definition.inputs ?? {})) assertRecord(schema, "inputs." + key);
|
|
412
624
|
for (const [key, schema] of Object.entries(definition.outputs ?? {})) assertRecord(schema, "outputs." + key);
|
|
413
625
|
if (definition.state?.schema !== void 0) assertRecord(definition.state.schema, "state.schema");
|
|
@@ -440,6 +652,137 @@ function createServiceProxy(definition, adapter, endpoint) {
|
|
|
440
652
|
return new ServiceProxy(definition, adapter, endpoint);
|
|
441
653
|
}
|
|
442
654
|
|
|
655
|
+
// src/events/EventBus.ts
|
|
656
|
+
var EventBus = class {
|
|
657
|
+
handlers = /* @__PURE__ */ new Map();
|
|
658
|
+
subscribe(event, handler) {
|
|
659
|
+
let listeners = this.handlers.get(event);
|
|
660
|
+
if (!listeners) {
|
|
661
|
+
listeners = /* @__PURE__ */ new Set();
|
|
662
|
+
this.handlers.set(event, listeners);
|
|
663
|
+
}
|
|
664
|
+
listeners.add(handler);
|
|
665
|
+
return {
|
|
666
|
+
unsubscribe: () => {
|
|
667
|
+
listeners?.delete(handler);
|
|
668
|
+
if (listeners && listeners.size === 0) this.handlers.delete(event);
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
on(event, handler) {
|
|
673
|
+
return this.subscribe(event, handler).unsubscribe;
|
|
674
|
+
}
|
|
675
|
+
emit(event, payload) {
|
|
676
|
+
const listeners = this.handlers.get(event);
|
|
677
|
+
if (!listeners) return;
|
|
678
|
+
for (const listener of Array.from(listeners)) {
|
|
679
|
+
try {
|
|
680
|
+
listener(payload);
|
|
681
|
+
} catch (error) {
|
|
682
|
+
console.error(`[EventBus] Handler error for '${event}':`, error);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
clear() {
|
|
687
|
+
this.handlers.clear();
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
function createEventChannels(bus, names) {
|
|
691
|
+
const channels = {};
|
|
692
|
+
for (const name of names) {
|
|
693
|
+
channels[name] = {
|
|
694
|
+
name,
|
|
695
|
+
subscribe: (handler) => bus.subscribe(name, handler),
|
|
696
|
+
on: (handler) => bus.on(name, handler)
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
return channels;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// src/functionalities/FunctionalityFacade.ts
|
|
703
|
+
var FunctionalityFacade = class {
|
|
704
|
+
id;
|
|
705
|
+
name;
|
|
706
|
+
description;
|
|
707
|
+
bubbleEvents;
|
|
708
|
+
definition;
|
|
709
|
+
eventBus;
|
|
710
|
+
events;
|
|
711
|
+
constructor(id2, definition, options = {}) {
|
|
712
|
+
if (!id2 || typeof id2 !== "string") {
|
|
713
|
+
throw new Error("[FunctionalityFacade] Functionality id must be a non-empty string.");
|
|
714
|
+
}
|
|
715
|
+
if (!definition || typeof definition.execute !== "function") {
|
|
716
|
+
throw new Error(`[FunctionalityFacade:${id2}] Functionality must define execute().`);
|
|
717
|
+
}
|
|
718
|
+
this.id = id2;
|
|
719
|
+
this.name = definition.name || id2;
|
|
720
|
+
this.description = definition.description || "";
|
|
721
|
+
this.bubbleEvents = options.bubbleEvents ?? definition.bubbleEvents ?? true;
|
|
722
|
+
this.definition = definition;
|
|
723
|
+
this.eventBus = new EventBus();
|
|
724
|
+
for (const reserved of ["triggered", "completed", "failed"]) {
|
|
725
|
+
if (definition.events && reserved in definition.events) {
|
|
726
|
+
throw new Error(`[FunctionalityFacade:${id2}] Event '${reserved}' is reserved for functionality lifecycle.`);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
const eventNames = [
|
|
730
|
+
"triggered",
|
|
731
|
+
"completed",
|
|
732
|
+
"failed",
|
|
733
|
+
...Object.keys(definition.events ?? {})
|
|
734
|
+
];
|
|
735
|
+
this.events = createEventChannels(this.eventBus, eventNames);
|
|
736
|
+
}
|
|
737
|
+
getEventDefinitions() {
|
|
738
|
+
return {
|
|
739
|
+
triggered: { name: "triggered", description: "Functionality execution started." },
|
|
740
|
+
completed: { name: "completed", description: "Functionality execution completed successfully." },
|
|
741
|
+
failed: { name: "failed", description: "Functionality execution failed." },
|
|
742
|
+
...this.definition.events ?? {}
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
subscribe(event, handler) {
|
|
746
|
+
return this.eventBus.on(event, handler);
|
|
747
|
+
}
|
|
748
|
+
emit(event, payload) {
|
|
749
|
+
this.eventBus.emit(event, payload);
|
|
750
|
+
}
|
|
751
|
+
async execute(input, context) {
|
|
752
|
+
const lifecycle = {
|
|
753
|
+
serviceId: context.serviceId,
|
|
754
|
+
functionalityId: this.id,
|
|
755
|
+
functionalityName: this.name,
|
|
756
|
+
correlationId: context.correlationId,
|
|
757
|
+
timestamp: Date.now(),
|
|
758
|
+
input
|
|
759
|
+
};
|
|
760
|
+
this.emit("triggered", lifecycle);
|
|
761
|
+
try {
|
|
762
|
+
const output = await this.definition.execute(input, {
|
|
763
|
+
...context,
|
|
764
|
+
emit: (event, payload) => this.emit(event, payload)
|
|
765
|
+
});
|
|
766
|
+
this.emit("completed", {
|
|
767
|
+
...lifecycle,
|
|
768
|
+
timestamp: Date.now(),
|
|
769
|
+
output
|
|
770
|
+
});
|
|
771
|
+
return output;
|
|
772
|
+
} catch (error) {
|
|
773
|
+
this.emit("failed", {
|
|
774
|
+
...lifecycle,
|
|
775
|
+
timestamp: Date.now(),
|
|
776
|
+
error: error?.message || String(error)
|
|
777
|
+
});
|
|
778
|
+
throw error;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
clearEventSubscriptions() {
|
|
782
|
+
this.eventBus.clear();
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
|
|
443
786
|
// ../../node_modules/.pnpm/p-queue@10.0.0/node_modules/p-queue/dist/lower-bound.js
|
|
444
787
|
function lowerBound(array, value, comparator) {
|
|
445
788
|
let first = 0;
|
|
@@ -494,11 +837,11 @@ var PriorityQueue = class {
|
|
|
494
837
|
return item.run;
|
|
495
838
|
}
|
|
496
839
|
enqueue(run, options) {
|
|
497
|
-
const { priority = 0, id } = options ?? {};
|
|
840
|
+
const { priority = 0, id: id2 } = options ?? {};
|
|
498
841
|
const { size } = this;
|
|
499
842
|
const element = {
|
|
500
843
|
priority,
|
|
501
|
-
id,
|
|
844
|
+
id: id2,
|
|
502
845
|
run
|
|
503
846
|
};
|
|
504
847
|
if (size === 0) {
|
|
@@ -515,16 +858,16 @@ var PriorityQueue = class {
|
|
|
515
858
|
const index = lowerBound(this.#queue, element, (a, b) => a.priority === b.priority ? 0 : b.priority - a.priority);
|
|
516
859
|
this.#queue.splice(index, 0, element);
|
|
517
860
|
}
|
|
518
|
-
setPriority(
|
|
519
|
-
const index = this.#findIndex((element) => element.id ===
|
|
861
|
+
setPriority(id2, priority) {
|
|
862
|
+
const index = this.#findIndex((element) => element.id === id2);
|
|
520
863
|
if (index === -1) {
|
|
521
|
-
throw new ReferenceError(`No promise function with the id "${
|
|
864
|
+
throw new ReferenceError(`No promise function with the id "${id2}" exists in the queue.`);
|
|
522
865
|
}
|
|
523
866
|
if (this.#queue[index].priority === priority) {
|
|
524
867
|
return;
|
|
525
868
|
}
|
|
526
869
|
const [item] = this.#queue.splice(index, 1);
|
|
527
|
-
this.enqueue(item.run, { priority, id });
|
|
870
|
+
this.enqueue(item.run, { priority, id: id2 });
|
|
528
871
|
}
|
|
529
872
|
remove(idOrRun) {
|
|
530
873
|
const index = this.#findIndex((element) => (typeof idOrRun === "string" ? element.id : element.run) === idOrRun);
|
|
@@ -940,17 +1283,17 @@ var PQueue = class extends EventTarget {
|
|
|
940
1283
|
}
|
|
941
1284
|
}
|
|
942
1285
|
async #add(function_, options, waitingTask) {
|
|
943
|
-
const
|
|
1286
|
+
const id2 = options.id ?? (this.#idAssigner++).toString();
|
|
944
1287
|
options = {
|
|
945
1288
|
timeout: this.#timeout,
|
|
946
1289
|
...options,
|
|
947
|
-
id
|
|
1290
|
+
id: id2
|
|
948
1291
|
};
|
|
949
1292
|
validateTimeout(options.timeout);
|
|
950
1293
|
if (options.priority !== void 0 && (typeof options.priority !== "number" || Number.isNaN(options.priority))) {
|
|
951
1294
|
throw new TypeError(`Expected \`priority\` to be a number, got \`${String(options.priority)}\` (${typeof options.priority})`);
|
|
952
1295
|
}
|
|
953
|
-
waitingTask.id =
|
|
1296
|
+
waitingTask.id = id2;
|
|
954
1297
|
waitingTask.priority = options.priority ?? 0;
|
|
955
1298
|
return new Promise((resolve, reject) => {
|
|
956
1299
|
if (options.signal?.aborted) {
|
|
@@ -967,7 +1310,7 @@ var PQueue = class extends EventTarget {
|
|
|
967
1310
|
}
|
|
968
1311
|
this.#pending++;
|
|
969
1312
|
const runningTask = {
|
|
970
|
-
id,
|
|
1313
|
+
id: id2,
|
|
971
1314
|
priority: waitingTask.priority,
|
|
972
1315
|
startTime: Date.now(),
|
|
973
1316
|
timeout: options.timeout
|
|
@@ -1034,7 +1377,7 @@ var PQueue = class extends EventTarget {
|
|
|
1034
1377
|
if (this.#queue instanceof PriorityQueue) {
|
|
1035
1378
|
this.#queue.remove(run);
|
|
1036
1379
|
} else {
|
|
1037
|
-
this.#queue.remove(
|
|
1380
|
+
this.#queue.remove(id2);
|
|
1038
1381
|
}
|
|
1039
1382
|
reject(signal.reason);
|
|
1040
1383
|
this.#processQueue();
|
|
@@ -1110,18 +1453,18 @@ var PQueue = class extends EventTarget {
|
|
|
1110
1453
|
// '🦀' runs last
|
|
1111
1454
|
```
|
|
1112
1455
|
*/
|
|
1113
|
-
setPriority(
|
|
1456
|
+
setPriority(id2, priority) {
|
|
1114
1457
|
if (typeof priority !== "number" || !Number.isFinite(priority)) {
|
|
1115
1458
|
throw new TypeError(`Expected \`priority\` to be a finite number, got \`${priority}\` (${typeof priority})`);
|
|
1116
1459
|
}
|
|
1117
1460
|
for (const waitingTask of this.#waitingTasks) {
|
|
1118
|
-
if (waitingTask.id ===
|
|
1119
|
-
this.#queue.setPriority(
|
|
1461
|
+
if (waitingTask.id === id2) {
|
|
1462
|
+
this.#queue.setPriority(id2, priority);
|
|
1120
1463
|
waitingTask.priority = priority;
|
|
1121
1464
|
return;
|
|
1122
1465
|
}
|
|
1123
1466
|
}
|
|
1124
|
-
throw new ReferenceError(`No promise function with the id "${
|
|
1467
|
+
throw new ReferenceError(`No promise function with the id "${id2}" exists in the queue.`);
|
|
1125
1468
|
}
|
|
1126
1469
|
/**
|
|
1127
1470
|
Adds a sync or async task to the queue.
|
|
@@ -1464,7 +1807,7 @@ var TimeoutError = class extends DOMException {
|
|
|
1464
1807
|
};
|
|
1465
1808
|
|
|
1466
1809
|
// src/orchestration/canonical/KrnlQ.ts
|
|
1467
|
-
var
|
|
1810
|
+
var import_node_crypto2 = require("crypto");
|
|
1468
1811
|
|
|
1469
1812
|
// src/orchestration/canonical/model.ts
|
|
1470
1813
|
function validateOrchestrationDAG(steps) {
|
|
@@ -1531,8 +1874,8 @@ function validateOrchestrationDAG(steps) {
|
|
|
1531
1874
|
}
|
|
1532
1875
|
}
|
|
1533
1876
|
const queue = [];
|
|
1534
|
-
for (const [
|
|
1535
|
-
if (deg === 0) queue.push(
|
|
1877
|
+
for (const [id2, deg] of inDegree.entries()) {
|
|
1878
|
+
if (deg === 0) queue.push(id2);
|
|
1536
1879
|
}
|
|
1537
1880
|
const order = [];
|
|
1538
1881
|
while (queue.length > 0) {
|
|
@@ -1668,7 +2011,7 @@ var KrnlWorkerPool = class {
|
|
|
1668
2011
|
// src/orchestration/canonical/KrnlQ.ts
|
|
1669
2012
|
function generateSecureId(prefix) {
|
|
1670
2013
|
try {
|
|
1671
|
-
return `${prefix}_${(0,
|
|
2014
|
+
return `${prefix}_${(0, import_node_crypto2.randomUUID)()}`;
|
|
1672
2015
|
} catch {
|
|
1673
2016
|
return `${prefix}_${Math.random().toString(36).substring(2, 11)}_${Date.now()}`;
|
|
1674
2017
|
}
|
|
@@ -1736,9 +2079,9 @@ var KrnlQueue = class {
|
|
|
1736
2079
|
if (this.jobs.size <= this.maxRetainedJobs) return;
|
|
1737
2080
|
const excess = this.jobs.size - this.maxRetainedJobs;
|
|
1738
2081
|
let pruned = 0;
|
|
1739
|
-
for (const [
|
|
2082
|
+
for (const [id2, record] of this.jobs.entries()) {
|
|
1740
2083
|
if (record.status === "completed" || record.status === "failed" || record.status === "cancelled") {
|
|
1741
|
-
this.jobs.delete(
|
|
2084
|
+
this.jobs.delete(id2);
|
|
1742
2085
|
pruned++;
|
|
1743
2086
|
if (pruned >= excess) break;
|
|
1744
2087
|
}
|
|
@@ -2352,6 +2695,7 @@ var ServiceFacade = class {
|
|
|
2352
2695
|
defaultFrontend;
|
|
2353
2696
|
instance;
|
|
2354
2697
|
queue;
|
|
2698
|
+
telemetry;
|
|
2355
2699
|
constructor(config) {
|
|
2356
2700
|
if (!config || !config.id || typeof config.id !== "string") {
|
|
2357
2701
|
throw new Error("[ServiceFacade] ServiceFacade requires a valid id.");
|
|
@@ -2360,7 +2704,10 @@ var ServiceFacade = class {
|
|
|
2360
2704
|
this.name = config.name || config.id;
|
|
2361
2705
|
this.version = config.version || "1.0.0";
|
|
2362
2706
|
this.description = config.description || "";
|
|
2363
|
-
this.functionalities =
|
|
2707
|
+
this.functionalities = {};
|
|
2708
|
+
for (const [name, functionality] of Object.entries(config.functionalities ?? {})) {
|
|
2709
|
+
this.registerFunctionality(name, functionality);
|
|
2710
|
+
}
|
|
2364
2711
|
this.functionalityOrchestrations = config.functionalityOrchestrations ? { ...config.functionalityOrchestrations } : config.orchestrations ? { ...config.orchestrations } : {};
|
|
2365
2712
|
this.libraries = config.libraries ? { ...config.libraries } : {};
|
|
2366
2713
|
this.dependencies = Object.freeze(config.dependencies ? [...config.dependencies] : []);
|
|
@@ -2378,6 +2725,7 @@ var ServiceFacade = class {
|
|
|
2378
2725
|
operations: {}
|
|
2379
2726
|
});
|
|
2380
2727
|
}
|
|
2728
|
+
this.telemetry = this.instance.telemetry;
|
|
2381
2729
|
this.queue = new KrnlQueue({
|
|
2382
2730
|
queueName: `krnl_queue_${this.id}`,
|
|
2383
2731
|
concurrency: config.concurrency ?? config.queueOptions?.concurrency ?? 10,
|
|
@@ -2421,6 +2769,8 @@ var ServiceFacade = class {
|
|
|
2421
2769
|
*/
|
|
2422
2770
|
async call(op, input, ctx) {
|
|
2423
2771
|
const correlationId = ctx?.correlationId || `corr_${Math.random().toString(36).substring(2, 11)}`;
|
|
2772
|
+
const telemetryParent = typeof ctx?.metadata?.__krnlTelemetryParentSpanId === "string" ? ctx.metadata.__krnlTelemetryParentSpanId : void 0;
|
|
2773
|
+
const telemetryHandle = this.telemetry.begin(op, "service", correlationId, telemetryParent);
|
|
2424
2774
|
const commandId = `cmd_${this.id}_${op}_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;
|
|
2425
2775
|
const command = {
|
|
2426
2776
|
id: commandId,
|
|
@@ -2430,23 +2780,48 @@ var ServiceFacade = class {
|
|
|
2430
2780
|
idempotencyKey: ctx?.idempotencyKey,
|
|
2431
2781
|
createdAt: Date.now()
|
|
2432
2782
|
};
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2783
|
+
try {
|
|
2784
|
+
const result = await this.queue.executeCommand(command, async (payload, workerCtx) => {
|
|
2785
|
+
WorkerExecutionBoundary.assertWorkerContext(workerCtx);
|
|
2786
|
+
const functionality = this.functionalities[op];
|
|
2787
|
+
if (functionality) {
|
|
2788
|
+
const functionalityTelemetry = this.telemetry.begin(
|
|
2789
|
+
functionality.name,
|
|
2790
|
+
"functionality",
|
|
2791
|
+
workerCtx.correlationId,
|
|
2792
|
+
telemetryHandle.spanId
|
|
2793
|
+
);
|
|
2794
|
+
try {
|
|
2795
|
+
return await functionality.execute(payload, {
|
|
2796
|
+
serviceId: this.id,
|
|
2797
|
+
functionalityId: functionality.id,
|
|
2798
|
+
correlationId: workerCtx.correlationId,
|
|
2799
|
+
state: this.instance.getState()
|
|
2800
|
+
});
|
|
2801
|
+
} catch (error) {
|
|
2802
|
+
functionalityTelemetry.end(error);
|
|
2803
|
+
throw error;
|
|
2804
|
+
} finally {
|
|
2805
|
+
functionalityTelemetry.end();
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
return await this.instance.call(op, payload, {
|
|
2437
2809
|
correlationId: workerCtx.correlationId,
|
|
2438
|
-
|
|
2810
|
+
idempotencyKey: command.idempotencyKey,
|
|
2811
|
+
metadata: { __krnlTelemetryInternal: true }
|
|
2439
2812
|
});
|
|
2440
|
-
}
|
|
2441
|
-
return await this.instance.call(op, payload, {
|
|
2442
|
-
correlationId: workerCtx.correlationId,
|
|
2443
|
-
idempotencyKey: command.idempotencyKey
|
|
2444
2813
|
});
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2814
|
+
if (result.status === "FAILED") {
|
|
2815
|
+
const error = new Error(result.error || `[ServiceFacade:${this.id}] Operation '${op}' execution failed.`);
|
|
2816
|
+
telemetryHandle.end(error);
|
|
2817
|
+
throw error;
|
|
2818
|
+
}
|
|
2819
|
+
telemetryHandle.end();
|
|
2820
|
+
return result.data;
|
|
2821
|
+
} catch (error) {
|
|
2822
|
+
telemetryHandle.end(error);
|
|
2823
|
+
throw error;
|
|
2448
2824
|
}
|
|
2449
|
-
return result.data;
|
|
2450
2825
|
}
|
|
2451
2826
|
/**
|
|
2452
2827
|
* External caller callable boundary for functionality orchestration execution.
|
|
@@ -2460,6 +2835,18 @@ var ServiceFacade = class {
|
|
|
2460
2835
|
);
|
|
2461
2836
|
}
|
|
2462
2837
|
const correlationId = options.correlationId || `corr_${Math.random().toString(36).substring(2, 11)}`;
|
|
2838
|
+
const serviceTelemetryHandle = this.telemetry.begin(
|
|
2839
|
+
`orchestrate:${orchestrationName}`,
|
|
2840
|
+
"service",
|
|
2841
|
+
correlationId,
|
|
2842
|
+
typeof options.metadata?.__krnlTelemetryParentSpanId === "string" ? options.metadata.__krnlTelemetryParentSpanId : void 0
|
|
2843
|
+
);
|
|
2844
|
+
const telemetryHandle = this.telemetry.begin(
|
|
2845
|
+
orchestrationName,
|
|
2846
|
+
"orchestration",
|
|
2847
|
+
correlationId,
|
|
2848
|
+
serviceTelemetryHandle.spanId
|
|
2849
|
+
);
|
|
2463
2850
|
const commandId = `cmd_orch_${this.id}_${orchestrationName}_${Date.now()}`;
|
|
2464
2851
|
const command = {
|
|
2465
2852
|
id: commandId,
|
|
@@ -2469,6 +2856,13 @@ var ServiceFacade = class {
|
|
|
2469
2856
|
idempotencyKey: options.idempotencyKey,
|
|
2470
2857
|
createdAt: Date.now()
|
|
2471
2858
|
};
|
|
2859
|
+
orch.events.emit("triggered", {
|
|
2860
|
+
serviceId: this.id,
|
|
2861
|
+
orchestrationId: orch.id,
|
|
2862
|
+
orchestrationName: orch.name,
|
|
2863
|
+
correlationId,
|
|
2864
|
+
timestamp: Date.now()
|
|
2865
|
+
});
|
|
2472
2866
|
const cmdResult = await this.queue.executeCommand(
|
|
2473
2867
|
command,
|
|
2474
2868
|
async (payload, workerCtx) => {
|
|
@@ -2500,7 +2894,8 @@ var ServiceFacade = class {
|
|
|
2500
2894
|
...s.compensation,
|
|
2501
2895
|
handler: async (compCtx) => {
|
|
2502
2896
|
await this.call(s.compensation.target, compCtx.results[s.id], {
|
|
2503
|
-
correlationId: compCtx.correlationId
|
|
2897
|
+
correlationId: compCtx.correlationId,
|
|
2898
|
+
metadata: { __krnlTelemetryParentSpanId: telemetryHandle.spanId }
|
|
2504
2899
|
});
|
|
2505
2900
|
}
|
|
2506
2901
|
};
|
|
@@ -2516,7 +2911,8 @@ var ServiceFacade = class {
|
|
|
2516
2911
|
stepPayload.input = stepCtx.results.input;
|
|
2517
2912
|
}
|
|
2518
2913
|
return await this.call(s.target, stepPayload, {
|
|
2519
|
-
correlationId: stepCtx.correlationId
|
|
2914
|
+
correlationId: stepCtx.correlationId,
|
|
2915
|
+
metadata: { __krnlTelemetryParentSpanId: telemetryHandle.spanId }
|
|
2520
2916
|
});
|
|
2521
2917
|
}
|
|
2522
2918
|
};
|
|
@@ -2536,7 +2932,8 @@ var ServiceFacade = class {
|
|
|
2536
2932
|
const nestedResult = await this.orchestrate(childOrchId, childInput, {
|
|
2537
2933
|
correlationId: childCtx.correlationId,
|
|
2538
2934
|
timeoutMs: childOrch.timeoutMs,
|
|
2539
|
-
abortSignal: options.abortSignal
|
|
2935
|
+
abortSignal: options.abortSignal,
|
|
2936
|
+
metadata: { __krnlTelemetryParentSpanId: telemetryHandle.spanId }
|
|
2540
2937
|
});
|
|
2541
2938
|
if (nestedResult.status !== "COMPLETED") {
|
|
2542
2939
|
throw new Error(nestedResult.error || `Nested orchestration "${childOrchId}" failed.`);
|
|
@@ -2558,9 +2955,39 @@ var ServiceFacade = class {
|
|
|
2558
2955
|
);
|
|
2559
2956
|
}
|
|
2560
2957
|
);
|
|
2561
|
-
|
|
2562
|
-
|
|
2958
|
+
const orchestrationResult = cmdResult.data;
|
|
2959
|
+
if (cmdResult.status === "COMPLETED" && orchestrationResult) {
|
|
2960
|
+
const lifecycleEvent = orchestrationResult.status === "COMPLETED" ? "completed" : "failed";
|
|
2961
|
+
orch.events.emit(lifecycleEvent, {
|
|
2962
|
+
serviceId: this.id,
|
|
2963
|
+
orchestrationId: orch.id,
|
|
2964
|
+
orchestrationName: orch.name,
|
|
2965
|
+
correlationId,
|
|
2966
|
+
executionId: String(orchestrationResult.executionMetadata.executionId ?? ""),
|
|
2967
|
+
timestamp: Date.now(),
|
|
2968
|
+
status: orchestrationResult.status,
|
|
2969
|
+
output: orchestrationResult.output,
|
|
2970
|
+
error: orchestrationResult.error
|
|
2971
|
+
});
|
|
2972
|
+
} else {
|
|
2973
|
+
orch.events.emit("failed", {
|
|
2974
|
+
serviceId: this.id,
|
|
2975
|
+
orchestrationId: orch.id,
|
|
2976
|
+
orchestrationName: orch.name,
|
|
2977
|
+
correlationId,
|
|
2978
|
+
timestamp: Date.now(),
|
|
2979
|
+
status: cmdResult.status,
|
|
2980
|
+
error: cmdResult.error
|
|
2981
|
+
});
|
|
2563
2982
|
}
|
|
2983
|
+
if (cmdResult.status === "FAILED" || orchestrationResult?.status === "FAILED") {
|
|
2984
|
+
const error = new Error(orchestrationResult?.error || cmdResult.error || `[ServiceFacade:${this.id}] Orchestration failed.`);
|
|
2985
|
+
telemetryHandle.end(error);
|
|
2986
|
+
serviceTelemetryHandle.end(error);
|
|
2987
|
+
throw error;
|
|
2988
|
+
}
|
|
2989
|
+
telemetryHandle.end();
|
|
2990
|
+
serviceTelemetryHandle.end();
|
|
2564
2991
|
return cmdResult.data;
|
|
2565
2992
|
}
|
|
2566
2993
|
/**
|
|
@@ -2587,17 +3014,29 @@ var ServiceFacade = class {
|
|
|
2587
3014
|
setState(mutation) {
|
|
2588
3015
|
this.instance.setState(mutation);
|
|
2589
3016
|
}
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
3017
|
+
getFunctionality(name) {
|
|
3018
|
+
const functionality = this.functionalities[name];
|
|
3019
|
+
if (!functionality) {
|
|
3020
|
+
throw new Error(`[ServiceFacade:${this.id}] Functionality "${name}" not found.`);
|
|
3021
|
+
}
|
|
3022
|
+
return functionality;
|
|
2595
3023
|
}
|
|
2596
|
-
registerFunctionality(name, functionality) {
|
|
3024
|
+
registerFunctionality(name, functionality, options = {}) {
|
|
2597
3025
|
if (!name || typeof name !== "string") {
|
|
2598
3026
|
throw new Error("[ServiceFacade] Functionality name must be a non-empty string.");
|
|
2599
3027
|
}
|
|
2600
|
-
|
|
3028
|
+
if (functionality instanceof FunctionalityFacade) {
|
|
3029
|
+
this.functionalities[name] = functionality;
|
|
3030
|
+
return this;
|
|
3031
|
+
}
|
|
3032
|
+
if (!functionality || typeof functionality !== "object" || typeof functionality.execute !== "function") {
|
|
3033
|
+
throw new Error(`[ServiceFacade:${this.id}] Functionality "${name}" must define execute().`);
|
|
3034
|
+
}
|
|
3035
|
+
this.functionalities[name] = new FunctionalityFacade(
|
|
3036
|
+
name,
|
|
3037
|
+
functionality,
|
|
3038
|
+
options
|
|
3039
|
+
);
|
|
2601
3040
|
return this;
|
|
2602
3041
|
}
|
|
2603
3042
|
registerFunctionalityOrchestration(name, orchestration) {
|
|
@@ -2635,14 +3074,22 @@ var ServiceFacade = class {
|
|
|
2635
3074
|
return this.getNamespace();
|
|
2636
3075
|
}
|
|
2637
3076
|
getNamespace() {
|
|
3077
|
+
const bubbledFunctionalities = {};
|
|
3078
|
+
for (const [name, functionality] of Object.entries(this.functionalities)) {
|
|
3079
|
+
if (functionality.bubbleEvents) bubbledFunctionalities[name] = functionality;
|
|
3080
|
+
}
|
|
3081
|
+
const bubbledOrchestrations = {};
|
|
3082
|
+
for (const [name, orchestration] of Object.entries(this.functionalityOrchestrations)) {
|
|
3083
|
+
if (orchestration.bubbleEvents) bubbledOrchestrations[name] = orchestration;
|
|
3084
|
+
}
|
|
2638
3085
|
return {
|
|
2639
3086
|
serviceId: this.id,
|
|
2640
3087
|
serviceName: this.name,
|
|
2641
3088
|
version: this.version,
|
|
2642
3089
|
description: this.description,
|
|
2643
3090
|
libraries: { ...this.libraries },
|
|
2644
|
-
functionalities:
|
|
2645
|
-
functionalityOrchestrations:
|
|
3091
|
+
functionalities: bubbledFunctionalities,
|
|
3092
|
+
functionalityOrchestrations: bubbledOrchestrations,
|
|
2646
3093
|
capabilities: this.capabilities,
|
|
2647
3094
|
dependencies: this.dependencies
|
|
2648
3095
|
};
|
|
@@ -3199,9 +3646,9 @@ var ServiceRuntime = class {
|
|
|
3199
3646
|
// src/discovery/ServiceDiscovery.ts
|
|
3200
3647
|
var ServiceDiscovery = class {
|
|
3201
3648
|
peers = /* @__PURE__ */ new Map();
|
|
3202
|
-
registerPeer(
|
|
3203
|
-
this.peers.set(
|
|
3204
|
-
id,
|
|
3649
|
+
registerPeer(id2, name, endpoints) {
|
|
3650
|
+
this.peers.set(id2, {
|
|
3651
|
+
id: id2,
|
|
3205
3652
|
name,
|
|
3206
3653
|
endpoints,
|
|
3207
3654
|
lastSeen: Date.now()
|
|
@@ -3210,8 +3657,8 @@ var ServiceDiscovery = class {
|
|
|
3210
3657
|
getPeers() {
|
|
3211
3658
|
return Array.from(this.peers.values());
|
|
3212
3659
|
}
|
|
3213
|
-
findPeer(
|
|
3214
|
-
return this.peers.get(
|
|
3660
|
+
findPeer(id2) {
|
|
3661
|
+
return this.peers.get(id2);
|
|
3215
3662
|
}
|
|
3216
3663
|
};
|
|
3217
3664
|
|
|
@@ -3339,6 +3786,34 @@ var KrnlPackageReconciler = class {
|
|
|
3339
3786
|
}
|
|
3340
3787
|
};
|
|
3341
3788
|
|
|
3789
|
+
// src/events/ServiceEventBus.ts
|
|
3790
|
+
var ServiceEventBus = class extends EventBus {
|
|
3791
|
+
};
|
|
3792
|
+
|
|
3793
|
+
// src/events/OrchestrationEventFacade.ts
|
|
3794
|
+
var OrchestrationEventFacade = class {
|
|
3795
|
+
eventBus = new EventBus();
|
|
3796
|
+
events;
|
|
3797
|
+
constructor() {
|
|
3798
|
+
this.events = createEventChannels(this.eventBus, ["triggered", "completed", "failed"]);
|
|
3799
|
+
}
|
|
3800
|
+
get triggered() {
|
|
3801
|
+
return this.events.triggered;
|
|
3802
|
+
}
|
|
3803
|
+
get completed() {
|
|
3804
|
+
return this.events.completed;
|
|
3805
|
+
}
|
|
3806
|
+
get failed() {
|
|
3807
|
+
return this.events.failed;
|
|
3808
|
+
}
|
|
3809
|
+
emit(event, payload) {
|
|
3810
|
+
this.eventBus.emit(event, payload);
|
|
3811
|
+
}
|
|
3812
|
+
clear() {
|
|
3813
|
+
this.eventBus.clear();
|
|
3814
|
+
}
|
|
3815
|
+
};
|
|
3816
|
+
|
|
3342
3817
|
// src/connectivity/ProtocolResolver.ts
|
|
3343
3818
|
var ProtocolResolver = class {
|
|
3344
3819
|
static selectProtocol(requiredCapabilities, availableEndpoints) {
|
|
@@ -3359,6 +3834,8 @@ var FunctionalityOrchestration = class {
|
|
|
3359
3834
|
description;
|
|
3360
3835
|
timeoutMs;
|
|
3361
3836
|
scope = "functionality";
|
|
3837
|
+
bubbleEvents;
|
|
3838
|
+
events;
|
|
3362
3839
|
steps = [];
|
|
3363
3840
|
flows = /* @__PURE__ */ new Map();
|
|
3364
3841
|
constructor(descriptor) {
|
|
@@ -3370,6 +3847,8 @@ var FunctionalityOrchestration = class {
|
|
|
3370
3847
|
this.version = descriptor.version || "1.0.0";
|
|
3371
3848
|
this.description = descriptor.description || "";
|
|
3372
3849
|
this.timeoutMs = descriptor.timeoutMs;
|
|
3850
|
+
this.bubbleEvents = descriptor.bubbleEvents ?? true;
|
|
3851
|
+
this.events = new OrchestrationEventFacade();
|
|
3373
3852
|
}
|
|
3374
3853
|
/**
|
|
3375
3854
|
* Adds a sequential functionality step to the DAG.
|
|
@@ -3530,6 +4009,7 @@ var FunctionalityOrchestration = class {
|
|
|
3530
4009
|
description: this.description,
|
|
3531
4010
|
timeoutMs: this.timeoutMs,
|
|
3532
4011
|
scope: "functionality",
|
|
4012
|
+
bubbleEvents: this.bubbleEvents,
|
|
3533
4013
|
dependencies: [],
|
|
3534
4014
|
steps: this.steps.map((s) => ({
|
|
3535
4015
|
id: s.id,
|
|
@@ -3634,6 +4114,8 @@ var FunctionalityOrchestration = class {
|
|
|
3634
4114
|
var gutts = __toESM(require("@dinilmusick/gutts"), 1);
|
|
3635
4115
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3636
4116
|
0 && (module.exports = {
|
|
4117
|
+
EventBus,
|
|
4118
|
+
FunctionalityFacade,
|
|
3637
4119
|
FunctionalityOrchestration,
|
|
3638
4120
|
HttpProtocolAdapter,
|
|
3639
4121
|
KrnlPackageReconciler,
|
|
@@ -3641,6 +4123,7 @@ var gutts = __toESM(require("@dinilmusick/gutts"), 1);
|
|
|
3641
4123
|
KrnlWorkerPool,
|
|
3642
4124
|
LifecycleManager,
|
|
3643
4125
|
MemoryTransport,
|
|
4126
|
+
OrchestrationEventFacade,
|
|
3644
4127
|
P2PProtocolAdapter,
|
|
3645
4128
|
ProtocolResolver,
|
|
3646
4129
|
ServiceDiscovery,
|
|
@@ -3651,14 +4134,19 @@ var gutts = __toESM(require("@dinilmusick/gutts"), 1);
|
|
|
3651
4134
|
ServiceRegistry,
|
|
3652
4135
|
ServiceRuntime,
|
|
3653
4136
|
ServiceStateStore,
|
|
4137
|
+
TelemetryController,
|
|
3654
4138
|
UdpProtocolAdapter,
|
|
3655
4139
|
WebSocketProtocolAdapter,
|
|
3656
4140
|
WorkerExecutionBoundary,
|
|
4141
|
+
createEventChannels,
|
|
3657
4142
|
createService,
|
|
3658
4143
|
createServiceDefinition,
|
|
3659
4144
|
createServiceFacade,
|
|
3660
4145
|
createServiceProxy,
|
|
4146
|
+
deltaResources,
|
|
3661
4147
|
gutts,
|
|
4148
|
+
sampleResources,
|
|
4149
|
+
telemetryNow,
|
|
3662
4150
|
validateOrchestrationDAG,
|
|
3663
4151
|
validateServiceDefinition
|
|
3664
4152
|
});
|