Thank you for helping improve ShadowAPI. Keep changes focused, deterministic, and safe for developers who may be inspecting sensitive HTTP traffic.
Requirements:
- Node.js 22 or newer
- pnpm through Corepack
- A native build toolchain when a prebuilt
better-sqlite3binary is unavailable
corepack enable
pnpm install --frozen-lockfile
pnpm buildUseful development commands:
pnpm format:check
pnpm lint
pnpm typecheck
pnpm test
pnpm buildapps/cli: command parsing, output, and process lifecyclepackages/core: project-level orchestration and configurationpackages/proxy: HTTP forwarding and capture integrationpackages/recorder: header/body redaction and capture limitspackages/storage: SQLite implementation ofTrafficStorepackages/inference: deterministic route and schema inferencepackages/openapi: OpenAPI 3.1 renderingpackages/mock: exact and normalized-route response replaypackages/diff: normalized contract comparisonpackages/shared: cross-package data contractsexamples/express-api: synthetic local test targetexamples/generated: reviewed, synthetic generated output only
Respect these boundaries. The CLI should not contain inference logic, inference should not depend on storage or presentation, and diffing should operate on normalized models rather than database details.
- Add a regression test for every bug fix.
- Add focused unit tests for deterministic logic.
- Add transport/storage integration coverage when behavior crosses a process or persistence boundary.
- Test both the expected case and the important negative case.
- Never use captures from a real system as fixtures.
- Run the complete verification suite before opening a pull request.
Inference tests must explicitly cover the evidence boundary being changed. Depending on the change, verify:
- too few samples do not produce a strong constraint;
- identifiers and high-cardinality values remain unconstrained;
- optional fields stay optional when absent from some observations;
nullis retained only when observed;- type conflicts are represented conservatively;
- known static paths are not normalized as identifiers;
- integer, UUID, ULID, ObjectId, hash, timestamp, and nanoid-like paths remain deterministic;
- repeated runs over the same observations produce the same schema apart from
generatedAt.
Avoid fixtures that accidentally look like real credentials, tokens, email addresses, or customer data.
Use separate terminals.
-
Start the synthetic API:
pnpm example
-
Start the proxy:
pnpm shadowapi --target http://localhost:3000
-
Generate traffic through the proxy:
curl -i http://localhost:9000/users/1 curl -i http://localhost:9000/users/2 curl -i http://localhost:9000/users/999 curl -i http://localhost:9000/users/me
-
Verify learned output:
pnpm shadowapi inspect pnpm shadowapi export --output /tmp/shadowapi-openapi.yaml -
Stop the proxy and verify the learned mock without the backend:
pnpm shadowapi mock curl -i http://localhost:4010/users/1
-
Verify contract diffing:
pnpm shadowapi snapshot # restart the proxy and exercise the changed synthetic API pnpm shadowapi diff
Expected diff exit codes are 0 for no breaking changes, 1 for breaking changes, and 2 for execution errors. Remove local .shadowapi/ state when the scenario is complete.
- Explain the problem, user-visible behavior, and important tradeoffs.
- Keep the diff reviewable and separate unrelated refactors.
- Include tests and the commands used to verify the change.
- Update README, configuration examples, architecture, roadmap, or changelog when behavior changes.
- Confirm
pnpm format:check,pnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass. - Do not commit generated
dist/, coverage, Turbo caches, local databases, HAR files, raw traffic, snapshots from private APIs, credentials, cookies, tokens, or personal data.
Security vulnerabilities must be reported privately according to SECURITY.md, not through a public issue or pull request.
By contributing, you agree that your contribution is licensed under Apache-2.0.