@tantainnovative/ndpr-toolkit 3.5.5 → 3.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,50 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
4
4
 
5
+ ## [3.6.1](https://github.com/mr-tanta/ndpr-toolkit/compare/v3.6.0...v3.6.1) (2026-05-24)
6
+
7
+ Phase A of the post-3.6.0 backlog — tooling foundation. Pure plumbing; no API changes on the main library, but `create-ndpr` (scoped) bumps to 0.2.0 and a new unscoped `create-ndpr` alias package ships.
8
+
9
+ ### Companion packages
10
+
11
+ * **`@tantainnovative/create-ndpr@0.2.0`** — scaffolder no longer emits broken Prisma imports when you pick `ORM=None`. Templates now support `{{#if ORM=prisma|drizzle|none}}` conditional blocks. The consent route template emits working code for all three ORMs (Prisma queries, Drizzle queries, or an in-memory stub with TODO comments). Other route templates (still hardcoded to Prisma) are skipped with a clear message when `ORM=none` instead of generating broken output. Also fixes the `StorageAdapter<unknown>` type error in the generated `ndpr-layout.tsx` (now correctly types the apiAdapter as `StorageAdapter<ConsentSettings>` and adds a CSRF header example).
12
+ * **`create-ndpr@1.0.0` (NEW, unscoped)** — 30-line alias that delegates to the scoped CLI via `npx`. Lets `npm create ndpr@latest`, `npx create-ndpr`, `pnpm create ndpr`, and `bun create ndpr` all work alongside the existing `npx @tantainnovative/create-ndpr`.
13
+
14
+ ### Docs
15
+
16
+ * README install block now shows all four idiomatic CLI invocations (`npm create ndpr@latest`, `npx create-ndpr`, the scoped form, pnpm and bun variants).
17
+ * README header now has "Open in StackBlitz" and "Open in CodeSandbox" badges that boot `examples/nextjs-app` zero-install in either environment.
18
+ * New "Bundle size guidance" subsection under "Available Import Paths" explains: (1) prefer narrow subpaths over root, (2) use `/presets/{consent,dsr,policy}` over the full `/presets` barrel when only one preset is needed, (3) the 3 manager components are heavy by design (they're full table+filter+modal UIs) — import from `/hooks` if you only need the hook, (4) `/server` carries zero React for SSR/edge/CI use.
19
+
20
+ ### No library code changes
21
+
22
+ The main `@tantainnovative/ndpr-toolkit` package is unchanged from 3.6.0; the version bump is for changelog clarity and so the repo, npm, and CHANGELOG all reference the same version.
23
+
24
+ ## [3.6.0](https://github.com/mr-tanta/ndpr-toolkit/compare/v3.5.5...v3.6.0) (2026-05-24)
25
+
26
+ ### Features (developer feedback)
27
+
28
+ This release lands changes flagged by integrating teams using the toolkit in production. All additions are backward-compatible — 3.5.x consumers can upgrade without changes.
29
+
30
+ * **`apiAdapter` is now production-ready.** Adds `credentials` (defaults to `'same-origin'`, set `'include'` for cross-origin), dynamic `headers` (function form for runtime CSRF token lookup), `loadMethod`/`saveMethod` overrides (e.g. `PUT` for upsert APIs), `unwrap` (transform `{ data: ... }` envelopes), configurable `retry` with exponential backoff and a `shouldRetry` predicate (default: retry on network errors + 5xx, skip 4xx), and `onError`/`onSuccess` hooks for telemetry. The pre-3.6.0 `console.warn` behavior is preserved when no `onError` is configured.
31
+ * **`NDPRConsent` exposes a `copy` prop.** Override `title` / `description` / `acceptAll` / `rejectAll` / `customize` / `save` strings without dropping to the lower-level `<ConsentBanner>` API.
32
+ * **`NDPRSubjectRights` adds public-form `submitTo` mode.** Public sites can POST to their backend instead of being state-managed by an adapter. Pairs with `submitOptions` (credentials, headers) and `onSubmitError`. The state-managed `adapter` mode is unchanged.
33
+ * **Per-preset subpath entries** for bundle-size-sensitive consumers:
34
+ - `@tantainnovative/ndpr-toolkit/presets/consent` — just `NDPRConsent` (~4KB vs ~8KB for the full barrel)
35
+ - `@tantainnovative/ndpr-toolkit/presets/dsr` — just `NDPRSubjectRights`
36
+ - `@tantainnovative/ndpr-toolkit/presets/policy` — just `NDPRPrivacyPolicy`
37
+ The full `/presets` barrel is unchanged. These are additive narrower entries.
38
+
39
+ ### Docs
40
+
41
+ * README install block now shows Bun, npm, and Yarn alongside pnpm.
42
+
43
+ ### Coming next (3.6.1+)
44
+
45
+ - Recipe pages for ecommerce / newsletter / contact-form / careers / admin DSR patterns
46
+ - Org-specific privacy policy templates (SaaS, ecommerce, school, healthcare, procurement)
47
+ - Continued bundle reduction
48
+
5
49
  ## [3.5.5](https://github.com/mr-tanta/ndpr-toolkit/compare/v3.5.4...v3.5.5) (2026-05-24)
6
50
 
7
51
  ### Features (tests + types)
package/README.md CHANGED
@@ -13,6 +13,9 @@ v3 ships **zero-config presets**, **pluggable storage adapters**, **compound com
13
13
 
14
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.4.0 Release](https://github.com/mr-tanta/ndpr-toolkit/releases/tag/v3.4.0)**
15
15
 
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
+ [![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
+
16
19
  > **What's new in 3.4.0:** components now ship styled defaults via a real stylesheet — Tailwind is no longer required. Add `import "@tantainnovative/ndpr-toolkit/styles";` once in your app entry. Plus a new `/server` subpath for RSC-safe pure-logic imports (validators, generators, scoring) with zero React in the import graph. Backward-compatible at the component API level. Full notes on the [release page](https://github.com/mr-tanta/ndpr-toolkit/releases/tag/v3.4.0).
17
20
 
18
21
  <p align="center">
@@ -74,14 +77,26 @@ That's it. NDPA-compliant consent with server-side persistence in under 20 lines
74
77
 
75
78
  ## Install
76
79
 
80
+ Pick your package manager:
81
+
77
82
  ```bash
83
+ # pnpm
78
84
  pnpm add @tantainnovative/ndpr-toolkit
85
+
86
+ # Bun
87
+ bun add @tantainnovative/ndpr-toolkit
88
+
89
+ # npm
90
+ npm install @tantainnovative/ndpr-toolkit
91
+
92
+ # Yarn
93
+ yarn add @tantainnovative/ndpr-toolkit
79
94
  ```
80
95
 
81
96
  Add the stylesheet import once in your app entry so components render with default styles:
82
97
 
83
98
  ```ts
84
- // app/layout.tsx (Next.js App Router) or src/main.tsx (Vite/CRA)
99
+ // app/layout.tsx (Next.js App Router) or src/main.tsx (Vite/CRA/Bun)
85
100
  import "@tantainnovative/ndpr-toolkit/styles";
86
101
  ```
87
102
 
@@ -90,13 +105,24 @@ The stylesheet is opinionated but token-driven — override any `--ndpr-*` CSS c
90
105
  Install UI peer dependencies (only needed if you use the higher-level Radix-based components from `/presets`):
91
106
 
92
107
  ```bash
108
+ # pnpm
93
109
  pnpm add @radix-ui/react-switch @radix-ui/react-tabs @radix-ui/react-label @radix-ui/react-slot lucide-react tailwind-merge clsx class-variance-authority
110
+
111
+ # Bun
112
+ bun add @radix-ui/react-switch @radix-ui/react-tabs @radix-ui/react-label @radix-ui/react-slot lucide-react tailwind-merge clsx class-variance-authority
94
113
  ```
95
114
 
96
115
  Or scaffold instantly with the CLI:
97
116
 
98
117
  ```bash
118
+ # Recommended (idiomatic):
119
+ npm create ndpr@latest
120
+
121
+ # Equivalent — pick whichever fits your muscle memory:
122
+ npx create-ndpr
99
123
  npx @tantainnovative/create-ndpr
124
+ pnpm create ndpr
125
+ bun create ndpr
100
126
  ```
101
127
 
102
128
  ---
@@ -399,7 +425,10 @@ Each component exports its `ClassNames` TypeScript interface for autocomplete. F
399
425
  | `/server` | **Pure validators, generators, scoring, locales, adapters, types — zero React** | `tslib` | **Yes** |
400
426
  | `/core` | Types, utility functions, NDPRProvider | `react`[^core] | Partial |
401
427
  | `/hooks` | React hooks for all 8 modules | `react` | No |
402
- | `/presets` | Zero-config preset components | `react`, Radix peers | No |
428
+ | `/presets` | All zero-config preset components (barrel) | `react`, Radix peers | No |
429
+ | `/presets/consent` | **Just `NDPRConsent`** — narrower barrel for bundle size | `react`, Radix peers | No |
430
+ | `/presets/dsr` | **Just `NDPRSubjectRights`** | `react`, Radix peers | No |
431
+ | `/presets/policy` | **Just `NDPRPrivacyPolicy`** | `react`, Radix peers | No |
403
432
  | `/adapters` | Storage adapters (localStorage, sessionStorage, cookie, api, memory, composeAdapters) | none | Yes |
404
433
  | `/consent` | ConsentBanner, ConsentManager, `Consent.*` compound API, useConsent | `react` | No |
405
434
  | `/dsr` | DSR components + hook | `react` | No |
@@ -414,6 +443,23 @@ Each component exports its `ClassNames` TypeScript interface for autocomplete. F
414
443
 
415
444
  [^core]: `/core` re-exports the React `NDPRProvider` for backward compatibility. For strictly server-side imports use `/server` — it carries the same pure validators with no React surface.
416
445
 
446
+ ### Bundle size guidance
447
+
448
+ The toolkit is published with `sideEffects: ["*.css"]`, so a modern bundler (Vite, Next.js / Webpack, esbuild, Bun) will tree-shake unused exports. A few practical rules to keep your bundle small:
449
+
450
+ 1. **Prefer narrow subpaths over the root.** `import { useConsent } from '@tantainnovative/ndpr-toolkit/hooks'` is tighter than the same import from `.`. The root entry has more transitive exports and bundlers don't always trace them perfectly.
451
+
452
+ 2. **Use the per-preset subpaths when you only need one preset.** `import { NDPRConsent } from '@tantainnovative/ndpr-toolkit/presets/consent'` is ~4 KB; the full `/presets` barrel is ~8 KB. Same for `/presets/dsr` and `/presets/policy`.
453
+
454
+ 3. **The 3 manager components are intentionally heavy** (each is ~50 KB src — full table + filter + modal + wizard UIs):
455
+ - `LawfulBasisTracker` (from `/lawful-basis`)
456
+ - `ROPAManager` (from `/ropa`)
457
+ - `CrossBorderTransferManager` (from `/cross-border`)
458
+
459
+ If your app only needs the hook (e.g. you're rendering ROPA records inside your own admin UI), import from `/hooks` instead of the feature subpath — the hook chunk doesn't drag the manager component into your bundle.
460
+
461
+ 4. **`/server` carries zero React.** For Server Actions, Route Handlers, scheduled jobs, or compliance-score computation in CI, prefer `/server` and you'll pay no React-tree cost.
462
+
417
463
  ---
418
464
 
419
465
  ## NDPA 2023 Overview
@@ -1,7 +1,141 @@
1
- export declare function apiAdapter<T = unknown>(endpoint: string, options?: ApiAdapterOptions): StorageAdapter<T>;
1
+ /**
2
+ * Production-ready API storage adapter.
3
+ *
4
+ * Backward-compatible with the 3.5.x signature — `apiAdapter('/api/x')`
5
+ * still works exactly as before. New options are all opt-in.
6
+ *
7
+ * @example basic
8
+ * const adapter = apiAdapter<ConsentSettings>('/api/consent');
9
+ *
10
+ * @example with credentials and CSRF
11
+ * const adapter = apiAdapter<ConsentSettings>('/api/consent', {
12
+ * credentials: 'include',
13
+ * headers: () => ({
14
+ * 'X-CSRF-Token': document.querySelector<HTMLMetaElement>(
15
+ * 'meta[name="csrf-token"]'
16
+ * )?.content ?? '',
17
+ * }),
18
+ * });
19
+ *
20
+ * @example with retry + telemetry
21
+ * const adapter = apiAdapter<ConsentSettings>('/api/consent', {
22
+ * retry: { attempts: 2, baseDelayMs: 300 },
23
+ * onError: (ctx) => Sentry.captureException(ctx.error, { extra: ctx }),
24
+ * onSuccess: (ctx) => analytics.track('consent_saved', { method: ctx.method }),
25
+ * });
26
+ *
27
+ * @example with response unwrap
28
+ * const adapter = apiAdapter<ConsentSettings>('/api/consent', {
29
+ * // API returns { data: ConsentSettings, ok: true }
30
+ * unwrap: (raw) => (raw as { data: ConsentSettings }).data,
31
+ * });
32
+ */
33
+ export declare function apiAdapter<T = unknown>(endpoint: string, options?: ApiAdapterOptions<T>): StorageAdapter<T>;
2
34
 
3
- export declare interface ApiAdapterOptions {
4
- headers?: Record<string, string>;
35
+ declare interface ApiAdapterErrorContext<T = unknown> {
36
+ /** Which adapter operation triggered this — `load`, `save`, or `remove`. */
37
+ method: ApiAdapterMethod;
38
+ /** The endpoint URL that failed. */
39
+ endpoint: string;
40
+ /** Underlying error (for network failures / parse errors). */
41
+ error?: unknown;
42
+ /** Response object, if a response was received. */
43
+ response?: Response;
44
+ /** HTTP status code, if available. */
45
+ status?: number;
46
+ /** For `save`, the payload that failed to send. */
47
+ payload?: T;
48
+ /** Which retry attempt this is (0 = first try). Capped at `retry.attempts`. */
49
+ attempt: number;
50
+ }
51
+
52
+ declare type ApiAdapterMethod = 'load' | 'save' | 'remove';
53
+
54
+ export declare interface ApiAdapterOptions<T = unknown> {
55
+ /**
56
+ * Extra HTTP headers to send with every request. Useful for `Authorization`,
57
+ * `X-CSRF-Token`, `X-Requested-With`, etc.
58
+ *
59
+ * Can also be a function that returns headers, which lets you read a CSRF
60
+ * token from the DOM/cookie at request time rather than at adapter
61
+ * construction time.
62
+ */
63
+ headers?: Record<string, string> | (() => Record<string, string>);
64
+ /**
65
+ * Forwarded to fetch's `credentials` option. Defaults to `'same-origin'`
66
+ * (the browser default). Set to `'include'` for cross-origin endpoints
67
+ * that need cookies / auth.
68
+ */
69
+ credentials?: RequestCredentials;
70
+ /**
71
+ * HTTP method override for the load operation. Defaults to `'GET'`.
72
+ */
73
+ loadMethod?: 'GET' | 'POST';
74
+ /**
75
+ * HTTP method override for the save operation. Defaults to `'POST'`. Some
76
+ * REST APIs prefer `'PUT'` for upsert semantics.
77
+ */
78
+ saveMethod?: 'POST' | 'PUT' | 'PATCH';
79
+ /**
80
+ * Transform the raw JSON response into the expected `T`. Useful for APIs
81
+ * that wrap responses in `{ data: ... }` or similar envelopes. Called
82
+ * after `res.json()`. If omitted, the parsed JSON is used as-is.
83
+ */
84
+ unwrap?: (raw: unknown) => T | null;
85
+ /**
86
+ * Retry policy for failed requests. Defaults to no retries (preserves the
87
+ * pre-3.6.0 behaviour). When configured, applies to all three operations.
88
+ */
89
+ retry?: ApiAdapterRetryConfig;
90
+ /**
91
+ * Called when a request fails (after all retries exhausted). The adapter
92
+ * still returns a graceful null/void result so the consuming hook
93
+ * doesn't crash — this hook is for telemetry, toasts, or audit logging.
94
+ */
95
+ onError?: (ctx: ApiAdapterErrorContext<T>) => void;
96
+ /**
97
+ * Called when a request succeeds. Useful for cache invalidation,
98
+ * analytics, or syncing other state.
99
+ */
100
+ onSuccess?: (ctx: ApiAdapterSuccessContext<T>) => void;
101
+ /**
102
+ * Per-request fetch options to merge into every request. Use this for
103
+ * things `fetch` itself supports that aren't directly modelled above —
104
+ * `signal`, `mode`, `cache`, `redirect`, etc.
105
+ */
106
+ fetchInit?: Omit<RequestInit, 'method' | 'headers' | 'body' | 'credentials'>;
107
+ }
108
+
109
+ declare interface ApiAdapterRetryConfig {
110
+ /**
111
+ * Number of additional attempts after the initial request. Defaults to 0
112
+ * (no retries). e.g. `attempts: 2` means up to 3 total requests.
113
+ */
114
+ attempts?: number;
115
+ /**
116
+ * Base delay in ms between attempts. Defaults to 250ms. The actual delay
117
+ * uses exponential backoff: `baseDelayMs * 2^attempt`.
118
+ */
119
+ baseDelayMs?: number;
120
+ /**
121
+ * Predicate that decides whether to retry given the failure context. By
122
+ * default we retry on network errors and 5xx responses, but not on 4xx
123
+ * (those are client errors that won't fix themselves).
124
+ */
125
+ shouldRetry?: (ctx: ApiAdapterErrorContext<unknown>) => boolean;
126
+ }
127
+
128
+ declare interface ApiAdapterSuccessContext<T = unknown> {
129
+ /** Which adapter operation succeeded — `load`, `save`, or `remove`. */
130
+ method: ApiAdapterMethod;
131
+ /** The endpoint URL. */
132
+ endpoint: string;
133
+ /** Response object. */
134
+ response: Response;
135
+ /** For `load` operations, the parsed (and optionally unwrapped) data. */
136
+ data?: T;
137
+ /** For `save` operations, the payload that was sent. */
138
+ payload?: T;
5
139
  }
6
140
 
7
141
  export declare function composeAdapters<T = unknown>(primary: StorageAdapter<T>, ...secondaries: StorageAdapter<T>[]): StorageAdapter<T>;
@@ -1,7 +1,141 @@
1
- export declare function apiAdapter<T = unknown>(endpoint: string, options?: ApiAdapterOptions): StorageAdapter<T>;
1
+ /**
2
+ * Production-ready API storage adapter.
3
+ *
4
+ * Backward-compatible with the 3.5.x signature — `apiAdapter('/api/x')`
5
+ * still works exactly as before. New options are all opt-in.
6
+ *
7
+ * @example basic
8
+ * const adapter = apiAdapter<ConsentSettings>('/api/consent');
9
+ *
10
+ * @example with credentials and CSRF
11
+ * const adapter = apiAdapter<ConsentSettings>('/api/consent', {
12
+ * credentials: 'include',
13
+ * headers: () => ({
14
+ * 'X-CSRF-Token': document.querySelector<HTMLMetaElement>(
15
+ * 'meta[name="csrf-token"]'
16
+ * )?.content ?? '',
17
+ * }),
18
+ * });
19
+ *
20
+ * @example with retry + telemetry
21
+ * const adapter = apiAdapter<ConsentSettings>('/api/consent', {
22
+ * retry: { attempts: 2, baseDelayMs: 300 },
23
+ * onError: (ctx) => Sentry.captureException(ctx.error, { extra: ctx }),
24
+ * onSuccess: (ctx) => analytics.track('consent_saved', { method: ctx.method }),
25
+ * });
26
+ *
27
+ * @example with response unwrap
28
+ * const adapter = apiAdapter<ConsentSettings>('/api/consent', {
29
+ * // API returns { data: ConsentSettings, ok: true }
30
+ * unwrap: (raw) => (raw as { data: ConsentSettings }).data,
31
+ * });
32
+ */
33
+ export declare function apiAdapter<T = unknown>(endpoint: string, options?: ApiAdapterOptions<T>): StorageAdapter<T>;
2
34
 
3
- export declare interface ApiAdapterOptions {
4
- headers?: Record<string, string>;
35
+ declare interface ApiAdapterErrorContext<T = unknown> {
36
+ /** Which adapter operation triggered this — `load`, `save`, or `remove`. */
37
+ method: ApiAdapterMethod;
38
+ /** The endpoint URL that failed. */
39
+ endpoint: string;
40
+ /** Underlying error (for network failures / parse errors). */
41
+ error?: unknown;
42
+ /** Response object, if a response was received. */
43
+ response?: Response;
44
+ /** HTTP status code, if available. */
45
+ status?: number;
46
+ /** For `save`, the payload that failed to send. */
47
+ payload?: T;
48
+ /** Which retry attempt this is (0 = first try). Capped at `retry.attempts`. */
49
+ attempt: number;
50
+ }
51
+
52
+ declare type ApiAdapterMethod = 'load' | 'save' | 'remove';
53
+
54
+ export declare interface ApiAdapterOptions<T = unknown> {
55
+ /**
56
+ * Extra HTTP headers to send with every request. Useful for `Authorization`,
57
+ * `X-CSRF-Token`, `X-Requested-With`, etc.
58
+ *
59
+ * Can also be a function that returns headers, which lets you read a CSRF
60
+ * token from the DOM/cookie at request time rather than at adapter
61
+ * construction time.
62
+ */
63
+ headers?: Record<string, string> | (() => Record<string, string>);
64
+ /**
65
+ * Forwarded to fetch's `credentials` option. Defaults to `'same-origin'`
66
+ * (the browser default). Set to `'include'` for cross-origin endpoints
67
+ * that need cookies / auth.
68
+ */
69
+ credentials?: RequestCredentials;
70
+ /**
71
+ * HTTP method override for the load operation. Defaults to `'GET'`.
72
+ */
73
+ loadMethod?: 'GET' | 'POST';
74
+ /**
75
+ * HTTP method override for the save operation. Defaults to `'POST'`. Some
76
+ * REST APIs prefer `'PUT'` for upsert semantics.
77
+ */
78
+ saveMethod?: 'POST' | 'PUT' | 'PATCH';
79
+ /**
80
+ * Transform the raw JSON response into the expected `T`. Useful for APIs
81
+ * that wrap responses in `{ data: ... }` or similar envelopes. Called
82
+ * after `res.json()`. If omitted, the parsed JSON is used as-is.
83
+ */
84
+ unwrap?: (raw: unknown) => T | null;
85
+ /**
86
+ * Retry policy for failed requests. Defaults to no retries (preserves the
87
+ * pre-3.6.0 behaviour). When configured, applies to all three operations.
88
+ */
89
+ retry?: ApiAdapterRetryConfig;
90
+ /**
91
+ * Called when a request fails (after all retries exhausted). The adapter
92
+ * still returns a graceful null/void result so the consuming hook
93
+ * doesn't crash — this hook is for telemetry, toasts, or audit logging.
94
+ */
95
+ onError?: (ctx: ApiAdapterErrorContext<T>) => void;
96
+ /**
97
+ * Called when a request succeeds. Useful for cache invalidation,
98
+ * analytics, or syncing other state.
99
+ */
100
+ onSuccess?: (ctx: ApiAdapterSuccessContext<T>) => void;
101
+ /**
102
+ * Per-request fetch options to merge into every request. Use this for
103
+ * things `fetch` itself supports that aren't directly modelled above —
104
+ * `signal`, `mode`, `cache`, `redirect`, etc.
105
+ */
106
+ fetchInit?: Omit<RequestInit, 'method' | 'headers' | 'body' | 'credentials'>;
107
+ }
108
+
109
+ declare interface ApiAdapterRetryConfig {
110
+ /**
111
+ * Number of additional attempts after the initial request. Defaults to 0
112
+ * (no retries). e.g. `attempts: 2` means up to 3 total requests.
113
+ */
114
+ attempts?: number;
115
+ /**
116
+ * Base delay in ms between attempts. Defaults to 250ms. The actual delay
117
+ * uses exponential backoff: `baseDelayMs * 2^attempt`.
118
+ */
119
+ baseDelayMs?: number;
120
+ /**
121
+ * Predicate that decides whether to retry given the failure context. By
122
+ * default we retry on network errors and 5xx responses, but not on 4xx
123
+ * (those are client errors that won't fix themselves).
124
+ */
125
+ shouldRetry?: (ctx: ApiAdapterErrorContext<unknown>) => boolean;
126
+ }
127
+
128
+ declare interface ApiAdapterSuccessContext<T = unknown> {
129
+ /** Which adapter operation succeeded — `load`, `save`, or `remove`. */
130
+ method: ApiAdapterMethod;
131
+ /** The endpoint URL. */
132
+ endpoint: string;
133
+ /** Response object. */
134
+ response: Response;
135
+ /** For `load` operations, the parsed (and optionally unwrapped) data. */
136
+ data?: T;
137
+ /** For `save` operations, the payload that was sent. */
138
+ payload?: T;
5
139
  }
6
140
 
7
141
  export declare function composeAdapters<T = unknown>(primary: StorageAdapter<T>, ...secondaries: StorageAdapter<T>[]): StorageAdapter<T>;
package/dist/adapters.js CHANGED
@@ -1 +1 @@
1
- 'use strict';var chunk6NXXQYQL_js=require('./chunk-6NXXQYQL.js'),chunk7ZZO7GVB_js=require('./chunk-7ZZO7GVB.js'),chunkVWED6UTN_js=require('./chunk-VWED6UTN.js');require('./chunk-RFPLZDIO.js');Object.defineProperty(exports,"apiAdapter",{enumerable:true,get:function(){return chunk6NXXQYQL_js.a}});Object.defineProperty(exports,"composeAdapters",{enumerable:true,get:function(){return chunk6NXXQYQL_js.c}});Object.defineProperty(exports,"memoryAdapter",{enumerable:true,get:function(){return chunk6NXXQYQL_js.b}});Object.defineProperty(exports,"cookieAdapter",{enumerable:true,get:function(){return chunk7ZZO7GVB_js.b}});Object.defineProperty(exports,"sessionStorageAdapter",{enumerable:true,get:function(){return chunk7ZZO7GVB_js.a}});Object.defineProperty(exports,"localStorageAdapter",{enumerable:true,get:function(){return chunkVWED6UTN_js.a}});
1
+ 'use strict';var chunkROTLSZMV_js=require('./chunk-ROTLSZMV.js'),chunk7ZZO7GVB_js=require('./chunk-7ZZO7GVB.js'),chunkVWED6UTN_js=require('./chunk-VWED6UTN.js');require('./chunk-RFPLZDIO.js');Object.defineProperty(exports,"apiAdapter",{enumerable:true,get:function(){return chunkROTLSZMV_js.a}});Object.defineProperty(exports,"composeAdapters",{enumerable:true,get:function(){return chunkROTLSZMV_js.c}});Object.defineProperty(exports,"memoryAdapter",{enumerable:true,get:function(){return chunkROTLSZMV_js.b}});Object.defineProperty(exports,"cookieAdapter",{enumerable:true,get:function(){return chunk7ZZO7GVB_js.b}});Object.defineProperty(exports,"sessionStorageAdapter",{enumerable:true,get:function(){return chunk7ZZO7GVB_js.a}});Object.defineProperty(exports,"localStorageAdapter",{enumerable:true,get:function(){return chunkVWED6UTN_js.a}});
package/dist/adapters.mjs CHANGED
@@ -1 +1 @@
1
- export{a as apiAdapter,c as composeAdapters,b as memoryAdapter}from'./chunk-NBZUZYTB.mjs';export{b as cookieAdapter,a as sessionStorageAdapter}from'./chunk-UASG46LP.mjs';export{a as localStorageAdapter}from'./chunk-DBZSN4WP.mjs';import'./chunk-ZJYULEER.mjs';
1
+ export{a as apiAdapter,c as composeAdapters,b as memoryAdapter}from'./chunk-PL4XNCQA.mjs';export{b as cookieAdapter,a as sessionStorageAdapter}from'./chunk-UASG46LP.mjs';export{a as localStorageAdapter}from'./chunk-DBZSN4WP.mjs';import'./chunk-ZJYULEER.mjs';
@@ -0,0 +1 @@
1
+ import {a}from'./chunk-BFAX7JQA.mjs';import {jsx}from'react/jsx-runtime';var p=[{id:"essential",label:"Essential Cookies",description:"Required for basic site functionality. Cannot be disabled.",required:true,purpose:"Site operation"},{id:"analytics",label:"Analytics",description:"Help us understand how visitors use our site to improve the experience.",required:false,purpose:"Usage analytics"},{id:"marketing",label:"Marketing",description:"Used to deliver relevant advertisements and track campaign effectiveness.",required:false,purpose:"Targeted advertising"},{id:"preferences",label:"Preferences",description:"Remember your settings and preferences for a personalised experience.",required:false,purpose:"Personalisation"}],f=({extraOptions:a$1=[],options:t,adapter:n,position:o="bottom",classNames:l,unstyled:d,onSave:s,copy:e})=>{let m=t!=null?t:[...p,...a$1];return jsx(a,{options:m,onSave:i=>{n&&n.save(i),s==null||s(i);},position:o,classNames:l,unstyled:d,manageStorage:!n,title:e==null?void 0:e.title,description:e==null?void 0:e.description,acceptAllButtonText:e==null?void 0:e.acceptAll,rejectAllButtonText:e==null?void 0:e.rejectAll,customizeButtonText:e==null?void 0:e.customize,saveButtonText:e==null?void 0:e.save})};export{f as a};
@@ -0,0 +1 @@
1
+ import {d,b,a}from'./chunk-ZJYULEER.mjs';function M(e){return e.response?e.response.status>=500:true}function O(e){return new Promise(r=>setTimeout(r,e))}function $(e){return e?typeof e=="function"?e():e:{}}function q(e,r={}){var w,k,g;let{headers:n,credentials:d$1="same-origin",loadMethod:p="GET",saveMethod:l="POST",unwrap:A,retry:s,onError:h,onSuccess:i,fetchInit:x}=r,T=(w=s==null?void 0:s.attempts)!=null?w:0,C=(k=s==null?void 0:s.baseDelayMs)!=null?k:250,E=(g=s==null?void 0:s.shouldRetry)!=null?g:M,y=h!=null?h:(t=>{t.method!=="load"&&(t.response?console.warn(`[ndpr-toolkit] Failed to ${t.method==="save"?"save to":"delete from"} ${t.endpoint}: ${t.response.status}`):console.warn(`[ndpr-toolkit] Failed to ${t.method==="save"?"save to":"delete from"} ${t.endpoint}`));});function v(t,o,u){return d(this,null,function*(){for(let c=0;c<=T;c++){let a$1,S;try{a$1=yield fetch(e,b(a(a({},x),o),{headers:a(a({},$(n)),o.headers),credentials:d$1}));}catch(b){S=b;}if(a$1&&a$1.ok)return {ok:true,response:a$1};let R={method:t,endpoint:e,error:S,response:a$1,status:a$1==null?void 0:a$1.status,payload:u,attempt:c};if(c===T||!E(R))return y(R),{ok:false};yield O(C*Math.pow(2,c));}return {ok:false}})}return {load(){return d(this,null,function*(){let t=yield v("load",{method:p,headers:{}});if(!t.ok)return null;try{let o=yield t.response.json(),u=A?A(o):o;return i&&i({method:"load",endpoint:e,response:t.response,data:u!=null?u:void 0}),u}catch(o){return y({method:"load",endpoint:e,error:o,response:t.response,status:t.response.status,attempt:T}),null}})},save(t){return d(this,null,function*(){let o=yield v("save",{method:l,headers:{"Content-Type":"application/json"},body:JSON.stringify(t)},t);o.ok&&i&&i({method:"save",endpoint:e,response:o.response,payload:t});})},remove(){return d(this,null,function*(){let t=yield v("remove",{method:"DELETE",headers:{}});t.ok&&i&&i({method:"remove",endpoint:e,response:t.response});})}}}function F(e){let r=e!=null?e:null;return {load(){return r},save(n){r=n;},remove(){r=null;}}}function H(e,...r){return {load(){return e.load()},save(n){let d=e.save(n),p=()=>{for(let l of r)try{l.save(n);}catch(A){console.warn("[ndpr-toolkit] Secondary adapter save failed:",A);}};if(d instanceof Promise)return d.then(()=>p());p();},remove(){let n=e.remove(),d=()=>{for(let p of r)try{p.remove();}catch(l){console.warn("[ndpr-toolkit] Secondary adapter remove failed:",l);}};if(n instanceof Promise)return n.then(()=>d());d();}}}export{q as a,F as b,H as c};
@@ -0,0 +1 @@
1
+ 'use strict';var chunkRFPLZDIO_js=require('./chunk-RFPLZDIO.js');function M(e){return e.response?e.response.status>=500:true}function O(e){return new Promise(r=>setTimeout(r,e))}function $(e){return e?typeof e=="function"?e():e:{}}function q(e,r={}){var w,k,g;let{headers:n,credentials:d="same-origin",loadMethod:p="GET",saveMethod:l="POST",unwrap:A,retry:s,onError:h,onSuccess:i,fetchInit:x}=r,T=(w=s==null?void 0:s.attempts)!=null?w:0,C=(k=s==null?void 0:s.baseDelayMs)!=null?k:250,E=(g=s==null?void 0:s.shouldRetry)!=null?g:M,y=h!=null?h:(t=>{t.method!=="load"&&(t.response?console.warn(`[ndpr-toolkit] Failed to ${t.method==="save"?"save to":"delete from"} ${t.endpoint}: ${t.response.status}`):console.warn(`[ndpr-toolkit] Failed to ${t.method==="save"?"save to":"delete from"} ${t.endpoint}`));});function v(t,o,u){return chunkRFPLZDIO_js.d(this,null,function*(){for(let c=0;c<=T;c++){let a,S;try{a=yield fetch(e,chunkRFPLZDIO_js.b(chunkRFPLZDIO_js.a(chunkRFPLZDIO_js.a({},x),o),{headers:chunkRFPLZDIO_js.a(chunkRFPLZDIO_js.a({},$(n)),o.headers),credentials:d}));}catch(b){S=b;}if(a&&a.ok)return {ok:true,response:a};let R={method:t,endpoint:e,error:S,response:a,status:a==null?void 0:a.status,payload:u,attempt:c};if(c===T||!E(R))return y(R),{ok:false};yield O(C*Math.pow(2,c));}return {ok:false}})}return {load(){return chunkRFPLZDIO_js.d(this,null,function*(){let t=yield v("load",{method:p,headers:{}});if(!t.ok)return null;try{let o=yield t.response.json(),u=A?A(o):o;return i&&i({method:"load",endpoint:e,response:t.response,data:u!=null?u:void 0}),u}catch(o){return y({method:"load",endpoint:e,error:o,response:t.response,status:t.response.status,attempt:T}),null}})},save(t){return chunkRFPLZDIO_js.d(this,null,function*(){let o=yield v("save",{method:l,headers:{"Content-Type":"application/json"},body:JSON.stringify(t)},t);o.ok&&i&&i({method:"save",endpoint:e,response:o.response,payload:t});})},remove(){return chunkRFPLZDIO_js.d(this,null,function*(){let t=yield v("remove",{method:"DELETE",headers:{}});t.ok&&i&&i({method:"remove",endpoint:e,response:t.response});})}}}function F(e){let r=e!=null?e:null;return {load(){return r},save(n){r=n;},remove(){r=null;}}}function H(e,...r){return {load(){return e.load()},save(n){let d=e.save(n),p=()=>{for(let l of r)try{l.save(n);}catch(A){console.warn("[ndpr-toolkit] Secondary adapter save failed:",A);}};if(d instanceof Promise)return d.then(()=>p());p();},remove(){let n=e.remove(),d=()=>{for(let p of r)try{p.remove();}catch(l){console.warn("[ndpr-toolkit] Secondary adapter remove failed:",l);}};if(n instanceof Promise)return n.then(()=>d());d();}}}exports.a=q;exports.b=F;exports.c=H;
@@ -0,0 +1 @@
1
+ import {a}from'./chunk-BNHQFZHL.mjs';import {a as a$1}from'./chunk-ZJYULEER.mjs';import {jsx}from'react/jsx-runtime';var p=t=>jsx(a,a$1({},t));export{p as a};
@@ -0,0 +1 @@
1
+ 'use strict';var chunkW47OSMT6_js=require('./chunk-W47OSMT6.js'),chunkRFPLZDIO_js=require('./chunk-RFPLZDIO.js'),jsxRuntime=require('react/jsx-runtime');var S=[{id:"access",name:"Access My Data",description:"Request a copy of your personal data held by us",ndpaSection:"Section 34(1)(a)\u2013(b)",estimatedCompletionTime:30,requiresAdditionalInfo:false},{id:"rectification",name:"Correct My Data",description:"Request corrections to inaccurate personal data",ndpaSection:"Section 34(1)(c)",estimatedCompletionTime:30,requiresAdditionalInfo:true,additionalFields:[{id:"correction_details",label:"What data needs to be corrected?",type:"textarea",required:true,placeholder:"Please describe the inaccurate data and what the correct information should be"}]},{id:"erasure",name:"Delete My Data",description:"Request deletion of your personal data",ndpaSection:"Section 34(1)(d), Section 34(2)",estimatedCompletionTime:30,requiresAdditionalInfo:false},{id:"portability",name:"Export My Data",description:"Receive your data in a portable format",ndpaSection:"Section 38",estimatedCompletionTime:30,requiresAdditionalInfo:false},{id:"restrict",name:"Restrict Processing",description:"Request restriction of data processing",ndpaSection:"Section 34(1)(e)",estimatedCompletionTime:30,requiresAdditionalInfo:false},{id:"object",name:"Object to Processing",description:"Object to processing of your personal data",ndpaSection:"Section 36",estimatedCompletionTime:30,requiresAdditionalInfo:false},{id:"withdraw_consent",name:"Withdraw My Consent",description:"Withdraw consent previously given for processing",ndpaSection:"Section 35",estimatedCompletionTime:30,requiresAdditionalInfo:false}],g=({requestTypes:p=S,adapter:i,classNames:m,unstyled:f,onSubmit:R=()=>{},submitTo:s,submitOptions:e,onSubmitError:t})=>jsxRuntime.jsx(chunkW47OSMT6_js.a,{requestTypes:p,onSubmit:o=>chunkRFPLZDIO_js.d(null,null,function*(){var n,r;if(s){let y=typeof(e==null?void 0:e.headers)=="function"?e.headers():(n=e==null?void 0:e.headers)!=null?n:{};try{let a=yield fetch(s,{method:"POST",headers:chunkRFPLZDIO_js.a({"Content-Type":"application/json"},y),credentials:(r=e==null?void 0:e.credentials)!=null?r:"same-origin",body:JSON.stringify(o)});a.ok||t==null||t({response:a});}catch(a){t==null||t({error:a});}}else i&&i.save(o);R(o);}),classNames:m,unstyled:f});exports.a=g;
@@ -0,0 +1 @@
1
+ import {a}from'./chunk-XJO4DH3L.mjs';import {d,a as a$1}from'./chunk-ZJYULEER.mjs';import {jsx}from'react/jsx-runtime';var S=[{id:"access",name:"Access My Data",description:"Request a copy of your personal data held by us",ndpaSection:"Section 34(1)(a)\u2013(b)",estimatedCompletionTime:30,requiresAdditionalInfo:false},{id:"rectification",name:"Correct My Data",description:"Request corrections to inaccurate personal data",ndpaSection:"Section 34(1)(c)",estimatedCompletionTime:30,requiresAdditionalInfo:true,additionalFields:[{id:"correction_details",label:"What data needs to be corrected?",type:"textarea",required:true,placeholder:"Please describe the inaccurate data and what the correct information should be"}]},{id:"erasure",name:"Delete My Data",description:"Request deletion of your personal data",ndpaSection:"Section 34(1)(d), Section 34(2)",estimatedCompletionTime:30,requiresAdditionalInfo:false},{id:"portability",name:"Export My Data",description:"Receive your data in a portable format",ndpaSection:"Section 38",estimatedCompletionTime:30,requiresAdditionalInfo:false},{id:"restrict",name:"Restrict Processing",description:"Request restriction of data processing",ndpaSection:"Section 34(1)(e)",estimatedCompletionTime:30,requiresAdditionalInfo:false},{id:"object",name:"Object to Processing",description:"Object to processing of your personal data",ndpaSection:"Section 36",estimatedCompletionTime:30,requiresAdditionalInfo:false},{id:"withdraw_consent",name:"Withdraw My Consent",description:"Withdraw consent previously given for processing",ndpaSection:"Section 35",estimatedCompletionTime:30,requiresAdditionalInfo:false}],g=({requestTypes:p=S,adapter:i,classNames:m,unstyled:f,onSubmit:R=()=>{},submitTo:s,submitOptions:e,onSubmitError:t})=>jsx(a,{requestTypes:p,onSubmit:o=>d(null,null,function*(){var n,r;if(s){let y=typeof(e==null?void 0:e.headers)=="function"?e.headers():(n=e==null?void 0:e.headers)!=null?n:{};try{let a=yield fetch(s,{method:"POST",headers:a$1({"Content-Type":"application/json"},y),credentials:(r=e==null?void 0:e.credentials)!=null?r:"same-origin",body:JSON.stringify(o)});a.ok||t==null||t({response:a});}catch(a){t==null||t({error:a});}}else i&&i.save(o);R(o);}),classNames:m,unstyled:f});export{g as a};
@@ -0,0 +1 @@
1
+ 'use strict';var chunk732C2EVN_js=require('./chunk-732C2EVN.js'),jsxRuntime=require('react/jsx-runtime');var p=[{id:"essential",label:"Essential Cookies",description:"Required for basic site functionality. Cannot be disabled.",required:true,purpose:"Site operation"},{id:"analytics",label:"Analytics",description:"Help us understand how visitors use our site to improve the experience.",required:false,purpose:"Usage analytics"},{id:"marketing",label:"Marketing",description:"Used to deliver relevant advertisements and track campaign effectiveness.",required:false,purpose:"Targeted advertising"},{id:"preferences",label:"Preferences",description:"Remember your settings and preferences for a personalised experience.",required:false,purpose:"Personalisation"}],f=({extraOptions:a=[],options:t,adapter:n,position:o="bottom",classNames:l,unstyled:d,onSave:s,copy:e})=>{let m=t!=null?t:[...p,...a];return jsxRuntime.jsx(chunk732C2EVN_js.a,{options:m,onSave:i=>{n&&n.save(i),s==null||s(i);},position:o,classNames:l,unstyled:d,manageStorage:!n,title:e==null?void 0:e.title,description:e==null?void 0:e.description,acceptAllButtonText:e==null?void 0:e.acceptAll,rejectAllButtonText:e==null?void 0:e.rejectAll,customizeButtonText:e==null?void 0:e.customize,saveButtonText:e==null?void 0:e.save})};exports.a=f;
@@ -0,0 +1 @@
1
+ 'use strict';var chunkI2LMQWK3_js=require('./chunk-I2LMQWK3.js'),chunkRFPLZDIO_js=require('./chunk-RFPLZDIO.js'),jsxRuntime=require('react/jsx-runtime');var p=t=>jsxRuntime.jsx(chunkI2LMQWK3_js.a,chunkRFPLZDIO_js.a({},t));exports.a=p;
@@ -0,0 +1,139 @@
1
+ import React__default from 'react';
2
+
3
+ declare interface ConsentBannerClassNames {
4
+ root?: string;
5
+ container?: string;
6
+ title?: string;
7
+ description?: string;
8
+ optionsList?: string;
9
+ optionItem?: string;
10
+ optionCheckbox?: string;
11
+ optionLabel?: string;
12
+ optionDescription?: string;
13
+ buttonGroup?: string;
14
+ acceptButton?: string;
15
+ rejectButton?: string;
16
+ customizeButton?: string;
17
+ saveButton?: string;
18
+ customizePanel?: string;
19
+ selectAllButton?: string;
20
+ /** Alias for acceptButton */
21
+ primaryButton?: string;
22
+ /** Alias for rejectButton */
23
+ secondaryButton?: string;
24
+ }
25
+
26
+ /**
27
+ * Consent types aligned with NDPA 2023 Section 25-26
28
+ * Consent must be freely given, specific, informed, and unambiguous
29
+ */
30
+ /**
31
+ * Represents a consent option that can be presented to users
32
+ */
33
+ declare interface ConsentOption {
34
+ /** Unique identifier for the consent option */
35
+ id: string;
36
+ /** Display label for the consent option */
37
+ label: string;
38
+ /** Detailed description of what this consent option covers */
39
+ description: string;
40
+ /** Whether this consent option is required (cannot be declined) */
41
+ required: boolean;
42
+ /**
43
+ * The specific purpose for which data will be processed
44
+ * NDPA Section 25(2) requires consent to be specific to each purpose
45
+ */
46
+ purpose: string;
47
+ /**
48
+ * Default state of the consent option
49
+ * @default false
50
+ */
51
+ defaultValue?: boolean;
52
+ /**
53
+ * Categories of personal data covered by this consent option
54
+ */
55
+ dataCategories?: string[];
56
+ }
57
+
58
+ /**
59
+ * Represents the user's consent settings
60
+ */
61
+ declare interface ConsentSettings {
62
+ /** Map of consent option IDs to boolean values indicating consent status */
63
+ consents: Record<string, boolean>;
64
+ /** Timestamp when consent was last updated */
65
+ timestamp: number;
66
+ /** Version of the consent form that was accepted */
67
+ version: string;
68
+ /** Method used to collect consent (e.g., "banner", "settings", "api") */
69
+ method: string;
70
+ /** Whether the user has actively made a choice (as opposed to default settings) */
71
+ hasInteracted: boolean;
72
+ /**
73
+ * The lawful basis under which processing is conducted
74
+ * Required by NDPA Section 25(1)
75
+ */
76
+ lawfulBasis?: LawfulBasisType;
77
+ }
78
+
79
+ /**
80
+ * Lawful basis for processing personal data per NDPA Section 25(1)
81
+ */
82
+ declare type LawfulBasisType = 'consent' | 'contract' | 'legal_obligation' | 'vital_interests' | 'public_interest' | 'legitimate_interests';
83
+
84
+ export declare const NDPRConsent: React__default.FC<NDPRConsentProps>;
85
+
86
+ /**
87
+ * UX copy overrides for the NDPRConsent preset. Pass any subset to
88
+ * replace the default text without dropping to the lower-level
89
+ * `<ConsentBanner>` API. Strings you omit fall back to the toolkit
90
+ * defaults (which already cite NDPA Section 26).
91
+ *
92
+ * @example
93
+ * <NDPRConsent copy={{
94
+ * title: 'Cookie preferences',
95
+ * description: 'Acme uses cookies to keep you signed in and improve our store.',
96
+ * acceptAll: 'Allow all',
97
+ * rejectAll: 'Only essentials',
98
+ * }} />
99
+ */
100
+ export declare interface NDPRConsentCopy {
101
+ /** Banner heading. Default: "We Value Your Privacy" */
102
+ title?: string;
103
+ /** Body paragraph under the heading. Default cites NDPA Section 26. */
104
+ description?: string;
105
+ /** Primary CTA — accepts all categories. Default: "Accept All" */
106
+ acceptAll?: string;
107
+ /** Secondary CTA — rejects all non-essential categories. Default: "Reject All" */
108
+ rejectAll?: string;
109
+ /** Tertiary CTA — opens the per-category controls. Default: "Customize" */
110
+ customize?: string;
111
+ /** Submit button on the per-category panel. Default: "Save Preferences" */
112
+ save?: string;
113
+ }
114
+
115
+ export declare interface NDPRConsentProps {
116
+ extraOptions?: ConsentOption[];
117
+ options?: ConsentOption[];
118
+ adapter?: StorageAdapter<ConsentSettings>;
119
+ position?: 'top' | 'bottom' | 'center' | 'inline';
120
+ classNames?: ConsentBannerClassNames;
121
+ unstyled?: boolean;
122
+ onSave?: (settings: ConsentSettings) => void;
123
+ /**
124
+ * UX copy overrides — see {@link NDPRConsentCopy}. Lets you brand the
125
+ * banner without dropping to the lower-level `<ConsentBanner>` API.
126
+ */
127
+ copy?: NDPRConsentCopy;
128
+ }
129
+
130
+ declare interface StorageAdapter<T = unknown> {
131
+ /** Load persisted data. Called once on hook mount. */
132
+ load(): T | null | Promise<T | null>;
133
+ /** Persist data. Called on every state change. */
134
+ save(data: T): void | Promise<void>;
135
+ /** Clear persisted data. Called on reset. */
136
+ remove(): void | Promise<void>;
137
+ }
138
+
139
+ export { }