@tantainnovative/ndpr-toolkit 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/dist/chunk-3APT25XO.mjs +1 -0
  3. package/dist/chunk-45D7AMB3.js +1 -0
  4. package/dist/chunk-65J4P5ID.js +1 -0
  5. package/dist/{chunk-NB6SKG76.mjs → chunk-6H6IXTHA.mjs} +1 -1
  6. package/dist/{chunk-GUERZD4O.mjs → chunk-6OPGI27L.mjs} +1 -1
  7. package/dist/{chunk-3XU6FL2I.js → chunk-6YFPDGNB.js} +1 -1
  8. package/dist/{chunk-IHNAFXDM.mjs → chunk-7Z7NURIA.mjs} +1 -1
  9. package/dist/chunk-D3HHDWBR.js +1 -0
  10. package/dist/chunk-DSIIEUAD.mjs +1 -0
  11. package/dist/chunk-H2FDWK4F.js +6 -0
  12. package/dist/chunk-HLFS3NXG.js +1 -0
  13. package/dist/chunk-JGY65SHX.mjs +1 -0
  14. package/dist/chunk-NI54X543.mjs +1 -0
  15. package/dist/{chunk-5F2IAUWJ.js → chunk-O4ATGGVK.js} +1 -1
  16. package/dist/chunk-P5MPUC5F.js +1 -0
  17. package/dist/{chunk-BGV2AKT7.mjs → chunk-PSPYIRIF.mjs} +1 -1
  18. package/dist/{chunk-HOAC5VUF.js → chunk-ROOUYQD4.js} +1 -1
  19. package/dist/{chunk-WMISQDSV.js → chunk-UJGNW6CH.js} +1 -1
  20. package/dist/chunk-UTXDZDYF.mjs +6 -0
  21. package/dist/chunk-W6VVLHRQ.mjs +1 -0
  22. package/dist/consent.d.mts +2 -0
  23. package/dist/consent.d.ts +2 -0
  24. package/dist/consent.js +1 -1
  25. package/dist/consent.mjs +1 -1
  26. package/dist/core.d.mts +13 -0
  27. package/dist/core.d.ts +13 -0
  28. package/dist/core.js +1 -1
  29. package/dist/core.mjs +1 -1
  30. package/dist/cross-border.d.mts +24 -0
  31. package/dist/cross-border.d.ts +24 -0
  32. package/dist/cross-border.js +1 -1
  33. package/dist/cross-border.mjs +1 -1
  34. package/dist/dsr.d.mts +3 -0
  35. package/dist/dsr.d.ts +3 -0
  36. package/dist/dsr.js +1 -1
  37. package/dist/dsr.mjs +1 -1
  38. package/dist/headless.d.mts +22 -1
  39. package/dist/headless.d.ts +22 -1
  40. package/dist/headless.js +1 -1
  41. package/dist/headless.mjs +1 -1
  42. package/dist/hooks.d.mts +22 -1
  43. package/dist/hooks.d.ts +22 -1
  44. package/dist/hooks.js +1 -1
  45. package/dist/hooks.mjs +1 -1
  46. package/dist/index.d.mts +215 -1
  47. package/dist/index.d.ts +215 -1
  48. package/dist/index.js +1 -1
  49. package/dist/index.mjs +1 -1
  50. package/dist/lawful-basis.d.mts +21 -0
  51. package/dist/lawful-basis.d.ts +21 -0
  52. package/dist/lawful-basis.js +1 -1
  53. package/dist/lawful-basis.mjs +1 -1
  54. package/dist/presets.d.mts +93 -0
  55. package/dist/presets.d.ts +93 -0
  56. package/dist/presets.js +1 -1
  57. package/dist/presets.mjs +1 -1
  58. package/dist/ropa-lite.d.mts +13 -1
  59. package/dist/ropa-lite.d.ts +13 -1
  60. package/dist/ropa-lite.js +1 -1
  61. package/dist/ropa-lite.mjs +1 -1
  62. package/dist/ropa.d.mts +43 -1
  63. package/dist/ropa.d.ts +43 -1
  64. package/dist/ropa.js +1 -1
  65. package/dist/ropa.mjs +1 -1
  66. package/dist/server.d.mts +135 -0
  67. package/dist/server.d.ts +135 -0
  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-42JPSNVV.mjs +0 -6
  73. package/dist/chunk-7FXNGGMO.js +0 -1
  74. package/dist/chunk-B46SJB5V.js +0 -1
  75. package/dist/chunk-BL5W472Q.js +0 -1
  76. package/dist/chunk-O2WEABB3.js +0 -1
  77. package/dist/chunk-OZHUINWS.js +0 -1
  78. package/dist/chunk-PJNKQPQP.mjs +0 -1
  79. package/dist/chunk-RBKFNCGO.mjs +0 -1
  80. package/dist/chunk-VPNK7OID.mjs +0 -1
  81. package/dist/chunk-WKUC65HL.mjs +0 -1
  82. package/dist/chunk-XYGGUGTO.js +0 -6
  83. package/dist/chunk-Z2M5VJX2.mjs +0 -1
