optiprune 1.0.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 (57) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +49 -0
  3. package/dist/cache.d.ts +14 -0
  4. package/dist/cache.js +36 -0
  5. package/dist/cache.js.map +1 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +53 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/config-loader.d.ts +4 -0
  10. package/dist/config-loader.js +68 -0
  11. package/dist/config-loader.js.map +1 -0
  12. package/dist/coverage-tracker.d.ts +39 -0
  13. package/dist/coverage-tracker.js +61 -0
  14. package/dist/coverage-tracker.js.map +1 -0
  15. package/dist/fs-utils.d.ts +33 -0
  16. package/dist/fs-utils.js +334 -0
  17. package/dist/fs-utils.js.map +1 -0
  18. package/dist/graph.d.ts +28 -0
  19. package/dist/graph.js +331 -0
  20. package/dist/graph.js.map +1 -0
  21. package/dist/index.d.ts +3 -0
  22. package/dist/index.js +313 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/instrument.d.ts +1 -0
  25. package/dist/instrument.js +20 -0
  26. package/dist/instrument.js.map +1 -0
  27. package/dist/instrumentation-plugin.d.ts +6 -0
  28. package/dist/instrumentation-plugin.js +84 -0
  29. package/dist/instrumentation-plugin.js.map +1 -0
  30. package/dist/layer2.d.ts +6 -0
  31. package/dist/layer2.js +227 -0
  32. package/dist/layer2.js.map +1 -0
  33. package/dist/layer3.d.ts +7 -0
  34. package/dist/layer3.js +244 -0
  35. package/dist/layer3.js.map +1 -0
  36. package/dist/layer4.d.ts +7 -0
  37. package/dist/layer4.js +90 -0
  38. package/dist/layer4.js.map +1 -0
  39. package/dist/layer5.d.ts +6 -0
  40. package/dist/layer5.js +141 -0
  41. package/dist/layer5.js.map +1 -0
  42. package/dist/layer6.d.ts +25 -0
  43. package/dist/layer6.js +183 -0
  44. package/dist/layer6.js.map +1 -0
  45. package/dist/parser.d.ts +24 -0
  46. package/dist/parser.js +481 -0
  47. package/dist/parser.js.map +1 -0
  48. package/dist/reporters.d.ts +3 -0
  49. package/dist/reporters.js +75 -0
  50. package/dist/reporters.js.map +1 -0
  51. package/dist/types.d.ts +205 -0
  52. package/dist/types.js +11 -0
  53. package/dist/types.js.map +1 -0
  54. package/dist/workspace.d.ts +6 -0
  55. package/dist/workspace.js +114 -0
  56. package/dist/workspace.js.map +1 -0
  57. package/package.json +112 -0
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2026 DreamLongYT
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,49 @@
1
+ ![Optiprune Logo](docs/public/logo.svg)
2
+ ![NPM Version](https://img.shields.io/npm/v/optiprune)
3
+ ![GitHub License](https://img.shields.io/github/license/DreamLongYT/optiprune)
4
+
5
+
6
+ # Optiprune
7
+
8
+ **Resilient static dead-code analyzer for TypeScript and JavaScript workspaces.**
9
+
10
+ ---
11
+
12
+ **Optiprune** is a multi-layered static analyzer designed to prune unreachable files, dead exports, untangled monorepo imports, and unused dependencies with surgical precision. Built for cross-platform reliability (Windows/Linux) and fast CI pipelines.
13
+
14
+ ## Key Features
15
+
16
+ * **Staged 6-Layer Engine:** From AST entry point resolution to SMT constraint solving, isolated V8 execution, and unused package sweeping.
17
+ * **Schema & Contract Preserver (Layer 5):** Introspects Zod schemas (`z.*`), Class declarations (`*Schema`), Prisma models, decorators, and OpenAPI resolvers to prevent false positives on public interfaces.
18
+ * **Cross-Platform Path Normalization:** Flawless resolution across Windows drive letters, backslashes, and POSIX path specs.
19
+ * **Circular Dependency Resilient:** Iterative worklist analysis using Strongly Connected Components (SCCs) to resolve cyclical import paths accurately.
20
+ * **Monorepo & Package Sweeper (Layer 6):** Identifies unused root and package-level `node_modules` dependencies alongside dead code.
21
+ * **VitePress Powered Docs:** Comprehensive architecture breakdown and guide available in the `/docs` suite.
22
+
23
+ ---
24
+
25
+ ## Architecture Overview
26
+
27
+ Optiprune runs code through a 6-stage analysis pipeline:
28
+
29
+ | Layer | Stage | Focus |
30
+ | :--- | :--- | :--- |
31
+ | **Layer 1** | Entry Point Graph | Static import/export dependency graph & entry point discovery |
32
+ | **Layer 2** | Monorepo & Workspaces | Cross-package references and boundary resolution |
33
+ | **Layer 3** | Dynamic Tracing & SMT | Path unreachability via Z3 constraint solving |
34
+ | **Layer 4** | Isolated V8 Execution | Candidate validation inside sandboxed `isolated-vm` Isolates |
35
+ | **Layer 5** | AST Contract & Schema | Preserves Zod/Schema classes, decorators, and API boundaries |
36
+ | **Layer 6** | Unused Package Sweeper | Flags unused external dependencies in `package.json` |
37
+
38
+ ---
39
+
40
+ ## Installation
41
+
42
+ Install Optiprune as a dev dependency via pnpm, npm, or yarn:
43
+
44
+ ```bash
45
+ pnpm add -D optiprune
46
+ # or
47
+ npm install --save-dev optiprune
48
+ # or
49
+ yarn add -D optiprune
@@ -0,0 +1,14 @@
1
+ import type { ModuleRecord } from "./types.js";
2
+ export interface CacheEntry {
3
+ hash: string;
4
+ moduleRecord: ModuleRecord;
5
+ timestamp: number;
6
+ }
7
+ export interface AnalysisCache {
8
+ version: string;
9
+ entries: Record<string, CacheEntry>;
10
+ }
11
+ export declare function getFileHash(content: string): string;
12
+ export declare function loadCache(rootDir: string): AnalysisCache;
13
+ export declare function saveCache(rootDir: string, cache: AnalysisCache): void;
14
+ export declare function isCacheValid(entry: CacheEntry, currentContent: string): boolean;
package/dist/cache.js ADDED
@@ -0,0 +1,36 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import crypto from "node:crypto";
4
+ const CACHE_DIR = ".optiprune";
5
+ const CACHE_FILE = "cache.json";
6
+ export function getFileHash(content) {
7
+ return crypto.createHash("sha256").update(content).digest("hex");
8
+ }
9
+ export function loadCache(rootDir) {
10
+ const cachePath = path.join(rootDir, CACHE_DIR, CACHE_FILE);
11
+ if (fs.existsSync(cachePath)) {
12
+ try {
13
+ return JSON.parse(fs.readFileSync(cachePath, "utf-8"));
14
+ }
15
+ catch (e) {
16
+ // Ignore
17
+ }
18
+ }
19
+ return { version: "1.0", entries: {} };
20
+ }
21
+ export function saveCache(rootDir, cache) {
22
+ try {
23
+ const dirPath = path.join(rootDir, CACHE_DIR);
24
+ if (!fs.existsSync(dirPath)) {
25
+ fs.mkdirSync(dirPath, { recursive: true });
26
+ }
27
+ fs.writeFileSync(path.join(dirPath, CACHE_FILE), JSON.stringify(cache, null, 2));
28
+ }
29
+ catch (e) {
30
+ // Ignore cache write errors in environments like tests where rootDir might be problematic
31
+ }
32
+ }
33
+ export function isCacheValid(entry, currentContent) {
34
+ return entry.hash === getFileHash(currentContent);
35
+ }
36
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,aAAa,CAAC;AAcjC,MAAM,SAAS,GAAG,YAAY,CAAC;AAC/B,MAAM,UAAU,GAAG,YAAY,CAAC;AAEhC,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IAC5D,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7B,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;QACzD,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,SAAS;QACX,CAAC;IACH,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAe,EAAE,KAAoB;IAC7D,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAC9C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACnF,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,0FAA0F;IAC5F,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAiB,EAAE,cAAsB;IACpE,OAAO,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,cAAc,CAAC,CAAC;AACpD,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ const program = new Command();
4
+ import { analyze, shouldFail } from "./index.js";
5
+ import { readJsonFile } from "./fs-utils.js";
6
+ import { formatTerminal, formatSarif } from "./reporters.js";
7
+ const PKG_JSON = await readJsonFile("package.json");
8
+ program
9
+ .name("optiprune")
10
+ .version(PKG_JSON?.version ?? "1.0.0")
11
+ .description("Finds dead code in TypeScript/JavaScript projects.")
12
+ .option("-r, --rootDir <path>", "Root directory of the project", process.cwd())
13
+ .option("-e, --entry <patterns...>", "Entry point patterns (glob or file paths)", [])
14
+ .option("-x, --extensions <exts...>", "File extensions to analyze", [".ts", ".tsx", ".js", ".jsx"])
15
+ .option("-i, --ignore <patterns...>", "Ignore patterns (glob)", [])
16
+ .option("--no-report-unused-exports", "Do not report unused exports")
17
+ .option("--no-conventional-entries", "Do not include conventional entry points (e.g., src/index.ts)")
18
+ .option("--fail-on <confidence>", "Fail on findings with confidence level (high, medium, low, none)", "high")
19
+ .option("--json", "Output results as JSON")
20
+ .option("--sarif", "Output results in SARIF format")
21
+ .action(async (options) => {
22
+ try {
23
+ const analyzerOptions = {
24
+ rootDir: options.rootDir,
25
+ entry: options.entry,
26
+ extensions: options.extensions,
27
+ ignore: options.ignore,
28
+ reportUnusedExports: options.reportUnusedExports,
29
+ includeConventionalEntries: options.conventionalEntries,
30
+ failOn: options.failOn,
31
+ json: options.json || options.sarif,
32
+ };
33
+ const report = await analyze(analyzerOptions);
34
+ if (options.sarif) {
35
+ console.log(formatSarif(report));
36
+ }
37
+ else if (options.json) {
38
+ console.log(JSON.stringify(report, null, 2));
39
+ }
40
+ else {
41
+ console.log(formatTerminal(report));
42
+ }
43
+ if (shouldFail(report, options.failOn)) {
44
+ process.exit(1);
45
+ }
46
+ }
47
+ catch (error) {
48
+ console.error("An unexpected error occurred:", error);
49
+ process.exit(1);
50
+ }
51
+ });
52
+ program.parse(process.argv);
53
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAC9B,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7D,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAuB,cAAc,CAAC,CAAC;AAE1E,OAAO;KACJ,IAAI,CAAC,WAAW,CAAC;KACjB,OAAO,CAAC,QAAQ,EAAE,OAAO,IAAI,OAAO,CAAC;KACrC,WAAW,CAAC,oDAAoD,CAAC;KACjE,MAAM,CAAC,sBAAsB,EAAE,+BAA+B,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;KAC9E,MAAM,CAAC,2BAA2B,EAAE,2CAA2C,EAAE,EAAE,CAAC;KACpF,MAAM,CAAC,4BAA4B,EAAE,4BAA4B,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;KAClG,MAAM,CAAC,4BAA4B,EAAE,wBAAwB,EAAE,EAAE,CAAC;KAClE,MAAM,CAAC,4BAA4B,EAAE,8BAA8B,CAAC;KACpE,MAAM,CAAC,2BAA2B,EAAE,+DAA+D,CAAC;KACpG,MAAM,CAAC,wBAAwB,EAAE,kEAAkE,EAAE,MAAM,CAAC;KAC5G,MAAM,CAAC,QAAQ,EAAE,wBAAwB,CAAC;KAC1C,MAAM,CAAC,SAAS,EAAE,gCAAgC,CAAC;KACnD,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACxB,IAAI,CAAC;QACH,MAAM,eAAe,GAAoB;YACvC,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,mBAAmB,EAAE,OAAO,CAAC,mBAAmB;YAChD,0BAA0B,EAAE,OAAO,CAAC,mBAAmB;YACvD,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,KAAK;SACpC,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;QAE9C,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;QACnC,CAAC;aAAM,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;QACtC,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,MAAa,CAAC,EAAE,CAAC;YAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;QACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Config, ResolvedOptions } from "./types.js";
2
+ export declare const DEFAULT_CONFIG: ResolvedOptions;
3
+ export declare function loadConfig(rootDir: string): Promise<Config>;
4
+ export declare function mergeConfig(base: ResolvedOptions, userConfig: Config): ResolvedOptions;
@@ -0,0 +1,68 @@
1
+ import path from "pathe";
2
+ import fs from "node:fs";
3
+ import { pathToFileURL } from "node:url";
4
+ import { DEFAULT_EXTENSIONS, DEFAULT_IGNORE, normalizeAbsolute } from "./fs-utils.js";
5
+ export const DEFAULT_CONFIG = {
6
+ rootDir: normalizeAbsolute(process.cwd()),
7
+ entry: [],
8
+ extensions: DEFAULT_EXTENSIONS,
9
+ ignore: DEFAULT_IGNORE,
10
+ reportUnusedExports: true,
11
+ schemaEnums: {},
12
+ failOn: "high",
13
+ json: false,
14
+ includeConventionalEntries: true,
15
+ externalContracts: [],
16
+ layers: {
17
+ smtTimeoutMs: 100,
18
+ isolateMemoryLimitMb: 16,
19
+ enableConcolicProof: true,
20
+ },
21
+ rules: {
22
+ 'unused-export': 'warning',
23
+ 'unreachable-file': 'warning',
24
+ 'constant-condition': 'warning',
25
+ 'unreachable-dynamic-path': 'warning',
26
+ }
27
+ };
28
+ export async function loadConfig(rootDir) {
29
+ const configPaths = [
30
+ path.join(rootDir, "optiprune.config.ts"),
31
+ path.join(rootDir, "optiprune.config.js"),
32
+ path.join(rootDir, "optiprune.config.mjs"),
33
+ ];
34
+ for (const configPath of configPaths) {
35
+ if (fs.existsSync(configPath)) {
36
+ try {
37
+ // In a real CLI, we'd use something like jiti or ts-node to load TS configs
38
+ // For this sandbox, we'll try dynamic import
39
+ const configUrl = pathToFileURL(configPath).href;
40
+ const module = await import(configUrl);
41
+ return module.default || module;
42
+ }
43
+ catch (e) {
44
+ console.warn(`[Config] Failed to load config from ${configPath}:`, e);
45
+ }
46
+ }
47
+ }
48
+ return {};
49
+ }
50
+ export function mergeConfig(base, userConfig) {
51
+ return {
52
+ ...base,
53
+ ...userConfig,
54
+ layers: {
55
+ ...base.layers,
56
+ ...userConfig.layers,
57
+ },
58
+ rules: {
59
+ ...base.rules,
60
+ ...userConfig.rules,
61
+ },
62
+ externalContracts: [
63
+ ...base.externalContracts,
64
+ ...(userConfig.externalContracts || []),
65
+ ],
66
+ };
67
+ }
68
+ //# sourceMappingURL=config-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-loader.js","sourceRoot":"","sources":["../src/config-loader.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,OAAO,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEtF,MAAM,CAAC,MAAM,cAAc,GAAoB;IAC7C,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,KAAK,EAAE,EAAE;IACT,UAAU,EAAE,kBAAkB;IAC9B,MAAM,EAAE,cAAc;IACtB,mBAAmB,EAAE,IAAI;IACzB,WAAW,EAAE,EAAE;IACf,MAAM,EAAE,MAAM;IACd,IAAI,EAAE,KAAK;IACX,0BAA0B,EAAE,IAAI;IAChC,iBAAiB,EAAE,EAAE;IACrB,MAAM,EAAE;QACN,YAAY,EAAE,GAAG;QACjB,oBAAoB,EAAE,EAAE;QACxB,mBAAmB,EAAE,IAAI;KAC1B;IACD,KAAK,EAAE;QACL,eAAe,EAAE,SAAS;QAC1B,kBAAkB,EAAE,SAAS;QAC7B,oBAAoB,EAAE,SAAS;QAC/B,0BAA0B,EAAE,SAAS;KACtC;CACF,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe;IAC9C,MAAM,WAAW,GAAG;QAClB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,qBAAqB,CAAC;QACzC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,qBAAqB,CAAC;QACzC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,sBAAsB,CAAC;KAC3C,CAAC;IAEF,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC;gBACH,4EAA4E;gBAC5E,6CAA6C;gBAC7C,MAAM,SAAS,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC;gBACjD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;gBACvC,OAAO,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC;YAClC,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,CAAC,IAAI,CAAC,uCAAuC,UAAU,GAAG,EAAE,CAAC,CAAC,CAAC;YACxE,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAqB,EAAE,UAAkB;IACnE,OAAO;QACL,GAAG,IAAI;QACP,GAAG,UAAU;QACb,MAAM,EAAE;YACN,GAAG,IAAI,CAAC,MAAM;YACd,GAAG,UAAU,CAAC,MAAM;SACrB;QACD,KAAK,EAAE;YACL,GAAG,IAAI,CAAC,KAAK;YACb,GAAG,UAAU,CAAC,KAAK;SACpB;QACD,iBAAiB,EAAE;YACjB,GAAG,IAAI,CAAC,iBAAiB;YACzB,GAAG,CAAC,UAAU,CAAC,iBAAiB,IAAI,EAAE,CAAC;SACxC;KACiB,CAAC;AACvB,CAAC"}
@@ -0,0 +1,39 @@
1
+ interface BranchCoverage {
2
+ file: string;
3
+ line: number;
4
+ condition: any;
5
+ hit: boolean;
6
+ }
7
+ interface FunctionCoverage {
8
+ file: string;
9
+ line: number;
10
+ name: string;
11
+ hit: boolean;
12
+ }
13
+ interface CallCoverage {
14
+ file: string;
15
+ line: number;
16
+ callee: string;
17
+ hit: boolean;
18
+ }
19
+ export declare class CoverageTracker {
20
+ private branches;
21
+ private functions;
22
+ private calls;
23
+ private currentFile;
24
+ private z3;
25
+ constructor(z3Context: any);
26
+ init(file: string): void;
27
+ traceBranch(file: string, line: number, condition: any): void;
28
+ traceFunction(file: string, line: number, name: string): void;
29
+ traceCall(file: string, line: number, callee: string): void;
30
+ traceAndExecuteCall(file: string, line: number, calleeName: string, callee: Function, args: any[]): any;
31
+ getCoverage(): {
32
+ branches: BranchCoverage[];
33
+ functions: FunctionCoverage[];
34
+ calls: CallCoverage[];
35
+ };
36
+ reset(): void;
37
+ }
38
+ export declare function createCoverageTracker(): Promise<CoverageTracker>;
39
+ export {};
@@ -0,0 +1,61 @@
1
+ import { init } from "z3-solver";
2
+ export class CoverageTracker {
3
+ branches = new Map();
4
+ functions = new Map();
5
+ calls = new Map();
6
+ currentFile = "";
7
+ z3;
8
+ constructor(z3Context) {
9
+ this.z3 = z3Context;
10
+ }
11
+ init(file) {
12
+ this.currentFile = file;
13
+ }
14
+ traceBranch(file, line, condition) {
15
+ const key = `${file}:${line}`;
16
+ if (!this.branches.has(key)) {
17
+ this.branches.set(key, { file, line, condition, hit: false });
18
+ }
19
+ this.branches.get(key).hit = true;
20
+ }
21
+ traceFunction(file, line, name) {
22
+ const key = `${file}:${line}:${name}`;
23
+ if (!this.functions.has(key)) {
24
+ this.functions.set(key, { file, line, name, hit: false });
25
+ }
26
+ this.functions.get(key).hit = true;
27
+ }
28
+ traceCall(file, line, callee) {
29
+ const key = `${file}:${line}:${callee}`;
30
+ if (!this.calls.has(key)) {
31
+ this.calls.set(key, { file, line, callee, hit: false });
32
+ }
33
+ this.calls.get(key).hit = true;
34
+ }
35
+ // New method to trace and execute calls
36
+ traceAndExecuteCall(file, line, calleeName, callee, args) {
37
+ this.traceCall(file, line, calleeName);
38
+ // Execute the original function call
39
+ return callee(...args);
40
+ }
41
+ getCoverage() {
42
+ return {
43
+ branches: Array.from(this.branches.values()),
44
+ functions: Array.from(this.functions.values()),
45
+ calls: Array.from(this.calls.values()),
46
+ };
47
+ }
48
+ reset() {
49
+ this.branches.clear();
50
+ this.functions.clear();
51
+ this.calls.clear();
52
+ this.currentFile = "";
53
+ }
54
+ }
55
+ // This will be initialized once in Layer 4 and passed to the sandbox
56
+ export async function createCoverageTracker() {
57
+ const { Context } = await init();
58
+ const z3 = Context("main");
59
+ return new CoverageTracker(z3);
60
+ }
61
+ //# sourceMappingURL=coverage-tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage-tracker.js","sourceRoot":"","sources":["../src/coverage-tracker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ export declare const DEFAULT_EXTENSIONS: string[];
2
+ export declare const DEFAULT_IGNORE: string[];
3
+ /**
4
+ * Normalizes paths universally for all operating systems and virtual filesystems.
5
+ * Ensures Windows drive letters are capitalized and separators are POSIX-style.
6
+ */
7
+ export declare function normalizeCanonicalPath(filePath: string): string;
8
+ export declare function toPosix(value: string): string;
9
+ export declare function normalizeAbsolute(value: string): string;
10
+ export declare function pathInside(parent: string, candidate: string): boolean;
11
+ /** A deliberately small glob matcher supporting the configuration patterns this tool documents. */
12
+ export declare function globToRegExp(pattern: string): RegExp;
13
+ export declare function compileGlobs(patterns: string[]): RegExp[];
14
+ export declare function matchesAnyGlob(relativePath: string, compiledPatterns: RegExp[]): boolean;
15
+ export declare function fileExists(candidate: string): Promise<boolean>;
16
+ export declare function directoryExists(candidate: string): Promise<boolean>;
17
+ export declare function discoverSourceFiles(rootDir: string, extensions: string[], compiledIgnorePatterns: RegExp[]): Promise<string[]>;
18
+ export declare function isLikelyLocalSpecifier(specifier: string): boolean;
19
+ export declare function removeQueryAndHash(specifier: string): string;
20
+ /**
21
+ * Resolves an import specifier against a set of known files.
22
+ * Uses POSIX path logic for consistency across platforms.
23
+ */
24
+ export declare function resolveLocalSpecifier(sourceFilePath: string, rawSpecifier: string, knownFiles: Set<string> | Map<string, unknown>, extensions?: string[]): string | undefined;
25
+ export declare function resolveDynamicPattern(fromFile: string, prefix: string, suffix: string, knownFiles: Set<string>): string[];
26
+ export declare function expandEntryPatterns(sourceFiles: string[], rootDir: string, patterns: string[]): string[];
27
+ export declare function discoverPackageEntryPatterns(rootDir: string): Promise<string[]>;
28
+ export declare function conventionalEntryPatterns(): string[];
29
+ export declare function normalizedConventionalEntryPatterns(): string[];
30
+ export declare function readJsonFile<T>(candidate: string): Promise<T | undefined>;
31
+ export declare function findNearestConfig(startDirectory: string): Promise<string | undefined>;
32
+ export declare function relativeDisplayPath(rootDir: string, candidate: string): string;
33
+ export declare function rootLooksValid(rootDir: string): Promise<boolean>;