@consenti/ui 0.2.0 → 0.4.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 +512 -138
- package/dist/angular.d.cts +610 -151
- package/dist/angular.d.ts +610 -151
- package/dist/angular.js +1 -1
- package/dist/angular.js.map +1 -1
- package/dist/angular.mjs +1 -1
- package/dist/angular.mjs.map +1 -1
- package/dist/chunk-3EXCD24H.mjs +2 -0
- package/dist/{chunk-K2UN3O67.mjs.map → chunk-3EXCD24H.mjs.map} +1 -1
- package/dist/chunk-RDE7CM6T.mjs +2 -0
- package/dist/chunk-RDE7CM6T.mjs.map +1 -0
- package/dist/http-AQVVUCX6.mjs +2 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +971 -125
- 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-5ZLWWN2L.mjs +2 -0
- package/dist/profiles-5ZLWWN2L.mjs.map +1 -0
- package/dist/react.d.cts +589 -129
- package/dist/react.d.ts +589 -129
- package/dist/react.js +1 -1
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +1 -1
- package/dist/react.mjs.map +1 -1
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/dist/testing.mjs.map +1 -1
- package/dist/vue.d.cts +562 -79
- package/dist/vue.d.ts +562 -79
- package/dist/vue.js +1 -1
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs +1 -1
- package/dist/vue.mjs.map +1 -1
- package/package.json +13 -8
- 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
package/dist/angular.d.ts
CHANGED
|
@@ -1,9 +1,44 @@
|
|
|
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
|
|
|
14
|
+
/**
|
|
15
|
+
* Single source of truth for all compliance groups, regulations, and country mappings.
|
|
16
|
+
*
|
|
17
|
+
* This is DATA, not types. The `Compliance` and `ComplianceGroupId` literal types
|
|
18
|
+
* derived from the ID arrays below live in @consenti/types (packages/types/src/compliance.ts),
|
|
19
|
+
* via a type-only import — that keeps @consenti/types free of runtime code.
|
|
20
|
+
*
|
|
21
|
+
* ─── HOW TO UPDATE ────────────────────────────────────────────────────────────
|
|
22
|
+
*
|
|
23
|
+
* ADD A NEW REGULATION:
|
|
24
|
+
* 1. Add regulation ID string to `COMPLIANCE_REGULATION_IDS` below
|
|
25
|
+
* (the `Compliance` type in @consenti/types auto-updates from it).
|
|
26
|
+
* 2. Add it to the appropriate group's `compliances` array below.
|
|
27
|
+
* 3. Add / update the country entry in `EMBEDDED_COMPLIANCE_MAP` below.
|
|
28
|
+
* That is all — no other files need touching.
|
|
29
|
+
*
|
|
30
|
+
* ADD A NEW COMPLIANCE GROUP:
|
|
31
|
+
* 1. Add the kebab-case ID to `COMPLIANCE_GROUP_IDS` below.
|
|
32
|
+
* 2. Add a full group entry to `COMPLIANCE_GROUPS` below (label, compliances, flags).
|
|
33
|
+
* 3. Add a validation rule set to `COMPLIANCE_VALIDATION_RULES` below.
|
|
34
|
+
* 4. Map affected countries in `EMBEDDED_COMPLIANCE_MAP` to the new group.
|
|
35
|
+
*
|
|
36
|
+
* ADD OR UPDATE A COUNTRY:
|
|
37
|
+
* Edit the entry in `EMBEDDED_COMPLIANCE_MAP.countries` below. That is the ONLY
|
|
38
|
+
* place that needs changing for country-level jurisdiction updates.
|
|
39
|
+
*/
|
|
6
40
|
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"];
|
|
41
|
+
declare const COOKIE_PURPOSE_IDS: readonly ["necessary", "functional", "preferences", "analytics", "marketing"];
|
|
7
42
|
|
|
8
43
|
/**
|
|
9
44
|
* Compliance-related literal types, derived from the regulation/group ID data
|
|
@@ -19,6 +54,13 @@ declare const COMPLIANCE_GROUP_IDS: readonly ["opt-in", "opt-out", "opt-out-stri
|
|
|
19
54
|
*/
|
|
20
55
|
|
|
21
56
|
type ComplianceGroupId = typeof COMPLIANCE_GROUP_IDS[number];
|
|
57
|
+
/**
|
|
58
|
+
* `(string & {})` (rather than plain `string`) keeps IDE autocomplete for the
|
|
59
|
+
* known `ComplianceGroupId` literals while still allowing any other string —
|
|
60
|
+
* used to target a profile authored against a custom (non-built-in)
|
|
61
|
+
* `customComplianceGroup` instead of one of the 8 built-in groups.
|
|
62
|
+
*/
|
|
63
|
+
type ComplianceType = 'auto' | ComplianceGroupId | (string & {}) | Symbol;
|
|
22
64
|
|
|
23
65
|
interface ComplianceMapData {
|
|
24
66
|
version: string;
|
|
@@ -26,31 +68,81 @@ interface ComplianceMapData {
|
|
|
26
68
|
}
|
|
27
69
|
interface CountryComplianceEntry {
|
|
28
70
|
complianceGroup: ComplianceGroupId;
|
|
29
|
-
|
|
30
|
-
|
|
71
|
+
/** Fallback group used when only the country (not the region) is known. Defaults to
|
|
72
|
+
* `complianceGroup` when omitted — only set this explicitly when a region within this
|
|
73
|
+
* country can be stricter than the country-wide baseline (see `overriddenRegions`). */
|
|
74
|
+
default?: ComplianceGroupId;
|
|
75
|
+
description?: string;
|
|
31
76
|
overriddenRegions?: Record<string, {
|
|
32
77
|
complianceGroup: ComplianceGroupId;
|
|
33
|
-
description
|
|
78
|
+
description?: string;
|
|
34
79
|
}>;
|
|
35
80
|
}
|
|
81
|
+
interface ConsentDbRecord {
|
|
82
|
+
id: string;
|
|
83
|
+
tenantId: string;
|
|
84
|
+
visitorId: string;
|
|
85
|
+
profileId: string;
|
|
86
|
+
locale: string;
|
|
87
|
+
consentJson: ConsentValue;
|
|
88
|
+
gpcDetected: boolean;
|
|
89
|
+
source: 'banner' | 'api' | 'import';
|
|
90
|
+
ageVerified?: boolean;
|
|
91
|
+
parentalConsentToken?: string;
|
|
92
|
+
tcfString?: string;
|
|
93
|
+
gppString?: string;
|
|
94
|
+
/** HMAC-SHA256 signature over the record's core fields, hex-encoded — tamper-evidence for
|
|
95
|
+
* server-stored records via `dataSigningHash` (auto-generated when unset), independent of the
|
|
96
|
+
* widget's own cookie-signing (`core.cookieSigningKey`). */
|
|
97
|
+
signature?: string;
|
|
98
|
+
createdAt: string;
|
|
99
|
+
updatedAt: string;
|
|
100
|
+
}
|
|
36
101
|
|
|
37
102
|
type ConsentStatus = 'granted' | 'denied' | 'objected';
|
|
38
103
|
type ConsentAction = 'accept_all' | 'reject_all' | 'custom' | 'update';
|
|
39
104
|
type ConsentValue = Record<string, ConsentStatus>;
|
|
105
|
+
/**
|
|
106
|
+
* Output format for getConsent(type).
|
|
107
|
+
* 'default' → raw ConsentValue, keyed by every cookie parameter ID in the profile (existing behaviour)
|
|
108
|
+
* 'google-gtm' → Google Consent Mode v2 keys + ads_data_redaction, url_passthrough
|
|
109
|
+
* 'purpose' → consent per the fixed CookiePurpose taxonomy (necessary/functional/preferences/analytics/marketing)
|
|
110
|
+
* 'category' → consent per the tenant's own authored preference-modal category, keyed by category ID.
|
|
111
|
+
* 'granted' only when every member parameter is granted; 'denied'/'objected' otherwise
|
|
112
|
+
* (same ConsentStatus values as everything else — see getCategoryConsent for the exact rule)
|
|
113
|
+
* 'adobe' → { analytics, target, manager, optimizer }
|
|
114
|
+
* 'meta' → { pixel, api, plugins, facebookLogin }
|
|
115
|
+
* 'microsoft-clarity' → { session, heatmaps, performance }
|
|
116
|
+
* 'twilio-segment' → { identify, page, track, group, alias }
|
|
117
|
+
*/
|
|
118
|
+
type ConsentType = 'default' | 'google-gtm' | 'purpose' | 'category' | 'adobe' | 'meta' | 'microsoft-clarity' | 'twilio-segment';
|
|
119
|
+
type GpcMode = 'ignore' | 'honor' | 'strict';
|
|
120
|
+
type LegalBasis = 'mandatory' | 'consent' | 'legitimate_interest';
|
|
121
|
+
type CookiePurpose = typeof COOKIE_PURPOSE_IDS[number];
|
|
122
|
+
/**
|
|
123
|
+
* A single consent parameter (tracker/cookie definition).
|
|
124
|
+
* Keyed by its own id inside `Record<string, Cookie>` — the map key IS the id,
|
|
125
|
+
* there is no `id` field on the value itself.
|
|
126
|
+
*/
|
|
40
127
|
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';
|
|
128
|
+
purpose?: CookiePurpose;
|
|
47
129
|
listenGpc?: boolean;
|
|
48
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Pre-grant this parameter's default consent to 'granted' (instead of the
|
|
132
|
+
* compliance-group-driven default) when no stored decision exists yet.
|
|
133
|
+
* Only meaningful when the owning category's `legalBasis === 'consent'` —
|
|
134
|
+
* `mandatory`/`legitimate_interest` categories are already effectively
|
|
135
|
+
* pre-granted regardless of this flag. Never overrides an active GPC signal.
|
|
136
|
+
* Default: false.
|
|
137
|
+
*/
|
|
138
|
+
preGrant?: boolean;
|
|
49
139
|
tcfVendorId?: number;
|
|
50
140
|
tcfPurposes?: number[];
|
|
51
141
|
tcfSpecialFeatures?: number[];
|
|
52
142
|
cpraCategory?: 'sale' | 'sharing' | 'sensitive';
|
|
53
143
|
}
|
|
144
|
+
/** `Cookie` keyed by its own id — the standard shape used everywhere a parameter list is passed around. */
|
|
145
|
+
type CookieMap = Record<string, Cookie>;
|
|
54
146
|
type ButtonStyle = 'primary' | 'secondary' | 'text' | 'accent';
|
|
55
147
|
type ButtonAction = 'submit' | 'manage' | 'close' | 'custom' | 'link';
|
|
56
148
|
interface Button {
|
|
@@ -59,8 +151,11 @@ interface Button {
|
|
|
59
151
|
action: ButtonAction;
|
|
60
152
|
cookies?: string[] | '*' | '!';
|
|
61
153
|
url?: string;
|
|
62
|
-
type?: string;
|
|
63
154
|
}
|
|
155
|
+
/** `Button` keyed by its own id — e.g. `'accept-all'`. The map key IS the id, rendered as the DOM
|
|
156
|
+
* `id` (`consenti-btn-{id}`) so integrators can target specific buttons, and is what
|
|
157
|
+
* `profileOverride`'s `deepMerge` deletes by (`{ 'reject-optional': null }` removes one button). */
|
|
158
|
+
type ButtonMap = Record<string, Button>;
|
|
64
159
|
interface MainBanner {
|
|
65
160
|
position: 'top' | 'bottom' | 'middle' | 'left-bottom' | 'right-bottom';
|
|
66
161
|
overlayOpacity?: number;
|
|
@@ -69,68 +164,187 @@ interface MainBanner {
|
|
|
69
164
|
heading?: string;
|
|
70
165
|
headingTag?: string;
|
|
71
166
|
htmlText: string;
|
|
72
|
-
buttons:
|
|
167
|
+
buttons: ButtonMap;
|
|
168
|
+
/** Stack buttons vertically below this viewport width in px. 0 or undefined = disabled. Default: 576. */
|
|
169
|
+
stackButtonsOnBreakpoint?: number;
|
|
170
|
+
/** Trap keyboard Tab focus within the consent root while this banner is visible. */
|
|
171
|
+
trapFocus?: boolean;
|
|
73
172
|
}
|
|
74
173
|
type GpcBanner = MainBanner;
|
|
174
|
+
/**
|
|
175
|
+
* A consent category — the single source of legal basis for every parameter
|
|
176
|
+
* it lists in `cookies`. Keyed by its own id inside `Record<string, Category>` —
|
|
177
|
+
* the map key IS the id, there is no `id` field on the value itself.
|
|
178
|
+
*
|
|
179
|
+
* A parameter must belong to exactly one category (enforced at authoring time,
|
|
180
|
+
* not by this type) — that relationship is what `getCookieLegalBasis`/
|
|
181
|
+
* `isMandatoryCookie` (`@consenti/utils`) resolve at runtime.
|
|
182
|
+
*/
|
|
75
183
|
interface Category {
|
|
76
|
-
id: string;
|
|
77
184
|
heading: string;
|
|
78
185
|
headingTag?: string;
|
|
79
186
|
htmlText: string;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
description?: string;
|
|
85
|
-
};
|
|
187
|
+
legalBasis: LegalBasis;
|
|
188
|
+
/** Only meaningful when legalBasis === 'legitimate_interest' — the GDPR balancing-test justification shown to visitors. */
|
|
189
|
+
legitimateInterestDescription?: string;
|
|
190
|
+
/** IDs of the parameters (from the profile's `cookies` map) that belong to this category. */
|
|
86
191
|
cookies: string[];
|
|
87
192
|
}
|
|
193
|
+
/** `Category` keyed by its own id — the standard shape used everywhere a category list is passed around. */
|
|
194
|
+
type CategoryMap = Record<string, Category>;
|
|
88
195
|
interface PreferenceModal extends Omit<MainBanner, "position"> {
|
|
89
196
|
position?: 'left' | 'right' | 'center';
|
|
90
197
|
subheading?: string;
|
|
91
|
-
categories:
|
|
198
|
+
categories: CategoryMap;
|
|
92
199
|
persistent?: boolean;
|
|
93
200
|
/** Screen width in px below which the modal expands to full screen. Default: 576. Set to 0 to disable. */
|
|
94
201
|
mobileFullScreenBreakpoint?: number;
|
|
202
|
+
/** Label for the optional consent-receipt download checkbox (shown when `allowReceipt` is true). Falls back to a default when omitted. */
|
|
203
|
+
receiptLabel?: string;
|
|
204
|
+
/** Description shown beneath the consent-receipt checkbox. Falls back to a default when omitted. */
|
|
205
|
+
receiptDescription?: string;
|
|
206
|
+
/**
|
|
207
|
+
* Renders a "Forget me" control in the modal body, below the categories — only shown to
|
|
208
|
+
* visitors who already have a stored consent decision (nothing to erase otherwise). Wired to
|
|
209
|
+
* the same `DELETE /consent/:visitorId` erasure endpoint as `widget.deleteConsent()`, giving
|
|
210
|
+
* visitors self-service access to GDPR Art. 17 / CCPA-CPRA / LGPD Art. 18 / UK GDPR / DPDPA /
|
|
211
|
+
* KVKK / POPIA / PDPA-TH / APPI erasure-equivalent rights without the host wiring a link
|
|
212
|
+
* themselves. See the "Right to erasure" guide for the full compliance mapping.
|
|
213
|
+
*/
|
|
214
|
+
showForgetMe?: boolean;
|
|
215
|
+
/** Label for the "Forget me" button (shown when `showForgetMe` is true). Falls back to a default when omitted. */
|
|
216
|
+
forgetMeLabel?: string;
|
|
95
217
|
}
|
|
96
218
|
interface DpdpaConfig {
|
|
97
219
|
dataFiduciary: string;
|
|
98
220
|
grievanceEmail: string;
|
|
99
221
|
purposeDescription?: string;
|
|
100
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Per-profile age gate — GDPR Art. 8's consent age varies 13–16 by EU member state and DPDPA has
|
|
225
|
+
* its own child-data age rules, so one global age (the old `ConsentiServerConfig.ageGate`) was
|
|
226
|
+
* wrong for a multi-region deployment. Mirrors `dpdpa` exactly: profile-scoped, not global.
|
|
227
|
+
*/
|
|
228
|
+
interface AgeGateConfig {
|
|
229
|
+
enabled: boolean;
|
|
230
|
+
minimumAge: number;
|
|
231
|
+
requireParentalConsent?: boolean;
|
|
232
|
+
}
|
|
233
|
+
/** Per-locale age-gate modal text — translated the same way `mainBanner`/`preferenceModal`
|
|
234
|
+
* content is, unlike the widget's own hardcoded UI chrome (close button, locale switcher). */
|
|
235
|
+
interface AgeGateModalContent {
|
|
236
|
+
heading?: string;
|
|
237
|
+
htmlText: string;
|
|
238
|
+
confirmButtonLabel: string;
|
|
239
|
+
denyButtonLabel: string;
|
|
240
|
+
parentalConsent: {
|
|
241
|
+
heading?: string;
|
|
242
|
+
htmlText: string;
|
|
243
|
+
confirmButtonLabel: string;
|
|
244
|
+
};
|
|
245
|
+
}
|
|
101
246
|
interface ResolvedProfile {
|
|
102
|
-
id:
|
|
103
|
-
|
|
104
|
-
version
|
|
247
|
+
id: string;
|
|
248
|
+
/** Incremented in place on every save of this profile. Absent for embedded/local profiles that have no server-side version counter. */
|
|
249
|
+
version?: number;
|
|
105
250
|
defaultLocale: string;
|
|
106
251
|
locales?: string[];
|
|
107
|
-
cookies:
|
|
252
|
+
cookies: CookieMap;
|
|
253
|
+
/** Days until consent expires and the visitor is asked again (profile-wide). Default: 365. */
|
|
254
|
+
expiryDays?: number;
|
|
255
|
+
allowReceipt?: boolean;
|
|
108
256
|
mainBanner: MainBanner;
|
|
109
257
|
gpcBanner?: GpcBanner;
|
|
110
258
|
preferenceModal: PreferenceModal;
|
|
111
259
|
cookieSigningKey?: string;
|
|
112
260
|
dpdpa?: DpdpaConfig;
|
|
261
|
+
ageGate?: AgeGateConfig;
|
|
262
|
+
ageGateModal?: AgeGateModalContent;
|
|
113
263
|
darkMode?: boolean;
|
|
114
|
-
gpcMode?:
|
|
115
|
-
|
|
264
|
+
gpcMode?: GpcMode;
|
|
265
|
+
/** One of the 8 built-in groups, or a custom group id (via `customComplianceGroup` /
|
|
266
|
+
* `ComplianceType`'s string passthrough) — this is the join key `complianceGroupsOverride`
|
|
267
|
+
* looks up by, so it must reflect whichever group this profile actually resolved against,
|
|
268
|
+
* built-in or custom. */
|
|
269
|
+
complianceGroup?: ComplianceGroupId | (string & {});
|
|
270
|
+
/** Set when the geo-resolved region carries a `requiresSensitiveOptIn` carve-out (e.g.
|
|
271
|
+
* Colorado under `opt-out`) — cookies tagged `cpraCategory: 'sensitive'` default to denied
|
|
272
|
+
* even though the rest of `complianceGroup`'s cookies default to granted. Only ever set by
|
|
273
|
+
* server-side geo resolution (`api.enabled: true` + a geoip/maxmind provider); the
|
|
274
|
+
* client-only timezone heuristic never resolves a region, so this is always unset there. */
|
|
275
|
+
requiresSensitiveOptIn?: boolean;
|
|
116
276
|
complianceConfig?: Record<string, string>;
|
|
277
|
+
/** Falls back to `true` (hidden) if neither the widget config nor the profile set it. */
|
|
278
|
+
hidePoweredBy?: boolean;
|
|
279
|
+
/** Show metadata footer strip (Consent ID, Date, Version, Privacy Settings link). */
|
|
280
|
+
showFooterMetadata?: boolean;
|
|
281
|
+
/** Apply WCAG 2.1 AA accessibility enhancements to the widget. */
|
|
282
|
+
enhanceAccessibility?: boolean;
|
|
117
283
|
}
|
|
118
284
|
type WidgetCountryResolverFn = () => Promise<{
|
|
119
285
|
country: string | null;
|
|
120
286
|
region: string | null;
|
|
121
287
|
confidence: number;
|
|
288
|
+
/** Optional escape hatch: when set, profile resolution uses this compliance group directly
|
|
289
|
+
* instead of computing one from `country`/`region` — mirrors the server-side `GeoResult`'s
|
|
290
|
+
* `complianceGroup` field (`@consenti/types`'s `api.ts`). `country`/`region` are still used for
|
|
291
|
+
* anything that reads them independently of group selection. Optional — omitting it preserves
|
|
292
|
+
* the existing timezone/language-based resolution exactly as before. */
|
|
293
|
+
complianceGroup?: string;
|
|
122
294
|
}>;
|
|
123
|
-
|
|
295
|
+
/** Mirrors the server-side `TcfConfig` shape (`@consenti/api`) — `cmpId`/`cmpVersion` must match
|
|
296
|
+
* what the backend uses to encode `tcfString`, since both feed the same simplified TC string.
|
|
297
|
+
*
|
|
298
|
+
* The client-side `__tcfapi` stub never fetches the Global Vendor List itself (multi-megabyte,
|
|
299
|
+
* and IAB policy requires CMPs to cache their own copy rather than have every visitor's browser
|
|
300
|
+
* fetch it) — `publisherCC`/`vendorListVersion`/`gvlVersion` are therefore operator-supplied
|
|
301
|
+
* config here, not auto-derived. Spec-correct binary TC-string encoding happens server-side in
|
|
302
|
+
* `@consenti/api` (see `compliance.tcf.publisherCC` there); this stub's `tcString` stays the
|
|
303
|
+
* simplified format documented in `@consenti/utils`'s `encodeTcString`. */
|
|
304
|
+
interface TcfWidgetConfig {
|
|
124
305
|
enabled: boolean;
|
|
125
|
-
|
|
126
|
-
|
|
306
|
+
cmpId: number;
|
|
307
|
+
cmpVersion: number;
|
|
308
|
+
/** ISO 3166-1 alpha-2 publisher country code. No honest default exists — leave unset only if
|
|
309
|
+
* you don't rely on this field downstream; ad-tech vendors that read it will see 'AA'
|
|
310
|
+
* (an explicit "not configured" placeholder, not a real country). */
|
|
311
|
+
publisherCC?: string;
|
|
312
|
+
/** Known Global Vendor List version, if you track it. Defaults to 0 ("unknown"). */
|
|
313
|
+
vendorListVersion?: number;
|
|
314
|
+
/** Known GVL specification version, if you track it. Defaults to 0 ("unknown"). */
|
|
315
|
+
gvlVersion?: number;
|
|
316
|
+
}
|
|
317
|
+
/** Mirrors the server-side `GppConfig` shape (`@consenti/api`) — `cmpId`/`cmpVersion` must match
|
|
318
|
+
* what the backend uses to encode `gppString`. Unlike TCF, the GPP US National section needs no
|
|
319
|
+
* Global Vendor List, so both the widget's `window.__gpp` and the server's `gppString` use the
|
|
320
|
+
* same real, spec-correct encoder (the optional `@iabgpp/cmpapi` peer dependency) — there's no
|
|
321
|
+
* "simplified fallback" format the way TCF has, since a non-spec GPP string has no consumer that
|
|
322
|
+
* would accept it. When `@iabgpp/cmpapi` isn't installed, `window.__gpp` is simply not exposed. */
|
|
323
|
+
interface GppWidgetConfig {
|
|
324
|
+
enabled: boolean;
|
|
325
|
+
cmpId: number;
|
|
326
|
+
cmpVersion: number;
|
|
327
|
+
/** MSPA "covered transaction" — whether this deployment's data transactions fall under MSPA
|
|
328
|
+
* signatory obligations. No honest default, so it's required rather than optional. */
|
|
329
|
+
mspaCoveredTransaction: boolean;
|
|
330
|
+
/** IAB's tri-state encoding for both MSPA fields: 0 = not applicable, 1 = yes, 2 = no. */
|
|
331
|
+
mspaOptOutOptionMode: 0 | 1 | 2;
|
|
332
|
+
mspaServiceProviderMode: 0 | 1 | 2;
|
|
127
333
|
}
|
|
128
334
|
interface ComplianceWidgetConfig {
|
|
129
|
-
type?:
|
|
335
|
+
type?: ComplianceType;
|
|
130
336
|
geoDataProvider?: 'default' | WidgetCountryResolverFn;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
337
|
+
/** Only meaningful in standalone mode (no `api.enabled`) — ignored (with a warning) when
|
|
338
|
+
* `api.enabled: true`, since the server resolves the compliance group in that mode.
|
|
339
|
+
* `'default'` (embedded map, the default), a URL to fetch a JSON `ComplianceMapData`
|
|
340
|
+
* document from (the browser's own HTTP cache honors whatever `Cache-Control`/`ETag` the
|
|
341
|
+
* response sends — no custom caching here), or an inline operator-supplied `ComplianceMapData`
|
|
342
|
+
* object. Invalid data from either a URL or an object logs a warning and falls back to
|
|
343
|
+
* `'default'`. Only overrides the country→group mapping — country/region detection itself
|
|
344
|
+
* always uses the embedded geo data. */
|
|
345
|
+
complianceMap?: 'default' | string | ComplianceMapData;
|
|
346
|
+
tcf?: TcfWidgetConfig;
|
|
347
|
+
gpp?: GppWidgetConfig;
|
|
134
348
|
}
|
|
135
349
|
interface GtmConfig {
|
|
136
350
|
containerId?: string;
|
|
@@ -138,6 +352,7 @@ interface GtmConfig {
|
|
|
138
352
|
dataLayer?: string;
|
|
139
353
|
urlPassthrough?: boolean;
|
|
140
354
|
adsDataRedaction?: boolean;
|
|
355
|
+
verbose?: boolean;
|
|
141
356
|
}
|
|
142
357
|
interface UtilsConfig {
|
|
143
358
|
gtm?: GtmConfig;
|
|
@@ -147,45 +362,81 @@ interface ApiConfig {
|
|
|
147
362
|
baseUrl?: string;
|
|
148
363
|
authToken?: string;
|
|
149
364
|
tenantId?: string;
|
|
150
|
-
complianceGroup?:
|
|
365
|
+
complianceGroup?: ComplianceGroupId;
|
|
151
366
|
trustDomain?: boolean;
|
|
152
367
|
}
|
|
368
|
+
/** Field names mirror their `--consenti-*` CSS variable literally (`--consenti-color-primary`
|
|
369
|
+
* → `colorPrimary`) — see `applyTheme()` in `consenti-setup.ts` for the full mapping. */
|
|
153
370
|
interface ThemeConfig {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
371
|
+
colorBg?: string;
|
|
372
|
+
colorText?: string;
|
|
373
|
+
colorTextMuted?: string;
|
|
374
|
+
colorPrimary?: string;
|
|
375
|
+
colorPrimaryText?: string;
|
|
376
|
+
colorSecondary?: string;
|
|
377
|
+
colorSecondaryText?: string;
|
|
378
|
+
colorBorder?: string;
|
|
379
|
+
colorSecondaryBorder?: string;
|
|
380
|
+
colorOverlay?: string;
|
|
381
|
+
colorAccent?: string;
|
|
382
|
+
colorAccentText?: string;
|
|
161
383
|
fontFamily?: string;
|
|
384
|
+
fontFamilyMono?: string;
|
|
162
385
|
fontSizeBase?: string;
|
|
163
386
|
fontSizeHeading?: string;
|
|
164
387
|
fontSizeMultiplier?: string;
|
|
388
|
+
fontWeightHeading?: string;
|
|
389
|
+
lineHeight?: string;
|
|
390
|
+
spacingXs?: string;
|
|
391
|
+
spacingSm?: string;
|
|
392
|
+
spacingMd?: string;
|
|
393
|
+
spacingLg?: string;
|
|
165
394
|
borderRadius?: string;
|
|
166
|
-
|
|
395
|
+
borderRadiusBtn?: string;
|
|
396
|
+
shadow?: string;
|
|
167
397
|
toggleBgOn?: string;
|
|
398
|
+
toggleBgPartial?: string;
|
|
168
399
|
toggleBgOff?: string;
|
|
169
|
-
|
|
170
|
-
|
|
400
|
+
toggleKnob?: string;
|
|
401
|
+
toggleWidth?: string;
|
|
402
|
+
toggleHeight?: string;
|
|
403
|
+
zBanner?: string;
|
|
404
|
+
zOverlay?: string;
|
|
405
|
+
zModal?: string;
|
|
171
406
|
}
|
|
172
407
|
interface CoreConfig {
|
|
173
408
|
tenantId?: string;
|
|
174
|
-
autoHonorGPC?: boolean | 'strict';
|
|
175
409
|
locale?: string;
|
|
410
|
+
/** Text direction for the banner/modal root. `'auto'` (default) derives it from `locale` via
|
|
411
|
+
* `Intl.Locale(...).getTextInfo().direction` (falling back to a hand-maintained RTL-language
|
|
412
|
+
* list on engines without `getTextInfo()` support); set explicitly to override. */
|
|
413
|
+
dir?: 'ltr' | 'rtl' | 'auto';
|
|
176
414
|
disableCssTemplate?: boolean;
|
|
177
415
|
cookieSigningKey?: string;
|
|
178
416
|
allowReceipt?: boolean;
|
|
179
417
|
cookieDomains?: string;
|
|
418
|
+
/** Name of the consent cookie/localStorage key. Default: `'consenti_data'`. Not switched
|
|
419
|
+
* automatically by detected region — set explicitly if you want a different name (e.g.
|
|
420
|
+
* `'euconsent-v2'`, the IAB TCF convention, but only meaningful for operators using the
|
|
421
|
+
* spec-correct binary encoder — `@consenti/api` + the optional `@iabtechlabtcf/core` peer
|
|
422
|
+
* dependency — since the IAB name implies IAB's binary format, not Consenti's own encoding). */
|
|
423
|
+
cookieName?: string;
|
|
180
424
|
storage?: 'cookie' | 'localStorage';
|
|
181
425
|
theme?: ThemeConfig;
|
|
426
|
+
/** Initial logged-in application user ID; empty/anonymous when unset. Prefer `getUserId()`/
|
|
427
|
+
* `setUserId()` (or the `consenti:listener:identify` event) to change it after init — those
|
|
428
|
+
* also handle reconsent when the identity changes on a shared device. */
|
|
182
429
|
userId?: string;
|
|
183
430
|
usePrebuiltProfiles?: 'all' | NonEmptyArray<ComplianceGroupId>;
|
|
184
431
|
cacheResolvedProfiles?: boolean;
|
|
185
432
|
console?: Array<'info' | 'log' | 'warning' | 'error'>;
|
|
186
433
|
}
|
|
187
434
|
interface ConsentiConfig {
|
|
188
|
-
|
|
435
|
+
verbose?: boolean;
|
|
436
|
+
/** Optional — every field of `CoreConfig` is itself optional, so `new ConsentiSetup({})` and
|
|
437
|
+
* `new ConsentiSetup({ compliance: { type: 'opt-in' } })` (omitting `core` entirely) both work,
|
|
438
|
+
* matching the widget's own "Minimal config" documentation. */
|
|
439
|
+
core?: CoreConfig;
|
|
189
440
|
compliance?: ComplianceWidgetConfig;
|
|
190
441
|
rootEl?: string | HTMLElement;
|
|
191
442
|
darkMode?: boolean;
|
|
@@ -194,13 +445,30 @@ interface ConsentiConfig {
|
|
|
194
445
|
utils?: UtilsConfig;
|
|
195
446
|
plugins?: ConsentiPlugin[];
|
|
196
447
|
profileOverride?: DeepPartial<ResolvedProfile>;
|
|
448
|
+
/** Per-compliance-group text/button/cookie overrides — applied on top of whichever profile the
|
|
449
|
+
* visitor's resolved group loads, keyed by that group's id (one of the 8 built-in
|
|
450
|
+
* `ComplianceGroupId`s, or any custom group id). Lets you author different copy per country
|
|
451
|
+
* without creating a separate profile (and separate `compliance.type` wiring) for each one —
|
|
452
|
+
* e.g. `{ 'opt-in': { mainBanner: { htmlText: '...' } }, 'my-custom-group': { ... } }`.
|
|
453
|
+
* Applied before `profileOverride`, which still always applies last as the universal catch-all.
|
|
454
|
+
* Works identically in standalone and server (`api.enabled`) mode. */
|
|
455
|
+
complianceGroupsOverride?: Partial<Record<ComplianceGroupId | (string & {}), DeepPartial<ResolvedProfile>>>;
|
|
197
456
|
/** When true, suppresses the "Powered by Consenti" footer link in the banner and modal. */
|
|
198
457
|
hidePoweredBy?: boolean;
|
|
199
458
|
}
|
|
200
|
-
type ConsentiEventName = 'bannerInitialized' | 'consenti:bannerInitialized' | 'bannerVisibility' | 'consenti:bannerVisibility' | 'modalVisibility' | 'consenti:modalVisibility' | 'consentBeingSubmitted' | 'consenti:consentBeingSubmitted' | 'consentSubmitted' | 'consenti:consentSubmitted';
|
|
459
|
+
type ConsentiEventName = 'bannerInitialized' | 'consenti:bannerInitialized' | 'bannerVisibility' | 'consenti:bannerVisibility' | 'modalVisibility' | 'consenti:modalVisibility' | 'consentBeingSubmitted' | 'consenti:consentBeingSubmitted' | 'consentSubmitted' | 'consenti:consentSubmitted' | 'parentalConsentRequired' | 'consenti:parentalConsentRequired' | 'forgetMeRequested' | 'consenti:forgetMeRequested' | 'forgotten' | 'consenti:forgotten';
|
|
460
|
+
/** Snapshot of the current visitor's identity. `visitorId` is `null` until a consent decision
|
|
461
|
+
* has actually happened — it's never minted just to answer this call, see `getVisitor()`. */
|
|
462
|
+
interface VisitorIdentity {
|
|
463
|
+
visitorId: string | null;
|
|
464
|
+
type: 'authenticated' | 'anonymous';
|
|
465
|
+
userId: string | null;
|
|
466
|
+
}
|
|
201
467
|
interface ConsentiWidgetAPI {
|
|
202
468
|
hasConsent(): boolean;
|
|
203
469
|
getConsent(): ConsentValue | null;
|
|
470
|
+
getConsent(type: ConsentType): Record<string, string> | null;
|
|
471
|
+
getConsent(type?: ConsentType): ConsentValue | Record<string, string> | null;
|
|
204
472
|
getConsentDate(): Date | false;
|
|
205
473
|
getGTMConsent(): Record<string, string> | null;
|
|
206
474
|
isCookieGranted(cookieId: string, requestValue?: boolean): boolean | ConsentStatus;
|
|
@@ -213,8 +481,8 @@ interface ConsentiWidgetAPI {
|
|
|
213
481
|
off(event: ConsentiEventName, handler: (data: ConsentEvent) => void): void;
|
|
214
482
|
version(): {
|
|
215
483
|
package: string;
|
|
216
|
-
profileVersion:
|
|
217
|
-
consentVersion:
|
|
484
|
+
profileVersion: string | null;
|
|
485
|
+
consentVersion: string | null;
|
|
218
486
|
};
|
|
219
487
|
bannerVisibility(): 'main' | 'gpc' | false;
|
|
220
488
|
modalVisibility(): 'preference' | false;
|
|
@@ -223,13 +491,28 @@ interface ConsentiWidgetAPI {
|
|
|
223
491
|
hideBanner(): void;
|
|
224
492
|
showModal(triggerEl?: HTMLElement): void;
|
|
225
493
|
hideModal(): void;
|
|
226
|
-
submitConsent(consent: Partial<ConsentValue>): Promise<void>;
|
|
494
|
+
submitConsent(consent: Partial<ConsentValue>): Promise<ConsentDbRecord | void>;
|
|
227
495
|
deleteConsent(): Promise<void>;
|
|
228
496
|
reConsent(): Promise<void>;
|
|
497
|
+
/** Erases the stored consent record (same underlying call as `deleteConsent()`) and
|
|
498
|
+
* re-prompts, dispatching `consenti:forgetMeRequested` / `consenti:forgotten` around it so a
|
|
499
|
+
* host app can hook its own identity-verified erasure workflow. Powers the preference modal's
|
|
500
|
+
* "Forget me" button; also callable directly for a custom placement of the same action. */
|
|
501
|
+
forgetMe(resetAgeGate?: boolean): Promise<void>;
|
|
229
502
|
getRootElement(): HTMLElement | null;
|
|
230
503
|
getBannerElement(): HTMLElement | null;
|
|
231
504
|
getModalElement(): HTMLElement | null;
|
|
232
505
|
switchLocale(locale: string): void;
|
|
506
|
+
getUserId(): string | null;
|
|
507
|
+
/** Sets the logged-in application user ID (`null` for anonymous/logout). When the stored
|
|
508
|
+
* consent record's user differs from the new value, reconsents by default — pass
|
|
509
|
+
* `reConsent: false` to just update the identity without prompting again. No-ops (no
|
|
510
|
+
* reconsent, no warning) when there's no prior consent record to compare against. */
|
|
511
|
+
setUserId(userId: string | null, reConsent?: boolean): Promise<void>;
|
|
512
|
+
/** Snapshot of the current visitor's identity: the stable per-browser `visitorId` (`null` if
|
|
513
|
+
* no consent decision has happened yet — this never creates one), whether they're
|
|
514
|
+
* `authenticated` or `anonymous`, and the app `userId` (same as `getUserId()`). */
|
|
515
|
+
getVisitor(): VisitorIdentity;
|
|
233
516
|
setDarkMode(enable?: boolean): void;
|
|
234
517
|
setTheme(theme: Partial<ThemeConfig>): void;
|
|
235
518
|
setConfig(config: DeepPartial<ConsentiConfig>): void;
|
|
@@ -247,19 +530,75 @@ declare abstract class ConsentiPlugin {
|
|
|
247
530
|
onModalShow?(): void | Promise<void>;
|
|
248
531
|
onModalHide?(): void | Promise<void>;
|
|
249
532
|
}
|
|
250
|
-
interface
|
|
251
|
-
profileId
|
|
252
|
-
|
|
253
|
-
hasExistingConsent
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
533
|
+
interface BannerInitializedDetail {
|
|
534
|
+
profileId: string;
|
|
535
|
+
complianceGroup?: ComplianceType;
|
|
536
|
+
hasExistingConsent: boolean;
|
|
537
|
+
gpcDetected: boolean;
|
|
538
|
+
willShow: boolean;
|
|
539
|
+
}
|
|
540
|
+
interface BannerVisibilityDetail {
|
|
541
|
+
visible: boolean;
|
|
542
|
+
variant: 'main' | 'gpc';
|
|
543
|
+
action: boolean;
|
|
544
|
+
}
|
|
545
|
+
interface ModalVisibilityDetail {
|
|
546
|
+
visible: boolean;
|
|
547
|
+
action: boolean;
|
|
548
|
+
}
|
|
549
|
+
interface ConsentBeingSubmitted {
|
|
550
|
+
consentId: string;
|
|
551
|
+
visitorId: string;
|
|
552
|
+
profileId: string;
|
|
553
|
+
consentJson: ConsentValue;
|
|
554
|
+
consentAction: ConsentAction;
|
|
555
|
+
gpcDetected: boolean;
|
|
556
|
+
pageUrl: string;
|
|
557
|
+
timestamp: number;
|
|
260
558
|
fromBroadcast?: boolean;
|
|
261
|
-
gpcDetected?: boolean;
|
|
262
559
|
}
|
|
560
|
+
interface ConsentSubmittedDetail extends ConsentBeingSubmitted {
|
|
561
|
+
apiResponse: ConsentDbRecord;
|
|
562
|
+
}
|
|
563
|
+
/** Dispatched when the age gate is declined and `requireParentalConsent` is set — see the
|
|
564
|
+
* profile's `AgeGateConfig`. A deny-all consent (mandatory cookies only) has already been
|
|
565
|
+
* submitted with `parentalConsentToken` attached; this event is the hook for the site
|
|
566
|
+
* owner to run their own out-of-band parental-consent verification. `parentalConsentToken` is
|
|
567
|
+
* server-issued (and signed, when `compliance.dataSigningHash` is configured) when `api.enabled`,
|
|
568
|
+
* otherwise a client-generated, unsigned fallback — see `POST /consent/:visitorId/parental-consent-request`. */
|
|
569
|
+
interface ParentalConsentRequiredDetail {
|
|
570
|
+
parentalConsentToken: string;
|
|
571
|
+
profileId: string;
|
|
572
|
+
visitorId: string;
|
|
573
|
+
timestamp: number;
|
|
574
|
+
}
|
|
575
|
+
/** Dispatched by the standalone `resolveParentalConsent()` util (`apps/ui/src/utils/parental-consent.ts`)
|
|
576
|
+
* after `POST /consent/parental-consent-resolve` succeeds — typically from a fresh page/session
|
|
577
|
+
* with no live `ConsentiSetup` instance (the parent's own device, not the child's). A page that
|
|
578
|
+
* does have a live widget instance can listen for this to react (e.g. re-show the banner/modal for
|
|
579
|
+
* the real consent choice); what "resolved" should actually do to consent state is host-defined. */
|
|
580
|
+
interface ParentalConsentGrantedDetail {
|
|
581
|
+
visitorId: string;
|
|
582
|
+
profileId: string;
|
|
583
|
+
}
|
|
584
|
+
/** Dispatched by `forgetMe()` right before the erasure call goes out — the hook for a host app
|
|
585
|
+
* to kick off its own identity-verified erasure workflow across other systems (CRM, DMP,
|
|
586
|
+
* analytics). The CMP itself only ever erases its own consent record; see the server-side
|
|
587
|
+
* `consent.erased` eventBus event (fired by `DELETE /consent/:visitorId`) for the same hook on
|
|
588
|
+
* the backend. See the "Right to erasure" guide for the full picture. */
|
|
589
|
+
interface ForgetMeRequestedDetail {
|
|
590
|
+
visitorId: string;
|
|
591
|
+
profileId: string;
|
|
592
|
+
timestamp: number;
|
|
593
|
+
}
|
|
594
|
+
/** Dispatched by `forgetMe()` after the consent record has been erased and the banner/age-gate
|
|
595
|
+
* has been re-prompted (see `reConsent()`, which `forgetMe()` wraps). */
|
|
596
|
+
interface ForgottenDetail {
|
|
597
|
+
visitorId: string;
|
|
598
|
+
profileId: string;
|
|
599
|
+
timestamp: number;
|
|
600
|
+
}
|
|
601
|
+
type ConsentEvent = BannerInitializedDetail | BannerVisibilityDetail | ModalVisibilityDetail | ConsentBeingSubmitted | ConsentSubmittedDetail | ParentalConsentRequiredDetail | ParentalConsentGrantedDetail | ForgetMeRequestedDetail | ForgottenDetail;
|
|
263
602
|
|
|
264
603
|
/**
|
|
265
604
|
* Main entry point for the Consenti widget.
|
|
@@ -279,16 +618,30 @@ interface ConsentEvent {
|
|
|
279
618
|
*/
|
|
280
619
|
|
|
281
620
|
declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
282
|
-
private config;
|
|
283
621
|
private initialized;
|
|
284
622
|
private initializing;
|
|
285
623
|
private profile;
|
|
286
624
|
private resolvedBase;
|
|
625
|
+
/** cookieId → Category lookup, rebuilt whenever `profile` changes. Avoids rescanning categories on every consent action. */
|
|
626
|
+
private categoryIndex;
|
|
627
|
+
/** Watchers created by the declarative `data-consenti-*` DOM scan — destroyed alongside the widget since they have no other owner. */
|
|
628
|
+
private scannedScripts;
|
|
287
629
|
private consentStore;
|
|
630
|
+
/**
|
|
631
|
+
* Stable per-browser identifier. Created lazily the first time a consent decision actually
|
|
632
|
+
* happens (manual submit, GPC auto-response, or relaying a decision already made in another
|
|
633
|
+
* tab) — never minted before that, so an undecided visitor leaves nothing in storage. See
|
|
634
|
+
* `getOrCreateVisitorId()`.
|
|
635
|
+
*/
|
|
636
|
+
private visitorId;
|
|
288
637
|
private eventBus;
|
|
289
638
|
private banner;
|
|
290
639
|
private modal;
|
|
640
|
+
private ageGate;
|
|
291
641
|
private renderer;
|
|
642
|
+
/** Set once the age gate (if enabled) resolves; threaded onto every `submitConsent` call thereafter. */
|
|
643
|
+
private _ageVerified;
|
|
644
|
+
private _parentalConsentToken;
|
|
292
645
|
private broadcastChannel;
|
|
293
646
|
private readyResolve;
|
|
294
647
|
private readyPromise;
|
|
@@ -297,26 +650,118 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
|
297
650
|
private lastSignature;
|
|
298
651
|
/** Tracks which banner variant is currently visible, or null when hidden. */
|
|
299
652
|
private _bannerVariant;
|
|
653
|
+
/** Tracks which banner variant is currently mounted in the DOM (persists when banner is hidden). */
|
|
654
|
+
private _bannerMounted;
|
|
655
|
+
/** Active focus trap keydown listener — removed when modal closes. */
|
|
656
|
+
private _focusTrapListener;
|
|
657
|
+
/** Inbound `consenti:listener:identify` window listener — installed once per init(), removed on destroy(). */
|
|
658
|
+
private _identifyListener;
|
|
300
659
|
/** Maps original handler functions to their DOM-wrapped listeners for on/off bookkeeping. */
|
|
301
660
|
private _eventHandlers;
|
|
661
|
+
/** `core` defaulted to `{}` in the constructor below — every one of its own fields is
|
|
662
|
+
* optional, so this narrows back to non-optional for every other method in this class. */
|
|
663
|
+
private config;
|
|
302
664
|
/**
|
|
303
665
|
* Creates and initialises the consent widget.
|
|
304
666
|
*
|
|
305
667
|
* Construction is synchronous; the async `init()` is launched in the background.
|
|
306
668
|
* Use `await widget.ready` or `widget.onReady(cb)` to run code after init completes.
|
|
307
669
|
*
|
|
308
|
-
* @param config - Widget configuration. See `ConsentiConfig` for all options.
|
|
670
|
+
* @param config - Widget configuration. See `ConsentiConfig` for all options. `core` may be
|
|
671
|
+
* omitted entirely — every one of its own fields is optional too.
|
|
309
672
|
*/
|
|
310
673
|
constructor(config: ConsentiConfig);
|
|
311
674
|
private profileId;
|
|
675
|
+
/** Patches `base` with `complianceGroupsOverride[base.complianceGroup]`, if both exist — the
|
|
676
|
+
* per-group counterpart to `profileOverride`. Lets an author supply different text/buttons/
|
|
677
|
+
* cookies per compliance group (built-in or custom) without authoring a separate profile for
|
|
678
|
+
* each one. Applied before `profileOverride`, which still always wins as the final catch-all. */
|
|
679
|
+
private applyComplianceGroupOverride;
|
|
680
|
+
/**
|
|
681
|
+
* Whether `window.__tcfapi`/`window.__gpp` should stay uninstalled because the corresponding
|
|
682
|
+
* server-side registration (`compliance.tcf`/`compliance.gpp`) is unconfirmed (cmpId/cmpVersion
|
|
683
|
+
* changed since the dashboard's confirmation checkbox was last checked, or never confirmed).
|
|
684
|
+
* Standalone (no-backend) deployments have no governance layer to check against, so this always
|
|
685
|
+
* resolves `false` there — the client-side config is used as configured, same as before this
|
|
686
|
+
* feature existed. A network failure fails closed (`true`) rather than assuming the
|
|
687
|
+
* unverifiable state is fine.
|
|
688
|
+
*/
|
|
689
|
+
private isRegistrationBlocked;
|
|
312
690
|
/**
|
|
313
691
|
* Starts widget initialisation. Called automatically unless `autoInit: false` is set.
|
|
314
692
|
* Can be called explicitly after construction when `autoInit: false`, or after `destroy()`
|
|
315
693
|
* to re-initialise the same instance.
|
|
316
694
|
*/
|
|
317
695
|
init(): Promise<void>;
|
|
696
|
+
/**
|
|
697
|
+
* Everything after the "does consent already exist" check — GPC/CCPA/CPRA silent paths,
|
|
698
|
+
* then the normal opt-in banner. Extracted from `init()` so the age gate (when enabled and
|
|
699
|
+
* no consent exists yet) can defer this whole sequence until the visitor answers it via
|
|
700
|
+
* `resolveAgeGate()`, then resume here with `hasExisting` fixed at `false`.
|
|
701
|
+
*/
|
|
702
|
+
private continueInit;
|
|
703
|
+
/** Mounts the age-gate prompt (`profile.ageGate`) in place of the normal banner flow. */
|
|
704
|
+
private showAgeGate;
|
|
705
|
+
/**
|
|
706
|
+
* Handles the visitor's age-gate answer.
|
|
707
|
+
* - Confirmed → records `ageVerified: true` for all future `submitConsent` calls and
|
|
708
|
+
* resumes the normal init flow via `continueInit()`.
|
|
709
|
+
* - Declined, no parental consent required → immediately submits a deny-all consent
|
|
710
|
+
* (mandatory cookies still granted), mirroring the GPC-strict silent-write pattern.
|
|
711
|
+
* - Declined, parental consent required → same deny-all submission, plus a
|
|
712
|
+
* `parentalConsentToken` and a `consenti:parentalConsentRequired` event so the site
|
|
713
|
+
* owner can wire their own out-of-band verification flow (no email infra exists in
|
|
714
|
+
* this package to do that here).
|
|
715
|
+
*/
|
|
716
|
+
private resolveAgeGate;
|
|
717
|
+
/**
|
|
718
|
+
* Marks the widget ready and runs the declarative DOM-scan for
|
|
719
|
+
* `data-consenti-consent-script`/`data-consenti-category-script` tags. Called from
|
|
720
|
+
* every exit path of `init()` so the scan always runs exactly once per init cycle,
|
|
721
|
+
* whether `init()` was triggered automatically (`autoInit !== false`) or explicitly
|
|
722
|
+
* by the caller (`autoInit: false`) — it never runs before the widget actually has
|
|
723
|
+
* a resolved profile to read consent from.
|
|
724
|
+
*/
|
|
725
|
+
private finishInit;
|
|
726
|
+
/** Installs the inbound `consenti:listener:identify` window listener — the event-driven
|
|
727
|
+
* counterpart to `setUserId()`. Removes any previously-installed listener first so repeated
|
|
728
|
+
* `init()` calls (e.g. from `switchLocale()`) don't stack up duplicate listeners. */
|
|
729
|
+
private _installIdentifyListener;
|
|
730
|
+
/** Returns the current logged-in application user ID, or `null` for an anonymous visitor. */
|
|
731
|
+
getUserId(): string | null;
|
|
732
|
+
/**
|
|
733
|
+
* Sets the logged-in application user ID (`null` for anonymous/logout). If a stored consent
|
|
734
|
+
* record exists and its user differs from `userId`, this is treated as a different data
|
|
735
|
+
* subject on a shared device: by default it reconsents (deletes the existing record and
|
|
736
|
+
* re-shows the banner) and logs a warning. Pass `reConsent: false` to just update the identity
|
|
737
|
+
* without prompting again (still logs the warning). No-ops entirely — no warning, no
|
|
738
|
+
* reconsent — when there is no prior consent record to compare against (true first-time visitor).
|
|
739
|
+
*/
|
|
740
|
+
setUserId(userId: string | null, reConsent?: boolean): Promise<void>;
|
|
741
|
+
/**
|
|
742
|
+
* Snapshot of the current visitor's identity. `visitorId` is the stable per-browser ID —
|
|
743
|
+
* `null` until a consent decision has actually happened, since this never mints one just to
|
|
744
|
+
* answer the call (see `getOrCreateVisitorId()`). `type` is `'authenticated'` whenever a
|
|
745
|
+
* `userId` is set (via `setUserId()` or `core.userId`), `'anonymous'` otherwise.
|
|
746
|
+
*/
|
|
747
|
+
getVisitor(): VisitorIdentity;
|
|
748
|
+
/** Sets the `dir` attribute on the root element from `core.dir` (default `'auto'`, derived from `core.locale`). */
|
|
749
|
+
private applyDirection;
|
|
318
750
|
/** Applies the `consenti-root--dark` class based on config or profile flag. */
|
|
319
751
|
private applyDarkMode;
|
|
752
|
+
/** Applies `consenti--enhanced-a11y` class when `profile.enhanceAccessibility` is set. */
|
|
753
|
+
private applyAccessibility;
|
|
754
|
+
/** Builds and appends the metadata footer strip to modal container. */
|
|
755
|
+
private _appendMetaFooter;
|
|
756
|
+
/**
|
|
757
|
+
* Injects a `<style>` element into the root for `stackButtonsOnBreakpoint`.
|
|
758
|
+
* Called once per profile load; a new style replaces the previous one.
|
|
759
|
+
*/
|
|
760
|
+
private applyStackButtonsBreakpoint;
|
|
761
|
+
/** Installs a keyboard focus trap on `container` while the modal is open. */
|
|
762
|
+
private _installFocusTrap;
|
|
763
|
+
/** Removes the active focus trap keyboard listener. */
|
|
764
|
+
private _removeFocusTrap;
|
|
320
765
|
/**
|
|
321
766
|
* Injects CSS custom property overrides supplied via `core.theme` into the
|
|
322
767
|
* Consenti root element so they cascade down to all widget elements.
|
|
@@ -346,22 +791,31 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
|
346
791
|
*/
|
|
347
792
|
hasConsent(): boolean;
|
|
348
793
|
/**
|
|
349
|
-
* Returns the stored consent map,
|
|
350
|
-
*
|
|
794
|
+
* Returns the stored consent map, optionally transformed for a specific vendor.
|
|
795
|
+
* Pass `type` to get a vendor-ready object instead of the raw Consenti format.
|
|
796
|
+
* Always returns `null` during SSR or when no consent has been saved.
|
|
797
|
+
*
|
|
798
|
+
* @param type - Output format:
|
|
799
|
+
* - omit / `'default'` → raw ConsentValue, keyed by every cookie parameter ID in the profile
|
|
800
|
+
* - `'purpose'` → consent per the fixed CookiePurpose taxonomy (necessary/functional/preferences/analytics/marketing)
|
|
801
|
+
* - `'category'` → consent per the tenant's own authored preference-modal category, keyed by category ID
|
|
802
|
+
* ('granted' only when every member parameter is granted; 'denied'/'objected' otherwise)
|
|
803
|
+
* - `'google-gtm'` → Google Consent Mode v2 keys + ads_data_redaction, url_passthrough
|
|
804
|
+
* - `'adobe'` → { analytics, target, manager, optimizer }
|
|
805
|
+
* - `'meta'` → { pixel, api, plugins, facebookLogin }
|
|
806
|
+
* - `'microsoft-clarity'` → { session, heatmaps, performance }
|
|
807
|
+
* - `'twilio-segment'` → { identify, page, track, group, alias }
|
|
351
808
|
*/
|
|
352
809
|
getConsent(): ConsentValue | null;
|
|
810
|
+
getConsent(type: ConsentType): Record<string, string> | null;
|
|
353
811
|
/**
|
|
354
812
|
* Returns the `Date` when consent was last saved, or `false` if no consent exists.
|
|
355
813
|
* Always returns `false` during SSR.
|
|
356
814
|
*/
|
|
357
815
|
getConsentDate(): Date | false;
|
|
358
816
|
/**
|
|
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).
|
|
817
|
+
* @deprecated Use `getConsent('google-gtm')` instead.
|
|
818
|
+
* Returns the consent map in Google Consent Mode v2 format.
|
|
365
819
|
*/
|
|
366
820
|
getGTMConsent(): Record<string, string> | null;
|
|
367
821
|
/**
|
|
@@ -373,7 +827,7 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
|
373
827
|
* @param consent - Partial or full map of cookie ID → status.
|
|
374
828
|
* @param gpcDerived - Internal flag set to `true` when called from the GPC strict path.
|
|
375
829
|
*/
|
|
376
|
-
submitConsent(consent: Partial<ConsentValue>, gpcDerived?: boolean): Promise<void>;
|
|
830
|
+
submitConsent(consent: Partial<ConsentValue>, gpcDerived?: boolean, source?: number): Promise<ConsentDbRecord | void>;
|
|
377
831
|
/**
|
|
378
832
|
* Returns the currently visible banner variant, or `false` if no banner is shown.
|
|
379
833
|
* Useful for programmatically checking state before calling `showBanner` / `hideBanner`.
|
|
@@ -442,8 +896,30 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
|
442
896
|
* method so callers do not need to coordinate the async deletion with the banner display.
|
|
443
897
|
*
|
|
444
898
|
* Use this to power "Review my consent" links in footers or privacy dashboards.
|
|
899
|
+
*
|
|
900
|
+
* @param resetAgeGate - When `true` (default) and the profile has `ageGate.enabled`, the
|
|
901
|
+
* age-gate prompt is shown again instead of jumping straight to the
|
|
902
|
+
* banner — mirroring the original first-visit flow. Pass `false` to
|
|
903
|
+
* skip it and go straight to the banner, keeping the visitor's prior
|
|
904
|
+
* age-gate answer.
|
|
445
905
|
*/
|
|
446
|
-
reConsent(): Promise<void>;
|
|
906
|
+
reConsent(resetAgeGate?: boolean): Promise<void>;
|
|
907
|
+
/**
|
|
908
|
+
* Self-service erasure entry point — GDPR Art. 17 / CCPA-CPRA / LGPD Art. 18 and equivalent
|
|
909
|
+
* "right to be forgotten" rights, exercised from the widget itself. Wraps `reConsent()` (same
|
|
910
|
+
* erase-then-reprompt behavior) with `consenti:forgetMeRequested` / `consenti:forgotten`
|
|
911
|
+
* dispatched around it, so a host app can hook its own identity-verified erasure workflow
|
|
912
|
+
* across other systems (CRM, DMP, analytics) — the CMP itself only ever erases its own consent
|
|
913
|
+
* record. See the "Right to erasure" guide for the full picture, including the server-side
|
|
914
|
+
* `consent:erased` eventBus event fired by `DELETE /consent/:visitorId`.
|
|
915
|
+
*
|
|
916
|
+
* Powers the preference modal's "Forget me" button (`preferenceModal.showForgetMe`, only
|
|
917
|
+
* rendered when the visitor already has a stored consent decision). Also callable directly —
|
|
918
|
+
* e.g. `widget.forgetMe()` from a custom footer link or privacy-settings page.
|
|
919
|
+
*
|
|
920
|
+
* @param resetAgeGate - Forwarded to `reConsent()` — see its docs.
|
|
921
|
+
*/
|
|
922
|
+
forgetMe(resetAgeGate?: boolean): Promise<void>;
|
|
447
923
|
/**
|
|
448
924
|
* Returns the fully resolved profile for the current session, or `null` during the
|
|
449
925
|
* async init phase (before the profile has been fetched/resolved).
|
|
@@ -542,8 +1018,8 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
|
542
1018
|
*/
|
|
543
1019
|
version(): {
|
|
544
1020
|
package: string;
|
|
545
|
-
profileVersion:
|
|
546
|
-
consentVersion:
|
|
1021
|
+
profileVersion: string;
|
|
1022
|
+
consentVersion: string | null;
|
|
547
1023
|
};
|
|
548
1024
|
/**
|
|
549
1025
|
* Toggles or sets dark mode at runtime without re-initialising the widget.
|
|
@@ -577,64 +1053,6 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
|
577
1053
|
destroy(): void;
|
|
578
1054
|
}
|
|
579
1055
|
|
|
580
|
-
/**
|
|
581
|
-
* Angular integration for `@consenti/ui`.
|
|
582
|
-
*
|
|
583
|
-
* Import from the `@consenti/ui/angular` subpath to keep this module
|
|
584
|
-
* tree-shaken out of vanilla JS, React, and Vue bundles.
|
|
585
|
-
*
|
|
586
|
-
* Designed for **Angular 15+** with standalone components or NgModule-based projects.
|
|
587
|
-
* Initialise `ConsentiSetup` once (typically in `app.component.ts` or a root service)
|
|
588
|
-
* and call `setConsentiWidget()` so that `injectConsent()` can read from it in any
|
|
589
|
-
* component or service.
|
|
590
|
-
*
|
|
591
|
-
* @example
|
|
592
|
-
* ```ts
|
|
593
|
-
* // app.component.ts
|
|
594
|
-
* import { Component, OnInit, OnDestroy } from '@angular/core'
|
|
595
|
-
* import { ConsentiSetup } from '@consenti/ui'
|
|
596
|
-
* import { setConsentiWidget } from '@consenti/ui/angular'
|
|
597
|
-
*
|
|
598
|
-
* @Component({ selector: 'app-root', template: '<router-outlet />' })
|
|
599
|
-
* export class AppComponent implements OnInit, OnDestroy {
|
|
600
|
-
* private widget!: ConsentiSetup
|
|
601
|
-
*
|
|
602
|
-
* ngOnInit() {
|
|
603
|
-
* this.widget = new ConsentiSetup({
|
|
604
|
-
* api: { enabled: true, baseUrl: environment.apiUrl },
|
|
605
|
-
* })
|
|
606
|
-
* setConsentiWidget(this.widget)
|
|
607
|
-
* }
|
|
608
|
-
*
|
|
609
|
-
* ngOnDestroy() { this.widget.destroy() }
|
|
610
|
-
* }
|
|
611
|
-
*
|
|
612
|
-
* // any-component.ts
|
|
613
|
-
* import { Component, ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core'
|
|
614
|
-
* import { injectConsent } from '@consenti/ui/angular'
|
|
615
|
-
*
|
|
616
|
-
* @Component({
|
|
617
|
-
* selector: 'app-cookie-status',
|
|
618
|
-
* template: `
|
|
619
|
-
* <p *ngIf="consent.hasConsent()">Consent given on {{ consent.getConsentDate() | date }}</p>
|
|
620
|
-
* <button *ngIf="!consent.hasConsent()" (click)="consent.showModal()">Manage Cookies</button>
|
|
621
|
-
* `,
|
|
622
|
-
* })
|
|
623
|
-
* export class CookieStatusComponent implements OnInit, OnDestroy {
|
|
624
|
-
* consent = injectConsent()
|
|
625
|
-
* private unsub!: () => void
|
|
626
|
-
*
|
|
627
|
-
* constructor(private cdr: ChangeDetectorRef) {}
|
|
628
|
-
*
|
|
629
|
-
* ngOnInit() {
|
|
630
|
-
* this.unsub = this.consent.onConsentChange(() => this.cdr.markForCheck())
|
|
631
|
-
* }
|
|
632
|
-
*
|
|
633
|
-
* ngOnDestroy() { this.unsub() }
|
|
634
|
-
* }
|
|
635
|
-
* ```
|
|
636
|
-
*/
|
|
637
|
-
|
|
638
1056
|
/**
|
|
639
1057
|
* Registers the active `ConsentiSetup` instance so `injectConsent()` can read from it.
|
|
640
1058
|
* Call this once after creating your widget, typically in `AppComponent.ngOnInit()` or
|
|
@@ -656,25 +1074,66 @@ declare function setConsentiWidget(widget: ConsentiSetup): void;
|
|
|
656
1074
|
* @returns An object with state accessors, widget actions, and change-subscription helpers.
|
|
657
1075
|
*/
|
|
658
1076
|
declare function injectConsent(): {
|
|
1077
|
+
/** Returns `true` if the visitor has saved a valid consent record. */
|
|
659
1078
|
hasConsent: () => boolean;
|
|
1079
|
+
/** Returns the stored consent map, or `null`. */
|
|
660
1080
|
getConsent: () => ConsentValue | null;
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
1081
|
+
/** Returns the `Date` of the last consent submission, or `false`. */
|
|
1082
|
+
getConsentDate: () => false | Date;
|
|
1083
|
+
/** Returns the current banner state: `'main'`, `'gpc'`, or `false` (hidden). */
|
|
1084
|
+
bannerVisibility: () => false | "main" | "gpc";
|
|
1085
|
+
/** Returns the current modal state: `'preference'` or `false` (hidden). */
|
|
1086
|
+
modalVisibility: () => false | "preference";
|
|
1087
|
+
/** Shows the consent banner. Pass `true` to force the GPC variant. */
|
|
1088
|
+
showBanner: (gpc?: boolean) => void | undefined;
|
|
1089
|
+
/** Hides the consent banner. */
|
|
1090
|
+
hideBanner: () => void | undefined;
|
|
1091
|
+
/** Opens the preference modal. */
|
|
1092
|
+
showModal: () => void | undefined;
|
|
1093
|
+
/** Closes the preference modal. */
|
|
1094
|
+
hideModal: () => void | undefined;
|
|
1095
|
+
/** Grants all non-mandatory cookies. Pass `true` to grant only mandatory ones and deny the rest. */
|
|
1096
|
+
grantAll: (onlyMandatory?: boolean) => Promise<void>;
|
|
1097
|
+
/** Denies all non-mandatory cookies. Pass `true` to deny mandatory ones too (use with care). */
|
|
1098
|
+
denyAll: (includingMandatory?: boolean) => Promise<void>;
|
|
1099
|
+
/** Saves the given partial consent map. Missing cookie IDs default to their current stored value. */
|
|
1100
|
+
submitConsent: (consent: Partial<ConsentValue>) => Promise<void | ConsentDbRecord>;
|
|
1101
|
+
/** Deletes the current consent record and re-shows the banner. Pass `false` to skip
|
|
1102
|
+
* re-showing the age-gate prompt (if the profile has one) and go straight to the banner. */
|
|
1103
|
+
reConsent: (resetAgeGate?: boolean) => Promise<void>;
|
|
1104
|
+
/** Returns `true` if the given cookie ID is `'granted'`. */
|
|
1105
|
+
isCookieGranted: (id: string) => boolean;
|
|
1106
|
+
/** Returns `true` if every cookie in the given category is `'granted'`. */
|
|
1107
|
+
isCategoryGranted: (id: string) => boolean;
|
|
1108
|
+
/** Switches the active locale and re-renders the banner and modal. */
|
|
1109
|
+
switchLocale: (locale: string) => void | undefined;
|
|
1110
|
+
/** Returns the current logged-in application user ID, or `null` if anonymous. */
|
|
1111
|
+
getUserId: () => string | null;
|
|
1112
|
+
/** Sets the logged-in application user ID; reconsents by default when it changes. */
|
|
1113
|
+
setUserId: (userId: string | null, reConsent?: boolean) => Promise<void>;
|
|
1114
|
+
/** Snapshot of the current visitor's identity (`visitorId`, `type`, `userId`). */
|
|
1115
|
+
getVisitor: () => VisitorIdentity;
|
|
1116
|
+
/**
|
|
1117
|
+
* Subscribes to consent change events. Call in `ngOnInit`; return value unsubscribes
|
|
1118
|
+
* in `ngOnDestroy`.
|
|
1119
|
+
*
|
|
1120
|
+
* @example
|
|
1121
|
+
* ```ts
|
|
1122
|
+
* ngOnInit() { this.unsub = this.consent.onConsentChange(() => this.cdr.markForCheck()) }
|
|
1123
|
+
* ngOnDestroy() { this.unsub() }
|
|
1124
|
+
* ```
|
|
1125
|
+
*/
|
|
1126
|
+
onConsentChange: (callback: () => void) => (() => void);
|
|
1127
|
+
/**
|
|
1128
|
+
* Subscribes to banner visibility changes. Call in `ngOnInit`; return value unsubscribes
|
|
1129
|
+
* in `ngOnDestroy`.
|
|
1130
|
+
*/
|
|
1131
|
+
onBannerChange: (callback: () => void) => (() => void);
|
|
1132
|
+
/**
|
|
1133
|
+
* Subscribes to modal visibility changes. Call in `ngOnInit`; return value unsubscribes
|
|
1134
|
+
* in `ngOnDestroy`.
|
|
1135
|
+
*/
|
|
1136
|
+
onModalChange: (callback: () => void) => (() => void);
|
|
678
1137
|
};
|
|
679
1138
|
|
|
680
1139
|
export { injectConsent, setConsentiWidget };
|