package/dist/index.d.mts CHANGED
@@ -1330,16 +1330,40 @@ export declare interface CrossBorderTransferManagerProps {
1330
1330
  transfers: CrossBorderTransfer[];
1331
1331
  /**
1332
1332
  * Callback when a new transfer is added
1333
+ * @deprecated Renamed to `onAdd` in 4.1. The legacy name still fires
1334
+ * for backward compatibility and will be removed in 5.0.
1333
1335
  */
1334
1336
  onAddTransfer?: (transfer: Omit<CrossBorderTransfer, 'id' | 'createdAt' | 'updatedAt'>) => void;
1335
1337
  /**
1336
1338
  * Callback when a transfer is updated
1339
+ * @deprecated Renamed to `onUpdate` in 4.1. The legacy name still fires
1340
+ * for backward compatibility and will be removed in 5.0.
1337
1341
  */
1338
1342
  onUpdateTransfer?: (id: string, updates: Partial<CrossBorderTransfer>) => void;
1339
1343
  /**
1340
1344
  * Callback when a transfer is removed
1345
+ * @deprecated Renamed to `onArchive` in 4.1 (NDPA prefers soft-delete
1346
+ * over hard delete). The legacy name still fires for backward
1347
+ * compatibility and will be removed in 5.0.
1341
1348
  */
1342
1349
  onRemoveTransfer?: (id: string) => void;
1350
+ /**
1351
+ * Callback when a new transfer is added (uniform 4.1+ name).
1352
+ * Takes precedence over `onAddTransfer` when both are provided.
1353
+ */
1354
+ onAdd?: (transfer: Omit<CrossBorderTransfer, 'id' | 'createdAt' | 'updatedAt'>) => void;
1355
+ /**
1356
+ * Callback when a transfer is updated (uniform 4.1+ name).
1357
+ * Takes precedence over `onUpdateTransfer` when both are provided.
1358
+ */
1359
+ onUpdate?: (id: string, updates: Partial<CrossBorderTransfer>) => void;
1360
+ /**
1361
+ * Callback when a transfer is archived (uniform 4.1+ name). NDPA
1362
+ * preference is soft-delete over hard-delete; consumers should treat
1363
+ * this as an archive signal. Takes precedence over `onRemoveTransfer`
1364
+ * when both are provided.
1365
+ */
1366
+ onArchive?: (id: string) => void;
1343
1367
  /**
1344
1368
  * Compliance summary data
1345
1369
  */
@@ -2219,6 +2243,7 @@ export declare function exportROPAToCSV(ropa: RecordOfProcessingActivities): str
2219
2243
  * Formats a DSR request for display or submission
2220
2244
  * @param request The DSR request to format
2221
2245
  * @returns Formatted request data
2246
+ * @deprecated Use `formatDSRRequestStructured()` for typed `{ field, code, message }[]` errors. The legacy string-returning shape will be removed in 5.0.
2222
2247
  */
2223
2248
  export declare function formatDSRRequest(request: DSRRequest): {
2224
2249
  formattedRequest: Record<string, unknown>;
@@ -2226,6 +2251,31 @@ export declare function formatDSRRequest(request: DSRRequest): {
2226
2251
  validationErrors: string[];
2227
2252
  };
2228
2253
 
2254
+ /**
2255
+ * Structured-result variant of {@link formatDSRRequest}. Same formatting
2256
+ * output, but `validationErrors` is replaced with a typed `errors` array
2257
+ * of `{ field, code, message }`.
2258
+ *
2259
+ * Codes emitted:
2260
+ * - `request_id_required`
2261
+ * - `request_type_required`
2262
+ * - `request_status_required`
2263
+ * - `created_at_required`
2264
+ * - `subject_name_required`
2265
+ * - `subject_email_required`
2266
+ */
2267
+ export declare function formatDSRRequestStructured(request: DSRRequest): FormatDSRRequestStructuredResult;
2268
+
2269
+ /** Result of {@link formatDSRRequestStructured}. */
2270
+ export declare interface FormatDSRRequestStructuredResult {
2271
+ valid: boolean;
2272
+ errors: StructuredValidationError[];
2273
+ /** Formatted request payload — always populated regardless of `valid`. */
2274
+ formattedRequest: Record<string, unknown>;
2275
+ /** Narrowed input — populated only on `valid: true`. */
2276
+ data?: DSRRequest;
2277
+ }
2278
+
2229
2279
  /**
2230
2280
  * Generates a summary of all lawful basis documentation across processing activities.
2231
2281
  *
@@ -2418,16 +2468,37 @@ export declare interface LawfulBasisTrackerProps {
2418
2468
  activities: ProcessingActivity[];
2419
2469
  /**
2420
2470
  * Callback when a new activity is created
2471
+ * @deprecated Renamed to `onAdd` in 4.1. The legacy name still fires
2472
+ * for backward compatibility and will be removed in 5.0.
2421
2473
  */
2422
2474
  onAddActivity?: (activity: Omit<ProcessingActivity, 'id' | 'createdAt' | 'updatedAt'>) => void;
2423
2475
  /**
2424
2476
  * Callback when an activity is updated
2477
+ * @deprecated Renamed to `onUpdate` in 4.1. The legacy name still fires
2478
+ * for backward compatibility and will be removed in 5.0.
2425
2479
  */
2426
2480
  onUpdateActivity?: (id: string, updates: Partial<ProcessingActivity>) => void;
2427
2481
  /**
2428
2482
  * Callback when an activity is archived
2483
+ * @deprecated Renamed to `onArchive` in 4.1. The legacy name still fires
2484
+ * for backward compatibility and will be removed in 5.0.
2429
2485
  */
2430
2486
  onArchiveActivity?: (id: string) => void;
2487
+ /**
2488
+ * Callback when a new activity is created (uniform 4.1+ name).
2489
+ * Takes precedence over `onAddActivity` when both are provided.
2490
+ */
2491
+ onAdd?: (activity: Omit<ProcessingActivity, 'id' | 'createdAt' | 'updatedAt'>) => void;
2492
+ /**
2493
+ * Callback when an activity is updated (uniform 4.1+ name).
2494
+ * Takes precedence over `onUpdateActivity` when both are provided.
2495
+ */
2496
+ onUpdate?: (id: string, updates: Partial<ProcessingActivity>) => void;
2497
+ /**
2498
+ * Callback when an activity is archived (uniform 4.1+ name).
2499
+ * Takes precedence over `onArchiveActivity` when both are provided.
2500
+ */
2501
+ onArchive?: (id: string) => void;
2431
2502
  /**
2432
2503
  * Title displayed on the tracker
2433
2504
  * @default "Lawful Basis Tracker"
@@ -3925,16 +3996,37 @@ export declare interface ROPAManagerProps {
3925
3996
  ropa: RecordOfProcessingActivities;
3926
3997
  /**
3927
3998
  * Callback when a new record is added
3999
+ * @deprecated Renamed to `onAdd` in 4.1. The legacy name still fires
4000
+ * for backward compatibility and will be removed in 5.0.
3928
4001
  */
3929
4002
  onAddRecord?: (record: ProcessingRecord) => void;
3930
4003
  /**
3931
4004
  * Callback when a record is updated
4005
+ * @deprecated Renamed to `onUpdate` in 4.1. The legacy name still fires
4006
+ * for backward compatibility and will be removed in 5.0.
3932
4007
  */
3933
4008
  onUpdateRecord?: (id: string, updates: Partial<ProcessingRecord>) => void;
3934
4009
  /**
3935
4010
  * Callback when a record is archived
4011
+ * @deprecated Renamed to `onArchive` in 4.1. The legacy name still fires
4012
+ * for backward compatibility and will be removed in 5.0.
3936
4013
  */
3937
4014
  onArchiveRecord?: (id: string) => void;
4015
+ /**
4016
+ * Callback when a new record is added (uniform 4.1+ name).
4017
+ * Takes precedence over `onAddRecord` when both are provided.
4018
+ */
4019
+ onAdd?: (record: ProcessingRecord) => void;
4020
+ /**
4021
+ * Callback when a record is updated (uniform 4.1+ name).
4022
+ * Takes precedence over `onUpdateRecord` when both are provided.
4023
+ */
4024
+ onUpdate?: (id: string, updates: Partial<ProcessingRecord>) => void;
4025
+ /**
4026
+ * Callback when a record is archived (uniform 4.1+ name).
4027
+ * Takes precedence over `onArchiveRecord` when both are provided.
4028
+ */
4029
+ onArchive?: (id: string) => void;
3938
4030
  /**
3939
4031
  * Title displayed on the manager
3940
4032
  * @default "Record of Processing Activities (ROPA)"
@@ -4117,6 +4209,30 @@ export declare interface StorageAdapter<T = unknown> {
4117
4209
  remove(): void | Promise<void>;
4118
4210
  }
4119
4211
 
4212
+ /**
4213
+ * Single structured validation error with a stable, locale-independent
4214
+ * `code` consumers can switch on programmatically.
4215
+ */
4216
+ export declare interface StructuredValidationError {
4217
+ /** Dot-path of the offending field (e.g. `'timestamp'`, `'dataSubject.email'`, `'options[0].purpose'`). */
4218
+ field: string;
4219
+ /** Stable, snake_case error code — safe to switch on across locales. */
4220
+ code: string;
4221
+ /** Human-readable English message — informational only; do not regex-match. */
4222
+ message: string;
4223
+ }
4224
+
4225
+ /**
4226
+ * Result of a structured validator. `errors` is an array (one entry per
4227
+ * failed rule). `data` is the narrowed, typed payload, only populated on
4228
+ * `valid: true`.
4229
+ */
4230
+ export declare interface StructuredValidationResult<T> {
4231
+ valid: boolean;
4232
+ errors: StructuredValidationError[];
4233
+ data?: T;
4234
+ }
4235
+
4120
4236
  /** Full context used to generate an adaptive privacy policy. */
4121
4237
  declare interface TemplateContext {
4122
4238
  /** Organisation details, extended with industry and size. */
@@ -5221,7 +5337,7 @@ declare interface UsePrivacyPolicyReturn {
5221
5337
  * }
5222
5338
  * ```
5223
5339
  */
5224
- export declare function useROPA({ initialData, adapter, onRecordAdd, onRecordUpdate, onRecordArchive, }: UseROPAOptions): UseROPAReturn;
5340
+ export declare function useROPA({ initialData, adapter, onRecordAdd, onRecordUpdate, onRecordArchive, onAdd, onUpdate, onArchive, }: UseROPAOptions): UseROPAReturn;
5225
5341
 
5226
5342
  export declare interface UseROPAOptions {
5227
5343
  /**
@@ -5236,16 +5352,37 @@ export declare interface UseROPAOptions {
5236
5352
  adapter?: StorageAdapter<RecordOfProcessingActivities>;
5237
5353
  /**
5238
5354
  * Callback when a record is added
5355
+ * @deprecated Renamed to `onAdd` in 4.1. The legacy name still fires
5356
+ * for backward compatibility and will be removed in 5.0.
5239
5357
  */
5240
5358
  onRecordAdd?: (record: ProcessingRecord) => void;
5241
5359
  /**
5242
5360
  * Callback when a record is updated
5361
+ * @deprecated Renamed to `onUpdate` in 4.1. The legacy name still fires
5362
+ * for backward compatibility and will be removed in 5.0.
5243
5363
  */
5244
5364
  onRecordUpdate?: (id: string, updates: Partial<ProcessingRecord>) => void;
5245
5365
  /**
5246
5366
  * Callback when a record is archived
5367
+ * @deprecated Renamed to `onArchive` in 4.1. The legacy name still fires
5368
+ * for backward compatibility and will be removed in 5.0.
5247
5369
  */
5248
5370
  onRecordArchive?: (id: string) => void;
5371
+ /**
5372
+ * Callback when a record is added (uniform 4.1+ name).
5373
+ * Takes precedence over `onRecordAdd` when both are provided.
5374
+ */
5375
+ onAdd?: (record: ProcessingRecord) => void;
5376
+ /**
5377
+ * Callback when a record is updated (uniform 4.1+ name).
5378
+ * Takes precedence over `onRecordUpdate` when both are provided.
5379
+ */
5380
+ onUpdate?: (id: string, updates: Partial<ProcessingRecord>) => void;
5381
+ /**
5382
+ * Callback when a record is archived (uniform 4.1+ name).
5383
+ * Takes precedence over `onRecordArchive` when both are provided.
5384
+ */
5385
+ onArchive?: (id: string) => void;
5249
5386
  }
5250
5387
 
5251
5388
  export declare interface UseROPAReturn {
@@ -5312,6 +5449,7 @@ export declare interface UseROPAReturn {
5312
5449
  * Validates consent settings to ensure they meet NDPA requirements
5313
5450
  * @param settings The consent settings to validate
5314
5451
  * @returns An object containing validation result and any error messages
5452
+ * @deprecated Use `validateConsentStructured()` for typed `{ field, code, message }[]` errors. The legacy string-returning shape will be removed in 5.0.
5315
5453
  */
5316
5454
  export declare function validateConsent(settings: ConsentSettings): {
5317
5455
  valid: boolean;
@@ -5324,12 +5462,50 @@ export declare function validateConsent(settings: ConsentSettings): {
5324
5462
  * as consent must be specific and informed per the Nigeria Data Protection Act.
5325
5463
  * @param options The consent options to validate
5326
5464
  * @returns An object containing validation result and any error messages
5465
+ * @deprecated Use `validateConsentOptionsStructured()` for typed `{ field, code, message }[]` errors. The legacy string-returning shape will be removed in 5.0.
5327
5466
  */
5328
5467
  export declare function validateConsentOptions(options: ConsentOption[]): {
5329
5468
  valid: boolean;
5330
5469
  errors: string[];
5331
5470
  };
5332
5471
 
5472
+ /**
5473
+ * Structured-result variant of {@link validateConsentOptions}. Each option
5474
+ * is checked for a non-empty `purpose` (NDPA Section 26). Failing options
5475
+ * are reported with `field: 'options[i].purpose'` so consumers can map
5476
+ * errors back to the originating option index.
5477
+ *
5478
+ * Codes emitted:
5479
+ * - `options_required` — empty / missing options array
5480
+ * - `purpose_required` — single option missing a purpose
5481
+ */
5482
+ export declare function validateConsentOptionsStructured(options: ConsentOption[]): StructuredValidationResult<ConsentOption[]>;
5483
+
5484
+ /**
5485
+ * Structured-result variant of {@link validateConsent}. Returns the same
5486
+ * checks as `{ field, code, message }[]` so consumers can switch on `code`
5487
+ * across locales without regex-matching English strings.
5488
+ *
5489
+ * Codes emitted:
5490
+ * - `consents_required`
5491
+ * - `timestamp_required`
5492
+ * - `timestamp_invalid`
5493
+ * - `version_required`
5494
+ * - `method_required`
5495
+ * - `has_interacted_required`
5496
+ * - `consent_stale`
5497
+ *
5498
+ * @example
5499
+ * ```ts
5500
+ * const { valid, errors, data } = validateConsentStructured(settings);
5501
+ * if (!valid) {
5502
+ * const stale = errors.find((e) => e.code === 'consent_stale');
5503
+ * if (stale) showRefreshBanner();
5504
+ * }
5505
+ * ```
5506
+ */
5507
+ export declare function validateConsentStructured(settings: ConsentSettings): StructuredValidationResult<ConsentSettings>;
5508
+
5333
5509
  /**
5334
5510
  * Validate a raw DSR submission payload against the same rules
5335
5511
  * `<DSRRequestForm />` enforces client-side. Designed to be called from a
@@ -5365,6 +5541,8 @@ export declare function validateConsentOptions(options: ConsentOption[]): {
5365
5541
  * ```ts
5366
5542
  * validateDsrSubmission(payload, { requireIdentityVerification: false });
5367
5543
  * ```
5544
+ *
5545
+ * @deprecated Use `validateDsrSubmissionStructured()` for typed `{ field, code, message }[]` errors. The legacy string-returning shape will be removed in 5.0.
5368
5546
  */
5369
5547
  export declare function validateDsrSubmission(payload: unknown, options?: ValidateDsrSubmissionOptions): DsrSubmissionValidationResult;
5370
5548
 
@@ -5385,6 +5563,42 @@ export declare interface ValidateDsrSubmissionOptions {
5385
5563
  allowedRequestTypes?: string[];
5386
5564
  }
5387
5565
 
5566
+ /**
5567
+ * Structured-result variant of {@link validateDsrSubmission}. Same rules,
5568
+ * but returns an array of `{ field, code, message }` so callers can switch
5569
+ * on `code` programmatically across locales.
5570
+ *
5571
+ * Codes emitted:
5572
+ * - `payload_not_object`
5573
+ * - `request_type_required`
5574
+ * - `request_type_not_allowed`
5575
+ * - `data_subject_required`
5576
+ * - `full_name_required`
5577
+ * - `email_required`
5578
+ * - `email_invalid_format`
5579
+ * - `phone_invalid_type`
5580
+ * - `identifier_type_required`
5581
+ * - `identifier_value_required`
5582
+ * - `submitted_at_invalid`
5583
+ * - `additional_info_invalid_type`
5584
+ * - `payload_final_narrowing_failed`
5585
+ *
5586
+ * @example **Next.js Route Handler**
5587
+ * ```ts
5588
+ * import { validateDsrSubmissionStructured } from '@tantainnovative/ndpr-toolkit/server';
5589
+ *
5590
+ * export async function POST(req: Request) {
5591
+ * const { valid, errors, data } = validateDsrSubmissionStructured(await req.json());
5592
+ * if (!valid) {
5593
+ * return Response.json({ errors }, { status: 422 });
5594
+ * }
5595
+ * await dsrStore.create(data);
5596
+ * return Response.json({ ok: true }, { status: 201 });
5597
+ * }
5598
+ * ```
5599
+ */
5600
+ export declare function validateDsrSubmissionStructured(payload: unknown, options?: ValidateDsrSubmissionOptions): StructuredValidationResult<DsrSubmissionPayload>;
5601
+
5388
5602
  /**
5389
5603
  * Validates that all required fields are present on a processing activity
5390
5604
  * and that the lawful basis is properly documented.
package/dist/index.d.ts CHANGED
@@ -1330,16 +1330,40 @@ export declare interface CrossBorderTransferManagerProps {
1330
1330
  transfers: CrossBorderTransfer[];
1331
1331
  /**
1332
1332
  * Callback when a new transfer is added
1333
+ * @deprecated Renamed to `onAdd` in 4.1. The legacy name still fires
1334
+ * for backward compatibility and will be removed in 5.0.
1333
1335
  */
1334
1336
  onAddTransfer?: (transfer: Omit<CrossBorderTransfer, 'id' | 'createdAt' | 'updatedAt'>) => void;
1335
1337
  /**
1336
1338
  * Callback when a transfer is updated
1339
+ * @deprecated Renamed to `onUpdate` in 4.1. The legacy name still fires
1340
+ * for backward compatibility and will be removed in 5.0.
1337
1341
  */
1338
1342
  onUpdateTransfer?: (id: string, updates: Partial<CrossBorderTransfer>) => void;
1339
1343
  /**
1340
1344
  * Callback when a transfer is removed
1345
+ * @deprecated Renamed to `onArchive` in 4.1 (NDPA prefers soft-delete
1346
+ * over hard delete). The legacy name still fires for backward
1347
+ * compatibility and will be removed in 5.0.
1341
1348
  */
1342
1349
  onRemoveTransfer?: (id: string) => void;
1350
+ /**
1351
+ * Callback when a new transfer is added (uniform 4.1+ name).
1352
+ * Takes precedence over `onAddTransfer` when both are provided.
1353
+ */
1354
+ onAdd?: (transfer: Omit<CrossBorderTransfer, 'id' | 'createdAt' | 'updatedAt'>) => void;
1355
+ /**
1356
+ * Callback when a transfer is updated (uniform 4.1+ name).
1357
+ * Takes precedence over `onUpdateTransfer` when both are provided.
1358
+ */
1359
+ onUpdate?: (id: string, updates: Partial<CrossBorderTransfer>) => void;
1360
+ /**
1361
+ * Callback when a transfer is archived (uniform 4.1+ name). NDPA
1362
+ * preference is soft-delete over hard-delete; consumers should treat
1363
+ * this as an archive signal. Takes precedence over `onRemoveTransfer`
1364
+ * when both are provided.
1365
+ */
1366
+ onArchive?: (id: string) => void;
1343
1367
  /**
1344
1368
  * Compliance summary data
1345
1369
  */
@@ -2219,6 +2243,7 @@ export declare function exportROPAToCSV(ropa: RecordOfProcessingActivities): str
2219
2243
  * Formats a DSR request for display or submission
2220
2244
  * @param request The DSR request to format
2221
2245
  * @returns Formatted request data
2246
+ * @deprecated Use `formatDSRRequestStructured()` for typed `{ field, code, message }[]` errors. The legacy string-returning shape will be removed in 5.0.
2222
2247
  */
2223
2248
  export declare function formatDSRRequest(request: DSRRequest): {
2224
2249
  formattedRequest: Record<string, unknown>;
@@ -2226,6 +2251,31 @@ export declare function formatDSRRequest(request: DSRRequest): {
2226
2251
  validationErrors: string[];
2227
2252
  };
2228
2253
 
2254
+ /**
2255
+ * Structured-result variant of {@link formatDSRRequest}. Same formatting
2256
+ * output, but `validationErrors` is replaced with a typed `errors` array
2257
+ * of `{ field, code, message }`.
2258
+ *
2259
+ * Codes emitted:
2260
+ * - `request_id_required`
2261
+ * - `request_type_required`
2262
+ * - `request_status_required`
2263
+ * - `created_at_required`
2264
+ * - `subject_name_required`
2265
+ * - `subject_email_required`
2266
+ */
2267
+ export declare function formatDSRRequestStructured(request: DSRRequest): FormatDSRRequestStructuredResult;
2268
+
2269
+ /** Result of {@link formatDSRRequestStructured}. */
2270
+ export declare interface FormatDSRRequestStructuredResult {
2271
+ valid: boolean;
2272
+ errors: StructuredValidationError[];
2273
+ /** Formatted request payload — always populated regardless of `valid`. */
2274
+ formattedRequest: Record<string, unknown>;
2275
+ /** Narrowed input — populated only on `valid: true`. */
2276
+ data?: DSRRequest;
2277
+ }
2278
+
2229
2279
  /**
2230
2280
  * Generates a summary of all lawful basis documentation across processing activities.
2231
2281
  *
@@ -2418,16 +2468,37 @@ export declare interface LawfulBasisTrackerProps {
2418
2468
  activities: ProcessingActivity[];
2419
2469
  /**
2420
2470
  * Callback when a new activity is created
2471
+ * @deprecated Renamed to `onAdd` in 4.1. The legacy name still fires
2472
+ * for backward compatibility and will be removed in 5.0.
2421
2473
  */
2422
2474
  onAddActivity?: (activity: Omit<ProcessingActivity, 'id' | 'createdAt' | 'updatedAt'>) => void;
2423
2475
  /**
2424
2476
  * Callback when an activity is updated
2477
+ * @deprecated Renamed to `onUpdate` in 4.1. The legacy name still fires
2478
+ * for backward compatibility and will be removed in 5.0.
2425
2479
  */
2426
2480
  onUpdateActivity?: (id: string, updates: Partial<ProcessingActivity>) => void;
2427
2481
  /**
2428
2482
  * Callback when an activity is archived
2483
+ * @deprecated Renamed to `onArchive` in 4.1. The legacy name still fires
2484
+ * for backward compatibility and will be removed in 5.0.
2429
2485
  */
2430
2486
  onArchiveActivity?: (id: string) => void;
2487
+ /**
2488
+ * Callback when a new activity is created (uniform 4.1+ name).
2489
+ * Takes precedence over `onAddActivity` when both are provided.
2490
+ */
2491
+ onAdd?: (activity: Omit<ProcessingActivity, 'id' | 'createdAt' | 'updatedAt'>) => void;
2492
+ /**
2493
+ * Callback when an activity is updated (uniform 4.1+ name).
2494
+ * Takes precedence over `onUpdateActivity` when both are provided.
2495
+ */
2496
+ onUpdate?: (id: string, updates: Partial<ProcessingActivity>) => void;
2497
+ /**
2498
+ * Callback when an activity is archived (uniform 4.1+ name).
2499
+ * Takes precedence over `onArchiveActivity` when both are provided.
2500
+ */
2501
+ onArchive?: (id: string) => void;
2431
2502
  /**
2432
2503
  * Title displayed on the tracker
2433
2504
  * @default "Lawful Basis Tracker"
@@ -3925,16 +3996,37 @@ export declare interface ROPAManagerProps {
3925
3996
  ropa: RecordOfProcessingActivities;
3926
3997
  /**
3927
3998
  * Callback when a new record is added
3999
+ * @deprecated Renamed to `onAdd` in 4.1. The legacy name still fires
4000
+ * for backward compatibility and will be removed in 5.0.
3928
4001
  */
3929
4002
  onAddRecord?: (record: ProcessingRecord) => void;
3930
4003
  /**
3931
4004
  * Callback when a record is updated
4005
+ * @deprecated Renamed to `onUpdate` in 4.1. The legacy name still fires
4006
+ * for backward compatibility and will be removed in 5.0.
3932
4007
  */
3933
4008
  onUpdateRecord?: (id: string, updates: Partial<ProcessingRecord>) => void;
3934
4009
  /**
3935
4010
  * Callback when a record is archived
4011
+ * @deprecated Renamed to `onArchive` in 4.1. The legacy name still fires
4012
+ * for backward compatibility and will be removed in 5.0.
3936
4013
  */
3937
4014
  onArchiveRecord?: (id: string) => void;
4015
+ /**
4016
+ * Callback when a new record is added (uniform 4.1+ name).
4017
+ * Takes precedence over `onAddRecord` when both are provided.
4018
+ */
4019
+ onAdd?: (record: ProcessingRecord) => void;
4020
+ /**
4021
+ * Callback when a record is updated (uniform 4.1+ name).
4022
+ * Takes precedence over `onUpdateRecord` when both are provided.
4023
+ */
4024
+ onUpdate?: (id: string, updates: Partial<ProcessingRecord>) => void;
4025
+ /**
4026
+ * Callback when a record is archived (uniform 4.1+ name).
4027
+ * Takes precedence over `onArchiveRecord` when both are provided.
4028
+ */
4029
+ onArchive?: (id: string) => void;
3938
4030
  /**
3939
4031
  * Title displayed on the manager
3940
4032
  * @default "Record of Processing Activities (ROPA)"
@@ -4117,6 +4209,30 @@ export declare interface StorageAdapter<T = unknown> {
4117
4209
  remove(): void | Promise<void>;
4118
4210
  }
4119
4211
 
4212
+ /**
4213
+ * Single structured validation error with a stable, locale-independent
4214
+ * `code` consumers can switch on programmatically.
4215
+ */
4216
+ export declare interface StructuredValidationError {
4217
+ /** Dot-path of the offending field (e.g. `'timestamp'`, `'dataSubject.email'`, `'options[0].purpose'`). */
4218
+ field: string;
4219
+ /** Stable, snake_case error code — safe to switch on across locales. */
4220
+ code: string;
4221
+ /** Human-readable English message — informational only; do not regex-match. */
4222
+ message: string;
4223
+ }
4224
+
4225
+ /**
4226
+ * Result of a structured validator. `errors` is an array (one entry per
4227
+ * failed rule). `data` is the narrowed, typed payload, only populated on
4228
+ * `valid: true`.
4229
+ */
4230
+ export declare interface StructuredValidationResult<T> {
4231
+ valid: boolean;
4232
+ errors: StructuredValidationError[];
4233
+ data?: T;
4234
+ }
4235
+
4120
4236
  /** Full context used to generate an adaptive privacy policy. */
4121
4237
  declare interface TemplateContext {
4122
4238
  /** Organisation details, extended with industry and size. */
@@ -5221,7 +5337,7 @@ declare interface UsePrivacyPolicyReturn {
5221
5337
  * }
5222
5338
  * ```
5223
5339
  */
5224
- export declare function useROPA({ initialData, adapter, onRecordAdd, onRecordUpdate, onRecordArchive, }: UseROPAOptions): UseROPAReturn;
5340
+ export declare function useROPA({ initialData, adapter, onRecordAdd, onRecordUpdate, onRecordArchive, onAdd, onUpdate, onArchive, }: UseROPAOptions): UseROPAReturn;
5225
5341
 
5226
5342
  export declare interface UseROPAOptions {
5227
5343
  /**
@@ -5236,16 +5352,37 @@ export declare interface UseROPAOptions {
5236
5352
  adapter?: StorageAdapter<RecordOfProcessingActivities>;
5237
5353
  /**
5238
5354
  * Callback when a record is added
5355
+ * @deprecated Renamed to `onAdd` in 4.1. The legacy name still fires
5356
+ * for backward compatibility and will be removed in 5.0.
5239
5357
  */
5240
5358
  onRecordAdd?: (record: ProcessingRecord) => void;
5241
5359
  /**
5242
5360
  * Callback when a record is updated
5361
+ * @deprecated Renamed to `onUpdate` in 4.1. The legacy name still fires
5362
+ * for backward compatibility and will be removed in 5.0.
5243
5363
  */
5244
5364
  onRecordUpdate?: (id: string, updates: Partial<ProcessingRecord>) => void;
5245
5365
  /**
5246
5366
  * Callback when a record is archived
5367
+ * @deprecated Renamed to `onArchive` in 4.1. The legacy name still fires
5368
+ * for backward compatibility and will be removed in 5.0.
5247
5369
  */
5248
5370
  onRecordArchive?: (id: string) => void;
5371
+ /**
5372
+ * Callback when a record is added (uniform 4.1+ name).
5373
+ * Takes precedence over `onRecordAdd` when both are provided.
5374
+ */
5375
+ onAdd?: (record: ProcessingRecord) => void;
5376
+ /**
5377
+ * Callback when a record is updated (uniform 4.1+ name).
5378
+ * Takes precedence over `onRecordUpdate` when both are provided.
5379
+ */
5380
+ onUpdate?: (id: string, updates: Partial<ProcessingRecord>) => void;
5381
+ /**
5382
+ * Callback when a record is archived (uniform 4.1+ name).
5383
+ * Takes precedence over `onRecordArchive` when both are provided.
5384
+ */
5385
+ onArchive?: (id: string) => void;
5249
5386
  }
5250
5387
 
5251
5388
  export declare interface UseROPAReturn {
@@ -5312,6 +5449,7 @@ export declare interface UseROPAReturn {
5312
5449
  * Validates consent settings to ensure they meet NDPA requirements
5313
5450
  * @param settings The consent settings to validate
5314
5451
  * @returns An object containing validation result and any error messages
5452
+ * @deprecated Use `validateConsentStructured()` for typed `{ field, code, message }[]` errors. The legacy string-returning shape will be removed in 5.0.
5315
5453
  */
5316
5454
  export declare function validateConsent(settings: ConsentSettings): {
5317
5455
  valid: boolean;
@@ -5324,12 +5462,50 @@ export declare function validateConsent(settings: ConsentSettings): {
5324
5462
  * as consent must be specific and informed per the Nigeria Data Protection Act.
5325
5463
  * @param options The consent options to validate
5326
5464
  * @returns An object containing validation result and any error messages
5465
+ * @deprecated Use `validateConsentOptionsStructured()` for typed `{ field, code, message }[]` errors. The legacy string-returning shape will be removed in 5.0.
5327
5466
  */
5328
5467
  export declare function validateConsentOptions(options: ConsentOption[]): {
5329
5468
  valid: boolean;
5330
5469
  errors: string[];
5331
5470
  };
5332
5471
 
5472
+ /**
5473
+ * Structured-result variant of {@link validateConsentOptions}. Each option
5474
+ * is checked for a non-empty `purpose` (NDPA Section 26). Failing options
5475
+ * are reported with `field: 'options[i].purpose'` so consumers can map
5476
+ * errors back to the originating option index.
5477
+ *
5478
+ * Codes emitted:
5479
+ * - `options_required` — empty / missing options array
5480
+ * - `purpose_required` — single option missing a purpose
5481
+ */
5482
+ export declare function validateConsentOptionsStructured(options: ConsentOption[]): StructuredValidationResult<ConsentOption[]>;
5483
+
5484
+ /**
5485
+ * Structured-result variant of {@link validateConsent}. Returns the same
5486
+ * checks as `{ field, code, message }[]` so consumers can switch on `code`
5487
+ * across locales without regex-matching English strings.
5488
+ *
5489
+ * Codes emitted:
5490
+ * - `consents_required`
5491
+ * - `timestamp_required`
5492
+ * - `timestamp_invalid`
5493
+ * - `version_required`
5494
+ * - `method_required`
5495
+ * - `has_interacted_required`
5496
+ * - `consent_stale`
5497
+ *
5498
+ * @example
5499
+ * ```ts
5500
+ * const { valid, errors, data } = validateConsentStructured(settings);
5501
+ * if (!valid) {
5502
+ * const stale = errors.find((e) => e.code === 'consent_stale');
5503
+ * if (stale) showRefreshBanner();
5504
+ * }
5505
+ * ```
5506
+ */
5507
+ export declare function validateConsentStructured(settings: ConsentSettings): StructuredValidationResult<ConsentSettings>;
5508
+
5333
5509
  /**
5334
5510
  * Validate a raw DSR submission payload against the same rules
5335
5511
  * `<DSRRequestForm />` enforces client-side. Designed to be called from a
@@ -5365,6 +5541,8 @@ export declare function validateConsentOptions(options: ConsentOption[]): {
5365
5541
  * ```ts
5366
5542
  * validateDsrSubmission(payload, { requireIdentityVerification: false });
5367
5543
  * ```
5544
+ *
5545
+ * @deprecated Use `validateDsrSubmissionStructured()` for typed `{ field, code, message }[]` errors. The legacy string-returning shape will be removed in 5.0.
5368
5546
  */
5369
5547
  export declare function validateDsrSubmission(payload: unknown, options?: ValidateDsrSubmissionOptions): DsrSubmissionValidationResult;
5370
5548
 
@@ -5385,6 +5563,42 @@ export declare interface ValidateDsrSubmissionOptions {
5385
5563
  allowedRequestTypes?: string[];
5386
5564
  }
5387
5565
 
5566
+ /**
5567
+ * Structured-result variant of {@link validateDsrSubmission}. Same rules,
5568
+ * but returns an array of `{ field, code, message }` so callers can switch
5569
+ * on `code` programmatically across locales.
5570
+ *
5571
+ * Codes emitted:
5572
+ * - `payload_not_object`
5573
+ * - `request_type_required`
5574
+ * - `request_type_not_allowed`
5575
+ * - `data_subject_required`
5576
+ * - `full_name_required`
5577
+ * - `email_required`
5578
+ * - `email_invalid_format`
5579
+ * - `phone_invalid_type`
5580
+ * - `identifier_type_required`
5581
+ * - `identifier_value_required`
5582
+ * - `submitted_at_invalid`
5583
+ * - `additional_info_invalid_type`
5584
+ * - `payload_final_narrowing_failed`
5585
+ *
5586
+ * @example **Next.js Route Handler**
5587
+ * ```ts
5588
+ * import { validateDsrSubmissionStructured } from '@tantainnovative/ndpr-toolkit/server';
5589
+ *
5590
+ * export async function POST(req: Request) {
5591
+ * const { valid, errors, data } = validateDsrSubmissionStructured(await req.json());
5592
+ * if (!valid) {
5593
+ * return Response.json({ errors }, { status: 422 });
5594
+ * }
5595
+ * await dsrStore.create(data);
5596
+ * return Response.json({ ok: true }, { status: 201 });
5597
+ * }
5598
+ * ```
5599
+ */
5600
+ export declare function validateDsrSubmissionStructured(payload: unknown, options?: ValidateDsrSubmissionOptions): StructuredValidationResult<DsrSubmissionPayload>;
5601
+
5388
5602
  /**
5389
5603
  * Validates that all required fields are present on a processing activity
5390
5604
  * and that the lawful basis is properly documented.