@consenti/ui 0.2.0 → 0.3.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/README.md +255 -66
- package/dist/angular.d.cts +306 -130
- package/dist/angular.d.ts +306 -130
- package/dist/chunk-3EXCD24H.mjs +2 -0
- package/dist/{chunk-K2UN3O67.mjs.map → chunk-3EXCD24H.mjs.map} +1 -1
- package/dist/chunk-J4BKXUD6.mjs +2 -0
- package/dist/chunk-J4BKXUD6.mjs.map +1 -0
- package/dist/http-AQVVUCX6.mjs +2 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +616 -105
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/profiles-OEZAKEWL.mjs +2 -0
- package/dist/profiles-OEZAKEWL.mjs.map +1 -0
- package/dist/react.d.cts +285 -108
- package/dist/react.d.ts +285 -108
- package/dist/vue.d.cts +257 -57
- package/dist/vue.d.ts +257 -57
- package/package.json +8 -7
- package/dist/chunk-K2UN3O67.mjs +0 -2
- package/dist/http-UU2NIW6M.mjs +0 -2
- /package/dist/{http-UU2NIW6M.mjs.map → http-AQVVUCX6.mjs.map} +0 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a,b,c,d,e,f,g,h,i,j}from"./chunk-J4BKXUD6.mjs";export{i as DEFAULT_PROFILES,g as GENERAL_PRIVACY_CONSENT_EN_PROFILE,h as NOTICE_ONLY_EN_PROFILE,f as OPT_IN_BRAZIL_EN_PROFILE,e as OPT_IN_CHINA_EN_PROFILE,d as OPT_IN_DPDPA_EN_PROFILE,a as OPT_IN_EN_PROFILE,b as OPT_OUT_EN_PROFILE,c as OPT_OUT_STRICT_EN_PROFILE,j as resolveLocaleTranslation};
|
|
2
|
+
//# sourceMappingURL=profiles-OEZAKEWL.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/react.d.cts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
type NonEmptyArray<T> = [T, ...T[]];
|
|
2
|
+
/**
|
|
3
|
+
* Deep-partial with JSON Merge Patch (RFC 7396) delete semantics: any key may be
|
|
4
|
+
* set to `null` instead of its usual type to signal "delete this key" when the
|
|
5
|
+
* partial is applied with `deepMerge()` — `undefined`/omitted keeps the base
|
|
6
|
+
* value, `null` removes it. Only meaningful for object/map keys (e.g. removing
|
|
7
|
+
* a `CategoryMap`/`CookieMap` entry via `profileOverride`); array elements are
|
|
8
|
+
* merged positionally and don't support deletion this way.
|
|
9
|
+
*/
|
|
2
10
|
type DeepPartial<T> = T extends Function ? T : T extends Array<infer InferredArrayType> ? Array<DeepPartial<InferredArrayType>> : T extends ReadonlyArray<infer InferredReadonlyArrayType> ? ReadonlyArray<DeepPartial<InferredReadonlyArrayType>> : T extends object ? {
|
|
3
|
-
[Key in keyof T]?: DeepPartial<T[Key]
|
|
11
|
+
[Key in keyof T]?: DeepPartial<T[Key]> | null;
|
|
4
12
|
} : T;
|
|
5
13
|
|
|
6
14
|
declare const COMPLIANCE_GROUP_IDS: readonly ["opt-in", "opt-out", "opt-out-strict", "opt-in-dpdpa", "opt-in-china", "opt-in-brazil", "general-privacy-consent", "notice-only"];
|
|
15
|
+
declare const COOKIE_PURPOSE_IDS: readonly ["necessary", "functional", "preferences", "analytics", "marketing"];
|
|
7
16
|
|
|
8
17
|
/**
|
|
9
18
|
* Compliance-related literal types, derived from the regulation/group ID data
|
|
@@ -19,6 +28,13 @@ declare const COMPLIANCE_GROUP_IDS: readonly ["opt-in", "opt-out", "opt-out-stri
|
|
|
19
28
|
*/
|
|
20
29
|
|
|
21
30
|
type ComplianceGroupId = typeof COMPLIANCE_GROUP_IDS[number];
|
|
31
|
+
/**
|
|
32
|
+
* `(string & {})` (rather than plain `string`) keeps IDE autocomplete for the
|
|
33
|
+
* known `ComplianceGroupId` literals while still allowing any other string —
|
|
34
|
+
* used to target a profile authored against a custom (non-built-in)
|
|
35
|
+
* `customComplianceGroup` instead of one of the 8 built-in groups.
|
|
36
|
+
*/
|
|
37
|
+
type ComplianceType = 'auto' | ComplianceGroupId | (string & {}) | Symbol;
|
|
22
38
|
|
|
23
39
|
interface ComplianceMapData {
|
|
24
40
|
version: string;
|
|
@@ -33,24 +49,67 @@ interface CountryComplianceEntry {
|
|
|
33
49
|
description: string;
|
|
34
50
|
}>;
|
|
35
51
|
}
|
|
52
|
+
interface ConsentDbRecord {
|
|
53
|
+
id: string;
|
|
54
|
+
tenantId: string;
|
|
55
|
+
visitorId: string;
|
|
56
|
+
profileId: string;
|
|
57
|
+
locale: string;
|
|
58
|
+
consentJson: ConsentValue;
|
|
59
|
+
gpcDetected: boolean;
|
|
60
|
+
source: 'banner' | 'api' | 'import';
|
|
61
|
+
ageVerified?: boolean;
|
|
62
|
+
parentalConsentToken?: string;
|
|
63
|
+
tcfString?: string;
|
|
64
|
+
/** HMAC-SHA256 signature over the record's core fields, hex-encoded. Only present when
|
|
65
|
+
* `consentSigningKey` is configured — opt-in tamper-evidence for server-stored records,
|
|
66
|
+
* independent of the widget's own cookie-signing (`core.cookieSigningKey`). */
|
|
67
|
+
signature?: string;
|
|
68
|
+
createdAt: string;
|
|
69
|
+
updatedAt: string;
|
|
70
|
+
}
|
|
36
71
|
|
|
37
72
|
type ConsentStatus = 'granted' | 'denied' | 'objected';
|
|
38
73
|
type ConsentAction = 'accept_all' | 'reject_all' | 'custom' | 'update';
|
|
39
74
|
type ConsentValue = Record<string, ConsentStatus>;
|
|
75
|
+
/**
|
|
76
|
+
* Output format for getConsent(type).
|
|
77
|
+
* 'default' → raw ConsentValue (existing behaviour)
|
|
78
|
+
* 'google-gtm' → Google Consent Mode v2 keys + ads_data_redaction, url_passthrough
|
|
79
|
+
* 'category' → consent per CookiePurpose category
|
|
80
|
+
* 'adobe' → { analytics, target, manager, optimizer }
|
|
81
|
+
* 'meta' → { pixel, api, plugins, facebookLogin }
|
|
82
|
+
* 'microsoft-clarity' → { session, heatmaps, performance }
|
|
83
|
+
* 'twilio-segment' → { identify, page, track, group, alias }
|
|
84
|
+
*/
|
|
85
|
+
type ConsentType = 'default' | 'google-gtm' | 'category' | 'adobe' | 'meta' | 'microsoft-clarity' | 'twilio-segment';
|
|
86
|
+
type GpcMode = 'ignore' | 'honor' | 'strict';
|
|
87
|
+
type LegalBasis = 'mandatory' | 'consent' | 'legitimate_interest';
|
|
88
|
+
type CookiePurpose = typeof COOKIE_PURPOSE_IDS[number];
|
|
89
|
+
/**
|
|
90
|
+
* A single consent parameter (tracker/cookie definition).
|
|
91
|
+
* Keyed by its own id inside `Record<string, Cookie>` — the map key IS the id,
|
|
92
|
+
* there is no `id` field on the value itself.
|
|
93
|
+
*/
|
|
40
94
|
interface Cookie {
|
|
41
|
-
|
|
42
|
-
legalBasis?: 'mandatory' | 'consent' | 'legitimate_interest';
|
|
43
|
-
/** @deprecated use legalBasis: 'mandatory' */
|
|
44
|
-
mandatory?: boolean;
|
|
45
|
-
/** @deprecated use legalBasis */
|
|
46
|
-
type?: 'consent' | 'legitimate_interest';
|
|
95
|
+
purpose?: CookiePurpose;
|
|
47
96
|
listenGpc?: boolean;
|
|
48
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Pre-grant this parameter's default consent to 'granted' (instead of the
|
|
99
|
+
* compliance-group-driven default) when no stored decision exists yet.
|
|
100
|
+
* Only meaningful when the owning category's `legalBasis === 'consent'` —
|
|
101
|
+
* `mandatory`/`legitimate_interest` categories are already effectively
|
|
102
|
+
* pre-granted regardless of this flag. Never overrides an active GPC signal.
|
|
103
|
+
* Default: false.
|
|
104
|
+
*/
|
|
105
|
+
preGrant?: boolean;
|
|
49
106
|
tcfVendorId?: number;
|
|
50
107
|
tcfPurposes?: number[];
|
|
51
108
|
tcfSpecialFeatures?: number[];
|
|
52
109
|
cpraCategory?: 'sale' | 'sharing' | 'sensitive';
|
|
53
110
|
}
|
|
111
|
+
/** `Cookie` keyed by its own id — the standard shape used everywhere a parameter list is passed around. */
|
|
112
|
+
type CookieMap = Record<string, Cookie>;
|
|
54
113
|
type ButtonStyle = 'primary' | 'secondary' | 'text' | 'accent';
|
|
55
114
|
type ButtonAction = 'submit' | 'manage' | 'close' | 'custom' | 'link';
|
|
56
115
|
interface Button {
|
|
@@ -59,8 +118,11 @@ interface Button {
|
|
|
59
118
|
action: ButtonAction;
|
|
60
119
|
cookies?: string[] | '*' | '!';
|
|
61
120
|
url?: string;
|
|
62
|
-
type?: string;
|
|
63
121
|
}
|
|
122
|
+
/** `Button` keyed by its own id — e.g. `'accept-all'`. The map key IS the id, rendered as the DOM
|
|
123
|
+
* `id` (`consenti-btn-{id}`) so integrators can target specific buttons, and is what
|
|
124
|
+
* `profileOverride`'s `deepMerge` deletes by (`{ 'reject-optional': null }` removes one button). */
|
|
125
|
+
type ButtonMap = Record<string, Button>;
|
|
64
126
|
interface MainBanner {
|
|
65
127
|
position: 'top' | 'bottom' | 'middle' | 'left-bottom' | 'right-bottom';
|
|
66
128
|
overlayOpacity?: number;
|
|
@@ -69,29 +131,45 @@ interface MainBanner {
|
|
|
69
131
|
heading?: string;
|
|
70
132
|
headingTag?: string;
|
|
71
133
|
htmlText: string;
|
|
72
|
-
buttons:
|
|
134
|
+
buttons: ButtonMap;
|
|
135
|
+
/** Stack buttons vertically below this viewport width in px. 0 or undefined = disabled. Default: 576. */
|
|
136
|
+
stackButtonsOnBreakpoint?: number;
|
|
137
|
+
/** Trap keyboard Tab focus within the consent root while this banner is visible. */
|
|
138
|
+
trapFocus?: boolean;
|
|
73
139
|
}
|
|
74
140
|
type GpcBanner = MainBanner;
|
|
141
|
+
/**
|
|
142
|
+
* A consent category — the single source of legal basis for every parameter
|
|
143
|
+
* it lists in `cookies`. Keyed by its own id inside `Record<string, Category>` —
|
|
144
|
+
* the map key IS the id, there is no `id` field on the value itself.
|
|
145
|
+
*
|
|
146
|
+
* A parameter must belong to exactly one category (enforced at authoring time,
|
|
147
|
+
* not by this type) — that relationship is what `getCookieLegalBasis`/
|
|
148
|
+
* `isMandatoryCookie` (`@consenti/utils`) resolve at runtime.
|
|
149
|
+
*/
|
|
75
150
|
interface Category {
|
|
76
|
-
id: string;
|
|
77
151
|
heading: string;
|
|
78
152
|
headingTag?: string;
|
|
79
153
|
htmlText: string;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
description?: string;
|
|
85
|
-
};
|
|
154
|
+
legalBasis: LegalBasis;
|
|
155
|
+
/** Only meaningful when legalBasis === 'legitimate_interest' — the GDPR balancing-test justification shown to visitors. */
|
|
156
|
+
legitimateInterestDescription?: string;
|
|
157
|
+
/** IDs of the parameters (from the profile's `cookies` map) that belong to this category. */
|
|
86
158
|
cookies: string[];
|
|
87
159
|
}
|
|
160
|
+
/** `Category` keyed by its own id — the standard shape used everywhere a category list is passed around. */
|
|
161
|
+
type CategoryMap = Record<string, Category>;
|
|
88
162
|
interface PreferenceModal extends Omit<MainBanner, "position"> {
|
|
89
163
|
position?: 'left' | 'right' | 'center';
|
|
90
164
|
subheading?: string;
|
|
91
|
-
categories:
|
|
165
|
+
categories: CategoryMap;
|
|
92
166
|
persistent?: boolean;
|
|
93
167
|
/** Screen width in px below which the modal expands to full screen. Default: 576. Set to 0 to disable. */
|
|
94
168
|
mobileFullScreenBreakpoint?: number;
|
|
169
|
+
/** Label for the optional consent-receipt download checkbox (shown when `allowReceipt` is true). Falls back to a default when omitted. */
|
|
170
|
+
receiptLabel?: string;
|
|
171
|
+
/** Description shown beneath the consent-receipt checkbox. Falls back to a default when omitted. */
|
|
172
|
+
receiptDescription?: string;
|
|
95
173
|
}
|
|
96
174
|
interface DpdpaConfig {
|
|
97
175
|
dataFiduciary: string;
|
|
@@ -99,21 +177,30 @@ interface DpdpaConfig {
|
|
|
99
177
|
purposeDescription?: string;
|
|
100
178
|
}
|
|
101
179
|
interface ResolvedProfile {
|
|
102
|
-
id:
|
|
103
|
-
|
|
104
|
-
version
|
|
180
|
+
id: string;
|
|
181
|
+
/** Incremented in place on every save of this profile. Absent for embedded/local profiles that have no server-side version counter. */
|
|
182
|
+
version?: number;
|
|
105
183
|
defaultLocale: string;
|
|
106
184
|
locales?: string[];
|
|
107
|
-
cookies:
|
|
185
|
+
cookies: CookieMap;
|
|
186
|
+
/** Days until consent expires and the visitor is asked again (profile-wide). Default: 365. */
|
|
187
|
+
expiryDays?: number;
|
|
188
|
+
allowReceipt?: boolean;
|
|
108
189
|
mainBanner: MainBanner;
|
|
109
190
|
gpcBanner?: GpcBanner;
|
|
110
191
|
preferenceModal: PreferenceModal;
|
|
111
192
|
cookieSigningKey?: string;
|
|
112
193
|
dpdpa?: DpdpaConfig;
|
|
113
194
|
darkMode?: boolean;
|
|
114
|
-
gpcMode?:
|
|
115
|
-
complianceGroup?:
|
|
195
|
+
gpcMode?: GpcMode;
|
|
196
|
+
complianceGroup?: ComplianceGroupId;
|
|
116
197
|
complianceConfig?: Record<string, string>;
|
|
198
|
+
/** Falls back to `true` (hidden) if neither the widget config nor the profile set it. */
|
|
199
|
+
hidePoweredBy?: boolean;
|
|
200
|
+
/** Show metadata footer strip (Consent ID, Date, Version, Privacy Settings link). */
|
|
201
|
+
showFooterMetadata?: boolean;
|
|
202
|
+
/** Apply WCAG 2.1 AA accessibility enhancements to the widget. */
|
|
203
|
+
enhanceAccessibility?: boolean;
|
|
117
204
|
}
|
|
118
205
|
type WidgetCountryResolverFn = () => Promise<{
|
|
119
206
|
country: string | null;
|
|
@@ -125,12 +212,20 @@ interface AgeGateWidgetConfig {
|
|
|
125
212
|
minimumAge: number;
|
|
126
213
|
requireParentalConsent?: boolean;
|
|
127
214
|
}
|
|
215
|
+
/** Mirrors the server-side `TcfConfig` shape (`@consenti/api`) — `cmpId`/`cmpVersion` must match
|
|
216
|
+
* what the backend uses to encode `tcfString`, since both feed the same simplified TC string. */
|
|
217
|
+
interface TcfWidgetConfig {
|
|
218
|
+
enabled: boolean;
|
|
219
|
+
cmpId: number;
|
|
220
|
+
cmpVersion: number;
|
|
221
|
+
}
|
|
128
222
|
interface ComplianceWidgetConfig {
|
|
129
|
-
type?:
|
|
223
|
+
type?: ComplianceType;
|
|
130
224
|
geoDataProvider?: 'default' | WidgetCountryResolverFn;
|
|
131
225
|
autoComplianceMap?: 'default' | ComplianceMapData | 'auto';
|
|
132
226
|
complianceMapUrl?: string;
|
|
133
227
|
ageGate?: AgeGateWidgetConfig;
|
|
228
|
+
tcf?: TcfWidgetConfig;
|
|
134
229
|
}
|
|
135
230
|
interface GtmConfig {
|
|
136
231
|
containerId?: string;
|
|
@@ -138,6 +233,7 @@ interface GtmConfig {
|
|
|
138
233
|
dataLayer?: string;
|
|
139
234
|
urlPassthrough?: boolean;
|
|
140
235
|
adsDataRedaction?: boolean;
|
|
236
|
+
verbose?: boolean;
|
|
141
237
|
}
|
|
142
238
|
interface UtilsConfig {
|
|
143
239
|
gtm?: GtmConfig;
|
|
@@ -147,7 +243,7 @@ interface ApiConfig {
|
|
|
147
243
|
baseUrl?: string;
|
|
148
244
|
authToken?: string;
|
|
149
245
|
tenantId?: string;
|
|
150
|
-
complianceGroup?:
|
|
246
|
+
complianceGroup?: ComplianceGroupId;
|
|
151
247
|
trustDomain?: boolean;
|
|
152
248
|
}
|
|
153
249
|
interface ThemeConfig {
|
|
@@ -171,8 +267,10 @@ interface ThemeConfig {
|
|
|
171
267
|
}
|
|
172
268
|
interface CoreConfig {
|
|
173
269
|
tenantId?: string;
|
|
174
|
-
autoHonorGPC?: boolean | 'strict';
|
|
175
270
|
locale?: string;
|
|
271
|
+
/** Text direction for the banner/modal root. `'auto'` (default) derives it from `locale`
|
|
272
|
+
* (Arabic, Hebrew, Persian, Urdu, ... → `rtl`); set explicitly to override. */
|
|
273
|
+
dir?: 'ltr' | 'rtl' | 'auto';
|
|
176
274
|
disableCssTemplate?: boolean;
|
|
177
275
|
cookieSigningKey?: string;
|
|
178
276
|
allowReceipt?: boolean;
|
|
@@ -185,7 +283,11 @@ interface CoreConfig {
|
|
|
185
283
|
console?: Array<'info' | 'log' | 'warning' | 'error'>;
|
|
186
284
|
}
|
|
187
285
|
interface ConsentiConfig {
|
|
188
|
-
|
|
286
|
+
verbose?: boolean;
|
|
287
|
+
/** Optional — every field of `CoreConfig` is itself optional, so `new ConsentiSetup({})` and
|
|
288
|
+
* `new ConsentiSetup({ compliance: { type: 'opt-in' } })` (omitting `core` entirely) both work,
|
|
289
|
+
* matching the widget's own "Minimal config" documentation. */
|
|
290
|
+
core?: CoreConfig;
|
|
189
291
|
compliance?: ComplianceWidgetConfig;
|
|
190
292
|
rootEl?: string | HTMLElement;
|
|
191
293
|
darkMode?: boolean;
|
|
@@ -197,10 +299,12 @@ interface ConsentiConfig {
|
|
|
197
299
|
/** When true, suppresses the "Powered by Consenti" footer link in the banner and modal. */
|
|
198
300
|
hidePoweredBy?: boolean;
|
|
199
301
|
}
|
|
200
|
-
type ConsentiEventName = 'bannerInitialized' | 'consenti:bannerInitialized' | 'bannerVisibility' | 'consenti:bannerVisibility' | 'modalVisibility' | 'consenti:modalVisibility' | 'consentBeingSubmitted' | 'consenti:consentBeingSubmitted' | 'consentSubmitted' | 'consenti:consentSubmitted';
|
|
302
|
+
type ConsentiEventName = 'bannerInitialized' | 'consenti:bannerInitialized' | 'bannerVisibility' | 'consenti:bannerVisibility' | 'modalVisibility' | 'consenti:modalVisibility' | 'consentBeingSubmitted' | 'consenti:consentBeingSubmitted' | 'consentSubmitted' | 'consenti:consentSubmitted' | 'parentalConsentRequired' | 'consenti:parentalConsentRequired';
|
|
201
303
|
interface ConsentiWidgetAPI {
|
|
202
304
|
hasConsent(): boolean;
|
|
203
305
|
getConsent(): ConsentValue | null;
|
|
306
|
+
getConsent(type: ConsentType): Record<string, string> | null;
|
|
307
|
+
getConsent(type?: ConsentType): ConsentValue | Record<string, string> | null;
|
|
204
308
|
getConsentDate(): Date | false;
|
|
205
309
|
getGTMConsent(): Record<string, string> | null;
|
|
206
310
|
isCookieGranted(cookieId: string, requestValue?: boolean): boolean | ConsentStatus;
|
|
@@ -213,8 +317,8 @@ interface ConsentiWidgetAPI {
|
|
|
213
317
|
off(event: ConsentiEventName, handler: (data: ConsentEvent) => void): void;
|
|
214
318
|
version(): {
|
|
215
319
|
package: string;
|
|
216
|
-
profileVersion:
|
|
217
|
-
consentVersion:
|
|
320
|
+
profileVersion: string | null;
|
|
321
|
+
consentVersion: string | null;
|
|
218
322
|
};
|
|
219
323
|
bannerVisibility(): 'main' | 'gpc' | false;
|
|
220
324
|
modalVisibility(): 'preference' | false;
|
|
@@ -223,7 +327,7 @@ interface ConsentiWidgetAPI {
|
|
|
223
327
|
hideBanner(): void;
|
|
224
328
|
showModal(triggerEl?: HTMLElement): void;
|
|
225
329
|
hideModal(): void;
|
|
226
|
-
submitConsent(consent: Partial<ConsentValue>): Promise<void>;
|
|
330
|
+
submitConsent(consent: Partial<ConsentValue>): Promise<ConsentDbRecord | void>;
|
|
227
331
|
deleteConsent(): Promise<void>;
|
|
228
332
|
reConsent(): Promise<void>;
|
|
229
333
|
getRootElement(): HTMLElement | null;
|
|
@@ -247,19 +351,47 @@ declare abstract class ConsentiPlugin {
|
|
|
247
351
|
onModalShow?(): void | Promise<void>;
|
|
248
352
|
onModalHide?(): void | Promise<void>;
|
|
249
353
|
}
|
|
250
|
-
interface
|
|
251
|
-
profileId
|
|
252
|
-
|
|
253
|
-
hasExistingConsent
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
354
|
+
interface BannerInitializedDetail {
|
|
355
|
+
profileId: string;
|
|
356
|
+
complianceGroup?: ComplianceType;
|
|
357
|
+
hasExistingConsent: boolean;
|
|
358
|
+
gpcDetected: boolean;
|
|
359
|
+
willShow: boolean;
|
|
360
|
+
}
|
|
361
|
+
interface BannerVisibilityDetail {
|
|
362
|
+
visible: boolean;
|
|
363
|
+
variant: 'main' | 'gpc';
|
|
364
|
+
action: boolean;
|
|
365
|
+
}
|
|
366
|
+
interface ModalVisibilityDetail {
|
|
367
|
+
visible: boolean;
|
|
368
|
+
action: boolean;
|
|
369
|
+
}
|
|
370
|
+
interface ConsentBeingSubmitted {
|
|
371
|
+
consentId: string;
|
|
372
|
+
visitorId: string;
|
|
373
|
+
profileId: string;
|
|
374
|
+
consentJson: ConsentValue;
|
|
375
|
+
consentAction: ConsentAction;
|
|
376
|
+
gpcDetected: boolean;
|
|
377
|
+
pageUrl: string;
|
|
378
|
+
timestamp: number;
|
|
260
379
|
fromBroadcast?: boolean;
|
|
261
|
-
gpcDetected?: boolean;
|
|
262
380
|
}
|
|
381
|
+
interface ConsentSubmittedDetail extends ConsentBeingSubmitted {
|
|
382
|
+
apiResponse: ConsentDbRecord;
|
|
383
|
+
}
|
|
384
|
+
/** Dispatched when the age gate is declined and `requireParentalConsent` is set — see
|
|
385
|
+
* `AgeGateWidgetConfig`. A deny-all consent (mandatory cookies only) has already been
|
|
386
|
+
* submitted with `parentalConsentToken` attached; this event is the hook for the site
|
|
387
|
+
* owner to run their own out-of-band parental-consent verification. */
|
|
388
|
+
interface ParentalConsentRequiredDetail {
|
|
389
|
+
parentalConsentToken: string;
|
|
390
|
+
profileId: string;
|
|
391
|
+
visitorId: string;
|
|
392
|
+
timestamp: number;
|
|
393
|
+
}
|
|
394
|
+
type ConsentEvent = BannerInitializedDetail | BannerVisibilityDetail | ModalVisibilityDetail | ConsentBeingSubmitted | ConsentSubmittedDetail | ParentalConsentRequiredDetail;
|
|
263
395
|
|
|
264
396
|
/**
|
|
265
397
|
* Main entry point for the Consenti widget.
|
|
@@ -279,16 +411,30 @@ interface ConsentEvent {
|
|
|
279
411
|
*/
|
|
280
412
|
|
|
281
413
|
declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
282
|
-
private config;
|
|
283
414
|
private initialized;
|
|
284
415
|
private initializing;
|
|
285
416
|
private profile;
|
|
286
417
|
private resolvedBase;
|
|
418
|
+
/** cookieId → Category lookup, rebuilt whenever `profile` changes. Avoids rescanning categories on every consent action. */
|
|
419
|
+
private categoryIndex;
|
|
420
|
+
/** Watchers created by the declarative `data-consenti-*` DOM scan — destroyed alongside the widget since they have no other owner. */
|
|
421
|
+
private scannedScripts;
|
|
287
422
|
private consentStore;
|
|
423
|
+
/**
|
|
424
|
+
* Stable per-browser identifier. Created lazily the first time a consent decision actually
|
|
425
|
+
* happens (manual submit, GPC auto-response, or relaying a decision already made in another
|
|
426
|
+
* tab) — never minted before that, so an undecided visitor leaves nothing in storage. See
|
|
427
|
+
* `getOrCreateVisitorId()`.
|
|
428
|
+
*/
|
|
429
|
+
private visitorId;
|
|
288
430
|
private eventBus;
|
|
289
431
|
private banner;
|
|
290
432
|
private modal;
|
|
433
|
+
private ageGate;
|
|
291
434
|
private renderer;
|
|
435
|
+
/** Set once the age gate (if enabled) resolves; threaded onto every `submitConsent` call thereafter. */
|
|
436
|
+
private _ageVerified;
|
|
437
|
+
private _parentalConsentToken;
|
|
292
438
|
private broadcastChannel;
|
|
293
439
|
private readyResolve;
|
|
294
440
|
private readyPromise;
|
|
@@ -297,15 +443,23 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
|
297
443
|
private lastSignature;
|
|
298
444
|
/** Tracks which banner variant is currently visible, or null when hidden. */
|
|
299
445
|
private _bannerVariant;
|
|
446
|
+
/** Tracks which banner variant is currently mounted in the DOM (persists when banner is hidden). */
|
|
447
|
+
private _bannerMounted;
|
|
448
|
+
/** Active focus trap keydown listener — removed when modal closes. */
|
|
449
|
+
private _focusTrapListener;
|
|
300
450
|
/** Maps original handler functions to their DOM-wrapped listeners for on/off bookkeeping. */
|
|
301
451
|
private _eventHandlers;
|
|
452
|
+
/** `core` defaulted to `{}` in the constructor below — every one of its own fields is
|
|
453
|
+
* optional, so this narrows back to non-optional for every other method in this class. */
|
|
454
|
+
private config;
|
|
302
455
|
/**
|
|
303
456
|
* Creates and initialises the consent widget.
|
|
304
457
|
*
|
|
305
458
|
* Construction is synchronous; the async `init()` is launched in the background.
|
|
306
459
|
* Use `await widget.ready` or `widget.onReady(cb)` to run code after init completes.
|
|
307
460
|
*
|
|
308
|
-
* @param config - Widget configuration. See `ConsentiConfig` for all options.
|
|
461
|
+
* @param config - Widget configuration. See `ConsentiConfig` for all options. `core` may be
|
|
462
|
+
* omitted entirely — every one of its own fields is optional too.
|
|
309
463
|
*/
|
|
310
464
|
constructor(config: ConsentiConfig);
|
|
311
465
|
private profileId;
|
|
@@ -315,8 +469,53 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
|
315
469
|
* to re-initialise the same instance.
|
|
316
470
|
*/
|
|
317
471
|
init(): Promise<void>;
|
|
472
|
+
/**
|
|
473
|
+
* Everything after the "does consent already exist" check — GPC/CCPA/CPRA silent paths,
|
|
474
|
+
* then the normal opt-in banner. Extracted from `init()` so the age gate (when enabled and
|
|
475
|
+
* no consent exists yet) can defer this whole sequence until the visitor answers it via
|
|
476
|
+
* `resolveAgeGate()`, then resume here with `hasExisting` fixed at `false`.
|
|
477
|
+
*/
|
|
478
|
+
private continueInit;
|
|
479
|
+
/** Mounts the age-gate prompt (`compliance.ageGate`) in place of the normal banner flow. */
|
|
480
|
+
private showAgeGate;
|
|
481
|
+
/**
|
|
482
|
+
* Handles the visitor's age-gate answer.
|
|
483
|
+
* - Confirmed → records `ageVerified: true` for all future `submitConsent` calls and
|
|
484
|
+
* resumes the normal init flow via `continueInit()`.
|
|
485
|
+
* - Declined, no parental consent required → immediately submits a deny-all consent
|
|
486
|
+
* (mandatory cookies still granted), mirroring the GPC-strict silent-write pattern.
|
|
487
|
+
* - Declined, parental consent required → same deny-all submission, plus a
|
|
488
|
+
* `parentalConsentToken` and a `consenti:parentalConsentRequired` event so the site
|
|
489
|
+
* owner can wire their own out-of-band verification flow (no email infra exists in
|
|
490
|
+
* this package to do that here).
|
|
491
|
+
*/
|
|
492
|
+
private resolveAgeGate;
|
|
493
|
+
/**
|
|
494
|
+
* Marks the widget ready and runs the declarative DOM-scan for
|
|
495
|
+
* `data-consenti-consent-script`/`data-consenti-category-script` tags. Called from
|
|
496
|
+
* every exit path of `init()` so the scan always runs exactly once per init cycle,
|
|
497
|
+
* whether `init()` was triggered automatically (`autoInit !== false`) or explicitly
|
|
498
|
+
* by the caller (`autoInit: false`) — it never runs before the widget actually has
|
|
499
|
+
* a resolved profile to read consent from.
|
|
500
|
+
*/
|
|
501
|
+
private finishInit;
|
|
502
|
+
/** Sets the `dir` attribute on the root element from `core.dir` (default `'auto'`, derived from `core.locale`). */
|
|
503
|
+
private applyDirection;
|
|
318
504
|
/** Applies the `consenti-root--dark` class based on config or profile flag. */
|
|
319
505
|
private applyDarkMode;
|
|
506
|
+
/** Applies `consenti--enhanced-a11y` class when `profile.enhanceAccessibility` is set. */
|
|
507
|
+
private applyAccessibility;
|
|
508
|
+
/** Builds and appends the metadata footer strip to a banner or modal container. */
|
|
509
|
+
private _appendMetaFooter;
|
|
510
|
+
/**
|
|
511
|
+
* Injects a `<style>` element into the root for `stackButtonsOnBreakpoint`.
|
|
512
|
+
* Called once per profile load; a new style replaces the previous one.
|
|
513
|
+
*/
|
|
514
|
+
private applyStackButtonsBreakpoint;
|
|
515
|
+
/** Installs a keyboard focus trap on `container` while the modal is open. */
|
|
516
|
+
private _installFocusTrap;
|
|
517
|
+
/** Removes the active focus trap keyboard listener. */
|
|
518
|
+
private _removeFocusTrap;
|
|
320
519
|
/**
|
|
321
520
|
* Injects CSS custom property overrides supplied via `core.theme` into the
|
|
322
521
|
* Consenti root element so they cascade down to all widget elements.
|
|
@@ -346,22 +545,23 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
|
346
545
|
*/
|
|
347
546
|
hasConsent(): boolean;
|
|
348
547
|
/**
|
|
349
|
-
* Returns the stored consent map,
|
|
350
|
-
*
|
|
548
|
+
* Returns the stored consent map, optionally transformed for a specific vendor.
|
|
549
|
+
* Pass `type` to get a vendor-ready object instead of the raw Consenti format.
|
|
550
|
+
* Always returns `null` during SSR or when no consent has been saved.
|
|
551
|
+
*
|
|
552
|
+
* @param type - Vendor format: 'google-gtm', 'category', 'adobe', 'meta',
|
|
553
|
+
* 'microsoft-clarity', 'twilio-segment', or omit for raw format.
|
|
351
554
|
*/
|
|
352
555
|
getConsent(): ConsentValue | null;
|
|
556
|
+
getConsent(type: ConsentType): Record<string, string> | null;
|
|
353
557
|
/**
|
|
354
558
|
* Returns the `Date` when consent was last saved, or `false` if no consent exists.
|
|
355
559
|
* Always returns `false` during SSR.
|
|
356
560
|
*/
|
|
357
561
|
getConsentDate(): Date | false;
|
|
358
562
|
/**
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
* Maps Consenti cookie IDs to the seven Google Consent Mode v2 parameters.
|
|
363
|
-
* Cookies whose IDs exactly match a GCM v2 key are forwarded directly.
|
|
364
|
-
* Non-GCM cookies are ignored by this method (they appear in `getConsent()` only).
|
|
563
|
+
* @deprecated Use `getConsent('google-gtm')` instead.
|
|
564
|
+
* Returns the consent map in Google Consent Mode v2 format.
|
|
365
565
|
*/
|
|
366
566
|
getGTMConsent(): Record<string, string> | null;
|
|
367
567
|
/**
|
|
@@ -373,7 +573,7 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
|
373
573
|
* @param consent - Partial or full map of cookie ID → status.
|
|
374
574
|
* @param gpcDerived - Internal flag set to `true` when called from the GPC strict path.
|
|
375
575
|
*/
|
|
376
|
-
submitConsent(consent: Partial<ConsentValue>, gpcDerived?: boolean): Promise<void>;
|
|
576
|
+
submitConsent(consent: Partial<ConsentValue>, gpcDerived?: boolean, source?: number): Promise<ConsentDbRecord | void>;
|
|
377
577
|
/**
|
|
378
578
|
* Returns the currently visible banner variant, or `false` if no banner is shown.
|
|
379
579
|
* Useful for programmatically checking state before calling `showBanner` / `hideBanner`.
|
|
@@ -542,8 +742,8 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
|
542
742
|
*/
|
|
543
743
|
version(): {
|
|
544
744
|
package: string;
|
|
545
|
-
profileVersion:
|
|
546
|
-
consentVersion:
|
|
745
|
+
profileVersion: string;
|
|
746
|
+
consentVersion: string | null;
|
|
547
747
|
};
|
|
548
748
|
/**
|
|
549
749
|
* Toggles or sets dark mode at runtime without re-initialising the widget.
|
|
@@ -577,45 +777,6 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
|
577
777
|
destroy(): void;
|
|
578
778
|
}
|
|
579
779
|
|
|
580
|
-
/**
|
|
581
|
-
* React integration for `@consenti/ui`.
|
|
582
|
-
*
|
|
583
|
-
* Import from the `@consenti/ui/react` subpath to keep this module
|
|
584
|
-
* tree-shaken out of vanilla JS or Vue bundles.
|
|
585
|
-
*
|
|
586
|
-
* Intended for **Next.js** (App Router and Pages Router) and any other React 18+
|
|
587
|
-
* project where you manage the `ConsentiSetup` instance yourself and want the
|
|
588
|
-
* `useConsent()` hook to re-render when consent or UI state changes.
|
|
589
|
-
*
|
|
590
|
-
* @example
|
|
591
|
-
* ```tsx
|
|
592
|
-
* // app/providers.tsx (Next.js App Router)
|
|
593
|
-
* 'use client'
|
|
594
|
-
* import { useEffect } from 'react'
|
|
595
|
-
* import { ConsentiSetup } from '@consenti/ui'
|
|
596
|
-
* import { setConsentiWidget } from '@consenti/ui/react'
|
|
597
|
-
*
|
|
598
|
-
* export function ConsentiProvider({ children }: { children: React.ReactNode }) {
|
|
599
|
-
* useEffect(() => {
|
|
600
|
-
* const widget = new ConsentiSetup({
|
|
601
|
-
* api: { enabled: true, baseUrl: process.env.NEXT_PUBLIC_API_URL },
|
|
602
|
-
* })
|
|
603
|
-
* setConsentiWidget(widget)
|
|
604
|
-
* }, [])
|
|
605
|
-
* return <>{children}</>
|
|
606
|
-
* }
|
|
607
|
-
*
|
|
608
|
-
* // Any component
|
|
609
|
-
* import { useConsent } from '@consenti/ui/react'
|
|
610
|
-
*
|
|
611
|
-
* function CookieStatus() {
|
|
612
|
-
* const { hasConsent, consent, showModal } = useConsent()
|
|
613
|
-
* if (!hasConsent) return <button onClick={showModal}>Manage Cookies</button>
|
|
614
|
-
* return <span>{consent?.analytics === 'granted' ? 'Analytics on' : 'Analytics off'}</span>
|
|
615
|
-
* }
|
|
616
|
-
* ```
|
|
617
|
-
*/
|
|
618
|
-
|
|
619
780
|
/**
|
|
620
781
|
* Registers the active `ConsentiSetup` instance so `useConsent()` can read from it.
|
|
621
782
|
* Call this once after creating your widget, typically inside a `useEffect`.
|
|
@@ -635,22 +796,38 @@ declare function setConsentiWidget(widget: ConsentiSetup): void;
|
|
|
635
796
|
* @returns Consent state values and widget action functions.
|
|
636
797
|
*/
|
|
637
798
|
declare function useConsent(): {
|
|
799
|
+
/** `true` if the visitor has saved a valid consent record. */
|
|
638
800
|
hasConsent: boolean;
|
|
801
|
+
/** The stored consent map (`Record<cookieId, status>`), or `null` before first submission. */
|
|
639
802
|
consent: ConsentValue | null;
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
803
|
+
/** `Date` of the last consent submission, or `false` if no consent exists. */
|
|
804
|
+
consentDate: false | Date;
|
|
805
|
+
/** Current banner state: `'main'`, `'gpc'`, or `false` (hidden). */
|
|
806
|
+
bannerVisible: false | "main" | "gpc";
|
|
807
|
+
/** Current modal state: `'preference'` or `false` (hidden). */
|
|
808
|
+
modalVisible: false | "preference";
|
|
809
|
+
/** Shows the consent banner. Pass `true` to force the GPC variant. */
|
|
810
|
+
showBanner: (gpc?: boolean) => void | undefined;
|
|
811
|
+
/** Hides the consent banner. */
|
|
812
|
+
hideBanner: () => void | undefined;
|
|
813
|
+
/** Opens the preference modal. */
|
|
814
|
+
showModal: () => void | undefined;
|
|
815
|
+
/** Closes the preference modal. */
|
|
816
|
+
hideModal: () => void | undefined;
|
|
817
|
+
/** Grants all non-mandatory cookies. Pass `true` to grant only mandatory ones and deny the rest. */
|
|
818
|
+
grantAll: (onlyMandatory?: boolean) => Promise<void>;
|
|
819
|
+
/** Denies all non-mandatory cookies. Pass `true` to deny mandatory ones too (use with care). */
|
|
820
|
+
denyAll: (includingMandatory?: boolean) => Promise<void>;
|
|
821
|
+
/** Saves the given partial consent map. */
|
|
822
|
+
submitConsent: (c: Partial<ConsentValue>) => Promise<void | ConsentDbRecord>;
|
|
823
|
+
/** Deletes the current consent record and re-shows the banner. */
|
|
650
824
|
reConsent: () => Promise<void>;
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
825
|
+
/** Returns `true` if the given cookie ID is `'granted'`. */
|
|
826
|
+
isCookieGranted: (id: string) => boolean;
|
|
827
|
+
/** Returns `true` if every cookie in the given category is `'granted'`. */
|
|
828
|
+
isCategoryGranted: (id: string) => boolean;
|
|
829
|
+
/** Switches the active locale and re-renders the banner and modal. */
|
|
830
|
+
switchLocale: (locale: string) => void | undefined;
|
|
654
831
|
};
|
|
655
832
|
|
|
656
833
|
export { setConsentiWidget, useConsent };
|