arkgate 2.8.0 → 2.8.1
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 +18 -0
- package/README.md +8 -0
- 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/index.cjs
CHANGED
|
@@ -80,7 +80,7 @@ __export(index_exports, {
|
|
|
80
80
|
module.exports = __toCommonJS(index_exports);
|
|
81
81
|
|
|
82
82
|
// src/version.ts
|
|
83
|
-
var version = "2.8.
|
|
83
|
+
var version = "2.8.1";
|
|
84
84
|
|
|
85
85
|
// src/kernel/intent/IntentRegistry.ts
|
|
86
86
|
var IntentRegistry = class {
|
|
@@ -548,12 +548,39 @@ var ObservedLayerFlowViolationError = class extends Error {
|
|
|
548
548
|
}
|
|
549
549
|
};
|
|
550
550
|
|
|
551
|
-
// src/kernel/event-bus/
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
551
|
+
// src/kernel/event-bus/publishGuards.ts
|
|
552
|
+
function assertIntentAllowed(intentName, options) {
|
|
553
|
+
if (!options.strictRegistry && !options.validateIntentNaming) {
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
if (options.validateIntentNaming) {
|
|
557
|
+
const validation = validateIntentName(intentName);
|
|
558
|
+
if (!validation.valid) {
|
|
559
|
+
throw new InvalidIntentNameError(intentName, validation.reason);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
if (options.strictRegistry && options.intentRegistry && !options.intentRegistry.has(intentName)) {
|
|
563
|
+
throw new UnregisteredIntentError(intentName);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
function assertSourceAllowed(event, options) {
|
|
567
|
+
if (!options.requireKnownSource) return;
|
|
568
|
+
if (!event.metadata.source || event.metadata.source === "unknown") {
|
|
569
|
+
throw new UnknownEventSourceError(event.intent);
|
|
570
|
+
}
|
|
571
|
+
if (options.intentRegistry && !options.intentRegistry.has(event.metadata.source)) {
|
|
572
|
+
throw new UnknownEventSourceError(event.intent, event.metadata.source);
|
|
573
|
+
}
|
|
556
574
|
}
|
|
575
|
+
function assertContractAllowed(event, options) {
|
|
576
|
+
if (!options.eventContracts) return;
|
|
577
|
+
const result = options.eventContracts.validate(event);
|
|
578
|
+
if (!result.ok && (options.strictEventContracts || result.contract)) {
|
|
579
|
+
throw new EventContractViolationError(event.intent, result.issues);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// src/kernel/event-bus/payloadPatch.ts
|
|
557
584
|
function isPlainRecord(value) {
|
|
558
585
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
559
586
|
return false;
|
|
@@ -624,29 +651,318 @@ function applyPayloadPatch(payload, patch) {
|
|
|
624
651
|
}
|
|
625
652
|
return mergeRecordPatch(payload, patch);
|
|
626
653
|
}
|
|
654
|
+
|
|
655
|
+
// src/kernel/event-bus/publishInterceptors.ts
|
|
656
|
+
async function applyInterceptors(event, deps) {
|
|
657
|
+
if (event.metadata.allowInterception === false) {
|
|
658
|
+
return event;
|
|
659
|
+
}
|
|
660
|
+
const matching = [...deps.interceptorsForIntent(event.intent)];
|
|
661
|
+
let current = event;
|
|
662
|
+
for (const registration of matching) {
|
|
663
|
+
const patches = [];
|
|
664
|
+
try {
|
|
665
|
+
await Promise.resolve(
|
|
666
|
+
registration.interceptor({
|
|
667
|
+
event: current,
|
|
668
|
+
intercept: (patch) => {
|
|
669
|
+
patches.push(patch);
|
|
670
|
+
}
|
|
671
|
+
})
|
|
672
|
+
);
|
|
673
|
+
if (patches.length === 0) {
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
677
|
+
let candidate = {
|
|
678
|
+
...current,
|
|
679
|
+
metadata: {
|
|
680
|
+
...current.metadata,
|
|
681
|
+
interceptions: [
|
|
682
|
+
...current.metadata.interceptions ?? [],
|
|
683
|
+
{ interceptorId: registration.interceptorId, timestamp }
|
|
684
|
+
]
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
for (const patch of patches) {
|
|
688
|
+
candidate = {
|
|
689
|
+
...candidate,
|
|
690
|
+
payload: applyPayloadPatch(candidate.payload, patch),
|
|
691
|
+
metadata: { ...candidate.metadata }
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
assertContractAllowed(candidate, {
|
|
695
|
+
eventContracts: deps.eventContracts,
|
|
696
|
+
strictEventContracts: deps.strictEventContracts
|
|
697
|
+
});
|
|
698
|
+
current = candidate;
|
|
699
|
+
registration.lastInterceptedAt = timestamp;
|
|
700
|
+
deps.appendTrace({
|
|
701
|
+
type: "event.intercepted",
|
|
702
|
+
timestamp,
|
|
703
|
+
intent: current.intent,
|
|
704
|
+
correlationId: current.metadata.correlationId,
|
|
705
|
+
traceId: current.metadata.traceId,
|
|
706
|
+
spanId: current.metadata.spanId,
|
|
707
|
+
details: {
|
|
708
|
+
registrationId: registration.registrationId,
|
|
709
|
+
interceptorId: registration.interceptorId,
|
|
710
|
+
patchesApplied: patches.length
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
await deps.recordAudit("event.intercepted", current, {
|
|
714
|
+
registrationId: registration.registrationId,
|
|
715
|
+
interceptorId: registration.interceptorId,
|
|
716
|
+
patchesApplied: patches.length
|
|
717
|
+
});
|
|
718
|
+
} catch (err) {
|
|
719
|
+
await recordInterceptorError(registration, current, err, deps);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return current;
|
|
723
|
+
}
|
|
724
|
+
async function recordInterceptorError(interceptor, event, error, deps) {
|
|
725
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
726
|
+
deps.appendTrace({
|
|
727
|
+
type: "interceptor.error",
|
|
728
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
729
|
+
intent: event.intent,
|
|
730
|
+
correlationId: event.metadata.correlationId,
|
|
731
|
+
traceId: event.metadata.traceId,
|
|
732
|
+
spanId: event.metadata.spanId,
|
|
733
|
+
details: {
|
|
734
|
+
registrationId: interceptor.registrationId,
|
|
735
|
+
interceptorId: interceptor.interceptorId,
|
|
736
|
+
error: message
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
await deps.recordAudit("interceptor.error", event, {
|
|
740
|
+
registrationId: interceptor.registrationId,
|
|
741
|
+
interceptorId: interceptor.interceptorId,
|
|
742
|
+
error: message
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// src/kernel/event-bus/observedLayerFlow.ts
|
|
747
|
+
async function assertObservedLayerFlowAllowed(event, deps) {
|
|
748
|
+
if (deps.mode === "off" || !deps.architectureProfile) {
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
const source = event.metadata.source;
|
|
752
|
+
if (!source || source === "unknown") return;
|
|
753
|
+
const profile = deps.architectureProfile;
|
|
754
|
+
const fromLayer = profile.resolveLayer(source);
|
|
755
|
+
const toLayer = profile.resolveLayer(event.intent);
|
|
756
|
+
if (!fromLayer || !toLayer) return;
|
|
757
|
+
const blocked = profile.rules.find(
|
|
758
|
+
(rule) => !rule.allowed && rule.from === fromLayer && rule.to === toLayer
|
|
759
|
+
);
|
|
760
|
+
if (!blocked) return;
|
|
761
|
+
const severity = deps.mode;
|
|
762
|
+
const message = blocked.message ?? `Observed layer violation: "${source}" (${fromLayer}) must not produce "${event.intent}" (${toLayer}).`;
|
|
763
|
+
const details = {
|
|
764
|
+
source,
|
|
765
|
+
intent: event.intent,
|
|
766
|
+
fromLayer,
|
|
767
|
+
toLayer,
|
|
768
|
+
severity,
|
|
769
|
+
message,
|
|
770
|
+
rule: blocked
|
|
771
|
+
};
|
|
772
|
+
deps.appendTrace({
|
|
773
|
+
type: "layer.observedViolation",
|
|
774
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
775
|
+
intent: event.intent,
|
|
776
|
+
correlationId: event.metadata.correlationId,
|
|
777
|
+
traceId: event.metadata.traceId,
|
|
778
|
+
spanId: event.metadata.spanId,
|
|
779
|
+
details
|
|
780
|
+
});
|
|
781
|
+
await deps.recordAudit("layer.observedViolation", event, details);
|
|
782
|
+
if (severity === "hard") {
|
|
783
|
+
throw new ObservedLayerFlowViolationError(
|
|
784
|
+
source,
|
|
785
|
+
event.intent,
|
|
786
|
+
fromLayer,
|
|
787
|
+
toLayer,
|
|
788
|
+
message
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// src/kernel/event-bus/publishPolicy.ts
|
|
794
|
+
async function enforcePublishPolicy(event, deps) {
|
|
795
|
+
const ctx = deps.getPolicyContext(event);
|
|
796
|
+
let policyResult;
|
|
797
|
+
try {
|
|
798
|
+
policyResult = deps.policyEngine.enforce(ctx);
|
|
799
|
+
} catch (err) {
|
|
800
|
+
if (err instanceof PolicyViolationError) {
|
|
801
|
+
deps.appendTrace({
|
|
802
|
+
type: "policy.hardViolation",
|
|
803
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
804
|
+
intent: event.intent,
|
|
805
|
+
correlationId: event.metadata.correlationId,
|
|
806
|
+
traceId: event.metadata.traceId,
|
|
807
|
+
spanId: event.metadata.spanId,
|
|
808
|
+
details: { violations: err.violations }
|
|
809
|
+
});
|
|
810
|
+
await deps.recordAudit("policy.hardViolation", event, {
|
|
811
|
+
violations: err.violations
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
throw err;
|
|
815
|
+
}
|
|
816
|
+
if (policyResult.softViolations.length > 0) {
|
|
817
|
+
deps.appendTrace({
|
|
818
|
+
type: "policy.softViolation",
|
|
819
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
820
|
+
intent: event.intent,
|
|
821
|
+
correlationId: event.metadata.correlationId,
|
|
822
|
+
traceId: event.metadata.traceId,
|
|
823
|
+
spanId: event.metadata.spanId,
|
|
824
|
+
details: { violations: policyResult.softViolations }
|
|
825
|
+
});
|
|
826
|
+
await deps.recordAudit("policy.softViolation", event, {
|
|
827
|
+
violations: policyResult.softViolations
|
|
828
|
+
});
|
|
829
|
+
if (deps.onSoftViolation) {
|
|
830
|
+
await deps.safeHook(
|
|
831
|
+
() => deps.onSoftViolation(policyResult, event),
|
|
832
|
+
"onSoftViolation",
|
|
833
|
+
event
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// src/kernel/event-bus/publishRecording.ts
|
|
840
|
+
function appendHistory(buffers, record) {
|
|
841
|
+
buffers.history.push(record);
|
|
842
|
+
if (buffers.maxHistorySize !== void 0 && buffers.history.length > buffers.maxHistorySize) {
|
|
843
|
+
buffers.history.splice(0, buffers.history.length - buffers.maxHistorySize);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
function appendTrace(buffers, record) {
|
|
847
|
+
buffers.trace.push(record);
|
|
848
|
+
if (buffers.maxHistorySize !== void 0 && buffers.trace.length > buffers.maxHistorySize) {
|
|
849
|
+
buffers.trace.splice(0, buffers.trace.length - buffers.maxHistorySize);
|
|
850
|
+
}
|
|
851
|
+
for (const sink of buffers.traceSinks) {
|
|
852
|
+
try {
|
|
853
|
+
sink(record);
|
|
854
|
+
} catch {
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
async function recordAudit(buffers, type, event, details) {
|
|
859
|
+
if (!buffers.auditTrail) return;
|
|
860
|
+
try {
|
|
861
|
+
await buffers.auditTrail.record({
|
|
862
|
+
type,
|
|
863
|
+
source: event.metadata.source,
|
|
864
|
+
intent: event.intent,
|
|
865
|
+
correlationId: event.metadata.correlationId,
|
|
866
|
+
causationId: event.metadata.causationId,
|
|
867
|
+
subject: event.intent,
|
|
868
|
+
details
|
|
869
|
+
});
|
|
870
|
+
} catch (err) {
|
|
871
|
+
appendTrace(buffers, {
|
|
872
|
+
type: "hook.error",
|
|
873
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
874
|
+
intent: event.intent,
|
|
875
|
+
correlationId: event.metadata.correlationId,
|
|
876
|
+
traceId: event.metadata.traceId,
|
|
877
|
+
spanId: event.metadata.spanId,
|
|
878
|
+
details: {
|
|
879
|
+
hook: "auditTrail",
|
|
880
|
+
error: err instanceof Error ? err.message : String(err)
|
|
881
|
+
}
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
async function recordRawPublishDiagnostic(buffers, event) {
|
|
886
|
+
const details = {
|
|
887
|
+
intent: event.intent,
|
|
888
|
+
source: event.metadata.source,
|
|
889
|
+
suggestion: "Publish through a registered intent creator so strict registry, contracts, and agent tooling share one source of truth."
|
|
890
|
+
};
|
|
891
|
+
appendTrace(buffers, {
|
|
892
|
+
type: "event.rawPublish",
|
|
893
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
894
|
+
intent: event.intent,
|
|
895
|
+
correlationId: event.metadata.correlationId,
|
|
896
|
+
traceId: event.metadata.traceId,
|
|
897
|
+
spanId: event.metadata.spanId,
|
|
898
|
+
details
|
|
899
|
+
});
|
|
900
|
+
await recordAudit(buffers, "event.rawPublish", event, details);
|
|
901
|
+
}
|
|
902
|
+
async function recordSuccessfulPublish(buffers, event, subscribersNotified) {
|
|
903
|
+
const record = {
|
|
904
|
+
event,
|
|
905
|
+
publishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
906
|
+
subscribersNotified
|
|
907
|
+
};
|
|
908
|
+
appendHistory(buffers, record);
|
|
909
|
+
await buffers.outbox?.enqueue(event);
|
|
910
|
+
appendTrace(buffers, {
|
|
911
|
+
type: "event.published",
|
|
912
|
+
timestamp: record.publishedAt,
|
|
913
|
+
intent: event.intent,
|
|
914
|
+
correlationId: event.metadata.correlationId,
|
|
915
|
+
traceId: event.metadata.traceId,
|
|
916
|
+
spanId: event.metadata.spanId,
|
|
917
|
+
details: { subscribersNotified }
|
|
918
|
+
});
|
|
919
|
+
await recordAudit(buffers, "event.published", event, {
|
|
920
|
+
subscribersNotified
|
|
921
|
+
});
|
|
922
|
+
return record;
|
|
923
|
+
}
|
|
924
|
+
function enrichMetadata(base, extra, instanceId) {
|
|
925
|
+
return {
|
|
926
|
+
...base,
|
|
927
|
+
...extra,
|
|
928
|
+
occurredAt: extra.occurredAt || base.occurredAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
929
|
+
source: extra.source || base.source || "unknown",
|
|
930
|
+
kernelInstanceId: extra.kernelInstanceId ?? base.kernelInstanceId ?? instanceId,
|
|
931
|
+
eventVersion: extra.eventVersion ?? base.eventVersion,
|
|
932
|
+
schemaVersion: extra.schemaVersion ?? base.schemaVersion,
|
|
933
|
+
allowInterception: extra.allowInterception ?? base.allowInterception,
|
|
934
|
+
interceptions: extra.interceptions ?? base.interceptions,
|
|
935
|
+
correlationId: extra.correlationId ?? base.correlationId,
|
|
936
|
+
causationId: extra.causationId ?? base.causationId,
|
|
937
|
+
traceId: extra.traceId ?? base.traceId,
|
|
938
|
+
spanId: extra.spanId ?? base.spanId,
|
|
939
|
+
parentSpanId: extra.parentSpanId ?? base.parentSpanId
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// src/kernel/event-bus/EventBus.ts
|
|
944
|
+
var interceptorSequence = 0;
|
|
945
|
+
function nextInterceptorRegistrationId() {
|
|
946
|
+
interceptorSequence += 1;
|
|
947
|
+
return `interceptor-${Date.now()}-${interceptorSequence}`;
|
|
948
|
+
}
|
|
627
949
|
var EventBusImpl = class {
|
|
628
950
|
subscriptions = [];
|
|
629
951
|
subscriptionsByIntent = /* @__PURE__ */ new Map();
|
|
630
952
|
interceptors = [];
|
|
631
953
|
interceptorsByIntent = /* @__PURE__ */ new Map();
|
|
632
|
-
|
|
633
|
-
trace = [];
|
|
954
|
+
recording;
|
|
634
955
|
onPublish;
|
|
635
956
|
onSoftViolation;
|
|
636
957
|
onHandlerError;
|
|
637
|
-
auditTrail;
|
|
638
958
|
eventContracts;
|
|
639
959
|
strictEventContracts;
|
|
640
960
|
requireKnownSource;
|
|
641
961
|
architectureProfile;
|
|
642
962
|
enforceObservedLayerFlowMode;
|
|
643
|
-
outbox;
|
|
644
|
-
instanceId;
|
|
645
|
-
traceSinks;
|
|
646
963
|
rethrowHandlerErrors;
|
|
647
964
|
policyEngine;
|
|
648
965
|
getPolicyContext;
|
|
649
|
-
maxHistorySize;
|
|
650
966
|
intentRegistry;
|
|
651
967
|
dependencyGraph;
|
|
652
968
|
strictRegistry;
|
|
@@ -655,21 +971,25 @@ var EventBusImpl = class {
|
|
|
655
971
|
this.onPublish = options.onPublish;
|
|
656
972
|
this.onSoftViolation = options.onSoftViolation;
|
|
657
973
|
this.onHandlerError = options.onHandlerError;
|
|
658
|
-
this.auditTrail = options.auditTrail;
|
|
659
974
|
this.eventContracts = options.eventContracts;
|
|
660
975
|
this.strictEventContracts = options.strictEventContracts ?? false;
|
|
661
976
|
this.requireKnownSource = options.requireKnownSource ?? false;
|
|
662
977
|
this.architectureProfile = options.architectureProfile;
|
|
663
978
|
this.enforceObservedLayerFlowMode = options.enforceObservedLayerFlow ?? "off";
|
|
664
|
-
this.outbox = options.outbox;
|
|
665
|
-
this.instanceId = options.instanceId;
|
|
666
|
-
this.traceSinks = [...options.traceSinks ?? []];
|
|
667
979
|
this.rethrowHandlerErrors = options.rethrowHandlerErrors ?? false;
|
|
668
|
-
this.maxHistorySize = options.maxHistorySize;
|
|
669
980
|
this.intentRegistry = options.intentRegistry;
|
|
670
981
|
this.dependencyGraph = options.dependencyGraph;
|
|
671
982
|
this.strictRegistry = options.strictRegistry ?? options.intentRegistry !== void 0;
|
|
672
983
|
this.validateIntentNaming = options.validateIntentNaming ?? this.strictRegistry;
|
|
984
|
+
this.recording = {
|
|
985
|
+
history: [],
|
|
986
|
+
trace: [],
|
|
987
|
+
maxHistorySize: options.maxHistorySize,
|
|
988
|
+
traceSinks: [...options.traceSinks ?? []],
|
|
989
|
+
auditTrail: options.auditTrail,
|
|
990
|
+
outbox: options.outbox,
|
|
991
|
+
instanceId: options.instanceId
|
|
992
|
+
};
|
|
673
993
|
if (options.policyEngine) {
|
|
674
994
|
this.policyEngine = options.policyEngine;
|
|
675
995
|
} else if (options.policies && options.policies.length > 0) {
|
|
@@ -700,94 +1020,77 @@ var EventBusImpl = class {
|
|
|
700
1020
|
const created = creator(payload);
|
|
701
1021
|
event = {
|
|
702
1022
|
...created,
|
|
703
|
-
metadata:
|
|
1023
|
+
metadata: enrichMetadata(
|
|
1024
|
+
created.metadata,
|
|
1025
|
+
extraMeta,
|
|
1026
|
+
this.recording.instanceId
|
|
1027
|
+
)
|
|
704
1028
|
};
|
|
705
1029
|
} else {
|
|
706
1030
|
const rawEvent = eventOrCreator;
|
|
707
1031
|
const extraMeta = metadata ?? payloadOrMeta ?? {};
|
|
708
1032
|
event = {
|
|
709
1033
|
...rawEvent,
|
|
710
|
-
metadata:
|
|
1034
|
+
metadata: enrichMetadata(
|
|
1035
|
+
rawEvent.metadata,
|
|
1036
|
+
extraMeta,
|
|
1037
|
+
this.recording.instanceId
|
|
1038
|
+
)
|
|
711
1039
|
};
|
|
712
1040
|
}
|
|
713
1041
|
if (rawPublish && this.strictRegistry) {
|
|
714
|
-
await this.
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
1042
|
+
await recordRawPublishDiagnostic(this.recording, event);
|
|
1043
|
+
}
|
|
1044
|
+
assertIntentAllowed(event.intent, {
|
|
1045
|
+
strictRegistry: this.strictRegistry,
|
|
1046
|
+
validateIntentNaming: this.validateIntentNaming,
|
|
1047
|
+
intentRegistry: this.intentRegistry
|
|
1048
|
+
});
|
|
1049
|
+
assertSourceAllowed(event, {
|
|
1050
|
+
requireKnownSource: this.requireKnownSource,
|
|
1051
|
+
intentRegistry: this.intentRegistry
|
|
1052
|
+
});
|
|
1053
|
+
assertContractAllowed(event, {
|
|
1054
|
+
eventContracts: this.eventContracts,
|
|
1055
|
+
strictEventContracts: this.strictEventContracts
|
|
1056
|
+
});
|
|
1057
|
+
event = await applyInterceptors(event, {
|
|
1058
|
+
interceptorsForIntent: (intent) => this.interceptorsByIntent.get(intent) ?? [],
|
|
1059
|
+
eventContracts: this.eventContracts,
|
|
1060
|
+
strictEventContracts: this.strictEventContracts,
|
|
1061
|
+
appendTrace: (r) => this.appendTrace(r),
|
|
1062
|
+
recordAudit: (type, e, details) => this.recordAudit(type, e, details)
|
|
1063
|
+
});
|
|
1064
|
+
assertContractAllowed(event, {
|
|
1065
|
+
eventContracts: this.eventContracts,
|
|
1066
|
+
strictEventContracts: this.strictEventContracts
|
|
1067
|
+
});
|
|
1068
|
+
await assertObservedLayerFlowAllowed(event, {
|
|
1069
|
+
mode: this.enforceObservedLayerFlowMode,
|
|
1070
|
+
architectureProfile: this.architectureProfile,
|
|
1071
|
+
appendTrace: (r) => this.appendTrace(r),
|
|
1072
|
+
recordAudit: (type, e, details) => this.recordAudit(type, e, details)
|
|
1073
|
+
});
|
|
722
1074
|
this.dependencyGraph?.registerEventFlow(event.metadata.source, event.intent);
|
|
723
1075
|
const matching = [...this.subscriptionsByIntent.get(event.intent) ?? []];
|
|
724
1076
|
if (this.policyEngine) {
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
734
|
-
intent: event.intent,
|
|
735
|
-
correlationId: event.metadata.correlationId,
|
|
736
|
-
traceId: event.metadata.traceId,
|
|
737
|
-
spanId: event.metadata.spanId,
|
|
738
|
-
details: { violations: err.violations }
|
|
739
|
-
});
|
|
740
|
-
await this.recordAudit("policy.hardViolation", event, {
|
|
741
|
-
violations: err.violations
|
|
742
|
-
});
|
|
743
|
-
}
|
|
744
|
-
throw err;
|
|
745
|
-
}
|
|
746
|
-
if (policyResult.softViolations.length > 0) {
|
|
747
|
-
this.appendTrace({
|
|
748
|
-
type: "policy.softViolation",
|
|
749
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
750
|
-
intent: event.intent,
|
|
751
|
-
correlationId: event.metadata.correlationId,
|
|
752
|
-
traceId: event.metadata.traceId,
|
|
753
|
-
spanId: event.metadata.spanId,
|
|
754
|
-
details: { violations: policyResult.softViolations }
|
|
755
|
-
});
|
|
756
|
-
await this.recordAudit("policy.softViolation", event, {
|
|
757
|
-
violations: policyResult.softViolations
|
|
758
|
-
});
|
|
759
|
-
if (this.onSoftViolation) {
|
|
760
|
-
await this.safeHook(
|
|
761
|
-
() => this.onSoftViolation(policyResult, event),
|
|
762
|
-
"onSoftViolation",
|
|
763
|
-
event
|
|
764
|
-
);
|
|
765
|
-
}
|
|
766
|
-
}
|
|
1077
|
+
await enforcePublishPolicy(event, {
|
|
1078
|
+
policyEngine: this.policyEngine,
|
|
1079
|
+
getPolicyContext: this.getPolicyContext,
|
|
1080
|
+
appendTrace: (r) => this.appendTrace(r),
|
|
1081
|
+
recordAudit: (type, e, details) => this.recordAudit(type, e, details),
|
|
1082
|
+
onSoftViolation: this.onSoftViolation,
|
|
1083
|
+
safeHook: (fn, name, e) => this.safeHook(fn, name, e)
|
|
1084
|
+
});
|
|
767
1085
|
}
|
|
768
|
-
|
|
1086
|
+
await recordSuccessfulPublish(
|
|
1087
|
+
this.recording,
|
|
769
1088
|
event,
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
await this.outbox?.enqueue(event);
|
|
775
|
-
this.appendTrace({
|
|
776
|
-
type: "event.published",
|
|
777
|
-
timestamp: record.publishedAt,
|
|
778
|
-
intent: event.intent,
|
|
779
|
-
correlationId: event.metadata.correlationId,
|
|
780
|
-
traceId: event.metadata.traceId,
|
|
781
|
-
spanId: event.metadata.spanId,
|
|
782
|
-
details: { subscribersNotified: matching.length }
|
|
783
|
-
});
|
|
784
|
-
await this.recordAudit("event.published", event, {
|
|
785
|
-
subscribersNotified: matching.length
|
|
786
|
-
});
|
|
787
|
-
const notifications = matching.map(
|
|
788
|
-
(sub) => this.invokeHandler(sub, event)
|
|
1089
|
+
matching.length
|
|
1090
|
+
);
|
|
1091
|
+
await Promise.all(
|
|
1092
|
+
matching.map((sub) => this.invokeHandler(sub, event))
|
|
789
1093
|
);
|
|
790
|
-
await Promise.all(notifications);
|
|
791
1094
|
if (this.onPublish) {
|
|
792
1095
|
await this.safeHook(
|
|
793
1096
|
() => this.onPublish(event),
|
|
@@ -798,7 +1101,11 @@ var EventBusImpl = class {
|
|
|
798
1101
|
}
|
|
799
1102
|
createPublisher(source) {
|
|
800
1103
|
const sourceName = typeof source === "string" ? source : source.name;
|
|
801
|
-
|
|
1104
|
+
assertIntentAllowed(sourceName, {
|
|
1105
|
+
strictRegistry: this.strictRegistry,
|
|
1106
|
+
validateIntentNaming: this.validateIntentNaming,
|
|
1107
|
+
intentRegistry: this.intentRegistry
|
|
1108
|
+
});
|
|
802
1109
|
return {
|
|
803
1110
|
source: sourceName,
|
|
804
1111
|
publish: async (intent, payload, metadata = {}) => {
|
|
@@ -814,7 +1121,11 @@ var EventBusImpl = class {
|
|
|
814
1121
|
}
|
|
815
1122
|
subscribe(intent, handler) {
|
|
816
1123
|
const intentName = typeof intent === "string" ? intent : intent.name;
|
|
817
|
-
|
|
1124
|
+
assertIntentAllowed(intentName, {
|
|
1125
|
+
strictRegistry: this.strictRegistry,
|
|
1126
|
+
validateIntentNaming: this.validateIntentNaming,
|
|
1127
|
+
intentRegistry: this.intentRegistry
|
|
1128
|
+
});
|
|
818
1129
|
const sub = {
|
|
819
1130
|
intentName,
|
|
820
1131
|
handler
|
|
@@ -835,7 +1146,11 @@ var EventBusImpl = class {
|
|
|
835
1146
|
}
|
|
836
1147
|
registerInterceptor(intent, interceptor, interceptorId) {
|
|
837
1148
|
const intentName = typeof intent === "string" ? intent : intent.name;
|
|
838
|
-
|
|
1149
|
+
assertIntentAllowed(intentName, {
|
|
1150
|
+
strictRegistry: this.strictRegistry,
|
|
1151
|
+
validateIntentNaming: this.validateIntentNaming,
|
|
1152
|
+
intentRegistry: this.intentRegistry
|
|
1153
|
+
});
|
|
839
1154
|
const registration = {
|
|
840
1155
|
registrationId: nextInterceptorRegistrationId(),
|
|
841
1156
|
interceptorId: interceptorId ?? intentName,
|
|
@@ -874,174 +1189,22 @@ var EventBusImpl = class {
|
|
|
874
1189
|
}));
|
|
875
1190
|
}
|
|
876
1191
|
getHistory() {
|
|
877
|
-
return [...this.history];
|
|
1192
|
+
return [...this.recording.history];
|
|
878
1193
|
}
|
|
879
1194
|
clearHistory() {
|
|
880
|
-
this.history.length = 0;
|
|
1195
|
+
this.recording.history.length = 0;
|
|
881
1196
|
}
|
|
882
1197
|
getTrace() {
|
|
883
|
-
return [...this.trace];
|
|
1198
|
+
return [...this.recording.trace];
|
|
884
1199
|
}
|
|
885
1200
|
clearTrace() {
|
|
886
|
-
this.trace.length = 0;
|
|
887
|
-
}
|
|
888
|
-
assertIntentAllowed(intentName) {
|
|
889
|
-
if (!this.strictRegistry && !this.validateIntentNaming) {
|
|
890
|
-
return;
|
|
891
|
-
}
|
|
892
|
-
if (this.validateIntentNaming) {
|
|
893
|
-
const validation = validateIntentName(intentName);
|
|
894
|
-
if (!validation.valid) {
|
|
895
|
-
throw new InvalidIntentNameError(intentName, validation.reason);
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
if (this.strictRegistry && this.intentRegistry && !this.intentRegistry.has(intentName)) {
|
|
899
|
-
throw new UnregisteredIntentError(intentName);
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
assertSourceAllowed(event) {
|
|
903
|
-
if (!this.requireKnownSource) return;
|
|
904
|
-
if (!event.metadata.source || event.metadata.source === "unknown") {
|
|
905
|
-
throw new UnknownEventSourceError(event.intent);
|
|
906
|
-
}
|
|
907
|
-
if (this.intentRegistry && !this.intentRegistry.has(event.metadata.source)) {
|
|
908
|
-
throw new UnknownEventSourceError(event.intent, event.metadata.source);
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
assertContractAllowed(event) {
|
|
912
|
-
if (!this.eventContracts) return;
|
|
913
|
-
const result = this.eventContracts.validate(event);
|
|
914
|
-
if (!result.ok && (this.strictEventContracts || result.contract)) {
|
|
915
|
-
throw new EventContractViolationError(event.intent, result.issues);
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
/**
|
|
919
|
-
* Enforce the OBSERVED producer→event flow (metadata.source → intent) against the
|
|
920
|
-
* architecture profile's layer rules. This is the runtime counterpart to the
|
|
921
|
-
* declared-model layer policy: it checks what the system actually did, resolving both
|
|
922
|
-
* layers directly from the profile. It runs BEFORE the flow is recorded via
|
|
923
|
-
* registerEventFlow, so in hard mode a rejected flow leaves no edge in the graph.
|
|
924
|
-
*/
|
|
925
|
-
async assertObservedLayerFlowAllowed(event) {
|
|
926
|
-
if (this.enforceObservedLayerFlowMode === "off" || !this.architectureProfile) {
|
|
927
|
-
return;
|
|
928
|
-
}
|
|
929
|
-
const source = event.metadata.source;
|
|
930
|
-
if (!source || source === "unknown") return;
|
|
931
|
-
const profile = this.architectureProfile;
|
|
932
|
-
const fromLayer = profile.resolveLayer(source);
|
|
933
|
-
const toLayer = profile.resolveLayer(event.intent);
|
|
934
|
-
if (!fromLayer || !toLayer) return;
|
|
935
|
-
const blocked = profile.rules.find(
|
|
936
|
-
(rule) => !rule.allowed && rule.from === fromLayer && rule.to === toLayer
|
|
937
|
-
);
|
|
938
|
-
if (!blocked) return;
|
|
939
|
-
const severity = this.enforceObservedLayerFlowMode;
|
|
940
|
-
const message = blocked.message ?? `Observed layer violation: "${source}" (${fromLayer}) must not produce "${event.intent}" (${toLayer}).`;
|
|
941
|
-
const details = {
|
|
942
|
-
source,
|
|
943
|
-
intent: event.intent,
|
|
944
|
-
fromLayer,
|
|
945
|
-
toLayer,
|
|
946
|
-
severity,
|
|
947
|
-
message,
|
|
948
|
-
rule: blocked
|
|
949
|
-
};
|
|
950
|
-
this.appendTrace({
|
|
951
|
-
type: "layer.observedViolation",
|
|
952
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
953
|
-
intent: event.intent,
|
|
954
|
-
correlationId: event.metadata.correlationId,
|
|
955
|
-
traceId: event.metadata.traceId,
|
|
956
|
-
spanId: event.metadata.spanId,
|
|
957
|
-
details
|
|
958
|
-
});
|
|
959
|
-
await this.recordAudit("layer.observedViolation", event, details);
|
|
960
|
-
if (severity === "hard") {
|
|
961
|
-
throw new ObservedLayerFlowViolationError(source, event.intent, fromLayer, toLayer, message);
|
|
962
|
-
}
|
|
1201
|
+
this.recording.trace.length = 0;
|
|
963
1202
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
intent: event.intent,
|
|
967
|
-
source: event.metadata.source,
|
|
968
|
-
suggestion: "Publish through a registered intent creator so strict registry, contracts, and agent tooling share one source of truth."
|
|
969
|
-
};
|
|
970
|
-
this.appendTrace({
|
|
971
|
-
type: "event.rawPublish",
|
|
972
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
973
|
-
intent: event.intent,
|
|
974
|
-
correlationId: event.metadata.correlationId,
|
|
975
|
-
traceId: event.metadata.traceId,
|
|
976
|
-
spanId: event.metadata.spanId,
|
|
977
|
-
details
|
|
978
|
-
});
|
|
979
|
-
await this.recordAudit("event.rawPublish", event, details);
|
|
1203
|
+
appendTrace(record) {
|
|
1204
|
+
appendTrace(this.recording, record);
|
|
980
1205
|
}
|
|
981
|
-
async
|
|
982
|
-
|
|
983
|
-
return event;
|
|
984
|
-
}
|
|
985
|
-
const matching = [...this.interceptorsByIntent.get(event.intent) ?? []];
|
|
986
|
-
let current = event;
|
|
987
|
-
for (const registration of matching) {
|
|
988
|
-
const patches = [];
|
|
989
|
-
try {
|
|
990
|
-
await Promise.resolve(
|
|
991
|
-
registration.interceptor({
|
|
992
|
-
event: current,
|
|
993
|
-
intercept: (patch) => {
|
|
994
|
-
patches.push(patch);
|
|
995
|
-
}
|
|
996
|
-
})
|
|
997
|
-
);
|
|
998
|
-
if (patches.length === 0) {
|
|
999
|
-
continue;
|
|
1000
|
-
}
|
|
1001
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1002
|
-
let candidate = {
|
|
1003
|
-
...current,
|
|
1004
|
-
metadata: {
|
|
1005
|
-
...current.metadata,
|
|
1006
|
-
interceptions: [
|
|
1007
|
-
...current.metadata.interceptions ?? [],
|
|
1008
|
-
{ interceptorId: registration.interceptorId, timestamp }
|
|
1009
|
-
]
|
|
1010
|
-
}
|
|
1011
|
-
};
|
|
1012
|
-
for (const patch of patches) {
|
|
1013
|
-
candidate = {
|
|
1014
|
-
...candidate,
|
|
1015
|
-
payload: applyPayloadPatch(candidate.payload, patch),
|
|
1016
|
-
metadata: { ...candidate.metadata }
|
|
1017
|
-
};
|
|
1018
|
-
}
|
|
1019
|
-
this.assertContractAllowed(candidate);
|
|
1020
|
-
current = candidate;
|
|
1021
|
-
registration.lastInterceptedAt = timestamp;
|
|
1022
|
-
this.appendTrace({
|
|
1023
|
-
type: "event.intercepted",
|
|
1024
|
-
timestamp,
|
|
1025
|
-
intent: current.intent,
|
|
1026
|
-
correlationId: current.metadata.correlationId,
|
|
1027
|
-
traceId: current.metadata.traceId,
|
|
1028
|
-
spanId: current.metadata.spanId,
|
|
1029
|
-
details: {
|
|
1030
|
-
registrationId: registration.registrationId,
|
|
1031
|
-
interceptorId: registration.interceptorId,
|
|
1032
|
-
patchesApplied: patches.length
|
|
1033
|
-
}
|
|
1034
|
-
});
|
|
1035
|
-
await this.recordAudit("event.intercepted", current, {
|
|
1036
|
-
registrationId: registration.registrationId,
|
|
1037
|
-
interceptorId: registration.interceptorId,
|
|
1038
|
-
patchesApplied: patches.length
|
|
1039
|
-
});
|
|
1040
|
-
} catch (err) {
|
|
1041
|
-
await this.recordInterceptorError(registration, current, err);
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
return current;
|
|
1206
|
+
async recordAudit(type, event, details) {
|
|
1207
|
+
await recordAudit(this.recording, type, event, details);
|
|
1045
1208
|
}
|
|
1046
1209
|
async invokeHandler(sub, event) {
|
|
1047
1210
|
try {
|
|
@@ -1094,90 +1257,6 @@ var EventBusImpl = class {
|
|
|
1094
1257
|
});
|
|
1095
1258
|
}
|
|
1096
1259
|
}
|
|
1097
|
-
async recordInterceptorError(interceptor, event, error) {
|
|
1098
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1099
|
-
this.appendTrace({
|
|
1100
|
-
type: "interceptor.error",
|
|
1101
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1102
|
-
intent: event.intent,
|
|
1103
|
-
correlationId: event.metadata.correlationId,
|
|
1104
|
-
traceId: event.metadata.traceId,
|
|
1105
|
-
spanId: event.metadata.spanId,
|
|
1106
|
-
details: {
|
|
1107
|
-
registrationId: interceptor.registrationId,
|
|
1108
|
-
interceptorId: interceptor.interceptorId,
|
|
1109
|
-
error: message
|
|
1110
|
-
}
|
|
1111
|
-
});
|
|
1112
|
-
await this.recordAudit("interceptor.error", event, {
|
|
1113
|
-
registrationId: interceptor.registrationId,
|
|
1114
|
-
interceptorId: interceptor.interceptorId,
|
|
1115
|
-
error: message
|
|
1116
|
-
});
|
|
1117
|
-
}
|
|
1118
|
-
appendHistory(record) {
|
|
1119
|
-
this.history.push(record);
|
|
1120
|
-
if (this.maxHistorySize !== void 0 && this.history.length > this.maxHistorySize) {
|
|
1121
|
-
this.history.splice(0, this.history.length - this.maxHistorySize);
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
appendTrace(record) {
|
|
1125
|
-
this.trace.push(record);
|
|
1126
|
-
if (this.maxHistorySize !== void 0 && this.trace.length > this.maxHistorySize) {
|
|
1127
|
-
this.trace.splice(0, this.trace.length - this.maxHistorySize);
|
|
1128
|
-
}
|
|
1129
|
-
for (const sink of this.traceSinks) {
|
|
1130
|
-
try {
|
|
1131
|
-
sink(record);
|
|
1132
|
-
} catch {
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
async recordAudit(type, event, details) {
|
|
1137
|
-
if (!this.auditTrail) return;
|
|
1138
|
-
try {
|
|
1139
|
-
await this.auditTrail.record({
|
|
1140
|
-
type,
|
|
1141
|
-
source: event.metadata.source,
|
|
1142
|
-
intent: event.intent,
|
|
1143
|
-
correlationId: event.metadata.correlationId,
|
|
1144
|
-
causationId: event.metadata.causationId,
|
|
1145
|
-
subject: event.intent,
|
|
1146
|
-
details
|
|
1147
|
-
});
|
|
1148
|
-
} catch (err) {
|
|
1149
|
-
this.appendTrace({
|
|
1150
|
-
type: "hook.error",
|
|
1151
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1152
|
-
intent: event.intent,
|
|
1153
|
-
correlationId: event.metadata.correlationId,
|
|
1154
|
-
traceId: event.metadata.traceId,
|
|
1155
|
-
spanId: event.metadata.spanId,
|
|
1156
|
-
details: {
|
|
1157
|
-
hook: "auditTrail",
|
|
1158
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1159
|
-
}
|
|
1160
|
-
});
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
enrichMetadata(base, extra) {
|
|
1164
|
-
return {
|
|
1165
|
-
...base,
|
|
1166
|
-
...extra,
|
|
1167
|
-
occurredAt: extra.occurredAt || base.occurredAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1168
|
-
source: extra.source || base.source || "unknown",
|
|
1169
|
-
kernelInstanceId: extra.kernelInstanceId ?? base.kernelInstanceId ?? this.instanceId,
|
|
1170
|
-
eventVersion: extra.eventVersion ?? base.eventVersion,
|
|
1171
|
-
schemaVersion: extra.schemaVersion ?? base.schemaVersion,
|
|
1172
|
-
allowInterception: extra.allowInterception ?? base.allowInterception,
|
|
1173
|
-
interceptions: extra.interceptions ?? base.interceptions,
|
|
1174
|
-
correlationId: extra.correlationId ?? base.correlationId,
|
|
1175
|
-
causationId: extra.causationId ?? base.causationId,
|
|
1176
|
-
traceId: extra.traceId ?? base.traceId,
|
|
1177
|
-
spanId: extra.spanId ?? base.spanId,
|
|
1178
|
-
parentSpanId: extra.parentSpanId ?? base.parentSpanId
|
|
1179
|
-
};
|
|
1180
|
-
}
|
|
1181
1260
|
};
|
|
1182
1261
|
function createEventBus(options) {
|
|
1183
1262
|
return new EventBusImpl(options);
|