@owlmeans/consent 0.1.18-rc.9 → 0.1.18

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.
Files changed (115) hide show
  1. package/README.md +229 -0
  2. package/build/consts.d.ts +78 -3
  3. package/build/consts.d.ts.map +1 -1
  4. package/build/consts.js +104 -0
  5. package/build/consts.js.map +1 -1
  6. package/build/consts.local.d.ts +12 -0
  7. package/build/consts.local.d.ts.map +1 -0
  8. package/build/consts.local.js +12 -0
  9. package/build/consts.local.js.map +1 -0
  10. package/build/geo/types.d.ts +47 -0
  11. package/build/geo/types.d.ts.map +1 -0
  12. package/build/geo/types.js +2 -0
  13. package/build/geo/types.js.map +1 -0
  14. package/build/geo.d.ts +4 -0
  15. package/build/geo.d.ts.map +1 -0
  16. package/build/geo.js +96 -0
  17. package/build/geo.js.map +1 -0
  18. package/build/gtobserver/types.d.ts +75 -0
  19. package/build/gtobserver/types.d.ts.map +1 -0
  20. package/build/gtobserver/types.js +2 -0
  21. package/build/gtobserver/types.js.map +1 -0
  22. package/build/gtobserver.d.ts +7 -0
  23. package/build/gtobserver.d.ts.map +1 -0
  24. package/build/gtobserver.js +142 -0
  25. package/build/gtobserver.js.map +1 -0
  26. package/build/i18n/be.json +8 -1
  27. package/build/i18n/de.json +8 -1
  28. package/build/i18n/en.json +8 -1
  29. package/build/i18n/es.json +8 -1
  30. package/build/i18n/fr.json +8 -1
  31. package/build/i18n/pl.json +8 -1
  32. package/build/i18n/ru.json +8 -1
  33. package/build/i18n/types.d.ts +11 -0
  34. package/build/i18n/types.d.ts.map +1 -0
  35. package/build/i18n/types.js +2 -0
  36. package/build/i18n/types.js.map +1 -0
  37. package/build/i18n/uk.json +8 -1
  38. package/build/i18n.d.ts +4 -14
  39. package/build/i18n.d.ts.map +1 -1
  40. package/build/i18n.js +12 -27
  41. package/build/i18n.js.map +1 -1
  42. package/build/index.d.ts +13 -5
  43. package/build/index.d.ts.map +1 -1
  44. package/build/index.js +5 -3
  45. package/build/index.js.map +1 -1
  46. package/build/linker/types.d.ts +59 -0
  47. package/build/linker/types.d.ts.map +1 -0
  48. package/build/linker/types.js +2 -0
  49. package/build/linker/types.js.map +1 -0
  50. package/build/linker.d.ts +5 -74
  51. package/build/linker.d.ts.map +1 -1
  52. package/build/linker.js +290 -324
  53. package/build/linker.js.map +1 -1
  54. package/build/plugins/types.d.ts +42 -0
  55. package/build/plugins/types.d.ts.map +1 -0
  56. package/build/plugins/types.js +2 -0
  57. package/build/plugins/types.js.map +1 -0
  58. package/build/plugins.d.ts +4 -68
  59. package/build/plugins.d.ts.map +1 -1
  60. package/build/plugins.js +73 -61
  61. package/build/plugins.js.map +1 -1
  62. package/build/storage/types.d.ts +31 -0
  63. package/build/storage/types.d.ts.map +1 -0
  64. package/build/storage/types.js +2 -0
  65. package/build/storage/types.js.map +1 -0
  66. package/build/storage.d.ts +6 -22
  67. package/build/storage.d.ts.map +1 -1
  68. package/build/storage.js +72 -81
  69. package/build/storage.js.map +1 -1
  70. package/build/store.d.ts +2 -2
  71. package/build/store.d.ts.map +1 -1
  72. package/build/store.js +115 -31
  73. package/build/store.js.map +1 -1
  74. package/build/types.d.ts +141 -5
  75. package/build/types.d.ts.map +1 -1
  76. package/build/types.local.d.ts +9 -0
  77. package/build/types.local.d.ts.map +1 -0
  78. package/build/types.local.js +2 -0
  79. package/build/types.local.js.map +1 -0
  80. package/package.json +3 -3
  81. package/src/consts.local.ts +16 -0
  82. package/src/consts.ts +120 -3
  83. package/src/geo/types.ts +49 -0
  84. package/src/geo.ts +122 -0
  85. package/src/gtobserver/types.ts +73 -0
  86. package/src/gtobserver.ts +182 -0
  87. package/src/i18n/be.json +8 -1
  88. package/src/i18n/de.json +8 -1
  89. package/src/i18n/en.json +8 -1
  90. package/src/i18n/es.json +8 -1
  91. package/src/i18n/fr.json +8 -1
  92. package/src/i18n/pl.json +8 -1
  93. package/src/i18n/ru.json +8 -1
  94. package/src/i18n/types.ts +11 -0
  95. package/src/i18n/uk.json +8 -1
  96. package/src/i18n.ts +20 -32
  97. package/src/index.ts +13 -12
  98. package/src/linker/types.ts +59 -0
  99. package/src/linker.ts +301 -349
  100. package/src/plugins/types.ts +42 -0
  101. package/src/plugins.ts +82 -105
  102. package/src/storage/types.ts +31 -0
  103. package/src/storage.ts +75 -77
  104. package/src/store.ts +125 -31
  105. package/src/types.local.ts +9 -0
  106. package/src/types.ts +148 -5
  107. package/tests/consent.spec.ts +26 -28
  108. package/tests/geo.spec.ts +474 -0
  109. package/tests/linker.spec.ts +68 -74
  110. package/tests/tracking.spec.ts +14 -14
  111. package/build/gtm.d.ts +0 -72
  112. package/build/gtm.d.ts.map +0 -1
  113. package/build/gtm.js +0 -182
  114. package/build/gtm.js.map +0 -1
  115. package/src/gtm.ts +0 -220
