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/dist/index.js
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
var DEFAULT_API_BASE = "https://getdpdp.net/api/v1";
|
|
3
|
+
var DPDPError = class extends Error {
|
|
4
|
+
constructor(status, detail, body) {
|
|
5
|
+
super(`DPDP API error ${status}: ${detail}`);
|
|
6
|
+
this.name = "DPDPError";
|
|
7
|
+
this.status = status;
|
|
8
|
+
this.detail = detail;
|
|
9
|
+
this.body = body;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
function buildQuery(query) {
|
|
13
|
+
if (!query) return "";
|
|
14
|
+
const parts = Object.entries(query).filter(([, v]) => v !== void 0 && v !== null && v !== "").map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);
|
|
15
|
+
return parts.length ? `?${parts.join("&")}` : "";
|
|
16
|
+
}
|
|
17
|
+
var DPDPStack = class {
|
|
18
|
+
constructor(options = {}) {
|
|
19
|
+
// --- Retention ----------------------------------------------------------
|
|
20
|
+
this.retention = {
|
|
21
|
+
/** List retention policies. Requires a secret key. */
|
|
22
|
+
list: () => this.request("GET", "/retention/policies"),
|
|
23
|
+
/** Create or update a retention policy. Requires a secret key. */
|
|
24
|
+
upsert: (input) => this.request("POST", "/retention/policies", { body: input }),
|
|
25
|
+
/** Run the retention sweep now (`{ dry_run: true }` to preview). Requires a secret key. */
|
|
26
|
+
run: (input = {}) => this.request("POST", "/retention/run", { body: input })
|
|
27
|
+
};
|
|
28
|
+
// --- Certificates -------------------------------------------------------
|
|
29
|
+
this.certificates = {
|
|
30
|
+
/** Issue a counter-signed Certificate of Erasure for a principal. Requires a secret key. */
|
|
31
|
+
issue: (input) => this.request("POST", "/certificate", { body: input }),
|
|
32
|
+
/** Verify a Certificate of Erasure (JWT) against the public key. Public — no key needed. */
|
|
33
|
+
verify: (certificateJwt) => this.request("POST", "/certificate/verify", { body: { certificate_jwt: certificateJwt } }),
|
|
34
|
+
/** Fetch the issuer public key. Public — no key needed. */
|
|
35
|
+
publicKey: () => this.request("GET", "/certificate/public-key"),
|
|
36
|
+
/** Look up a certificate in the public registry by fingerprint. Public — no key needed. */
|
|
37
|
+
registry: (fingerprint) => this.request("GET", `/certificate/registry/${encodeURIComponent(fingerprint)}`),
|
|
38
|
+
/** Issue a certificate from SDK-pushed evidence. Requires a secret key. */
|
|
39
|
+
issueFromEvidence: (input) => this.request("POST", "/evidence/certificate", { body: input })
|
|
40
|
+
};
|
|
41
|
+
// --- Evidence -----------------------------------------------------------
|
|
42
|
+
this.evidence = {
|
|
43
|
+
/** Push tamper-evident audit evidence (hash chain) for server-timestamping. Requires a secret key. */
|
|
44
|
+
ingest: (input) => this.request("POST", "/evidence", { body: input }),
|
|
45
|
+
/** List stored evidence for a source/subject, with chain status. Requires a secret key. */
|
|
46
|
+
list: (query = {}) => this.request("GET", "/evidence", { query })
|
|
47
|
+
};
|
|
48
|
+
// --- Data-subject requests (DSR) ----------------------------------------
|
|
49
|
+
this.dsr = {
|
|
50
|
+
/** List rights requests (filter by status/type/principal/overdue). Requires a secret key. */
|
|
51
|
+
list: (query = {}) => this.request("GET", "/dsr", { query }),
|
|
52
|
+
/** Create a rights request. Requires a secret key. */
|
|
53
|
+
create: (input) => this.request("POST", "/dsr", { body: input }),
|
|
54
|
+
/** Get a single rights request. Requires a secret key. */
|
|
55
|
+
get: (id) => this.request("GET", `/dsr/${id}`),
|
|
56
|
+
/** Advance a rights request (acknowledge/start/complete/reject/extend). Requires a secret key. */
|
|
57
|
+
act: (id, input) => this.request("POST", `/dsr/${id}`, { body: input })
|
|
58
|
+
};
|
|
59
|
+
// --- Breaches -----------------------------------------------------------
|
|
60
|
+
this.breaches = {
|
|
61
|
+
/** List breach incidents. Requires a secret key. */
|
|
62
|
+
list: (query = {}) => this.request("GET", "/breaches", { query }),
|
|
63
|
+
/** Report a breach incident (metadata only — never PII). Requires a secret key. */
|
|
64
|
+
report: (input) => this.request("POST", "/breaches", { body: input }),
|
|
65
|
+
/** Get a single breach. Requires a secret key. */
|
|
66
|
+
get: (id) => this.request("GET", `/breaches/${id}`),
|
|
67
|
+
/** Advance a breach (investigate/contain/notify_board/notify_principals/close). Requires a secret key. */
|
|
68
|
+
act: (id, input) => this.request("POST", `/breaches/${id}`, { body: input }),
|
|
69
|
+
/** Generate draft Board + principal breach notices. Requires a secret key. */
|
|
70
|
+
notifications: (id) => this.request("GET", `/breaches/${id}/notification`)
|
|
71
|
+
};
|
|
72
|
+
// --- Targets & fan-out tasks --------------------------------------------
|
|
73
|
+
this.targets = {
|
|
74
|
+
/** List downstream erasure targets. Requires a secret key. */
|
|
75
|
+
list: () => this.request("GET", "/targets"),
|
|
76
|
+
/** Register a target. The signing `secret` is returned only once. Requires a secret key. */
|
|
77
|
+
create: (input) => this.request("POST", "/targets", { body: input }),
|
|
78
|
+
/** Get a single target. Requires a secret key. */
|
|
79
|
+
get: (id) => this.request("GET", `/targets/${id}`),
|
|
80
|
+
/** Update a target. Requires a secret key. */
|
|
81
|
+
update: (id, input) => this.request("POST", `/targets/${id}`, { body: input }),
|
|
82
|
+
/** Delete a target. Requires a secret key. */
|
|
83
|
+
remove: (id) => this.request("DELETE", `/targets/${id}`)
|
|
84
|
+
};
|
|
85
|
+
this.erasureTasks = {
|
|
86
|
+
/** List per-system erasure fan-out tasks (the propagation evidence). Requires a secret key. */
|
|
87
|
+
list: (query = {}) => this.request("GET", "/erasure/tasks", { query }),
|
|
88
|
+
/** Re-deliver an erasure instruction to a target. Requires a secret key. */
|
|
89
|
+
retry: (id) => this.request("POST", `/erasure/tasks/${id}/retry`)
|
|
90
|
+
};
|
|
91
|
+
this.apiBase = (options.apiBase ?? DEFAULT_API_BASE).replace(/\/+$/, "");
|
|
92
|
+
this.apiKey = options.apiKey;
|
|
93
|
+
this.defaultHeaders = options.headers ?? {};
|
|
94
|
+
this.credentials = options.credentials;
|
|
95
|
+
const f = options.fetch ?? globalThis.fetch;
|
|
96
|
+
if (typeof f !== "function") {
|
|
97
|
+
throw new Error(
|
|
98
|
+
"No fetch implementation found. Use Node 18+, a browser, or pass `fetch` in options."
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
this.fetchImpl = f.bind(globalThis);
|
|
102
|
+
}
|
|
103
|
+
/** Low-level request. Most callers use the typed methods below. */
|
|
104
|
+
async request(method, path, opts = {}) {
|
|
105
|
+
const url = `${this.apiBase}${path}${buildQuery(opts.query)}`;
|
|
106
|
+
const headers = { ...this.defaultHeaders };
|
|
107
|
+
if (this.apiKey) headers["X-API-Key"] = this.apiKey;
|
|
108
|
+
const hasBody = opts.body !== void 0;
|
|
109
|
+
if (hasBody) headers["Content-Type"] = "application/json";
|
|
110
|
+
const res = await this.fetchImpl(url, {
|
|
111
|
+
method,
|
|
112
|
+
headers,
|
|
113
|
+
body: hasBody ? JSON.stringify(opts.body) : void 0,
|
|
114
|
+
...this.credentials ? { credentials: this.credentials } : {}
|
|
115
|
+
});
|
|
116
|
+
const text = await res.text();
|
|
117
|
+
let data = void 0;
|
|
118
|
+
if (text) {
|
|
119
|
+
try {
|
|
120
|
+
data = JSON.parse(text);
|
|
121
|
+
} catch {
|
|
122
|
+
data = text;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (!res.ok) {
|
|
126
|
+
const detail = (data && typeof data === "object" && "detail" in data ? String(data.detail) : void 0) ?? res.statusText ?? "Request failed";
|
|
127
|
+
throw new DPDPError(res.status, detail, data);
|
|
128
|
+
}
|
|
129
|
+
return data;
|
|
130
|
+
}
|
|
131
|
+
// --- Purposes & consent -------------------------------------------------
|
|
132
|
+
/** List consent purposes (with multilingual notices). Publishable-key safe. */
|
|
133
|
+
listPurposes() {
|
|
134
|
+
return this.request("GET", "/purposes");
|
|
135
|
+
}
|
|
136
|
+
/** Create a consent purpose. Requires a secret key. */
|
|
137
|
+
createPurpose(input) {
|
|
138
|
+
return this.request("POST", "/purposes", { body: input });
|
|
139
|
+
}
|
|
140
|
+
/** Record purpose-level consent and get an immutable receipt. Publishable-key safe. */
|
|
141
|
+
grantConsent(input) {
|
|
142
|
+
return this.request("POST", "/consent", { body: input });
|
|
143
|
+
}
|
|
144
|
+
/** Withdraw consent for a purpose (triggers erasure/deferral). Requires a secret key. */
|
|
145
|
+
withdrawConsent(input) {
|
|
146
|
+
return this.request("POST", "/consent/withdraw", { body: input });
|
|
147
|
+
}
|
|
148
|
+
/** Current consent state for a principal. Requires a secret key. */
|
|
149
|
+
consentStatus(principalRef) {
|
|
150
|
+
return this.request("GET", "/consent/status", { query: { principal_ref: principalRef } });
|
|
151
|
+
}
|
|
152
|
+
/** List the organization's consent records (latest first). Requires a secret key. */
|
|
153
|
+
listConsentRecords() {
|
|
154
|
+
return this.request("GET", "/consent/records");
|
|
155
|
+
}
|
|
156
|
+
/** Record principal activity, resetting inactivity-based retention. Requires a secret key. */
|
|
157
|
+
recordActivity(input) {
|
|
158
|
+
return this.request("POST", "/activity", { body: input });
|
|
159
|
+
}
|
|
160
|
+
// --- Erasure ------------------------------------------------------------
|
|
161
|
+
/** Right to erasure: resolve erasure across the principal's purposes. Requires a secret key. */
|
|
162
|
+
requestErasure(input) {
|
|
163
|
+
return this.request("POST", "/erasure", { body: input });
|
|
164
|
+
}
|
|
165
|
+
/** Confirm a downstream erasure with the token delivered to that system. No API key needed. */
|
|
166
|
+
confirmErasure(token) {
|
|
167
|
+
return this.request("POST", "/erasure/confirm", { body: { token } });
|
|
168
|
+
}
|
|
169
|
+
// --- Audit --------------------------------------------------------------
|
|
170
|
+
/** Hash-chained audit trail (optionally filtered by principal), plus chain status. Requires a secret key. */
|
|
171
|
+
getAuditLog(query = {}) {
|
|
172
|
+
return this.request("GET", "/audit", { query });
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// src/widget.ts
|
|
177
|
+
var DEFAULT_TEXTS = {
|
|
178
|
+
heading: "We value your privacy",
|
|
179
|
+
subheading: "Choose what you consent to. You can withdraw anytime (DPDP Act).",
|
|
180
|
+
save: "Save consent preferences",
|
|
181
|
+
saving: "Saving\u2026",
|
|
182
|
+
saved: "Preferences saved.",
|
|
183
|
+
loading: "Loading\u2026",
|
|
184
|
+
error: "Could not save your preferences. Please try again."
|
|
185
|
+
};
|
|
186
|
+
function el(tag, attrs = {}, children = []) {
|
|
187
|
+
const node = document.createElement(tag);
|
|
188
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
189
|
+
if (k === "style" && typeof v === "string") node.style.cssText = v;
|
|
190
|
+
else if (k === "class" && typeof v === "string") node.className = v;
|
|
191
|
+
else if (k.startsWith("on") && typeof v === "function") node.addEventListener(k.slice(2), v);
|
|
192
|
+
else if (typeof v === "string") node.setAttribute(k, v);
|
|
193
|
+
}
|
|
194
|
+
for (const c of children) node.appendChild(typeof c === "string" ? document.createTextNode(c) : c);
|
|
195
|
+
return node;
|
|
196
|
+
}
|
|
197
|
+
function noticeFor(purpose, locale) {
|
|
198
|
+
const t = purpose.translations?.[locale] ?? {};
|
|
199
|
+
return {
|
|
200
|
+
name: t.name || purpose.name,
|
|
201
|
+
description: t.description || purpose.description
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
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);";
|
|
205
|
+
function mountConsentWidget(target, options) {
|
|
206
|
+
const container = typeof target === "string" ? document.querySelector(target) : target;
|
|
207
|
+
if (!container) throw new Error(`mountConsentWidget: container not found: ${String(target)}`);
|
|
208
|
+
const client = options.client ?? new DPDPStack({ apiBase: options.apiBase, apiKey: options.apiKey });
|
|
209
|
+
const texts = { ...DEFAULT_TEXTS, ...options.texts };
|
|
210
|
+
const defaultChecked = new Set(options.defaultChecked ?? []);
|
|
211
|
+
let locale = options.locale ?? "en";
|
|
212
|
+
let purposes = options.purposes ?? null;
|
|
213
|
+
function message(text, color) {
|
|
214
|
+
return el("div", { style: `margin-top:12px;font-size:13px;color:${color};` }, [text]);
|
|
215
|
+
}
|
|
216
|
+
function render() {
|
|
217
|
+
container.innerHTML = "";
|
|
218
|
+
const card = el("div", { style: CARD }, [
|
|
219
|
+
el("div", { style: "font-weight:600;font-size:15px;margin-bottom:4px;" }, [texts.heading]),
|
|
220
|
+
el("div", { style: "font-size:13px;color:#666;margin-bottom:12px;" }, [texts.subheading])
|
|
221
|
+
]);
|
|
222
|
+
if (!purposes) {
|
|
223
|
+
card.appendChild(message(texts.loading, "#666"));
|
|
224
|
+
container.appendChild(card);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const checks = {};
|
|
228
|
+
for (const p of purposes) {
|
|
229
|
+
const n = noticeFor(p, locale);
|
|
230
|
+
const box = el("input", { type: "checkbox", id: `dpdp_${p.code}` });
|
|
231
|
+
if (defaultChecked.has(p.code)) box.checked = true;
|
|
232
|
+
checks[p.code] = box;
|
|
233
|
+
card.appendChild(
|
|
234
|
+
el(
|
|
235
|
+
"label",
|
|
236
|
+
{
|
|
237
|
+
style: "display:flex;align-items:flex-start;gap:8px;margin:10px 0;font-size:14px;cursor:pointer;",
|
|
238
|
+
for: `dpdp_${p.code}`
|
|
239
|
+
},
|
|
240
|
+
[
|
|
241
|
+
box,
|
|
242
|
+
el("span", {}, [
|
|
243
|
+
el("strong", {}, [n.name]),
|
|
244
|
+
el("div", { style: "font-size:12px;color:#777;margin-top:2px;" }, [n.description])
|
|
245
|
+
])
|
|
246
|
+
]
|
|
247
|
+
)
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
const status = el("div", {});
|
|
251
|
+
const saveBtn = el(
|
|
252
|
+
"button",
|
|
253
|
+
{
|
|
254
|
+
type: "button",
|
|
255
|
+
style: "margin-top:14px;background:#4f46e5;color:#fff;border:0;border-radius:8px;padding:10px 16px;font:inherit;font-weight:600;cursor:pointer;"
|
|
256
|
+
},
|
|
257
|
+
[texts.save]
|
|
258
|
+
);
|
|
259
|
+
saveBtn.addEventListener("click", async () => {
|
|
260
|
+
const selected = purposes.filter((p) => checks[p.code]?.checked);
|
|
261
|
+
saveBtn.disabled = true;
|
|
262
|
+
saveBtn.textContent = texts.saving;
|
|
263
|
+
status.innerHTML = "";
|
|
264
|
+
try {
|
|
265
|
+
const receipts = await Promise.all(
|
|
266
|
+
selected.map(
|
|
267
|
+
(p) => client.grantConsent({ principal_ref: options.principalRef, purpose: p.code, locale })
|
|
268
|
+
)
|
|
269
|
+
);
|
|
270
|
+
status.appendChild(message(texts.saved, "#16a34a"));
|
|
271
|
+
options.onSave?.(receipts);
|
|
272
|
+
} catch (err) {
|
|
273
|
+
status.appendChild(message(texts.error, "#dc2626"));
|
|
274
|
+
options.onError?.(err);
|
|
275
|
+
} finally {
|
|
276
|
+
saveBtn.disabled = false;
|
|
277
|
+
saveBtn.textContent = texts.save;
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
card.appendChild(saveBtn);
|
|
281
|
+
card.appendChild(status);
|
|
282
|
+
container.appendChild(card);
|
|
283
|
+
}
|
|
284
|
+
async function refresh() {
|
|
285
|
+
if (!options.purposes) {
|
|
286
|
+
render();
|
|
287
|
+
try {
|
|
288
|
+
purposes = await client.listPurposes();
|
|
289
|
+
} catch (err) {
|
|
290
|
+
purposes = [];
|
|
291
|
+
options.onError?.(err);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
render();
|
|
295
|
+
}
|
|
296
|
+
void refresh();
|
|
297
|
+
return {
|
|
298
|
+
setLocale(next) {
|
|
299
|
+
locale = next;
|
|
300
|
+
render();
|
|
301
|
+
},
|
|
302
|
+
refresh,
|
|
303
|
+
destroy() {
|
|
304
|
+
container.innerHTML = "";
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// src/index.ts
|
|
310
|
+
var DPDPConsent = {
|
|
311
|
+
init(config) {
|
|
312
|
+
const { el: el2, ...rest } = config;
|
|
313
|
+
return mountConsentWidget(el2, rest);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
export { DEFAULT_API_BASE, DPDPConsent, DPDPError, DPDPStack, mountConsentWidget };
|
|
318
|
+
//# sourceMappingURL=index.js.map
|
|
319
|
+
//# sourceMappingURL=index.js.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.js","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"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dpdpstack-js-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official JavaScript/TypeScript SDK for the DPDPStack API (getdpdp.net) — DPDP Act consent, erasure, audit, DSR/breach workflows, and verifiable Certificates of Erasure.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"unpkg": "./dist/dpdpstack.global.js",
|
|
17
|
+
"jsdelivr": "./dist/dpdpstack.global.js",
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsup",
|
|
25
|
+
"dev": "tsup --watch",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"dpdp",
|
|
31
|
+
"dpdp-act",
|
|
32
|
+
"consent",
|
|
33
|
+
"privacy",
|
|
34
|
+
"india",
|
|
35
|
+
"erasure",
|
|
36
|
+
"compliance",
|
|
37
|
+
"audit",
|
|
38
|
+
"getdpdp",
|
|
39
|
+
"dpdpstack"
|
|
40
|
+
],
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"author": "DPDPStack",
|
|
43
|
+
"homepage": "https://getdpdp.net/docs",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/getdpdp/dpdpstack-js-sdk.git"
|
|
47
|
+
},
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/getdpdp/dpdpstack-js-sdk/issues"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=18"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"tsup": "^8.3.5",
|
|
56
|
+
"typescript": "^5.7.2"
|
|
57
|
+
}
|
|
58
|
+
}
|