dpdpstack-js-sdk 0.1.0
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/README.md +142 -0
- package/dist/dpdpstack.global.js +2 -0
- package/dist/dpdpstack.global.js.map +1 -0
- package/dist/index.cjs +325 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +539 -0
- package/dist/index.d.ts +539 -0
- package/dist/index.js +319 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# dpdpstack-js-sdk
|
|
2
|
+
|
|
3
|
+
Official JS/TS client for the [DPDPStack](https://getdpdp.net) API — DPDP Act
|
|
4
|
+
consent, erasure, audit, DSR/breach workflows, and verifiable Certificates of
|
|
5
|
+
Erasure. Zero runtime dependencies; works in Node 18+ and the browser.
|
|
6
|
+
|
|
7
|
+
> This is the SDK for the **hosted platform** API. The open-source erasure
|
|
8
|
+
> engine is the Python package [`dpdpstack`](https://pypi.org/project/dpdpstack/).
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install dpdpstack-js-sdk
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Or drop it on a page via CDN (exposes `window.dpdpstack`):
|
|
17
|
+
|
|
18
|
+
```html
|
|
19
|
+
<script src="https://cdn.jsdelivr.net/npm/dpdpstack-js-sdk/dist/dpdpstack.global.js"></script>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## API keys
|
|
23
|
+
|
|
24
|
+
| Key | Prefix | Use | Capabilities |
|
|
25
|
+
|---|---|---|---|
|
|
26
|
+
| **Secret** | `dpdp_sk_…` | Server-side only | Full access |
|
|
27
|
+
| **Publishable** | `dpdp_pk_…` | Safe in the browser | Read purposes + record consent only |
|
|
28
|
+
|
|
29
|
+
A publishable key is the **only** kind that should ever reach a browser. Set an
|
|
30
|
+
origin allowlist on it in the dashboard. Some endpoints (certificate verify /
|
|
31
|
+
registry / public-key) are fully public and need no key.
|
|
32
|
+
|
|
33
|
+
## Quick start (server)
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { DPDPStack, DPDPError } from "dpdpstack-js-sdk";
|
|
37
|
+
|
|
38
|
+
const dpdp = new DPDPStack({ apiKey: process.env.DPDP_SECRET_KEY }); // dpdp_sk_…
|
|
39
|
+
|
|
40
|
+
await dpdp.grantConsent({ principal_ref: "user_42", purpose: "marketing" });
|
|
41
|
+
|
|
42
|
+
const cert = await dpdp.certificates.issue({ principal_ref: "user_42", purpose: "marketing" });
|
|
43
|
+
const { valid } = await dpdp.certificates.verify(cert.certificate_jwt);
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
await dpdp.requestErasure({ principal_ref: "user_42" });
|
|
47
|
+
} catch (err) {
|
|
48
|
+
if (err instanceof DPDPError) console.error(err.status, err.detail);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
> `principal_ref` is **your** opaque user id (an internal id or hash) — never an
|
|
53
|
+
> email, name, or other PII.
|
|
54
|
+
|
|
55
|
+
## Consent widget (browser)
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { DPDPStack, mountConsentWidget } from "dpdpstack-js-sdk";
|
|
59
|
+
|
|
60
|
+
const dpdp = new DPDPStack({ apiBase: "/api/v1", apiKey: "dpdp_pk_…" });
|
|
61
|
+
|
|
62
|
+
const widget = mountConsentWidget("#consent", {
|
|
63
|
+
client: dpdp,
|
|
64
|
+
principalRef: "user_123",
|
|
65
|
+
locale: "en", // notices are shown per-locale, English fallback
|
|
66
|
+
onSave: (receipts) => console.log(receipts),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
widget.setLocale("hi"); // switch language
|
|
70
|
+
widget.destroy(); // remove from the DOM
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Purposes are fetched via the API when omitted; pass `purposes: [...]` to render
|
|
74
|
+
inline. See [`examples/browser.html`](./examples/browser.html).
|
|
75
|
+
|
|
76
|
+
## Configuration
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
new DPDPStack({
|
|
80
|
+
apiKey: "dpdp_sk_… | dpdp_pk_…", // omit for public-only calls
|
|
81
|
+
apiBase: "https://getdpdp.net/api/v1", // default; use "/api/v1" for a same-origin proxy
|
|
82
|
+
fetch: customFetch, // optional (Node < 18, tests)
|
|
83
|
+
headers: { "X-Trace": "…" }, // sent with every request
|
|
84
|
+
credentials: "include", // optional fetch credentials mode
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Every non-2xx response throws a `DPDPError` with `.status`, `.detail`, and `.body`.
|
|
89
|
+
|
|
90
|
+
## Methods
|
|
91
|
+
|
|
92
|
+
The SDK mirrors the HTTP API; field names match the wire format exactly.
|
|
93
|
+
|
|
94
|
+
| Area | Methods |
|
|
95
|
+
|---|---|
|
|
96
|
+
| **Consent** | `listPurposes()` · `createPurpose()` · `grantConsent()` · `withdrawConsent()` · `consentStatus(ref)` · `listConsentRecords()` · `recordActivity()` |
|
|
97
|
+
| **Erasure** | `requestErasure()` · `confirmErasure(token)` |
|
|
98
|
+
| **Audit** | `getAuditLog({ principal_ref? })` |
|
|
99
|
+
| **Retention** | `retention.list()` · `retention.upsert()` · `retention.run({ dry_run? })` |
|
|
100
|
+
| **Certificates** | `certificates.issue()` · `certificates.verify(jwt)` · `certificates.publicKey()` · `certificates.registry(fp)` · `certificates.issueFromEvidence()` |
|
|
101
|
+
| **Evidence** | `evidence.ingest()` · `evidence.list({ source?, subject? })` |
|
|
102
|
+
| **DSR** | `dsr.list()` · `dsr.create()` · `dsr.get(id)` · `dsr.act(id, { action })` |
|
|
103
|
+
| **Breaches** | `breaches.list()` · `breaches.report()` · `breaches.get(id)` · `breaches.act(id, { action })` · `breaches.notifications(id)` |
|
|
104
|
+
| **Targets** | `targets.list()` · `targets.create()` · `targets.get(id)` · `targets.update(id)` · `targets.remove(id)` |
|
|
105
|
+
| **Erasure tasks** | `erasureTasks.list()` · `erasureTasks.retry(id)` |
|
|
106
|
+
|
|
107
|
+
Public (no key): `certificates.verify`, `certificates.registry`,
|
|
108
|
+
`certificates.publicKey`, `confirmErasure`.
|
|
109
|
+
|
|
110
|
+
## Build from source
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npm install
|
|
114
|
+
npm run build # → dist/ (ESM, CJS, IIFE, .d.ts)
|
|
115
|
+
npm run typecheck
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Releasing (CI/CD)
|
|
119
|
+
|
|
120
|
+
Publishing is automated by GitHub Actions ([.github/workflows/publish.yml](.github/workflows/publish.yml)).
|
|
121
|
+
|
|
122
|
+
**One-time setup:** add an npm **automation** token as a repo secret named `NPM_TOKEN`
|
|
123
|
+
(npm → Access Tokens → Generate → *Automation* → copy → GitHub repo → Settings →
|
|
124
|
+
Secrets and variables → Actions → New repository secret).
|
|
125
|
+
|
|
126
|
+
**To cut a release:**
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
npm version patch # bump package.json + create tag vX.Y.Z (minor/major as needed)
|
|
130
|
+
git push --follow-tags # pushing the tag triggers the publish workflow
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The workflow runs typecheck + build, checks the tag matches `package.json`, then
|
|
134
|
+
publishes to npm with provenance. The CDN (jsDelivr / unpkg) updates automatically.
|
|
135
|
+
`.github/workflows/ci.yml` runs typecheck + build on every push/PR.
|
|
136
|
+
|
|
137
|
+
> Provenance needs a **public** repo. On a private repo, remove `--provenance`
|
|
138
|
+
> from the publish step.
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
MIT
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var dpdpstack=(()=>{var E=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var S=Object.prototype.hasOwnProperty;var I=(s,e)=>{for(var t in e)E(s,t,{get:e[t],enumerable:!0})},q=(s,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of x(e))!S.call(s,r)&&r!==t&&E(s,r,{get:()=>e[r],enumerable:!(n=R(e,r))||n.enumerable});return s};var L=s=>q(E({},"__esModule",{value:!0}),s);var B={};I(B,{DEFAULT_API_BASE:()=>v,DPDPConsent:()=>A,DPDPError:()=>m,DPDPStack:()=>g,mountConsentWidget:()=>b});var v="https://getdpdp.net/api/v1",m=class extends Error{constructor(e,t,n){super(`DPDP API error ${e}: ${t}`),this.name="DPDPError",this.status=e,this.detail=t,this.body=n}};function w(s){if(!s)return"";let e=Object.entries(s).filter(([,t])=>t!=null&&t!=="").map(([t,n])=>`${encodeURIComponent(t)}=${encodeURIComponent(String(n))}`);return e.length?`?${e.join("&")}`:""}var g=class{constructor(e={}){this.retention={list:()=>this.request("GET","/retention/policies"),upsert:e=>this.request("POST","/retention/policies",{body:e}),run:(e={})=>this.request("POST","/retention/run",{body:e})};this.certificates={issue:e=>this.request("POST","/certificate",{body:e}),verify:e=>this.request("POST","/certificate/verify",{body:{certificate_jwt:e}}),publicKey:()=>this.request("GET","/certificate/public-key"),registry:e=>this.request("GET",`/certificate/registry/${encodeURIComponent(e)}`),issueFromEvidence:e=>this.request("POST","/evidence/certificate",{body:e})};this.evidence={ingest:e=>this.request("POST","/evidence",{body:e}),list:(e={})=>this.request("GET","/evidence",{query:e})};this.dsr={list:(e={})=>this.request("GET","/dsr",{query:e}),create:e=>this.request("POST","/dsr",{body:e}),get:e=>this.request("GET",`/dsr/${e}`),act:(e,t)=>this.request("POST",`/dsr/${e}`,{body:t})};this.breaches={list:(e={})=>this.request("GET","/breaches",{query:e}),report:e=>this.request("POST","/breaches",{body:e}),get:e=>this.request("GET",`/breaches/${e}`),act:(e,t)=>this.request("POST",`/breaches/${e}`,{body:t}),notifications:e=>this.request("GET",`/breaches/${e}/notification`)};this.targets={list:()=>this.request("GET","/targets"),create:e=>this.request("POST","/targets",{body:e}),get:e=>this.request("GET",`/targets/${e}`),update:(e,t)=>this.request("POST",`/targets/${e}`,{body:t}),remove:e=>this.request("DELETE",`/targets/${e}`)};this.erasureTasks={list:(e={})=>this.request("GET","/erasure/tasks",{query:e}),retry:e=>this.request("POST",`/erasure/tasks/${e}/retry`)};this.apiBase=(e.apiBase??v).replace(/\/+$/,""),this.apiKey=e.apiKey,this.defaultHeaders=e.headers??{},this.credentials=e.credentials;let t=e.fetch??globalThis.fetch;if(typeof t!="function")throw new Error("No fetch implementation found. Use Node 18+, a browser, or pass `fetch` in options.");this.fetchImpl=t.bind(globalThis)}async request(e,t,n={}){let r=`${this.apiBase}${t}${w(n.query)}`,i={...this.defaultHeaders};this.apiKey&&(i["X-API-Key"]=this.apiKey);let f=n.body!==void 0;f&&(i["Content-Type"]="application/json");let u=await this.fetchImpl(r,{method:e,headers:i,body:f?JSON.stringify(n.body):void 0,...this.credentials?{credentials:this.credentials}:{}}),p=await u.text(),o;if(p)try{o=JSON.parse(p)}catch{o=p}if(!u.ok){let P=(o&&typeof o=="object"&&"detail"in o?String(o.detail):void 0)??u.statusText??"Request failed";throw new m(u.status,P,o)}return o}listPurposes(){return this.request("GET","/purposes")}createPurpose(e){return this.request("POST","/purposes",{body:e})}grantConsent(e){return this.request("POST","/consent",{body:e})}withdrawConsent(e){return this.request("POST","/consent/withdraw",{body:e})}consentStatus(e){return this.request("GET","/consent/status",{query:{principal_ref:e}})}listConsentRecords(){return this.request("GET","/consent/records")}recordActivity(e){return this.request("POST","/activity",{body:e})}requestErasure(e){return this.request("POST","/erasure",{body:e})}confirmErasure(e){return this.request("POST","/erasure/confirm",{body:{token:e}})}getAuditLog(e={}){return this.request("GET","/audit",{query:e})}};var k={heading:"We value your privacy",subheading:"Choose what you consent to. You can withdraw anytime (DPDP Act).",save:"Save consent preferences",saving:"Saving\u2026",saved:"Preferences saved.",loading:"Loading\u2026",error:"Could not save your preferences. Please try again."};function c(s,e={},t=[]){let n=document.createElement(s);for(let[r,i]of Object.entries(e))r==="style"&&typeof i=="string"?n.style.cssText=i:r==="class"&&typeof i=="string"?n.className=i:r.startsWith("on")&&typeof i=="function"?n.addEventListener(r.slice(2),i):typeof i=="string"&&n.setAttribute(r,i);for(let r of t)n.appendChild(typeof r=="string"?document.createTextNode(r):r);return n}function D(s,e){let t=s.translations?.[e]??{};return{name:t.name||s.name,description:t.description||s.description}}var O="border:1px solid #e3e3ef;border-radius:12px;padding:20px;background:#fff;max-width:480px;font-family:system-ui,-apple-system,Segoe UI,sans-serif;color:#11113a;box-shadow:0 1px 3px rgba(15,23,42,0.06);";function b(s,e){let t=typeof s=="string"?document.querySelector(s):s;if(!t)throw new Error(`mountConsentWidget: container not found: ${String(s)}`);let n=e.client??new g({apiBase:e.apiBase,apiKey:e.apiKey}),r={...k,...e.texts},i=new Set(e.defaultChecked??[]),f=e.locale??"en",u=e.purposes??null;function p(a,C){return c("div",{style:`margin-top:12px;font-size:13px;color:${C};`},[a])}function o(){t.innerHTML="";let a=c("div",{style:O},[c("div",{style:"font-weight:600;font-size:15px;margin-bottom:4px;"},[r.heading]),c("div",{style:"font-size:13px;color:#666;margin-bottom:12px;"},[r.subheading])]);if(!u){a.appendChild(p(r.loading,"#666")),t.appendChild(a);return}let C={};for(let l of u){let d=D(l,f),h=c("input",{type:"checkbox",id:`dpdp_${l.code}`});i.has(l.code)&&(h.checked=!0),C[l.code]=h,a.appendChild(c("label",{style:"display:flex;align-items:flex-start;gap:8px;margin:10px 0;font-size:14px;cursor:pointer;",for:`dpdp_${l.code}`},[h,c("span",{},[c("strong",{},[d.name]),c("div",{style:"font-size:12px;color:#777;margin-top:2px;"},[d.description])])]))}let T=c("div",{}),y=c("button",{type:"button",style:"margin-top:14px;background:#4f46e5;color:#fff;border:0;border-radius:8px;padding:10px 16px;font:inherit;font-weight:600;cursor:pointer;"},[r.save]);y.addEventListener("click",async()=>{let l=u.filter(d=>C[d.code]?.checked);y.disabled=!0,y.textContent=r.saving,T.innerHTML="";try{let d=await Promise.all(l.map(h=>n.grantConsent({principal_ref:e.principalRef,purpose:h.code,locale:f})));T.appendChild(p(r.saved,"#16a34a")),e.onSave?.(d)}catch(d){T.appendChild(p(r.error,"#dc2626")),e.onError?.(d)}finally{y.disabled=!1,y.textContent=r.save}}),a.appendChild(y),a.appendChild(T),t.appendChild(a)}async function P(){if(!e.purposes){o();try{u=await n.listPurposes()}catch(a){u=[],e.onError?.(a)}}o()}return P(),{setLocale(a){f=a,o()},refresh:P,destroy(){t.innerHTML=""}}}var A={init(s){let{el:e,...t}=s;return b(e,t)}};return L(B);})();
|
|
2
|
+
//# sourceMappingURL=dpdpstack.global.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/widget.ts"],"sourcesContent":["export { DPDPStack, DPDPError, DEFAULT_API_BASE } from \"./client.js\";\nexport type { DPDPStackOptions } from \"./client.js\";\n\nexport { mountConsentWidget } from \"./widget.js\";\nexport type {\n ConsentWidgetOptions,\n ConsentWidgetController,\n ConsentWidgetTexts,\n} from \"./widget.js\";\n\nexport type * from \"./types.js\";\n\nimport { mountConsentWidget, type ConsentWidgetController } from \"./widget.js\";\nimport type { ConsentReceipt, Purpose } from \"./types.js\";\n\n/**\n * Backward-compatible shim for the original `DPDPConsent.init({ el, … })`\n * script-tag widget. Prefer {@link mountConsentWidget} in new code.\n */\nexport const DPDPConsent = {\n init(config: {\n el: string | HTMLElement;\n apiBase?: string;\n apiKey?: string;\n principalRef: string;\n locale?: string;\n purposes?: Purpose[];\n onSave?: (receipts: ConsentReceipt[]) => void;\n }): ConsentWidgetController {\n const { el, ...rest } = config;\n return mountConsentWidget(el, rest);\n },\n};\n","import type {\n ActivityInput,\n ActivityResult,\n AuditLog,\n Breach,\n BreachActionInput,\n BreachListQuery,\n BreachNotifications,\n BreachReportInput,\n CertificatePublicKey,\n CertificateRegistryResult,\n CertificateVerifyResult,\n ConsentGrantInput,\n ConsentReceipt,\n ConsentRecordSummary,\n ConsentStatus,\n ConsentWithdrawInput,\n ConsentWithdrawResult,\n DSR,\n DSRActionInput,\n DSRCreateInput,\n DSRListQuery,\n ErasureConfirmResult,\n ErasureInput,\n ErasureResult,\n ErasureTask,\n ErasureTaskListQuery,\n EvidenceIngestInput,\n EvidenceIngestResult,\n EvidenceListQuery,\n EvidenceListResult,\n IssueCertificateInput,\n IssueEvidenceCertificateInput,\n IssuedCertificate,\n Purpose,\n PurposeInput,\n RetentionPolicy,\n RetentionPolicyInput,\n RetentionRunResult,\n Target,\n TargetCreateInput,\n TargetUpdateInput,\n TargetWithSecret,\n} from \"./types.js\";\n\nexport const DEFAULT_API_BASE = \"https://getdpdp.net/api/v1\";\n\nexport interface DPDPStackOptions {\n /**\n * API key. A **secret** key (`dpdp_sk_…`) for server-side use, or a\n * **publishable** key (`dpdp_pk_…`) — the only kind safe to ship to a browser,\n * limited to reading purposes and recording consent. Omit for public-only\n * calls (certificate verify/registry/public-key).\n */\n apiKey?: string;\n /** API base URL. Default `https://getdpdp.net/api/v1`. Use a relative path (e.g. `/api/v1`) to call a same-origin proxy. */\n apiBase?: string;\n /** Custom fetch implementation (for Node < 18, testing, or proxies). Defaults to the global `fetch`. */\n fetch?: typeof fetch;\n /** Extra headers sent with every request. */\n headers?: Record<string, string>;\n /** `fetch` credentials mode (e.g. `\"include\"` to send cookies). Default: unset. */\n credentials?: RequestCredentials;\n}\n\n/** Thrown for any non-2xx API response. */\nexport class DPDPError extends Error {\n readonly status: number;\n readonly detail: string;\n readonly body: unknown;\n\n constructor(status: number, detail: string, body: unknown) {\n super(`DPDP API error ${status}: ${detail}`);\n this.name = \"DPDPError\";\n this.status = status;\n this.detail = detail;\n this.body = body;\n }\n}\n\nfunction buildQuery(query?: object): string {\n if (!query) return \"\";\n const parts = Object.entries(query as Record<string, unknown>)\n .filter(([, v]) => v !== undefined && v !== null && v !== \"\")\n .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);\n return parts.length ? `?${parts.join(\"&\")}` : \"\";\n}\n\n/**\n * Thin, typed client for the DPDPStack API.\n *\n * ```ts\n * const dpdp = new DPDPStack({ apiKey: \"dpdp_sk_…\" });\n * await dpdp.grantConsent({ principal_ref: \"user_42\", purpose: \"marketing\" });\n * ```\n */\nexport class DPDPStack {\n private readonly apiBase: string;\n private readonly apiKey?: string;\n private readonly fetchImpl: typeof fetch;\n private readonly defaultHeaders: Record<string, string>;\n private readonly credentials?: RequestCredentials;\n\n constructor(options: DPDPStackOptions = {}) {\n this.apiBase = (options.apiBase ?? DEFAULT_API_BASE).replace(/\\/+$/, \"\");\n this.apiKey = options.apiKey;\n this.defaultHeaders = options.headers ?? {};\n this.credentials = options.credentials;\n\n const f = options.fetch ?? globalThis.fetch;\n if (typeof f !== \"function\") {\n throw new Error(\n \"No fetch implementation found. Use Node 18+, a browser, or pass `fetch` in options.\",\n );\n }\n this.fetchImpl = f.bind(globalThis);\n }\n\n /** Low-level request. Most callers use the typed methods below. */\n async request<T>(\n method: string,\n path: string,\n opts: { query?: object; body?: unknown } = {},\n ): Promise<T> {\n const url = `${this.apiBase}${path}${buildQuery(opts.query)}`;\n const headers: Record<string, string> = { ...this.defaultHeaders };\n if (this.apiKey) headers[\"X-API-Key\"] = this.apiKey;\n\n const hasBody = opts.body !== undefined;\n if (hasBody) headers[\"Content-Type\"] = \"application/json\";\n\n const res = await this.fetchImpl(url, {\n method,\n headers,\n body: hasBody ? JSON.stringify(opts.body) : undefined,\n ...(this.credentials ? { credentials: this.credentials } : {}),\n });\n\n const text = await res.text();\n let data: unknown = undefined;\n if (text) {\n try {\n data = JSON.parse(text);\n } catch {\n data = text;\n }\n }\n\n if (!res.ok) {\n const detail =\n (data && typeof data === \"object\" && \"detail\" in data\n ? String((data as { detail: unknown }).detail)\n : undefined) ?? res.statusText ?? \"Request failed\";\n throw new DPDPError(res.status, detail, data);\n }\n\n return data as T;\n }\n\n // --- Purposes & consent -------------------------------------------------\n\n /** List consent purposes (with multilingual notices). Publishable-key safe. */\n listPurposes(): Promise<Purpose[]> {\n return this.request(\"GET\", \"/purposes\");\n }\n\n /** Create a consent purpose. Requires a secret key. */\n createPurpose(input: PurposeInput): Promise<Purpose> {\n return this.request(\"POST\", \"/purposes\", { body: input });\n }\n\n /** Record purpose-level consent and get an immutable receipt. Publishable-key safe. */\n grantConsent(input: ConsentGrantInput): Promise<ConsentReceipt> {\n return this.request(\"POST\", \"/consent\", { body: input });\n }\n\n /** Withdraw consent for a purpose (triggers erasure/deferral). Requires a secret key. */\n withdrawConsent(input: ConsentWithdrawInput): Promise<ConsentWithdrawResult> {\n return this.request(\"POST\", \"/consent/withdraw\", { body: input });\n }\n\n /** Current consent state for a principal. Requires a secret key. */\n consentStatus(principalRef: string): Promise<ConsentStatus> {\n return this.request(\"GET\", \"/consent/status\", { query: { principal_ref: principalRef } });\n }\n\n /** List the organization's consent records (latest first). Requires a secret key. */\n listConsentRecords(): Promise<ConsentRecordSummary[]> {\n return this.request(\"GET\", \"/consent/records\");\n }\n\n /** Record principal activity, resetting inactivity-based retention. Requires a secret key. */\n recordActivity(input: ActivityInput): Promise<ActivityResult> {\n return this.request(\"POST\", \"/activity\", { body: input });\n }\n\n // --- Erasure ------------------------------------------------------------\n\n /** Right to erasure: resolve erasure across the principal's purposes. Requires a secret key. */\n requestErasure(input: ErasureInput): Promise<ErasureResult> {\n return this.request(\"POST\", \"/erasure\", { body: input });\n }\n\n /** Confirm a downstream erasure with the token delivered to that system. No API key needed. */\n confirmErasure(token: string): Promise<ErasureConfirmResult> {\n return this.request(\"POST\", \"/erasure/confirm\", { body: { token } });\n }\n\n // --- Audit --------------------------------------------------------------\n\n /** Hash-chained audit trail (optionally filtered by principal), plus chain status. Requires a secret key. */\n getAuditLog(query: { principal_ref?: string } = {}): Promise<AuditLog> {\n return this.request(\"GET\", \"/audit\", { query });\n }\n\n // --- Retention ----------------------------------------------------------\n\n readonly retention = {\n /** List retention policies. Requires a secret key. */\n list: (): Promise<RetentionPolicy[]> => this.request(\"GET\", \"/retention/policies\"),\n /** Create or update a retention policy. Requires a secret key. */\n upsert: (input: RetentionPolicyInput): Promise<RetentionPolicy> =>\n this.request(\"POST\", \"/retention/policies\", { body: input }),\n /** Run the retention sweep now (`{ dry_run: true }` to preview). Requires a secret key. */\n run: (input: { dry_run?: boolean } = {}): Promise<RetentionRunResult> =>\n this.request(\"POST\", \"/retention/run\", { body: input }),\n };\n\n // --- Certificates -------------------------------------------------------\n\n readonly certificates = {\n /** Issue a counter-signed Certificate of Erasure for a principal. Requires a secret key. */\n issue: (input: IssueCertificateInput): Promise<IssuedCertificate> =>\n this.request(\"POST\", \"/certificate\", { body: input }),\n /** Verify a Certificate of Erasure (JWT) against the public key. Public — no key needed. */\n verify: (certificateJwt: string): Promise<CertificateVerifyResult> =>\n this.request(\"POST\", \"/certificate/verify\", { body: { certificate_jwt: certificateJwt } }),\n /** Fetch the issuer public key. Public — no key needed. */\n publicKey: (): Promise<CertificatePublicKey> =>\n this.request(\"GET\", \"/certificate/public-key\"),\n /** Look up a certificate in the public registry by fingerprint. Public — no key needed. */\n registry: (fingerprint: string): Promise<CertificateRegistryResult> =>\n this.request(\"GET\", `/certificate/registry/${encodeURIComponent(fingerprint)}`),\n /** Issue a certificate from SDK-pushed evidence. Requires a secret key. */\n issueFromEvidence: (input: IssueEvidenceCertificateInput): Promise<IssuedCertificate> =>\n this.request(\"POST\", \"/evidence/certificate\", { body: input }),\n };\n\n // --- Evidence -----------------------------------------------------------\n\n readonly evidence = {\n /** Push tamper-evident audit evidence (hash chain) for server-timestamping. Requires a secret key. */\n ingest: (input: EvidenceIngestInput): Promise<EvidenceIngestResult> =>\n this.request(\"POST\", \"/evidence\", { body: input }),\n /** List stored evidence for a source/subject, with chain status. Requires a secret key. */\n list: (query: EvidenceListQuery = {}): Promise<EvidenceListResult> =>\n this.request(\"GET\", \"/evidence\", { query }),\n };\n\n // --- Data-subject requests (DSR) ----------------------------------------\n\n readonly dsr = {\n /** List rights requests (filter by status/type/principal/overdue). Requires a secret key. */\n list: (query: DSRListQuery = {}): Promise<DSR[]> => this.request(\"GET\", \"/dsr\", { query }),\n /** Create a rights request. Requires a secret key. */\n create: (input: DSRCreateInput): Promise<DSR> => this.request(\"POST\", \"/dsr\", { body: input }),\n /** Get a single rights request. Requires a secret key. */\n get: (id: number): Promise<DSR> => this.request(\"GET\", `/dsr/${id}`),\n /** Advance a rights request (acknowledge/start/complete/reject/extend). Requires a secret key. */\n act: (id: number, input: DSRActionInput): Promise<DSR> =>\n this.request(\"POST\", `/dsr/${id}`, { body: input }),\n };\n\n // --- Breaches -----------------------------------------------------------\n\n readonly breaches = {\n /** List breach incidents. Requires a secret key. */\n list: (query: BreachListQuery = {}): Promise<Breach[]> =>\n this.request(\"GET\", \"/breaches\", { query }),\n /** Report a breach incident (metadata only — never PII). Requires a secret key. */\n report: (input: BreachReportInput): Promise<Breach> =>\n this.request(\"POST\", \"/breaches\", { body: input }),\n /** Get a single breach. Requires a secret key. */\n get: (id: number): Promise<Breach> => this.request(\"GET\", `/breaches/${id}`),\n /** Advance a breach (investigate/contain/notify_board/notify_principals/close). Requires a secret key. */\n act: (id: number, input: BreachActionInput): Promise<Breach> =>\n this.request(\"POST\", `/breaches/${id}`, { body: input }),\n /** Generate draft Board + principal breach notices. Requires a secret key. */\n notifications: (id: number): Promise<BreachNotifications> =>\n this.request(\"GET\", `/breaches/${id}/notification`),\n };\n\n // --- Targets & fan-out tasks --------------------------------------------\n\n readonly targets = {\n /** List downstream erasure targets. Requires a secret key. */\n list: (): Promise<Target[]> => this.request(\"GET\", \"/targets\"),\n /** Register a target. The signing `secret` is returned only once. Requires a secret key. */\n create: (input: TargetCreateInput): Promise<TargetWithSecret> =>\n this.request(\"POST\", \"/targets\", { body: input }),\n /** Get a single target. Requires a secret key. */\n get: (id: number): Promise<Target> => this.request(\"GET\", `/targets/${id}`),\n /** Update a target. Requires a secret key. */\n update: (id: number, input: TargetUpdateInput): Promise<Target> =>\n this.request(\"POST\", `/targets/${id}`, { body: input }),\n /** Delete a target. Requires a secret key. */\n remove: (id: number): Promise<void> => this.request(\"DELETE\", `/targets/${id}`),\n };\n\n readonly erasureTasks = {\n /** List per-system erasure fan-out tasks (the propagation evidence). Requires a secret key. */\n list: (query: ErasureTaskListQuery = {}): Promise<ErasureTask[]> =>\n this.request(\"GET\", \"/erasure/tasks\", { query }),\n /** Re-deliver an erasure instruction to a target. Requires a secret key. */\n retry: (id: number): Promise<ErasureTask> =>\n this.request(\"POST\", `/erasure/tasks/${id}/retry`),\n };\n}\n","import { DPDPStack } from \"./client.js\";\nimport type { ConsentReceipt, LocalizedNotice, Purpose } from \"./types.js\";\n\nexport interface ConsentWidgetTexts {\n heading: string;\n subheading: string;\n save: string;\n saving: string;\n saved: string;\n loading: string;\n error: string;\n}\n\nconst DEFAULT_TEXTS: ConsentWidgetTexts = {\n heading: \"We value your privacy\",\n subheading: \"Choose what you consent to. You can withdraw anytime (DPDP Act).\",\n save: \"Save consent preferences\",\n saving: \"Saving…\",\n saved: \"Preferences saved.\",\n loading: \"Loading…\",\n error: \"Could not save your preferences. Please try again.\",\n};\n\nexport interface ConsentWidgetOptions {\n /** Your opaque user id (internal id or hash) — never PII. */\n principalRef: string;\n /** Pre-built client. If omitted, one is built from `apiBase`/`apiKey`. */\n client?: DPDPStack;\n /** Used to build a client when `client` is not supplied. */\n apiBase?: string;\n /** Publishable key (`dpdp_pk_…`). Used to build a client when `client` is not supplied. */\n apiKey?: string;\n /** Purposes to render. If omitted, they're fetched via `client.listPurposes()`. */\n purposes?: Purpose[];\n /** Initial locale for notice text. Default `\"en\"`. */\n locale?: string;\n /** Purpose codes checked on first render. */\n defaultChecked?: string[];\n /** Override any UI strings. */\n texts?: Partial<ConsentWidgetTexts>;\n /** Called with the receipts after a successful save. */\n onSave?: (receipts: ConsentReceipt[]) => void;\n /** Called if loading purposes or saving fails. */\n onError?: (error: unknown) => void;\n}\n\nexport interface ConsentWidgetController {\n /** Switch the notice language and re-render. */\n setLocale(locale: string): void;\n /** Re-fetch purposes (when not passed in) and re-render. */\n refresh(): Promise<void>;\n /** Remove the widget from the DOM. */\n destroy(): void;\n}\n\ntype Attrs = Record<string, string | EventListenerOrEventListenerObject>;\n\nfunction el<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attrs: Attrs = {},\n children: Array<Node | string> = [],\n): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag);\n for (const [k, v] of Object.entries(attrs)) {\n if (k === \"style\" && typeof v === \"string\") node.style.cssText = v;\n else if (k === \"class\" && typeof v === \"string\") node.className = v;\n else if (k.startsWith(\"on\") && typeof v === \"function\") node.addEventListener(k.slice(2), v);\n else if (typeof v === \"string\") node.setAttribute(k, v);\n }\n for (const c of children) node.appendChild(typeof c === \"string\" ? document.createTextNode(c) : c);\n return node;\n}\n\nfunction noticeFor(purpose: Purpose, locale: string): Required<LocalizedNotice> {\n const t: LocalizedNotice = purpose.translations?.[locale] ?? {};\n return {\n name: t.name || purpose.name,\n description: t.description || purpose.description,\n };\n}\n\nconst CARD =\n \"border:1px solid #e3e3ef;border-radius:12px;padding:20px;background:#fff;max-width:480px;font-family:system-ui,-apple-system,Segoe UI,sans-serif;color:#11113a;box-shadow:0 1px 3px rgba(15,23,42,0.06);\";\n\n/**\n * Mount a drop-in consent capture widget. Returns a controller for switching\n * locale, refreshing, or removing it.\n *\n * ```ts\n * mountConsentWidget(\"#consent\", {\n * apiBase: \"/api/v1\",\n * apiKey: \"dpdp_pk_…\", // publishable key\n * principalRef: \"user_123\",\n * });\n * ```\n */\nexport function mountConsentWidget(\n target: string | HTMLElement,\n options: ConsentWidgetOptions,\n): ConsentWidgetController {\n const container =\n typeof target === \"string\" ? document.querySelector<HTMLElement>(target) : target;\n if (!container) throw new Error(`mountConsentWidget: container not found: ${String(target)}`);\n\n const client =\n options.client ?? new DPDPStack({ apiBase: options.apiBase, apiKey: options.apiKey });\n const texts: ConsentWidgetTexts = { ...DEFAULT_TEXTS, ...options.texts };\n const defaultChecked = new Set(options.defaultChecked ?? []);\n\n let locale = options.locale ?? \"en\";\n let purposes: Purpose[] | null = options.purposes ?? null;\n\n function message(text: string, color: string): HTMLElement {\n return el(\"div\", { style: `margin-top:12px;font-size:13px;color:${color};` }, [text]);\n }\n\n function render(): void {\n container!.innerHTML = \"\";\n const card = el(\"div\", { style: CARD }, [\n el(\"div\", { style: \"font-weight:600;font-size:15px;margin-bottom:4px;\" }, [texts.heading]),\n el(\"div\", { style: \"font-size:13px;color:#666;margin-bottom:12px;\" }, [texts.subheading]),\n ]);\n\n if (!purposes) {\n card.appendChild(message(texts.loading, \"#666\"));\n container!.appendChild(card);\n return;\n }\n\n const checks: Record<string, HTMLInputElement> = {};\n for (const p of purposes) {\n const n = noticeFor(p, locale);\n const box = el(\"input\", { type: \"checkbox\", id: `dpdp_${p.code}` }) as HTMLInputElement;\n if (defaultChecked.has(p.code)) box.checked = true;\n checks[p.code] = box;\n card.appendChild(\n el(\n \"label\",\n {\n style:\n \"display:flex;align-items:flex-start;gap:8px;margin:10px 0;font-size:14px;cursor:pointer;\",\n for: `dpdp_${p.code}`,\n },\n [\n box,\n el(\"span\", {}, [\n el(\"strong\", {}, [n.name]),\n el(\"div\", { style: \"font-size:12px;color:#777;margin-top:2px;\" }, [n.description]),\n ]),\n ],\n ),\n );\n }\n\n const status = el(\"div\", {});\n const saveBtn = el(\n \"button\",\n {\n type: \"button\",\n style:\n \"margin-top:14px;background:#4f46e5;color:#fff;border:0;border-radius:8px;padding:10px 16px;font:inherit;font-weight:600;cursor:pointer;\",\n },\n [texts.save],\n ) as HTMLButtonElement;\n\n saveBtn.addEventListener(\"click\", async () => {\n const selected = purposes!.filter((p) => checks[p.code]?.checked);\n saveBtn.disabled = true;\n saveBtn.textContent = texts.saving;\n status.innerHTML = \"\";\n try {\n const receipts = await Promise.all(\n selected.map((p) =>\n client.grantConsent({ principal_ref: options.principalRef, purpose: p.code, locale }),\n ),\n );\n status.appendChild(message(texts.saved, \"#16a34a\"));\n options.onSave?.(receipts);\n } catch (err) {\n status.appendChild(message(texts.error, \"#dc2626\"));\n options.onError?.(err);\n } finally {\n saveBtn.disabled = false;\n saveBtn.textContent = texts.save;\n }\n });\n\n card.appendChild(saveBtn);\n card.appendChild(status);\n container!.appendChild(card);\n }\n\n async function refresh(): Promise<void> {\n if (!options.purposes) {\n render(); // show loading\n try {\n purposes = await client.listPurposes();\n } catch (err) {\n purposes = [];\n options.onError?.(err);\n }\n }\n render();\n }\n\n void refresh();\n\n return {\n setLocale(next: string) {\n locale = next;\n render();\n },\n refresh,\n destroy() {\n container!.innerHTML = \"\";\n },\n };\n}\n"],"mappings":"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","names":["src_exports","__export","DEFAULT_API_BASE","DPDPConsent","DPDPError","DPDPStack","mountConsentWidget","DEFAULT_API_BASE","DPDPError","status","detail","body","buildQuery","query","parts","v","k","DPDPStack","options","input","certificateJwt","fingerprint","id","f","method","path","opts","url","headers","hasBody","res","text","data","principalRef","token","DEFAULT_TEXTS","el","tag","attrs","children","node","k","v","c","noticeFor","purpose","locale","CARD","mountConsentWidget","target","options","container","client","DPDPStack","texts","defaultChecked","purposes","message","text","color","render","card","checks","p","n","box","status","saveBtn","selected","receipts","err","refresh","next","DPDPConsent","config","el","rest","mountConsentWidget"]}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/client.ts
|
|
4
|
+
var DEFAULT_API_BASE = "https://getdpdp.net/api/v1";
|
|
5
|
+
var DPDPError = class extends Error {
|
|
6
|
+
constructor(status, detail, body) {
|
|
7
|
+
super(`DPDP API error ${status}: ${detail}`);
|
|
8
|
+
this.name = "DPDPError";
|
|
9
|
+
this.status = status;
|
|
10
|
+
this.detail = detail;
|
|
11
|
+
this.body = body;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
function buildQuery(query) {
|
|
15
|
+
if (!query) return "";
|
|
16
|
+
const parts = Object.entries(query).filter(([, v]) => v !== void 0 && v !== null && v !== "").map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);
|
|
17
|
+
return parts.length ? `?${parts.join("&")}` : "";
|
|
18
|
+
}
|
|
19
|
+
var DPDPStack = class {
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
// --- Retention ----------------------------------------------------------
|
|
22
|
+
this.retention = {
|
|
23
|
+
/** List retention policies. Requires a secret key. */
|
|
24
|
+
list: () => this.request("GET", "/retention/policies"),
|
|
25
|
+
/** Create or update a retention policy. Requires a secret key. */
|
|
26
|
+
upsert: (input) => this.request("POST", "/retention/policies", { body: input }),
|
|
27
|
+
/** Run the retention sweep now (`{ dry_run: true }` to preview). Requires a secret key. */
|
|
28
|
+
run: (input = {}) => this.request("POST", "/retention/run", { body: input })
|
|
29
|
+
};
|
|
30
|
+
// --- Certificates -------------------------------------------------------
|
|
31
|
+
this.certificates = {
|
|
32
|
+
/** Issue a counter-signed Certificate of Erasure for a principal. Requires a secret key. */
|
|
33
|
+
issue: (input) => this.request("POST", "/certificate", { body: input }),
|
|
34
|
+
/** Verify a Certificate of Erasure (JWT) against the public key. Public — no key needed. */
|
|
35
|
+
verify: (certificateJwt) => this.request("POST", "/certificate/verify", { body: { certificate_jwt: certificateJwt } }),
|
|
36
|
+
/** Fetch the issuer public key. Public — no key needed. */
|
|
37
|
+
publicKey: () => this.request("GET", "/certificate/public-key"),
|
|
38
|
+
/** Look up a certificate in the public registry by fingerprint. Public — no key needed. */
|
|
39
|
+
registry: (fingerprint) => this.request("GET", `/certificate/registry/${encodeURIComponent(fingerprint)}`),
|
|
40
|
+
/** Issue a certificate from SDK-pushed evidence. Requires a secret key. */
|
|
41
|
+
issueFromEvidence: (input) => this.request("POST", "/evidence/certificate", { body: input })
|
|
42
|
+
};
|
|
43
|
+
// --- Evidence -----------------------------------------------------------
|
|
44
|
+
this.evidence = {
|
|
45
|
+
/** Push tamper-evident audit evidence (hash chain) for server-timestamping. Requires a secret key. */
|
|
46
|
+
ingest: (input) => this.request("POST", "/evidence", { body: input }),
|
|
47
|
+
/** List stored evidence for a source/subject, with chain status. Requires a secret key. */
|
|
48
|
+
list: (query = {}) => this.request("GET", "/evidence", { query })
|
|
49
|
+
};
|
|
50
|
+
// --- Data-subject requests (DSR) ----------------------------------------
|
|
51
|
+
this.dsr = {
|
|
52
|
+
/** List rights requests (filter by status/type/principal/overdue). Requires a secret key. */
|
|
53
|
+
list: (query = {}) => this.request("GET", "/dsr", { query }),
|
|
54
|
+
/** Create a rights request. Requires a secret key. */
|
|
55
|
+
create: (input) => this.request("POST", "/dsr", { body: input }),
|
|
56
|
+
/** Get a single rights request. Requires a secret key. */
|
|
57
|
+
get: (id) => this.request("GET", `/dsr/${id}`),
|
|
58
|
+
/** Advance a rights request (acknowledge/start/complete/reject/extend). Requires a secret key. */
|
|
59
|
+
act: (id, input) => this.request("POST", `/dsr/${id}`, { body: input })
|
|
60
|
+
};
|
|
61
|
+
// --- Breaches -----------------------------------------------------------
|
|
62
|
+
this.breaches = {
|
|
63
|
+
/** List breach incidents. Requires a secret key. */
|
|
64
|
+
list: (query = {}) => this.request("GET", "/breaches", { query }),
|
|
65
|
+
/** Report a breach incident (metadata only — never PII). Requires a secret key. */
|
|
66
|
+
report: (input) => this.request("POST", "/breaches", { body: input }),
|
|
67
|
+
/** Get a single breach. Requires a secret key. */
|
|
68
|
+
get: (id) => this.request("GET", `/breaches/${id}`),
|
|
69
|
+
/** Advance a breach (investigate/contain/notify_board/notify_principals/close). Requires a secret key. */
|
|
70
|
+
act: (id, input) => this.request("POST", `/breaches/${id}`, { body: input }),
|
|
71
|
+
/** Generate draft Board + principal breach notices. Requires a secret key. */
|
|
72
|
+
notifications: (id) => this.request("GET", `/breaches/${id}/notification`)
|
|
73
|
+
};
|
|
74
|
+
// --- Targets & fan-out tasks --------------------------------------------
|
|
75
|
+
this.targets = {
|
|
76
|
+
/** List downstream erasure targets. Requires a secret key. */
|
|
77
|
+
list: () => this.request("GET", "/targets"),
|
|
78
|
+
/** Register a target. The signing `secret` is returned only once. Requires a secret key. */
|
|
79
|
+
create: (input) => this.request("POST", "/targets", { body: input }),
|
|
80
|
+
/** Get a single target. Requires a secret key. */
|
|
81
|
+
get: (id) => this.request("GET", `/targets/${id}`),
|
|
82
|
+
/** Update a target. Requires a secret key. */
|
|
83
|
+
update: (id, input) => this.request("POST", `/targets/${id}`, { body: input }),
|
|
84
|
+
/** Delete a target. Requires a secret key. */
|
|
85
|
+
remove: (id) => this.request("DELETE", `/targets/${id}`)
|
|
86
|
+
};
|
|
87
|
+
this.erasureTasks = {
|
|
88
|
+
/** List per-system erasure fan-out tasks (the propagation evidence). Requires a secret key. */
|
|
89
|
+
list: (query = {}) => this.request("GET", "/erasure/tasks", { query }),
|
|
90
|
+
/** Re-deliver an erasure instruction to a target. Requires a secret key. */
|
|
91
|
+
retry: (id) => this.request("POST", `/erasure/tasks/${id}/retry`)
|
|
92
|
+
};
|
|
93
|
+
this.apiBase = (options.apiBase ?? DEFAULT_API_BASE).replace(/\/+$/, "");
|
|
94
|
+
this.apiKey = options.apiKey;
|
|
95
|
+
this.defaultHeaders = options.headers ?? {};
|
|
96
|
+
this.credentials = options.credentials;
|
|
97
|
+
const f = options.fetch ?? globalThis.fetch;
|
|
98
|
+
if (typeof f !== "function") {
|
|
99
|
+
throw new Error(
|
|
100
|
+
"No fetch implementation found. Use Node 18+, a browser, or pass `fetch` in options."
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
this.fetchImpl = f.bind(globalThis);
|
|
104
|
+
}
|
|
105
|
+
/** Low-level request. Most callers use the typed methods below. */
|
|
106
|
+
async request(method, path, opts = {}) {
|
|
107
|
+
const url = `${this.apiBase}${path}${buildQuery(opts.query)}`;
|
|
108
|
+
const headers = { ...this.defaultHeaders };
|
|
109
|
+
if (this.apiKey) headers["X-API-Key"] = this.apiKey;
|
|
110
|
+
const hasBody = opts.body !== void 0;
|
|
111
|
+
if (hasBody) headers["Content-Type"] = "application/json";
|
|
112
|
+
const res = await this.fetchImpl(url, {
|
|
113
|
+
method,
|
|
114
|
+
headers,
|
|
115
|
+
body: hasBody ? JSON.stringify(opts.body) : void 0,
|
|
116
|
+
...this.credentials ? { credentials: this.credentials } : {}
|
|
117
|
+
});
|
|
118
|
+
const text = await res.text();
|
|
119
|
+
let data = void 0;
|
|
120
|
+
if (text) {
|
|
121
|
+
try {
|
|
122
|
+
data = JSON.parse(text);
|
|
123
|
+
} catch {
|
|
124
|
+
data = text;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (!res.ok) {
|
|
128
|
+
const detail = (data && typeof data === "object" && "detail" in data ? String(data.detail) : void 0) ?? res.statusText ?? "Request failed";
|
|
129
|
+
throw new DPDPError(res.status, detail, data);
|
|
130
|
+
}
|
|
131
|
+
return data;
|
|
132
|
+
}
|
|
133
|
+
// --- Purposes & consent -------------------------------------------------
|
|
134
|
+
/** List consent purposes (with multilingual notices). Publishable-key safe. */
|
|
135
|
+
listPurposes() {
|
|
136
|
+
return this.request("GET", "/purposes");
|
|
137
|
+
}
|
|
138
|
+
/** Create a consent purpose. Requires a secret key. */
|
|
139
|
+
createPurpose(input) {
|
|
140
|
+
return this.request("POST", "/purposes", { body: input });
|
|
141
|
+
}
|
|
142
|
+
/** Record purpose-level consent and get an immutable receipt. Publishable-key safe. */
|
|
143
|
+
grantConsent(input) {
|
|
144
|
+
return this.request("POST", "/consent", { body: input });
|
|
145
|
+
}
|
|
146
|
+
/** Withdraw consent for a purpose (triggers erasure/deferral). Requires a secret key. */
|
|
147
|
+
withdrawConsent(input) {
|
|
148
|
+
return this.request("POST", "/consent/withdraw", { body: input });
|
|
149
|
+
}
|
|
150
|
+
/** Current consent state for a principal. Requires a secret key. */
|
|
151
|
+
consentStatus(principalRef) {
|
|
152
|
+
return this.request("GET", "/consent/status", { query: { principal_ref: principalRef } });
|
|
153
|
+
}
|
|
154
|
+
/** List the organization's consent records (latest first). Requires a secret key. */
|
|
155
|
+
listConsentRecords() {
|
|
156
|
+
return this.request("GET", "/consent/records");
|
|
157
|
+
}
|
|
158
|
+
/** Record principal activity, resetting inactivity-based retention. Requires a secret key. */
|
|
159
|
+
recordActivity(input) {
|
|
160
|
+
return this.request("POST", "/activity", { body: input });
|
|
161
|
+
}
|
|
162
|
+
// --- Erasure ------------------------------------------------------------
|
|
163
|
+
/** Right to erasure: resolve erasure across the principal's purposes. Requires a secret key. */
|
|
164
|
+
requestErasure(input) {
|
|
165
|
+
return this.request("POST", "/erasure", { body: input });
|
|
166
|
+
}
|
|
167
|
+
/** Confirm a downstream erasure with the token delivered to that system. No API key needed. */
|
|
168
|
+
confirmErasure(token) {
|
|
169
|
+
return this.request("POST", "/erasure/confirm", { body: { token } });
|
|
170
|
+
}
|
|
171
|
+
// --- Audit --------------------------------------------------------------
|
|
172
|
+
/** Hash-chained audit trail (optionally filtered by principal), plus chain status. Requires a secret key. */
|
|
173
|
+
getAuditLog(query = {}) {
|
|
174
|
+
return this.request("GET", "/audit", { query });
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// src/widget.ts
|
|
179
|
+
var DEFAULT_TEXTS = {
|
|
180
|
+
heading: "We value your privacy",
|
|
181
|
+
subheading: "Choose what you consent to. You can withdraw anytime (DPDP Act).",
|
|
182
|
+
save: "Save consent preferences",
|
|
183
|
+
saving: "Saving\u2026",
|
|
184
|
+
saved: "Preferences saved.",
|
|
185
|
+
loading: "Loading\u2026",
|
|
186
|
+
error: "Could not save your preferences. Please try again."
|
|
187
|
+
};
|
|
188
|
+
function el(tag, attrs = {}, children = []) {
|
|
189
|
+
const node = document.createElement(tag);
|
|
190
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
191
|
+
if (k === "style" && typeof v === "string") node.style.cssText = v;
|
|
192
|
+
else if (k === "class" && typeof v === "string") node.className = v;
|
|
193
|
+
else if (k.startsWith("on") && typeof v === "function") node.addEventListener(k.slice(2), v);
|
|
194
|
+
else if (typeof v === "string") node.setAttribute(k, v);
|
|
195
|
+
}
|
|
196
|
+
for (const c of children) node.appendChild(typeof c === "string" ? document.createTextNode(c) : c);
|
|
197
|
+
return node;
|
|
198
|
+
}
|
|
199
|
+
function noticeFor(purpose, locale) {
|
|
200
|
+
const t = purpose.translations?.[locale] ?? {};
|
|
201
|
+
return {
|
|
202
|
+
name: t.name || purpose.name,
|
|
203
|
+
description: t.description || purpose.description
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
var CARD = "border:1px solid #e3e3ef;border-radius:12px;padding:20px;background:#fff;max-width:480px;font-family:system-ui,-apple-system,Segoe UI,sans-serif;color:#11113a;box-shadow:0 1px 3px rgba(15,23,42,0.06);";
|
|
207
|
+
function mountConsentWidget(target, options) {
|
|
208
|
+
const container = typeof target === "string" ? document.querySelector(target) : target;
|
|
209
|
+
if (!container) throw new Error(`mountConsentWidget: container not found: ${String(target)}`);
|
|
210
|
+
const client = options.client ?? new DPDPStack({ apiBase: options.apiBase, apiKey: options.apiKey });
|
|
211
|
+
const texts = { ...DEFAULT_TEXTS, ...options.texts };
|
|
212
|
+
const defaultChecked = new Set(options.defaultChecked ?? []);
|
|
213
|
+
let locale = options.locale ?? "en";
|
|
214
|
+
let purposes = options.purposes ?? null;
|
|
215
|
+
function message(text, color) {
|
|
216
|
+
return el("div", { style: `margin-top:12px;font-size:13px;color:${color};` }, [text]);
|
|
217
|
+
}
|
|
218
|
+
function render() {
|
|
219
|
+
container.innerHTML = "";
|
|
220
|
+
const card = el("div", { style: CARD }, [
|
|
221
|
+
el("div", { style: "font-weight:600;font-size:15px;margin-bottom:4px;" }, [texts.heading]),
|
|
222
|
+
el("div", { style: "font-size:13px;color:#666;margin-bottom:12px;" }, [texts.subheading])
|
|
223
|
+
]);
|
|
224
|
+
if (!purposes) {
|
|
225
|
+
card.appendChild(message(texts.loading, "#666"));
|
|
226
|
+
container.appendChild(card);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const checks = {};
|
|
230
|
+
for (const p of purposes) {
|
|
231
|
+
const n = noticeFor(p, locale);
|
|
232
|
+
const box = el("input", { type: "checkbox", id: `dpdp_${p.code}` });
|
|
233
|
+
if (defaultChecked.has(p.code)) box.checked = true;
|
|
234
|
+
checks[p.code] = box;
|
|
235
|
+
card.appendChild(
|
|
236
|
+
el(
|
|
237
|
+
"label",
|
|
238
|
+
{
|
|
239
|
+
style: "display:flex;align-items:flex-start;gap:8px;margin:10px 0;font-size:14px;cursor:pointer;",
|
|
240
|
+
for: `dpdp_${p.code}`
|
|
241
|
+
},
|
|
242
|
+
[
|
|
243
|
+
box,
|
|
244
|
+
el("span", {}, [
|
|
245
|
+
el("strong", {}, [n.name]),
|
|
246
|
+
el("div", { style: "font-size:12px;color:#777;margin-top:2px;" }, [n.description])
|
|
247
|
+
])
|
|
248
|
+
]
|
|
249
|
+
)
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
const status = el("div", {});
|
|
253
|
+
const saveBtn = el(
|
|
254
|
+
"button",
|
|
255
|
+
{
|
|
256
|
+
type: "button",
|
|
257
|
+
style: "margin-top:14px;background:#4f46e5;color:#fff;border:0;border-radius:8px;padding:10px 16px;font:inherit;font-weight:600;cursor:pointer;"
|
|
258
|
+
},
|
|
259
|
+
[texts.save]
|
|
260
|
+
);
|
|
261
|
+
saveBtn.addEventListener("click", async () => {
|
|
262
|
+
const selected = purposes.filter((p) => checks[p.code]?.checked);
|
|
263
|
+
saveBtn.disabled = true;
|
|
264
|
+
saveBtn.textContent = texts.saving;
|
|
265
|
+
status.innerHTML = "";
|
|
266
|
+
try {
|
|
267
|
+
const receipts = await Promise.all(
|
|
268
|
+
selected.map(
|
|
269
|
+
(p) => client.grantConsent({ principal_ref: options.principalRef, purpose: p.code, locale })
|
|
270
|
+
)
|
|
271
|
+
);
|
|
272
|
+
status.appendChild(message(texts.saved, "#16a34a"));
|
|
273
|
+
options.onSave?.(receipts);
|
|
274
|
+
} catch (err) {
|
|
275
|
+
status.appendChild(message(texts.error, "#dc2626"));
|
|
276
|
+
options.onError?.(err);
|
|
277
|
+
} finally {
|
|
278
|
+
saveBtn.disabled = false;
|
|
279
|
+
saveBtn.textContent = texts.save;
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
card.appendChild(saveBtn);
|
|
283
|
+
card.appendChild(status);
|
|
284
|
+
container.appendChild(card);
|
|
285
|
+
}
|
|
286
|
+
async function refresh() {
|
|
287
|
+
if (!options.purposes) {
|
|
288
|
+
render();
|
|
289
|
+
try {
|
|
290
|
+
purposes = await client.listPurposes();
|
|
291
|
+
} catch (err) {
|
|
292
|
+
purposes = [];
|
|
293
|
+
options.onError?.(err);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
render();
|
|
297
|
+
}
|
|
298
|
+
void refresh();
|
|
299
|
+
return {
|
|
300
|
+
setLocale(next) {
|
|
301
|
+
locale = next;
|
|
302
|
+
render();
|
|
303
|
+
},
|
|
304
|
+
refresh,
|
|
305
|
+
destroy() {
|
|
306
|
+
container.innerHTML = "";
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// src/index.ts
|
|
312
|
+
var DPDPConsent = {
|
|
313
|
+
init(config) {
|
|
314
|
+
const { el: el2, ...rest } = config;
|
|
315
|
+
return mountConsentWidget(el2, rest);
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
exports.DEFAULT_API_BASE = DEFAULT_API_BASE;
|
|
320
|
+
exports.DPDPConsent = DPDPConsent;
|
|
321
|
+
exports.DPDPError = DPDPError;
|
|
322
|
+
exports.DPDPStack = DPDPStack;
|
|
323
|
+
exports.mountConsentWidget = mountConsentWidget;
|
|
324
|
+
//# sourceMappingURL=index.cjs.map
|
|
325
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/widget.ts","../src/index.ts"],"names":["el"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n ActivityInput,\n ActivityResult,\n AuditLog,\n Breach,\n BreachActionInput,\n BreachListQuery,\n BreachNotifications,\n BreachReportInput,\n CertificatePublicKey,\n CertificateRegistryResult,\n CertificateVerifyResult,\n ConsentGrantInput,\n ConsentReceipt,\n ConsentRecordSummary,\n ConsentStatus,\n ConsentWithdrawInput,\n ConsentWithdrawResult,\n DSR,\n DSRActionInput,\n DSRCreateInput,\n DSRListQuery,\n ErasureConfirmResult,\n ErasureInput,\n ErasureResult,\n ErasureTask,\n ErasureTaskListQuery,\n EvidenceIngestInput,\n EvidenceIngestResult,\n EvidenceListQuery,\n EvidenceListResult,\n IssueCertificateInput,\n IssueEvidenceCertificateInput,\n IssuedCertificate,\n Purpose,\n PurposeInput,\n RetentionPolicy,\n RetentionPolicyInput,\n RetentionRunResult,\n Target,\n TargetCreateInput,\n TargetUpdateInput,\n TargetWithSecret,\n} from \"./types.js\";\n\nexport const DEFAULT_API_BASE = \"https://getdpdp.net/api/v1\";\n\nexport interface DPDPStackOptions {\n /**\n * API key. A **secret** key (`dpdp_sk_…`) for server-side use, or a\n * **publishable** key (`dpdp_pk_…`) — the only kind safe to ship to a browser,\n * limited to reading purposes and recording consent. Omit for public-only\n * calls (certificate verify/registry/public-key).\n */\n apiKey?: string;\n /** API base URL. Default `https://getdpdp.net/api/v1`. Use a relative path (e.g. `/api/v1`) to call a same-origin proxy. */\n apiBase?: string;\n /** Custom fetch implementation (for Node < 18, testing, or proxies). Defaults to the global `fetch`. */\n fetch?: typeof fetch;\n /** Extra headers sent with every request. */\n headers?: Record<string, string>;\n /** `fetch` credentials mode (e.g. `\"include\"` to send cookies). Default: unset. */\n credentials?: RequestCredentials;\n}\n\n/** Thrown for any non-2xx API response. */\nexport class DPDPError extends Error {\n readonly status: number;\n readonly detail: string;\n readonly body: unknown;\n\n constructor(status: number, detail: string, body: unknown) {\n super(`DPDP API error ${status}: ${detail}`);\n this.name = \"DPDPError\";\n this.status = status;\n this.detail = detail;\n this.body = body;\n }\n}\n\nfunction buildQuery(query?: object): string {\n if (!query) return \"\";\n const parts = Object.entries(query as Record<string, unknown>)\n .filter(([, v]) => v !== undefined && v !== null && v !== \"\")\n .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);\n return parts.length ? `?${parts.join(\"&\")}` : \"\";\n}\n\n/**\n * Thin, typed client for the DPDPStack API.\n *\n * ```ts\n * const dpdp = new DPDPStack({ apiKey: \"dpdp_sk_…\" });\n * await dpdp.grantConsent({ principal_ref: \"user_42\", purpose: \"marketing\" });\n * ```\n */\nexport class DPDPStack {\n private readonly apiBase: string;\n private readonly apiKey?: string;\n private readonly fetchImpl: typeof fetch;\n private readonly defaultHeaders: Record<string, string>;\n private readonly credentials?: RequestCredentials;\n\n constructor(options: DPDPStackOptions = {}) {\n this.apiBase = (options.apiBase ?? DEFAULT_API_BASE).replace(/\\/+$/, \"\");\n this.apiKey = options.apiKey;\n this.defaultHeaders = options.headers ?? {};\n this.credentials = options.credentials;\n\n const f = options.fetch ?? globalThis.fetch;\n if (typeof f !== \"function\") {\n throw new Error(\n \"No fetch implementation found. Use Node 18+, a browser, or pass `fetch` in options.\",\n );\n }\n this.fetchImpl = f.bind(globalThis);\n }\n\n /** Low-level request. Most callers use the typed methods below. */\n async request<T>(\n method: string,\n path: string,\n opts: { query?: object; body?: unknown } = {},\n ): Promise<T> {\n const url = `${this.apiBase}${path}${buildQuery(opts.query)}`;\n const headers: Record<string, string> = { ...this.defaultHeaders };\n if (this.apiKey) headers[\"X-API-Key\"] = this.apiKey;\n\n const hasBody = opts.body !== undefined;\n if (hasBody) headers[\"Content-Type\"] = \"application/json\";\n\n const res = await this.fetchImpl(url, {\n method,\n headers,\n body: hasBody ? JSON.stringify(opts.body) : undefined,\n ...(this.credentials ? { credentials: this.credentials } : {}),\n });\n\n const text = await res.text();\n let data: unknown = undefined;\n if (text) {\n try {\n data = JSON.parse(text);\n } catch {\n data = text;\n }\n }\n\n if (!res.ok) {\n const detail =\n (data && typeof data === \"object\" && \"detail\" in data\n ? String((data as { detail: unknown }).detail)\n : undefined) ?? res.statusText ?? \"Request failed\";\n throw new DPDPError(res.status, detail, data);\n }\n\n return data as T;\n }\n\n // --- Purposes & consent -------------------------------------------------\n\n /** List consent purposes (with multilingual notices). Publishable-key safe. */\n listPurposes(): Promise<Purpose[]> {\n return this.request(\"GET\", \"/purposes\");\n }\n\n /** Create a consent purpose. Requires a secret key. */\n createPurpose(input: PurposeInput): Promise<Purpose> {\n return this.request(\"POST\", \"/purposes\", { body: input });\n }\n\n /** Record purpose-level consent and get an immutable receipt. Publishable-key safe. */\n grantConsent(input: ConsentGrantInput): Promise<ConsentReceipt> {\n return this.request(\"POST\", \"/consent\", { body: input });\n }\n\n /** Withdraw consent for a purpose (triggers erasure/deferral). Requires a secret key. */\n withdrawConsent(input: ConsentWithdrawInput): Promise<ConsentWithdrawResult> {\n return this.request(\"POST\", \"/consent/withdraw\", { body: input });\n }\n\n /** Current consent state for a principal. Requires a secret key. */\n consentStatus(principalRef: string): Promise<ConsentStatus> {\n return this.request(\"GET\", \"/consent/status\", { query: { principal_ref: principalRef } });\n }\n\n /** List the organization's consent records (latest first). Requires a secret key. */\n listConsentRecords(): Promise<ConsentRecordSummary[]> {\n return this.request(\"GET\", \"/consent/records\");\n }\n\n /** Record principal activity, resetting inactivity-based retention. Requires a secret key. */\n recordActivity(input: ActivityInput): Promise<ActivityResult> {\n return this.request(\"POST\", \"/activity\", { body: input });\n }\n\n // --- Erasure ------------------------------------------------------------\n\n /** Right to erasure: resolve erasure across the principal's purposes. Requires a secret key. */\n requestErasure(input: ErasureInput): Promise<ErasureResult> {\n return this.request(\"POST\", \"/erasure\", { body: input });\n }\n\n /** Confirm a downstream erasure with the token delivered to that system. No API key needed. */\n confirmErasure(token: string): Promise<ErasureConfirmResult> {\n return this.request(\"POST\", \"/erasure/confirm\", { body: { token } });\n }\n\n // --- Audit --------------------------------------------------------------\n\n /** Hash-chained audit trail (optionally filtered by principal), plus chain status. Requires a secret key. */\n getAuditLog(query: { principal_ref?: string } = {}): Promise<AuditLog> {\n return this.request(\"GET\", \"/audit\", { query });\n }\n\n // --- Retention ----------------------------------------------------------\n\n readonly retention = {\n /** List retention policies. Requires a secret key. */\n list: (): Promise<RetentionPolicy[]> => this.request(\"GET\", \"/retention/policies\"),\n /** Create or update a retention policy. Requires a secret key. */\n upsert: (input: RetentionPolicyInput): Promise<RetentionPolicy> =>\n this.request(\"POST\", \"/retention/policies\", { body: input }),\n /** Run the retention sweep now (`{ dry_run: true }` to preview). Requires a secret key. */\n run: (input: { dry_run?: boolean } = {}): Promise<RetentionRunResult> =>\n this.request(\"POST\", \"/retention/run\", { body: input }),\n };\n\n // --- Certificates -------------------------------------------------------\n\n readonly certificates = {\n /** Issue a counter-signed Certificate of Erasure for a principal. Requires a secret key. */\n issue: (input: IssueCertificateInput): Promise<IssuedCertificate> =>\n this.request(\"POST\", \"/certificate\", { body: input }),\n /** Verify a Certificate of Erasure (JWT) against the public key. Public — no key needed. */\n verify: (certificateJwt: string): Promise<CertificateVerifyResult> =>\n this.request(\"POST\", \"/certificate/verify\", { body: { certificate_jwt: certificateJwt } }),\n /** Fetch the issuer public key. Public — no key needed. */\n publicKey: (): Promise<CertificatePublicKey> =>\n this.request(\"GET\", \"/certificate/public-key\"),\n /** Look up a certificate in the public registry by fingerprint. Public — no key needed. */\n registry: (fingerprint: string): Promise<CertificateRegistryResult> =>\n this.request(\"GET\", `/certificate/registry/${encodeURIComponent(fingerprint)}`),\n /** Issue a certificate from SDK-pushed evidence. Requires a secret key. */\n issueFromEvidence: (input: IssueEvidenceCertificateInput): Promise<IssuedCertificate> =>\n this.request(\"POST\", \"/evidence/certificate\", { body: input }),\n };\n\n // --- Evidence -----------------------------------------------------------\n\n readonly evidence = {\n /** Push tamper-evident audit evidence (hash chain) for server-timestamping. Requires a secret key. */\n ingest: (input: EvidenceIngestInput): Promise<EvidenceIngestResult> =>\n this.request(\"POST\", \"/evidence\", { body: input }),\n /** List stored evidence for a source/subject, with chain status. Requires a secret key. */\n list: (query: EvidenceListQuery = {}): Promise<EvidenceListResult> =>\n this.request(\"GET\", \"/evidence\", { query }),\n };\n\n // --- Data-subject requests (DSR) ----------------------------------------\n\n readonly dsr = {\n /** List rights requests (filter by status/type/principal/overdue). Requires a secret key. */\n list: (query: DSRListQuery = {}): Promise<DSR[]> => this.request(\"GET\", \"/dsr\", { query }),\n /** Create a rights request. Requires a secret key. */\n create: (input: DSRCreateInput): Promise<DSR> => this.request(\"POST\", \"/dsr\", { body: input }),\n /** Get a single rights request. Requires a secret key. */\n get: (id: number): Promise<DSR> => this.request(\"GET\", `/dsr/${id}`),\n /** Advance a rights request (acknowledge/start/complete/reject/extend). Requires a secret key. */\n act: (id: number, input: DSRActionInput): Promise<DSR> =>\n this.request(\"POST\", `/dsr/${id}`, { body: input }),\n };\n\n // --- Breaches -----------------------------------------------------------\n\n readonly breaches = {\n /** List breach incidents. Requires a secret key. */\n list: (query: BreachListQuery = {}): Promise<Breach[]> =>\n this.request(\"GET\", \"/breaches\", { query }),\n /** Report a breach incident (metadata only — never PII). Requires a secret key. */\n report: (input: BreachReportInput): Promise<Breach> =>\n this.request(\"POST\", \"/breaches\", { body: input }),\n /** Get a single breach. Requires a secret key. */\n get: (id: number): Promise<Breach> => this.request(\"GET\", `/breaches/${id}`),\n /** Advance a breach (investigate/contain/notify_board/notify_principals/close). Requires a secret key. */\n act: (id: number, input: BreachActionInput): Promise<Breach> =>\n this.request(\"POST\", `/breaches/${id}`, { body: input }),\n /** Generate draft Board + principal breach notices. Requires a secret key. */\n notifications: (id: number): Promise<BreachNotifications> =>\n this.request(\"GET\", `/breaches/${id}/notification`),\n };\n\n // --- Targets & fan-out tasks --------------------------------------------\n\n readonly targets = {\n /** List downstream erasure targets. Requires a secret key. */\n list: (): Promise<Target[]> => this.request(\"GET\", \"/targets\"),\n /** Register a target. The signing `secret` is returned only once. Requires a secret key. */\n create: (input: TargetCreateInput): Promise<TargetWithSecret> =>\n this.request(\"POST\", \"/targets\", { body: input }),\n /** Get a single target. Requires a secret key. */\n get: (id: number): Promise<Target> => this.request(\"GET\", `/targets/${id}`),\n /** Update a target. Requires a secret key. */\n update: (id: number, input: TargetUpdateInput): Promise<Target> =>\n this.request(\"POST\", `/targets/${id}`, { body: input }),\n /** Delete a target. Requires a secret key. */\n remove: (id: number): Promise<void> => this.request(\"DELETE\", `/targets/${id}`),\n };\n\n readonly erasureTasks = {\n /** List per-system erasure fan-out tasks (the propagation evidence). Requires a secret key. */\n list: (query: ErasureTaskListQuery = {}): Promise<ErasureTask[]> =>\n this.request(\"GET\", \"/erasure/tasks\", { query }),\n /** Re-deliver an erasure instruction to a target. Requires a secret key. */\n retry: (id: number): Promise<ErasureTask> =>\n this.request(\"POST\", `/erasure/tasks/${id}/retry`),\n };\n}\n","import { DPDPStack } from \"./client.js\";\nimport type { ConsentReceipt, LocalizedNotice, Purpose } from \"./types.js\";\n\nexport interface ConsentWidgetTexts {\n heading: string;\n subheading: string;\n save: string;\n saving: string;\n saved: string;\n loading: string;\n error: string;\n}\n\nconst DEFAULT_TEXTS: ConsentWidgetTexts = {\n heading: \"We value your privacy\",\n subheading: \"Choose what you consent to. You can withdraw anytime (DPDP Act).\",\n save: \"Save consent preferences\",\n saving: \"Saving…\",\n saved: \"Preferences saved.\",\n loading: \"Loading…\",\n error: \"Could not save your preferences. Please try again.\",\n};\n\nexport interface ConsentWidgetOptions {\n /** Your opaque user id (internal id or hash) — never PII. */\n principalRef: string;\n /** Pre-built client. If omitted, one is built from `apiBase`/`apiKey`. */\n client?: DPDPStack;\n /** Used to build a client when `client` is not supplied. */\n apiBase?: string;\n /** Publishable key (`dpdp_pk_…`). Used to build a client when `client` is not supplied. */\n apiKey?: string;\n /** Purposes to render. If omitted, they're fetched via `client.listPurposes()`. */\n purposes?: Purpose[];\n /** Initial locale for notice text. Default `\"en\"`. */\n locale?: string;\n /** Purpose codes checked on first render. */\n defaultChecked?: string[];\n /** Override any UI strings. */\n texts?: Partial<ConsentWidgetTexts>;\n /** Called with the receipts after a successful save. */\n onSave?: (receipts: ConsentReceipt[]) => void;\n /** Called if loading purposes or saving fails. */\n onError?: (error: unknown) => void;\n}\n\nexport interface ConsentWidgetController {\n /** Switch the notice language and re-render. */\n setLocale(locale: string): void;\n /** Re-fetch purposes (when not passed in) and re-render. */\n refresh(): Promise<void>;\n /** Remove the widget from the DOM. */\n destroy(): void;\n}\n\ntype Attrs = Record<string, string | EventListenerOrEventListenerObject>;\n\nfunction el<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attrs: Attrs = {},\n children: Array<Node | string> = [],\n): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag);\n for (const [k, v] of Object.entries(attrs)) {\n if (k === \"style\" && typeof v === \"string\") node.style.cssText = v;\n else if (k === \"class\" && typeof v === \"string\") node.className = v;\n else if (k.startsWith(\"on\") && typeof v === \"function\") node.addEventListener(k.slice(2), v);\n else if (typeof v === \"string\") node.setAttribute(k, v);\n }\n for (const c of children) node.appendChild(typeof c === \"string\" ? document.createTextNode(c) : c);\n return node;\n}\n\nfunction noticeFor(purpose: Purpose, locale: string): Required<LocalizedNotice> {\n const t: LocalizedNotice = purpose.translations?.[locale] ?? {};\n return {\n name: t.name || purpose.name,\n description: t.description || purpose.description,\n };\n}\n\nconst CARD =\n \"border:1px solid #e3e3ef;border-radius:12px;padding:20px;background:#fff;max-width:480px;font-family:system-ui,-apple-system,Segoe UI,sans-serif;color:#11113a;box-shadow:0 1px 3px rgba(15,23,42,0.06);\";\n\n/**\n * Mount a drop-in consent capture widget. Returns a controller for switching\n * locale, refreshing, or removing it.\n *\n * ```ts\n * mountConsentWidget(\"#consent\", {\n * apiBase: \"/api/v1\",\n * apiKey: \"dpdp_pk_…\", // publishable key\n * principalRef: \"user_123\",\n * });\n * ```\n */\nexport function mountConsentWidget(\n target: string | HTMLElement,\n options: ConsentWidgetOptions,\n): ConsentWidgetController {\n const container =\n typeof target === \"string\" ? document.querySelector<HTMLElement>(target) : target;\n if (!container) throw new Error(`mountConsentWidget: container not found: ${String(target)}`);\n\n const client =\n options.client ?? new DPDPStack({ apiBase: options.apiBase, apiKey: options.apiKey });\n const texts: ConsentWidgetTexts = { ...DEFAULT_TEXTS, ...options.texts };\n const defaultChecked = new Set(options.defaultChecked ?? []);\n\n let locale = options.locale ?? \"en\";\n let purposes: Purpose[] | null = options.purposes ?? null;\n\n function message(text: string, color: string): HTMLElement {\n return el(\"div\", { style: `margin-top:12px;font-size:13px;color:${color};` }, [text]);\n }\n\n function render(): void {\n container!.innerHTML = \"\";\n const card = el(\"div\", { style: CARD }, [\n el(\"div\", { style: \"font-weight:600;font-size:15px;margin-bottom:4px;\" }, [texts.heading]),\n el(\"div\", { style: \"font-size:13px;color:#666;margin-bottom:12px;\" }, [texts.subheading]),\n ]);\n\n if (!purposes) {\n card.appendChild(message(texts.loading, \"#666\"));\n container!.appendChild(card);\n return;\n }\n\n const checks: Record<string, HTMLInputElement> = {};\n for (const p of purposes) {\n const n = noticeFor(p, locale);\n const box = el(\"input\", { type: \"checkbox\", id: `dpdp_${p.code}` }) as HTMLInputElement;\n if (defaultChecked.has(p.code)) box.checked = true;\n checks[p.code] = box;\n card.appendChild(\n el(\n \"label\",\n {\n style:\n \"display:flex;align-items:flex-start;gap:8px;margin:10px 0;font-size:14px;cursor:pointer;\",\n for: `dpdp_${p.code}`,\n },\n [\n box,\n el(\"span\", {}, [\n el(\"strong\", {}, [n.name]),\n el(\"div\", { style: \"font-size:12px;color:#777;margin-top:2px;\" }, [n.description]),\n ]),\n ],\n ),\n );\n }\n\n const status = el(\"div\", {});\n const saveBtn = el(\n \"button\",\n {\n type: \"button\",\n style:\n \"margin-top:14px;background:#4f46e5;color:#fff;border:0;border-radius:8px;padding:10px 16px;font:inherit;font-weight:600;cursor:pointer;\",\n },\n [texts.save],\n ) as HTMLButtonElement;\n\n saveBtn.addEventListener(\"click\", async () => {\n const selected = purposes!.filter((p) => checks[p.code]?.checked);\n saveBtn.disabled = true;\n saveBtn.textContent = texts.saving;\n status.innerHTML = \"\";\n try {\n const receipts = await Promise.all(\n selected.map((p) =>\n client.grantConsent({ principal_ref: options.principalRef, purpose: p.code, locale }),\n ),\n );\n status.appendChild(message(texts.saved, \"#16a34a\"));\n options.onSave?.(receipts);\n } catch (err) {\n status.appendChild(message(texts.error, \"#dc2626\"));\n options.onError?.(err);\n } finally {\n saveBtn.disabled = false;\n saveBtn.textContent = texts.save;\n }\n });\n\n card.appendChild(saveBtn);\n card.appendChild(status);\n container!.appendChild(card);\n }\n\n async function refresh(): Promise<void> {\n if (!options.purposes) {\n render(); // show loading\n try {\n purposes = await client.listPurposes();\n } catch (err) {\n purposes = [];\n options.onError?.(err);\n }\n }\n render();\n }\n\n void refresh();\n\n return {\n setLocale(next: string) {\n locale = next;\n render();\n },\n refresh,\n destroy() {\n container!.innerHTML = \"\";\n },\n };\n}\n","export { DPDPStack, DPDPError, DEFAULT_API_BASE } from \"./client.js\";\nexport type { DPDPStackOptions } from \"./client.js\";\n\nexport { mountConsentWidget } from \"./widget.js\";\nexport type {\n ConsentWidgetOptions,\n ConsentWidgetController,\n ConsentWidgetTexts,\n} from \"./widget.js\";\n\nexport type * from \"./types.js\";\n\nimport { mountConsentWidget, type ConsentWidgetController } from \"./widget.js\";\nimport type { ConsentReceipt, Purpose } from \"./types.js\";\n\n/**\n * Backward-compatible shim for the original `DPDPConsent.init({ el, … })`\n * script-tag widget. Prefer {@link mountConsentWidget} in new code.\n */\nexport const DPDPConsent = {\n init(config: {\n el: string | HTMLElement;\n apiBase?: string;\n apiKey?: string;\n principalRef: string;\n locale?: string;\n purposes?: Purpose[];\n onSave?: (receipts: ConsentReceipt[]) => void;\n }): ConsentWidgetController {\n const { el, ...rest } = config;\n return mountConsentWidget(el, rest);\n },\n};\n"]}
|