progmune-runtime 2.1.5 → 3.2.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 (356) hide show
  1. package/README.md +108 -468
  2. package/dist/ablation-study.js +144 -0
  3. package/dist/ablation-study.test.js +18 -0
  4. package/dist/action-runtime.js +3 -1
  5. package/dist/active-learning.js +211 -0
  6. package/dist/analytics.js +139 -0
  7. package/dist/asset-factory.js +309 -0
  8. package/dist/asset-growth.js +244 -0
  9. package/dist/asset-promotion.js +382 -0
  10. package/dist/asset-quality.js +550 -0
  11. package/dist/audit/business-translator.js +285 -0
  12. package/dist/audit/cli.js +66 -0
  13. package/dist/audit/formatters/html.js +379 -0
  14. package/dist/audit/formatters/json.js +11 -0
  15. package/dist/audit/formatters/markdown.js +192 -0
  16. package/dist/audit/formatters/terminal.js +189 -0
  17. package/dist/audit/index.js +25 -0
  18. package/dist/audit/report-builder.js +318 -0
  19. package/dist/audit/types.js +8 -0
  20. package/dist/audit.js +3 -3
  21. package/dist/auto-benchmark-generator.js +137 -0
  22. package/dist/auto-benchmark-generator.test.js +45 -0
  23. package/dist/auto-protocol-synthesizer.js +362 -0
  24. package/dist/auto-protocol-synthesizer.test.js +82 -0
  25. package/dist/autonomous-patch.js +175 -0
  26. package/dist/autonomous-patch.test.js +128 -0
  27. package/dist/badge/badge-server.js +98 -0
  28. package/dist/behavior-miner.js +442 -0
  29. package/dist/belief-layer.js +475 -0
  30. package/dist/benchmark-count.js +5 -0
  31. package/dist/benchmark-generator.js +211 -0
  32. package/dist/benchmark-harness.js +201 -0
  33. package/dist/benchmark-pass-rate.js +7 -0
  34. package/dist/benchmark-report.js +8 -3
  35. package/dist/benchmark-save.js +14 -1
  36. package/dist/bootstrap-validation.js +197 -0
  37. package/dist/bootstrap-validation.test.js +51 -0
  38. package/dist/branch-ledger.js +1 -1
  39. package/dist/capability-gap.js +130 -0
  40. package/dist/certify-html.js +351 -0
  41. package/dist/certify.js +326 -0
  42. package/dist/check.js +4 -4
  43. package/dist/compliance-miner.js +447 -0
  44. package/dist/continuous-benchmark.js +194 -0
  45. package/dist/continuous-benchmark.test.js +116 -0
  46. package/dist/corpus-stats.js +173 -0
  47. package/dist/counterfactual-engine.js +288 -0
  48. package/dist/coverage-dashboard.js +109 -0
  49. package/dist/coverage-system.test.js +205 -0
  50. package/dist/cross-repo-precision.js +352 -0
  51. package/dist/cve-benchmark.js +180 -0
  52. package/dist/cve-benchmark.test.js +28 -0
  53. package/dist/cve-collector.js +73 -0
  54. package/dist/data-quality.js +141 -0
  55. package/dist/decision-engine.js +388 -0
  56. package/dist/derive-metadata.js +250 -0
  57. package/dist/difficulty-active.test.js +198 -0
  58. package/dist/difficulty-map.js +244 -0
  59. package/dist/discovery-analytics.js +125 -0
  60. package/dist/discovery-model.js +149 -0
  61. package/dist/discovery-optimize.test.js +199 -0
  62. package/dist/discovery-trace.js +276 -0
  63. package/dist/discovery-trace.test.js +97 -0
  64. package/dist/emitter.js +83 -1
  65. package/dist/enterprise-dashboard.js +405 -0
  66. package/dist/eval-hardening.js +297 -0
  67. package/dist/eval-hardening.test.js +85 -0
  68. package/dist/evaluation-campaign.js +359 -0
  69. package/dist/evaluation-campaign.test.js +181 -0
  70. package/dist/evidence-growth.js +143 -0
  71. package/dist/evidence-repository.js +209 -0
  72. package/dist/evidence-system.js +441 -0
  73. package/dist/execute.js +15 -7
  74. package/dist/experimental/software-physics.js +291 -0
  75. package/dist/experimental/state-inference.js +516 -0
  76. package/dist/experimental/unsupervised-physics.js +230 -0
  77. package/dist/extract-ir-python.js +54 -7
  78. package/dist/extract-ir.js +376 -12
  79. package/dist/failure-collector.js +2 -2
  80. package/dist/failure-corpus.js +322 -9
  81. package/dist/feedback.js +16 -5
  82. package/dist/feedback.test.js +49 -0
  83. package/dist/file-lock.js +1 -1
  84. package/dist/flywheel-batch.js +292 -0
  85. package/dist/frameworks/express-cli.js +237 -0
  86. package/dist/frameworks/express-detector.js +445 -0
  87. package/dist/frameworks/express-detector.test.js +206 -0
  88. package/dist/frameworks/index.js +30 -0
  89. package/dist/frameworks/nestjs-detector.js +302 -0
  90. package/dist/frameworks/trpc-detector.js +161 -0
  91. package/dist/frameworks/version-awareness.js +179 -0
  92. package/dist/function-synonyms.js +164 -0
  93. package/dist/function-synonyms.test.js +68 -0
  94. package/dist/generalization.test.js +352 -0
  95. package/dist/goal-annotator.js +113 -0
  96. package/dist/goal-planner.js +563 -0
  97. package/dist/gold-cve.js +164 -0
  98. package/dist/gold-cve.test.js +104 -0
  99. package/dist/gold-quality.js +206 -0
  100. package/dist/gold-tiers.js +241 -0
  101. package/dist/governance-dashboard.js +327 -0
  102. package/dist/graph-viz.js +240 -0
  103. package/dist/guided-frontier.js +195 -0
  104. package/dist/hierarchical-planner.js +148 -0
  105. package/dist/identifier-parser.js +260 -0
  106. package/dist/immune-metrics.js +93 -0
  107. package/dist/immune-receiver.js +158 -0
  108. package/dist/immune-reporter.js +1 -1
  109. package/dist/improvement-orchestrator.js +206 -0
  110. package/dist/inject-p0-vocabulary.js +300 -0
  111. package/dist/intent-parser.js +218 -0
  112. package/dist/invariant-algebra.js +476 -0
  113. package/dist/invariant-calculus.js +533 -0
  114. package/dist/ir-utils.js +70 -0
  115. package/dist/ir-utils.test.js +50 -0
  116. package/dist/knowledge-api.js +312 -0
  117. package/dist/knowledge-evolution.js +452 -0
  118. package/dist/knowledge-explorer.js +506 -0
  119. package/dist/knowledge-flywheel.js +274 -0
  120. package/dist/knowledge-governance.js +338 -0
  121. package/dist/knowledge-governance.test.js +150 -0
  122. package/dist/knowledge-graph.js +181 -0
  123. package/dist/knowledge-guided-synth.js +246 -0
  124. package/dist/knowledge-loop.test.js +77 -0
  125. package/dist/knowledge-object.js +316 -0
  126. package/dist/knowledge-package.js +98 -0
  127. package/dist/kpi-dashboard.js +561 -0
  128. package/dist/l3-cross-function.js +280 -0
  129. package/dist/learning-ranker.js +148 -0
  130. package/dist/learning-ranker.test.js +291 -0
  131. package/dist/ledger/accountability.js +322 -0
  132. package/dist/ledger/chain-builder.js +185 -0
  133. package/dist/ledger/cli.js +222 -0
  134. package/dist/ledger/index.js +13 -0
  135. package/dist/ledger/signatures.js +193 -0
  136. package/dist/ledger/types.js +9 -0
  137. package/dist/llm.js +74 -3
  138. package/dist/load-benchmarks.js +8 -3
  139. package/dist/logger.js +66 -0
  140. package/dist/logger.test.js +37 -0
  141. package/dist/logistic-reward.js +339 -0
  142. package/dist/logistic-reward.test.js +180 -0
  143. package/dist/macro-graph.js +193 -0
  144. package/dist/macro-repair.js +183 -0
  145. package/dist/mcp-server.mjs +1202 -483
  146. package/dist/memory-layer.js +42 -5
  147. package/dist/multi-repo-precision.js +422 -0
  148. package/dist/name-free-protocol.js +425 -0
  149. package/dist/name-free-protocol.test.js +170 -0
  150. package/dist/name-scrambling.js +138 -0
  151. package/dist/name-scrambling.test.js +16 -0
  152. package/dist/p3-observability.test.js +281 -0
  153. package/dist/p5-orchestrator.test.js +225 -0
  154. package/dist/pairwise-preference.js +294 -0
  155. package/dist/pairwise-preference.test.js +140 -0
  156. package/dist/planner-constraints.js +104 -0
  157. package/dist/planner-prompts.js +155 -0
  158. package/dist/planner-telemetry.js +415 -0
  159. package/dist/planner-trace.js +214 -0
  160. package/dist/planner.js +162 -167
  161. package/dist/plsb/artifact.js +116 -0
  162. package/dist/plsb/cli.js +71 -0
  163. package/dist/plsb/index.js +19 -0
  164. package/dist/plsb/leaderboard.js +249 -0
  165. package/dist/plsb/report-md.js +156 -0
  166. package/dist/plsb/schema.js +179 -0
  167. package/dist/plsb-benchmark.js +284 -0
  168. package/dist/plsb-benchmark.test.js +119 -0
  169. package/dist/policy/cli.js +134 -0
  170. package/dist/policy/engine.js +333 -0
  171. package/dist/policy/index.js +12 -0
  172. package/dist/policy/types.js +59 -0
  173. package/dist/policy-miner.js +505 -0
  174. package/dist/precision-analyze.js +229 -0
  175. package/dist/precision-benchmark.js +147 -0
  176. package/dist/precision-label-c.js +134 -0
  177. package/dist/precision-label.js +193 -0
  178. package/dist/precision-report-c.js +149 -0
  179. package/dist/precision-report.js +246 -0
  180. package/dist/progmune-status.js +108 -0
  181. package/dist/proof-engine.js +479 -0
  182. package/dist/proof-provenance.js +315 -0
  183. package/dist/protocol-coverage.js +294 -0
  184. package/dist/protocol-detector.js +1189 -0
  185. package/dist/protocol-embedding-expanded.js +297 -0
  186. package/dist/protocol-embedding-expanded.test.js +97 -0
  187. package/dist/protocol-embedding.js +195 -0
  188. package/dist/protocol-embedding.test.js +82 -0
  189. package/dist/protocol-extractor-v2.js +354 -0
  190. package/dist/protocol-extractor-v2.test.js +140 -0
  191. package/dist/protocol-extractor.js +310 -0
  192. package/dist/protocol-extractor.test.js +113 -0
  193. package/dist/protocol-foundation.js +322 -0
  194. package/dist/protocol-foundation.test.js +163 -0
  195. package/dist/protocol-frontier.js +243 -0
  196. package/dist/protocol-frontier.test.js +92 -0
  197. package/dist/protocol-gap-analyzer.js +228 -0
  198. package/dist/protocol-gap-analyzer.test.js +49 -0
  199. package/dist/protocol-invariants.js +276 -0
  200. package/dist/protocol-invariants.test.js +111 -0
  201. package/dist/protocol-knowledge.js +464 -0
  202. package/dist/protocol-miner.js +343 -0
  203. package/dist/protocol-mining.js +207 -0
  204. package/dist/protocol-mining.test.js +37 -0
  205. package/dist/protocol-registry.js +1 -1
  206. package/dist/protocol-security-benchmark.js +222 -0
  207. package/dist/protocol-vulnerability.js +257 -0
  208. package/dist/protocol-vulnerability.test.js +60 -0
  209. package/dist/python-benchmark.js +120 -0
  210. package/dist/python-emitter.js +163 -45
  211. package/dist/python-protocol-extractor.js +187 -0
  212. package/dist/python-protocol-extractor.test.js +116 -0
  213. package/dist/realworld-benchmark.js +646 -0
  214. package/dist/realworld-benchmark.test.js +36 -0
  215. package/dist/repair-arch.test.js +411 -0
  216. package/dist/repair-evolution.test.js +454 -0
  217. package/dist/repair-executor.js +719 -0
  218. package/dist/repair-proposal.js +4 -4
  219. package/dist/repair-ranker.js +141 -0
  220. package/dist/repair-strategies.js +419 -0
  221. package/dist/repair-taxonomy.js +234 -0
  222. package/dist/repair-types.js +12 -0
  223. package/dist/repo-evaluator.js +250 -0
  224. package/dist/repo-evaluator.test.js +128 -0
  225. package/dist/resource-abstraction.js +242 -0
  226. package/dist/resource-detector.js +211 -0
  227. package/dist/result.test.js +43 -0
  228. package/dist/reward-system.js +411 -0
  229. package/dist/reward-system.test.js +175 -0
  230. package/dist/risk-model.js +215 -0
  231. package/dist/rule-miner.js +234 -7
  232. package/dist/rule-specificity.js +254 -0
  233. package/dist/runtime-types.js +27 -0
  234. package/dist/scaffold.js +208 -0
  235. package/dist/scale-collector.test.js +101 -0
  236. package/dist/scale-trajectory-collector.js +128 -0
  237. package/dist/sdk.js +250 -0
  238. package/dist/search-planner.js +4 -41
  239. package/dist/semantic-snapshot.js +1 -1
  240. package/dist/semantic-topology.js +121 -0
  241. package/dist/semantic-trace.js +310 -317
  242. package/dist/sequence-extractor.js +343 -0
  243. package/dist/skill-library.js +245 -0
  244. package/dist/skill-planner.test.js +189 -0
  245. package/dist/software-physics.js +291 -0
  246. package/dist/software-physics.test.js +81 -0
  247. package/dist/ssg-precision.js +478 -0
  248. package/dist/ssg-validator.js +71 -21
  249. package/dist/state-inference-doubleblind.test.js +160 -0
  250. package/dist/state-inference.js +516 -0
  251. package/dist/state-inference.test.js +115 -0
  252. package/dist/state-machine-fingerprint.js +345 -0
  253. package/dist/state-machine-fingerprint.test.js +120 -0
  254. package/dist/state-miner.js +386 -0
  255. package/dist/state-name-inference.js +213 -0
  256. package/dist/state-name-inference.test.js +69 -0
  257. package/dist/strategy-planner.js +326 -59
  258. package/dist/strategy-planner.test.js +135 -0
  259. package/dist/telemetry-analytics.test.js +402 -0
  260. package/dist/terminal-format.js +68 -0
  261. package/dist/terminal-format.test.js +83 -0
  262. package/dist/topology-factory.js +196 -0
  263. package/dist/topology-representation.js +242 -0
  264. package/dist/topology-representation.test.js +27 -0
  265. package/dist/trajectory-augmentation.js +254 -0
  266. package/dist/trajectory-augmentation.test.js +63 -0
  267. package/dist/trajectory-corpus.js +440 -0
  268. package/dist/trajectory-corpus.test.js +32 -0
  269. package/dist/trajectory-feedback.test.js +116 -0
  270. package/dist/transition-synthesizer.js +286 -0
  271. package/dist/transition-synthesizer.test.js +123 -0
  272. package/dist/trust/api-semantic-mapper.js +809 -0
  273. package/dist/trust/call-graph-propagator.js +225 -0
  274. package/dist/trust/cli.js +122 -0
  275. package/dist/trust/compliance-scorer.js +283 -0
  276. package/dist/trust/confidence-calculator.js +261 -0
  277. package/dist/trust/engine.js +1145 -0
  278. package/dist/trust/explainability.js +85 -0
  279. package/dist/trust/formatters/ci.js +42 -0
  280. package/dist/trust/formatters/json.js +11 -0
  281. package/dist/trust/formatters/terminal.js +152 -0
  282. package/dist/trust/index.js +39 -0
  283. package/dist/trust/phase1-verify.js +171 -0
  284. package/dist/trust/protocol-domain-validator.js +697 -0
  285. package/dist/trust/score-calculator.js +282 -0
  286. package/dist/trust/ssg-bridge.js +641 -0
  287. package/dist/trust/ssg-bridge.test.js +269 -0
  288. package/dist/trust/types.js +67 -0
  289. package/dist/trust/violation-trace.js +335 -0
  290. package/dist/trust-api.js +179 -0
  291. package/dist/trust-calibration.js +279 -0
  292. package/dist/unknown-protocol-discovery.js +339 -0
  293. package/dist/unknown-protocol-discovery.test.js +102 -0
  294. package/dist/unsupervised-physics.js +230 -0
  295. package/dist/unsupervised-physics.test.js +95 -0
  296. package/dist/utils.test.js +37 -0
  297. package/dist/validator.js +187 -10
  298. package/dist/verification-intelligence.js +475 -0
  299. package/dist/verify-api.js +432 -0
  300. package/dist/vi-impact-report.js +293 -0
  301. package/dist/wl-fingerprint.js +162 -0
  302. package/dist/wl-fingerprint.test.js +130 -0
  303. package/dist/zeroshot-strategy.js +139 -0
  304. package/docs/Progmune_/346/212/225/350/265/204/344/272/272/347/231/275/347/232/256/344/271/246_v2.0.html +576 -0
  305. package/docs/Progmune_/351/241/271/347/233/256/345/205/250/350/247/243.html +710 -0
  306. package/package.json +74 -7
  307. package/protocols.json +1956 -50
  308. package/.dockerignore +0 -14
  309. package/.mcp.json +0 -11
  310. package/.progmune_allowlist +0 -50
  311. package/.test_report/test_report.md +0 -87
  312. package/Dockerfile +0 -9
  313. package/FAQ.md +0 -167
  314. package/WHITEPAPER.md +0 -540
  315. package/demo-project/auth.ts +0 -55
  316. package/demo-project/tsconfig.json +0 -8
  317. package/dist/acl-breakdown.js +0 -13
  318. package/dist/all-sessions.js +0 -11
  319. package/dist/antibody-stats.js +0 -11
  320. package/dist/branch-tree-count.js +0 -14
  321. package/dist/common-fixpath.js +0 -12
  322. package/dist/constraint-types.js +0 -12
  323. package/dist/exec-metrics.js +0 -11
  324. package/dist/failure-report.js +0 -11
  325. package/dist/fast-path-hits.js +0 -13
  326. package/dist/fingerprint-list.js +0 -15
  327. package/dist/gen-history-log.js +0 -13
  328. package/dist/heatmap-data.js +0 -11
  329. package/dist/recent-session.js +0 -12
  330. package/dist/svl-distribution.js +0 -11
  331. package/dist/terminal-status.js +0 -11
  332. package/dist/token-savings.js +0 -11
  333. package/dist/total-repairs.js +0 -12
  334. package/dist/unresolved-count.js +0 -12
  335. package/dist/valid-fingerprints.js +0 -13
  336. package/dist/verify-ledgers.js +0 -11
  337. package/docs/whitepaper-style.css +0 -77
  338. package/docs/whitepaper-v2.1.md +0 -609
  339. package/docs/whitepaper-v2.2.md +0 -1064
  340. package/docs/whitepaper-v2.2.pdf +0 -0
  341. package/fly.toml +0 -31
  342. package/public/dashboard.html +0 -119
  343. package/server/hub.js +0 -116
  344. package/test/replay-golden/sess_1780063202050_mgeld.json +0 -9
  345. package/test/replay-golden/sess_1780064032560_gocld.json +0 -354
  346. package/test/replay-golden/sess_1780064413331_s2709.json +0 -606
  347. package/test/replay-golden/sess_1780064792710_y3avo.json +0 -614
  348. package/test/replay-golden.ts +0 -84
  349. package/test_benchmark.js +0 -165
  350. package/test_comprehensive.mjs +0 -638
  351. package/test_concurrency.js +0 -129
  352. package/test_ir_robustness.js +0 -85
  353. package/test_semantic_contracts.js +0 -269
  354. package/test_ssg_stress.js +0 -156
  355. package/test_svl3.js +0 -58
  356. package/tsconfig.json +0 -17
