@inspira-npm/events 0.0.0-stage → 0.3.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.
@@ -0,0 +1,203 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Quién emite el evento:
4
+ * - `browser`: el SDK desde el navegador (`tracker.track`).
5
+ * - `server`: un backend, server-to-server (p. ej. conversiones). El SDK de navegador
6
+ * no lo puede emitir: su tipo queda fuera de `BrowserEventName`.
7
+ */
8
+ type EventSource = "browser" | "server";
9
+ interface EventDefinition<Props extends z.ZodType = z.ZodType, Source extends EventSource = EventSource> {
10
+ /** Qué significa el evento y CUÁNDO se dispara. Lo leen el backend, el MCP y la skill. */
11
+ readonly description: string;
12
+ /** Agrupa eventos para reportes y permisos: `ads`, `cards`, `map`, `search`, … */
13
+ readonly domain: string;
14
+ readonly source: Source;
15
+ /** Versión del SDK que introdujo el evento (o el último cambio de su esquema). */
16
+ readonly since: string;
17
+ /** Esquema de `props`. Siempre `z.strictObject`: una prop desconocida es un error. */
18
+ readonly props: Props;
19
+ }
20
+ /**
21
+ * Catálogo de eventos: la ÚNICA fuente de verdad de qué eventos existen y qué props llevan.
22
+ * El collector (inspira-promotions-laravel) valida contra `dist/catalog.json`, que se genera
23
+ * de aquí en el build. Para agregar un evento ver `docs/catalog.md`.
24
+ */
25
+ declare const catalog: {
26
+ conversion: EventDefinition<z.ZodObject<{
27
+ conversion_id: z.ZodString;
28
+ type: z.ZodEnum<{
29
+ booking: "booking";
30
+ lead: "lead";
31
+ signup: "signup";
32
+ purchase: "purchase";
33
+ }>;
34
+ service: z.ZodOptional<z.ZodEnum<{
35
+ hotels: "hotels";
36
+ resorts: "resorts";
37
+ flights: "flights";
38
+ cars: "cars";
39
+ activities: "activities";
40
+ transfers: "transfers";
41
+ cruises: "cruises";
42
+ }>>;
43
+ value: z.ZodOptional<z.ZodNumber>;
44
+ currency: z.ZodOptional<z.ZodString>;
45
+ click_id: z.ZodOptional<z.ZodUUID>;
46
+ }, z.core.$strict>, "server">;
47
+ outbound_click: EventDefinition<z.ZodObject<{
48
+ placement: z.ZodString;
49
+ item_id: z.ZodOptional<z.ZodString>;
50
+ click_id: z.ZodUUID;
51
+ via_click_id: z.ZodOptional<z.ZodUUID>;
52
+ destination_url: z.ZodOptional<z.ZodString>;
53
+ }, z.core.$strict>, "browser">;
54
+ search_performed: EventDefinition<z.ZodObject<{
55
+ search_id: z.ZodUUID;
56
+ service: z.ZodEnum<{
57
+ hotels: "hotels";
58
+ resorts: "resorts";
59
+ flights: "flights";
60
+ cars: "cars";
61
+ activities: "activities";
62
+ transfers: "transfers";
63
+ cruises: "cruises";
64
+ }>;
65
+ destination: z.ZodOptional<z.ZodObject<{
66
+ id: z.ZodOptional<z.ZodString>;
67
+ name: z.ZodOptional<z.ZodString>;
68
+ type: z.ZodOptional<z.ZodEnum<{
69
+ city: "city";
70
+ region: "region";
71
+ country: "country";
72
+ hotel: "hotel";
73
+ airport: "airport";
74
+ poi: "poi";
75
+ }>>;
76
+ }, z.core.$strict>>;
77
+ origin: z.ZodOptional<z.ZodObject<{
78
+ id: z.ZodOptional<z.ZodString>;
79
+ name: z.ZodOptional<z.ZodString>;
80
+ type: z.ZodOptional<z.ZodEnum<{
81
+ city: "city";
82
+ region: "region";
83
+ country: "country";
84
+ hotel: "hotel";
85
+ airport: "airport";
86
+ poi: "poi";
87
+ }>>;
88
+ }, z.core.$strict>>;
89
+ check_in: z.ZodOptional<z.ZodISODate>;
90
+ check_out: z.ZodOptional<z.ZodISODate>;
91
+ rooms: z.ZodOptional<z.ZodInt>;
92
+ adults: z.ZodOptional<z.ZodInt>;
93
+ children: z.ZodOptional<z.ZodInt>;
94
+ results_count: z.ZodOptional<z.ZodInt>;
95
+ filters: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodString>]>>>;
96
+ sort: z.ZodOptional<z.ZodString>;
97
+ page: z.ZodOptional<z.ZodInt>;
98
+ }, z.core.$strict>, "browser">;
99
+ search_result_click: EventDefinition<z.ZodObject<{
100
+ search_id: z.ZodUUID;
101
+ service: z.ZodEnum<{
102
+ hotels: "hotels";
103
+ resorts: "resorts";
104
+ flights: "flights";
105
+ cars: "cars";
106
+ activities: "activities";
107
+ transfers: "transfers";
108
+ cruises: "cruises";
109
+ }>;
110
+ item_id: z.ZodString;
111
+ position: z.ZodInt;
112
+ price: z.ZodOptional<z.ZodNumber>;
113
+ currency: z.ZodOptional<z.ZodString>;
114
+ }, z.core.$strict>, "browser">;
115
+ map_marker_click: EventDefinition<z.ZodObject<{
116
+ placement: z.ZodString;
117
+ item_type: z.ZodString;
118
+ item_id: z.ZodString;
119
+ zoom: z.ZodOptional<z.ZodNumber>;
120
+ }, z.core.$strict>, "browser">;
121
+ map_interaction: EventDefinition<z.ZodObject<{
122
+ placement: z.ZodString;
123
+ action: z.ZodEnum<{
124
+ zoom: "zoom";
125
+ cluster_click: "cluster_click";
126
+ search_area: "search_area";
127
+ popup_open: "popup_open";
128
+ fullscreen: "fullscreen";
129
+ }>;
130
+ zoom: z.ZodOptional<z.ZodNumber>;
131
+ }, z.core.$strict>, "browser">;
132
+ card_impression: EventDefinition<z.ZodObject<{
133
+ card_type: z.ZodString;
134
+ item_id: z.ZodString;
135
+ placement: z.ZodString;
136
+ position: z.ZodOptional<z.ZodInt>;
137
+ list_id: z.ZodOptional<z.ZodString>;
138
+ }, z.core.$strict>, "browser">;
139
+ card_click: EventDefinition<z.ZodObject<{
140
+ action: z.ZodOptional<z.ZodEnum<{
141
+ map: "map";
142
+ open: "open";
143
+ book: "book";
144
+ favorite: "favorite";
145
+ share: "share";
146
+ }>>;
147
+ card_type: z.ZodString;
148
+ item_id: z.ZodString;
149
+ placement: z.ZodString;
150
+ position: z.ZodOptional<z.ZodInt>;
151
+ list_id: z.ZodOptional<z.ZodString>;
152
+ }, z.core.$strict>, "browser">;
153
+ ad_impression: EventDefinition<z.ZodObject<{
154
+ ad_id: z.ZodString;
155
+ ad_type: z.ZodString;
156
+ placement: z.ZodString;
157
+ position: z.ZodOptional<z.ZodInt>;
158
+ campaign_id: z.ZodOptional<z.ZodString>;
159
+ token: z.ZodOptional<z.ZodString>;
160
+ }, z.core.$strict>, "browser">;
161
+ ad_click: EventDefinition<z.ZodObject<{
162
+ click_id: z.ZodUUID;
163
+ destination_url: z.ZodOptional<z.ZodString>;
164
+ ad_id: z.ZodString;
165
+ ad_type: z.ZodString;
166
+ placement: z.ZodString;
167
+ position: z.ZodOptional<z.ZodInt>;
168
+ campaign_id: z.ZodOptional<z.ZodString>;
169
+ token: z.ZodOptional<z.ZodString>;
170
+ }, z.core.$strict>, "browser">;
171
+ page_view: EventDefinition<z.ZodObject<{
172
+ page_type: z.ZodOptional<z.ZodString>;
173
+ }, z.core.$strict>, "browser">;
174
+ };
175
+ type Catalog = typeof catalog;
176
+ type EventName = keyof Catalog;
177
+ /** Eventos que puede emitir el navegador (excluye los `source: "server"`, p. ej. conversiones). */
178
+ type BrowserEventName = { [N in EventName]: Catalog[N]["source"] extends "browser" ? N : never; }[EventName];
179
+ /** Eventos que se miden con `observeImpression` / `useImpression`. */
180
+ type ImpressionEventName = Extract<BrowserEventName, `${string}_impression`>;
181
+ type EventProps<N extends EventName> = z.input<Catalog[N]["props"]>;
182
+ declare function isEventName(name: string): name is EventName;
183
+ /** Valida las props de un evento contra el catálogo (tests, herramientas server-side). */
184
+ declare function validateEventProps(name: string, props: unknown): {
185
+ success: false;
186
+ error: string;
187
+ } | {
188
+ success: true;
189
+ error?: undefined;
190
+ };
191
+ interface CatalogJson {
192
+ sdk_version: string;
193
+ events: Record<string, {
194
+ description: string;
195
+ domain: string;
196
+ source: string;
197
+ since: string;
198
+ schema: Record<string, unknown>;
199
+ }>;
200
+ }
201
+ /** Catálogo serializado como JSON Schema (draft 2020-12) para el backend, el MCP y la skill. */
202
+ declare function toJsonCatalog(): CatalogJson;
203
+ export { EventProps as a, isEventName as c, EventDefinition as d, EventSource as f, EventName as i, toJsonCatalog as l, Catalog as n, ImpressionEventName as o, CatalogJson as r, catalog as s, BrowserEventName as t, validateEventProps as u };
@@ -0,0 +1,202 @@
1
+ import { a as EventProps, i as EventName, o as ImpressionEventName, t as BrowserEventName } from "./index-BShrVaIU.js";
2
+ import { n as ClickRecordOptions, t as AdClickRecordPayload } from "./click-record-B8KyALZZ.js";
3
+ /** Query param que lleva el click id de un ad hasta el sitio destino. */
4
+ export declare const CLICK_ID_PARAM = "ipc";
5
+ export declare function isClickId(value: unknown): value is string;
6
+ /**
7
+ * Agrega `?ipc=<clickId>` a una URL conservando el resto de la query y el hash.
8
+ * Las URLs relativas siguen siendo relativas. Si la URL no se puede parsear se devuelve igual.
9
+ */
10
+ export declare function withClickId(url: string, clickId: string, param?: string): string;
11
+ /** Lee un click id válido de una URL (o null). */
12
+ export declare function readClickIdFromUrl(href: string, param?: string): string | null;
13
+ /**
14
+ * Copia a `url` los params de atribución de `from` (por defecto, la página actual): `ipc`,
15
+ * `utm_*`, `gclid` y `fbclid`. Los que `url` ya trae no se pisan. Igual que `withClickId`, el
16
+ * resto de la query y el hash quedan intactos (nada se vuelve a codificar) y las URLs relativas
17
+ * siguen siendo relativas. Si no hay nada que copiar, o alguna URL no se puede parsear, devuelve
18
+ * `url` tal cual.
19
+ *
20
+ * Para sitios intermedios (landings) que arman la URL de regreso: sin esto el `?ipc=` y las
21
+ * `utm_*` con las que llegó el visitante se pierden en el siguiente salto.
22
+ */
23
+ export declare function withTrackingParams(url: string, from?: string): string;
24
+ /**
25
+ * - `granted`: aceptó cookies de estadísticas → ids persistentes (anon/session/user).
26
+ * - `denied`: solo necesarias → eventos sin ningún identificador ni storage.
27
+ * - `unknown`: aún no decidió → igual que `denied`.
28
+ */
29
+ type ConsentState = "granted" | "denied" | "unknown";
30
+ export declare const DEFAULT_CONSENT_COOKIE = "CookieConsent";
31
+ /** Evento que dispara el banner de inspira-frontend-v2 al guardar el consentimiento. */
32
+ export declare const CONSENT_CHANGE_EVENT = "cookie-consent-change";
33
+ /** Interpreta el valor crudo de la cookie de consentimiento (puede venir URL-encoded). */
34
+ export declare function parseConsentValue(raw: string | null | undefined): ConsentState;
35
+ /**
36
+ * Lector de consentimiento basado en la cookie del banner de Inspira. Pásalo como
37
+ * `consent` a `createTracker`. Se evalúa en cada evento, así que un cambio de
38
+ * consentimiento aplica al siguiente evento sin recargar.
39
+ */
40
+ export declare function cookieConsent(cookieName?: string): () => ConsentState;
41
+ interface EventContext {
42
+ /** URL sin query, salvo los parámetros de la allowlist (utm_*, ipc, gclid…). */
43
+ url: string;
44
+ path: string;
45
+ /** Solo origin + path del referrer (nunca su query). */
46
+ referrer: string | null;
47
+ locale: string | null;
48
+ tz: string | null;
49
+ screen: {
50
+ w: number;
51
+ h: number;
52
+ } | null;
53
+ viewport: {
54
+ w: number;
55
+ h: number;
56
+ } | null;
57
+ }
58
+ /**
59
+ * Query params que SÍ se conservan en `ctx.url`. El resto se descarta: las URLs pueden
60
+ * traer tokens, emails o ids de reserva.
61
+ */
62
+ export declare const DEFAULT_URL_PARAM_ALLOWLIST: string[];
63
+ export declare function sanitizeUrl(href: string, allowlist: readonly string[]): string;
64
+ interface ImpressionOptions {
65
+ /** Fracción del elemento que debe verse (0–1). Default 0.5 (estándar IAB display). */
66
+ threshold?: number;
67
+ /** Tiempo CONTINUO visible, en ms, antes de contar la impresión. Default 1000 (IAB). */
68
+ minVisibleMs?: number;
69
+ /** Contar una sola vez por observación (por montaje del componente). Default true. */
70
+ once?: boolean;
71
+ }
72
+ /**
73
+ * - `sent`: el collector lo aceptó (o se encoló un beacon, que no da respuesta).
74
+ * - `retry`: error de red, 429 o 5xx → se reintenta más tarde.
75
+ * - `drop`: 4xx (payload inválido, site key desconocida) → reintentar no ayuda.
76
+ */
77
+ type SendResult = "sent" | "retry" | "drop";
78
+ interface TransportOptions {
79
+ /** La página se está ocultando/cerrando: usar `sendBeacon`, no se espera respuesta. */
80
+ beacon: boolean;
81
+ debug: boolean;
82
+ }
83
+ interface Transport {
84
+ send(url: string, body: string, options: TransportOptions): SendResult | Promise<SendResult>;
85
+ }
86
+ export declare const fetchTransport: Transport;
87
+ interface TrackerConfig {
88
+ /** Base URL del collector, p. ej. `https://events.inspiratest.com`. */
89
+ endpoint: string;
90
+ /** Clave PÚBLICA del sitio: identifica el sitio y su allowlist de orígenes. No es secreta. */
91
+ siteKey: string;
92
+ /** Organización/whitelabel del tenant actual. Se puede cambiar luego con `setWl`. */
93
+ wl?: number | null;
94
+ /** Estado de consentimiento, evaluado en cada evento. Default: siempre `unknown`. */
95
+ consent?: () => ConsentState;
96
+ /** Espera máxima antes de enviar un lote incompleto. Default 5000 ms. */
97
+ flushIntervalMs?: number;
98
+ /** Eventos por request. Default 20. */
99
+ maxBatchSize?: number;
100
+ /** Eventos en cola como máximo (si el collector no responde se descartan los más viejos). Default 500. */
101
+ maxQueueSize?: number;
102
+ /**
103
+ * Query params que se conservan en `ctx.url` (nombres exactos, sin comodines). Default:
104
+ * utm_source, utm_medium, utm_campaign, utm_term, utm_content, ipc, gclid, fbclid.
105
+ */
106
+ urlParamAllowlist?: readonly string[];
107
+ /**
108
+ * Registro de cada click en un ad (`trackAdClick` / `useAdClick`) en el servidor del sitio,
109
+ * aparte del evento `ad_click`: ruta del mismo origen (p. ej. `/api/ad-click`) creada con
110
+ * `createAdClickRoute` de `@inspira-npm/events/server`, que agrega usuario, IP y organización
111
+ * desde la sesión. Con un string, o sin `requireConsent`, se registran TODOS los clicks, con o
112
+ * sin consentimiento; `{ endpoint, requireConsent: true }` lo limita a `granted`. Ver README,
113
+ * "Registro de clicks (servidor)".
114
+ */
115
+ clickRecord?: string | ClickRecordOptions;
116
+ /** Loguea en consola los eventos y los rechazos del collector. */
117
+ debug?: boolean;
118
+ /** Reemplaza el envío HTTP (tests, entornos especiales). */
119
+ transport?: Transport;
120
+ }
121
+ /** Un evento tal como viaja al collector (ver docs/protocol.md). */
122
+ interface EventEnvelope {
123
+ id: string;
124
+ name: string;
125
+ /** ISO 8601 del reloj del cliente; el collector guarda además su propia hora de recepción. */
126
+ ts: string;
127
+ props: Record<string, unknown>;
128
+ ctx: EventContext;
129
+ wl: number | null;
130
+ consent: ConsentState;
131
+ anon_id: string | null;
132
+ session_id: string | null;
133
+ user_id: string | null;
134
+ }
135
+ interface BatchPayload {
136
+ sdk: {
137
+ name: string;
138
+ version: string;
139
+ };
140
+ sent_at: string;
141
+ events: EventEnvelope[];
142
+ }
143
+ type AdClickProps = Omit<EventProps<"ad_click">, "click_id" | "destination_url">;
144
+ /**
145
+ * Props de `outbound_click` que pasa el sitio: `click_id`, `destination_url` y `via_click_id`
146
+ * los pone el SDK.
147
+ */
148
+ type OutboundClickProps = Omit<EventProps<"outbound_click">, "click_id" | "destination_url" | "via_click_id">;
149
+ interface Tracker {
150
+ /**
151
+ * Encola un evento del catálogo. Devuelve su id, o null si no se encoló: tracker no-op (SSR),
152
+ * después de `shutdown()`, o props no serializables a JSON.
153
+ */
154
+ track<N extends BrowserEventName>(name: N, props: EventProps<N>): string | null;
155
+ /** Mide una impresión VISIBLE de `element` (≥50% durante ≥1 s por defecto). Devuelve el cleanup. */
156
+ observeImpression<N extends ImpressionEventName>(element: Element, name: N, props: EventProps<N>, options?: ImpressionOptions): () => void;
157
+ /**
158
+ * Registra `ad_click` con un click id nuevo y devuelve `url` con `?ipc=<clickId>`. Con
159
+ * consentimiento, guarda además el click id en este sitio (ver `getClickId`). Con
160
+ * `clickRecord` configurado, manda también el registro del click al servidor del sitio.
161
+ */
162
+ trackAdClick(props: AdClickProps, url: string): string;
163
+ /**
164
+ * Como `trackAdClick`, para un link o CTA medido hacia otro sitio o página: registra
165
+ * `outbound_click` (con `via_click_id` = el click id con el que llegó el visitante, si hay) y
166
+ * devuelve `url` con `?ipc=<clickId>`.
167
+ */
168
+ trackOutboundClick(props: OutboundClickProps, url: string): string;
169
+ /**
170
+ * En el sitio destino: lee `?ipc=` de la URL y, con consentimiento, lo guarda para la conversión.
171
+ * Si el usuario acepta el banner después, se vuelve a capturar solo (ver CONSENT_CHANGE_EVENT).
172
+ */
173
+ captureClickId(): string | null;
174
+ /**
175
+ * Click id para enviar con la conversión: el `?ipc=` de la URL actual o, si no hay, el más
176
+ * reciente entre el capturado al llegar y el generado en un click de este sitio, guardados hace
177
+ * ≤ `maxAgeDays` (default 7). Sin consentimiento solo cuenta el de la URL.
178
+ */
179
+ getClickId(options?: {
180
+ maxAgeDays?: number;
181
+ }): string | null;
182
+ /** Usuario logueado. Solo se envía con consentimiento `granted`. */
183
+ identify(userId: string | number | null): void;
184
+ setWl(wl: number | null): void;
185
+ /** Logout: olvida usuario, ids y click ids guardados. */
186
+ reset(): void;
187
+ /** Envía lo que haya en cola. */
188
+ flush(): Promise<void>;
189
+ /** Envía lo pendiente por beacon y desconecta listeners/observers. */
190
+ shutdown(): void;
191
+ }
192
+ export declare function createNoopTracker(): Tracker;
193
+ /**
194
+ * Crea el tracker. En el servidor (SSR, sin `window`) devuelve un tracker no-op, así que es
195
+ * seguro crearlo a nivel de módulo en un client component de Next.js.
196
+ */
197
+ export declare function createTracker(config: TrackerConfig): Tracker;
198
+ /** UUID v4. `crypto.randomUUID` solo existe en contextos seguros (https/localhost). */
199
+ declare function uuid(): string;
200
+ export declare const SDK_NAME = "@inspira-npm/events";
201
+ export declare const SDK_VERSION = "0.3.0";
202
+ export { type AdClickProps, type AdClickRecordPayload, type BatchPayload, type BrowserEventName, type ClickRecordOptions, type ConsentState, type EventContext, type EventEnvelope, type EventName, type EventProps, type ImpressionEventName, type ImpressionOptions, type OutboundClickProps, type SendResult, type Tracker, type TrackerConfig, type Transport, type TransportOptions, uuid as newId };