@tantainnovative/ndpr-toolkit 3.10.5 → 3.11.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +106 -0
  2. package/README.md +39 -25
  3. package/dist/adapters.d.mts +90 -4
  4. package/dist/adapters.d.ts +90 -4
  5. package/dist/breach.d.mts +13 -1
  6. package/dist/breach.d.ts +13 -1
  7. package/dist/breach.js +1 -1
  8. package/dist/breach.mjs +1 -1
  9. package/dist/{chunk-HBLGN4SD.js → chunk-5AECGMEB.js} +1 -1
  10. package/dist/{chunk-4G3SRVRI.mjs → chunk-B6BRD5SL.mjs} +1 -1
  11. package/dist/{chunk-VJTQXVAF.js → chunk-EHQVTFYO.js} +1 -1
  12. package/dist/{chunk-CNM6G5IA.js → chunk-FC3PTJFL.js} +1 -1
  13. package/dist/{chunk-H3IULCE3.js → chunk-HOAC5VUF.js} +1 -1
  14. package/dist/{chunk-H3XJV2IR.mjs → chunk-I3V3ITN7.mjs} +1 -1
  15. package/dist/{chunk-MPBPAEZC.mjs → chunk-IB3KSUPZ.mjs} +1 -1
  16. package/dist/{chunk-TAHSSITO.mjs → chunk-IHNAFXDM.mjs} +1 -1
  17. package/dist/chunk-IQO3SIAG.mjs +1 -0
  18. package/dist/{chunk-UTFBKL73.js → chunk-IRRUYR6M.js} +1 -1
  19. package/dist/{chunk-C7IDR2IV.js → chunk-JLQT3W3E.js} +1 -1
  20. package/dist/chunk-OZHUINWS.js +1 -0
  21. package/dist/{chunk-66NQ5CVY.mjs → chunk-Q4CSVWSS.mjs} +1 -1
  22. package/dist/{chunk-CR2QZTGW.js → chunk-QMAVEHOR.js} +1 -1
  23. package/dist/{chunk-3HOXQNCH.mjs → chunk-RFXGD5NE.mjs} +1 -1
  24. package/dist/chunk-VPNK7OID.mjs +1 -0
  25. package/dist/consent.d.mts +23 -1
  26. package/dist/consent.d.ts +23 -1
  27. package/dist/consent.js +1 -1
  28. package/dist/consent.mjs +1 -1
  29. package/dist/core.js +1 -1
  30. package/dist/core.mjs +1 -1
  31. package/dist/cross-border.d.mts +12 -1
  32. package/dist/cross-border.d.ts +12 -1
  33. package/dist/dpia.d.mts +16 -1
  34. package/dist/dpia.d.ts +16 -1
  35. package/dist/dpia.js +1 -1
  36. package/dist/dpia.mjs +1 -1
  37. package/dist/dsr.d.mts +21 -1
  38. package/dist/dsr.d.ts +21 -1
  39. package/dist/dsr.js +1 -1
  40. package/dist/dsr.mjs +1 -1
  41. package/dist/headless.d.mts +193 -21
  42. package/dist/headless.d.ts +193 -21
  43. package/dist/headless.js +1 -1
  44. package/dist/headless.mjs +1 -1
  45. package/dist/hooks.d.mts +193 -21
  46. package/dist/hooks.d.ts +193 -21
  47. package/dist/hooks.js +1 -1
  48. package/dist/hooks.mjs +1 -1
  49. package/dist/index.d.mts +395 -26
  50. package/dist/index.d.ts +395 -26
  51. package/dist/index.js +1 -1
  52. package/dist/index.mjs +1 -1
  53. package/dist/lawful-basis.d.mts +10 -0
  54. package/dist/lawful-basis.d.ts +10 -0
  55. package/dist/lawful-basis.js +1 -1
  56. package/dist/lawful-basis.mjs +1 -1
  57. package/dist/policy.d.mts +39 -1
  58. package/dist/policy.d.ts +39 -1
  59. package/dist/ropa.d.mts +15 -0
  60. package/dist/ropa.d.ts +15 -0
  61. package/dist/server.d.mts +86 -0
  62. package/dist/server.d.ts +86 -0
  63. package/dist/server.js +1 -1
  64. package/dist/server.mjs +1 -1
  65. package/package.json +1 -1
  66. package/dist/chunk-L2BRFMVS.js +0 -1
  67. package/dist/chunk-MOHBL6LX.mjs +0 -1
  68. package/dist/chunk-RZ6GC6WN.mjs +0 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,112 @@
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.11.0](https://github.com/mr-tanta/ndpr-toolkit/compare/v3.10.6...v3.11.0) (2026-05-27)
6
+
7
+ Release 3 of 6 on the post-audit roadmap. Strictly additive — every change here adds to the public surface or fixes a docs lie. Existing consumers keep working without changes.
8
+
9
+ ### Type-export baseline — `*Props` interfaces, adapter ecosystem types, hook return types now reachable
10
+
11
+ Every component's `Props` interface is now re-exported from `src/index.ts` (the default entry):
12
+
13
+ `ConsentBannerProps`, `ConsentManagerProps`, `ConsentStorageProps`, `DSRRequestFormProps`, `DSRDashboardProps`, `DSRTrackerProps`, `DPIAQuestionnaireProps`, `DPIAReportProps`, `StepIndicatorProps`, `BreachReportFormProps`, `BreachRiskAssessmentProps`, `BreachNotificationManagerProps`, `RegulatoryReportGeneratorProps`, `PolicyGeneratorProps`, `PolicyPreviewProps`, `PolicyExporterProps`, `LawfulBasisTrackerProps`, `CrossBorderTransferManagerProps`, `ROPAManagerProps`.
14
+
15
+ Consumers can now type-safely wrap the toolkit's components — e.g.
16
+
17
+ ```ts
18
+ import type { ConsentBannerProps, NDPRThemeProvider } from '@tantainnovative/ndpr-toolkit';
19
+
20
+ function MyConsentBanner(props: ConsentBannerProps & { brand?: string }) { … }
21
+ ```
22
+
23
+ Adapter ecosystem types reachable too:
24
+ - `ApiAdapterErrorContext<T>`, `ApiAdapterSuccessContext<T>`, `ApiAdapterRetryConfig`, `ApiAdapterMethod` from `/adapters` and root
25
+ - `CookieAdapterOptions`, `StorageAdapter<T>` from `/adapters` and root
26
+
27
+ DSR validator types reachable:
28
+ - `DsrSubmissionPayload`, `DsrSubmissionValidationResult`, `ValidateDsrSubmissionOptions` from `/server` (canonical) and root
29
+
30
+ Hook option/return interfaces promoted to `export interface` and re-exported through `hooks-entry.ts` (auto-flows to `/headless`):
31
+ - `UseConsentOptions`/`Return`, `UseDSROptions`, `UseBreachOptions`, `UseDPIAOptions`, `UseLawfulBasisOptions`, `UseCrossBorderTransferOptions`, `UseComplianceScoreOptions`, `UsePrivacyPolicyOptions`.
32
+
33
+ ### JSDoc `@example` blocks on every public hook + adapter
34
+
35
+ Added `@example` blocks to all 10 public hooks (`useConsent`, `useDSR`, `useBreach`, `useDPIA`, `useLawfulBasis`, `useCrossBorderTransfer`, `usePrivacyPolicy`, `useROPA`, `useAdaptivePolicyWizard`, `useComplianceScore`) and 5 adapters (`cookieAdapter`, `localStorageAdapter`, `sessionStorageAdapter`, `memoryAdapter`, `composeAdapters`). `useComplianceScore` previously had zero JSDoc; full block added (description + `@param` + `@returns` + `@example`).
36
+
37
+ ### Internal: `validateDsrSubmission` type-guard refactor
38
+
39
+ Replaced the `as string` cast chain in `utils/dsr.ts` with a `isDsrPayloadRaw(payload): payload is DsrPayloadRaw` type guard. External signature unchanged. Safer narrowing; no consumer-visible difference.
40
+
41
+ ### Docs
42
+
43
+ **9 new component pages** under `src/app/docs/components/`, all wired into the sidebar nav:
44
+
45
+ - `ndpr-theme-provider`, `ndpr-provider`, `ndpr-dashboard`, `adaptive-policy-wizard`, `policy-page`, `legal-notice` (previously had no dedicated pages)
46
+ - `lawful-basis-tracker-lite`, `cross-border-transfer-manager-lite`, `ropa-manager-lite` (Lite variants previously only mentioned inline)
47
+
48
+ **Fixed broken docs** that taught fictitious symbol names:
49
+ - 5 `useDSR()` calls without the required `requestTypes` arg, in `components/data-subject-rights/page.tsx`, `components/hooks/page.tsx`, and `guides/data-subject-requests/page.tsx`. All now show `useDSR({ requestTypes })` with the correct API.
50
+ - `getRequestById` → `getRequest`, `filterRequestsByStatus` → `getRequestsByStatus`, `filterRequestsByType` → `getRequestsByType` (the docs were inventing names that don't exist). `deleteRequest` removed — no such function ships.
51
+ - `onSubmit` prop on the DSR component page now typed `DSRFormSubmission`, not the fictitious `DSRFormData`.
52
+
53
+ ### README compact pass
54
+
55
+ - Bumped version refs `v3.10.3` → `v3.11.0` (6 occurrences: header release link + 5 screenshot URL tags).
56
+ - Replaced the 3-File Quickstart's throwaway `let store: unknown = null` API route with a clean 2-file quickstart that uses `localStorageAdapter` by default and shows `cookieAdapter` / `apiAdapter` / `composeAdapters` as opt-ins. The throwaway demo always looked unprofessional next to claims of production-readiness.
57
+ - New **"When NOT to use this toolkit"** section between Adapters and Pluggable Storage. Honest framing: non-React stacks, banner-only use cases, GDPR-primary regimes, enterprise CMP shoppers should pick something else. Builds trust with the people who are the right fit.
58
+ - "What's new" notice rewritten for 3.11.0 (less narrative, more skimmable).
59
+
60
+ ### CONTRIBUTING.md rewritten
61
+
62
+ The previous file was generic boilerplate. The new one covers the practical things contributors actually need: pnpm 10 / Node ≥20 setup, repo layout (with a callout that the root `package.json` is the publish surface, not the inner one), how to run a single test, the `verify:tarball` gate, the release flow, branch conventions, patch/minor/major decision table, i18n contribution guide.
63
+
64
+ ### Verification
65
+
66
+ - Jest: **1212 / 1212 passing** (no behaviour changes)
67
+ - `tsc --noEmit -p tsconfig.json` — clean
68
+ - `pnpm verify:tarball --skip-ts` — clean across all 22 subpaths
69
+
70
+ ## [3.10.6](https://github.com/mr-tanta/ndpr-toolkit/compare/v3.10.5...v3.10.6) (2026-05-27)
71
+
72
+ Release 2 of 6 on the post-audit roadmap. CI / repo plumbing only — zero `dist/` changes, zero behaviour changes for consumers.
73
+
74
+ ### CI workflows hardened
75
+
76
+ - **`ci.yml`** — synced the `Verify entry points` loop with `publish.yml` (17 → 22 entries). Pre-3.10.6 a PR could pass CI while failing the publish workflow at release time. Both now check the same 22 entries plus `dist/styles.css`. Also added the **`verify:tarball` step**: the same ESM + CJS + TS resolution gate that runs in `publish.yml` now runs on every PR, so the 3.8.0–3.10.2 missing-exports class of bug can never reach a tag again.
77
+ - **`concurrency:` groups + `timeout-minutes:`** added to all three workflows.
78
+ - **`publish.yml`** — `npm install -g npm@11` (was `npm@latest`). Pin deliberately so a future npm major can't break release day.
79
+ - **`nextjs.yml`** — moved `id-token: write` from workflow-level to the `deploy` job only (least privilege). Added a docs-site typecheck step on PR builds, closing the "docs site never typechecks" gap. PRs build without deploying.
80
+
81
+ ### New workflows
82
+
83
+ - **`.github/workflows/codeql.yml`** — CodeQL SAST on push + PR + weekly cron.
84
+ - **`.github/dependabot.yml`** — weekly automated PRs for `github-actions` + `npm`, grouped.
85
+
86
+ ### Governance docs
87
+
88
+ - `SECURITY.md`, `CODE_OF_CONDUCT.md`, `.github/FUNDING.yml`, `CODEOWNERS`, `.github/PULL_REQUEST_TEMPLATE.md`, `.github/ISSUE_TEMPLATE/{bug_report,feature_request,config}.{md,yml}`.
89
+
90
+ ### Example apps hygiene
91
+
92
+ - `engines.node: >=20.0.0` added to all **14** example `package.json` files (was 2 of 14).
93
+ - `.gitignore` added to `examples/ssr/{remix,astro}`. `examples/ssr/remix/public/.gitkeep` so the conventional dir exists.
94
+ - `examples/dsr-backend-prod/README.md` — added a "Switching to PostgreSQL for production" subsection with the schema diff.
95
+
96
+ ### README
97
+
98
+ - Tests badge swapped from a static `tests-1192 passing` shield to a live `CI` badge driven by the actual workflow status.
99
+
100
+ ### Known follow-up
101
+
102
+ - The plan called for switching all three workflows to `--frozen-lockfile`. Defer: significant pre-existing lockfile drift (next 16.2.2 → 16.2.6, the docs-site self-dep, `@phosphor-icons/react` addition all pre-date proper lockfile maintenance). Tightening alongside the lockfile regeneration warrants its own dedicated release rather than risking unrelated breakage here.
103
+
104
+ ### Verification
105
+
106
+ - Workflow YAML lints; CI green on the new entry-point loop and verify-tarball step.
107
+ - Jest: 1212 / 1212 passing (no functional changes).
108
+ - `tsc --noEmit -p tsconfig.json` clean for the docs site.
109
+ - `pnpm verify:tarball` clean across all 22 subpaths.
110
+
5
111
  ## [3.10.5](https://github.com/mr-tanta/ndpr-toolkit/compare/v3.10.4...v3.10.5) (2026-05-27)
6
112
 
7
113
  First of six releases on the post-audit roadmap (3.10.5 → 3.10.6 → 3.11.0 → 3.12.0 → 3.13.0 → 4.0.0). This patch covers the "real bugs consumers actively hit" tier — no API changes observable to consumers.
package/README.md CHANGED
@@ -6,33 +6,34 @@
6
6
  [![npm downloads](https://img.shields.io/npm/dm/@tantainnovative/ndpr-toolkit.svg)](https://www.npmjs.com/package/@tantainnovative/ndpr-toolkit)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
8
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-5%2B-3178C6.svg)](https://www.typescriptlang.org/)
9
- [![Tests](https://img.shields.io/badge/tests-1192%20passing-brightgreen.svg)](#)
9
+ [![CI](https://github.com/mr-tanta/ndpr-toolkit/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/mr-tanta/ndpr-toolkit/actions/workflows/ci.yml)
10
10
  [![Bundle Size](https://img.shields.io/bundlephobia/minzip/@tantainnovative/ndpr-toolkit)](https://bundlephobia.com/package/@tantainnovative/ndpr-toolkit)
11
11
 
12
12
  v3 ships **zero-config presets**, **pluggable storage adapters**, **compound components**, and a **compliance score engine** — eight production-ready modules covering consent, data subject rights, DPIA, breach notification, privacy policies, lawful basis, cross-border transfers, and ROPA.
13
13
 
14
- **[Documentation](https://ndprtoolkit.com.ng)** | **[Live Demos](https://ndprtoolkit.com.ng/ndpr-demos)** | **[npm](https://www.npmjs.com/package/@tantainnovative/ndpr-toolkit)** | **[Blog](https://ndprtoolkit.com.ng/blog)** | **[v3.10.3 Release](https://github.com/mr-tanta/ndpr-toolkit/releases/tag/v3.10.3)**
14
+ **[Documentation](https://ndprtoolkit.com.ng)** | **[Live Demos](https://ndprtoolkit.com.ng/ndpr-demos)** | **[npm](https://www.npmjs.com/package/@tantainnovative/ndpr-toolkit)** | **[Blog](https://ndprtoolkit.com.ng/blog)** | **[v3.11.0 Release](https://github.com/mr-tanta/ndpr-toolkit/releases/tag/v3.11.0)**
15
15
 
16
16
  [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mr-tanta/ndpr-toolkit/tree/main/examples/nextjs-app)
17
17
  [![Open in CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/github/mr-tanta/ndpr-toolkit/main/examples/nextjs-app)
18
18
 
19
- > **What's new in 3.10.x:** Typed theming via `<NDPRThemeProvider>`, a `/headless` subpath alias of `/hooks` for headless-UI consumers, and a production-grade DSR backend reference at `examples/dsr-backend-prod/` (Prisma + Resend behind no-infra shims). New docs guides: [theming](https://ndprtoolkit.com.ng/docs/guides/theming), [headless](https://ndprtoolkit.com.ng/docs/guides/headless), [production-dsr-backend](https://ndprtoolkit.com.ng/docs/guides/production-dsr-backend).
19
+ > **What's new in 3.11.0:** Every component's `Props` interface is now re-exported from the root (consumers can finally wrap toolkit components without copying types). Adapter ecosystem types (`ApiAdapterErrorContext`, `StorageAdapter<T>`, …) and DSR validator types reachable from `/server` + root. 9 new component docs pages cover `NDPRThemeProvider`, `NDPRProvider`, `NDPRDashboard`, `AdaptivePolicyWizard`, `PolicyPage`, the 3 Lite manager variants, and `LegalNotice`.
20
20
  >
21
- > **Upgrading from 3.7.x?** See the [3.7 → 3.10 upgrade guide](https://ndprtoolkit.com.ng/docs/guides/upgrading-3-7-to-3-10) — fully additive, no API breaks. Full changelog on [GitHub](https://github.com/mr-tanta/ndpr-toolkit/blob/main/CHANGELOG.md).
21
+ > **3.10.x highlights:** Typed theming via `<NDPRThemeProvider>`, a `/headless` subpath alias of `/hooks`, the production-grade DSR backend reference at `examples/dsr-backend-prod/`, plus the `verify:tarball` CI gate that catches broken exports at PR time. Upgrading from 3.7.x? See the [3.7 → 3.10 upgrade guide](https://ndprtoolkit.com.ng/docs/guides/upgrading-3-7-to-3-10). Full history in the [CHANGELOG](https://github.com/mr-tanta/ndpr-toolkit/blob/main/CHANGELOG.md).
22
22
 
23
23
  <p align="center">
24
- <img src="https://raw.githubusercontent.com/mr-tanta/ndpr-toolkit/v3.10.3/public/screenshots/hero.png" alt="NDPA Toolkit — NDPA Compliance Made Beautiful" width="800" />
24
+ <img src="https://raw.githubusercontent.com/mr-tanta/ndpr-toolkit/v3.11.0/public/screenshots/hero.png" alt="NDPA Toolkit — NDPA Compliance Made Beautiful" width="800" />
25
25
  </p>
26
26
 
27
27
  ---
28
28
 
29
- ## The 3-File Quickstart
29
+ ## Quickstart
30
30
 
31
- Three files. Full NDPA consent compliance.
31
+ Two files. Full NDPA-compliant consent with no backend.
32
32
 
33
33
  **`app/layout.tsx`**
34
34
  ```tsx
35
35
  import { NDPRConsent } from '@tantainnovative/ndpr-toolkit/presets';
36
+ import '@tantainnovative/ndpr-toolkit/styles';
36
37
 
37
38
  export default function RootLayout({ children }: { children: React.ReactNode }) {
38
39
  return (
@@ -46,31 +47,31 @@ export default function RootLayout({ children }: { children: React.ReactNode })
46
47
  }
47
48
  ```
48
49
 
49
- **`app/api/consent/route.ts`**
50
- ```ts
51
- import { NextRequest, NextResponse } from 'next/server';
52
-
53
- let store: unknown = null;
50
+ That's it — `NDPRConsent` defaults to `localStorageAdapter`, so consent persists across page loads with zero backend code.
54
51
 
55
- export async function GET() { return NextResponse.json(store ?? {}); }
56
- export async function POST(req: NextRequest) {
57
- store = await req.json();
58
- return NextResponse.json({ ok: true });
59
- }
60
- ```
52
+ **Want server-side persistence?** Pass any `StorageAdapter`:
61
53
 
62
- **Persist to your API instead of localStorage:**
63
54
  ```tsx
64
55
  import { NDPRConsent } from '@tantainnovative/ndpr-toolkit/presets';
65
- import { apiAdapter } from '@tantainnovative/ndpr-toolkit/adapters';
56
+ import { cookieAdapter, apiAdapter, composeAdapters, localStorageAdapter } from '@tantainnovative/ndpr-toolkit/adapters';
66
57
 
58
+ // Server-readable cookie (best for SSR consent gating)
59
+ <NDPRConsent adapter={cookieAdapter('ndpr_consent', { expires: 180 })} />
60
+
61
+ // Or POST to your own backend
67
62
  <NDPRConsent adapter={apiAdapter('/api/consent')} />
63
+
64
+ // Or both — fan-out writes
65
+ <NDPRConsent adapter={composeAdapters(
66
+ apiAdapter('/api/consent'),
67
+ localStorageAdapter('ndpr_consent'),
68
+ )} />
68
69
  ```
69
70
 
70
- That's it. NDPA-compliant consent with server-side persistence in under 20 lines.
71
+ The full SSR pattern (cookie read server-side banner hydrates already-resolved, no flash) is in the [Server-Side Storage guide](https://ndprtoolkit.com.ng/docs/guides/server-side-storage).
71
72
 
72
73
  <p align="center">
73
- <img src="https://raw.githubusercontent.com/mr-tanta/ndpr-toolkit/v3.10.3/public/screenshots/consent-demo.png" alt="Consent Management Demo — interactive consent banner with state inspector" width="800" />
74
+ <img src="https://raw.githubusercontent.com/mr-tanta/ndpr-toolkit/v3.11.0/public/screenshots/consent-demo.png" alt="Consent Management Demo — interactive consent banner with state inspector" width="800" />
74
75
  <br />
75
76
  <em>Interactive consent demo with configurable position, theme, storage, and real-time state inspector</em>
76
77
  </p>
@@ -304,6 +305,19 @@ import { apiAdapter, localStorageAdapter, cookieAdapter } from '@tantainnovative
304
305
 
305
306
  ---
306
307
 
308
+ ## When NOT to use this toolkit
309
+
310
+ The toolkit is React-first, NDPA-specific, and built for product engineers shipping a compliant app — not a generic cookie-banner library. Pick something else if:
311
+
312
+ - **You're not on React.** No Vue / Svelte / Angular bindings exist. The `/server` entry exposes framework-agnostic validators and the compliance-score engine — usable from any Node runtime — but the UI components are React-only.
313
+ - **You only need a cookie banner**, with no DSR portal, breach reporting, DPIA, ROPA, or compliance scoring. A purpose-built consent library (Iubenda, OneTrust, Cookiebot, Osano) is a better fit and will integrate with your CMP / TCF setup. The toolkit can do the banner alone, but you'd be paying for surface you don't use.
314
+ - **Your compliance regime is GDPR / CCPA-primary** and you don't operate under the Nigeria Data Protection Act 2023. The validators, statutory deadlines, and section citations are NDPA-specific. (NDPA + GDPR overlap a lot in practice; the toolkit doesn't pretend to be a GDPR product.)
315
+ - **You need an enterprise consent-management platform** with audit trails, marketing-team UIs, regional CMP exports, and a vendor SLA. That's a different product category.
316
+
317
+ The toolkit is what we wished existed when building Nigerian SaaS apps in 2025 and need NDPA components that don't fight the rest of the stack. If that's your shape, read on.
318
+
319
+ ---
320
+
307
321
  ## Pluggable Storage
308
322
 
309
323
  Every stateful component accepts an `adapter` prop. Built-in adapters ship out of the box.
@@ -434,13 +448,13 @@ Every module has an interactive demo. No signup, no setup — try them instantly
434
448
 
435
449
  <p align="center">
436
450
  <a href="https://ndprtoolkit.com.ng/ndpr-demos">
437
- <img src="https://raw.githubusercontent.com/mr-tanta/ndpr-toolkit/v3.10.3/public/screenshots/demos-overview.png" alt="8 interactive live demos — zero setup required" width="800" />
451
+ <img src="https://raw.githubusercontent.com/mr-tanta/ndpr-toolkit/v3.11.0/public/screenshots/demos-overview.png" alt="8 interactive live demos — zero setup required" width="800" />
438
452
  </a>
439
453
  </p>
440
454
 
441
455
  <p align="center">
442
- <img src="https://raw.githubusercontent.com/mr-tanta/ndpr-toolkit/v3.10.3/public/screenshots/dsr-demo.png" alt="Data Subject Rights — 8 rights with request tracking" width="400" />
443
- <img src="https://raw.githubusercontent.com/mr-tanta/ndpr-toolkit/v3.10.3/public/screenshots/breach-demo.png" alt="Breach Notification — 72-hour countdown with step-by-step workflow" width="400" />
456
+ <img src="https://raw.githubusercontent.com/mr-tanta/ndpr-toolkit/v3.11.0/public/screenshots/dsr-demo.png" alt="Data Subject Rights — 8 rights with request tracking" width="400" />
457
+ <img src="https://raw.githubusercontent.com/mr-tanta/ndpr-toolkit/v3.11.0/public/screenshots/breach-demo.png" alt="Breach Notification — 72-hour countdown with step-by-step workflow" width="400" />
444
458
  </p>
445
459
 
446
460
  <p align="center">
@@ -32,7 +32,7 @@
32
32
  */
33
33
  export declare function apiAdapter<T = unknown>(endpoint: string, options?: ApiAdapterOptions<T>): StorageAdapter<T>;
34
34
 
35
- declare interface ApiAdapterErrorContext<T = unknown> {
35
+ export declare interface ApiAdapterErrorContext<T = unknown> {
36
36
  /** Which adapter operation triggered this — `load`, `save`, or `remove`. */
37
37
  method: ApiAdapterMethod;
38
38
  /** The endpoint URL that failed. */
@@ -49,7 +49,7 @@ declare interface ApiAdapterErrorContext<T = unknown> {
49
49
  attempt: number;
50
50
  }
51
51
 
52
- declare type ApiAdapterMethod = 'load' | 'save' | 'remove';
52
+ export declare type ApiAdapterMethod = 'load' | 'save' | 'remove';
53
53
 
54
54
  export declare interface ApiAdapterOptions<T = unknown> {
55
55
  /**
@@ -106,7 +106,7 @@ export declare interface ApiAdapterOptions<T = unknown> {
106
106
  fetchInit?: Omit<RequestInit, 'method' | 'headers' | 'body' | 'credentials'>;
107
107
  }
108
108
 
109
- declare interface ApiAdapterRetryConfig {
109
+ export declare interface ApiAdapterRetryConfig {
110
110
  /**
111
111
  * Number of additional attempts after the initial request. Defaults to 0
112
112
  * (no retries). e.g. `attempts: 2` means up to 3 total requests.
@@ -125,7 +125,7 @@ declare interface ApiAdapterRetryConfig {
125
125
  shouldRetry?: (ctx: ApiAdapterErrorContext<unknown>) => boolean;
126
126
  }
127
127
 
128
- declare interface ApiAdapterSuccessContext<T = unknown> {
128
+ export declare interface ApiAdapterSuccessContext<T = unknown> {
129
129
  /** Which adapter operation succeeded — `load`, `save`, or `remove`. */
130
130
  method: ApiAdapterMethod;
131
131
  /** The endpoint URL. */
@@ -138,8 +138,51 @@ declare interface ApiAdapterSuccessContext<T = unknown> {
138
138
  payload?: T;
139
139
  }
140
140
 
141
+ /**
142
+ * Compose a primary adapter with one or more secondary adapters. Reads
143
+ * always go to the primary; writes (`save` / `remove`) fan out to the
144
+ * primary first, then each secondary. Secondary failures are logged but
145
+ * never propagated.
146
+ *
147
+ * Useful for shadowing localStorage to an API endpoint, mirroring consent
148
+ * to a cookie for SSR, or building offline-first sync.
149
+ *
150
+ * @example
151
+ * ```ts
152
+ * import {
153
+ * composeAdapters,
154
+ * localStorageAdapter,
155
+ * apiAdapter,
156
+ * } from '@tantainnovative/ndpr-toolkit/adapters';
157
+ * import { useConsent } from '@tantainnovative/ndpr-toolkit/hooks';
158
+ *
159
+ * const adapter = composeAdapters(
160
+ * localStorageAdapter('ndpr_consent'),
161
+ * apiAdapter('/api/consent'),
162
+ * );
163
+ * useConsent({ options, adapter });
164
+ * ```
165
+ */
141
166
  export declare function composeAdapters<T = unknown>(primary: StorageAdapter<T>, ...secondaries: StorageAdapter<T>[]): StorageAdapter<T>;
142
167
 
168
+ /**
169
+ * Storage adapter backed by a browser cookie. Useful for consent state that
170
+ * needs to be shared with server-side rendering, or for cross-subdomain
171
+ * persistence via the `domain` option.
172
+ *
173
+ * @example
174
+ * ```ts
175
+ * import { cookieAdapter } from '@tantainnovative/ndpr-toolkit/adapters';
176
+ * import { useConsent } from '@tantainnovative/ndpr-toolkit/hooks';
177
+ *
178
+ * const adapter = cookieAdapter('ndpr_consent', {
179
+ * domain: '.example.com',
180
+ * sameSite: 'Lax',
181
+ * expires: 180,
182
+ * });
183
+ * useConsent({ options, adapter });
184
+ * ```
185
+ */
143
186
  export declare function cookieAdapter<T = unknown>(key: string, options?: CookieAdapterOptions): StorageAdapter<T>;
144
187
 
145
188
  export declare interface CookieAdapterOptions {
@@ -150,10 +193,53 @@ export declare interface CookieAdapterOptions {
150
193
  sameSite?: 'Strict' | 'Lax' | 'None';
151
194
  }
152
195
 
196
+ /**
197
+ * Storage adapter backed by `window.localStorage`. The default adapter used
198
+ * by every hook in the toolkit when no `adapter` prop is supplied.
199
+ *
200
+ * Safe to import server-side — every method short-circuits when
201
+ * `window` is undefined, so calling `load()` on the server returns `null`.
202
+ *
203
+ * @example
204
+ * ```ts
205
+ * import { localStorageAdapter } from '@tantainnovative/ndpr-toolkit/adapters';
206
+ * import { useConsent } from '@tantainnovative/ndpr-toolkit/hooks';
207
+ *
208
+ * const adapter = localStorageAdapter('ndpr_consent');
209
+ * useConsent({ options, adapter });
210
+ * ```
211
+ */
153
212
  export declare function localStorageAdapter<T = unknown>(key: string): StorageAdapter<T>;
154
213
 
214
+ /**
215
+ * Storage adapter backed by an in-memory value. Useful in tests, Storybook,
216
+ * SSR previews, or anywhere persistence across reloads is undesirable.
217
+ *
218
+ * @example
219
+ * ```ts
220
+ * import { memoryAdapter } from '@tantainnovative/ndpr-toolkit/adapters';
221
+ * import { useConsent } from '@tantainnovative/ndpr-toolkit/hooks';
222
+ *
223
+ * const adapter = memoryAdapter({ consents: {}, version: '1.0' });
224
+ * useConsent({ options, adapter });
225
+ * ```
226
+ */
155
227
  export declare function memoryAdapter<T = unknown>(initialData?: T): StorageAdapter<T>;
156
228
 
229
+ /**
230
+ * Storage adapter backed by `window.sessionStorage`. Data is scoped to the
231
+ * current tab and discarded when the tab closes — useful for consent
232
+ * choices that should not survive a fresh session.
233
+ *
234
+ * @example
235
+ * ```ts
236
+ * import { sessionStorageAdapter } from '@tantainnovative/ndpr-toolkit/adapters';
237
+ * import { useConsent } from '@tantainnovative/ndpr-toolkit/hooks';
238
+ *
239
+ * const adapter = sessionStorageAdapter('ndpr_consent');
240
+ * useConsent({ options, adapter });
241
+ * ```
242
+ */
157
243
  export declare function sessionStorageAdapter<T = unknown>(key: string): StorageAdapter<T>;
158
244
 
159
245
  export declare interface StorageAdapter<T = unknown> {
@@ -32,7 +32,7 @@
32
32
  */
33
33
  export declare function apiAdapter<T = unknown>(endpoint: string, options?: ApiAdapterOptions<T>): StorageAdapter<T>;
34
34
 
35
- declare interface ApiAdapterErrorContext<T = unknown> {
35
+ export declare interface ApiAdapterErrorContext<T = unknown> {
36
36
  /** Which adapter operation triggered this — `load`, `save`, or `remove`. */
37
37
  method: ApiAdapterMethod;
38
38
  /** The endpoint URL that failed. */
@@ -49,7 +49,7 @@ declare interface ApiAdapterErrorContext<T = unknown> {
49
49
  attempt: number;
50
50
  }
51
51
 
52
- declare type ApiAdapterMethod = 'load' | 'save' | 'remove';
52
+ export declare type ApiAdapterMethod = 'load' | 'save' | 'remove';
53
53
 
54
54
  export declare interface ApiAdapterOptions<T = unknown> {
55
55
  /**
@@ -106,7 +106,7 @@ export declare interface ApiAdapterOptions<T = unknown> {
106
106
  fetchInit?: Omit<RequestInit, 'method' | 'headers' | 'body' | 'credentials'>;
107
107
  }
108
108
 
109
- declare interface ApiAdapterRetryConfig {
109
+ export declare interface ApiAdapterRetryConfig {
110
110
  /**
111
111
  * Number of additional attempts after the initial request. Defaults to 0
112
112
  * (no retries). e.g. `attempts: 2` means up to 3 total requests.
@@ -125,7 +125,7 @@ declare interface ApiAdapterRetryConfig {
125
125
  shouldRetry?: (ctx: ApiAdapterErrorContext<unknown>) => boolean;
126
126
  }
127
127
 
128
- declare interface ApiAdapterSuccessContext<T = unknown> {
128
+ export declare interface ApiAdapterSuccessContext<T = unknown> {
129
129
  /** Which adapter operation succeeded — `load`, `save`, or `remove`. */
130
130
  method: ApiAdapterMethod;
131
131
  /** The endpoint URL. */
@@ -138,8 +138,51 @@ declare interface ApiAdapterSuccessContext<T = unknown> {
138
138
  payload?: T;
139
139
  }
140
140
 
141
+ /**
142
+ * Compose a primary adapter with one or more secondary adapters. Reads
143
+ * always go to the primary; writes (`save` / `remove`) fan out to the
144
+ * primary first, then each secondary. Secondary failures are logged but
145
+ * never propagated.
146
+ *
147
+ * Useful for shadowing localStorage to an API endpoint, mirroring consent
148
+ * to a cookie for SSR, or building offline-first sync.
149
+ *
150
+ * @example
151
+ * ```ts
152
+ * import {
153
+ * composeAdapters,
154
+ * localStorageAdapter,
155
+ * apiAdapter,
156
+ * } from '@tantainnovative/ndpr-toolkit/adapters';
157
+ * import { useConsent } from '@tantainnovative/ndpr-toolkit/hooks';
158
+ *
159
+ * const adapter = composeAdapters(
160
+ * localStorageAdapter('ndpr_consent'),
161
+ * apiAdapter('/api/consent'),
162
+ * );
163
+ * useConsent({ options, adapter });
164
+ * ```
165
+ */
141
166
  export declare function composeAdapters<T = unknown>(primary: StorageAdapter<T>, ...secondaries: StorageAdapter<T>[]): StorageAdapter<T>;
142
167
 
168
+ /**
169
+ * Storage adapter backed by a browser cookie. Useful for consent state that
170
+ * needs to be shared with server-side rendering, or for cross-subdomain
171
+ * persistence via the `domain` option.
172
+ *
173
+ * @example
174
+ * ```ts
175
+ * import { cookieAdapter } from '@tantainnovative/ndpr-toolkit/adapters';
176
+ * import { useConsent } from '@tantainnovative/ndpr-toolkit/hooks';
177
+ *
178
+ * const adapter = cookieAdapter('ndpr_consent', {
179
+ * domain: '.example.com',
180
+ * sameSite: 'Lax',
181
+ * expires: 180,
182
+ * });
183
+ * useConsent({ options, adapter });
184
+ * ```
185
+ */
143
186
  export declare function cookieAdapter<T = unknown>(key: string, options?: CookieAdapterOptions): StorageAdapter<T>;
144
187
 
145
188
  export declare interface CookieAdapterOptions {
@@ -150,10 +193,53 @@ export declare interface CookieAdapterOptions {
150
193
  sameSite?: 'Strict' | 'Lax' | 'None';
151
194
  }
152
195
 
196
+ /**
197
+ * Storage adapter backed by `window.localStorage`. The default adapter used
198
+ * by every hook in the toolkit when no `adapter` prop is supplied.
199
+ *
200
+ * Safe to import server-side — every method short-circuits when
201
+ * `window` is undefined, so calling `load()` on the server returns `null`.
202
+ *
203
+ * @example
204
+ * ```ts
205
+ * import { localStorageAdapter } from '@tantainnovative/ndpr-toolkit/adapters';
206
+ * import { useConsent } from '@tantainnovative/ndpr-toolkit/hooks';
207
+ *
208
+ * const adapter = localStorageAdapter('ndpr_consent');
209
+ * useConsent({ options, adapter });
210
+ * ```
211
+ */
153
212
  export declare function localStorageAdapter<T = unknown>(key: string): StorageAdapter<T>;
154
213
 
214
+ /**
215
+ * Storage adapter backed by an in-memory value. Useful in tests, Storybook,
216
+ * SSR previews, or anywhere persistence across reloads is undesirable.
217
+ *
218
+ * @example
219
+ * ```ts
220
+ * import { memoryAdapter } from '@tantainnovative/ndpr-toolkit/adapters';
221
+ * import { useConsent } from '@tantainnovative/ndpr-toolkit/hooks';
222
+ *
223
+ * const adapter = memoryAdapter({ consents: {}, version: '1.0' });
224
+ * useConsent({ options, adapter });
225
+ * ```
226
+ */
155
227
  export declare function memoryAdapter<T = unknown>(initialData?: T): StorageAdapter<T>;
156
228
 
229
+ /**
230
+ * Storage adapter backed by `window.sessionStorage`. Data is scoped to the
231
+ * current tab and discarded when the tab closes — useful for consent
232
+ * choices that should not survive a fresh session.
233
+ *
234
+ * @example
235
+ * ```ts
236
+ * import { sessionStorageAdapter } from '@tantainnovative/ndpr-toolkit/adapters';
237
+ * import { useConsent } from '@tantainnovative/ndpr-toolkit/hooks';
238
+ *
239
+ * const adapter = sessionStorageAdapter('ndpr_consent');
240
+ * useConsent({ options, adapter });
241
+ * ```
242
+ */
157
243
  export declare function sessionStorageAdapter<T = unknown>(key: string): StorageAdapter<T>;
158
244
 
159
245
  export declare interface StorageAdapter<T = unknown> {
package/dist/breach.d.mts CHANGED
@@ -770,7 +770,19 @@ export declare interface StorageAdapter<T = unknown> {
770
770
  }
771
771
 
772
772
  /**
773
- * Hook for managing data breach notifications in compliance with the NDPA (Section 40)
773
+ * Hook for managing data breach notifications in compliance with the NDPA (Section 40).
774
+ *
775
+ * @example
776
+ * ```tsx
777
+ * import { useBreach } from '@tantainnovative/ndpr-toolkit/hooks';
778
+ *
779
+ * function BreachConsole() {
780
+ * const { reports, reportBreach } = useBreach({
781
+ * categories: [{ id: 'unauthorized-access', name: 'Unauthorised access', description: '' }],
782
+ * });
783
+ * return <p>{reports.length} breach report(s) on record.</p>;
784
+ * }
785
+ * ```
774
786
  */
775
787
  export declare function useBreach({ categories, initialReports, adapter, storageKey, useLocalStorage, onReport, onAssessment, onNotification, }: UseBreachOptions): UseBreachReturn;
776
788
 
package/dist/breach.d.ts CHANGED
@@ -770,7 +770,19 @@ export declare interface StorageAdapter<T = unknown> {
770
770
  }
771
771
 
772
772
  /**
773
- * Hook for managing data breach notifications in compliance with the NDPA (Section 40)
773
+ * Hook for managing data breach notifications in compliance with the NDPA (Section 40).
774
+ *
775
+ * @example
776
+ * ```tsx
777
+ * import { useBreach } from '@tantainnovative/ndpr-toolkit/hooks';
778
+ *
779
+ * function BreachConsole() {
780
+ * const { reports, reportBreach } = useBreach({
781
+ * categories: [{ id: 'unauthorized-access', name: 'Unauthorised access', description: '' }],
782
+ * });
783
+ * return <p>{reports.length} breach report(s) on record.</p>;
784
+ * }
785
+ * ```
774
786
  */
775
787
  export declare function useBreach({ categories, initialReports, adapter, storageKey, useLocalStorage, onReport, onAssessment, onNotification, }: UseBreachOptions): UseBreachReturn;
776
788
 
package/dist/breach.js CHANGED
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- 'use strict';var chunkPGI2LM6P_js=require('./chunk-PGI2LM6P.js'),chunkO2RDZGM2_js=require('./chunk-O2RDZGM2.js'),chunkVJTQXVAF_js=require('./chunk-VJTQXVAF.js'),chunk3YTAOT5O_js=require('./chunk-3YTAOT5O.js');require('./chunk-UXUMYP4L.js'),require('./chunk-NDKDKDDX.js'),require('./chunk-WKTKTLMF.js'),require('./chunk-ZVOIR4QH.js'),require('./chunk-AME4HJR4.js'),require('./chunk-VWED6UTN.js');var chunkRFPLZDIO_js=require('./chunk-RFPLZDIO.js'),react=require('react'),jsxRuntime=require('react/jsx-runtime');var c=react.createContext(null);function P(){let e=react.useContext(c);if(!e)throw new Error("Breach compound components must be wrapped in <Breach.Provider>. Example: <Breach.Provider categories={...}><Breach.ReportForm /></Breach.Provider>");return e}var p=({categories:e,adapter:m,storageKey:h,useLocalStorage:B,initialReports:f,onReport:R,onAssessment:x,onNotification:u,children:d})=>{let y=chunkVJTQXVAF_js.a({categories:e,adapter:m,storageKey:h,useLocalStorage:B,initialReports:f,onReport:R,onAssessment:x,onNotification:u}),l=chunkRFPLZDIO_js.b(chunkRFPLZDIO_js.a({},y),{categories:e});return jsxRuntime.jsx(c.Provider,{value:l,children:d})};var A={Provider:p,ReportForm:chunkO2RDZGM2_js.a,RiskAssessment:chunkPGI2LM6P_js.a,NotificationManager:chunkPGI2LM6P_js.b,ReportGenerator:chunkPGI2LM6P_js.c};Object.defineProperty(exports,"BreachNotificationManager",{enumerable:true,get:function(){return chunkPGI2LM6P_js.b}});Object.defineProperty(exports,"BreachRiskAssessment",{enumerable:true,get:function(){return chunkPGI2LM6P_js.a}});Object.defineProperty(exports,"RegulatoryReportGenerator",{enumerable:true,get:function(){return chunkPGI2LM6P_js.c}});Object.defineProperty(exports,"BreachReportForm",{enumerable:true,get:function(){return chunkO2RDZGM2_js.a}});Object.defineProperty(exports,"useBreach",{enumerable:true,get:function(){return chunkVJTQXVAF_js.a}});Object.defineProperty(exports,"calculateBreachSeverity",{enumerable:true,get:function(){return chunk3YTAOT5O_js.a}});exports.Breach=A;exports.BreachProvider=p;exports.useBreachCompound=P;
2
+ 'use strict';var chunkPGI2LM6P_js=require('./chunk-PGI2LM6P.js'),chunkO2RDZGM2_js=require('./chunk-O2RDZGM2.js'),chunkEHQVTFYO_js=require('./chunk-EHQVTFYO.js'),chunk3YTAOT5O_js=require('./chunk-3YTAOT5O.js');require('./chunk-UXUMYP4L.js'),require('./chunk-NDKDKDDX.js'),require('./chunk-WKTKTLMF.js'),require('./chunk-ZVOIR4QH.js'),require('./chunk-AME4HJR4.js'),require('./chunk-VWED6UTN.js');var chunkRFPLZDIO_js=require('./chunk-RFPLZDIO.js'),react=require('react'),jsxRuntime=require('react/jsx-runtime');var c=react.createContext(null);function P(){let e=react.useContext(c);if(!e)throw new Error("Breach compound components must be wrapped in <Breach.Provider>. Example: <Breach.Provider categories={...}><Breach.ReportForm /></Breach.Provider>");return e}var p=({categories:e,adapter:m,storageKey:h,useLocalStorage:B,initialReports:f,onReport:R,onAssessment:x,onNotification:u,children:d})=>{let y=chunkEHQVTFYO_js.a({categories:e,adapter:m,storageKey:h,useLocalStorage:B,initialReports:f,onReport:R,onAssessment:x,onNotification:u}),l=chunkRFPLZDIO_js.b(chunkRFPLZDIO_js.a({},y),{categories:e});return jsxRuntime.jsx(c.Provider,{value:l,children:d})};var A={Provider:p,ReportForm:chunkO2RDZGM2_js.a,RiskAssessment:chunkPGI2LM6P_js.a,NotificationManager:chunkPGI2LM6P_js.b,ReportGenerator:chunkPGI2LM6P_js.c};Object.defineProperty(exports,"BreachNotificationManager",{enumerable:true,get:function(){return chunkPGI2LM6P_js.b}});Object.defineProperty(exports,"BreachRiskAssessment",{enumerable:true,get:function(){return chunkPGI2LM6P_js.a}});Object.defineProperty(exports,"RegulatoryReportGenerator",{enumerable:true,get:function(){return chunkPGI2LM6P_js.c}});Object.defineProperty(exports,"BreachReportForm",{enumerable:true,get:function(){return chunkO2RDZGM2_js.a}});Object.defineProperty(exports,"useBreach",{enumerable:true,get:function(){return chunkEHQVTFYO_js.a}});Object.defineProperty(exports,"calculateBreachSeverity",{enumerable:true,get:function(){return chunk3YTAOT5O_js.a}});exports.Breach=A;exports.BreachProvider=p;exports.useBreachCompound=P;
package/dist/breach.mjs CHANGED
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import {c as c$1,b,a}from'./chunk-X3GCGC3H.mjs';export{b as BreachNotificationManager,a as BreachRiskAssessment,c as RegulatoryReportGenerator}from'./chunk-X3GCGC3H.mjs';import {a as a$1}from'./chunk-KY6WYHWB.mjs';export{a as BreachReportForm}from'./chunk-KY6WYHWB.mjs';import {a as a$2}from'./chunk-3HOXQNCH.mjs';export{a as useBreach}from'./chunk-3HOXQNCH.mjs';export{a as calculateBreachSeverity}from'./chunk-WTGKZX7J.mjs';import'./chunk-EWVK45Z3.mjs';import'./chunk-25TNTLHJ.mjs';import'./chunk-RBMLGRDN.mjs';import'./chunk-ITCY2Z66.mjs';import'./chunk-SFGW37LE.mjs';import'./chunk-DBZSN4WP.mjs';import {b as b$1,a as a$3}from'./chunk-ZJYULEER.mjs';import {createContext,useContext}from'react';import {jsx}from'react/jsx-runtime';var c=createContext(null);function P(){let e=useContext(c);if(!e)throw new Error("Breach compound components must be wrapped in <Breach.Provider>. Example: <Breach.Provider categories={...}><Breach.ReportForm /></Breach.Provider>");return e}var p=({categories:e,adapter:m,storageKey:h,useLocalStorage:B,initialReports:f,onReport:R,onAssessment:x,onNotification:u,children:d})=>{let y=a$2({categories:e,adapter:m,storageKey:h,useLocalStorage:B,initialReports:f,onReport:R,onAssessment:x,onNotification:u}),l=b$1(a$3({},y),{categories:e});return jsx(c.Provider,{value:l,children:d})};var A={Provider:p,ReportForm:a$1,RiskAssessment:a,NotificationManager:b,ReportGenerator:c$1};export{A as Breach,p as BreachProvider,P as useBreachCompound};
2
+ import {c as c$1,b,a}from'./chunk-X3GCGC3H.mjs';export{b as BreachNotificationManager,a as BreachRiskAssessment,c as RegulatoryReportGenerator}from'./chunk-X3GCGC3H.mjs';import {a as a$1}from'./chunk-KY6WYHWB.mjs';export{a as BreachReportForm}from'./chunk-KY6WYHWB.mjs';import {a as a$2}from'./chunk-RFXGD5NE.mjs';export{a as useBreach}from'./chunk-RFXGD5NE.mjs';export{a as calculateBreachSeverity}from'./chunk-WTGKZX7J.mjs';import'./chunk-EWVK45Z3.mjs';import'./chunk-25TNTLHJ.mjs';import'./chunk-RBMLGRDN.mjs';import'./chunk-ITCY2Z66.mjs';import'./chunk-SFGW37LE.mjs';import'./chunk-DBZSN4WP.mjs';import {b as b$1,a as a$3}from'./chunk-ZJYULEER.mjs';import {createContext,useContext}from'react';import {jsx}from'react/jsx-runtime';var c=createContext(null);function P(){let e=useContext(c);if(!e)throw new Error("Breach compound components must be wrapped in <Breach.Provider>. Example: <Breach.Provider categories={...}><Breach.ReportForm /></Breach.Provider>");return e}var p=({categories:e,adapter:m,storageKey:h,useLocalStorage:B,initialReports:f,onReport:R,onAssessment:x,onNotification:u,children:d})=>{let y=a$2({categories:e,adapter:m,storageKey:h,useLocalStorage:B,initialReports:f,onReport:R,onAssessment:x,onNotification:u}),l=b$1(a$3({},y),{categories:e});return jsx(c.Provider,{value:l,children:d})};var A={Provider:p,ReportForm:a$1,RiskAssessment:a,NotificationManager:b,ReportGenerator:c$1};export{A as Breach,p as BreachProvider,P as useBreachCompound};
@@ -1 +1 @@
1
- 'use strict';var chunkL2BRFMVS_js=require('./chunk-L2BRFMVS.js'),chunkVWED6UTN_js=require('./chunk-VWED6UTN.js'),chunkRFPLZDIO_js=require('./chunk-RFPLZDIO.js'),react=require('react');function K(f,R){return R?chunkVWED6UTN_js.a(f):{load:()=>null,save:()=>{},remove:()=>{}}}function G({initialRequests:f=[],requestTypes:R,adapter:p,storageKey:T="ndpr_dsr_requests",useLocalStorage:h=true,onSubmit:g,onUpdate:m}){let y=p!=null?p:K(T,h),a=react.useRef(y);a.current=y;let[n,i]=react.useState(f),[k,l]=react.useState(true);react.useEffect(()=>{let e=false;try{let t=a.current.load();t instanceof Promise?t.then(s=>{e||(s&&i(s),l(!1));},()=>{e||l(!1);}):(t&&i(t),l(!1));}catch(t){e||l(false);}return ()=>{e=true;}},[]);let c=react.useCallback(e=>{Promise.resolve(a.current.save(e)).catch(t=>{console.warn("[ndpr-toolkit] Failed to save DSR requests:",t);});},[]),P=()=>"dsr_"+Date.now()+"_"+Math.random().toString(36).substr(2,9),x=react.useCallback(e=>{let t=R.find(o=>o.id===e.type),s=(t==null?void 0:t.estimatedCompletionTime)||30,r=Date.now(),d=r+s*24*60*60*1e3,q=chunkRFPLZDIO_js.a({id:P(),status:"pending",createdAt:r,updatedAt:r,dueDate:d},e);return i(o=>{let S=[...o,q];return c(S),S}),g&&g(q),q},[R,c,g]),B=react.useCallback((e,t)=>{let s=null;return i(r=>{let d=r.findIndex(S=>S.id===e);if(d===-1)return r;let q=r[d];s=chunkRFPLZDIO_js.b(chunkRFPLZDIO_js.a(chunkRFPLZDIO_js.a({},q),t),{updatedAt:Date.now()});let o=[...r];return o[d]=s,c(o),o}),s&&m&&m(s),s},[c,m]),C=react.useCallback(e=>n.find(t=>t.id===e)||null,[n]),I=react.useCallback(e=>n.filter(t=>t.status===e),[n]),L=react.useCallback(e=>n.filter(t=>t.type===e),[n]),O=react.useCallback(e=>R.find(t=>t.id===e),[R]),_=react.useCallback(e=>{let{formattedRequest:t}=chunkL2BRFMVS_js.b(e);return t},[]),U=react.useCallback(()=>{i([]),Promise.resolve(a.current.remove()).catch(e=>{console.warn("[ndpr-toolkit] Failed to remove DSR requests:",e);});},[a]);return {requests:n,submitRequest:x,updateRequest:B,getRequest:C,getRequestsByStatus:I,getRequestsByType:L,getRequestType:O,formatRequest:_,clearRequests:U,isLoading:k}}exports.a=G;
1
+ 'use strict';var chunkOZHUINWS_js=require('./chunk-OZHUINWS.js'),chunkVWED6UTN_js=require('./chunk-VWED6UTN.js'),chunkRFPLZDIO_js=require('./chunk-RFPLZDIO.js'),react=require('react');function K(f,R){return R?chunkVWED6UTN_js.a(f):{load:()=>null,save:()=>{},remove:()=>{}}}function G({initialRequests:f=[],requestTypes:R,adapter:p,storageKey:T="ndpr_dsr_requests",useLocalStorage:h=true,onSubmit:g,onUpdate:m}){let y=p!=null?p:K(T,h),a=react.useRef(y);a.current=y;let[n,i]=react.useState(f),[k,l]=react.useState(true);react.useEffect(()=>{let e=false;try{let t=a.current.load();t instanceof Promise?t.then(s=>{e||(s&&i(s),l(!1));},()=>{e||l(!1);}):(t&&i(t),l(!1));}catch(t){e||l(false);}return ()=>{e=true;}},[]);let c=react.useCallback(e=>{Promise.resolve(a.current.save(e)).catch(t=>{console.warn("[ndpr-toolkit] Failed to save DSR requests:",t);});},[]),x=()=>"dsr_"+Date.now()+"_"+Math.random().toString(36).substr(2,9),P=react.useCallback(e=>{let t=R.find(o=>o.id===e.type),s=(t==null?void 0:t.estimatedCompletionTime)||30,r=Date.now(),d=r+s*24*60*60*1e3,q=chunkRFPLZDIO_js.a({id:x(),status:"pending",createdAt:r,updatedAt:r,dueDate:d},e);return i(o=>{let S=[...o,q];return c(S),S}),g&&g(q),q},[R,c,g]),B=react.useCallback((e,t)=>{let s=null;return i(r=>{let d=r.findIndex(S=>S.id===e);if(d===-1)return r;let q=r[d];s=chunkRFPLZDIO_js.b(chunkRFPLZDIO_js.a(chunkRFPLZDIO_js.a({},q),t),{updatedAt:Date.now()});let o=[...r];return o[d]=s,c(o),o}),s&&m&&m(s),s},[c,m]),C=react.useCallback(e=>n.find(t=>t.id===e)||null,[n]),I=react.useCallback(e=>n.filter(t=>t.status===e),[n]),L=react.useCallback(e=>n.filter(t=>t.type===e),[n]),O=react.useCallback(e=>R.find(t=>t.id===e),[R]),_=react.useCallback(e=>{let{formattedRequest:t}=chunkOZHUINWS_js.b(e);return t},[]),U=react.useCallback(()=>{i([]),Promise.resolve(a.current.remove()).catch(e=>{console.warn("[ndpr-toolkit] Failed to remove DSR requests:",e);});},[a]);return {requests:n,submitRequest:P,updateRequest:B,getRequest:C,getRequestsByStatus:I,getRequestsByType:L,getRequestType:O,formatRequest:_,clearRequests:U,isLoading:k}}exports.a=G;
@@ -1 +1 @@
1
- import {d,a as a$1}from'./chunk-LWIKDDSU.mjs';import {a as a$2}from'./chunk-DBZSN4WP.mjs';import {b,a}from'./chunk-ZJYULEER.mjs';import {useRef,useState,useEffect,useCallback}from'react';function F(v,c){return c?a$2(v):{load:()=>null,save:()=>{},remove:()=>{}}}function z({initialActivities:v=[],adapter:c,storageKey:b$1="ndpr_lawful_basis_activities",useLocalStorage:R=true,onAdd:g,onUpdate:A,onRemove:y}={}){let p=c!=null?c:F(b$1,R),f=useRef(p);f.current=p;let[o,u]=useState(v),[x,l]=useState(true);useEffect(()=>{let t=false;try{let i=f.current.load();i instanceof Promise?i.then(e=>{t||(e&&u(e),l(!1));},()=>{t||l(!1);}):(i&&u(i),l(!1));}catch(i){t||l(false);}return ()=>{t=true;}},[]);let a$2=useCallback(t=>{Promise.resolve(f.current.save(t)).catch(i=>{console.warn("[ndpr-toolkit] Failed to save lawful basis activities:",i);});},[]),_=()=>"lb_"+Date.now()+"_"+Math.random().toString(36).substring(2,11),h=useCallback(t=>{let i=Date.now(),e=b(a({},t),{id:_(),createdAt:i,updatedAt:i});return u(s=>{let r=[...s,e];return a$2(r),r}),g&&g(e),e},[g,a$2]),O=useCallback((t,i)=>{let e=null;return u(s=>{let r=s.findIndex(k=>k.id===t);if(r===-1)return s;e=b(a(a({},s[r]),i),{id:s[r].id,updatedAt:Date.now()});let P=[...s];return P[r]=e,a$2(P),P}),e&&A&&A(e),e},[A,a$2]),D=useCallback(t=>{u(i=>{let e=i.filter(s=>s.id!==t);return a$2(e),e}),y&&y(t);},[y,a$2]),I=useCallback(t=>o.find(i=>i.id===t)||null,[o]),U=useCallback(()=>d(o),[o]),V=useCallback(t=>a$1(t),[]);return {activities:o,addActivity:h,updateActivity:O,removeActivity:D,getActivity:I,getSummary:U,validateActivity:V,isLoading:x}}export{z as a};
1
+ import {d,a as a$1}from'./chunk-LWIKDDSU.mjs';import {a as a$2}from'./chunk-DBZSN4WP.mjs';import {b,a}from'./chunk-ZJYULEER.mjs';import {useRef,useState,useEffect,useCallback}from'react';function F(v,c){return c?a$2(v):{load:()=>null,save:()=>{},remove:()=>{}}}function z({initialActivities:v=[],adapter:c,storageKey:b$1="ndpr_lawful_basis_activities",useLocalStorage:x=true,onAdd:g,onUpdate:A,onRemove:y}={}){let p=c!=null?c:F(b$1,x),f=useRef(p);f.current=p;let[o,u]=useState(v),[R,l]=useState(true);useEffect(()=>{let t=false;try{let i=f.current.load();i instanceof Promise?i.then(e=>{t||(e&&u(e),l(!1));},()=>{t||l(!1);}):(i&&u(i),l(!1));}catch(i){t||l(false);}return ()=>{t=true;}},[]);let a$2=useCallback(t=>{Promise.resolve(f.current.save(t)).catch(i=>{console.warn("[ndpr-toolkit] Failed to save lawful basis activities:",i);});},[]),_=()=>"lb_"+Date.now()+"_"+Math.random().toString(36).substring(2,11),h=useCallback(t=>{let i=Date.now(),e=b(a({},t),{id:_(),createdAt:i,updatedAt:i});return u(s=>{let r=[...s,e];return a$2(r),r}),g&&g(e),e},[g,a$2]),O=useCallback((t,i)=>{let e=null;return u(s=>{let r=s.findIndex(k=>k.id===t);if(r===-1)return s;e=b(a(a({},s[r]),i),{id:s[r].id,updatedAt:Date.now()});let P=[...s];return P[r]=e,a$2(P),P}),e&&A&&A(e),e},[A,a$2]),D=useCallback(t=>{u(i=>{let e=i.filter(s=>s.id!==t);return a$2(e),e}),y&&y(t);},[y,a$2]),I=useCallback(t=>o.find(i=>i.id===t)||null,[o]),U=useCallback(()=>d(o),[o]),V=useCallback(t=>a$1(t),[]);return {activities:o,addActivity:h,updateActivity:O,removeActivity:D,getActivity:I,getSummary:U,validateActivity:V,isLoading:R}}export{z as a};