@inspira-npm/events 0.0.0-stage → 0.1.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/dist/catalog.d.ts +2 -0
- package/dist/catalog.js +251 -0
- package/dist/catalog.json +619 -0
- package/dist/index-BdIq1xly.d.ts +196 -0
- package/dist/index.d.ts +162 -0
- package/dist/index.js +579 -0
- package/dist/version-Cqps0u2Y.js +4 -0
- package/package.json +35 -4
- package/README.md +0 -3
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as EventProps, c as isEventName, d as EventDefinition, f as EventSource, i as EventName, l as toJsonCatalog, n as Catalog, o as ImpressionEventName, r as CatalogJson, s as catalog, t as BrowserEventName, u as validateEventProps } from "./index-BdIq1xly.js";
|
|
2
|
+
export { BrowserEventName, Catalog, CatalogJson, type EventDefinition, EventName, EventProps, type EventSource, ImpressionEventName, catalog, isEventName, toJsonCatalog, validateEventProps };
|
package/dist/catalog.js
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { n as SDK_VERSION } from "./version-Cqps0u2Y.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
// src/catalog/define.ts
|
|
4
|
+
function defineEvent(definition) {
|
|
5
|
+
return definition;
|
|
6
|
+
}
|
|
7
|
+
// src/catalog/shared.ts
|
|
8
|
+
/**
|
|
9
|
+
* Dónde se mostró algo, como `<página>.<sección>` en minúsculas: `home.resorts-carousel`,
|
|
10
|
+
* `hotels-srp.side-rail`, `landing-ufc.hero`. Es la dimensión principal de los reportes:
|
|
11
|
+
* mantenla estable (no metas ids ni textos traducidos).
|
|
12
|
+
*/
|
|
13
|
+
const placement = z.string().max(100).regex(/^[a-z0-9_-]+(\.[a-z0-9_-]+)*$/, "placement: <pagina>.<seccion> en minúsculas").describe("Dónde se mostró: <pagina>.<seccion>, p. ej. home.resorts-carousel");
|
|
14
|
+
/** Posición 0-based dentro de un carrusel/lista. */
|
|
15
|
+
const position = z.int().nonnegative().describe("Posición 0-based en la lista o carrusel");
|
|
16
|
+
const itemId = z.string().min(1).max(200);
|
|
17
|
+
/** Servicios de búsqueda/reserva de la plataforma. */
|
|
18
|
+
const service = z.enum([
|
|
19
|
+
"hotels",
|
|
20
|
+
"resorts",
|
|
21
|
+
"flights",
|
|
22
|
+
"cars",
|
|
23
|
+
"activities",
|
|
24
|
+
"transfers",
|
|
25
|
+
"cruises"
|
|
26
|
+
]).describe("Servicio de la plataforma");
|
|
27
|
+
const currency = z.string().regex(/^[A-Z]{3}$/, "currency: código ISO 4217 en mayúsculas").describe("Código ISO 4217, p. ej. USD");
|
|
28
|
+
// src/catalog/events/ads.ts
|
|
29
|
+
const adProps = {
|
|
30
|
+
ad_id: z.string().min(1).max(100).describe("`id` del ad tal como lo devuelve el ads engine"),
|
|
31
|
+
ad_type: z.string().min(1).max(50).describe("`adType` del engine: resort, hotel, destinations, promotions, banner…"),
|
|
32
|
+
placement,
|
|
33
|
+
position: position.optional(),
|
|
34
|
+
campaign_id: z.string().max(100).optional().describe("Campaña que sirvió el ad, si se conoce"),
|
|
35
|
+
token: z.string().max(2e3).optional().describe("Token firmado que el engine devuelve con cada ad (cuando exista); valida el evento")
|
|
36
|
+
};
|
|
37
|
+
const adEvents = {
|
|
38
|
+
ad_impression: defineEvent({
|
|
39
|
+
domain: "ads",
|
|
40
|
+
source: "browser",
|
|
41
|
+
since: "0.1.0",
|
|
42
|
+
description: "Impresión VISIBLE de un ad: ≥50% del ad en pantalla durante ≥1 s (estándar IAB). Una por ad y montaje.",
|
|
43
|
+
props: z.strictObject(adProps)
|
|
44
|
+
}),
|
|
45
|
+
ad_click: defineEvent({
|
|
46
|
+
domain: "ads",
|
|
47
|
+
source: "browser",
|
|
48
|
+
since: "0.1.0",
|
|
49
|
+
description: "Click en un ad. `click_id` viaja en la URL de destino (`?ipc=`) para atribuir conversiones.",
|
|
50
|
+
props: z.strictObject({
|
|
51
|
+
...adProps,
|
|
52
|
+
click_id: z.uuid().describe("Id único del click; también va en la URL de destino como ?ipc="),
|
|
53
|
+
destination_url: z.string().max(2e3).optional().describe("URL a la que lleva el ad")
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
};
|
|
57
|
+
// src/catalog/events/cards.ts
|
|
58
|
+
const cardProps = {
|
|
59
|
+
card_type: z.string().min(1).max(50).describe("Qué representa la card: hotel, resort, destination, activity, car, promotion…"),
|
|
60
|
+
item_id: itemId.describe("Id del ítem que representa la card"),
|
|
61
|
+
placement,
|
|
62
|
+
position: position.optional(),
|
|
63
|
+
list_id: z.string().max(100).optional().describe("Id de la lista o búsqueda que la contiene")
|
|
64
|
+
};
|
|
65
|
+
const cardEvents = {
|
|
66
|
+
card_impression: defineEvent({
|
|
67
|
+
domain: "cards",
|
|
68
|
+
source: "browser",
|
|
69
|
+
since: "0.1.0",
|
|
70
|
+
description: "Impresión VISIBLE de una card (≥50% en pantalla durante ≥1 s). Una por card y montaje.",
|
|
71
|
+
props: z.strictObject(cardProps)
|
|
72
|
+
}),
|
|
73
|
+
card_click: defineEvent({
|
|
74
|
+
domain: "cards",
|
|
75
|
+
source: "browser",
|
|
76
|
+
since: "0.1.0",
|
|
77
|
+
description: "Click en una card o en una de sus acciones.",
|
|
78
|
+
props: z.strictObject({
|
|
79
|
+
...cardProps,
|
|
80
|
+
action: z.enum([
|
|
81
|
+
"open",
|
|
82
|
+
"book",
|
|
83
|
+
"favorite",
|
|
84
|
+
"share",
|
|
85
|
+
"map"
|
|
86
|
+
]).optional().describe("Qué parte de la card se clickeó; sin valor = la card completa (open)")
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
};
|
|
90
|
+
// src/catalog/events/conversions.ts
|
|
91
|
+
const conversionEvents = { conversion: defineEvent({
|
|
92
|
+
domain: "conversions",
|
|
93
|
+
source: "server",
|
|
94
|
+
since: "0.1.0",
|
|
95
|
+
description: "Conversión confirmada (reserva, lead, registro). La envía un BACKEND server-to-server, nunca el navegador. Idempotente por `conversion_id`.",
|
|
96
|
+
props: z.strictObject({
|
|
97
|
+
conversion_id: z.string().min(1).max(100).describe("Referencia única (n.º de reserva/lead); reenviar el mismo id no duplica"),
|
|
98
|
+
type: z.enum([
|
|
99
|
+
"booking",
|
|
100
|
+
"lead",
|
|
101
|
+
"signup",
|
|
102
|
+
"purchase"
|
|
103
|
+
]).describe("Qué se convirtió: reserva, lead, registro o compra"),
|
|
104
|
+
service: service.optional(),
|
|
105
|
+
value: z.number().nonnegative().optional().describe("Monto de la conversión"),
|
|
106
|
+
currency: currency.optional(),
|
|
107
|
+
click_id: z.uuid().optional().describe("`ipc` capturado en el sitio destino; atribuye la conversión al ad clickeado")
|
|
108
|
+
})
|
|
109
|
+
}) };
|
|
110
|
+
// src/catalog/events/map.ts
|
|
111
|
+
const zoom = z.number().min(0).max(22).describe("Nivel de zoom del mapa al momento del evento");
|
|
112
|
+
const mapEvents = {
|
|
113
|
+
map_marker_click: defineEvent({
|
|
114
|
+
domain: "map",
|
|
115
|
+
source: "browser",
|
|
116
|
+
since: "0.1.0",
|
|
117
|
+
description: "Click en un marcador del mapa (hotel, resort, punto de interés…).",
|
|
118
|
+
props: z.strictObject({
|
|
119
|
+
placement,
|
|
120
|
+
item_type: z.string().min(1).max(50).describe("Qué representa el marcador: hotel, resort, poi…"),
|
|
121
|
+
item_id: itemId.describe("Id del ítem que representa el marcador"),
|
|
122
|
+
zoom: zoom.optional()
|
|
123
|
+
})
|
|
124
|
+
}),
|
|
125
|
+
map_interaction: defineEvent({
|
|
126
|
+
domain: "map",
|
|
127
|
+
source: "browser",
|
|
128
|
+
since: "0.1.0",
|
|
129
|
+
description: "Interacción con el mapa que no es click en marcador. No se trackea el paneo (demasiado ruido).",
|
|
130
|
+
props: z.strictObject({
|
|
131
|
+
placement,
|
|
132
|
+
action: z.enum([
|
|
133
|
+
"zoom",
|
|
134
|
+
"cluster_click",
|
|
135
|
+
"search_area",
|
|
136
|
+
"popup_open",
|
|
137
|
+
"fullscreen"
|
|
138
|
+
]).describe("search_area = botón 'buscar en esta zona'"),
|
|
139
|
+
zoom: zoom.optional()
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
};
|
|
143
|
+
// src/catalog/events/page.ts
|
|
144
|
+
const pageEvents = { page_view: defineEvent({
|
|
145
|
+
domain: "page",
|
|
146
|
+
source: "browser",
|
|
147
|
+
since: "0.1.0",
|
|
148
|
+
description: "Vista de página. Se dispara al cargar y en cada navegación del router (SPA). La URL ya va en el contexto del evento.",
|
|
149
|
+
props: z.strictObject({ page_type: z.string().max(50).optional().describe("Tipo de página estable: home, hotels-srp, hotel-details, checkout, landing…") })
|
|
150
|
+
}) };
|
|
151
|
+
// src/catalog/events/search.ts
|
|
152
|
+
const location = z.strictObject({
|
|
153
|
+
id: z.string().max(100).optional().describe("Id del destino/origen en el proveedor"),
|
|
154
|
+
name: z.string().max(200).optional().describe("Nombre de la sugerencia SELECCIONADA"),
|
|
155
|
+
type: z.enum([
|
|
156
|
+
"city",
|
|
157
|
+
"region",
|
|
158
|
+
"country",
|
|
159
|
+
"hotel",
|
|
160
|
+
"airport",
|
|
161
|
+
"poi"
|
|
162
|
+
]).optional().describe("Tipo de lugar de la sugerencia")
|
|
163
|
+
}).describe("Destino u origen elegido de las sugerencias");
|
|
164
|
+
const searchId = z.uuid().describe("Id de la búsqueda (generado en el cliente); une resultados y clicks de la misma búsqueda");
|
|
165
|
+
const searchEvents = {
|
|
166
|
+
search_performed: defineEvent({
|
|
167
|
+
domain: "search",
|
|
168
|
+
source: "browser",
|
|
169
|
+
since: "0.1.0",
|
|
170
|
+
description: "Búsqueda ejecutada (al recibir resultados). Una por búsqueda; un cambio de filtros que vuelve a buscar es otra.",
|
|
171
|
+
props: z.strictObject({
|
|
172
|
+
search_id: searchId,
|
|
173
|
+
service,
|
|
174
|
+
destination: location.optional(),
|
|
175
|
+
origin: location.optional().describe("Solo vuelos/traslados"),
|
|
176
|
+
check_in: z.iso.date().optional().describe("YYYY-MM-DD (o fecha de salida)"),
|
|
177
|
+
check_out: z.iso.date().optional().describe("YYYY-MM-DD (o fecha de regreso)"),
|
|
178
|
+
rooms: z.int().min(1).max(20).optional().describe("Habitaciones pedidas"),
|
|
179
|
+
adults: z.int().min(0).max(50).optional().describe("Adultos (total de la búsqueda)"),
|
|
180
|
+
children: z.int().min(0).max(50).optional().describe("Menores (total de la búsqueda)"),
|
|
181
|
+
results_count: z.int().nonnegative().optional().describe("Resultados devueltos; 0 = sin resultados"),
|
|
182
|
+
filters: z.record(z.string().max(50), z.union([
|
|
183
|
+
z.string().max(100),
|
|
184
|
+
z.number(),
|
|
185
|
+
z.boolean(),
|
|
186
|
+
z.array(z.string().max(100)).max(50)
|
|
187
|
+
])).optional().describe("Filtros activos: { stars: [4,5] → ['4','5'], price_max: 300, all_inclusive: true }"),
|
|
188
|
+
sort: z.string().max(50).optional().describe("Orden aplicado: price_asc, rating_desc…"),
|
|
189
|
+
page: z.int().min(1).optional().describe("Página de resultados (1-based)")
|
|
190
|
+
})
|
|
191
|
+
}),
|
|
192
|
+
search_result_click: defineEvent({
|
|
193
|
+
domain: "search",
|
|
194
|
+
source: "browser",
|
|
195
|
+
since: "0.1.0",
|
|
196
|
+
description: "Click en un resultado de búsqueda.",
|
|
197
|
+
props: z.strictObject({
|
|
198
|
+
search_id: searchId,
|
|
199
|
+
service,
|
|
200
|
+
item_id: itemId.describe("Id del resultado clickeado (hotel, vuelo, auto…)"),
|
|
201
|
+
position,
|
|
202
|
+
price: z.number().nonnegative().optional().describe("Precio mostrado en el resultado"),
|
|
203
|
+
currency: currency.optional()
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
};
|
|
207
|
+
// src/catalog/index.ts
|
|
208
|
+
/**
|
|
209
|
+
* Catálogo de eventos: la ÚNICA fuente de verdad de qué eventos existen y qué props llevan.
|
|
210
|
+
* El collector (inspira-promotions-laravel) valida contra `dist/catalog.json`, que se genera
|
|
211
|
+
* de aquí en el build. Para agregar un evento ver `docs/catalog.md`.
|
|
212
|
+
*/
|
|
213
|
+
const catalog = {
|
|
214
|
+
...pageEvents,
|
|
215
|
+
...adEvents,
|
|
216
|
+
...cardEvents,
|
|
217
|
+
...mapEvents,
|
|
218
|
+
...searchEvents,
|
|
219
|
+
...conversionEvents
|
|
220
|
+
};
|
|
221
|
+
function isEventName(name) {
|
|
222
|
+
return Object.hasOwn(catalog, name);
|
|
223
|
+
}
|
|
224
|
+
/** Valida las props de un evento contra el catálogo (tests, herramientas server-side). */
|
|
225
|
+
function validateEventProps(name, props) {
|
|
226
|
+
if (!isEventName(name)) return {
|
|
227
|
+
success: false,
|
|
228
|
+
error: `unknown event: ${name}`
|
|
229
|
+
};
|
|
230
|
+
const result = catalog[name].props.safeParse(props);
|
|
231
|
+
return result.success ? { success: true } : {
|
|
232
|
+
success: false,
|
|
233
|
+
error: z.prettifyError(result.error)
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/** Catálogo serializado como JSON Schema (draft 2020-12) para el backend, el MCP y la skill. */
|
|
237
|
+
function toJsonCatalog() {
|
|
238
|
+
const events = {};
|
|
239
|
+
for (const [name, definition] of Object.entries(catalog)) events[name] = {
|
|
240
|
+
description: definition.description,
|
|
241
|
+
domain: definition.domain,
|
|
242
|
+
source: definition.source,
|
|
243
|
+
since: definition.since,
|
|
244
|
+
schema: z.toJSONSchema(definition.props, { io: "input" })
|
|
245
|
+
};
|
|
246
|
+
return {
|
|
247
|
+
sdk_version: SDK_VERSION,
|
|
248
|
+
events
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
export { catalog, isEventName, toJsonCatalog, validateEventProps };
|