Auto-collect
Automatically populate dataCollected and thirdParties from your source code
Auto-collect scans your source files at build time and populates the dataCollected and thirdParties fields of your privacy policy automatically — no need to keep those arrays up to date by hand.
It works through two complementary mechanisms:
collecting()— a zero-cost wrapper you place around data storage calls to declare what you're storingthirdParty()— a side-effect-free call you place next to third-party SDK initialisation to declare an external service
The @openpolicy/vite-auto-collect Vite plugin scans your source files at build time, extracts these declarations, and merges them into your policy config before compilation runs.
Install
bun add -D @openpolicy/vite-auto-collectSetup
Add autoCollect() before openPolicy() in your Vite plugin array. The scan must complete before the policy config is imported.
// vite.config.ts
import { defineConfig } from "vite";
import { autoCollect } from "@openpolicy/vite-auto-collect";
import { openPolicy } from "@openpolicy/vite";
export default defineConfig({
plugins: [
autoCollect(),
openPolicy(),
],
});collecting()
Wrap any call that stores personal data with collecting(). It returns the second argument unchanged at runtime, so it composes naturally with ORM insert calls and similar patterns.
import { collecting } from "@openpolicy/sdk";
export async function createUser(name: string, email: string) {
return db.insert(users).values(
collecting(
"Account Information", // category — appears as a section heading in the policy
{ name, email }, // value — returned unchanged; matches your ORM schema
{ name: "Name", email: "Email address" }, // labels — human-readable names used in the policy
),
);
}Arguments:
| Position | Name | Description |
|---|---|---|
| 1 | category | Policy section heading (e.g. "Account Information") |
| 2 | value | The value being stored — returned as-is at runtime |
| 3 | labels | Object mapping field names to human-readable policy labels |
Constraints:
- The
categorystring and all label values must be string literals. Dynamic values (variables, template literals) are silently skipped by the analyser. - Fields omitted from the label record are excluded from the policy — use this to hide internal fields like
hashedPassword. - Multiple
collecting()calls with the same category are merged; duplicate labels are deduplicated.
thirdParty()
Call thirdParty() next to third-party SDK initialisation to declare an external service. This is a no-op at runtime.
import { thirdParty } from "@openpolicy/sdk";
import { PostHog } from "posthog-js";
thirdParty(
"PostHog", // service name
"Product analytics", // purpose — appears in the policy
"https://posthog.com/privacy" // URL to the service's own privacy policy
);
export const posthog = new PostHog(process.env.POSTHOG_KEY);Arguments:
| Position | Name | Description |
|---|---|---|
| 1 | name | Service name as it appears in the policy |
| 2 | purpose | Short description of why you use the service |
| 3 | policyUrl | URL to the service's own privacy policy |
Constraints:
- All three arguments must be string literals. Dynamic values are silently skipped.
- If multiple
thirdParty()calls declare the samename, the first one (alphabetically by file path) wins.
NPM package auto-detection
Instead of writing thirdParty() calls manually, you can enable usePackageJson to detect known third-party services from your package.json dependencies automatically.
// vite.config.ts
autoCollect({
thirdParties: {
usePackageJson: true,
},
})The plugin reads both dependencies and devDependencies from your project root package.json and matches against a built-in registry of known packages. Explicit thirdParty() calls always take precedence — usePackageJson only adds entries not already declared in source.
Known packages:
| npm package | Service | Purpose |
|---|---|---|
stripe, @stripe/stripe-js | Stripe | Payment processing |
braintree, @braintree/browser-drop-in | Braintree | Payment processing |
@sentry/browser, @sentry/node, @sentry/nextjs, @sentry/react, @sentry/vue | Sentry | Error tracking |
@datadog/browser-rum, dd-trace | Datadog | Monitoring |
posthog-js, posthog-node | PostHog | Product analytics |
mixpanel-browser | Mixpanel | Product analytics |
@segment/analytics-next | Segment | Customer data platform |
@amplitude/analytics-browser, amplitude-js | Amplitude | Product analytics |
@vercel/analytics | Vercel Analytics | Web analytics |
plausible-tracker | Plausible | Web analytics |
logrocket | LogRocket | Session recording |
@hotjar/browser | Hotjar | Session recording |
resend | Resend | Transactional email |
@sendgrid/mail | SendGrid | Transactional email |
intercom-client, @intercom/messenger-js-sdk | Intercom | Customer messaging |
Plugin options
autoCollect({
srcDir: "src", // directory to scan
extensions: [".ts", ".tsx"], // file extensions to include
ignore: ["generated"], // extra directory names to skip
thirdParties: {
usePackageJson: true, // detect services from package.json
},
})| Option | Type | Default | Description |
|---|---|---|---|
srcDir | string | "src" | Directory walked for collecting() calls, relative to the Vite project root |
extensions | string[] | [".ts", ".tsx"] | File extensions scanned |
ignore | string[] | [] | Extra directory names skipped during the walk (appended to built-in defaults: node_modules, dist, .git, .next, .output, .svelte-kit, .cache) |
thirdParties.usePackageJson | boolean | false | Detect third-party services from package.json dependencies |