{"_id":"@aminhanifm/envparity","name":"@aminhanifm/envparity","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@aminhanifm/envparity","version":"0.1.0","description":"Detect drift between environment-variable references and .env.example.","type":"module","license":"MIT","author":{"name":"Amin Hanif"},"repository":{"type":"git","url":"git+https://github.com/aminhanifm/EnvParity.git"},"homepage":"https://github.com/aminhanifm/EnvParity#readme","bugs":{"url":"https://github.com/aminhanifm/EnvParity/issues"},"keywords":["environment-variables","dotenv","cli","github-action","static-analysis"],"engines":{"node":">=22"},"bin":{"envparity":"dist/cli/index.js"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"format":"prettier --write .","format:check":"prettier --check .","lint":"eslint .","typecheck":"tsc --noEmit","test":"vitest run","test:coverage":"vitest run --coverage","build":"tsup && node scripts/write-action-package.mjs","demo":"vite --config demo/vite.config.ts","build:demo":"vite build --config demo/vite.config.ts","check":"npm run format:check && npm run lint && npm run typecheck && npm test && npm run build && npm run build:demo"},"dependencies":{"@actions/core":"^1.11.1","commander":"^14.0.0","typescript":"^5.8.3"},"devDependencies":{"@types/node":"^24.0.0","@vitest/coverage-v8":"^3.2.4","eslint":"^9.30.0","prettier":"^3.6.2","tsup":"^8.5.0","typescript-eslint":"^8.35.1","vite":"^7.3.6","vitest":"^3.2.4"},"overrides":{"esbuild":">=0.28.1 <0.29","undici":">=6.27.0 <7"},"gitHead":"d154e1aee4a2912f7f44cf18eafaacb2399cd423","_id":"@aminhanifm/envparity@0.1.0","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-soPHSaRbcUjIBzyRUSVARwEAWyn+jkL5C9H+mFqI7pxFZvtv8a1bqWKUgeTgjWZGBXgmLf/XmqTVwzE8IzpQUA==","shasum":"b87dedb08e460c0dd2d2bc7c33e5c4916eaa5826","tarball":"https://registry.npmjs.org/@aminhanifm/envparity/-/envparity-0.1.0.tgz","fileCount":13,"unpackedSize":11008061,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDzes3l9IVHEoGgkeQaTkvGX1B1+rztBzmbRvs+F6e5tgIhAKOpWfJVnHI0AuzbMNDG8aAIM3voDghr2TQ8jx+N+AGi"}]},"_npmUser":{"name":"aminhanifm","email":"aminhanif24@gmail.com"},"directories":{},"maintainers":[{"name":"aminhanifm","email":"aminhanif24@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/envparity_0.1.0_1784634456165_0.6048323353783407"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-21T11:47:35.925Z","0.1.0":"2026-07-21T11:47:36.313Z","modified":"2026-07-21T11:47:36.548Z"},"maintainers":[{"name":"aminhanifm","email":"aminhanif24@gmail.com"}],"description":"Detect drift between environment-variable references and .env.example.","homepage":"https://github.com/aminhanifm/EnvParity#readme","keywords":["environment-variables","dotenv","cli","github-action","static-analysis"],"repository":{"type":"git","url":"git+https://github.com/aminhanifm/EnvParity.git"},"author":{"name":"Amin Hanif"},"bugs":{"url":"https://github.com/aminhanifm/EnvParity/issues"},"license":"MIT","readme":"# EnvParity\n\n[![CI](https://github.com/aminhanifm/EnvParity/actions/workflows/ci.yml/badge.svg)](https://github.com/aminhanifm/EnvParity/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)\n\n**[Try the interactive demo](https://aminhanifm.github.io/projects/EnvParity/)** · [View on npm](https://www.npmjs.com/package/@aminhanifm/envparity)\n\nEnvParity is a fast, deterministic CLI and GitHub Action that detects drift between environment-variable references in source code and the variables documented in an example file.\n\n## Why EnvParity exists\n\nA new `DATABASE_URL` in code can quietly break every fresh setup when `.env.example` is not updated. Old entries create the opposite problem: they imply configuration that no longer matters. EnvParity turns both kinds of drift into an explicit, reviewable report without reading real secret files.\n\n## What it checks\n\nEnvParity reports:\n\n- source variables missing from the example file (error);\n- duplicate or invalid example-file keys (error);\n- documented variables not detected in source (warning, or error with `--strict-unused`);\n- sensitive-looking names with public client prefixes (review warning);\n- unreadable supported source files (warning); and\n- dynamic references it cannot resolve (informational count).\n\nFindings, source references, and output arrays use deterministic ordering.\n\n## Installation\n\nEnvParity requires Node.js 22 or newer.\n\nOnce the package is published:\n\n```sh\nnpx @aminhanifm/envparity check\n```\n\nOr install it globally:\n\n```sh\nnpm install --global @aminhanifm/envparity\nenvparity check\n```\n\nTo use the current source checkout:\n\n```sh\nnpm install\nnpm run build\nnode dist/cli/index.js check\n```\n\n## Interactive demo\n\nThe static browser lab at [aminhanifm.github.io/projects/EnvParity](https://aminhanifm.github.io/projects/EnvParity/) runs the same parser, extractors, and classification engine against one editable virtual source file and one virtual `.env.example`. It is an educational preview of the report model, not a replacement for recursive CLI or Action scans.\n\nThe lab executes entirely in the browser. It has no backend, uploads, analytics, or external API calls, and it does not retain editor contents. Run it locally with `npm run demo` or build the nested-path artifact with `npm run build:demo`.\n\n## CLI usage\n\n```text\nenvparity check [directory]\n\nOptions:\n  --example <path>    Example file (default: .env.example)\n  --format <format>   terminal, json, or markdown (default: terminal)\n  --output <path>     Also write the report to a file\n  --strict-unused     Treat documented-but-unused variables as errors\n  --ignore <name>     Ignore a variable; repeat for multiple names\n  --no-color          Disable terminal colors\n  --help\n  --version\n```\n\nExamples:\n\n```sh\nenvparity check\nenvparity check ./apps/api --example .env.sample\nenvparity check --format json\nenvparity check --format markdown --output envparity-report.md\nenvparity check --strict-unused --ignore NODE_ENV --ignore CI\n```\n\nThe optional output file contains the selected format. JSON uses a versioned public schema with tool and schema versions, relative paths, summary counts, used and documented variables, findings, the dynamic-reference count, and skipped files.\n\n## GitHub Action\n\n```yaml\nname: Environment parity\n\non:\n  pull_request:\n  push:\n    branches: [main]\n\njobs:\n  envparity:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: aminhanifm/EnvParity@v1\n```\n\nOptional configuration:\n\n```yaml\n- uses: aminhanifm/EnvParity@v1\n  with:\n    working-directory: apps/api\n    example-file: .env.sample\n    strict-unused: true\n    ignore: NODE_ENV,CI\n```\n\nInputs are `working-directory`, `example-file`, `strict-unused`, and comma-separated `ignore`. Outputs are `result` (`passed` or `failed`), `error-count`, `warning-count`, and `verified-count`. The Action writes Markdown to the job summary, annotates error findings that have locations, and fails only for error-level findings. It does not post pull-request comments and needs no extra permissions.\n\n## Example output\n\n```text\nEnvParity\n\n× DATABASE_URL\n  Used in src/database.ts:8 but missing from .env.example.\n\n! LEGACY_API_KEY\n  Documented in .env.example but not detected in source code.\n\n! VITE_SECRET_KEY\n  Publicly exposed variable name contains a sensitive keyword. Review required; this is not proof that a secret is exposed.\n\n1 error, 2 warnings, 4 variables verified\n```\n\n## Finding definitions\n\n| Finding               | Default level | Meaning                                                                                                  |\n| --------------------- | ------------- | -------------------------------------------------------------------------------------------------------- |\n| Missing example key   | Error         | A static source reference has no matching documented key.                                                |\n| Duplicate example key | Error         | A valid key occurs more than once; all line numbers are retained.                                        |\n| Invalid example key   | Error         | The name does not match `[A-Za-z_][A-Za-z0-9_]*`, or the declaration lacks `=`.                          |\n| Unused example key    | Warning       | A documented key was not statically detected; `--strict-unused` promotes it to an error.                 |\n| Sensitive public key  | Warning       | A public prefix and sensitive keyword occur in the name. This is a review prompt, not proof of exposure. |\n| Unreadable source     | Warning       | A supported source path could not be read.                                                               |\n\n## Supported patterns\n\nJavaScript and TypeScript are parsed with the TypeScript compiler API:\n\n```js\nprocess.env.DATABASE_URL;\nprocess.env[\"DATABASE_URL\"];\nprocess.env[\"DATABASE_URL\"];\nimport.meta.env.VITE_API_URL;\nDeno.env.get(\"DATABASE_URL\");\n```\n\nPython uses a small conservative lexical extractor:\n\n```py\nos.getenv(\"DATABASE_URL\")\nos.environ.get(\"DATABASE_URL\")\nos.environ[\"DATABASE_URL\"]\nos.environ['DATABASE_URL']\n```\n\nThe scanner recursively considers `.js`, `.jsx`, `.mjs`, `.cjs`, `.ts`, `.tsx`, `.mts`, `.cts`, and `.py`. It ignores `.git`, `node_modules`, `dist`, `build`, `coverage`, `.next`, `.turbo`, and `vendor`. Symlinks are never followed. Supported files over 1 MiB and files containing a NUL byte are safely skipped.\n\nThe example parser accepts blank lines, comments, an optional `export` prefix, quoted or unquoted placeholder values, and empty values. It never expands or executes content.\n\n## Exit codes\n\n- `0`: no error-level findings;\n- `1`: one or more error-level findings;\n- `2`: invalid CLI usage, a missing input, or an unexpected execution failure.\n\nWarnings alone do not fail unless their documented strict option applies.\n\n## Security and privacy guarantees\n\nEnvParity scans source files locally and parses only the example/template path you explicitly select. It does not discover or read `.env`, `.env.local`, `.env.production`, or similar secret files. Values from the example file are discarded during parsing and never appear in terminal, JSON, Markdown, Action output, or annotations.\n\nEnvParity does not execute scanned code, expand variables, make network requests, send repository content anywhere, or include analytics or telemetry. Reports contain variable names and repository-relative source locations only. See [SECURITY.md](SECURITY.md) for the threat model and vulnerability-reporting process.\n\n## Architecture\n\n```text\nsrc/\n  core/          discovery, comparison, classification, stable report types\n  extractors/    TypeScript-AST and conservative Python extraction\n  parsers/       value-discarding example-file parser\n  reporters/     terminal, JSON, and Markdown renderers\n  cli.ts         Commander adapter\n  action.ts      @actions/core adapter\ndemo/            static one-file browser lab using the shared engine\ntest/            unit tests, integration tests, and fixtures\nexamples/        small passing and failing demonstration projects\ndist/            bundled release artifacts\n```\n\nCore analysis is independent from both delivery interfaces and can be imported from the package root.\n\nRuntime dependencies are intentionally limited:\n\n- `commander` parses the CLI contract and help output;\n- `typescript` provides syntax-aware JavaScript and TypeScript extraction; and\n- `@actions/core` integrates the same engine with GitHub Actions.\n\nThe Action bundle includes its dependencies because Action consumers do not run `npm install`.\n\n## Testing and development\n\n```sh\nnpm install\nnpm run format\nnpm run lint\nnpm run typecheck\nnpm test\nnpm run test:coverage\nnpm run build\nnpm run build:demo\nnpm run check\n```\n\nCoverage thresholds focus on the core analysis and example parser. CI runs formatting, linting, strict type checking, tests, and builds across Node.js 22 and 24 on Ubuntu and Windows, then verifies the committed distribution bundle.\n\n## Known limitations\n\n- Computed names such as `process.env[name]` are counted but cannot be compared.\n- Static detection is intentionally pattern-based; aliases, wrappers, destructuring, data flow, and framework-specific transforms are not resolved.\n- Python extraction is conservative lexical matching, not a full Python parser.\n- The browser demo checks one virtual source file; recursive filesystem discovery remains a CLI and Action feature.\n- Only the documented file extensions and environment APIs are supported.\n- EnvParity scans the one directory selected by the caller; it does not auto-discover monorepo packages.\n- Sensitive-name warnings inspect names only. EnvParity does not validate, detect, or prove the presence of secret values.\n\n## Roadmap\n\n- Configuration-file support for additional ignore rules and file-size limits.\n- Opt-in support for more established environment APIs and languages.\n- SARIF output for richer code-scanning integrations.\n- More monorepo documentation while retaining explicit scan roots.\n\nVersion 1 will not add automatic template edits, runtime validation, a plugin system, cloud scanning, or hosted services.\n\n## Contributing and license\n\nIssues and focused pull requests are welcome. Read [CONTRIBUTING.md](CONTRIBUTING.md) and the [Code of Conduct](CODE_OF_CONDUCT.md) first. EnvParity is available under the [MIT License](LICENSE).\n","readmeFilename":"README.md","_rev":"1-5c0221872008683935d4b592fde79f80"}