@consenti/api 0.5.0 → 0.6.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 +143 -11
- package/dist/chunk-7NKW7UTJ.js +2 -0
- package/dist/chunk-7NKW7UTJ.js.map +1 -0
- package/dist/dashboard/assets/{ApiConfig-dmDLJNfI.js → ApiConfig-DLPkgWsM.js} +1 -1
- package/dist/dashboard/assets/{ArchivedProfileList-ChWp1RHE.js → ArchivedProfileList-fRvXAaWy.js} +1 -1
- package/dist/dashboard/assets/{AuditLog-3lekUOk8.js → AuditLog-9flkPiEQ.js} +1 -1
- package/dist/dashboard/assets/{ChangePassword-BYlF08cE.js → ChangePassword-D--mNLLR.js} +1 -1
- package/dist/dashboard/assets/{ConfirmDialog-sMBVcbiI.js → ConfirmDialog-CJAqYRfE.js} +1 -1
- package/dist/dashboard/assets/{ConsentList-DLDBSjxO.js → ConsentList-CyddMOrW.js} +1 -1
- package/dist/dashboard/assets/{ConsentTemplateEditor-0iB6G95-.js → ConsentTemplateEditor-DZGFzkTA.js} +1 -1
- package/dist/dashboard/assets/{ConsentTemplateList-BnU_nL8h.js → ConsentTemplateList-CwNVxHyZ.js} +1 -1
- package/dist/dashboard/assets/{HeadingTagInput-7owwn9Vj.js → HeadingTagInput-DGKZYLTR.js} +1 -1
- package/dist/dashboard/assets/{HowItWorks-B05y-6xX.js → HowItWorks-10CyO4Kp.js} +1 -1
- package/dist/dashboard/assets/{PermissionGate-D45f3WrO.js → PermissionGate-CkaZ9DKx.js} +1 -1
- package/dist/dashboard/assets/PreviewPane-ArCfR0c9.js +38 -0
- package/dist/dashboard/assets/{ProfileEditor-CuMRNzdM.js → ProfileEditor-BNUxXKMk.js} +1 -1
- package/dist/dashboard/assets/{ProfileList-CFZ_i1zk.js → ProfileList-Ti5ZML_L.js} +1 -1
- package/dist/dashboard/assets/{ProfileVersionHistory-40HtQSrd.js → ProfileVersionHistory-D2KtUROF.js} +1 -1
- package/dist/dashboard/assets/{RecordDetailModal-hpJgK02E.js → RecordDetailModal-4OyHjtux.js} +1 -1
- package/dist/dashboard/assets/{Reports-BlvcycZ8.js → Reports-CHpF_j2k.js} +1 -1
- package/dist/dashboard/assets/{RoleList-CTeQdx6f.js → RoleList-BICQW5gU.js} +1 -1
- package/dist/dashboard/assets/{Select-CeC6GRyb.js → Select-C7q2vevu.js} +1 -1
- package/dist/dashboard/assets/{Settings-DLsvGtMh.js → Settings-BlOcU0wL.js} +1 -1
- package/dist/dashboard/assets/{SetupWizard-BVzGrBm2.js → SetupWizard-DReZ5DWr.js} +1 -1
- package/dist/dashboard/assets/{Table-CcBP7eXS.js → Table-C59yMXY2.js} +1 -1
- package/dist/dashboard/assets/{TenantList-DLgPnlVv.js → TenantList-Bbwcxyo8.js} +1 -1
- package/dist/dashboard/assets/{UITemplateEditor-B9EBcvOB.js → UITemplateEditor-CR7TnHBH.js} +1 -1
- package/dist/dashboard/assets/{UITemplateList-CGYRRlCh.js → UITemplateList-NKwDQJAw.js} +1 -1
- package/dist/dashboard/assets/{UserList-BbpiUrVr.js → UserList-DMk0_UF0.js} +1 -1
- package/dist/dashboard/assets/{VendorList-mlOlW7ht.js → VendorList-BawJ8Kyc.js} +1 -1
- package/dist/dashboard/assets/{VisitorList-Dne7xkNx.js → VisitorList-BVSFRtul.js} +1 -1
- package/dist/dashboard/assets/check-BAoHs1bd.js +1 -0
- package/dist/dashboard/assets/{copy-FTaGH7-z.js → copy-DjZpSOQM.js} +1 -1
- package/dist/dashboard/assets/{external-link-5y29qnhn.js → external-link-CFayXpKz.js} +1 -1
- package/dist/dashboard/assets/{index-BaYVhd1M.js → index-D7qdwIxs.js} +2 -2
- package/dist/dashboard/assets/{profiles-D-hvl_fK.js → profiles-Jb6uV-0U.js} +1 -1
- package/dist/dashboard/assets/{roles-DZyZwZDr.js → roles-ChXEt79v.js} +1 -1
- package/dist/dashboard/assets/{rotate-ccw-clock-C5cVu2_f.js → rotate-ccw-clock-503u3yEE.js} +1 -1
- package/dist/dashboard/assets/{search-mdMD-WTj.js → search-B05qPv24.js} +1 -1
- package/dist/dashboard/assets/{square-pen-BoEcHaSW.js → square-pen-BX2H18m3.js} +1 -1
- package/dist/dashboard/assets/{templates-DLMsZs7d.js → templates-DalUjETH.js} +1 -1
- package/dist/dashboard/assets/{trash-BVDX09Xe.js → trash-Bfqti-Og.js} +1 -1
- package/dist/dashboard/assets/{useHashQuery-wXzkY-tk.js → useHashQuery-DzYBqPIg.js} +1 -1
- package/dist/dashboard/assets/{users-BLTbK4tk.js → users-CDdO4hYQ.js} +1 -1
- package/dist/dashboard/assets/x-Clw5rGTJ.js +1 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/dist-MNR4WETI.js +2 -0
- package/dist/index.cjs +77 -75
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -9
- package/dist/index.d.ts +45 -9
- package/dist/index.js +76 -74
- package/dist/index.js.map +1 -1
- package/dist/widget/widget.js +1 -1
- package/package.json +3 -2
- package/scripts/migrate-to-consenti-prefix.mjs +196 -0
- package/dist/chunk-COR72NFQ.js +0 -2
- package/dist/chunk-COR72NFQ.js.map +0 -1
- package/dist/dashboard/assets/PreviewPane-Czax39la.js +0 -38
- package/dist/dashboard/assets/check-IM5PInrX.js +0 -1
- package/dist/dashboard/assets/x-CtCy0YcJ.js +0 -1
- package/dist/dist-QJYW5KUK.js +0 -2
- /package/dist/{dist-QJYW5KUK.js.map → dist-MNR4WETI.js.map} +0 -0
package/README.md
CHANGED
|
@@ -63,6 +63,23 @@ npm install mongodb
|
|
|
63
63
|
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
+
### Schema upgrades
|
|
67
|
+
|
|
68
|
+
SQL drivers (SQLite, PostgreSQL, MySQL/MariaDB) track a schema version (the `consenti_schema_version` table, or `PRAGMA user_version` on SQLite). A fresh database is created at the current version; an older one is upgraded automatically on the next start — no manual step, except the one-off rename to `consenti_*` table names when upgrading a PostgreSQL / MySQL / MongoDB install from before 0.6.0 (see [Upgrading from before 0.6.0](#upgrading-from-before-060)). MongoDB and the JSON file store are schemaless, so older data simply lacks the newer optional fields.
|
|
69
|
+
|
|
70
|
+
| Version | Change |
|
|
71
|
+
|---|---|
|
|
72
|
+
| 2 | `consent_records.profile_id` / `notice_shown.profile_id` no longer reference `profiles` (a consent record is evidence and survives deleting its profile); `profile_version` added to `consent_records` and `consent_history` — with `profile_id` it pins the exact on-disk snapshot a visitor was shown. |
|
|
73
|
+
|
|
74
|
+
The upgrade changes table structure only — it never edits, drops or re-creates your rows — and is built to fail safe:
|
|
75
|
+
|
|
76
|
+
- **Permissions.** The database user needs to alter tables: `ALTER` on MySQL/MariaDB, table ownership on PostgreSQL, a writable file on SQLite. Without it the API refuses to start with an explanatory error and nothing is changed.
|
|
77
|
+
- **SQLite** first saves a copy of the database next to it (`consenti.db.pre-v2-<timestamp>.bak`; delete it once you are happy), then rebuilds the two affected tables inside one transaction, checking that each copy has exactly the original row count before the old table is dropped. A table with a column this version doesn't know is never rebuilt (it would delete that column) — the upgrade stops and changes nothing. Allow roughly double the database size in free disk space; a very large database takes a moment longer to start once.
|
|
78
|
+
- **PostgreSQL** runs the whole upgrade in one transaction (all or nothing).
|
|
79
|
+
- **MySQL/MariaDB** DDL cannot be rolled back, so each step checks first and is safe to repeat; an interrupted upgrade resumes on the next start.
|
|
80
|
+
- For MySQL/MariaDB and PostgreSQL, still take your usual database backup first — Consenti cannot make one for you.
|
|
81
|
+
- Starting an older Consenti version against an upgraded database keeps working.
|
|
82
|
+
|
|
66
83
|
### Which SQLite driver should I use?
|
|
67
84
|
|
|
68
85
|
| Driver | Config | Node requirement | Extra install? |
|
|
@@ -80,7 +97,7 @@ On Node 20/21, choose between `node-sqlite3-wasm` (pure WASM, no compilation) or
|
|
|
80
97
|
|
|
81
98
|
Consenti ships with four geo resolver options. The default requires zero installation; the others are opt-in.
|
|
82
99
|
|
|
83
|
-
**Bonus:** You can also supply a custom resolver function via `compliance.geoDataProvider` — it receives `{ ip, timezone, language }` and must return `{ country: string | null, region: string | null, locale: string | null }`, plus an optional `complianceGroup?: string` — when set, that group is used directly, skipping the country/region jurisdiction-map lookup entirely (for a provider that already carries legal-grade jurisdiction data and needs to route into an operator-defined custom group).
|
|
100
|
+
**Bonus:** You can also supply a custom resolver function via `compliance.geoDataProvider` — it receives `{ ip, timezone, language, languages, locale }` (`language` is the browser's primary language tag, `languages` its full preference list, `locale` the locale the widget requested) and must return `{ country: string | null, region: string | null, locale: string | null }`, plus an optional `complianceGroup?: string` — when set, that group is used directly, skipping the country/region jurisdiction-map lookup entirely (for a provider that already carries legal-grade jurisdiction data and needs to route into an operator-defined custom group).
|
|
84
101
|
|
|
85
102
|
> **Note:** The default (`'default'`) and `'hosted-geoip-lite'` resolvers are suitable for development and lower-traffic deployments. For high-accuracy production use, choose `'geoip'` or `'maxmind'`.
|
|
86
103
|
|
|
@@ -128,14 +145,16 @@ const consenti = createConsenti({
|
|
|
128
145
|
// ── Storage ─────────────────────────────────────────────────────────────────
|
|
129
146
|
storage: {
|
|
130
147
|
driver: 'json', // 'json' | 'node:sqlite' | 'node-sqlite3-wasm' | 'better-sqlite3' | 'sqlite' | 'postgresql' | 'mysql' | 'mongodb'
|
|
131
|
-
path: './consenti-data', // base directory
|
|
148
|
+
path: './consenti-data', // ALWAYS required, for every driver — base directory for profile files; also holds db/ for json/SQLite
|
|
132
149
|
|
|
133
150
|
// Optional
|
|
134
151
|
uri: 'mongodb://localhost:27017/consenti', // db connection string/uri, only for (driver: 'postgresql' | 'mysql' | 'mongodb')
|
|
135
152
|
database: 'consenti', // database name when not in above uri
|
|
136
153
|
|
|
137
|
-
//
|
|
138
|
-
poolMax:
|
|
154
|
+
// PostgreSQL / MySQL / MongoDB — connection pool
|
|
155
|
+
poolMax: 5, // max connections (default: 5 — shared hosts often cap connections per
|
|
156
|
+
// database user; raise it on a dedicated server)
|
|
157
|
+
// PostgreSQL / MySQL only
|
|
139
158
|
statementTimeoutMs: 30_000, // abort a query after N ms — unset by default; a forced
|
|
140
159
|
// default could break a legitimate long-running export
|
|
141
160
|
idleInTransactionTimeoutMs: 30_000, // Postgres only: kill a connection idle inside an open transaction after N ms
|
|
@@ -198,13 +217,20 @@ const consenti = createConsenti({
|
|
|
198
217
|
// Geo-based auto routing (recommended)
|
|
199
218
|
type: 'auto', // 'auto' = geo-resolve per visitor | ComplianceGroupId = one fixed group globally
|
|
200
219
|
geoDataProvider: 'default', // 'default' (timezone+language heuristic, zero-dep) | 'hosted-geoip-lite' (no install, calls ipinfo.io) | 'geoip' (optional peer dep) | 'maxmind' (optional peer dep) | CountryResolverFn
|
|
201
|
-
complianceMap: 'default', // 'default' = embedded 240-country map | a URL string (fetched, refreshed per Cache-Control/Expires, 24h fallback) | an operator-supplied ComplianceMapData object
|
|
220
|
+
complianceMap: 'default', // 'default' = embedded 240-country map | a URL string (fetched, refreshed per Cache-Control/Expires, 24h fallback) | an operator-supplied ComplianceMapData object. The supplied map replaces the embedded one (only the countries you list resolve); set `override: false` in the document to merge your countries over the embedded map instead (unlisted ones keep their built-in group)
|
|
202
221
|
// complianceMap: 'https://consenti.dev/data/v1/compliance-map.json',
|
|
203
222
|
// A region under overriddenRegions can set requiresSensitiveOptIn: true — a per-region carve-out
|
|
204
223
|
// (not a different group) denying cpraCategory:'sensitive' cookies by default for that region only.
|
|
205
224
|
// The embedded map sets this for Colorado's CPA already; only takes effect with a geoip/maxmind
|
|
206
225
|
// geoDataProvider (the timezone/language heuristic never resolves a US state).
|
|
207
226
|
|
|
227
|
+
// Server-side Global Privacy Control enforcement on POST/PUT /consent. When a write carries
|
|
228
|
+
// gpcDetected: true the server denies CPRA sale/sharing cookies (CPRA) or cookies with listenGpc
|
|
229
|
+
// (every other regulation), whatever the client sent; skipped for DPDPA. false = off. true / 'strict' /
|
|
230
|
+
// omitted = on ('strict' currently behaves like true). The banner's own GPC behaviour is the per-profile
|
|
231
|
+
// gpcMode — a separate setting.
|
|
232
|
+
gpc: true,
|
|
233
|
+
|
|
208
234
|
// IAB TCF v2.3
|
|
209
235
|
tcf: {
|
|
210
236
|
enabled: false,
|
|
@@ -236,8 +262,7 @@ const consenti = createConsenti({
|
|
|
236
262
|
|
|
237
263
|
// Data retention
|
|
238
264
|
dataRetention: {
|
|
239
|
-
purgeAfterDays: 365, // delete consent records older than N days; runs nightly
|
|
240
|
-
auditLogPurgeAfterDays: 730, // optional, independent from purgeAfterDays — unset = keep forever
|
|
265
|
+
purgeAfterDays: 365, // delete consent records older than N days; runs nightly (audit logs are never purged)
|
|
241
266
|
},
|
|
242
267
|
},
|
|
243
268
|
|
|
@@ -454,7 +479,7 @@ Gated by `tenant_settings.setup_completed` (per-tenant in multi-tenant mode) —
|
|
|
454
479
|
|
|
455
480
|
## Storage Directory Structure
|
|
456
481
|
|
|
457
|
-
`storage.path` is a **directory path
|
|
482
|
+
`storage.path` is a **directory path**, and it is **required for every driver** — including PostgreSQL, MySQL and MongoDB. The database holds consent records, users, templates and so on, but compiled profiles are always written to disk under `${storage.path}/profiles/` so the public `GET` routes can serve them with zero DB access. Consenti creates the following layout automatically:
|
|
458
483
|
|
|
459
484
|
```
|
|
460
485
|
${storage.path}/
|
|
@@ -474,6 +499,8 @@ ${storage.path}/
|
|
|
474
499
|
logs/ # reserved for future structured logging
|
|
475
500
|
```
|
|
476
501
|
|
|
502
|
+
With `postgresql` / `mysql` / `mongodb`, `db/` stays empty (the data lives in your database), but `profiles/` is still used — so keep `storage.path` set and on **persistent, writable** storage. Running more than one instance? Point every instance's `storage.path` at the same shared volume, otherwise a profile saved on one instance won't be served by the others.
|
|
503
|
+
|
|
477
504
|
**Key rules:**
|
|
478
505
|
- Every profile save writes to a new immutable version directory under `${profileId}/${version}/`
|
|
479
506
|
- `${complianceGroup}/` only exists when a profile is **active** for that group — it's a directory symlink (junction on Windows), not a copy
|
|
@@ -484,6 +511,48 @@ ${storage.path}/
|
|
|
484
511
|
|
|
485
512
|
**Backward compat:** If `storage.path` has a file extension (`.json`, `.db`), the parent directory is used and a deprecation warning is logged.
|
|
486
513
|
|
|
514
|
+
### Table and collection names
|
|
515
|
+
|
|
516
|
+
With **PostgreSQL, MySQL/MariaDB and MongoDB** every table (collection) is named with a fixed `consenti_` prefix — `consenti_tenants`, `consenti_profiles`, `consenti_consent_records`, `consenti_users`, `consenti_schema_version`, … (17 in all) — so Consenti can never collide with, or silently adopt, a table of the same name from another app in the same database. The prefix is not configurable. SQLite and the JSON store keep plain names: their data lives in a file Consenti owns.
|
|
517
|
+
|
|
518
|
+
If the database still holds the old **unprefixed** tables from a version before 0.6.0 and no prefixed ones, Consenti refuses to start and says so, instead of creating an empty set next to your data. See [Upgrading from before 0.6.0](#upgrading-from-before-060).
|
|
519
|
+
|
|
520
|
+
### Shared hosting and restricted database users
|
|
521
|
+
|
|
522
|
+
Consenti runs fine against a database you can't administer — the cPanel / Plesk / managed-database case where the database and its user are created in a control panel and the user can only touch that one database. It never runs `CREATE DATABASE`, `USE`, `CREATE EXTENSION`, `GRANT`, triggers or stored procedures: it connects to the database named in your config, creates its `consenti_*` tables on first start and upgrades them in place later.
|
|
523
|
+
|
|
524
|
+
| Driver | What the database user needs |
|
|
525
|
+
|---|---|
|
|
526
|
+
| MySQL / MariaDB | `SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX, REFERENCES` on that database (`ALL PRIVILEGES`, the shared-host default, covers it) |
|
|
527
|
+
| PostgreSQL | `CREATE` on the schema it connects to (normally `public` — the database owner has it) plus `SELECT, INSERT, UPDATE, DELETE` |
|
|
528
|
+
| MongoDB | the `readWrite` role on that database |
|
|
529
|
+
|
|
530
|
+
Things to know:
|
|
531
|
+
|
|
532
|
+
- **Use a dedicated database** (or at least don't share it with an app that also has `consenti_*` tables). The `consenti_` prefix keeps Consenti's tables from clashing with *other* apps' tables, but one database per app is still the clean setup.
|
|
533
|
+
- **Set `storage.path` anyway** — profiles are files, see [Storage Directory Structure](#storage-directory-structure). It must be a writable directory that persists across restarts.
|
|
534
|
+
- **Connections:** the pool defaults to 5 per process, because hosts commonly cap connections per database user at 5–25 (shared with everything else on the account). Lower it with `storage.poolMax` if you see `too many connections`, raise it on a dedicated server. Remember each app instance has its own pool.
|
|
535
|
+
- **Names are used as given.** cPanel prefixes names (`cpuser_consenti`, `cpuser_app`) — use the full string. With a connection string (`CONSENTI_DB_URI`), URL-encode special characters in the password (`@` → `%40`, `/` → `%2F`, `#` → `%23`), or use the separate `host` / `user` / `password` / `database` fields instead.
|
|
536
|
+
- **Versions:** MySQL 8+ / MariaDB 10.6+ (the dashboard statistics use `JSON_TABLE`, added in MariaDB 10.6 and MySQL 8.0.4), PostgreSQL 13+, MongoDB 6+.
|
|
537
|
+
- **Several app instances** can start at once against an empty database: the first start is serialised with a named lock (MySQL/MariaDB) / advisory lock (PostgreSQL), neither needs a special privilege.
|
|
538
|
+
- **A first start that dies halfway** (dropped connection, host limit) is safe on every driver: PostgreSQL rolls back; MySQL/MariaDB can't roll back DDL, so every statement is re-runnable and the version table is written last — fix the cause and start again. If the user lacks a privilege the error names the ones needed.
|
|
539
|
+
|
|
540
|
+
### Upgrading from before 0.6.0
|
|
541
|
+
|
|
542
|
+
Applies to **PostgreSQL, MySQL/MariaDB and MongoDB** only (SQLite and JSON: nothing to do). 0.6.0 renamed the tables to `consenti_*`; rename your existing ones once, before starting the new version:
|
|
543
|
+
|
|
544
|
+
```bash
|
|
545
|
+
# 1. Back up the database and stop the app.
|
|
546
|
+
# 2. Dry run — prints what would be renamed, changes nothing.
|
|
547
|
+
# Uses the same CONSENTI_DB_* variables as the app (or --driver, --uri, --host/--port/--user/--password, --database).
|
|
548
|
+
node node_modules/@consenti/api/scripts/migrate-to-consenti-prefix.mjs
|
|
549
|
+
# 3. Rename.
|
|
550
|
+
node node_modules/@consenti/api/scripts/migrate-to-consenti-prefix.mjs --apply
|
|
551
|
+
# 4. Start the app — it finishes any remaining schema upgrade itself.
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
The script only renames — no rows are read, copied or deleted — so it takes moments whatever the database size, and it is safe to re-run. MySQL/MariaDB renames every table in one atomic statement, PostgreSQL in one transaction, MongoDB collection by collection (each keeps its data and indexes). It refuses to overwrite a `consenti_*` table that already exists. It needs `ALTER` + `DROP` on the old tables and `CREATE` + `INSERT` on the new ones (MySQL; `ALL PRIVILEGES` on the database covers it), table ownership (PostgreSQL) or the `renameCollection` action (MongoDB; `readWrite` includes it). Anything else that reads these tables directly — reports, BI tools — must switch to the new names.
|
|
555
|
+
|
|
487
556
|
---
|
|
488
557
|
|
|
489
558
|
## Public REST API
|
|
@@ -695,7 +764,7 @@ available account-hardening, not a login gate, until wired into the login flow.
|
|
|
695
764
|
| `POST` | `/profiles` | Create a profile — 422 on missing mandatory content or compliance errors; `{ conflict, requiresChoice: true }` when an active profile exists on the same `complianceGroup` |
|
|
696
765
|
| `GET` | `/profiles/:id` | Get a profile |
|
|
697
766
|
| `PUT` | `/profiles/:id` | Edit a profile — mutates the row **in place**, increments `version`; returned `id` always matches the URL's. Same mandatory-content + compliance validation + conflict detection |
|
|
698
|
-
| `DELETE` | `/profiles/:id` | Delete a profile
|
|
767
|
+
| `DELETE` | `/profiles/:id` | Delete a profile — removes the DB row only; version snapshots, consent records and the audit entry are kept |
|
|
699
768
|
| `POST` | `/profiles/:id/copy` | Duplicate a profile as a new, always-inactive profile (`version` resets to 1). Optional `{ name }` body, defaults to `Copy of {name}` |
|
|
700
769
|
| `POST` | `/profiles/:id/activate` | Activate a profile — writes locale JSON files to `${complianceGroup}/` |
|
|
701
770
|
| `POST` | `/profiles/:id/deactivate` | Deactivate a profile — removes `${complianceGroup}/` locale files |
|
|
@@ -1022,7 +1091,7 @@ Served at `{basePath}/` when `dashboard: true`.
|
|
|
1022
1091
|
| `name` | `string` | Human-readable profile name |
|
|
1023
1092
|
| `defaultLocale` | `string` (BCP 47) | Locale served when visitor locale is unavailable; written as `default.json` |
|
|
1024
1093
|
| `complianceGroup` | `ComplianceGroupId` | Regulation group for geo-routing (e.g. `opt-in`, `opt-out-strict`) |
|
|
1025
|
-
| `customComplianceGroup` | `string` (lower-kebab-case)
|
|
1094
|
+
| `customComplianceGroup` | `string` (lower-kebab-case, 1–64 chars of `a-z 0-9 - _`, starting with a letter or digit; not a reserved name such as `con`/`nul`, and not starting with `prof_`) | Required when `complianceGroup` is unset ("None / Custom") — it's the identifier the widget's `compliance.type` config uses to fetch this profile (see [Public REST API](#public-rest-api) hot-serve path). Drives activation, deactivation, and "one active profile per group" conflict detection the same way `complianceGroup` does; no `COMPLIANCE_GROUPS` validation rules or GPC defaults apply to it, since none exist for a free-form name. |
|
|
1026
1095
|
| `gpcMode` | `'ignore' \| 'honor' \| 'strict'` | GPC signal handling. Overrides group default. |
|
|
1027
1096
|
| `expiryDays` | `number` (default `365`) | Days until stored consent expires and the visitor is asked again. Profile-wide — replaces the old per-parameter expiry field. |
|
|
1028
1097
|
| `darkMode` | `boolean` | Enable dark mode in the consent banner |
|
|
@@ -1431,7 +1500,7 @@ All config fields can be set via environment variables. Code config takes preced
|
|
|
1431
1500
|
| `CONSENTI_ADMIN_MASTER_SECRET` | `auth.masterSecret` | random per restart |
|
|
1432
1501
|
| `CONSENTI_DATA_SIGNING_HASH` | `compliance.dataSigningHash` | random per restart |
|
|
1433
1502
|
| `CONSENTI_DB_DRIVER` | `storage.driver` | `'json'` |
|
|
1434
|
-
| `CONSENTI_DB_PATH` | `storage.path`
|
|
1503
|
+
| `CONSENTI_DB_PATH` | `storage.path` — **always needed, for every driver** (profiles live here) | `'./consenti-data'` |
|
|
1435
1504
|
| `CONSENTI_DB_URI` | `storage.uri` | — |
|
|
1436
1505
|
| `CONSENTI_DB_DATABASE` | `storage.database` | — |
|
|
1437
1506
|
| `CONSENTI_DB_HOST` | `storage.host` | — |
|
|
@@ -1442,6 +1511,69 @@ All config fields can be set via environment variables. Code config takes preced
|
|
|
1442
1511
|
| `CONSENTI_RATE_LIMIT_MAX_REQUESTS` | `rateLimit.maxRequests` | `60` |
|
|
1443
1512
|
| `CONSENTI_MAX_BODY_SIZE` | `maxBodySize` | `1048576` (1 MB) |
|
|
1444
1513
|
|
|
1514
|
+
### Storage `.env` examples
|
|
1515
|
+
|
|
1516
|
+
`CONSENTI_DB_PATH` is set in **every** example below — profiles are always stored on disk, whichever database you pick. Install the matching driver package first (see [Database / Storage](#database--storage)).
|
|
1517
|
+
|
|
1518
|
+
**JSON file** (development only — no install):
|
|
1519
|
+
|
|
1520
|
+
```bash
|
|
1521
|
+
CONSENTI_DB_DRIVER=json
|
|
1522
|
+
CONSENTI_DB_PATH=./consenti-data
|
|
1523
|
+
```
|
|
1524
|
+
|
|
1525
|
+
**SQLite** (`node:sqlite` needs Node 22.5+; or `better-sqlite3` / `node-sqlite3-wasm`):
|
|
1526
|
+
|
|
1527
|
+
```bash
|
|
1528
|
+
CONSENTI_DB_DRIVER=node:sqlite
|
|
1529
|
+
CONSENTI_DB_PATH=./consenti-data # holds db/consenti.db and profiles/
|
|
1530
|
+
```
|
|
1531
|
+
|
|
1532
|
+
**PostgreSQL** (`npm install pg`) — either a connection string…
|
|
1533
|
+
|
|
1534
|
+
```bash
|
|
1535
|
+
CONSENTI_DB_DRIVER=postgresql
|
|
1536
|
+
CONSENTI_DB_PATH=./consenti-data # still required: profiles are stored here
|
|
1537
|
+
CONSENTI_DB_URI=postgresql://consenti:your-password@localhost:5432/consenti
|
|
1538
|
+
```
|
|
1539
|
+
|
|
1540
|
+
…or individual fields (ignored when `CONSENTI_DB_URI` is set):
|
|
1541
|
+
|
|
1542
|
+
```bash
|
|
1543
|
+
CONSENTI_DB_DRIVER=postgresql
|
|
1544
|
+
CONSENTI_DB_PATH=./consenti-data
|
|
1545
|
+
CONSENTI_DB_HOST=localhost
|
|
1546
|
+
CONSENTI_DB_PORT=5432
|
|
1547
|
+
CONSENTI_DB_USER=consenti
|
|
1548
|
+
CONSENTI_DB_PASSWORD=your-password
|
|
1549
|
+
CONSENTI_DB_DATABASE=consenti
|
|
1550
|
+
```
|
|
1551
|
+
|
|
1552
|
+
**MySQL / MariaDB** (`npm install mysql2`):
|
|
1553
|
+
|
|
1554
|
+
```bash
|
|
1555
|
+
CONSENTI_DB_DRIVER=mysql
|
|
1556
|
+
CONSENTI_DB_PATH=./consenti-data # still required: profiles are stored here
|
|
1557
|
+
CONSENTI_DB_HOST=localhost
|
|
1558
|
+
CONSENTI_DB_PORT=3306
|
|
1559
|
+
CONSENTI_DB_USER=consenti
|
|
1560
|
+
CONSENTI_DB_PASSWORD=your-password
|
|
1561
|
+
CONSENTI_DB_DATABASE=consenti
|
|
1562
|
+
# or, instead of the five lines above:
|
|
1563
|
+
# CONSENTI_DB_URI=mysql://consenti:your-password@localhost:3306/consenti
|
|
1564
|
+
```
|
|
1565
|
+
|
|
1566
|
+
**MongoDB** (`npm install mongodb`):
|
|
1567
|
+
|
|
1568
|
+
```bash
|
|
1569
|
+
CONSENTI_DB_DRIVER=mongodb
|
|
1570
|
+
CONSENTI_DB_PATH=./consenti-data # still required: profiles are stored here
|
|
1571
|
+
CONSENTI_DB_URI=mongodb://consenti:your-password@localhost:27017/?authSource=admin
|
|
1572
|
+
CONSENTI_DB_DATABASE=consenti # database name (default: consenti)
|
|
1573
|
+
```
|
|
1574
|
+
|
|
1575
|
+
The same settings in code: `storage: { driver: 'mysql', path: './consenti-data', host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: 'consenti' }`.
|
|
1576
|
+
|
|
1445
1577
|
---
|
|
1446
1578
|
|
|
1447
1579
|
## Interactive API Docs
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function V(e,n){let a;return function(...o){a!==void 0&&clearTimeout(a),a=setTimeout(()=>{e.apply(this,o)},n)}}function W(e,n){let a=0;return function(...o){let t=Date.now();t-a>=n&&(a=t,e.apply(this,o))}}function P(){return typeof process>"u"||process.env.NO_COLOR?!1:process.env.FORCE_COLOR?!0:!!process.stdout?.isTTY}function u(e,n){return a=>P()?`\x1B[${e}m${a}\x1B[${n}m`:a}var r={bold:u(1,22),dim:u(2,22),red:u(31,39),green:u(32,39),yellow:u(33,39),blue:u(34,39),magenta:u(35,39),cyan:u(36,39),gray:u(90,39)},b={pass:e=>r.bold(r.green(e)),fail:e=>r.bold(r.red(e)),error:e=>r.bold(r.red(e)),"not-applicable":e=>r.gray(e),"new-baseline":e=>r.bold(r.blue(e))};function Z(e,n=10){let a=e.toUpperCase().padEnd(n);return b[e](a)}var C={info:{symbol:"\u2139",paint:r.cyan},success:{symbol:"\u2713",paint:r.green},warn:{symbol:"\u26A0",paint:r.yellow},error:{symbol:"\u2717",paint:r.red}};function $(e,n){let{symbol:a,paint:o}=C[e];return`${o(a)} ${n}`}var ee=["opt-in","opt-out","opt-out-strict","opt-in-dpdpa","opt-in-china","opt-in-brazil","general-privacy-consent","notice-only"],ne={"opt-out-strict":{label:"Opt-out Strict (CPRA / California)",description:"California CPRA: opt-out with sale/sharing/sensitive categories required. GPC must be honored. Stricter than general US state laws.",compliances:["cpra"],defaultGpc:"honor",gpcAutoHonor:!0,allowsLegitimateInterest:!1,allowsTcf:!1,requiresCpraCategory:!0,requiresDpdpaDisclosure:!1},"opt-out":{label:"Opt-out (US State Laws)",description:"US state privacy laws. Collect first, allow opt-out of sale/share. Covers CCPA and all state-level equivalents.",compliances:["ccpa","vcdpa","cpa-co","ctdpa","ucpa","icdpa-ia","icdpa-in","tipa","mcdpa-mt","mcdpa-mn","ocpa","tdpsa","dpdpa-de","njdpa","nhpa","ndpa-ne","modpa","kcdpa","ridtpa"],defaultGpc:"honor",gpcAutoHonor:!0,allowsLegitimateInterest:!1,allowsTcf:!1,requiresCpraCategory:!1,requiresDpdpaDisclosure:!1},"notice-only":{label:"Notice Only",description:"A privacy notice is generally sufficient. No dedicated cookie banner law is encoded for this jurisdiction.",compliances:["privacy-fj","privacy-pg"],defaultGpc:"ignore",gpcAutoHonor:!1,allowsLegitimateInterest:!0,allowsTcf:!1,requiresCpraCategory:!1,requiresDpdpaDisclosure:!1},"opt-in":{label:"Standard Opt-in (GDPR)",description:"GDPR / ePrivacy style: prior consent required before non-essential cookies. Granular consent, easy reject, easy withdrawal.",compliances:["gdpr","eprivacy","uk-gdpr","pecr","revfadp","kvkk","pdpa-th","pdpl-sa","uae-pdpl","difc-dpl","adgm-dpr","qpdl-qa","pdpl-bh","pdpl-om","law25"],defaultGpc:"ignore",gpcAutoHonor:!1,allowsLegitimateInterest:!0,allowsTcf:!0,requiresCpraCategory:!1,requiresDpdpaDisclosure:!1},"opt-in-dpdpa":{label:"Opt-in (India DPDPA)",description:"India's Digital Personal Data Protection Act. Opt-in consent with data fiduciary disclosure. No legitimate interest basis.",compliances:["dpdpa"],defaultGpc:"ignore",gpcAutoHonor:!1,allowsLegitimateInterest:!1,allowsTcf:!1,requiresCpraCategory:!1,requiresDpdpaDisclosure:!0},"opt-in-china":{label:"Opt-in (China PIPL / DSL)",description:"China PIPL + DSL + CSL: strict opt-in with purpose specification, data minimisation, and cross-border transfer controls.",compliances:["pipl","dsl-cn","csl-cn"],defaultGpc:"ignore",gpcAutoHonor:!1,allowsLegitimateInterest:!1,allowsTcf:!1,requiresCpraCategory:!1,requiresDpdpaDisclosure:!1},"opt-in-brazil":{label:"Opt-in (Brazil LGPD)",description:"Brazil LGPD: opt-in consent required. Legitimate interest is a valid basis. Analytics and advertising may differ in treatment.",compliances:["lgpd"],defaultGpc:"ignore",gpcAutoHonor:!1,allowsLegitimateInterest:!0,allowsTcf:!1,requiresCpraCategory:!1,requiresDpdpaDisclosure:!1},"general-privacy-consent":{label:"General Privacy Consent",description:"Privacy notice or consent rules exist but the jurisdiction does not have a GDPR/PECR-style strict cookie banner law. Documenting legal basis is best practice.",compliances:["pipeda","popia","appi","pipa-kr","pdpa-sg","pdpa-my","dpa-ph","pdp-id","pdpd-vn","privacy-au","privacy-nz","pdpl-hk","pdpa-mo","pdpl-tw","privacy-bn","privacy-lk","privacy-np","privacy-kw","privacy-il","ndpa-ng","dpa-ke","dpa-gh","dpa-ug","dpa-rw","dpa-mu","dpa-bw","dpa-zm","dpa-zw","dpa-eg","dpa-ma","dpa-tn","dpa-dz","lfpdppp","lpdp-ar","lpdp-pe","law1581-co","law18331-uy","law19628-cl","lopdp-ec","lpdp-cr","data-protection-pa","data-protection-do"],defaultGpc:"ignore",gpcAutoHonor:!1,allowsLegitimateInterest:!0,allowsTcf:!1,requiresCpraCategory:!1,requiresDpdpaDisclosure:!1}},ae=[{value:"ignore",label:"Ignore",description:"Do not check or respond to the GPC browser signal."},{value:"honor",label:"Honor",description:"Respect GPC as a valid opt-out signal. Show GPC variant banner once."},{value:"strict",label:"Strict",description:"Silently deny all non-mandatory cookies when GPC is detected. No banner shown."}],oe=[{value:"mandatory",label:"Strictly Necessary",description:"Required for the service to function. No consent needed."},{value:"consent",label:"Consent",description:"Requires explicit user agreement. User can withdraw at any time."},{value:"legitimate_interest",label:"Legitimate Interest",description:"Based on legitimate business interest. User has the right to object."}],te=[{value:"sale",label:"Sale",description:"Sale of personal data to third parties for their own use."},{value:"sharing",label:"Sharing",description:"Sharing for cross-context behavioral advertising (even without payment)."},{value:"sensitive",label:"Sensitive",description:"Sensitive personal information (geolocation, health, race, biometrics, etc.). Requires opt-in even in California."}],ie=["necessary","functional","preferences","analytics","marketing"],re=[{value:"necessary",label:"Necessary",description:"Strictly required to deliver the service or ensure security/compliance. Always granted."},{value:"functional",label:"Functional",description:"Optional features that enhance functionality but are not essential."},{value:"preferences",label:"Preferences",description:'Remember user choices and personalize the experience ("Remember me").'},{value:"analytics",label:"Analytics",description:"Measure usage and performance to improve the product."},{value:"marketing",label:"Marketing",description:"Advertising, profiling, cross-site tracking, retargeting, ad personalization."}],ce={necessary:{legalBasis:"mandatory",listenGpc:!1},functional:{legalBasis:"legitimate_interest",listenGpc:!1},preferences:{legalBasis:"legitimate_interest",listenGpc:!1},analytics:{legalBasis:"consent",listenGpc:!0},marketing:{legalBasis:"consent",listenGpc:!0,cpraCategory:"sharing"}},S={security_storage:"necessary",functionality_storage:"functional",personalization_storage:"preferences",analytics_storage:"analytics",ad_storage:"marketing",ad_user_data:"marketing",ad_personalization:"marketing"};function le(e){return S[e.id]??"functional"}var se=[{id:1,name:"Store and/or access information on a device",description:"Cookies, device identifiers, or other information can be stored or accessed on your device."},{id:2,name:"Select basic ads",description:"Ads can be shown based on the content you're viewing, the app you're using, or your approximate location."},{id:3,name:"Create a personalised ads profile",description:"A profile can be built about you and your interests to show you personalised ads."},{id:4,name:"Select personalised ads",description:"Personalised ads can be shown to you based on a profile about you."},{id:5,name:"Create a personalised content profile",description:"A profile can be built about you and your interests to show you personalised content."},{id:6,name:"Select personalised content",description:"Personalised content can be shown to you based on a profile about you."},{id:7,name:"Measure ad performance",description:"The performance and effectiveness of ads that you see or interact with can be measured."},{id:8,name:"Measure content performance",description:"The performance and effectiveness of content that you see or interact with can be measured."},{id:9,name:"Apply market research to generate audience insights",description:"Market research can be used to learn more about the audiences who visit sites or use apps."},{id:10,name:"Develop and improve products",description:"Your data can be used to improve existing systems and software, and to develop new products."},{id:11,name:"Use limited data to select content",description:"Content can be selected based on limited data such as the website you are visiting."}],pe=[{id:1,name:"Use precise geolocation data",description:"Your precise location (within a radius of less than 500 metres) can be used."},{id:2,name:"Actively scan device characteristics for identification",description:"Your device can be identified based on a scan of your device's unique combination of characteristics."}];function ue(e,n){for(let a of Object.values(n))if(a.cookies.includes(e))return a}function me(e){let n=new Map;for(let a of Object.values(e))for(let o of a.cookies)n.set(o,a);return n}function z(e,n){return n.get(e)?.legalBasis??"consent"}function de(e,n){return z(e,n)==="mandatory"}var ye={"opt-in":{categoryErrors:[{rule:"legal-basis-required",field:"legalBasis",check:e=>!e.legalBasis,message:e=>`Category "${e}" must have a legal basis set (Consent, Legitimate Interest, or Strictly Necessary) for GDPR compliance.`}],categoryWarnings:[{rule:"legitimate-interest-balancing-test-recommended",field:"legitimateInterestDescription",check:e=>e.legalBasis==="legitimate_interest"&&!String(e.legitimateInterestDescription??"").trim(),message:e=>`Category "${e}" relies on Legitimate Interest but has no balancing-test description. GDPR Art. 6(1)(f) requires you to conduct and be able to demonstrate a Legitimate Interests Assessment \u2014 note that Art. 5(3) ePrivacy still requires consent (LI cannot substitute) for the cookie storage/access itself, so this basis should only cover the downstream processing purpose, not the placement of a non-essential cookie.`,suggestion:`Document your Legitimate Interests Assessment in the category's "Legitimate Interest Description" field.`}],cookieErrors:[],cookieWarnings:[{rule:"pre-grant-opt-in-risk",field:"preGrant",check:(e,n)=>n==="consent"&&e.preGrant===!0,message:e=>`Parameter "${e}" is pre-granted, which may violate GDPR's opt-in requirement. Only use "Pre Grant" on custom profiles targeting jurisdictions where pre-granted consent is valid.`,suggestion:'Disable "Pre Grant" unless this profile specifically targets a jurisdiction that permits pre-granted consent.'}]},"opt-out":{categoryErrors:[],categoryWarnings:[],cookieErrors:[],cookieWarnings:[{rule:"gpc-recommended",field:"listenGpc",check:(e,n)=>n!=="mandatory"&&!e.listenGpc,message:e=>`Parameter "${e}" does not listen for the GPC signal. US state privacy laws require honoring GPC for opt-out of sale/sharing.`,suggestion:'Enable "Listen for GPC signal" on this parameter.'}]},"opt-out-strict":{categoryErrors:[],categoryWarnings:[],cookieErrors:[{rule:"cpra-category-required",field:"cpraCategory",check:(e,n)=>n!=="mandatory"&&!e.cpraCategory,message:e=>`Parameter "${e}" must have a CPRA category (Sale, Sharing, or Sensitive) assigned. Required under CPRA for all non-necessary parameters.`},{rule:"gpc-strict-required",field:"listenGpc",check:e=>(e.cpraCategory==="sale"||e.cpraCategory==="sharing")&&!e.listenGpc,message:e=>`Parameter "${e}" is tagged Sale/Sharing but does not listen for the GPC signal. 11 CCR \xA77025 requires honoring GPC as a Do Not Sell/Share My Personal Information request for these parameters \u2014 this is mandatory, not optional.`}],cookieWarnings:[]},"opt-in-dpdpa":{categoryErrors:[{rule:"no-li-allowed",field:"legalBasis",check:e=>e.legalBasis==="legitimate_interest",message:e=>`Category "${e}" uses Legitimate Interest, which is not a valid legal basis under India's DPDPA. Use Consent or Strictly Necessary.`},{rule:"legal-basis-required",field:"legalBasis",check:e=>!e.legalBasis,message:e=>`Category "${e}" must have a legal basis set (Consent or Strictly Necessary) for DPDPA compliance.`}],categoryWarnings:[],cookieErrors:[],cookieWarnings:[{rule:"pre-grant-opt-in-risk",field:"preGrant",check:(e,n)=>n==="consent"&&e.preGrant===!0,message:e=>`Parameter "${e}" is pre-granted, which may violate DPDPA's opt-in requirement. Only use "Pre Grant" on custom profiles targeting jurisdictions where pre-granted consent is valid.`,suggestion:'Disable "Pre Grant" unless this profile specifically targets a jurisdiction that permits pre-granted consent.'}]},"opt-in-china":{categoryErrors:[{rule:"no-li-allowed",field:"legalBasis",check:e=>e.legalBasis==="legitimate_interest",message:e=>`Category "${e}" uses Legitimate Interest, which is not one of PIPL Article 13's enumerated legal bases. Use Consent or Strictly Necessary.`},{rule:"legal-basis-required",field:"legalBasis",check:e=>!e.legalBasis,message:e=>`Category "${e}" must have a legal basis set for China PIPL compliance. Consent is the primary valid basis.`}],categoryWarnings:[],cookieErrors:[],cookieWarnings:[{rule:"pre-grant-opt-in-risk",field:"preGrant",check:(e,n)=>n==="consent"&&e.preGrant===!0,message:e=>`Parameter "${e}" is pre-granted, which may violate PIPL's opt-in requirement. Only use "Pre Grant" on custom profiles targeting jurisdictions where pre-granted consent is valid.`,suggestion:'Disable "Pre Grant" unless this profile specifically targets a jurisdiction that permits pre-granted consent.'}]},"opt-in-brazil":{categoryErrors:[{rule:"legal-basis-required",field:"legalBasis",check:e=>!e.legalBasis,message:e=>`Category "${e}" must have a legal basis set. Brazil's LGPD requires one of ten legal bases; Consent or Legitimate Interest are most common for parameters.`}],categoryWarnings:[{rule:"legitimate-interest-balancing-test-recommended",field:"legitimateInterestDescription",check:e=>e.legalBasis==="legitimate_interest"&&!String(e.legitimateInterestDescription??"").trim(),message:e=>`Category "${e}" relies on Legitimate Interest but has no balancing-test description. LGPD Art. 10 requires the legitimate interest basis to be documented and, on request, made available to the ANPD as a Data Protection Impact Report (relat\xF3rio de impacto).`,suggestion:`Document your legitimate interest assessment in the category's "Legitimate Interest Description" field.`}],cookieErrors:[],cookieWarnings:[{rule:"pre-grant-opt-in-risk",field:"preGrant",check:(e,n)=>n==="consent"&&e.preGrant===!0,message:e=>`Parameter "${e}" is pre-granted, which may violate LGPD's opt-in requirement. Only use "Pre Grant" on custom profiles targeting jurisdictions where pre-granted consent is valid.`,suggestion:'Disable "Pre Grant" unless this profile specifically targets a jurisdiction that permits pre-granted consent.'}]},"general-privacy-consent":{categoryErrors:[],categoryWarnings:[{rule:"legal-basis-recommended",field:"legalBasis",check:e=>!e.legalBasis,message:e=>`Category "${e}" has no legal basis documented. While a strict cookie banner may not be legally required in this jurisdiction, documenting legal basis is a privacy best practice.`,suggestion:"Set a legal basis to improve compliance documentation and future-proof against stricter enforcement."},{rule:"legitimate-interest-balancing-test-recommended",field:"legitimateInterestDescription",check:e=>e.legalBasis==="legitimate_interest"&&!String(e.legitimateInterestDescription??"").trim(),message:e=>`Category "${e}" relies on Legitimate Interest but has no balancing-test description documented. Most legitimate-interest-style bases in this jurisdiction group expect a documented justification, and it future-proofs the profile against stricter enforcement.`,suggestion:`Document your legitimate interest justification in the category's "Legitimate Interest Description" field.`}],cookieErrors:[],cookieWarnings:[]},"notice-only":{categoryErrors:[],categoryWarnings:[],cookieErrors:[],cookieWarnings:[]}},d={version:"2026-07",countries:{AT:{name:"Austria",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Austria (GDPR)",locales:["de-AT"],compliance:["gdpr","eprivacy"],timezones:["Europe/Vienna"]},BE:{name:"Belgium",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Belgium (GDPR)",locales:["nl-BE","fr-BE"],compliance:["gdpr","eprivacy"],timezones:["Europe/Brussels"]},BG:{name:"Bulgaria",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Bulgaria (GDPR)",locales:["bg-BG"],compliance:["gdpr","eprivacy"],timezones:["Europe/Sofia"]},HR:{name:"Croatia",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Croatia (GDPR)",locales:["hr-HR"],compliance:["gdpr","eprivacy"],timezones:["Europe/Zagreb"]},CY:{name:"Cyprus",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Cyprus (GDPR)",locales:["el-CY"],compliance:["gdpr","eprivacy"],timezones:["Asia/Nicosia"]},CZ:{name:"Czechia",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Czechia (GDPR)",locales:["cs-CZ"],compliance:["gdpr","eprivacy"],timezones:["Europe/Prague"]},DK:{name:"Denmark",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Denmark (GDPR)",locales:["da-DK"],compliance:["gdpr","eprivacy"],timezones:["Europe/Copenhagen"]},EE:{name:"Estonia",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Estonia (GDPR)",locales:["et-EE"],compliance:["gdpr","eprivacy"],timezones:["Europe/Tallinn"]},FI:{name:"Finland",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Finland (GDPR)",locales:["fi-FI"],compliance:["gdpr","eprivacy"],timezones:["Europe/Helsinki"]},FR:{name:"France",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"France (GDPR)",locales:["fr-FR"],compliance:["gdpr","eprivacy"],timezones:["Europe/Paris"]},DE:{name:"Germany",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Germany (GDPR)",locales:["de-DE"],compliance:["gdpr","eprivacy"],timezones:["Europe/Berlin"]},GR:{name:"Greece",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Greece (GDPR)",locales:["el-GR"],compliance:["gdpr","eprivacy"],timezones:["Europe/Athens"]},HU:{name:"Hungary",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Hungary (GDPR)",locales:["hu-HU"],compliance:["gdpr","eprivacy"],timezones:["Europe/Budapest"]},IE:{name:"Ireland",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Ireland (GDPR)",locales:["en"],compliance:["gdpr","eprivacy"],timezones:["Europe/Dublin"]},IT:{name:"Italy",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Italy (GDPR)",locales:["it-IT"],compliance:["gdpr","eprivacy"],timezones:["Europe/Rome"]},LV:{name:"Latvia",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Latvia (GDPR)",locales:["lv-LV"],compliance:["gdpr","eprivacy"],timezones:["Europe/Riga"]},LT:{name:"Lithuania",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Lithuania (GDPR)",locales:["lt-LT"],compliance:["gdpr","eprivacy"],timezones:["Europe/Vilnius"]},LU:{name:"Luxembourg",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Luxembourg (GDPR)",locales:["fr-LU","de-LU"],compliance:["gdpr","eprivacy"],timezones:["Europe/Luxembourg"]},MT:{name:"Malta",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Malta (GDPR)",locales:["mt-MT","en"],compliance:["gdpr","eprivacy"],timezones:["Europe/Malta"]},NL:{name:"Netherlands",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Netherlands (GDPR)",locales:["nl-NL"],compliance:["gdpr","eprivacy"],timezones:["Europe/Amsterdam"]},PL:{name:"Poland",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Poland (GDPR)",locales:["pl-PL"],compliance:["gdpr","eprivacy"],timezones:["Europe/Warsaw"]},PT:{name:"Portugal",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Portugal (GDPR)",locales:["pt-PT"],compliance:["gdpr","eprivacy"],timezones:["Europe/Lisbon","Atlantic/Madeira","Atlantic/Azores"]},RO:{name:"Romania",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Romania (GDPR)",locales:["ro-RO"],compliance:["gdpr","eprivacy"],timezones:["Europe/Bucharest"]},SK:{name:"Slovakia",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Slovakia (GDPR)",locales:["sk-SK"],compliance:["gdpr","eprivacy"],timezones:["Europe/Bratislava"]},SI:{name:"Slovenia",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Slovenia (GDPR)",locales:["sl-SI"],compliance:["gdpr","eprivacy"],timezones:["Europe/Ljubljana"]},ES:{name:"Spain",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Spain (GDPR)",locales:["es-ES"],compliance:["gdpr","eprivacy"],timezones:["Europe/Madrid","Africa/Ceuta","Atlantic/Canary"]},SE:{name:"Sweden",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Sweden (GDPR)",locales:["sv-SE"],compliance:["gdpr","eprivacy"],timezones:["Europe/Stockholm"]},IS:{name:"Iceland",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Iceland (GDPR via EEA)",locales:["is-IS"],compliance:["gdpr","eprivacy"],timezones:["Atlantic/Reykjavik"]},LI:{name:"Liechtenstein",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Liechtenstein (GDPR via EEA)",locales:["de-LI"],compliance:["gdpr","eprivacy"],timezones:["Europe/Vaduz"]},NO:{name:"Norway",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Norway (GDPR via EEA)",locales:["nb-NO"],compliance:["gdpr","eprivacy"],timezones:["Europe/Oslo","Arctic/Longyearbyen"]},AD:{name:"Andorra",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Andorra (GDPR-aligned)",locales:["ca-AD"],compliance:["gdpr"],timezones:["Europe/Andorra"]},AL:{name:"Albania",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Albania (GDPR-aligned)",locales:["sq-AL"],compliance:["gdpr"],timezones:["Europe/Tirane"]},BA:{name:"Bosnia and Herzegovina",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Bosnia and Herzegovina (GDPR-aligned)",locales:["bs-BA","sr-Latn-BA"],compliance:["gdpr"],timezones:["Europe/Sarajevo"]},GG:{name:"Guernsey",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Guernsey (DPA based on GDPR)",locales:["en"],compliance:["gdpr"],timezones:["Europe/Guernsey"]},GI:{name:"Gibraltar",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Gibraltar (DPA based on UK GDPR)",locales:["en"],compliance:["uk-gdpr"],timezones:["Europe/Gibraltar"]},GL:{name:"Greenland",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Greenland (Danish GDPR implementation)",locales:["da-GL"],compliance:["gdpr"],timezones:["America/Nuuk","America/Danmarkshavn","America/Scoresbysund","America/Thule"]},MC:{name:"Monaco",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Monaco (GDPR-aligned)",locales:["fr-MC"],compliance:["gdpr"],timezones:["Europe/Monaco"]},ME:{name:"Montenegro",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Montenegro (GDPR-aligned)",locales:["sr-Latn-ME"],compliance:["gdpr"],timezones:["Europe/Podgorica"]},MK:{name:"North Macedonia",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"North Macedonia (GDPR-aligned)",locales:["mk-MK"],compliance:["gdpr"],timezones:["Europe/Skopje"]},RS:{name:"Serbia",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Serbia (GDPR-aligned)",locales:["sr-RS"],compliance:["gdpr"],timezones:["Europe/Belgrade"]},SM:{name:"San Marino",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"San Marino (GDPR-aligned)",locales:["it-SM"],compliance:["gdpr"],timezones:["Europe/San_Marino"]},TR:{name:"T\xFCrkiye",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"T\xFCrkiye (KVKK)",locales:["tr-TR"],compliance:["kvkk"],timezones:["Europe/Istanbul"]},CA:{name:"Canada",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Canada (PIPEDA / Law 25)",locales:["en","fr-CA"],compliance:["pipeda","law25"],timezones:["America/St_Johns","America/Halifax","America/Toronto","America/Winnipeg","America/Edmonton","America/Vancouver"],overriddenRegions:{QC:{name:"Quebec",type:"region",complianceGroup:"opt-in",description:"Quebec \u2014 Law 25 (GDPR-style opt-in)"}}},US:{name:"United States",type:"country",complianceGroup:"opt-out",default:"opt-out-strict",description:"United States \u2014 opt-out-strict (CPRA) used when state cannot be detected",locales:["en","es-US"],compliance:["ccpa","cpra","vcdpa","cpa-co","ctdpa","ucpa","icdpa-ia","icdpa-in","tipa","mcdpa-mt","mcdpa-mn","ocpa","tdpsa","dpdpa-de","njdpa","nhpa","ndpa-ne","modpa","kcdpa","ridtpa"],timezones:["America/New_York","America/Chicago","America/Denver","America/Phoenix","America/Los_Angeles","America/Anchorage","Pacific/Honolulu"],overriddenRegions:{CA:{name:"California",type:"region",complianceGroup:"opt-out-strict",description:"California \u2014 CPRA/CCPA"},VA:{name:"Virginia",type:"region",complianceGroup:"opt-out",description:"Virginia \u2014 VCDPA"},CO:{name:"Colorado",type:"region",complianceGroup:"opt-out",description:"Colorado \u2014 CPA",requiresSensitiveOptIn:!0},CT:{name:"Connecticut",type:"region",complianceGroup:"opt-out",description:"Connecticut \u2014 CTDPA"},UT:{name:"Utah",type:"region",complianceGroup:"opt-out",description:"Utah \u2014 UCPA"},TX:{name:"Texas",type:"region",complianceGroup:"opt-out",description:"Texas \u2014 TDPSA"},DE:{name:"Delaware",type:"region",complianceGroup:"opt-out",description:"Delaware \u2014 DPDPA"},NJ:{name:"New Jersey",type:"region",complianceGroup:"opt-out",description:"New Jersey \u2014 NJDPA"},OR:{name:"Oregon",type:"region",complianceGroup:"opt-out",description:"Oregon \u2014 OCPA"},MN:{name:"Minnesota",type:"region",complianceGroup:"opt-out",description:"Minnesota \u2014 MCDPA"},MD:{name:"Maryland",type:"region",complianceGroup:"opt-out",description:"Maryland \u2014 MODPA"},KY:{name:"Kentucky",type:"region",complianceGroup:"opt-out",description:"Kentucky \u2014 KCDPA"},TN:{name:"Tennessee",type:"region",complianceGroup:"opt-out",description:"Tennessee \u2014 TIPA"},NE:{name:"Nebraska",type:"region",complianceGroup:"opt-out",description:"Nebraska \u2014 NDPA"},IA:{name:"Iowa",type:"region",complianceGroup:"opt-out",description:"Iowa \u2014 ICDPA"},IN:{name:"Indiana",type:"region",complianceGroup:"opt-out",description:"Indiana \u2014 ICDPA"},MT:{name:"Montana",type:"region",complianceGroup:"opt-out",description:"Montana \u2014 MCDPA"},NH:{name:"New Hampshire",type:"region",complianceGroup:"opt-out",description:"New Hampshire \u2014 NHPA"},RI:{name:"Rhode Island",type:"region",complianceGroup:"opt-out",description:"Rhode Island \u2014 RIDTPA"}}},AR:{name:"Argentina",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Argentina (LPDP)",locales:["es-AR"],compliance:["lpdp-ar"],timezones:["America/Argentina/Buenos_Aires"]},BO:{name:"Bolivia",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Bolivia",locales:["es-BO"],compliance:[],timezones:["America/La_Paz"]},BR:{name:"Brazil",type:"country",complianceGroup:"opt-in-brazil",default:"opt-in-brazil",description:"Brazil (LGPD)",locales:["pt-BR"],compliance:["lgpd"],timezones:["America/Noronha","America/Sao_Paulo","America/Manaus","America/Rio_Branco"]},CL:{name:"Chile",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Chile (Law 19.628)",locales:["es-CL"],compliance:["law19628-cl"],timezones:["America/Santiago","Pacific/Easter"]},CO:{name:"Colombia",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Colombia (Law 1581)",locales:["es-CO"],compliance:["law1581-co"],timezones:["America/Bogota"]},CR:{name:"Costa Rica",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Costa Rica (LPDP)",locales:["es-CR"],compliance:["lpdp-cr"],timezones:["America/Costa_Rica"]},DO:{name:"Dominican Republic",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Dominican Republic",locales:["es-DO"],compliance:["data-protection-do"],timezones:["America/Santo_Domingo"]},EC:{name:"Ecuador",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Ecuador (LOPDP)",locales:["es-EC"],compliance:["lopdp-ec"],timezones:["America/Guayaquil","Pacific/Galapagos"]},GF:{name:"French Guiana",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"French Guiana",locales:["fr-GF"],compliance:[],timezones:["America/Cayenne"]},GT:{name:"Guatemala",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Guatemala",locales:["es-GT"],compliance:[],timezones:["America/Guatemala"]},GY:{name:"Guyana",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Guyana",locales:["en"],compliance:[],timezones:["America/Guyana"]},HN:{name:"Honduras",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Honduras",locales:["es-HN"],compliance:[],timezones:["America/Tegucigalpa"]},HT:{name:"Haiti",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Haiti",locales:["fr-HT"],compliance:[],timezones:["America/Port-au-Prince"]},JM:{name:"Jamaica",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Jamaica",locales:["en"],compliance:[],timezones:["America/Jamaica"]},MX:{name:"Mexico",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Mexico (LFPDPPP)",locales:["es-MX"],compliance:["lfpdppp"],timezones:["America/Mexico_City","America/Cancun","America/Chihuahua","America/Hermosillo","America/Tijuana"]},NI:{name:"Nicaragua",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Nicaragua",locales:["es-NI"],compliance:[],timezones:["America/Managua"]},PA:{name:"Panama",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Panama",locales:["es-PA"],compliance:["data-protection-pa"],timezones:["America/Panama"]},PE:{name:"Peru",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Peru (LPDP)",locales:["es-PE"],compliance:["lpdp-pe"],timezones:["America/Lima"]},PY:{name:"Paraguay",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Paraguay",locales:["es-PY"],compliance:[],timezones:["America/Asuncion"]},SR:{name:"Suriname",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Suriname",locales:["nl-SR"],compliance:[],timezones:["America/Paramaribo"]},SV:{name:"El Salvador",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"El Salvador",locales:["es-SV"],compliance:[],timezones:["America/El_Salvador"]},TT:{name:"Trinidad and Tobago",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Trinidad and Tobago",locales:["en"],compliance:[],timezones:["America/Port_of_Spain"]},UY:{name:"Uruguay",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Uruguay (Law 18.331)",locales:["es-UY"],compliance:["law18331-uy"],timezones:["America/Montevideo"]},VE:{name:"Venezuela",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Venezuela",locales:["es-VE"],compliance:[],timezones:["America/Caracas"]},AG:{name:"Antigua and Barbuda",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Antigua and Barbuda",locales:["en"],compliance:[],timezones:["America/Antigua"]},BB:{name:"Barbados",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Barbados",locales:["en"],compliance:[],timezones:["America/Barbados"]},BL:{name:"Saint Barth\xE9lemy",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Saint Barth\xE9lemy",locales:["fr-BL"],compliance:[],timezones:["America/St_Barthelemy"]},BM:{name:"Bermuda",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Bermuda",locales:["en"],compliance:[],timezones:["Atlantic/Bermuda"]},BS:{name:"Bahamas",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Bahamas",locales:["en"],compliance:[],timezones:["America/Nassau"]},CU:{name:"Cuba",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Cuba",locales:["es-CU"],compliance:[],timezones:["America/Havana"]},DM:{name:"Dominica",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Dominica",locales:["en"],compliance:[],timezones:["America/Dominica"]},GD:{name:"Grenada",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Grenada",locales:["en"],compliance:[],timezones:["America/Grenada"]},KN:{name:"Saint Kitts and Nevis",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Saint Kitts and Nevis",locales:["en"],compliance:[],timezones:["America/St_Kitts"]},LC:{name:"Saint Lucia",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Saint Lucia",locales:["en"],compliance:[],timezones:["America/St_Lucia"]},PM:{name:"Saint Pierre and Miquelon",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Saint Pierre and Miquelon",locales:["fr-PM"],compliance:[],timezones:["America/Miquelon"]},PR:{name:"Puerto Rico",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Puerto Rico",locales:["es-PR","en"],compliance:[],timezones:["America/Puerto_Rico"]},VC:{name:"Saint Vincent and the Grenadines",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Saint Vincent and the Grenadines",locales:["en"],compliance:[],timezones:["America/St_Vincent"]},IN:{name:"India",type:"country",complianceGroup:"opt-in-dpdpa",default:"opt-in-dpdpa",description:"India (DPDPA)",locales:["hi-IN","en","bn-IN","ta-IN"],compliance:["dpdpa"],timezones:["Asia/Kolkata","Asia/Calcutta"]},CN:{name:"China",type:"country",complianceGroup:"opt-in-china",default:"opt-in-china",description:"China (PIPL + DSL + CSL)",locales:["zh-CN"],compliance:["pipl","dsl-cn","csl-cn"],timezones:["Asia/Shanghai"]},BN:{name:"Brunei",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Brunei (Personal Data Protection Order)",locales:["ms-BN","en"],compliance:["privacy-bn"],timezones:["Asia/Brunei"]},ID:{name:"Indonesia",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Indonesia (PDP Law)",locales:["id-ID"],compliance:["pdp-id"],timezones:["Asia/Jakarta","Asia/Makassar","Asia/Jayapura"]},KH:{name:"Cambodia",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Cambodia",locales:["km-KH"],compliance:[],timezones:["Asia/Phnom_Penh"]},LA:{name:"Laos",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Laos",locales:["lo-LA"],compliance:[],timezones:["Asia/Vientiane"]},MM:{name:"Myanmar",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Myanmar",locales:["my-MM"],compliance:[],timezones:["Asia/Yangon"]},MY:{name:"Malaysia",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Malaysia (PDPA)",locales:["ms-MY","zh-MY","en"],compliance:["pdpa-my"],timezones:["Asia/Kuala_Lumpur","Asia/Kuching"]},PH:{name:"Philippines",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Philippines (DPA)",locales:["fil-PH","en"],compliance:["dpa-ph"],timezones:["Asia/Manila"]},SG:{name:"Singapore",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Singapore (PDPA)",locales:["zh-SG","ms-SG","ta-SG","en"],compliance:["pdpa-sg"],timezones:["Asia/Singapore"]},TH:{name:"Thailand",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Thailand (PDPA \u2014 GDPR-style opt-in)",locales:["th-TH"],compliance:["pdpa-th"],timezones:["Asia/Bangkok"]},TL:{name:"Timor-Leste",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Timor-Leste",locales:["pt-TL"],compliance:[],timezones:["Asia/Dili"]},VN:{name:"Vietnam",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Vietnam (PDPD)",locales:["vi-VN"],compliance:["pdpd-vn"],timezones:["Asia/Ho_Chi_Minh"]},HK:{name:"Hong Kong",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Hong Kong SAR (PDPO)",locales:["zh-HK"],compliance:["pdpl-hk"],timezones:["Asia/Hong_Kong"]},JP:{name:"Japan",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Japan (APPI)",locales:["ja-JP"],compliance:["appi"],timezones:["Asia/Tokyo"]},KP:{name:"North Korea",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"North Korea",locales:["ko-KP"],compliance:[],timezones:["Asia/Pyongyang"]},KR:{name:"South Korea",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"South Korea (PIPA)",locales:["ko-KR"],compliance:["pipa-kr"],timezones:["Asia/Seoul"]},MN:{name:"Mongolia",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Mongolia",locales:["mn-MN"],compliance:[],timezones:["Asia/Ulaanbaatar","Asia/Hovd"]},MO:{name:"Macau",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Macau SAR (PDPA)",locales:["zh-MO","pt-MO"],compliance:["pdpa-mo"],timezones:["Asia/Macau"]},TW:{name:"Taiwan",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Taiwan (PDPL)",locales:["zh-TW"],compliance:["pdpl-tw"],timezones:["Asia/Taipei"]},AF:{name:"Afghanistan",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Afghanistan",locales:["fa-AF"],compliance:[],timezones:["Asia/Kabul"]},BD:{name:"Bangladesh",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Bangladesh",locales:["bn-BD"],compliance:[],timezones:["Asia/Dhaka"]},BT:{name:"Bhutan",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Bhutan",locales:["dz-BT"],compliance:[],timezones:["Asia/Thimphu"]},IO:{name:"British Indian Ocean Territory",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"British Indian Ocean Territory",locales:["en"],compliance:[],timezones:["Indian/Chagos"]},LK:{name:"Sri Lanka",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Sri Lanka",locales:["si-LK","ta-LK"],compliance:["privacy-lk"],timezones:["Asia/Colombo"]},MV:{name:"Maldives",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Maldives",locales:["dv-MV"],compliance:[],timezones:["Indian/Maldives"]},NP:{name:"Nepal",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Nepal",locales:["ne-NP"],compliance:["privacy-np"],timezones:["Asia/Kathmandu"]},PK:{name:"Pakistan",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Pakistan",locales:["ur-PK","en"],compliance:[],timezones:["Asia/Karachi"]},AM:{name:"Armenia",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Armenia",locales:["hy-AM"],compliance:[],timezones:["Asia/Yerevan"]},AZ:{name:"Azerbaijan",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Azerbaijan",locales:["az-AZ"],compliance:[],timezones:["Asia/Baku"]},GE:{name:"Georgia",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Georgia",locales:["ka-GE"],compliance:[],timezones:["Asia/Tbilisi"]},KG:{name:"Kyrgyzstan",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Kyrgyzstan",locales:["ky-KG","ru-KG"],compliance:[],timezones:["Asia/Bishkek"]},KZ:{name:"Kazakhstan",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Kazakhstan",locales:["kk-KZ","ru-KZ"],compliance:[],timezones:["Asia/Almaty","Asia/Aqtobe"]},TJ:{name:"Tajikistan",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Tajikistan",locales:["tg-TJ"],compliance:[],timezones:["Asia/Dushanbe"]},TM:{name:"Turkmenistan",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Turkmenistan",locales:["tk-TM"],compliance:[],timezones:["Asia/Ashgabat"]},UZ:{name:"Uzbekistan",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Uzbekistan",locales:["uz-UZ"],compliance:[],timezones:["Asia/Tashkent","Asia/Samarkand"]},AE:{name:"United Arab Emirates",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"United Arab Emirates (UAE PDPL / DIFC DPL / ADGM DPR)",locales:["ar-AE"],compliance:["uae-pdpl","difc-dpl","adgm-dpr"],timezones:["Asia/Dubai"]},BH:{name:"Bahrain",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Bahrain (PDPL)",locales:["ar-BH"],compliance:["pdpl-bh"],timezones:["Asia/Bahrain"]},IL:{name:"Israel",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Israel (Privacy Protection Law)",locales:["he-IL","ar-IL"],compliance:["privacy-il"],timezones:["Asia/Jerusalem"]},IQ:{name:"Iraq",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Iraq",locales:["ar-IQ"],compliance:[],timezones:["Asia/Baghdad"]},IR:{name:"Iran",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Iran",locales:["fa-IR"],compliance:[],timezones:["Asia/Tehran"]},JO:{name:"Jordan",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Jordan",locales:["ar-JO"],compliance:[],timezones:["Asia/Amman"]},KW:{name:"Kuwait",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Kuwait",locales:["ar-KW"],compliance:["privacy-kw"],timezones:["Asia/Kuwait"]},LB:{name:"Lebanon",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Lebanon",locales:["ar-LB"],compliance:[],timezones:["Asia/Beirut"]},OM:{name:"Oman",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Oman (PDPL)",locales:["ar-OM"],compliance:["pdpl-om"],timezones:["Asia/Muscat"]},PS:{name:"Palestine",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Palestine",locales:["ar-PS"],compliance:[],timezones:["Asia/Gaza","Asia/Hebron"]},QA:{name:"Qatar",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Qatar (PDPL)",locales:["ar-QA"],compliance:["qpdl-qa"],timezones:["Asia/Qatar"]},SA:{name:"Saudi Arabia",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Saudi Arabia (PDPL)",locales:["ar-SA"],compliance:["pdpl-sa"],timezones:["Asia/Riyadh"]},SY:{name:"Syria",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Syria",locales:["ar-SY"],compliance:[],timezones:["Asia/Damascus"]},YE:{name:"Yemen",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Yemen",locales:["ar-YE"],compliance:[],timezones:["Asia/Aden"]},BF:{name:"Burkina Faso",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Burkina Faso",locales:["fr-BF"],compliance:[],timezones:["Africa/Ouagadougou"]},BJ:{name:"Benin",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Benin",locales:["fr-BJ"],compliance:[],timezones:["Africa/Porto-Novo"]},CI:{name:"C\xF4te d'Ivoire",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"C\xF4te d'Ivoire",locales:["fr-CI"],compliance:[],timezones:["Africa/Abidjan"]},CM:{name:"Cameroon",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Cameroon",locales:["fr-CM","en"],compliance:[],timezones:["Africa/Douala"]},CV:{name:"Cabo Verde",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Cabo Verde",locales:["pt-CV"],compliance:[],timezones:["Atlantic/Cape_Verde"]},GA:{name:"Gabon",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Gabon",locales:["fr-GA"],compliance:[],timezones:["Africa/Libreville"]},GH:{name:"Ghana",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Ghana (DPA)",locales:["en"],compliance:["dpa-gh"],timezones:["Africa/Accra"]},GM:{name:"Gambia",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Gambia",locales:["en"],compliance:[],timezones:["Africa/Banjul"]},GN:{name:"Guinea",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Guinea",locales:["fr-GN"],compliance:[],timezones:["Africa/Conakry"]},GQ:{name:"Equatorial Guinea",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Equatorial Guinea",locales:["es-GQ","fr-GQ"],compliance:[],timezones:["Africa/Malabo"]},GW:{name:"Guinea-Bissau",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Guinea-Bissau",locales:["pt-GW"],compliance:[],timezones:["Africa/Bissau"]},LR:{name:"Liberia",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Liberia",locales:["en"],compliance:[],timezones:["Africa/Monrovia"]},ML:{name:"Mali",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Mali",locales:["fr-ML"],compliance:[],timezones:["Africa/Bamako"]},MR:{name:"Mauritania",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Mauritania",locales:["ar-MR"],compliance:[],timezones:["Africa/Nouakchott"]},NE:{name:"Niger",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Niger",locales:["fr-NE"],compliance:[],timezones:["Africa/Niamey"]},NG:{name:"Nigeria",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Nigeria (NDPA)",locales:["en"],compliance:["ndpa-ng"],timezones:["Africa/Lagos"]},SL:{name:"Sierra Leone",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Sierra Leone",locales:["en"],compliance:[],timezones:["Africa/Freetown"]},SN:{name:"Senegal",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Senegal",locales:["fr-SN"],compliance:[],timezones:["Africa/Dakar"]},TG:{name:"Togo",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Togo",locales:["fr-TG"],compliance:[],timezones:["Africa/Lome"]},BI:{name:"Burundi",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Burundi",locales:["fr-BI"],compliance:[],timezones:["Africa/Bujumbura"]},DJ:{name:"Djibouti",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Djibouti",locales:["fr-DJ","ar-DJ"],compliance:[],timezones:["Africa/Djibouti"]},ER:{name:"Eritrea",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Eritrea",locales:["ti-ER"],compliance:[],timezones:["Africa/Asmara"]},ET:{name:"Ethiopia",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Ethiopia",locales:["am-ET"],compliance:[],timezones:["Africa/Addis_Ababa"]},KE:{name:"Kenya",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Kenya (DPA)",locales:["sw-KE","en"],compliance:["dpa-ke"],timezones:["Africa/Nairobi"]},KM:{name:"Comoros",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Comoros",locales:["ar-KM","fr-KM"],compliance:[],timezones:["Indian/Comoro"]},MG:{name:"Madagascar",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Madagascar",locales:["fr-MG"],compliance:[],timezones:["Indian/Antananarivo"]},MU:{name:"Mauritius",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Mauritius (DPA)",locales:["en","fr-MU"],compliance:["dpa-mu"],timezones:["Indian/Mauritius"]},MW:{name:"Malawi",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Malawi",locales:["en"],compliance:[],timezones:["Africa/Blantyre"]},MZ:{name:"Mozambique",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Mozambique",locales:["pt-MZ"],compliance:[],timezones:["Africa/Maputo"]},RW:{name:"Rwanda",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Rwanda (DPA)",locales:["rw-RW","en"],compliance:["dpa-rw"],timezones:["Africa/Kigali"]},SC:{name:"Seychelles",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Seychelles",locales:["fr-SC","en"],compliance:[],timezones:["Indian/Mahe"]},SO:{name:"Somalia",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Somalia",locales:["so-SO"],compliance:[],timezones:["Africa/Mogadishu"]},SS:{name:"South Sudan",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"South Sudan",locales:["en"],compliance:[],timezones:["Africa/Juba"]},ST:{name:"S\xE3o Tom\xE9 and Pr\xEDncipe",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"S\xE3o Tom\xE9 and Pr\xEDncipe",locales:["pt-ST"],compliance:[],timezones:["Africa/Sao_Tome"]},TZ:{name:"Tanzania",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Tanzania",locales:["sw-TZ","en"],compliance:[],timezones:["Africa/Dar_es_Salaam"]},UG:{name:"Uganda",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Uganda (DPA)",locales:["en"],compliance:["dpa-ug"],timezones:["Africa/Kampala"]},AO:{name:"Angola",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Angola",locales:["pt-AO"],compliance:[],timezones:["Africa/Luanda"]},BW:{name:"Botswana",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Botswana (DPA)",locales:["en"],compliance:["dpa-bw"],timezones:["Africa/Gaborone"]},LS:{name:"Lesotho",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Lesotho",locales:["en"],compliance:[],timezones:["Africa/Maseru"]},NA:{name:"Namibia",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Namibia",locales:["en"],compliance:[],timezones:["Africa/Windhoek"]},SZ:{name:"Eswatini",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Eswatini",locales:["en"],compliance:[],timezones:["Africa/Mbabane"]},ZA:{name:"South Africa",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"South Africa (POPIA)",locales:["en","af-ZA"],compliance:["popia"],timezones:["Africa/Johannesburg"]},ZM:{name:"Zambia",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Zambia (DPA)",locales:["en"],compliance:["dpa-zm"],timezones:["Africa/Lusaka"]},ZW:{name:"Zimbabwe",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Zimbabwe (DPA)",locales:["en"],compliance:["dpa-zw"],timezones:["Africa/Harare"]},DZ:{name:"Algeria",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Algeria",locales:["ar-DZ","fr-DZ"],compliance:["dpa-dz"],timezones:["Africa/Algiers"]},EG:{name:"Egypt",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Egypt (DPA)",locales:["ar-EG"],compliance:["dpa-eg"],timezones:["Africa/Cairo"]},LY:{name:"Libya",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Libya",locales:["ar-LY"],compliance:[],timezones:["Africa/Tripoli"]},MA:{name:"Morocco",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Morocco",locales:["ar-MA","fr-MA"],compliance:["dpa-ma"],timezones:["Africa/Casablanca"]},SD:{name:"Sudan",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Sudan",locales:["ar-SD"],compliance:[],timezones:["Africa/Khartoum"]},TN:{name:"Tunisia",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Tunisia (DPA)",locales:["ar-TN","fr-TN"],compliance:["dpa-tn"],timezones:["Africa/Tunis"]},CD:{name:"DR Congo",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"DR Congo",locales:["fr-CD"],compliance:[],timezones:["Africa/Kinshasa","Africa/Lubumbashi"]},CF:{name:"Central African Republic",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Central African Republic",locales:["fr-CF"],compliance:[],timezones:["Africa/Bangui"]},CG:{name:"Republic of Congo",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Republic of Congo",locales:["fr-CG"],compliance:[],timezones:["Africa/Brazzaville"]},TD:{name:"Chad",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Chad",locales:["fr-TD","ar-TD"],compliance:[],timezones:["Africa/Ndjamena"]},BY:{name:"Belarus",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Belarus",locales:["be-BY","ru-BY"],compliance:[],timezones:["Europe/Minsk"]},MD:{name:"Moldova",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Moldova",locales:["ro-MD"],compliance:[],timezones:["Europe/Chisinau"]},RU:{name:"Russia",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Russia",locales:["ru-RU"],compliance:[],timezones:["Europe/Kaliningrad","Europe/Moscow","Europe/Samara","Asia/Yekaterinburg","Asia/Omsk","Asia/Krasnoyarsk","Asia/Irkutsk","Asia/Yakutsk","Asia/Vladivostok","Asia/Magadan","Asia/Kamchatka"]},UA:{name:"Ukraine",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Ukraine",locales:["uk-UA"],compliance:[],timezones:["Europe/Kyiv"]},VA:{name:"Holy See",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Holy See",locales:["it-VA"],compliance:[],timezones:["Europe/Vatican"]},AU:{name:"Australia",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"Australia (Privacy Act)",locales:["en"],compliance:["privacy-au"],timezones:["Australia/Sydney","Australia/Brisbane","Australia/Adelaide","Australia/Darwin","Australia/Perth","Australia/Hobart"]},FJ:{name:"Fiji",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Fiji",locales:["en"],compliance:["privacy-fj"],timezones:["Pacific/Fiji"]},FM:{name:"Micronesia",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Micronesia",locales:["en"],compliance:[],timezones:["Pacific/Chuuk","Pacific/Pohnpei","Pacific/Kosrae"]},KI:{name:"Kiribati",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Kiribati",locales:["en"],compliance:[],timezones:["Pacific/Tarawa","Pacific/Enderbury","Pacific/Kiritimati"]},MH:{name:"Marshall Islands",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Marshall Islands",locales:["en"],compliance:[],timezones:["Pacific/Majuro","Pacific/Kwajalein"]},MP:{name:"Northern Mariana Islands",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Northern Mariana Islands",locales:["en"],compliance:[],timezones:["Pacific/Saipan"]},NC:{name:"New Caledonia",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"New Caledonia",locales:["fr-NC"],compliance:[],timezones:["Pacific/Noumea"]},NF:{name:"Norfolk Island",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Norfolk Island",locales:["en"],compliance:[],timezones:["Pacific/Norfolk"]},NR:{name:"Nauru",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Nauru",locales:["en"],compliance:[],timezones:["Pacific/Nauru"]},NU:{name:"Niue",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Niue",locales:["en"],compliance:[],timezones:["Pacific/Niue"]},NZ:{name:"New Zealand",type:"country",complianceGroup:"general-privacy-consent",default:"general-privacy-consent",description:"New Zealand (Privacy Act)",locales:["en"],compliance:["privacy-nz"],timezones:["Pacific/Auckland","Pacific/Chatham"]},PF:{name:"French Polynesia",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"French Polynesia",locales:["fr-PF"],compliance:[],timezones:["Pacific/Tahiti","Pacific/Marquesas","Pacific/Gambier"]},PG:{name:"Papua New Guinea",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Papua New Guinea",locales:["en"],compliance:["privacy-pg"],timezones:["Pacific/Port_Moresby","Pacific/Bougainville"]},PW:{name:"Palau",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Palau",locales:["en"],compliance:[],timezones:["Pacific/Palau"]},SB:{name:"Solomon Islands",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Solomon Islands",locales:["en"],compliance:[],timezones:["Pacific/Guadalcanal"]},TO:{name:"Tonga",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Tonga",locales:["en"],compliance:[],timezones:["Pacific/Tongatapu"]},TV:{name:"Tuvalu",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Tuvalu",locales:["en"],compliance:[],timezones:["Pacific/Funafuti"]},VU:{name:"Vanuatu",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Vanuatu",locales:["fr-VU","en"],compliance:[],timezones:["Pacific/Efate"]},WS:{name:"Samoa",type:"country",complianceGroup:"notice-only",default:"notice-only",description:"Samoa",locales:["en"],compliance:[],timezones:["Pacific/Apia"]},UK:{name:"United Kingdom",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"United Kingdom (UK GDPR + PECR)",locales:["en"],compliance:["uk-gdpr","pecr"],timezones:["Europe/London"]},CH:{name:"Switzerland",type:"country",complianceGroup:"opt-in",default:"opt-in",description:"Switzerland (revFADP)",locales:["de-CH","fr-CH","it-CH"],compliance:["revfadp"],timezones:["Europe/Zurich"]}}},G={GB:"UK",UK:"GB"};function ge(e,n){let a=G[n];return e[n]??(a!==void 0?e[a]:void 0)}var fe=[...Object.values(d.countries).reduce((e,n)=>(n.compliance.forEach(a=>{e.add(a)}),e),new Set)],ve=Object.entries(d.countries).reduce(function(e,[n,a]){return e[n]=a.name,e},{}),Ge=Object.values(d.countries).reduce(function(e,n){return n.locales.forEach(a=>{e[a]=n.name}),e},{});function k(e){if(!e||typeof e!="object")return!1;let n=e;if(typeof n.version!="string"||n.override!==void 0&&typeof n.override!="boolean")return!1;let a=n.countries;return!a||typeof a!="object"?!1:Object.values(a).every(o=>!!o&&typeof o=="object"&&typeof o.complianceGroup=="string")}async function Ae(e,n){let a={map:d};if(e===void 0||e==="default")return a;let o,t;if(typeof e=="string")try{if(t=await fetch(e),!t.ok)return n(`compliance.complianceMap fetch from "${e}" failed (HTTP ${t.status}) \u2014 falling back to the embedded map.`),a;o=await t.json()}catch(c){return n(`compliance.complianceMap fetch from "${e}" failed (${c instanceof Error?c.message:String(c)}) \u2014 falling back to the embedded map.`),a}else o=e;if(!k(o))return n(`compliance.complianceMap ${typeof e=="string"?`at "${e}"`:"object"} failed validation \u2014 falling back to the embedded map.`),a;for(let[c,l]of Object.entries(o.countries))l.overriddenRegions&&l.default===void 0&&n(`compliance.complianceMap country "${c}" has overriddenRegions but no explicit default \u2014 falling back to complianceGroup ("${l.complianceGroup}"), which may under-comply for regions not listed.`);let i={map:o.override===!1?D(o):o};return t&&(i.response=t),i}function D(e){let n={...d.countries};for(let a of Object.keys(e.countries)){let o=G[a];o!==void 0&&!(o in e.countries)&&delete n[o]}return Object.assign(n,e.countries),{version:e.version,countries:n}}var A=[{pattern:"security_storage",vendor:"Google Consent Mode v2",category:"necessary"},{pattern:"functionality_storage",vendor:"Google Consent Mode v2",category:"functional"},{pattern:"personalization_storage",vendor:"Google Consent Mode v2",category:"preferences"},{pattern:"analytics_storage",vendor:"Google Consent Mode v2",category:"analytics"},{pattern:"ad_storage",vendor:"Google Consent Mode v2",category:"marketing"},{pattern:"ad_user_data",vendor:"Google Consent Mode v2",category:"marketing"},{pattern:"ad_personalization",vendor:"Google Consent Mode v2",category:"marketing"},{pattern:"_ga",vendor:"Google Analytics",category:"analytics"},{pattern:"_ga_*",vendor:"Google Analytics 4",category:"analytics"},{pattern:"_gid",vendor:"Google Analytics",category:"analytics"},{pattern:"_gat",vendor:"Google Analytics",category:"analytics"},{pattern:"_gat_*",vendor:"Google Analytics",category:"analytics"},{pattern:"_gcl_au",vendor:"Google Ads",category:"marketing"},{pattern:"_gac_*",vendor:"Google Ads",category:"marketing"},{pattern:"_gcl_aw",vendor:"Google Ads",category:"marketing"},{pattern:"_fbp",vendor:"Meta Pixel",category:"marketing"},{pattern:"_fbc",vendor:"Meta Pixel",category:"marketing"},{pattern:"fr",vendor:"Meta Pixel",category:"marketing"},{pattern:"_hjSessionUser_*",vendor:"Hotjar",category:"analytics"},{pattern:"_hjSession_*",vendor:"Hotjar",category:"analytics"},{pattern:"_hjid",vendor:"Hotjar",category:"analytics"},{pattern:"hjViewportId",vendor:"Hotjar",category:"analytics"},{pattern:"ajs_user_id",vendor:"Segment",category:"analytics"},{pattern:"ajs_anonymous_id",vendor:"Segment",category:"analytics"},{pattern:"ajs_group_id",vendor:"Segment",category:"analytics"},{pattern:"intercom-id-*",vendor:"Intercom",category:"functional"},{pattern:"intercom-session-*",vendor:"Intercom",category:"functional"},{pattern:"intercom-device-id-*",vendor:"Intercom",category:"functional"},{pattern:"__hstc",vendor:"HubSpot",category:"marketing"},{pattern:"hubspotutk",vendor:"HubSpot",category:"marketing"},{pattern:"__hssc",vendor:"HubSpot",category:"marketing"},{pattern:"__hssrc",vendor:"HubSpot",category:"marketing"},{pattern:"mp_*",vendor:"Mixpanel",category:"analytics"},{pattern:"li_sugr",vendor:"LinkedIn Insight",category:"marketing"},{pattern:"bcookie",vendor:"LinkedIn Insight",category:"marketing"},{pattern:"lidc",vendor:"LinkedIn Insight",category:"marketing"},{pattern:"UserMatchHistory",vendor:"LinkedIn Insight",category:"marketing"},{pattern:"_ttp",vendor:"TikTok Pixel",category:"marketing"},{pattern:"_clck",vendor:"Microsoft Clarity",category:"analytics"},{pattern:"_clsk",vendor:"Microsoft Clarity",category:"analytics"},{pattern:"_pinterest_ct*",vendor:"Pinterest Tag",category:"marketing"},{pattern:"_pin_unauth",vendor:"Pinterest Tag",category:"marketing"},{pattern:"personalization_id",vendor:"X (Twitter) Pixel",category:"marketing"},{pattern:"amplitude_id_*",vendor:"Amplitude",category:"analytics"},{pattern:"__stripe_mid",vendor:"Stripe",category:"necessary"},{pattern:"__stripe_sid",vendor:"Stripe",category:"necessary"},{pattern:"__cf_bm",vendor:"Cloudflare",category:"necessary"},{pattern:"cf_clearance",vendor:"Cloudflare",category:"necessary"},{pattern:"_zendesk_*",vendor:"Zendesk",category:"functional"},{pattern:"drift_aid",vendor:"Drift",category:"functional"},{pattern:"driftt_aid",vendor:"Drift",category:"functional"},{pattern:"crisp-client*",vendor:"Crisp",category:"functional"},{pattern:"AMCV_*",vendor:"Adobe Experience Cloud",category:"analytics"},{pattern:"AMCVS_*",vendor:"Adobe Experience Cloud",category:"analytics"},{pattern:"s_cc",vendor:"Adobe Analytics",category:"analytics"},{pattern:"s_sq",vendor:"Adobe Analytics",category:"analytics"},{pattern:"s_vi",vendor:"Adobe Analytics",category:"analytics"},{pattern:"mbox",vendor:"Adobe Target",category:"preferences"},{pattern:"_mkto_trk",vendor:"Marketo",category:"marketing"},{pattern:"cto_bundle",vendor:"Criteo",category:"marketing"},{pattern:"cto_id_out",vendor:"Criteo",category:"marketing"},{pattern:"TTD_DEVICE_ID",vendor:"The Trade Desk",category:"marketing"},{pattern:"ttd_*",vendor:"The Trade Desk",category:"marketing"},{pattern:"id",vendor:"Google DoubleClick",category:"marketing"},{pattern:"IDE",vendor:"Google DoubleClick",category:"marketing"},{pattern:"DSID",vendor:"Google DoubleClick",category:"marketing"},{pattern:"KADUSERCOOKIE",vendor:"PubMatic",category:"marketing"},{pattern:"rubicon_user_*",vendor:"Rubicon Project",category:"marketing"},{pattern:"Anonymon_id",vendor:"OpenX",category:"marketing"},{pattern:"pdomid",vendor:"OpenX",category:"marketing"},{pattern:"uuid2",vendor:"AppNexus",category:"marketing"},{pattern:"icu",vendor:"AppNexus",category:"marketing"},{pattern:"_scid",vendor:"Snap Pixel",category:"marketing"},{pattern:"_sc_cspv",vendor:"Snap Pixel",category:"marketing"},{pattern:"_rdt_uuid",vendor:"Reddit Pixel",category:"marketing"},{pattern:"obuid",vendor:"Outbrain",category:"marketing"},{pattern:"t_gid",vendor:"Taboola",category:"marketing"},{pattern:"__kla_id",vendor:"Klaviyo",category:"marketing"},{pattern:"sc_anonymous_id",vendor:"Salesforce Marketing Cloud",category:"marketing"},{pattern:"vst",vendor:"Salesforce Marketing Cloud",category:"marketing"},{pattern:"visitor_id*",vendor:"Salesforce Pardot",category:"marketing"},{pattern:"piAId",vendor:"Salesforce Pardot",category:"marketing"},{pattern:"exp_last_visit",vendor:"ActiveCampaign",category:"marketing"},{pattern:"exp_tracker",vendor:"ActiveCampaign",category:"marketing"},{pattern:"_ce.s*",vendor:"Crazy Egg",category:"analytics"},{pattern:"lr_*",vendor:"LogRocket",category:"analytics"},{pattern:"_hp2_id.*",vendor:"Heap Analytics",category:"analytics"},{pattern:"_hp2_ses.*",vendor:"Heap Analytics",category:"analytics"},{pattern:"_lo_*",vendor:"Lucky Orange",category:"analytics"},{pattern:"fs_uid",vendor:"FullStory",category:"analytics"},{pattern:"_fs_*",vendor:"FullStory",category:"analytics"},{pattern:"ln_or",vendor:"LiveSession",category:"analytics"},{pattern:"_vwo_uuid*",vendor:"Visual Website Optimizer (VWO)",category:"analytics"},{pattern:"_vis_opt_*",vendor:"Visual Website Optimizer (VWO)",category:"analytics"},{pattern:"OptimizelyBuckets",vendor:"Optimizely",category:"analytics"},{pattern:"optimizelyEndUserId",vendor:"Optimizely",category:"analytics"},{pattern:"OptanonConsent",vendor:"OneTrust",category:"necessary"},{pattern:"OptanonAlertBoxClosed",vendor:"OneTrust",category:"necessary"},{pattern:"SyrenisCookieConsentDate_*",vendor:"Syrenis Cassie",category:"necessary"},{pattern:"SyrenisCookieFormConsent_*",vendor:"Syrenis Cassie",category:"necessary"},{pattern:"SyrenisCookiePrivacyLink_*",vendor:"Syrenis Cassie",category:"necessary"},{pattern:"SyrenisGtmConsent_*",vendor:"Syrenis Cassie",category:"necessary"},{pattern:"SyrenisGuid_*",vendor:"Syrenis Cassie",category:"necessary"},{pattern:"CookieConsent",vendor:"Cookiebot",category:"necessary"},{pattern:"uc_settings",vendor:"Usercentrics",category:"necessary"},{pattern:"uc_user_interaction",vendor:"Usercentrics",category:"necessary"},{pattern:"didomi_token",vendor:"Didomi",category:"necessary"},{pattern:"euconsent-v2",vendor:"IAB TCF Global Vendor List",category:"necessary"},{pattern:"cconsent",vendor:"Cookie Consent Studio",category:"necessary"},{pattern:"JSESSIONID",vendor:"New Relic",category:"necessary"},{pattern:"NRAGENT",vendor:"New Relic",category:"necessary"},{pattern:"_dd_s",vendor:"Datadog",category:"necessary"},{pattern:"dtCookie",vendor:"Dynatrace",category:"necessary"},{pattern:"dtPC",vendor:"Dynatrace",category:"necessary"},{pattern:"rxVisitor",vendor:"Dynatrace",category:"necessary"},{pattern:"ak_bmsc",vendor:"Akamai",category:"necessary"},{pattern:"_abck",vendor:"Akamai",category:"necessary"},{pattern:"vuid",vendor:"Vimeo",category:"analytics"},{pattern:"__cf_vuid",vendor:"Vimeo",category:"analytics"},{pattern:"VISITOR_INFO1_LIVE",vendor:"YouTube",category:"marketing"},{pattern:"YSC",vendor:"YouTube",category:"necessary"},{pattern:"GPS",vendor:"YouTube",category:"marketing"},{pattern:"_wistia_*",vendor:"Wistia",category:"analytics"},{pattern:"_elvr_*",vendor:"Elevar",category:"analytics"},{pattern:"stkn",vendor:"Shopify Core Analytics",category:"analytics"},{pattern:"rmStore*",vendor:"Rakuten Advertising",category:"marketing"},{pattern:"_ir_*",vendor:"Impact Radius",category:"marketing"},{pattern:"aw*",vendor:"Awin",category:"marketing"},{pattern:"bctc",vendor:"Awin",category:"marketing"},{pattern:"mcookie",vendor:"CJ Affiliate",category:"marketing"},{pattern:"_shopify_y",vendor:"Shopify",category:"analytics"},{pattern:"_shopify_s",vendor:"Shopify",category:"analytics"},{pattern:"_shopify_sa_p",vendor:"Shopify",category:"marketing"},{pattern:"shopify_sa_t",vendor:"Shopify",category:"marketing"},{pattern:"cart_sig",vendor:"Shopify",category:"necessary"},{pattern:"secure_customer_sig",vendor:"Shopify",category:"necessary"},{pattern:"wp_woocommerce_session*",vendor:"WooCommerce",category:"necessary"},{pattern:"woocommerce_items_in_cart",vendor:"WooCommerce",category:"necessary"},{pattern:"woocommerce_cart_hash",vendor:"WooCommerce",category:"necessary"},{pattern:"QSI_",vendor:"Qualtrics",category:"functional"},{pattern:"feedback_id",vendor:"SurveyMonkey",category:"functional"},{pattern:"usbl.",vendor:"Usabilla",category:"functional"}];function be(e){let n=e.trim();if(!n)return;let a=A.find(t=>!t.pattern.endsWith("*")&&t.pattern===n);if(a)return a;let o=A.filter(t=>t.pattern.endsWith("*")&&n.startsWith(t.pattern.slice(0,-1)));if(o.length!==0)return o.reduce((t,i)=>i.pattern.length>t.pattern.length?i:t)}function _(e){return e.map(f).join("")}function f(e){return e.t==="text"?E(e.v):e.t==="br"?"<br>":e.t==="a"?`<a href="${M(e.href)}">${e.c.map(f).join("")}</a>`:`<${e.t}>${e.c.map(f).join("")}</${e.t}>`}function E(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function M(e){return e.replace(/"/g,""").replace(/</g,"<")}function ze(e){if(!e)return"";let n=e.trim();if(n.startsWith("["))try{return _(JSON.parse(n))}catch{}return e}function h(e){return e.t==="text"?e.v.trim().length>0:e.t==="br"?!1:e.c.some(h)}function I(e){return e.replace(/<[^>]*>/g,"").trim().length>0}function ke(e){if(!e)return!1;let n=e.trim();if(!n)return!1;if(n.startsWith("["))try{return JSON.parse(n).some(h)}catch{}return I(n)}var y="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";function L(e){return new TextEncoder().encode(e)}function T(e){return new TextDecoder().decode(e)}function w(e){let n="";for(let a=0;a<e.length;a+=3){let o=e[a]??0,t=a+1<e.length?e[a+1]??0:0,i=a+2<e.length?e[a+2]??0:0;n+=y[o>>2],n+=y[(o&3)<<4|t>>4],a+1<e.length&&(n+=y[(t&15)<<2|i>>6]),a+2<e.length&&(n+=y[i&63])}return n}function R(e){let n=new Map(y.split("").map((t,i)=>[t,i])),a=e.replace(/[^A-Za-z0-9\-_]/g,""),o=[];for(let t=0;t<a.length;t+=4){let i=n.get(a[t]??"")??0,c=n.get(a[t+1]??"")??0,l=a[t+2]!==void 0?n.get(a[t+2]):void 0,m=a[t+3]!==void 0?n.get(a[t+3]):void 0;o.push(i<<2|c>>4),l!==void 0&&o.push((c&15)<<4|l>>2),m!==void 0&&o.push(((l??0)&3)<<6|m)}return new Uint8Array(o)}function Ee(e){let n={v:2,ts:Math.floor(Date.now()/100),cmpId:e.cmpId,cmpV:e.cmpVersion,cs:e.consentScreen,lang:e.consentLanguage.toUpperCase().slice(0,2),vlV:e.vendorListVersion,pc:e.purposeConsents,vc:e.vendorConsents};return w(L(JSON.stringify(n)))}function Me(e){try{let n=JSON.parse(T(R(e)));return{version:n.v,cmpId:n.cmpId,consentLanguage:n.lang,purposeConsents:Array.isArray(n.pc)?n.pc:[],vendorConsents:Array.isArray(n.vc)?n.vc:[],created:n.ts*100}}catch{return null}}var g="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";function O(e){return new TextEncoder().encode(e)}function N(e){return new TextDecoder().decode(e)}function B(e){let n="";for(let a=0;a<e.length;a+=3){let o=e[a]??0,t=a+1<e.length?e[a+1]??0:0,i=a+2<e.length?e[a+2]??0:0;n+=g[o>>2],n+=g[(o&3)<<4|t>>4],a+1<e.length&&(n+=g[(t&15)<<2|i>>6]),a+2<e.length&&(n+=g[i&63])}return n}function x(e){let n=new Map(g.split("").map((t,i)=>[t,i])),a=e.replace(/[^A-Za-z0-9\-_]/g,""),o=[];for(let t=0;t<a.length;t+=4){let i=n.get(a[t]??"")??0,c=n.get(a[t+1]??"")??0,l=a[t+2]!==void 0?n.get(a[t+2]):void 0,m=a[t+3]!==void 0?n.get(a[t+3]):void 0;o.push(i<<2|c>>4),l!==void 0&&o.push((c&15)<<4|l>>2),m!==void 0&&o.push(((l??0)&3)<<6|m)}return new Uint8Array(o)}var K=0,U=1,H=2;function v(e,n){return e?n?U:H:K}function Te(e){let n={v:1,ts:Math.floor(Date.now()/100),cmpId:e.cmpId,cmpV:e.cmpVersion,sect:"usnat",saleOO:v(e.saleApplicable,e.saleOptOut),shareOO:v(e.sharingApplicable,e.sharingOptOut),taOO:v(e.sharingApplicable,e.sharingOptOut),mspaCT:e.mspaCoveredTransaction?1:2,mspaOOM:e.mspaOptOutOptionMode,mspaSPM:e.mspaServiceProviderMode,...e.gpcDetected!==void 0?{gpc:e.gpcDetected}:{}};return B(O(JSON.stringify(n)))}function we(e){try{let n=JSON.parse(N(x(e)));return{version:n.v,cmpId:n.cmpId,cmpVersion:n.cmpV,saleOptOut:n.saleOO,sharingOptOut:n.shareOO,targetedAdvertisingOptOut:n.taOO,...n.gpc!==void 0?{gpc:n.gpc}:{},created:n.ts*100}}catch{return null}}var j=["ar","he","fa","ur","ps","sd","ug","yi"];function Ne(e,n){if(n==="ltr"||n==="rtl")return n;try{let o=new Intl.Locale(e).getTextInfo?.().direction;if(o==="ltr"||o==="rtl")return o}catch{}let a=e.slice(0,2).toLowerCase();return j.includes(a)?"rtl":"ltr"}export{V as a,W as b,r as c,Z as d,$ as e,ee as f,ne as g,ae as h,oe as i,te as j,ie as k,re as l,ce as m,S as n,le as o,se as p,pe as q,ue as r,me as s,z as t,de as u,ye as v,d as w,ge as x,fe as y,ve as z,Ge as A,Ae as B,A as C,be as D,_ as E,ze as F,ke as G,Ee as H,Me as I,Te as J,we as K,Ne as L};
|
|
2
|
+
//# sourceMappingURL=chunk-7NKW7UTJ.js.map
|