git clone https://github.com/jamiedavenport/policystack
cd policystack
mise install
mise run setupmise installs the Node.js version declared in
mise.toml and the pnpm version pinned in package.json. The setup task
installs workspace dependencies and configures the repository's git hooks.
vp config installs git hooks into .vite-hooks/ and points core.hooksPath at them:
- pre-commit —
vp staged: runs Oxfmt + Oxlint on staged files (auto-fixes in place) - pre-push —
vp run -r check-types: runstsc --noEmitacross all packages
This is a pnpm monorepo (workspaces declared in pnpm-workspace.yaml):
| Package | Description |
|---|---|
packages/sdk |
@policystack/sdk — public API (defineConfig, renderLlmsTxt) |
packages/core |
@policystack/core — compilation engine + consent runtime (./consent) |
packages/vite |
@policystack/vite — Vite plugin + opt-in consent scanner |
packages/cli |
@policystack/cli — install/configure CLI |
packages/renderers |
@policystack/renderers — shared Markdown/HTML/PDF render layer |
packages/scripts |
@policystack/scripts — consent-gated third-party script loaders |
packages/react |
@policystack/react — ./policy / ./consent / ./provider |
packages/vue |
@policystack/vue — ./policy / ./consent |
packages/svelte |
@policystack/svelte — ./policy / ./consent |
packages/solid |
@policystack/solid — ./consent (source-only) |
packages/angular |
@policystack/angular — ./consent |
apps/web |
policystack.dev — marketing + docs site (TanStack Start) |
apps/www |
openpolicy.sh redirect shim (Vercel redirects only) |
tooling/tsconfig |
@policystack/tooling — shared TypeScript base config |
# Run all tests
vp test
# Type-check all packages
vp run -r check-types
# Build all packages (produces dist/*.js + dist/*.d.ts)
vp run -r buildcore's package.json exports point to ./dist/ (not ./src/). After changing source files in packages/core, rebuild it before other packages will pick up the changes:
pnpm --filter @policystack/core run buildpnpm --filter @policystack/cli exec tsx src/cli.ts --helpPolicyStackConfig → compilePrivacyPolicy()/compileCookiePolicy() → section builders → DocumentSection[] → renderer → string
PolicyStackConfigis the single, flat public config. There is no intermediate per-document projection — the section builders read it directly and derive values (userRights,consentMechanism, the per-documentversion) at their point of use.- Section builders are functions
(config: PolicyStackConfig, t) => DocumentSection | null. Returningnullomits the section. compilePrivacyPolicy/compileCookiePolicygate emission viashouldEmit()and returnDocument | null.- Renderers (
@policystack/renderers) turn theDocumenttree into Markdown, HTML, or PDF.
- Add a builder function in
packages/core/src/documents/privacy.tsordocuments/cookie.ts. - Register it in the relevant
compile*Document()SECTION_BUILDERSarray. - Add any new fields to
PolicyStackConfigintypes.ts. - Write tests in
packages/core/src/*.test.ts.
# All packages
vp test
# Single package
pnpm --filter @policystack/core run testTests use Vitest via Vite+. Import test utilities from vite-plus/test (not vitest):
import { expect, test } from "vite-plus/test";Keep tests co-located or in the same package as the code they cover.
Oxfmt handles formatting and Oxlint handles linting, both via Vite+. The pre-commit hook auto-fixes staged files — you generally don't need to run it manually. To check manually:
vp check --fixTypeScript strict mode is on (verbatimModuleSyntax, moduleResolution: bundler). Use import type for type-only imports.
This repo uses Changesets for versioning.
-
After making your changes, run:
pnpm changeset
Follow the prompts to describe what changed and which packages are affected.
-
Commit the generated
.changeset/*.mdfile alongside your code changes. -
Open a pull request against
main. CI will validate your changes. -
Once merged, the GitHub Actions workflow automatically opens a "Version Packages" PR. Merging that PR publishes the updated packages to NPM.
pnpm publishrewritesworkspace:*references to the real published version on the way out.
Publishable packages (one fixed Changesets group — they version and publish together): @policystack/sdk, @policystack/core, @policystack/vite, @policystack/cli, @policystack/react, @policystack/vue, @policystack/svelte, @policystack/solid, @policystack/angular, @policystack/renderers, @policystack/scripts.
- Keep changes focused — one concern per PR.
- Include a changeset for any user-facing change to a published package.
- Ensure
vp testandvp run -r check-typespass before opening a PR. - For significant changes to the compilation pipeline or public API, open an issue first to discuss the approach.