@tantainnovative/ndpr-toolkit 3.5.1 → 3.6.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/CHANGELOG.md +125 -0
- package/README.md +29 -8
- package/dist/adapters.d.mts +137 -3
- package/dist/adapters.d.ts +137 -3
- package/dist/adapters.js +1 -2
- package/dist/adapters.mjs +1 -2
- package/dist/breach.d.mts +80 -1
- package/dist/breach.d.ts +80 -1
- package/dist/breach.js +1 -2
- package/dist/breach.mjs +1 -2
- package/dist/{chunk-PATONNTZ.mjs → chunk-3HOXQNCH.mjs} +1 -2
- package/dist/{chunk-MJGOLP5M.js → chunk-3IA3KDII.js} +1 -2
- package/dist/{chunk-M2TPT5GB.js → chunk-3JPDTXGC.js} +1 -2
- package/dist/{chunk-BKRETVJ6.js → chunk-3YTAOT5O.js} +1 -2
- package/dist/{chunk-GOU6FU6Y.js → chunk-4CVBQC66.js} +1 -2
- package/dist/{chunk-E4NCJ7RD.mjs → chunk-4G3SRVRI.mjs} +1 -2
- package/dist/{chunk-6QPRDQZF.js → chunk-4QXTB3L6.js} +1 -2
- package/dist/{chunk-XNSZ7KUH.js → chunk-5GVMKUMP.js} +1 -2
- package/dist/chunk-66NQ5CVY.mjs +1 -0
- package/dist/chunk-732C2EVN.js +1 -0
- package/dist/chunk-7BJXI2HI.mjs +1 -0
- package/dist/{chunk-C5QO3SX4.js → chunk-7IFSWCQP.js} +1 -2
- package/dist/{chunk-A4PK7JB2.js → chunk-7ZZO7GVB.js} +1 -2
- package/dist/chunk-AME4HJR4.js +1 -0
- package/dist/chunk-AOHKVFAS.mjs +322 -0
- package/dist/{chunk-HO5M7M4M.js → chunk-B46SJB5V.js} +1 -2
- package/dist/chunk-BFAX7JQA.mjs +1 -0
- package/dist/{chunk-ID2NYIVE.mjs → chunk-BNHQFZHL.mjs} +2 -3
- package/dist/{chunk-RLYTX3MM.js → chunk-BRS52EDT.js} +1 -2
- package/dist/{chunk-OPYQIJKY.js → chunk-C7IDR2IV.js} +1 -2
- package/dist/{chunk-CISJAQ6W.mjs → chunk-COD3RMTL.mjs} +1 -2
- package/dist/chunk-CR2QZTGW.js +1 -0
- package/dist/{chunk-6WIP33TW.mjs → chunk-DBZSN4WP.mjs} +1 -2
- package/dist/{chunk-74Z23WUA.mjs → chunk-EEQALYOY.mjs} +1 -2
- package/dist/chunk-EFIBHKQE.mjs +1 -0
- package/dist/{chunk-GKKAK6ES.mjs → chunk-EWVK45Z3.mjs} +1 -2
- package/dist/{chunk-QSVVAZVT.mjs → chunk-EXEXUAF6.mjs} +1 -2
- package/dist/chunk-EZCGTHQV.js +6 -0
- package/dist/{chunk-SYMQJO2W.mjs → chunk-GTYXVAJX.mjs} +2 -3
- package/dist/{chunk-6FGCGLH5.mjs → chunk-H3XJV2IR.mjs} +1 -2
- package/dist/chunk-HBLGN4SD.js +1 -0
- package/dist/{chunk-MCWV7S2G.js → chunk-HHK5LHEG.js} +1 -2
- package/dist/{chunk-XHROISIF.mjs → chunk-HWHBINVN.mjs} +1 -2
- package/dist/{chunk-3EGQWLJ6.js → chunk-I2LMQWK3.js} +2 -3
- package/dist/{chunk-NQNFS3QI.js → chunk-I3Y4LOSL.js} +4 -5
- package/dist/chunk-ITCY2Z66.mjs +4 -0
- package/dist/{chunk-Z6IIMLZU.mjs → chunk-KDAZQO3N.mjs} +1 -2
- package/dist/{chunk-J5WCPZLW.js → chunk-L2BRFMVS.js} +1 -2
- package/dist/chunk-L2VO3MEJ.js +1 -0
- package/dist/{chunk-R5FW5XUQ.mjs → chunk-LQTARVPU.mjs} +1 -2
- package/dist/{chunk-6TA2MVTU.mjs → chunk-LRRENTT5.mjs} +1 -2
- package/dist/chunk-LTPSN2SU.mjs +1 -0
- package/dist/{chunk-LIM64IV2.js → chunk-LVGT3DLT.js} +2 -3
- package/dist/{chunk-TXBZPCGF.mjs → chunk-LWIKDDSU.mjs} +1 -2
- package/dist/chunk-MPBPAEZC.mjs +1 -0
- package/dist/{chunk-Z73T6MWY.js → chunk-N3MQQUQP.js} +39 -35
- package/dist/{chunk-HGGLW5TE.js → chunk-NUWVPRNI.js} +1 -2
- package/dist/chunk-O45PKBZA.mjs +6 -0
- package/dist/chunk-O6CUBNXK.mjs +3 -0
- package/dist/{chunk-7SMLHZ4B.js → chunk-P4LNLCSF.js} +1 -2
- package/dist/{chunk-VWPGIES4.mjs → chunk-PCU6GKBE.mjs} +1 -2
- package/dist/chunk-PGI2LM6P.js +103 -0
- package/dist/{chunk-U62QYKVG.mjs → chunk-PJNKQPQP.mjs} +1 -2
- package/dist/chunk-PL4XNCQA.mjs +1 -0
- package/dist/{chunk-LSNL4XR5.js → chunk-PZRQWPWD.js} +1 -2
- package/dist/chunk-Q64735OC.js +144 -0
- package/dist/chunk-QPRYXVH2.js +1 -0
- package/dist/{chunk-MQFZHA2D.js → chunk-RFPLZDIO.js} +1 -2
- package/dist/chunk-RMQ7OLNY.mjs +144 -0
- package/dist/chunk-ROTLSZMV.js +1 -0
- package/dist/{chunk-2AW7KAZO.mjs → chunk-RPXRPGHL.mjs} +1 -2
- package/dist/chunk-RV2VMWZJ.mjs +1 -0
- package/dist/chunk-RXZFYBUJ.js +1 -0
- package/dist/chunk-RYREGZVQ.js +1 -0
- package/dist/{chunk-B4Z5MBUC.mjs → chunk-RZ6GC6WN.mjs} +1 -2
- package/dist/chunk-S6COXIZA.js +2 -0
- package/dist/chunk-SFGW37LE.mjs +1 -0
- package/dist/{chunk-3NQQSU4P.js → chunk-SJDDNB6M.js} +1 -2
- package/dist/chunk-SJRIOZ4K.mjs +1 -0
- package/dist/{chunk-H3EYBSVP.mjs → chunk-TMXK4QKK.mjs} +2 -3
- package/dist/{chunk-P42Z5CFE.js → chunk-TQZWJGJ2.js} +1 -2
- package/dist/{chunk-EPT2K355.mjs → chunk-U5RWJRGA.mjs} +1 -2
- package/dist/{chunk-R3OYAJI6.mjs → chunk-UASG46LP.mjs} +1 -2
- package/dist/{chunk-RXL6CZAI.js → chunk-UAV7V4EM.js} +1 -2
- package/dist/chunk-UI536RU2.js +3 -0
- package/dist/{chunk-7AVN424U.js → chunk-UTFBKL73.js} +1 -2
- package/dist/{chunk-I54CDQGN.js → chunk-UXUMYP4L.js} +1 -2
- package/dist/chunk-V3RYHNHN.js +1 -0
- package/dist/{chunk-P3PULLYP.mjs → chunk-V7UFP6QU.mjs} +1 -2
- package/dist/{chunk-I4M2AA3N.js → chunk-VJTQXVAF.js} +1 -2
- package/dist/{chunk-ELKB2AFZ.js → chunk-VWED6UTN.js} +1 -2
- package/dist/{chunk-LU7PKE7Y.mjs → chunk-VYAL2TGT.mjs} +1 -2
- package/dist/chunk-W47OSMT6.js +2 -0
- package/dist/chunk-W65ZWTLD.mjs +1 -0
- package/dist/chunk-W7RBGZCC.js +1 -0
- package/dist/{chunk-5HL4UBFV.js → chunk-WDDCKYWA.js} +1 -2
- package/dist/{chunk-DJGS7SSN.mjs → chunk-WTGKZX7J.mjs} +1 -2
- package/dist/{chunk-TDDAYVKK.js → chunk-WZYCBW2R.js} +1 -2
- package/dist/chunk-X3GCGC3H.mjs +103 -0
- package/dist/chunk-XJO4DH3L.mjs +2 -0
- package/dist/{chunk-LWXZMKC2.js → chunk-XO3VQYTL.js} +7 -8
- package/dist/{chunk-IZCWCE7W.mjs → chunk-XOH4WXOZ.mjs} +1 -2
- package/dist/{chunk-SHMJNRHO.mjs → chunk-XP5PL6K7.mjs} +1 -2
- package/dist/{chunk-CKGJK4D7.mjs → chunk-Y3MKMAFQ.mjs} +2 -3
- package/dist/chunk-YFBDJ4FH.js +1 -0
- package/dist/chunk-YTU4FNM2.mjs +1 -0
- package/dist/{chunk-TV4U6AIS.js → chunk-Z763UI5U.js} +1 -2
- package/dist/chunk-ZHFLBL63.mjs +2 -0
- package/dist/{chunk-RRVKUCFR.mjs → chunk-ZIZL37BG.mjs} +1 -2
- package/dist/{chunk-WWT2ZSNU.mjs → chunk-ZJYULEER.mjs} +1 -2
- package/dist/{chunk-LJNNPAFU.mjs → chunk-ZQZJNKVB.mjs} +1 -2
- package/dist/chunk-ZVOIR4QH.js +4 -0
- package/dist/consent.js +1 -2
- package/dist/consent.mjs +1 -2
- package/dist/core.d.mts +98 -19
- package/dist/core.d.ts +98 -19
- package/dist/core.js +1 -2
- package/dist/core.mjs +1 -2
- package/dist/cross-border.d.mts +36 -14
- package/dist/cross-border.d.ts +36 -14
- package/dist/cross-border.js +1 -2
- package/dist/cross-border.mjs +1 -2
- package/dist/dpia.d.mts +4 -4
- package/dist/dpia.d.ts +4 -4
- package/dist/dpia.js +1 -2
- package/dist/dpia.mjs +1 -2
- package/dist/dsr.d.mts +30 -17
- package/dist/dsr.d.ts +30 -17
- package/dist/dsr.js +1 -2
- package/dist/dsr.mjs +1 -2
- package/dist/hooks.d.mts +113 -20
- package/dist/hooks.d.ts +113 -20
- package/dist/hooks.js +1 -2
- package/dist/hooks.mjs +1 -2
- package/dist/index.d.mts +422 -26
- package/dist/index.d.ts +422 -26
- package/dist/index.js +1 -2
- package/dist/index.mjs +1 -2
- package/dist/lawful-basis.js +1 -2
- package/dist/lawful-basis.mjs +1 -2
- package/dist/policy.js +1 -2
- package/dist/policy.mjs +1 -2
- package/dist/presets-consent.d.mts +139 -0
- package/dist/presets-consent.d.ts +139 -0
- package/dist/presets-consent.js +2 -0
- package/dist/presets-consent.mjs +2 -0
- package/dist/presets-dsr.d.mts +133 -0
- package/dist/presets-dsr.d.ts +133 -0
- package/dist/presets-dsr.js +2 -0
- package/dist/presets-dsr.mjs +2 -0
- package/dist/presets-policy.d.mts +203 -0
- package/dist/presets-policy.d.ts +203 -0
- package/dist/presets-policy.js +2 -0
- package/dist/presets-policy.mjs +2 -0
- package/dist/presets.d.mts +127 -5
- package/dist/presets.d.ts +127 -5
- package/dist/presets.js +1 -2
- package/dist/presets.mjs +1 -2
- package/dist/ropa.js +1 -2
- package/dist/ropa.mjs +1 -2
- package/dist/server.d.mts +226 -22
- package/dist/server.d.ts +226 -22
- package/dist/server.js +1 -2
- package/dist/server.mjs +1 -2
- package/dist/styles.css +34 -0
- package/dist/unstyled.d.mts +3 -1
- package/dist/unstyled.d.ts +3 -1
- package/dist/unstyled.js +1 -2
- package/dist/unstyled.mjs +1 -2
- package/package.json +83 -20
- package/dist/chunk-3VQAYQR7.js +0 -7
- package/dist/chunk-6HZL2WDU.mjs +0 -2
- package/dist/chunk-75TJPK2N.mjs +0 -2
- package/dist/chunk-ABDB7LEV.mjs +0 -2
- package/dist/chunk-AYKLAEOU.mjs +0 -2
- package/dist/chunk-C4YM4UMI.js +0 -2
- package/dist/chunk-CKJAECGV.js +0 -2
- package/dist/chunk-CPK5D5FY.js +0 -132
- package/dist/chunk-CWHBCQGT.mjs +0 -2
- package/dist/chunk-E64TU6IU.js +0 -2
- package/dist/chunk-F5TXUA4O.mjs +0 -4
- package/dist/chunk-GN5C32JB.mjs +0 -2
- package/dist/chunk-GRLIPT5V.mjs +0 -132
- package/dist/chunk-I557S566.mjs +0 -15
- package/dist/chunk-JBSCER34.js +0 -2
- package/dist/chunk-JFFOPHU3.mjs +0 -318
- package/dist/chunk-KF3EFJEF.mjs +0 -3
- package/dist/chunk-ORFC66EA.js +0 -4
- package/dist/chunk-RY3PGVLZ.mjs +0 -2
- package/dist/chunk-S4GRSNB4.js +0 -2
- package/dist/chunk-SCWNM4PC.mjs +0 -2
- package/dist/chunk-SKKOMFXH.js +0 -81
- package/dist/chunk-TCN22KYP.mjs +0 -7
- package/dist/chunk-TUNQUVHU.mjs +0 -81
- package/dist/chunk-VIQUXWJC.js +0 -2
- package/dist/chunk-WTJGLNTB.js +0 -3
- package/dist/chunk-XIM7KMD6.js +0 -2
- package/dist/chunk-YPKUHSK4.js +0 -15
- package/dist/chunk-ZPKVLTSX.js +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,131 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [3.6.0](https://github.com/mr-tanta/ndpr-toolkit/compare/v3.5.5...v3.6.0) (2026-05-24)
|
|
6
|
+
|
|
7
|
+
### Features (developer feedback)
|
|
8
|
+
|
|
9
|
+
This release lands changes flagged by integrating teams using the toolkit in production. All additions are backward-compatible — 3.5.x consumers can upgrade without changes.
|
|
10
|
+
|
|
11
|
+
* **`apiAdapter` is now production-ready.** Adds `credentials` (defaults to `'same-origin'`, set `'include'` for cross-origin), dynamic `headers` (function form for runtime CSRF token lookup), `loadMethod`/`saveMethod` overrides (e.g. `PUT` for upsert APIs), `unwrap` (transform `{ data: ... }` envelopes), configurable `retry` with exponential backoff and a `shouldRetry` predicate (default: retry on network errors + 5xx, skip 4xx), and `onError`/`onSuccess` hooks for telemetry. The pre-3.6.0 `console.warn` behavior is preserved when no `onError` is configured.
|
|
12
|
+
* **`NDPRConsent` exposes a `copy` prop.** Override `title` / `description` / `acceptAll` / `rejectAll` / `customize` / `save` strings without dropping to the lower-level `<ConsentBanner>` API.
|
|
13
|
+
* **`NDPRSubjectRights` adds public-form `submitTo` mode.** Public sites can POST to their backend instead of being state-managed by an adapter. Pairs with `submitOptions` (credentials, headers) and `onSubmitError`. The state-managed `adapter` mode is unchanged.
|
|
14
|
+
* **Per-preset subpath entries** for bundle-size-sensitive consumers:
|
|
15
|
+
- `@tantainnovative/ndpr-toolkit/presets/consent` — just `NDPRConsent` (~4KB vs ~8KB for the full barrel)
|
|
16
|
+
- `@tantainnovative/ndpr-toolkit/presets/dsr` — just `NDPRSubjectRights`
|
|
17
|
+
- `@tantainnovative/ndpr-toolkit/presets/policy` — just `NDPRPrivacyPolicy`
|
|
18
|
+
The full `/presets` barrel is unchanged. These are additive narrower entries.
|
|
19
|
+
|
|
20
|
+
### Docs
|
|
21
|
+
|
|
22
|
+
* README install block now shows Bun, npm, and Yarn alongside pnpm.
|
|
23
|
+
|
|
24
|
+
### Coming next (3.6.1+)
|
|
25
|
+
|
|
26
|
+
- Recipe pages for ecommerce / newsletter / contact-form / careers / admin DSR patterns
|
|
27
|
+
- Org-specific privacy policy templates (SaaS, ecommerce, school, healthcare, procurement)
|
|
28
|
+
- Continued bundle reduction
|
|
29
|
+
|
|
30
|
+
## [3.5.5](https://github.com/mr-tanta/ndpr-toolkit/compare/v3.5.4...v3.5.5) (2026-05-24)
|
|
31
|
+
|
|
32
|
+
### Features (tests + types)
|
|
33
|
+
|
|
34
|
+
* **Tests:** 8 new tests for the 72-hour NDPC notification deadline (`useBreach.getBreachesRequiringNotification`) — covers 1h / 24h / 48h / 71.5h / expired / sort-by-urgency / already-notified cases. Test suite now at 1134 passing (up from 1126).
|
|
35
|
+
* **CI:** Tests now run with `--coverage` and the report uploads as a workflow artifact on each run. Thresholds re-set as a ratchet (45% branches, 50% functions, 65% lines/statements — at or just below current to catch regressions, raised in follow-up patches).
|
|
36
|
+
* **types:** `DPIAAnswerMap` and `DPIAAnswerValue` exported from `/` and `/hooks`. `DPIAProvider` and `<NDPRDPIA>` props now use these instead of `Record<string, any>`, restoring callsite type-safety on `onComplete`, `initialAnswers`, and `adapter`.
|
|
37
|
+
|
|
38
|
+
### Bug Fixes (API contract)
|
|
39
|
+
|
|
40
|
+
* **useDSR:** `submitRequest` previously declared `Omit<DSRRequest, 'id' | 'status' | 'submittedAt' | 'updatedAt' | 'estimatedCompletionDate'>` — but `submittedAt` and `estimatedCompletionDate` don't exist on `DSRRequest`. Corrected to `Omit<DSRRequest, 'id' | 'status' | 'createdAt' | 'updatedAt' | 'dueDate'>` so the type contract matches the implementation.
|
|
41
|
+
* **useDSR:** `getRequestsByStatus` now accepts `DSRStatus | RequestStatus` instead of only the deprecated `RequestStatus`. Callers using the modern `DSRStatus` literals (e.g. `'awaitingVerification'`) no longer get a type error.
|
|
42
|
+
|
|
43
|
+
## [3.5.4](https://github.com/mr-tanta/ndpr-toolkit/compare/v3.5.3...v3.5.4) (2026-05-23)
|
|
44
|
+
|
|
45
|
+
### Features (accessibility)
|
|
46
|
+
|
|
47
|
+
* **useFocusTrap:** New shared hook that captures `document.activeElement` on activation, traps Tab cycling inside the container, optionally handles Escape, and restores focus on deactivation (WCAG 2.4.3). Exported from `/`, `/hooks`. Drops a previously-missing piece — closing `<ConsentBanner>` now correctly returns focus to whatever triggered it.
|
|
48
|
+
* **prefers-reduced-motion:** Stylesheet now neutralises all toolkit animations (slide-in, fade-in, scale-in) and transitions when the user sets `prefers-reduced-motion: reduce` at the OS level (WCAG 2.3.3). Applies to banners, modals, dashboards, and policy previews.
|
|
49
|
+
|
|
50
|
+
### Bug Fixes (accessibility)
|
|
51
|
+
|
|
52
|
+
* **ConsentBanner:** Internal focus trap now restores focus on close (was previously leaving focus at `<body>`). Replaced the duplicated trap implementation with the new shared `useFocusTrap` hook.
|
|
53
|
+
* **BreachReportForm:** Icon-only "remove attachment" button now has an accessible label (`aria-label="Remove attachment {filename}"`) and a 44×44 px touch target. SVG marked `aria-hidden`. WCAG 4.1.2 / 2.5.5.
|
|
54
|
+
* **BreachReportForm:** `dataTypes` fieldset is now properly wired to its error message via `aria-invalid` + `aria-describedby="dataTypes-error"`. Required-asterisk now announced via `<span className="sr-only">(required)</span>` (was visual-only). WCAG 1.3.1 / 3.3.2.
|
|
55
|
+
|
|
56
|
+
## [3.5.3](https://github.com/mr-tanta/ndpr-toolkit/compare/v3.5.2...v3.5.3) (2026-05-23)
|
|
57
|
+
|
|
58
|
+
### Bug Fixes (developer experience)
|
|
59
|
+
|
|
60
|
+
* **dsr:** `<DSRRequestForm>` now throws a clear `[ndpr-toolkit] <DSRRequestForm requestTypes={...}> requires an array of RequestType[]` error when the required prop is missing — previously crashed deep in a minified chunk with `Cannot read properties of undefined (reading 'find')`. Points users at the `<NDPRSubjectRights>` preset for defaults.
|
|
61
|
+
* **dsr:** Default form description updated from "NDPA Part IV, Sections 29-36" to "NDPA Part VI" (matching the 3.5.2 citation fixes).
|
|
62
|
+
|
|
63
|
+
### Features
|
|
64
|
+
|
|
65
|
+
* **package.json:** `sideEffects: ["*.css"]` is now declared on both root and workspace manifests — bundlers can reliably tree-shake unused subpaths.
|
|
66
|
+
* **package.json:** `engines.node: ">=18.0.0"` declared. Quiets installer warnings on Node 16 setups and matches React 18/19 requirements.
|
|
67
|
+
* **package.json:** `funding` field added (GitHub Sponsors URL).
|
|
68
|
+
* **keywords:** Added high-intent search terms developers actually type — `ndpa-2023`, `nigeria-compliance`, `data-privacy`, `compliance-tools`, `nitda`, `gdpr`, `gdpr-nigeria`, `africa`, `cookie-banner`, `nextjs`. Improves npm search ranking for queries like "react NDPA" and "cookie consent Nigeria".
|
|
69
|
+
* **root exports:** `useComplianceScore` and `useAdaptivePolicyWizard` now re-exported from the root entry. Previously only on `/hooks` — caused silent discoverability gap when users autocompleted from the bare `@tantainnovative/ndpr-toolkit`.
|
|
70
|
+
|
|
71
|
+
## [3.5.2](https://github.com/mr-tanta/ndpr-toolkit/compare/v3.5.1...v3.5.2) (2026-05-23)
|
|
72
|
+
|
|
73
|
+
### ⚠️ Legal correctness — please re-review generated artifacts
|
|
74
|
+
|
|
75
|
+
3.5.2 corrects NDPA 2023 section citations across the toolkit against the gazetted text of the Act. If you embedded any pre-3.5.2 output (privacy policy text, DPIA labels, breach report templates) in a regulatory submission, please regenerate or manually update the citations. See the migration table below.
|
|
76
|
+
|
|
77
|
+
### Bug Fixes (legal correctness)
|
|
78
|
+
|
|
79
|
+
* **dsr:** Right citations now follow NDPA Part VI as gazetted — access = Section 34(1)(a)–(b), rectification = Section 34(1)(c), erasure = Section 34(1)(d) + 34(2), restriction = Section 34(1)(e), withdraw consent = Section 35, object = Section 36, automated decisions = Section 37, portability = Section 38. Previously cited Sections 30–36 in a way that did not match the Act.
|
|
80
|
+
* **dpia:** DPIA + NDPC prior-consultation citations corrected from Section 38/39 to **Section 28** (which covers both per Section 28(1) and 28(2)).
|
|
81
|
+
* **cross-border:** Transfer-mechanism citations now follow NDPA Part VIII — adequacy decision = Section 42, SCCs / BCRs = Section 41(1)(a), NDPC-approved instruments = Section 42(5), and all derogations = Section 43(1)(a)–(f). Previously cited Sections 41–45 in a way that did not match the Act.
|
|
82
|
+
* **lawful-basis, breach:** Sensitive personal data citation corrected from Section 27 to **Section 30**.
|
|
83
|
+
* **policy:** Privacy notice provisions now cite Section 27 (provision of information) rather than Section 29 (controller obligations).
|
|
84
|
+
* **country-adequacy:** Removed fabricated NDPC adequacy claims. NDPC has not (as of publication) published a Section 42 adequacy list. The 22 entries previously labelled `recognizedBy: 'NDPC'` are now `recognizedBy: 'self-assessment'` with a clear disclaimer in the module header.
|
|
85
|
+
* **dsr (types):** DSRType union extends with `'withdraw_consent'` to reflect the explicit Section 35 right.
|
|
86
|
+
|
|
87
|
+
### Features
|
|
88
|
+
|
|
89
|
+
* **legal-notice:** New `LEGAL_DISCLAIMER_SHORT`, `LEGAL_DISCLAIMER_LONG`, and `legalDisclaimerBlock()` exports (from `/`, `/core`, `/server`), plus a `<LegalNotice>` component. Injected into all generated artifacts (HTML / Markdown / DOCX / PDF policy exports, regulatory breach report output, DPIA report footer, breach form preview).
|
|
90
|
+
* **breach (NDPC report content):** `BreachReport` / `BreachFormSubmission` extended with the fields the NDPC actually requires under Section 40(2)–(3) — `approximateRecordCount`, `dataSubjectCategories`, `likelyConsequences`, `mitigationMeasures`, `dpoContact`, `isPhasedReport`, `supplementsReportId`. `BreachReportForm` now collects them; `RegulatoryReportGenerator` includes them in the printed NDPC report.
|
|
91
|
+
* **core:** `StorageAdapter` is now re-exported from `/core` for ergonomics (the concrete adapters still live in `/adapters`).
|
|
92
|
+
|
|
93
|
+
### Build / packaging
|
|
94
|
+
|
|
95
|
+
* **build:** Sourcemaps are no longer published. Pass `NDPR_SOURCEMAPS=1` to emit them for local debugging. Cuts the published tarball by ~3 MB.
|
|
96
|
+
* **build:** `dist/` is no longer tracked in git. CI rebuilds from source on every publish.
|
|
97
|
+
* **packaging:** Workspace `packages/ndpr-toolkit/package.json` exports now match the root manifest (drops accidental `-entry` suffixes that pointed at non-existent files). Workspace tsup config also aligned.
|
|
98
|
+
* **packaging:** README is now synced from the repo root via `prepublishOnly`, with absolute image URLs so npm renders the hero correctly.
|
|
99
|
+
|
|
100
|
+
### Release pipeline
|
|
101
|
+
|
|
102
|
+
* **publish workflow:** Now publishes with `--provenance` (sigstore attestation) and checks out the tagged commit (not main HEAD).
|
|
103
|
+
* **repo:** `main` is now branch-protected — PRs + 1 approval + green CI required, no force-push, no deletions.
|
|
104
|
+
|
|
105
|
+
### Docs
|
|
106
|
+
|
|
107
|
+
* **README:** Fixed broken code examples — `Consent.Provider` uses `onChange` (not `onSave`); `getComplianceScore` example now includes all 8 required module inputs; `StorageAdapter` re-export documented.
|
|
108
|
+
|
|
109
|
+
### Migration table — old → new NDPA citation
|
|
110
|
+
|
|
111
|
+
| Concept | Old (pre-3.5.2) | Correct (3.5.2+) |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| Right of access | Section 30 | Section 34(1)(a)–(b) |
|
|
114
|
+
| Right to rectification | Section 31 | Section 34(1)(c) |
|
|
115
|
+
| Right to erasure | Section 32 | Section 34(1)(d), Section 34(2) |
|
|
116
|
+
| Right to restrict processing | Section 33 | Section 34(1)(e) |
|
|
117
|
+
| Right to portability | Section 34 | Section 38 |
|
|
118
|
+
| Right to object | Section 35 | Section 36 |
|
|
119
|
+
| Right to automated-decision opt-out | Section 36 | Section 37 |
|
|
120
|
+
| Right to withdraw consent | (missing) | Section 35 |
|
|
121
|
+
| DPIA + prior consultation | Section 38 / 39 | Section 28 |
|
|
122
|
+
| Sensitive personal data | Section 27 | Section 30 |
|
|
123
|
+
| Cross-border adequacy | Section 41 | Section 42 |
|
|
124
|
+
| Cross-border SCCs / BCRs | Section 42 / 43 | Section 41(1)(a) |
|
|
125
|
+
| Cross-border derogations | Section 45(a)–(e) | Section 43(1)(a)–(f) |
|
|
126
|
+
| Right to complain to NDPC | (missing) | Section 46(1) |
|
|
127
|
+
|
|
128
|
+
> The toolkit produces guidance artifacts only — not legal advice. Verify with your DPO or qualified Nigerian privacy counsel before relying on any output for a regulatory submission.
|
|
129
|
+
|
|
5
130
|
## [3.5.1](https://github.com/mr-tanta/ndpr-toolkit/compare/v3.5.0...v3.5.1) (2026-05-03)
|
|
6
131
|
|
|
7
132
|
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ v3 ships **zero-config presets**, **pluggable storage adapters**, **compound com
|
|
|
16
16
|
> **What's new in 3.4.0:** components now ship styled defaults via a real stylesheet — Tailwind is no longer required. Add `import "@tantainnovative/ndpr-toolkit/styles";` once in your app entry. Plus a new `/server` subpath for RSC-safe pure-logic imports (validators, generators, scoring) with zero React in the import graph. Backward-compatible at the component API level. Full notes on the [release page](https://github.com/mr-tanta/ndpr-toolkit/releases/tag/v3.4.0).
|
|
17
17
|
|
|
18
18
|
<p align="center">
|
|
19
|
-
<img src="public/screenshots/hero.png" alt="NDPA Toolkit — NDPA Compliance Made Beautiful" width="800" />
|
|
19
|
+
<img src="https://raw.githubusercontent.com/mr-tanta/ndpr-toolkit/v3.5.2/public/screenshots/hero.png" alt="NDPA Toolkit — NDPA Compliance Made Beautiful" width="800" />
|
|
20
20
|
</p>
|
|
21
21
|
|
|
22
22
|
---
|
|
@@ -65,7 +65,7 @@ import { apiAdapter } from '@tantainnovative/ndpr-toolkit/adapters';
|
|
|
65
65
|
That's it. NDPA-compliant consent with server-side persistence in under 20 lines.
|
|
66
66
|
|
|
67
67
|
<p align="center">
|
|
68
|
-
<img src="public/screenshots/consent-demo.png" alt="Consent Management Demo — interactive consent banner with state inspector" width="800" />
|
|
68
|
+
<img src="https://raw.githubusercontent.com/mr-tanta/ndpr-toolkit/v3.5.2/public/screenshots/consent-demo.png" alt="Consent Management Demo — interactive consent banner with state inspector" width="800" />
|
|
69
69
|
<br />
|
|
70
70
|
<em>Interactive consent demo with configurable position, theme, storage, and real-time state inspector</em>
|
|
71
71
|
</p>
|
|
@@ -74,14 +74,26 @@ That's it. NDPA-compliant consent with server-side persistence in under 20 lines
|
|
|
74
74
|
|
|
75
75
|
## Install
|
|
76
76
|
|
|
77
|
+
Pick your package manager:
|
|
78
|
+
|
|
77
79
|
```bash
|
|
80
|
+
# pnpm
|
|
78
81
|
pnpm add @tantainnovative/ndpr-toolkit
|
|
82
|
+
|
|
83
|
+
# Bun
|
|
84
|
+
bun add @tantainnovative/ndpr-toolkit
|
|
85
|
+
|
|
86
|
+
# npm
|
|
87
|
+
npm install @tantainnovative/ndpr-toolkit
|
|
88
|
+
|
|
89
|
+
# Yarn
|
|
90
|
+
yarn add @tantainnovative/ndpr-toolkit
|
|
79
91
|
```
|
|
80
92
|
|
|
81
93
|
Add the stylesheet import once in your app entry so components render with default styles:
|
|
82
94
|
|
|
83
95
|
```ts
|
|
84
|
-
// app/layout.tsx (Next.js App Router) or src/main.tsx (Vite/CRA)
|
|
96
|
+
// app/layout.tsx (Next.js App Router) or src/main.tsx (Vite/CRA/Bun)
|
|
85
97
|
import "@tantainnovative/ndpr-toolkit/styles";
|
|
86
98
|
```
|
|
87
99
|
|
|
@@ -90,7 +102,11 @@ The stylesheet is opinionated but token-driven — override any `--ndpr-*` CSS c
|
|
|
90
102
|
Install UI peer dependencies (only needed if you use the higher-level Radix-based components from `/presets`):
|
|
91
103
|
|
|
92
104
|
```bash
|
|
105
|
+
# pnpm
|
|
93
106
|
pnpm add @radix-ui/react-switch @radix-ui/react-tabs @radix-ui/react-label @radix-ui/react-slot lucide-react tailwind-merge clsx class-variance-authority
|
|
107
|
+
|
|
108
|
+
# Bun
|
|
109
|
+
bun add @radix-ui/react-switch @radix-ui/react-tabs @radix-ui/react-label @radix-ui/react-slot lucide-react tailwind-merge clsx class-variance-authority
|
|
94
110
|
```
|
|
95
111
|
|
|
96
112
|
Or scaffold instantly with the CLI:
|
|
@@ -127,7 +143,7 @@ Full control over layout without rebuilding logic.
|
|
|
127
143
|
```tsx
|
|
128
144
|
import { Consent } from '@tantainnovative/ndpr-toolkit/consent';
|
|
129
145
|
|
|
130
|
-
<Consent.Provider options={options}
|
|
146
|
+
<Consent.Provider options={options} onChange={handleSave}>
|
|
131
147
|
<div className="my-layout">
|
|
132
148
|
<Consent.OptionList />
|
|
133
149
|
<div className="flex gap-2">
|
|
@@ -249,7 +265,12 @@ const report = getComplianceScore({
|
|
|
249
265
|
supportsObjection: false,
|
|
250
266
|
responseTimelineDays: 30,
|
|
251
267
|
},
|
|
252
|
-
|
|
268
|
+
dpia: { conductedForHighRisk: true, documentedRisks: true, mitigationMeasures: true },
|
|
269
|
+
breach: { hasNotificationProcess: true, notifiesWithin72Hours: true, hasRiskAssessment: true, hasRecordKeeping: true },
|
|
270
|
+
policy: { hasPrivacyPolicy: true, isPubliclyAccessible: true, lastUpdated: '2026-01-01', coversAllSections: true },
|
|
271
|
+
lawfulBasis: { documentedForAllProcessing: true, hasLegitimateInterestAssessment: false },
|
|
272
|
+
crossBorder: { hasTransferMechanisms: true, adequacyAssessed: true, ndpcApprovalObtained: false },
|
|
273
|
+
ropa: { maintained: true, includesAllProcessing: true, lastReviewed: '2026-01-01' },
|
|
253
274
|
});
|
|
254
275
|
|
|
255
276
|
console.log(report.score); // e.g. 74
|
|
@@ -306,13 +327,13 @@ Every module has an interactive demo. No signup, no setup — try them instantly
|
|
|
306
327
|
|
|
307
328
|
<p align="center">
|
|
308
329
|
<a href="https://ndprtoolkit.com.ng/ndpr-demos">
|
|
309
|
-
<img src="public/screenshots/demos-overview.png" alt="8 interactive live demos — zero setup required" width="800" />
|
|
330
|
+
<img src="https://raw.githubusercontent.com/mr-tanta/ndpr-toolkit/v3.5.2/public/screenshots/demos-overview.png" alt="8 interactive live demos — zero setup required" width="800" />
|
|
310
331
|
</a>
|
|
311
332
|
</p>
|
|
312
333
|
|
|
313
334
|
<p align="center">
|
|
314
|
-
<img src="public/screenshots/dsr-demo.png" alt="Data Subject Rights — 8 rights with request tracking" width="400" />
|
|
315
|
-
<img src="public/screenshots/breach-demo.png" alt="Breach Notification — 72-hour countdown with step-by-step workflow" width="400" />
|
|
335
|
+
<img src="https://raw.githubusercontent.com/mr-tanta/ndpr-toolkit/v3.5.2/public/screenshots/dsr-demo.png" alt="Data Subject Rights — 8 rights with request tracking" width="400" />
|
|
336
|
+
<img src="https://raw.githubusercontent.com/mr-tanta/ndpr-toolkit/v3.5.2/public/screenshots/breach-demo.png" alt="Breach Notification — 72-hour countdown with step-by-step workflow" width="400" />
|
|
316
337
|
</p>
|
|
317
338
|
|
|
318
339
|
<p align="center">
|
package/dist/adapters.d.mts
CHANGED
|
@@ -1,7 +1,141 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Production-ready API storage adapter.
|
|
3
|
+
*
|
|
4
|
+
* Backward-compatible with the 3.5.x signature — `apiAdapter('/api/x')`
|
|
5
|
+
* still works exactly as before. New options are all opt-in.
|
|
6
|
+
*
|
|
7
|
+
* @example basic
|
|
8
|
+
* const adapter = apiAdapter<ConsentSettings>('/api/consent');
|
|
9
|
+
*
|
|
10
|
+
* @example with credentials and CSRF
|
|
11
|
+
* const adapter = apiAdapter<ConsentSettings>('/api/consent', {
|
|
12
|
+
* credentials: 'include',
|
|
13
|
+
* headers: () => ({
|
|
14
|
+
* 'X-CSRF-Token': document.querySelector<HTMLMetaElement>(
|
|
15
|
+
* 'meta[name="csrf-token"]'
|
|
16
|
+
* )?.content ?? '',
|
|
17
|
+
* }),
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* @example with retry + telemetry
|
|
21
|
+
* const adapter = apiAdapter<ConsentSettings>('/api/consent', {
|
|
22
|
+
* retry: { attempts: 2, baseDelayMs: 300 },
|
|
23
|
+
* onError: (ctx) => Sentry.captureException(ctx.error, { extra: ctx }),
|
|
24
|
+
* onSuccess: (ctx) => analytics.track('consent_saved', { method: ctx.method }),
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* @example with response unwrap
|
|
28
|
+
* const adapter = apiAdapter<ConsentSettings>('/api/consent', {
|
|
29
|
+
* // API returns { data: ConsentSettings, ok: true }
|
|
30
|
+
* unwrap: (raw) => (raw as { data: ConsentSettings }).data,
|
|
31
|
+
* });
|
|
32
|
+
*/
|
|
33
|
+
export declare function apiAdapter<T = unknown>(endpoint: string, options?: ApiAdapterOptions<T>): StorageAdapter<T>;
|
|
2
34
|
|
|
3
|
-
|
|
4
|
-
|
|
35
|
+
declare interface ApiAdapterErrorContext<T = unknown> {
|
|
36
|
+
/** Which adapter operation triggered this — `load`, `save`, or `remove`. */
|
|
37
|
+
method: ApiAdapterMethod;
|
|
38
|
+
/** The endpoint URL that failed. */
|
|
39
|
+
endpoint: string;
|
|
40
|
+
/** Underlying error (for network failures / parse errors). */
|
|
41
|
+
error?: unknown;
|
|
42
|
+
/** Response object, if a response was received. */
|
|
43
|
+
response?: Response;
|
|
44
|
+
/** HTTP status code, if available. */
|
|
45
|
+
status?: number;
|
|
46
|
+
/** For `save`, the payload that failed to send. */
|
|
47
|
+
payload?: T;
|
|
48
|
+
/** Which retry attempt this is (0 = first try). Capped at `retry.attempts`. */
|
|
49
|
+
attempt: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
declare type ApiAdapterMethod = 'load' | 'save' | 'remove';
|
|
53
|
+
|
|
54
|
+
export declare interface ApiAdapterOptions<T = unknown> {
|
|
55
|
+
/**
|
|
56
|
+
* Extra HTTP headers to send with every request. Useful for `Authorization`,
|
|
57
|
+
* `X-CSRF-Token`, `X-Requested-With`, etc.
|
|
58
|
+
*
|
|
59
|
+
* Can also be a function that returns headers, which lets you read a CSRF
|
|
60
|
+
* token from the DOM/cookie at request time rather than at adapter
|
|
61
|
+
* construction time.
|
|
62
|
+
*/
|
|
63
|
+
headers?: Record<string, string> | (() => Record<string, string>);
|
|
64
|
+
/**
|
|
65
|
+
* Forwarded to fetch's `credentials` option. Defaults to `'same-origin'`
|
|
66
|
+
* (the browser default). Set to `'include'` for cross-origin endpoints
|
|
67
|
+
* that need cookies / auth.
|
|
68
|
+
*/
|
|
69
|
+
credentials?: RequestCredentials;
|
|
70
|
+
/**
|
|
71
|
+
* HTTP method override for the load operation. Defaults to `'GET'`.
|
|
72
|
+
*/
|
|
73
|
+
loadMethod?: 'GET' | 'POST';
|
|
74
|
+
/**
|
|
75
|
+
* HTTP method override for the save operation. Defaults to `'POST'`. Some
|
|
76
|
+
* REST APIs prefer `'PUT'` for upsert semantics.
|
|
77
|
+
*/
|
|
78
|
+
saveMethod?: 'POST' | 'PUT' | 'PATCH';
|
|
79
|
+
/**
|
|
80
|
+
* Transform the raw JSON response into the expected `T`. Useful for APIs
|
|
81
|
+
* that wrap responses in `{ data: ... }` or similar envelopes. Called
|
|
82
|
+
* after `res.json()`. If omitted, the parsed JSON is used as-is.
|
|
83
|
+
*/
|
|
84
|
+
unwrap?: (raw: unknown) => T | null;
|
|
85
|
+
/**
|
|
86
|
+
* Retry policy for failed requests. Defaults to no retries (preserves the
|
|
87
|
+
* pre-3.6.0 behaviour). When configured, applies to all three operations.
|
|
88
|
+
*/
|
|
89
|
+
retry?: ApiAdapterRetryConfig;
|
|
90
|
+
/**
|
|
91
|
+
* Called when a request fails (after all retries exhausted). The adapter
|
|
92
|
+
* still returns a graceful null/void result so the consuming hook
|
|
93
|
+
* doesn't crash — this hook is for telemetry, toasts, or audit logging.
|
|
94
|
+
*/
|
|
95
|
+
onError?: (ctx: ApiAdapterErrorContext<T>) => void;
|
|
96
|
+
/**
|
|
97
|
+
* Called when a request succeeds. Useful for cache invalidation,
|
|
98
|
+
* analytics, or syncing other state.
|
|
99
|
+
*/
|
|
100
|
+
onSuccess?: (ctx: ApiAdapterSuccessContext<T>) => void;
|
|
101
|
+
/**
|
|
102
|
+
* Per-request fetch options to merge into every request. Use this for
|
|
103
|
+
* things `fetch` itself supports that aren't directly modelled above —
|
|
104
|
+
* `signal`, `mode`, `cache`, `redirect`, etc.
|
|
105
|
+
*/
|
|
106
|
+
fetchInit?: Omit<RequestInit, 'method' | 'headers' | 'body' | 'credentials'>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
declare interface ApiAdapterRetryConfig {
|
|
110
|
+
/**
|
|
111
|
+
* Number of additional attempts after the initial request. Defaults to 0
|
|
112
|
+
* (no retries). e.g. `attempts: 2` means up to 3 total requests.
|
|
113
|
+
*/
|
|
114
|
+
attempts?: number;
|
|
115
|
+
/**
|
|
116
|
+
* Base delay in ms between attempts. Defaults to 250ms. The actual delay
|
|
117
|
+
* uses exponential backoff: `baseDelayMs * 2^attempt`.
|
|
118
|
+
*/
|
|
119
|
+
baseDelayMs?: number;
|
|
120
|
+
/**
|
|
121
|
+
* Predicate that decides whether to retry given the failure context. By
|
|
122
|
+
* default we retry on network errors and 5xx responses, but not on 4xx
|
|
123
|
+
* (those are client errors that won't fix themselves).
|
|
124
|
+
*/
|
|
125
|
+
shouldRetry?: (ctx: ApiAdapterErrorContext<unknown>) => boolean;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
declare interface ApiAdapterSuccessContext<T = unknown> {
|
|
129
|
+
/** Which adapter operation succeeded — `load`, `save`, or `remove`. */
|
|
130
|
+
method: ApiAdapterMethod;
|
|
131
|
+
/** The endpoint URL. */
|
|
132
|
+
endpoint: string;
|
|
133
|
+
/** Response object. */
|
|
134
|
+
response: Response;
|
|
135
|
+
/** For `load` operations, the parsed (and optionally unwrapped) data. */
|
|
136
|
+
data?: T;
|
|
137
|
+
/** For `save` operations, the payload that was sent. */
|
|
138
|
+
payload?: T;
|
|
5
139
|
}
|
|
6
140
|
|
|
7
141
|
export declare function composeAdapters<T = unknown>(primary: StorageAdapter<T>, ...secondaries: StorageAdapter<T>[]): StorageAdapter<T>;
|
package/dist/adapters.d.ts
CHANGED
|
@@ -1,7 +1,141 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Production-ready API storage adapter.
|
|
3
|
+
*
|
|
4
|
+
* Backward-compatible with the 3.5.x signature — `apiAdapter('/api/x')`
|
|
5
|
+
* still works exactly as before. New options are all opt-in.
|
|
6
|
+
*
|
|
7
|
+
* @example basic
|
|
8
|
+
* const adapter = apiAdapter<ConsentSettings>('/api/consent');
|
|
9
|
+
*
|
|
10
|
+
* @example with credentials and CSRF
|
|
11
|
+
* const adapter = apiAdapter<ConsentSettings>('/api/consent', {
|
|
12
|
+
* credentials: 'include',
|
|
13
|
+
* headers: () => ({
|
|
14
|
+
* 'X-CSRF-Token': document.querySelector<HTMLMetaElement>(
|
|
15
|
+
* 'meta[name="csrf-token"]'
|
|
16
|
+
* )?.content ?? '',
|
|
17
|
+
* }),
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* @example with retry + telemetry
|
|
21
|
+
* const adapter = apiAdapter<ConsentSettings>('/api/consent', {
|
|
22
|
+
* retry: { attempts: 2, baseDelayMs: 300 },
|
|
23
|
+
* onError: (ctx) => Sentry.captureException(ctx.error, { extra: ctx }),
|
|
24
|
+
* onSuccess: (ctx) => analytics.track('consent_saved', { method: ctx.method }),
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* @example with response unwrap
|
|
28
|
+
* const adapter = apiAdapter<ConsentSettings>('/api/consent', {
|
|
29
|
+
* // API returns { data: ConsentSettings, ok: true }
|
|
30
|
+
* unwrap: (raw) => (raw as { data: ConsentSettings }).data,
|
|
31
|
+
* });
|
|
32
|
+
*/
|
|
33
|
+
export declare function apiAdapter<T = unknown>(endpoint: string, options?: ApiAdapterOptions<T>): StorageAdapter<T>;
|
|
2
34
|
|
|
3
|
-
|
|
4
|
-
|
|
35
|
+
declare interface ApiAdapterErrorContext<T = unknown> {
|
|
36
|
+
/** Which adapter operation triggered this — `load`, `save`, or `remove`. */
|
|
37
|
+
method: ApiAdapterMethod;
|
|
38
|
+
/** The endpoint URL that failed. */
|
|
39
|
+
endpoint: string;
|
|
40
|
+
/** Underlying error (for network failures / parse errors). */
|
|
41
|
+
error?: unknown;
|
|
42
|
+
/** Response object, if a response was received. */
|
|
43
|
+
response?: Response;
|
|
44
|
+
/** HTTP status code, if available. */
|
|
45
|
+
status?: number;
|
|
46
|
+
/** For `save`, the payload that failed to send. */
|
|
47
|
+
payload?: T;
|
|
48
|
+
/** Which retry attempt this is (0 = first try). Capped at `retry.attempts`. */
|
|
49
|
+
attempt: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
declare type ApiAdapterMethod = 'load' | 'save' | 'remove';
|
|
53
|
+
|
|
54
|
+
export declare interface ApiAdapterOptions<T = unknown> {
|
|
55
|
+
/**
|
|
56
|
+
* Extra HTTP headers to send with every request. Useful for `Authorization`,
|
|
57
|
+
* `X-CSRF-Token`, `X-Requested-With`, etc.
|
|
58
|
+
*
|
|
59
|
+
* Can also be a function that returns headers, which lets you read a CSRF
|
|
60
|
+
* token from the DOM/cookie at request time rather than at adapter
|
|
61
|
+
* construction time.
|
|
62
|
+
*/
|
|
63
|
+
headers?: Record<string, string> | (() => Record<string, string>);
|
|
64
|
+
/**
|
|
65
|
+
* Forwarded to fetch's `credentials` option. Defaults to `'same-origin'`
|
|
66
|
+
* (the browser default). Set to `'include'` for cross-origin endpoints
|
|
67
|
+
* that need cookies / auth.
|
|
68
|
+
*/
|
|
69
|
+
credentials?: RequestCredentials;
|
|
70
|
+
/**
|
|
71
|
+
* HTTP method override for the load operation. Defaults to `'GET'`.
|
|
72
|
+
*/
|
|
73
|
+
loadMethod?: 'GET' | 'POST';
|
|
74
|
+
/**
|
|
75
|
+
* HTTP method override for the save operation. Defaults to `'POST'`. Some
|
|
76
|
+
* REST APIs prefer `'PUT'` for upsert semantics.
|
|
77
|
+
*/
|
|
78
|
+
saveMethod?: 'POST' | 'PUT' | 'PATCH';
|
|
79
|
+
/**
|
|
80
|
+
* Transform the raw JSON response into the expected `T`. Useful for APIs
|
|
81
|
+
* that wrap responses in `{ data: ... }` or similar envelopes. Called
|
|
82
|
+
* after `res.json()`. If omitted, the parsed JSON is used as-is.
|
|
83
|
+
*/
|
|
84
|
+
unwrap?: (raw: unknown) => T | null;
|
|
85
|
+
/**
|
|
86
|
+
* Retry policy for failed requests. Defaults to no retries (preserves the
|
|
87
|
+
* pre-3.6.0 behaviour). When configured, applies to all three operations.
|
|
88
|
+
*/
|
|
89
|
+
retry?: ApiAdapterRetryConfig;
|
|
90
|
+
/**
|
|
91
|
+
* Called when a request fails (after all retries exhausted). The adapter
|
|
92
|
+
* still returns a graceful null/void result so the consuming hook
|
|
93
|
+
* doesn't crash — this hook is for telemetry, toasts, or audit logging.
|
|
94
|
+
*/
|
|
95
|
+
onError?: (ctx: ApiAdapterErrorContext<T>) => void;
|
|
96
|
+
/**
|
|
97
|
+
* Called when a request succeeds. Useful for cache invalidation,
|
|
98
|
+
* analytics, or syncing other state.
|
|
99
|
+
*/
|
|
100
|
+
onSuccess?: (ctx: ApiAdapterSuccessContext<T>) => void;
|
|
101
|
+
/**
|
|
102
|
+
* Per-request fetch options to merge into every request. Use this for
|
|
103
|
+
* things `fetch` itself supports that aren't directly modelled above —
|
|
104
|
+
* `signal`, `mode`, `cache`, `redirect`, etc.
|
|
105
|
+
*/
|
|
106
|
+
fetchInit?: Omit<RequestInit, 'method' | 'headers' | 'body' | 'credentials'>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
declare interface ApiAdapterRetryConfig {
|
|
110
|
+
/**
|
|
111
|
+
* Number of additional attempts after the initial request. Defaults to 0
|
|
112
|
+
* (no retries). e.g. `attempts: 2` means up to 3 total requests.
|
|
113
|
+
*/
|
|
114
|
+
attempts?: number;
|
|
115
|
+
/**
|
|
116
|
+
* Base delay in ms between attempts. Defaults to 250ms. The actual delay
|
|
117
|
+
* uses exponential backoff: `baseDelayMs * 2^attempt`.
|
|
118
|
+
*/
|
|
119
|
+
baseDelayMs?: number;
|
|
120
|
+
/**
|
|
121
|
+
* Predicate that decides whether to retry given the failure context. By
|
|
122
|
+
* default we retry on network errors and 5xx responses, but not on 4xx
|
|
123
|
+
* (those are client errors that won't fix themselves).
|
|
124
|
+
*/
|
|
125
|
+
shouldRetry?: (ctx: ApiAdapterErrorContext<unknown>) => boolean;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
declare interface ApiAdapterSuccessContext<T = unknown> {
|
|
129
|
+
/** Which adapter operation succeeded — `load`, `save`, or `remove`. */
|
|
130
|
+
method: ApiAdapterMethod;
|
|
131
|
+
/** The endpoint URL. */
|
|
132
|
+
endpoint: string;
|
|
133
|
+
/** Response object. */
|
|
134
|
+
response: Response;
|
|
135
|
+
/** For `load` operations, the parsed (and optionally unwrapped) data. */
|
|
136
|
+
data?: T;
|
|
137
|
+
/** For `save` operations, the payload that was sent. */
|
|
138
|
+
payload?: T;
|
|
5
139
|
}
|
|
6
140
|
|
|
7
141
|
export declare function composeAdapters<T = unknown>(primary: StorageAdapter<T>, ...secondaries: StorageAdapter<T>[]): StorageAdapter<T>;
|
package/dist/adapters.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
'use strict';var
|
|
2
|
-
//# sourceMappingURL=adapters.js.map
|
|
1
|
+
'use strict';var chunkROTLSZMV_js=require('./chunk-ROTLSZMV.js'),chunk7ZZO7GVB_js=require('./chunk-7ZZO7GVB.js'),chunkVWED6UTN_js=require('./chunk-VWED6UTN.js');require('./chunk-RFPLZDIO.js');Object.defineProperty(exports,"apiAdapter",{enumerable:true,get:function(){return chunkROTLSZMV_js.a}});Object.defineProperty(exports,"composeAdapters",{enumerable:true,get:function(){return chunkROTLSZMV_js.c}});Object.defineProperty(exports,"memoryAdapter",{enumerable:true,get:function(){return chunkROTLSZMV_js.b}});Object.defineProperty(exports,"cookieAdapter",{enumerable:true,get:function(){return chunk7ZZO7GVB_js.b}});Object.defineProperty(exports,"sessionStorageAdapter",{enumerable:true,get:function(){return chunk7ZZO7GVB_js.a}});Object.defineProperty(exports,"localStorageAdapter",{enumerable:true,get:function(){return chunkVWED6UTN_js.a}});
|
package/dist/adapters.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export{a as apiAdapter,c as composeAdapters,b as memoryAdapter}from'./chunk-
|
|
2
|
-
//# sourceMappingURL=adapters.mjs.map
|
|
1
|
+
export{a as apiAdapter,c as composeAdapters,b as memoryAdapter}from'./chunk-PL4XNCQA.mjs';export{b as cookieAdapter,a as sessionStorageAdapter}from'./chunk-UASG46LP.mjs';export{a as localStorageAdapter}from'./chunk-DBZSN4WP.mjs';import'./chunk-ZJYULEER.mjs';
|
package/dist/breach.d.mts
CHANGED
|
@@ -67,6 +67,44 @@ export declare interface BreachFormSubmission {
|
|
|
67
67
|
dataTypes: string[];
|
|
68
68
|
/** Estimated number of affected data subjects */
|
|
69
69
|
estimatedAffectedSubjects?: number;
|
|
70
|
+
/**
|
|
71
|
+
* Approximate number of personal data RECORDS concerned. Distinct from
|
|
72
|
+
* subject count (one subject may have many records). NDPA Section 40(2).
|
|
73
|
+
*/
|
|
74
|
+
approximateRecordCount?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Categories of data subjects affected (e.g. customers, employees, minors).
|
|
77
|
+
* NDPA Section 40(2).
|
|
78
|
+
*/
|
|
79
|
+
dataSubjectCategories?: string[];
|
|
80
|
+
/** Whether sensitive personal data (NDPA Section 30) is involved */
|
|
81
|
+
involvesSensitiveData?: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Likely consequences of the breach for affected data subjects.
|
|
84
|
+
* Required content for the NDPC report and Section 40(3) communications.
|
|
85
|
+
*/
|
|
86
|
+
likelyConsequences?: string;
|
|
87
|
+
/**
|
|
88
|
+
* Measures taken or proposed to mitigate adverse effects.
|
|
89
|
+
* NDPA Section 40(3).
|
|
90
|
+
*/
|
|
91
|
+
mitigationMeasures?: string;
|
|
92
|
+
/**
|
|
93
|
+
* Data Protection Officer contact details (Section 32(3)(c) — DPO is the
|
|
94
|
+
* named NDPC contact). Falls back to organisation-level DPO if omitted.
|
|
95
|
+
*/
|
|
96
|
+
dpoContact?: {
|
|
97
|
+
name: string;
|
|
98
|
+
email: string;
|
|
99
|
+
phone?: string;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Whether this is a phased / interim report submitted under Section 40(2)
|
|
103
|
+
* before complete information is available.
|
|
104
|
+
*/
|
|
105
|
+
isPhasedReport?: boolean;
|
|
106
|
+
/** ID of the prior phased report this report supplements, if any. */
|
|
107
|
+
supplementsReportId?: string;
|
|
70
108
|
/** Current status of the breach */
|
|
71
109
|
status: 'ongoing' | 'contained' | 'resolved';
|
|
72
110
|
/** Initial actions taken to address the breach */
|
|
@@ -214,10 +252,51 @@ export declare interface BreachReport {
|
|
|
214
252
|
affectedSystems: string[];
|
|
215
253
|
/** Types of data involved in the breach */
|
|
216
254
|
dataTypes: string[];
|
|
217
|
-
/** Whether sensitive personal data is involved (NDPA Section
|
|
255
|
+
/** Whether sensitive personal data is involved (NDPA Section 30) */
|
|
218
256
|
involvesSensitiveData?: boolean;
|
|
219
257
|
/** Estimated number of data subjects affected */
|
|
220
258
|
estimatedAffectedSubjects?: number;
|
|
259
|
+
/**
|
|
260
|
+
* Approximate number of personal data RECORDS concerned (distinct from subject count).
|
|
261
|
+
* Required content under NDPA Section 40(1)(a) and Section 40(2).
|
|
262
|
+
*/
|
|
263
|
+
approximateRecordCount?: number;
|
|
264
|
+
/**
|
|
265
|
+
* Categories of data subjects affected (e.g. customers, employees, minors, patients).
|
|
266
|
+
* Required content under NDPA Section 40(1)(a) and Section 40(2).
|
|
267
|
+
*/
|
|
268
|
+
dataSubjectCategories?: string[];
|
|
269
|
+
/**
|
|
270
|
+
* Likely consequences of the breach for affected data subjects (e.g. identity theft,
|
|
271
|
+
* financial loss, reputational damage). Reported to the NDPC and, where applicable,
|
|
272
|
+
* communicated to data subjects under Section 40(3).
|
|
273
|
+
*/
|
|
274
|
+
likelyConsequences?: string;
|
|
275
|
+
/**
|
|
276
|
+
* Measures taken or proposed to mitigate adverse effects of the breach.
|
|
277
|
+
* Required content for Section 40(3) communications to data subjects.
|
|
278
|
+
*/
|
|
279
|
+
mitigationMeasures?: string;
|
|
280
|
+
/**
|
|
281
|
+
* Whether this is a phased / interim report submitted before full investigation
|
|
282
|
+
* is complete. The NDPC permits phased reporting where complete information is
|
|
283
|
+
* not available within 72 hours.
|
|
284
|
+
*/
|
|
285
|
+
isPhasedReport?: boolean;
|
|
286
|
+
/**
|
|
287
|
+
* ID of the prior phased report this report supplements, if any.
|
|
288
|
+
*/
|
|
289
|
+
supplementsReportId?: string;
|
|
290
|
+
/**
|
|
291
|
+
* Data Protection Officer contact details. The DPO is the named contact point
|
|
292
|
+
* for the NDPC per NDPA Section 32(3)(c). Required content in the regulatory
|
|
293
|
+
* report (Section 40(2)).
|
|
294
|
+
*/
|
|
295
|
+
dpoContact?: {
|
|
296
|
+
name: string;
|
|
297
|
+
email: string;
|
|
298
|
+
phone?: string;
|
|
299
|
+
};
|
|
221
300
|
/** Whether the breach is ongoing or contained */
|
|
222
301
|
status: 'ongoing' | 'contained' | 'resolved';
|
|
223
302
|
/** Initial actions taken to address the breach */
|