@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.
@@ -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
- return await operation.handler(input, ctx);
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 response = await this.adapter.send(this.endpoint, envelope);
342
- if (response.error) {
343
- throw new Error("[ServiceProxy:" + this.name + "] Remote error: " + response.error);
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(id, priority) {
519
- const index = this.#findIndex((element) => element.id === 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 "${id}" exists in the queue.`);
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 id = options.id ?? (this.#idAssigner++).toString();
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 = 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(id);
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(id, priority) {
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 === id) {
1119
- this.#queue.setPriority(id, priority);
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 "${id}" exists in the queue.`);
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 import_node_crypto = require("crypto");
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 [id, deg] of inDegree.entries()) {
1535
- if (deg === 0) queue.push(id);
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, import_node_crypto.randomUUID)()}`;
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 [id, record] of this.jobs.entries()) {
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(id);
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 = config.functionalities ? { ...config.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
- const result = await this.queue.executeCommand(command, async (payload, workerCtx) => {
2434
- WorkerExecutionBoundary.assertWorkerContext(workerCtx);
2435
- if (this.functionalities[op] && typeof this.functionalities[op].execute === "function") {
2436
- return await this.functionalities[op].execute(payload, {
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
- state: this.instance.getState()
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
- if (result.status === "FAILED") {
2447
- throw new Error(result.error || `[ServiceFacade:${this.id}] Operation '${op}' execution failed.`);
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
- if (cmdResult.status === "FAILED") {
2562
- throw new Error(cmdResult.error || `[ServiceFacade:${this.id}] Orchestration failed.`);
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
- on(event, handler) {
2591
- return this.instance.on(event, handler);
2592
- }
2593
- emit(event, payload) {
2594
- this.instance.emit(event, payload);
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
- this.functionalities[name] = functionality;
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: { ...this.functionalities },
2645
- functionalityOrchestrations: { ...this.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(id, name, endpoints) {
3203
- this.peers.set(id, {
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(id) {
3214
- return this.peers.get(id);
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
  });