assertloom 0.0.0-stage → 0.1.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 (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +346 -2
  3. package/dist/ai/index.d.ts +5 -0
  4. package/dist/ai/index.js +14 -0
  5. package/dist/ai/index.js.map +1 -0
  6. package/dist/ai/openai.d.ts +9 -0
  7. package/dist/ai/openai.js +57 -0
  8. package/dist/ai/openai.js.map +1 -0
  9. package/dist/ai/pricing.d.ts +27 -0
  10. package/dist/ai/pricing.js +76 -0
  11. package/dist/ai/pricing.js.map +1 -0
  12. package/dist/ai/prompts.d.ts +27 -0
  13. package/dist/ai/prompts.js +89 -0
  14. package/dist/ai/prompts.js.map +1 -0
  15. package/dist/ai/provider.d.ts +26 -0
  16. package/dist/ai/provider.js +4 -0
  17. package/dist/ai/provider.js.map +1 -0
  18. package/dist/ai/redact.d.ts +5 -0
  19. package/dist/ai/redact.js +31 -0
  20. package/dist/ai/redact.js.map +1 -0
  21. package/dist/analyzer/dependencies.d.ts +16 -0
  22. package/dist/analyzer/dependencies.js +168 -0
  23. package/dist/analyzer/dependencies.js.map +1 -0
  24. package/dist/analyzer/format.d.ts +3 -0
  25. package/dist/analyzer/format.js +14 -0
  26. package/dist/analyzer/format.js.map +1 -0
  27. package/dist/analyzer/functions.d.ts +4 -0
  28. package/dist/analyzer/functions.js +274 -0
  29. package/dist/analyzer/functions.js.map +1 -0
  30. package/dist/analyzer/index.d.ts +5 -0
  31. package/dist/analyzer/index.js +29 -0
  32. package/dist/analyzer/index.js.map +1 -0
  33. package/dist/analyzer/parser.d.ts +11 -0
  34. package/dist/analyzer/parser.js +38 -0
  35. package/dist/analyzer/parser.js.map +1 -0
  36. package/dist/analyzer/usage.d.ts +16 -0
  37. package/dist/analyzer/usage.js +159 -0
  38. package/dist/analyzer/usage.js.map +1 -0
  39. package/dist/cli/commands/analyze.d.ts +2 -0
  40. package/dist/cli/commands/analyze.js +49 -0
  41. package/dist/cli/commands/analyze.js.map +1 -0
  42. package/dist/cli/commands/generate.d.ts +3 -0
  43. package/dist/cli/commands/generate.js +200 -0
  44. package/dist/cli/commands/generate.js.map +1 -0
  45. package/dist/cli/commands/run.d.ts +4 -0
  46. package/dist/cli/commands/run.js +53 -0
  47. package/dist/cli/commands/run.js.map +1 -0
  48. package/dist/cli/index.d.ts +2 -0
  49. package/dist/cli/index.js +16 -0
  50. package/dist/cli/index.js.map +1 -0
  51. package/dist/cli/program.d.ts +2 -0
  52. package/dist/cli/program.js +18 -0
  53. package/dist/cli/program.js.map +1 -0
  54. package/dist/cli/report.d.ts +3 -0
  55. package/dist/cli/report.js +36 -0
  56. package/dist/cli/report.js.map +1 -0
  57. package/dist/cli/validateFile.d.ts +5 -0
  58. package/dist/cli/validateFile.js +43 -0
  59. package/dist/cli/validateFile.js.map +1 -0
  60. package/dist/errors.d.ts +4 -0
  61. package/dist/errors.js +8 -0
  62. package/dist/errors.js.map +1 -0
  63. package/dist/generator/diff.d.ts +5 -0
  64. package/dist/generator/diff.js +56 -0
  65. package/dist/generator/diff.js.map +1 -0
  66. package/dist/generator/importPlan.d.ts +15 -0
  67. package/dist/generator/importPlan.js +62 -0
  68. package/dist/generator/importPlan.js.map +1 -0
  69. package/dist/generator/index.d.ts +5 -0
  70. package/dist/generator/index.js +6 -0
  71. package/dist/generator/index.js.map +1 -0
  72. package/dist/generator/mocks.d.ts +48 -0
  73. package/dist/generator/mocks.js +356 -0
  74. package/dist/generator/mocks.js.map +1 -0
  75. package/dist/generator/schema.d.ts +40 -0
  76. package/dist/generator/schema.js +15 -0
  77. package/dist/generator/schema.js.map +1 -0
  78. package/dist/generator/testGenerator.d.ts +33 -0
  79. package/dist/generator/testGenerator.js +80 -0
  80. package/dist/generator/testGenerator.js.map +1 -0
  81. package/dist/generator/testWriter.d.ts +22 -0
  82. package/dist/generator/testWriter.js +93 -0
  83. package/dist/generator/testWriter.js.map +1 -0
  84. package/dist/generator/validate.d.ts +31 -0
  85. package/dist/generator/validate.js +180 -0
  86. package/dist/generator/validate.js.map +1 -0
  87. package/dist/repair/classifyFailure.d.ts +11 -0
  88. package/dist/repair/classifyFailure.js +26 -0
  89. package/dist/repair/classifyFailure.js.map +1 -0
  90. package/dist/repair/index.d.ts +2 -0
  91. package/dist/repair/index.js +3 -0
  92. package/dist/repair/index.js.map +1 -0
  93. package/dist/repair/repairLoop.d.ts +74 -0
  94. package/dist/repair/repairLoop.js +219 -0
  95. package/dist/repair/repairLoop.js.map +1 -0
  96. package/dist/repair/schema.d.ts +17 -0
  97. package/dist/repair/schema.js +14 -0
  98. package/dist/repair/schema.js.map +1 -0
  99. package/dist/runner/coverage.d.ts +5 -0
  100. package/dist/runner/coverage.js +42 -0
  101. package/dist/runner/coverage.js.map +1 -0
  102. package/dist/runner/index.d.ts +4 -0
  103. package/dist/runner/index.js +4 -0
  104. package/dist/runner/index.js.map +1 -0
  105. package/dist/runner/jestConfig.d.ts +11 -0
  106. package/dist/runner/jestConfig.js +103 -0
  107. package/dist/runner/jestConfig.js.map +1 -0
  108. package/dist/runner/jestRunner.d.ts +14 -0
  109. package/dist/runner/jestRunner.js +140 -0
  110. package/dist/runner/jestRunner.js.map +1 -0
  111. package/dist/runner/networkGuard.d.ts +4 -0
  112. package/dist/runner/networkGuard.js +55 -0
  113. package/dist/runner/networkGuard.js.map +1 -0
  114. package/dist/runner/project.d.ts +30 -0
  115. package/dist/runner/project.js +86 -0
  116. package/dist/runner/project.js.map +1 -0
  117. package/dist/runner/results.d.ts +32 -0
  118. package/dist/runner/results.js +58 -0
  119. package/dist/runner/results.js.map +1 -0
  120. package/dist/runner/types.d.ts +39 -0
  121. package/dist/runner/types.js +2 -0
  122. package/dist/runner/types.js.map +1 -0
  123. package/dist/types/index.d.ts +95 -0
  124. package/dist/types/index.js +2 -0
  125. package/dist/types/index.js.map +1 -0
  126. package/package.json +66 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../../src/cli/commands/analyze.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { type Command } from "commander";
2
+ export declare const DEFAULT_MAX_COST_USD = 1;
3
+ export declare function registerGenerateCommand(program: Command): void;
@@ -0,0 +1,200 @@
1
+ import { rm, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { Option } from "commander";
4
+ import { createProvider, PROVIDERS } from "../../ai/index.js";
5
+ import { Budget, estimateCost, formatUsd } from "../../ai/pricing.js";
6
+ import { UserError } from "../../errors.js";
7
+ import { generateTests, testPathFor, TestFileExistsError, writeTestFile } from "../../generator/index.js";
8
+ import { MAX_REPAIR_ATTEMPTS, repairTests, summarize } from "../../repair/index.js";
9
+ import { runJest } from "../../runner/index.js";
10
+ import { formatCoverage, printRunResult } from "../report.js";
11
+ import { validateSourceFile } from "../validateFile.js";
12
+ export const DEFAULT_MAX_COST_USD = 1;
13
+ function tokens(usage, model) {
14
+ const cost = estimateCost(model, usage);
15
+ return `${usage.inputTokens.toLocaleString()} in / ${usage.outputTokens.toLocaleString()} out${cost === undefined ? "" : `, ≈${formatUsd(cost)}`}`;
16
+ }
17
+ const collect = (value, previous) => [...previous, value];
18
+ function printMocks(result) {
19
+ const { mocked, real, globals } = result.mocks;
20
+ const names = [...mocked.map((m) => m.source), ...globals.map((g) => `global ${g}`)];
21
+ if (names.length)
22
+ console.log(`✓ Mocked ${names.length} ${names.length === 1 ? "dependency" : "dependencies"}: ${names.join(", ")}`);
23
+ for (const m of mocked)
24
+ console.log(` - ${m.source}: ${m.reason}${m.virtual ? " (virtual mock)" : ""}`);
25
+ if (real.length)
26
+ console.log(` Kept real: ${real.map((r) => `${r.source} (${r.reason})`).join(", ")}`);
27
+ }
28
+ function relative(file) {
29
+ return path.relative(process.cwd(), file) || file;
30
+ }
31
+ /** Prints what changes in an existing test file before it is overwritten. Previews are scratch files. */
32
+ function showDiff(file) {
33
+ if (file.includes(".test.preview."))
34
+ return undefined;
35
+ return (diff) => {
36
+ console.log(`\nOverwriting ${relative(file)}. Changes:`);
37
+ for (const line of diff)
38
+ console.log(` ${line}`);
39
+ };
40
+ }
41
+ const STOP_MESSAGES = {
42
+ "all-passing": "all tests pass",
43
+ "max-attempts": "reached the repair limit",
44
+ regressed: "a repair broke passing tests and was reverted",
45
+ "no-valid-repairs": "the model proposed no usable repairs",
46
+ unrepairable: "the remaining failures cannot be fixed by editing tests",
47
+ budget: "another repair round could exceed the spending limit",
48
+ };
49
+ function printRepair(repair, model) {
50
+ for (const attempt of repair.attempts) {
51
+ const counts = ["fix", "skip", "remove"]
52
+ .map((action) => [action, attempt.changes.filter((c) => c.action === action).length])
53
+ .filter(([, n]) => n > 0)
54
+ .map(([action, n]) => `${n} ${action === "fix" ? "fixed" : action === "skip" ? "flagged as source bugs" : "removed"}`);
55
+ const after = attempt.after ? ` → ${attempt.after.passed} passed, ${attempt.after.failed} failed` : "";
56
+ console.log(`${attempt.regressed ? "!" : "✓"} Repair attempt ${attempt.attempt}: ${counts.join(", ") || "no changes"}${after}`);
57
+ for (const change of attempt.changes)
58
+ console.log(` - ${change.action} ${change.functionName} › "${change.name}": ${change.reason}`);
59
+ for (const invalid of attempt.invalid)
60
+ console.log(` ! ignored repair for ${invalid.functionName} › "${invalid.name}": ${invalid.reason}`);
61
+ if (attempt.regressed)
62
+ console.log(" ! This attempt broke passing tests; it was reverted.");
63
+ }
64
+ for (const change of repair.gaveUp)
65
+ console.log(` - skipped ${change.functionName} › "${change.name}": ${change.reason}`);
66
+ const { final } = repair;
67
+ const total = final.passed + final.failed + final.skipped;
68
+ const skipped = final.skipped ? `, ${final.skipped} skipped` : "";
69
+ console.log(`✓ Final result: ${final.passed}/${total} passing${skipped} (${STOP_MESSAGES[repair.stopReason]})`);
70
+ if (final.coverage)
71
+ console.log(`✓ Coverage: ${formatCoverage(final.coverage)}`);
72
+ console.log(` Repair tokens: ${tokens(repair.usage, model)}`);
73
+ if (!repair.sourceUnchanged)
74
+ console.error("✗ A test run modified the source file. Assertloom restored the original contents.");
75
+ }
76
+ function buildReport(file, generation, run, repair, budget) {
77
+ return {
78
+ file,
79
+ model: generation.model,
80
+ mocks: { mocked: generation.mocks.mocked, real: generation.mocks.real, globals: generation.mocks.globals },
81
+ generated: generation.suites.reduce((n, s) => n + s.cases.length, 0),
82
+ rejected: generation.rejected,
83
+ initial: repair?.initial ?? summarize(run),
84
+ attempts: repair?.attempts ?? [],
85
+ gaveUp: repair?.gaveUp ?? [],
86
+ final: summarize(run),
87
+ stopReason: repair?.stopReason ?? (run.status === "passed" ? "all-passing" : "not-repaired"),
88
+ sourceUnchanged: repair?.sourceUnchanged ?? true,
89
+ usage: {
90
+ generation: generation.usage,
91
+ repair: repair?.usage ?? { inputTokens: 0, outputTokens: 0 },
92
+ },
93
+ costUsd: budget.spentUsd ?? null,
94
+ };
95
+ }
96
+ export function registerGenerateCommand(program) {
97
+ program
98
+ .command("generate")
99
+ .description("Generate Jest tests for a source file, run them, and keep them if they pass")
100
+ .argument("<file>", "path to a JavaScript or TypeScript source file")
101
+ .addOption(new Option("--provider <name>", "LLM provider").choices(PROVIDERS).default("openai"))
102
+ .option("--model <model>", "model name (default: $ASSERTLOOM_MODEL or the provider default)")
103
+ .option("--out <path>", "where to write the tests (default: <file>.test.preview.<ext>, promoted to <file>.test.<ext> when all pass)")
104
+ .option("--dry-run", "print the generated tests instead of writing or running them")
105
+ .option("--no-run", "write the preview file without running it")
106
+ .option("--no-repair", "do not try to repair failing tests")
107
+ .option("--max-attempts <n>", `repair rounds, 0-${MAX_REPAIR_ATTEMPTS}`, String(MAX_REPAIR_ATTEMPTS))
108
+ .option("--report <file>", "write before/after results of every run and repair round as JSON")
109
+ .option("--mock <module>", "mock this import even if Assertloom would keep it real (repeatable)", collect, [])
110
+ .option("--keep-real <module>", "do not mock this import (repeatable)", collect, [])
111
+ .option("--allow-network", "let generated tests open network connections (blocked by default)")
112
+ .option("--max-cost <usd>", "stop before model calls could push spending past this many US dollars", String(DEFAULT_MAX_COST_USD))
113
+ .option("--force", "overwrite an existing test file that Assertloom did not generate")
114
+ .action(async (file, options) => {
115
+ const maxAttempts = Number(options.maxAttempts);
116
+ if (!Number.isInteger(maxAttempts) || maxAttempts < 0 || maxAttempts > MAX_REPAIR_ATTEMPTS) {
117
+ throw new UserError(`--max-attempts must be a whole number from 0 to ${MAX_REPAIR_ATTEMPTS}.`);
118
+ }
119
+ const maxCost = Number(options.maxCost);
120
+ if (!Number.isFinite(maxCost) || maxCost <= 0)
121
+ throw new UserError("--max-cost must be a positive number of US dollars.");
122
+ const source = await validateSourceFile(file);
123
+ const provider = createProvider(options.provider, options.model);
124
+ const budget = new Budget(provider.model, maxCost);
125
+ const testPath = options.out ? path.resolve(options.out) : undefined;
126
+ const allowNetwork = !!options.allowNetwork;
127
+ console.log(`Generating tests for ${source.inputPath} with ${provider.name} (${provider.model})...`);
128
+ const result = await generateTests({ source, provider, testPath, mock: options.mock, keepReal: options.keepReal, budget });
129
+ const exported = result.analysis.functions.filter((f) => f.exported).length;
130
+ const caseCount = result.suites.reduce((n, s) => n + s.cases.length, 0);
131
+ const byCategory = (category) => result.suites.reduce((n, s) => n + s.cases.filter((c) => c.category === category).length, 0);
132
+ console.log(`✓ Analyzed ${exported} exported functions (${result.suites.length} tested)`);
133
+ printMocks(result);
134
+ if (result.redactions)
135
+ console.log(`✓ Redacted ${result.redactions} likely secrets before sending`);
136
+ console.log(`✓ Generated ${caseCount} test cases (${byCategory("normal")} normal, ${byCategory("edge")} edge, ${byCategory("error")} error)`);
137
+ for (const r of result.rejected)
138
+ console.log(` ! rejected ${r.functionName} › "${r.name}": ${r.reason}`);
139
+ console.log(` Tokens: ${tokens(result.usage, result.model)} (${result.model})`);
140
+ if (options.dryRun) {
141
+ console.log(`\n${result.content}`);
142
+ return;
143
+ }
144
+ await writeTestFile(result.testPath, result.content, options.force, showDiff(result.testPath));
145
+ if (!options.run) {
146
+ console.log(`\nTests saved to ${relative(result.testPath)} (not run)`);
147
+ return;
148
+ }
149
+ let run = await runJest({ testPath: result.testPath, sourcePath: source.absolutePath, allowNetwork });
150
+ printRunResult(run);
151
+ let content = result.content;
152
+ let repair;
153
+ if (run.status === "failed" && options.repair) {
154
+ repair = await repairTests({
155
+ provider,
156
+ analysis: result.analysis,
157
+ promptSource: result.promptSource,
158
+ sourcePath: source.absolutePath,
159
+ testPath: result.testPath,
160
+ extension: source.extension,
161
+ preamble: result.preamble,
162
+ mockSummary: result.mocks.summary,
163
+ suites: result.suites,
164
+ initialRun: run,
165
+ maxAttempts,
166
+ allowNetwork,
167
+ budget,
168
+ });
169
+ printRepair(repair, result.model);
170
+ run = repair.run;
171
+ content = repair.content;
172
+ }
173
+ if (options.report) {
174
+ await writeFile(options.report, JSON.stringify(buildReport(source.inputPath, result, run, repair, budget), null, 2));
175
+ console.log(` Report written to ${relative(path.resolve(options.report))}`);
176
+ }
177
+ if (run.status !== "passed") {
178
+ console.log(`\nTests kept in ${relative(result.testPath)} for review.`);
179
+ process.exitCode = 1;
180
+ return;
181
+ }
182
+ if (testPath) {
183
+ console.log(`\nTests saved to ${relative(result.testPath)}`);
184
+ return;
185
+ }
186
+ const finalPath = testPathFor(source.absolutePath);
187
+ try {
188
+ await writeTestFile(finalPath, content, options.force, showDiff(finalPath));
189
+ await rm(result.testPath);
190
+ console.log(`\nTests saved to ${relative(finalPath)}`);
191
+ }
192
+ catch (error) {
193
+ if (!(error instanceof TestFileExistsError))
194
+ throw error;
195
+ console.log(`\n${relative(finalPath)} already exists and was not generated by Assertloom.`);
196
+ console.log(`Passing tests kept in ${relative(result.testPath)}; rerun with --force to replace the existing file.`);
197
+ }
198
+ });
199
+ }
200
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Command } from "commander";
2
+ /** `math.test.preview.ts` / `math.test.ts` / `math.spec.ts` → `math.ts`. */
3
+ export declare function inferSourcePath(testPath: string): string | undefined;
4
+ export declare function registerRunCommand(program: Command): void;
@@ -0,0 +1,53 @@
1
+ import { stat } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { FileValidationError, validateSourceFile } from "../validateFile.js";
4
+ import { runJest } from "../../runner/index.js";
5
+ import { printRunResult } from "../report.js";
6
+ /** Node timers overflow above 2^31 - 1 ms and fire immediately. */
7
+ const MAX_TIMEOUT_SECONDS = 2_147_483;
8
+ /** `math.test.preview.ts` / `math.test.ts` / `math.spec.ts` → `math.ts`. */
9
+ export function inferSourcePath(testPath) {
10
+ const inferred = testPath.replace(/\.(test|spec)(\.preview)?(?=\.[cm]?[jt]sx?$)/, "");
11
+ return inferred === testPath ? undefined : inferred;
12
+ }
13
+ export function registerRunCommand(program) {
14
+ program
15
+ .command("run")
16
+ .description("Run a test file with Jest and report results and coverage of its source file")
17
+ .argument("<testFile>", "test file to run, such as math.test.preview.ts")
18
+ .option("--source <file>", "source file to measure coverage for (default: inferred from the test file name)")
19
+ .option("--timeout <seconds>", "kill Jest if it runs longer than this", "120")
20
+ .option("--json", "print the full result as JSON")
21
+ .option("--allow-network", "let the tests open network connections (blocked by default)")
22
+ .action(async (testFile, options) => {
23
+ const testPath = path.resolve(testFile);
24
+ try {
25
+ if (!(await stat(testPath)).isFile())
26
+ throw new Error();
27
+ }
28
+ catch {
29
+ throw new FileValidationError(`Test file not found: ${testFile}`);
30
+ }
31
+ const sourceArg = options.source ?? inferSourcePath(testFile);
32
+ if (!sourceArg)
33
+ throw new FileValidationError(`Cannot infer the source file from ${testFile}; pass --source.`);
34
+ const source = await validateSourceFile(sourceArg);
35
+ const timeoutSeconds = Number(options.timeout);
36
+ if (!Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0 || timeoutSeconds > MAX_TIMEOUT_SECONDS) {
37
+ throw new FileValidationError(`--timeout must be a positive number of seconds, at most ${MAX_TIMEOUT_SECONDS}.`);
38
+ }
39
+ const result = await runJest({
40
+ testPath,
41
+ sourcePath: source.absolutePath,
42
+ timeoutMs: timeoutSeconds * 1000,
43
+ allowNetwork: !!options.allowNetwork,
44
+ });
45
+ if (options.json)
46
+ console.log(JSON.stringify(result, null, 2));
47
+ else
48
+ printRunResult(result);
49
+ if (result.status !== "passed")
50
+ process.exitCode = 1;
51
+ });
52
+ }
53
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/cli/commands/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,mEAAmE;AACnE,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAStC,4EAA4E;AAC5E,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,8CAA8C,EAAE,EAAE,CAAC,CAAC;IACtF,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAgB;IACjD,OAAO;SACJ,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,8EAA8E,CAAC;SAC3F,QAAQ,CAAC,YAAY,EAAE,gDAAgD,CAAC;SACxE,MAAM,CAAC,iBAAiB,EAAE,iFAAiF,CAAC;SAC5G,MAAM,CAAC,qBAAqB,EAAE,uCAAuC,EAAE,KAAK,CAAC;SAC7E,MAAM,CAAC,QAAQ,EAAE,+BAA+B,CAAC;SACjD,MAAM,CAAC,iBAAiB,EAAE,6DAA6D,CAAC;SACxF,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,OAA0B,EAAE,EAAE;QAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAI,CAAC;YACH,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE;gBAAE,MAAM,IAAI,KAAK,EAAE,CAAC;QAC1D,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,mBAAmB,CAAC,wBAAwB,QAAQ,EAAE,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,IAAI,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC9D,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,mBAAmB,CAAC,qCAAqC,QAAQ,kBAAkB,CAAC,CAAC;QAC/G,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,SAAS,CAAC,CAAC;QAEnD,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,cAAc,IAAI,CAAC,IAAI,cAAc,GAAG,mBAAmB,EAAE,CAAC;YACpG,MAAM,IAAI,mBAAmB,CAAC,2DAA2D,mBAAmB,GAAG,CAAC,CAAC;QACnH,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;YAC3B,QAAQ;YACR,UAAU,EAAE,MAAM,CAAC,YAAY;YAC/B,SAAS,EAAE,cAAc,GAAG,IAAI;YAChC,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY;SACrC,CAAC,CAAC;QAEH,IAAI,OAAO,CAAC,IAAI;YAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;;YAC1D,cAAc,CAAC,MAAM,CAAC,CAAC;QAE5B,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { UserError } from "../errors.js";
3
+ import { createProgram } from "./program.js";
4
+ try {
5
+ await createProgram().parseAsync(process.argv);
6
+ }
7
+ catch (error) {
8
+ if (error instanceof UserError) {
9
+ console.error(`✗ ${error.message}`);
10
+ }
11
+ else {
12
+ console.error(error);
13
+ }
14
+ process.exitCode = 1;
15
+ }
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,IAAI,CAAC;IACH,MAAM,aAAa,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACjD,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,IAAI,KAAK,YAAY,SAAS,EAAE,CAAC;QAC/B,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACtC,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IACD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function createProgram(): Command;
@@ -0,0 +1,18 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { Command } from "commander";
3
+ import { registerAnalyzeCommand } from "./commands/analyze.js";
4
+ import { registerGenerateCommand } from "./commands/generate.js";
5
+ import { registerRunCommand } from "./commands/run.js";
6
+ const { version } = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8"));
7
+ export function createProgram() {
8
+ const program = new Command()
9
+ .name("assertloom")
10
+ .description("Assertloom: AI-generated Jest tests that are executed, verified, and repaired.")
11
+ .version(version)
12
+ .showHelpAfterError();
13
+ registerAnalyzeCommand(program);
14
+ registerGenerateCommand(program);
15
+ registerRunCommand(program);
16
+ return program;
17
+ }
18
+ //# sourceMappingURL=program.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"program.js","sourceRoot":"","sources":["../../src/cli/program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,oBAAoB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAElG,CAAC;AAEF,MAAM,UAAU,aAAa;IAC3B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE;SAC1B,IAAI,CAAC,YAAY,CAAC;SAClB,WAAW,CAAC,gFAAgF,CAAC;SAC7F,OAAO,CAAC,OAAO,CAAC;SAChB,kBAAkB,EAAE,CAAC;IAExB,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACjC,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAE5B,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CoverageMetrics, RunResult } from "../runner/index.js";
2
+ export declare function formatCoverage(coverage: CoverageMetrics): string;
3
+ export declare function printRunResult(result: RunResult): void;
@@ -0,0 +1,36 @@
1
+ function pct(value) {
2
+ return value === null ? "n/a" : `${Math.round(value * 10) / 10}%`;
3
+ }
4
+ export function formatCoverage(coverage) {
5
+ return `${pct(coverage.lines)} lines, ${pct(coverage.branches)} branches, ${pct(coverage.functions)} functions`;
6
+ }
7
+ function firstLines(text, count) {
8
+ return text
9
+ .split("\n")
10
+ .filter((line) => line.trim())
11
+ .slice(0, count)
12
+ .map((line) => ` ${line.trim()}`)
13
+ .join("\n");
14
+ }
15
+ export function printRunResult(result) {
16
+ const seconds = (result.durationMs / 1000).toFixed(1);
17
+ if (result.sourceRestored)
18
+ console.log("! The tests modified the source file under test; Assertloom restored the original.");
19
+ if (result.infra) {
20
+ console.log(`✗ Jest could not run the tests (${result.infra.kind}, ${result.setup}, ${seconds}s)`);
21
+ console.log(firstLines(result.infra.message, 8));
22
+ return;
23
+ }
24
+ const skipped = result.skipped ? `, ${result.skipped} skipped` : "";
25
+ const mark = result.failed ? "!" : "✓";
26
+ console.log(`${mark} Ran Jest: ${result.passed} passed, ${result.failed} failed${skipped} (${result.setup}, ${seconds}s)`);
27
+ for (const test of result.tests) {
28
+ if (test.status !== "failed" || !test.failure)
29
+ continue;
30
+ console.log(` ✗ ${test.suite ? `${test.suite} › ` : ""}${test.name} [${test.failure.kind}]`);
31
+ console.log(firstLines(test.failure.message, 4));
32
+ }
33
+ if (result.coverage)
34
+ console.log(`✓ Coverage: ${formatCoverage(result.coverage)}`);
35
+ }
36
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/cli/report.ts"],"names":[],"mappings":"AAEA,SAAS,GAAG,CAAC,KAAoB;IAC/B,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAyB;IACtD,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,YAAY,CAAC;AAClH,CAAC;AAED,SAAS,UAAU,CAAC,IAAY,EAAE,KAAa;IAC7C,OAAO,IAAI;SACR,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC7B,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;SACf,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;SACrC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAiB;IAC9C,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACtD,IAAI,MAAM,CAAC,cAAc;QAAE,OAAO,CAAC,GAAG,CAAC,oFAAoF,CAAC,CAAC;IAE7H,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,mCAAmC,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,KAAK,KAAK,OAAO,IAAI,CAAC,CAAC;QACnG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;QACjD,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;IACpE,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACvC,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,cAAc,MAAM,CAAC,MAAM,YAAY,MAAM,CAAC,MAAM,UAAU,OAAO,KAAK,MAAM,CAAC,KAAK,KAAK,OAAO,IAAI,CAAC,CAAC;IAE3H,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,SAAS;QACxD,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC;QAC/F,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;IACnD,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ;QAAE,OAAO,CAAC,GAAG,CAAC,eAAe,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AACrF,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { UserError } from "../errors.js";
2
+ import { type SourceFile } from "../types/index.js";
3
+ export declare class FileValidationError extends UserError {
4
+ }
5
+ export declare function validateSourceFile(inputPath: string, cwd?: string): Promise<SourceFile>;
@@ -0,0 +1,43 @@
1
+ import { stat } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { UserError } from "../errors.js";
4
+ import { SUPPORTED_EXTENSIONS } from "../types/index.js";
5
+ export class FileValidationError extends UserError {
6
+ }
7
+ const MAX_FILE_SIZE_BYTES = 1024 * 1024;
8
+ function isSupportedExtension(ext) {
9
+ return SUPPORTED_EXTENSIONS.includes(ext);
10
+ }
11
+ export async function validateSourceFile(inputPath, cwd = process.cwd()) {
12
+ const absolutePath = path.resolve(cwd, inputPath);
13
+ const displayPath = path.relative(cwd, absolutePath) || inputPath;
14
+ const extension = path.extname(absolutePath).toLowerCase();
15
+ if (!isSupportedExtension(extension)) {
16
+ throw new FileValidationError(`Unsupported file type "${extension || "(none)"}" for ${displayPath}. ` +
17
+ `Supported extensions: ${SUPPORTED_EXTENSIONS.join(", ")}`);
18
+ }
19
+ if (/\.(test|spec)\.[cm]?[jt]sx?$/i.test(absolutePath)) {
20
+ throw new FileValidationError(`${displayPath} looks like a test file. Point Assertloom at source code instead.`);
21
+ }
22
+ let stats;
23
+ try {
24
+ stats = await stat(absolutePath);
25
+ }
26
+ catch {
27
+ throw new FileValidationError(`File not found: ${displayPath}`);
28
+ }
29
+ if (!stats.isFile()) {
30
+ throw new FileValidationError(`Not a file: ${displayPath}`);
31
+ }
32
+ if (stats.size > MAX_FILE_SIZE_BYTES) {
33
+ throw new FileValidationError(`${displayPath} is ${(stats.size / 1024).toFixed(0)} KB; files larger than 1 MB are not supported.`);
34
+ }
35
+ return {
36
+ inputPath: displayPath,
37
+ absolutePath,
38
+ extension,
39
+ language: /^\.[cm]?tsx?$/.test(extension) ? "typescript" : "javascript",
40
+ sizeBytes: stats.size,
41
+ };
42
+ }
43
+ //# sourceMappingURL=validateFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateFile.js","sourceRoot":"","sources":["../../src/cli/validateFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,oBAAoB,EAA4C,MAAM,mBAAmB,CAAC;AAEnG,MAAM,OAAO,mBAAoB,SAAQ,SAAS;CAAG;AAErD,MAAM,mBAAmB,GAAG,IAAI,GAAG,IAAI,CAAC;AAExC,SAAS,oBAAoB,CAAC,GAAW;IACvC,OAAQ,oBAA0C,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,SAAiB,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAC7E,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAClD,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,IAAI,SAAS,CAAC;IAElE,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;IAC3D,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,mBAAmB,CAC3B,0BAA0B,SAAS,IAAI,QAAQ,SAAS,WAAW,IAAI;YACrE,yBAAyB,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC7D,CAAC;IACJ,CAAC;IAED,IAAI,+BAA+B,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,mBAAmB,CAAC,GAAG,WAAW,mEAAmE,CAAC,CAAC;IACnH,CAAC;IAED,IAAI,KAAK,CAAC;IACV,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,mBAAmB,CAAC,mBAAmB,WAAW,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;QACpB,MAAM,IAAI,mBAAmB,CAAC,eAAe,WAAW,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,GAAG,mBAAmB,EAAE,CAAC;QACrC,MAAM,IAAI,mBAAmB,CAC3B,GAAG,WAAW,OAAO,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,gDAAgD,CACpG,CAAC;IACJ,CAAC;IAED,OAAO;QACL,SAAS,EAAE,WAAW;QACtB,YAAY;QACZ,SAAS;QACT,QAAQ,EAAE,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY;QACvE,SAAS,EAAE,KAAK,CAAC,IAAI;KACtB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ /** An expected failure with a message fit to show the user as-is (no stack trace). */
2
+ export declare class UserError extends Error {
3
+ constructor(message: string);
4
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,8 @@
1
+ /** An expected failure with a message fit to show the user as-is (no stack trace). */
2
+ export class UserError extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = new.target.name;
6
+ }
7
+ }
8
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,MAAM,OAAO,SAAU,SAAQ,KAAK;IAClC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC;IAC9B,CAAC;CACF"}
@@ -0,0 +1,5 @@
1
+ /** A unified line diff (`-` removed, `+` added) with `context` unchanged lines around each change. */
2
+ export declare function unifiedDiff(before: string, after: string, { context, maxLines }?: {
3
+ context?: number | undefined;
4
+ maxLines?: number | undefined;
5
+ }): string[];
@@ -0,0 +1,56 @@
1
+ /** Larger inputs get a one-line summary instead of a line diff, to bound memory. */
2
+ const MAX_DIFF_CELLS = 4_000_000;
3
+ /** A unified line diff (`-` removed, `+` added) with `context` unchanged lines around each change. */
4
+ export function unifiedDiff(before, after, { context = 3, maxLines = 120 } = {}) {
5
+ const a = before.split("\n");
6
+ const b = after.split("\n");
7
+ if (before === after)
8
+ return [];
9
+ if (a.length * b.length > MAX_DIFF_CELLS)
10
+ return [`(${a.length} lines → ${b.length} lines; too large to diff)`];
11
+ // lcs[i][j] = length of the longest common subsequence of a[i..] and b[j..].
12
+ const width = b.length + 1;
13
+ const lcs = new Uint32Array((a.length + 1) * width);
14
+ for (let i = a.length - 1; i >= 0; i--) {
15
+ for (let j = b.length - 1; j >= 0; j--) {
16
+ lcs[i * width + j] = a[i] === b[j] ? lcs[(i + 1) * width + j + 1] + 1 : Math.max(lcs[(i + 1) * width + j], lcs[i * width + j + 1]);
17
+ }
18
+ }
19
+ const ops = [];
20
+ let i = 0;
21
+ let j = 0;
22
+ while (i < a.length || j < b.length) {
23
+ if (i < a.length && j < b.length && a[i] === b[j])
24
+ ops.push({ kind: " ", text: a[i], oldLine: i++, newLine: j++ });
25
+ else if (i < a.length && (j === b.length || lcs[(i + 1) * width + j] >= lcs[i * width + j + 1]))
26
+ ops.push({ kind: "-", text: a[i], oldLine: i++, newLine: j });
27
+ else
28
+ ops.push({ kind: "+", text: b[j], oldLine: i, newLine: j++ });
29
+ }
30
+ const keep = ops.map(() => false);
31
+ ops.forEach((op, index) => {
32
+ if (op.kind === " ")
33
+ return;
34
+ for (let k = Math.max(0, index - context); k <= Math.min(ops.length - 1, index + context); k++)
35
+ keep[k] = true;
36
+ });
37
+ const lines = [];
38
+ for (let start = 0; start < ops.length; start++) {
39
+ if (!keep[start])
40
+ continue;
41
+ let end = start;
42
+ while (end + 1 < ops.length && keep[end + 1])
43
+ end++;
44
+ const hunk = ops.slice(start, end + 1);
45
+ const oldCount = hunk.filter((op) => op.kind !== "+").length;
46
+ const newCount = hunk.filter((op) => op.kind !== "-").length;
47
+ lines.push(`@@ -${hunk[0].oldLine + 1},${oldCount} +${hunk[0].newLine + 1},${newCount} @@`);
48
+ for (const op of hunk)
49
+ lines.push(`${op.kind}${op.text}`);
50
+ start = end;
51
+ }
52
+ if (lines.length > maxLines)
53
+ return [...lines.slice(0, maxLines), `… ${lines.length - maxLines} more diff lines`];
54
+ return lines;
55
+ }
56
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/generator/diff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { FileAnalysis, FunctionInfo } from "../types/index.js";
2
+ export interface Binding {
3
+ /** Name the test code uses to call the function. */
4
+ binding: string;
5
+ /** Exported name in the source module, or `"default"`. */
6
+ exportName: string;
7
+ fn: FunctionInfo;
8
+ }
9
+ export interface ImportPlan {
10
+ statements: string[];
11
+ bindings: Binding[];
12
+ }
13
+ /** Module specifier for importing `sourcePath` from a test file at `testPath`. */
14
+ export declare function importSpecifier(testPath: string, sourcePath: string): string;
15
+ export declare function planImports(analysis: FileAnalysis, functions: FunctionInfo[], sourcePath: string, testPath: string): ImportPlan;