@cookieyes/core 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 +99 -8
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +471 -36
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The integration format — the shared contract between the consent engine and
|
|
3
|
+
* every vendor preset (Google, Segment, Meta, …). It carries no vendor
|
|
4
|
+
* knowledge; a preset in `@cookieyes/scripts` fills it in.
|
|
5
|
+
*
|
|
6
|
+
* Two plain axes describe every vendor:
|
|
7
|
+
* - `load`: when the vendor starts — right away, or only after consent.
|
|
8
|
+
* - `onRevoke`: what happens when consent is withdrawn — one of three modes.
|
|
9
|
+
*
|
|
10
|
+
* `onRevoke` is declarative (readable without running `setup`, so a debug view
|
|
11
|
+
* or a version check can inspect it), and it's a discriminated union so the
|
|
12
|
+
* compiler forces the matching handler — you can't declare `"silence"` with no
|
|
13
|
+
* way to actually silence.
|
|
14
|
+
*/
|
|
15
|
+
type Cleanup = () => void;
|
|
16
|
+
type SilenceControl = {
|
|
17
|
+
silence: () => void;
|
|
18
|
+
resume: () => void;
|
|
19
|
+
};
|
|
20
|
+
/** What a vendor's `setup` receives from the engine. */
|
|
21
|
+
type SetupCtx = {
|
|
22
|
+
/** Is this integration's category currently committed-granted? */
|
|
23
|
+
granted: () => boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Subscribe to committed-consent changes; returns an unsubscribe function.
|
|
26
|
+
* `keep` vendors (e.g. Google Consent Mode) use this to emit their own
|
|
27
|
+
* updates on every change. The engine also releases these automatically when
|
|
28
|
+
* the integration is torn down, so a vendor listener never outlives it.
|
|
29
|
+
*/
|
|
30
|
+
onConsentChange: (fn: () => void) => () => void;
|
|
31
|
+
/** Resolved region/regulation — for a vendor's US-state privacy switch. */
|
|
32
|
+
region: RegionDecision;
|
|
33
|
+
};
|
|
34
|
+
type Base = {
|
|
35
|
+
id: string;
|
|
36
|
+
/**
|
|
37
|
+
* The consent category (or categories) that gate this integration. Pass an
|
|
38
|
+
* array to require more than one — combined with {@link Base.match}.
|
|
39
|
+
*/
|
|
40
|
+
category: ConsentCategory | ConsentCategory[];
|
|
41
|
+
/**
|
|
42
|
+
* How to combine multiple categories: `"all"` (default) needs every one
|
|
43
|
+
* granted; `"any"` needs at least one. Ignored for a single category.
|
|
44
|
+
*/
|
|
45
|
+
match?: "all" | "any";
|
|
46
|
+
/** Format version; an unknown version is refused (see {@link runIntegrations}). */
|
|
47
|
+
version: number;
|
|
48
|
+
/**
|
|
49
|
+
* When to start. Note: `setup` always runs on a microtask, so `immediately`
|
|
50
|
+
* is *very early* but not synchronous / same-tick. The truly-first-thing case
|
|
51
|
+
* (Consent Mode's deny-default before any tag fires) is the server `<head>`
|
|
52
|
+
* snippet's job, not the engine's.
|
|
53
|
+
*/
|
|
54
|
+
load: "immediately" | "afterConsent";
|
|
55
|
+
};
|
|
56
|
+
type Integration = Base & ({
|
|
57
|
+
onRevoke: "keep";
|
|
58
|
+
setup: (ctx: SetupCtx) => void | Promise<void>;
|
|
59
|
+
} | {
|
|
60
|
+
onRevoke: "remove";
|
|
61
|
+
setup: (ctx: SetupCtx) => Cleanup | Promise<Cleanup>;
|
|
62
|
+
} | {
|
|
63
|
+
onRevoke: "silence";
|
|
64
|
+
setup: (ctx: SetupCtx) => SilenceControl | Promise<SilenceControl>;
|
|
65
|
+
});
|
|
66
|
+
/** The format version this engine understands. Bump on a breaking format change. */
|
|
67
|
+
declare const INTEGRATION_FORMAT_VERSION = 1;
|
|
68
|
+
/**
|
|
69
|
+
* Warn when a vendor is configured on both sides — a new `integrations` preset
|
|
70
|
+
* whose id matches a deprecated `builtInIntegrations` vendor. Both would run,
|
|
71
|
+
* which for a tracker means double-counted events (the DEVP-38 double-pixel).
|
|
72
|
+
* Preset ids are the vendor's canonical name (`"segment"`, `"meta"`, …), which
|
|
73
|
+
* is exactly the built-in vendor name, so the match is a plain id === vendor.
|
|
74
|
+
*/
|
|
75
|
+
declare function warnOverlappingVendors(integrationIds: string[], builtInVendors: string[]): void;
|
|
76
|
+
/**
|
|
77
|
+
* Warn when an `afterConsent` integration is gated on a category that isn't in
|
|
78
|
+
* the configured taxonomy — otherwise it waits for consent that can never be
|
|
79
|
+
* granted and silently never loads. A common trap with a custom `categories`
|
|
80
|
+
* list plus a preset left on its default category (e.g. `segment()` → "analytics").
|
|
81
|
+
* `immediately` integrations aren't gated by category, so they're not checked.
|
|
82
|
+
*/
|
|
83
|
+
declare function warnUnknownCategories(integrations: Integration[], knownCategoryIds: string[]): void;
|
|
84
|
+
/** Live status of one integration — read by the debug/self-check view. */
|
|
85
|
+
type IntegrationStatus = "idle" | "loading" | "active" | "silenced" | "removed" | "error";
|
|
86
|
+
/** What the engine needs from the consent runtime — framework-agnostic. */
|
|
87
|
+
type IntegrationHost = {
|
|
88
|
+
/** Committed-granted state for a category (never the unsaved toggle). */
|
|
89
|
+
granted: (category: ConsentCategory) => boolean;
|
|
90
|
+
/** Subscribe to committed-consent changes; returns unsubscribe. */
|
|
91
|
+
subscribe: (fn: () => void) => () => void;
|
|
92
|
+
region: RegionDecision;
|
|
93
|
+
};
|
|
94
|
+
/** One row for a debug view: an integration's config plus its live status. */
|
|
95
|
+
type IntegrationDebugInfo = {
|
|
96
|
+
id: string;
|
|
97
|
+
category: ConsentCategory | ConsentCategory[];
|
|
98
|
+
load: "immediately" | "afterConsent";
|
|
99
|
+
onRevoke: "keep" | "remove" | "silence";
|
|
100
|
+
status: IntegrationStatus;
|
|
101
|
+
};
|
|
102
|
+
type IntegrationRunner = {
|
|
103
|
+
/** Current status per integration id — the debug/self-check view reads this. */
|
|
104
|
+
status: () => Record<string, IntegrationStatus>;
|
|
105
|
+
/**
|
|
106
|
+
* Config + live status for every registered integration, in order — the data
|
|
107
|
+
* for a debug view (e.g. `console.table(runner.list())`).
|
|
108
|
+
*/
|
|
109
|
+
list: () => IntegrationDebugInfo[];
|
|
110
|
+
/**
|
|
111
|
+
* Tear the runner down: stop reconciling, release every vendor's
|
|
112
|
+
* consent-change listener, and undo each loaded vendor — `remove` runs its
|
|
113
|
+
* cleanup, `silence` is silenced. `keep` vendors' scripts stay (that's the
|
|
114
|
+
* mode), but their listeners are still released. Safe to call more than once.
|
|
115
|
+
*/
|
|
116
|
+
stop: () => void;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Run a set of integrations against the consent runtime. Reconciles each one on
|
|
120
|
+
* load and on every committed-consent change:
|
|
121
|
+
* - not loaded → load it (immediately, or once its category is granted)
|
|
122
|
+
* - `keep` → nothing on revoke (the vendor manages its own update)
|
|
123
|
+
* - `remove` → run cleanup on revoke; re-load on re-grant
|
|
124
|
+
* - `silence` → call `silence()` on revoke; `resume()` on re-grant
|
|
125
|
+
*
|
|
126
|
+
* `setup` may be async and may fail; a failure marks the integration `error`
|
|
127
|
+
* and is retried on the next trigger (never loops on its own). Nothing here
|
|
128
|
+
* throws into the host.
|
|
129
|
+
*/
|
|
130
|
+
declare function runIntegrations(integrations: Integration[], host: IntegrationHost): IntegrationRunner;
|
|
131
|
+
|
|
1
132
|
type NetworkBlockerRule = {
|
|
2
133
|
id: string;
|
|
3
134
|
domain: string;
|
|
@@ -90,6 +221,11 @@ declare function _clearStopHandlers(): void;
|
|
|
90
221
|
*/
|
|
91
222
|
type ConsentCategory = "necessary" | "functional" | "analytics" | "performance" | "advertisement" | (string & {});
|
|
92
223
|
type Regulation = "GDPR" | "CCPA" | "DEFAULT";
|
|
224
|
+
/** Display text for one consent category. */
|
|
225
|
+
type CategoryText = {
|
|
226
|
+
label: string;
|
|
227
|
+
description: string;
|
|
228
|
+
};
|
|
93
229
|
type TranslationMap = {
|
|
94
230
|
bannerTitle: string;
|
|
95
231
|
bannerDescription: string;
|
|
@@ -104,27 +240,12 @@ type TranslationMap = {
|
|
|
104
240
|
preferencesTitle: string;
|
|
105
241
|
preferencesIntro: string;
|
|
106
242
|
categories: {
|
|
107
|
-
necessary:
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
description: string;
|
|
114
|
-
};
|
|
115
|
-
analytics: {
|
|
116
|
-
label: string;
|
|
117
|
-
description: string;
|
|
118
|
-
};
|
|
119
|
-
performance: {
|
|
120
|
-
label: string;
|
|
121
|
-
description: string;
|
|
122
|
-
};
|
|
123
|
-
advertisement: {
|
|
124
|
-
label: string;
|
|
125
|
-
description: string;
|
|
126
|
-
};
|
|
127
|
-
};
|
|
243
|
+
necessary: CategoryText;
|
|
244
|
+
functional: CategoryText;
|
|
245
|
+
analytics: CategoryText;
|
|
246
|
+
performance: CategoryText;
|
|
247
|
+
advertisement: CategoryText;
|
|
248
|
+
} & Record<string, CategoryText>;
|
|
128
249
|
optOut: {
|
|
129
250
|
title: string;
|
|
130
251
|
description: string;
|
|
@@ -138,6 +259,43 @@ type TranslationMap = {
|
|
|
138
259
|
dismissButton: string;
|
|
139
260
|
};
|
|
140
261
|
};
|
|
262
|
+
/** A subset of TranslationMap — lets a customer override just a few strings. */
|
|
263
|
+
type DeepPartial<T> = T extends object ? {
|
|
264
|
+
[K in keyof T]?: DeepPartial<T[K]>;
|
|
265
|
+
} : T;
|
|
266
|
+
type PartialTranslations = DeepPartial<TranslationMap>;
|
|
267
|
+
/** Reading direction of a language. */
|
|
268
|
+
type TextDirection = "ltr" | "rtl";
|
|
269
|
+
/** The active language, its reading direction, and the languages currently loaded. */
|
|
270
|
+
type LanguageInfo = {
|
|
271
|
+
language: string;
|
|
272
|
+
direction: TextDirection;
|
|
273
|
+
languages: string[];
|
|
274
|
+
};
|
|
275
|
+
/** Returns the visitor's region synchronously, e.g. "DE" or "US-CA" (or undefined). */
|
|
276
|
+
type RegionDetector = () => string | undefined;
|
|
277
|
+
/** Optional geo-detection: pick the banner's regulation from the visitor's region. */
|
|
278
|
+
type RegionConfig = {
|
|
279
|
+
/** Return the visitor's region synchronously — e.g. from a hosting header you read. */
|
|
280
|
+
detect?: RegionDetector | undefined;
|
|
281
|
+
/** Which regulation each region maps to (you own this). Matched most-specific first: "US-CA" then "US". */
|
|
282
|
+
map?: Record<string, Regulation> | undefined;
|
|
283
|
+
/** Honour the browser's GPC "do not sell/share" signal (a CCPA opt-out). Default `true`. */
|
|
284
|
+
honorGpc?: boolean | undefined;
|
|
285
|
+
/** Regulation to apply when the region is unknown or detection fails. Default `"GDPR"`. */
|
|
286
|
+
strictest?: Regulation | undefined;
|
|
287
|
+
/** Log the region decision to the console at setup (for local debugging). Default `false`. */
|
|
288
|
+
debug?: boolean | undefined;
|
|
289
|
+
};
|
|
290
|
+
/** How the active regulation was decided. */
|
|
291
|
+
type RegionSource = "manual" | "detected" | "strictest";
|
|
292
|
+
/** The outcome of geo-detection — the region seen and the regulation chosen. */
|
|
293
|
+
type RegionDecision = {
|
|
294
|
+
region: string | undefined;
|
|
295
|
+
regulation: Regulation;
|
|
296
|
+
source: RegionSource;
|
|
297
|
+
confidence: "high" | "low";
|
|
298
|
+
};
|
|
141
299
|
type ThemeConfig = {
|
|
142
300
|
primaryColor?: string | undefined;
|
|
143
301
|
backgroundColor?: string | undefined;
|
|
@@ -157,9 +315,16 @@ type ScriptEntry = {
|
|
|
157
315
|
onLoad?: (() => void) | undefined;
|
|
158
316
|
};
|
|
159
317
|
type I18nConfig = {
|
|
160
|
-
|
|
318
|
+
/** Translations per language. Each may be partial — missing text falls back to English. */
|
|
319
|
+
messages?: Record<string, PartialTranslations> | undefined;
|
|
161
320
|
locale?: string | undefined;
|
|
162
321
|
detectBrowserLanguage?: boolean | undefined;
|
|
322
|
+
/**
|
|
323
|
+
* Called when a language is switched to that isn't already in `messages` —
|
|
324
|
+
* return its translations (fetch them from your own URL, import them, etc.).
|
|
325
|
+
* Lets you load languages on demand instead of bundling them all upfront.
|
|
326
|
+
*/
|
|
327
|
+
loadLanguage?: ((tag: string) => PartialTranslations | Promise<PartialTranslations>) | undefined;
|
|
163
328
|
};
|
|
164
329
|
type ConsentConfig = {
|
|
165
330
|
apiUrl?: string | undefined;
|
|
@@ -176,6 +341,12 @@ type ConsentConfig = {
|
|
|
176
341
|
theme?: ThemeConfig | undefined;
|
|
177
342
|
colorScheme?: ColorScheme | undefined;
|
|
178
343
|
reloadOnRevoke?: boolean | undefined;
|
|
344
|
+
/**
|
|
345
|
+
* How to combine multiple categories that map to the same Google Consent Mode
|
|
346
|
+
* signal: `"any"` (default) grants the signal if any maps-and-granted; `"all"`
|
|
347
|
+
* requires every mapping category. Only affects custom overlapping mappings.
|
|
348
|
+
*/
|
|
349
|
+
googleConsentMatch?: "all" | "any" | undefined;
|
|
179
350
|
/**
|
|
180
351
|
* Built-in, first-party integrations to stop cleanly (no reload) when their
|
|
181
352
|
* category is revoked — e.g. `{ vendor: "meta" }`. (Google Analytics/Tag
|
|
@@ -190,6 +361,14 @@ type ConsentConfig = {
|
|
|
190
361
|
* shows the reload notice rather than silently continuing to track.
|
|
191
362
|
*/
|
|
192
363
|
customStopHandlers?: StopHandler[] | undefined;
|
|
364
|
+
/** Detected region (e.g. "US-CA"), recorded on the consent-log payload. */
|
|
365
|
+
region?: string | undefined;
|
|
366
|
+
/**
|
|
367
|
+
* Internal — set by the runtime when a CCPA visitor arrives with the browser's
|
|
368
|
+
* GPC "do not sell" signal on. Starts them opted out (non-required categories
|
|
369
|
+
* off) until they explicitly choose otherwise, so nothing is shared first.
|
|
370
|
+
*/
|
|
371
|
+
gpcOptOut?: boolean | undefined;
|
|
193
372
|
onConsentReady?: ((state: ConsentSnapshot) => void) | undefined;
|
|
194
373
|
onConsentUpdate?: ((state: ConsentSnapshot) => void) | undefined;
|
|
195
374
|
};
|
|
@@ -251,6 +430,8 @@ type ConsentPayload = {
|
|
|
251
430
|
categories: Record<string, boolean>;
|
|
252
431
|
regulation: Regulation;
|
|
253
432
|
domain: string;
|
|
433
|
+
/** Detected region when geo-detection is on (e.g. "US-CA"); omitted otherwise. */
|
|
434
|
+
region?: string | undefined;
|
|
254
435
|
};
|
|
255
436
|
/**
|
|
256
437
|
* Customer-implemented adapter that decides how a consent decision
|
|
@@ -284,6 +465,12 @@ type CookieYesConfigCommon = {
|
|
|
284
465
|
* nested `overrides.regulation`).
|
|
285
466
|
*/
|
|
286
467
|
regulation?: Regulation | undefined;
|
|
468
|
+
/**
|
|
469
|
+
* Optional geo-detection: choose the regulation from the visitor's region.
|
|
470
|
+
* Fully optional — omit it and nothing changes. A manual `regulation` (above)
|
|
471
|
+
* always wins over detection. See {@link RegionConfig}.
|
|
472
|
+
*/
|
|
473
|
+
region?: RegionConfig | undefined;
|
|
287
474
|
colorScheme?: ColorScheme | undefined;
|
|
288
475
|
theme?: ThemeConfig | undefined;
|
|
289
476
|
i18n?: I18nConfig | undefined;
|
|
@@ -298,12 +485,29 @@ type CookieYesConfigCommon = {
|
|
|
298
485
|
networkBlocker?: NetworkBlockerConfig | undefined;
|
|
299
486
|
reloadOnRevoke?: boolean | undefined;
|
|
300
487
|
/**
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
* needed.) Integrations with no clean runtime stop fall back to the reload notice.
|
|
488
|
+
* How to combine multiple categories that map to the same Google Consent Mode
|
|
489
|
+
* signal: `"any"` (default) or `"all"`. Only matters for a custom taxonomy
|
|
490
|
+
* where more than one category maps to the same signal.
|
|
305
491
|
*/
|
|
306
|
-
|
|
492
|
+
googleConsentMatch?: "all" | "any" | undefined;
|
|
493
|
+
/**
|
|
494
|
+
* Ready-made third-party integrations to gate behind consent — Segment, Meta,
|
|
495
|
+
* Google, and more — using a preset from `@cookieyes/scripts`. Each preset
|
|
496
|
+
* returns an {@link Integration}: it loads only once its category is granted
|
|
497
|
+
* (or, for Google Consent Mode, loads immediately and denies by default), and
|
|
498
|
+
* is removed or silenced on withdrawal.
|
|
499
|
+
*
|
|
500
|
+
* @example integrations: [segment({ writeKey: "..." })]
|
|
501
|
+
*/
|
|
502
|
+
integrations?: Integration[] | undefined;
|
|
503
|
+
/**
|
|
504
|
+
* @deprecated Renamed from `integrations`. Built-in stop-handlers for a few
|
|
505
|
+
* first-party vendors — e.g. `{ vendor: "meta" }` — stopped cleanly (no
|
|
506
|
+
* reload) when their category is revoked. Prefer the new `integrations` field
|
|
507
|
+
* with a preset from `@cookieyes/scripts`; this will be removed in a future
|
|
508
|
+
* release.
|
|
509
|
+
*/
|
|
510
|
+
builtInIntegrations?: BuiltInIntegration[] | undefined;
|
|
307
511
|
/**
|
|
308
512
|
* Your own scripts' stop instructions, for anything without a built-in
|
|
309
513
|
* integration. A handler that can stop cleanly provides `stop()`; one that
|
|
@@ -369,6 +573,24 @@ type ConsentChangePayload = {
|
|
|
369
573
|
allowedCategories: ConsentCategory[];
|
|
370
574
|
deniedCategories: ConsentCategory[];
|
|
371
575
|
};
|
|
576
|
+
/** Which consent event to listen for. See {@link ConsentStore.on}. */
|
|
577
|
+
type ConsentEventType = "save" | "change";
|
|
578
|
+
type ConsentEventPayload = {
|
|
579
|
+
/** The full committed consent map in effect when the event fired. */
|
|
580
|
+
categories: Record<string, boolean>;
|
|
581
|
+
/** Categories whose value differed from before. Empty on the initial replay. */
|
|
582
|
+
changedCategories: ConsentCategory[];
|
|
583
|
+
/**
|
|
584
|
+
* `true` when this is the one-off replay a listener gets on attach (here's
|
|
585
|
+
* the current state), `false` when the visitor actually just acted.
|
|
586
|
+
*/
|
|
587
|
+
isInitial: boolean;
|
|
588
|
+
};
|
|
589
|
+
type ConsentEventListener = (payload: ConsentEventPayload) => void;
|
|
590
|
+
/** Restrict a listener to a single category (fires only when it changes). */
|
|
591
|
+
type ConsentEventOptions = {
|
|
592
|
+
category?: ConsentCategory;
|
|
593
|
+
};
|
|
372
594
|
type ActiveUI = "banner" | "dialog" | null;
|
|
373
595
|
type ConsentStoreState = ConsentSnapshot & {
|
|
374
596
|
activeUI: ActiveUI;
|
|
@@ -395,10 +617,40 @@ type ConsentStoreState = ConsentSnapshot & {
|
|
|
395
617
|
type ConsentStore = {
|
|
396
618
|
subscribe: (listener: (state: ConsentStoreState) => void) => () => void;
|
|
397
619
|
getState: () => ConsentStoreState;
|
|
620
|
+
/** Text for the active language (English fills gaps). Swaps on `setLanguage`. */
|
|
621
|
+
translations: TranslationMap;
|
|
622
|
+
/** The active language, its reading direction, and the languages loaded. */
|
|
623
|
+
getLanguageInfo: () => LanguageInfo;
|
|
624
|
+
/**
|
|
625
|
+
* Switch language live (no reload) — `subscribe` listeners fire so a custom UI
|
|
626
|
+
* can re-render. Loads the language via `i18n.loadLanguage` if not bundled.
|
|
627
|
+
*/
|
|
628
|
+
setLanguage: (tag: string) => Promise<void>;
|
|
629
|
+
/** Customer-provided text for a category in the active language, if any. */
|
|
630
|
+
getCategoryText: (id: string) => Partial<CategoryText> | undefined;
|
|
631
|
+
/**
|
|
632
|
+
* The category taxonomy in effect (custom list or the built-in five) — its
|
|
633
|
+
* ids, which are `required`, etc. Use it to render categories in a custom UI
|
|
634
|
+
* so it follows whatever taxonomy is configured.
|
|
635
|
+
*/
|
|
636
|
+
categories: ResolvedCategories;
|
|
637
|
+
/** How the active regulation was decided (region, source, confidence). */
|
|
638
|
+
getRegion: () => RegionDecision;
|
|
639
|
+
/**
|
|
640
|
+
* React to consent decisions. `"save"` fires on every save (even an
|
|
641
|
+
* unchanged re-confirm); `"change"` fires only when a category actually
|
|
642
|
+
* differs — use it to (re)load a script without re-running on a re-confirm.
|
|
643
|
+
* The listener fires once immediately with the current state
|
|
644
|
+
* (`isInitial: true`). Pass `{ category }` to only hear about one category.
|
|
645
|
+
* Returns an unsubscribe function.
|
|
646
|
+
*/
|
|
647
|
+
on: (type: ConsentEventType, listener: ConsentEventListener, options?: ConsentEventOptions) => () => void;
|
|
398
648
|
};
|
|
399
649
|
type ConsentRuntime = {
|
|
400
650
|
consentManager: ConsentManager;
|
|
401
651
|
consentStore: ConsentStore;
|
|
652
|
+
/** Config + live status for each script integration — data for a debug view. */
|
|
653
|
+
getIntegrations: () => IntegrationDebugInfo[];
|
|
402
654
|
};
|
|
403
655
|
|
|
404
656
|
/**
|
|
@@ -465,6 +717,7 @@ declare function resolveCategories(defs?: CategoryDef[]): ResolvedCategories;
|
|
|
465
717
|
type _NormalizedConfig = {
|
|
466
718
|
mode: ConsentRuntimeMode;
|
|
467
719
|
regulation?: Regulation | undefined;
|
|
720
|
+
region?: RegionConfig | undefined;
|
|
468
721
|
colorScheme?: ColorScheme | undefined;
|
|
469
722
|
theme?: ThemeConfig | undefined;
|
|
470
723
|
i18n?: I18nConfig | undefined;
|
|
@@ -472,7 +725,10 @@ type _NormalizedConfig = {
|
|
|
472
725
|
categories?: CategoryDef[] | undefined;
|
|
473
726
|
networkBlocker?: NetworkBlockerConfig | undefined;
|
|
474
727
|
reloadOnRevoke?: boolean | undefined;
|
|
475
|
-
|
|
728
|
+
googleConsentMatch?: "all" | "any" | undefined;
|
|
729
|
+
integrations?: Integration[] | undefined;
|
|
730
|
+
/** @deprecated Renamed from `integrations`; use `integrations` with a `@cookieyes/scripts` preset. */
|
|
731
|
+
builtInIntegrations?: BuiltInIntegration[] | undefined;
|
|
476
732
|
customStopHandlers?: StopHandler[] | undefined;
|
|
477
733
|
onConsentReady?: ((state: ConsentSnapshot) => void) | undefined;
|
|
478
734
|
onConsentUpdate?: ((state: ConsentSnapshot) => void) | undefined;
|
|
@@ -506,6 +762,15 @@ type RawCookieFields = {
|
|
|
506
762
|
};
|
|
507
763
|
declare function parseCookie(raw: string): RawCookieFields;
|
|
508
764
|
declare function serializeCookie(snapshot: ConsentSnapshot): string;
|
|
765
|
+
/**
|
|
766
|
+
* Find and parse the consent cookie inside a `name=value; name2=value2` string.
|
|
767
|
+
*
|
|
768
|
+
* Shared by the browser (`document.cookie`) and the server (a request's `Cookie`
|
|
769
|
+
* header) — the two formats are identical, and one implementation means the
|
|
770
|
+
* server can never disagree with the client about what a visitor's cookie says.
|
|
771
|
+
* Returns `null` when the cookie is absent or its value cannot be decoded.
|
|
772
|
+
*/
|
|
773
|
+
declare function parseCookieHeader(header: string): RawCookieFields | null;
|
|
509
774
|
declare function generateConsentId(): string;
|
|
510
775
|
|
|
511
776
|
/**
|
|
@@ -516,18 +781,48 @@ declare function generateConsentId(): string;
|
|
|
516
781
|
declare function _warnOfflineModeDeprecated(): void;
|
|
517
782
|
/** @internal test-only — resets the one-time warning guard between test cases. */
|
|
518
783
|
declare function _resetOfflineModeWarning(): void;
|
|
784
|
+
/**
|
|
785
|
+
* One-time-per-page-load console warning for the deprecated `builtInIntegrations`
|
|
786
|
+
* config field (formerly `integrations`). Both packages call this so the wording
|
|
787
|
+
* and the "once" behavior stay identical.
|
|
788
|
+
*/
|
|
789
|
+
declare function _warnBuiltInIntegrationsDeprecated(): void;
|
|
790
|
+
/** @internal test-only — resets the one-time warning guard between test cases. */
|
|
791
|
+
declare function _resetBuiltInIntegrationsWarning(): void;
|
|
792
|
+
|
|
793
|
+
type ConsentEmitter = {
|
|
794
|
+
/**
|
|
795
|
+
* Listen for consent events. `"save"` fires on every saved decision (even an
|
|
796
|
+
* unchanged re-confirm); `"change"` fires only when a category actually
|
|
797
|
+
* differs. The listener fires once immediately with the current state
|
|
798
|
+
* (`isInitial: true`) so a late listener isn't blind to earlier choices.
|
|
799
|
+
* Pass `{ category }` to only be called when that one category changes.
|
|
800
|
+
* Returns an unsubscribe function.
|
|
801
|
+
*/
|
|
802
|
+
on: (type: ConsentEventType, listener: ConsentEventListener, options?: ConsentEventOptions) => () => void;
|
|
803
|
+
/** Feed in the committed categories after a save; the emitter fans out events. */
|
|
804
|
+
push: (categories: Record<string, boolean>) => void;
|
|
805
|
+
};
|
|
806
|
+
/**
|
|
807
|
+
* The consent event fan-out, shared by the core and React runtimes so both
|
|
808
|
+
* behave identically. `getCommitted` returns the consent currently in effect,
|
|
809
|
+
* used for the immediate replay a new listener receives.
|
|
810
|
+
*/
|
|
811
|
+
declare function createConsentEmitter(getCommitted: () => Record<string, boolean>): ConsentEmitter;
|
|
519
812
|
|
|
520
813
|
type GcmValue = "granted" | "denied";
|
|
521
814
|
/**
|
|
522
815
|
* Compute the granted/denied value for every GCM signal from the current
|
|
523
816
|
* category consent, using each category's `gcm` mapping.
|
|
524
817
|
*
|
|
525
|
-
* -
|
|
526
|
-
*
|
|
527
|
-
*
|
|
528
|
-
*
|
|
818
|
+
* - When several categories map to the same signal, `match` decides: `"any"`
|
|
819
|
+
* (default) grants it if *any* mapping category is granted; `"all"` requires
|
|
820
|
+
* *every* mapping category to be granted. For the built-in five (one category
|
|
821
|
+
* per signal) the two are identical — `match` only matters for custom overlaps.
|
|
822
|
+
* - `security_storage` is always `granted` (strictly necessary, not consentable).
|
|
823
|
+
* - A signal that no category maps to stays `denied`.
|
|
529
824
|
*/
|
|
530
|
-
declare function computeGoogleConsent(resolved: ResolvedCategories, categories: Record<string, boolean
|
|
825
|
+
declare function computeGoogleConsent(resolved: ResolvedCategories, categories: Record<string, boolean>, match?: "all" | "any"): Record<GoogleConsentSignal, GcmValue>;
|
|
531
826
|
/**
|
|
532
827
|
* Push a Consent Mode `update` for all seven signals onto the dataLayer, if one
|
|
533
828
|
* is present. Safe to call on load and on every consent change; a no-op when no
|
|
@@ -537,14 +832,83 @@ declare function computeGoogleConsent(resolved: ResolvedCategories, categories:
|
|
|
537
832
|
* that must run before their Google tags, typically denying everything). This
|
|
538
833
|
* function owns the *update* that reflects the visitor's actual choice.
|
|
539
834
|
*/
|
|
540
|
-
declare function broadcastGoogleConsent(resolved: ResolvedCategories, categories: Record<string, boolean
|
|
835
|
+
declare function broadcastGoogleConsent(resolved: ResolvedCategories, categories: Record<string, boolean>, match?: "all" | "any"): void;
|
|
541
836
|
|
|
542
837
|
declare const en: TranslationMap;
|
|
543
838
|
|
|
839
|
+
/** The base subtag of a language tag, lowercased: "en-GB" → "en". */
|
|
840
|
+
declare function primaryOf(tag: string): string;
|
|
841
|
+
/** Reading direction for a language tag, e.g. "ar" or "ar-EG" → "rtl". */
|
|
842
|
+
declare function getTextDirection(tag: string): TextDirection;
|
|
843
|
+
/** Deep-merge a (possibly partial) override onto a complete base map. */
|
|
844
|
+
declare function mergeTranslations(base: TranslationMap, override?: PartialTranslations): TranslationMap;
|
|
845
|
+
/**
|
|
846
|
+
* The language to start in, resolved in order: explicit `locale`, then the
|
|
847
|
+
* browser's language, then English. Only returns one we actually have text for
|
|
848
|
+
* (others can be brought in later via `loadLanguage`).
|
|
849
|
+
*/
|
|
850
|
+
declare function pickLanguage(i18n?: I18nConfig): string;
|
|
851
|
+
/** Full translations for the resolved starting language, English filling any gaps. */
|
|
544
852
|
declare function resolveTranslations(i18n?: I18nConfig): TranslationMap;
|
|
545
853
|
|
|
854
|
+
type LanguageController = {
|
|
855
|
+
/** Text for the active language (English fills any gaps). */
|
|
856
|
+
getTranslations: () => TranslationMap;
|
|
857
|
+
getLanguageInfo: () => LanguageInfo;
|
|
858
|
+
/** Switch language live; loads via `i18n.loadLanguage` if not already present. */
|
|
859
|
+
setLanguage: (tag: string) => Promise<void>;
|
|
860
|
+
/**
|
|
861
|
+
* The customer's own text for a category in the *active* language, if they
|
|
862
|
+
* provided it — kept separate from the English defaults so a translation can
|
|
863
|
+
* win over a category's config label without the English default masking it.
|
|
864
|
+
*/
|
|
865
|
+
getCategoryText: (id: string) => Partial<CategoryText> | undefined;
|
|
866
|
+
};
|
|
867
|
+
/**
|
|
868
|
+
* Owns the active language: which one is showing, its (English-filled) text,
|
|
869
|
+
* and switching to another — loading it on demand when a loader is provided.
|
|
870
|
+
* `onChange` runs after every switch so the UI can re-render.
|
|
871
|
+
*
|
|
872
|
+
* Framework-agnostic: used by both the core and React runtimes, so they behave
|
|
873
|
+
* identically.
|
|
874
|
+
*/
|
|
875
|
+
declare function createLanguageController(i18n: I18nConfig | undefined, onChange: () => void): LanguageController;
|
|
876
|
+
|
|
546
877
|
declare function createConsentManager(config: ConsentConfig): ConsentManager;
|
|
547
878
|
|
|
879
|
+
/** Anything with a header getter — a `Headers` object, Next's `headers()`, etc. */
|
|
880
|
+
type HeaderSource = {
|
|
881
|
+
get(name: string): string | null | undefined;
|
|
882
|
+
};
|
|
883
|
+
/**
|
|
884
|
+
* Read the visitor's region from request headers on the server (Next.js, or any
|
|
885
|
+
* framework). Pass the request's headers and get back a region like "US-CA" or
|
|
886
|
+
* "DE" (or undefined). By default it reads the well-known Vercel/Cloudflare
|
|
887
|
+
* headers; pass `{ header }` to read your own instead. Hand the result to
|
|
888
|
+
* `region.detect` in your client config.
|
|
889
|
+
*/
|
|
890
|
+
declare function regionFromHeaders(headers: HeaderSource, options?: {
|
|
891
|
+
header?: string;
|
|
892
|
+
}): string | undefined;
|
|
893
|
+
/** True when the browser is sending the GPC "do not sell/share" signal. */
|
|
894
|
+
declare function readGpc(): boolean;
|
|
895
|
+
/**
|
|
896
|
+
* Decide which regulation applies from the visitor's region alone. A manual
|
|
897
|
+
* regulation always wins; otherwise the detected region is mapped to a
|
|
898
|
+
* regulation, and anything unknown falls back to the strictest — never to the
|
|
899
|
+
* lightest, so a required banner is never skipped.
|
|
900
|
+
*
|
|
901
|
+
* GPC is deliberately *not* considered here: it never changes which banner
|
|
902
|
+
* shows (that is geo only), it only opts a CCPA visitor out client-side. Server
|
|
903
|
+
* and client therefore resolve the same regulation, with no hydration mismatch.
|
|
904
|
+
*/
|
|
905
|
+
declare function resolveRegion(config: RegionConfig, manual?: Regulation): RegionDecision;
|
|
906
|
+
/**
|
|
907
|
+
* @internal Dev aid for `region.debug`: print how the regulation was decided,
|
|
908
|
+
* plus whether GPC started the visitor opted out. Shared by both runtimes.
|
|
909
|
+
*/
|
|
910
|
+
declare function _logRegionDecision(decision: RegionDecision, gpcOptOut: boolean): void;
|
|
911
|
+
|
|
548
912
|
declare function getOrCreateConsentRuntime(config: CookieYesConfig): ConsentRuntime;
|
|
549
913
|
/**
|
|
550
914
|
* Canonical setup entry point. Alias of {@link getOrCreateConsentRuntime} that
|
|
@@ -555,5 +919,76 @@ declare function getOrCreateConsentRuntime(config: CookieYesConfig): ConsentRunt
|
|
|
555
919
|
declare function initCookieYes(config: CookieYesConfig): ConsentRuntime;
|
|
556
920
|
declare function resetConsentRuntime(): void;
|
|
557
921
|
|
|
558
|
-
|
|
559
|
-
|
|
922
|
+
/**
|
|
923
|
+
* @internal Test-only — empty the script registry and forget what was injected.
|
|
924
|
+
* Mirrors {@link _clearStopHandlers}. When a `document` is present the injected
|
|
925
|
+
* `<script>` elements are removed from it too, so one test can never leave a
|
|
926
|
+
* gated script behind for the next one. Safe to call with nothing registered.
|
|
927
|
+
*/
|
|
928
|
+
declare function _clearScriptRegistry(): void;
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* The subset of your consent config that affects reading a stored decision.
|
|
932
|
+
* `CookieYesConfig` satisfies this structurally, so you can pass the same object
|
|
933
|
+
* you give `initCookieYes`.
|
|
934
|
+
*/
|
|
935
|
+
type ServerConsentOptions = {
|
|
936
|
+
regulation?: Regulation | undefined;
|
|
937
|
+
categories?: CategoryDef[] | undefined;
|
|
938
|
+
};
|
|
939
|
+
/**
|
|
940
|
+
* Read a visitor's already-made consent decision from a request's `Cookie`
|
|
941
|
+
* header, on the server, with no `document` and no browser APIs.
|
|
942
|
+
*
|
|
943
|
+
* Use it to keep the banner out of the HTML entirely for a returning visitor.
|
|
944
|
+
* Without it the server has no idea whether the visitor has chosen, so it sends
|
|
945
|
+
* banner markup to everyone and the client removes it after hydration — the
|
|
946
|
+
* banner visibly appears and then vanishes, which reads as a bug.
|
|
947
|
+
*
|
|
948
|
+
* Returns `null` whenever the banner *should* be shown:
|
|
949
|
+
* - no consent cookie (a first-time visitor),
|
|
950
|
+
* - a cookie that records no decision yet (`action:no`, e.g. a CCPA visitor who
|
|
951
|
+
* has an implicit-consent cookie but has not acted),
|
|
952
|
+
* - a corrupt cookie,
|
|
953
|
+
* - a cookie written against a **different category taxonomy**, which the client
|
|
954
|
+
* also treats as stale and re-requests. The one exception mirrors the client
|
|
955
|
+
* exactly: a legacy cookie with no taxonomy stamp is still honoured when the
|
|
956
|
+
* built-in five categories are in effect, so existing visitors are not
|
|
957
|
+
* re-prompted by an upgrade.
|
|
958
|
+
*
|
|
959
|
+
* Otherwise returns the stored snapshot, ready to hand to `CookieYesProvider`'s
|
|
960
|
+
* `initialConsent`.
|
|
961
|
+
*
|
|
962
|
+
* ```ts
|
|
963
|
+
* // Any SSR framework — pass the request's Cookie header:
|
|
964
|
+
* const initialConsent = readServerConsent(request.headers.get("cookie") ?? "", config);
|
|
965
|
+
* ```
|
|
966
|
+
*
|
|
967
|
+
* In Next.js App Router, prefer `getServerConsent(config)` from
|
|
968
|
+
* `@cookieyes/nextjs`, which reads `cookies()` for you.
|
|
969
|
+
*
|
|
970
|
+
* **Never** put the result on `initCookieYes` or the runtime: the runtime is a
|
|
971
|
+
* module-level singleton shared across concurrent requests, so per-visitor state
|
|
972
|
+
* there would leak between them. It belongs in the component tree.
|
|
973
|
+
*/
|
|
974
|
+
declare function readServerConsent(cookieHeader: string, options?: ServerConsentOptions): ConsentSnapshot | null;
|
|
975
|
+
|
|
976
|
+
/**
|
|
977
|
+
* The version of `@cookieyes/core` this build was produced from.
|
|
978
|
+
*
|
|
979
|
+
* The literal below is a **sentinel, replaced at build time** with the real
|
|
980
|
+
* version from `package.json` (see the `injectPkgVersion` plugin in
|
|
981
|
+
* `rollup.shared.mjs`). It is done that way rather than hand-maintained because
|
|
982
|
+
* Changesets bumps `package.json` on the release PR — a constant someone has to
|
|
983
|
+
* remember to update would go stale on exactly the commit that matters, and a
|
|
984
|
+
* test guarding it would block an automated release PR instead.
|
|
985
|
+
*
|
|
986
|
+
* Reading this from source (this repo's own tests, or a `workspace:*` link)
|
|
987
|
+
* leaves the sentinel in place. Treat `0.0.0-dev` as "unknown version", never as
|
|
988
|
+
* a real one — `@cookieyes/test` does exactly that before deciding whether to
|
|
989
|
+
* warn about a mismatched pair.
|
|
990
|
+
*/
|
|
991
|
+
declare const CORE_VERSION = "0.0.0-dev";
|
|
992
|
+
|
|
993
|
+
export { CORE_VERSION, DEFAULT_CATEGORIES, INTEGRATION_FORMAT_VERSION, _clearScriptRegistry, _clearStopHandlers, _logRegionDecision, _normalizeConfig, _resetBuiltInIntegrationsWarning, _resetOfflineModeWarning, _warnBuiltInIntegrationsDeprecated, _warnOfflineModeDeprecated, broadcastGoogleConsent, computeGoogleConsent, createConsentEmitter, createConsentManager, createLanguageController, en as defaultTranslations, generateConsentId, getOrCreateConsentRuntime, getTextDirection, initCookieYes, installNetworkBlocker, mergeTranslations, parseCookie, parseCookieHeader, pickLanguage, primaryOf, readGpc, readServerConsent, regionFromHeaders, registerStopHandler, resetConsentRuntime, resolveBuiltInIntegration, resolveCategories, resolveRegion, resolveTranslations, runIntegrations, serializeCookie, uninstallNetworkBlocker, warnOverlappingVendors, warnUnknownCategories };
|
|
994
|
+
export type { ActiveUI, AnyStopHandler, BlockedRequestInfo, BuiltInIntegration, CategoryDef, CategoryText, Cleanup, ColorScheme, ConsentBackend, ConsentCategory, ConsentChangePayload, ConsentConfig, ConsentEmitter, ConsentEventListener, ConsentEventOptions, ConsentEventPayload, ConsentEventType, ConsentManager, ConsentPayload, ConsentRuntime, ConsentRuntimeMode, ConsentRuntimeOptions, ConsentSnapshot, ConsentStore, ConsentStoreState, CookieYesConfig, CookieYesOfflineConfig, CookieYesSelfHostedConfig, GoogleConsentSignal, HeaderSource, I18nConfig, Integration, IntegrationDebugInfo, IntegrationHost, IntegrationRunner, IntegrationStatus, LanguageController, LanguageInfo, NetworkBlockerConfig, NetworkBlockerRule, PartialTranslations, RegionConfig, RegionDecision, RegionDetector, RegionSource, Regulation, ReloadNoticeState, ReloadOnlyHandler, ResolvedCategories, ScriptEntry, ServerConsentOptions, SetupCtx, SilenceControl, StopHandler, TextDirection, ThemeConfig, TranslationMap, _NormalizedConfig };
|