OpenPolicy

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 storing
  • thirdParty() — 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-collect

Setup

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:

PositionNameDescription
1categoryPolicy section heading (e.g. "Account Information")
2valueThe value being stored — returned as-is at runtime
3labelsObject mapping field names to human-readable policy labels

Constraints:

  • The category string 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:

PositionNameDescription
1nameService name as it appears in the policy
2purposeShort description of why you use the service
3policyUrlURL 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 same name, 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 packageServicePurpose
stripe, @stripe/stripe-jsStripePayment processing
braintree, @braintree/browser-drop-inBraintreePayment processing
@sentry/browser, @sentry/node, @sentry/nextjs, @sentry/react, @sentry/vueSentryError tracking
@datadog/browser-rum, dd-traceDatadogMonitoring
posthog-js, posthog-nodePostHogProduct analytics
mixpanel-browserMixpanelProduct analytics
@segment/analytics-nextSegmentCustomer data platform
@amplitude/analytics-browser, amplitude-jsAmplitudeProduct analytics
@vercel/analyticsVercel AnalyticsWeb analytics
plausible-trackerPlausibleWeb analytics
logrocketLogRocketSession recording
@hotjar/browserHotjarSession recording
resendResendTransactional email
@sendgrid/mailSendGridTransactional email
intercom-client, @intercom/messenger-js-sdkIntercomCustomer 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
  },
})
OptionTypeDefaultDescription
srcDirstring"src"Directory walked for collecting() calls, relative to the Vite project root
extensionsstring[][".ts", ".tsx"]File extensions scanned
ignorestring[][]Extra directory names skipped during the walk (appended to built-in defaults: node_modules, dist, .git, .next, .output, .svelte-kit, .cache)
thirdParties.usePackageJsonbooleanfalseDetect third-party services from package.json dependencies

On this page