@noidme/cmp-migrate
v0.1.0-beta.0
Published
Migrate from another CMP. Convert an incumbent consent-management config (OneTrust, Cookiebot, …) into a noidme.js PolicyConfig so a prospect can switch without re-mapping everything, plus a diff/summary report of what mapped and what didn't. Zero-depende
Maintainers
Readme
@noidme/cmp-migrate
Zero-dependency CMP migration importers. Convert an incumbent consent-management config
(OneTrust, Cookiebot, Ketch, …) into a noidme.js
PolicyConfig so a prospect can switch without re-mapping everything by hand — plus a
diff/summary report of what mapped and what didn't.
Extracted from noidme.js; usable standalone.
A competitor export is untrusted input, and the output is a suggested
PolicyConfigthat a human must review before enforcing. The input shapes are representative of each vendor's export; real exports vary by account and version, so every importer returns a report of what mapped, what didn't, what was rejected, and — critically — which hosts would become never-blocked (report.essentialCandidates).
Install
npm i @noidme/cmp-migrateUsage
import { importOneTrust, importCookiebot, importKetch } from '@noidme/cmp-migrate';
const { config, report } = importOneTrust(oneTrustExport);
// config → a noidme.js PolicyConfig (hosts, essentialSuffixes, …)
// report → { source, entries, processed, byPurpose, unmapped, rejected, essentialCandidates, conflicts }
if (report.essentialCandidates.length) {
// These hosts would be NEVER blocked — the highest-trust decision. Review before enforcing.
console.warn('Review — becomes never-blocked:', report.essentialCandidates);
}
if (report.unmapped.length) console.warn('Category could not be mapped:', report.unmapped);
if (report.rejected.length) console.warn('Dropped as unsafe:', report.rejected);Report fields
| field | meaning |
| --------------------- | ----------------------------------------------------------------------------- |
| entries | total input rows (entries === processed + unmapped.length + rejected.length)|
| processed | rows that mapped to a purpose (summed in byPurpose) |
| byPurpose | count per purpose |
| unmapped | host (category) — category couldn't be mapped |
| rejected | host (reason) — dropped by a safety guard (empty / dangerous / public suffix)|
| essentialCandidates | hosts whose winning purpose is essential → never-blocked; review these |
| conflicts | hosts seen in multiple groups (most-restrictive purpose kept) |
Import stored consent values
import { importConsentValues } from '@noidme/cmp-migrate';
// Turn a competitor's category→granted map into a purpose-keyed ConsentMap.
const consent = importConsentValues({ Analytics: true, Advertising: false });
// { analytics: true, advertising: false }Deny-wins: if several source keys map to the same purpose and any is not granted, the purpose is denied. In a migration, over-blocking is the privacy-safe failure — a sticky grant would silently under-block.
Category mapping
mapCategory(name) maps a vendor category label to a noidme purpose by whole-token keyword
match (advertising, analytics, functional, social, essential), returning null when
nothing matches so the caller can flag it rather than guess. essential is last-resort: a
real tracking category always wins over an incidental security/essential token.
Security
Competitor exports are untrusted input, so the importers actively defend the enforcement boundary:
- No prototype pollution.
__proto__,constructor, andprototypeare dropped from host and consent maps. - No essential-laundering.
essentialmaps last, and a host's most-restrictive purpose across all groups wins — so a tracker taggedAdvertisingin one group andSecurityin another resolves to advertising (blocked), never to the never-blocked allowlist. Every host that does become essential is surfaced inreport.essentialCandidatesfor review. - No public-suffix bypass. A bare TLD or public-suffix host (
com,co.uk) is rejected before it can enteressentialSuffixes, where it would otherwise match every subdomain.
API
importOneTrust(cfg): ImportResultimportCookiebot(cfg): ImportResultimportKetch(cfg): ImportResultimportConsentValues(values, categoryMap?): ConsentMapmapCategory(name): string | null
ImportResult = { config: PolicyConfig; report: ImportReport } (see the report-fields table above).
License
MIT
