Migrating from 4.1.x to 5.0.0
Four breaking changes — all aliased and dev-warned in 4.1.0. If your 4.1.x dev console is clean, your 5.0 upgrade is a one-line bump.
TL;DR
5.0 closes the deprecation window opened by 4.1.0. Every removal here was already deprecated and dev-warned a minor release ago, so the upgrade path is:
- On 4.1.x, run your dev server. Any deprecated prop, callback, or function fires a one-shot
[ndpr-toolkit/<module>]warning in the console. - Fix each warning at its callsite.
- Bump to
^5.0.0.
pnpm up @tantainnovative/ndpr-toolkit@^5.0.0
# or
bun add @tantainnovative/ndpr-toolkit@^5.0.0
# or
npm install @tantainnovative/ndpr-toolkit@^5.0.0If you skipped 4.1, read the 4.1 release notes first — every removal here had a one-minor window where both shapes worked, but a 4.0 → 5.0 jump means making the changes blind.
The breaking changes
1. Uniform list-manager callbacks: onAdd / onUpdate / onArchive
The three list-manager components and the useROPA hook each had module-specific callback names. 4.1 added uniform onAdd/onUpdate/onArchive names alongside the legacy ones. 5.0 drops the legacy names entirely.
| Component / Hook | Removed (4.x) | Use instead (5.0) |
|---|---|---|
LawfulBasisTracker | onAddActivity / onUpdateActivity / onArchiveActivity | onAdd / onUpdate / onArchive |
CrossBorderTransferManager | onAddTransfer / onUpdateTransfer / onRemoveTransfer | onAdd / onUpdate / onArchive |
ROPAManager | onAddRecord / onUpdateRecord / onArchiveRecord | onAdd / onUpdate / onArchive |
useROPA options | onRecordAdd / onRecordUpdate / onRecordArchive | onAdd / onUpdate / onArchive |
// Before (4.x)
<LawfulBasisTracker
activities={activities}
onAddActivity={handleAdd}
onUpdateActivity={handleUpdate}
onArchiveActivity={handleArchive}
/>
// After (5.0)
<LawfulBasisTracker
activities={activities}
onAdd={handleAdd}
onUpdate={handleUpdate}
onArchive={handleArchive}
/>Note: CrossBorderTransferManager's legacy callback was onRemoveTransfer but the canonical name is onArchive — the NDPA prefers soft-delete with audit trails over hard removal, and the new name matches what the component actually does.
2. NDPRDPIA: onComplete(answers) → onResult(result)
The 4.x onComplete callback fired with just the raw answer map, throwing away the computed risk assessment. The new onResult receives the full DPIAResult — overall risk level, can-proceed verdict, conclusion, recommendations — so consumers can branch on outcome without re-running the assessment.
// Before (4.x)
<NDPRDPIA
sections={sections}
onComplete={(answers) => {
// had to re-run assessDPIARisk() yourself to get risk score
const result = assessDPIARisk(answers);
if (result.overallRiskLevel === 'high') notifyDpo(result);
}}
/>
// After (5.0)
<NDPRDPIA
sections={sections}
onResult={(result) => {
if (result.overallRiskLevel === 'high') notifyDpo(result);
// result.answers, result.overallRiskLevel, result.canProceed,
// result.conclusion, result.recommendations all available
}}
/>3. ROPAManagerLite: records → ropa
ROPAManagerLite previously took a flat records: ProcessingRecord[]. The full ROPAManager takes a ropa: RecordOfProcessingActivities object (records + metadata). 5.0 unifies the API on the richer shape.
// Before (4.x)
<ROPAManagerLite records={records} />
// After (5.0)
<ROPAManagerLite
ropa={{
records,
lastUpdated: Date.now(),
version: '1.0',
// ...other RecordOfProcessingActivities metadata
}}
/>4. Validators return structured errors, not English strings
Four validator functions were renamed to drop the Structured suffix and their legacy string-returning counterparts were removed. The new shape returns { valid; errors: Array<{ field, code, message }>; data? } — locale-independent and switch-able on stable codes.
| Removed (4.x) | Use instead (5.0) |
|---|---|
validateConsent | validateConsentStructured |
validateConsentOptions | validateConsentOptionsStructured |
validateDsrSubmission | validateDsrSubmissionStructured |
formatDSRRequest | formatDSRRequestStructured |
// Before (4.x) — Next.js Route Handler
import { validateDsrSubmission } from '@tantainnovative/ndpr-toolkit/server';
export async function POST(req: Request) {
const { valid, errors, data } = validateDsrSubmission(await req.json());
if (!valid) {
// errors is Record<string, string> of English messages
return Response.json({ errors }, { status: 422 });
}
await dsrStore.create(data);
return Response.json({ ok: true }, { status: 201 });
}
// After (5.0)
import { validateDsrSubmissionStructured } from '@tantainnovative/ndpr-toolkit/server';
export async function POST(req: Request) {
const { valid, errors, data } = validateDsrSubmissionStructured(await req.json());
if (!valid) {
// errors is Array<{ field, code, message }> — switch on `code`
return Response.json({ errors }, { status: 422 });
}
await dsrStore.create(data);
return Response.json({ ok: true }, { status: 201 });
}The error codevalues are stable and locale-independent — branch on them in client code instead of regex-matching English. Each validator's complete list of emitted codes is documented in its JSDoc.
What hasn't changed
- Peer dependency range: still
react ^18 || ^19. - Subpath exports: all 22 still resolve in ESM + CJS + TS.
- Locales: same seven shipped (en, yo, ig, ha, pcm, ar, fr).
- The
NDPRProvider/NDPRThemeProviderAPIs. - The cookie / localStorage / api / memory adapter contracts.
- The Lite variant set (consent, lawful-basis, cross-border, ROPA).
Upgrade checklist
- Pin to
^4.1.0if you're not already. - Run your test suite and dev server. Watch the console for
[ndpr-toolkit/...]deprecation warnings. - For each warning, fix the callsite using the tables above.
- Re-run tests until the console is clean.
- Bump to
^5.0.0. - Re-run tests one more time.
Stuck? Open an issue at github.com/mr-tanta/ndpr-toolkit/issues.