specproof 0.2.0 → 0.3.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 CHANGED
@@ -1,53 +1,61 @@
1
- <img src="public/banner.png" alt="SpecProof" />
1
+ <img src="https://raw.githubusercontent.com/Durable-Quality/specproof/main/public/icon.png" alt="SpecProof" width="120" />
2
2
 
3
3
  # SpecProof
4
4
 
5
- A standalone Next.js app that renders an audit view of any repo's API test coverage: every OpenAPI operation and response status, cross-examined against the repo's test assertions. Click a stamped verdict to read the test itself.
5
+ Audit your API test coverage against your OpenAPI spec. SpecProof cross-examines every operation and response status in the spec against your test suite's assertions and renders the verdicts as a browsable report. Click any verdict to read the test that proves it, or see exactly what's untested.
6
6
 
7
- Point it at a repo the same way OpenAPI tooling works when installed into one — no repo-specific configuration is baked in:
8
-
9
- - **Sources of truth** (read from the target repo, `SPECPROOF_REPO`): the OpenAPI spec (auto-discovered `openapi*.json` / `swagger*.json`, or set `SPECPROOF_SPEC`) + the repo's `*.test.ts` files, parsed from `describe("METHOD /path")` blocks and `.status).toBe(NNN)` assertions. `{param}`, `[param]`, and `:param` path segments are treated as equivalent when matching.
10
- - **Generator**: `scripts/generate-proof.ts` compiles them into `app/proof.generated.json` — checked in, deterministic, regenerated automatically before `dev`/`build`.
11
- - **Consumer**: the app renders the checked-in artifact; no target checkout is needed to build or deploy.
12
- - **Drift guard**: `app/proof-contract.test.ts` fails CI when the artifact is stale relative to the spec/tests, when the proof doesn't cover exactly the spec's operations, or when a quoted snippet no longer points at a real `it()` block. It self-skips when no target repo is configured.
13
-
14
- ## Setup
7
+ ## Quick start
15
8
 
