@ultrst-armada/krnl 1.0.8 → 1.0.10
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 +1 -1
- package/packages/cli/dist/bin/krnl.cjs +2 -0
- package/packages/cli/dist/bin/krnl.js +1 -1
- package/packages/cli/dist/{chunk-3DD775A7.js → chunk-OOVZPIMX.js} +2 -0
- package/packages/cli/dist/index.cjs +2 -0
- package/packages/cli/dist/index.js +1 -1
- package/packages/core/dist/index.cjs +382 -57
- package/packages/core/dist/index.d.cts +191 -1
- package/packages/core/dist/index.d.ts +191 -1
- package/packages/core/dist/index.js +377 -57
- package/packages/core/package.json +1 -1
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
DefaultExecutionController: () => DefaultExecutionController,
|
|
33
34
|
EventBus: () => EventBus,
|
|
34
35
|
FunctionalityFacade: () => FunctionalityFacade,
|
|
35
36
|
FunctionalityOrchestration: () => FunctionalityOrchestration,
|
|
@@ -50,6 +51,7 @@ __export(index_exports, {
|
|
|
50
51
|
ServiceRegistry: () => ServiceRegistry,
|
|
51
52
|
ServiceRuntime: () => ServiceRuntime,
|
|
52
53
|
ServiceStateStore: () => ServiceStateStore,
|
|
54
|
+
TelemetryController: () => TelemetryController,
|
|
53
55
|
UdpProtocolAdapter: () => UdpProtocolAdapter,
|
|
54
56
|
WebSocketProtocolAdapter: () => WebSocketProtocolAdapter,
|
|
55
57
|
WorkerExecutionBoundary: () => WorkerExecutionBoundary,
|
|
@@ -58,7 +60,10 @@ __export(index_exports, {
|
|
|
58
60
|
createServiceDefinition: () => createServiceDefinition,
|
|
59
61
|
createServiceFacade: () => createServiceFacade,
|
|
60
62
|
createServiceProxy: () => createServiceProxy,
|
|
63
|
+
deltaResources: () => deltaResources,
|
|
61
64
|
gutts: () => gutts,
|
|
65
|
+
sampleResources: () => sampleResources,
|
|
66
|
+
telemetryNow: () => telemetryNow,
|
|
62
67
|
validateOrchestrationDAG: () => validateOrchestrationDAG,
|
|
63
68
|
validateServiceDefinition: () => validateServiceDefinition
|
|
64
69
|
});
|
|
@@ -128,6 +133,227 @@ var LifecycleManager = class {
|
|
|
128
133
|
}
|
|
129
134
|
};
|
|
130
135
|
|
|
136
|
+
// src/telemetry/TelemetryController.ts
|
|
137
|
+
var import_node_crypto = require("crypto");
|
|
138
|
+
|
|
139
|
+
// src/telemetry/TelemetryResources.ts
|
|
140
|
+
var runtimeProcess = typeof globalThis !== "undefined" ? globalThis.process : void 0;
|
|
141
|
+
var runtimePerformance = typeof globalThis !== "undefined" ? globalThis.performance : void 0;
|
|
142
|
+
function telemetryNow() {
|
|
143
|
+
return runtimePerformance?.now ? runtimePerformance.now() : Date.now();
|
|
144
|
+
}
|
|
145
|
+
function sampleResources() {
|
|
146
|
+
const memory = runtimeProcess?.memoryUsage?.();
|
|
147
|
+
const cpu = runtimeProcess?.cpuUsage?.();
|
|
148
|
+
const eventLoop = runtimePerformance?.eventLoopUtilization?.();
|
|
149
|
+
return {
|
|
150
|
+
cpuUserMicros: cpu?.user,
|
|
151
|
+
cpuSystemMicros: cpu?.system,
|
|
152
|
+
heapUsedBytes: memory?.heapUsed,
|
|
153
|
+
heapTotalBytes: memory?.heapTotal,
|
|
154
|
+
rssBytes: memory?.rss,
|
|
155
|
+
externalBytes: memory?.external,
|
|
156
|
+
arrayBuffersBytes: memory?.arrayBuffers,
|
|
157
|
+
eventLoopUtilization: eventLoop?.utilization,
|
|
158
|
+
scope: memory || cpu || eventLoop ? "process" : "runtime"
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function deltaResources(start, end) {
|
|
162
|
+
const delta = (a, b) => a === void 0 || b === void 0 ? void 0 : b - a;
|
|
163
|
+
return {
|
|
164
|
+
cpuUserMicros: delta(start.cpuUserMicros, end.cpuUserMicros),
|
|
165
|
+
cpuSystemMicros: delta(start.cpuSystemMicros, end.cpuSystemMicros),
|
|
166
|
+
heapUsedBytes: delta(start.heapUsedBytes, end.heapUsedBytes),
|
|
167
|
+
heapTotalBytes: delta(start.heapTotalBytes, end.heapTotalBytes),
|
|
168
|
+
rssBytes: delta(start.rssBytes, end.rssBytes),
|
|
169
|
+
externalBytes: delta(start.externalBytes, end.externalBytes),
|
|
170
|
+
arrayBuffersBytes: delta(start.arrayBuffersBytes, end.arrayBuffersBytes),
|
|
171
|
+
eventLoopUtilization: delta(start.eventLoopUtilization, end.eventLoopUtilization),
|
|
172
|
+
scope: start.scope
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/telemetry/TelemetryController.ts
|
|
177
|
+
var id = (prefix) => `${prefix}_${(0, import_node_crypto.randomUUID)()}`;
|
|
178
|
+
function percentiles(values) {
|
|
179
|
+
if (!values.length) return { min: 0, mean: 0, p50: 0, p90: 0, p95: 0, p99: 0, max: 0 };
|
|
180
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
181
|
+
const pick = (p) => sorted[Math.min(sorted.length - 1, Math.floor((sorted.length - 1) * p))];
|
|
182
|
+
return {
|
|
183
|
+
min: sorted[0],
|
|
184
|
+
mean: sorted.reduce((sum, value) => sum + value, 0) / sorted.length,
|
|
185
|
+
p50: pick(0.5),
|
|
186
|
+
p90: pick(0.9),
|
|
187
|
+
p95: pick(0.95),
|
|
188
|
+
p99: pick(0.99),
|
|
189
|
+
max: sorted[sorted.length - 1]
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
var TelemetryController = class {
|
|
193
|
+
constructor(serviceId, options = {}) {
|
|
194
|
+
this.serviceId = serviceId;
|
|
195
|
+
this.mode = options.mode ?? "off";
|
|
196
|
+
this.maxSamples = Math.max(1, options.maxSamples ?? 2048);
|
|
197
|
+
this.maxRecentInvocations = Math.max(1, options.maxRecentInvocations ?? 100);
|
|
198
|
+
}
|
|
199
|
+
serviceId;
|
|
200
|
+
mode;
|
|
201
|
+
maxSamples;
|
|
202
|
+
maxRecentInvocations;
|
|
203
|
+
samples = [];
|
|
204
|
+
active = /* @__PURE__ */ new Map();
|
|
205
|
+
totalInvocationCount = 0;
|
|
206
|
+
totalSuccessCount = 0;
|
|
207
|
+
totalErrorCount = 0;
|
|
208
|
+
lastInvocationAt;
|
|
209
|
+
enable(mode = "standard") {
|
|
210
|
+
this.mode = mode;
|
|
211
|
+
}
|
|
212
|
+
disable() {
|
|
213
|
+
this.mode = "off";
|
|
214
|
+
this.active.clear();
|
|
215
|
+
}
|
|
216
|
+
setMode(mode) {
|
|
217
|
+
this.mode = mode;
|
|
218
|
+
if (mode === "off") this.active.clear();
|
|
219
|
+
}
|
|
220
|
+
getMode() {
|
|
221
|
+
return this.mode;
|
|
222
|
+
}
|
|
223
|
+
status() {
|
|
224
|
+
return {
|
|
225
|
+
enabled: this.mode !== "off",
|
|
226
|
+
mode: this.mode,
|
|
227
|
+
serviceId: this.serviceId,
|
|
228
|
+
invocationCount: this.totalInvocationCount,
|
|
229
|
+
activeInvocations: this.active.size,
|
|
230
|
+
lastInvocationAt: this.lastInvocationAt
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
begin(target, kind, correlationId, parentSpanId) {
|
|
234
|
+
if (this.mode === "off") {
|
|
235
|
+
return {
|
|
236
|
+
invocationId: "",
|
|
237
|
+
traceId: "",
|
|
238
|
+
spanId: "",
|
|
239
|
+
parentSpanId,
|
|
240
|
+
end: () => void 0
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
const invocationId = id("inv");
|
|
244
|
+
const spanId = id("span");
|
|
245
|
+
const traceId = parentSpanId ? this.findTraceId(parentSpanId) ?? id("trace") : id("trace");
|
|
246
|
+
const active = {
|
|
247
|
+
invocationId,
|
|
248
|
+
traceId,
|
|
249
|
+
spanId,
|
|
250
|
+
parentSpanId,
|
|
251
|
+
serviceId: this.serviceId,
|
|
252
|
+
target,
|
|
253
|
+
kind,
|
|
254
|
+
correlationId,
|
|
255
|
+
startedAt: telemetryNow(),
|
|
256
|
+
resourcesStart: this.mode === "standard" ? void 0 : sampleResources()
|
|
257
|
+
};
|
|
258
|
+
this.active.set(invocationId, active);
|
|
259
|
+
return {
|
|
260
|
+
invocationId,
|
|
261
|
+
traceId,
|
|
262
|
+
spanId,
|
|
263
|
+
parentSpanId,
|
|
264
|
+
end: (error) => this.finish(active, error)
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
reset() {
|
|
268
|
+
this.samples.length = 0;
|
|
269
|
+
this.active.clear();
|
|
270
|
+
this.totalInvocationCount = 0;
|
|
271
|
+
this.totalSuccessCount = 0;
|
|
272
|
+
this.totalErrorCount = 0;
|
|
273
|
+
this.lastInvocationAt = void 0;
|
|
274
|
+
}
|
|
275
|
+
snapshot() {
|
|
276
|
+
const invocations = this.samples.slice();
|
|
277
|
+
const durations = invocations.map((item) => item.durationMs);
|
|
278
|
+
const targetMap = /* @__PURE__ */ new Map();
|
|
279
|
+
for (const invocation of invocations) {
|
|
280
|
+
const key = `${invocation.kind}:${invocation.target}`;
|
|
281
|
+
const group = targetMap.get(key) ?? [];
|
|
282
|
+
group.push(invocation);
|
|
283
|
+
targetMap.set(key, group);
|
|
284
|
+
}
|
|
285
|
+
const targets = [...targetMap.values()].map((group) => ({
|
|
286
|
+
target: group[0].target,
|
|
287
|
+
kind: group[0].kind,
|
|
288
|
+
count: group.length,
|
|
289
|
+
successCount: group.filter((item) => item.status === "completed").length,
|
|
290
|
+
errorCount: group.filter((item) => item.status === "failed").length,
|
|
291
|
+
durationMs: percentiles(group.map((item) => item.durationMs)),
|
|
292
|
+
lastInvocationAt: group[group.length - 1]?.completedAt
|
|
293
|
+
}));
|
|
294
|
+
return {
|
|
295
|
+
serviceId: this.serviceId,
|
|
296
|
+
mode: this.mode,
|
|
297
|
+
capturedAt: Date.now(),
|
|
298
|
+
status: this.status(),
|
|
299
|
+
totals: {
|
|
300
|
+
invocationCount: this.totalInvocationCount,
|
|
301
|
+
successCount: this.totalSuccessCount,
|
|
302
|
+
errorCount: this.totalErrorCount,
|
|
303
|
+
durationMs: percentiles(durations)
|
|
304
|
+
},
|
|
305
|
+
targets,
|
|
306
|
+
activeInvocations: this.active.size,
|
|
307
|
+
recentInvocations: invocations.slice(-this.maxRecentInvocations)
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
functionality(name) {
|
|
311
|
+
return this.snapshot().targets.find((target) => target.kind === "functionality" && target.target === name);
|
|
312
|
+
}
|
|
313
|
+
functionalities() {
|
|
314
|
+
return this.snapshot().targets.filter((target) => target.kind === "functionality");
|
|
315
|
+
}
|
|
316
|
+
findTraceId(parentSpanId) {
|
|
317
|
+
for (let index = this.samples.length - 1; index >= 0; index -= 1) {
|
|
318
|
+
if (this.samples[index].spanId === parentSpanId) return this.samples[index].traceId;
|
|
319
|
+
}
|
|
320
|
+
for (const active of this.active.values()) {
|
|
321
|
+
if (active.spanId === parentSpanId) return active.traceId;
|
|
322
|
+
}
|
|
323
|
+
return void 0;
|
|
324
|
+
}
|
|
325
|
+
finish(active, error) {
|
|
326
|
+
if (!this.active.delete(active.invocationId)) return;
|
|
327
|
+
const completedAt = telemetryNow();
|
|
328
|
+
const resourcesEnd = this.mode === "standard" ? void 0 : sampleResources();
|
|
329
|
+
const invocation = {
|
|
330
|
+
invocationId: active.invocationId,
|
|
331
|
+
traceId: active.traceId,
|
|
332
|
+
spanId: active.spanId,
|
|
333
|
+
parentSpanId: active.parentSpanId,
|
|
334
|
+
serviceId: active.serviceId,
|
|
335
|
+
target: active.target,
|
|
336
|
+
kind: active.kind,
|
|
337
|
+
correlationId: active.correlationId,
|
|
338
|
+
startedAt: active.startedAt,
|
|
339
|
+
completedAt,
|
|
340
|
+
durationMs: Math.max(0, completedAt - active.startedAt),
|
|
341
|
+
status: error === void 0 ? "completed" : "failed",
|
|
342
|
+
error: error === void 0 ? void 0 : {
|
|
343
|
+
name: error instanceof Error ? error.name : "Error",
|
|
344
|
+
message: error instanceof Error ? error.message : String(error)
|
|
345
|
+
},
|
|
346
|
+
resources: active.resourcesStart && resourcesEnd ? { start: active.resourcesStart, end: resourcesEnd, delta: deltaResources(active.resourcesStart, resourcesEnd) } : void 0
|
|
347
|
+
};
|
|
348
|
+
this.samples.push(invocation);
|
|
349
|
+
this.totalInvocationCount += 1;
|
|
350
|
+
if (invocation.status === "completed") this.totalSuccessCount += 1;
|
|
351
|
+
else this.totalErrorCount += 1;
|
|
352
|
+
if (this.samples.length > this.maxSamples) this.samples.splice(0, this.samples.length - this.maxSamples);
|
|
353
|
+
this.lastInvocationAt = Date.now();
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
|
|
131
357
|
// src/services/ServiceInstance.ts
|
|
132
358
|
var ServiceInstance = class {
|
|
133
359
|
constructor(definition) {
|
|
@@ -135,6 +361,7 @@ var ServiceInstance = class {
|
|
|
135
361
|
this.id = definition.id;
|
|
136
362
|
this.name = definition.name;
|
|
137
363
|
this.version = definition.version;
|
|
364
|
+
this.telemetry = new TelemetryController(this.id);
|
|
138
365
|
this.lifecycle = new LifecycleManager();
|
|
139
366
|
if (definition.state) {
|
|
140
367
|
this.stateStore = new ServiceStateStore(definition.state.initial);
|
|
@@ -144,6 +371,7 @@ var ServiceInstance = class {
|
|
|
144
371
|
id;
|
|
145
372
|
name;
|
|
146
373
|
version;
|
|
374
|
+
telemetry;
|
|
147
375
|
stateStore;
|
|
148
376
|
lifecycle;
|
|
149
377
|
initialized = false;
|
|
@@ -208,7 +436,19 @@ var ServiceInstance = class {
|
|
|
208
436
|
if (!operation) {
|
|
209
437
|
throw new Error("[Service:" + this.name + "] Operation '" + op + "' not found.");
|
|
210
438
|
}
|
|
211
|
-
|
|
439
|
+
if (ctx.metadata?.__krnlTelemetryInternal === true) {
|
|
440
|
+
return await operation.handler(input, ctx);
|
|
441
|
+
}
|
|
442
|
+
const parentSpanId = typeof ctx.metadata?.__krnlTelemetryParentSpanId === "string" ? ctx.metadata.__krnlTelemetryParentSpanId : void 0;
|
|
443
|
+
const handle = this.telemetry.begin(op, "service", ctx.correlationId, parentSpanId);
|
|
444
|
+
try {
|
|
445
|
+
const output = await operation.handler(input, ctx);
|
|
446
|
+
handle.end();
|
|
447
|
+
return output;
|
|
448
|
+
} catch (error) {
|
|
449
|
+
handle.end(error);
|
|
450
|
+
throw error;
|
|
451
|
+
}
|
|
212
452
|
}
|
|
213
453
|
getState() {
|
|
214
454
|
return this.stateStore?.getState() ?? Object.freeze({});
|
|
@@ -240,6 +480,7 @@ var ServiceProxy = class {
|
|
|
240
480
|
this.id = definition.id;
|
|
241
481
|
this.name = definition.name;
|
|
242
482
|
this.version = definition.version;
|
|
483
|
+
this.telemetry = new TelemetryController(this.id);
|
|
243
484
|
this.stateCache = { ...definition.state?.initial ?? {} };
|
|
244
485
|
}
|
|
245
486
|
definition;
|
|
@@ -248,6 +489,7 @@ var ServiceProxy = class {
|
|
|
248
489
|
id;
|
|
249
490
|
name;
|
|
250
491
|
version;
|
|
492
|
+
telemetry;
|
|
251
493
|
stateCache;
|
|
252
494
|
eventHandlers = /* @__PURE__ */ new Map();
|
|
253
495
|
lifecycle = new LifecycleManager();
|
|
@@ -305,11 +547,21 @@ var ServiceProxy = class {
|
|
|
305
547
|
payload: input,
|
|
306
548
|
timestamp: Date.now()
|
|
307
549
|
};
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
550
|
+
const parentSpanId = typeof ctx.metadata?.__krnlTelemetryParentSpanId === "string" ? ctx.metadata.__krnlTelemetryParentSpanId : void 0;
|
|
551
|
+
const handle = this.telemetry.begin(op, "service", ctx.correlationId, parentSpanId);
|
|
552
|
+
try {
|
|
553
|
+
const response = await this.adapter.send(this.endpoint, envelope);
|
|
554
|
+
if (response.error) {
|
|
555
|
+
const error = new Error("[ServiceProxy:" + this.name + "] Remote error: " + response.error);
|
|
556
|
+
handle.end(error);
|
|
557
|
+
throw error;
|
|
558
|
+
}
|
|
559
|
+
handle.end();
|
|
560
|
+
return response.payload;
|
|
561
|
+
} catch (error) {
|
|
562
|
+
handle.end(error);
|
|
563
|
+
throw error;
|
|
311
564
|
}
|
|
312
|
-
return response.payload;
|
|
313
565
|
}
|
|
314
566
|
getState() {
|
|
315
567
|
return Object.freeze({ ...this.stateCache });
|
|
@@ -457,22 +709,22 @@ var FunctionalityFacade = class {
|
|
|
457
709
|
definition;
|
|
458
710
|
eventBus;
|
|
459
711
|
events;
|
|
460
|
-
constructor(
|
|
461
|
-
if (!
|
|
712
|
+
constructor(id2, definition, options = {}) {
|
|
713
|
+
if (!id2 || typeof id2 !== "string") {
|
|
462
714
|
throw new Error("[FunctionalityFacade] Functionality id must be a non-empty string.");
|
|
463
715
|
}
|
|
464
716
|
if (!definition || typeof definition.execute !== "function") {
|
|
465
|
-
throw new Error(`[FunctionalityFacade:${
|
|
717
|
+
throw new Error(`[FunctionalityFacade:${id2}] Functionality must define execute().`);
|
|
466
718
|
}
|
|
467
|
-
this.id =
|
|
468
|
-
this.name = definition.name ||
|
|
719
|
+
this.id = id2;
|
|
720
|
+
this.name = definition.name || id2;
|
|
469
721
|
this.description = definition.description || "";
|
|
470
722
|
this.bubbleEvents = options.bubbleEvents ?? definition.bubbleEvents ?? true;
|
|
471
723
|
this.definition = definition;
|
|
472
724
|
this.eventBus = new EventBus();
|
|
473
725
|
for (const reserved of ["triggered", "completed", "failed"]) {
|
|
474
726
|
if (definition.events && reserved in definition.events) {
|
|
475
|
-
throw new Error(`[FunctionalityFacade:${
|
|
727
|
+
throw new Error(`[FunctionalityFacade:${id2}] Event '${reserved}' is reserved for functionality lifecycle.`);
|
|
476
728
|
}
|
|
477
729
|
}
|
|
478
730
|
const eventNames = [
|
|
@@ -586,11 +838,11 @@ var PriorityQueue = class {
|
|
|
586
838
|
return item.run;
|
|
587
839
|
}
|
|
588
840
|
enqueue(run, options) {
|
|
589
|
-
const { priority = 0, id } = options ?? {};
|
|
841
|
+
const { priority = 0, id: id2 } = options ?? {};
|
|
590
842
|
const { size } = this;
|
|
591
843
|
const element = {
|
|
592
844
|
priority,
|
|
593
|
-
id,
|
|
845
|
+
id: id2,
|
|
594
846
|
run
|
|
595
847
|
};
|
|
596
848
|
if (size === 0) {
|
|
@@ -607,16 +859,16 @@ var PriorityQueue = class {
|
|
|
607
859
|
const index = lowerBound(this.#queue, element, (a, b) => a.priority === b.priority ? 0 : b.priority - a.priority);
|
|
608
860
|
this.#queue.splice(index, 0, element);
|
|
609
861
|
}
|
|
610
|
-
setPriority(
|
|
611
|
-
const index = this.#findIndex((element) => element.id ===
|
|
862
|
+
setPriority(id2, priority) {
|
|
863
|
+
const index = this.#findIndex((element) => element.id === id2);
|
|
612
864
|
if (index === -1) {
|
|
613
|
-
throw new ReferenceError(`No promise function with the id "${
|
|
865
|
+
throw new ReferenceError(`No promise function with the id "${id2}" exists in the queue.`);
|
|
614
866
|
}
|
|
615
867
|
if (this.#queue[index].priority === priority) {
|
|
616
868
|
return;
|
|
617
869
|
}
|
|
618
870
|
const [item] = this.#queue.splice(index, 1);
|
|
619
|
-
this.enqueue(item.run, { priority, id });
|
|
871
|
+
this.enqueue(item.run, { priority, id: id2 });
|
|
620
872
|
}
|
|
621
873
|
remove(idOrRun) {
|
|
622
874
|
const index = this.#findIndex((element) => (typeof idOrRun === "string" ? element.id : element.run) === idOrRun);
|
|
@@ -1032,17 +1284,17 @@ var PQueue = class extends EventTarget {
|
|
|
1032
1284
|
}
|
|
1033
1285
|
}
|
|
1034
1286
|
async #add(function_, options, waitingTask) {
|
|
1035
|
-
const
|
|
1287
|
+
const id2 = options.id ?? (this.#idAssigner++).toString();
|
|
1036
1288
|
options = {
|
|
1037
1289
|
timeout: this.#timeout,
|
|
1038
1290
|
...options,
|
|
1039
|
-
id
|
|
1291
|
+
id: id2
|
|
1040
1292
|
};
|
|
1041
1293
|
validateTimeout(options.timeout);
|
|
1042
1294
|
if (options.priority !== void 0 && (typeof options.priority !== "number" || Number.isNaN(options.priority))) {
|
|
1043
1295
|
throw new TypeError(`Expected \`priority\` to be a number, got \`${String(options.priority)}\` (${typeof options.priority})`);
|
|
1044
1296
|
}
|
|
1045
|
-
waitingTask.id =
|
|
1297
|
+
waitingTask.id = id2;
|
|
1046
1298
|
waitingTask.priority = options.priority ?? 0;
|
|
1047
1299
|
return new Promise((resolve, reject) => {
|
|
1048
1300
|
if (options.signal?.aborted) {
|
|
@@ -1059,7 +1311,7 @@ var PQueue = class extends EventTarget {
|
|
|
1059
1311
|
}
|
|
1060
1312
|
this.#pending++;
|
|
1061
1313
|
const runningTask = {
|
|
1062
|
-
id,
|
|
1314
|
+
id: id2,
|
|
1063
1315
|
priority: waitingTask.priority,
|
|
1064
1316
|
startTime: Date.now(),
|
|
1065
1317
|
timeout: options.timeout
|
|
@@ -1126,7 +1378,7 @@ var PQueue = class extends EventTarget {
|
|
|
1126
1378
|
if (this.#queue instanceof PriorityQueue) {
|
|
1127
1379
|
this.#queue.remove(run);
|
|
1128
1380
|
} else {
|
|
1129
|
-
this.#queue.remove(
|
|
1381
|
+
this.#queue.remove(id2);
|
|
1130
1382
|
}
|
|
1131
1383
|
reject(signal.reason);
|
|
1132
1384
|
this.#processQueue();
|
|
@@ -1202,18 +1454,18 @@ var PQueue = class extends EventTarget {
|
|
|
1202
1454
|
// '🦀' runs last
|
|
1203
1455
|
```
|
|
1204
1456
|
*/
|
|
1205
|
-
setPriority(
|
|
1457
|
+
setPriority(id2, priority) {
|
|
1206
1458
|
if (typeof priority !== "number" || !Number.isFinite(priority)) {
|
|
1207
1459
|
throw new TypeError(`Expected \`priority\` to be a finite number, got \`${priority}\` (${typeof priority})`);
|
|
1208
1460
|
}
|
|
1209
1461
|
for (const waitingTask of this.#waitingTasks) {
|
|
1210
|
-
if (waitingTask.id ===
|
|
1211
|
-
this.#queue.setPriority(
|
|
1462
|
+
if (waitingTask.id === id2) {
|
|
1463
|
+
this.#queue.setPriority(id2, priority);
|
|
1212
1464
|
waitingTask.priority = priority;
|
|
1213
1465
|
return;
|
|
1214
1466
|
}
|
|
1215
1467
|
}
|
|
1216
|
-
throw new ReferenceError(`No promise function with the id "${
|
|
1468
|
+
throw new ReferenceError(`No promise function with the id "${id2}" exists in the queue.`);
|
|
1217
1469
|
}
|
|
1218
1470
|
/**
|
|
1219
1471
|
Adds a sync or async task to the queue.
|
|
@@ -1556,7 +1808,7 @@ var TimeoutError = class extends DOMException {
|
|
|
1556
1808
|
};
|
|
1557
1809
|
|
|
1558
1810
|
// src/orchestration/canonical/KrnlQ.ts
|
|
1559
|
-
var
|
|
1811
|
+
var import_node_crypto2 = require("crypto");
|
|
1560
1812
|
|
|
1561
1813
|
// src/orchestration/canonical/model.ts
|
|
1562
1814
|
function validateOrchestrationDAG(steps) {
|
|
@@ -1623,8 +1875,8 @@ function validateOrchestrationDAG(steps) {
|
|
|
1623
1875
|
}
|
|
1624
1876
|
}
|
|
1625
1877
|
const queue = [];
|
|
1626
|
-
for (const [
|
|
1627
|
-
if (deg === 0) queue.push(
|
|
1878
|
+
for (const [id2, deg] of inDegree.entries()) {
|
|
1879
|
+
if (deg === 0) queue.push(id2);
|
|
1628
1880
|
}
|
|
1629
1881
|
const order = [];
|
|
1630
1882
|
while (queue.length > 0) {
|
|
@@ -1760,7 +2012,7 @@ var KrnlWorkerPool = class {
|
|
|
1760
2012
|
// src/orchestration/canonical/KrnlQ.ts
|
|
1761
2013
|
function generateSecureId(prefix) {
|
|
1762
2014
|
try {
|
|
1763
|
-
return `${prefix}_${(0,
|
|
2015
|
+
return `${prefix}_${(0, import_node_crypto2.randomUUID)()}`;
|
|
1764
2016
|
} catch {
|
|
1765
2017
|
return `${prefix}_${Math.random().toString(36).substring(2, 11)}_${Date.now()}`;
|
|
1766
2018
|
}
|
|
@@ -1828,9 +2080,9 @@ var KrnlQueue = class {
|
|
|
1828
2080
|
if (this.jobs.size <= this.maxRetainedJobs) return;
|
|
1829
2081
|
const excess = this.jobs.size - this.maxRetainedJobs;
|
|
1830
2082
|
let pruned = 0;
|
|
1831
|
-
for (const [
|
|
2083
|
+
for (const [id2, record] of this.jobs.entries()) {
|
|
1832
2084
|
if (record.status === "completed" || record.status === "failed" || record.status === "cancelled") {
|
|
1833
|
-
this.jobs.delete(
|
|
2085
|
+
this.jobs.delete(id2);
|
|
1834
2086
|
pruned++;
|
|
1835
2087
|
if (pruned >= excess) break;
|
|
1836
2088
|
}
|
|
@@ -2444,6 +2696,7 @@ var ServiceFacade = class {
|
|
|
2444
2696
|
defaultFrontend;
|
|
2445
2697
|
instance;
|
|
2446
2698
|
queue;
|
|
2699
|
+
telemetry;
|
|
2447
2700
|
constructor(config) {
|
|
2448
2701
|
if (!config || !config.id || typeof config.id !== "string") {
|
|
2449
2702
|
throw new Error("[ServiceFacade] ServiceFacade requires a valid id.");
|
|
@@ -2473,6 +2726,7 @@ var ServiceFacade = class {
|
|
|
2473
2726
|
operations: {}
|
|
2474
2727
|
});
|
|
2475
2728
|
}
|
|
2729
|
+
this.telemetry = this.instance.telemetry;
|
|
2476
2730
|
this.queue = new KrnlQueue({
|
|
2477
2731
|
queueName: `krnl_queue_${this.id}`,
|
|
2478
2732
|
concurrency: config.concurrency ?? config.queueOptions?.concurrency ?? 10,
|
|
@@ -2516,6 +2770,8 @@ var ServiceFacade = class {
|
|
|
2516
2770
|
*/
|
|
2517
2771
|
async call(op, input, ctx) {
|
|
2518
2772
|
const correlationId = ctx?.correlationId || `corr_${Math.random().toString(36).substring(2, 11)}`;
|
|
2773
|
+
const telemetryParent = typeof ctx?.metadata?.__krnlTelemetryParentSpanId === "string" ? ctx.metadata.__krnlTelemetryParentSpanId : void 0;
|
|
2774
|
+
const telemetryHandle = this.telemetry.begin(op, "service", correlationId, telemetryParent);
|
|
2519
2775
|
const commandId = `cmd_${this.id}_${op}_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;
|
|
2520
2776
|
const command = {
|
|
2521
2777
|
id: commandId,
|
|
@@ -2525,26 +2781,48 @@ var ServiceFacade = class {
|
|
|
2525
2781
|
idempotencyKey: ctx?.idempotencyKey,
|
|
2526
2782
|
createdAt: Date.now()
|
|
2527
2783
|
};
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2784
|
+
try {
|
|
2785
|
+
const result = await this.queue.executeCommand(command, async (payload, workerCtx) => {
|
|
2786
|
+
WorkerExecutionBoundary.assertWorkerContext(workerCtx);
|
|
2787
|
+
const functionality = this.functionalities[op];
|
|
2788
|
+
if (functionality) {
|
|
2789
|
+
const functionalityTelemetry = this.telemetry.begin(
|
|
2790
|
+
functionality.name,
|
|
2791
|
+
"functionality",
|
|
2792
|
+
workerCtx.correlationId,
|
|
2793
|
+
telemetryHandle.spanId
|
|
2794
|
+
);
|
|
2795
|
+
try {
|
|
2796
|
+
return await functionality.execute(payload, {
|
|
2797
|
+
serviceId: this.id,
|
|
2798
|
+
functionalityId: functionality.id,
|
|
2799
|
+
correlationId: workerCtx.correlationId,
|
|
2800
|
+
state: this.instance.getState()
|
|
2801
|
+
});
|
|
2802
|
+
} catch (error) {
|
|
2803
|
+
functionalityTelemetry.end(error);
|
|
2804
|
+
throw error;
|
|
2805
|
+
} finally {
|
|
2806
|
+
functionalityTelemetry.end();
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
return await this.instance.call(op, payload, {
|
|
2535
2810
|
correlationId: workerCtx.correlationId,
|
|
2536
|
-
|
|
2811
|
+
idempotencyKey: command.idempotencyKey,
|
|
2812
|
+
metadata: { __krnlTelemetryInternal: true }
|
|
2537
2813
|
});
|
|
2538
|
-
}
|
|
2539
|
-
return await this.instance.call(op, payload, {
|
|
2540
|
-
correlationId: workerCtx.correlationId,
|
|
2541
|
-
idempotencyKey: command.idempotencyKey
|
|
2542
2814
|
});
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2815
|
+
if (result.status === "FAILED") {
|
|
2816
|
+
const error = new Error(result.error || `[ServiceFacade:${this.id}] Operation '${op}' execution failed.`);
|
|
2817
|
+
telemetryHandle.end(error);
|
|
2818
|
+
throw error;
|
|
2819
|
+
}
|
|
2820
|
+
telemetryHandle.end();
|
|
2821
|
+
return result.data;
|
|
2822
|
+
} catch (error) {
|
|
2823
|
+
telemetryHandle.end(error);
|
|
2824
|
+
throw error;
|
|
2546
2825
|
}
|
|
2547
|
-
return result.data;
|
|
2548
2826
|
}
|
|
2549
2827
|
/**
|
|
2550
2828
|
* External caller callable boundary for functionality orchestration execution.
|
|
@@ -2558,6 +2836,18 @@ var ServiceFacade = class {
|
|
|
2558
2836
|
);
|
|
2559
2837
|
}
|
|
2560
2838
|
const correlationId = options.correlationId || `corr_${Math.random().toString(36).substring(2, 11)}`;
|
|
2839
|
+
const serviceTelemetryHandle = this.telemetry.begin(
|
|
2840
|
+
`orchestrate:${orchestrationName}`,
|
|
2841
|
+
"service",
|
|
2842
|
+
correlationId,
|
|
2843
|
+
typeof options.metadata?.__krnlTelemetryParentSpanId === "string" ? options.metadata.__krnlTelemetryParentSpanId : void 0
|
|
2844
|
+
);
|
|
2845
|
+
const telemetryHandle = this.telemetry.begin(
|
|
2846
|
+
orchestrationName,
|
|
2847
|
+
"orchestration",
|
|
2848
|
+
correlationId,
|
|
2849
|
+
serviceTelemetryHandle.spanId
|
|
2850
|
+
);
|
|
2561
2851
|
const commandId = `cmd_orch_${this.id}_${orchestrationName}_${Date.now()}`;
|
|
2562
2852
|
const command = {
|
|
2563
2853
|
id: commandId,
|
|
@@ -2605,7 +2895,8 @@ var ServiceFacade = class {
|
|
|
2605
2895
|
...s.compensation,
|
|
2606
2896
|
handler: async (compCtx) => {
|
|
2607
2897
|
await this.call(s.compensation.target, compCtx.results[s.id], {
|
|
2608
|
-
correlationId: compCtx.correlationId
|
|
2898
|
+
correlationId: compCtx.correlationId,
|
|
2899
|
+
metadata: { __krnlTelemetryParentSpanId: telemetryHandle.spanId }
|
|
2609
2900
|
});
|
|
2610
2901
|
}
|
|
2611
2902
|
};
|
|
@@ -2621,7 +2912,8 @@ var ServiceFacade = class {
|
|
|
2621
2912
|
stepPayload.input = stepCtx.results.input;
|
|
2622
2913
|
}
|
|
2623
2914
|
return await this.call(s.target, stepPayload, {
|
|
2624
|
-
correlationId: stepCtx.correlationId
|
|
2915
|
+
correlationId: stepCtx.correlationId,
|
|
2916
|
+
metadata: { __krnlTelemetryParentSpanId: telemetryHandle.spanId }
|
|
2625
2917
|
});
|
|
2626
2918
|
}
|
|
2627
2919
|
};
|
|
@@ -2641,7 +2933,8 @@ var ServiceFacade = class {
|
|
|
2641
2933
|
const nestedResult = await this.orchestrate(childOrchId, childInput, {
|
|
2642
2934
|
correlationId: childCtx.correlationId,
|
|
2643
2935
|
timeoutMs: childOrch.timeoutMs,
|
|
2644
|
-
abortSignal: options.abortSignal
|
|
2936
|
+
abortSignal: options.abortSignal,
|
|
2937
|
+
metadata: { __krnlTelemetryParentSpanId: telemetryHandle.spanId }
|
|
2645
2938
|
});
|
|
2646
2939
|
if (nestedResult.status !== "COMPLETED") {
|
|
2647
2940
|
throw new Error(nestedResult.error || `Nested orchestration "${childOrchId}" failed.`);
|
|
@@ -2689,8 +2982,13 @@ var ServiceFacade = class {
|
|
|
2689
2982
|
});
|
|
2690
2983
|
}
|
|
2691
2984
|
if (cmdResult.status === "FAILED" || orchestrationResult?.status === "FAILED") {
|
|
2692
|
-
|
|
2985
|
+
const error = new Error(orchestrationResult?.error || cmdResult.error || `[ServiceFacade:${this.id}] Orchestration failed.`);
|
|
2986
|
+
telemetryHandle.end(error);
|
|
2987
|
+
serviceTelemetryHandle.end(error);
|
|
2988
|
+
throw error;
|
|
2693
2989
|
}
|
|
2990
|
+
telemetryHandle.end();
|
|
2991
|
+
serviceTelemetryHandle.end();
|
|
2694
2992
|
return cmdResult.data;
|
|
2695
2993
|
}
|
|
2696
2994
|
/**
|
|
@@ -3349,9 +3647,9 @@ var ServiceRuntime = class {
|
|
|
3349
3647
|
// src/discovery/ServiceDiscovery.ts
|
|
3350
3648
|
var ServiceDiscovery = class {
|
|
3351
3649
|
peers = /* @__PURE__ */ new Map();
|
|
3352
|
-
registerPeer(
|
|
3353
|
-
this.peers.set(
|
|
3354
|
-
id,
|
|
3650
|
+
registerPeer(id2, name, endpoints) {
|
|
3651
|
+
this.peers.set(id2, {
|
|
3652
|
+
id: id2,
|
|
3355
3653
|
name,
|
|
3356
3654
|
endpoints,
|
|
3357
3655
|
lastSeen: Date.now()
|
|
@@ -3360,8 +3658,8 @@ var ServiceDiscovery = class {
|
|
|
3360
3658
|
getPeers() {
|
|
3361
3659
|
return Array.from(this.peers.values());
|
|
3362
3660
|
}
|
|
3363
|
-
findPeer(
|
|
3364
|
-
return this.peers.get(
|
|
3661
|
+
findPeer(id2) {
|
|
3662
|
+
return this.peers.get(id2);
|
|
3365
3663
|
}
|
|
3366
3664
|
};
|
|
3367
3665
|
|
|
@@ -3815,8 +4113,31 @@ var FunctionalityOrchestration = class {
|
|
|
3815
4113
|
|
|
3816
4114
|
// src/index.ts
|
|
3817
4115
|
var gutts = __toESM(require("@dinilmusick/gutts"), 1);
|
|
4116
|
+
|
|
4117
|
+
// src/execution/execution-controller.ts
|
|
4118
|
+
var DefaultExecutionController = class {
|
|
4119
|
+
providers = /* @__PURE__ */ new Map();
|
|
4120
|
+
executions = /* @__PURE__ */ new Map();
|
|
4121
|
+
register(provider) {
|
|
4122
|
+
this.providers.set(provider.id, provider);
|
|
4123
|
+
}
|
|
4124
|
+
async execute(request, providerId) {
|
|
4125
|
+
const provider = providerId ? this.providers.get(providerId) : this.providers.values().next().value;
|
|
4126
|
+
if (!provider) throw new Error("No execution provider is registered.");
|
|
4127
|
+
const result = await provider.execute(request);
|
|
4128
|
+
this.executions.set(result.executionId, provider);
|
|
4129
|
+
return result;
|
|
4130
|
+
}
|
|
4131
|
+
async cancel(executionId) {
|
|
4132
|
+
const provider = this.executions.get(executionId);
|
|
4133
|
+
if (!provider) throw new Error(`Unknown execution: ${executionId}`);
|
|
4134
|
+
await provider.cancel(executionId);
|
|
4135
|
+
this.executions.delete(executionId);
|
|
4136
|
+
}
|
|
4137
|
+
};
|
|
3818
4138
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3819
4139
|
0 && (module.exports = {
|
|
4140
|
+
DefaultExecutionController,
|
|
3820
4141
|
EventBus,
|
|
3821
4142
|
FunctionalityFacade,
|
|
3822
4143
|
FunctionalityOrchestration,
|
|
@@ -3837,6 +4158,7 @@ var gutts = __toESM(require("@dinilmusick/gutts"), 1);
|
|
|
3837
4158
|
ServiceRegistry,
|
|
3838
4159
|
ServiceRuntime,
|
|
3839
4160
|
ServiceStateStore,
|
|
4161
|
+
TelemetryController,
|
|
3840
4162
|
UdpProtocolAdapter,
|
|
3841
4163
|
WebSocketProtocolAdapter,
|
|
3842
4164
|
WorkerExecutionBoundary,
|
|
@@ -3845,7 +4167,10 @@ var gutts = __toESM(require("@dinilmusick/gutts"), 1);
|
|
|
3845
4167
|
createServiceDefinition,
|
|
3846
4168
|
createServiceFacade,
|
|
3847
4169
|
createServiceProxy,
|
|
4170
|
+
deltaResources,
|
|
3848
4171
|
gutts,
|
|
4172
|
+
sampleResources,
|
|
4173
|
+
telemetryNow,
|
|
3849
4174
|
validateOrchestrationDAG,
|
|
3850
4175
|
validateServiceDefinition
|
|
3851
4176
|
});
|