chainlesschain 0.81.0 → 0.143.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.
Files changed (209) hide show
  1. package/bin/chainlesschain.js +0 -0
  2. package/package.json +1 -1
  3. package/src/commands/a2a.js +62 -0
  4. package/src/commands/activitypub.js +61 -0
  5. package/src/commands/agent-network.js +254 -1
  6. package/src/commands/agent.js +117 -0
  7. package/src/commands/audit.js +302 -0
  8. package/src/commands/automation.js +271 -1
  9. package/src/commands/bi.js +61 -0
  10. package/src/commands/bm25.js +78 -0
  11. package/src/commands/browse.js +64 -0
  12. package/src/commands/ccron.js +78 -0
  13. package/src/commands/codegen.js +224 -0
  14. package/src/commands/collab.js +341 -0
  15. package/src/commands/compliance.js +1075 -0
  16. package/src/commands/compt.js +78 -0
  17. package/src/commands/consol.js +231 -0
  18. package/src/commands/cowork.js +263 -0
  19. package/src/commands/crosschain.js +62 -0
  20. package/src/commands/dao.js +62 -0
  21. package/src/commands/dbevo.js +284 -0
  22. package/src/commands/dev.js +252 -0
  23. package/src/commands/did.js +358 -0
  24. package/src/commands/dlp.js +61 -0
  25. package/src/commands/economy.js +56 -0
  26. package/src/commands/encrypt.js +341 -0
  27. package/src/commands/evolution.js +56 -0
  28. package/src/commands/evomap.js +61 -0
  29. package/src/commands/export.js +256 -1
  30. package/src/commands/fflag.js +178 -0
  31. package/src/commands/fusion.js +258 -0
  32. package/src/commands/git.js +45 -0
  33. package/src/commands/governance.js +325 -0
  34. package/src/commands/hardening.js +411 -0
  35. package/src/commands/hmemory.js +56 -0
  36. package/src/commands/hook.js +148 -0
  37. package/src/commands/import.js +252 -0
  38. package/src/commands/incentive.js +322 -0
  39. package/src/commands/inference.js +42 -0
  40. package/src/commands/infra.js +244 -0
  41. package/src/commands/instinct.js +260 -0
  42. package/src/commands/ipfs.js +318 -0
  43. package/src/commands/itbudget.js +45 -0
  44. package/src/commands/kg.js +387 -0
  45. package/src/commands/llm.js +263 -0
  46. package/src/commands/lowcode.js +44 -0
  47. package/src/commands/matrix.js +62 -0
  48. package/src/commands/mcp.js +221 -0
  49. package/src/commands/mcpscaf.js +41 -0
  50. package/src/commands/meminj.js +41 -0
  51. package/src/commands/memory.js +248 -0
  52. package/src/commands/multimodal.js +296 -0
  53. package/src/commands/nlprog.js +356 -0
  54. package/src/commands/nostr.js +62 -0
  55. package/src/commands/note.js +244 -0
  56. package/src/commands/ops.js +354 -0
  57. package/src/commands/orchestrate.js +166 -0
  58. package/src/commands/orchgov.js +45 -0
  59. package/src/commands/org.js +277 -0
  60. package/src/commands/p2p.js +390 -0
  61. package/src/commands/pdfp.js +78 -0
  62. package/src/commands/perception.js +290 -0
  63. package/src/commands/perf.js +39 -0
  64. package/src/commands/perm.js +45 -0
  65. package/src/commands/permmem.js +251 -0
  66. package/src/commands/pipeline.js +57 -1
  67. package/src/commands/planmode.js +45 -0
  68. package/src/commands/plugin-ecosystem.js +273 -0
  69. package/src/commands/pqc.js +393 -0
  70. package/src/commands/promcomp.js +82 -0
  71. package/src/commands/quantization.js +351 -0
  72. package/src/commands/rcache.js +271 -0
  73. package/src/commands/recommend.js +382 -0
  74. package/src/commands/runtime.js +307 -0
  75. package/src/commands/scim.js +262 -0
  76. package/src/commands/seshhook.js +41 -0
  77. package/src/commands/seshsearch.js +41 -0
  78. package/src/commands/seshtail.js +41 -0
  79. package/src/commands/seshu.js +41 -0
  80. package/src/commands/session.js +258 -0
  81. package/src/commands/sganal.js +78 -0
  82. package/src/commands/siem.js +40 -0
  83. package/src/commands/skill.js +267 -1
  84. package/src/commands/slotfill.js +41 -0
  85. package/src/commands/social.js +290 -0
  86. package/src/commands/sso.js +186 -1
  87. package/src/commands/svccont.js +45 -0
  88. package/src/commands/sync.js +256 -0
  89. package/src/commands/tech.js +338 -0
  90. package/src/commands/tenant.js +351 -0
  91. package/src/commands/tms.js +45 -0
  92. package/src/commands/tokens.js +269 -0
  93. package/src/commands/topiccls.js +45 -0
  94. package/src/commands/trust.js +249 -0
  95. package/src/commands/uprof.js +45 -0
  96. package/src/commands/vcheck.js +78 -0
  97. package/src/commands/wallet.js +277 -0
  98. package/src/commands/webfetch.js +41 -0
  99. package/src/commands/workflow.js +171 -0
  100. package/src/commands/zkp.js +62 -0
  101. package/src/harness/prompt-compressor.js +331 -0
  102. package/src/index.js +65 -1
  103. package/src/lib/a2a-protocol.js +105 -0
  104. package/src/lib/activitypub-bridge.js +105 -0
  105. package/src/lib/agent-coordinator.js +325 -0
  106. package/src/lib/agent-economy.js +105 -0
  107. package/src/lib/agent-network.js +387 -0
  108. package/src/lib/agent-router.js +395 -0
  109. package/src/lib/aiops.js +478 -0
  110. package/src/lib/app-builder.js +105 -0
  111. package/src/lib/audit-logger.js +379 -0
  112. package/src/lib/automation-engine.js +330 -0
  113. package/src/lib/autonomous-agent.js +105 -0
  114. package/src/lib/autonomous-developer.js +350 -0
  115. package/src/lib/bi-engine.js +105 -0
  116. package/src/lib/bm25-search.js +81 -0
  117. package/src/lib/browser-automation.js +105 -0
  118. package/src/lib/code-agent.js +323 -0
  119. package/src/lib/collaboration-governance.js +364 -0
  120. package/src/lib/community-governance.js +436 -0
  121. package/src/lib/compliance-framework-reporter.js +105 -0
  122. package/src/lib/compliance-manager.js +434 -0
  123. package/src/lib/compression-telemetry.js +81 -0
  124. package/src/lib/content-recommendation.js +469 -0
  125. package/src/lib/content-recommender.js +105 -0
  126. package/src/lib/cowork-cron.js +81 -0
  127. package/src/lib/cowork-task-runner.js +105 -0
  128. package/src/lib/cross-chain.js +105 -0
  129. package/src/lib/crypto-manager.js +350 -0
  130. package/src/lib/dao-governance.js +105 -0
  131. package/src/lib/dbevo.js +338 -0
  132. package/src/lib/decentral-infra.js +340 -0
  133. package/src/lib/did-manager.js +367 -0
  134. package/src/lib/dlp-engine.js +105 -0
  135. package/src/lib/evolution-system.js +105 -0
  136. package/src/lib/evomap-manager.js +105 -0
  137. package/src/lib/execution-backend.js +105 -0
  138. package/src/lib/feature-flags.js +85 -0
  139. package/src/lib/git-integration.js +105 -0
  140. package/src/lib/hardening-manager.js +348 -0
  141. package/src/lib/hierarchical-memory.js +105 -0
  142. package/src/lib/hook-manager.js +380 -0
  143. package/src/lib/inference-network.js +105 -0
  144. package/src/lib/instinct-manager.js +332 -0
  145. package/src/lib/ipfs-storage.js +334 -0
  146. package/src/lib/iteration-budget.js +105 -0
  147. package/src/lib/knowledge-exporter.js +381 -0
  148. package/src/lib/knowledge-graph.js +432 -0
  149. package/src/lib/knowledge-importer.js +379 -0
  150. package/src/lib/llm-providers.js +391 -0
  151. package/src/lib/matrix-bridge.js +105 -0
  152. package/src/lib/mcp-registry.js +333 -0
  153. package/src/lib/mcp-scaffold.js +81 -0
  154. package/src/lib/memory-injection.js +81 -0
  155. package/src/lib/memory-manager.js +330 -0
  156. package/src/lib/multimodal.js +346 -0
  157. package/src/lib/nl-programming.js +343 -0
  158. package/src/lib/nostr-bridge.js +105 -0
  159. package/src/lib/note-versioning.js +327 -0
  160. package/src/lib/orchestrator.js +105 -0
  161. package/src/lib/org-manager.js +323 -0
  162. package/src/lib/p2p-manager.js +387 -0
  163. package/src/lib/pdf-parser.js +81 -0
  164. package/src/lib/perception.js +346 -0
  165. package/src/lib/perf-tuning.js +109 -1
  166. package/src/lib/permanent-memory.js +320 -0
  167. package/src/lib/permission-engine.js +81 -0
  168. package/src/lib/pipeline-orchestrator.js +105 -0
  169. package/src/lib/plan-mode.js +81 -0
  170. package/src/lib/plugin-ecosystem.js +377 -0
  171. package/src/lib/pqc-manager.js +368 -0
  172. package/src/lib/prompt-compressor.js +1 -10
  173. package/src/lib/protocol-fusion.js +417 -0
  174. package/src/lib/quantization.js +325 -0
  175. package/src/lib/response-cache.js +327 -0
  176. package/src/lib/scim-manager.js +329 -0
  177. package/src/lib/service-container.js +81 -0
  178. package/src/lib/session-consolidator.js +105 -0
  179. package/src/lib/session-hooks.js +81 -0
  180. package/src/lib/session-manager.js +329 -0
  181. package/src/lib/session-search.js +81 -0
  182. package/src/lib/session-tail.js +81 -0
  183. package/src/lib/session-usage.js +83 -0
  184. package/src/lib/siem-exporter.js +105 -0
  185. package/src/lib/skill-loader.js +377 -0
  186. package/src/lib/slot-filler.js +81 -0
  187. package/src/lib/social-graph-analytics.js +81 -0
  188. package/src/lib/social-graph.js +81 -0
  189. package/src/lib/social-manager.js +326 -0
  190. package/src/lib/sso-manager.js +332 -0
  191. package/src/lib/sub-agent-registry.js +110 -0
  192. package/src/lib/sync-manager.js +326 -0
  193. package/src/lib/task-model-selector.js +81 -0
  194. package/src/lib/tech-learning-engine.js +369 -0
  195. package/src/lib/tenant-saas.js +460 -0
  196. package/src/lib/threat-intel.js +335 -0
  197. package/src/lib/todo-manager.js +105 -0
  198. package/src/lib/token-incentive.js +293 -0
  199. package/src/lib/token-tracker.js +329 -0
  200. package/src/lib/topic-classifier.js +105 -0
  201. package/src/lib/trust-security.js +390 -0
  202. package/src/lib/ueba.js +389 -0
  203. package/src/lib/universal-runtime.js +325 -0
  204. package/src/lib/user-profile.js +81 -0
  205. package/src/lib/version-checker.js +81 -0
  206. package/src/lib/wallet-manager.js +326 -0
  207. package/src/lib/web-fetch.js +81 -0
  208. package/src/lib/workflow-engine.js +322 -0
  209. package/src/lib/zkp-engine.js +105 -0
