# Contributing

> prepublishOnly gates, Node engine, Apache-2.0 package metadata, and the files published as the vwaffle CLI.

- Repository: jaredpalmer/vwaffle
- GitHub: https://github.com/jaredpalmer/vwaffle
- Human docs: https://grok-wiki.com/public/docs/jaredpalmer-vwaffle-7983cb893581
- Complete Markdown: https://grok-wiki.com/public/docs/jaredpalmer-vwaffle-7983cb893581/llms-full.txt

## Source Files

- `package.json`
- `src/index.ts`
- `src/config.test.ts`
- `README.md`

---

---
title: "Contributing"
description: "prepublishOnly gates, Node engine, Apache-2.0 package metadata, and the files published as the vwaffle CLI."
---

`package.json` is the publish contract for **vwaffle**. `prepublishOnly` runs `typecheck`, then `test`, then `build` before a publish; `engines.node` is `>=18`; `license` is `Apache-2.0`; and `files` ships `dist`, `README.md`, and `LICENSE`. The installed CLI is `bin.vwaffle` → `./dist/index.js`, produced from `src/index.ts` by `bun build src/index.ts --outdir dist --target node`.

## Package metadata

| Field | Value |
| --- | --- |
| `name` | `vwaffle` |
| `version` | `0.1.0` |
| `description` | Config-as-code for Vercel WAF, Security & Bot Protection. Pull, diff, and apply firewall rules from a versioned JSON file. |
| `license` | `Apache-2.0` |
| `author` | Jared Palmer |
| `type` | `module` |
| `repository` | `git+https://github.com/jaredpalmer/vwaffle.git` |
| `bin.vwaffle` | `./dist/index.js` |
| `engines.node` | `>=18` |

`vwaffle version`, `-v`, and `--version` print `VERSION` imported from `package.json`. Changing `version` is what the CLI reports.

Keywords on the package are `vercel`, `firewall`, `waf`, `security`, `bot-protection`, `config-as-code`, `infrastructure-as-code`, and `cli`.

There are no production `dependencies`. Workspace tools are `devDependencies` only: `@types/bun` `^1.2.0` and `typescript` `^5.6.0`.

## Node engine

<ParamField body="engines.node" type="string" required>
`>=18`. Applies to the published Node-targeted binary. Local scripts (`dev`, `test`, `typecheck`, `build`, `prepublishOnly`) are invoked with Bun.
</ParamField>

<Info>
Install and one-shot invocation for consumers is `npm install -g vwaffle`, `npx vwaffle help`, or `bunx vwaffle help`. The published entry is still a Node binary (`#!/usr/bin/env node` on `src/index.ts`, `--target node` on `build`).
</Info>

## Published files

`files` is the npm allowlist for the published tarball:

<ParamField body="files" type="string[]" required>
`dist`, `README.md`, `LICENSE`
</ParamField>

<ParamField body="bin.vwaffle" type="string" required>
`./dist/index.js` — the `vwaffle` executable after `npm install -g vwaffle`.
</ParamField>

:::files
published package (files)
├── dist/index.js    bun build of src/index.ts --target node
├── README.md
└── LICENSE

workspace sources used by the gates
├── package.json     metadata, engines, scripts, bin
├── src/index.ts     CLI entry (#!/usr/bin/env node)
└── src/config.test.ts
:::

`build` writes the Node bundle to `dist/` from `src/index.ts`. `README.md` and `LICENSE` ship next to that bundle. Source, tests, and `devDependencies` are not listed in `files`.

## prepublishOnly gates

```json
"prepublishOnly": "bun run typecheck && bun run test && bun run build"
```

npm runs this lifecycle script before publish. The `&&` chain is fail-fast: a typecheck or test failure skips `build` and stops the publish.

| Order | Script | Command | Pass signal |
| --- | --- | --- | --- |
| 1 | `typecheck` | `tsc --noEmit` | `tsc` exits 0 |
| 2 | `test` | `bun test` | Bun test runner exits 0 |
| 3 | `build` | `bun build src/index.ts --outdir dist --target node` | `dist/` bundle written |

`package.json` does not define lint, format, or a `publish` script. The only scripted release gates are the three commands above.

## Local development

The README development path uses Bun:

<Steps>
<Step title="Install workspace dependencies">
```sh
bun install
```
</Step>
<Step title="Run the CLI from source">
```sh
bun run dev help
```

`dev` is `bun run src/index.ts`. Pass any CLI command after `dev` (`help`, `init`, `pull`, `plan`, `apply`, `version`).
</Step>
<Step title="Run the same gates as prepublishOnly">
```sh
bun run typecheck
bun test
bun run build
```

Or run the chained lifecycle script:

```sh
bun run prepublishOnly
```
</Step>
</Steps>

<Check>
`bun test` exits 0, `tsc --noEmit` exits 0, and `dist/index.js` exists after `bun run build`. `bun run dev version` prints the `package.json` `version` (`0.1.0` in this tree).
</Check>

## Tests the publish gate runs

`test` is `bun test`. `src/config.test.ts` uses `bun:test` and covers:

| Suite | Assertion |
| --- | --- |
| `interpolate` | Replaces `${VAR}` from the env, records secrets, leaves unset placeholders, and records missing vars with a JSON path such as `NOPE ($.a[0])` |
| `removeRulesWithMissingEnv` | Drops only rules that reference unset variables |
| `redacted` | Serialized output replaces secret values with `[REDACTED]` |
| `diff` | Identical configs return `NO_DRIFT`; differing configs include `-` / `+` lines |

A failing case in this file fails `bun test` and therefore `prepublishOnly`.

## License

<ParamField body="license" type="string" required>
`Apache-2.0`, also stated in the README. The `LICENSE` file is included in `files`.
</ParamField>

## Next

<CardGroup>
<Card title="Build and test" href="/build-and-test">
Bun scripts for typecheck, bun test, bun build of src/index.ts to dist, and bun run src/index.ts.
</Card>
<Card title="Installation" href="/installation">
Node >=18, global npm install, npx/bunx, and the published dist/index.js binary.
</Card>
<Card title="CLI reference" href="/cli-reference">
Commands and flags the published vwaffle binary accepts.
</Card>
<Card title="Overview" href="/overview">
What vwaffle exposes and the shortest pull / plan / apply path.
</Card>
</CardGroup>
