Tier 2 — Advanced
⚠️ Beta API — può breakare in MINOR. Annuncio su Slack
#avacy-dev1 settimana prima.Tier 2 è solo via TypeScript import. Non disponibile su
window.Avacy.
Quando usare Tier 2
- Stai scrivendo uno storage provider custom (es. il consenso deve vivere in IndexedDB invece di cookie, oppure in una WebView nativa che espone un bridge custom)
- Stai integrando il CMP in un sistema che ha bisogno di osservare cambi di state del CMP in modo programmatico (non solo via DOM events Tier 1)
Per il controllo programmatico sul timing del boot (animazioni hero, SPA, config async) usa la Tier 1 window.Avacy.init — non serve toccare l'import TS.
Indice export
| Export | Type | Purpose |
|---|---|---|
init | function | (Tier 1 — programmatic boot, vedi Lifecycle) |
registerStoreProvider | function | Aggiungere un nuovo store.type |
CoreEvents | object | Event emitter (mitt) sui cambi di state — advanced, usa solo se i DOM events Tier 1 non bastano |
getConfig | function | Snapshot read-only della config merged |
| Types | type | Vedi Types reference |
serializeRawConsent/parseRawConsentnon sono più esposti:RawConsentè opaco per l'integratore (le custom-build interfacciano conIStoreProviderdirettamente).
registerStoreProvider
Registra un'implementazione di IStoreProvider mappata a un store.type arbitrario. Da chiamare prima di init.
import { registerStoreProvider } from '@avacy/cmp-web';
import type { IStoreProvider, RawConsent } from '@avacy/cmp-web';
class IndexedDbStore implements IStoreProvider {
async readConsent(): Promise<RawConsent | null> {
// … leggi da IndexedDB
}
async writeConsent(consent: RawConsent): Promise<void> {
// … scrivi su IndexedDB
}
}
registerStoreProvider('indexeddb', IndexedDbStore);
await init({
store: { type: 'indexeddb', consentKey: 'avacy_consent' },
// …
});
Behavior
- I tipi built-in
'cookie'e'local-storage'sono sovrascrivibili (last-write-wins, silenzioso) — utile per override avanzati. - Se
config.store.typenon è registrato ainit, throwError: Store provider 'X' not registered. - Registrare dopo
init/initCorenon lancia errori, ma non fa nulla di utile: il registro accetta la scrittura, ma la sessione corrente ha già istanziato il provider — la nuova registrazione vale solo per uninit/reloadsuccessivo. Nessun avviso se lo fai per errore.
See also
IStoreProvider— interfaccia da implementare
CoreEvents
⚠️ Advanced — internal observation hook. Pensato per addon TS o tooling che hanno bisogno di osservare ogni mutazione del state interno. Per la logica consumer-facing usa i DOM events Tier 1 (events.md): coprono i casi normali con un contratto stabile e zero accoppiamento al state shape.
CoreEvents espone bucket interni del CMP — può cambiare in MINOR senza preavviso.
Event emitter (basato su mitt) che notifica ogni mutazione del state interno del CMP. Stesso state che vedi via getData(), ma con notifica push.
import { CoreEvents } from '@avacy/cmp-web';
CoreEvents.on('consent', ({ value, prev }) => {
console.log('consent changed', { value, prev });
});
CoreEvents.on('language', ({ value, prev }) => {
console.log('language changed', { value, prev });
});
Available keys
Una key per ogni bucket di state:
type CoreEventsType = {
core: EventPayload<{ ready: boolean }>;
config: EventPayload<ConfigType>;
storage: EventPayload<{ type: string; technology: string }>;
language: EventPayload<{ words: LanguageLabels; activeLanguage: string }>;
consent: EventPayload<ConsentState>;
customization: EventPayload<{ privacySettings: PrivacySettingsMap; vendorsCount?: number }>;
glossary: EventPayload<GlossaryEntry[]>;
stacks: EventPayload<ResolvedStack[]>;
decodedConsent: EventPayload<DecodedConsent | undefined>;
acmAddtlConsent: EventPayload<{ value: string } | undefined>;
};
CoreEventsTypeè un mapped type suContextData([K in keyof ContextData]: EventPayload<ContextData[K]>) — una key per bucket, automaticamente in sync: seContextDataguadagna un campo,CoreEventsTypelo eredita senza bisogno di aggiornare questo type a mano.
Per il signal "core è ready", subscribe a
'core'(non'ready'): la chiave del bucket ècore, ilready: booleanvive dentro.CoreEvents.on('core', ({ value }) => { if (value.ready) { /* ... */ } });
EventPayload<T> = { value: T; prev: T }.
When to use it (vs DOM events)
| Caso | Usa |
|---|---|
| Reagire a consenso utente in script user-facing | DOM avacy:consent-saved |
| Logging strutturato di tutte le mutazioni state | CoreEvents |
| Bridge nativo, RUM tool, GTM | DOM events |
| Scrivere addon TS che reagisce a cambi di lingua/config | CoreEvents |
💡 Non duplicare la stessa logica su entrambi. Scegli un canale.
getConfig
Snapshot read-only della config merged (utente + remoto + default).
import { getConfig } from '@avacy/cmp-web';
import type { ConfigType } from '@avacy/cmp-web';
const config: Readonly<ConfigType> = getConfig();
console.log(config.cmpVersion, config.policyVersion);
Ritorna null se init non è ancora chiamato.
💡 Per uso "common" (lettura
policyVersion,publisherCountryCode, ecc.), preferiscigetData()Tier 1 — espone gli stessi campi più stato UI e altro.
Types reference
UserConfigType
interface UserConfigType extends Partial<ConfigType> {}
Tutti i campi opzionali; defaults applicati internamente da ConfigProvider. Vedi ConfigType per la shape completa.
ConfigType
interface ConfigType {
store: { type: StoreType; consentKey: string };
core: { autoLoad?: boolean };
frameworks: FrameworkItem[];
loggers: LoggerType[];
language: LanguageCode;
assetPath: string;
remoteConfigName?: string;
remoteConfigUrl?: string;
remoteGVLName?: string;
publisherCountryCode: string;
policyVersion: number;
cmpVersion?: string;
consentExpiryDays: number;
vendorsCount?: number;
ui?: UiConfig;
autoLanguage?: boolean;
customLabels?: Partial<Record<LanguageCode, Partial<LanguageLabels>>>;
filters?: ContextFilters;
}
CmpData
type CmpData = ContextData & {
ui: { cpcOpen: boolean };
};
interface ContextData {
core: { ready: boolean };
config: ConfigType;
/** Tipo/tecnologia dello storage effettivamente in uso (es. 'cookie') — utile
* quando la view deve mostrare la label risolta invece della sola config.store.type. */
storage: { type: string; technology: string };
language: { words: LanguageLabels; activeLanguage: string };
consent: ConsentState;
customization: { privacySettings: PrivacySettingsMap; vendorsCount?: number };
glossary: GlossaryEntry[];
/** Stack IAB risolti, in ordine whitelist. Vuoto se nessun framework configura una whitelist. */
stacks: ResolvedStack[];
decodedConsent?: DecodedConsent;
acmAddtlConsent?: { value: string };
}
Returnato da getData() Tier 1. Il "ready" del CMP vive sotto data.core.ready (non al top-level): consistente con i bucket di CoreEvents. L'ui bucket per ora contiene solo cpcOpen — è il punto di estensione futuro per altri segnali di stato visivo. ResolvedStack è esportato per Tier 2 — vedi Altri types esportati.
ConsentState
type ConsentState =
| { ready: false }
| { ready: true; valid: false }
| {
ready: true;
valid: true;
type: 'AcceptAll' | 'RejectAll' | 'CloseBanner' | 'Personalized';
timestamp: number;
choices: PrivacyChoice[];
};
⚠️
ConsentState(questo type, esportato per Tier 2) NON hapurposes/vendors. Quelle due viste pre-computate esistono solo sulConsentdi Tier 1 (il valore ritornato dagetConsent()), che intersectConsentStatecon{ purposes, vendors, choices }— maConsentstesso non è esportato per l'import TypeScript. Chi scrive codice Tier 2 e ha bisogno di quelle viste le ricalcola dachoices(stesso algoritmo: raggruppa perframework, chiaveid, valorehasConsent), o usagetConsent()da Tier 1 invece diCoreEvents/import diretto.
PrivacyChoice
interface PrivacyChoice {
framework: 'tcf' | 'gcm' | 'custom' | 'acm';
type: 'purpose' | 'vendor' | 'specialFeature' | 'customPurpose' | 'feature' | 'specialPurpose';
id: number;
hasConsent?: boolean;
hasLegitimateInterest?: boolean;
// … altri campi opzionali (details)
}
hasConsent e hasLegitimateInterest sono entrambi opzionali: i due tipi 'feature' e 'specialPurpose' sono disclosure-only (TCF) e non hanno toggle di consenso, quindi non possono apparire nei PrivacySelector accettati da acceptAll({except}) / rejectAll({except}) / updateConsent ma possono comparire in consent.choices come metadata.
IStoreProvider
interface IStoreProvider {
readConsent(): Promise<RawConsent | null>;
writeConsent(consent: RawConsent): Promise<void>;
}
L'unica interfaccia da implementare per scrivere uno store custom. RawConsent è una string opaca — non c'è bisogno di parserla per la maggior parte dei casi (read → leggi raw dal tuo storage; write → salva raw nel tuo storage).
OnConsentPayload
type ConsentScreen = 'first' | 'second' | 'api';
type OnConsentPayload =
| { type: 'AcceptAll'; screen: ConsentScreen }
| { type: 'RejectAll'; screen: ConsentScreen }
| { type: 'CloseBanner'; screen: ConsentScreen }
| { type: 'Personalized'; screen: ConsentScreen; privacyChoices: PrivacyChoice[] };
screen è un campo audit obbligatorio (drive il consentScreen nel TCString). Per le chiamate via window.Avacy.* il bundle stampa sempre 'api'; banner/CPC components passano 'first'/'second' direttamente al core.
Altri types esportati
ConsentPatches—Partial<Record<PrivacySelector, boolean>>, payload diupdateConsentIStoreProviderClass— shape statica della class cheregisterStoreProvideraccettaDecodedConsent,FrameworkDecodedConsent,FrameworkRawConsent,FrameworkRawConsentMapPrivacySettingOnConsentAcceptAllPayload,OnConsentRejectAllPayload,OnConsentCloseBannerPayload,OnConsentPersonalizedPayloadUiConfig,StoreType,LoggerType,FrameworkType,FrameworkItem,FrameworkConfigItemCoreEventsType,EventPayload<T>ConsentState,ProviderInstanceMap,ProviderName,GlossaryEntry,ResolvedStack