arkgate 2.8.0 → 2.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/README.md +8 -0
- package/bin/ark-shared.mjs +66 -4
- package/bin/ark.mjs +50 -9
- package/bin/lib/agent-gates.mjs +32 -3
- package/bin/lib/doctor-plan.mjs +16 -0
- package/bin/lib/html-report.mjs +15 -0
- package/bin/lib/presets.mjs +86 -12
- package/dist/index.cjs +417 -338
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -9
- package/dist/index.d.ts +29 -9
- package/dist/index.js +417 -338
- package/dist/index.js.map +1 -1
- package/dist/nestjs/index.cjs +452 -373
- package/dist/nestjs/index.cjs.map +1 -1
- package/dist/nestjs/index.d.cts +1 -1
- package/dist/nestjs/index.d.ts +1 -1
- package/dist/nestjs/index.js +452 -373
- package/dist/nestjs/index.js.map +1 -1
- package/dist/runtime/index.cjs +417 -338
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.d.cts +1 -1
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +417 -338
- package/dist/runtime/index.js.map +1 -1
- package/dist/{types-CP3KkwZt.d.cts → types-CSJhEOk2.d.cts} +34 -0
- package/dist/{types-CP3KkwZt.d.ts → types-CSJhEOk2.d.ts} +34 -0
- package/docs/package-surface.md +1 -1
- package/docs/production-hardening.md +11 -4
- package/package.json +1 -1
- package/server.json +2 -2
package/dist/runtime/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/version.ts
|
|
2
|
-
var version = "2.8.
|
|
2
|
+
var version = "2.8.2";
|
|
3
3
|
|
|
4
4
|
// src/kernel/intent/IntentRegistry.ts
|
|
5
5
|
var IntentRegistry = class {
|
|
@@ -467,12 +467,39 @@ var ObservedLayerFlowViolationError = class extends Error {
|
|
|
467
467
|
}
|
|
468
468
|
};
|
|
469
469
|
|
|
470
|
-
// src/kernel/event-bus/
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
470
|
+
// src/kernel/event-bus/publishGuards.ts
|
|
471
|
+
function assertIntentAllowed(intentName, options) {
|
|
472
|
+
if (!options.strictRegistry && !options.validateIntentNaming) {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
if (options.validateIntentNaming) {
|
|
476
|
+
const validation = validateIntentName(intentName);
|
|
477
|
+
if (!validation.valid) {
|
|
478
|
+
throw new InvalidIntentNameError(intentName, validation.reason);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
if (options.strictRegistry && options.intentRegistry && !options.intentRegistry.has(intentName)) {
|
|
482
|
+
throw new UnregisteredIntentError(intentName);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
function assertSourceAllowed(event, options) {
|
|
486
|
+
if (!options.requireKnownSource) return;
|
|
487
|
+
if (!event.metadata.source || event.metadata.source === "unknown") {
|
|
488
|
+
throw new UnknownEventSourceError(event.intent);
|
|
489
|
+
}
|
|
490
|
+
if (options.intentRegistry && !options.intentRegistry.has(event.metadata.source)) {
|
|
491
|
+
throw new UnknownEventSourceError(event.intent, event.metadata.source);
|
|
492
|
+
}
|
|
475
493
|
}
|
|
494
|
+
function assertContractAllowed(event, options) {
|
|
495
|
+
if (!options.eventContracts) return;
|
|
496
|
+
const result = options.eventContracts.validate(event);
|
|
497
|
+
if (!result.ok && (options.strictEventContracts || result.contract)) {
|
|
498
|
+
throw new EventContractViolationError(event.intent, result.issues);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// src/kernel/event-bus/payloadPatch.ts
|
|
476
503
|
function isPlainRecord(value) {
|
|
477
504
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
478
505
|
return false;
|
|
@@ -543,29 +570,318 @@ function applyPayloadPatch(payload, patch) {
|
|
|
543
570
|
}
|
|
544
571
|
return mergeRecordPatch(payload, patch);
|
|
545
572
|
}
|
|
573
|
+
|
|
574
|
+
// src/kernel/event-bus/publishInterceptors.ts
|
|
575
|
+
async function applyInterceptors(event, deps) {
|
|
576
|
+
if (event.metadata.allowInterception === false) {
|
|
577
|
+
return event;
|
|
578
|
+
}
|
|
579
|
+
const matching = [...deps.interceptorsForIntent(event.intent)];
|
|
580
|
+
let current = event;
|
|
581
|
+
for (const registration of matching) {
|
|
582
|
+
const patches = [];
|
|
583
|
+
try {
|
|
584
|
+
await Promise.resolve(
|
|
585
|
+
registration.interceptor({
|
|
586
|
+
event: current,
|
|
587
|
+
intercept: (patch) => {
|
|
588
|
+
patches.push(patch);
|
|
589
|
+
}
|
|
590
|
+
})
|
|
591
|
+
);
|
|
592
|
+
if (patches.length === 0) {
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
596
|
+
let candidate = {
|
|
597
|
+
...current,
|
|
598
|
+
metadata: {
|
|
599
|
+
...current.metadata,
|
|
600
|
+
interceptions: [
|
|
601
|
+
...current.metadata.interceptions ?? [],
|
|
602
|
+
{ interceptorId: registration.interceptorId, timestamp }
|
|
603
|
+
]
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
for (const patch of patches) {
|
|
607
|
+
candidate = {
|
|
608
|
+
...candidate,
|
|
609
|
+
payload: applyPayloadPatch(candidate.payload, patch),
|
|
610
|
+
metadata: { ...candidate.metadata }
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
assertContractAllowed(candidate, {
|
|
614
|
+
eventContracts: deps.eventContracts,
|
|
615
|
+
strictEventContracts: deps.strictEventContracts
|
|
616
|
+
});
|
|
617
|
+
current = candidate;
|
|
618
|
+
registration.lastInterceptedAt = timestamp;
|
|
619
|
+
deps.appendTrace({
|
|
620
|
+
type: "event.intercepted",
|
|
621
|
+
timestamp,
|
|
622
|
+
intent: current.intent,
|
|
623
|
+
correlationId: current.metadata.correlationId,
|
|
624
|
+
traceId: current.metadata.traceId,
|
|
625
|
+
spanId: current.metadata.spanId,
|
|
626
|
+
details: {
|
|
627
|
+
registrationId: registration.registrationId,
|
|
628
|
+
interceptorId: registration.interceptorId,
|
|
629
|
+
patchesApplied: patches.length
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
await deps.recordAudit("event.intercepted", current, {
|
|
633
|
+
registrationId: registration.registrationId,
|
|
634
|
+
interceptorId: registration.interceptorId,
|
|
635
|
+
patchesApplied: patches.length
|
|
636
|
+
});
|
|
637
|
+
} catch (err) {
|
|
638
|
+
await recordInterceptorError(registration, current, err, deps);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return current;
|
|
642
|
+
}
|
|
643
|
+
async function recordInterceptorError(interceptor, event, error, deps) {
|
|
644
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
645
|
+
deps.appendTrace({
|
|
646
|
+
type: "interceptor.error",
|
|
647
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
648
|
+
intent: event.intent,
|
|
649
|
+
correlationId: event.metadata.correlationId,
|
|
650
|
+
traceId: event.metadata.traceId,
|
|
651
|
+
spanId: event.metadata.spanId,
|
|
652
|
+
details: {
|
|
653
|
+
registrationId: interceptor.registrationId,
|
|
654
|
+
interceptorId: interceptor.interceptorId,
|
|
655
|
+
error: message
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
await deps.recordAudit("interceptor.error", event, {
|
|
659
|
+
registrationId: interceptor.registrationId,
|
|
660
|
+
interceptorId: interceptor.interceptorId,
|
|
661
|
+
error: message
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// src/kernel/event-bus/observedLayerFlow.ts
|
|
666
|
+
async function assertObservedLayerFlowAllowed(event, deps) {
|
|
667
|
+
if (deps.mode === "off" || !deps.architectureProfile) {
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
const source = event.metadata.source;
|
|
671
|
+
if (!source || source === "unknown") return;
|
|
672
|
+
const profile = deps.architectureProfile;
|
|
673
|
+
const fromLayer = profile.resolveLayer(source);
|
|
674
|
+
const toLayer = profile.resolveLayer(event.intent);
|
|
675
|
+
if (!fromLayer || !toLayer) return;
|
|
676
|
+
const blocked = profile.rules.find(
|
|
677
|
+
(rule) => !rule.allowed && rule.from === fromLayer && rule.to === toLayer
|
|
678
|
+
);
|
|
679
|
+
if (!blocked) return;
|
|
680
|
+
const severity = deps.mode;
|
|
681
|
+
const message = blocked.message ?? `Observed layer violation: "${source}" (${fromLayer}) must not produce "${event.intent}" (${toLayer}).`;
|
|
682
|
+
const details = {
|
|
683
|
+
source,
|
|
684
|
+
intent: event.intent,
|
|
685
|
+
fromLayer,
|
|
686
|
+
toLayer,
|
|
687
|
+
severity,
|
|
688
|
+
message,
|
|
689
|
+
rule: blocked
|
|
690
|
+
};
|
|
691
|
+
deps.appendTrace({
|
|
692
|
+
type: "layer.observedViolation",
|
|
693
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
694
|
+
intent: event.intent,
|
|
695
|
+
correlationId: event.metadata.correlationId,
|
|
696
|
+
traceId: event.metadata.traceId,
|
|
697
|
+
spanId: event.metadata.spanId,
|
|
698
|
+
details
|
|
699
|
+
});
|
|
700
|
+
await deps.recordAudit("layer.observedViolation", event, details);
|
|
701
|
+
if (severity === "hard") {
|
|
702
|
+
throw new ObservedLayerFlowViolationError(
|
|
703
|
+
source,
|
|
704
|
+
event.intent,
|
|
705
|
+
fromLayer,
|
|
706
|
+
toLayer,
|
|
707
|
+
message
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// src/kernel/event-bus/publishPolicy.ts
|
|
713
|
+
async function enforcePublishPolicy(event, deps) {
|
|
714
|
+
const ctx = deps.getPolicyContext(event);
|
|
715
|
+
let policyResult;
|
|
716
|
+
try {
|
|
717
|
+
policyResult = deps.policyEngine.enforce(ctx);
|
|
718
|
+
} catch (err) {
|
|
719
|
+
if (err instanceof PolicyViolationError) {
|
|
720
|
+
deps.appendTrace({
|
|
721
|
+
type: "policy.hardViolation",
|
|
722
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
723
|
+
intent: event.intent,
|
|
724
|
+
correlationId: event.metadata.correlationId,
|
|
725
|
+
traceId: event.metadata.traceId,
|
|
726
|
+
spanId: event.metadata.spanId,
|
|
727
|
+
details: { violations: err.violations }
|
|
728
|
+
});
|
|
729
|
+
await deps.recordAudit("policy.hardViolation", event, {
|
|
730
|
+
violations: err.violations
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
throw err;
|
|
734
|
+
}
|
|
735
|
+
if (policyResult.softViolations.length > 0) {
|
|
736
|
+
deps.appendTrace({
|
|
737
|
+
type: "policy.softViolation",
|
|
738
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
739
|
+
intent: event.intent,
|
|
740
|
+
correlationId: event.metadata.correlationId,
|
|
741
|
+
traceId: event.metadata.traceId,
|
|
742
|
+
spanId: event.metadata.spanId,
|
|
743
|
+
details: { violations: policyResult.softViolations }
|
|
744
|
+
});
|
|
745
|
+
await deps.recordAudit("policy.softViolation", event, {
|
|
746
|
+
violations: policyResult.softViolations
|
|
747
|
+
});
|
|
748
|
+
if (deps.onSoftViolation) {
|
|
749
|
+
await deps.safeHook(
|
|
750
|
+
() => deps.onSoftViolation(policyResult, event),
|
|
751
|
+
"onSoftViolation",
|
|
752
|
+
event
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// src/kernel/event-bus/publishRecording.ts
|
|
759
|
+
function appendHistory(buffers, record) {
|
|
760
|
+
buffers.history.push(record);
|
|
761
|
+
if (buffers.maxHistorySize !== void 0 && buffers.history.length > buffers.maxHistorySize) {
|
|
762
|
+
buffers.history.splice(0, buffers.history.length - buffers.maxHistorySize);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
function appendTrace(buffers, record) {
|
|
766
|
+
buffers.trace.push(record);
|
|
767
|
+
if (buffers.maxHistorySize !== void 0 && buffers.trace.length > buffers.maxHistorySize) {
|
|
768
|
+
buffers.trace.splice(0, buffers.trace.length - buffers.maxHistorySize);
|
|
769
|
+
}
|
|
770
|
+
for (const sink of buffers.traceSinks) {
|
|
771
|
+
try {
|
|
772
|
+
sink(record);
|
|
773
|
+
} catch {
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
async function recordAudit(buffers, type, event, details) {
|
|
778
|
+
if (!buffers.auditTrail) return;
|
|
779
|
+
try {
|
|
780
|
+
await buffers.auditTrail.record({
|
|
781
|
+
type,
|
|
782
|
+
source: event.metadata.source,
|
|
783
|
+
intent: event.intent,
|
|
784
|
+
correlationId: event.metadata.correlationId,
|
|
785
|
+
causationId: event.metadata.causationId,
|
|
786
|
+
subject: event.intent,
|
|
787
|
+
details
|
|
788
|
+
});
|
|
789
|
+
} catch (err) {
|
|
790
|
+
appendTrace(buffers, {
|
|
791
|
+
type: "hook.error",
|
|
792
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
793
|
+
intent: event.intent,
|
|
794
|
+
correlationId: event.metadata.correlationId,
|
|
795
|
+
traceId: event.metadata.traceId,
|
|
796
|
+
spanId: event.metadata.spanId,
|
|
797
|
+
details: {
|
|
798
|
+
hook: "auditTrail",
|
|
799
|
+
error: err instanceof Error ? err.message : String(err)
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
async function recordRawPublishDiagnostic(buffers, event) {
|
|
805
|
+
const details = {
|
|
806
|
+
intent: event.intent,
|
|
807
|
+
source: event.metadata.source,
|
|
808
|
+
suggestion: "Publish through a registered intent creator so strict registry, contracts, and agent tooling share one source of truth."
|
|
809
|
+
};
|
|
810
|
+
appendTrace(buffers, {
|
|
811
|
+
type: "event.rawPublish",
|
|
812
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
813
|
+
intent: event.intent,
|
|
814
|
+
correlationId: event.metadata.correlationId,
|
|
815
|
+
traceId: event.metadata.traceId,
|
|
816
|
+
spanId: event.metadata.spanId,
|
|
817
|
+
details
|
|
818
|
+
});
|
|
819
|
+
await recordAudit(buffers, "event.rawPublish", event, details);
|
|
820
|
+
}
|
|
821
|
+
async function recordSuccessfulPublish(buffers, event, subscribersNotified) {
|
|
822
|
+
const record = {
|
|
823
|
+
event,
|
|
824
|
+
publishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
825
|
+
subscribersNotified
|
|
826
|
+
};
|
|
827
|
+
appendHistory(buffers, record);
|
|
828
|
+
await buffers.outbox?.enqueue(event);
|
|
829
|
+
appendTrace(buffers, {
|
|
830
|
+
type: "event.published",
|
|
831
|
+
timestamp: record.publishedAt,
|
|
832
|
+
intent: event.intent,
|
|
833
|
+
correlationId: event.metadata.correlationId,
|
|
834
|
+
traceId: event.metadata.traceId,
|
|
835
|
+
spanId: event.metadata.spanId,
|
|
836
|
+
details: { subscribersNotified }
|
|
837
|
+
});
|
|
838
|
+
await recordAudit(buffers, "event.published", event, {
|
|
839
|
+
subscribersNotified
|
|
840
|
+
});
|
|
841
|
+
return record;
|
|
842
|
+
}
|
|
843
|
+
function enrichMetadata(base, extra, instanceId) {
|
|
844
|
+
return {
|
|
845
|
+
...base,
|
|
846
|
+
...extra,
|
|
847
|
+
occurredAt: extra.occurredAt || base.occurredAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
848
|
+
source: extra.source || base.source || "unknown",
|
|
849
|
+
kernelInstanceId: extra.kernelInstanceId ?? base.kernelInstanceId ?? instanceId,
|
|
850
|
+
eventVersion: extra.eventVersion ?? base.eventVersion,
|
|
851
|
+
schemaVersion: extra.schemaVersion ?? base.schemaVersion,
|
|
852
|
+
allowInterception: extra.allowInterception ?? base.allowInterception,
|
|
853
|
+
interceptions: extra.interceptions ?? base.interceptions,
|
|
854
|
+
correlationId: extra.correlationId ?? base.correlationId,
|
|
855
|
+
causationId: extra.causationId ?? base.causationId,
|
|
856
|
+
traceId: extra.traceId ?? base.traceId,
|
|
857
|
+
spanId: extra.spanId ?? base.spanId,
|
|
858
|
+
parentSpanId: extra.parentSpanId ?? base.parentSpanId
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// src/kernel/event-bus/EventBus.ts
|
|
863
|
+
var interceptorSequence = 0;
|
|
864
|
+
function nextInterceptorRegistrationId() {
|
|
865
|
+
interceptorSequence += 1;
|
|
866
|
+
return `interceptor-${Date.now()}-${interceptorSequence}`;
|
|
867
|
+
}
|
|
546
868
|
var EventBusImpl = class {
|
|
547
869
|
subscriptions = [];
|
|
548
870
|
subscriptionsByIntent = /* @__PURE__ */ new Map();
|
|
549
871
|
interceptors = [];
|
|
550
872
|
interceptorsByIntent = /* @__PURE__ */ new Map();
|
|
551
|
-
|
|
552
|
-
trace = [];
|
|
873
|
+
recording;
|
|
553
874
|
onPublish;
|
|
554
875
|
onSoftViolation;
|
|
555
876
|
onHandlerError;
|
|
556
|
-
auditTrail;
|
|
557
877
|
eventContracts;
|
|
558
878
|
strictEventContracts;
|
|
559
879
|
requireKnownSource;
|
|
560
880
|
architectureProfile;
|
|
561
881
|
enforceObservedLayerFlowMode;
|
|
562
|
-
outbox;
|
|
563
|
-
instanceId;
|
|
564
|
-
traceSinks;
|
|
565
882
|
rethrowHandlerErrors;
|
|
566
883
|
policyEngine;
|
|
567
884
|
getPolicyContext;
|
|
568
|
-
maxHistorySize;
|
|
569
885
|
intentRegistry;
|
|
570
886
|
dependencyGraph;
|
|
571
887
|
strictRegistry;
|
|
@@ -574,21 +890,25 @@ var EventBusImpl = class {
|
|
|
574
890
|
this.onPublish = options.onPublish;
|
|
575
891
|
this.onSoftViolation = options.onSoftViolation;
|
|
576
892
|
this.onHandlerError = options.onHandlerError;
|
|
577
|
-
this.auditTrail = options.auditTrail;
|
|
578
893
|
this.eventContracts = options.eventContracts;
|
|
579
894
|
this.strictEventContracts = options.strictEventContracts ?? false;
|
|
580
895
|
this.requireKnownSource = options.requireKnownSource ?? false;
|
|
581
896
|
this.architectureProfile = options.architectureProfile;
|
|
582
897
|
this.enforceObservedLayerFlowMode = options.enforceObservedLayerFlow ?? "off";
|
|
583
|
-
this.outbox = options.outbox;
|
|
584
|
-
this.instanceId = options.instanceId;
|
|
585
|
-
this.traceSinks = [...options.traceSinks ?? []];
|
|
586
898
|
this.rethrowHandlerErrors = options.rethrowHandlerErrors ?? false;
|
|
587
|
-
this.maxHistorySize = options.maxHistorySize;
|
|
588
899
|
this.intentRegistry = options.intentRegistry;
|
|
589
900
|
this.dependencyGraph = options.dependencyGraph;
|
|
590
901
|
this.strictRegistry = options.strictRegistry ?? options.intentRegistry !== void 0;
|
|
591
902
|
this.validateIntentNaming = options.validateIntentNaming ?? this.strictRegistry;
|
|
903
|
+
this.recording = {
|
|
904
|
+
history: [],
|
|
905
|
+
trace: [],
|
|
906
|
+
maxHistorySize: options.maxHistorySize,
|
|
907
|
+
traceSinks: [...options.traceSinks ?? []],
|
|
908
|
+
auditTrail: options.auditTrail,
|
|
909
|
+
outbox: options.outbox,
|
|
910
|
+
instanceId: options.instanceId
|
|
911
|
+
};
|
|
592
912
|
if (options.policyEngine) {
|
|
593
913
|
this.policyEngine = options.policyEngine;
|
|
594
914
|
} else if (options.policies && options.policies.length > 0) {
|
|
@@ -619,94 +939,77 @@ var EventBusImpl = class {
|
|
|
619
939
|
const created = creator(payload);
|
|
620
940
|
event = {
|
|
621
941
|
...created,
|
|
622
|
-
metadata:
|
|
942
|
+
metadata: enrichMetadata(
|
|
943
|
+
created.metadata,
|
|
944
|
+
extraMeta,
|
|
945
|
+
this.recording.instanceId
|
|
946
|
+
)
|
|
623
947
|
};
|
|
624
948
|
} else {
|
|
625
949
|
const rawEvent = eventOrCreator;
|
|
626
950
|
const extraMeta = metadata ?? payloadOrMeta ?? {};
|
|
627
951
|
event = {
|
|
628
952
|
...rawEvent,
|
|
629
|
-
metadata:
|
|
953
|
+
metadata: enrichMetadata(
|
|
954
|
+
rawEvent.metadata,
|
|
955
|
+
extraMeta,
|
|
956
|
+
this.recording.instanceId
|
|
957
|
+
)
|
|
630
958
|
};
|
|
631
959
|
}
|
|
632
960
|
if (rawPublish && this.strictRegistry) {
|
|
633
|
-
await this.
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
961
|
+
await recordRawPublishDiagnostic(this.recording, event);
|
|
962
|
+
}
|
|
963
|
+
assertIntentAllowed(event.intent, {
|
|
964
|
+
strictRegistry: this.strictRegistry,
|
|
965
|
+
validateIntentNaming: this.validateIntentNaming,
|
|
966
|
+
intentRegistry: this.intentRegistry
|
|
967
|
+
});
|
|
968
|
+
assertSourceAllowed(event, {
|
|
969
|
+
requireKnownSource: this.requireKnownSource,
|
|
970
|
+
intentRegistry: this.intentRegistry
|
|
971
|
+
});
|
|
972
|
+
assertContractAllowed(event, {
|
|
973
|
+
eventContracts: this.eventContracts,
|
|
974
|
+
strictEventContracts: this.strictEventContracts
|
|
975
|
+
});
|
|
976
|
+
event = await applyInterceptors(event, {
|
|
977
|
+
interceptorsForIntent: (intent) => this.interceptorsByIntent.get(intent) ?? [],
|
|
978
|
+
eventContracts: this.eventContracts,
|
|
979
|
+
strictEventContracts: this.strictEventContracts,
|
|
980
|
+
appendTrace: (r) => this.appendTrace(r),
|
|
981
|
+
recordAudit: (type, e, details) => this.recordAudit(type, e, details)
|
|
982
|
+
});
|
|
983
|
+
assertContractAllowed(event, {
|
|
984
|
+
eventContracts: this.eventContracts,
|
|
985
|
+
strictEventContracts: this.strictEventContracts
|
|
986
|
+
});
|
|
987
|
+
await assertObservedLayerFlowAllowed(event, {
|
|
988
|
+
mode: this.enforceObservedLayerFlowMode,
|
|
989
|
+
architectureProfile: this.architectureProfile,
|
|
990
|
+
appendTrace: (r) => this.appendTrace(r),
|
|
991
|
+
recordAudit: (type, e, details) => this.recordAudit(type, e, details)
|
|
992
|
+
});
|
|
641
993
|
this.dependencyGraph?.registerEventFlow(event.metadata.source, event.intent);
|
|
642
994
|
const matching = [...this.subscriptionsByIntent.get(event.intent) ?? []];
|
|
643
995
|
if (this.policyEngine) {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
653
|
-
intent: event.intent,
|
|
654
|
-
correlationId: event.metadata.correlationId,
|
|
655
|
-
traceId: event.metadata.traceId,
|
|
656
|
-
spanId: event.metadata.spanId,
|
|
657
|
-
details: { violations: err.violations }
|
|
658
|
-
});
|
|
659
|
-
await this.recordAudit("policy.hardViolation", event, {
|
|
660
|
-
violations: err.violations
|
|
661
|
-
});
|
|
662
|
-
}
|
|
663
|
-
throw err;
|
|
664
|
-
}
|
|
665
|
-
if (policyResult.softViolations.length > 0) {
|
|
666
|
-
this.appendTrace({
|
|
667
|
-
type: "policy.softViolation",
|
|
668
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
669
|
-
intent: event.intent,
|
|
670
|
-
correlationId: event.metadata.correlationId,
|
|
671
|
-
traceId: event.metadata.traceId,
|
|
672
|
-
spanId: event.metadata.spanId,
|
|
673
|
-
details: { violations: policyResult.softViolations }
|
|
674
|
-
});
|
|
675
|
-
await this.recordAudit("policy.softViolation", event, {
|
|
676
|
-
violations: policyResult.softViolations
|
|
677
|
-
});
|
|
678
|
-
if (this.onSoftViolation) {
|
|
679
|
-
await this.safeHook(
|
|
680
|
-
() => this.onSoftViolation(policyResult, event),
|
|
681
|
-
"onSoftViolation",
|
|
682
|
-
event
|
|
683
|
-
);
|
|
684
|
-
}
|
|
685
|
-
}
|
|
996
|
+
await enforcePublishPolicy(event, {
|
|
997
|
+
policyEngine: this.policyEngine,
|
|
998
|
+
getPolicyContext: this.getPolicyContext,
|
|
999
|
+
appendTrace: (r) => this.appendTrace(r),
|
|
1000
|
+
recordAudit: (type, e, details) => this.recordAudit(type, e, details),
|
|
1001
|
+
onSoftViolation: this.onSoftViolation,
|
|
1002
|
+
safeHook: (fn, name, e) => this.safeHook(fn, name, e)
|
|
1003
|
+
});
|
|
686
1004
|
}
|
|
687
|
-
|
|
1005
|
+
await recordSuccessfulPublish(
|
|
1006
|
+
this.recording,
|
|
688
1007
|
event,
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
await this.outbox?.enqueue(event);
|
|
694
|
-
this.appendTrace({
|
|
695
|
-
type: "event.published",
|
|
696
|
-
timestamp: record.publishedAt,
|
|
697
|
-
intent: event.intent,
|
|
698
|
-
correlationId: event.metadata.correlationId,
|
|
699
|
-
traceId: event.metadata.traceId,
|
|
700
|
-
spanId: event.metadata.spanId,
|
|
701
|
-
details: { subscribersNotified: matching.length }
|
|
702
|
-
});
|
|
703
|
-
await this.recordAudit("event.published", event, {
|
|
704
|
-
subscribersNotified: matching.length
|
|
705
|
-
});
|
|
706
|
-
const notifications = matching.map(
|
|
707
|
-
(sub) => this.invokeHandler(sub, event)
|
|
1008
|
+
matching.length
|
|
1009
|
+
);
|
|
1010
|
+
await Promise.all(
|
|
1011
|
+
matching.map((sub) => this.invokeHandler(sub, event))
|
|
708
1012
|
);
|
|
709
|
-
await Promise.all(notifications);
|
|
710
1013
|
if (this.onPublish) {
|
|
711
1014
|
await this.safeHook(
|
|
712
1015
|
() => this.onPublish(event),
|
|
@@ -717,7 +1020,11 @@ var EventBusImpl = class {
|
|
|
717
1020
|
}
|
|
718
1021
|
createPublisher(source) {
|
|
719
1022
|
const sourceName = typeof source === "string" ? source : source.name;
|
|
720
|
-
|
|
1023
|
+
assertIntentAllowed(sourceName, {
|
|
1024
|
+
strictRegistry: this.strictRegistry,
|
|
1025
|
+
validateIntentNaming: this.validateIntentNaming,
|
|
1026
|
+
intentRegistry: this.intentRegistry
|
|
1027
|
+
});
|
|
721
1028
|
return {
|
|
722
1029
|
source: sourceName,
|
|
723
1030
|
publish: async (intent, payload, metadata = {}) => {
|
|
@@ -733,7 +1040,11 @@ var EventBusImpl = class {
|
|
|
733
1040
|
}
|
|
734
1041
|
subscribe(intent, handler) {
|
|
735
1042
|
const intentName = typeof intent === "string" ? intent : intent.name;
|
|
736
|
-
|
|
1043
|
+
assertIntentAllowed(intentName, {
|
|
1044
|
+
strictRegistry: this.strictRegistry,
|
|
1045
|
+
validateIntentNaming: this.validateIntentNaming,
|
|
1046
|
+
intentRegistry: this.intentRegistry
|
|
1047
|
+
});
|
|
737
1048
|
const sub = {
|
|
738
1049
|
intentName,
|
|
739
1050
|
handler
|
|
@@ -754,7 +1065,11 @@ var EventBusImpl = class {
|
|
|
754
1065
|
}
|
|
755
1066
|
registerInterceptor(intent, interceptor, interceptorId) {
|
|
756
1067
|
const intentName = typeof intent === "string" ? intent : intent.name;
|
|
757
|
-
|
|
1068
|
+
assertIntentAllowed(intentName, {
|
|
1069
|
+
strictRegistry: this.strictRegistry,
|
|
1070
|
+
validateIntentNaming: this.validateIntentNaming,
|
|
1071
|
+
intentRegistry: this.intentRegistry
|
|
1072
|
+
});
|
|
758
1073
|
const registration = {
|
|
759
1074
|
registrationId: nextInterceptorRegistrationId(),
|
|
760
1075
|
interceptorId: interceptorId ?? intentName,
|
|
@@ -793,174 +1108,22 @@ var EventBusImpl = class {
|
|
|
793
1108
|
}));
|
|
794
1109
|
}
|
|
795
1110
|
getHistory() {
|
|
796
|
-
return [...this.history];
|
|
1111
|
+
return [...this.recording.history];
|
|
797
1112
|
}
|
|
798
1113
|
clearHistory() {
|
|
799
|
-
this.history.length = 0;
|
|
1114
|
+
this.recording.history.length = 0;
|
|
800
1115
|
}
|
|
801
1116
|
getTrace() {
|
|
802
|
-
return [...this.trace];
|
|
1117
|
+
return [...this.recording.trace];
|
|
803
1118
|
}
|
|
804
1119
|
clearTrace() {
|
|
805
|
-
this.trace.length = 0;
|
|
806
|
-
}
|
|
807
|
-
assertIntentAllowed(intentName) {
|
|
808
|
-
if (!this.strictRegistry && !this.validateIntentNaming) {
|
|
809
|
-
return;
|
|
810
|
-
}
|
|
811
|
-
if (this.validateIntentNaming) {
|
|
812
|
-
const validation = validateIntentName(intentName);
|
|
813
|
-
if (!validation.valid) {
|
|
814
|
-
throw new InvalidIntentNameError(intentName, validation.reason);
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
if (this.strictRegistry && this.intentRegistry && !this.intentRegistry.has(intentName)) {
|
|
818
|
-
throw new UnregisteredIntentError(intentName);
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
assertSourceAllowed(event) {
|
|
822
|
-
if (!this.requireKnownSource) return;
|
|
823
|
-
if (!event.metadata.source || event.metadata.source === "unknown") {
|
|
824
|
-
throw new UnknownEventSourceError(event.intent);
|
|
825
|
-
}
|
|
826
|
-
if (this.intentRegistry && !this.intentRegistry.has(event.metadata.source)) {
|
|
827
|
-
throw new UnknownEventSourceError(event.intent, event.metadata.source);
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
assertContractAllowed(event) {
|
|
831
|
-
if (!this.eventContracts) return;
|
|
832
|
-
const result = this.eventContracts.validate(event);
|
|
833
|
-
if (!result.ok && (this.strictEventContracts || result.contract)) {
|
|
834
|
-
throw new EventContractViolationError(event.intent, result.issues);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
/**
|
|
838
|
-
* Enforce the OBSERVED producer→event flow (metadata.source → intent) against the
|
|
839
|
-
* architecture profile's layer rules. This is the runtime counterpart to the
|
|
840
|
-
* declared-model layer policy: it checks what the system actually did, resolving both
|
|
841
|
-
* layers directly from the profile. It runs BEFORE the flow is recorded via
|
|
842
|
-
* registerEventFlow, so in hard mode a rejected flow leaves no edge in the graph.
|
|
843
|
-
*/
|
|
844
|
-
async assertObservedLayerFlowAllowed(event) {
|
|
845
|
-
if (this.enforceObservedLayerFlowMode === "off" || !this.architectureProfile) {
|
|
846
|
-
return;
|
|
847
|
-
}
|
|
848
|
-
const source = event.metadata.source;
|
|
849
|
-
if (!source || source === "unknown") return;
|
|
850
|
-
const profile = this.architectureProfile;
|
|
851
|
-
const fromLayer = profile.resolveLayer(source);
|
|
852
|
-
const toLayer = profile.resolveLayer(event.intent);
|
|
853
|
-
if (!fromLayer || !toLayer) return;
|
|
854
|
-
const blocked = profile.rules.find(
|
|
855
|
-
(rule) => !rule.allowed && rule.from === fromLayer && rule.to === toLayer
|
|
856
|
-
);
|
|
857
|
-
if (!blocked) return;
|
|
858
|
-
const severity = this.enforceObservedLayerFlowMode;
|
|
859
|
-
const message = blocked.message ?? `Observed layer violation: "${source}" (${fromLayer}) must not produce "${event.intent}" (${toLayer}).`;
|
|
860
|
-
const details = {
|
|
861
|
-
source,
|
|
862
|
-
intent: event.intent,
|
|
863
|
-
fromLayer,
|
|
864
|
-
toLayer,
|
|
865
|
-
severity,
|
|
866
|
-
message,
|
|
867
|
-
rule: blocked
|
|
868
|
-
};
|
|
869
|
-
this.appendTrace({
|
|
870
|
-
type: "layer.observedViolation",
|
|
871
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
872
|
-
intent: event.intent,
|
|
873
|
-
correlationId: event.metadata.correlationId,
|
|
874
|
-
traceId: event.metadata.traceId,
|
|
875
|
-
spanId: event.metadata.spanId,
|
|
876
|
-
details
|
|
877
|
-
});
|
|
878
|
-
await this.recordAudit("layer.observedViolation", event, details);
|
|
879
|
-
if (severity === "hard") {
|
|
880
|
-
throw new ObservedLayerFlowViolationError(source, event.intent, fromLayer, toLayer, message);
|
|
881
|
-
}
|
|
1120
|
+
this.recording.trace.length = 0;
|
|
882
1121
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
intent: event.intent,
|
|
886
|
-
source: event.metadata.source,
|
|
887
|
-
suggestion: "Publish through a registered intent creator so strict registry, contracts, and agent tooling share one source of truth."
|
|
888
|
-
};
|
|
889
|
-
this.appendTrace({
|
|
890
|
-
type: "event.rawPublish",
|
|
891
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
892
|
-
intent: event.intent,
|
|
893
|
-
correlationId: event.metadata.correlationId,
|
|
894
|
-
traceId: event.metadata.traceId,
|
|
895
|
-
spanId: event.metadata.spanId,
|
|
896
|
-
details
|
|
897
|
-
});
|
|
898
|
-
await this.recordAudit("event.rawPublish", event, details);
|
|
1122
|
+
appendTrace(record) {
|
|
1123
|
+
appendTrace(this.recording, record);
|
|
899
1124
|
}
|
|
900
|
-
async
|
|
901
|
-
|
|
902
|
-
return event;
|
|
903
|
-
}
|
|
904
|
-
const matching = [...this.interceptorsByIntent.get(event.intent) ?? []];
|
|
905
|
-
let current = event;
|
|
906
|
-
for (const registration of matching) {
|
|
907
|
-
const patches = [];
|
|
908
|
-
try {
|
|
909
|
-
await Promise.resolve(
|
|
910
|
-
registration.interceptor({
|
|
911
|
-
event: current,
|
|
912
|
-
intercept: (patch) => {
|
|
913
|
-
patches.push(patch);
|
|
914
|
-
}
|
|
915
|
-
})
|
|
916
|
-
);
|
|
917
|
-
if (patches.length === 0) {
|
|
918
|
-
continue;
|
|
919
|
-
}
|
|
920
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
921
|
-
let candidate = {
|
|
922
|
-
...current,
|
|
923
|
-
metadata: {
|
|
924
|
-
...current.metadata,
|
|
925
|
-
interceptions: [
|
|
926
|
-
...current.metadata.interceptions ?? [],
|
|
927
|
-
{ interceptorId: registration.interceptorId, timestamp }
|
|
928
|
-
]
|
|
929
|
-
}
|
|
930
|
-
};
|
|
931
|
-
for (const patch of patches) {
|
|
932
|
-
candidate = {
|
|
933
|
-
...candidate,
|
|
934
|
-
payload: applyPayloadPatch(candidate.payload, patch),
|
|
935
|
-
metadata: { ...candidate.metadata }
|
|
936
|
-
};
|
|
937
|
-
}
|
|
938
|
-
this.assertContractAllowed(candidate);
|
|
939
|
-
current = candidate;
|
|
940
|
-
registration.lastInterceptedAt = timestamp;
|
|
941
|
-
this.appendTrace({
|
|
942
|
-
type: "event.intercepted",
|
|
943
|
-
timestamp,
|
|
944
|
-
intent: current.intent,
|
|
945
|
-
correlationId: current.metadata.correlationId,
|
|
946
|
-
traceId: current.metadata.traceId,
|
|
947
|
-
spanId: current.metadata.spanId,
|
|
948
|
-
details: {
|
|
949
|
-
registrationId: registration.registrationId,
|
|
950
|
-
interceptorId: registration.interceptorId,
|
|
951
|
-
patchesApplied: patches.length
|
|
952
|
-
}
|
|
953
|
-
});
|
|
954
|
-
await this.recordAudit("event.intercepted", current, {
|
|
955
|
-
registrationId: registration.registrationId,
|
|
956
|
-
interceptorId: registration.interceptorId,
|
|
957
|
-
patchesApplied: patches.length
|
|
958
|
-
});
|
|
959
|
-
} catch (err) {
|
|
960
|
-
await this.recordInterceptorError(registration, current, err);
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
return current;
|
|
1125
|
+
async recordAudit(type, event, details) {
|
|
1126
|
+
await recordAudit(this.recording, type, event, details);
|
|
964
1127
|
}
|
|
965
1128
|
async invokeHandler(sub, event) {
|
|
966
1129
|
try {
|
|
@@ -1013,90 +1176,6 @@ var EventBusImpl = class {
|
|
|
1013
1176
|
});
|
|
1014
1177
|
}
|
|
1015
1178
|
}
|
|
1016
|
-
async recordInterceptorError(interceptor, event, error) {
|
|
1017
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1018
|
-
this.appendTrace({
|
|
1019
|
-
type: "interceptor.error",
|
|
1020
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1021
|
-
intent: event.intent,
|
|
1022
|
-
correlationId: event.metadata.correlationId,
|
|
1023
|
-
traceId: event.metadata.traceId,
|
|
1024
|
-
spanId: event.metadata.spanId,
|
|
1025
|
-
details: {
|
|
1026
|
-
registrationId: interceptor.registrationId,
|
|
1027
|
-
interceptorId: interceptor.interceptorId,
|
|
1028
|
-
error: message
|
|
1029
|
-
}
|
|
1030
|
-
});
|
|
1031
|
-
await this.recordAudit("interceptor.error", event, {
|
|
1032
|
-
registrationId: interceptor.registrationId,
|
|
1033
|
-
interceptorId: interceptor.interceptorId,
|
|
1034
|
-
error: message
|
|
1035
|
-
});
|
|
1036
|
-
}
|
|
1037
|
-
appendHistory(record) {
|
|
1038
|
-
this.history.push(record);
|
|
1039
|
-
if (this.maxHistorySize !== void 0 && this.history.length > this.maxHistorySize) {
|
|
1040
|
-
this.history.splice(0, this.history.length - this.maxHistorySize);
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
appendTrace(record) {
|
|
1044
|
-
this.trace.push(record);
|
|
1045
|
-
if (this.maxHistorySize !== void 0 && this.trace.length > this.maxHistorySize) {
|
|
1046
|
-
this.trace.splice(0, this.trace.length - this.maxHistorySize);
|
|
1047
|
-
}
|
|
1048
|
-
for (const sink of this.traceSinks) {
|
|
1049
|
-
try {
|
|
1050
|
-
sink(record);
|
|
1051
|
-
} catch {
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
async recordAudit(type, event, details) {
|
|
1056
|
-
if (!this.auditTrail) return;
|
|
1057
|
-
try {
|
|
1058
|
-
await this.auditTrail.record({
|
|
1059
|
-
type,
|
|
1060
|
-
source: event.metadata.source,
|
|
1061
|
-
intent: event.intent,
|
|
1062
|
-
correlationId: event.metadata.correlationId,
|
|
1063
|
-
causationId: event.metadata.causationId,
|
|
1064
|
-
subject: event.intent,
|
|
1065
|
-
details
|
|
1066
|
-
});
|
|
1067
|
-
} catch (err) {
|
|
1068
|
-
this.appendTrace({
|
|
1069
|
-
type: "hook.error",
|
|
1070
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1071
|
-
intent: event.intent,
|
|
1072
|
-
correlationId: event.metadata.correlationId,
|
|
1073
|
-
traceId: event.metadata.traceId,
|
|
1074
|
-
spanId: event.metadata.spanId,
|
|
1075
|
-
details: {
|
|
1076
|
-
hook: "auditTrail",
|
|
1077
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1078
|
-
}
|
|
1079
|
-
});
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
enrichMetadata(base, extra) {
|
|
1083
|
-
return {
|
|
1084
|
-
...base,
|
|
1085
|
-
...extra,
|
|
1086
|
-
occurredAt: extra.occurredAt || base.occurredAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1087
|
-
source: extra.source || base.source || "unknown",
|
|
1088
|
-
kernelInstanceId: extra.kernelInstanceId ?? base.kernelInstanceId ?? this.instanceId,
|
|
1089
|
-
eventVersion: extra.eventVersion ?? base.eventVersion,
|
|
1090
|
-
schemaVersion: extra.schemaVersion ?? base.schemaVersion,
|
|
1091
|
-
allowInterception: extra.allowInterception ?? base.allowInterception,
|
|
1092
|
-
interceptions: extra.interceptions ?? base.interceptions,
|
|
1093
|
-
correlationId: extra.correlationId ?? base.correlationId,
|
|
1094
|
-
causationId: extra.causationId ?? base.causationId,
|
|
1095
|
-
traceId: extra.traceId ?? base.traceId,
|
|
1096
|
-
spanId: extra.spanId ?? base.spanId,
|
|
1097
|
-
parentSpanId: extra.parentSpanId ?? base.parentSpanId
|
|
1098
|
-
};
|
|
1099
|
-
}
|
|
1100
1179
|
};
|
|
1101
1180
|
function createEventBus(options) {
|
|
1102
1181
|
return new EventBusImpl(options);
|