@tantainnovative/ndpr-toolkit 4.0.0 → 5.0.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 (83) hide show
  1. package/CHANGELOG.md +123 -0
  2. package/dist/chunk-4FXRJH37.js +1 -0
  3. package/dist/{chunk-3XU6FL2I.js → chunk-6YFPDGNB.js} +1 -1
  4. package/dist/{chunk-WMISQDSV.js → chunk-ASEUTU45.js} +1 -1
  5. package/dist/chunk-BRJKIF7E.mjs +1 -0
  6. package/dist/{chunk-XYGGUGTO.js → chunk-CSE36REY.js} +4 -4
  7. package/dist/chunk-DKLJ5DYN.js +1 -0
  8. package/dist/chunk-FRMVSG4N.mjs +1 -0
  9. package/dist/chunk-MAD7QYRK.js +1 -0
  10. package/dist/chunk-NBOJ2KGN.mjs +1 -0
  11. package/dist/chunk-PQ5IPUJN.mjs +1 -0
  12. package/dist/{chunk-BGV2AKT7.mjs → chunk-PSPYIRIF.mjs} +1 -1
  13. package/dist/chunk-QKXGVT2Q.js +1 -0
  14. package/dist/chunk-R2ZZMATR.js +1 -0
  15. package/dist/chunk-R3ZKV2J7.mjs +1 -0
  16. package/dist/chunk-RRVML7CU.mjs +1 -0
  17. package/dist/{chunk-NB6SKG76.mjs → chunk-SBSYHCPK.mjs} +1 -1
  18. package/dist/chunk-TLIHFGIJ.js +1 -0
  19. package/dist/{chunk-5F2IAUWJ.js → chunk-TVA6D6S4.js} +1 -1
  20. package/dist/{chunk-42JPSNVV.mjs → chunk-WPH6CJDL.mjs} +4 -4
  21. package/dist/{chunk-GUERZD4O.mjs → chunk-ZSRO4L3C.mjs} +1 -1
  22. package/dist/consent.d.mts +59 -19
  23. package/dist/consent.d.ts +59 -19
  24. package/dist/consent.js +1 -1
  25. package/dist/consent.mjs +1 -1
  26. package/dist/core.d.mts +124 -69
  27. package/dist/core.d.ts +124 -69
  28. package/dist/core.js +1 -1
  29. package/dist/core.mjs +1 -1
  30. package/dist/cross-border.d.mts +7 -6
  31. package/dist/cross-border.d.ts +7 -6
  32. package/dist/cross-border.js +1 -1
  33. package/dist/cross-border.mjs +1 -1
  34. package/dist/dsr.d.mts +86 -54
  35. package/dist/dsr.d.ts +86 -54
  36. package/dist/dsr.js +1 -1
  37. package/dist/dsr.mjs +1 -1
  38. package/dist/headless.d.mts +23 -9
  39. package/dist/headless.d.ts +23 -9
  40. package/dist/headless.js +1 -1
  41. package/dist/headless.mjs +1 -1
  42. package/dist/hooks.d.mts +23 -9
  43. package/dist/hooks.d.ts +23 -9
  44. package/dist/hooks.js +1 -1
  45. package/dist/hooks.mjs +1 -1
  46. package/dist/index.d.mts +144 -95
  47. package/dist/index.d.ts +144 -95
  48. package/dist/index.js +1 -1
  49. package/dist/index.mjs +1 -1
  50. package/dist/lawful-basis.d.mts +6 -6
  51. package/dist/lawful-basis.d.ts +6 -6
  52. package/dist/lawful-basis.js +1 -1
  53. package/dist/lawful-basis.mjs +1 -1
  54. package/dist/presets.d.mts +87 -2
  55. package/dist/presets.d.ts +87 -2
  56. package/dist/presets.js +1 -1
  57. package/dist/presets.mjs +1 -1
  58. package/dist/ropa-lite.d.mts +4 -1
  59. package/dist/ropa-lite.d.ts +4 -1
  60. package/dist/ropa-lite.js +1 -1
  61. package/dist/ropa-lite.mjs +1 -1
  62. package/dist/ropa.d.mts +16 -16
  63. package/dist/ropa.d.ts +16 -16
  64. package/dist/ropa.js +1 -1
  65. package/dist/ropa.mjs +1 -1
  66. package/dist/server.d.mts +123 -68
  67. package/dist/server.d.ts +123 -68
  68. package/dist/server.js +1 -1
  69. package/dist/server.mjs +1 -1
  70. package/dist/styles.css +18 -16
  71. package/package.json +1 -1
  72. package/dist/chunk-7FXNGGMO.js +0 -1
  73. package/dist/chunk-B46SJB5V.js +0 -1
  74. package/dist/chunk-BL5W472Q.js +0 -1
  75. package/dist/chunk-HOAC5VUF.js +0 -1
  76. package/dist/chunk-IHNAFXDM.mjs +0 -1
  77. package/dist/chunk-O2WEABB3.js +0 -1
  78. package/dist/chunk-OZHUINWS.js +0 -1
  79. package/dist/chunk-PJNKQPQP.mjs +0 -1
  80. package/dist/chunk-RBKFNCGO.mjs +0 -1
  81. package/dist/chunk-VPNK7OID.mjs +0 -1
  82. package/dist/chunk-WKUC65HL.mjs +0 -1
  83. package/dist/chunk-Z2M5VJX2.mjs +0 -1