16
9
  Requires [Bun](https://bun.sh).
17
10
 
18
11
  ```bash
19
- bun install
20
- SPECPROOF_REPO=/path/to/your-repo bun run dev # compiles the proof, then serves on http://localhost:3001
12
+ bun add -d specproof # or npm install -D specproof
13
+ bunx specproof dev # audit the current repo http://localhost:3001
21
14
  ```
22
15
 
23
- `SPECPROOF_REPO` defaults to the current working directory, so running the generator from inside the target repo needs no configuration at all. If the spec isn't named `openapi*.json` / `swagger*.json`, point `SPECPROOF_SPEC` at it (relative to the repo root).
16
+ The OpenAPI spec is auto-discovered (`openapi*.json` / `swagger*.json`); tests are your `*.test.ts` / `*.test.tsx` / `*.test.js` files.
24
17
 
25
- ## Commands
18
+ ## CLI
26
19
 
27
20
  ```bash
28
- bun run dev # generate:proof + next dev (port 3001)
29
- bun run build # generate:proof + next build
30
- bun run start # serve the production build (port 3001)
31
- bun run generate:proof # regenerate app/proof.generated.json only
32
- bun run test:unit # contract tests (self-skip without a configured target repo)
33
- bun run lint # ESLint + tsc
21
+ specproof generate [--out proof.json] [--check] # compile the coverage proof
22
+ specproof dev # generate + serve the report
23
+ specproof build && specproof start # production build + serve
34
24
  ```
35
25
 
36
- ## Updating the proof
26
+ | Option | Env var | Meaning |
27
+ | --- | --- | --- |
28
+ | `--repo <path>` | `SPECPROOF_REPO` | Repo to audit (default: current directory) |
29
+ | `--spec <path>` | `SPECPROOF_SPEC` | Spec file, relative to the repo root, when auto-discovery doesn't apply |
30
+ | `--out <path>` | `SPECPROOF_OUT` | Where `generate` writes the proof |
31
+ | `--port <port>` | | Port for `dev` / `start` (default: 3001) |
37
32
 
38
- When the target repo's tests or OpenAPI spec change, the checked-in artifact goes stale and the contract tests fail. The fix:
33
+ ## Drift guard in CI
34
+
35
+ Commit the proof next to your code, then have CI fail whenever the spec or tests change without regenerating it:
39
36
 
40
37
  ```bash
41
- SPECPROOF_REPO=/path/to/your-repo bun run generate:proof
42
- git add app/proof.generated.json && git commit
38
+ specproof generate --out specproof.json # regenerate + commit
39
+ specproof generate --out specproof.json --check # CI: exits 1 when stale
43
40
  ```
44
41
 
45
42
  ## Conventions the parser relies on
46
43
 
47
- - Test suites titled `describe("METHOD /path")` (`GET|POST|PUT|DELETE|PATCH`) the path is matched against the spec's paths.
44
+ - Test suites titled `describe("METHOD /path")` (`GET|POST|PUT|DELETE|PATCH`). The path is matched against the spec's paths; `{param}`, `[param]`, and `:param` segments are equivalent.
48
45
  - Status assertions written as `.status).toBe(NNN)` or `.status).toEqual(NNN)`.
49
46
  - Prettier-consistent formatting: `it()`/`test()` blocks are extracted by indentation, not a full parser.
50
47
 
51
- ## Releasing
48
+ ## Development
49
+
50
+ ```bash
51
+ bun install
52
+ bun run dev # audits the bundled example/ fixture on port 3001
53
+ bun run test:unit # analyzer unit tests + proof drift guards
54
+ bun run lint # ESLint + tsc
55
+ ```
56
+
57
+ Point a checkout at a real repo with `SPECPROOF_REPO=/path/to/repo bun run dev`. See [CLAUDE.md](CLAUDE.md) for architecture notes.
58
+
59
+ ## License
52
60
 
53
- Merges to `main` run the [Release workflow](.github/workflows/release.yml), which publishes the package to npm whenever `package.json`'s version isn't on the registry yet (requires the `NPM_TOKEN` repository secret).
61
+ MIT
@@ -370,8 +370,8 @@ export function CoverageProof({
370
370
  >
371
371
  <p className="text-sm font-semibold tracking-[0.14em]">NO API DEFINITION PROVIDED</p>
372
372
  <div className="mt-4 flex flex-col gap-1 text-xs text-muted-foreground">
373
- <code className="text-foreground/70">SPECPROOF_REPO=/path/to/repo</code>
374
- <code className="text-foreground/70">bun run generate:proof</code>
373
+ <code className="text-foreground/70">specproof dev --repo /path/to/repo</code>
374
+ <code className="text-foreground/70">specproof generate --spec path/to/openapi.json</code>
375
375
  </div>
376
376
  </section>
377
377
  ) : (
package/next.config.js CHANGED
@@ -1,6 +1,9 @@
1
1
  /** @type {import('next').NextConfig} */
2
2
  const nextConfig = {
3
3
  reactStrictMode: false,
4
+ // The app is built from source even when installed under node_modules
5
+ // (`specproof dev`/`build`); without this, Next's loaders skip its files.
6
+ transpilePackages: ['specproof'],
4
7
  };
5
8
 
6
9
  export default nextConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specproof",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Audit view of a repo's API test coverage: every OpenAPI operation and response status, cross-examined against the repo's test assertions.",
5
5
  "license": "MIT",
6
6
  "repository": "github:Durable-Quality/specproof",
@@ -13,6 +13,22 @@
13
13
  "audit"
14
14
  ],
15
15
  "type": "module",
16
+ "bin": {
17
+ "specproof": "scripts/cli.ts"
18
+ },
19
+ "files": [
20
+ "app",
21
+ "components",
22
+ "lib",
23
+ "scripts",
24
+ "!app/proof-contract.test.ts",
25
+ "!app/proof.generated.json",
26
+ "!lib/api-test-coverage.test.ts",
27
+ "next.config.js",
28
+ "postcss.config.js",
29
+ "tailwind.config.ts",
30
+ "tsconfig.json"
31
+ ],
16
32
  "scripts": {
17
33
  "build": "bun run generate:proof && bunx next build",
18
34
  "dev": "bun run generate:proof && bunx next dev --port 3001",
@@ -28,29 +44,29 @@
28
44
  "dependencies": {
29
45
  "@radix-ui/react-accordion": "^1.2.16",
30
46
  "@radix-ui/react-dialog": "^1.1.15",
47
+ "@types/node": "24.1.0",
48
+ "@types/react": "^19.2.0",
49
+ "@types/react-dom": "^19.2.0",
50
+ "autoprefixer": "^10.4.22",
31
51
  "class-variance-authority": "^0.7.1",
32
52
  "clsx": "^2.1.1",
33
53
  "lucide-react": "^0.562.0",
34
54
  "next": "^16.0.7",
55
+ "postcss": "^8.5.6",
56
+ "postcss-import": "^16.1.1",
35
57
  "react": "19.2.3",
36
58
  "react-dom": "19.2.3",
37
59
  "tailwind-merge": "^3.4.0",
38
- "tailwindcss-animate": "^1.0.7"
60
+ "tailwindcss": "^3.4.18",
61
+ "tailwindcss-animate": "^1.0.7",
62
+ "typescript": "^5.9.3"
39
63
  },
40
64
  "devDependencies": {
41
65
  "@eslint/js": "^9.39.1",
42
- "@types/node": "24.1.0",
43
- "@types/react": "^19.2.0",
44
- "@types/react-dom": "^19.2.0",
45
- "autoprefixer": "^10.4.22",
46
66
  "eslint": "^9.39.1",
47
67
  "eslint-plugin-react": "^7.37.5",
48
68
  "eslint-plugin-react-hooks": "^7.0.1",
49
69
  "globals": "^16.5.0",
50
- "postcss": "^8.5.6",
51
- "postcss-import": "^16.1.1",
52
- "tailwindcss": "^3.4.18",
53
- "typescript": "^5.9.3",
54
70
  "typescript-eslint": "^8.48.1",
55
71
  "vitest": "^4.1.8"
56
72
  }
package/scripts/cli.ts ADDED
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env bun
2
+ // SpecProof CLI — the entrypoint when the package is installed into a target
3
+ // repo (`bunx specproof <command>`). Audits the repo it is run from (override
4
+ // with --repo / SPECPROOF_REPO) while Next.js dev/build/start run against the
5
+ // SpecProof package directory itself, wherever it is installed.
6
+
7
+ import path from 'path';
8
+ import { createRequire } from 'module';
9
+ import { spawnSync } from 'child_process';
10
+ import { fileURLToPath } from 'url';
11
+
12
+ const appRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
13
+
14
+ const USAGE = `specproof — audit a repo's API test coverage against its OpenAPI spec
15
+
16
+ Usage: specproof <command> [options]
17
+
18
+ Commands:
19
+ generate Compile the coverage proof from the target repo's spec + tests
20
+ dev generate, then serve the audit view with next dev
21
+ build generate, then production-build the audit view
22
+ start Serve the production build
23
+
24
+ Options:
25
+ --repo <path> Repo to audit (default: current directory; env SPECPROOF_REPO)
26
+ --spec <path> OpenAPI spec, relative to the repo root when the auto-discovery
27
+ of openapi*.json / swagger*.json doesn't apply (env SPECPROOF_SPEC)
28
+ --out <path> generate only: where to write the proof (default: the app's
29
+ bundled artifact; env SPECPROOF_OUT)
30
+ --check generate only: verify the proof at --out is up to date instead
31
+ of writing — exits 1 on drift (the CI guard)
32
+ --port <port> dev/start only: port to serve on (default: 3001)
33
+ `;
34
+
35
+ function fail(message: string): never {
36
+ console.error(`specproof: ${message}\n\n${USAGE}`);
37
+ process.exit(1);
38
+ }
39
+
40
+ const [command, ...rest] = process.argv.slice(2);
41
+
42
+ let port = '3001';
43
+ const generateArgs: string[] = [];
44
+ for (let i = 0; i < rest.length; i++) {
45
+ const arg = rest[i];
46
+ const value = () => {
47
+ const v = rest[++i];
48
+ if (!v) fail(`${arg} requires a value`);
49
+ return v;
50
+ };
51
+ // --repo/--spec become env vars so the analyzer (which reads them at import
52
+ // time) picks them up when the generator is dynamically imported below.
53
+ if (arg === '--repo') process.env.SPECPROOF_REPO = value();
54
+ else if (arg === '--spec') process.env.SPECPROOF_SPEC = value();
55
+ else if (arg === '--port') port = value();
56
+ else if (arg === '--out' || arg === '--check') {
57
+ if (command !== 'generate') fail(`${arg} only applies to the generate command`);
58
+ generateArgs.push(arg);
59
+ if (arg === '--out') generateArgs.push(value());
60
+ } else fail(`unknown option: ${arg}`);
61
+ }
62
+
63
+ function generate(argv: string[]): number {
64
+ // Dynamic import so the env vars set above are read, not the values at CLI
65
+ // startup. require() keeps this synchronous under Bun and Node alike.
66
+ const require = createRequire(import.meta.url);
67
+ const { parseGenerateArgs, runGenerate } = require('./generate-proof') as
68
+ typeof import('./generate-proof');
69
+ return runGenerate(parseGenerateArgs(argv));
70
+ }
71
+
72
+ function nextCli(...args: string[]): number {
73
+ const require = createRequire(import.meta.url);
74
+ // Resolve Next's bin relative to this package so the CLI works no matter
75
+ // where the consumer's package manager hoisted the dependency tree.
76
+ const nextBin = require.resolve('next/dist/bin/next', { paths: [appRoot] });
77
+ const result = spawnSync(process.execPath, [nextBin, ...args], {
78
+ stdio: 'inherit',
79
+ env: process.env,
80
+ });
81
+ return result.status ?? 1;
82
+ }
83
+
84
+ switch (command) {
85
+ case 'generate':
86
+ process.exit(generate(generateArgs));
87
+ break;
88
+ case 'dev':
89
+ case 'build': {
90
+ // dev/build always refresh the bundled artifact — it is what the app
91
+ // renders. A consumer's committed copy (--out) is generate's concern.
92
+ const generated = generate([]);
93
+ if (generated !== 0) process.exit(generated);
94
+ process.exit(
95
+ command === 'dev'
96
+ ? nextCli('dev', appRoot, '--port', port)
97
+ : nextCli('build', appRoot)
98
+ );
99
+ break;
100
+ }
101
+ case 'start':
102
+ process.exit(nextCli('start', appRoot, '--port', port));
103
+ break;
104
+ case 'help':
105
+ case '--help':
106
+ case '-h':
107
+ case undefined:
108
+ console.log(USAGE);
109
+ process.exit(command ? 0 : 1);
110
+ break;
111
+ default:
112
+ fail(`unknown command: ${command}`);
113
+ }
@@ -4,16 +4,24 @@
4
4
  // SPECPROOF_SPEC).
5
5
  //
6
6
  // Run via `bun run generate:proof` (also runs automatically before
7
- // `bun run dev` / `bun run build`). The output is checked in at
8
- // app/proof.generated.json; the proof-contract test fails if it goes stale
9
- // relative to the spec or the tests. When no target spec is found, the
10
- // existing artifact is left untouched so the app still builds and renders.
7
+ // `bun run dev` / `bun run build`), or `specproof generate` when installed.
8
+ // The output defaults to the checked-in artifact at app/proof.generated.json;
9
+ // override with --out / SPECPROOF_OUT to write the proof into the audited
10
+ // repo instead (e.g. to commit it there). --check verifies the output file is
11
+ // up to date without writing — the CI drift guard. When no target spec is
12
+ // found, the existing artifact is left untouched so the app still builds and
13
+ // renders.
11
14
 
12
15
  import fs from 'fs';
13
16
  import path from 'path';
14
17
  import { fileURLToPath } from 'url';
15
18
 
16
- import { buildCoverageReport, resolveSpecPath, TARGET_REPO_ROOT } from '../lib/api-test-coverage';
19
+ import {
20
+ buildCoverageReport,
21
+ resolveSpecPath,
22
+ TARGET_REPO_ROOT,
23
+ type CoverageReport,
24
+ } from '../lib/api-test-coverage';
17
25
 
18
26
  const appRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
19
27
 
@@ -26,24 +34,99 @@ export function buildProof(): Record<string, unknown> {
26
34
  return buildCoverageReport() as unknown as Record<string, unknown>;
27
35
  }
28
36
 
29
- const isMain =
30
- process.argv[1] &&
31
- path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
37
+ export interface GenerateOptions {
38
+ /** Where to write (or, with check, what to verify). Resolved against cwd.
39
+ * Defaults to SPECPROOF_OUT, then the app's checked-in artifact. */
40
+ out?: string;
41
+ /** Verify the file at `out` matches a fresh proof instead of writing. */
42
+ check?: boolean;
43
+ }
44
+
45
+ /** Parses --out <path> / --check from a generate argv slice. Throws on
46
+ * anything unrecognized so callers surface their own usage text. */
47
+ export function parseGenerateArgs(argv: string[]): GenerateOptions {
48
+ const options: GenerateOptions = {};
49
+ for (let i = 0; i < argv.length; i++) {
50
+ const arg = argv[i];
51
+ if (arg === '--check') {
52
+ options.check = true;
53
+ } else if (arg === '--out') {
54
+ const value = argv[++i];
55
+ if (!value) throw new Error('--out requires a path');
56
+ options.out = value;
57
+ } else {
58
+ throw new Error(`unknown argument: ${arg}`);
59
+ }
60
+ }
61
+ return options;
62
+ }
63
+
64
+ export function runGenerate(options: GenerateOptions = {}): number {
65
+ const outPath = path.resolve(
66
+ options.out ?? process.env.SPECPROOF_OUT ?? GENERATED_PROOF_PATH
67
+ );
68
+ const relative = path.relative(process.cwd(), outPath);
69
+ const outLabel = relative && !relative.startsWith('..') ? relative : outPath;
32
70
 
33
- if (isMain) {
34
71
  if (!resolveSpecPath()) {
35
- console.warn(
36
- `generate-proof: no OpenAPI spec found under ${TARGET_REPO_ROOT} — ` +
37
- 'set SPECPROOF_REPO to the repo to audit (or SPECPROOF_SPEC to the spec file); keeping the existing proof'
38
- );
39
- process.exit(0);
72
+ const hint =
73
+ `no OpenAPI spec found under ${TARGET_REPO_ROOT} — ` +
74
+ 'set SPECPROOF_REPO to the repo to audit (or SPECPROOF_SPEC to the spec file)';
75
+ if (options.check) {
76
+ // A drift check with nothing to check against is a failure, not a skip:
77
+ // CI asked us to verify the proof and we can't.
78
+ console.error(`generate-proof: ${hint}`);
79
+ return 1;
80
+ }
81
+ if (!fs.existsSync(outPath)) {
82
+ // The artifact is not published with the package, so a fresh install has
83
+ // no proof at all. Write an empty one: the app needs the file to build,
84
+ // and an empty report renders the "no API definition" state.
85
+ const empty: CoverageReport = {
86
+ tags: [],
87
+ operationCount: 0,
88
+ coveredCount: 0,
89
+ totalCount: 0,
90
+ untestedOperations: 0,
91
+ };
92
+ fs.writeFileSync(outPath, JSON.stringify(empty, null, 2) + '\n');
93
+ console.warn(`generate-proof: ${hint}; wrote an empty proof to ${outLabel}`);
94
+ return 0;
95
+ }
96
+ console.warn(`generate-proof: ${hint}; keeping the existing proof`);
97
+ return 0;
40
98
  }
99
+
41
100
  const proof = buildProof();
42
101
  const operationCount = (proof.operationCount as number) ?? 0;
43
102
  if (operationCount === 0) {
44
103
  console.error('generate-proof: no operations found in the OpenAPI spec — refusing to write an empty proof');
45
- process.exit(1);
104
+ return 1;
46
105
  }
47
- fs.writeFileSync(GENERATED_PROOF_PATH, JSON.stringify(proof, null, 2) + '\n');
48
- console.log(`generate-proof: wrote ${path.relative(appRoot, GENERATED_PROOF_PATH)} (${operationCount} operations)`);
106
+ const serialized = JSON.stringify(proof, null, 2) + '\n';
107
+
108
+ if (options.check) {
109
+ const existing = fs.existsSync(outPath) ? fs.readFileSync(outPath, 'utf8') : null;
110
+ if (existing !== serialized) {
111
+ console.error(
112
+ `generate-proof: ${outLabel} is stale relative to the spec/tests — ` +
113
+ 'regenerate it (specproof generate) and commit the result'
114
+ );
115
+ return 1;
116
+ }
117
+ console.log(`generate-proof: ${outLabel} is up to date (${operationCount} operations)`);
118
+ return 0;
119
+ }
120
+
121
+ fs.writeFileSync(outPath, serialized);
122
+ console.log(`generate-proof: wrote ${outLabel} (${operationCount} operations)`);
123
+ return 0;
124
+ }
125
+
126
+ const isMain =
127
+ process.argv[1] &&
128
+ path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
129
+
130
+ if (isMain) {
131
+ process.exit(runGenerate(parseGenerateArgs(process.argv.slice(2))));
49
132
  }
@@ -1,49 +0,0 @@
1
- name: 🚀 Release
2
-
3
- on:
4
- push:
5
- branches: main
6
-
7
- permissions:
8
- contents: read
9
- id-token: write # npm provenance
10
-
11
- jobs:
12
- publish:
13
- name: 🚀 Publish to npm
14
- runs-on: ubuntu-latest
15
-
16
- env:
17
- NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
18
-
19
- steps:
20
- - name: 📥 Checkout code
21
- uses: actions/checkout@v5
22
-
23
- - name: 🟢 Setup Node
24
- uses: actions/setup-node@v4
25
- with:
26
- node-version: 22
27
- registry-url: https://registry.npmjs.org
28
-
29
- - name: 🔎 Skip if this version is already on npm
30
- id: check
31
- run: |
32
- NAME=$(node -p "require('./package.json').name")
33
- VERSION=$(node -p "require('./package.json').version")
34
- if npm view "$NAME@$VERSION" version >/dev/null 2>&1; then
35
- echo "::notice::$NAME@$VERSION is already published — nothing to do"
36
- echo "published=true" >> "$GITHUB_OUTPUT"
37
- else
38
- echo "published=false" >> "$GITHUB_OUTPUT"
39
- fi
40
-
41
- - name: ⚠️ Warn when NPM_TOKEN is missing
42
- if: steps.check.outputs.published == 'false' && env.NPM_TOKEN == ''
43
- run: echo "::warning::NPM_TOKEN secret is not set — skipping npm publish. Add it under Settings → Secrets and variables → Actions."
44
-
45
- - name: 🚀 Publish to npm
46
- if: steps.check.outputs.published == 'false' && env.NPM_TOKEN != ''
47
- run: npm publish --provenance --access public
48
- env:
49
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -1,69 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- pull_request:
5
- branches: main
6
- merge_group:
7
- push:
8
- branches: main
9
- workflow_dispatch:
10
-
11
- jobs:
12
- build:
13
- name: Build
14
- runs-on: ubuntu-latest
15
- steps:
16
- - name: 📥 Checkout code
17
- uses: actions/checkout@v5
18
-
19
- - name: 🍞 Setup Bun
20
- uses: oven-sh/setup-bun@v2
21
- with:
22
- bun-version: latest
23
-
24
- - name: 🧰 Install dependencies
25
- run: bun install
26
-
27
- - name: 🏗️ Build
28
- run: bun run build
29
-
30
- lint:
31
- name: Lint
32
- runs-on: ubuntu-latest
33
- steps:
34
- - name: 📥 Checkout code
35
- uses: actions/checkout@v5
36
-
37
- - name: 🍞 Setup Bun
38
- uses: oven-sh/setup-bun@v2
39
- with:
40
- bun-version: latest
41
-
42
- - name: 🧰 Install dependencies
43
- run: bun install
44
-
45
- - name: 🧹 ESLint
46
- run: bun run lint:es
47
-
48
- - name: 🔎 Typecheck
49
- run: bun run lint:ts
50
-
51
- test:
52
- name: Test
53
- runs-on: ubuntu-latest
54
- env:
55
- NODE_ENV: test
56
- steps:
57
- - name: 📥 Checkout code
58
- uses: actions/checkout@v5
59
-
60
- - name: 🍞 Setup Bun
61
- uses: oven-sh/setup-bun@v2
62
- with:
63
- bun-version: latest
64
-
65
- - name: 🧰 Install dependencies
66
- run: bun install
67
-
68
- - name: 🧾 Run unit & contract tests
69
- run: bun run test:unit
package/CHANGELOG.md DELETED
@@ -1,47 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to this project will be documented in this file.
4
-
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
-
8
- ## [0.2.0] - 2026-07-14
9
-
10
- ### Added
11
- - Bundled `example/` demo target: the fictional TaskFlow API (7 operations) plus fixture test files, audited automatically when no `SPECPROOF_REPO` is set — the app now renders a meaningful proof out of the box, demonstrating every verdict state.
12
- - Unit test suite for the coverage analyzer (`lib/api-test-coverage.test.ts`, 30 tests): path-param normalization, snippet extraction, test-file parsing, spec discovery, evidence merging, and report assembly with independently computed expectations.
13
- - CI workflow with separate `Build`, `Lint`, and `Test` jobs, running on pull requests and in the merge queue.
14
- - Merge queue ruleset on `main`: PRs required, all CI checks must pass, no direct pushes.
15
- - Marketing showcase page (`marketing/`) and brand assets (`public/`), with a banner in the README.
16
-
17
- ### Changed
18
- - Releases now publish only after a PR merges to `main` through the merge queue; the manual release trigger was removed.
19
- - `buildCoverageReport()` accepts an optional target repo root, and the analyzer's parsing internals are exported for testing.
20
- - Larger SpecProof masthead in the coverage view.
21
-
22
- ## [0.1.0] - 2026-07-13
23
-
24
- ### Added
25
- - Initial public release under new name SpecProof (npm package `specproof`).
26
- - GitHub Actions release workflow: automatically publishes to npm when package version is updated.
27
- - Comprehensive project documentation in CLAUDE.md.
28
- - Generalized spec/test discovery: auto-discovers OpenAPI specs (`openapi*.json`, `swagger*.json`) and test files in any target repo; no longer OmniLens-specific.
29
- - Support for explicit spec path via `SPECPROOF_SPEC` environment variable.
30
-
31
- ### Changed
32
- - **Breaking**: Renamed environment variable `OMNILENS_REPO` → `SPECPROOF_REPO` for specifying the target repo to audit.
33
- - **Breaking**: Renamed environment variable `OMNILENS_SPEC` → `SPECPROOF_SPEC` for specifying the OpenAPI spec path.
34
- - Renamed npm package from `test-ledger` to `specproof`.
35
- - Renamed generator script `scripts/generate-ledger.ts` → `scripts/generate-proof.ts` and npm command `generate:ledger` → `generate:proof`.
36
- - Renamed generated artifact `app/ledger.generated.json` → `app/proof.generated.json`.
37
- - Renamed contract test file `app/ledger-contract.test.ts` → `app/proof-contract.test.ts`.
38
- - Renamed stylesheet `app/ledger.css` → `app/proof.css`.
39
- - Renamed React component `CoverageLedger` → `CoverageProof`.
40
- - Updated CSS class prefix `lg-` → `sp-` throughout the codebase.
41
- - Generalized code comments and documentation from OmniLens-specific to target-agnostic language.
42
- - GitHub Actions workflow `test-unit.yml` renamed to "Proof Contract Tests" with updated environment variable references.
43
- - Removed `"private": true` from package.json to allow npm publishing.
44
- - Updated package.json metadata: added `repository`, `homepage`, `description`, and `keywords`.
45
-
46
- ### Removed
47
- - OmniLens-specific hardcoded paths and configuration.