@tantainnovative/ndpr-toolkit 3.5.5 → 3.6.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.
- package/CHANGELOG.md +25 -0
- package/README.md +17 -1
- package/dist/adapters.d.mts +137 -3
- package/dist/adapters.d.ts +137 -3
- package/dist/adapters.js +1 -1
- package/dist/adapters.mjs +1 -1
- package/dist/chunk-LTPSN2SU.mjs +1 -0
- package/dist/chunk-PL4XNCQA.mjs +1 -0
- package/dist/chunk-ROTLSZMV.js +1 -0
- package/dist/chunk-RV2VMWZJ.mjs +1 -0
- package/dist/chunk-RXZFYBUJ.js +1 -0
- package/dist/chunk-SJRIOZ4K.mjs +1 -0
- package/dist/chunk-V3RYHNHN.js +1 -0
- package/dist/chunk-W7RBGZCC.js +1 -0
- package/dist/presets-consent.d.mts +139 -0
- package/dist/presets-consent.d.ts +139 -0
- package/dist/presets-consent.js +2 -0
- package/dist/presets-consent.mjs +2 -0
- package/dist/presets-dsr.d.mts +133 -0
- package/dist/presets-dsr.d.ts +133 -0
- package/dist/presets-dsr.js +2 -0
- package/dist/presets-dsr.mjs +2 -0
- package/dist/presets-policy.d.mts +203 -0
- package/dist/presets-policy.d.ts +203 -0
- package/dist/presets-policy.js +2 -0
- package/dist/presets-policy.mjs +2 -0
- package/dist/presets.d.mts +73 -0
- package/dist/presets.d.ts +73 -0
- package/dist/presets.js +1 -1
- package/dist/presets.mjs +1 -1
- package/dist/server.d.mts +137 -3
- package/dist/server.d.ts +137 -3
- package/dist/server.js +1 -1
- package/dist/server.mjs +1 -1
- package/package.json +25 -1
- package/dist/chunk-6NXXQYQL.js +0 -1
- package/dist/chunk-NBZUZYTB.mjs +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
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.0](https://github.com/mr-tanta/ndpr-toolkit/compare/v3.5.5...v3.6.0) (2026-05-24)
|
|
6
|
+
|
|
7
|
+
### Features (developer feedback)
|
|
8
|
+
|
|
9
|
+
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.
|
|
10
|
+
|
|
11
|
+
* **`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.
|
|
12
|
+
* **`NDPRConsent` exposes a `copy` prop.** Override `title` / `description` / `acceptAll` / `rejectAll` / `customize` / `save` strings without dropping to the lower-level `<ConsentBanner>` API.
|
|
13
|
+
* **`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.
|
|
14
|
+
* **Per-preset subpath entries** for bundle-size-sensitive consumers:
|
|
15
|
+
- `@tantainnovative/ndpr-toolkit/presets/consent` — just `NDPRConsent` (~4KB vs ~8KB for the full barrel)
|
|
16
|
+
- `@tantainnovative/ndpr-toolkit/presets/dsr` — just `NDPRSubjectRights`
|
|
17
|
+
- `@tantainnovative/ndpr-toolkit/presets/policy` — just `NDPRPrivacyPolicy`
|
|
18
|
+
The full `/presets` barrel is unchanged. These are additive narrower entries.
|
|
19
|
+
|
|
20
|
+
### Docs
|
|
21
|
+
|
|
22
|
+
* README install block now shows Bun, npm, and Yarn alongside pnpm.
|
|
23
|
+
|
|
24
|
+
### Coming next (3.6.1+)
|
|
25
|
+
|
|
26
|
+
- Recipe pages for ecommerce / newsletter / contact-form / careers / admin DSR patterns
|
|
27
|
+
- Org-specific privacy policy templates (SaaS, ecommerce, school, healthcare, procurement)
|
|
28
|
+
- Continued bundle reduction
|
|
29
|
+
|
|
5
30
|
## [3.5.5](https://github.com/mr-tanta/ndpr-toolkit/compare/v3.5.4...v3.5.5) (2026-05-24)
|
|
6
31
|
|
|
7
32
|
### Features (tests + types)
|
package/README.md
CHANGED
|
@@ -74,14 +74,26 @@ That's it. NDPA-compliant consent with server-side persistence in under 20 lines
|
|
|
74
74
|
|
|
75
75
|
## Install
|
|
76
76
|
|
|
77
|
+
Pick your package manager:
|
|
78
|
+
|
|
77
79
|
```bash
|
|
80
|
+
# pnpm
|
|
78
81
|
pnpm add @tantainnovative/ndpr-toolkit
|
|
82
|
+
|
|
83
|
+
# Bun
|
|
84
|
+
bun add @tantainnovative/ndpr-toolkit
|
|
85
|
+
|
|
86
|
+
# npm
|
|
87
|
+
npm install @tantainnovative/ndpr-toolkit
|
|
88
|
+
|
|
89
|
+
# Yarn
|
|
90
|
+
yarn add @tantainnovative/ndpr-toolkit
|
|
79
91
|
```
|
|
80
92
|
|
|
81
93
|
Add the stylesheet import once in your app entry so components render with default styles:
|
|
82
94
|
|
|
83
95
|
```ts
|
|
84
|
-
// app/layout.tsx (Next.js App Router) or src/main.tsx (Vite/CRA)
|
|
96
|
+
// app/layout.tsx (Next.js App Router) or src/main.tsx (Vite/CRA/Bun)
|
|
85
97
|
import "@tantainnovative/ndpr-toolkit/styles";
|
|
86
98
|
```
|
|
87
99
|
|
|
@@ -90,7 +102,11 @@ The stylesheet is opinionated but token-driven — override any `--ndpr-*` CSS c
|
|
|
90
102
|
Install UI peer dependencies (only needed if you use the higher-level Radix-based components from `/presets`):
|
|
91
103
|
|
|
92
104
|
```bash
|
|
105
|
+
# pnpm
|
|
93
106
|
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
|
|
107
|
+
|
|
108
|
+
# Bun
|
|
109
|
+
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
110
|
```
|
|
95
111
|
|
|
96
112
|
Or scaffold instantly with the CLI:
|
package/dist/adapters.d.mts
CHANGED
|
@@ -1,7 +1,141 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
4
|
-
|
|
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.d.ts
CHANGED
|
@@ -1,7 +1,141 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
4
|
-
|
|
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
|
|
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-
|
|
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 { }
|