package/dist/hooks.d.ts CHANGED
@@ -1343,6 +1343,19 @@ declare interface StorageAdapter<T = unknown> {
1343
1343
  remove(): void | Promise<void>;
1344
1344
  }
1345
1345
 
1346
+ /**
1347
+ * Single structured validation error with a stable, locale-independent
1348
+ * `code` consumers can switch on programmatically.
1349
+ */
1350
+ declare interface StructuredValidationError {
1351
+ /** Dot-path of the offending field (e.g. `'timestamp'`, `'dataSubject.email'`, `'options[0].purpose'`). */
1352
+ field: string;
1353
+ /** Stable, snake_case error code — safe to switch on across locales. */
1354
+ code: string;
1355
+ /** Human-readable English message — informational only; do not regex-match. */
1356
+ message: string;
1357
+ }
1358
+
1346
1359
  /** Full context used to generate an adaptive privacy policy. */
1347
1360
  declare interface TemplateContext {
1348
1361
  /** Organisation details, extended with industry and size. */
@@ -1718,9 +1731,10 @@ export declare interface UseConsentReturn {
1718
1731
  */
1719
1732
  isValid: boolean;
1720
1733
  /**
1721
- * Validation errors (if any)
1734
+ * Validation errors (if any). Each entry is a structured `{ field, code,
1735
+ * message }` so consumers can switch on `code` across locales.
1722
1736
  */
1723
- validationErrors: string[];
1737
+ validationErrors: StructuredValidationError[];
1724
1738
  /**
1725
1739
  * Reset consent settings (clear from storage)
1726
1740
  */
@@ -2389,7 +2403,7 @@ export declare interface UsePrivacyPolicyReturn {
2389
2403
  * }
2390
2404
  * ```
2391
2405
  */
2392
- export declare function useROPA({ initialData, adapter, onRecordAdd, onRecordUpdate, onRecordArchive, }: UseROPAOptions): UseROPAReturn;
2406
+ export declare function useROPA({ initialData, adapter, onAdd, onUpdate, onArchive, }: UseROPAOptions): UseROPAReturn;
2393
2407
 
2394
2408
  export declare interface UseROPAOptions {
2395
2409
  /**
@@ -2403,17 +2417,17 @@ export declare interface UseROPAOptions {
2403
2417
  */
2404
2418
  adapter?: StorageAdapter<RecordOfProcessingActivities>;
2405
2419
  /**
2406
- * Callback when a record is added
2420
+ * Callback when a record is added.
2407
2421
  */
2408
- onRecordAdd?: (record: ProcessingRecord) => void;
2422
+ onAdd?: (record: ProcessingRecord) => void;
2409
2423
  /**
2410
- * Callback when a record is updated
2424
+ * Callback when a record is updated.
2411
2425
  */
2412
- onRecordUpdate?: (id: string, updates: Partial<ProcessingRecord>) => void;
2426
+ onUpdate?: (id: string, updates: Partial<ProcessingRecord>) => void;
2413
2427
  /**
2414
- * Callback when a record is archived
2428
+ * Callback when a record is archived.
2415
2429
  */
2416
- onRecordArchive?: (id: string) => void;
2430
+ onArchive?: (id: string) => void;
2417
2431
  }
2418
2432
 
2419
2433
  export declare interface UseROPAReturn {
package/dist/hooks.js CHANGED
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- 'use strict';require('./chunk-OZCNFB5C.js');var chunkFC3PTJFL_js=require('./chunk-FC3PTJFL.js'),chunkEHQVTFYO_js=require('./chunk-EHQVTFYO.js'),chunkRC3XFXTJ_js=require('./chunk-RC3XFXTJ.js'),chunkQHW4UKGJ_js=require('./chunk-QHW4UKGJ.js'),chunkIRRUYR6M_js=require('./chunk-IRRUYR6M.js'),chunkHHK5LHEG_js=require('./chunk-HHK5LHEG.js'),chunkO2WEABB3_js=require('./chunk-O2WEABB3.js');require('./chunk-JS7SYL5P.js'),require('./chunk-RDALAH3Y.js'),require('./chunk-3YTAOT5O.js'),require('./chunk-D2ZKDQVL.js'),require('./chunk-6LJHLE6G.js'),require('./chunk-YFBDJ4FH.js'),require('./chunk-WZYCBW2R.js'),require('./chunk-4CVBQC66.js');var chunkHOAC5VUF_js=require('./chunk-HOAC5VUF.js'),chunkL2VO3MEJ_js=require('./chunk-L2VO3MEJ.js');require('./chunk-C2KEXHRX.js'),require('./chunk-B46SJB5V.js');var chunk5F2IAUWJ_js=require('./chunk-5F2IAUWJ.js');require('./chunk-OZHUINWS.js');var chunkJLQT3W3E_js=require('./chunk-JLQT3W3E.js');require('./chunk-TQZWJGJ2.js'),require('./chunk-ZVOIR4QH.js'),require('./chunk-VWED6UTN.js'),require('./chunk-RFPLZDIO.js');Object.defineProperty(exports,"useComplianceScore",{enumerable:true,get:function(){return chunkFC3PTJFL_js.a}});Object.defineProperty(exports,"useBreach",{enumerable:true,get:function(){return chunkEHQVTFYO_js.a}});Object.defineProperty(exports,"useDefaultPrivacyPolicy",{enumerable:true,get:function(){return chunkRC3XFXTJ_js.b}});Object.defineProperty(exports,"usePrivacyPolicy",{enumerable:true,get:function(){return chunkRC3XFXTJ_js.a}});Object.defineProperty(exports,"useAdaptivePolicyWizard",{enumerable:true,get:function(){return chunkQHW4UKGJ_js.a}});Object.defineProperty(exports,"useLawfulBasis",{enumerable:true,get:function(){return chunkIRRUYR6M_js.a}});Object.defineProperty(exports,"useCrossBorderTransfer",{enumerable:true,get:function(){return chunkHHK5LHEG_js.a}});Object.defineProperty(exports,"useROPA",{enumerable:true,get:function(){return chunkO2WEABB3_js.a}});Object.defineProperty(exports,"useConsent",{enumerable:true,get:function(){return chunkHOAC5VUF_js.a}});Object.defineProperty(exports,"useFocusTrap",{enumerable:true,get:function(){return chunkL2VO3MEJ_js.a}});Object.defineProperty(exports,"useDSR",{enumerable:true,get:function(){return chunk5F2IAUWJ_js.a}});Object.defineProperty(exports,"useDPIA",{enumerable:true,get:function(){return chunkJLQT3W3E_js.a}});
2
+ 'use strict';require('./chunk-OZCNFB5C.js');var chunkFC3PTJFL_js=require('./chunk-FC3PTJFL.js'),chunkEHQVTFYO_js=require('./chunk-EHQVTFYO.js'),chunkRC3XFXTJ_js=require('./chunk-RC3XFXTJ.js'),chunkQHW4UKGJ_js=require('./chunk-QHW4UKGJ.js'),chunkIRRUYR6M_js=require('./chunk-IRRUYR6M.js'),chunkHHK5LHEG_js=require('./chunk-HHK5LHEG.js'),chunkTLIHFGIJ_js=require('./chunk-TLIHFGIJ.js');require('./chunk-JS7SYL5P.js'),require('./chunk-RDALAH3Y.js'),require('./chunk-3YTAOT5O.js'),require('./chunk-D2ZKDQVL.js'),require('./chunk-6LJHLE6G.js'),require('./chunk-YFBDJ4FH.js'),require('./chunk-WZYCBW2R.js'),require('./chunk-4CVBQC66.js');var chunkQKXGVT2Q_js=require('./chunk-QKXGVT2Q.js'),chunkL2VO3MEJ_js=require('./chunk-L2VO3MEJ.js');require('./chunk-C2KEXHRX.js'),require('./chunk-DKLJ5DYN.js');var chunkTVA6D6S4_js=require('./chunk-TVA6D6S4.js');require('./chunk-R2ZZMATR.js');var chunkJLQT3W3E_js=require('./chunk-JLQT3W3E.js');require('./chunk-TQZWJGJ2.js'),require('./chunk-ZVOIR4QH.js'),require('./chunk-VWED6UTN.js'),require('./chunk-RFPLZDIO.js');Object.defineProperty(exports,"useComplianceScore",{enumerable:true,get:function(){return chunkFC3PTJFL_js.a}});Object.defineProperty(exports,"useBreach",{enumerable:true,get:function(){return chunkEHQVTFYO_js.a}});Object.defineProperty(exports,"useDefaultPrivacyPolicy",{enumerable:true,get:function(){return chunkRC3XFXTJ_js.b}});Object.defineProperty(exports,"usePrivacyPolicy",{enumerable:true,get:function(){return chunkRC3XFXTJ_js.a}});Object.defineProperty(exports,"useAdaptivePolicyWizard",{enumerable:true,get:function(){return chunkQHW4UKGJ_js.a}});Object.defineProperty(exports,"useLawfulBasis",{enumerable:true,get:function(){return chunkIRRUYR6M_js.a}});Object.defineProperty(exports,"useCrossBorderTransfer",{enumerable:true,get:function(){return chunkHHK5LHEG_js.a}});Object.defineProperty(exports,"useROPA",{enumerable:true,get:function(){return chunkTLIHFGIJ_js.a}});Object.defineProperty(exports,"useConsent",{enumerable:true,get:function(){return chunkQKXGVT2Q_js.a}});Object.defineProperty(exports,"useFocusTrap",{enumerable:true,get:function(){return chunkL2VO3MEJ_js.a}});Object.defineProperty(exports,"useDSR",{enumerable:true,get:function(){return chunkTVA6D6S4_js.a}});Object.defineProperty(exports,"useDPIA",{enumerable:true,get:function(){return chunkJLQT3W3E_js.a}});
package/dist/hooks.mjs CHANGED
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import'./chunk-GQYBS3A7.mjs';export{a as useComplianceScore}from'./chunk-IQO3SIAG.mjs';export{a as useBreach}from'./chunk-RFXGD5NE.mjs';export{b as useDefaultPrivacyPolicy,a as usePrivacyPolicy}from'./chunk-UVXS7KRV.mjs';export{a as useAdaptivePolicyWizard}from'./chunk-KE2FZH2V.mjs';export{a as useLawfulBasis}from'./chunk-B6BRD5SL.mjs';export{a as useCrossBorderTransfer}from'./chunk-KDAZQO3N.mjs';export{a as useROPA}from'./chunk-WKUC65HL.mjs';import'./chunk-IVSNHT24.mjs';import'./chunk-7RBO42IW.mjs';import'./chunk-WTGKZX7J.mjs';import'./chunk-NBQQ2GN3.mjs';import'./chunk-BIJSMSUU.mjs';import'./chunk-7BJXI2HI.mjs';import'./chunk-LWIKDDSU.mjs';import'./chunk-XP5PL6K7.mjs';export{a as useConsent}from'./chunk-IHNAFXDM.mjs';export{a as useFocusTrap}from'./chunk-YTU4FNM2.mjs';import'./chunk-XC3DLYEG.mjs';import'./chunk-PJNKQPQP.mjs';export{a as useDSR}from'./chunk-GUERZD4O.mjs';import'./chunk-VPNK7OID.mjs';export{a as useDPIA}from'./chunk-I3V3ITN7.mjs';import'./chunk-LRRENTT5.mjs';import'./chunk-ITCY2Z66.mjs';import'./chunk-DBZSN4WP.mjs';import'./chunk-ZJYULEER.mjs';
2
+ import'./chunk-GQYBS3A7.mjs';export{a as useComplianceScore}from'./chunk-IQO3SIAG.mjs';export{a as useBreach}from'./chunk-RFXGD5NE.mjs';export{b as useDefaultPrivacyPolicy,a as usePrivacyPolicy}from'./chunk-UVXS7KRV.mjs';export{a as useAdaptivePolicyWizard}from'./chunk-KE2FZH2V.mjs';export{a as useLawfulBasis}from'./chunk-B6BRD5SL.mjs';export{a as useCrossBorderTransfer}from'./chunk-KDAZQO3N.mjs';export{a as useROPA}from'./chunk-FRMVSG4N.mjs';import'./chunk-IVSNHT24.mjs';import'./chunk-7RBO42IW.mjs';import'./chunk-WTGKZX7J.mjs';import'./chunk-NBQQ2GN3.mjs';import'./chunk-BIJSMSUU.mjs';import'./chunk-7BJXI2HI.mjs';import'./chunk-LWIKDDSU.mjs';import'./chunk-XP5PL6K7.mjs';export{a as useConsent}from'./chunk-PQ5IPUJN.mjs';export{a as useFocusTrap}from'./chunk-YTU4FNM2.mjs';import'./chunk-XC3DLYEG.mjs';import'./chunk-R3ZKV2J7.mjs';export{a as useDSR}from'./chunk-ZSRO4L3C.mjs';import'./chunk-RRVML7CU.mjs';export{a as useDPIA}from'./chunk-I3V3ITN7.mjs';import'./chunk-LRRENTT5.mjs';import'./chunk-ITCY2Z66.mjs';import'./chunk-DBZSN4WP.mjs';import'./chunk-ZJYULEER.mjs';
package/dist/index.d.mts CHANGED
@@ -1329,17 +1329,18 @@ export declare interface CrossBorderTransferManagerProps {
1329
1329
  */
1330
1330
  transfers: CrossBorderTransfer[];
1331
1331
  /**
1332
- * Callback when a new transfer is added
1332
+ * Callback when a new transfer is added.
1333
1333
  */
1334
- onAddTransfer?: (transfer: Omit<CrossBorderTransfer, 'id' | 'createdAt' | 'updatedAt'>) => void;
1334
+ onAdd?: (transfer: Omit<CrossBorderTransfer, 'id' | 'createdAt' | 'updatedAt'>) => void;
1335
1335
  /**
1336
- * Callback when a transfer is updated
1336
+ * Callback when a transfer is updated.
1337
1337
  */
1338
- onUpdateTransfer?: (id: string, updates: Partial<CrossBorderTransfer>) => void;
1338
+ onUpdate?: (id: string, updates: Partial<CrossBorderTransfer>) => void;
1339
1339
  /**
1340
- * Callback when a transfer is removed
1340
+ * Callback when a transfer is archived. NDPA preference is soft-delete
1341
+ * over hard-delete; consumers should treat this as an archive signal.
1341
1342
  */
1342
- onRemoveTransfer?: (id: string) => void;
1343
+ onArchive?: (id: string) => void;
1343
1344
  /**
1344
1345
  * Compliance summary data
1345
1346
  */
@@ -2077,7 +2078,7 @@ export declare type DSRStatus = 'pending' | 'awaitingVerification' | 'inProgress
2077
2078
  /**
2078
2079
  * Validated DSR submission shape — matches what `<DSRRequestForm onSubmit>`
2079
2080
  * emits client-side. Use this as the typed parameter for your server-side
2080
- * handler after `validateDsrSubmission` returns `valid: true`.
2081
+ * handler after `validateDsrSubmissionStructured` returns `valid: true`.
2081
2082
  */
2082
2083
  export declare interface DsrSubmissionPayload {
2083
2084
  requestType: string;
@@ -2092,16 +2093,6 @@ export declare interface DsrSubmissionPayload {
2092
2093
  submittedAt: number;
2093
2094
  }
2094
2095
 
2095
- /** Result of validating a raw DSR submission payload. */
2096
- export declare interface DsrSubmissionValidationResult {
2097
- /** True when the payload conforms to the DSR submission contract. */
2098
- valid: boolean;
2099
- /** Field-keyed error messages. Empty when `valid` is true. */
2100
- errors: Record<string, string>;
2101
- /** The narrowed, typed payload — only populated when `valid` is true. */
2102
- data?: DsrSubmissionPayload;
2103
- }
2104
-
2105
2096
  /**
2106
2097
  * DSR tracking and analytics component. Supports compliance with NDPA Part IV,
2107
2098
  * providing summary statistics, deadline tracking, and compliance metrics for data subject requests.
@@ -2216,15 +2207,29 @@ export declare type EffortLevel = 'low' | 'medium' | 'high';
2216
2207
  export declare function exportROPAToCSV(ropa: RecordOfProcessingActivities): string;
2217
2208
 
2218
2209
  /**
2219
- * Formats a DSR request for display or submission
2220
- * @param request The DSR request to format
2221
- * @returns Formatted request data
2210
+ * Format a DSR request for display or submission. Returns the formatted
2211
+ * payload plus a typed `errors` array of `{ field, code, message }` for any
2212
+ * required fields missing from the source request.
2213
+ *
2214
+ * Codes emitted:
2215
+ * - `request_id_required`
2216
+ * - `request_type_required`
2217
+ * - `request_status_required`
2218
+ * - `created_at_required`
2219
+ * - `subject_name_required`
2220
+ * - `subject_email_required`
2222
2221
  */
2223
- export declare function formatDSRRequest(request: DSRRequest): {
2222
+ export declare function formatDSRRequestStructured(request: DSRRequest): FormatDSRRequestStructuredResult;
2223
+
2224
+ /** Result of {@link formatDSRRequestStructured}. */
2225
+ export declare interface FormatDSRRequestStructuredResult {
2226
+ valid: boolean;
2227
+ errors: StructuredValidationError[];
2228
+ /** Formatted request payload — always populated regardless of `valid`. */
2224
2229
  formattedRequest: Record<string, unknown>;
2225
- isValid: boolean;
2226
- validationErrors: string[];
2227
- };
2230
+ /** Narrowed input — populated only on `valid: true`. */
2231
+ data?: DSRRequest;
2232
+ }
2228
2233
 
2229
2234
  /**
2230
2235
  * Generates a summary of all lawful basis documentation across processing activities.
@@ -2417,17 +2422,17 @@ export declare interface LawfulBasisTrackerProps {
2417
2422
  */
2418
2423
  activities: ProcessingActivity[];
2419
2424
  /**
2420
- * Callback when a new activity is created
2425
+ * Callback when a new activity is created.
2421
2426
  */
2422
- onAddActivity?: (activity: Omit<ProcessingActivity, 'id' | 'createdAt' | 'updatedAt'>) => void;
2427
+ onAdd?: (activity: Omit<ProcessingActivity, 'id' | 'createdAt' | 'updatedAt'>) => void;
2423
2428
  /**
2424
- * Callback when an activity is updated
2429
+ * Callback when an activity is updated.
2425
2430
  */
2426
- onUpdateActivity?: (id: string, updates: Partial<ProcessingActivity>) => void;
2431
+ onUpdate?: (id: string, updates: Partial<ProcessingActivity>) => void;
2427
2432
  /**
2428
- * Callback when an activity is archived
2433
+ * Callback when an activity is archived.
2429
2434
  */
2430
- onArchiveActivity?: (id: string) => void;
2435
+ onArchive?: (id: string) => void;
2431
2436
  /**
2432
2437
  * Title displayed on the tracker
2433
2438
  * @default "Lawful Basis Tracker"
@@ -3924,17 +3929,17 @@ export declare interface ROPAManagerProps {
3924
3929
  */
3925
3930
  ropa: RecordOfProcessingActivities;
3926
3931
  /**
3927
- * Callback when a new record is added
3932
+ * Callback when a new record is added.
3928
3933
  */
3929
- onAddRecord?: (record: ProcessingRecord) => void;
3934
+ onAdd?: (record: ProcessingRecord) => void;
3930
3935
  /**
3931
- * Callback when a record is updated
3936
+ * Callback when a record is updated.
3932
3937
  */
3933
- onUpdateRecord?: (id: string, updates: Partial<ProcessingRecord>) => void;
3938
+ onUpdate?: (id: string, updates: Partial<ProcessingRecord>) => void;
3934
3939
  /**
3935
- * Callback when a record is archived
3940
+ * Callback when a record is archived.
3936
3941
  */
3937
- onArchiveRecord?: (id: string) => void;
3942
+ onArchive?: (id: string) => void;
3938
3943
  /**
3939
3944
  * Title displayed on the manager
3940
3945
  * @default "Record of Processing Activities (ROPA)"
@@ -4117,6 +4122,30 @@ export declare interface StorageAdapter<T = unknown> {
4117
4122
  remove(): void | Promise<void>;
4118
4123
  }
4119
4124
 
4125
+ /**
4126
+ * Single structured validation error with a stable, locale-independent
4127
+ * `code` consumers can switch on programmatically.
4128
+ */
4129
+ export declare interface StructuredValidationError {
4130
+ /** Dot-path of the offending field (e.g. `'timestamp'`, `'dataSubject.email'`, `'options[0].purpose'`). */
4131
+ field: string;
4132
+ /** Stable, snake_case error code — safe to switch on across locales. */
4133
+ code: string;
4134
+ /** Human-readable English message — informational only; do not regex-match. */
4135
+ message: string;
4136
+ }
4137
+
4138
+ /**
4139
+ * Result of a structured validator. `errors` is an array (one entry per
4140
+ * failed rule). `data` is the narrowed, typed payload, only populated on
4141
+ * `valid: true`.
4142
+ */
4143
+ export declare interface StructuredValidationResult<T> {
4144
+ valid: boolean;
4145
+ errors: StructuredValidationError[];
4146
+ data?: T;
4147
+ }
4148
+
4120
4149
  /** Full context used to generate an adaptive privacy policy. */
4121
4150
  declare interface TemplateContext {
4122
4151
  /** Organisation details, extended with industry and size. */
@@ -4544,9 +4573,10 @@ declare interface UseConsentReturn {
4544
4573
  */
4545
4574
  isValid: boolean;
4546
4575
  /**
4547
- * Validation errors (if any)
4576
+ * Validation errors (if any). Each entry is a structured `{ field, code,
4577
+ * message }` so consumers can switch on `code` across locales.
4548
4578
  */
4549
- validationErrors: string[];
4579
+ validationErrors: StructuredValidationError[];
4550
4580
  /**
4551
4581
  * Reset consent settings (clear from storage)
4552
4582
  */
@@ -5221,7 +5251,7 @@ declare interface UsePrivacyPolicyReturn {
5221
5251
  * }
5222
5252
  * ```
5223
5253
  */
5224
- export declare function useROPA({ initialData, adapter, onRecordAdd, onRecordUpdate, onRecordArchive, }: UseROPAOptions): UseROPAReturn;
5254
+ export declare function useROPA({ initialData, adapter, onAdd, onUpdate, onArchive, }: UseROPAOptions): UseROPAReturn;
5225
5255
 
5226
5256
  export declare interface UseROPAOptions {
5227
5257
  /**
@@ -5235,17 +5265,17 @@ export declare interface UseROPAOptions {
5235
5265
  */
5236
5266
  adapter?: StorageAdapter<RecordOfProcessingActivities>;
5237
5267
  /**
5238
- * Callback when a record is added
5268
+ * Callback when a record is added.
5239
5269
  */
5240
- onRecordAdd?: (record: ProcessingRecord) => void;
5270
+ onAdd?: (record: ProcessingRecord) => void;
5241
5271
  /**
5242
- * Callback when a record is updated
5272
+ * Callback when a record is updated.
5243
5273
  */
5244
- onRecordUpdate?: (id: string, updates: Partial<ProcessingRecord>) => void;
5274
+ onUpdate?: (id: string, updates: Partial<ProcessingRecord>) => void;
5245
5275
  /**
5246
- * Callback when a record is archived
5276
+ * Callback when a record is archived.
5247
5277
  */
5248
- onRecordArchive?: (id: string) => void;
5278
+ onArchive?: (id: string) => void;
5249
5279
  }
5250
5280
 
5251
5281
  export declare interface UseROPAReturn {
@@ -5309,66 +5339,43 @@ export declare interface UseROPAReturn {
5309
5339
  }
5310
5340
 
5311
5341
  /**
5312
- * Validates consent settings to ensure they meet NDPA requirements
5313
- * @param settings The consent settings to validate
5314
- * @returns An object containing validation result and any error messages
5315
- */
5316
- export declare function validateConsent(settings: ConsentSettings): {
5317
- valid: boolean;
5318
- errors: string[];
5319
- };
5320
-
5321
- /**
5322
- * Validates that consent options meet NDPA Section 26 requirements.
5323
- * Each consent option must specify a purpose for which data will be processed,
5324
- * as consent must be specific and informed per the Nigeria Data Protection Act.
5325
- * @param options The consent options to validate
5326
- * @returns An object containing validation result and any error messages
5342
+ * Validates consent options against NDPA Section 26 requirements. Each option
5343
+ * is checked for a non-empty `purpose`. Failing options are reported with
5344
+ * `field: 'options[i].purpose'` so consumers can map errors back to the
5345
+ * originating option index.
5346
+ *
5347
+ * Codes emitted:
5348
+ * - `options_required` — empty / missing options array
5349
+ * - `purpose_required` — single option missing a purpose
5327
5350
  */
5328
- export declare function validateConsentOptions(options: ConsentOption[]): {
5329
- valid: boolean;
5330
- errors: string[];
5331
- };
5351
+ export declare function validateConsentOptionsStructured(options: ConsentOption[]): StructuredValidationResult<ConsentOption[]>;
5332
5352
 
5333
5353
  /**
5334
- * Validate a raw DSR submission payload against the same rules
5335
- * `<DSRRequestForm />` enforces client-side. Designed to be called from a
5336
- * server-side handler (Next.js Route Handler, NestJS controller, Express
5337
- * middleware, Cloudflare Worker) so client and server stay in sync without
5338
- * the consumer hand-rolling zod / class-validator schemas.
5354
+ * Validates consent settings against NDPA requirements. Returns structured
5355
+ * `{ field, code, message }[]` errors so consumers can switch on `code`
5356
+ * across locales without regex-matching English strings.
5339
5357
  *
5340
- * Defensive — accepts `unknown` and narrows. Safe to call directly on
5341
- * `await request.json()`.
5358
+ * Codes emitted:
5359
+ * - `consents_required`
5360
+ * - `timestamp_required`
5361
+ * - `timestamp_invalid`
5362
+ * - `version_required`
5363
+ * - `method_required`
5364
+ * - `has_interacted_required`
5365
+ * - `consent_stale`
5342
5366
  *
5343
- * @example **Next.js Route Handler**
5367
+ * @example
5344
5368
  * ```ts
5345
- * // app/api/dsr/route.ts
5346
- * import { validateDsrSubmission } from '@tantainnovative/ndpr-toolkit/server';
5347
- *
5348
- * export async function POST(req: Request) {
5349
- * const { valid, errors, data } = validateDsrSubmission(await req.json());
5350
- * if (!valid) return Response.json({ errors }, { status: 422 });
5351
- * // `data` is the typed DsrSubmissionPayload
5352
- * await dsrStore.create(data);
5353
- * return Response.json({ ok: true }, { status: 201 });
5369
+ * const { valid, errors, data } = validateConsentStructured(settings);
5370
+ * if (!valid) {
5371
+ * const stale = errors.find((e) => e.code === 'consent_stale');
5372
+ * if (stale) showRefreshBanner();
5354
5373
  * }
5355
5374
  * ```
5356
- *
5357
- * @example **Lock to specific request types**
5358
- * ```ts
5359
- * validateDsrSubmission(payload, {
5360
- * allowedRequestTypes: ['access', 'erasure', 'rectification'],
5361
- * });
5362
- * ```
5363
- *
5364
- * @example **Skip identity verification (e.g. authenticated session)**
5365
- * ```ts
5366
- * validateDsrSubmission(payload, { requireIdentityVerification: false });
5367
- * ```
5368
5375
  */
5369
- export declare function validateDsrSubmission(payload: unknown, options?: ValidateDsrSubmissionOptions): DsrSubmissionValidationResult;
5376
+ export declare function validateConsentStructured(settings: ConsentSettings): StructuredValidationResult<ConsentSettings>;
5370
5377
 
5371
- /** Options for {@link validateDsrSubmission}. */
5378
+ /** Options for {@link validateDsrSubmissionStructured}. */
5372
5379
  export declare interface ValidateDsrSubmissionOptions {
5373
5380
  /**
5374
5381
  * Whether the data subject is required to provide an identifier
@@ -5385,6 +5392,48 @@ export declare interface ValidateDsrSubmissionOptions {
5385
5392
  allowedRequestTypes?: string[];
5386
5393
  }
5387
5394
 
5395
+ /**
5396
+ * Validate a raw DSR submission payload against the same rules
5397
+ * `<DSRRequestForm />` enforces client-side. Designed to be called from a
5398
+ * server-side handler (Next.js Route Handler, NestJS controller, Express
5399
+ * middleware, Cloudflare Worker) so client and server stay in sync without
5400
+ * the consumer hand-rolling zod / class-validator schemas.
5401
+ *
5402
+ * Defensive — accepts `unknown` and narrows. Safe to call directly on
5403
+ * `await request.json()`. Returns `{ field, code, message }[]` errors so
5404
+ * callers can switch on `code` programmatically across locales.
5405
+ *
5406
+ * Codes emitted:
5407
+ * - `payload_not_object`
5408
+ * - `request_type_required`
5409
+ * - `request_type_not_allowed`
5410
+ * - `data_subject_required`
5411
+ * - `full_name_required`
5412
+ * - `email_required`
5413
+ * - `email_invalid_format`
5414
+ * - `phone_invalid_type`
5415
+ * - `identifier_type_required`
5416
+ * - `identifier_value_required`
5417
+ * - `submitted_at_invalid`
5418
+ * - `additional_info_invalid_type`
5419
+ * - `payload_final_narrowing_failed`
5420
+ *
5421
+ * @example **Next.js Route Handler**
5422
+ * ```ts
5423
+ * import { validateDsrSubmissionStructured } from '@tantainnovative/ndpr-toolkit/server';
5424
+ *
5425
+ * export async function POST(req: Request) {
5426
+ * const { valid, errors, data } = validateDsrSubmissionStructured(await req.json());
5427
+ * if (!valid) {
5428
+ * return Response.json({ errors }, { status: 422 });
5429
+ * }
5430
+ * await dsrStore.create(data);
5431
+ * return Response.json({ ok: true }, { status: 201 });
5432
+ * }
5433
+ * ```
5434
+ */
5435
+ export declare function validateDsrSubmissionStructured(payload: unknown, options?: ValidateDsrSubmissionOptions): StructuredValidationResult<DsrSubmissionPayload>;
5436
+
5388
5437
  /**
5389
5438
  * Validates that all required fields are present on a processing activity
5390
5439
  * and that the lawful basis is properly documented.