@cookiemunch/sdk 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/webhooks.ts","../src/index.ts"],"sourcesContent":["/**\n * Verify an inbound Cookie Munch webhook signature.\n *\n * Cookie Munch signs every webhook delivery with the subscription's `whsec_…` secret\n * (returned once when the webhook is created). Each POST carries:\n *\n * X-CookieMunch-Signature: sha256=<hex HMAC-SHA256(secret, `${timestamp}.${rawBody}`)>\n * X-CookieMunch-Timestamp: <unix SECONDS>\n * X-CookieMunch-Event: <event type, e.g. dsar.action_required>\n *\n * The signature covers the timestamp as well as the body, so a captured delivery cannot be\n * replayed forever — reject deliveries whose timestamp is outside `toleranceSeconds`\n * (default 300s / 5 min). This matches the platform-standard (Stripe-style) verifyWebhook shape.\n *\n * @example\n * import { verifyWebhookSignature } from '@cookiemunch/sdk';\n * // Express — capture the RAW body: app.use(express.raw({ type: 'application/json' }))\n * app.post('/hooks/cm', (req, res) => {\n * const ok = verifyWebhookSignature({\n * payload: req.body.toString('utf8'), // exact bytes, not a re-stringified object\n * signature: req.header('X-CookieMunch-Signature'),\n * timestamp: req.header('X-CookieMunch-Timestamp'),\n * secret: process.env.CM_WEBHOOK_SECRET!, // the whsec_… value\n * });\n * if (!ok) return res.sendStatus(400);\n * const event = JSON.parse(req.body.toString('utf8')); // { id, type, cbid, createdAt, data }\n * // …handle event.type…\n * res.sendStatus(200);\n * });\n *\n * Server-side only: uses `node:crypto`. Pass the EXACT raw request body — verifying against a\n * re-serialized JSON object fails whenever key order or whitespace differs from what was signed.\n */\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\nexport interface VerifyWebhookInput {\n /** The raw request body, exactly as received. Do NOT re-stringify parsed JSON. */\n payload: string;\n /** The `X-CookieMunch-Signature` header value. */\n signature: string | undefined | null;\n /** The `X-CookieMunch-Timestamp` header value (unix seconds). */\n timestamp: string | number | undefined | null;\n /** The subscription secret (`whsec_…`), shown once when the webhook was created. */\n secret: string;\n /**\n * Maximum age (in seconds) of a delivery before it is rejected as stale/replayed.\n * Defaults to 300 (5 minutes). Pass 0 to disable the freshness check entirely.\n */\n toleranceSeconds?: number;\n /** Current time in ms (injectable for tests); defaults to `Date.now()`. */\n now?: number;\n}\n\n/**\n * Returns true only when the signature is valid AND (unless disabled) the timestamp is fresh.\n * Fail-closed: any missing/malformed input returns false rather than throwing.\n */\nexport function verifyWebhookSignature(input: VerifyWebhookInput): boolean {\n const { payload, signature, timestamp, secret } = input;\n if (!signature || timestamp == null || timestamp === '' || !secret) return false;\n\n const tsRaw = String(timestamp);\n const tsNum = Number(tsRaw);\n if (!Number.isFinite(tsNum)) return false;\n\n const tolerance = input.toleranceSeconds ?? 300;\n if (tolerance > 0) {\n const nowSec = Math.floor((input.now ?? Date.now()) / 1000);\n if (Math.abs(nowSec - tsNum) > tolerance) return false; // stale or replayed\n }\n\n // HMAC over the EXACT timestamp bytes received, joined to the raw body with a dot — matching\n // the server's `sign(secret, timestamp, body)` (packages/server/src/webhook-delivery.ts).\n const expected = `sha256=${createHmac('sha256', secret).update(`${tsRaw}.${payload}`).digest('hex')}`;\n\n // Constant-time compare; timingSafeEqual throws on length mismatch, so guard first.\n const got = Buffer.from(signature);\n const want = Buffer.from(expected);\n if (got.length !== want.length) return false;\n return timingSafeEqual(got, want);\n}\n","/**\n * @cookiemunch/sdk — a small, typed REST client for the Cookie Munch Developer API\n * (the /v1 surface). Construct it with an API key; the org is derived server-side\n * from the key, so callers never pass an orgId. `fetch` is injectable for testing\n * and non-browser/non-Node runtimes; it defaults to the global fetch.\n *\n * const fc = createCookieMunch({ apiKey: 'fck_…', baseUrl: 'https://cmp.example.com' });\n * const sites = await fc.sites.list();\n */\n\nexport * from './types.js';\nexport * from './consent.js';\nexport * from './webhooks.js';\nimport type {\n Identity,\n AccountOrgs,\n Site,\n SiteCreate,\n SiteConfig,\n ConsentDay,\n ConsentLogRow,\n RangeQuery,\n LogQuery,\n DsarRequest,\n DsarCreate,\n DsarStatus,\n ScoredVendor,\n VendorInput,\n VendorRecord,\n RiskScore,\n RopaEntry,\n RopaInput,\n SignedReceipt,\n PreferenceRecord,\n} from './types.js';\n\nexport interface CookieMunchOptions {\n apiKey: string;\n /** e.g. \"https://cmp.example.com\" — the /v1 prefix is appended automatically. */\n baseUrl: string;\n /** Injectable for tests / custom runtimes. Defaults to the global fetch. */\n fetch?: typeof fetch;\n}\n\n/**\n * Thrown for any non-2xx response. `message` is the server's `error` field when present;\n * `code` is populated from the server's `code` field whenever the error body carries one\n * (e.g. banner-library conflicts, docs-admin checks) — not only for scoped-key\n * (`insufficient_scope`) responses. It's `undefined` when the body has no `code`.\n */\nexport class CookieMunchApiError extends Error {\n readonly status: number;\n readonly code?: string;\n constructor(status: number, message: string, code?: string) {\n super(message);\n this.name = 'CookieMunchApiError';\n this.status = status;\n if (code !== undefined) this.code = code;\n }\n}\n\n/*\n * Additional /v1 response shapes. These live here (rather than in types.ts) but are\n * still re-exported from the package root via `export * from './types.js'` above plus\n * these top-level `export interface` declarations.\n */\n\n/** A cookie discovered on a site, classified into a consent category (wire shape of the\n * cookie-declaration feed). Matches the server's CategorizedCookie schema. */\nexport interface CategorizedCookie {\n name: string;\n category: 'necessary' | 'preferences' | 'statistics' | 'marketing' | 'unclassified' | (string & {});\n domain?: string;\n provider?: string;\n purpose?: string;\n expiry?: string;\n}\n\n/** A site's cookie declaration: the latest scan's categorized cookies. This is what\n * GET /v1/sites/:cbid/cookies actually returns (a wrapper, not a bare array). */\nexport interface CookieDeclaration {\n /** Epoch-ms of the latest scan, or 0 if none. */\n updatedAt: number;\n cookies: CategorizedCookie[];\n}\n\n/** @deprecated The server returns {@link CookieDeclaration}; kept only for back-compat. */\nexport interface SiteCookie {\n name: string;\n domain: string;\n category: string;\n provider?: string;\n purpose?: string;\n expiry?: string;\n firstSeen?: number;\n}\n\n/** Cookie-scan status, from GET /v1/sites/:cbid/scan. */\nexport interface ScanStatus {\n status: 'idle' | 'scanning';\n /** Epoch-ms of the last completed scan, or null. */\n lastScannedAt: number | null;\n}\n\n/** @deprecated The server returns {@link ScanStatus}; kept only for back-compat. */\nexport interface ScanResult {\n scanId: string;\n status: 'queued' | 'running' | 'complete' | 'failed';\n startedAt?: number;\n finishedAt?: number;\n pagesScanned?: number;\n cookiesFound?: number;\n}\n\n/** A/B banner experiment results for a single variant. */\nexport interface AbResult {\n variant: string;\n impressions: number;\n optIns: number;\n optInRate: number;\n}\n\n/** The response shape of GET /v1/sites/:cbid/snippet. */\n// Keep in sync with packages/server/src/snippet.ts InstallSnippet\nexport interface InstallSnippet {\n /** The exact `<script>` tag to paste into `<head>`. */\n snippet: string;\n /** URL of the consent.js embed file (the embed/CDN origin). */\n src: string;\n /** The API origin (beacons + consent-tcf.js). Present when the server reports it. */\n api?: string;\n /** The site identifier. */\n cbid: string;\n /** The blocking mode on the tag — the site's `blocking.mode` unless overridden. */\n blockingMode: string;\n /** Where `blockingMode` came from: the site's config, or the `blockingMode` option. */\n blockingModeSource?: 'config' | 'query';\n /** The tag's data-culture, when it has one. */\n culture?: string;\n /** CSP sources this site needs, by directive, plus a paste-able `policy`. */\n csp?: CspSources;\n /** Present when an override disagrees with the stored config. */\n warnings?: ConfigWarning[];\n}\n\n/** Content Security Policy sources the embed needs. */\nexport interface CspSources {\n script: string[];\n connect: string[];\n img: string[];\n /** Always includes 'unsafe-inline' (the banner's shadow-root <style>), unless you use a nonce. */\n style: string[];\n font: string[];\n frame: string[];\n /** The same as one policy fragment. */\n policy: string;\n}\n\n/** Something a config write did not store as sent. */\nexport interface ConfigWarning {\n field: string;\n reason: string;\n}\n\n/** One leaf a config write would change. */\nexport interface ConfigChange {\n path: string;\n from?: unknown;\n to?: unknown;\n}\n\n/** What a config write would do (`?dryRun=1`) — nothing is saved. */\nexport interface ConfigDryRun {\n dryRun: true;\n /** The config as it would be saved. */\n config: SiteConfig;\n /** Every leaf that would change. */\n diff: ConfigChange[];\n warnings?: ConfigWarning[];\n}\n\n/** Options for POST /v1/sites/:cbid/preview-token. All optional. */\nexport interface PreviewTokenOptions {\n /** Lifetime in seconds, 1..3600. Default 900. */\n ttlSeconds?: number;\n /** The page to preview. Default: the site's registered domain. */\n url?: string;\n /** Region to force: a country or subdivision code (`de`, `us-ca`) or `eu`. */\n region?: string;\n /** Language to force, as a BCP 47 tag (`de`, `pt-BR`). */\n lang?: string;\n}\n\n/** A banner preview token, and the page URL that opens the preview. */\nexport interface PreviewToken {\n cbid: string;\n token: string;\n /** Epoch milliseconds. */\n expiresAt: number;\n region?: string;\n lang?: string;\n /** The page with `cm_preview` / `cm_region` / `cm_lang` added. */\n previewUrl?: string;\n}\n\n/** Options for the snippet endpoint. */\nexport interface SnippetOptions {\n /** Override the site's configured `blocking.mode`. Default: the config's. */\n blockingMode?: 'auto' | 'manual';\n /** Language culture override, e.g. \"en\" or \"fr\". */\n culture?: string;\n}\n\n/**\n * The result of domain verification.\n * Keep in sync with the POST /v1/sites/:cbid/verify response in\n * packages/server/src/dev-api.ts (~L267) — note the server adds `method` on success,\n * so this wire shape is wider than domain-verify.ts's internal VerifyResult.\n */\nexport interface VerifyResult {\n verified: boolean;\n method?: 'dns' | 'meta' | 'file';\n reason?: string;\n}\n\n/** A site brand suggestion (\"Match my site\"). Keep in sync with packages/server/src/brand-extract.ts BrandSuggestion. */\nexport interface BrandSuggestion {\n background?: string;\n text?: string;\n highlight?: string;\n fontFamily?: string;\n fontUrl?: string;\n palette?: string[];\n}\n\n/** The response shape of POST /v1/sites/:cbid/brand. */\nexport interface BrandExtractionResult {\n suggestion: BrandSuggestion;\n}\n\n/** A validation/lint issue on a v2 banner flow. */\nexport interface FlowIssue {\n code: string;\n message: string;\n path?: string;\n}\n\n/** A site's v2 banner flow + lint issues, from GET /v1/sites/:cbid/flow. */\nexport interface SiteFlow {\n v: 2;\n flow: Record<string, unknown>;\n categories: Record<string, unknown>;\n customCss?: string;\n lint: FlowIssue[];\n}\n\n/**\n * A single structured flow edit operation. `op` selects the kind; remaining fields are\n * that op's args (e.g. { op: 'addView', id, surface, layoutMode }).\n */\nexport type FlowOp = { op: string } & Record<string, unknown>;\n\n/**\n * Result of editFlow/setFlow. On success the new flow is persisted and returned;\n * on a validation/lint failure the config is NOT saved and `issues` says why.\n */\nexport type FlowOpResponse =\n | { ok: true; flow: Record<string, unknown> }\n | { ok: false; failedAt?: number; op?: unknown; issues: FlowIssue[] };\n\n/** A reusable brand kit (colors/logo/typography) for banner theming. */\nexport interface BrandKit {\n id: string;\n orgId: string;\n name: string;\n /** Banner theme tokens (opaque design object). */\n theme: Record<string, unknown>;\n /** Optional banner content overrides (opaque). */\n content?: Record<string, unknown>;\n logoUrl?: string;\n customCss?: string;\n}\n\n/** A configurable consent preference/purpose item. */\nexport interface PreferenceItem {\n id: string;\n orgId: string;\n cbid?: string;\n label: string;\n description?: string;\n category?: string;\n}\n\n/** An organization member. */\nexport interface Member {\n userId: string;\n email: string;\n role: 'owner' | 'admin' | 'member' | 'viewer' | (string & {});\n}\n\n/** Display metadata for an issued key — never the secret. Returned by keys.list(). */\nexport interface ApiKeyPrefix {\n prefix: string;\n createdAt?: number;\n}\n\n/** A newly issued key. `key` is returned ONCE and never again. Returned by keys.issue(). */\nexport interface ApiKeyIssued {\n key: string;\n prefix: string;\n}\n\n/** @deprecated No endpoint returns this shape; use {@link ApiKeyPrefix} / {@link ApiKeyIssued}. */\nexport interface ApiKey {\n id: string;\n orgId: string;\n prefix: string;\n name?: string;\n createdAt: number;\n lastUsedAt?: number;\n secret?: string;\n}\n\n/**\n * Input for issuing a new API key.\n *\n * NOTE: the server's POST /v1/keys currently accepts no body and always mints an\n * unscoped key — `name` is not yet honoured server-side. It's kept here (rather than\n * dropped) so a future scoped/named-key endpoint can start reading it without an SDK\n * major version bump; see {@link CookieMunchClient.keys}.\n */\nexport interface ApiKeyIssueInput {\n name?: string;\n /** Least privilege: grant only these scopes. Omit for a full-access key. */\n scopes?: string[];\n /**\n * Lock the key to these properties. A locked key works only on those sites and on no\n * org-wide endpoint — see \"Keys locked to properties\" in the client reference.\n */\n cbids?: string[];\n /** Expire the key after this many days (1–3650). */\n expiresInDays?: number;\n}\n\n/** Org usage/quota summary. Matches the server's Usage schema. */\nexport interface Usage {\n domains: number;\n seats: number;\n monthlyEvents: number;\n}\n\n/** A webhook subscription. `secret` is present only in the create response. */\nexport interface WebhookSubscription {\n id: string;\n orgId: string;\n url: string;\n secret?: string;\n events: string[];\n cbid: string | null;\n active: boolean;\n createdAt: number;\n}\n\n/** One cookie read from another CMP's export. */\nexport interface ImportedCookie {\n name: string;\n domain: string;\n /** Ours, or `unclassified` when their category did not map to one. */\n category: 'necessary' | 'preferences' | 'statistics' | 'marketing' | 'unclassified';\n /** What the previous vendor called it, kept for review. */\n sourceCategory: string;\n purpose: string;\n provider: string;\n expiry: string;\n}\n\n/** What a competitor's cookie-declaration export contained, in our vocabulary. */\nexport interface ImportedDeclaration {\n source: 'onetrust' | 'cookiebot' | 'cookieyes' | 'unknown';\n cookies: ImportedCookie[];\n /** Rows whose category could not be placed — review these first. */\n unmapped: Array<{ name: string; category: string }>;\n}\n\n/**\n * A page that refused to load the banner renderer. The site is live and configured, so\n * every other signal looks healthy; this is the one that says nobody can be asked.\n */\nexport interface BlockedReport {\n cbid: string;\n /** Origin of the page the embed was running on. */\n page: string;\n /** The bundle URL that page refused. */\n url: string;\n reason: string;\n firstSeen: number;\n lastSeen: number;\n /** How many times this page has reported. Repeats are counted, not stored. */\n count: number;\n}\n\n/** A webhook delivery that failed every retry. Replay it by `id`. */\nexport interface WebhookDeadLetter {\n id: string;\n orgId: string;\n subscriptionId: string;\n url: string;\n eventType: string;\n cbid: string | null;\n payload: unknown;\n attempts: number;\n lastStatus?: number;\n lastError?: string;\n failedAt: number;\n}\n\n/** What a test delivery's endpoint answered. */\nexport interface WebhookTestResult {\n ok: boolean;\n status?: number;\n error?: string;\n}\n\n/** The key's organisation. */\nexport interface Org {\n id: string;\n name: string;\n plan: string;\n logoUrl: string | null;\n}\n\n/** Change an org's name or logo. `logoUrl: null` removes the logo. */\nexport interface OrgUpdate {\n name?: string;\n logoUrl?: string | null;\n}\n\n/** One entry in the org's audit log. */\nexport interface AuditEntry {\n id: string;\n orgId: string;\n actorUserId: string;\n actorEmail?: string;\n action: string;\n target?: string;\n meta?: Record<string, unknown>;\n at: number;\n}\n\n/** Rename a key, or replace its scopes or the properties it is locked to. */\nexport interface ApiKeyUpdate {\n name?: string;\n scopes?: string[];\n cbids?: string[];\n}\n\n/** An image to upload: base64 (or a `data:` URL) plus its type. */\nexport interface AssetUpload {\n data: string;\n contentType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif' | 'image/svg+xml';\n}\n\n/** Result of erasing a subject's consent for a deletion request. */\nexport interface DsarEraseResult {\n erased: number;\n encryptionEnabled: boolean;\n /** Present when no KEK is configured, so nothing was cryptographically erased. */\n warning?: string;\n request: DsarRequest;\n}\n\n/** Result of exporting a subject's consent for an access or portability request. */\nexport interface DsarExportResult {\n records: unknown[];\n count: number;\n request: DsarRequest;\n}\n\n/** Input for creating a webhook subscription. */\nexport interface WebhookCreate {\n url: string;\n events: string[];\n cbid?: string;\n}\n\n/** Fields to change on a webhook subscription. Only those present are sent. */\nexport interface WebhookUpdate {\n url?: string;\n events?: string[];\n /** One property, or null for every property in the org. */\n cbid?: string | null;\n /** false pauses delivery without deleting the subscription. */\n active?: boolean;\n}\n\n/** One site to create in a bulk call. */\nexport interface BulkSiteInput {\n domain: string;\n cbid?: string;\n platform?: string;\n}\n\n/** Per-item outcome of a bulk create, in input order. */\nexport interface BulkSiteResult {\n ok: boolean;\n cbid?: string;\n domain?: string;\n error?: string;\n}\n\n/** Input for creating a brand kit. */\nexport interface BrandKitCreate {\n name: string;\n theme: unknown;\n content?: unknown;\n logoUrl?: string;\n customCss?: string;\n}\n\n/** A lightweight summary of an account-level banner, as returned by GET /v1/banners. */\nexport interface BannerSummary {\n id: string;\n name: string;\n updatedAt: number;\n assignedCbids: string[];\n}\n\n/** A full account-level banner record, including its config JSON. */\nexport interface BannerRecord {\n id: string;\n orgId: string;\n name: string;\n json: SiteConfig;\n createdAt: number;\n updatedAt: number;\n}\n\n/**\n * Input for the public consent-log ingest endpoint (POST /api/v1/consent) — the\n * tamper-evident, hash-chained ledger write the browser embed makes. Mirrors the\n * server's ConsentPayload; optional fields are sent only when set. Records are\n * anonymised (IP truncated) and hash-chained server-side.\n */\nexport interface ConsentIngestInput {\n /** The registered site id the consent belongs to. */\n cbid: string;\n /** Stable per-subject consent-receipt id (used for erase/export). */\n stamp: string;\n /** The subject's cookie-category choices. */\n choices: { preferences: boolean; statistics: boolean; marketing: boolean };\n /** How consent was captured. */\n method: 'explicit' | 'implied';\n /** Consent/notice version number. */\n ver: number;\n /** Epoch-ms the decision was made. */\n utc: number;\n /** The URL / screen where consent was captured. */\n url: string;\n /** Optional IAB TCF consent string (TCF mode only). */\n tcString?: string;\n /** Optional GPP string (GPP mode only). */\n gppString?: string;\n /** Optional named-purpose map beyond the standard categories. */\n purposes?: Record<string, boolean>;\n /** Optional opaque digest of the exact notice text shown to the subject. */\n subjectPolicyHash?: string;\n /** Optional map: named purpose → RoPA entry id. */\n purposeRopa?: Record<string, string>;\n /** Optional A/B variant id. */\n variant?: string;\n /**\n * Optional, app-supplied STABLE cross-surface subject id. Lets an org retrieve one\n * subject's consent history across ALL its sites/surfaces (web + mobile + desktop).\n * Opaque — stored and bound into the record hash server-side, never interpreted.\n */\n subjectId?: string;\n}\n\n/** One of a person's identifiers: an identity-space code plus the normalised value. */\nexport interface Identifier {\n space: string;\n value: string;\n}\n\nexport interface ConsentDecisionInput {\n purpose: string;\n allowed: boolean;\n legalBasis?: string;\n jurisdiction?: string;\n provenance?: string;\n collectedAt?: number;\n}\n\nexport interface ProfileAttributeInput {\n value: string;\n purpose?: string;\n collectedAt?: number;\n}\n\nexport interface SubscriptionTopicInput {\n code: string;\n name?: string;\n channels: string[];\n downstream?: Record<string, string>;\n}\n\n/** A warehouse-native enforcement rule. See POST /v1/discovery/enforcement. */\nexport interface EnforcementRuleInput {\n id: string;\n action: 'mask' | 'restrict';\n /** Select columns at or above this sensitivity. */\n minSensitivity?: 'personal' | 'sensitive';\n /** Select columns in any of these categories. */\n categories?: string[];\n /** mask: roles that still see the raw value. */\n allowRoles?: string[];\n /** restrict: the purpose a subject must have consented to. */\n purpose?: string;\n /** restrict: the column holding the subject id. Default SUBJECT_ID. */\n subjectColumn?: string;\n}\n\nexport interface RegulationSummary {\n id: string;\n jurisdiction: string;\n name: string;\n /** ISO date the law takes (or took) effect. */\n effective: string;\n model: 'opt-in' | 'opt-out';\n obligations: string[];\n /** Primary source — the statute or the regulator. */\n source: string;\n}\n\nexport interface RegulatoryFeedResult {\n /** When a human last checked the dataset against its sources. */\n reviewedAt: string;\n /** Days since that review. */\n ageDays: number;\n /** How long a review is considered good for. */\n reviewAfterDays: number;\n /** True once the review interval has passed — treat the answer with caution. */\n stale: boolean;\n regulations: RegulationSummary[];\n}\n\nexport interface AiInspectInput {\n prompt: string;\n purpose: string;\n model?: string;\n actor?: string;\n direction?: 'prompt' | 'response';\n /** The subject's current permits, when the call concerns an identified person. */\n consent?: Record<string, boolean>;\n}\n\nexport interface AiInspectResult {\n action: 'allow' | 'redact' | 'block';\n /** The text to forward: redacted when `redact`, empty when `block`. */\n prompt: string;\n findings: unknown[];\n reasons: string[];\n policyIds: string[];\n blocked: boolean;\n direction: 'prompt' | 'response';\n}\n\nexport type FulfillmentOp = 'locate' | 'export' | 'erase' | 'optOut';\n\n/** A child org provisioned by a reseller. */\nexport interface ResellerChild {\n id: string;\n name: string;\n status: 'active' | 'suspended';\n delegatedAccess?: boolean;\n /** Per-child DSAR routing override; unset defers to the reseller's policy. */\n dsarRouting?: 'reseller' | 'child';\n createdAt?: number;\n usage?: unknown;\n}\n\nexport interface ResellerChildList {\n children: ResellerChild[];\n /** Pooled usage across the reseller and all its children, when metering is wired. */\n aggregate?: { childOrgs: number; sites: number; monthlyEvents: number; monthlyEventsCap: number | null; overCap: boolean };\n}\n\nexport interface ResellerChildCreate {\n name: string;\n ownerEmail?: string;\n /** legalName, contactEmail, address, dpoContact. */\n controller?: Record<string, unknown>;\n /** Branding overrides, merged onto the reseller's white-label defaults. */\n whiteLabel?: Record<string, unknown>;\n delegatedAccess?: boolean;\n /** Also issue an API key for the new child; it is returned once, as `apiKey`. */\n mintKey?: boolean;\n /** Scopes for that key. Defaults to [\"sites:read\"]. */\n keyScopes?: string[];\n}\n\nexport interface ResellerChildPatch {\n status?: 'active' | 'suspended';\n delegatedAccess?: boolean;\n /** Pin this child's DSAR routing; null clears the override. */\n dsarRouting?: 'reseller' | 'child' | null;\n controller?: Record<string, unknown>;\n}\n\nexport interface ChildKeyInput {\n name?: string;\n scopes?: string[];\n /** Lock the key to these properties of the child org. */\n cbids?: string[];\n}\n\nexport interface PolicyOptions {\n /** Contact email shown in the policy. Falls back to the org controller's, then the owner's. */\n contactEmail?: string;\n /** YYYY-MM-DD. Defaults to today. */\n effectiveDate?: string;\n /** Override the jurisdictions the policy addresses. */\n jurisdictions?: string[];\n}\n\nexport interface AdPersonalizationInput {\n enabled: boolean;\n /** Whether personalised ads start on before the visitor chooses. */\n default?: boolean;\n label?: string;\n}\n\nexport interface SessionAnalysisInput {\n /** A HAR export of the captured session. */\n har?: unknown;\n /** Or the requests directly. */\n requests?: unknown[];\n /** The consent state the session ran under. */\n consent?: Record<string, boolean>;\n /** Whether Global Privacy Control was set. */\n gpc?: boolean;\n}\n\n/**\n * How to drive one system's rights API, as data. This is what makes a connection specific to\n * a platform — Cookie Munch names none of them, so connecting a new system is a profile\n * rather than a release. Paths are relative to `baseUrl`; placeholders are `{email}`,\n * `{subject}`, `{operation}`, `{correlation}` and `{id}`.\n */\nexport interface ExecutorProfile {\n /** How requests carry the credential. Defaults to `Authorization: Bearer {secret}`. */\n auth?: { header: string; format: string };\n /**\n * Optional first step: turn the subject's email into the id this system uses. `noMatch`\n * names how the system says \"no record of that person\" in its body — a sentinel id is\n * never read as a person when it is set.\n */\n subjectLookup?: {\n method: 'GET' | 'POST';\n path: string;\n body?: unknown;\n idPath: string;\n noMatch?: { statusPath: string; values: string[] };\n };\n /** Response header carrying the system's version, recorded in the task log. Default `X-Version`. */\n versionHeader?: string;\n /** How to open a request. An operation left out of `operations` is one this system does not run. */\n open: {\n method: 'POST' | 'PUT';\n path: string;\n body?: unknown;\n operations: { export?: string; erase?: string };\n idPath: string;\n statusPath?: string;\n };\n /** How to ask how it is going. Polling is the truth; a webhook only prompts a check. */\n poll: {\n method?: 'GET';\n path: string;\n statusPath: string;\n resultPath?: string;\n /** Whether `resultPath` holds the bundle itself or a URL to fetch it from. */\n resultIs?: 'inline' | 'url';\n /** Where a date the work is waiting for lives (an erasure in a grace period). */\n scheduledPath?: string;\n };\n /** That system's status words → the three answers a sub-task can have. */\n statuses: { running: string[]; done: string[]; failed: string[] };\n idempotencyHeader?: string;\n /** How that system signs its webhooks, if it sends any. */\n webhook?: {\n algorithm: 'hmac-sha256';\n /** Must include `{body}`, or the signature proves nothing about it. */\n signedPayload: string;\n headers: { signature: string; timestamp?: string; id?: string };\n encoding?: 'base64' | 'hex';\n prefix?: string;\n toleranceSeconds?: number;\n timestampUnit?: 'seconds' | 'milliseconds' | 'auto';\n };\n}\n\n/** A system connected to run part of a rights request. Credentials are never included. */\nexport interface DsarExecutor {\n id: string;\n /** The name it answers to in a fulfillment plan, e.g. `identity` or `crm`. */\n system: string;\n profile: ExecutorProfile;\n baseUrl: string;\n keyPrefix: string;\n hasWebhookSecret: boolean;\n /** Open a sub-task automatically when a request reaches fulfilment. */\n auto: boolean;\n createdAt: number;\n}\n\nexport interface DsarExecutorInput {\n /** The name this connection answers to in a plan, e.g. `identity` or `crm`. */\n system: string;\n /** The system's public https origin. */\n baseUrl: string;\n /** Its API key. Stored encrypted; never returned. */\n secretKey: string;\n /** The signing secret of its webhook endpoint. Optional — polling closes tasks without it. */\n webhookSecret?: string;\n auto?: boolean;\n /** How to drive it. */\n profile: ExecutorProfile;\n}\n\nexport interface DsarExecutorCreated extends DsarExecutor {\n /** Point the connected system's webhook endpoint here. */\n webhookUrl: string;\n}\n\n/** One entry in a fulfilment task's status history. */\nexport interface DsarTaskEvent {\n at: number;\n kind: 'created' | 'changed' | 'version_changed';\n status: 'pending' | 'running' | 'done' | 'failed';\n detail?: string;\n outcome?: DsarTaskOutcome;\n scheduledFor?: number;\n externalRef?: string;\n}\n\n/** What a task's answer means for the person. `exported` only once the export was fetched. */\nexport type DsarTaskOutcome = 'located' | 'exported' | 'erased' | 'no_record' | 'done_without_artifact' | 'not_applicable';\n\n/** One request made to a connected system on a task's behalf, credentials removed. Kept 30 days. */\nexport interface DsarTaskLogEntry {\n id?: string;\n system?: string;\n step: 'lookup' | 'open' | 'poll' | 'export';\n method: string;\n path: string;\n /** Absent when the system never answered. */\n status?: number;\n latencyMs: number;\n requestHeaders?: Record<string, string>;\n /** The first ~2 KB of what was sent. */\n request?: string;\n /** The first ~2 KB of what came back. */\n response?: string;\n error?: string;\n /** The system's version header on this answer. */\n version?: string;\n at: number;\n}\n\n/** One fulfilment task with its history and its upstream request log. */\nexport interface DsarTask {\n id: string;\n requestId: string;\n system: string;\n operation: FulfillmentOp;\n status: 'pending' | 'running' | 'done' | 'failed';\n detail?: string;\n outcome?: DsarTaskOutcome;\n attempts: number;\n executor?: string;\n externalRef?: string;\n scheduledFor?: number;\n artifacts?: Array<{ kind: string; system: string; ref: string }>;\n createdAt: number;\n updatedAt: number;\n events: DsarTaskEvent[];\n log: DsarTaskLogEntry[];\n /** Versions the system reported while working on this task, in order of change. */\n upstreamVersions?: string[];\n /** The system was redeployed while this task was in flight. */\n versionChanged: boolean;\n}\n\n/** What testing a connection found. Nothing was filed. */\nexport interface ExecutorTestResult {\n system: string;\n /** Did the lookup find the person; null when there is no lookup step or it failed. */\n matched: boolean | null;\n subject?: string;\n operationWord?: string;\n /** The open request a real request would send, when it was not sent. */\n wouldSend?: { method: string; path: string; body: unknown };\n /** One sentence: what a real request would have done. */\n verdict: string;\n ok: boolean;\n /** Every upstream answer, as the system gave it (credentials removed). */\n exchanges: DsarTaskLogEntry[];\n}\n\nexport interface ExecutorTestInput {\n email: string;\n /** Default `locate`. */\n operation?: 'locate' | 'export' | 'erase';\n /** For `export` only: open a real export and poll it once. An erasure is never sent. */\n open?: boolean;\n}\n\nexport interface DsarTaskExport {\n system: string;\n ref: string;\n /** The bundle as that system returned it. Not stored here. */\n bundle: unknown;\n}\n\n/** A language the banner already has copy for. */\nexport interface SupportedLanguage {\n /** BCP-47 tag, lowercase. Matched exactly, then by language prefix. */\n code: string;\n /** English name. */\n name: string;\n /** The language's name in itself. */\n endonym: string;\n /** Written right to left; the banner flips for these. */\n rtl: boolean;\n /** `bundled` ships in consent.js; `extended` ships in the renderer, fetched when a banner is first drawn. */\n source: 'bundled' | 'extended';\n}\n\nexport interface CookieMunchClient {\n me(): Promise<Identity>;\n /**\n * Languages the banner already speaks. Diff this against the locales your visitors\n * use to see which ones you still need to write copy for in `banner.i18n`.\n */\n languages(): Promise<SupportedLanguage[]>;\n orgs: {\n /**\n * Every organisation on this key's account, each with its effective plan and where that\n * comes from, plus the per-account org cap and the upgrade URL. Needs an unscoped key that\n * is not locked to properties.\n */\n list(): Promise<AccountOrgs>;\n /**\n * Create a sibling organisation owned by the same account — for a separate business of\n * your own. Needs an unscoped key. Not reseller provisioning, which is for your customers.\n */\n create(\n name: string,\n opts?: { mintKey?: boolean; keyScopes?: string[] },\n ): Promise<{ org: { id: string; name: string; role: string }; apiKey?: string; keyError?: string }>;\n };\n sites: {\n list(): Promise<Site[]>;\n create(input: SiteCreate): Promise<Site>;\n get(cbid: string): Promise<Site>;\n delete(cbid: string): Promise<void>;\n getConfig(cbid: string): Promise<SiteConfig>;\n /** Replaces the whole config — a top-level field you omit reverts to its default. */\n putConfig(cbid: string, config: SiteConfig): Promise<SiteConfig>;\n /** Changes some of the config — omitted fields keep their stored value. */\n patchConfig(cbid: string, config: Partial<SiteConfig>): Promise<SiteConfig>;\n /**\n * Preview a config write without saving it: the merged result and a diff. `mode: 'put'`\n * previews a replace (the diff then lists everything that would revert to its default).\n */\n dryRunConfig(cbid: string, config: Partial<SiteConfig>, opts?: { mode?: 'patch' | 'put' }): Promise<ConfigDryRun>;\n cookies(cbid: string): Promise<CookieDeclaration>;\n scan(cbid: string): Promise<ScanStatus>;\n scanStatus(cbid: string): Promise<ScanStatus>;\n ab(cbid: string): Promise<AbResult[]>;\n /** Generate the site's privacy and cookie policy, as Markdown. */\n policy(cbid: string, opts?: PolicyOptions): Promise<string>;\n /** Turn on the personalised-ads split on whichever banner form the site uses. */\n setAdPersonalization(cbid: string, input: AdPersonalizationInput): Promise<unknown>;\n /** Analyse a captured session: trackers that fired after opt-out, personal data leaving the page. */\n analyzeSession(cbid: string, input: SessionAnalysisInput): Promise<unknown>;\n /** Which banner design this site uses, or null when none is assigned. */\n banner(cbid: string): Promise<{ bannerId: string | null }>;\n /**\n * Pages where the embed could not load its banner renderer — the host page's CSP or\n * Trusted Types policy refused it, so nobody on that page can be asked. An empty list\n * is the healthy answer.\n */\n blocked(cbid: string): Promise<{ reports: BlockedReport[] }>;\n /**\n * Read a cookie declaration exported from another CMP and translate its categories\n * into ours. Nothing is applied: their vocabulary is not ours, and a cookie in the\n * wrong category is a tag firing against a refusal, so the result is for review.\n */\n importDeclaration(cbid: string, data: string): Promise<ImportedDeclaration>;\n snippet(cbid: string, opts?: SnippetOptions): Promise<InstallSnippet>;\n /**\n * Mint a short-lived signed token to see the banner as a visitor in a given region and\n * language sees it — even from a headless browser, and without recording anything.\n */\n previewToken(cbid: string, opts?: PreviewTokenOptions): Promise<PreviewToken>;\n verify(cbid: string, method: 'dns' | 'meta' | 'file' | 'embed'): Promise<VerifyResult>;\n /** Exactly what to publish to prove control of the domain, for each verification method. */\n verifyChallenge(cbid: string): Promise<unknown>;\n /** Create up to 100 sites. Partial success: a bad or duplicate item fails only itself. */\n createBulk(sites: BulkSiteInput[]): Promise<{ results: BulkSiteResult[] }>;\n brand(cbid: string): Promise<BrandExtractionResult>;\n /** Read a site's v2 banner flow (views, categories) plus any lint issues. */\n getFlow(cbid: string): Promise<SiteFlow>;\n /** Apply an ordered batch of structured edit ops; validated + persisted if lint-clean. */\n editFlow(cbid: string, operations: FlowOp[]): Promise<FlowOpResponse>;\n /** Wholesale-replace the flow with a full v2 config; validated + persisted if valid. */\n setFlow(cbid: string, config: Record<string, unknown>): Promise<FlowOpResponse>;\n };\n consent: {\n /**\n * Verify the consent log's tamper-evident hash chain. Each record carries the hash of the\n * one before it, so an edited, reordered or removed record answers `false`. This is the\n * evidence behind the log: proof that what it says today is what it said when written.\n */\n verify(cbid: string): Promise<{ valid: boolean }>;\n stats(cbid: string, query?: RangeQuery): Promise<ConsentDay[]>;\n log(cbid: string, query?: LogQuery): Promise<ConsentLogRow[]>;\n export(cbid: string, query?: RangeQuery): Promise<string>;\n receipt(cbid: string, stamp: string): Promise<SignedReceipt>;\n /** Crypto-erase a subject's consent records by their consent-receipt stamp. Irreversible. */\n eraseSubject(cbid: string, stamp: string): Promise<{ erased: number }>;\n /** Export a data subject's consent records by their consent-receipt stamp (GDPR access/portability). */\n exportSubject(cbid: string, stamp: string): Promise<{ cbid: string; stamp: string; records: unknown[]; count: number }>;\n /** Record a consent decision via the PUBLIC ingest endpoint (POST /api/v1/consent). No `/v1`\n * prefix and no auth is required by the server (the cbid must be a registered site); for\n * server-side / non-browser consent flows. Pass `subjectId` to correlate a subject cross-surface. */\n ingest(input: ConsentIngestInput): Promise<void>;\n };\n dsar: {\n list(): Promise<DsarRequest[]>;\n /**\n * One request, in the same shape `list` returns — note that `create` answers with it\n * wrapped as `{ request }`, so `create(...).request.id` is what you pass here.\n */\n get(id: string): Promise<DsarRequest>;\n create(input: DsarCreate): Promise<{ request: DsarRequest }>;\n advance(id: string, toStatus: DsarStatus): Promise<{ request: DsarRequest }>;\n /** The subject-facing response notice for a request, as plain text. */\n response(id: string): Promise<string>;\n /**\n * Erase a subject's consent records on one site, for a deletion request past identity\n * verification. Noted on the request. Requires dsar:write and consent:write.\n */\n erase(id: string, cbid: string, stamp: string): Promise<DsarEraseResult>;\n /**\n * A subject's consent records on one site, for an access or portability request past\n * identity verification. Noted on the request. Requires dsar:write and consent:read.\n */\n export(id: string, cbid: string, stamp: string): Promise<DsarExportResult>;\n /**\n * Delete a TEST request (created with `test: true`), with its tasks and their log. A\n * real request cannot be deleted (409 `not_a_test_request`); the deletion is audited.\n */\n delete(id: string): Promise<void>;\n };\n vendors: {\n list(): Promise<ScoredVendor[]>;\n create(input: VendorInput): Promise<{ vendor: VendorRecord; risk: RiskScore }>;\n };\n ropa: {\n list(): Promise<RopaEntry[]>;\n create(input: RopaInput): Promise<{ entry: RopaEntry }>;\n /** The org's RoPA (GDPR Art. 30) as CSV. */\n exportCsv(): Promise<string>;\n };\n brandKits: {\n list(): Promise<BrandKit[]>;\n create(input: BrandKitCreate): Promise<{ kit: BrandKit }>;\n delete(id: string): Promise<void>;\n };\n /**\n * Identity resolution. Reads are POSTs on purpose: a person's identifiers travel in the\n * body, never a URL where they would land in access logs.\n */\n identity: {\n /** Canonical subject id for any known identifier, else null. */\n resolve(identifiers: Identifier[]): Promise<{ subjectId: string | null }>;\n /** Stitch identifiers into one subject (merging clusters when a link bridges them). */\n link(identifiers: Identifier[]): Promise<{ subjectId: string }>;\n cluster(subjectId: string): Promise<{ subjectId: string; identifiers: Identifier[] }>;\n };\n /** The Permission Vault: a resolved person's current consent state. */\n vault: {\n record(identifiers: Identifier[], decisions: ConsentDecisionInput[]): Promise<{ subjectId: string; permits: unknown[] }>;\n current(identifiers: Identifier[]): Promise<{ purposes: Record<string, boolean> }>;\n permits(identifiers: Identifier[]): Promise<{ permits: unknown[] }>;\n };\n /** The unified profile, with consent enforced at ACTIVATION time. */\n profile: {\n get(identifiers: Identifier[]): Promise<unknown>;\n setAttributes(identifiers: Identifier[], attributes: Record<string, ProfileAttributeInput>): Promise<{ subjectId: string }>;\n /** Attribute values usable for `purpose` — {} when the person hasn't consented to it. */\n activate(identifiers: Identifier[], purpose: string): Promise<{ attributes: Record<string, string> }>;\n };\n /** Marketing preferences as topics x channels. */\n subscriptions: {\n /** The org's topic catalog — what a subject can subscribe TO. */\n topics(): Promise<{ topics: SubscriptionTopicInput[] }>;\n /** Replace the catalog. It is authored whole, not patched. */\n setTopics(topics: SubscriptionTopicInput[]): Promise<{ topics: SubscriptionTopicInput[] }>;\n get(subjectId: string): Promise<unknown>;\n set(subjectId: string, topic: string, channel: string, optedIn: boolean): Promise<unknown>;\n unsubscribeAll(subjectId: string): Promise<unknown>;\n resubscribe(subjectId: string): Promise<unknown>;\n activation(subjectId: string, topics: SubscriptionTopicInput[]): Promise<{ entries: unknown[] }>;\n };\n /** DPIA / PIA / LIA / TIA / AI-impact / vendor assessments. */\n assessments: {\n templates(): Promise<{ templates: unknown[] }>;\n list(): Promise<{ assessments: unknown[] }>;\n start(template: string, subject: string): Promise<unknown>;\n /** The record plus its derived score, completeness and SME routing. */\n get(id: string): Promise<unknown>;\n answer(id: string, questionId: string, value: string | number | boolean): Promise<unknown>;\n /** Fill from the org's latest data map; never overwrites a human answer. */\n autoPopulateFromMap(id: string): Promise<{ assessment: unknown; applied: string[] }>;\n /** Fill from evidence you supply. Never overwrites a human answer; records `source` as provenance. */\n autoPopulate(id: string, evidence: Record<string, unknown>, source?: string): Promise<{ assessment: unknown; applied: string[] }>;\n submit(id: string): Promise<unknown>;\n approve(id: string, by: string): Promise<unknown>;\n reject(id: string, by: string, reason: string): Promise<unknown>;\n };\n /** The data map produced by an in-environment scan (metadata only). */\n discovery: {\n ingestMap(map: unknown): Promise<{ scannedAt: number; systems: number }>;\n getMap(): Promise<unknown>;\n ropaDrafts(): Promise<{ drafts: unknown[] }>;\n evidence(): Promise<{ evidence: Record<string, unknown> }>;\n /** What changed since the last scan, and where the RoPA disagrees with reality. */\n drift(): Promise<{ comparedTo: number | null; sinceLastScan: unknown[]; againstRecord: unknown[] }>;\n /**\n * Plan warehouse-native policy from the latest map. Returns a PLAN with its own\n * revert — nothing is applied to your warehouse by calling this.\n */\n planEnforcement(\n dialect: 'postgres' | 'mysql' | 'snowflake',\n rules: EnforcementRuleInput[],\n opts?: { permitsTable?: string; policyPrefix?: string },\n ): Promise<{ plan: { statements: unknown[]; revert: unknown[]; skipped: unknown[] } }>;\n };\n /**\n * Regulatory intelligence: the curated privacy-law dataset. Every response carries\n * `reviewedAt` — the dataset ships with the product and is only as current as its\n * last review. Not legal advice.\n */\n regulatory: {\n /** The dataset, optionally narrowed to the jurisdictions you operate in. */\n feed(jurisdictions?: string[]): Promise<RegulatoryFeedResult>;\n /** What takes effect within `days` (default 180), soonest first. */\n upcoming(days?: number): Promise<RegulatoryFeedResult>;\n };\n /** AI governance: policy, the inline gateway, inventory and lineage. */\n ai: {\n getPolicy(): Promise<{ policy: unknown; configured: boolean }>;\n setPolicy(policy: unknown): Promise<{ policy: unknown }>;\n /** Enforce consent + policy on a prompt or response. Requires the ai:inspect scope. */\n inspect(input: AiInspectInput): Promise<AiInspectResult>;\n inventory(): Promise<{ systems: unknown[]; shadow: unknown[] }>;\n lineage(): Promise<{ lineage: Record<string, string[]> }>;\n registerSystem(input: { id: string; name: string; provider?: string; purpose?: string }): Promise<unknown>;\n /** The AI systems declared with registerSystem. */\n systems(): Promise<unknown>;\n audit(limit?: number): Promise<{ entries: unknown[] }>;\n };\n /** DSR fulfillment: plan work per system, poll it from an in-VPC agent, report outcomes. */\n fulfillment: {\n sla(): Promise<unknown>;\n plan(requestId: string, systems: Array<{ system: string; operation: FulfillmentOp }>, includeHistorical?: boolean): Promise<{ requestId: string; tasks: unknown[] }>;\n status(requestId: string): Promise<unknown>;\n /** In-VPC agent: poll pending tasks. */\n pendingTasks(limit?: number): Promise<{ tasks: unknown[] }>;\n /** In-VPC agent: report an outcome. Only the outcome crosses the boundary. */\n reportTask(taskId: string, ok: boolean, error?: string): Promise<unknown>;\n /**\n * Systems you have connected that run part of a request themselves — an identity\n * platform holding your users' accounts, for instance. The in-VPC agent covers the\n * systems we cannot reach; these are the ones we can.\n */\n executors(): Promise<DsarExecutor[]>;\n /**\n * Connect one. The secret is stored encrypted and never returned; the response carries\n * the URL to point that system's webhook at.\n */\n connectExecutor(input: DsarExecutorInput): Promise<DsarExecutorCreated>;\n /**\n * Change a connection in place. Only what you send changes, and the stored credential\n * is kept unless you pass a new `secretKey` — so adding a field to a profile does not\n * cost you the connection or its id.\n */\n updateExecutor(\n id: string,\n patch: {\n profile?: unknown;\n baseUrl?: string;\n secretKey?: string;\n webhookSecret?: string | null;\n auto?: boolean;\n },\n ): Promise<DsarExecutor>;\n /** Disconnect a system; its open sub-tasks stop being driven. */\n disconnectExecutor(id: string): Promise<void>;\n /** The export bundle a connected system produced, fetched from it on demand. */\n taskExport(requestId: string, taskId: string): Promise<DsarTaskExport>;\n /**\n * One task: its status history (`events`), every request made to a connected system on\n * its behalf (`log`, redacted, 30 days) and whether that system's version changed mid-way.\n */\n task(requestId: string, taskId: string): Promise<DsarTask>;\n /**\n * Try a connection against one email without filing anything: runs the lookup, returns\n * the raw (redacted) upstream answers and what a real request would have sent.\n */\n testExecutor(id: string, input: ExecutorTestInput): Promise<ExecutorTestResult>;\n };\n preferences: {\n list(): Promise<PreferenceItem[]>;\n /** Save/update a subject's named-purpose opt-ins. Returns the persisted record. */\n save(subjectId: string, purposes: Record<string, boolean>): Promise<{ record: PreferenceRecord }>;\n /** One subject's record. A subject with none has empty `purposes`. */\n get(subjectId: string): Promise<PreferenceRecord>;\n };\n members: {\n list(): Promise<Member[]>;\n invite(email: string, role: string): Promise<{ member: { userId: string; email: string; role: string } }>;\n setRole(userId: string, role: string): Promise<{ member: { userId: string; email: string; role: string } }>;\n remove(userId: string): Promise<{ ok: true }>;\n };\n keys: {\n list(): Promise<ApiKeyPrefix[]>;\n /**\n * Issue a new API key; the secret is returned once. Pass `scopes` and/or `cbids` for a\n * least-privilege key — omit both for full access to the whole org.\n */\n issue(input?: ApiKeyIssueInput): Promise<ApiKeyIssued>;\n /** Revoke a key by its prefix. Immediate. */\n revoke(prefix: string): Promise<void>;\n /** Rotate a key: a new secret, returned once, with the same scopes, lock and expiry. The old one stops working. */\n roll(prefix: string): Promise<ApiKeyIssued>;\n /** Rename a key, or replace its scopes or property lock. Only the fields sent change. */\n update(prefix: string, patch: ApiKeyUpdate): Promise<{ ok: true }>;\n };\n usage(): Promise<Usage>;\n /** The key's organisation. Requires an unscoped key that is not property-locked. */\n org: {\n get(): Promise<Org>;\n /** Rename the org or set its logo. Deleting the org is not available through the API. */\n update(patch: OrgUpdate): Promise<Org>;\n };\n /** The org's audit log, newest first. API actions appear as `apikey:<prefix>`. */\n audit(limit?: number): Promise<{ entries: AuditEntry[] }>;\n assets: {\n /** Upload a banner image (≤ 1,000,000 bytes) and get its public URL. Requires sites:write. */\n upload(input: AssetUpload): Promise<{ url: string }>;\n /**\n * Delete a stored image. Takes the URL `upload` returned, or just its file name. Only\n * your own org's images are reachable — the folder comes from your API key.\n */\n delete(urlOrFileName: string): Promise<void>;\n };\n /** Provision and manage child orgs. Requires a key with the reseller:* scopes. */\n reseller: {\n list(): Promise<ResellerChildList>;\n /** Provision a child org. With `mintKey`, its first API key is returned once, as `apiKey`. */\n create(input: ResellerChildCreate): Promise<{ child: ResellerChild; apiKey?: string }>;\n get(id: string): Promise<unknown>;\n update(id: string, patch: ResellerChildPatch): Promise<unknown>;\n /**\n * Deprovision a child. Suspends it by default, which is reversible; `{ purge: true }`\n * hard-deletes it and its data, which is not.\n */\n deprovision(id: string, opts?: { purge?: boolean }): Promise<void>;\n listKeys(id: string): Promise<ApiKeyPrefix[]>;\n /** Mint an API key for a child. The secret is returned once. */\n mintKey(id: string, input?: ChildKeyInput): Promise<ApiKeyIssued>;\n revokeKey(id: string, prefix: string): Promise<void>;\n };\n webhooks: {\n list(): Promise<WebhookSubscription[]>;\n create(input: WebhookCreate): Promise<WebhookSubscription>;\n /** Change or pause a subscription. `{ active: false }` pauses without deleting it. */\n update(id: string, patch: WebhookUpdate): Promise<WebhookSubscription>;\n delete(id: string): Promise<void>;\n /** Rotate the signing secret. The new secret is returned once. */\n rollSecret(id: string): Promise<{ secret: string }>;\n /** Send a signed test event now and report what the endpoint answered. */\n test(id: string): Promise<WebhookTestResult>;\n /** Deliveries that failed every retry, newest first. */\n deadLetters(): Promise<{ deadLetters: WebhookDeadLetter[] }>;\n /** Deliver a dead letter again, to the subscription as it is now. */\n replayDeadLetter(id: string): Promise<unknown>;\n };\n /** One person's consent across every site in the org, by the subjectId your apps attach. */\n subjects: {\n /** Requires consent:read; not available to property-locked keys. */\n consent(subjectId: string): Promise<{ subjectId: string; records: unknown[]; count: number; sites: string[] }>;\n };\n banners: {\n list(): Promise<BannerSummary[]>;\n create(input: { name: string; json: SiteConfig }): Promise<BannerRecord>;\n get(id: string): Promise<BannerRecord>;\n update(id: string, patch: { name?: string; json?: SiteConfig }): Promise<BannerRecord>;\n delete(id: string): Promise<void>;\n assignments(id: string): Promise<{ cbids: string[] }>;\n setAssignments(id: string, cbids: string[]): Promise<{ cbids: string[] }>;\n publish(id: string): Promise<{ publishedCbids: string[] }>;\n };\n}\n\nfunction qs(query: Record<string, number | string | undefined> | undefined): string {\n if (!query) return '';\n const parts: string[] = [];\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) parts.push(`${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);\n }\n return parts.length ? `?${parts.join('&')}` : '';\n}\n\n/** Widen a typed query interface (RangeQuery/LogQuery) to the plain record qs() wants. */\nfunction toQuery(q: RangeQuery | LogQuery | undefined): Record<string, number | string | undefined> | undefined {\n return q as Record<string, number | string | undefined> | undefined;\n}\n\nexport function createCookieMunch(opts: CookieMunchOptions): CookieMunchClient {\n const base = `${opts.baseUrl.replace(/\\/+$/, '')}/v1`;\n const doFetch = opts.fetch ?? globalThis.fetch;\n if (!doFetch) throw new Error('No fetch available; pass { fetch } in CookieMunchOptions.');\n\n async function request(method: string, path: string, body?: unknown, raw = false): Promise<unknown> {\n const headers: Record<string, string> = { Authorization: `Bearer ${opts.apiKey}` };\n const init: RequestInit = { method, headers };\n if (body !== undefined) {\n headers['Content-Type'] = 'application/json';\n init.body = JSON.stringify(body);\n }\n const res = await doFetch(`${base}${path}`, init);\n if (!res.ok) {\n let message = `request failed with status ${res.status}`;\n let code: string | undefined;\n try {\n const data = (await res.json()) as { error?: string; code?: string };\n if (typeof data.error === 'string') message = data.error;\n if (typeof data.code === 'string') code = data.code;\n } catch {\n /* non-JSON error body — keep the default message */\n }\n throw new CookieMunchApiError(res.status, message, code);\n }\n if (res.status === 204) return undefined;\n if (raw) return res.text();\n const contentType = res.headers.get('content-type') ?? '';\n return contentType.includes('application/json') ? res.json() : res.text();\n }\n\n const get = (p: string) => request('GET', p);\n const enc = encodeURIComponent;\n\n return {\n me: () => get('/me') as Promise<Identity>,\n languages: () => get('/languages') as Promise<SupportedLanguage[]>,\n orgs: {\n list: () => get('/orgs') as Promise<AccountOrgs>,\n create: (name, opts) =>\n request('POST', '/orgs', { name, ...(opts ?? {}) }) as Promise<{\n org: { id: string; name: string; role: string };\n apiKey?: string;\n keyError?: string;\n }>,\n },\n sites: {\n list: () => get('/sites') as Promise<Site[]>,\n create: (input) => request('POST', '/sites', input) as Promise<Site>,\n get: (cbid) => get(`/sites/${enc(cbid)}`) as Promise<Site>,\n delete: async (cbid) => {\n await request('DELETE', `/sites/${enc(cbid)}`);\n },\n getConfig: (cbid) => get(`/sites/${enc(cbid)}/config`) as Promise<SiteConfig>,\n putConfig: (cbid, config) => request('PUT', `/sites/${enc(cbid)}/config`, config) as Promise<SiteConfig>,\n patchConfig: (cbid, config) => request('PATCH', `/sites/${enc(cbid)}/config`, config) as Promise<SiteConfig>,\n dryRunConfig: (cbid, config, opts) =>\n request(opts?.mode === 'put' ? 'PUT' : 'PATCH', `/sites/${enc(cbid)}/config?dryRun=1`, config) as Promise<ConfigDryRun>,\n cookies: (cbid) => get(`/sites/${enc(cbid)}/cookies`) as Promise<CookieDeclaration>,\n scan: (cbid) => request('POST', `/sites/${enc(cbid)}/scan`) as Promise<ScanStatus>,\n scanStatus: (cbid) => get(`/sites/${enc(cbid)}/scan`) as Promise<ScanStatus>,\n ab: (cbid) => get(`/sites/${enc(cbid)}/ab`) as Promise<AbResult[]>,\n policy: (cbid, o) =>\n request(\n 'GET',\n `/sites/${enc(cbid)}/policy${qs({ contactEmail: o?.contactEmail, effectiveDate: o?.effectiveDate, jurisdictions: o?.jurisdictions?.join(',') })}`,\n undefined,\n true,\n ) as Promise<string>,\n setAdPersonalization: (cbid, input) => request('POST', `/sites/${enc(cbid)}/elements/ad-personalization`, input),\n analyzeSession: (cbid, input) => request('POST', `/sites/${enc(cbid)}/sentry`, input),\n banner: (cbid) => get(`/sites/${enc(cbid)}/banner`) as Promise<{ bannerId: string | null }>,\n blocked: (cbid) => get(`/sites/${enc(cbid)}/blocked`) as Promise<{ reports: BlockedReport[] }>,\n importDeclaration: (cbid, data) => request('POST', `/sites/${enc(cbid)}/import`, { data }) as Promise<ImportedDeclaration>,\n snippet: (cbid, opts) =>\n get(`/sites/${enc(cbid)}/snippet${qs({ blockingmode: opts?.blockingMode, culture: opts?.culture })}`) as Promise<InstallSnippet>,\n previewToken: (cbid, o) => request('POST', `/sites/${enc(cbid)}/preview-token`, o ?? {}) as Promise<PreviewToken>,\n verify: (cbid, method) => request('POST', `/sites/${enc(cbid)}/verify`, { method }) as Promise<VerifyResult>,\n verifyChallenge: (cbid) => get(`/sites/${enc(cbid)}/verify/challenge`),\n createBulk: (sites) => request('POST', '/sites/bulk', { sites }) as Promise<{ results: BulkSiteResult[] }>,\n brand: (cbid) => request('POST', `/sites/${enc(cbid)}/brand`, {}) as Promise<BrandExtractionResult>,\n getFlow: (cbid) => get(`/sites/${enc(cbid)}/flow`) as Promise<SiteFlow>,\n editFlow: (cbid, operations) =>\n request('POST', `/sites/${enc(cbid)}/flow/ops`, { operations }) as Promise<FlowOpResponse>,\n setFlow: (cbid, config) => request('PUT', `/sites/${enc(cbid)}/flow`, config) as Promise<FlowOpResponse>,\n },\n consent: {\n verify: (cbid) => get(`/sites/${enc(cbid)}/consent/verify`) as Promise<{ valid: boolean }>,\n stats: (cbid, query) => get(`/sites/${enc(cbid)}/consent/stats${qs(toQuery(query))}`) as Promise<ConsentDay[]>,\n log: (cbid, query) => get(`/sites/${enc(cbid)}/consent/log${qs(toQuery(query))}`) as Promise<ConsentLogRow[]>,\n export: (cbid, query) => request('GET', `/sites/${enc(cbid)}/consent/export${qs(toQuery(query))}`, undefined, true) as Promise<string>,\n receipt: (cbid, stamp) => get(`/sites/${enc(cbid)}/receipt/${enc(stamp)}`) as Promise<SignedReceipt>,\n eraseSubject: (cbid, stamp) =>\n request('POST', `/sites/${enc(cbid)}/erase-consent`, { stamp }) as Promise<{ erased: number }>,\n exportSubject: (cbid, stamp) =>\n request('GET', `/sites/${enc(cbid)}/subject-export?stamp=${enc(stamp)}`) as Promise<{ cbid: string; stamp: string; records: unknown[]; count: number }>,\n ingest: async (input) => {\n // The public ingest endpoint lives under /api/v1, not the /v1 dev-API prefix,\n // so it is called directly rather than via `request` (which prepends /v1).\n const res = await doFetch(`${opts.baseUrl.replace(/\\/+$/, '')}/api/v1/consent`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n });\n if (!res.ok) {\n let message = `consent ingest failed with status ${res.status}`;\n let code: string | undefined;\n try {\n const data = (await res.json()) as { error?: string; code?: string };\n if (typeof data.error === 'string') message = data.error;\n if (typeof data.code === 'string') code = data.code;\n } catch {\n /* non-JSON error body — keep the default message */\n }\n throw new CookieMunchApiError(res.status, message, code);\n }\n },\n },\n dsar: {\n list: () => get('/dsar') as Promise<DsarRequest[]>,\n get: (id) => get(`/dsar/${enc(id)}`) as Promise<DsarRequest>,\n create: (input) => request('POST', '/dsar', input) as Promise<{ request: DsarRequest }>,\n advance: (id, toStatus) => request('POST', `/dsar/${enc(id)}/advance`, { toStatus }) as Promise<{ request: DsarRequest }>,\n response: (id) => request('GET', `/dsar/${enc(id)}/response`, undefined, true) as Promise<string>,\n erase: (id, cbid, stamp) => request('POST', `/dsar/${enc(id)}/erase`, { cbid, stamp }) as Promise<DsarEraseResult>,\n export: (id, cbid, stamp) => request('POST', `/dsar/${enc(id)}/export`, { cbid, stamp }) as Promise<DsarExportResult>,\n delete: async (id) => {\n await request('DELETE', `/dsar/${enc(id)}`);\n },\n },\n vendors: {\n list: () => get('/vendors') as Promise<ScoredVendor[]>,\n create: (input) => request('POST', '/vendors', input) as Promise<{ vendor: VendorRecord; risk: RiskScore }>,\n },\n ropa: {\n list: () => get('/ropa') as Promise<RopaEntry[]>,\n create: (input) => request('POST', '/ropa', input) as Promise<{ entry: RopaEntry }>,\n exportCsv: () => request('GET', '/ropa/export.csv', undefined, true) as Promise<string>,\n },\n brandKits: {\n list: () => get('/brand-kits') as Promise<BrandKit[]>,\n create: (input) => request('POST', '/brand-kits', input) as Promise<{ kit: BrandKit }>,\n delete: async (id) => {\n await request('DELETE', `/brand-kits/${enc(id)}`);\n },\n },\n identity: {\n resolve: (identifiers) => request('POST', '/identity/resolve', { identifiers }) as Promise<{ subjectId: string | null }>,\n link: (identifiers) => request('POST', '/identity/link', { identifiers }) as Promise<{ subjectId: string }>,\n cluster: (subjectId) => get(`/identity/${enc(subjectId)}`) as Promise<{ subjectId: string; identifiers: Identifier[] }>,\n },\n vault: {\n record: (identifiers, decisions) => request('POST', '/vault/record', { identifiers, decisions }) as Promise<{ subjectId: string; permits: unknown[] }>,\n current: (identifiers) => request('POST', '/vault/current', { identifiers }) as Promise<{ purposes: Record<string, boolean> }>,\n permits: (identifiers) => request('POST', '/vault/permits', { identifiers }) as Promise<{ permits: unknown[] }>,\n },\n profile: {\n get: (identifiers) => request('POST', '/profile/get', { identifiers }),\n setAttributes: (identifiers, attributes) => request('POST', '/profile/attributes', { identifiers, attributes }) as Promise<{ subjectId: string }>,\n activate: (identifiers, purpose) => request('POST', '/profile/activate', { identifiers, purpose }) as Promise<{ attributes: Record<string, string> }>,\n },\n subscriptions: {\n topics: () => get('/subscriptions/topics') as Promise<{ topics: SubscriptionTopicInput[] }>,\n setTopics: (topics) => request('PUT', '/subscriptions/topics', { topics }) as Promise<{ topics: SubscriptionTopicInput[] }>,\n get: (subjectId) => get(`/subscriptions/${enc(subjectId)}`),\n set: (subjectId, topic, channel, optedIn) => request('PUT', `/subscriptions/${enc(subjectId)}`, { topic, channel, optedIn }),\n unsubscribeAll: (subjectId) => request('POST', `/subscriptions/${enc(subjectId)}/unsubscribe-all`),\n resubscribe: (subjectId) => request('POST', `/subscriptions/${enc(subjectId)}/resubscribe`),\n activation: (subjectId, topics) => request('POST', `/subscriptions/${enc(subjectId)}/activation`, { topics }) as Promise<{ entries: unknown[] }>,\n },\n assessments: {\n templates: () => get('/assessments/templates') as Promise<{ templates: unknown[] }>,\n list: () => get('/assessments') as Promise<{ assessments: unknown[] }>,\n start: (template, subject) => request('POST', '/assessments', { template, subject }),\n get: (id) => get(`/assessments/${enc(id)}`),\n answer: (id, questionId, value) => request('POST', `/assessments/${enc(id)}/answer`, { questionId, value }),\n autoPopulateFromMap: (id) => request('POST', `/assessments/${enc(id)}/autopopulate-from-map`) as Promise<{ assessment: unknown; applied: string[] }>,\n autoPopulate: (id, evidence, source) =>\n request('POST', `/assessments/${enc(id)}/autopopulate`, { evidence, ...(source !== undefined ? { source } : {}) }) as Promise<{ assessment: unknown; applied: string[] }>,\n submit: (id) => request('POST', `/assessments/${enc(id)}/submit`),\n approve: (id, by) => request('POST', `/assessments/${enc(id)}/approve`, { by }),\n reject: (id, by, reason) => request('POST', `/assessments/${enc(id)}/reject`, { by, reason }),\n },\n discovery: {\n ingestMap: (map) => request('POST', '/discovery/map', { map }) as Promise<{ scannedAt: number; systems: number }>,\n getMap: () => get('/discovery/map'),\n ropaDrafts: () => get('/discovery/ropa-drafts') as Promise<{ drafts: unknown[] }>,\n evidence: () => get('/discovery/evidence') as Promise<{ evidence: Record<string, unknown> }>,\n drift: () => get('/discovery/drift') as Promise<{ comparedTo: number | null; sinceLastScan: unknown[]; againstRecord: unknown[] }>,\n planEnforcement: (dialect, rules, opts) =>\n request('POST', '/discovery/enforcement', {\n dialect,\n rules,\n ...(opts?.permitsTable !== undefined ? { permitsTable: opts.permitsTable } : {}),\n ...(opts?.policyPrefix !== undefined ? { policyPrefix: opts.policyPrefix } : {}),\n }) as Promise<{ plan: { statements: unknown[]; revert: unknown[]; skipped: unknown[] } }>,\n },\n regulatory: {\n feed: (jurisdictions) =>\n get(`/regulatory/feed${qs({ jurisdictions: jurisdictions?.join(',') })}`) as Promise<RegulatoryFeedResult>,\n upcoming: (days) => get(`/regulatory/upcoming${qs({ days })}`) as Promise<RegulatoryFeedResult>,\n },\n ai: {\n getPolicy: () => get('/ai/policy') as Promise<{ policy: unknown; configured: boolean }>,\n setPolicy: (policy) => request('PUT', '/ai/policy', { policy }) as Promise<{ policy: unknown }>,\n inspect: (input) => request('POST', '/ai/inspect', input) as Promise<AiInspectResult>,\n inventory: () => get('/ai/inventory') as Promise<{ systems: unknown[]; shadow: unknown[] }>,\n lineage: () => get('/ai/lineage') as Promise<{ lineage: Record<string, string[]> }>,\n registerSystem: (input) => request('POST', '/ai/systems', input),\n systems: () => get('/ai/systems'),\n audit: (limit) => get(`/ai/audit${qs({ limit })}`) as Promise<{ entries: unknown[] }>,\n },\n fulfillment: {\n sla: () => get('/dsar/sla'),\n plan: (requestId, systems, includeHistorical) =>\n request('POST', `/dsar/${enc(requestId)}/plan`, { systems, ...(includeHistorical ? { includeHistorical: true } : {}) }) as Promise<{ requestId: string; tasks: unknown[] }>,\n status: (requestId) => get(`/dsar/${enc(requestId)}/fulfillment`),\n pendingTasks: (limit) => get(`/dsar/agent/tasks${qs({ limit })}`) as Promise<{ tasks: unknown[] }>,\n reportTask: (taskId, ok, error) => request('POST', `/dsar/agent/tasks/${enc(taskId)}/result`, { ok, ...(error !== undefined ? { error } : {}) }),\n executors: () => get('/dsar/executors') as Promise<DsarExecutor[]>,\n connectExecutor: (input) => request('POST', '/dsar/executors', input) as Promise<DsarExecutorCreated>,\n updateExecutor: (id, patch) => request('PATCH', `/dsar/executors/${enc(id)}`, patch) as Promise<DsarExecutor>,\n disconnectExecutor: async (id) => {\n await request('DELETE', `/dsar/executors/${enc(id)}`);\n },\n taskExport: (requestId, taskId) => get(`/dsar/${enc(requestId)}/tasks/${enc(taskId)}/export`) as Promise<DsarTaskExport>,\n task: (requestId, taskId) => get(`/dsar/${enc(requestId)}/tasks/${enc(taskId)}`) as Promise<DsarTask>,\n testExecutor: (id, input) => request('POST', `/dsar/executors/${enc(id)}/test`, input) as Promise<ExecutorTestResult>,\n },\n preferences: {\n list: () => get('/preferences') as Promise<PreferenceItem[]>,\n save: (subjectId, purposes) => request('POST', '/preferences', { subjectId, purposes }) as Promise<{ record: PreferenceRecord }>,\n get: (subjectId) => get(`/preferences/${enc(subjectId)}`) as Promise<PreferenceRecord>,\n },\n members: {\n list: () => get('/members') as Promise<Member[]>,\n invite: (email, role) =>\n request('POST', '/members', { email, role }) as Promise<{ member: { userId: string; email: string; role: string } }>,\n setRole: (userId, role) =>\n request('PATCH', `/members/${enc(userId)}`, { role }) as Promise<{ member: { userId: string; email: string; role: string } }>,\n remove: (userId) => request('DELETE', `/members/${enc(userId)}`) as Promise<{ ok: true }>,\n },\n keys: {\n list: () => get('/keys') as Promise<ApiKeyPrefix[]>,\n issue: (input) => request('POST', '/keys', input ?? {}) as Promise<ApiKeyIssued>,\n revoke: async (prefix) => {\n await request('DELETE', `/keys/${enc(prefix)}`);\n },\n roll: (prefix) => request('POST', `/keys/${enc(prefix)}/roll`) as Promise<ApiKeyIssued>,\n update: (prefix, patch) => request('PATCH', `/keys/${enc(prefix)}`, patch) as Promise<{ ok: true }>,\n },\n usage: () => get('/usage') as Promise<Usage>,\n org: {\n get: () => get('/org') as Promise<Org>,\n update: (patch) => request('PATCH', '/org', patch) as Promise<Org>,\n },\n audit: (limit) => get(`/audit${qs({ limit })}`) as Promise<{ entries: AuditEntry[] }>,\n assets: {\n upload: (input) => request('POST', '/assets', input) as Promise<{ url: string }>,\n delete: async (urlOrFileName) => {\n const name = urlOrFileName.split('/').pop() ?? urlOrFileName;\n await request('DELETE', `/assets/${enc(name)}`);\n },\n },\n reseller: {\n list: () => get('/reseller/customers') as Promise<ResellerChildList>,\n create: (input) => request('POST', '/reseller/customers', input) as Promise<{ child: ResellerChild; apiKey?: string }>,\n get: (id) => get(`/reseller/customers/${enc(id)}`),\n update: (id, patch) => request('PATCH', `/reseller/customers/${enc(id)}`, patch),\n deprovision: async (id, o) => {\n await request('DELETE', `/reseller/customers/${enc(id)}${qs({ purge: o?.purge ? 'true' : undefined })}`);\n },\n listKeys: (id) => get(`/reseller/customers/${enc(id)}/keys`) as Promise<ApiKeyPrefix[]>,\n mintKey: (id, input) => request('POST', `/reseller/customers/${enc(id)}/keys`, input ?? {}) as Promise<ApiKeyIssued>,\n revokeKey: async (id, prefix) => {\n await request('DELETE', `/reseller/customers/${enc(id)}/keys/${enc(prefix)}`);\n },\n },\n webhooks: {\n list: () => get('/webhooks') as Promise<WebhookSubscription[]>,\n create: (input) => request('POST', '/webhooks', input) as Promise<WebhookSubscription>,\n update: (id, patch) => request('PATCH', `/webhooks/${enc(id)}`, patch) as Promise<WebhookSubscription>,\n delete: async (id) => {\n await request('DELETE', `/webhooks/${enc(id)}`);\n },\n rollSecret: (id) => request('POST', `/webhooks/${enc(id)}/roll`) as Promise<{ secret: string }>,\n test: (id) => request('POST', `/webhooks/${enc(id)}/test`) as Promise<WebhookTestResult>,\n deadLetters: () => get('/webhooks/dead-letters') as Promise<{ deadLetters: WebhookDeadLetter[] }>,\n replayDeadLetter: (id) => request('POST', `/webhooks/dead-letters/${enc(id)}/replay`),\n },\n subjects: {\n consent: (subjectId) =>\n get(`/subjects/${enc(subjectId)}/consent`) as Promise<{ subjectId: string; records: unknown[]; count: number; sites: string[] }>,\n },\n banners: {\n list: () => get('/banners') as Promise<BannerSummary[]>,\n create: (input) => request('POST', '/banners', input) as Promise<BannerRecord>,\n get: (id) => get(`/banners/${enc(id)}`) as Promise<BannerRecord>,\n update: (id, patch) => request('PUT', `/banners/${enc(id)}`, patch) as Promise<BannerRecord>,\n delete: async (id) => {\n await request('DELETE', `/banners/${enc(id)}`);\n },\n assignments: (id) => get(`/banners/${enc(id)}/assignments`) as Promise<{ cbids: string[] }>,\n setAssignments: (id, cbids) => request('PUT', `/banners/${enc(id)}/assignments`, { cbids }) as Promise<{ cbids: string[] }>,\n publish: (id) => request('POST', `/banners/${enc(id)}/publish`) as Promise<{ publishedCbids: string[] }>,\n },\n };\n}\n"],"mappings":"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","names":["opts"]}
1
+ {"version":3,"sources":["../src/webhooks.ts","../src/index.ts"],"sourcesContent":["/**\n * Verify an inbound Cookie Munch webhook signature.\n *\n * Cookie Munch signs every webhook delivery with the subscription's `whsec_…` secret\n * (returned once when the webhook is created). Each POST carries:\n *\n * X-CookieMunch-Signature: sha256=<hex HMAC-SHA256(secret, `${timestamp}.${rawBody}`)>\n * X-CookieMunch-Timestamp: <unix SECONDS>\n * X-CookieMunch-Event: <event type, e.g. dsar.action_required>\n *\n * The signature covers the timestamp as well as the body, so a captured delivery cannot be\n * replayed forever — reject deliveries whose timestamp is outside `toleranceSeconds`\n * (default 300s / 5 min). This matches the platform-standard (Stripe-style) verifyWebhook shape.\n *\n * @example\n * import { verifyWebhookSignature } from '@cookiemunch/sdk';\n * // Express — capture the RAW body: app.use(express.raw({ type: 'application/json' }))\n * app.post('/hooks/cm', (req, res) => {\n * const ok = verifyWebhookSignature({\n * payload: req.body.toString('utf8'), // exact bytes, not a re-stringified object\n * signature: req.header('X-CookieMunch-Signature'),\n * timestamp: req.header('X-CookieMunch-Timestamp'),\n * secret: process.env.CM_WEBHOOK_SECRET!, // the whsec_… value\n * });\n * if (!ok) return res.sendStatus(400);\n * const event = JSON.parse(req.body.toString('utf8')); // { id, type, cbid, createdAt, data }\n * // …handle event.type…\n * res.sendStatus(200);\n * });\n *\n * Server-side only: uses `node:crypto`. Pass the EXACT raw request body — verifying against a\n * re-serialized JSON object fails whenever key order or whitespace differs from what was signed.\n */\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\nexport interface VerifyWebhookInput {\n /** The raw request body, exactly as received. Do NOT re-stringify parsed JSON. */\n payload: string;\n /** The `X-CookieMunch-Signature` header value. */\n signature: string | undefined | null;\n /** The `X-CookieMunch-Timestamp` header value (unix seconds). */\n timestamp: string | number | undefined | null;\n /** The subscription secret (`whsec_…`), shown once when the webhook was created. */\n secret: string;\n /**\n * Maximum age (in seconds) of a delivery before it is rejected as stale/replayed.\n * Defaults to 300 (5 minutes). Pass 0 to disable the freshness check entirely.\n */\n toleranceSeconds?: number;\n /** Current time in ms (injectable for tests); defaults to `Date.now()`. */\n now?: number;\n}\n\n/**\n * Returns true only when the signature is valid AND (unless disabled) the timestamp is fresh.\n * Fail-closed: any missing/malformed input returns false rather than throwing.\n */\nexport function verifyWebhookSignature(input: VerifyWebhookInput): boolean {\n const { payload, signature, timestamp, secret } = input;\n if (!signature || timestamp == null || timestamp === '' || !secret) return false;\n\n const tsRaw = String(timestamp);\n const tsNum = Number(tsRaw);\n if (!Number.isFinite(tsNum)) return false;\n\n const tolerance = input.toleranceSeconds ?? 300;\n if (tolerance > 0) {\n const nowSec = Math.floor((input.now ?? Date.now()) / 1000);\n if (Math.abs(nowSec - tsNum) > tolerance) return false; // stale or replayed\n }\n\n // HMAC over the EXACT timestamp bytes received, joined to the raw body with a dot — matching\n // the server's `sign(secret, timestamp, body)` (packages/server/src/webhook-delivery.ts).\n const expected = `sha256=${createHmac('sha256', secret).update(`${tsRaw}.${payload}`).digest('hex')}`;\n\n // Constant-time compare; timingSafeEqual throws on length mismatch, so guard first.\n const got = Buffer.from(signature);\n const want = Buffer.from(expected);\n if (got.length !== want.length) return false;\n return timingSafeEqual(got, want);\n}\n","/**\n * @cookiemunch/sdk — a small, typed REST client for the Cookie Munch Developer API\n * (the /v1 surface). Construct it with an API key; the org is derived server-side\n * from the key, so callers never pass an orgId. `fetch` is injectable for testing\n * and non-browser/non-Node runtimes; it defaults to the global fetch.\n *\n * const fc = createCookieMunch({ apiKey: 'fck_…', baseUrl: 'https://cmp.example.com' });\n * const sites = await fc.sites.list();\n */\n\nexport * from './types.js';\nexport * from './consent.js';\nexport * from './webhooks.js';\nimport type {\n Identity,\n AccountOrgs,\n Site,\n SiteCreate,\n SiteConfig,\n ConsentDay,\n ConsentLogRow,\n RangeQuery,\n LogQuery,\n DsarRequest,\n DsarCreate,\n DsarStatus,\n ScoredVendor,\n VendorInput,\n VendorRecord,\n RiskScore,\n RopaEntry,\n RopaInput,\n SignedReceipt,\n PreferenceRecord,\n} from './types.js';\n\nexport interface CookieMunchOptions {\n apiKey: string;\n /** e.g. \"https://cmp.example.com\" — the /v1 prefix is appended automatically. */\n baseUrl: string;\n /** Injectable for tests / custom runtimes. Defaults to the global fetch. */\n fetch?: typeof fetch;\n}\n\n/**\n * Thrown for any non-2xx response. `message` is the server's `error` field when present;\n * `code` is populated from the server's `code` field whenever the error body carries one\n * (e.g. banner-library conflicts, docs-admin checks) — not only for scoped-key\n * (`insufficient_scope`) responses. It's `undefined` when the body has no `code`.\n */\nexport class CookieMunchApiError extends Error {\n readonly status: number;\n readonly code?: string;\n constructor(status: number, message: string, code?: string) {\n super(message);\n this.name = 'CookieMunchApiError';\n this.status = status;\n if (code !== undefined) this.code = code;\n }\n}\n\n/*\n * Additional /v1 response shapes. These live here (rather than in types.ts) but are\n * still re-exported from the package root via `export * from './types.js'` above plus\n * these top-level `export interface` declarations.\n */\n\n/** A cookie discovered on a site, classified into a consent category (wire shape of the\n * cookie-declaration feed). Matches the server's CategorizedCookie schema. */\nexport interface CategorizedCookie {\n name: string;\n category: 'necessary' | 'preferences' | 'statistics' | 'marketing' | 'unclassified' | (string & {});\n domain?: string;\n provider?: string;\n purpose?: string;\n expiry?: string;\n /** Page URLs (multi-page scan) that set this cookie, earliest first. */\n foundOnPages?: string[];\n /** The site owner declared this cookie; it overrides the scan. */\n declared?: boolean;\n /** A declared `prefix*` wildcard row that no observed cookie matched yet. */\n pattern?: boolean;\n /** Why a person should look at it. Visitors see such cookies under \"Unclassified\". */\n review?: 'unclassified' | 'category_disabled' | 'category_unknown';\n}\n\n/** A site's cookie declaration: the latest scan and observed visits with the site's own\n * declarations on top. This is what GET /v1/sites/:cbid/cookies returns. */\nexport interface CookieDeclaration {\n /** Epoch-ms of the latest scan or declaration change, or 0 if none. */\n updatedAt: number;\n cookies: CategorizedCookie[];\n /** The site owner's declarations, as stored. */\n declared?: DeclaredCookie[];\n}\n\n/** What the site owner says a cookie is (PUT /v1/sites/:cbid/cookies/:name). */\nexport interface DeclaredCookieInput {\n /** A standard category or one of the site's `categories.custom` ids. Not `unclassified`. */\n category: string;\n /** Shown to visitors. */\n purpose: string;\n provider?: string;\n expiry?: string;\n domain?: string;\n /** Treat the name as a prefix wildcard (a trailing `*` does the same). */\n pattern?: boolean;\n}\n\n/** A stored declaration. */\nexport interface DeclaredCookie extends DeclaredCookieInput {\n name: string;\n updatedAt: number;\n}\n\n/** A warning about a write: e.g. the category is switched off for the site. */\nexport interface DeclarationWarning {\n field: string;\n reason: string;\n}\n\n/** What an import with `apply` declared, on top of what it read. */\nexport interface AppliedImport extends ImportedDeclaration {\n applied: number;\n skipped: Array<{ name: string; reason: string }>;\n warnings?: DeclarationWarning[];\n}\n\n/** @deprecated The server returns {@link CookieDeclaration}; kept only for back-compat. */\nexport interface SiteCookie {\n name: string;\n domain: string;\n category: string;\n provider?: string;\n purpose?: string;\n expiry?: string;\n firstSeen?: number;\n}\n\n/** Cookie-scan status, from GET /v1/sites/:cbid/scan. */\nexport interface ScanStatus {\n status: 'idle' | 'scanning';\n /** Epoch-ms of the last completed scan, or null. */\n lastScannedAt: number | null;\n}\n\n/** @deprecated The server returns {@link ScanStatus}; kept only for back-compat. */\nexport interface ScanResult {\n scanId: string;\n status: 'queued' | 'running' | 'complete' | 'failed';\n startedAt?: number;\n finishedAt?: number;\n pagesScanned?: number;\n cookiesFound?: number;\n}\n\n/** A/B banner experiment results for a single variant. */\nexport interface AbResult {\n variant: string;\n impressions: number;\n optIns: number;\n optInRate: number;\n}\n\n/** The response shape of GET /v1/sites/:cbid/snippet. */\n// Keep in sync with packages/server/src/snippet.ts InstallSnippet\nexport interface InstallSnippet {\n /** The exact `<script>` tag to paste into `<head>`. */\n snippet: string;\n /** URL of the consent.js embed file (the embed/CDN origin). */\n src: string;\n /** The API origin (beacons + consent-tcf.js). Present when the server reports it. */\n api?: string;\n /** The site identifier. */\n cbid: string;\n /** The blocking mode on the tag — the site's `blocking.mode` unless overridden. */\n blockingMode: string;\n /** Where `blockingMode` came from: the site's config, or the `blockingMode` option. */\n blockingModeSource?: 'config' | 'query';\n /** The tag's data-culture, when it has one. */\n culture?: string;\n /** CSP sources this site needs, by directive, plus a paste-able `policy`. */\n csp?: CspSources;\n /** Present when an override disagrees with the stored config. */\n warnings?: ConfigWarning[];\n}\n\n/** Content Security Policy sources the embed needs. */\nexport interface CspSources {\n script: string[];\n connect: string[];\n img: string[];\n /** Always includes 'unsafe-inline' (the banner's shadow-root <style>), unless you use a nonce. */\n style: string[];\n font: string[];\n frame: string[];\n /** The same as one policy fragment. */\n policy: string;\n}\n\n/** Something a config write did not store as sent. */\nexport interface ConfigWarning {\n field: string;\n reason: string;\n}\n\n/** One leaf a config write would change. */\nexport interface ConfigChange {\n path: string;\n from?: unknown;\n to?: unknown;\n}\n\n/** `PUT …/config` options. `keep: 'unspecified'` keeps the stored value of every top-level field the body omits. */\nexport interface PutConfigOptions {\n keep?: 'unspecified';\n}\n\n/** What a config write would do (`?dryRun=1`) — nothing is saved. */\nexport interface ConfigDryRun {\n dryRun: true;\n /** The config as it would be saved. */\n config: SiteConfig;\n /** Every leaf that would change. */\n diff: ConfigChange[];\n warnings?: ConfigWarning[];\n}\n\n/** Options for POST /v1/sites/:cbid/preview-token. All optional. */\nexport interface PreviewTokenOptions {\n /** Lifetime in seconds, 1..3600. Default 900. */\n ttlSeconds?: number;\n /** The page to preview. Default: the site's registered domain. */\n url?: string;\n /** Region to force: a country or subdivision code (`de`, `us-ca`) or `eu`. */\n region?: string;\n /** Language to force, as a BCP 47 tag (`de`, `pt-BR`). */\n lang?: string;\n}\n\n/** A banner preview token, and the page URL that opens the preview. */\nexport interface PreviewToken {\n cbid: string;\n token: string;\n /** Epoch milliseconds. */\n expiresAt: number;\n region?: string;\n lang?: string;\n /** The page with `cm_preview` / `cm_region` / `cm_lang` added. */\n previewUrl?: string;\n}\n\n/** Options for the snippet endpoint. */\nexport interface SnippetOptions {\n /** Override the site's configured `blocking.mode`. Default: the config's. */\n blockingMode?: 'auto' | 'manual';\n /** Language culture override, e.g. \"en\" or \"fr\". */\n culture?: string;\n}\n\n/**\n * The result of domain verification.\n * Keep in sync with the POST /v1/sites/:cbid/verify response in\n * packages/server/src/dev-api.ts (~L267) — note the server adds `method` on success,\n * so this wire shape is wider than domain-verify.ts's internal VerifyResult.\n */\nexport interface VerifyResult {\n verified: boolean;\n method?: 'dns' | 'meta' | 'file';\n reason?: string;\n}\n\n/** A site brand suggestion (\"Match my site\"). Keep in sync with packages/server/src/brand-extract.ts BrandSuggestion. */\nexport interface BrandSuggestion {\n background?: string;\n text?: string;\n highlight?: string;\n fontFamily?: string;\n fontUrl?: string;\n palette?: string[];\n}\n\n/** The response shape of POST /v1/sites/:cbid/brand. */\nexport interface BrandExtractionResult {\n suggestion: BrandSuggestion;\n}\n\n/** A validation/lint issue on a v2 banner flow. */\nexport interface FlowIssue {\n code: string;\n message: string;\n path?: string;\n}\n\n/** A site's v2 banner flow + lint issues, from GET /v1/sites/:cbid/flow. */\nexport interface SiteFlow {\n v: 2;\n flow: Record<string, unknown>;\n categories: Record<string, unknown>;\n customCss?: string;\n lint: FlowIssue[];\n}\n\n/**\n * A single structured flow edit operation. `op` selects the kind; remaining fields are\n * that op's args (e.g. { op: 'addView', id, surface, layoutMode }).\n */\nexport type FlowOp = { op: string } & Record<string, unknown>;\n\n/**\n * Result of editFlow/setFlow. On success the new flow is persisted and returned;\n * on a validation/lint failure the config is NOT saved and `issues` says why.\n */\nexport type FlowOpResponse =\n | { ok: true; flow: Record<string, unknown> }\n | { ok: false; failedAt?: number; op?: unknown; issues: FlowIssue[] };\n\n/** A reusable brand kit (colors/logo/typography) for banner theming. */\nexport interface BrandKit {\n id: string;\n orgId: string;\n name: string;\n /** Banner theme tokens (opaque design object). */\n theme: Record<string, unknown>;\n /** Optional banner content overrides (opaque). */\n content?: Record<string, unknown>;\n logoUrl?: string;\n customCss?: string;\n}\n\n/** A configurable consent preference/purpose item. */\nexport interface PreferenceItem {\n id: string;\n orgId: string;\n cbid?: string;\n label: string;\n description?: string;\n category?: string;\n}\n\n/** An organization member. */\nexport interface Member {\n userId: string;\n email: string;\n role: 'owner' | 'admin' | 'member' | 'viewer' | (string & {});\n}\n\n/** Display metadata for an issued key — never the secret. Returned by keys.list(). */\nexport interface ApiKeyPrefix {\n prefix: string;\n createdAt?: number;\n}\n\n/** A newly issued key. `key` is returned ONCE and never again. Returned by keys.issue(). */\nexport interface ApiKeyIssued {\n key: string;\n prefix: string;\n}\n\n/** @deprecated No endpoint returns this shape; use {@link ApiKeyPrefix} / {@link ApiKeyIssued}. */\nexport interface ApiKey {\n id: string;\n orgId: string;\n prefix: string;\n name?: string;\n createdAt: number;\n lastUsedAt?: number;\n secret?: string;\n}\n\n/**\n * Input for issuing a new API key.\n *\n * NOTE: the server's POST /v1/keys currently accepts no body and always mints an\n * unscoped key — `name` is not yet honoured server-side. It's kept here (rather than\n * dropped) so a future scoped/named-key endpoint can start reading it without an SDK\n * major version bump; see {@link CookieMunchClient.keys}.\n */\nexport interface ApiKeyIssueInput {\n name?: string;\n /** Least privilege: grant only these scopes. Omit for a full-access key. */\n scopes?: string[];\n /**\n * Lock the key to these properties. A locked key works only on those sites and on no\n * org-wide endpoint — see \"Keys locked to properties\" in the client reference.\n */\n cbids?: string[];\n /** Expire the key after this many days (1–3650). */\n expiresInDays?: number;\n}\n\n/** Org usage/quota summary. Matches the server's Usage schema. */\nexport interface Usage {\n domains: number;\n seats: number;\n monthlyEvents: number;\n}\n\n/** A webhook subscription. `secret` is present only in the create response. */\nexport interface WebhookSubscription {\n id: string;\n orgId: string;\n url: string;\n secret?: string;\n events: string[];\n cbid: string | null;\n active: boolean;\n createdAt: number;\n}\n\n/** One cookie read from another CMP's export. */\nexport interface ImportedCookie {\n name: string;\n domain: string;\n /** Ours, or `unclassified` when their category did not map to one. */\n category: 'necessary' | 'preferences' | 'statistics' | 'marketing' | 'unclassified';\n /** What the previous vendor called it, kept for review. */\n sourceCategory: string;\n purpose: string;\n provider: string;\n expiry: string;\n}\n\n/** What a competitor's cookie-declaration export contained, in our vocabulary. */\nexport interface ImportedDeclaration {\n source: 'onetrust' | 'cookiebot' | 'cookieyes' | 'unknown';\n cookies: ImportedCookie[];\n /** Rows whose category could not be placed — review these first. */\n unmapped: Array<{ name: string; category: string }>;\n}\n\n/**\n * A page that refused to load the banner renderer. The site is live and configured, so\n * every other signal looks healthy; this is the one that says nobody can be asked.\n */\nexport interface BlockedReport {\n cbid: string;\n /** Origin of the page the embed was running on. */\n page: string;\n /** The bundle URL that page refused. */\n url: string;\n reason: string;\n firstSeen: number;\n lastSeen: number;\n /** How many times this page has reported. Repeats are counted, not stored. */\n count: number;\n}\n\n/** A webhook delivery that failed every retry. Replay it by `id`. */\nexport interface WebhookDeadLetter {\n id: string;\n orgId: string;\n subscriptionId: string;\n url: string;\n eventType: string;\n cbid: string | null;\n payload: unknown;\n attempts: number;\n lastStatus?: number;\n lastError?: string;\n failedAt: number;\n}\n\n/** What a test delivery's endpoint answered. */\nexport interface WebhookTestResult {\n ok: boolean;\n status?: number;\n error?: string;\n}\n\n/** The key's organisation. */\nexport interface Org {\n id: string;\n name: string;\n plan: string;\n logoUrl: string | null;\n}\n\n/** Change an org's name or logo. `logoUrl: null` removes the logo. */\nexport interface OrgUpdate {\n name?: string;\n logoUrl?: string | null;\n}\n\n/** One entry in the org's audit log. */\nexport interface AuditEntry {\n id: string;\n orgId: string;\n actorUserId: string;\n actorEmail?: string;\n action: string;\n target?: string;\n meta?: Record<string, unknown>;\n at: number;\n}\n\n/** Rename a key, or replace its scopes or the properties it is locked to. */\nexport interface ApiKeyUpdate {\n name?: string;\n scopes?: string[];\n cbids?: string[];\n}\n\n/** An image to upload: base64 (or a `data:` URL) plus its type. */\nexport interface AssetUpload {\n data: string;\n contentType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif' | 'image/svg+xml';\n}\n\n/** Result of erasing a subject's consent for a deletion request. */\nexport interface DsarEraseResult {\n erased: number;\n encryptionEnabled: boolean;\n /** Present when no KEK is configured, so nothing was cryptographically erased. */\n warning?: string;\n request: DsarRequest;\n}\n\n/** Result of exporting a subject's consent for an access or portability request. */\nexport interface DsarExportResult {\n records: unknown[];\n count: number;\n request: DsarRequest;\n}\n\n/** Input for creating a webhook subscription. */\nexport interface WebhookCreate {\n url: string;\n events: string[];\n cbid?: string;\n}\n\n/** Fields to change on a webhook subscription. Only those present are sent. */\nexport interface WebhookUpdate {\n url?: string;\n events?: string[];\n /** One property, or null for every property in the org. */\n cbid?: string | null;\n /** false pauses delivery without deleting the subscription. */\n active?: boolean;\n}\n\n/** One site to create in a bulk call. */\nexport interface BulkSiteInput {\n domain: string;\n cbid?: string;\n platform?: string;\n}\n\n/** Per-item outcome of a bulk create, in input order. */\nexport interface BulkSiteResult {\n ok: boolean;\n cbid?: string;\n domain?: string;\n error?: string;\n}\n\n/** Input for creating a brand kit. */\nexport interface BrandKitCreate {\n name: string;\n theme: unknown;\n content?: unknown;\n logoUrl?: string;\n customCss?: string;\n}\n\n/** A lightweight summary of an account-level banner, as returned by GET /v1/banners. */\nexport interface BannerSummary {\n id: string;\n name: string;\n updatedAt: number;\n assignedCbids: string[];\n}\n\n/** A full account-level banner record, including its config JSON. */\nexport interface BannerRecord {\n id: string;\n orgId: string;\n name: string;\n json: SiteConfig;\n createdAt: number;\n updatedAt: number;\n}\n\n/**\n * Input for the public consent-log ingest endpoint (POST /api/v1/consent) — the\n * tamper-evident, hash-chained ledger write the browser embed makes. Mirrors the\n * server's ConsentPayload; optional fields are sent only when set. Records are\n * anonymised (IP truncated) and hash-chained server-side.\n */\nexport interface ConsentIngestInput {\n /** The registered site id the consent belongs to. */\n cbid: string;\n /** Stable per-subject consent-receipt id (used for erase/export). */\n stamp: string;\n /** The subject's cookie-category choices. */\n choices: { preferences: boolean; statistics: boolean; marketing: boolean };\n /** How consent was captured. */\n method: 'explicit' | 'implied';\n /** Consent/notice version number. */\n ver: number;\n /** Epoch-ms the decision was made. */\n utc: number;\n /** The URL / screen where consent was captured. */\n url: string;\n /** Optional IAB TCF consent string (TCF mode only). */\n tcString?: string;\n /** Optional GPP string (GPP mode only). */\n gppString?: string;\n /** Optional named-purpose map beyond the standard categories. */\n purposes?: Record<string, boolean>;\n /** Optional opaque digest of the exact notice text shown to the subject. */\n subjectPolicyHash?: string;\n /** Optional map: named purpose → RoPA entry id. */\n purposeRopa?: Record<string, string>;\n /** Optional A/B variant id. */\n variant?: string;\n /**\n * Optional, app-supplied STABLE cross-surface subject id. Lets an org retrieve one\n * subject's consent history across ALL its sites/surfaces (web + mobile + desktop).\n * Opaque — stored and bound into the record hash server-side, never interpreted.\n */\n subjectId?: string;\n}\n\n/** One of a person's identifiers: an identity-space code plus the normalised value. */\nexport interface Identifier {\n space: string;\n value: string;\n}\n\nexport interface ConsentDecisionInput {\n purpose: string;\n allowed: boolean;\n legalBasis?: string;\n jurisdiction?: string;\n provenance?: string;\n collectedAt?: number;\n}\n\nexport interface ProfileAttributeInput {\n value: string;\n purpose?: string;\n collectedAt?: number;\n}\n\nexport interface SubscriptionTopicInput {\n code: string;\n name?: string;\n channels: string[];\n downstream?: Record<string, string>;\n}\n\n/** A warehouse-native enforcement rule. See POST /v1/discovery/enforcement. */\nexport interface EnforcementRuleInput {\n id: string;\n action: 'mask' | 'restrict';\n /** Select columns at or above this sensitivity. */\n minSensitivity?: 'personal' | 'sensitive';\n /** Select columns in any of these categories. */\n categories?: string[];\n /** mask: roles that still see the raw value. */\n allowRoles?: string[];\n /** restrict: the purpose a subject must have consented to. */\n purpose?: string;\n /** restrict: the column holding the subject id. Default SUBJECT_ID. */\n subjectColumn?: string;\n}\n\nexport interface RegulationSummary {\n id: string;\n jurisdiction: string;\n name: string;\n /** ISO date the law takes (or took) effect. */\n effective: string;\n model: 'opt-in' | 'opt-out';\n obligations: string[];\n /** Primary source — the statute or the regulator. */\n source: string;\n}\n\nexport interface RegulatoryFeedResult {\n /** When a human last checked the dataset against its sources. */\n reviewedAt: string;\n /** Days since that review. */\n ageDays: number;\n /** How long a review is considered good for. */\n reviewAfterDays: number;\n /** True once the review interval has passed — treat the answer with caution. */\n stale: boolean;\n regulations: RegulationSummary[];\n}\n\nexport interface AiInspectInput {\n prompt: string;\n purpose: string;\n model?: string;\n actor?: string;\n direction?: 'prompt' | 'response';\n /** The subject's current permits, when the call concerns an identified person. */\n consent?: Record<string, boolean>;\n}\n\nexport interface AiInspectResult {\n action: 'allow' | 'redact' | 'block';\n /** The text to forward: redacted when `redact`, empty when `block`. */\n prompt: string;\n findings: unknown[];\n reasons: string[];\n policyIds: string[];\n blocked: boolean;\n direction: 'prompt' | 'response';\n}\n\nexport type FulfillmentOp = 'locate' | 'export' | 'erase' | 'optOut';\n\n/** A child org provisioned by a reseller. */\nexport interface ResellerChild {\n id: string;\n name: string;\n status: 'active' | 'suspended';\n delegatedAccess?: boolean;\n /** Per-child DSAR routing override; unset defers to the reseller's policy. */\n dsarRouting?: 'reseller' | 'child';\n createdAt?: number;\n usage?: unknown;\n}\n\nexport interface ResellerChildList {\n children: ResellerChild[];\n /** Pooled usage across the reseller and all its children, when metering is wired. */\n aggregate?: { childOrgs: number; sites: number; monthlyEvents: number; monthlyEventsCap: number | null; overCap: boolean };\n}\n\nexport interface ResellerChildCreate {\n name: string;\n ownerEmail?: string;\n /** legalName, contactEmail, address, dpoContact. */\n controller?: Record<string, unknown>;\n /** Branding overrides, merged onto the reseller's white-label defaults. */\n whiteLabel?: Record<string, unknown>;\n delegatedAccess?: boolean;\n /** Also issue an API key for the new child; it is returned once, as `apiKey`. */\n mintKey?: boolean;\n /** Scopes for that key. Defaults to [\"sites:read\"]. */\n keyScopes?: string[];\n}\n\nexport interface ResellerChildPatch {\n status?: 'active' | 'suspended';\n delegatedAccess?: boolean;\n /** Pin this child's DSAR routing; null clears the override. */\n dsarRouting?: 'reseller' | 'child' | null;\n controller?: Record<string, unknown>;\n}\n\nexport interface ChildKeyInput {\n name?: string;\n scopes?: string[];\n /** Lock the key to these properties of the child org. */\n cbids?: string[];\n}\n\nexport interface PolicyOptions {\n /** Contact email shown in the policy. Falls back to the org controller's, then the owner's. */\n contactEmail?: string;\n /** YYYY-MM-DD. Defaults to today. */\n effectiveDate?: string;\n /** Override the jurisdictions the policy addresses. */\n jurisdictions?: string[];\n}\n\nexport interface AdPersonalizationInput {\n enabled: boolean;\n /** Whether personalised ads start on before the visitor chooses. */\n default?: boolean;\n label?: string;\n}\n\nexport interface SessionAnalysisInput {\n /** A HAR export of the captured session. */\n har?: unknown;\n /** Or the requests directly. */\n requests?: unknown[];\n /** The consent state the session ran under. */\n consent?: Record<string, boolean>;\n /** Whether Global Privacy Control was set. */\n gpc?: boolean;\n}\n\n/**\n * How to drive one system's rights API, as data. This is what makes a connection specific to\n * a platform — Cookie Munch names none of them, so connecting a new system is a profile\n * rather than a release. Paths are relative to `baseUrl`; placeholders are `{email}`,\n * `{subject}`, `{operation}`, `{correlation}` and `{id}`.\n */\nexport interface ExecutorProfile {\n /** How requests carry the credential. Defaults to `Authorization: Bearer {secret}`. */\n auth?: { header: string; format: string };\n /**\n * Optional first step: turn the subject's email into the id this system uses. `noMatch`\n * names how the system says \"no record of that person\" in its body — a sentinel id is\n * never read as a person when it is set.\n */\n subjectLookup?: {\n method: 'GET' | 'POST';\n path: string;\n body?: unknown;\n idPath: string;\n noMatch?: { statusPath: string; values: string[] };\n };\n /** Response header carrying the system's version, recorded in the task log. Default `X-Version`. */\n versionHeader?: string;\n /** How to open a request. An operation left out of `operations` is one this system does not run. */\n open: {\n method: 'POST' | 'PUT';\n path: string;\n body?: unknown;\n operations: { export?: string; erase?: string };\n idPath: string;\n statusPath?: string;\n };\n /** How to ask how it is going. Polling is the truth; a webhook only prompts a check. */\n poll: {\n method?: 'GET';\n path: string;\n statusPath: string;\n resultPath?: string;\n /** Whether `resultPath` holds the bundle itself or a URL to fetch it from. */\n resultIs?: 'inline' | 'url';\n /** Where a date the work is waiting for lives (an erasure in a grace period). */\n scheduledPath?: string;\n };\n /** That system's status words → the three answers a sub-task can have. */\n statuses: { running: string[]; done: string[]; failed: string[] };\n idempotencyHeader?: string;\n /** How that system signs its webhooks, if it sends any. */\n webhook?: {\n algorithm: 'hmac-sha256';\n /** Must include `{body}`, or the signature proves nothing about it. */\n signedPayload: string;\n headers: { signature: string; timestamp?: string; id?: string };\n encoding?: 'base64' | 'hex';\n prefix?: string;\n toleranceSeconds?: number;\n timestampUnit?: 'seconds' | 'milliseconds' | 'auto';\n };\n}\n\n/** A system connected to run part of a rights request. Credentials are never included. */\nexport interface DsarExecutor {\n id: string;\n /** The name it answers to in a fulfillment plan, e.g. `identity` or `crm`. */\n system: string;\n profile: ExecutorProfile;\n baseUrl: string;\n keyPrefix: string;\n hasWebhookSecret: boolean;\n /** Open a sub-task automatically when a request reaches fulfilment. */\n auto: boolean;\n createdAt: number;\n}\n\nexport interface DsarExecutorInput {\n /** The name this connection answers to in a plan, e.g. `identity` or `crm`. */\n system: string;\n /** The system's public https origin. */\n baseUrl: string;\n /** Its API key. Stored encrypted; never returned. */\n secretKey: string;\n /** The signing secret of its webhook endpoint. Optional — polling closes tasks without it. */\n webhookSecret?: string;\n auto?: boolean;\n /** How to drive it. */\n profile: ExecutorProfile;\n}\n\nexport interface DsarExecutorCreated extends DsarExecutor {\n /** Point the connected system's webhook endpoint here. */\n webhookUrl: string;\n}\n\n/** One entry in a fulfilment task's status history. */\nexport interface DsarTaskEvent {\n at: number;\n kind: 'created' | 'changed' | 'version_changed';\n status: 'pending' | 'running' | 'done' | 'failed';\n detail?: string;\n outcome?: DsarTaskOutcome;\n scheduledFor?: number;\n externalRef?: string;\n}\n\n/** What a task's answer means for the person. `exported` only once the export was fetched. */\nexport type DsarTaskOutcome = 'located' | 'exported' | 'erased' | 'no_record' | 'done_without_artifact' | 'not_applicable';\n\n/** One request made to a connected system on a task's behalf, credentials removed. Kept 30 days. */\nexport interface DsarTaskLogEntry {\n id?: string;\n system?: string;\n step: 'lookup' | 'open' | 'poll' | 'export';\n method: string;\n path: string;\n /** Absent when the system never answered. */\n status?: number;\n latencyMs: number;\n requestHeaders?: Record<string, string>;\n /** The first ~2 KB of what was sent. */\n request?: string;\n /** The first ~2 KB of what came back. */\n response?: string;\n error?: string;\n /** The system's version header on this answer. */\n version?: string;\n at: number;\n}\n\n/** One fulfilment task with its history and its upstream request log. */\nexport interface DsarTask {\n id: string;\n requestId: string;\n system: string;\n operation: FulfillmentOp;\n status: 'pending' | 'running' | 'done' | 'failed';\n detail?: string;\n outcome?: DsarTaskOutcome;\n attempts: number;\n executor?: string;\n externalRef?: string;\n scheduledFor?: number;\n artifacts?: Array<{ kind: string; system: string; ref: string }>;\n createdAt: number;\n updatedAt: number;\n events: DsarTaskEvent[];\n log: DsarTaskLogEntry[];\n /** Versions the system reported while working on this task, in order of change. */\n upstreamVersions?: string[];\n /** The system was redeployed while this task was in flight. */\n versionChanged: boolean;\n}\n\n/** What testing a connection found. Nothing was filed. */\nexport interface ExecutorTestResult {\n system: string;\n /** Did the lookup find the person; null when there is no lookup step or it failed. */\n matched: boolean | null;\n subject?: string;\n operationWord?: string;\n /** The open request a real request would send, when it was not sent. */\n wouldSend?: { method: string; path: string; body: unknown };\n /** One sentence: what a real request would have done. */\n verdict: string;\n ok: boolean;\n /** Every upstream answer, as the system gave it (credentials removed). */\n exchanges: DsarTaskLogEntry[];\n}\n\nexport interface ExecutorTestInput {\n email: string;\n /** Default `locate`. */\n operation?: 'locate' | 'export' | 'erase';\n /** For `export` only: open a real export and poll it once. An erasure is never sent. */\n open?: boolean;\n}\n\nexport interface DsarTaskExport {\n system: string;\n ref: string;\n /** The bundle as that system returned it. Not stored here. */\n bundle: unknown;\n}\n\n/** A language the banner already has copy for. */\nexport interface SupportedLanguage {\n /** BCP-47 tag, lowercase. Matched exactly, then by language prefix. */\n code: string;\n /** English name. */\n name: string;\n /** The language's name in itself. */\n endonym: string;\n /** Written right to left; the banner flips for these. */\n rtl: boolean;\n /** `bundled` ships in consent.js; `extended` ships in the renderer, fetched when a banner is first drawn. */\n source: 'bundled' | 'extended';\n}\n\nexport interface CookieMunchClient {\n me(): Promise<Identity>;\n /**\n * Languages the banner already speaks. Diff this against the locales your visitors\n * use to see which ones you still need to write copy for in `banner.i18n`.\n */\n languages(): Promise<SupportedLanguage[]>;\n orgs: {\n /**\n * Every organisation on this key's account, each with its effective plan and where that\n * comes from, plus the per-account org cap and the upgrade URL. Needs an unscoped key that\n * is not locked to properties.\n */\n list(): Promise<AccountOrgs>;\n /**\n * Create a sibling organisation owned by the same account — for a separate business of\n * your own. Needs an unscoped key. Not reseller provisioning, which is for your customers.\n */\n create(\n name: string,\n opts?: { mintKey?: boolean; keyScopes?: string[] },\n ): Promise<{ org: { id: string; name: string; role: string }; apiKey?: string; keyError?: string }>;\n };\n sites: {\n list(): Promise<Site[]>;\n create(input: SiteCreate): Promise<Site>;\n get(cbid: string): Promise<Site>;\n delete(cbid: string): Promise<void>;\n getConfig(cbid: string): Promise<SiteConfig>;\n /**\n * Replaces the whole config — a top-level field you omit reverts to its default. With\n * `{ keep: 'unspecified' }` (`?keep=unspecified`) an omitted top-level field keeps its\n * stored value instead; each field you send is still replaced whole, and one sent as\n * `null` reverts to its default.\n */\n putConfig(cbid: string, config: SiteConfig, opts?: PutConfigOptions): Promise<SiteConfig>;\n /**\n * Changes some of the config — a JSON Merge Patch (RFC 7386): omitted fields keep their\n * stored value, nested objects merge, and `null` removes a field (it takes its default):\n * `{ banner: { i18n: { es: null } } }` drops the Spanish override. Arrays replace whole.\n */\n patchConfig(cbid: string, config: Partial<SiteConfig>): Promise<SiteConfig>;\n /**\n * Preview a config write without saving it: the merged result and a diff. `mode: 'put'`\n * previews a replace (the diff then lists everything that would revert to its default);\n * add `keep: 'unspecified'` to preview a replace that keeps omitted fields.\n */\n dryRunConfig(cbid: string, config: Partial<SiteConfig>, opts?: { mode?: 'patch' | 'put' } & PutConfigOptions): Promise<ConfigDryRun>;\n cookies(cbid: string): Promise<CookieDeclaration>;\n scan(cbid: string): Promise<ScanStatus>;\n scanStatus(cbid: string): Promise<ScanStatus>;\n ab(cbid: string): Promise<AbResult[]>;\n /** Generate the site's privacy and cookie policy, as Markdown. */\n policy(cbid: string, opts?: PolicyOptions): Promise<string>;\n /** Turn on the personalised-ads split on whichever banner form the site uses. */\n setAdPersonalization(cbid: string, input: AdPersonalizationInput): Promise<unknown>;\n /** Analyse a captured session: trackers that fired after opt-out, personal data leaving the page. */\n analyzeSession(cbid: string, input: SessionAnalysisInput): Promise<unknown>;\n /** Which banner design this site uses, or null when none is assigned. */\n banner(cbid: string): Promise<{ bannerId: string | null }>;\n /**\n * Pages where the embed could not load its banner renderer — the host page's CSP or\n * Trusted Types policy refused it, so nobody on that page can be asked. An empty list\n * is the healthy answer.\n */\n blocked(cbid: string): Promise<{ reports: BlockedReport[] }>;\n /**\n * Read a cookie declaration exported from another CMP and translate its categories\n * into ours. Nothing is applied: their vocabulary is not ours, and a cookie in the\n * wrong category is a tag firing against a refusal, so the result is for review.\n */\n importDeclaration(cbid: string, data: string): Promise<ImportedDeclaration>;\n /**\n * Import AND declare: every row that mapped to one of our categories and has a purpose\n * is declared; the rest come back in `skipped`. Already-declared cookies are kept\n * unless `overwrite`.\n */\n applyImport(cbid: string, data: string, opts?: { overwrite?: boolean }): Promise<AppliedImport>;\n /**\n * Declare what one cookie is. Overrides scanned, observed and database classification\n * in the API, the preference modal and `data-cc=\"declaration\"`. A name ending in `*`\n * declares a prefix wildcard.\n */\n declareCookie(cbid: string, name: string, input: DeclaredCookieInput): Promise<{ cookie: DeclaredCookie; warnings?: DeclarationWarning[] }>;\n /** Remove a declaration; the scanned classification applies again. */\n undeclareCookie(cbid: string, name: string): Promise<void>;\n /** Declare many at once (all-or-nothing). `replace` also removes declarations not listed. */\n declareCookies(\n cbid: string,\n cookies: Array<DeclaredCookieInput & { name: string }>,\n opts?: { replace?: boolean },\n ): Promise<{ declared: DeclaredCookie[]; saved: number; removed: number; warnings?: DeclarationWarning[] }>;\n snippet(cbid: string, opts?: SnippetOptions): Promise<InstallSnippet>;\n /**\n * Mint a short-lived signed token to see the banner as a visitor in a given region and\n * language sees it — even from a headless browser, and without recording anything.\n */\n previewToken(cbid: string, opts?: PreviewTokenOptions): Promise<PreviewToken>;\n verify(cbid: string, method: 'dns' | 'meta' | 'file' | 'embed'): Promise<VerifyResult>;\n /** Exactly what to publish to prove control of the domain, for each verification method. */\n verifyChallenge(cbid: string): Promise<unknown>;\n /** Create up to 100 sites. Partial success: a bad or duplicate item fails only itself. */\n createBulk(sites: BulkSiteInput[]): Promise<{ results: BulkSiteResult[] }>;\n brand(cbid: string): Promise<BrandExtractionResult>;\n /** Read a site's v2 banner flow (views, categories) plus any lint issues. */\n getFlow(cbid: string): Promise<SiteFlow>;\n /** Apply an ordered batch of structured edit ops; validated + persisted if lint-clean. */\n editFlow(cbid: string, operations: FlowOp[]): Promise<FlowOpResponse>;\n /** Wholesale-replace the flow with a full v2 config; validated + persisted if valid. */\n setFlow(cbid: string, config: Record<string, unknown>): Promise<FlowOpResponse>;\n };\n consent: {\n /**\n * Verify the consent log's tamper-evident hash chain. Each record carries the hash of the\n * one before it, so an edited, reordered or removed record answers `false`. This is the\n * evidence behind the log: proof that what it says today is what it said when written.\n */\n verify(cbid: string): Promise<{ valid: boolean }>;\n stats(cbid: string, query?: RangeQuery): Promise<ConsentDay[]>;\n log(cbid: string, query?: LogQuery): Promise<ConsentLogRow[]>;\n export(cbid: string, query?: RangeQuery): Promise<string>;\n receipt(cbid: string, stamp: string): Promise<SignedReceipt>;\n /** Crypto-erase a subject's consent records by their consent-receipt stamp. Irreversible. */\n eraseSubject(cbid: string, stamp: string): Promise<{ erased: number }>;\n /** Export a data subject's consent records by their consent-receipt stamp (GDPR access/portability). */\n exportSubject(cbid: string, stamp: string): Promise<{ cbid: string; stamp: string; records: unknown[]; count: number }>;\n /** Record a consent decision via the PUBLIC ingest endpoint (POST /api/v1/consent). No `/v1`\n * prefix and no auth is required by the server (the cbid must be a registered site); for\n * server-side / non-browser consent flows. Pass `subjectId` to correlate a subject cross-surface. */\n ingest(input: ConsentIngestInput): Promise<void>;\n };\n dsar: {\n list(): Promise<DsarRequest[]>;\n /**\n * One request, in the same shape `list` returns — note that `create` answers with it\n * wrapped as `{ request }`, so `create(...).request.id` is what you pass here.\n */\n get(id: string): Promise<DsarRequest>;\n create(input: DsarCreate): Promise<{ request: DsarRequest }>;\n advance(id: string, toStatus: DsarStatus): Promise<{ request: DsarRequest }>;\n /** The subject-facing response notice for a request, as plain text. */\n response(id: string): Promise<string>;\n /**\n * Erase a subject's consent records on one site, for a deletion request past identity\n * verification. Noted on the request. Requires dsar:write and consent:write.\n */\n erase(id: string, cbid: string, stamp: string): Promise<DsarEraseResult>;\n /**\n * A subject's consent records on one site, for an access or portability request past\n * identity verification. Noted on the request. Requires dsar:write and consent:read.\n */\n export(id: string, cbid: string, stamp: string): Promise<DsarExportResult>;\n /**\n * Delete a TEST request (created with `test: true`), with its tasks and their log. A\n * real request cannot be deleted (409 `not_a_test_request`); the deletion is audited.\n */\n delete(id: string): Promise<void>;\n /**\n * Mark a CLOSED request (completed or rejected) as a test, after the fact — for trials\n * filed before `test` existed. An open one is refused (409 `request_not_closed`). Audited\n * as `dsar.marked_test`; afterwards `delete` works on it. `changed` is false on a repeat.\n */\n markTest(id: string): Promise<{ request: DsarRequest; changed: boolean }>;\n };\n vendors: {\n list(): Promise<ScoredVendor[]>;\n create(input: VendorInput): Promise<{ vendor: VendorRecord; risk: RiskScore }>;\n };\n ropa: {\n list(): Promise<RopaEntry[]>;\n create(input: RopaInput): Promise<{ entry: RopaEntry }>;\n /** The org's RoPA (GDPR Art. 30) as CSV. */\n exportCsv(): Promise<string>;\n };\n brandKits: {\n list(): Promise<BrandKit[]>;\n create(input: BrandKitCreate): Promise<{ kit: BrandKit }>;\n delete(id: string): Promise<void>;\n };\n /**\n * Identity resolution. Reads are POSTs on purpose: a person's identifiers travel in the\n * body, never a URL where they would land in access logs.\n */\n identity: {\n /** Canonical subject id for any known identifier, else null. */\n resolve(identifiers: Identifier[]): Promise<{ subjectId: string | null }>;\n /** Stitch identifiers into one subject (merging clusters when a link bridges them). */\n link(identifiers: Identifier[]): Promise<{ subjectId: string }>;\n cluster(subjectId: string): Promise<{ subjectId: string; identifiers: Identifier[] }>;\n };\n /** The Permission Vault: a resolved person's current consent state. */\n vault: {\n record(identifiers: Identifier[], decisions: ConsentDecisionInput[]): Promise<{ subjectId: string; permits: unknown[] }>;\n current(identifiers: Identifier[]): Promise<{ purposes: Record<string, boolean> }>;\n permits(identifiers: Identifier[]): Promise<{ permits: unknown[] }>;\n };\n /** The unified profile, with consent enforced at ACTIVATION time. */\n profile: {\n get(identifiers: Identifier[]): Promise<unknown>;\n setAttributes(identifiers: Identifier[], attributes: Record<string, ProfileAttributeInput>): Promise<{ subjectId: string }>;\n /** Attribute values usable for `purpose` — {} when the person hasn't consented to it. */\n activate(identifiers: Identifier[], purpose: string): Promise<{ attributes: Record<string, string> }>;\n };\n /** Marketing preferences as topics x channels. */\n subscriptions: {\n /** The org's topic catalog — what a subject can subscribe TO. */\n topics(): Promise<{ topics: SubscriptionTopicInput[] }>;\n /** Replace the catalog. It is authored whole, not patched. */\n setTopics(topics: SubscriptionTopicInput[]): Promise<{ topics: SubscriptionTopicInput[] }>;\n get(subjectId: string): Promise<unknown>;\n set(subjectId: string, topic: string, channel: string, optedIn: boolean): Promise<unknown>;\n unsubscribeAll(subjectId: string): Promise<unknown>;\n resubscribe(subjectId: string): Promise<unknown>;\n activation(subjectId: string, topics: SubscriptionTopicInput[]): Promise<{ entries: unknown[] }>;\n };\n /** DPIA / PIA / LIA / TIA / AI-impact / vendor assessments. */\n assessments: {\n templates(): Promise<{ templates: unknown[] }>;\n list(): Promise<{ assessments: unknown[] }>;\n start(template: string, subject: string): Promise<unknown>;\n /** The record plus its derived score, completeness and SME routing. */\n get(id: string): Promise<unknown>;\n answer(id: string, questionId: string, value: string | number | boolean): Promise<unknown>;\n /** Fill from the org's latest data map; never overwrites a human answer. */\n autoPopulateFromMap(id: string): Promise<{ assessment: unknown; applied: string[] }>;\n /** Fill from evidence you supply. Never overwrites a human answer; records `source` as provenance. */\n autoPopulate(id: string, evidence: Record<string, unknown>, source?: string): Promise<{ assessment: unknown; applied: string[] }>;\n submit(id: string): Promise<unknown>;\n approve(id: string, by: string): Promise<unknown>;\n reject(id: string, by: string, reason: string): Promise<unknown>;\n };\n /** The data map produced by an in-environment scan (metadata only). */\n discovery: {\n ingestMap(map: unknown): Promise<{ scannedAt: number; systems: number }>;\n getMap(): Promise<unknown>;\n ropaDrafts(): Promise<{ drafts: unknown[] }>;\n evidence(): Promise<{ evidence: Record<string, unknown> }>;\n /** What changed since the last scan, and where the RoPA disagrees with reality. */\n drift(): Promise<{ comparedTo: number | null; sinceLastScan: unknown[]; againstRecord: unknown[] }>;\n /**\n * Plan warehouse-native policy from the latest map. Returns a PLAN with its own\n * revert — nothing is applied to your warehouse by calling this.\n */\n planEnforcement(\n dialect: 'postgres' | 'mysql' | 'snowflake',\n rules: EnforcementRuleInput[],\n opts?: { permitsTable?: string; policyPrefix?: string },\n ): Promise<{ plan: { statements: unknown[]; revert: unknown[]; skipped: unknown[] } }>;\n };\n /**\n * Regulatory intelligence: the curated privacy-law dataset. Every response carries\n * `reviewedAt` — the dataset ships with the product and is only as current as its\n * last review. Not legal advice.\n */\n regulatory: {\n /** The dataset, optionally narrowed to the jurisdictions you operate in. */\n feed(jurisdictions?: string[]): Promise<RegulatoryFeedResult>;\n /** What takes effect within `days` (default 180), soonest first. */\n upcoming(days?: number): Promise<RegulatoryFeedResult>;\n };\n /** AI governance: policy, the inline gateway, inventory and lineage. */\n ai: {\n getPolicy(): Promise<{ policy: unknown; configured: boolean }>;\n setPolicy(policy: unknown): Promise<{ policy: unknown }>;\n /** Enforce consent + policy on a prompt or response. Requires the ai:inspect scope. */\n inspect(input: AiInspectInput): Promise<AiInspectResult>;\n inventory(): Promise<{ systems: unknown[]; shadow: unknown[] }>;\n lineage(): Promise<{ lineage: Record<string, string[]> }>;\n registerSystem(input: { id: string; name: string; provider?: string; purpose?: string }): Promise<unknown>;\n /** The AI systems declared with registerSystem. */\n systems(): Promise<unknown>;\n audit(limit?: number): Promise<{ entries: unknown[] }>;\n };\n /** DSR fulfillment: plan work per system, poll it from an in-VPC agent, report outcomes. */\n fulfillment: {\n sla(): Promise<unknown>;\n plan(requestId: string, systems: Array<{ system: string; operation: FulfillmentOp }>, includeHistorical?: boolean): Promise<{ requestId: string; tasks: unknown[] }>;\n status(requestId: string): Promise<unknown>;\n /** In-VPC agent: poll pending tasks. */\n pendingTasks(limit?: number): Promise<{ tasks: unknown[] }>;\n /** In-VPC agent: report an outcome. Only the outcome crosses the boundary. */\n reportTask(taskId: string, ok: boolean, error?: string): Promise<unknown>;\n /**\n * Systems you have connected that run part of a request themselves — an identity\n * platform holding your users' accounts, for instance. The in-VPC agent covers the\n * systems we cannot reach; these are the ones we can.\n */\n executors(): Promise<DsarExecutor[]>;\n /**\n * Connect one. The secret is stored encrypted and never returned; the response carries\n * the URL to point that system's webhook at.\n */\n connectExecutor(input: DsarExecutorInput): Promise<DsarExecutorCreated>;\n /**\n * Change a connection in place. Only what you send changes, and the stored credential\n * is kept unless you pass a new `secretKey` — so adding a field to a profile does not\n * cost you the connection or its id.\n */\n updateExecutor(\n id: string,\n patch: {\n profile?: unknown;\n baseUrl?: string;\n secretKey?: string;\n webhookSecret?: string | null;\n auto?: boolean;\n },\n ): Promise<DsarExecutor>;\n /** Disconnect a system; its open sub-tasks stop being driven. */\n disconnectExecutor(id: string): Promise<void>;\n /** The export bundle a connected system produced, fetched from it on demand. */\n taskExport(requestId: string, taskId: string): Promise<DsarTaskExport>;\n /**\n * One task: its status history (`events`), every request made to a connected system on\n * its behalf (`log`, redacted, 30 days) and whether that system's version changed mid-way.\n */\n task(requestId: string, taskId: string): Promise<DsarTask>;\n /**\n * Try a connection against one email without filing anything: runs the lookup, returns\n * the raw (redacted) upstream answers and what a real request would have sent.\n */\n testExecutor(id: string, input: ExecutorTestInput): Promise<ExecutorTestResult>;\n };\n preferences: {\n list(): Promise<PreferenceItem[]>;\n /** Save/update a subject's named-purpose opt-ins. Returns the persisted record. */\n save(subjectId: string, purposes: Record<string, boolean>): Promise<{ record: PreferenceRecord }>;\n /** One subject's record. A subject with none has empty `purposes`. */\n get(subjectId: string): Promise<PreferenceRecord>;\n };\n members: {\n list(): Promise<Member[]>;\n invite(email: string, role: string): Promise<{ member: { userId: string; email: string; role: string } }>;\n setRole(userId: string, role: string): Promise<{ member: { userId: string; email: string; role: string } }>;\n remove(userId: string): Promise<{ ok: true }>;\n };\n keys: {\n list(): Promise<ApiKeyPrefix[]>;\n /**\n * Issue a new API key; the secret is returned once. Pass `scopes` and/or `cbids` for a\n * least-privilege key — omit both for full access to the whole org.\n */\n issue(input?: ApiKeyIssueInput): Promise<ApiKeyIssued>;\n /** Revoke a key by its prefix. Immediate. */\n revoke(prefix: string): Promise<void>;\n /** Rotate a key: a new secret, returned once, with the same scopes, lock and expiry. The old one stops working. */\n roll(prefix: string): Promise<ApiKeyIssued>;\n /** Rename a key, or replace its scopes or property lock. Only the fields sent change. */\n update(prefix: string, patch: ApiKeyUpdate): Promise<{ ok: true }>;\n };\n usage(): Promise<Usage>;\n /** The key's organisation. Requires an unscoped key that is not property-locked. */\n org: {\n get(): Promise<Org>;\n /** Rename the org or set its logo. Deleting the org is not available through the API. */\n update(patch: OrgUpdate): Promise<Org>;\n };\n /** The org's audit log, newest first. API actions appear as `apikey:<prefix>`. */\n audit(limit?: number): Promise<{ entries: AuditEntry[] }>;\n assets: {\n /** Upload a banner image (≤ 1,000,000 bytes) and get its public URL. Requires sites:write. */\n upload(input: AssetUpload): Promise<{ url: string }>;\n /**\n * Delete a stored image. Takes the URL `upload` returned, or just its file name. Only\n * your own org's images are reachable — the folder comes from your API key.\n */\n delete(urlOrFileName: string): Promise<void>;\n };\n /** Provision and manage child orgs. Requires a key with the reseller:* scopes. */\n reseller: {\n list(): Promise<ResellerChildList>;\n /** Provision a child org. With `mintKey`, its first API key is returned once, as `apiKey`. */\n create(input: ResellerChildCreate): Promise<{ child: ResellerChild; apiKey?: string }>;\n get(id: string): Promise<unknown>;\n update(id: string, patch: ResellerChildPatch): Promise<unknown>;\n /**\n * Deprovision a child. Suspends it by default, which is reversible; `{ purge: true }`\n * hard-deletes it and its data, which is not.\n */\n deprovision(id: string, opts?: { purge?: boolean }): Promise<void>;\n listKeys(id: string): Promise<ApiKeyPrefix[]>;\n /** Mint an API key for a child. The secret is returned once. */\n mintKey(id: string, input?: ChildKeyInput): Promise<ApiKeyIssued>;\n revokeKey(id: string, prefix: string): Promise<void>;\n };\n webhooks: {\n list(): Promise<WebhookSubscription[]>;\n create(input: WebhookCreate): Promise<WebhookSubscription>;\n /** Change or pause a subscription. `{ active: false }` pauses without deleting it. */\n update(id: string, patch: WebhookUpdate): Promise<WebhookSubscription>;\n delete(id: string): Promise<void>;\n /** Rotate the signing secret. The new secret is returned once. */\n rollSecret(id: string): Promise<{ secret: string }>;\n /** Send a signed test event now and report what the endpoint answered. */\n test(id: string): Promise<WebhookTestResult>;\n /** Deliveries that failed every retry, newest first. */\n deadLetters(): Promise<{ deadLetters: WebhookDeadLetter[] }>;\n /** Deliver a dead letter again, to the subscription as it is now. */\n replayDeadLetter(id: string): Promise<unknown>;\n };\n /** One person's consent across every site in the org, by the subjectId your apps attach. */\n subjects: {\n /** Requires consent:read; not available to property-locked keys. */\n consent(subjectId: string): Promise<{ subjectId: string; records: unknown[]; count: number; sites: string[] }>;\n };\n banners: {\n list(): Promise<BannerSummary[]>;\n create(input: { name: string; json: SiteConfig }): Promise<BannerRecord>;\n get(id: string): Promise<BannerRecord>;\n update(id: string, patch: { name?: string; json?: SiteConfig }): Promise<BannerRecord>;\n delete(id: string): Promise<void>;\n assignments(id: string): Promise<{ cbids: string[] }>;\n setAssignments(id: string, cbids: string[]): Promise<{ cbids: string[] }>;\n publish(id: string): Promise<{ publishedCbids: string[] }>;\n };\n}\n\nfunction qs(query: Record<string, number | string | undefined> | undefined): string {\n if (!query) return '';\n const parts: string[] = [];\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) parts.push(`${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);\n }\n return parts.length ? `?${parts.join('&')}` : '';\n}\n\n/** Widen a typed query interface (RangeQuery/LogQuery) to the plain record qs() wants. */\nfunction toQuery(q: RangeQuery | LogQuery | undefined): Record<string, number | string | undefined> | undefined {\n return q as Record<string, number | string | undefined> | undefined;\n}\n\nexport function createCookieMunch(opts: CookieMunchOptions): CookieMunchClient {\n const base = `${opts.baseUrl.replace(/\\/+$/, '')}/v1`;\n const doFetch = opts.fetch ?? globalThis.fetch;\n if (!doFetch) throw new Error('No fetch available; pass { fetch } in CookieMunchOptions.');\n\n async function request(method: string, path: string, body?: unknown, raw = false): Promise<unknown> {\n const headers: Record<string, string> = { Authorization: `Bearer ${opts.apiKey}` };\n const init: RequestInit = { method, headers };\n if (body !== undefined) {\n headers['Content-Type'] = 'application/json';\n init.body = JSON.stringify(body);\n }\n const res = await doFetch(`${base}${path}`, init);\n if (!res.ok) {\n let message = `request failed with status ${res.status}`;\n let code: string | undefined;\n try {\n const data = (await res.json()) as { error?: string; code?: string };\n if (typeof data.error === 'string') message = data.error;\n if (typeof data.code === 'string') code = data.code;\n } catch {\n /* non-JSON error body — keep the default message */\n }\n throw new CookieMunchApiError(res.status, message, code);\n }\n if (res.status === 204) return undefined;\n if (raw) return res.text();\n const contentType = res.headers.get('content-type') ?? '';\n return contentType.includes('application/json') ? res.json() : res.text();\n }\n\n const get = (p: string) => request('GET', p);\n const enc = encodeURIComponent;\n\n return {\n me: () => get('/me') as Promise<Identity>,\n languages: () => get('/languages') as Promise<SupportedLanguage[]>,\n orgs: {\n list: () => get('/orgs') as Promise<AccountOrgs>,\n create: (name, opts) =>\n request('POST', '/orgs', { name, ...(opts ?? {}) }) as Promise<{\n org: { id: string; name: string; role: string };\n apiKey?: string;\n keyError?: string;\n }>,\n },\n sites: {\n list: () => get('/sites') as Promise<Site[]>,\n create: (input) => request('POST', '/sites', input) as Promise<Site>,\n get: (cbid) => get(`/sites/${enc(cbid)}`) as Promise<Site>,\n delete: async (cbid) => {\n await request('DELETE', `/sites/${enc(cbid)}`);\n },\n getConfig: (cbid) => get(`/sites/${enc(cbid)}/config`) as Promise<SiteConfig>,\n putConfig: (cbid, config, opts) =>\n request('PUT', `/sites/${enc(cbid)}/config${qs({ keep: opts?.keep })}`, config) as Promise<SiteConfig>,\n patchConfig: (cbid, config) => request('PATCH', `/sites/${enc(cbid)}/config`, config) as Promise<SiteConfig>,\n dryRunConfig: (cbid, config, opts) =>\n request(\n opts?.mode === 'put' ? 'PUT' : 'PATCH',\n `/sites/${enc(cbid)}/config${qs({ dryRun: 1, keep: opts?.mode === 'put' ? opts.keep : undefined })}`,\n config,\n ) as Promise<ConfigDryRun>,\n cookies: (cbid) => get(`/sites/${enc(cbid)}/cookies`) as Promise<CookieDeclaration>,\n scan: (cbid) => request('POST', `/sites/${enc(cbid)}/scan`) as Promise<ScanStatus>,\n scanStatus: (cbid) => get(`/sites/${enc(cbid)}/scan`) as Promise<ScanStatus>,\n ab: (cbid) => get(`/sites/${enc(cbid)}/ab`) as Promise<AbResult[]>,\n policy: (cbid, o) =>\n request(\n 'GET',\n `/sites/${enc(cbid)}/policy${qs({ contactEmail: o?.contactEmail, effectiveDate: o?.effectiveDate, jurisdictions: o?.jurisdictions?.join(',') })}`,\n undefined,\n true,\n ) as Promise<string>,\n setAdPersonalization: (cbid, input) => request('POST', `/sites/${enc(cbid)}/elements/ad-personalization`, input),\n analyzeSession: (cbid, input) => request('POST', `/sites/${enc(cbid)}/sentry`, input),\n banner: (cbid) => get(`/sites/${enc(cbid)}/banner`) as Promise<{ bannerId: string | null }>,\n blocked: (cbid) => get(`/sites/${enc(cbid)}/blocked`) as Promise<{ reports: BlockedReport[] }>,\n importDeclaration: (cbid, data) => request('POST', `/sites/${enc(cbid)}/import`, { data }) as Promise<ImportedDeclaration>,\n applyImport: (cbid, data, o) =>\n request('POST', `/sites/${enc(cbid)}/import${qs({ apply: '1', overwrite: o?.overwrite ? '1' : undefined })}`, { data }) as Promise<AppliedImport>,\n declareCookie: (cbid, name, input) =>\n request('PUT', `/sites/${enc(cbid)}/cookies/${enc(name)}`, input) as Promise<{ cookie: DeclaredCookie; warnings?: DeclarationWarning[] }>,\n undeclareCookie: async (cbid, name) => {\n await request('DELETE', `/sites/${enc(cbid)}/cookies/${enc(name)}`);\n },\n declareCookies: (cbid, cookies, o) =>\n request('PUT', `/sites/${enc(cbid)}/cookies`, { cookies, ...(o?.replace ? { replace: true } : {}) }) as Promise<{\n declared: DeclaredCookie[];\n saved: number;\n removed: number;\n warnings?: DeclarationWarning[];\n }>,\n snippet: (cbid, opts) =>\n get(`/sites/${enc(cbid)}/snippet${qs({ blockingmode: opts?.blockingMode, culture: opts?.culture })}`) as Promise<InstallSnippet>,\n previewToken: (cbid, o) => request('POST', `/sites/${enc(cbid)}/preview-token`, o ?? {}) as Promise<PreviewToken>,\n verify: (cbid, method) => request('POST', `/sites/${enc(cbid)}/verify`, { method }) as Promise<VerifyResult>,\n verifyChallenge: (cbid) => get(`/sites/${enc(cbid)}/verify/challenge`),\n createBulk: (sites) => request('POST', '/sites/bulk', { sites }) as Promise<{ results: BulkSiteResult[] }>,\n brand: (cbid) => request('POST', `/sites/${enc(cbid)}/brand`, {}) as Promise<BrandExtractionResult>,\n getFlow: (cbid) => get(`/sites/${enc(cbid)}/flow`) as Promise<SiteFlow>,\n editFlow: (cbid, operations) =>\n request('POST', `/sites/${enc(cbid)}/flow/ops`, { operations }) as Promise<FlowOpResponse>,\n setFlow: (cbid, config) => request('PUT', `/sites/${enc(cbid)}/flow`, config) as Promise<FlowOpResponse>,\n },\n consent: {\n verify: (cbid) => get(`/sites/${enc(cbid)}/consent/verify`) as Promise<{ valid: boolean }>,\n stats: (cbid, query) => get(`/sites/${enc(cbid)}/consent/stats${qs(toQuery(query))}`) as Promise<ConsentDay[]>,\n log: (cbid, query) => get(`/sites/${enc(cbid)}/consent/log${qs(toQuery(query))}`) as Promise<ConsentLogRow[]>,\n export: (cbid, query) => request('GET', `/sites/${enc(cbid)}/consent/export${qs(toQuery(query))}`, undefined, true) as Promise<string>,\n receipt: (cbid, stamp) => get(`/sites/${enc(cbid)}/receipt/${enc(stamp)}`) as Promise<SignedReceipt>,\n eraseSubject: (cbid, stamp) =>\n request('POST', `/sites/${enc(cbid)}/erase-consent`, { stamp }) as Promise<{ erased: number }>,\n exportSubject: (cbid, stamp) =>\n request('GET', `/sites/${enc(cbid)}/subject-export?stamp=${enc(stamp)}`) as Promise<{ cbid: string; stamp: string; records: unknown[]; count: number }>,\n ingest: async (input) => {\n // The public ingest endpoint lives under /api/v1, not the /v1 dev-API prefix,\n // so it is called directly rather than via `request` (which prepends /v1).\n const res = await doFetch(`${opts.baseUrl.replace(/\\/+$/, '')}/api/v1/consent`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n });\n if (!res.ok) {\n let message = `consent ingest failed with status ${res.status}`;\n let code: string | undefined;\n try {\n const data = (await res.json()) as { error?: string; code?: string };\n if (typeof data.error === 'string') message = data.error;\n if (typeof data.code === 'string') code = data.code;\n } catch {\n /* non-JSON error body — keep the default message */\n }\n throw new CookieMunchApiError(res.status, message, code);\n }\n },\n },\n dsar: {\n list: () => get('/dsar') as Promise<DsarRequest[]>,\n get: (id) => get(`/dsar/${enc(id)}`) as Promise<DsarRequest>,\n create: (input) => request('POST', '/dsar', input) as Promise<{ request: DsarRequest }>,\n advance: (id, toStatus) => request('POST', `/dsar/${enc(id)}/advance`, { toStatus }) as Promise<{ request: DsarRequest }>,\n response: (id) => request('GET', `/dsar/${enc(id)}/response`, undefined, true) as Promise<string>,\n erase: (id, cbid, stamp) => request('POST', `/dsar/${enc(id)}/erase`, { cbid, stamp }) as Promise<DsarEraseResult>,\n export: (id, cbid, stamp) => request('POST', `/dsar/${enc(id)}/export`, { cbid, stamp }) as Promise<DsarExportResult>,\n delete: async (id) => {\n await request('DELETE', `/dsar/${enc(id)}`);\n },\n markTest: (id) => request('POST', `/dsar/${enc(id)}/mark-test`) as Promise<{ request: DsarRequest; changed: boolean }>,\n },\n vendors: {\n list: () => get('/vendors') as Promise<ScoredVendor[]>,\n create: (input) => request('POST', '/vendors', input) as Promise<{ vendor: VendorRecord; risk: RiskScore }>,\n },\n ropa: {\n list: () => get('/ropa') as Promise<RopaEntry[]>,\n create: (input) => request('POST', '/ropa', input) as Promise<{ entry: RopaEntry }>,\n exportCsv: () => request('GET', '/ropa/export.csv', undefined, true) as Promise<string>,\n },\n brandKits: {\n list: () => get('/brand-kits') as Promise<BrandKit[]>,\n create: (input) => request('POST', '/brand-kits', input) as Promise<{ kit: BrandKit }>,\n delete: async (id) => {\n await request('DELETE', `/brand-kits/${enc(id)}`);\n },\n },\n identity: {\n resolve: (identifiers) => request('POST', '/identity/resolve', { identifiers }) as Promise<{ subjectId: string | null }>,\n link: (identifiers) => request('POST', '/identity/link', { identifiers }) as Promise<{ subjectId: string }>,\n cluster: (subjectId) => get(`/identity/${enc(subjectId)}`) as Promise<{ subjectId: string; identifiers: Identifier[] }>,\n },\n vault: {\n record: (identifiers, decisions) => request('POST', '/vault/record', { identifiers, decisions }) as Promise<{ subjectId: string; permits: unknown[] }>,\n current: (identifiers) => request('POST', '/vault/current', { identifiers }) as Promise<{ purposes: Record<string, boolean> }>,\n permits: (identifiers) => request('POST', '/vault/permits', { identifiers }) as Promise<{ permits: unknown[] }>,\n },\n profile: {\n get: (identifiers) => request('POST', '/profile/get', { identifiers }),\n setAttributes: (identifiers, attributes) => request('POST', '/profile/attributes', { identifiers, attributes }) as Promise<{ subjectId: string }>,\n activate: (identifiers, purpose) => request('POST', '/profile/activate', { identifiers, purpose }) as Promise<{ attributes: Record<string, string> }>,\n },\n subscriptions: {\n topics: () => get('/subscriptions/topics') as Promise<{ topics: SubscriptionTopicInput[] }>,\n setTopics: (topics) => request('PUT', '/subscriptions/topics', { topics }) as Promise<{ topics: SubscriptionTopicInput[] }>,\n get: (subjectId) => get(`/subscriptions/${enc(subjectId)}`),\n set: (subjectId, topic, channel, optedIn) => request('PUT', `/subscriptions/${enc(subjectId)}`, { topic, channel, optedIn }),\n unsubscribeAll: (subjectId) => request('POST', `/subscriptions/${enc(subjectId)}/unsubscribe-all`),\n resubscribe: (subjectId) => request('POST', `/subscriptions/${enc(subjectId)}/resubscribe`),\n activation: (subjectId, topics) => request('POST', `/subscriptions/${enc(subjectId)}/activation`, { topics }) as Promise<{ entries: unknown[] }>,\n },\n assessments: {\n templates: () => get('/assessments/templates') as Promise<{ templates: unknown[] }>,\n list: () => get('/assessments') as Promise<{ assessments: unknown[] }>,\n start: (template, subject) => request('POST', '/assessments', { template, subject }),\n get: (id) => get(`/assessments/${enc(id)}`),\n answer: (id, questionId, value) => request('POST', `/assessments/${enc(id)}/answer`, { questionId, value }),\n autoPopulateFromMap: (id) => request('POST', `/assessments/${enc(id)}/autopopulate-from-map`) as Promise<{ assessment: unknown; applied: string[] }>,\n autoPopulate: (id, evidence, source) =>\n request('POST', `/assessments/${enc(id)}/autopopulate`, { evidence, ...(source !== undefined ? { source } : {}) }) as Promise<{ assessment: unknown; applied: string[] }>,\n submit: (id) => request('POST', `/assessments/${enc(id)}/submit`),\n approve: (id, by) => request('POST', `/assessments/${enc(id)}/approve`, { by }),\n reject: (id, by, reason) => request('POST', `/assessments/${enc(id)}/reject`, { by, reason }),\n },\n discovery: {\n ingestMap: (map) => request('POST', '/discovery/map', { map }) as Promise<{ scannedAt: number; systems: number }>,\n getMap: () => get('/discovery/map'),\n ropaDrafts: () => get('/discovery/ropa-drafts') as Promise<{ drafts: unknown[] }>,\n evidence: () => get('/discovery/evidence') as Promise<{ evidence: Record<string, unknown> }>,\n drift: () => get('/discovery/drift') as Promise<{ comparedTo: number | null; sinceLastScan: unknown[]; againstRecord: unknown[] }>,\n planEnforcement: (dialect, rules, opts) =>\n request('POST', '/discovery/enforcement', {\n dialect,\n rules,\n ...(opts?.permitsTable !== undefined ? { permitsTable: opts.permitsTable } : {}),\n ...(opts?.policyPrefix !== undefined ? { policyPrefix: opts.policyPrefix } : {}),\n }) as Promise<{ plan: { statements: unknown[]; revert: unknown[]; skipped: unknown[] } }>,\n },\n regulatory: {\n feed: (jurisdictions) =>\n get(`/regulatory/feed${qs({ jurisdictions: jurisdictions?.join(',') })}`) as Promise<RegulatoryFeedResult>,\n upcoming: (days) => get(`/regulatory/upcoming${qs({ days })}`) as Promise<RegulatoryFeedResult>,\n },\n ai: {\n getPolicy: () => get('/ai/policy') as Promise<{ policy: unknown; configured: boolean }>,\n setPolicy: (policy) => request('PUT', '/ai/policy', { policy }) as Promise<{ policy: unknown }>,\n inspect: (input) => request('POST', '/ai/inspect', input) as Promise<AiInspectResult>,\n inventory: () => get('/ai/inventory') as Promise<{ systems: unknown[]; shadow: unknown[] }>,\n lineage: () => get('/ai/lineage') as Promise<{ lineage: Record<string, string[]> }>,\n registerSystem: (input) => request('POST', '/ai/systems', input),\n systems: () => get('/ai/systems'),\n audit: (limit) => get(`/ai/audit${qs({ limit })}`) as Promise<{ entries: unknown[] }>,\n },\n fulfillment: {\n sla: () => get('/dsar/sla'),\n plan: (requestId, systems, includeHistorical) =>\n request('POST', `/dsar/${enc(requestId)}/plan`, { systems, ...(includeHistorical ? { includeHistorical: true } : {}) }) as Promise<{ requestId: string; tasks: unknown[] }>,\n status: (requestId) => get(`/dsar/${enc(requestId)}/fulfillment`),\n pendingTasks: (limit) => get(`/dsar/agent/tasks${qs({ limit })}`) as Promise<{ tasks: unknown[] }>,\n reportTask: (taskId, ok, error) => request('POST', `/dsar/agent/tasks/${enc(taskId)}/result`, { ok, ...(error !== undefined ? { error } : {}) }),\n executors: () => get('/dsar/executors') as Promise<DsarExecutor[]>,\n connectExecutor: (input) => request('POST', '/dsar/executors', input) as Promise<DsarExecutorCreated>,\n updateExecutor: (id, patch) => request('PATCH', `/dsar/executors/${enc(id)}`, patch) as Promise<DsarExecutor>,\n disconnectExecutor: async (id) => {\n await request('DELETE', `/dsar/executors/${enc(id)}`);\n },\n taskExport: (requestId, taskId) => get(`/dsar/${enc(requestId)}/tasks/${enc(taskId)}/export`) as Promise<DsarTaskExport>,\n task: (requestId, taskId) => get(`/dsar/${enc(requestId)}/tasks/${enc(taskId)}`) as Promise<DsarTask>,\n testExecutor: (id, input) => request('POST', `/dsar/executors/${enc(id)}/test`, input) as Promise<ExecutorTestResult>,\n },\n preferences: {\n list: () => get('/preferences') as Promise<PreferenceItem[]>,\n save: (subjectId, purposes) => request('POST', '/preferences', { subjectId, purposes }) as Promise<{ record: PreferenceRecord }>,\n get: (subjectId) => get(`/preferences/${enc(subjectId)}`) as Promise<PreferenceRecord>,\n },\n members: {\n list: () => get('/members') as Promise<Member[]>,\n invite: (email, role) =>\n request('POST', '/members', { email, role }) as Promise<{ member: { userId: string; email: string; role: string } }>,\n setRole: (userId, role) =>\n request('PATCH', `/members/${enc(userId)}`, { role }) as Promise<{ member: { userId: string; email: string; role: string } }>,\n remove: (userId) => request('DELETE', `/members/${enc(userId)}`) as Promise<{ ok: true }>,\n },\n keys: {\n list: () => get('/keys') as Promise<ApiKeyPrefix[]>,\n issue: (input) => request('POST', '/keys', input ?? {}) as Promise<ApiKeyIssued>,\n revoke: async (prefix) => {\n await request('DELETE', `/keys/${enc(prefix)}`);\n },\n roll: (prefix) => request('POST', `/keys/${enc(prefix)}/roll`) as Promise<ApiKeyIssued>,\n update: (prefix, patch) => request('PATCH', `/keys/${enc(prefix)}`, patch) as Promise<{ ok: true }>,\n },\n usage: () => get('/usage') as Promise<Usage>,\n org: {\n get: () => get('/org') as Promise<Org>,\n update: (patch) => request('PATCH', '/org', patch) as Promise<Org>,\n },\n audit: (limit) => get(`/audit${qs({ limit })}`) as Promise<{ entries: AuditEntry[] }>,\n assets: {\n upload: (input) => request('POST', '/assets', input) as Promise<{ url: string }>,\n delete: async (urlOrFileName) => {\n const name = urlOrFileName.split('/').pop() ?? urlOrFileName;\n await request('DELETE', `/assets/${enc(name)}`);\n },\n },\n reseller: {\n list: () => get('/reseller/customers') as Promise<ResellerChildList>,\n create: (input) => request('POST', '/reseller/customers', input) as Promise<{ child: ResellerChild; apiKey?: string }>,\n get: (id) => get(`/reseller/customers/${enc(id)}`),\n update: (id, patch) => request('PATCH', `/reseller/customers/${enc(id)}`, patch),\n deprovision: async (id, o) => {\n await request('DELETE', `/reseller/customers/${enc(id)}${qs({ purge: o?.purge ? 'true' : undefined })}`);\n },\n listKeys: (id) => get(`/reseller/customers/${enc(id)}/keys`) as Promise<ApiKeyPrefix[]>,\n mintKey: (id, input) => request('POST', `/reseller/customers/${enc(id)}/keys`, input ?? {}) as Promise<ApiKeyIssued>,\n revokeKey: async (id, prefix) => {\n await request('DELETE', `/reseller/customers/${enc(id)}/keys/${enc(prefix)}`);\n },\n },\n webhooks: {\n list: () => get('/webhooks') as Promise<WebhookSubscription[]>,\n create: (input) => request('POST', '/webhooks', input) as Promise<WebhookSubscription>,\n update: (id, patch) => request('PATCH', `/webhooks/${enc(id)}`, patch) as Promise<WebhookSubscription>,\n delete: async (id) => {\n await request('DELETE', `/webhooks/${enc(id)}`);\n },\n rollSecret: (id) => request('POST', `/webhooks/${enc(id)}/roll`) as Promise<{ secret: string }>,\n test: (id) => request('POST', `/webhooks/${enc(id)}/test`) as Promise<WebhookTestResult>,\n deadLetters: () => get('/webhooks/dead-letters') as Promise<{ deadLetters: WebhookDeadLetter[] }>,\n replayDeadLetter: (id) => request('POST', `/webhooks/dead-letters/${enc(id)}/replay`),\n },\n subjects: {\n consent: (subjectId) =>\n get(`/subjects/${enc(subjectId)}/consent`) as Promise<{ subjectId: string; records: unknown[]; count: number; sites: string[] }>,\n },\n banners: {\n list: () => get('/banners') as Promise<BannerSummary[]>,\n create: (input) => request('POST', '/banners', input) as Promise<BannerRecord>,\n get: (id) => get(`/banners/${enc(id)}`) as Promise<BannerRecord>,\n update: (id, patch) => request('PUT', `/banners/${enc(id)}`, patch) as Promise<BannerRecord>,\n delete: async (id) => {\n await request('DELETE', `/banners/${enc(id)}`);\n },\n assignments: (id) => get(`/banners/${enc(id)}/assignments`) as Promise<{ cbids: string[] }>,\n setAssignments: (id, cbids) => request('PUT', `/banners/${enc(id)}/assignments`, { cbids }) as Promise<{ cbids: string[] }>,\n publish: (id) => request('POST', `/banners/${enc(id)}/publish`) as Promise<{ publishedCbids: string[] }>,\n },\n };\n}\n"],"mappings":"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","names":["opts"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cookiemunch/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Typed REST client for the Cookie Munch Developer API (/v1)",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -24,12 +24,15 @@
24
24
  ],
25
25
  "repository": {
26
26
  "type": "git",
27
- "url": "git+https://github.com/alphajew420/cookiemunch.git",
28
- "directory": "packages/sdk"
27
+ "url": "git+https://github.com/Cookie-Munch/sdk.git"
29
28
  },
30
29
  "publishConfig": {
31
30
  "access": "public"
32
31
  },
32
+ "homepage": "https://cookiemunch.net/docs",
33
+ "bugs": {
34
+ "url": "https://github.com/Cookie-Munch/sdk/issues"
35
+ },
33
36
  "scripts": {
34
37
  "build": "tsup",
35
38
  "test": "vitest run"