package/README.md ADDED
@@ -0,0 +1,229 @@
1
+ # @owlmeans/consent
2
+
3
+ The framework-free core of OwlMeans cookie consent: the category model, dual localStorage/cookie
4
+ storage with in-place migration, the observable document-level `consentStore`, Google Consent Mode
5
+ v2 signalling with the inline head scripts that must run before a tag manager loads, the built-in
6
+ dialog copy in eight languages, and cross-domain consent through decorated links. It has **zero
7
+ runtime dependencies**, so an Astro island or a plain script can load it. An app uses it directly
8
+ to stamp the head snippet, read a decision outside React, or gate a loader. It renders nothing — the
9
+ dialog, the re-open button and the cookie-policy page are `@owlmeans/web-consent` (or
10
+ `@owlmeans/web-panel/consent` bound to OwlMeans i18n), and the Google tag snippet itself is
11
+ `@owlmeans/web-gtm`. Per-person marketing preferences on the server are `@owlmeans/marketing-consent`,
12
+ not this package.
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ bun add @owlmeans/consent@^0.1.18
18
+ ```
19
+
20
+ ## Concepts
21
+
22
+ - **Category** — a `ConsentCategory`: a stable `key`, label/description keys, an optional
23
+ `globalVar` written on `window`, the Consent Mode `signals` it drives and an optional `dataLayer`
24
+ `event`. The defaults are `essential` (required), `analytics` and `marketing`.
25
+ - **Required category** — always granted and rendered locked: disclosure, not a question.
26
+ - **Record** — a `ConsentRecord` `{ v?, [categoryKey]: boolean }`, stored under
27
+ `site_cookie_consent` in both localStorage and a 365-day `SameSite=Lax` cookie, localStorage read
28
+ first. A record without `v` is migrated in place (`essential: true`, `v: 2`), never discarded.
29
+ - **Store** — `consentStore`, a module singleton: consent belongs to the document, not to a
30
+ component tree, and is reachable from click handlers and non-React code.
31
+ - **Ordering rule** — `consent/default` must be on `window.dataLayer` before the tag manager loads,
32
+ so it is pushed by an inline script stamped above the snippet, not by a bundle.
33
+ - **Plugin** — a `ConsentPlugin` that can `adopt` a decision or a language from outside storage,
34
+ `decorate` outgoing URLs and disclose `domains`. `consentLinkHelper.consentLinker()` is the one
35
+ built-in plugin.
36
+ - **Interface language** — strictly necessary storage (`owlmeans-lng`); no category governs it.
37
+
38
+ ## Usage
39
+
40
+ ### Start the store and read the decision
41
+
42
+ ```typescript
43
+ import { consentStore, CONSENT_ANALYTICS } from '@owlmeans/consent'
44
+
45
+ consentStore.init() // push defaults, read + migrate, apply, open when nothing is stored
46
+
47
+ const unsubscribe = consentStore.subscribe(state => {
48
+ if (state.open) {
49
+ showDialog(state.reason) // 'initial' | 'reopen' | 'login' | custom
50
+ }
51
+ })
52
+
53
+ if (consentStore.granted(CONSENT_ANALYTICS)) {
54
+ startAnalytics()
55
+ }
56
+
57
+ consentStore.save({ essential: true, analytics: true, marketing: false })
58
+ consentStore.acceptAll()
59
+ consentStore.open('reopen') // e.g. from a footer link
60
+ ```
61
+
62
+ `useConsent()` in `@owlmeans/web-consent` calls `init` on mount; a host that mounts neither the
63
+ dialog nor a tag loader calls it once itself, with the same options everything else receives.
64
+
65
+ ### Stamp the head snippet above the tag manager
66
+
67
+ ```typescript
68
+ import { consentModeHelper } from '@owlmeans/consent'
69
+
70
+ const bootstrap = consentModeHelper.consentBootstrapScript() // defaults + the stored record
71
+
72
+ // Withhold a loader until tracking is granted (now or by a later CONSENT_EVENT)
73
+ const gated = consentModeHelper.consentGateScript(loaderIife)
74
+
75
+ // Escaping `</script` / `<!--` in the composed inline script is the caller's job
76
+ const head = `<script>${bootstrap};${gated}</script>`
77
+ ```
78
+
79
+ ### Custom categories
80
+
81
+ ```typescript
82
+ import { DEFAULT_CONSENT_CATEGORIES, consentStore } from '@owlmeans/consent'
83
+ import type { ConsentCategory } from '@owlmeans/consent'
84
+
85
+ const categories: ConsentCategory[] = [
86
+ ...DEFAULT_CONSENT_CATEGORIES.filter(category => category.key !== 'marketing'),
87
+ { key: 'chat', labelKey: 'chat', descriptionKey: 'chatDesc', globalVar: 'owlConsentChat' },
88
+ ]
89
+
90
+ consentStore.init({ categories })
91
+ ```
92
+
93
+ ### Share a decision across first-party domains
94
+
95
+ ```typescript
96
+ import { consentLinkHelper, consentStore } from '@owlmeans/consent'
97
+
98
+ const linker = {
99
+ domains: ['owlmeans.com', 'app.example.com'],
100
+ language: { supported: ['en', 'pl', 'de'] }, // omit `supported` on a send-only site
101
+ }
102
+
103
+ consentStore.init({ linker }) // registers consentLinker, adopts, writes the language, strips `owlcc`
104
+
105
+ // For a page that must adopt before any bundle (and before its i18n reads storage)
106
+ const fragment = consentLinkHelper.consentLinkerScript({ linker })
107
+ ```
108
+
109
+ ### Ask only where the law requires it
110
+
111
+ ```typescript
112
+ import { consentPluginHelper, consentStore } from '@owlmeans/consent'
113
+
114
+ // Cloudflare mode: a same-origin GET of /cdn-cgi/trace, `loc=` decides
115
+ consentStore.init({ geo: { cloudflare: true } })
116
+
117
+ // …or a locator of your own — one async method that resolves the country or throws
118
+ consentPluginHelper.registerConsentPlugin({
119
+ alias: 'my-geo', locate: async () => ({ country: await myCountryLookup() }),
120
+ })
121
+ consentStore.init({ geo: {} })
122
+
123
+ await consentStore.settled() // the state once the lookup is over
124
+ ```
125
+
126
+ A visitor located in `CONSENT_REQUIRED_COUNTRIES` (or `geo.countries`) is asked; anyone else gets an
127
+ automatic decision (`auto: <unix s>` on the record) — every category, or only the required ones
128
+ under Global Privacy Control; a visitor nobody can locate is asked. An automatic decision is trusted
129
+ for `CONSENT_AUTO_MAX_AGE` (one hour) and then re-checked silently; an explicit decision always wins.
130
+
131
+ ### Built-in copy
132
+
133
+ ```typescript
134
+ import { consentI18nHelper } from '@owlmeans/consent'
135
+
136
+ const t = consentI18nHelper.defaultConsentTranslate('pl-PL') // falls back to 'en'
137
+ t('analytics', 'Analytics')
138
+ ```
139
+
140
+ ## API
141
+
142
+ ### Store and helpers
143
+
144
+ | Symbol | Kind | Purpose |
145
+ |---|---|---|
146
+ | `consentStore`, `makeConsentStore()` | store | `get`, `subscribe`, `init(opts?)`, `save(record)`, `acceptAll`, `open(reason?)`, `close`, `granted(key)`, `options()`, `settled()` |
147
+ | `consentStorageHelper`, `createConsentStorageHelper()` | helper | `migrateConsent`, `readConsent`, `writeConsent`, `clearConsent` |
148
+ | `consentModeHelper`, `createConsentModeHelper()` | helper | `gtagConsent`, `consentDefaults`, `consentUpdate`, `pushConsentDefaults`, `applyConsent`, `trackingGranted`, `consentBootstrapScript`, `consentGateScript` |
149
+ | `consentLinkHelper`, `createConsentLinkHelper()` | helper | `encodeConsentLink`, `decodeConsentLink`, `writeConsentLanguage`, `consentLinker`, `stripConsentLinkParam`, `consentLinkerScript` |
150
+ | `consentPluginHelper`, `createConsentPluginHelper()` | helper | `registerConsentPlugin`, `unregisterConsentPlugin`, `consentPlugins`, `decorateConsentUrl`, `consentDomains`, `adoptConsent`, `adoptConsentLanguage`, `startConsentPlugins`, `locateConsent` |
151
+ | `consentGeoHelper`, `createConsentGeoHelper()` | helper | `enabled`, `parseTrace`, `cloudflareLocator`, `requiresConsent`, `decide`, `privacySignal`, `automaticRecord`, `autoState` |
152
+ | `consentI18nHelper`, `createConsentI18nHelper()` | helper | `normalizeLocale`, `defaultConsentTranslate`, `interpolate` |
153
+
154
+ Deprecated top-level wrappers (`compat:factory-refactor`) remain exported: `readConsent`,
155
+ `writeConsent`, `clearConsent`, `openConsent`, `isConsented`, `consentBootstrapScript`,
156
+ `defaultConsentTranslate`, `decorateConsentUrl`, `encodeConsentLink`, `consentLinkerScript`. New code
157
+ calls the helper members.
158
+
159
+ ### Constants
160
+
161
+ | Symbol | Value / purpose |
162
+ |---|---|
163
+ | `CONSENT_KEY` | `'site_cookie_consent'` — storage key, must not change |
164
+ | `CONSENT_COOKIE_DAYS` | `365` |
165
+ | `CONSENT_SCHEMA_VERSION` | `2` — stored record shape |
166
+ | `CONSENT_SETUP_FLAG` | `'cookieConsentSetup'` — window flag making `pushConsentDefaults` idempotent |
167
+ | `CONSENT_EVENT` | `'owlmeans:consent'` — dispatched by `applyConsent`, `detail.record` |
168
+ | `CONSENT_ESSENTIAL`, `CONSENT_ANALYTICS`, `CONSENT_MARKETING` | default category keys |
169
+ | `CONSENT_LANGUAGE_KEY` | `'owlmeans-lng'` — the interface-language storage key |
170
+ | `CONSENT_LOCALES` | `en pl ru be uk es de fr` |
171
+ | `DEFAULT_CONSENT_MESSAGES` | built-in dialog copy per locale |
172
+ | `DEFAULT_CONSENT_CATEGORIES` | `essential` (required), `analytics`, `marketing` |
173
+ | `CONSENT_SIGNAL_DEFAULTS` | every Consent Mode v2 signal `denied`, except `security_storage` `granted` |
174
+ | `CONSENT_LINK_PARAM`, `CONSENT_LINK_MAX_AGE`, `CONSENT_LINK_SKEW` | `'owlcc'`, `300` s, `60` s |
175
+ | `CONSENT_REQUIRED_COUNTRIES` | where a located visitor is asked — the union of `CONSENT_COUNTRIES_GDPR`, `CONSENT_COUNTRIES_ALIGNED`, `CONSENT_COUNTRIES_OPT_IN` |
176
+ | `CONSENT_GEO_UNKNOWN` | codes that name no country (`XX`, `T1`, …) — asked |
177
+ | `CONSENT_TRACE_PATH`, `CONSENT_GEO_TIMEOUT` | `'/cdn-cgi/trace'`, `2500` ms |
178
+ | `CONSENT_AUTO_MAX_AGE` | `3600` s — how long an automatic decision is trusted |
179
+ | `CONSENT_IDLE_STATE` | the frozen pre-init state and server snapshot |
180
+ | `CONSENT_STATE_ATTRIBUTE` | `'data-consent'` on `<html>`: `locating`, `open`, `decided`, `idle` |
181
+
182
+ ### Types
183
+
184
+ | Symbol | Purpose |
185
+ |---|---|
186
+ | `ConsentSignal` | the Consent Mode v2 signal names |
187
+ | `ConsentCategory`, `ConsentRecord`, `ConsentService` | category, stored decision, disclosed third-party service |
188
+ | `ConsentOptions` | `categories?`, `storageKey?`, `cookieDays?`, `cookieDomain?`, `silent?`, `linker?`, `geo?` |
189
+ | `ConsentGeoOptions`, `ConsentCloudflareOptions`, `ConsentGeoPlugin`, `ConsentGeoLocation`, `ConsentLocating` | the geo gate: `cloudflare?`, `countries?`, `timeout?`; a locator plugin; its answer; the lookup phase |
190
+ | `ConsentGeoHelper`, `ConsentGeoVerdict`, `ConsentAutoState` | the geo helper's interface, `'ask' \| 'auto'`, `'fresh' \| 'stale'` |
191
+ | `ConsentLinkerOptions`, `ConsentLinkerLanguage` | `domains`, `param?`, `maxAge?`, `language?`; `supported?`, `storageKey?` |
192
+ | `ConsentState`, `ConsentReason`, `ConsentListener`, `ConsentStore` | store state and contract |
193
+ | `ConsentLocale`, `ConsentLinkPayload`, `ConsentPlugin` | locale union, decoded `owlcc` payload, plugin seam |
194
+ | `ConsentModeHelper`, `ConsentStorageHelper`, `ConsentLinkHelper`, `ConsentPluginHelper`, `ConsentI18nHelper` | helper interfaces |
195
+
196
+ ## Cross-domain linking rules
197
+
198
+ `consentLinker().adopt` accepts a decorated link only when the payload decodes with the current
199
+ version, the referrer host is a listed domain, the timestamp is within `maxAge` (and at most 60 s in
200
+ the future), and every local optional category is present. It runs only when the document has no
201
+ stored record. The parameter is stripped from the visible URL whether or not it was adopted. Links
202
+ with `rel="noreferrer"` are never decorated.
203
+
204
+ ## Common pitfalls
205
+
206
+ - Stamp `consentBootstrapScript()` inline above the tag-manager snippet; pushing defaults from a
207
+ React bundle is too late.
208
+ - `silent: true` suppresses runtime pushes only — the stamped bootstrap string still pushes.
209
+ - Do not set `cookieDomain` on an existing site: it orphans the host-only cookie and re-asks every
210
+ visitor. Do not change `CONSENT_KEY` for the same reason.
211
+ - Never present the required category as a choice, and never word a login gate as consent to
212
+ essential cookies.
213
+ - Consent Mode speaks on `window.dataLayer` only; a tag on another queue never hears it.
214
+ - Neither `consentBootstrapScript`, `consentGateScript` nor the composed result is HTML-escaped by
215
+ this package beyond JSON-encoding its own values.
216
+ - Release both ends of a linked domain pair together: a receiver that asks about a category the
217
+ sender dropped refuses the link.
218
+
219
+ The `consent` skill covers the category model, storage migration, the ordering rule, gated loading,
220
+ the plugin seam and language linking in depth.
221
+
222
+ ## Related packages
223
+
224
+ - [`@owlmeans/web-consent`](../web-consent) — the React dialog, re-open button, `CookiePolicy` and `useConsent`
225
+ - [`@owlmeans/web-gtm`](../web-gtm) — Google tag head snippet, CSP hosts and Google service disclosure
226
+ - [`@owlmeans/web-panel`](../web-panel) — `/consent` subpath bound to OwlMeans i18n and the panel menu
227
+ - [`@owlmeans/astro`](../astro) — `owlHeadScripts` for Astro sites, legal-page suppression
228
+ - [`@owlmeans/web-marketing-consent`](../web-marketing-consent) — bridges marketing preferences into `consentStore.save`
229
+ - [`@owlmeans/client-i18n`](../client-i18n) — reads the `owlmeans-lng` language choice this package may write
package/build/consts.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ConsentCategory } from './types.js';
1
+ import type { ConsentCategory, ConsentLocale, ConsentState } from './types.js';
2
2
  /**
3
3
  * Where a visitor's choice is stored.
4
4
  *
@@ -38,8 +38,16 @@ export declare const CONSENT_MARKETING = "marketing";
38
38
  * language part has to run in an inline `<head>` script, before any bundle exists.
39
39
  */
40
40
  export declare const CONSENT_LANGUAGE_KEY = "owlmeans-lng";
41
- export declare const CONSENT_LOCALES: readonly ['en', 'pl', 'ru', 'be', 'uk', 'es', 'de', 'fr'];
42
- export type ConsentLocale = (typeof CONSENT_LOCALES)[number];
41
+ export declare const CONSENT_LOCALES: readonly ["en", "pl", "ru", "be", "uk", "es", "de", "fr"];
42
+ /**
43
+ * The dialog's copy, in the box.
44
+ *
45
+ * Carried here rather than registered into an i18n framework because one of the three consumers is
46
+ * an Astro site with React islands and no OwlMeans i18n at all — and a consent dialog that renders
47
+ * raw keys is worse than no dialog. An application that HAS translations passes `translate` and
48
+ * overrides every one of these.
49
+ */
50
+ export declare const DEFAULT_CONSENT_MESSAGES: Record<ConsentLocale, Record<string, string>>;
43
51
  /**
44
52
  * The categories every OwlMeans surface starts with.
45
53
  *
@@ -56,4 +64,71 @@ export declare const DEFAULT_CONSENT_CATEGORIES: ConsentCategory[];
56
64
  * fraud prevention, which are strictly necessary. Everything else starts denied.
57
65
  */
58
66
  export declare const CONSENT_SIGNAL_DEFAULTS: Record<string, 'granted' | 'denied'>;
67
+ /** The URL parameter a decorated link carries the decision in, absent an override. */
68
+ export declare const CONSENT_LINK_PARAM = "owlcc";
69
+ /** How old a decorated link's timestamp may be and still be trusted, in seconds, absent an override. */
70
+ export declare const CONSENT_LINK_MAX_AGE = 300;
71
+ /** Clock skew allowed the OTHER way — a timestamp up to this far in the future is still trusted. */
72
+ export declare const CONSENT_LINK_SKEW = 60;
73
+ /**
74
+ * Where prior consent for non-essential cookies is the law: the EU and EEA (ePrivacy Art. 5(3) with
75
+ * the GDPR), including the parts of member states that carry an ISO code of their own — Åland and
76
+ * the French outermost regions (French Guiana, Guadeloupe, Martinique, Réunion, Mayotte,
77
+ * Saint-Martin). The Canary Islands, Azores, Madeira, Ceuta and Melilla report their state's code.
78
+ * Greece is `GR`, as Cloudflare and every ISO feed spell it.
79
+ */
80
+ export declare const CONSENT_COUNTRIES_GDPR: readonly string[];
81
+ /**
82
+ * Jurisdictions outside the EEA whose rule for cookies is the same prior opt-in, by statute or by
83
+ * the regulator's guidance (2026):
84
+ *
85
+ * - the United Kingdom (PECR — the 2025 Data (Use and Access) Act exempts some first-party
86
+ * analytics, advertising still needs consent), the Crown Dependencies and Gibraltar;
87
+ * - Switzerland (FDPIC guidelines, 2025: explicit consent for tracking and profiling);
88
+ * - Andorra, Monaco, San Marino and the Vatican; Türkiye (KVKK cookie guide); Serbia (ZZPL and the
89
+ * electronic-communications law); Albania, Bosnia and Herzegovina, Montenegro, North Macedonia,
90
+ * Kosovo, Moldova, Georgia and Ukraine (GDPR-modelled laws);
91
+ * - territories under a member state's regulator but outside the EU: the Faroe Islands and
92
+ * Greenland (Denmark); Saint-Barthélemy, Saint-Pierre-et-Miquelon, New Caledonia, French
93
+ * Polynesia, Wallis and Futuna (France); the Caribbean Netherlands.
94
+ */
95
+ export declare const CONSENT_COUNTRIES_ALIGNED: readonly string[];
96
+ /**
97
+ * Prior opt-in regimes elsewhere (2026): Brazil (LGPD, ANPD cookie guide), Canada (Québec's Law 25
98
+ * — the country is the finest grain a CDN reports, so all of Canada), China (PIPL), South Korea
99
+ * (PIPA), Nigeria (NDPA), Saudi Arabia (PDPL), Thailand (PDPA), Vietnam (PDPL 2026).
100
+ *
101
+ * Deliberately absent: India (the DPDP Act's consent rules apply from mid-2027 — add `IN` then),
102
+ * and the notice-or-opt-out regimes — the United States, Japan, Australia, Singapore. South
103
+ * Africa's POPIA is read both ways by the guides and is left out until its regulator says more.
104
+ */
105
+ export declare const CONSENT_COUNTRIES_OPT_IN: readonly string[];
106
+ /**
107
+ * Every country where a located visitor is ASKED (`ConsentGeoOptions.countries` replaces it). A
108
+ * visitor located anywhere else gets an automatic decision; one who cannot be located is asked.
109
+ */
110
+ export declare const CONSENT_REQUIRED_COUNTRIES: readonly string[];
111
+ /**
112
+ * Codes that name no country: Cloudflare's `XX` (no data) and `T1` (Tor), the retired MaxMind
113
+ * pseudo-codes (`A1` anonymous proxy, `A2` satellite, `O1` other) and the continent buckets some
114
+ * feeds fall back to (`EU`, `AP`), plus `ZZ` (unknown). A visitor reported with one is asked.
115
+ */
116
+ export declare const CONSENT_GEO_UNKNOWN: readonly string[];
117
+ /** Cloudflare's trace endpoint — served by the edge on every proxied host, `loc=` among its lines. */
118
+ export declare const CONSENT_TRACE_PATH = "/cdn-cgi/trace";
119
+ /** How long locating may take before the visitor is asked anyway, in ms. */
120
+ export declare const CONSENT_GEO_TIMEOUT = 2500;
121
+ /**
122
+ * How long an automatic decision is trusted, in seconds. Older, it counts as undecided — in the
123
+ * head scripts as much as in the store — until the visitor is located again, so a grant derived in
124
+ * one country never reaches a page opened an hour later in another.
125
+ */
126
+ export declare const CONSENT_AUTO_MAX_AGE = 3600;
127
+ /** The state of a store nobody has initialised — and the only server-side snapshot. */
128
+ export declare const CONSENT_IDLE_STATE: ConsentState;
129
+ /**
130
+ * The attribute on `<html>` that mirrors the consent phase: `locating`, `open`, `decided` or `idle`.
131
+ * Absent until the store first runs. A test waits on it, and a stylesheet may key on it.
132
+ */
133
+ export declare const CONSENT_STATE_ATTRIBUTE = "data-consent";
59
134
  //# sourceMappingURL=consts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,wBAAwB,CAAA;AAEhD,eAAO,MAAM,mBAAmB,MAAM,CAAA;AAEtC;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAA;AAEvC,2FAA2F;AAC3F,eAAO,MAAM,kBAAkB,uBAAuB,CAAA;AAEtD;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,qBAAqB,CAAA;AAE/C,eAAO,MAAM,iBAAiB,cAAc,CAAA;AAC5C,eAAO,MAAM,iBAAiB,cAAc,CAAA;AAC5C,eAAO,MAAM,iBAAiB,cAAc,CAAA;AAE5C;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,iBAAiB,CAAA;AAElD,eAAO,MAAM,eAAe,YAAI,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAA;AAExF,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAE5D;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,EAAE,eAAe,EAmBvD,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,CAQxE,CAAA"}
1
+ {"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAW9E;;;;;GAKG;AACH,eAAO,MAAM,WAAW,wBAAwB,CAAA;AAEhD,eAAO,MAAM,mBAAmB,MAAM,CAAA;AAEtC;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAA;AAEvC,2FAA2F;AAC3F,eAAO,MAAM,kBAAkB,uBAAuB,CAAA;AAEtD;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,qBAAqB,CAAA;AAE/C,eAAO,MAAM,iBAAiB,cAAc,CAAA;AAC5C,eAAO,MAAM,iBAAiB,cAAc,CAAA;AAC5C,eAAO,MAAM,iBAAiB,cAAc,CAAA;AAE5C;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,iBAAiB,CAAA;AAElD,eAAO,MAAM,eAAe,2DAA+F,CAAA;AAE3H;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAElF,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,EAAE,eAAe,EAmBvD,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,CAQxE,CAAA;AAED,sFAAsF;AACtF,eAAO,MAAM,kBAAkB,UAAU,CAAA;AAEzC,wGAAwG;AACxG,eAAO,MAAM,oBAAoB,MAAM,CAAA;AAEvC,oGAAoG;AACpG,eAAO,MAAM,iBAAiB,KAAK,CAAA;AAEnC;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAKlD,CAAA;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yBAAyB,EAAE,SAAS,MAAM,EAKrD,CAAA;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAEpD,CAAA;AAEF;;;GAGG;AACH,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAEtD,CAAA;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAE/C,CAAA;AAEF,sGAAsG;AACtG,eAAO,MAAM,kBAAkB,mBAAmB,CAAA;AAElD,4EAA4E;AAC5E,eAAO,MAAM,mBAAmB,OAAO,CAAA;AAEvC;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,OAAO,CAAA;AAExC,uFAAuF;AACvF,eAAO,MAAM,kBAAkB,EAAE,YAE/B,CAAA;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,iBAAiB,CAAA"}
package/build/consts.js CHANGED
@@ -1,3 +1,11 @@
1
+ import en from './i18n/en.json' with { type: 'json' };
2
+ import pl from './i18n/pl.json' with { type: 'json' };
3
+ import ru from './i18n/ru.json' with { type: 'json' };
4
+ import be from './i18n/be.json' with { type: 'json' };
5
+ import uk from './i18n/uk.json' with { type: 'json' };
6
+ import es from './i18n/es.json' with { type: 'json' };
7
+ import de from './i18n/de.json' with { type: 'json' };
8
+ import fr from './i18n/fr.json' with { type: 'json' };
1
9
  /**
2
10
  * Where a visitor's choice is stored.
3
11
  *
@@ -38,6 +46,17 @@ export const CONSENT_MARKETING = 'marketing';
38
46
  */
39
47
  export const CONSENT_LANGUAGE_KEY = 'owlmeans-lng';
40
48
  export const CONSENT_LOCALES = ['en', 'pl', 'ru', 'be', 'uk', 'es', 'de', 'fr'];
49
+ /**
50
+ * The dialog's copy, in the box.
51
+ *
52
+ * Carried here rather than registered into an i18n framework because one of the three consumers is
53
+ * an Astro site with React islands and no OwlMeans i18n at all — and a consent dialog that renders
54
+ * raw keys is worse than no dialog. An application that HAS translations passes `translate` and
55
+ * overrides every one of these.
56
+ */
57
+ export const DEFAULT_CONSENT_MESSAGES = {
58
+ en, pl, ru, be, uk, es, de, fr,
59
+ };
41
60
  /**
42
61
  * The categories every OwlMeans surface starts with.
43
62
  *
@@ -81,4 +100,89 @@ export const CONSENT_SIGNAL_DEFAULTS = {
81
100
  personalization_storage: 'denied',
82
101
  security_storage: 'granted',
83
102
  };
103
+ /** The URL parameter a decorated link carries the decision in, absent an override. */
104
+ export const CONSENT_LINK_PARAM = 'owlcc';
105
+ /** How old a decorated link's timestamp may be and still be trusted, in seconds, absent an override. */
106
+ export const CONSENT_LINK_MAX_AGE = 300;
107
+ /** Clock skew allowed the OTHER way — a timestamp up to this far in the future is still trusted. */
108
+ export const CONSENT_LINK_SKEW = 60;
109
+ /**
110
+ * Where prior consent for non-essential cookies is the law: the EU and EEA (ePrivacy Art. 5(3) with
111
+ * the GDPR), including the parts of member states that carry an ISO code of their own — Åland and
112
+ * the French outermost regions (French Guiana, Guadeloupe, Martinique, Réunion, Mayotte,
113
+ * Saint-Martin). The Canary Islands, Azores, Madeira, Ceuta and Melilla report their state's code.
114
+ * Greece is `GR`, as Cloudflare and every ISO feed spell it.
115
+ */
116
+ export const CONSENT_COUNTRIES_GDPR = Object.freeze([
117
+ 'AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GR', 'HR', 'HU',
118
+ 'IE', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK',
119
+ 'AX', 'GF', 'GP', 'MQ', 'RE', 'YT', 'MF',
120
+ 'IS', 'LI', 'NO',
121
+ ]);
122
+ /**
123
+ * Jurisdictions outside the EEA whose rule for cookies is the same prior opt-in, by statute or by
124
+ * the regulator's guidance (2026):
125
+ *
126
+ * - the United Kingdom (PECR — the 2025 Data (Use and Access) Act exempts some first-party
127
+ * analytics, advertising still needs consent), the Crown Dependencies and Gibraltar;
128
+ * - Switzerland (FDPIC guidelines, 2025: explicit consent for tracking and profiling);
129
+ * - Andorra, Monaco, San Marino and the Vatican; Türkiye (KVKK cookie guide); Serbia (ZZPL and the
130
+ * electronic-communications law); Albania, Bosnia and Herzegovina, Montenegro, North Macedonia,
131
+ * Kosovo, Moldova, Georgia and Ukraine (GDPR-modelled laws);
132
+ * - territories under a member state's regulator but outside the EU: the Faroe Islands and
133
+ * Greenland (Denmark); Saint-Barthélemy, Saint-Pierre-et-Miquelon, New Caledonia, French
134
+ * Polynesia, Wallis and Futuna (France); the Caribbean Netherlands.
135
+ */
136
+ export const CONSENT_COUNTRIES_ALIGNED = Object.freeze([
137
+ 'GB', 'GG', 'JE', 'IM', 'GI',
138
+ 'CH',
139
+ 'AD', 'MC', 'SM', 'VA', 'TR', 'RS', 'AL', 'BA', 'ME', 'MK', 'XK', 'MD', 'GE', 'UA',
140
+ 'FO', 'GL', 'BL', 'PM', 'NC', 'PF', 'WF', 'BQ',
141
+ ]);
142
+ /**
143
+ * Prior opt-in regimes elsewhere (2026): Brazil (LGPD, ANPD cookie guide), Canada (Québec's Law 25
144
+ * — the country is the finest grain a CDN reports, so all of Canada), China (PIPL), South Korea
145
+ * (PIPA), Nigeria (NDPA), Saudi Arabia (PDPL), Thailand (PDPA), Vietnam (PDPL 2026).
146
+ *
147
+ * Deliberately absent: India (the DPDP Act's consent rules apply from mid-2027 — add `IN` then),
148
+ * and the notice-or-opt-out regimes — the United States, Japan, Australia, Singapore. South
149
+ * Africa's POPIA is read both ways by the guides and is left out until its regulator says more.
150
+ */
151
+ export const CONSENT_COUNTRIES_OPT_IN = Object.freeze([
152
+ 'BR', 'CA', 'CN', 'KR', 'NG', 'SA', 'TH', 'VN',
153
+ ]);
154
+ /**
155
+ * Every country where a located visitor is ASKED (`ConsentGeoOptions.countries` replaces it). A
156
+ * visitor located anywhere else gets an automatic decision; one who cannot be located is asked.
157
+ */
158
+ export const CONSENT_REQUIRED_COUNTRIES = Object.freeze([
159
+ ...CONSENT_COUNTRIES_GDPR, ...CONSENT_COUNTRIES_ALIGNED, ...CONSENT_COUNTRIES_OPT_IN,
160
+ ]);
161
+ /**
162
+ * Codes that name no country: Cloudflare's `XX` (no data) and `T1` (Tor), the retired MaxMind
163
+ * pseudo-codes (`A1` anonymous proxy, `A2` satellite, `O1` other) and the continent buckets some
164
+ * feeds fall back to (`EU`, `AP`), plus `ZZ` (unknown). A visitor reported with one is asked.
165
+ */
166
+ export const CONSENT_GEO_UNKNOWN = Object.freeze([
167
+ 'XX', 'T1', 'A1', 'A2', 'O1', 'EU', 'AP', 'ZZ',
168
+ ]);
169
+ /** Cloudflare's trace endpoint — served by the edge on every proxied host, `loc=` among its lines. */
170
+ export const CONSENT_TRACE_PATH = '/cdn-cgi/trace';
171
+ /** How long locating may take before the visitor is asked anyway, in ms. */
172
+ export const CONSENT_GEO_TIMEOUT = 2500;
173
+ /**
174
+ * How long an automatic decision is trusted, in seconds. Older, it counts as undecided — in the
175
+ * head scripts as much as in the store — until the visitor is located again, so a grant derived in
176
+ * one country never reaches a page opened an hour later in another.
177
+ */
178
+ export const CONSENT_AUTO_MAX_AGE = 3600;
179
+ /** The state of a store nobody has initialised — and the only server-side snapshot. */
180
+ export const CONSENT_IDLE_STATE = Object.freeze({
181
+ record: null, open: false, reason: null, locating: null,
182
+ });
183
+ /**
184
+ * The attribute on `<html>` that mirrors the consent phase: `locating`, `open`, `decided` or `idle`.
185
+ * Absent until the store first runs. A test waits on it, and a stylesheet may key on it.
186
+ */
187
+ export const CONSENT_STATE_ATTRIBUTE = 'data-consent';
84
188
  //# sourceMappingURL=consts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,qBAAqB,CAAA;AAEhD,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAA;AAEtC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAA;AAEvC,2FAA2F;AAC3F,MAAM,CAAC,MAAM,kBAAkB,GAAG,oBAAoB,CAAA;AAEtD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,kBAAkB,CAAA;AAE/C,MAAM,CAAC,MAAM,iBAAiB,GAAG,WAAW,CAAA;AAC5C,MAAM,CAAC,MAAM,iBAAiB,GAAG,WAAW,CAAA;AAC5C,MAAM,CAAC,MAAM,iBAAiB,GAAG,WAAW,CAAA;AAE5C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,cAAc,CAAA;AAElD,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAA;AAIxF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAsB;IAC3D;QACE,GAAG,EAAE,iBAAiB,EAAE,QAAQ,EAAE,IAAI;QACtC,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,eAAe;QACtD,SAAS,EAAE,qBAAqB;QAChC,OAAO,EAAE,CAAC,kBAAkB,EAAE,uBAAuB,CAAC;KACvD;IACD;QACE,GAAG,EAAE,iBAAiB;QACtB,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,eAAe;QACtD,SAAS,EAAE,qBAAqB;QAChC,OAAO,EAAE,CAAC,mBAAmB,CAAC;KAC/B;IACD;QACE,GAAG,EAAE,iBAAiB;QACtB,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,eAAe;QACtD,SAAS,EAAE,qBAAqB;QAChC,OAAO,EAAE,CAAC,YAAY,EAAE,cAAc,EAAE,oBAAoB,CAAC;KAC9D;CACF,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAyC;IAC3E,UAAU,EAAE,QAAQ;IACpB,YAAY,EAAE,QAAQ;IACtB,kBAAkB,EAAE,QAAQ;IAC5B,iBAAiB,EAAE,QAAQ;IAC3B,qBAAqB,EAAE,QAAQ;IAC/B,uBAAuB,EAAE,QAAQ;IACjC,gBAAgB,EAAE,SAAS;CAC5B,CAAA"}
1
+ {"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export declare const CONSENT_LINK_VERSION = 2;
2
+ /** A BCP 47-shaped tag: a 2–3 letter language and up to three subtags (`pl`, `pt-BR`, `zh-Hant-TW`). */
3
+ export declare const LANGUAGE_TAG: RegExp;
4
+ /** The built-in Cloudflare locator's registry alias. */
5
+ export declare const CLOUDFLARE_LOCATOR_ALIAS = "cloudflare";
6
+ /** Below anything an application registers: an application's own locator always asks first. */
7
+ export declare const CLOUDFLARE_LOCATOR_PRIORITY = -100;
8
+ /** An ISO 3166-1 alpha-2 code, upper case. */
9
+ export declare const COUNTRY_CODE: RegExp;
10
+ /** Clock skew tolerated on an automatic decision's timestamp, in seconds. */
11
+ export declare const AUTO_SKEW = 60;
12
+ //# sourceMappingURL=consts.local.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.local.d.ts","sourceRoot":"","sources":["../src/consts.local.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,IAAI,CAAA;AAErC,wGAAwG;AACxG,eAAO,MAAM,YAAY,QAA4C,CAAA;AAErE,wDAAwD;AACxD,eAAO,MAAM,wBAAwB,eAAe,CAAA;AAEpD,+FAA+F;AAC/F,eAAO,MAAM,2BAA2B,OAAO,CAAA;AAE/C,8CAA8C;AAC9C,eAAO,MAAM,YAAY,QAAe,CAAA;AAExC,6EAA6E;AAC7E,eAAO,MAAM,SAAS,KAAK,CAAA"}
@@ -0,0 +1,12 @@
1
+ export const CONSENT_LINK_VERSION = 2;
2
+ /** A BCP 47-shaped tag: a 2–3 letter language and up to three subtags (`pl`, `pt-BR`, `zh-Hant-TW`). */
3
+ export const LANGUAGE_TAG = /^[a-z]{2,3}(?:[-_][a-z0-9]{1,8}){0,3}$/i;
4
+ /** The built-in Cloudflare locator's registry alias. */
5
+ export const CLOUDFLARE_LOCATOR_ALIAS = 'cloudflare';
6
+ /** Below anything an application registers: an application's own locator always asks first. */
7
+ export const CLOUDFLARE_LOCATOR_PRIORITY = -100;
8
+ /** An ISO 3166-1 alpha-2 code, upper case. */
9
+ export const COUNTRY_CODE = /^[A-Z]{2}$/;
10
+ /** Clock skew tolerated on an automatic decision's timestamp, in seconds. */
11
+ export const AUTO_SKEW = 60;
12
+ //# sourceMappingURL=consts.local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.local.js","sourceRoot":"","sources":["../src/consts.local.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAA;AAErC,wGAAwG;AACxG,MAAM,CAAC,MAAM,YAAY,GAAG,yCAAyC,CAAA;AAErE,wDAAwD;AACxD,MAAM,CAAC,MAAM,wBAAwB,GAAG,YAAY,CAAA;AAEpD,+FAA+F;AAC/F,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,GAAG,CAAA;AAE/C,8CAA8C;AAC9C,MAAM,CAAC,MAAM,YAAY,GAAG,YAAY,CAAA;AAExC,6EAA6E;AAC7E,MAAM,CAAC,MAAM,SAAS,GAAG,EAAE,CAAA"}
@@ -0,0 +1,47 @@
1
+ import type { ConsentGeoPlugin, ConsentOptions, ConsentRecord } from '../types.js';
2
+ /** What locating the visitor decided: ask them, or decide for them automatically. */
3
+ export type ConsentGeoVerdict = 'ask' | 'auto';
4
+ /** Whether a record is an automatic decision, and whether it is still trusted. */
5
+ export type ConsentAutoState = 'fresh' | 'stale';
6
+ /**
7
+ * Asking only where the law requires it: locating the visitor, the decision that follows, and the
8
+ * automatic record written where nobody needs to be asked.
9
+ */
10
+ export interface ConsentGeoHelper {
11
+ /** Whether `opts.geo` turns the gate on AND something can answer — the Cloudflare locator or a plugin. */
12
+ enabled: (opts?: ConsentOptions) => boolean;
13
+ /** Every `key=value` line of a Cloudflare trace body. Never throws; anything else parses to `{}`. */
14
+ parseTrace: (text: string) => Record<string, string>;
15
+ /**
16
+ * The built-in locator: a same-origin GET of the trace endpoint, cookies left out, never cached.
17
+ * Rejects when `opts.geo.cloudflare` is off, the request fails or answers non-2xx, or the body is
18
+ * not a trace (an SPA serving its `index.html` for every path answers 200 here).
19
+ */
20
+ cloudflareLocator: () => ConsentGeoPlugin;
21
+ /**
22
+ * Whether a visitor located in `country` must be asked: yes inside `opts.geo.countries`
23
+ * (`CONSENT_REQUIRED_COUNTRIES`), and yes for anything that names no country.
24
+ */
25
+ requiresConsent: (country: string | null, opts?: ConsentOptions) => boolean;
26
+ /**
27
+ * Locate and decide, bounded by `opts.geo.timeout`. Never rejects: a visitor nobody could locate
28
+ * resolves `'ask'`.
29
+ */
30
+ decide: (opts: ConsentOptions) => Promise<ConsentGeoVerdict>;
31
+ /**
32
+ * Whether the browser sends Global Privacy Control — a legally binding opt-out in several US
33
+ * states, so an automatic decision honours it.
34
+ */
35
+ privacySignal: () => boolean;
36
+ /**
37
+ * The automatic decision for a visitor nobody needs to ask: every category on — or, under Global
38
+ * Privacy Control, only the required ones — stamped `auto` with the current time.
39
+ */
40
+ automaticRecord: (opts?: ConsentOptions) => ConsentRecord;
41
+ /**
42
+ * `fresh` for an automatic decision younger than `CONSENT_AUTO_MAX_AGE`, `stale` for an older one
43
+ * (or one stamped in the future beyond the skew), `null` for an explicit record or none.
44
+ */
45
+ autoState: (record: ConsentRecord | null) => ConsentAutoState | null;
46
+ }
47
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/geo/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAElF,qFAAqF;AACrF,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,MAAM,CAAA;AAE9C,kFAAkF;AAClF,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,OAAO,CAAA;AAEhD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,0GAA0G;IAC1G,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,cAAc,KAAK,OAAO,CAAA;IAC3C,qGAAqG;IACrG,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpD;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;IACzC;;;OAGG;IACH,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE,cAAc,KAAK,OAAO,CAAA;IAC3E;;;OAGG;IACH,MAAM,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAC5D;;;OAGG;IACH,aAAa,EAAE,MAAM,OAAO,CAAA;IAC5B;;;OAGG;IACH,eAAe,EAAE,CAAC,IAAI,CAAC,EAAE,cAAc,KAAK,aAAa,CAAA;IACzD;;;OAGG;IACH,SAAS,EAAE,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,KAAK,gBAAgB,GAAG,IAAI,CAAA;CACrE"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/geo/types.ts"],"names":[],"mappings":""}
package/build/geo.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { ConsentGeoHelper } from './geo/types.js';
2
+ export declare const createConsentGeoHelper: () => ConsentGeoHelper;
3
+ export declare const consentGeoHelper: ConsentGeoHelper;
4
+ //# sourceMappingURL=geo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geo.d.ts","sourceRoot":"","sources":["../src/geo.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAoB,gBAAgB,EAAqB,MAAM,gBAAgB,CAAA;AAE3F,eAAO,MAAM,sBAAsB,QAAO,gBA2GzC,CAAA;AAED,eAAO,MAAM,gBAAgB,kBAA2B,CAAA"}