@@ -0,0 +1,641 @@
1
+ "use strict";
2
+ /**
3
+ * SSG Bridge — connects the SSG state machine (protocols.json) to the
4
+ * trust engine's semantic validation pipeline.
5
+ *
6
+ * The bridge solves the abstraction gap: protocol rules use abstract function
7
+ * names (verify_password, begin_tx, open_file), while real code uses concrete
8
+ * library APIs (bcrypt.compare, db.query, fs.openSync).
9
+ *
10
+ * Strategy (multi-pass, best-effort):
11
+ * 1. Direct match — normalized call name == rule name
12
+ * 2. Substring match — call name contains rule name or vice versa
13
+ * 3. Domain-guided — semantic domain maps to namespace, keywords infer rule
14
+ *
15
+ * Architecture:
16
+ * SemanticStep[] → inferRuleMatches() → SSG validateTransition() chain
17
+ * → SSGViolation[] (with BFS fix paths from findFixPathStatic)
18
+ */
19
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ var desc = Object.getOwnPropertyDescriptor(m, k);
22
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
23
+ desc = { enumerable: true, get: function() { return m[k]; } };
24
+ }
25
+ Object.defineProperty(o, k2, desc);
26
+ }) : (function(o, m, k, k2) {
27
+ if (k2 === undefined) k2 = k;
28
+ o[k2] = m[k];
29
+ }));
30
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
31
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
32
+ }) : function(o, v) {
33
+ o["default"] = v;
34
+ });
35
+ var __importStar = (this && this.__importStar) || (function () {
36
+ var ownKeys = function(o) {
37
+ ownKeys = Object.getOwnPropertyNames || function (o) {
38
+ var ar = [];
39
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
40
+ return ar;
41
+ };
42
+ return ownKeys(o);
43
+ };
44
+ return function (mod) {
45
+ if (mod && mod.__esModule) return mod;
46
+ var result = {};
47
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
48
+ __setModuleDefault(result, mod);
49
+ return result;
50
+ };
51
+ })();
52
+ Object.defineProperty(exports, "__esModule", { value: true });
53
+ exports.validateSequenceWithSSG = validateSequenceWithSSG;
54
+ exports.ssgViolationsToTrustViolations = ssgViolationsToTrustViolations;
55
+ exports.loadProjectAliases = loadProjectAliases;
56
+ exports.loadProtocolRules = loadProtocolRules;
57
+ exports.summarizeSSGCoverage = summarizeSSGCoverage;
58
+ const fs = __importStar(require("fs"));
59
+ const path = __importStar(require("path"));
60
+ const ssg_validator_1 = require("../ssg-validator");
61
+ // ═══════════════════════════════════════════════════════════════
62
+ // Domain → Namespace Mapping
63
+ // ═══════════════════════════════════════════════════════════════
64
+ /**
65
+ * Maps semantic protocol domains to SSG state machine namespaces.
66
+ * Multiple domains can map to the same namespace (e.g., tls_config,
67
+ * tls_handshake, tls_cert all map to "tls").
68
+ */
69
+ const DOMAIN_TO_NAMESPACE = {
70
+ // TLS/SSL
71
+ tls_config: "tls",
72
+ tls_handshake: "tls",
73
+ tls_cert: "tls",
74
+ tls_session: "tls",
75
+ tls_alpn: "tls",
76
+ // Auth
77
+ auth_cred: "auth",
78
+ auth_mech: "auth",
79
+ auth_hash: "auth",
80
+ auth_spn: "auth",
81
+ auth_gssapi: "auth",
82
+ // Connection management → resource lifecycle
83
+ conn_mgmt: "resource",
84
+ conn_poll: "resource",
85
+ // HTTP
86
+ http_ops: "tls", // HTTP server → TLS namespace
87
+ http2_ops: "tls",
88
+ // Protocol operations → stateless (no state machine, always pass)
89
+ ldap_ops: "stateless",
90
+ ftp_ops: "stateless",
91
+ smtp_ops: "stateless",
92
+ imap_ops: "stateless",
93
+ mqtt_ops: "stateless",
94
+ smb_ops: "stateless",
95
+ ssh_ops: "stateless",
96
+ telnet_ops: "stateless",
97
+ dns_ops: "stateless",
98
+ // Memory → stateless (tracked by safeguard rules, not state machine)
99
+ mem_alloc: "stateless",
100
+ mem_free: "stateless",
101
+ mem_util: "stateless",
102
+ // Utilities → stateless
103
+ str_util: "stateless",
104
+ str_format: "stateless",
105
+ buf_util: "stateless",
106
+ net_util: "stateless",
107
+ platform_util: "stateless",
108
+ debug_trace: "stateless",
109
+ error_handle: "stateless",
110
+ util: "stateless",
111
+ };
112
+ /**
113
+ * Domains that map to "auth" namespace with their position in the auth flow.
114
+ * Used to infer which protocol rule a call matches based on domain + position.
115
+ */
116
+ const AUTH_DOMAIN_FLOW = [
117
+ "auth_hash", // password hashing/verification
118
+ "auth_mech", // token generation, SASL negotiation
119
+ "auth_cred", // credential retrieval
120
+ "auth_spn", // Kerberos SPN
121
+ "auth_gssapi", // GSSAPI wrap/unwrap
122
+ ];
123
+ const NAMESPACE_RULE_HINTS = {
124
+ auth: [
125
+ { keyword: "hash_password", ruleName: "hash_password" },
126
+ { keyword: "argon2.hash", ruleName: "hash_password" },
127
+ { keyword: "scrypt.hash", ruleName: "hash_password" },
128
+ { keyword: "verify_hash", ruleName: "verify_hash" },
129
+ { keyword: "verify_password", ruleName: "verify_password" },
130
+ { keyword: "generate_jwt", ruleName: "generate_jwt" },
131
+ { keyword: "create_session", ruleName: "create_session" },
132
+ { keyword: "revoke_token", ruleName: "revoke_token" },
133
+ { keyword: "verify_token", ruleName: "verify_token" },
134
+ { keyword: "validate_token", ruleName: "verify_token" },
135
+ { keyword: "check_owner", ruleName: "check_owner" },
136
+ ],
137
+ tls: [
138
+ { keyword: "load_tls_config", ruleName: "load_tls_config" },
139
+ { keyword: "tls_config", ruleName: "load_tls_config" },
140
+ { keyword: "ssl_config", ruleName: "load_tls_config" },
141
+ ],
142
+ resource: [
143
+ { keyword: "open_file", ruleName: "open_file" },
144
+ { keyword: "read_file", ruleName: "read_file" },
145
+ { keyword: "write_file", ruleName: "write_file" },
146
+ { keyword: "close_file", ruleName: "close_file" },
147
+ { keyword: "connect_db", ruleName: "connect_db" },
148
+ { keyword: "query_db", ruleName: "query_db" },
149
+ { keyword: "disconnect_db", ruleName: "disconnect_db" },
150
+ { keyword: "sanitize", ruleName: "sanitize" },
151
+ { keyword: "validate_type", ruleName: "validate_type" },
152
+ { keyword: "validate_range", ruleName: "validate_range" },
153
+ ],
154
+ payment: [
155
+ { keyword: "initiate_payment", ruleName: "initiate_payment" },
156
+ { keyword: "create_payment", ruleName: "initiate_payment" },
157
+ { keyword: "stripe", ruleName: "initiate_payment" },
158
+ { keyword: "webhook", ruleName: "receive_payment_callback" },
159
+ { keyword: "confirm_payment", ruleName: "confirm_payment" },
160
+ { keyword: "verify_signature", ruleName: "verify_payment_signature" },
161
+ { keyword: "refund_payment", ruleName: "refund_payment" },
162
+ { keyword: "cancel_payment", ruleName: "cancel_payment" },
163
+ ],
164
+ transaction: [
165
+ { keyword: "begin_tx", ruleName: "begin_tx" },
166
+ { keyword: "commit_tx", ruleName: "commit_tx" },
167
+ { keyword: "rollback_tx", ruleName: "rollback_tx" },
168
+ { keyword: "insert_record", ruleName: "insert_record" },
169
+ { keyword: "update_record", ruleName: "update_record" },
170
+ { keyword: "delete_record", ruleName: "delete_record" },
171
+ ],
172
+ file_upload: [
173
+ { keyword: "multer", ruleName: "receive_upload" },
174
+ { keyword: "validate_file", ruleName: "validate_file" },
175
+ { keyword: "store_file", ruleName: "store_file" },
176
+ ],
177
+ session_mgmt: [
178
+ { keyword: "create_user_session", ruleName: "create_user_session" },
179
+ { keyword: "refresh_session", ruleName: "refresh_session" },
180
+ { keyword: "rotate_session_token", ruleName: "rotate_session_token" },
181
+ { keyword: "revoke_session", ruleName: "revoke_session" },
182
+ { keyword: "timeout_session", ruleName: "timeout_session" },
183
+ { keyword: "validate_session", ruleName: "validate_session" },
184
+ ],
185
+ registration: [
186
+ { keyword: "register_user", ruleName: "register_user" },
187
+ { keyword: "send_verification_code", ruleName: "send_verification_code" },
188
+ { keyword: "send_verification", ruleName: "send_verification_code" },
189
+ { keyword: "verify_code", ruleName: "verify_code" },
190
+ { keyword: "activate_account", ruleName: "activate_account" },
191
+ ],
192
+ missing_validation: [
193
+ { keyword: "validate", ruleName: "validate_request" },
194
+ { keyword: "sanitize", ruleName: "validate_request" },
195
+ { keyword: "safeParse", ruleName: "validate_request" },
196
+ { keyword: "parseAsync", ruleName: "validate_request" },
197
+ { keyword: "checkSchema", ruleName: "validate_request" },
198
+ { keyword: "zod", ruleName: "validate_request" },
199
+ ],
200
+ };
201
+ // ═══════════════════════════════════════════════════════════════
202
+ // Call → Rule Inference
203
+ // ═══════════════════════════════════════════════════════════════
204
+ /**
205
+ * Normalize a function name for matching:
206
+ * "bcrypt.hashPassword" → "hash_password"
207
+ * "SSL_CTX_new" → "ssl_ctx_new"
208
+ */
209
+ function normalizeName(name) {
210
+ // Split on dots, take the last segment
211
+ const segments = name.split(".");
212
+ const last = segments[segments.length - 1];
213
+ // CamelCase → snake_case
214
+ const snake = last
215
+ .replace(/([A-Z])/g, "_$1")
216
+ .toLowerCase()
217
+ .replace(/^_/, "")
218
+ .replace(/__+/g, "_");
219
+ return snake;
220
+ }
221
+ /**
222
+ * Try to match a call (real API name + semantic domain) to a protocol rule name.
223
+ * Returns the rule name if matched, or null if no match.
224
+ *
225
+ * Multi-strategy, in priority order:
226
+ * 1. Exact normalized match against all rule names
227
+ * 2. Substring match (call contains rule name or vice versa)
228
+ * 3. Domain-guided keyword match
229
+ */
230
+ function inferRuleName(apiName, domain, description, ruleNames, namespace, aliases, wildcardAliases) {
231
+ const normalized = normalizeName(apiName);
232
+ const lowerApi = apiName.toLowerCase();
233
+ const lowerDesc = description.toLowerCase();
234
+ // Strategy 0a: Wildcard alias match — e.g., "db.*" matches "db.getUserByOpenId"
235
+ // Wildcard aliases are stored with their prefix (without "*") as key, ruleName as value.
236
+ // They're kept in a separate map: wildcardAliases: Map<prefix, ruleName>
237
+ for (const [prefix, ruleName] of wildcardAliases) {
238
+ if (lowerApi.startsWith(prefix))
239
+ return ruleName;
240
+ }
241
+ // Strategy 0b: Alias exact match (highest priority after wildcard)
242
+ // Try full API name, normalized form, and function-only form
243
+ const aliasKeys = [lowerApi, normalized];
244
+ const dotIdx = lowerApi.lastIndexOf(".");
245
+ if (dotIdx >= 0) {
246
+ aliasKeys.push(lowerApi.slice(dotIdx + 1));
247
+ }
248
+ for (const key of aliasKeys) {
249
+ const ruleName = aliases.get(key);
250
+ if (ruleName)
251
+ return ruleName;
252
+ }
253
+ // Strategy 1: Exact normalized match against rule names
254
+ for (const ruleName of ruleNames) {
255
+ if (normalized === ruleName || lowerApi === ruleName) {
256
+ return ruleName;
257
+ }
258
+ }
259
+ // Strategy 2: Word-segment match — only match on complete word boundaries.
260
+ // Split both the rule name and normalized call name into word segments
261
+ // (separated by _). Match if every word in the rule name appears as a
262
+ // word segment in the normalized call name. This prevents "status" from
263
+ // matching "poll_status" via raw substring, since "status" must appear
264
+ // as a complete _-delimited segment.
265
+ for (const ruleName of ruleNames) {
266
+ const ruleWords = ruleName.split("_");
267
+ const callWords = normalized.split("_");
268
+ // Rule must have at least 2 words for this strategy (single-word rules
269
+ // are too prone to false positives via substring)
270
+ if (ruleWords.length < 2)
271
+ continue;
272
+ // Every rule word must appear as a complete call word segment
273
+ const allWordsMatch = ruleWords.every((rw) => callWords.includes(rw));
274
+ if (allWordsMatch) {
275
+ return ruleName;
276
+ }
277
+ }
278
+ // Strategy 3: Domain-guided keyword match
279
+ const hints = NAMESPACE_RULE_HINTS[namespace];
280
+ if (hints) {
281
+ for (const hint of hints) {
282
+ const kw = hint.keyword.toLowerCase();
283
+ // Check call name, normalized name, and semantic description
284
+ if (lowerApi.includes(kw) || normalized.includes(kw) || lowerDesc.includes(kw)) {
285
+ return hint.ruleName;
286
+ }
287
+ }
288
+ }
289
+ return null;
290
+ }
291
+ // ═══════════════════════════════════════════════════════════════
292
+ // SSG Validation
293
+ // ═══════════════════════════════════════════════════════════════
294
+ /**
295
+ * Validate a sequence of semantic steps against the SSG state machine.
296
+ *
297
+ * For each call, attempts to match it to a protocol rule. If matched,
298
+ * validates the state transition. Tracks state per-namespace.
299
+ *
300
+ * @param steps — semantic steps from the mapper
301
+ * @param rules — Map<functionName, StateAnnotation> from protocols.json
302
+ * @param namespaceInitialStates — initial states per namespace
303
+ * @param file — source file path (for violation reporting)
304
+ * @returns SSG validation result with violations and trace
305
+ */
306
+ function validateSequenceWithSSG(steps, rules, namespaceInitialStates, file, aliasIndex, wildcardAliases) {
307
+ const allRuleNames = Array.from(rules.keys());
308
+ const aliases = aliasIndex || new Map();
309
+ const wildcards = wildcardAliases || new Map();
310
+ // Build namespace→initialState map
311
+ const nsInitMap = new Map();
312
+ for (const [ns, state] of Object.entries(namespaceInitialStates)) {
313
+ nsInitMap.set(ns, state);
314
+ }
315
+ if (!nsInitMap.has("_global"))
316
+ nsInitMap.set("_global", "INIT");
317
+ if (!nsInitMap.has("stateless"))
318
+ nsInitMap.set("stateless", "IDLE");
319
+ // Per-namespace validation contexts
320
+ const contexts = new Map(); // namespace → ValidationContext
321
+ const ensureContext = (ns) => {
322
+ if (!contexts.has(ns)) {
323
+ const initState = nsInitMap.get(ns) || "IDLE";
324
+ contexts.set(ns, {
325
+ ledger: [],
326
+ currentState: { [ns]: [initState] },
327
+ });
328
+ }
329
+ return contexts.get(ns);
330
+ };
331
+ const trace = [];
332
+ const violations = [];
333
+ let matchedCalls = 0;
334
+ let validatedCalls = 0;
335
+ let violatedCalls = 0;
336
+ // Pre-compute rule hashes for integrity
337
+ const ruleHash = (() => {
338
+ try {
339
+ return (0, ssg_validator_1.hashRules)(rules);
340
+ }
341
+ catch {
342
+ return undefined;
343
+ }
344
+ })();
345
+ for (let i = 0; i < steps.length; i++) {
346
+ const step = steps[i];
347
+ const namespace = DOMAIN_TO_NAMESPACE[step.domain] || "stateless";
348
+ // Try to match call to a protocol rule FIRST (before namespace check).
349
+ // Even if the semantic domain maps to "stateless", an alias may still
350
+ // map this call to a specific protocol rule with its own namespace.
351
+ // Priority: wildcard alias → exact alias → name match → word match → keyword
352
+ const ruleName = inferRuleName(step.api, step.domain, step.description, allRuleNames, namespace, aliases, wildcards);
353
+ // Use matched rule's namespace, falling back to domain-inferred namespace
354
+ const effectiveNamespace = ruleName
355
+ ? (rules.get(ruleName)?.namespace || namespace)
356
+ : namespace;
357
+ // Skip stateless — no state machine validation needed (unless alias matched)
358
+ if (effectiveNamespace === "stateless" && !ruleName) {
359
+ trace.push({
360
+ call: step.api,
361
+ namespace: effectiveNamespace,
362
+ matchedRule: null,
363
+ valid: true,
364
+ });
365
+ continue;
366
+ }
367
+ if (!ruleName) {
368
+ trace.push({
369
+ call: step.api,
370
+ namespace,
371
+ matchedRule: null,
372
+ valid: true, // unmatched calls pass through
373
+ });
374
+ continue;
375
+ }
376
+ matchedCalls++;
377
+ // Use the matched rule's actual namespace, not the domain-inferred one.
378
+ const ruleNamespace = effectiveNamespace;
379
+ // Validate transition
380
+ try {
381
+ const ctx = ensureContext(ruleNamespace);
382
+ const result = (0, ssg_validator_1.validateTransition)(ctx, ruleName, i, rules, nsInitMap, ruleHash);
383
+ validatedCalls++;
384
+ if (result.valid) {
385
+ // Advance state
386
+ ctx.ledger.push(result.transition);
387
+ ctx.currentState = result.transition.statesAfter;
388
+ }
389
+ else {
390
+ violatedCalls++;
391
+ const rejection = result.rejection;
392
+ // Compute BFS fix path
393
+ let fixPath = [];
394
+ try {
395
+ fixPath = (0, ssg_validator_1.findFixPathStatic)(rules, ruleNamespace, rejection.currentState, rejection.requiredState);
396
+ }
397
+ catch {
398
+ fixPath = rejection.missingFunctions || [];
399
+ }
400
+ violations.push({
401
+ callName: step.api,
402
+ namespace: ruleNamespace,
403
+ currentState: rejection.currentState,
404
+ requiredState: rejection.requiredState,
405
+ fixPath,
406
+ matchedRule: ruleName,
407
+ explanation: `SSG state violation: "${step.api}" (mapped to "${ruleName}") ` +
408
+ `requires states [${rejection.requiredState.join(", ")}] ` +
409
+ `but current ${ruleNamespace} state is [${rejection.currentState.join(", ")}]. ` +
410
+ (fixPath.length > 0
411
+ ? `Fix path: ${fixPath.join(" → ")}`
412
+ : `No fix path found — protocol gap.`),
413
+ });
414
+ // Still advance state on rejection (best-effort: apply transition anyway
415
+ // so subsequent calls can be validated)
416
+ ctx.ledger.push(result.transition);
417
+ }
418
+ }
419
+ catch {
420
+ // Validation threw — record as unmatched (graceful degradation)
421
+ trace.push({
422
+ call: step.api,
423
+ namespace,
424
+ matchedRule: ruleName,
425
+ valid: false,
426
+ rejection: {
427
+ blocked: ruleName,
428
+ currentState: [],
429
+ requiredState: [],
430
+ missingFunctions: [],
431
+ fixPath: [],
432
+ namespace,
433
+ },
434
+ });
435
+ }
436
+ trace.push({
437
+ call: step.api,
438
+ namespace,
439
+ matchedRule: ruleName,
440
+ valid: true,
441
+ });
442
+ }
443
+ return {
444
+ passed: violations.length === 0,
445
+ trace,
446
+ violations,
447
+ stats: {
448
+ totalCalls: steps.length,
449
+ matchedCalls,
450
+ unmatchedCalls: steps.length - matchedCalls,
451
+ validatedCalls,
452
+ violatedCalls,
453
+ },
454
+ };
455
+ }
456
+ /**
457
+ * Convert SSG violations to TrustViolation format for the trust engine.
458
+ */
459
+ function ssgViolationsToTrustViolations(ssgResult, file, funcName) {
460
+ return ssgResult.violations.map((v) => ({
461
+ severity: "medium",
462
+ rule_id: `SSG_${v.namespace.toUpperCase()}_STATE_VIOLATION`,
463
+ file,
464
+ function: funcName,
465
+ message: v.explanation,
466
+ evidence: `Call: ${v.callName} | Rule: ${v.matchedRule || "unknown"} | ` +
467
+ `Required: [${v.requiredState.join(", ")}] | ` +
468
+ `Current: [${v.currentState.join(", ")}]`,
469
+ why: `Protocol state machine violation in namespace "${v.namespace}": ` +
470
+ `function "${v.callName}" cannot be called in current state ` +
471
+ `[${v.currentState.join(", ")}]. Required pre-states: [${v.requiredState.join(", ")}].`,
472
+ fix: v.fixPath.length > 0
473
+ ? `Insert before the violating call: ${v.fixPath.join(" → ")}`
474
+ : `Review protocol documentation for namespace "${v.namespace}" to understand required state transitions.`,
475
+ policy_ref: `protocol-safety.ssg.${v.namespace}`,
476
+ }));
477
+ }
478
+ /**
479
+ * Load protocol rules from protocols.json.
480
+ * Returns { rules, namespaceInitialStates } or null if unavailable.
481
+ */
482
+ /**
483
+ * Load project-level aliases from .progmune_aliases.json.
484
+ *
485
+ * Format: { "aliases": { "createSessionToken": "create_user_session", ... } }
486
+ *
487
+ * Project aliases are supplemental — they never override built-in global aliases.
488
+ * Each alias is validated: the target rule must exist in the rule set.
489
+ *
490
+ * @returns { aliases, warnings } or null if no config file exists
491
+ */
492
+ function loadProjectAliases(projectPath, ruleNames) {
493
+ try {
494
+ const aliasPath = path.join(projectPath, ".progmune_aliases.json");
495
+ if (!fs.existsSync(aliasPath))
496
+ return null;
497
+ const raw = JSON.parse(fs.readFileSync(aliasPath, "utf-8"));
498
+ if (!raw.aliases || typeof raw.aliases !== "object") {
499
+ return { aliases: {}, warnings: [".progmune_aliases.json: missing or invalid 'aliases' key"] };
500
+ }
501
+ const aliases = {};
502
+ const warnings = [];
503
+ for (const [callName, ruleName] of Object.entries(raw.aliases)) {
504
+ if (typeof ruleName !== "string") {
505
+ warnings.push(`.progmune_aliases.json: alias "${callName}" value must be a string, skipping`);
506
+ continue;
507
+ }
508
+ const normalizedCall = callName.toLowerCase().trim();
509
+ const normalizedRule = ruleName.trim();
510
+ if (!normalizedCall || !normalizedRule)
511
+ continue;
512
+ // Validate: target rule must exist
513
+ if (!ruleNames.has(normalizedRule)) {
514
+ warnings.push(`.progmune_aliases.json: "${callName}" → "${ruleName}" — ` +
515
+ `rule "${ruleName}" not found in protocol rules, skipping`);
516
+ continue;
517
+ }
518
+ aliases[normalizedCall] = normalizedRule;
519
+ }
520
+ return { aliases, warnings };
521
+ }
522
+ catch (e) {
523
+ return { aliases: {}, warnings: [`Failed to load .progmune_aliases.json: ${e.message}`] };
524
+ }
525
+ }
526
+ function loadProtocolRules(projectPath) {
527
+ try {
528
+ // Resolve protocols.json using multiple strategies:
529
+ // 1. Project-local (if projectPath provided)
530
+ // 2. Relative to cwd (works in vitest/dev)
531
+ // 3. Relative to this source file (works with __dirname in CJS)
532
+ const searchPaths = [];
533
+ if (projectPath) {
534
+ searchPaths.push(path.join(projectPath, "protocols.json"), path.join(projectPath, ".progmune_protocols.json"));
535
+ }
536
+ // cwd-based resolution (most reliable cross-environment)
537
+ searchPaths.push(path.resolve(process.cwd(), "protocols.json"), path.resolve(process.cwd(), "..", "protocols.json"), path.resolve(process.cwd(), "progmune-runtime", "protocols.json"));
538
+ // __dirname-based (works in CJS context)
539
+ try {
540
+ searchPaths.push(path.resolve(__dirname, "../../protocols.json"));
541
+ searchPaths.push(path.resolve(__dirname, "../protocols.json"));
542
+ }
543
+ catch {
544
+ // __dirname not available (ESM context)
545
+ }
546
+ let protoDef = null;
547
+ for (const p of searchPaths) {
548
+ try {
549
+ if (fs.existsSync(p)) {
550
+ const parsed = JSON.parse(fs.readFileSync(p, "utf-8"));
551
+ // Only accept if it has the "rules" structure (some project-local
552
+ // .progmune_protocols.json files have a different format)
553
+ if (parsed.rules && typeof parsed.rules === "object") {
554
+ protoDef = parsed;
555
+ break;
556
+ }
557
+ // Otherwise continue searching — this file exists but isn't a rules definition
558
+ }
559
+ }
560
+ catch {
561
+ continue;
562
+ }
563
+ }
564
+ if (!protoDef || !protoDef.rules)
565
+ return null;
566
+ const protocols = (0, ssg_validator_1.parseProtocolsFromJSON)(protoDef);
567
+ const rules = new Map();
568
+ const aliasIndex = new Map();
569
+ const wildcardAliases = new Map();
570
+ for (const p of protocols) {
571
+ rules.set(p.function, p.protocol);
572
+ // Build alias → ruleName reverse index
573
+ if (p.protocol.aliases) {
574
+ for (const alias of p.protocol.aliases) {
575
+ const normalized = alias.toLowerCase().trim();
576
+ if (!normalized)
577
+ continue;
578
+ // Wildcard aliases end with ".*" — match any function with that prefix
579
+ if (normalized.endsWith(".*")) {
580
+ const prefix = normalized.slice(0, -2); // remove ".*"
581
+ if (prefix && !wildcardAliases.has(prefix)) {
582
+ wildcardAliases.set(prefix, p.function);
583
+ }
584
+ }
585
+ else {
586
+ // First alias wins (no overwrite — prevents ambiguous mappings)
587
+ if (!aliasIndex.has(normalized)) {
588
+ aliasIndex.set(normalized, p.function);
589
+ }
590
+ }
591
+ }
592
+ }
593
+ }
594
+ const nsInit = {};
595
+ if (protoDef.namespaceInitialStates) {
596
+ Object.assign(nsInit, protoDef.namespaceInitialStates);
597
+ }
598
+ // Ensure essential namespaces have defaults
599
+ if (!nsInit._global)
600
+ nsInit._global = "INIT";
601
+ if (!nsInit.stateless)
602
+ nsInit.stateless = "IDLE";
603
+ // ── Load project-level aliases (supplemental, never override global) ──
604
+ let projectAliases;
605
+ let aliasWarnings;
606
+ if (projectPath) {
607
+ const ruleNameSet = new Set(rules.keys());
608
+ const pa = loadProjectAliases(projectPath, ruleNameSet);
609
+ if (pa) {
610
+ projectAliases = pa.aliases;
611
+ aliasWarnings = pa.warnings;
612
+ // Merge into exact-match alias index (project aliases DON'T override global)
613
+ for (const [callName, ruleName] of Object.entries(pa.aliases)) {
614
+ if (!aliasIndex.has(callName)) {
615
+ aliasIndex.set(callName, ruleName);
616
+ }
617
+ }
618
+ }
619
+ }
620
+ return { rules, namespaceInitialStates: nsInit, aliasIndex, wildcardAliases, projectAliases, aliasWarnings };
621
+ }
622
+ catch {
623
+ return null;
624
+ }
625
+ }
626
+ /**
627
+ * Generate a human-readable summary of SSG validation coverage.
628
+ */
629
+ function summarizeSSGCoverage(results) {
630
+ const totalCalls = results.reduce((s, r) => s + r.stats.totalCalls, 0);
631
+ const totalMatched = results.reduce((s, r) => s + r.stats.matchedCalls, 0);
632
+ const totalViolations = results.reduce((s, r) => s + r.violations.length, 0);
633
+ if (totalCalls === 0)
634
+ return "No calls validated by SSG state machine.";
635
+ const matchRate = ((totalMatched / totalCalls) * 100).toFixed(1);
636
+ return [
637
+ `SSG State Machine: ${totalCalls} calls scanned, `,
638
+ `${totalMatched} matched (${matchRate}%), `,
639
+ `${totalViolations} state violation(s) found.`,
640
+ ].join("");
641
+ }