@@ -829,3 +829,463 @@ export function _resetState() {
829
829
  _tenantUsage.clear();
830
830
  _seq = 0;
831
831
  }
832
+
833
+ /* ═══════════════════════════════════════════════════════════════
834
+ * Phase 97 V2 — Tenant Maturity + Subscription Lifecycle
835
+ * Strictly additive. Legacy surface above is preserved.
836
+ * ═════════════════════════════════════════════════════════════ */
837
+
838
+ export const TENANT_MATURITY_V2 = Object.freeze({
839
+ PROVISIONING: "provisioning",
840
+ ACTIVE: "active",
841
+ SUSPENDED: "suspended",
842
+ ARCHIVED: "archived",
843
+ CANCELLED: "cancelled",
844
+ });
845
+
846
+ export const SUBSCRIPTION_LIFECYCLE_V2 = Object.freeze({
847
+ PENDING: "pending",
848
+ ACTIVE: "active",
849
+ PAST_DUE: "past_due",
850
+ CANCELLED: "cancelled",
851
+ EXPIRED: "expired",
852
+ });
853
+
854
+ const TENANT_TRANSITIONS_V2 = new Map([
855
+ ["provisioning", new Set(["active", "cancelled"])],
856
+ ["active", new Set(["suspended", "archived", "cancelled"])],
857
+ ["suspended", new Set(["active", "archived", "cancelled"])],
858
+ ["archived", new Set(["active", "cancelled"])],
859
+ ]);
860
+ const TENANT_TERMINALS_V2 = new Set(["cancelled"]);
861
+
862
+ const SUBSCRIPTION_TRANSITIONS_V2 = new Map([
863
+ ["pending", new Set(["active", "cancelled"])],
864
+ ["active", new Set(["past_due", "cancelled", "expired"])],
865
+ ["past_due", new Set(["active", "cancelled", "expired"])],
866
+ ]);
867
+ const SUBSCRIPTION_TERMINALS_V2 = new Set(["cancelled", "expired"]);
868
+
869
+ export const SAAS_DEFAULT_MAX_ACTIVE_TENANTS_PER_PLAN = 1000;
870
+ export const SAAS_DEFAULT_MAX_SUBSCRIPTIONS_PER_TENANT = 5;
871
+ export const SAAS_DEFAULT_TENANT_IDLE_MS = 180 * 86400000; // 180 days
872
+ export const SAAS_DEFAULT_PAST_DUE_GRACE_MS = 7 * 86400000; // 7 days
873
+
874
+ let _maxActiveTenantsPerPlanV2 = SAAS_DEFAULT_MAX_ACTIVE_TENANTS_PER_PLAN;
875
+ let _maxSubscriptionsPerTenantV2 = SAAS_DEFAULT_MAX_SUBSCRIPTIONS_PER_TENANT;
876
+ let _tenantIdleMsV2 = SAAS_DEFAULT_TENANT_IDLE_MS;
877
+ let _pastDueGraceMsV2 = SAAS_DEFAULT_PAST_DUE_GRACE_MS;
878
+
879
+ const _tenantStatesV2 = new Map(); // tenantId → V2 record
880
+ const _subscriptionStatesV2 = new Map(); // subId → V2 record
881
+
882
+ function _positiveIntV2(n, label) {
883
+ const num = Number(n);
884
+ if (!Number.isFinite(num) || num <= 0) {
885
+ throw new Error(`${label} must be a positive integer`);
886
+ }
887
+ return Math.floor(num);
888
+ }
889
+
890
+ function _validTenantStatusV2(s) {
891
+ return (
892
+ s === "provisioning" ||
893
+ s === "active" ||
894
+ s === "suspended" ||
895
+ s === "archived" ||
896
+ s === "cancelled"
897
+ );
898
+ }
899
+
900
+ function _validSubscriptionStatusV2(s) {
901
+ return (
902
+ s === "pending" ||
903
+ s === "active" ||
904
+ s === "past_due" ||
905
+ s === "cancelled" ||
906
+ s === "expired"
907
+ );
908
+ }
909
+
910
+ export function getDefaultMaxActiveTenantsPerPlanV2() {
911
+ return SAAS_DEFAULT_MAX_ACTIVE_TENANTS_PER_PLAN;
912
+ }
913
+ export function getMaxActiveTenantsPerPlanV2() {
914
+ return _maxActiveTenantsPerPlanV2;
915
+ }
916
+ export function setMaxActiveTenantsPerPlanV2(n) {
917
+ _maxActiveTenantsPerPlanV2 = _positiveIntV2(n, "maxActiveTenantsPerPlan");
918
+ return _maxActiveTenantsPerPlanV2;
919
+ }
920
+
921
+ export function getDefaultMaxSubscriptionsPerTenantV2() {
922
+ return SAAS_DEFAULT_MAX_SUBSCRIPTIONS_PER_TENANT;
923
+ }
924
+ export function getMaxSubscriptionsPerTenantV2() {
925
+ return _maxSubscriptionsPerTenantV2;
926
+ }
927
+ export function setMaxSubscriptionsPerTenantV2(n) {
928
+ _maxSubscriptionsPerTenantV2 = _positiveIntV2(n, "maxSubscriptionsPerTenant");
929
+ return _maxSubscriptionsPerTenantV2;
930
+ }
931
+
932
+ export function getDefaultTenantIdleMsV2() {
933
+ return SAAS_DEFAULT_TENANT_IDLE_MS;
934
+ }
935
+ export function getTenantIdleMsV2() {
936
+ return _tenantIdleMsV2;
937
+ }
938
+ export function setTenantIdleMsV2(ms) {
939
+ _tenantIdleMsV2 = _positiveIntV2(ms, "tenantIdleMs");
940
+ return _tenantIdleMsV2;
941
+ }
942
+
943
+ export function getDefaultPastDueGraceMsV2() {
944
+ return SAAS_DEFAULT_PAST_DUE_GRACE_MS;
945
+ }
946
+ export function getPastDueGraceMsV2() {
947
+ return _pastDueGraceMsV2;
948
+ }
949
+ export function setPastDueGraceMsV2(ms) {
950
+ _pastDueGraceMsV2 = _positiveIntV2(ms, "pastDueGraceMs");
951
+ return _pastDueGraceMsV2;
952
+ }
953
+
954
+ /* ── Tenant V2 ──────────────────────────────────────────────── */
955
+
956
+ export function registerTenantV2(db, config = {}) {
957
+ void db;
958
+ const tenantId = String(config.tenantId || "").trim();
959
+ if (!tenantId) throw new Error("tenantId is required");
960
+ const plan = String(config.plan || "").trim();
961
+ if (!plan) throw new Error("plan is required");
962
+ if (_tenantStatesV2.has(tenantId)) {
963
+ throw new Error(`Tenant already registered in V2: ${tenantId}`);
964
+ }
965
+
966
+ const now = Number(config.now ?? Date.now());
967
+ const initialStatus = config.initialStatus || "provisioning";
968
+ if (!_validTenantStatusV2(initialStatus)) {
969
+ throw new Error(`Invalid initial status: ${initialStatus}`);
970
+ }
971
+ if (TENANT_TERMINALS_V2.has(initialStatus)) {
972
+ throw new Error(
973
+ `Cannot register tenant in terminal status '${initialStatus}'`,
974
+ );
975
+ }
976
+
977
+ if (initialStatus === "active") {
978
+ let activeCount = 0;
979
+ for (const rec of _tenantStatesV2.values()) {
980
+ if (rec.plan === plan && rec.status === "active") activeCount += 1;
981
+ }
982
+ if (activeCount >= _maxActiveTenantsPerPlanV2) {
983
+ throw new Error(
984
+ `Max active tenants per plan reached (${_maxActiveTenantsPerPlanV2})`,
985
+ );
986
+ }
987
+ }
988
+
989
+ const record = {
990
+ tenantId,
991
+ plan,
992
+ ownerId: config.ownerId ? String(config.ownerId) : null,
993
+ status: initialStatus,
994
+ metadata: config.metadata ? { ...config.metadata } : {},
995
+ createdAt: now,
996
+ updatedAt: now,
997
+ lastActivityAt: now,
998
+ reason: null,
999
+ };
1000
+ _tenantStatesV2.set(tenantId, record);
1001
+ return { ...record, metadata: { ...record.metadata } };
1002
+ }
1003
+
1004
+ export function getTenantV2(tenantId) {
1005
+ const rec = _tenantStatesV2.get(String(tenantId || ""));
1006
+ if (!rec) return null;
1007
+ return { ...rec, metadata: { ...rec.metadata } };
1008
+ }
1009
+
1010
+ export function setTenantMaturityV2(db, tenantId, newStatus, patch = {}) {
1011
+ void db;
1012
+ const id = String(tenantId || "");
1013
+ const record = _tenantStatesV2.get(id);
1014
+ if (!record) throw new Error(`Tenant not registered in V2: ${id}`);
1015
+ if (!_validTenantStatusV2(newStatus)) {
1016
+ throw new Error(`Invalid tenant status: ${newStatus}`);
1017
+ }
1018
+ if (TENANT_TERMINALS_V2.has(record.status)) {
1019
+ throw new Error(
1020
+ `Tenant is in terminal status '${record.status}' and cannot transition`,
1021
+ );
1022
+ }
1023
+ const allowed = TENANT_TRANSITIONS_V2.get(record.status);
1024
+ if (!allowed || !allowed.has(newStatus)) {
1025
+ throw new Error(`Invalid transition: ${record.status} → ${newStatus}`);
1026
+ }
1027
+
1028
+ if (newStatus === "active" && record.status !== "active") {
1029
+ let activeCount = 0;
1030
+ for (const rec of _tenantStatesV2.values()) {
1031
+ if (rec.plan === record.plan && rec.status === "active") activeCount += 1;
1032
+ }
1033
+ if (activeCount >= _maxActiveTenantsPerPlanV2) {
1034
+ throw new Error(
1035
+ `Max active tenants per plan reached (${_maxActiveTenantsPerPlanV2})`,
1036
+ );
1037
+ }
1038
+ }
1039
+
1040
+ record.status = newStatus;
1041
+ record.updatedAt = Number(patch.now ?? Date.now());
1042
+ if (patch.reason !== undefined) record.reason = patch.reason;
1043
+ if (patch.metadata && typeof patch.metadata === "object") {
1044
+ record.metadata = { ...record.metadata, ...patch.metadata };
1045
+ }
1046
+ return { ...record, metadata: { ...record.metadata } };
1047
+ }
1048
+
1049
+ export function activateTenant(db, tenantId, reason) {
1050
+ return setTenantMaturityV2(db, tenantId, "active", { reason });
1051
+ }
1052
+ export function suspendTenant(db, tenantId, reason) {
1053
+ return setTenantMaturityV2(db, tenantId, "suspended", { reason });
1054
+ }
1055
+ export function archiveTenantV2(db, tenantId, reason) {
1056
+ return setTenantMaturityV2(db, tenantId, "archived", { reason });
1057
+ }
1058
+ export function cancelTenant(db, tenantId, reason) {
1059
+ return setTenantMaturityV2(db, tenantId, "cancelled", { reason });
1060
+ }
1061
+
1062
+ export function touchTenantActivity(tenantId) {
1063
+ const rec = _tenantStatesV2.get(String(tenantId || ""));
1064
+ if (!rec) throw new Error(`Tenant not registered in V2: ${tenantId}`);
1065
+ rec.lastActivityAt = Date.now();
1066
+ return { ...rec, metadata: { ...rec.metadata } };
1067
+ }
1068
+
1069
+ /* ── Subscription V2 ────────────────────────────────────────── */
1070
+
1071
+ export function registerSubscriptionV2(db, config = {}) {
1072
+ void db;
1073
+ const subscriptionId = String(config.subscriptionId || "").trim();
1074
+ if (!subscriptionId) throw new Error("subscriptionId is required");
1075
+ const tenantId = String(config.tenantId || "").trim();
1076
+ if (!tenantId) throw new Error("tenantId is required");
1077
+ const plan = String(config.plan || "").trim();
1078
+ if (!plan) throw new Error("plan is required");
1079
+
1080
+ if (_subscriptionStatesV2.has(subscriptionId)) {
1081
+ throw new Error(`Subscription already registered in V2: ${subscriptionId}`);
1082
+ }
1083
+
1084
+ const tenant = _tenantStatesV2.get(tenantId);
1085
+ if (!tenant) {
1086
+ throw new Error(`Tenant not registered in V2: ${tenantId}`);
1087
+ }
1088
+ if (
1089
+ tenant.status === "cancelled" ||
1090
+ tenant.status === "archived" ||
1091
+ tenant.status === "suspended"
1092
+ ) {
1093
+ throw new Error(`Tenant is ${tenant.status}, cannot register subscription`);
1094
+ }
1095
+
1096
+ let openCount = 0;
1097
+ for (const rec of _subscriptionStatesV2.values()) {
1098
+ if (
1099
+ rec.tenantId === tenantId &&
1100
+ !SUBSCRIPTION_TERMINALS_V2.has(rec.status)
1101
+ ) {
1102
+ openCount += 1;
1103
+ }
1104
+ }
1105
+ if (openCount >= _maxSubscriptionsPerTenantV2) {
1106
+ throw new Error(
1107
+ `Max subscriptions per tenant reached (${_maxSubscriptionsPerTenantV2})`,
1108
+ );
1109
+ }
1110
+
1111
+ const now = Number(config.now ?? Date.now());
1112
+ const record = {
1113
+ subscriptionId,
1114
+ tenantId,
1115
+ plan,
1116
+ status: "pending",
1117
+ metadata: config.metadata ? { ...config.metadata } : {},
1118
+ createdAt: now,
1119
+ updatedAt: now,
1120
+ activatedAt: null,
1121
+ expiresAt: config.expiresAt ? Number(config.expiresAt) : null,
1122
+ pastDueAt: null,
1123
+ reason: null,
1124
+ };
1125
+ _subscriptionStatesV2.set(subscriptionId, record);
1126
+ return { ...record, metadata: { ...record.metadata } };
1127
+ }
1128
+
1129
+ export function getSubscriptionV2(subscriptionId) {
1130
+ const rec = _subscriptionStatesV2.get(String(subscriptionId || ""));
1131
+ if (!rec) return null;
1132
+ return { ...rec, metadata: { ...rec.metadata } };
1133
+ }
1134
+
1135
+ export function setSubscriptionStatusV2(
1136
+ db,
1137
+ subscriptionId,
1138
+ newStatus,
1139
+ patch = {},
1140
+ ) {
1141
+ void db;
1142
+ const id = String(subscriptionId || "");
1143
+ const record = _subscriptionStatesV2.get(id);
1144
+ if (!record) throw new Error(`Subscription not registered in V2: ${id}`);
1145
+ if (!_validSubscriptionStatusV2(newStatus)) {
1146
+ throw new Error(`Invalid subscription status: ${newStatus}`);
1147
+ }
1148
+ if (SUBSCRIPTION_TERMINALS_V2.has(record.status)) {
1149
+ throw new Error(
1150
+ `Subscription is in terminal status '${record.status}' and cannot transition`,
1151
+ );
1152
+ }
1153
+ const allowed = SUBSCRIPTION_TRANSITIONS_V2.get(record.status);
1154
+ if (!allowed || !allowed.has(newStatus)) {
1155
+ throw new Error(`Invalid transition: ${record.status} → ${newStatus}`);
1156
+ }
1157
+ const now = Number(patch.now ?? Date.now());
1158
+ record.status = newStatus;
1159
+ record.updatedAt = now;
1160
+ if (newStatus === "active" && record.activatedAt === null) {
1161
+ record.activatedAt = now;
1162
+ }
1163
+ if (newStatus === "past_due" && record.pastDueAt === null) {
1164
+ record.pastDueAt = now;
1165
+ }
1166
+ if (patch.reason !== undefined) record.reason = patch.reason;
1167
+ if (patch.metadata && typeof patch.metadata === "object") {
1168
+ record.metadata = { ...record.metadata, ...patch.metadata };
1169
+ }
1170
+ return { ...record, metadata: { ...record.metadata } };
1171
+ }
1172
+
1173
+ export function activateSubscription(db, subscriptionId, reason) {
1174
+ return setSubscriptionStatusV2(db, subscriptionId, "active", { reason });
1175
+ }
1176
+ export function markSubscriptionPastDue(db, subscriptionId, reason) {
1177
+ return setSubscriptionStatusV2(db, subscriptionId, "past_due", { reason });
1178
+ }
1179
+ export function cancelSubscriptionV2(db, subscriptionId, reason) {
1180
+ return setSubscriptionStatusV2(db, subscriptionId, "cancelled", { reason });
1181
+ }
1182
+ export function expireSubscription(db, subscriptionId, reason) {
1183
+ return setSubscriptionStatusV2(db, subscriptionId, "expired", { reason });
1184
+ }
1185
+
1186
+ /* ── Counts ─────────────────────────────────────────────────── */
1187
+
1188
+ export function getActiveTenantCount(plan) {
1189
+ let n = 0;
1190
+ for (const rec of _tenantStatesV2.values()) {
1191
+ if (rec.status !== "active") continue;
1192
+ if (plan !== undefined && rec.plan !== String(plan)) continue;
1193
+ n += 1;
1194
+ }
1195
+ return n;
1196
+ }
1197
+
1198
+ export function getOpenSubscriptionCount(tenantId) {
1199
+ let n = 0;
1200
+ for (const rec of _subscriptionStatesV2.values()) {
1201
+ if (SUBSCRIPTION_TERMINALS_V2.has(rec.status)) continue;
1202
+ if (tenantId !== undefined && rec.tenantId !== String(tenantId)) continue;
1203
+ n += 1;
1204
+ }
1205
+ return n;
1206
+ }
1207
+
1208
+ /* ── Auto-flip Bulk Ops ─────────────────────────────────────── */
1209
+
1210
+ export function autoArchiveIdleTenants(db, nowMs) {
1211
+ void db;
1212
+ const now = Number(nowMs ?? Date.now());
1213
+ const flipped = [];
1214
+ for (const rec of _tenantStatesV2.values()) {
1215
+ if (rec.status !== "active" && rec.status !== "suspended") continue;
1216
+ if (now - rec.lastActivityAt > _tenantIdleMsV2) {
1217
+ rec.status = "archived";
1218
+ rec.updatedAt = now;
1219
+ rec.reason = "idle";
1220
+ flipped.push(rec.tenantId);
1221
+ }
1222
+ }
1223
+ return flipped;
1224
+ }
1225
+
1226
+ export function autoExpirePastDueSubscriptions(db, nowMs) {
1227
+ void db;
1228
+ const now = Number(nowMs ?? Date.now());
1229
+ const flipped = [];
1230
+ for (const rec of _subscriptionStatesV2.values()) {
1231
+ if (rec.status !== "past_due") continue;
1232
+ const pastDueAt = rec.pastDueAt ?? rec.updatedAt;
1233
+ if (now - pastDueAt > _pastDueGraceMsV2) {
1234
+ rec.status = "expired";
1235
+ rec.updatedAt = now;
1236
+ rec.reason = "past_due_grace_expired";
1237
+ flipped.push(rec.subscriptionId);
1238
+ }
1239
+ }
1240
+ return flipped;
1241
+ }
1242
+
1243
+ /* ── Stats V2 ───────────────────────────────────────────────── */
1244
+
1245
+ export function getSaasStatsV2() {
1246
+ const tenantsByStatus = {
1247
+ provisioning: 0,
1248
+ active: 0,
1249
+ suspended: 0,
1250
+ archived: 0,
1251
+ cancelled: 0,
1252
+ };
1253
+ const subscriptionsByStatus = {
1254
+ pending: 0,
1255
+ active: 0,
1256
+ past_due: 0,
1257
+ cancelled: 0,
1258
+ expired: 0,
1259
+ };
1260
+ for (const rec of _tenantStatesV2.values()) {
1261
+ if (tenantsByStatus[rec.status] !== undefined) {
1262
+ tenantsByStatus[rec.status] += 1;
1263
+ }
1264
+ }
1265
+ for (const rec of _subscriptionStatesV2.values()) {
1266
+ if (subscriptionsByStatus[rec.status] !== undefined) {
1267
+ subscriptionsByStatus[rec.status] += 1;
1268
+ }
1269
+ }
1270
+ return {
1271
+ totalTenantsV2: _tenantStatesV2.size,
1272
+ totalSubscriptionsV2: _subscriptionStatesV2.size,
1273
+ maxActiveTenantsPerPlan: _maxActiveTenantsPerPlanV2,
1274
+ maxSubscriptionsPerTenant: _maxSubscriptionsPerTenantV2,
1275
+ tenantIdleMs: _tenantIdleMsV2,
1276
+ pastDueGraceMs: _pastDueGraceMsV2,
1277
+ tenantsByStatus,
1278
+ subscriptionsByStatus,
1279
+ };
1280
+ }
1281
+
1282
+ /* ── Reset V2 (tests) ───────────────────────────────────────── */
1283
+
1284
+ export function _resetStateV2() {
1285
+ _tenantStatesV2.clear();
1286
+ _subscriptionStatesV2.clear();
1287
+ _maxActiveTenantsPerPlanV2 = SAAS_DEFAULT_MAX_ACTIVE_TENANTS_PER_PLAN;
1288
+ _maxSubscriptionsPerTenantV2 = SAAS_DEFAULT_MAX_SUBSCRIPTIONS_PER_TENANT;
1289
+ _tenantIdleMsV2 = SAAS_DEFAULT_TENANT_IDLE_MS;
1290
+ _pastDueGraceMsV2 = SAAS_DEFAULT_PAST_DUE_GRACE_MS;
1291
+ }