@consenti/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,770 @@
1
+ type ConsentStatus = 'granted' | 'denied' | 'objected';
2
+ type ConsentAction = 'accept_all' | 'reject_all' | 'custom' | 'update';
3
+ type ConsentValue = Record<string, ConsentStatus>;
4
+ interface Cookie {
5
+ id: string;
6
+ type?: 'consent' | 'legitimate_interest';
7
+ mandatory?: boolean;
8
+ listenGpc?: boolean;
9
+ expiry?: number;
10
+ tcfVendorId?: number;
11
+ tcfPurposes?: number[];
12
+ cpraCategory?: 'sale' | 'sharing' | 'sensitive';
13
+ }
14
+ type ButtonStyle = 'primary' | 'secondary' | 'text' | 'accent';
15
+ type ButtonAction = 'submit' | 'manage' | 'close' | 'custom' | 'link';
16
+ interface Button {
17
+ text: string;
18
+ style: ButtonStyle;
19
+ action: ButtonAction;
20
+ cookies?: string[] | '*' | '!';
21
+ url?: string;
22
+ }
23
+ interface MainBanner {
24
+ position: 'top' | 'bottom' | 'middle' | 'left-bottom' | 'right-bottom';
25
+ overlayOpacity?: number;
26
+ showClose?: boolean;
27
+ showLocaleSwitcher?: boolean;
28
+ heading?: string;
29
+ headingTag?: string;
30
+ htmlText: string;
31
+ buttons: Button[];
32
+ }
33
+ type GpcBanner = MainBanner;
34
+ interface Category {
35
+ id: string;
36
+ heading: string;
37
+ headingTag?: string;
38
+ htmlText: string;
39
+ mandatory?: boolean;
40
+ type?: 'consent' | 'legitimate_interest';
41
+ legitimateInterest?: {
42
+ enabled?: boolean;
43
+ description?: string;
44
+ };
45
+ cookies: string[];
46
+ }
47
+ interface PreferenceModal {
48
+ position?: 'left' | 'right' | 'center';
49
+ overlayOpacity?: number;
50
+ showClose?: boolean;
51
+ showLocaleSwitcher?: boolean;
52
+ heading?: string;
53
+ headingTag?: string;
54
+ subheading?: string;
55
+ htmlText?: string;
56
+ buttons: Button[];
57
+ categories: Category[];
58
+ persistent?: boolean;
59
+ }
60
+ interface LocaleTranslations {
61
+ mainBanner: MainBanner;
62
+ gpcBanner?: GpcBanner;
63
+ preferenceModal: PreferenceModal;
64
+ }
65
+ interface ProfileTranslations {
66
+ [locale: string]: LocaleTranslations;
67
+ }
68
+ interface DpdpaConfig {
69
+ dataFiduciary: string;
70
+ grievanceEmail: string;
71
+ purposeDescription?: string;
72
+ }
73
+ interface LocaleTextContent {
74
+ mainBanner: {
75
+ heading: string;
76
+ htmlText: string;
77
+ buttonLabels: string[];
78
+ };
79
+ gpcBanner: {
80
+ heading: string;
81
+ htmlText: string;
82
+ buttonLabels: string[];
83
+ };
84
+ preferenceModal: {
85
+ heading: string;
86
+ subheading?: string;
87
+ htmlText?: string;
88
+ buttonLabels: string[];
89
+ categories: Array<{
90
+ id: string;
91
+ heading: string;
92
+ htmlText: string;
93
+ }>;
94
+ };
95
+ }
96
+ interface ProfileConfig {
97
+ cookies?: Cookie[];
98
+ defaultLocale: string;
99
+ translations?: ProfileTranslations;
100
+ gpcBanner?: boolean;
101
+ darkMode?: boolean;
102
+ allowedOrigins?: string[];
103
+ dpdpa?: DpdpaConfig;
104
+ regulation?: 'gdpr' | 'ccpa' | 'cpra' | 'dpdpa' | 'uk-gdpr' | 'lgpd' | 'pipeda' | 'popia' | 'pdpa-th' | 'appi' | 'kvkk';
105
+ regulations?: string[];
106
+ cookieTemplateId?: string;
107
+ uiTemplateId?: string;
108
+ localeContents?: Record<string, LocaleTextContent>;
109
+ }
110
+ interface ResolvedProfile {
111
+ id: number;
112
+ version: number;
113
+ defaultLocale: string;
114
+ locales?: string[];
115
+ cookies: Cookie[];
116
+ mainBanner: MainBanner;
117
+ gpcBanner?: GpcBanner;
118
+ preferenceModal: PreferenceModal;
119
+ cookieSigningKey?: string;
120
+ dpdpa?: DpdpaConfig;
121
+ darkMode?: boolean;
122
+ }
123
+ interface GtmConfig {
124
+ containerId?: string;
125
+ events?: string[];
126
+ dataLayer?: string;
127
+ urlPassthrough?: boolean;
128
+ adsDataRedaction?: boolean;
129
+ }
130
+ interface UtilsConfig {
131
+ gtm?: GtmConfig;
132
+ }
133
+ interface ApiConfig {
134
+ enabled?: boolean;
135
+ baseUrl?: string;
136
+ authToken?: string;
137
+ }
138
+ interface ThemeConfig {
139
+ bgColor?: string;
140
+ textColor?: string;
141
+ primaryColor?: string;
142
+ primaryTextColor?: string;
143
+ secondaryColor?: string;
144
+ secondaryTextColor?: string;
145
+ borderColor?: string;
146
+ fontFamily?: string;
147
+ fontSizeBase?: string;
148
+ fontSizeHeading?: string;
149
+ fontSizeMultiplier?: string;
150
+ borderRadius?: string;
151
+ buttonBorderRadius?: string;
152
+ toggleBgOn?: string;
153
+ toggleBgOff?: string;
154
+ accentColor?: string;
155
+ accentTextColor?: string;
156
+ }
157
+ interface CoreConfig {
158
+ profileId?: number;
159
+ autoHonorGPC?: boolean | 'strict';
160
+ locale?: string;
161
+ disableCssTemplate?: boolean;
162
+ signCookies?: boolean;
163
+ cookieSigningKey?: string;
164
+ allowReceipt?: boolean;
165
+ cookieDomains?: string;
166
+ storage?: 'cookie' | 'localStorage';
167
+ theme?: ThemeConfig;
168
+ privacyPolicyUrl?: string;
169
+ regulation?: 'gdpr' | 'ccpa' | 'cpra' | 'dpdpa' | 'uk-gdpr' | 'lgpd' | 'pipeda' | 'popia' | 'pdpa-th' | 'appi' | 'kvkk';
170
+ userId?: string;
171
+ }
172
+ interface ConsentiConfig {
173
+ core: CoreConfig;
174
+ rootEl?: string | HTMLElement;
175
+ darkMode?: boolean;
176
+ autoInit?: boolean;
177
+ api?: ApiConfig;
178
+ utils?: UtilsConfig;
179
+ plugins?: ConsentiPlugin$1[];
180
+ profileOverride?: Partial<ResolvedProfile>;
181
+ }
182
+ interface ConsentiWidgetAPI {
183
+ hasConsent(): boolean;
184
+ getConsent(): ConsentValue | null;
185
+ getConsentDate(): Date | false;
186
+ getGTMConsent(): Record<string, string> | null;
187
+ bannerVisibility(): 'main' | 'gpc' | false;
188
+ modalVisibility(): 'preference' | false;
189
+ getProfile(): ResolvedProfile | null;
190
+ showBanner(gpc?: boolean): void;
191
+ hideBanner(): void;
192
+ showModal(triggerEl?: HTMLElement): void;
193
+ hideModal(): void;
194
+ submitConsent(consent: Partial<ConsentValue>): Promise<void>;
195
+ deleteConsent(): Promise<void>;
196
+ reConsent(): Promise<void>;
197
+ getRootElement(): HTMLElement | null;
198
+ getBannerElement(): HTMLElement | null;
199
+ getModalElement(): HTMLElement | null;
200
+ switchLocale(locale: string): void;
201
+ init(): Promise<void>;
202
+ onReady(callback: () => void): void;
203
+ destroy(): void;
204
+ }
205
+ declare abstract class ConsentiPlugin$1 {
206
+ abstract initialize(widget: ConsentiWidgetAPI): void | Promise<void>;
207
+ abstract destroy(): void;
208
+ onConsentSubmit?(consent: ConsentValue): void | Promise<void>;
209
+ onBannerShow?(): void | Promise<void>;
210
+ onBannerHide?(): void | Promise<void>;
211
+ onModalShow?(): void | Promise<void>;
212
+ onModalHide?(): void | Promise<void>;
213
+ }
214
+ interface CookieOptions {
215
+ maxAge?: number;
216
+ path?: string;
217
+ sameSite?: 'Lax' | 'Strict' | 'None';
218
+ secure?: boolean;
219
+ domain?: string;
220
+ }
221
+ interface ParsedConsent {
222
+ timestamp: string;
223
+ consent: ConsentValue;
224
+ signature?: string;
225
+ }
226
+ interface ConsentRecord {
227
+ visitorId: string;
228
+ consentId?: string;
229
+ profileId: number;
230
+ profileVersion: number;
231
+ consent: ConsentValue;
232
+ timestamp: string;
233
+ gpc?: boolean;
234
+ action?: ConsentAction;
235
+ pageUrl?: string;
236
+ }
237
+ interface ConsentReceipt {
238
+ version: '1.0';
239
+ issuedAt: string;
240
+ visitorId: string;
241
+ profileId: number;
242
+ profileVersion: number;
243
+ locale: string;
244
+ consent: ConsentValue;
245
+ signature?: string;
246
+ }
247
+ interface ConsentEvent {
248
+ profileId?: number;
249
+ consent?: ConsentValue;
250
+ hasExistingConsent?: boolean;
251
+ show?: boolean;
252
+ action?: boolean;
253
+ consentAction?: ConsentAction;
254
+ consentId?: string;
255
+ pageUrl?: string;
256
+ apiResponse?: unknown;
257
+ fromBroadcast?: boolean;
258
+ gpcDetected?: boolean;
259
+ }
260
+ type ConsentiMessage = {
261
+ type: 'consentUpdated';
262
+ consent: ConsentValue;
263
+ profileId: number;
264
+ } | {
265
+ type: 'bannerClosed';
266
+ } | {
267
+ type: 'consentDeleted';
268
+ };
269
+
270
+ /**
271
+ * Main entry point for the Consenti widget.
272
+ *
273
+ * Instantiate one `ConsentiSetup` per page (or per profile ID if you run multiple
274
+ * consent contexts on the same page). The constructor is safe to call during SSR —
275
+ * it silently no-ops when `window` is not defined.
276
+ *
277
+ * @example
278
+ * ```ts
279
+ * const widget = new ConsentiSetup({
280
+ * core: { profileId: 0 },
281
+ * })
282
+ * await widget.ready
283
+ * console.log(widget.hasConsent())
284
+ * ```
285
+ */
286
+
287
+ declare class ConsentiSetup implements ConsentiWidgetAPI {
288
+ private config;
289
+ private initialized;
290
+ private initializing;
291
+ private profile;
292
+ private consentStore;
293
+ private eventBus;
294
+ private banner;
295
+ private modal;
296
+ private renderer;
297
+ private broadcastChannel;
298
+ private readyResolve;
299
+ private readyPromise;
300
+ private plugins;
301
+ /** Signature from the most recent `writeAndSign` call. Included in consent receipts. */
302
+ private lastSignature;
303
+ /** Tracks which banner variant is currently visible, or null when hidden. */
304
+ private _bannerVariant;
305
+ /**
306
+ * Creates and initialises the consent widget.
307
+ *
308
+ * Construction is synchronous; the async `init()` is launched in the background.
309
+ * Use `await widget.ready` or `widget.onReady(cb)` to run code after init completes.
310
+ *
311
+ * @param config - Widget configuration. See `ConsentiConfig` for all options.
312
+ */
313
+ constructor(config: ConsentiConfig);
314
+ private profileId;
315
+ /**
316
+ * Starts widget initialisation. Called automatically unless `autoInit: false` is set.
317
+ * Can be called explicitly after construction when `autoInit: false`, or after `destroy()`
318
+ * to re-initialise the same instance.
319
+ */
320
+ init(): Promise<void>;
321
+ /** Applies the `consenti-root--dark` class based on config or profile flag. */
322
+ private applyDarkMode;
323
+ /**
324
+ * Injects CSS custom property overrides supplied via `core.theme` into the
325
+ * Consenti root element so they cascade down to all widget elements.
326
+ */
327
+ private applyTheme;
328
+ private runPlugins;
329
+ /**
330
+ * Invokes an optional plugin hook on all registered plugins.
331
+ * Async hooks are awaited; errors are caught and logged so a broken plugin
332
+ * never interferes with the consent flow.
333
+ *
334
+ * @param invoke - Function that calls the desired hook method on a single plugin.
335
+ */
336
+ private callPluginHook;
337
+ private buildHandlers;
338
+ /**
339
+ * Classifies a consent submission as `accept_all`, `reject_all`, `custom`, or `update`.
340
+ * Used to enrich events and API payloads with intent metadata.
341
+ */
342
+ private deriveAction;
343
+ private handleBroadcastMessage;
344
+ /**
345
+ * Returns `true` if the visitor has saved consent for the active profile.
346
+ * Always returns `false` during SSR.
347
+ */
348
+ hasConsent(): boolean;
349
+ /**
350
+ * Returns the stored consent map, or `null` if no consent has been saved.
351
+ * Always returns `null` during SSR.
352
+ */
353
+ getConsent(): ConsentValue | null;
354
+ /**
355
+ * Returns the `Date` when consent was last saved, or `false` if no consent exists.
356
+ * Always returns `false` during SSR.
357
+ */
358
+ getConsentDate(): Date | false;
359
+ /**
360
+ * Returns the consent map in Google Consent Mode v2 format, ready to pass to
361
+ * `gtag('consent', 'update', ...)`. Returns `null` if no consent has been saved.
362
+ *
363
+ * Maps Consenti cookie IDs to the seven Google Consent Mode v2 parameters.
364
+ * Cookies whose IDs exactly match a GCM v2 key are forwarded directly.
365
+ * Non-GCM cookies are ignored by this method (they appear in `getConsent()` only).
366
+ */
367
+ getGTMConsent(): Record<string, string> | null;
368
+ /**
369
+ * Records and persists the user's consent choices.
370
+ *
371
+ * Mandatory cookies are always forced to `'granted'` regardless of what is passed.
372
+ * Non-mandatory cookies not present in `consent` default to `'denied'`.
373
+ *
374
+ * @param consent - Partial or full map of cookie ID → status.
375
+ * @param gpcDerived - Internal flag set to `true` when called from the GPC strict path.
376
+ */
377
+ submitConsent(consent: Partial<ConsentValue>, gpcDerived?: boolean): Promise<void>;
378
+ /**
379
+ * Returns the currently visible banner variant, or `false` if no banner is shown.
380
+ * Useful for programmatically checking state before calling `showBanner` / `hideBanner`.
381
+ */
382
+ bannerVisibility(): 'main' | 'gpc' | false;
383
+ /**
384
+ * Returns `'preference'` when the preference modal is open, or `false` when closed.
385
+ */
386
+ modalVisibility(): 'preference' | false;
387
+ /**
388
+ * Mounts and shows the consent banner.
389
+ *
390
+ * No-ops if the requested variant is already visible. If a different variant is
391
+ * currently shown (e.g. switching from main to GPC), the existing banner is replaced.
392
+ *
393
+ * @param useGpcVariant - When `true` and the profile has a `gpcBanner`, that variant
394
+ * is shown instead of `mainBanner`. Used by the GPC flow.
395
+ */
396
+ showBanner(useGpcVariant?: boolean): void;
397
+ /** Hides the banner and notifies other tabs via BroadcastChannel. No-op if already hidden. */
398
+ hideBanner(): void;
399
+ /**
400
+ * Opens the preference modal and hides the banner while the modal is open.
401
+ * No-ops if the modal is already open.
402
+ * If the modal is closed without saving consent, the banner is re-shown automatically
403
+ * (see `hideModal()`). Only one banner or modal is visible at a time.
404
+ *
405
+ * @param triggerEl - The element that triggered the modal open. Focus returns here on close.
406
+ */
407
+ showModal(triggerEl?: HTMLElement): void;
408
+ /**
409
+ * Closes the preference modal and returns focus to the element that opened it.
410
+ * No-ops if the modal is already closed.
411
+ * If the user has not yet given consent, the banner is re-shown automatically.
412
+ */
413
+ hideModal(): void;
414
+ /**
415
+ * Registers a callback to run once the widget has finished initialising.
416
+ * If the widget is already initialised, the callback is invoked synchronously.
417
+ *
418
+ * @param callback - Function to call when the widget is ready.
419
+ */
420
+ onReady(callback: () => void): void;
421
+ /**
422
+ * A `Promise` that resolves once the widget has finished initialising.
423
+ * Equivalent to wrapping `onReady()` in a `Promise`.
424
+ *
425
+ * @example
426
+ * ```ts
427
+ * await widget.ready
428
+ * console.log('Widget ready')
429
+ * ```
430
+ */
431
+ get ready(): Promise<void>;
432
+ /**
433
+ * Deletes the stored consent record and notifies other tabs.
434
+ * The banner will reappear on the next page load (or immediately if the
435
+ * receiving tab calls `showBanner()` in response to `consentDeleted`).
436
+ */
437
+ deleteConsent(): Promise<void>;
438
+ /**
439
+ * Removes existing consent and immediately re-shows the consent banner so the user
440
+ * can review and update their preferences.
441
+ *
442
+ * Equivalent to calling `deleteConsent()` followed by `showBanner()`, but in a single
443
+ * method so callers do not need to coordinate the async deletion with the banner display.
444
+ *
445
+ * Use this to power "Review my consent" links in footers or privacy dashboards.
446
+ */
447
+ reConsent(): Promise<void>;
448
+ /**
449
+ * Returns the fully resolved profile for the current session, or `null` during the
450
+ * async init phase (before the profile has been fetched/resolved).
451
+ *
452
+ * Plugins can use this to read cookie definitions, banner config, modal categories,
453
+ * and the profile version without accessing internal state.
454
+ */
455
+ getProfile(): ResolvedProfile | null;
456
+ /**
457
+ * Returns the `#consenti-root` container element that wraps all widget DOM,
458
+ * or `null` before the widget has mounted or after `destroy()`.
459
+ *
460
+ * Plugins can use this to append custom elements, apply extra CSS variables,
461
+ * or observe mutations within the widget container.
462
+ */
463
+ getRootElement(): HTMLElement | null;
464
+ /**
465
+ * Returns the `#consenti-banner` element when it is present in the DOM, or `null`.
466
+ * The element may be hidden (`display: none`) — check `bannerVisibility()` for visibility state.
467
+ *
468
+ * Useful for adding extra content, custom event listeners, or mutation observers on the banner.
469
+ */
470
+ getBannerElement(): HTMLElement | null;
471
+ /**
472
+ * Returns the `#consenti-modal` element when the modal has been built, or `null`.
473
+ * The element is built on first `showModal()` call and reused on subsequent opens.
474
+ *
475
+ * Useful for injecting extra content into the modal or listening to modal-specific events.
476
+ */
477
+ getModalElement(): HTMLElement | null;
478
+ /**
479
+ * Switches the active locale, re-resolves the profile, and re-renders the widget.
480
+ * Called by the locale switcher UI when the user selects a different language.
481
+ *
482
+ * @param locale - BCP 47 locale code to switch to (e.g. `'fr'`, `'de-AT'`).
483
+ */
484
+ switchLocale(locale: string): void;
485
+ /**
486
+ * Destroys the widget — removes all DOM elements, disconnects the BroadcastChannel,
487
+ * and calls `destroy()` on all registered plugins.
488
+ *
489
+ * After `destroy()`, the instance should not be used again.
490
+ */
491
+ destroy(): void;
492
+ }
493
+
494
+ /**
495
+ * Abstract base class for Consenti UI plugins.
496
+ *
497
+ * Extend this class to hook into the widget lifecycle. Each plugin receives the
498
+ * full `ConsentiWidgetAPI` in `initialize()` — call any public method, inspect
499
+ * the resolved profile, or manipulate the widget DOM.
500
+ *
501
+ * Errors thrown in `initialize()` or `destroy()` are caught by `ConsentiSetup`
502
+ * and logged as warnings — a broken plugin never blocks the consent flow.
503
+ *
504
+ * @example
505
+ * ```ts
506
+ * import { ConsentiPlugin } from '@consenti/ui'
507
+ * import type { ConsentiWidgetAPI } from '@consenti/ui'
508
+ *
509
+ * class AnalyticsPlugin extends ConsentiPlugin {
510
+ * initialize(widget: ConsentiWidgetAPI) {
511
+ * window.addEventListener('consenti:consentSubmitted', () => {
512
+ * const consent = widget.getConsent()
513
+ * myAnalytics.track('consent_saved', consent)
514
+ * })
515
+ * }
516
+ *
517
+ * destroy() {
518
+ * // remove listeners, clear timers, etc.
519
+ * }
520
+ * }
521
+ *
522
+ * class BannerStylePlugin extends ConsentiPlugin {
523
+ * initialize(widget: ConsentiWidgetAPI) {
524
+ * widget.onReady(() => {
525
+ * const banner = widget.getBannerElement()
526
+ * if (banner) banner.style.setProperty('--my-custom-prop', 'red')
527
+ * })
528
+ * }
529
+ * destroy() {}
530
+ * }
531
+ *
532
+ * const widget = new ConsentiSetup({
533
+ * core: { profileId: 1 },
534
+ * plugins: [new AnalyticsPlugin(), new BannerStylePlugin()],
535
+ * })
536
+ * ```
537
+ */
538
+ declare abstract class ConsentiPlugin {
539
+ /**
540
+ * Called once after `ConsentiSetup` has finished its async `init()`.
541
+ * May be async — the widget awaits the result before continuing.
542
+ *
543
+ * @param widget - The full public API of the initialised widget.
544
+ */
545
+ abstract initialize(widget: ConsentiWidgetAPI): void | Promise<void>;
546
+ /**
547
+ * Called when `widget.destroy()` is invoked.
548
+ * Use this to remove event listeners, cancel timers, and release resources.
549
+ */
550
+ abstract destroy(): void;
551
+ /**
552
+ * Called after consent has been saved to storage and dispatched to the API.
553
+ * Receives the full, normalised consent map (mandatory cookies always `'granted'`).
554
+ *
555
+ * @param consent - The complete consent record that was saved.
556
+ */
557
+ onConsentSubmit?(consent: ConsentValue): void | Promise<void>;
558
+ /** Called after the consent banner has been mounted and made visible. */
559
+ onBannerShow?(): void | Promise<void>;
560
+ /** Called after the consent banner has been hidden. */
561
+ onBannerHide?(): void | Promise<void>;
562
+ /** Called after the preference modal has been opened. */
563
+ onModalShow?(): void | Promise<void>;
564
+ /** Called after the preference modal has been closed. */
565
+ onModalHide?(): void | Promise<void>;
566
+ }
567
+
568
+ /**
569
+ * Client-side profile builder for API-less deployments.
570
+ *
571
+ * `ConsentiProfile` provides a type-safe way to define consent profiles directly
572
+ * in application code, without running a backend. Each instance is registered in
573
+ * the module-level profile registry so `ConsentiSetup` can resolve it by ID.
574
+ *
575
+ * IDs are auto-assigned starting from 1000 to avoid conflicts with API-backed profiles
576
+ * (which use small sequential integers assigned by the database).
577
+ *
578
+ * @example
579
+ * ```ts
580
+ * import { ConsentiProfile, ConsentiSetup } from '@consenti/ui'
581
+ *
582
+ * const profile = new ConsentiProfile({
583
+ * cookies: [
584
+ * { id: 'necessary', mandatory: true },
585
+ * { id: 'analytics' },
586
+ * ],
587
+ * defaultLocale: 'en',
588
+ * translations: {
589
+ * en: {
590
+ * mainBanner: {
591
+ * position: 'bottom',
592
+ * htmlText: 'We use cookies.',
593
+ * buttons: [{ text: 'Accept All', type: 'primary', cookies: '*' }],
594
+ * },
595
+ * preferenceModal: {
596
+ * categories: [],
597
+ * buttons: [{ text: 'Save', type: 'submit' }],
598
+ * },
599
+ * },
600
+ * },
601
+ * })
602
+ *
603
+ * const widget = new ConsentiSetup({ core: { profileId: profile.getId() } })
604
+ * ```
605
+ */
606
+
607
+ /**
608
+ * Defines a local consent profile and registers it for use by `ConsentiSetup`.
609
+ */
610
+ declare class ConsentiProfile {
611
+ private config;
612
+ private id;
613
+ /**
614
+ * @param config - Full profile configuration including cookies, locales, and UI text.
615
+ */
616
+ constructor(config: ProfileConfig);
617
+ /** Returns the auto-assigned numeric profile ID. Pass this to `core.profileId`. */
618
+ getId(): number;
619
+ /** Returns the raw `ProfileConfig` passed to the constructor. */
620
+ toJSON(): ProfileConfig;
621
+ }
622
+
623
+ /**
624
+ * ConsentScript — conditionally loads/unloads a third-party `<script>` tag
625
+ * based on a specific consent cookie's status.
626
+ *
627
+ * Instantiate after the widget is ready. The script is injected into `<head>`
628
+ * when the target cookie's consent is `'granted'` and removed when it is
629
+ * revoked. Ideal for lazy-loading analytics, ad pixels, or chat widgets without
630
+ * manual `consenti:consentSubmitted` listeners.
631
+ *
632
+ * @example
633
+ * ```ts
634
+ * import { ConsentiSetup, ConsentScript } from '@consenti/ui'
635
+ *
636
+ * const widget = new ConsentiSetup({ core: { profileId: 0 } })
637
+ * await widget.ready
638
+ *
639
+ * const cs = new ConsentScript({
640
+ * cookieId: 'analytics_storage',
641
+ * widget,
642
+ * src: 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX',
643
+ * attributes: { async: '' },
644
+ * onLoad: () => console.log('Analytics loaded'),
645
+ * onRevoke: () => console.log('Analytics removed'),
646
+ * })
647
+ * ```
648
+ */
649
+
650
+ /** Options for constructing a {@link ConsentScript} instance. */
651
+ interface ConsentScriptOptions {
652
+ /** The consent cookie ID to watch (e.g. `'analytics_storage'`). */
653
+ cookieId: string;
654
+ /** The `ConsentiSetup` widget instance to read consent from. */
655
+ widget: ConsentiSetup;
656
+ /** Remote script `src` URL to inject when consent is granted. */
657
+ src?: string;
658
+ /** Inline JavaScript content to execute when consent is granted. */
659
+ innerHTML?: string;
660
+ /**
661
+ * Additional HTML attributes applied to the injected `<script>` element.
662
+ * Set a key to an empty string for boolean attributes (e.g. `{ async: '', defer: '' }`).
663
+ */
664
+ attributes?: Record<string, string>;
665
+ /** Called after the `<script>` element has been appended to `<head>`. */
666
+ onLoad?: () => void;
667
+ /** Called after the `<script>` element has been removed from the DOM. */
668
+ onRevoke?: () => void;
669
+ }
670
+ /**
671
+ * Conditionally injects and removes a `<script>` element based on a specific
672
+ * cookie's consent status.
673
+ *
674
+ * The check runs immediately on construction so existing consent is honoured
675
+ * without waiting for the next `consenti:consentSubmitted` event. It then
676
+ * re-evaluates on every future consent change.
677
+ *
678
+ * Call {@link ConsentScript.destroy} when the trigger is no longer needed
679
+ * (e.g. on SPA route unmount) to remove the event listener and the injected
680
+ * element.
681
+ */
682
+ declare class ConsentScript {
683
+ private readonly options;
684
+ private scriptEl;
685
+ private readonly handler;
686
+ constructor(options: ConsentScriptOptions);
687
+ private evaluate;
688
+ private inject;
689
+ private revoke;
690
+ /**
691
+ * Removes the event listener and the injected `<script>` element (if present).
692
+ * Call this when the widget is destroyed or the script is no longer needed.
693
+ */
694
+ destroy(): void;
695
+ }
696
+
697
+ /**
698
+ * CookieTrigger — a pre-built accessible UI element that reopens the Consenti
699
+ * preference panel when clicked.
700
+ *
701
+ * Use this to add a "Cookie Settings" link in your site footer or cookie policy
702
+ * page without wiring up the click handler manually or importing the widget module
703
+ * into your layout component.
704
+ *
705
+ * @example
706
+ * ```ts
707
+ * import { ConsentiSetup, CookieTrigger } from '@consenti/ui'
708
+ *
709
+ * const widget = new ConsentiSetup({ core: { profileId: 0 } })
710
+ *
711
+ * // Attach to an existing element
712
+ * new CookieTrigger({ widget, el: '#footer-cookie-settings' })
713
+ *
714
+ * // — or — create a new button and place it yourself
715
+ * const trigger = new CookieTrigger({ widget, label: 'Cookie Settings' })
716
+ * document.querySelector('.site-footer')?.appendChild(trigger.getElement())
717
+ * ```
718
+ */
719
+
720
+ /** Options for constructing a {@link CookieTrigger} instance. */
721
+ interface CookieTriggerOptions {
722
+ /** The widget instance to control when the trigger is clicked. */
723
+ widget: ConsentiSetup;
724
+ /**
725
+ * Element to attach the click handler to.
726
+ * - `HTMLElement` — used directly.
727
+ * - `string` — treated as a CSS selector; the first matching element is used.
728
+ * - Omitted — a new `<button>` is created. Retrieve it with {@link CookieTrigger.getElement}
729
+ * and append it to the DOM at the desired location.
730
+ */
731
+ el?: HTMLElement | string;
732
+ /**
733
+ * Which panel opens on click.
734
+ * - `'modal'` (default) — preference modal.
735
+ * - `'banner'` — main consent banner.
736
+ */
737
+ action?: 'modal' | 'banner';
738
+ /**
739
+ * Label for the auto-created button element.
740
+ * Ignored when `el` points to an existing element. Defaults to `'Cookie Settings'`.
741
+ */
742
+ label?: string;
743
+ }
744
+ /**
745
+ * Attaches a click handler that opens the Consenti preference panel to a DOM
746
+ * element, or creates an accessible `<button>` when no existing element is provided.
747
+ *
748
+ * The created button uses the `.consenti-btn` and `.consenti-btn--text` classes so
749
+ * it inherits the widget's theme automatically.
750
+ */
751
+ declare class CookieTrigger {
752
+ private readonly el;
753
+ private readonly clickHandler;
754
+ /** Whether this instance created the element (used to decide if `destroy()` removes it). */
755
+ private readonly created;
756
+ constructor(options: CookieTriggerOptions);
757
+ /**
758
+ * Returns the trigger element.
759
+ * When no `el` was provided, append the returned element to the desired location
760
+ * in the DOM after construction.
761
+ */
762
+ getElement(): HTMLElement;
763
+ /**
764
+ * Removes the click handler and, if this instance auto-created the button,
765
+ * removes it from the DOM.
766
+ */
767
+ destroy(): void;
768
+ }
769
+
770
+ export { type ApiConfig, type Button, type ButtonAction, type ButtonStyle, type Category, type ConsentAction, type ConsentEvent, type ConsentReceipt, type ConsentRecord, ConsentScript, type ConsentScriptOptions, type ConsentStatus, type ConsentValue, type ConsentiConfig, type ConsentiMessage, ConsentiPlugin, ConsentiProfile, ConsentiSetup, type ConsentiWidgetAPI, type Cookie, type CookieOptions, CookieTrigger, type CookieTriggerOptions, type CoreConfig, type GpcBanner, type GtmConfig, type LocaleTranslations, type MainBanner, type ParsedConsent, type PreferenceModal, type ProfileConfig, type ProfileTranslations, type ResolvedProfile, type ThemeConfig, type UtilsConfig };