progmune-runtime 2.1.6 → 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.
- package/README.md +108 -468
- package/dist/ablation-study.js +144 -0
- package/dist/ablation-study.test.js +18 -0
- package/dist/action-runtime.js +3 -1
- package/dist/active-learning.js +211 -0
- package/dist/analytics.js +139 -0
- package/dist/asset-factory.js +309 -0
- package/dist/asset-growth.js +244 -0
- package/dist/asset-promotion.js +382 -0
- package/dist/asset-quality.js +550 -0
- package/dist/audit/business-translator.js +285 -0
- package/dist/audit/cli.js +66 -0
- package/dist/audit/formatters/html.js +379 -0
- package/dist/audit/formatters/json.js +11 -0
- package/dist/audit/formatters/markdown.js +192 -0
- package/dist/audit/formatters/terminal.js +189 -0
- package/dist/audit/index.js +25 -0
- package/dist/audit/report-builder.js +318 -0
- package/dist/audit/types.js +8 -0
- package/dist/audit.js +3 -3
- package/dist/auto-benchmark-generator.js +137 -0
- package/dist/auto-benchmark-generator.test.js +45 -0
- package/dist/auto-protocol-synthesizer.js +362 -0
- package/dist/auto-protocol-synthesizer.test.js +82 -0
- package/dist/autonomous-patch.js +175 -0
- package/dist/autonomous-patch.test.js +128 -0
- package/dist/badge/badge-server.js +98 -0
- package/dist/behavior-miner.js +442 -0
- package/dist/belief-layer.js +475 -0
- package/dist/benchmark-count.js +5 -0
- package/dist/benchmark-generator.js +211 -0
- package/dist/benchmark-harness.js +201 -0
- package/dist/benchmark-pass-rate.js +7 -0
- package/dist/benchmark-report.js +8 -3
- package/dist/benchmark-save.js +14 -1
- package/dist/bootstrap-validation.js +197 -0
- package/dist/bootstrap-validation.test.js +51 -0
- package/dist/branch-ledger.js +1 -1
- package/dist/capability-gap.js +130 -0
- package/dist/certify-html.js +351 -0
- package/dist/certify.js +326 -0
- package/dist/check.js +4 -4
- package/dist/compliance-miner.js +447 -0
- package/dist/continuous-benchmark.js +194 -0
- package/dist/continuous-benchmark.test.js +116 -0
- package/dist/corpus-stats.js +173 -0
- package/dist/counterfactual-engine.js +288 -0
- package/dist/coverage-dashboard.js +109 -0
- package/dist/coverage-system.test.js +205 -0
- package/dist/cross-repo-precision.js +352 -0
- package/dist/cve-benchmark.js +180 -0
- package/dist/cve-benchmark.test.js +28 -0
- package/dist/cve-collector.js +73 -0
- package/dist/data-quality.js +141 -0
- package/dist/decision-engine.js +388 -0
- package/dist/derive-metadata.js +250 -0
- package/dist/difficulty-active.test.js +198 -0
- package/dist/difficulty-map.js +244 -0
- package/dist/discovery-analytics.js +125 -0
- package/dist/discovery-model.js +149 -0
- package/dist/discovery-optimize.test.js +199 -0
- package/dist/discovery-trace.js +276 -0
- package/dist/discovery-trace.test.js +97 -0
- package/dist/emitter.js +83 -1
- package/dist/enterprise-dashboard.js +405 -0
- package/dist/eval-hardening.js +297 -0
- package/dist/eval-hardening.test.js +85 -0
- package/dist/evaluation-campaign.js +359 -0
- package/dist/evaluation-campaign.test.js +181 -0
- package/dist/evidence-growth.js +143 -0
- package/dist/evidence-repository.js +209 -0
- package/dist/evidence-system.js +441 -0
- package/dist/execute.js +15 -7
- package/dist/experimental/software-physics.js +291 -0
- package/dist/experimental/state-inference.js +516 -0
- package/dist/experimental/unsupervised-physics.js +230 -0
- package/dist/extract-ir-python.js +54 -7
- package/dist/extract-ir.js +376 -12
- package/dist/failure-collector.js +2 -2
- package/dist/failure-corpus.js +322 -9
- package/dist/feedback.js +16 -5
- package/dist/feedback.test.js +49 -0
- package/dist/file-lock.js +1 -1
- package/dist/flywheel-batch.js +292 -0
- package/dist/frameworks/express-cli.js +237 -0
- package/dist/frameworks/express-detector.js +445 -0
- package/dist/frameworks/express-detector.test.js +206 -0
- package/dist/frameworks/index.js +30 -0
- package/dist/frameworks/nestjs-detector.js +302 -0
- package/dist/frameworks/trpc-detector.js +161 -0
- package/dist/frameworks/version-awareness.js +179 -0
- package/dist/function-synonyms.js +164 -0
- package/dist/function-synonyms.test.js +68 -0
- package/dist/generalization.test.js +352 -0
- package/dist/goal-annotator.js +113 -0
- package/dist/goal-planner.js +563 -0
- package/dist/gold-cve.js +164 -0
- package/dist/gold-cve.test.js +104 -0
- package/dist/gold-quality.js +206 -0
- package/dist/gold-tiers.js +241 -0
- package/dist/governance-dashboard.js +327 -0
- package/dist/graph-viz.js +240 -0
- package/dist/guided-frontier.js +195 -0
- package/dist/hierarchical-planner.js +148 -0
- package/dist/identifier-parser.js +260 -0
- package/dist/immune-metrics.js +93 -0
- package/dist/immune-receiver.js +158 -0
- package/dist/immune-reporter.js +1 -1
- package/dist/improvement-orchestrator.js +206 -0
- package/dist/inject-p0-vocabulary.js +300 -0
- package/dist/intent-parser.js +218 -0
- package/dist/invariant-algebra.js +476 -0
- package/dist/invariant-calculus.js +533 -0
- package/dist/ir-utils.js +70 -0
- package/dist/ir-utils.test.js +50 -0
- package/dist/knowledge-api.js +312 -0
- package/dist/knowledge-evolution.js +452 -0
- package/dist/knowledge-explorer.js +506 -0
- package/dist/knowledge-flywheel.js +274 -0
- package/dist/knowledge-governance.js +338 -0
- package/dist/knowledge-governance.test.js +150 -0
- package/dist/knowledge-graph.js +181 -0
- package/dist/knowledge-guided-synth.js +246 -0
- package/dist/knowledge-loop.test.js +77 -0
- package/dist/knowledge-object.js +316 -0
- package/dist/knowledge-package.js +98 -0
- package/dist/kpi-dashboard.js +561 -0
- package/dist/l3-cross-function.js +280 -0
- package/dist/learning-ranker.js +148 -0
- package/dist/learning-ranker.test.js +291 -0
- package/dist/ledger/accountability.js +322 -0
- package/dist/ledger/chain-builder.js +185 -0
- package/dist/ledger/cli.js +222 -0
- package/dist/ledger/index.js +13 -0
- package/dist/ledger/signatures.js +193 -0
- package/dist/ledger/types.js +9 -0
- package/dist/llm.js +74 -3
- package/dist/load-benchmarks.js +8 -3
- package/dist/logger.js +66 -0
- package/dist/logger.test.js +37 -0
- package/dist/logistic-reward.js +339 -0
- package/dist/logistic-reward.test.js +180 -0
- package/dist/macro-graph.js +193 -0
- package/dist/macro-repair.js +183 -0
- package/dist/mcp-server.mjs +1202 -483
- package/dist/memory-layer.js +42 -5
- package/dist/multi-repo-precision.js +422 -0
- package/dist/name-free-protocol.js +425 -0
- package/dist/name-free-protocol.test.js +170 -0
- package/dist/name-scrambling.js +138 -0
- package/dist/name-scrambling.test.js +16 -0
- package/dist/p3-observability.test.js +281 -0
- package/dist/p5-orchestrator.test.js +225 -0
- package/dist/pairwise-preference.js +294 -0
- package/dist/pairwise-preference.test.js +140 -0
- package/dist/planner-constraints.js +104 -0
- package/dist/planner-prompts.js +155 -0
- package/dist/planner-telemetry.js +415 -0
- package/dist/planner-trace.js +214 -0
- package/dist/planner.js +162 -167
- package/dist/plsb/artifact.js +116 -0
- package/dist/plsb/cli.js +71 -0
- package/dist/plsb/index.js +19 -0
- package/dist/plsb/leaderboard.js +249 -0
- package/dist/plsb/report-md.js +156 -0
- package/dist/plsb/schema.js +179 -0
- package/dist/plsb-benchmark.js +284 -0
- package/dist/plsb-benchmark.test.js +119 -0
- package/dist/policy/cli.js +134 -0
- package/dist/policy/engine.js +333 -0
- package/dist/policy/index.js +12 -0
- package/dist/policy/types.js +59 -0
- package/dist/policy-miner.js +505 -0
- package/dist/precision-analyze.js +229 -0
- package/dist/precision-benchmark.js +147 -0
- package/dist/precision-label-c.js +134 -0
- package/dist/precision-label.js +193 -0
- package/dist/precision-report-c.js +149 -0
- package/dist/precision-report.js +246 -0
- package/dist/progmune-status.js +108 -0
- package/dist/proof-engine.js +479 -0
- package/dist/proof-provenance.js +315 -0
- package/dist/protocol-coverage.js +294 -0
- package/dist/protocol-detector.js +1189 -0
- package/dist/protocol-embedding-expanded.js +297 -0
- package/dist/protocol-embedding-expanded.test.js +97 -0
- package/dist/protocol-embedding.js +195 -0
- package/dist/protocol-embedding.test.js +82 -0
- package/dist/protocol-extractor-v2.js +354 -0
- package/dist/protocol-extractor-v2.test.js +140 -0
- package/dist/protocol-extractor.js +310 -0
- package/dist/protocol-extractor.test.js +113 -0
- package/dist/protocol-foundation.js +322 -0
- package/dist/protocol-foundation.test.js +163 -0
- package/dist/protocol-frontier.js +243 -0
- package/dist/protocol-frontier.test.js +92 -0
- package/dist/protocol-gap-analyzer.js +228 -0
- package/dist/protocol-gap-analyzer.test.js +49 -0
- package/dist/protocol-invariants.js +276 -0
- package/dist/protocol-invariants.test.js +111 -0
- package/dist/protocol-knowledge.js +464 -0
- package/dist/protocol-miner.js +343 -0
- package/dist/protocol-mining.js +207 -0
- package/dist/protocol-mining.test.js +37 -0
- package/dist/protocol-registry.js +1 -1
- package/dist/protocol-security-benchmark.js +222 -0
- package/dist/protocol-vulnerability.js +257 -0
- package/dist/protocol-vulnerability.test.js +60 -0
- package/dist/python-benchmark.js +120 -0
- package/dist/python-emitter.js +163 -45
- package/dist/python-protocol-extractor.js +187 -0
- package/dist/python-protocol-extractor.test.js +116 -0
- package/dist/realworld-benchmark.js +646 -0
- package/dist/realworld-benchmark.test.js +36 -0
- package/dist/repair-arch.test.js +411 -0
- package/dist/repair-evolution.test.js +454 -0
- package/dist/repair-executor.js +719 -0
- package/dist/repair-proposal.js +4 -4
- package/dist/repair-ranker.js +141 -0
- package/dist/repair-strategies.js +419 -0
- package/dist/repair-taxonomy.js +234 -0
- package/dist/repair-types.js +12 -0
- package/dist/repo-evaluator.js +250 -0
- package/dist/repo-evaluator.test.js +128 -0
- package/dist/resource-abstraction.js +242 -0
- package/dist/resource-detector.js +211 -0
- package/dist/result.test.js +43 -0
- package/dist/reward-system.js +411 -0
- package/dist/reward-system.test.js +175 -0
- package/dist/risk-model.js +215 -0
- package/dist/rule-miner.js +234 -7
- package/dist/rule-specificity.js +254 -0
- package/dist/runtime-types.js +27 -0
- package/dist/scaffold.js +208 -0
- package/dist/scale-collector.test.js +101 -0
- package/dist/scale-trajectory-collector.js +128 -0
- package/dist/sdk.js +250 -0
- package/dist/search-planner.js +4 -41
- package/dist/semantic-snapshot.js +1 -1
- package/dist/semantic-topology.js +121 -0
- package/dist/semantic-trace.js +310 -317
- package/dist/sequence-extractor.js +343 -0
- package/dist/skill-library.js +245 -0
- package/dist/skill-planner.test.js +189 -0
- package/dist/software-physics.js +291 -0
- package/dist/software-physics.test.js +81 -0
- package/dist/ssg-precision.js +478 -0
- package/dist/ssg-validator.js +71 -21
- package/dist/state-inference-doubleblind.test.js +160 -0
- package/dist/state-inference.js +516 -0
- package/dist/state-inference.test.js +115 -0
- package/dist/state-machine-fingerprint.js +345 -0
- package/dist/state-machine-fingerprint.test.js +120 -0
- package/dist/state-miner.js +386 -0
- package/dist/state-name-inference.js +213 -0
- package/dist/state-name-inference.test.js +69 -0
- package/dist/strategy-planner.js +262 -96
- package/dist/strategy-planner.test.js +135 -0
- package/dist/telemetry-analytics.test.js +402 -0
- package/dist/terminal-format.js +68 -0
- package/dist/terminal-format.test.js +83 -0
- package/dist/topology-factory.js +196 -0
- package/dist/topology-representation.js +242 -0
- package/dist/topology-representation.test.js +27 -0
- package/dist/trajectory-augmentation.js +254 -0
- package/dist/trajectory-augmentation.test.js +63 -0
- package/dist/trajectory-corpus.js +440 -0
- package/dist/trajectory-corpus.test.js +32 -0
- package/dist/trajectory-feedback.test.js +116 -0
- package/dist/transition-synthesizer.js +286 -0
- package/dist/transition-synthesizer.test.js +123 -0
- package/dist/trust/api-semantic-mapper.js +809 -0
- package/dist/trust/call-graph-propagator.js +225 -0
- package/dist/trust/cli.js +122 -0
- package/dist/trust/compliance-scorer.js +283 -0
- package/dist/trust/confidence-calculator.js +261 -0
- package/dist/trust/engine.js +1145 -0
- package/dist/trust/explainability.js +85 -0
- package/dist/trust/formatters/ci.js +42 -0
- package/dist/trust/formatters/json.js +11 -0
- package/dist/trust/formatters/terminal.js +152 -0
- package/dist/trust/index.js +39 -0
- package/dist/trust/phase1-verify.js +171 -0
- package/dist/trust/protocol-domain-validator.js +697 -0
- package/dist/trust/score-calculator.js +282 -0
- package/dist/trust/ssg-bridge.js +641 -0
- package/dist/trust/ssg-bridge.test.js +269 -0
- package/dist/trust/types.js +67 -0
- package/dist/trust/violation-trace.js +335 -0
- package/dist/trust-api.js +179 -0
- package/dist/trust-calibration.js +279 -0
- package/dist/unknown-protocol-discovery.js +339 -0
- package/dist/unknown-protocol-discovery.test.js +102 -0
- package/dist/unsupervised-physics.js +230 -0
- package/dist/unsupervised-physics.test.js +95 -0
- package/dist/utils.test.js +37 -0
- package/dist/validator.js +187 -10
- package/dist/verification-intelligence.js +475 -0
- package/dist/verify-api.js +432 -0
- package/dist/vi-impact-report.js +293 -0
- package/dist/wl-fingerprint.js +162 -0
- package/dist/wl-fingerprint.test.js +130 -0
- package/dist/zeroshot-strategy.js +139 -0
- 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
- package/docs/Progmune_/351/241/271/347/233/256/345/205/250/350/247/243.html +710 -0
- package/package.json +74 -7
- package/protocols.json +1956 -50
- package/.dockerignore +0 -14
- package/.mcp.json +0 -11
- package/.progmune_allowlist +0 -50
- package/.test_report/test_report.md +0 -87
- package/Dockerfile +0 -9
- package/FAQ.md +0 -167
- package/WHITEPAPER.md +0 -540
- package/demo-project/auth.ts +0 -55
- package/demo-project/tsconfig.json +0 -8
- package/dist/acl-breakdown.js +0 -13
- package/dist/all-sessions.js +0 -11
- package/dist/antibody-stats.js +0 -11
- package/dist/branch-tree-count.js +0 -14
- package/dist/common-fixpath.js +0 -12
- package/dist/constraint-types.js +0 -12
- package/dist/exec-metrics.js +0 -11
- package/dist/failure-report.js +0 -11
- package/dist/fast-path-hits.js +0 -13
- package/dist/fingerprint-list.js +0 -15
- package/dist/gen-history-log.js +0 -13
- package/dist/heatmap-data.js +0 -11
- package/dist/recent-session.js +0 -12
- package/dist/svl-distribution.js +0 -11
- package/dist/terminal-status.js +0 -11
- package/dist/token-savings.js +0 -11
- package/dist/total-repairs.js +0 -12
- package/dist/unresolved-count.js +0 -12
- package/dist/valid-fingerprints.js +0 -13
- package/dist/verify-ledgers.js +0 -11
- package/docs/whitepaper-style.css +0 -77
- package/docs/whitepaper-v2.1.md +0 -609
- package/docs/whitepaper-v2.2.md +0 -1064
- package/docs/whitepaper-v2.2.pdf +0 -0
- package/fly.toml +0 -31
- package/public/dashboard.html +0 -119
- package/server/hub.js +0 -116
- package/test/replay-golden/sess_1780063202050_mgeld.json +0 -9
- package/test/replay-golden/sess_1780064032560_gocld.json +0 -354
- package/test/replay-golden/sess_1780064413331_s2709.json +0 -606
- package/test/replay-golden/sess_1780064792710_y3avo.json +0 -614
- package/test/replay-golden.ts +0 -84
- package/test_benchmark.js +0 -165
- package/test_comprehensive.mjs +0 -638
- package/test_concurrency.js +0 -129
- package/test_ir_robustness.js +0 -85
- package/test_semantic_contracts.js +0 -269
- package/test_ssg_stress.js +0 -156
- package/test_svl3.js +0 -58
- package/tsconfig.json +0 -17
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Resource Abstraction Layer — from words to semantics.
|
|
4
|
+
*
|
|
5
|
+
* The detector used to work like this:
|
|
6
|
+
* createPost → Post Asset → Protocol
|
|
7
|
+
*
|
|
8
|
+
* Now it works like this:
|
|
9
|
+
* create<X> → X is a MutableResource → needs Ownership + Validation + Authorization
|
|
10
|
+
*
|
|
11
|
+
* This is the generalization breakthrough:
|
|
12
|
+
* Post, Issue, Ticket, Task, Order, Artifact → ALL automatically recognized
|
|
13
|
+
*
|
|
14
|
+
* Three components:
|
|
15
|
+
* 1. Resource Classifier — identifies what kind of resource an entity is
|
|
16
|
+
* 2. Protocol Deriver — derives required protocols from resource type
|
|
17
|
+
* 3. Lexical Generalization — maps unknown entity names to known categories
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.parseResourceName = parseResourceName;
|
|
21
|
+
exports.classifyResource = classifyResource;
|
|
22
|
+
exports.abstractResource = abstractResource;
|
|
23
|
+
exports.isMutatingOperation = isMutatingOperation;
|
|
24
|
+
exports.isReadOperation = isReadOperation;
|
|
25
|
+
exports.getRequiredProtocols = getRequiredProtocols;
|
|
26
|
+
exports.testLexicalGeneralization = testLexicalGeneralization;
|
|
27
|
+
// ═══════════════════════════════════════════════════════════════
|
|
28
|
+
// Verb → Resource Kind mapping
|
|
29
|
+
// ═══════════════════════════════════════════════════════════════
|
|
30
|
+
const MUTABLE_VERBS = new Set([
|
|
31
|
+
"create", "add", "insert", "new", "make", "build",
|
|
32
|
+
"update", "edit", "modify", "change", "set", "put",
|
|
33
|
+
"delete", "remove", "destroy", "drop", "clear", "erase",
|
|
34
|
+
]);
|
|
35
|
+
const READ_VERBS = new Set([
|
|
36
|
+
"get", "fetch", "find", "query", "read", "load", "list", "search", "lookup",
|
|
37
|
+
]);
|
|
38
|
+
const AUTH_VERBS = new Set([
|
|
39
|
+
"login", "logout", "register", "signup", "signin", "authenticate", "authorize",
|
|
40
|
+
"verify", "validate", "check",
|
|
41
|
+
]);
|
|
42
|
+
// ═══════════════════════════════════════════════════════════════
|
|
43
|
+
// Entity → Resource Kind heuristics
|
|
44
|
+
// ═══════════════════════════════════════════════════════════════
|
|
45
|
+
/**
|
|
46
|
+
* Known entity suffixes that indicate resource kinds.
|
|
47
|
+
* These are generalizable — they work for ANY entity name.
|
|
48
|
+
*/
|
|
49
|
+
const RESOURCE_SUFFIX_PATTERNS = [
|
|
50
|
+
{
|
|
51
|
+
// Mutable resources: Post, Issue, Ticket, Task, Order, Product, Project, etc.
|
|
52
|
+
// These need: Ownership + Validation + Authorization + DataIntegrity
|
|
53
|
+
pattern: /^(Post|Issue|Ticket|Task|Order|Product|Project|Item|Entry|Record|Document|Article|Page|Thread|Topic|Subject|Object|Entity|Artifact|Asset|Resource|Job|Workflow|Pipeline|Deployment|Release)$/i,
|
|
54
|
+
kind: "mutable_resource",
|
|
55
|
+
protocols: ["ownership_check", "input_validation", "authorization", "data_integrity"],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
// Comments/Replies: always reference a parent resource
|
|
59
|
+
pattern: /^(Comment|Reply|Response|Note|Annotation|Feedback|Review|Remark)$/i,
|
|
60
|
+
kind: "mutable_resource",
|
|
61
|
+
protocols: ["ownership_check", "input_validation", "data_integrity", "authorization"],
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
// Credential material
|
|
65
|
+
pattern: /^(Password|Token|Key|Secret|Credential|ApiKey|AccessKey|Certificate)$/i,
|
|
66
|
+
kind: "credential",
|
|
67
|
+
protocols: ["password_hashing", "token_security", "secure_storage"],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
// File assets
|
|
71
|
+
pattern: /^(File|Attachment|Image|Photo|Video|Audio|Document|Media|Asset|Binary|Blob|Upload|Download)$/i,
|
|
72
|
+
kind: "file_asset",
|
|
73
|
+
protocols: ["file_validation", "ownership_check", "authorization"],
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
// Reference data
|
|
77
|
+
pattern: /^(Category|Tag|Label|Status|Type|State|Priority|Severity|Role|Permission|Group|Team|Organization)$/i,
|
|
78
|
+
kind: "reference_data",
|
|
79
|
+
protocols: ["authorization"],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
// Session/connection
|
|
83
|
+
pattern: /^(Session|Connection|Stream|Channel|Socket|WebSocket|SSE)$/i,
|
|
84
|
+
kind: "session",
|
|
85
|
+
protocols: ["tls_enforcement", "token_verification"],
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
// ═══════════════════════════════════════════════════════════════
|
|
89
|
+
// Core Engine
|
|
90
|
+
// ═══════════════════════════════════════════════════════════════
|
|
91
|
+
/**
|
|
92
|
+
* Parse a function name into verb + entity.
|
|
93
|
+
*
|
|
94
|
+
* Examples:
|
|
95
|
+
* createPost → { verb: "create", entity: "Post" }
|
|
96
|
+
* deleteUser → { verb: "delete", entity: "User" }
|
|
97
|
+
* addProductItem → { verb: "add", entity: "ProductItem" }
|
|
98
|
+
* getNotifications → { verb: "get", entity: "Notifications" }
|
|
99
|
+
*/
|
|
100
|
+
function parseResourceName(functionName) {
|
|
101
|
+
// Common verb prefixes
|
|
102
|
+
const verbPatterns = [
|
|
103
|
+
"create", "delete", "update", "remove", "destroy",
|
|
104
|
+
"add", "insert", "new", "make", "build",
|
|
105
|
+
"get", "fetch", "find", "query", "read", "load", "list",
|
|
106
|
+
"edit", "modify", "change", "set", "put",
|
|
107
|
+
"login", "logout", "register", "signup", "signin",
|
|
108
|
+
"verify", "validate", "check", "authorize", "authenticate",
|
|
109
|
+
"upload", "download", "send", "receive",
|
|
110
|
+
"toggle", "lock", "unlock", "enable", "disable",
|
|
111
|
+
"follow", "unfollow", "like", "unlike",
|
|
112
|
+
"hash", "generate", "sign", "encrypt", "decrypt",
|
|
113
|
+
];
|
|
114
|
+
for (const verb of verbPatterns) {
|
|
115
|
+
if (functionName.toLowerCase().startsWith(verb.toLowerCase())) {
|
|
116
|
+
const entity = functionName.slice(verb.length);
|
|
117
|
+
if (entity.length >= 2) {
|
|
118
|
+
return { verb: verb.toLowerCase(), entity };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Classify a resource by its entity name.
|
|
126
|
+
* Uses suffix patterns for known categories, heuristics for unknown ones.
|
|
127
|
+
*/
|
|
128
|
+
function classifyResource(entity) {
|
|
129
|
+
// Normalize: handle plurals, camelCase suffixes
|
|
130
|
+
const normalized = entity.replace(/s$/, ""); // Remove trailing 's' for plurals
|
|
131
|
+
// Check known patterns
|
|
132
|
+
for (const { pattern, kind, protocols } of RESOURCE_SUFFIX_PATTERNS) {
|
|
133
|
+
if (pattern.test(entity) || pattern.test(normalized)) {
|
|
134
|
+
return { kind, protocols, confidence: 0.9 };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Heuristic: if the entity starts with an uppercase letter (camelCase), it's likely a resource
|
|
138
|
+
// Unknown entities default to mutable_resource with full protocol requirements
|
|
139
|
+
if (/^[A-Z]/.test(entity)) {
|
|
140
|
+
return {
|
|
141
|
+
kind: "mutable_resource",
|
|
142
|
+
protocols: ["ownership_check", "input_validation", "authorization", "data_integrity"],
|
|
143
|
+
confidence: 0.5, // Lower confidence for unknown entities
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return { kind: "unknown", protocols: [], confidence: 0.1 };
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Full resource abstraction pipeline:
|
|
150
|
+
* functionName → verb + entity → resource kind → required protocols
|
|
151
|
+
*/
|
|
152
|
+
function abstractResource(functionName) {
|
|
153
|
+
const parsed = parseResourceName(functionName);
|
|
154
|
+
if (!parsed)
|
|
155
|
+
return null;
|
|
156
|
+
const { kind, protocols, confidence } = classifyResource(parsed.entity);
|
|
157
|
+
// Read operations on mutable resources: classify as reference_data
|
|
158
|
+
// (accessing a resource doesn't mutate it; the entity type reflects the access mode)
|
|
159
|
+
const adjustedKind = READ_VERBS.has(parsed.verb) && kind === "mutable_resource"
|
|
160
|
+
? "reference_data"
|
|
161
|
+
: kind;
|
|
162
|
+
return {
|
|
163
|
+
original: functionName,
|
|
164
|
+
verb: parsed.verb,
|
|
165
|
+
entity: parsed.entity,
|
|
166
|
+
kind: adjustedKind,
|
|
167
|
+
requiredProtocols: protocols,
|
|
168
|
+
confidence,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Check if a given function name is a mutating operation on a resource.
|
|
173
|
+
* Mutations require: Ownership + Authorization + (optionally) Validation
|
|
174
|
+
*/
|
|
175
|
+
function isMutatingOperation(functionName) {
|
|
176
|
+
const parsed = parseResourceName(functionName);
|
|
177
|
+
if (!parsed)
|
|
178
|
+
return false;
|
|
179
|
+
return MUTABLE_VERBS.has(parsed.verb);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Check if a given function name is a read operation.
|
|
183
|
+
*/
|
|
184
|
+
function isReadOperation(functionName) {
|
|
185
|
+
const parsed = parseResourceName(functionName);
|
|
186
|
+
if (!parsed)
|
|
187
|
+
return false;
|
|
188
|
+
return READ_VERBS.has(parsed.verb);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Get the protocols required for a given function.
|
|
192
|
+
* Combines verb semantics with resource classification.
|
|
193
|
+
*/
|
|
194
|
+
function getRequiredProtocols(functionName) {
|
|
195
|
+
const abs = abstractResource(functionName);
|
|
196
|
+
if (!abs)
|
|
197
|
+
return [];
|
|
198
|
+
const protocols = [...abs.requiredProtocols];
|
|
199
|
+
// Mutating operations ALWAYS need authorization
|
|
200
|
+
if (MUTABLE_VERBS.has(abs.verb) && !protocols.includes("authorization")) {
|
|
201
|
+
protocols.push("authorization");
|
|
202
|
+
}
|
|
203
|
+
// Mutating operations on mutable resources need ownership checks
|
|
204
|
+
if (MUTABLE_VERBS.has(abs.verb) && abs.kind === "mutable_resource" && !protocols.includes("ownership_check")) {
|
|
205
|
+
protocols.push("ownership_check");
|
|
206
|
+
}
|
|
207
|
+
return protocols;
|
|
208
|
+
}
|
|
209
|
+
// ═══════════════════════════════════════════════════════════════
|
|
210
|
+
// Lexical Generalization Test
|
|
211
|
+
// ═══════════════════════════════════════════════════════════════
|
|
212
|
+
/**
|
|
213
|
+
* Test whether the resource abstraction correctly identifies
|
|
214
|
+
* that DIFFERENT entity names with the SAME semantics require
|
|
215
|
+
* the SAME protocols.
|
|
216
|
+
*
|
|
217
|
+
* Known entities: Post, Comment, Task, Order, Product → should all require ownership_check
|
|
218
|
+
* Unknown entities: Ticket, Issue, Artifact, Foo → should ALSO require ownership_check
|
|
219
|
+
*/
|
|
220
|
+
function testLexicalGeneralization() {
|
|
221
|
+
const knownEntities = ["Post", "Comment", "Task", "Order", "Product", "Project", "Item"];
|
|
222
|
+
const unknownEntities = ["Ticket", "Issue", "Artifact", "Document", "Foo", "Widget", "Thing"];
|
|
223
|
+
const expectedProtocol = "ownership_check";
|
|
224
|
+
let knownCorrect = 0;
|
|
225
|
+
for (const entity of knownEntities) {
|
|
226
|
+
const { protocols } = classifyResource(entity);
|
|
227
|
+
if (protocols.includes(expectedProtocol))
|
|
228
|
+
knownCorrect++;
|
|
229
|
+
}
|
|
230
|
+
let unknownCorrect = 0;
|
|
231
|
+
for (const entity of unknownEntities) {
|
|
232
|
+
const { protocols } = classifyResource(entity);
|
|
233
|
+
if (protocols.includes(expectedProtocol))
|
|
234
|
+
unknownCorrect++;
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
knownEntities,
|
|
238
|
+
unknownEntities,
|
|
239
|
+
knownRecall: knownCorrect / knownEntities.length,
|
|
240
|
+
unknownRecall: unknownCorrect / unknownEntities.length,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Resource Lifecycle Detector
|
|
4
|
+
*
|
|
5
|
+
* Focused detection of resource acquire/release violations.
|
|
6
|
+
* Replaces noisy auto-discovered call-ordering rules with
|
|
7
|
+
* explicit acquire→release pair analysis.
|
|
8
|
+
*
|
|
9
|
+
* Strategy:
|
|
10
|
+
* 1. Identify resource-acquiring calls (malloc, open, connect, SSL_new, ...)
|
|
11
|
+
* 2. Identify resource-releasing calls (free, close, disconnect, SSL_free, ...)
|
|
12
|
+
* 3. Flag violations: acquire without corresponding release in same scope
|
|
13
|
+
* 4. Exclude "allocation-return" pattern (function allocates for caller)
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.detectResourceViolations = detectResourceViolations;
|
|
17
|
+
exports.validateResourceLifecycle = validateResourceLifecycle;
|
|
18
|
+
// ── Known resource pairs ──
|
|
19
|
+
const RESOURCE_PAIRS = [
|
|
20
|
+
// Memory
|
|
21
|
+
{ acquire: /\b(malloc|calloc|realloc|curlx_malloc|curlx_calloc|curlx_realloc|zmalloc|zcalloc|zrealloc|ngx_alloc|ngx_calloc|ngx_palloc)\b/i, release: /\b(free|curlx_free|zfree|ngx_free|ngx_pfree)\b/i, category: "memory" },
|
|
22
|
+
// File descriptors (accept removed — listening sockets stay open)
|
|
23
|
+
{ acquire: /\b(open|fopen|socket|curlx_open)\b/i, release: /\b(close|fclose|closesocket|curlx_close|shutdown)\b/i, category: "file" },
|
|
24
|
+
// SSL/TLS (expanded with CTX and bio patterns)
|
|
25
|
+
{ acquire: /\b(SSL_new|TLS_new|wolfSSL_new|gtls_new|BIO_new|ossl_new|nghttp2_session_new|SSL_CTX_new|BIO_new_mem_buf|BIO_s_file|BIO_read_filename)\b/i, release: /\b(SSL_free|TLS_free|wolfSSL_free|gtls_free|BIO_free|ossl_free|nghttp2_session_del|SSL_CTX_free|BIO_free_all)\b/i, category: "ssl" },
|
|
26
|
+
// Connections (accept removed — server socket, not per-connection resource)
|
|
27
|
+
{ acquire: /\b(connect|Curl_connect|ngx_connect|redisConnect)\b/i, release: /\b(disconnect|Curl_disconnect|ngx_disconnect|redisFree)\b/i, category: "connection" },
|
|
28
|
+
// General lifecycle — only for double-release / UAF checks, not missing-release
|
|
29
|
+
{ acquire: /\b(_init|_create|_setup|_start|_begin|_open|_alloc|_connect|init|create|setup|start|open|alloc)\b/i, release: /\b(_free|_destroy|_cleanup|_close|_stop|_end|_release|_unlock|_done|_finish|_remove|_delete|cleanup|destroy|close|free)\b/i, category: "lifecycle" },
|
|
30
|
+
// C error-handling: goto cleanup/fail pattern (synthetic calls from sequence-extractor)
|
|
31
|
+
{ acquire: /\b(goto_cleanup|goto_fail|goto_err|goto_error|goto_out)\b/i, release: /\b(_free|_destroy|_cleanup|_close|_release|free|destroy|cleanup|close)\b/i, category: "lifecycle" },
|
|
32
|
+
// Python resource pairs
|
|
33
|
+
{ acquire: /\b(sqlite3\.connect|psycopg2\.connect|create_engine|pymongo\.MongoClient|redis\.Redis|redis\.StrictRedis)\b/i, release: /\b(close|disconnect)\b/i, category: "connection" },
|
|
34
|
+
{ acquire: /\b(socket\.socket|socket\.create_connection|socket\.socketpair)\b/i, release: /\b(close)\b/i, category: "file" },
|
|
35
|
+
{ acquire: /\b(asyncio\.(Lock|Semaphore|Event|Condition)|threading\.(Lock|RLock|Semaphore|Condition))\b/i, release: /\b(release)\b/i, category: "lock" },
|
|
36
|
+
{ acquire: /\b(tempfile\.(TemporaryFile|NamedTemporaryFile|mkstemp|mkdtemp|SpooledTemporaryFile))\b/i, release: /\b(close|cleanup)\b/i, category: "file" },
|
|
37
|
+
];
|
|
38
|
+
// ── Allocation-return patterns (functions that allocate and return to caller) ──
|
|
39
|
+
const ALLOCATOR_PATTERNS = [
|
|
40
|
+
/\b(create|alloc|new|init|open|dup|clone|copy|build|make|setup|construct)\w*\b/i,
|
|
41
|
+
];
|
|
42
|
+
// ── Cleanup/destructor functions (expected to call release without acquire) ──
|
|
43
|
+
const CLEANUP_PATTERNS = [
|
|
44
|
+
/\b(remove|cleanup|destroy|free|close|delete|detach|release|shutdown|teardown|done|finish)\w*\b/i,
|
|
45
|
+
// Curl-specific cleanup functions (cross-function alloc/free patterns)
|
|
46
|
+
/\b(Curl_close|curl_multi_cleanup|Curl_detach_connection|curl_easy_cleanup|Curl_expire_clear|Curl_resolv_destroy_all|Curl_conncache_remove|Curl_ssl_close_all|Curl_req_free)\b/i,
|
|
47
|
+
];
|
|
48
|
+
function isAllocatorFunction(funcName) {
|
|
49
|
+
return ALLOCATOR_PATTERNS.some(p => p.test(funcName));
|
|
50
|
+
}
|
|
51
|
+
function isCleanupFunction(funcName) {
|
|
52
|
+
return CLEANUP_PATTERNS.some(p => p.test(funcName));
|
|
53
|
+
}
|
|
54
|
+
function detectResourceViolations(calls, enclosingFuncName) {
|
|
55
|
+
const violations = [];
|
|
56
|
+
const funcName = enclosingFuncName || "";
|
|
57
|
+
for (const pair of RESOURCE_PAIRS) {
|
|
58
|
+
const acquires = [];
|
|
59
|
+
const releases = [];
|
|
60
|
+
for (let i = 0; i < calls.length; i++) {
|
|
61
|
+
const c = calls[i];
|
|
62
|
+
if (pair.acquire.test(c) && !isAllocatorFunction(c))
|
|
63
|
+
acquires.push(i);
|
|
64
|
+
if (pair.release.test(c))
|
|
65
|
+
releases.push(i);
|
|
66
|
+
}
|
|
67
|
+
// Rule 1: more acquires than releases → potential leak
|
|
68
|
+
// Exception: allocator functions (return resource to caller)
|
|
69
|
+
// Exception: cleanup functions (release resources allocated elsewhere)
|
|
70
|
+
// Exception: lifecycle category (too broad for missing-release check)
|
|
71
|
+
if (acquires.length > releases.length && acquires.length > 0 && pair.category !== "lifecycle") {
|
|
72
|
+
if (!isAllocatorFunction(funcName) && !isCleanupFunction(funcName)) {
|
|
73
|
+
for (let i = releases.length; i < acquires.length; i++) {
|
|
74
|
+
violations.push({
|
|
75
|
+
type: "missing_release",
|
|
76
|
+
category: pair.category,
|
|
77
|
+
acquireCall: calls[acquires[i]],
|
|
78
|
+
detail: `${calls[acquires[i]]} acquired without matching ${pair.release.source} — possible resource leak`,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Rule 2: release without acquire → only flag if 2+ releases (true double-free)
|
|
84
|
+
// Single release without acquire = cross-function cleanup (normal in C code)
|
|
85
|
+
// Exception: cleanup/destructor functions always exempt
|
|
86
|
+
if (releases.length >= 2 && acquires.length === 0) {
|
|
87
|
+
if (!isCleanupFunction(funcName)) {
|
|
88
|
+
violations.push({
|
|
89
|
+
type: "double_free",
|
|
90
|
+
category: pair.category,
|
|
91
|
+
releaseCall: calls[releases[0]],
|
|
92
|
+
detail: `${calls[releases[0]]} → ${calls[releases[releases.length - 1]]}: multiple releases without any acquire — possible double-free`,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Rule 3: release before corresponding acquire (interleaved)
|
|
97
|
+
if (acquires.length > 0 && releases.length > 0) {
|
|
98
|
+
for (const ri of releases) {
|
|
99
|
+
// A release at position ri must have at least one acquire before it
|
|
100
|
+
const hasPriorAcquire = acquires.some(ai => ai < ri);
|
|
101
|
+
if (!hasPriorAcquire) {
|
|
102
|
+
violations.push({
|
|
103
|
+
type: "use_after_release",
|
|
104
|
+
category: pair.category,
|
|
105
|
+
releaseCall: calls[ri],
|
|
106
|
+
detail: `${calls[ri]} called before any ${pair.acquire.source} — possible double-free`,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return violations;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Validate a call sequence using resource lifecycle analysis.
|
|
116
|
+
* Returns valid=true if no resource violations found.
|
|
117
|
+
*/
|
|
118
|
+
function validateResourceLifecycle(calls, enclosingFuncName) {
|
|
119
|
+
const violations = detectResourceViolations(calls, enclosingFuncName);
|
|
120
|
+
const missingCount = violations.filter(v => v.type === "missing_release").length;
|
|
121
|
+
const otherCount = violations.length - missingCount;
|
|
122
|
+
let detail = "";
|
|
123
|
+
if (violations.length === 0) {
|
|
124
|
+
detail = "Resource lifecycle clean";
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
const parts = [];
|
|
128
|
+
if (missingCount > 0)
|
|
129
|
+
parts.push(`${missingCount} potential resource leak(s)`);
|
|
130
|
+
if (otherCount > 0)
|
|
131
|
+
parts.push(`${otherCount} ordering violation(s)`);
|
|
132
|
+
detail = parts.join(", ");
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
valid: violations.length === 0,
|
|
136
|
+
violations,
|
|
137
|
+
detail,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
// ═══════════════════════════════════════════════════════════════
|
|
141
|
+
// CLI — Precision Report
|
|
142
|
+
// ═══════════════════════════════════════════════════════════════
|
|
143
|
+
if (require.main === module) {
|
|
144
|
+
const fs = require("fs");
|
|
145
|
+
const path = require("path");
|
|
146
|
+
const args = process.argv.slice(2);
|
|
147
|
+
const repoPath = path.resolve(args.find(a => !a.startsWith("--")) || ".");
|
|
148
|
+
const baseDir = path.dirname(repoPath);
|
|
149
|
+
const repoName = path.basename(repoPath);
|
|
150
|
+
const seqFile = path.join(baseDir, `${repoName}-sequences.json`);
|
|
151
|
+
const labelFile = path.join(baseDir, `${repoName}-labels.json`);
|
|
152
|
+
if (!fs.existsSync(seqFile) || !fs.existsSync(labelFile)) {
|
|
153
|
+
console.error(`❌ Files not found: ${seqFile}, ${labelFile}`);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
const seqData = JSON.parse(fs.readFileSync(seqFile, "utf-8"));
|
|
157
|
+
const labelData = JSON.parse(fs.readFileSync(labelFile, "utf-8"));
|
|
158
|
+
const sequences = seqData.sequences || seqData;
|
|
159
|
+
const labels = labelData.labels || labelData;
|
|
160
|
+
let tp = 0, fp = 0, tn = 0, fn = 0;
|
|
161
|
+
const mismatches = [];
|
|
162
|
+
for (const seq of sequences) {
|
|
163
|
+
const idx = sequences.indexOf(seq);
|
|
164
|
+
const expected = labels[idx];
|
|
165
|
+
if (!expected || expected === "s" || expected === "skip")
|
|
166
|
+
continue;
|
|
167
|
+
const result = validateResourceLifecycle(seq.calls || [], seq.function);
|
|
168
|
+
const detected = result.valid ? "clean" : "violation";
|
|
169
|
+
if (expected === "clean" && detected === "clean")
|
|
170
|
+
tn++;
|
|
171
|
+
else if (expected === "violation" && detected === "violation")
|
|
172
|
+
tp++;
|
|
173
|
+
else if (expected === "clean" && detected === "violation")
|
|
174
|
+
fp++;
|
|
175
|
+
else if (expected === "violation" && detected === "clean")
|
|
176
|
+
fn++;
|
|
177
|
+
if (expected !== detected || result.violations.length > 0) {
|
|
178
|
+
mismatches.push({ idx, fn: seq.function, expected, detected, calls: (seq.calls || []).slice(0, 5), vios: result.violations.map(v => v.detail) });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const total = tp + fp + tn + fn;
|
|
182
|
+
const precision = tp + fp > 0 ? tp / (tp + fp) : 0;
|
|
183
|
+
const recall = tp + fn > 0 ? tp / (tp + fn) : 0;
|
|
184
|
+
const f1 = precision + recall > 0 ? 2 * precision * recall / (precision + recall) : 0;
|
|
185
|
+
const pct = (v) => `${(v * 100).toFixed(0)}%`;
|
|
186
|
+
const C = { reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m", green: "\x1b[32m", red: "\x1b[31m", yellow: "\x1b[33m", cyan: "\x1b[36m" };
|
|
187
|
+
const color = (v) => v >= 0.7 ? C.green : v >= 0.5 ? C.yellow : C.red;
|
|
188
|
+
console.log(`\n${C.bold}${C.cyan}╔══════════════════════════════════════════════╗${C.reset}`);
|
|
189
|
+
console.log(`${C.bold}${C.cyan}║${C.reset} ${C.bold}Resource Lifecycle Precision — ${repoName}${C.reset}${" ".repeat(Math.max(0, 19 - repoName.length))}${C.bold}${C.cyan}║${C.reset}`);
|
|
190
|
+
console.log(`${C.bold}${C.cyan}╚══════════════════════════════════════════════╝${C.reset}`);
|
|
191
|
+
console.log("");
|
|
192
|
+
console.log(` Samples: ${total}`);
|
|
193
|
+
console.log(` TP: ${C.green}${tp}${C.reset} FP: ${C.red}${fp}${C.reset} TN: ${C.green}${tn}${C.reset} FN: ${C.red}${fn}${C.reset}`);
|
|
194
|
+
console.log("");
|
|
195
|
+
console.log(` Precision: ${color(precision)}${pct(precision)}${C.reset}`);
|
|
196
|
+
console.log(` Recall: ${color(recall)}${pct(recall)}${C.reset}`);
|
|
197
|
+
console.log(` F1: ${color(f1)}${pct(f1)}${C.reset}`);
|
|
198
|
+
console.log("");
|
|
199
|
+
if (mismatches.length > 0) {
|
|
200
|
+
console.log(` ${C.yellow}Details:${C.reset}`);
|
|
201
|
+
for (const m of mismatches.slice(0, 12)) {
|
|
202
|
+
const icon = m.expected === "violation" ? `${C.red}FN${C.reset}` : `${C.yellow}FP${C.reset}`;
|
|
203
|
+
console.log(` ${icon} [${m.idx}] ${m.expected}→${m.detected} ${(m.calls || []).join(" → ")}`);
|
|
204
|
+
if (m.vios.length)
|
|
205
|
+
console.log(` ${C.dim}${m.vios[0]}${C.reset}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
console.log("");
|
|
209
|
+
const rating = f1 >= 0.7 ? `${C.green}GOOD${C.reset}` : f1 >= 0.5 ? `${C.yellow}FAIR${C.reset}` : `${C.red}NEEDS IMPROVEMENT${C.reset}`;
|
|
210
|
+
console.log(` Rating: ${rating}\n`);
|
|
211
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* Unit tests for the Result<T,E> type and factory functions.
|
|
5
|
+
*/
|
|
6
|
+
const vitest_1 = require("vitest");
|
|
7
|
+
const runtime_types_1 = require("./runtime-types");
|
|
8
|
+
(0, vitest_1.describe)("Result type", () => {
|
|
9
|
+
(0, vitest_1.it)("ok() returns { ok: true, value }", () => {
|
|
10
|
+
const r = (0, runtime_types_1.ok)(42);
|
|
11
|
+
(0, vitest_1.expect)(r.ok).toBe(true);
|
|
12
|
+
if (r.ok)
|
|
13
|
+
(0, vitest_1.expect)(r.value).toBe(42);
|
|
14
|
+
});
|
|
15
|
+
(0, vitest_1.it)("err() returns { ok: false, error }", () => {
|
|
16
|
+
const r = (0, runtime_types_1.err)("something broke");
|
|
17
|
+
(0, vitest_1.expect)(r.ok).toBe(false);
|
|
18
|
+
if (!r.ok)
|
|
19
|
+
(0, vitest_1.expect)(r.error).toBe("something broke");
|
|
20
|
+
});
|
|
21
|
+
(0, vitest_1.it)("supports discriminated union narrowing", () => {
|
|
22
|
+
function divide(a, b) {
|
|
23
|
+
if (b === 0)
|
|
24
|
+
return (0, runtime_types_1.err)("division by zero");
|
|
25
|
+
return (0, runtime_types_1.ok)(a / b);
|
|
26
|
+
}
|
|
27
|
+
const good = divide(10, 2);
|
|
28
|
+
if (good.ok)
|
|
29
|
+
(0, vitest_1.expect)(good.value).toBe(5);
|
|
30
|
+
const bad = divide(1, 0);
|
|
31
|
+
if (!bad.ok)
|
|
32
|
+
(0, vitest_1.expect)(bad.error).toBe("division by zero");
|
|
33
|
+
});
|
|
34
|
+
(0, vitest_1.it)("supports typed ValidationError", () => {
|
|
35
|
+
const e = { message: "type mismatch", code: "SVL-2", index: 3 };
|
|
36
|
+
const r = (0, runtime_types_1.err)([e]);
|
|
37
|
+
(0, vitest_1.expect)(r.ok).toBe(false);
|
|
38
|
+
if (!r.ok) {
|
|
39
|
+
(0, vitest_1.expect)(r.error[0].code).toBe("SVL-2");
|
|
40
|
+
(0, vitest_1.expect)(r.error[0].index).toBe(3);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|