veo-sdk 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/CONTRACT.md +144 -0
- package/README.md +141 -0
- package/dist/index.cjs +2536 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +238 -0
- package/dist/index.d.ts +238 -0
- package/dist/index.mjs +2528 -0
- package/dist/index.mjs.map +1 -0
- package/dist/veo.js +170 -0
- package/dist/veo.js.map +1 -0
- package/package.json +64 -0
package/CONTRACT.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Contrato HTTP entre @veo/sdk y veo-backend
|
|
2
|
+
|
|
3
|
+
Versión: **v1**
|
|
4
|
+
|
|
5
|
+
Este documento es la fuente de verdad sobre el contrato HTTP. Cualquier
|
|
6
|
+
cambio debe reflejarse simultáneamente en ambos repos.
|
|
7
|
+
|
|
8
|
+
## Base URL
|
|
9
|
+
|
|
10
|
+
- Producción: `https://api.veo.io`
|
|
11
|
+
- Desarrollo: `http://localhost:3001`
|
|
12
|
+
|
|
13
|
+
## Versionado
|
|
14
|
+
|
|
15
|
+
Todos los endpoints viven bajo `/v1/`. Cambios incompatibles requieren
|
|
16
|
+
nueva versión (`/v2/`).
|
|
17
|
+
|
|
18
|
+
## Authentication
|
|
19
|
+
|
|
20
|
+
Todos los endpoints requieren API key del workspace:
|
|
21
|
+
|
|
22
|
+
- **Header:** `X-Api-Key: pk_xxx`
|
|
23
|
+
- **O query param:** `?key=pk_xxx` (necesario para `navigator.sendBeacon`)
|
|
24
|
+
|
|
25
|
+
Si la API key falta o es inválida: `401 Unauthorized`.
|
|
26
|
+
|
|
27
|
+
## Headers comunes
|
|
28
|
+
|
|
29
|
+
| Header | Requerido | Valor |
|
|
30
|
+
|--------|-----------|-------|
|
|
31
|
+
| `Content-Type` | Sí | `application/json` |
|
|
32
|
+
| `X-Api-Key` | Sí (o query) | `pk_xxx` |
|
|
33
|
+
| `X-Sdk-Version` | Recomendado | `0.0.1` |
|
|
34
|
+
|
|
35
|
+
## Endpoints
|
|
36
|
+
|
|
37
|
+
### POST /v1/identify
|
|
38
|
+
|
|
39
|
+
Crea o actualiza un end_user y opcionalmente su organization.
|
|
40
|
+
|
|
41
|
+
**Request body:**
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"endUser": {
|
|
45
|
+
"id": "user_123",
|
|
46
|
+
"anonymousId": "anon_xyz",
|
|
47
|
+
"traits": {
|
|
48
|
+
"email": "user@example.com",
|
|
49
|
+
"role": "admin",
|
|
50
|
+
"plan": "free"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"organization": {
|
|
54
|
+
"id": "acme-corp",
|
|
55
|
+
"attributes": {
|
|
56
|
+
"plan": "pro",
|
|
57
|
+
"mrr": 99.99,
|
|
58
|
+
"industry": "saas"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Response 200:**
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"endUserId": "user_123",
|
|
68
|
+
"organizationId": "acme-corp",
|
|
69
|
+
"workspaceId": "uuid"
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Errors:**
|
|
74
|
+
- `400` - payload inválido
|
|
75
|
+
- `401` - API key inválida
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
### POST /v1/events
|
|
80
|
+
|
|
81
|
+
Ingesta batch de eventos (máx 100 por request).
|
|
82
|
+
|
|
83
|
+
**Request body:**
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"events": [
|
|
87
|
+
{
|
|
88
|
+
"actionId": "01900000-0000-7000-8000-000000000001",
|
|
89
|
+
"endUserId": "user_123",
|
|
90
|
+
"organizationId": "acme-corp",
|
|
91
|
+
"sessionId": "session_abc",
|
|
92
|
+
"actionType": "track",
|
|
93
|
+
"actionName": "project_created",
|
|
94
|
+
"occurredAt": "2026-05-08T10:00:00.000Z",
|
|
95
|
+
"pageUrl": "https://app.example.com/dashboard",
|
|
96
|
+
"pagePath": "/dashboard",
|
|
97
|
+
"pageTitle": "Dashboard",
|
|
98
|
+
"pageReferrer": "https://google.com",
|
|
99
|
+
"actionProperties": {
|
|
100
|
+
"templateId": "blank"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Action types válidos:**
|
|
108
|
+
- `pageview` - usuario visitó una página
|
|
109
|
+
- `track` - evento custom con nombre
|
|
110
|
+
- `identify` - usuario se identificó (también triggea upsert)
|
|
111
|
+
- `guide` - interacción con guía (futuro)
|
|
112
|
+
|
|
113
|
+
**Response 200:**
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"accepted": 95,
|
|
117
|
+
"deduped": 5
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Errors:**
|
|
122
|
+
- `400` - batch vacío, payload inválido, o > 100 eventos
|
|
123
|
+
- `401` - API key inválida
|
|
124
|
+
- `413` - body demasiado grande
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Idempotencia
|
|
129
|
+
|
|
130
|
+
Cada `actionId` es único. El backend mantiene ventana de 24h para
|
|
131
|
+
detectar duplicados. Si el SDK reintenta por timeout, el evento NO se
|
|
132
|
+
duplica.
|
|
133
|
+
|
|
134
|
+
## Reglas
|
|
135
|
+
|
|
136
|
+
- `occurredAt` siempre en ISO 8601 con timezone (Z o offset)
|
|
137
|
+
- `traits` y `attributes` son JSONB, sin schema fijo
|
|
138
|
+
- Máximo 64KB por campo JSONB
|
|
139
|
+
- Máximo 100 eventos por request
|
|
140
|
+
- Encoding: UTF-8
|
|
141
|
+
|
|
142
|
+
## SDK Version
|
|
143
|
+
|
|
144
|
+
El SDK debe enviar `X-Sdk-Version` en cada request para trazabilidad.
|
package/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# @veo/sdk
|
|
2
|
+
|
|
3
|
+
SDK oficial de Veo para web apps. Captura eventos de analytics y muestra
|
|
4
|
+
guías in-app (tooltips, modales, walkthroughs).
|
|
5
|
+
|
|
6
|
+
## Stack
|
|
7
|
+
|
|
8
|
+
- TypeScript estricto
|
|
9
|
+
- Cero dependencias en runtime
|
|
10
|
+
- 3 formatos: ESM, CJS, IIFE (CDN)
|
|
11
|
+
- < 10KB gzipped
|
|
12
|
+
|
|
13
|
+
## Instalación
|
|
14
|
+
|
|
15
|
+
### npm
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @veo/sdk
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { init } from '@veo/sdk';
|
|
23
|
+
|
|
24
|
+
const veo = init({
|
|
25
|
+
apiKey: 'pk_xxx',
|
|
26
|
+
apiUrl: 'https://api.veo.io',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
veo.identify({ visitor: { id: 'user_123', traits: { email: 'a@b.com' } } });
|
|
30
|
+
veo.track('button_clicked');
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### CDN (sin bundler)
|
|
34
|
+
|
|
35
|
+
```html
|
|
36
|
+
<script src="https://cdn.veo.io/sdk/v1.js"></script>
|
|
37
|
+
<script>
|
|
38
|
+
veo.init({ apiKey: 'pk_xxx', apiUrl: 'https://api.veo.io' });
|
|
39
|
+
veo.identify({ visitor: { id: 'user_123' } });
|
|
40
|
+
</script>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Snippet (recomendado)
|
|
44
|
+
|
|
45
|
+
Ver `snippet/snippet.html` para el snippet copy-paste.
|
|
46
|
+
|
|
47
|
+
## API
|
|
48
|
+
|
|
49
|
+
(En desarrollo. Implementación completa en sprints siguientes.)
|
|
50
|
+
|
|
51
|
+
- `init(config)` - inicializa el SDK
|
|
52
|
+
- `identify(payload)` - identifica un usuario
|
|
53
|
+
- `track(name, properties?)` - emite evento custom
|
|
54
|
+
- `pageview(path?)` - emite pageview (automático en SPAs)
|
|
55
|
+
- `reset()` - limpia identidad
|
|
56
|
+
- `flush()` - fuerza envío de la queue
|
|
57
|
+
|
|
58
|
+
## Autocaptura
|
|
59
|
+
|
|
60
|
+
Al hacer `init()`, el SDK captura automáticamente **clicks, submits y changes**
|
|
61
|
+
del DOM (sin código por evento), estilo Pendo/PostHog. Cada interacción genera
|
|
62
|
+
un `track('$autocapture', …)` con metadata del elemento (tag, id, clases
|
|
63
|
+
"humanas", texto, atributos, selector CSS y ancestros). Va por la misma queue
|
|
64
|
+
(mismo batching/retry) que el resto de eventos.
|
|
65
|
+
|
|
66
|
+
Privacidad agresiva por defecto: **nunca** se captura el `value` de inputs, ni
|
|
67
|
+
el texto de campos de entrada, y se ignoran inputs sensibles
|
|
68
|
+
(`password`, `email`, `tel`, datos de tarjeta, campos con `name`/`id` tipo
|
|
69
|
+
`password`/`ssn`/`credit`/`card`).
|
|
70
|
+
|
|
71
|
+
### Opt-out de un elemento (y su subárbol)
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<button data-veo-ignore>No me trackees</button>
|
|
75
|
+
<div class="veo-ignore"> … nada aquí se captura … </div>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Nombre semántico (tagging)
|
|
79
|
+
|
|
80
|
+
`data-veo-tag` da un nombre estable al elemento (prioritario en el selector,
|
|
81
|
+
ideal para definir "features" en el dashboard aunque cambien clases/estructura):
|
|
82
|
+
|
|
83
|
+
```html
|
|
84
|
+
<button data-veo-tag="checkout-cta">Comprar</button>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Configuración
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
init({
|
|
91
|
+
apiKey: 'pk_xxx',
|
|
92
|
+
apiUrl: 'https://api.veo.io',
|
|
93
|
+
autocapture: {
|
|
94
|
+
captureClicks: true, // default
|
|
95
|
+
captureSubmits: true, // default
|
|
96
|
+
captureChanges: true, // default (solo selects y checkbox/radio)
|
|
97
|
+
maskAllText: false, // true → no capturar texto visible
|
|
98
|
+
blockSelectors: ['.private', '#chat-widget'], // ignorar selectores propios
|
|
99
|
+
throttleMs: 100, // anti-spam por elemento
|
|
100
|
+
maxTextLength: 200, // truncado de textos
|
|
101
|
+
captureAncestors: 5, // niveles de ancestros (máx 10)
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Deshabilitar por completo:
|
|
106
|
+
init({ apiKey: 'pk_xxx', apiUrl: '…', autocapture: false });
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Desarrollo
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Setup
|
|
113
|
+
pnpm install
|
|
114
|
+
|
|
115
|
+
# Desarrollo (watch)
|
|
116
|
+
pnpm dev
|
|
117
|
+
|
|
118
|
+
# Build
|
|
119
|
+
pnpm build
|
|
120
|
+
|
|
121
|
+
# Tests
|
|
122
|
+
pnpm test
|
|
123
|
+
|
|
124
|
+
# Ver tamaño del bundle
|
|
125
|
+
pnpm size
|
|
126
|
+
|
|
127
|
+
# Lint
|
|
128
|
+
pnpm lint
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Roadmap
|
|
132
|
+
|
|
133
|
+
- ✅ Sprint 1: Core (identify, track, pageview, SPA router)
|
|
134
|
+
- ✅ Sprint A: Autocaptura de clicks/submits/changes (PII-safe)
|
|
135
|
+
- ⬜ Sprint 4: Guías in-app (tooltips, modales, banners)
|
|
136
|
+
- ⬜ Sprint 5: Walkthroughs multi-paso
|
|
137
|
+
- ⬜ Futuro: Session replay
|
|
138
|
+
|
|
139
|
+
## Licencia
|
|
140
|
+
|
|
141
|
+
UNLICENSED (proyecto privado)
|