chainlesschain 0.66.0 → 0.132.0
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/bin/chainlesschain.js +0 -0
- package/package.json +1 -1
- package/src/commands/a2a.js +380 -0
- package/src/commands/agent-network.js +254 -1
- package/src/commands/audit.js +302 -0
- package/src/commands/automation.js +271 -1
- package/src/commands/bi.js +348 -0
- package/src/commands/codegen.js +224 -0
- package/src/commands/collab.js +341 -0
- package/src/commands/compliance.js +1035 -0
- package/src/commands/cowork.js +221 -0
- package/src/commands/crosschain.js +218 -0
- package/src/commands/dbevo.js +284 -0
- package/src/commands/dev.js +252 -0
- package/src/commands/did.js +358 -0
- package/src/commands/dlp.js +341 -0
- package/src/commands/encrypt.js +341 -0
- package/src/commands/evomap.js +394 -0
- package/src/commands/export.js +256 -1
- package/src/commands/federation.js +283 -0
- package/src/commands/fusion.js +258 -0
- package/src/commands/governance.js +325 -0
- package/src/commands/hardening.js +411 -0
- package/src/commands/hook.js +148 -0
- package/src/commands/import.js +252 -0
- package/src/commands/incentive.js +322 -0
- package/src/commands/inference.js +318 -0
- package/src/commands/infra.js +244 -0
- package/src/commands/instinct.js +260 -0
- package/src/commands/ipfs.js +318 -0
- package/src/commands/kg.js +387 -0
- package/src/commands/llm.js +263 -0
- package/src/commands/lowcode.js +356 -0
- package/src/commands/marketplace.js +256 -0
- package/src/commands/mcp.js +221 -0
- package/src/commands/memory.js +248 -0
- package/src/commands/multimodal.js +296 -0
- package/src/commands/nlprog.js +356 -0
- package/src/commands/note.js +244 -0
- package/src/commands/ops.js +354 -0
- package/src/commands/orchestrate.js +166 -0
- package/src/commands/org.js +277 -0
- package/src/commands/p2p.js +390 -0
- package/src/commands/perception.js +290 -0
- package/src/commands/permmem.js +251 -0
- package/src/commands/plugin-ecosystem.js +273 -0
- package/src/commands/pqc.js +393 -0
- package/src/commands/privacy.js +321 -0
- package/src/commands/quantization.js +351 -0
- package/src/commands/rcache.js +271 -0
- package/src/commands/recommend.js +340 -0
- package/src/commands/reputation.js +261 -0
- package/src/commands/runtime.js +307 -0
- package/src/commands/scim.js +262 -0
- package/src/commands/session.js +258 -0
- package/src/commands/siem.js +246 -0
- package/src/commands/skill.js +267 -1
- package/src/commands/sla.js +259 -0
- package/src/commands/social.js +256 -0
- package/src/commands/sso.js +186 -1
- package/src/commands/stress.js +230 -0
- package/src/commands/sync.js +256 -0
- package/src/commands/tech.js +338 -0
- package/src/commands/tenant.js +351 -0
- package/src/commands/terraform.js +245 -0
- package/src/commands/tokens.js +269 -0
- package/src/commands/trust.js +249 -0
- package/src/commands/wallet.js +277 -0
- package/src/commands/workflow.js +171 -0
- package/src/commands/zkp.js +335 -0
- package/src/index.js +4 -0
- package/src/lib/a2a-protocol.js +451 -0
- package/src/lib/agent-coordinator.js +325 -0
- package/src/lib/agent-network.js +387 -0
- package/src/lib/agent-router.js +395 -0
- package/src/lib/aiops.js +478 -0
- package/src/lib/app-builder.js +239 -0
- package/src/lib/audit-logger.js +379 -0
- package/src/lib/automation-engine.js +330 -0
- package/src/lib/autonomous-developer.js +350 -0
- package/src/lib/bi-engine.js +338 -0
- package/src/lib/code-agent.js +323 -0
- package/src/lib/collaboration-governance.js +364 -0
- package/src/lib/community-governance.js +436 -0
- package/src/lib/compliance-manager.js +434 -0
- package/src/lib/content-recommendation.js +469 -0
- package/src/lib/cross-chain.js +345 -0
- package/src/lib/crypto-manager.js +350 -0
- package/src/lib/dbevo.js +338 -0
- package/src/lib/decentral-infra.js +340 -0
- package/src/lib/did-manager.js +367 -0
- package/src/lib/dlp-engine.js +389 -0
- package/src/lib/evomap-federation.js +177 -0
- package/src/lib/evomap-governance.js +276 -0
- package/src/lib/federation-hardening.js +259 -0
- package/src/lib/hardening-manager.js +348 -0
- package/src/lib/hook-manager.js +380 -0
- package/src/lib/inference-network.js +330 -0
- package/src/lib/instinct-manager.js +332 -0
- package/src/lib/ipfs-storage.js +334 -0
- package/src/lib/knowledge-exporter.js +381 -0
- package/src/lib/knowledge-graph.js +432 -0
- package/src/lib/knowledge-importer.js +379 -0
- package/src/lib/llm-providers.js +391 -0
- package/src/lib/mcp-registry.js +333 -0
- package/src/lib/memory-manager.js +330 -0
- package/src/lib/multimodal.js +346 -0
- package/src/lib/nl-programming.js +343 -0
- package/src/lib/note-versioning.js +327 -0
- package/src/lib/org-manager.js +323 -0
- package/src/lib/p2p-manager.js +387 -0
- package/src/lib/perception.js +346 -0
- package/src/lib/perf-tuning.js +4 -1
- package/src/lib/permanent-memory.js +320 -0
- package/src/lib/plugin-ecosystem.js +377 -0
- package/src/lib/pqc-manager.js +368 -0
- package/src/lib/privacy-computing.js +427 -0
- package/src/lib/protocol-fusion.js +417 -0
- package/src/lib/quantization.js +325 -0
- package/src/lib/reputation-optimizer.js +299 -0
- package/src/lib/response-cache.js +327 -0
- package/src/lib/scim-manager.js +329 -0
- package/src/lib/session-manager.js +329 -0
- package/src/lib/siem-exporter.js +333 -0
- package/src/lib/skill-loader.js +377 -0
- package/src/lib/skill-marketplace.js +325 -0
- package/src/lib/sla-manager.js +275 -0
- package/src/lib/social-manager.js +326 -0
- package/src/lib/sso-manager.js +332 -0
- package/src/lib/stress-tester.js +330 -0
- package/src/lib/sync-manager.js +326 -0
- package/src/lib/tech-learning-engine.js +369 -0
- package/src/lib/tenant-saas.js +460 -0
- package/src/lib/terraform-manager.js +363 -0
- package/src/lib/threat-intel.js +335 -0
- package/src/lib/token-incentive.js +293 -0
- package/src/lib/token-tracker.js +329 -0
- package/src/lib/trust-security.js +390 -0
- package/src/lib/ueba.js +389 -0
- package/src/lib/universal-runtime.js +325 -0
- package/src/lib/wallet-manager.js +326 -0
- package/src/lib/workflow-engine.js +322 -0
- package/src/lib/zkp-engine.js +274 -0
|
@@ -598,3 +598,472 @@ export function _resetState() {
|
|
|
598
598
|
_userRecs.clear();
|
|
599
599
|
_seq = 0;
|
|
600
600
|
}
|
|
601
|
+
|
|
602
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
603
|
+
* Phase 48 V2 — Profile Maturity + Feed Channel Lifecycle
|
|
604
|
+
* Strictly additive. Legacy surface above is preserved.
|
|
605
|
+
* ═════════════════════════════════════════════════════════════ */
|
|
606
|
+
|
|
607
|
+
export const PROFILE_MATURITY_V2 = Object.freeze({
|
|
608
|
+
ONBOARDING: "onboarding",
|
|
609
|
+
ACTIVE: "active",
|
|
610
|
+
DORMANT: "dormant",
|
|
611
|
+
RETIRED: "retired",
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
export const FEED_LIFECYCLE_V2 = Object.freeze({
|
|
615
|
+
DRAFT: "draft",
|
|
616
|
+
ACTIVE: "active",
|
|
617
|
+
PAUSED: "paused",
|
|
618
|
+
ARCHIVED: "archived",
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
const PROFILE_TRANSITIONS_V2 = new Map([
|
|
622
|
+
["onboarding", new Set(["active", "retired"])],
|
|
623
|
+
["active", new Set(["dormant", "retired"])],
|
|
624
|
+
["dormant", new Set(["active", "retired"])],
|
|
625
|
+
]);
|
|
626
|
+
const PROFILE_TERMINALS_V2 = new Set(["retired"]);
|
|
627
|
+
|
|
628
|
+
const FEED_TRANSITIONS_V2 = new Map([
|
|
629
|
+
["draft", new Set(["active", "archived"])],
|
|
630
|
+
["active", new Set(["paused", "archived"])],
|
|
631
|
+
["paused", new Set(["active", "archived"])],
|
|
632
|
+
]);
|
|
633
|
+
const FEED_TERMINALS_V2 = new Set(["archived"]);
|
|
634
|
+
|
|
635
|
+
export const REC_DEFAULT_MAX_ACTIVE_PROFILES_PER_SEGMENT = 10000;
|
|
636
|
+
export const REC_DEFAULT_MAX_ACTIVE_FEEDS_PER_CURATOR = 20;
|
|
637
|
+
export const REC_DEFAULT_PROFILE_IDLE_MS = 90 * 86400000; // 90 days
|
|
638
|
+
export const REC_DEFAULT_FEED_STALE_MS = 30 * 86400000; // 30 days
|
|
639
|
+
|
|
640
|
+
let _maxActiveProfilesPerSegmentV2 =
|
|
641
|
+
REC_DEFAULT_MAX_ACTIVE_PROFILES_PER_SEGMENT;
|
|
642
|
+
let _maxActiveFeedsPerCuratorV2 = REC_DEFAULT_MAX_ACTIVE_FEEDS_PER_CURATOR;
|
|
643
|
+
let _profileIdleMsV2 = REC_DEFAULT_PROFILE_IDLE_MS;
|
|
644
|
+
let _feedStaleMsV2 = REC_DEFAULT_FEED_STALE_MS;
|
|
645
|
+
|
|
646
|
+
const _profileStatesV2 = new Map(); // profileId → V2 record
|
|
647
|
+
const _feedStatesV2 = new Map(); // feedId → V2 record
|
|
648
|
+
|
|
649
|
+
function _positiveIntV2(n, label) {
|
|
650
|
+
const num = Number(n);
|
|
651
|
+
if (!Number.isFinite(num) || num <= 0) {
|
|
652
|
+
throw new Error(`${label} must be a positive integer`);
|
|
653
|
+
}
|
|
654
|
+
return Math.floor(num);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function _validProfileStatusV2(s) {
|
|
658
|
+
return (
|
|
659
|
+
s === "onboarding" || s === "active" || s === "dormant" || s === "retired"
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
function _validFeedStatusV2(s) {
|
|
664
|
+
return s === "draft" || s === "active" || s === "paused" || s === "archived";
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
export function getDefaultMaxActiveProfilesPerSegmentV2() {
|
|
668
|
+
return REC_DEFAULT_MAX_ACTIVE_PROFILES_PER_SEGMENT;
|
|
669
|
+
}
|
|
670
|
+
export function getMaxActiveProfilesPerSegmentV2() {
|
|
671
|
+
return _maxActiveProfilesPerSegmentV2;
|
|
672
|
+
}
|
|
673
|
+
export function setMaxActiveProfilesPerSegmentV2(n) {
|
|
674
|
+
_maxActiveProfilesPerSegmentV2 = _positiveIntV2(
|
|
675
|
+
n,
|
|
676
|
+
"maxActiveProfilesPerSegment",
|
|
677
|
+
);
|
|
678
|
+
return _maxActiveProfilesPerSegmentV2;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
export function getDefaultMaxActiveFeedsPerCuratorV2() {
|
|
682
|
+
return REC_DEFAULT_MAX_ACTIVE_FEEDS_PER_CURATOR;
|
|
683
|
+
}
|
|
684
|
+
export function getMaxActiveFeedsPerCuratorV2() {
|
|
685
|
+
return _maxActiveFeedsPerCuratorV2;
|
|
686
|
+
}
|
|
687
|
+
export function setMaxActiveFeedsPerCuratorV2(n) {
|
|
688
|
+
_maxActiveFeedsPerCuratorV2 = _positiveIntV2(n, "maxActiveFeedsPerCurator");
|
|
689
|
+
return _maxActiveFeedsPerCuratorV2;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
export function getDefaultProfileIdleMsV2() {
|
|
693
|
+
return REC_DEFAULT_PROFILE_IDLE_MS;
|
|
694
|
+
}
|
|
695
|
+
export function getProfileIdleMsV2() {
|
|
696
|
+
return _profileIdleMsV2;
|
|
697
|
+
}
|
|
698
|
+
export function setProfileIdleMsV2(ms) {
|
|
699
|
+
_profileIdleMsV2 = _positiveIntV2(ms, "profileIdleMs");
|
|
700
|
+
return _profileIdleMsV2;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
export function getDefaultFeedStaleMsV2() {
|
|
704
|
+
return REC_DEFAULT_FEED_STALE_MS;
|
|
705
|
+
}
|
|
706
|
+
export function getFeedStaleMsV2() {
|
|
707
|
+
return _feedStaleMsV2;
|
|
708
|
+
}
|
|
709
|
+
export function setFeedStaleMsV2(ms) {
|
|
710
|
+
_feedStaleMsV2 = _positiveIntV2(ms, "feedStaleMs");
|
|
711
|
+
return _feedStaleMsV2;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/* ── Profile V2 ─────────────────────────────────────────────── */
|
|
715
|
+
|
|
716
|
+
export function registerProfileV2(db, config = {}) {
|
|
717
|
+
void db;
|
|
718
|
+
const profileId = String(config.profileId || "").trim();
|
|
719
|
+
if (!profileId) throw new Error("profileId is required");
|
|
720
|
+
const segment = String(config.segment || "").trim();
|
|
721
|
+
if (!segment) throw new Error("segment is required");
|
|
722
|
+
if (_profileStatesV2.has(profileId)) {
|
|
723
|
+
throw new Error(`Profile already registered in V2: ${profileId}`);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
const now = Number(config.now ?? Date.now());
|
|
727
|
+
const initialStatus = config.initialStatus || "onboarding";
|
|
728
|
+
if (!_validProfileStatusV2(initialStatus)) {
|
|
729
|
+
throw new Error(`Invalid initial status: ${initialStatus}`);
|
|
730
|
+
}
|
|
731
|
+
if (PROFILE_TERMINALS_V2.has(initialStatus)) {
|
|
732
|
+
throw new Error(
|
|
733
|
+
`Cannot register profile in terminal status '${initialStatus}'`,
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
if (initialStatus === "active") {
|
|
738
|
+
let activeCount = 0;
|
|
739
|
+
for (const rec of _profileStatesV2.values()) {
|
|
740
|
+
if (rec.segment === segment && rec.status === "active") {
|
|
741
|
+
activeCount += 1;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
if (activeCount >= _maxActiveProfilesPerSegmentV2) {
|
|
745
|
+
throw new Error(
|
|
746
|
+
`Max active profiles per segment reached (${_maxActiveProfilesPerSegmentV2})`,
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
const record = {
|
|
752
|
+
profileId,
|
|
753
|
+
segment,
|
|
754
|
+
ownerId: config.ownerId ? String(config.ownerId) : null,
|
|
755
|
+
status: initialStatus,
|
|
756
|
+
metadata: config.metadata ? { ...config.metadata } : {},
|
|
757
|
+
createdAt: now,
|
|
758
|
+
updatedAt: now,
|
|
759
|
+
lastActivityAt: now,
|
|
760
|
+
reason: null,
|
|
761
|
+
};
|
|
762
|
+
_profileStatesV2.set(profileId, record);
|
|
763
|
+
return { ...record, metadata: { ...record.metadata } };
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
export function getProfileV2(profileId) {
|
|
767
|
+
const rec = _profileStatesV2.get(String(profileId || ""));
|
|
768
|
+
if (!rec) return null;
|
|
769
|
+
return { ...rec, metadata: { ...rec.metadata } };
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
export function setProfileMaturityV2(db, profileId, newStatus, patch = {}) {
|
|
773
|
+
void db;
|
|
774
|
+
const id = String(profileId || "");
|
|
775
|
+
const record = _profileStatesV2.get(id);
|
|
776
|
+
if (!record) throw new Error(`Profile not registered in V2: ${id}`);
|
|
777
|
+
if (!_validProfileStatusV2(newStatus)) {
|
|
778
|
+
throw new Error(`Invalid profile status: ${newStatus}`);
|
|
779
|
+
}
|
|
780
|
+
if (PROFILE_TERMINALS_V2.has(record.status)) {
|
|
781
|
+
throw new Error(
|
|
782
|
+
`Profile is in terminal status '${record.status}' and cannot transition`,
|
|
783
|
+
);
|
|
784
|
+
}
|
|
785
|
+
const allowed = PROFILE_TRANSITIONS_V2.get(record.status);
|
|
786
|
+
if (!allowed || !allowed.has(newStatus)) {
|
|
787
|
+
throw new Error(`Invalid transition: ${record.status} → ${newStatus}`);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
if (newStatus === "active" && record.status !== "active") {
|
|
791
|
+
let activeCount = 0;
|
|
792
|
+
for (const rec of _profileStatesV2.values()) {
|
|
793
|
+
if (rec.segment === record.segment && rec.status === "active") {
|
|
794
|
+
activeCount += 1;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
if (activeCount >= _maxActiveProfilesPerSegmentV2) {
|
|
798
|
+
throw new Error(
|
|
799
|
+
`Max active profiles per segment reached (${_maxActiveProfilesPerSegmentV2})`,
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
record.status = newStatus;
|
|
805
|
+
record.updatedAt = Number(patch.now ?? Date.now());
|
|
806
|
+
if (patch.reason !== undefined) record.reason = patch.reason;
|
|
807
|
+
if (patch.metadata && typeof patch.metadata === "object") {
|
|
808
|
+
record.metadata = { ...record.metadata, ...patch.metadata };
|
|
809
|
+
}
|
|
810
|
+
return { ...record, metadata: { ...record.metadata } };
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
export function activateProfile(db, profileId, reason) {
|
|
814
|
+
return setProfileMaturityV2(db, profileId, "active", { reason });
|
|
815
|
+
}
|
|
816
|
+
export function dormantProfile(db, profileId, reason) {
|
|
817
|
+
return setProfileMaturityV2(db, profileId, "dormant", { reason });
|
|
818
|
+
}
|
|
819
|
+
export function retireProfile(db, profileId, reason) {
|
|
820
|
+
return setProfileMaturityV2(db, profileId, "retired", { reason });
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
export function touchProfileActivity(profileId) {
|
|
824
|
+
const rec = _profileStatesV2.get(String(profileId || ""));
|
|
825
|
+
if (!rec) throw new Error(`Profile not registered in V2: ${profileId}`);
|
|
826
|
+
rec.lastActivityAt = Date.now();
|
|
827
|
+
return { ...rec, metadata: { ...rec.metadata } };
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/* ── Feed V2 ────────────────────────────────────────────────── */
|
|
831
|
+
|
|
832
|
+
export function registerFeedV2(db, config = {}) {
|
|
833
|
+
void db;
|
|
834
|
+
const feedId = String(config.feedId || "").trim();
|
|
835
|
+
if (!feedId) throw new Error("feedId is required");
|
|
836
|
+
const curatorId = String(config.curatorId || "").trim();
|
|
837
|
+
if (!curatorId) throw new Error("curatorId is required");
|
|
838
|
+
const name = String(config.name || "").trim();
|
|
839
|
+
if (!name) throw new Error("name is required");
|
|
840
|
+
if (_feedStatesV2.has(feedId)) {
|
|
841
|
+
throw new Error(`Feed already registered in V2: ${feedId}`);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
const now = Number(config.now ?? Date.now());
|
|
845
|
+
const initialStatus = config.initialStatus || "draft";
|
|
846
|
+
if (!_validFeedStatusV2(initialStatus)) {
|
|
847
|
+
throw new Error(`Invalid initial status: ${initialStatus}`);
|
|
848
|
+
}
|
|
849
|
+
if (FEED_TERMINALS_V2.has(initialStatus)) {
|
|
850
|
+
throw new Error(
|
|
851
|
+
`Cannot register feed in terminal status '${initialStatus}'`,
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
if (initialStatus === "active") {
|
|
856
|
+
let activeCount = 0;
|
|
857
|
+
for (const rec of _feedStatesV2.values()) {
|
|
858
|
+
if (rec.curatorId === curatorId && rec.status === "active") {
|
|
859
|
+
activeCount += 1;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
if (activeCount >= _maxActiveFeedsPerCuratorV2) {
|
|
863
|
+
throw new Error(
|
|
864
|
+
`Max active feeds per curator reached (${_maxActiveFeedsPerCuratorV2})`,
|
|
865
|
+
);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
const record = {
|
|
870
|
+
feedId,
|
|
871
|
+
curatorId,
|
|
872
|
+
name,
|
|
873
|
+
topics: Array.isArray(config.topics) ? [...config.topics] : [],
|
|
874
|
+
status: initialStatus,
|
|
875
|
+
metadata: config.metadata ? { ...config.metadata } : {},
|
|
876
|
+
createdAt: now,
|
|
877
|
+
updatedAt: now,
|
|
878
|
+
lastPublishedAt: now,
|
|
879
|
+
reason: null,
|
|
880
|
+
};
|
|
881
|
+
_feedStatesV2.set(feedId, record);
|
|
882
|
+
return {
|
|
883
|
+
...record,
|
|
884
|
+
topics: [...record.topics],
|
|
885
|
+
metadata: { ...record.metadata },
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
export function getFeedV2(feedId) {
|
|
890
|
+
const rec = _feedStatesV2.get(String(feedId || ""));
|
|
891
|
+
if (!rec) return null;
|
|
892
|
+
return {
|
|
893
|
+
...rec,
|
|
894
|
+
topics: [...rec.topics],
|
|
895
|
+
metadata: { ...rec.metadata },
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
export function setFeedStatusV2(db, feedId, newStatus, patch = {}) {
|
|
900
|
+
void db;
|
|
901
|
+
const id = String(feedId || "");
|
|
902
|
+
const record = _feedStatesV2.get(id);
|
|
903
|
+
if (!record) throw new Error(`Feed not registered in V2: ${id}`);
|
|
904
|
+
if (!_validFeedStatusV2(newStatus)) {
|
|
905
|
+
throw new Error(`Invalid feed status: ${newStatus}`);
|
|
906
|
+
}
|
|
907
|
+
if (FEED_TERMINALS_V2.has(record.status)) {
|
|
908
|
+
throw new Error(
|
|
909
|
+
`Feed is in terminal status '${record.status}' and cannot transition`,
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
const allowed = FEED_TRANSITIONS_V2.get(record.status);
|
|
913
|
+
if (!allowed || !allowed.has(newStatus)) {
|
|
914
|
+
throw new Error(`Invalid transition: ${record.status} → ${newStatus}`);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
if (newStatus === "active" && record.status !== "active") {
|
|
918
|
+
let activeCount = 0;
|
|
919
|
+
for (const rec of _feedStatesV2.values()) {
|
|
920
|
+
if (rec.curatorId === record.curatorId && rec.status === "active") {
|
|
921
|
+
activeCount += 1;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
if (activeCount >= _maxActiveFeedsPerCuratorV2) {
|
|
925
|
+
throw new Error(
|
|
926
|
+
`Max active feeds per curator reached (${_maxActiveFeedsPerCuratorV2})`,
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
record.status = newStatus;
|
|
932
|
+
record.updatedAt = Number(patch.now ?? Date.now());
|
|
933
|
+
if (patch.reason !== undefined) record.reason = patch.reason;
|
|
934
|
+
if (patch.metadata && typeof patch.metadata === "object") {
|
|
935
|
+
record.metadata = { ...record.metadata, ...patch.metadata };
|
|
936
|
+
}
|
|
937
|
+
return {
|
|
938
|
+
...record,
|
|
939
|
+
topics: [...record.topics],
|
|
940
|
+
metadata: { ...record.metadata },
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
export function activateFeed(db, feedId, reason) {
|
|
945
|
+
return setFeedStatusV2(db, feedId, "active", { reason });
|
|
946
|
+
}
|
|
947
|
+
export function pauseFeed(db, feedId, reason) {
|
|
948
|
+
return setFeedStatusV2(db, feedId, "paused", { reason });
|
|
949
|
+
}
|
|
950
|
+
export function archiveFeed(db, feedId, reason) {
|
|
951
|
+
return setFeedStatusV2(db, feedId, "archived", { reason });
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
export function touchFeedPublish(feedId) {
|
|
955
|
+
const rec = _feedStatesV2.get(String(feedId || ""));
|
|
956
|
+
if (!rec) throw new Error(`Feed not registered in V2: ${feedId}`);
|
|
957
|
+
rec.lastPublishedAt = Date.now();
|
|
958
|
+
return {
|
|
959
|
+
...rec,
|
|
960
|
+
topics: [...rec.topics],
|
|
961
|
+
metadata: { ...rec.metadata },
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/* ── Counts ─────────────────────────────────────────────────── */
|
|
966
|
+
|
|
967
|
+
export function getActiveProfileCount(segment) {
|
|
968
|
+
let n = 0;
|
|
969
|
+
for (const rec of _profileStatesV2.values()) {
|
|
970
|
+
if (rec.status !== "active") continue;
|
|
971
|
+
if (segment !== undefined && rec.segment !== String(segment)) continue;
|
|
972
|
+
n += 1;
|
|
973
|
+
}
|
|
974
|
+
return n;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
export function getActiveFeedCount(curatorId) {
|
|
978
|
+
let n = 0;
|
|
979
|
+
for (const rec of _feedStatesV2.values()) {
|
|
980
|
+
if (rec.status !== "active") continue;
|
|
981
|
+
if (curatorId !== undefined && rec.curatorId !== String(curatorId)) {
|
|
982
|
+
continue;
|
|
983
|
+
}
|
|
984
|
+
n += 1;
|
|
985
|
+
}
|
|
986
|
+
return n;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/* ── Auto-flip Bulk Ops ─────────────────────────────────────── */
|
|
990
|
+
|
|
991
|
+
export function autoDormantIdleProfiles(db, nowMs) {
|
|
992
|
+
void db;
|
|
993
|
+
const now = Number(nowMs ?? Date.now());
|
|
994
|
+
const flipped = [];
|
|
995
|
+
for (const rec of _profileStatesV2.values()) {
|
|
996
|
+
if (rec.status !== "active") continue;
|
|
997
|
+
if (now - rec.lastActivityAt > _profileIdleMsV2) {
|
|
998
|
+
rec.status = "dormant";
|
|
999
|
+
rec.updatedAt = now;
|
|
1000
|
+
rec.reason = "idle";
|
|
1001
|
+
flipped.push(rec.profileId);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
return flipped;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
export function autoArchiveStaleFeeds(db, nowMs) {
|
|
1008
|
+
void db;
|
|
1009
|
+
const now = Number(nowMs ?? Date.now());
|
|
1010
|
+
const flipped = [];
|
|
1011
|
+
for (const rec of _feedStatesV2.values()) {
|
|
1012
|
+
if (rec.status !== "active" && rec.status !== "paused") continue;
|
|
1013
|
+
if (now - rec.lastPublishedAt > _feedStaleMsV2) {
|
|
1014
|
+
rec.status = "archived";
|
|
1015
|
+
rec.updatedAt = now;
|
|
1016
|
+
rec.reason = "stale";
|
|
1017
|
+
flipped.push(rec.feedId);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
return flipped;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
/* ── Stats V2 ───────────────────────────────────────────────── */
|
|
1024
|
+
|
|
1025
|
+
export function getRecommendationStatsV2() {
|
|
1026
|
+
const profilesByStatus = {
|
|
1027
|
+
onboarding: 0,
|
|
1028
|
+
active: 0,
|
|
1029
|
+
dormant: 0,
|
|
1030
|
+
retired: 0,
|
|
1031
|
+
};
|
|
1032
|
+
const feedsByStatus = {
|
|
1033
|
+
draft: 0,
|
|
1034
|
+
active: 0,
|
|
1035
|
+
paused: 0,
|
|
1036
|
+
archived: 0,
|
|
1037
|
+
};
|
|
1038
|
+
for (const rec of _profileStatesV2.values()) {
|
|
1039
|
+
if (profilesByStatus[rec.status] !== undefined) {
|
|
1040
|
+
profilesByStatus[rec.status] += 1;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
for (const rec of _feedStatesV2.values()) {
|
|
1044
|
+
if (feedsByStatus[rec.status] !== undefined) {
|
|
1045
|
+
feedsByStatus[rec.status] += 1;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
return {
|
|
1049
|
+
totalProfilesV2: _profileStatesV2.size,
|
|
1050
|
+
totalFeedsV2: _feedStatesV2.size,
|
|
1051
|
+
maxActiveProfilesPerSegment: _maxActiveProfilesPerSegmentV2,
|
|
1052
|
+
maxActiveFeedsPerCurator: _maxActiveFeedsPerCuratorV2,
|
|
1053
|
+
profileIdleMs: _profileIdleMsV2,
|
|
1054
|
+
feedStaleMs: _feedStaleMsV2,
|
|
1055
|
+
profilesByStatus,
|
|
1056
|
+
feedsByStatus,
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
/* ── Reset V2 (tests) ───────────────────────────────────────── */
|
|
1061
|
+
|
|
1062
|
+
export function _resetStateV2() {
|
|
1063
|
+
_profileStatesV2.clear();
|
|
1064
|
+
_feedStatesV2.clear();
|
|
1065
|
+
_maxActiveProfilesPerSegmentV2 = REC_DEFAULT_MAX_ACTIVE_PROFILES_PER_SEGMENT;
|
|
1066
|
+
_maxActiveFeedsPerCuratorV2 = REC_DEFAULT_MAX_ACTIVE_FEEDS_PER_CURATOR;
|
|
1067
|
+
_profileIdleMsV2 = REC_DEFAULT_PROFILE_IDLE_MS;
|
|
1068
|
+
_feedStaleMsV2 = REC_DEFAULT_FEED_STALE_MS;
|
|
1069
|
+
}
|