@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
|
@@ -26,35 +26,6 @@ var ServiceStateStore = class {
|
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
// src/events/ServiceEventBus.ts
|
|
30
|
-
var ServiceEventBus = class {
|
|
31
|
-
handlers = /* @__PURE__ */ new Map();
|
|
32
|
-
on(event, handler) {
|
|
33
|
-
if (!this.handlers.has(event)) {
|
|
34
|
-
this.handlers.set(event, /* @__PURE__ */ new Set());
|
|
35
|
-
}
|
|
36
|
-
const set = this.handlers.get(event);
|
|
37
|
-
set.add(handler);
|
|
38
|
-
return () => {
|
|
39
|
-
set.delete(handler);
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
emit(event, payload) {
|
|
43
|
-
const set = this.handlers.get(event);
|
|
44
|
-
if (!set) return;
|
|
45
|
-
for (const handler of Array.from(set)) {
|
|
46
|
-
try {
|
|
47
|
-
handler(payload);
|
|
48
|
-
} catch (err) {
|
|
49
|
-
console.error(`[ServiceEventBus] Handler error for '${event}':`, err);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
clear() {
|
|
54
|
-
this.handlers.clear();
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
29
|
// src/lifecycle/LifecycleManager.ts
|
|
59
30
|
var TRANSITIONS = {
|
|
60
31
|
uninitialized: ["initializing"],
|
|
@@ -91,6 +62,227 @@ var LifecycleManager = class {
|
|
|
91
62
|
}
|
|
92
63
|
};
|
|
93
64
|
|
|
65
|
+
// src/telemetry/TelemetryController.ts
|
|
66
|
+
import { randomUUID } from "crypto";
|
|
67
|
+
|
|
68
|
+
// src/telemetry/TelemetryResources.ts
|
|
69
|
+
var runtimeProcess = typeof globalThis !== "undefined" ? globalThis.process : void 0;
|
|
70
|
+
var runtimePerformance = typeof globalThis !== "undefined" ? globalThis.performance : void 0;
|
|
71
|
+
function telemetryNow() {
|
|
72
|
+
return runtimePerformance?.now ? runtimePerformance.now() : Date.now();
|
|
73
|
+
}
|
|
74
|
+
function sampleResources() {
|
|
75
|
+
const memory = runtimeProcess?.memoryUsage?.();
|
|
76
|
+
const cpu = runtimeProcess?.cpuUsage?.();
|
|
77
|
+
const eventLoop = runtimePerformance?.eventLoopUtilization?.();
|
|
78
|
+
return {
|
|
79
|
+
cpuUserMicros: cpu?.user,
|
|
80
|
+
cpuSystemMicros: cpu?.system,
|
|
81
|
+
heapUsedBytes: memory?.heapUsed,
|
|
82
|
+
heapTotalBytes: memory?.heapTotal,
|
|
83
|
+
rssBytes: memory?.rss,
|
|
84
|
+
externalBytes: memory?.external,
|
|
85
|
+
arrayBuffersBytes: memory?.arrayBuffers,
|
|
86
|
+
eventLoopUtilization: eventLoop?.utilization,
|
|
87
|
+
scope: memory || cpu || eventLoop ? "process" : "runtime"
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function deltaResources(start, end) {
|
|
91
|
+
const delta = (a, b) => a === void 0 || b === void 0 ? void 0 : b - a;
|
|
92
|
+
return {
|
|
93
|
+
cpuUserMicros: delta(start.cpuUserMicros, end.cpuUserMicros),
|
|
94
|
+
cpuSystemMicros: delta(start.cpuSystemMicros, end.cpuSystemMicros),
|
|
95
|
+
heapUsedBytes: delta(start.heapUsedBytes, end.heapUsedBytes),
|
|
96
|
+
heapTotalBytes: delta(start.heapTotalBytes, end.heapTotalBytes),
|
|
97
|
+
rssBytes: delta(start.rssBytes, end.rssBytes),
|
|
98
|
+
externalBytes: delta(start.externalBytes, end.externalBytes),
|
|
99
|
+
arrayBuffersBytes: delta(start.arrayBuffersBytes, end.arrayBuffersBytes),
|
|
100
|
+
eventLoopUtilization: delta(start.eventLoopUtilization, end.eventLoopUtilization),
|
|
101
|
+
scope: start.scope
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/telemetry/TelemetryController.ts
|
|
106
|
+
var id = (prefix) => `${prefix}_${randomUUID()}`;
|
|
107
|
+
function percentiles(values) {
|
|
108
|
+
if (!values.length) return { min: 0, mean: 0, p50: 0, p90: 0, p95: 0, p99: 0, max: 0 };
|
|
109
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
110
|
+
const pick = (p) => sorted[Math.min(sorted.length - 1, Math.floor((sorted.length - 1) * p))];
|
|
111
|
+
return {
|
|
112
|
+
min: sorted[0],
|
|
113
|
+
mean: sorted.reduce((sum, value) => sum + value, 0) / sorted.length,
|
|
114
|
+
p50: pick(0.5),
|
|
115
|
+
p90: pick(0.9),
|
|
116
|
+
p95: pick(0.95),
|
|
117
|
+
p99: pick(0.99),
|
|
118
|
+
max: sorted[sorted.length - 1]
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
var TelemetryController = class {
|
|
122
|
+
constructor(serviceId, options = {}) {
|
|
123
|
+
this.serviceId = serviceId;
|
|
124
|
+
this.mode = options.mode ?? "off";
|
|
125
|
+
this.maxSamples = Math.max(1, options.maxSamples ?? 2048);
|
|
126
|
+
this.maxRecentInvocations = Math.max(1, options.maxRecentInvocations ?? 100);
|
|
127
|
+
}
|
|
128
|
+
serviceId;
|
|
129
|
+
mode;
|
|
130
|
+
maxSamples;
|
|
131
|
+
maxRecentInvocations;
|
|
132
|
+
samples = [];
|
|
133
|
+
active = /* @__PURE__ */ new Map();
|
|
134
|
+
totalInvocationCount = 0;
|
|
135
|
+
totalSuccessCount = 0;
|
|
136
|
+
totalErrorCount = 0;
|
|
137
|
+
lastInvocationAt;
|
|
138
|
+
enable(mode = "standard") {
|
|
139
|
+
this.mode = mode;
|
|
140
|
+
}
|
|
141
|
+
disable() {
|
|
142
|
+
this.mode = "off";
|
|
143
|
+
this.active.clear();
|
|
144
|
+
}
|
|
145
|
+
setMode(mode) {
|
|
146
|
+
this.mode = mode;
|
|
147
|
+
if (mode === "off") this.active.clear();
|
|
148
|
+
}
|
|
149
|
+
getMode() {
|
|
150
|
+
return this.mode;
|
|
151
|
+
}
|
|
152
|
+
status() {
|
|
153
|
+
return {
|
|
154
|
+
enabled: this.mode !== "off",
|
|
155
|
+
mode: this.mode,
|
|
156
|
+
serviceId: this.serviceId,
|
|
157
|
+
invocationCount: this.totalInvocationCount,
|
|
158
|
+
activeInvocations: this.active.size,
|
|
159
|
+
lastInvocationAt: this.lastInvocationAt
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
begin(target, kind, correlationId, parentSpanId) {
|
|
163
|
+
if (this.mode === "off") {
|
|
164
|
+
return {
|
|
165
|
+
invocationId: "",
|
|
166
|
+
traceId: "",
|
|
167
|
+
spanId: "",
|
|
168
|
+
parentSpanId,
|
|
169
|
+
end: () => void 0
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const invocationId = id("inv");
|
|
173
|
+
const spanId = id("span");
|
|
174
|
+
const traceId = parentSpanId ? this.findTraceId(parentSpanId) ?? id("trace") : id("trace");
|
|
175
|
+
const active = {
|
|
176
|
+
invocationId,
|
|
177
|
+
traceId,
|
|
178
|
+
spanId,
|
|
179
|
+
parentSpanId,
|
|
180
|
+
serviceId: this.serviceId,
|
|
181
|
+
target,
|
|
182
|
+
kind,
|
|
183
|
+
correlationId,
|
|
184
|
+
startedAt: telemetryNow(),
|
|
185
|
+
resourcesStart: this.mode === "standard" ? void 0 : sampleResources()
|
|
186
|
+
};
|
|
187
|
+
this.active.set(invocationId, active);
|
|
188
|
+
return {
|
|
189
|
+
invocationId,
|
|
190
|
+
traceId,
|
|
191
|
+
spanId,
|
|
192
|
+
parentSpanId,
|
|
193
|
+
end: (error) => this.finish(active, error)
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
reset() {
|
|
197
|
+
this.samples.length = 0;
|
|
198
|
+
this.active.clear();
|
|
199
|
+
this.totalInvocationCount = 0;
|
|
200
|
+
this.totalSuccessCount = 0;
|
|
201
|
+
this.totalErrorCount = 0;
|
|
202
|
+
this.lastInvocationAt = void 0;
|
|
203
|
+
}
|
|
204
|
+
snapshot() {
|
|
205
|
+
const invocations = this.samples.slice();
|
|
206
|
+
const durations = invocations.map((item) => item.durationMs);
|
|
207
|
+
const targetMap = /* @__PURE__ */ new Map();
|
|
208
|
+
for (const invocation of invocations) {
|
|
209
|
+
const key = `${invocation.kind}:${invocation.target}`;
|
|
210
|
+
const group = targetMap.get(key) ?? [];
|
|
211
|
+
group.push(invocation);
|
|
212
|
+
targetMap.set(key, group);
|
|
213
|
+
}
|
|
214
|
+
const targets = [...targetMap.values()].map((group) => ({
|
|
215
|
+
target: group[0].target,
|
|
216
|
+
kind: group[0].kind,
|
|
217
|
+
count: group.length,
|
|
218
|
+
successCount: group.filter((item) => item.status === "completed").length,
|
|
219
|
+
errorCount: group.filter((item) => item.status === "failed").length,
|
|
220
|
+
durationMs: percentiles(group.map((item) => item.durationMs)),
|
|
221
|
+
lastInvocationAt: group[group.length - 1]?.completedAt
|
|
222
|
+
}));
|
|
223
|
+
return {
|
|
224
|
+
serviceId: this.serviceId,
|
|
225
|
+
mode: this.mode,
|
|
226
|
+
capturedAt: Date.now(),
|
|
227
|
+
status: this.status(),
|
|
228
|
+
totals: {
|
|
229
|
+
invocationCount: this.totalInvocationCount,
|
|
230
|
+
successCount: this.totalSuccessCount,
|
|
231
|
+
errorCount: this.totalErrorCount,
|
|
232
|
+
durationMs: percentiles(durations)
|
|
233
|
+
},
|
|
234
|
+
targets,
|
|
235
|
+
activeInvocations: this.active.size,
|
|
236
|
+
recentInvocations: invocations.slice(-this.maxRecentInvocations)
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
functionality(name) {
|
|
240
|
+
return this.snapshot().targets.find((target) => target.kind === "functionality" && target.target === name);
|
|
241
|
+
}
|
|
242
|
+
functionalities() {
|
|
243
|
+
return this.snapshot().targets.filter((target) => target.kind === "functionality");
|
|
244
|
+
}
|
|
245
|
+
findTraceId(parentSpanId) {
|
|
246
|
+
for (let index = this.samples.length - 1; index >= 0; index -= 1) {
|
|
247
|
+
if (this.samples[index].spanId === parentSpanId) return this.samples[index].traceId;
|
|
248
|
+
}
|
|
249
|
+
for (const active of this.active.values()) {
|
|
250
|
+
if (active.spanId === parentSpanId) return active.traceId;
|
|
251
|
+
}
|
|
252
|
+
return void 0;
|
|
253
|
+
}
|
|
254
|
+
finish(active, error) {
|
|
255
|
+
if (!this.active.delete(active.invocationId)) return;
|
|
256
|
+
const completedAt = telemetryNow();
|
|
257
|
+
const resourcesEnd = this.mode === "standard" ? void 0 : sampleResources();
|
|
258
|
+
const invocation = {
|
|
259
|
+
invocationId: active.invocationId,
|
|
260
|
+
traceId: active.traceId,
|
|
261
|
+
spanId: active.spanId,
|
|
262
|
+
parentSpanId: active.parentSpanId,
|
|
263
|
+
serviceId: active.serviceId,
|
|
264
|
+
target: active.target,
|
|
265
|
+
kind: active.kind,
|
|
266
|
+
correlationId: active.correlationId,
|
|
267
|
+
startedAt: active.startedAt,
|
|
268
|
+
completedAt,
|
|
269
|
+
durationMs: Math.max(0, completedAt - active.startedAt),
|
|
270
|
+
status: error === void 0 ? "completed" : "failed",
|
|
271
|
+
error: error === void 0 ? void 0 : {
|
|
272
|
+
name: error instanceof Error ? error.name : "Error",
|
|
273
|
+
message: error instanceof Error ? error.message : String(error)
|
|
274
|
+
},
|
|
275
|
+
resources: active.resourcesStart && resourcesEnd ? { start: active.resourcesStart, end: resourcesEnd, delta: deltaResources(active.resourcesStart, resourcesEnd) } : void 0
|
|
276
|
+
};
|
|
277
|
+
this.samples.push(invocation);
|
|
278
|
+
this.totalInvocationCount += 1;
|
|
279
|
+
if (invocation.status === "completed") this.totalSuccessCount += 1;
|
|
280
|
+
else this.totalErrorCount += 1;
|
|
281
|
+
if (this.samples.length > this.maxSamples) this.samples.splice(0, this.samples.length - this.maxSamples);
|
|
282
|
+
this.lastInvocationAt = Date.now();
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
|
|
94
286
|
// src/services/ServiceInstance.ts
|
|
95
287
|
var ServiceInstance = class {
|
|
96
288
|
constructor(definition) {
|
|
@@ -98,8 +290,8 @@ var ServiceInstance = class {
|
|
|
98
290
|
this.id = definition.id;
|
|
99
291
|
this.name = definition.name;
|
|
100
292
|
this.version = definition.version;
|
|
293
|
+
this.telemetry = new TelemetryController(this.id);
|
|
101
294
|
this.lifecycle = new LifecycleManager();
|
|
102
|
-
this.eventBus = new ServiceEventBus();
|
|
103
295
|
if (definition.state) {
|
|
104
296
|
this.stateStore = new ServiceStateStore(definition.state.initial);
|
|
105
297
|
}
|
|
@@ -108,8 +300,8 @@ var ServiceInstance = class {
|
|
|
108
300
|
id;
|
|
109
301
|
name;
|
|
110
302
|
version;
|
|
303
|
+
telemetry;
|
|
111
304
|
stateStore;
|
|
112
|
-
eventBus;
|
|
113
305
|
lifecycle;
|
|
114
306
|
initialized = false;
|
|
115
307
|
disposed = false;
|
|
@@ -173,7 +365,19 @@ var ServiceInstance = class {
|
|
|
173
365
|
if (!operation) {
|
|
174
366
|
throw new Error("[Service:" + this.name + "] Operation '" + op + "' not found.");
|
|
175
367
|
}
|
|
176
|
-
|
|
368
|
+
if (ctx.metadata?.__krnlTelemetryInternal === true) {
|
|
369
|
+
return await operation.handler(input, ctx);
|
|
370
|
+
}
|
|
371
|
+
const parentSpanId = typeof ctx.metadata?.__krnlTelemetryParentSpanId === "string" ? ctx.metadata.__krnlTelemetryParentSpanId : void 0;
|
|
372
|
+
const handle = this.telemetry.begin(op, "service", ctx.correlationId, parentSpanId);
|
|
373
|
+
try {
|
|
374
|
+
const output = await operation.handler(input, ctx);
|
|
375
|
+
handle.end();
|
|
376
|
+
return output;
|
|
377
|
+
} catch (error) {
|
|
378
|
+
handle.end(error);
|
|
379
|
+
throw error;
|
|
380
|
+
}
|
|
177
381
|
}
|
|
178
382
|
getState() {
|
|
179
383
|
return this.stateStore?.getState() ?? Object.freeze({});
|
|
@@ -185,12 +389,6 @@ var ServiceInstance = class {
|
|
|
185
389
|
}
|
|
186
390
|
this.stateStore.setState(mutation);
|
|
187
391
|
}
|
|
188
|
-
on(event, handler) {
|
|
189
|
-
return this.eventBus.on(event, handler);
|
|
190
|
-
}
|
|
191
|
-
emit(event, payload) {
|
|
192
|
-
this.eventBus.emit(event, payload);
|
|
193
|
-
}
|
|
194
392
|
};
|
|
195
393
|
|
|
196
394
|
// src/services/ServiceProxy.ts
|
|
@@ -211,6 +409,7 @@ var ServiceProxy = class {
|
|
|
211
409
|
this.id = definition.id;
|
|
212
410
|
this.name = definition.name;
|
|
213
411
|
this.version = definition.version;
|
|
412
|
+
this.telemetry = new TelemetryController(this.id);
|
|
214
413
|
this.stateCache = { ...definition.state?.initial ?? {} };
|
|
215
414
|
}
|
|
216
415
|
definition;
|
|
@@ -219,6 +418,7 @@ var ServiceProxy = class {
|
|
|
219
418
|
id;
|
|
220
419
|
name;
|
|
221
420
|
version;
|
|
421
|
+
telemetry;
|
|
222
422
|
stateCache;
|
|
223
423
|
eventHandlers = /* @__PURE__ */ new Map();
|
|
224
424
|
lifecycle = new LifecycleManager();
|
|
@@ -276,11 +476,21 @@ var ServiceProxy = class {
|
|
|
276
476
|
payload: input,
|
|
277
477
|
timestamp: Date.now()
|
|
278
478
|
};
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
479
|
+
const parentSpanId = typeof ctx.metadata?.__krnlTelemetryParentSpanId === "string" ? ctx.metadata.__krnlTelemetryParentSpanId : void 0;
|
|
480
|
+
const handle = this.telemetry.begin(op, "service", ctx.correlationId, parentSpanId);
|
|
481
|
+
try {
|
|
482
|
+
const response = await this.adapter.send(this.endpoint, envelope);
|
|
483
|
+
if (response.error) {
|
|
484
|
+
const error = new Error("[ServiceProxy:" + this.name + "] Remote error: " + response.error);
|
|
485
|
+
handle.end(error);
|
|
486
|
+
throw error;
|
|
487
|
+
}
|
|
488
|
+
handle.end();
|
|
489
|
+
return response.payload;
|
|
490
|
+
} catch (error) {
|
|
491
|
+
handle.end(error);
|
|
492
|
+
throw error;
|
|
282
493
|
}
|
|
283
|
-
return response.payload;
|
|
284
494
|
}
|
|
285
495
|
getState() {
|
|
286
496
|
return Object.freeze({ ...this.stateCache });
|
|
@@ -340,12 +550,6 @@ function validateServiceDefinition(definition) {
|
|
|
340
550
|
throw new Error("[Service:" + definition.name + "] Operation '" + key + "' must define a handler.");
|
|
341
551
|
}
|
|
342
552
|
}
|
|
343
|
-
for (const [key, event] of Object.entries(definition.events ?? {})) {
|
|
344
|
-
if (!event || typeof event !== "object") {
|
|
345
|
-
throw new Error("[Service:" + definition.name + "] Invalid event '" + key + "'.");
|
|
346
|
-
}
|
|
347
|
-
assertNonEmptyString(event.name, "events." + key + ".name");
|
|
348
|
-
}
|
|
349
553
|
for (const [key, schema] of Object.entries(definition.inputs ?? {})) assertRecord(schema, "inputs." + key);
|
|
350
554
|
for (const [key, schema] of Object.entries(definition.outputs ?? {})) assertRecord(schema, "outputs." + key);
|
|
351
555
|
if (definition.state?.schema !== void 0) assertRecord(definition.state.schema, "state.schema");
|
|
@@ -378,6 +582,137 @@ function createServiceProxy(definition, adapter, endpoint) {
|
|
|
378
582
|
return new ServiceProxy(definition, adapter, endpoint);
|
|
379
583
|
}
|
|
380
584
|
|
|
585
|
+
// src/events/EventBus.ts
|
|
586
|
+
var EventBus = class {
|
|
587
|
+
handlers = /* @__PURE__ */ new Map();
|
|
588
|
+
subscribe(event, handler) {
|
|
589
|
+
let listeners = this.handlers.get(event);
|
|
590
|
+
if (!listeners) {
|
|
591
|
+
listeners = /* @__PURE__ */ new Set();
|
|
592
|
+
this.handlers.set(event, listeners);
|
|
593
|
+
}
|
|
594
|
+
listeners.add(handler);
|
|
595
|
+
return {
|
|
596
|
+
unsubscribe: () => {
|
|
597
|
+
listeners?.delete(handler);
|
|
598
|
+
if (listeners && listeners.size === 0) this.handlers.delete(event);
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
on(event, handler) {
|
|
603
|
+
return this.subscribe(event, handler).unsubscribe;
|
|
604
|
+
}
|
|
605
|
+
emit(event, payload) {
|
|
606
|
+
const listeners = this.handlers.get(event);
|
|
607
|
+
if (!listeners) return;
|
|
608
|
+
for (const listener of Array.from(listeners)) {
|
|
609
|
+
try {
|
|
610
|
+
listener(payload);
|
|
611
|
+
} catch (error) {
|
|
612
|
+
console.error(`[EventBus] Handler error for '${event}':`, error);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
clear() {
|
|
617
|
+
this.handlers.clear();
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
function createEventChannels(bus, names) {
|
|
621
|
+
const channels = {};
|
|
622
|
+
for (const name of names) {
|
|
623
|
+
channels[name] = {
|
|
624
|
+
name,
|
|
625
|
+
subscribe: (handler) => bus.subscribe(name, handler),
|
|
626
|
+
on: (handler) => bus.on(name, handler)
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
return channels;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// src/functionalities/FunctionalityFacade.ts
|
|
633
|
+
var FunctionalityFacade = class {
|
|
634
|
+
id;
|
|
635
|
+
name;
|
|
636
|
+
description;
|
|
637
|
+
bubbleEvents;
|
|
638
|
+
definition;
|
|
639
|
+
eventBus;
|
|
640
|
+
events;
|
|
641
|
+
constructor(id2, definition, options = {}) {
|
|
642
|
+
if (!id2 || typeof id2 !== "string") {
|
|
643
|
+
throw new Error("[FunctionalityFacade] Functionality id must be a non-empty string.");
|
|
644
|
+
}
|
|
645
|
+
if (!definition || typeof definition.execute !== "function") {
|
|
646
|
+
throw new Error(`[FunctionalityFacade:${id2}] Functionality must define execute().`);
|
|
647
|
+
}
|
|
648
|
+
this.id = id2;
|
|
649
|
+
this.name = definition.name || id2;
|
|
650
|
+
this.description = definition.description || "";
|
|
651
|
+
this.bubbleEvents = options.bubbleEvents ?? definition.bubbleEvents ?? true;
|
|
652
|
+
this.definition = definition;
|
|
653
|
+
this.eventBus = new EventBus();
|
|
654
|
+
for (const reserved of ["triggered", "completed", "failed"]) {
|
|
655
|
+
if (definition.events && reserved in definition.events) {
|
|
656
|
+
throw new Error(`[FunctionalityFacade:${id2}] Event '${reserved}' is reserved for functionality lifecycle.`);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
const eventNames = [
|
|
660
|
+
"triggered",
|
|
661
|
+
"completed",
|
|
662
|
+
"failed",
|
|
663
|
+
...Object.keys(definition.events ?? {})
|
|
664
|
+
];
|
|
665
|
+
this.events = createEventChannels(this.eventBus, eventNames);
|
|
666
|
+
}
|
|
667
|
+
getEventDefinitions() {
|
|
668
|
+
return {
|
|
669
|
+
triggered: { name: "triggered", description: "Functionality execution started." },
|
|
670
|
+
completed: { name: "completed", description: "Functionality execution completed successfully." },
|
|
671
|
+
failed: { name: "failed", description: "Functionality execution failed." },
|
|
672
|
+
...this.definition.events ?? {}
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
subscribe(event, handler) {
|
|
676
|
+
return this.eventBus.on(event, handler);
|
|
677
|
+
}
|
|
678
|
+
emit(event, payload) {
|
|
679
|
+
this.eventBus.emit(event, payload);
|
|
680
|
+
}
|
|
681
|
+
async execute(input, context) {
|
|
682
|
+
const lifecycle = {
|
|
683
|
+
serviceId: context.serviceId,
|
|
684
|
+
functionalityId: this.id,
|
|
685
|
+
functionalityName: this.name,
|
|
686
|
+
correlationId: context.correlationId,
|
|
687
|
+
timestamp: Date.now(),
|
|
688
|
+
input
|
|
689
|
+
};
|
|
690
|
+
this.emit("triggered", lifecycle);
|
|
691
|
+
try {
|
|
692
|
+
const output = await this.definition.execute(input, {
|
|
693
|
+
...context,
|
|
694
|
+
emit: (event, payload) => this.emit(event, payload)
|
|
695
|
+
});
|
|
696
|
+
this.emit("completed", {
|
|
697
|
+
...lifecycle,
|
|
698
|
+
timestamp: Date.now(),
|
|
699
|
+
output
|
|
700
|
+
});
|
|
701
|
+
return output;
|
|
702
|
+
} catch (error) {
|
|
703
|
+
this.emit("failed", {
|
|
704
|
+
...lifecycle,
|
|
705
|
+
timestamp: Date.now(),
|
|
706
|
+
error: error?.message || String(error)
|
|
707
|
+
});
|
|
708
|
+
throw error;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
clearEventSubscriptions() {
|
|
712
|
+
this.eventBus.clear();
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
|
|
381
716
|
// ../../node_modules/.pnpm/p-queue@10.0.0/node_modules/p-queue/dist/lower-bound.js
|
|
382
717
|
function lowerBound(array, value, comparator) {
|
|
383
718
|
let first = 0;
|
|
@@ -432,11 +767,11 @@ var PriorityQueue = class {
|
|
|
432
767
|
return item.run;
|
|
433
768
|
}
|
|
434
769
|
enqueue(run, options) {
|
|
435
|
-
const { priority = 0, id } = options ?? {};
|
|
770
|
+
const { priority = 0, id: id2 } = options ?? {};
|
|
436
771
|
const { size } = this;
|
|
437
772
|
const element = {
|
|
438
773
|
priority,
|
|
439
|
-
id,
|
|
774
|
+
id: id2,
|
|
440
775
|
run
|
|
441
776
|
};
|
|
442
777
|
if (size === 0) {
|
|
@@ -453,16 +788,16 @@ var PriorityQueue = class {
|
|
|
453
788
|
const index = lowerBound(this.#queue, element, (a, b) => a.priority === b.priority ? 0 : b.priority - a.priority);
|
|
454
789
|
this.#queue.splice(index, 0, element);
|
|
455
790
|
}
|
|
456
|
-
setPriority(
|
|
457
|
-
const index = this.#findIndex((element) => element.id ===
|
|
791
|
+
setPriority(id2, priority) {
|
|
792
|
+
const index = this.#findIndex((element) => element.id === id2);
|
|
458
793
|
if (index === -1) {
|
|
459
|
-
throw new ReferenceError(`No promise function with the id "${
|
|
794
|
+
throw new ReferenceError(`No promise function with the id "${id2}" exists in the queue.`);
|
|
460
795
|
}
|
|
461
796
|
if (this.#queue[index].priority === priority) {
|
|
462
797
|
return;
|
|
463
798
|
}
|
|
464
799
|
const [item] = this.#queue.splice(index, 1);
|
|
465
|
-
this.enqueue(item.run, { priority, id });
|
|
800
|
+
this.enqueue(item.run, { priority, id: id2 });
|
|
466
801
|
}
|
|
467
802
|
remove(idOrRun) {
|
|
468
803
|
const index = this.#findIndex((element) => (typeof idOrRun === "string" ? element.id : element.run) === idOrRun);
|
|
@@ -878,17 +1213,17 @@ var PQueue = class extends EventTarget {
|
|
|
878
1213
|
}
|
|
879
1214
|
}
|
|
880
1215
|
async #add(function_, options, waitingTask) {
|
|
881
|
-
const
|
|
1216
|
+
const id2 = options.id ?? (this.#idAssigner++).toString();
|
|
882
1217
|
options = {
|
|
883
1218
|
timeout: this.#timeout,
|
|
884
1219
|
...options,
|
|
885
|
-
id
|
|
1220
|
+
id: id2
|
|
886
1221
|
};
|
|
887
1222
|
validateTimeout(options.timeout);
|
|
888
1223
|
if (options.priority !== void 0 && (typeof options.priority !== "number" || Number.isNaN(options.priority))) {
|
|
889
1224
|
throw new TypeError(`Expected \`priority\` to be a number, got \`${String(options.priority)}\` (${typeof options.priority})`);
|
|
890
1225
|
}
|
|
891
|
-
waitingTask.id =
|
|
1226
|
+
waitingTask.id = id2;
|
|
892
1227
|
waitingTask.priority = options.priority ?? 0;
|
|
893
1228
|
return new Promise((resolve, reject) => {
|
|
894
1229
|
if (options.signal?.aborted) {
|
|
@@ -905,7 +1240,7 @@ var PQueue = class extends EventTarget {
|
|
|
905
1240
|
}
|
|
906
1241
|
this.#pending++;
|
|
907
1242
|
const runningTask = {
|
|
908
|
-
id,
|
|
1243
|
+
id: id2,
|
|
909
1244
|
priority: waitingTask.priority,
|
|
910
1245
|
startTime: Date.now(),
|
|
911
1246
|
timeout: options.timeout
|
|
@@ -972,7 +1307,7 @@ var PQueue = class extends EventTarget {
|
|
|
972
1307
|
if (this.#queue instanceof PriorityQueue) {
|
|
973
1308
|
this.#queue.remove(run);
|
|
974
1309
|
} else {
|
|
975
|
-
this.#queue.remove(
|
|
1310
|
+
this.#queue.remove(id2);
|
|
976
1311
|
}
|
|
977
1312
|
reject(signal.reason);
|
|
978
1313
|
this.#processQueue();
|
|
@@ -1048,18 +1383,18 @@ var PQueue = class extends EventTarget {
|
|
|
1048
1383
|
// '🦀' runs last
|
|
1049
1384
|
```
|
|
1050
1385
|
*/
|
|
1051
|
-
setPriority(
|
|
1386
|
+
setPriority(id2, priority) {
|
|
1052
1387
|
if (typeof priority !== "number" || !Number.isFinite(priority)) {
|
|
1053
1388
|
throw new TypeError(`Expected \`priority\` to be a finite number, got \`${priority}\` (${typeof priority})`);
|
|
1054
1389
|
}
|
|
1055
1390
|
for (const waitingTask of this.#waitingTasks) {
|
|
1056
|
-
if (waitingTask.id ===
|
|
1057
|
-
this.#queue.setPriority(
|
|
1391
|
+
if (waitingTask.id === id2) {
|
|
1392
|
+
this.#queue.setPriority(id2, priority);
|
|
1058
1393
|
waitingTask.priority = priority;
|
|
1059
1394
|
return;
|
|
1060
1395
|
}
|
|
1061
1396
|
}
|
|
1062
|
-
throw new ReferenceError(`No promise function with the id "${
|
|
1397
|
+
throw new ReferenceError(`No promise function with the id "${id2}" exists in the queue.`);
|
|
1063
1398
|
}
|
|
1064
1399
|
/**
|
|
1065
1400
|
Adds a sync or async task to the queue.
|
|
@@ -1402,7 +1737,7 @@ var TimeoutError = class extends DOMException {
|
|
|
1402
1737
|
};
|
|
1403
1738
|
|
|
1404
1739
|
// src/orchestration/canonical/KrnlQ.ts
|
|
1405
|
-
import { randomUUID } from "crypto";
|
|
1740
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
1406
1741
|
|
|
1407
1742
|
// src/orchestration/canonical/model.ts
|
|
1408
1743
|
function validateOrchestrationDAG(steps) {
|
|
@@ -1469,8 +1804,8 @@ function validateOrchestrationDAG(steps) {
|
|
|
1469
1804
|
}
|
|
1470
1805
|
}
|
|
1471
1806
|
const queue = [];
|
|
1472
|
-
for (const [
|
|
1473
|
-
if (deg === 0) queue.push(
|
|
1807
|
+
for (const [id2, deg] of inDegree.entries()) {
|
|
1808
|
+
if (deg === 0) queue.push(id2);
|
|
1474
1809
|
}
|
|
1475
1810
|
const order = [];
|
|
1476
1811
|
while (queue.length > 0) {
|
|
@@ -1606,7 +1941,7 @@ var KrnlWorkerPool = class {
|
|
|
1606
1941
|
// src/orchestration/canonical/KrnlQ.ts
|
|
1607
1942
|
function generateSecureId(prefix) {
|
|
1608
1943
|
try {
|
|
1609
|
-
return `${prefix}_${
|
|
1944
|
+
return `${prefix}_${randomUUID2()}`;
|
|
1610
1945
|
} catch {
|
|
1611
1946
|
return `${prefix}_${Math.random().toString(36).substring(2, 11)}_${Date.now()}`;
|
|
1612
1947
|
}
|
|
@@ -1674,9 +2009,9 @@ var KrnlQueue = class {
|
|
|
1674
2009
|
if (this.jobs.size <= this.maxRetainedJobs) return;
|
|
1675
2010
|
const excess = this.jobs.size - this.maxRetainedJobs;
|
|
1676
2011
|
let pruned = 0;
|
|
1677
|
-
for (const [
|
|
2012
|
+
for (const [id2, record] of this.jobs.entries()) {
|
|
1678
2013
|
if (record.status === "completed" || record.status === "failed" || record.status === "cancelled") {
|
|
1679
|
-
this.jobs.delete(
|
|
2014
|
+
this.jobs.delete(id2);
|
|
1680
2015
|
pruned++;
|
|
1681
2016
|
if (pruned >= excess) break;
|
|
1682
2017
|
}
|
|
@@ -2290,6 +2625,7 @@ var ServiceFacade = class {
|
|
|
2290
2625
|
defaultFrontend;
|
|
2291
2626
|
instance;
|
|
2292
2627
|
queue;
|
|
2628
|
+
telemetry;
|
|
2293
2629
|
constructor(config) {
|
|
2294
2630
|
if (!config || !config.id || typeof config.id !== "string") {
|
|
2295
2631
|
throw new Error("[ServiceFacade] ServiceFacade requires a valid id.");
|
|
@@ -2298,7 +2634,10 @@ var ServiceFacade = class {
|
|
|
2298
2634
|
this.name = config.name || config.id;
|
|
2299
2635
|
this.version = config.version || "1.0.0";
|
|
2300
2636
|
this.description = config.description || "";
|
|
2301
|
-
this.functionalities =
|
|
2637
|
+
this.functionalities = {};
|
|
2638
|
+
for (const [name, functionality] of Object.entries(config.functionalities ?? {})) {
|
|
2639
|
+
this.registerFunctionality(name, functionality);
|
|
2640
|
+
}
|
|
2302
2641
|
this.functionalityOrchestrations = config.functionalityOrchestrations ? { ...config.functionalityOrchestrations } : config.orchestrations ? { ...config.orchestrations } : {};
|
|
2303
2642
|
this.libraries = config.libraries ? { ...config.libraries } : {};
|
|
2304
2643
|
this.dependencies = Object.freeze(config.dependencies ? [...config.dependencies] : []);
|
|
@@ -2316,6 +2655,7 @@ var ServiceFacade = class {
|
|
|
2316
2655
|
operations: {}
|
|
2317
2656
|
});
|
|
2318
2657
|
}
|
|
2658
|
+
this.telemetry = this.instance.telemetry;
|
|
2319
2659
|
this.queue = new KrnlQueue({
|
|
2320
2660
|
queueName: `krnl_queue_${this.id}`,
|
|
2321
2661
|
concurrency: config.concurrency ?? config.queueOptions?.concurrency ?? 10,
|
|
@@ -2359,6 +2699,8 @@ var ServiceFacade = class {
|
|
|
2359
2699
|
*/
|
|
2360
2700
|
async call(op, input, ctx) {
|
|
2361
2701
|
const correlationId = ctx?.correlationId || `corr_${Math.random().toString(36).substring(2, 11)}`;
|
|
2702
|
+
const telemetryParent = typeof ctx?.metadata?.__krnlTelemetryParentSpanId === "string" ? ctx.metadata.__krnlTelemetryParentSpanId : void 0;
|
|
2703
|
+
const telemetryHandle = this.telemetry.begin(op, "service", correlationId, telemetryParent);
|
|
2362
2704
|
const commandId = `cmd_${this.id}_${op}_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;
|
|
2363
2705
|
const command = {
|
|
2364
2706
|
id: commandId,
|
|
@@ -2368,23 +2710,48 @@ var ServiceFacade = class {
|
|
|
2368
2710
|
idempotencyKey: ctx?.idempotencyKey,
|
|
2369
2711
|
createdAt: Date.now()
|
|
2370
2712
|
};
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2713
|
+
try {
|
|
2714
|
+
const result = await this.queue.executeCommand(command, async (payload, workerCtx) => {
|
|
2715
|
+
WorkerExecutionBoundary.assertWorkerContext(workerCtx);
|
|
2716
|
+
const functionality = this.functionalities[op];
|
|
2717
|
+
if (functionality) {
|
|
2718
|
+
const functionalityTelemetry = this.telemetry.begin(
|
|
2719
|
+
functionality.name,
|
|
2720
|
+
"functionality",
|
|
2721
|
+
workerCtx.correlationId,
|
|
2722
|
+
telemetryHandle.spanId
|
|
2723
|
+
);
|
|
2724
|
+
try {
|
|
2725
|
+
return await functionality.execute(payload, {
|
|
2726
|
+
serviceId: this.id,
|
|
2727
|
+
functionalityId: functionality.id,
|
|
2728
|
+
correlationId: workerCtx.correlationId,
|
|
2729
|
+
state: this.instance.getState()
|
|
2730
|
+
});
|
|
2731
|
+
} catch (error) {
|
|
2732
|
+
functionalityTelemetry.end(error);
|
|
2733
|
+
throw error;
|
|
2734
|
+
} finally {
|
|
2735
|
+
functionalityTelemetry.end();
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
return await this.instance.call(op, payload, {
|
|
2375
2739
|
correlationId: workerCtx.correlationId,
|
|
2376
|
-
|
|
2740
|
+
idempotencyKey: command.idempotencyKey,
|
|
2741
|
+
metadata: { __krnlTelemetryInternal: true }
|
|
2377
2742
|
});
|
|
2378
|
-
}
|
|
2379
|
-
return await this.instance.call(op, payload, {
|
|
2380
|
-
correlationId: workerCtx.correlationId,
|
|
2381
|
-
idempotencyKey: command.idempotencyKey
|
|
2382
2743
|
});
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2744
|
+
if (result.status === "FAILED") {
|
|
2745
|
+
const error = new Error(result.error || `[ServiceFacade:${this.id}] Operation '${op}' execution failed.`);
|
|
2746
|
+
telemetryHandle.end(error);
|
|
2747
|
+
throw error;
|
|
2748
|
+
}
|
|
2749
|
+
telemetryHandle.end();
|
|
2750
|
+
return result.data;
|
|
2751
|
+
} catch (error) {
|
|
2752
|
+
telemetryHandle.end(error);
|
|
2753
|
+
throw error;
|
|
2386
2754
|
}
|
|
2387
|
-
return result.data;
|
|
2388
2755
|
}
|
|
2389
2756
|
/**
|
|
2390
2757
|
* External caller callable boundary for functionality orchestration execution.
|
|
@@ -2398,6 +2765,18 @@ var ServiceFacade = class {
|
|
|
2398
2765
|
);
|
|
2399
2766
|
}
|
|
2400
2767
|
const correlationId = options.correlationId || `corr_${Math.random().toString(36).substring(2, 11)}`;
|
|
2768
|
+
const serviceTelemetryHandle = this.telemetry.begin(
|
|
2769
|
+
`orchestrate:${orchestrationName}`,
|
|
2770
|
+
"service",
|
|
2771
|
+
correlationId,
|
|
2772
|
+
typeof options.metadata?.__krnlTelemetryParentSpanId === "string" ? options.metadata.__krnlTelemetryParentSpanId : void 0
|
|
2773
|
+
);
|
|
2774
|
+
const telemetryHandle = this.telemetry.begin(
|
|
2775
|
+
orchestrationName,
|
|
2776
|
+
"orchestration",
|
|
2777
|
+
correlationId,
|
|
2778
|
+
serviceTelemetryHandle.spanId
|
|
2779
|
+
);
|
|
2401
2780
|
const commandId = `cmd_orch_${this.id}_${orchestrationName}_${Date.now()}`;
|
|
2402
2781
|
const command = {
|
|
2403
2782
|
id: commandId,
|
|
@@ -2407,6 +2786,13 @@ var ServiceFacade = class {
|
|
|
2407
2786
|
idempotencyKey: options.idempotencyKey,
|
|
2408
2787
|
createdAt: Date.now()
|
|
2409
2788
|
};
|
|
2789
|
+
orch.events.emit("triggered", {
|
|
2790
|
+
serviceId: this.id,
|
|
2791
|
+
orchestrationId: orch.id,
|
|
2792
|
+
orchestrationName: orch.name,
|
|
2793
|
+
correlationId,
|
|
2794
|
+
timestamp: Date.now()
|
|
2795
|
+
});
|
|
2410
2796
|
const cmdResult = await this.queue.executeCommand(
|
|
2411
2797
|
command,
|
|
2412
2798
|
async (payload, workerCtx) => {
|
|
@@ -2438,7 +2824,8 @@ var ServiceFacade = class {
|
|
|
2438
2824
|
...s.compensation,
|
|
2439
2825
|
handler: async (compCtx) => {
|
|
2440
2826
|
await this.call(s.compensation.target, compCtx.results[s.id], {
|
|
2441
|
-
correlationId: compCtx.correlationId
|
|
2827
|
+
correlationId: compCtx.correlationId,
|
|
2828
|
+
metadata: { __krnlTelemetryParentSpanId: telemetryHandle.spanId }
|
|
2442
2829
|
});
|
|
2443
2830
|
}
|
|
2444
2831
|
};
|
|
@@ -2454,7 +2841,8 @@ var ServiceFacade = class {
|
|
|
2454
2841
|
stepPayload.input = stepCtx.results.input;
|
|
2455
2842
|
}
|
|
2456
2843
|
return await this.call(s.target, stepPayload, {
|
|
2457
|
-
correlationId: stepCtx.correlationId
|
|
2844
|
+
correlationId: stepCtx.correlationId,
|
|
2845
|
+
metadata: { __krnlTelemetryParentSpanId: telemetryHandle.spanId }
|
|
2458
2846
|
});
|
|
2459
2847
|
}
|
|
2460
2848
|
};
|
|
@@ -2474,7 +2862,8 @@ var ServiceFacade = class {
|
|
|
2474
2862
|
const nestedResult = await this.orchestrate(childOrchId, childInput, {
|
|
2475
2863
|
correlationId: childCtx.correlationId,
|
|
2476
2864
|
timeoutMs: childOrch.timeoutMs,
|
|
2477
|
-
abortSignal: options.abortSignal
|
|
2865
|
+
abortSignal: options.abortSignal,
|
|
2866
|
+
metadata: { __krnlTelemetryParentSpanId: telemetryHandle.spanId }
|
|
2478
2867
|
});
|
|
2479
2868
|
if (nestedResult.status !== "COMPLETED") {
|
|
2480
2869
|
throw new Error(nestedResult.error || `Nested orchestration "${childOrchId}" failed.`);
|
|
@@ -2496,9 +2885,39 @@ var ServiceFacade = class {
|
|
|
2496
2885
|
);
|
|
2497
2886
|
}
|
|
2498
2887
|
);
|
|
2499
|
-
|
|
2500
|
-
|
|
2888
|
+
const orchestrationResult = cmdResult.data;
|
|
2889
|
+
if (cmdResult.status === "COMPLETED" && orchestrationResult) {
|
|
2890
|
+
const lifecycleEvent = orchestrationResult.status === "COMPLETED" ? "completed" : "failed";
|
|
2891
|
+
orch.events.emit(lifecycleEvent, {
|
|
2892
|
+
serviceId: this.id,
|
|
2893
|
+
orchestrationId: orch.id,
|
|
2894
|
+
orchestrationName: orch.name,
|
|
2895
|
+
correlationId,
|
|
2896
|
+
executionId: String(orchestrationResult.executionMetadata.executionId ?? ""),
|
|
2897
|
+
timestamp: Date.now(),
|
|
2898
|
+
status: orchestrationResult.status,
|
|
2899
|
+
output: orchestrationResult.output,
|
|
2900
|
+
error: orchestrationResult.error
|
|
2901
|
+
});
|
|
2902
|
+
} else {
|
|
2903
|
+
orch.events.emit("failed", {
|
|
2904
|
+
serviceId: this.id,
|
|
2905
|
+
orchestrationId: orch.id,
|
|
2906
|
+
orchestrationName: orch.name,
|
|
2907
|
+
correlationId,
|
|
2908
|
+
timestamp: Date.now(),
|
|
2909
|
+
status: cmdResult.status,
|
|
2910
|
+
error: cmdResult.error
|
|
2911
|
+
});
|
|
2501
2912
|
}
|
|
2913
|
+
if (cmdResult.status === "FAILED" || orchestrationResult?.status === "FAILED") {
|
|
2914
|
+
const error = new Error(orchestrationResult?.error || cmdResult.error || `[ServiceFacade:${this.id}] Orchestration failed.`);
|
|
2915
|
+
telemetryHandle.end(error);
|
|
2916
|
+
serviceTelemetryHandle.end(error);
|
|
2917
|
+
throw error;
|
|
2918
|
+
}
|
|
2919
|
+
telemetryHandle.end();
|
|
2920
|
+
serviceTelemetryHandle.end();
|
|
2502
2921
|
return cmdResult.data;
|
|
2503
2922
|
}
|
|
2504
2923
|
/**
|
|
@@ -2525,17 +2944,29 @@ var ServiceFacade = class {
|
|
|
2525
2944
|
setState(mutation) {
|
|
2526
2945
|
this.instance.setState(mutation);
|
|
2527
2946
|
}
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2947
|
+
getFunctionality(name) {
|
|
2948
|
+
const functionality = this.functionalities[name];
|
|
2949
|
+
if (!functionality) {
|
|
2950
|
+
throw new Error(`[ServiceFacade:${this.id}] Functionality "${name}" not found.`);
|
|
2951
|
+
}
|
|
2952
|
+
return functionality;
|
|
2533
2953
|
}
|
|
2534
|
-
registerFunctionality(name, functionality) {
|
|
2954
|
+
registerFunctionality(name, functionality, options = {}) {
|
|
2535
2955
|
if (!name || typeof name !== "string") {
|
|
2536
2956
|
throw new Error("[ServiceFacade] Functionality name must be a non-empty string.");
|
|
2537
2957
|
}
|
|
2538
|
-
|
|
2958
|
+
if (functionality instanceof FunctionalityFacade) {
|
|
2959
|
+
this.functionalities[name] = functionality;
|
|
2960
|
+
return this;
|
|
2961
|
+
}
|
|
2962
|
+
if (!functionality || typeof functionality !== "object" || typeof functionality.execute !== "function") {
|
|
2963
|
+
throw new Error(`[ServiceFacade:${this.id}] Functionality "${name}" must define execute().`);
|
|
2964
|
+
}
|
|
2965
|
+
this.functionalities[name] = new FunctionalityFacade(
|
|
2966
|
+
name,
|
|
2967
|
+
functionality,
|
|
2968
|
+
options
|
|
2969
|
+
);
|
|
2539
2970
|
return this;
|
|
2540
2971
|
}
|
|
2541
2972
|
registerFunctionalityOrchestration(name, orchestration) {
|
|
@@ -2573,14 +3004,22 @@ var ServiceFacade = class {
|
|
|
2573
3004
|
return this.getNamespace();
|
|
2574
3005
|
}
|
|
2575
3006
|
getNamespace() {
|
|
3007
|
+
const bubbledFunctionalities = {};
|
|
3008
|
+
for (const [name, functionality] of Object.entries(this.functionalities)) {
|
|
3009
|
+
if (functionality.bubbleEvents) bubbledFunctionalities[name] = functionality;
|
|
3010
|
+
}
|
|
3011
|
+
const bubbledOrchestrations = {};
|
|
3012
|
+
for (const [name, orchestration] of Object.entries(this.functionalityOrchestrations)) {
|
|
3013
|
+
if (orchestration.bubbleEvents) bubbledOrchestrations[name] = orchestration;
|
|
3014
|
+
}
|
|
2576
3015
|
return {
|
|
2577
3016
|
serviceId: this.id,
|
|
2578
3017
|
serviceName: this.name,
|
|
2579
3018
|
version: this.version,
|
|
2580
3019
|
description: this.description,
|
|
2581
3020
|
libraries: { ...this.libraries },
|
|
2582
|
-
functionalities:
|
|
2583
|
-
functionalityOrchestrations:
|
|
3021
|
+
functionalities: bubbledFunctionalities,
|
|
3022
|
+
functionalityOrchestrations: bubbledOrchestrations,
|
|
2584
3023
|
capabilities: this.capabilities,
|
|
2585
3024
|
dependencies: this.dependencies
|
|
2586
3025
|
};
|
|
@@ -3137,9 +3576,9 @@ var ServiceRuntime = class {
|
|
|
3137
3576
|
// src/discovery/ServiceDiscovery.ts
|
|
3138
3577
|
var ServiceDiscovery = class {
|
|
3139
3578
|
peers = /* @__PURE__ */ new Map();
|
|
3140
|
-
registerPeer(
|
|
3141
|
-
this.peers.set(
|
|
3142
|
-
id,
|
|
3579
|
+
registerPeer(id2, name, endpoints) {
|
|
3580
|
+
this.peers.set(id2, {
|
|
3581
|
+
id: id2,
|
|
3143
3582
|
name,
|
|
3144
3583
|
endpoints,
|
|
3145
3584
|
lastSeen: Date.now()
|
|
@@ -3148,8 +3587,8 @@ var ServiceDiscovery = class {
|
|
|
3148
3587
|
getPeers() {
|
|
3149
3588
|
return Array.from(this.peers.values());
|
|
3150
3589
|
}
|
|
3151
|
-
findPeer(
|
|
3152
|
-
return this.peers.get(
|
|
3590
|
+
findPeer(id2) {
|
|
3591
|
+
return this.peers.get(id2);
|
|
3153
3592
|
}
|
|
3154
3593
|
};
|
|
3155
3594
|
|
|
@@ -3277,6 +3716,34 @@ var KrnlPackageReconciler = class {
|
|
|
3277
3716
|
}
|
|
3278
3717
|
};
|
|
3279
3718
|
|
|
3719
|
+
// src/events/ServiceEventBus.ts
|
|
3720
|
+
var ServiceEventBus = class extends EventBus {
|
|
3721
|
+
};
|
|
3722
|
+
|
|
3723
|
+
// src/events/OrchestrationEventFacade.ts
|
|
3724
|
+
var OrchestrationEventFacade = class {
|
|
3725
|
+
eventBus = new EventBus();
|
|
3726
|
+
events;
|
|
3727
|
+
constructor() {
|
|
3728
|
+
this.events = createEventChannels(this.eventBus, ["triggered", "completed", "failed"]);
|
|
3729
|
+
}
|
|
3730
|
+
get triggered() {
|
|
3731
|
+
return this.events.triggered;
|
|
3732
|
+
}
|
|
3733
|
+
get completed() {
|
|
3734
|
+
return this.events.completed;
|
|
3735
|
+
}
|
|
3736
|
+
get failed() {
|
|
3737
|
+
return this.events.failed;
|
|
3738
|
+
}
|
|
3739
|
+
emit(event, payload) {
|
|
3740
|
+
this.eventBus.emit(event, payload);
|
|
3741
|
+
}
|
|
3742
|
+
clear() {
|
|
3743
|
+
this.eventBus.clear();
|
|
3744
|
+
}
|
|
3745
|
+
};
|
|
3746
|
+
|
|
3280
3747
|
// src/connectivity/ProtocolResolver.ts
|
|
3281
3748
|
var ProtocolResolver = class {
|
|
3282
3749
|
static selectProtocol(requiredCapabilities, availableEndpoints) {
|
|
@@ -3297,6 +3764,8 @@ var FunctionalityOrchestration = class {
|
|
|
3297
3764
|
description;
|
|
3298
3765
|
timeoutMs;
|
|
3299
3766
|
scope = "functionality";
|
|
3767
|
+
bubbleEvents;
|
|
3768
|
+
events;
|
|
3300
3769
|
steps = [];
|
|
3301
3770
|
flows = /* @__PURE__ */ new Map();
|
|
3302
3771
|
constructor(descriptor) {
|
|
@@ -3308,6 +3777,8 @@ var FunctionalityOrchestration = class {
|
|
|
3308
3777
|
this.version = descriptor.version || "1.0.0";
|
|
3309
3778
|
this.description = descriptor.description || "";
|
|
3310
3779
|
this.timeoutMs = descriptor.timeoutMs;
|
|
3780
|
+
this.bubbleEvents = descriptor.bubbleEvents ?? true;
|
|
3781
|
+
this.events = new OrchestrationEventFacade();
|
|
3311
3782
|
}
|
|
3312
3783
|
/**
|
|
3313
3784
|
* Adds a sequential functionality step to the DAG.
|
|
@@ -3468,6 +3939,7 @@ var FunctionalityOrchestration = class {
|
|
|
3468
3939
|
description: this.description,
|
|
3469
3940
|
timeoutMs: this.timeoutMs,
|
|
3470
3941
|
scope: "functionality",
|
|
3942
|
+
bubbleEvents: this.bubbleEvents,
|
|
3471
3943
|
dependencies: [],
|
|
3472
3944
|
steps: this.steps.map((s) => ({
|
|
3473
3945
|
id: s.id,
|
|
@@ -3571,6 +4043,8 @@ var FunctionalityOrchestration = class {
|
|
|
3571
4043
|
// src/index.ts
|
|
3572
4044
|
import * as gutts from "@dinilmusick/gutts";
|
|
3573
4045
|
export {
|
|
4046
|
+
EventBus,
|
|
4047
|
+
FunctionalityFacade,
|
|
3574
4048
|
FunctionalityOrchestration,
|
|
3575
4049
|
HttpProtocolAdapter,
|
|
3576
4050
|
KrnlPackageReconciler,
|
|
@@ -3578,6 +4052,7 @@ export {
|
|
|
3578
4052
|
KrnlWorkerPool,
|
|
3579
4053
|
LifecycleManager,
|
|
3580
4054
|
MemoryTransport,
|
|
4055
|
+
OrchestrationEventFacade,
|
|
3581
4056
|
P2PProtocolAdapter,
|
|
3582
4057
|
ProtocolResolver,
|
|
3583
4058
|
ServiceDiscovery,
|
|
@@ -3588,14 +4063,19 @@ export {
|
|
|
3588
4063
|
ServiceRegistry,
|
|
3589
4064
|
ServiceRuntime,
|
|
3590
4065
|
ServiceStateStore,
|
|
4066
|
+
TelemetryController,
|
|
3591
4067
|
UdpProtocolAdapter,
|
|
3592
4068
|
WebSocketProtocolAdapter,
|
|
3593
4069
|
WorkerExecutionBoundary,
|
|
4070
|
+
createEventChannels,
|
|
3594
4071
|
createService,
|
|
3595
4072
|
createServiceDefinition,
|
|
3596
4073
|
createServiceFacade,
|
|
3597
4074
|
createServiceProxy,
|
|
4075
|
+
deltaResources,
|
|
3598
4076
|
gutts,
|
|
4077
|
+
sampleResources,
|
|
4078
|
+
telemetryNow,
|
|
3599
4079
|
validateOrchestrationDAG,
|
|
3600
4080
|
validateServiceDefinition
|
|
3601
4081
|
};
|