@holdyapp/sdk 0.2.1 → 1.0.1
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/README.md +20 -147
- package/dist/auto-capture.d.ts +12 -16
- package/dist/auto-capture.d.ts.map +1 -0
- package/dist/browser.d.ts +23 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/core.d.ts +42 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/holdy.js +2 -0
- package/dist/holdy.js.map +7 -0
- package/dist/index.cjs +430 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +5 -46
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +399 -0
- package/dist/index.mjs.map +7 -0
- package/dist/server.cjs +177 -0
- package/dist/server.cjs.map +7 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.mjs +148 -0
- package/dist/server.mjs.map +7 -0
- package/dist/types.d.ts +41 -109
- package/dist/types.d.ts.map +1 -0
- package/dist/web-context.d.ts +51 -0
- package/dist/web-context.d.ts.map +1 -0
- package/dist/web.d.ts +41 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.mjs +389 -0
- package/dist/web.mjs.map +7 -0
- package/package.json +24 -20
- package/src/auto-capture.ts +51 -78
- package/src/browser.ts +55 -0
- package/src/core.test.ts +147 -0
- package/src/core.ts +172 -0
- package/src/index.ts +4 -427
- package/src/server.ts +19 -0
- package/src/types.ts +38 -162
- package/src/web-context.test.ts +71 -0
- package/src/web-context.ts +227 -0
- package/src/web.ts +108 -0
- package/LICENSE +0 -21
- package/dist/anchor.d.ts +0 -8
- package/dist/holdy.cjs.js +0 -255
- package/dist/holdy.cjs.js.map +0 -7
- package/dist/holdy.esm.js +0 -255
- package/dist/holdy.esm.js.map +0 -7
- package/dist/holdy.iife.js +0 -255
- package/dist/holdy.iife.js.map +0 -7
- package/dist/outcome.d.ts +0 -19
- package/dist/queue.d.ts +0 -54
- package/dist/render/kinds/banner.d.ts +0 -3
- package/dist/render/kinds/checklist.d.ts +0 -19
- package/dist/render/kinds/cta.d.ts +0 -8
- package/dist/render/kinds/modal.d.ts +0 -16
- package/dist/render/kinds/shared.d.ts +0 -16
- package/dist/render/kinds/spotlight.d.ts +0 -17
- package/dist/render/kinds/tooltip.d.ts +0 -3
- package/dist/render/root.d.ts +0 -3
- package/dist/render/styles.d.ts +0 -5
- package/dist/store.d.ts +0 -36
- package/dist/transport.d.ts +0 -59
- package/src/anchor.ts +0 -72
- package/src/outcome.ts +0 -51
- package/src/queue.ts +0 -296
- package/src/render/kinds/banner.ts +0 -52
- package/src/render/kinds/checklist.ts +0 -175
- package/src/render/kinds/cta.ts +0 -53
- package/src/render/kinds/modal.ts +0 -129
- package/src/render/kinds/shared.ts +0 -67
- package/src/render/kinds/spotlight.ts +0 -176
- package/src/render/kinds/tooltip.ts +0 -82
- package/src/render/root.ts +0 -69
- package/src/render/styles.ts +0 -280
- package/src/store.ts +0 -106
- package/src/transport.ts +0 -182
package/README.md
CHANGED
|
@@ -1,165 +1,38 @@
|
|
|
1
1
|
# @holdyapp/sdk
|
|
2
2
|
|
|
3
|
-
Holdy
|
|
3
|
+
SDK canônico do Holdy · `track` / `identify` / `group` → `POST /api/v1/events/bulk` (entry-contract v1). Mesmo envelope e mesma porta HTTP dos conectores — zero endpoint privado.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Instalação rápida
|
|
10
|
-
|
|
11
|
-
### Opção 1 · `npx` (recomendado · 1 minuto)
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
npx @holdyapp/install <SUA_API_KEY>
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Detecta seu framework (Next.js, Vite, Vue, Nuxt, vanilla HTML) e injeta o snippet no lugar certo.
|
|
18
|
-
|
|
19
|
-
### Opção 2 · Script tag (CDN)
|
|
20
|
-
|
|
21
|
-
```html
|
|
22
|
-
<script src="https://app.holdy.com.br/sdk/v0/holdy.iife.js"></script>
|
|
23
|
-
<script>
|
|
24
|
-
Holdy.init({
|
|
25
|
-
apiKey: 'hol_live_xxxxxxxx',
|
|
26
|
-
accountId: 'acme-corp',
|
|
27
|
-
autoCapture: true,
|
|
28
|
-
})
|
|
29
|
-
</script>
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### Opção 3 · npm + ES module
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
npm install @holdyapp/sdk
|
|
36
|
-
```
|
|
5
|
+
## Web (`sdk_web`)
|
|
37
6
|
|
|
38
7
|
```ts
|
|
39
|
-
import {
|
|
40
|
-
|
|
41
|
-
Holdy.init({
|
|
42
|
-
apiKey: 'hol_live_xxxxxxxx',
|
|
43
|
-
accountId: 'acme-corp',
|
|
44
|
-
autoCapture: true,
|
|
45
|
-
})
|
|
46
|
-
```
|
|
8
|
+
import { initHoldy } from '@holdyapp/sdk/web'
|
|
47
9
|
|
|
48
|
-
|
|
10
|
+
const holdy = initHoldy({ apiKey: 'hol_live_...' })
|
|
49
11
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
```ts
|
|
55
|
-
Holdy.identify({
|
|
56
|
-
account: {
|
|
57
|
-
external_id: 'acct_123',
|
|
58
|
-
name: 'Acme Corp',
|
|
59
|
-
logo_url: 'https://acme.com/logo.png',
|
|
60
|
-
plan: 'Enterprise',
|
|
61
|
-
mrr: 5000,
|
|
62
|
-
},
|
|
63
|
-
user: {
|
|
64
|
-
external_id: 'user_456',
|
|
65
|
-
email: 'ceo@acme.com',
|
|
66
|
-
name: 'John Doe',
|
|
67
|
-
role: 'decision_maker',
|
|
68
|
-
},
|
|
69
|
-
})
|
|
12
|
+
holdy.track('invite_sent', { plan: 'pro' })
|
|
13
|
+
holdy.identify('user_42', { role: 'admin' }) // emite $identify · user_id persiste na sessão
|
|
14
|
+
holdy.group('acme-inc', { plan: 'scale' }) // emite $group · associa a chave de account aos próximos events
|
|
15
|
+
holdy.page('pricing')
|
|
70
16
|
```
|
|
71
17
|
|
|
72
|
-
|
|
18
|
+
`anonymous_id` é gerado e persistido em `localStorage` (`holdy_anon_id`). O merge anônimo → identificado acontece no identity graph do Holdy, nunca no SDK.
|
|
73
19
|
|
|
74
|
-
##
|
|
75
|
-
|
|
76
|
-
20 primitives semânticos validados por taxonomia v1. Lista completa em https://app.holdy.com.br/api/v1/docs.
|
|
20
|
+
## Server (`sdk_server` · Node ≥18 / Edge)
|
|
77
21
|
|
|
78
22
|
```ts
|
|
79
|
-
|
|
80
|
-
Holdy.track('workflow.blocked', {
|
|
81
|
-
workflow: 'invoice-export',
|
|
82
|
-
reason: 'permission_denied',
|
|
83
|
-
severity: 'high',
|
|
84
|
-
})
|
|
23
|
+
import { Holdy } from '@holdyapp/sdk/server'
|
|
85
24
|
|
|
86
|
-
|
|
87
|
-
Holdy.track('friction.detected', {
|
|
88
|
-
signal: 'rage_click',
|
|
89
|
-
surface: '/billing',
|
|
90
|
-
})
|
|
25
|
+
const holdy = new Holdy({ apiKey: process.env.HOLDY_API_KEY! })
|
|
91
26
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
workflow: 'pricing-page-view',
|
|
95
|
-
evidence: 'visited 3x in 7d',
|
|
96
|
-
})
|
|
27
|
+
holdy.track('sync_completed', { rows: 1200 }, { userId: 'user_42', accountKey: 'acme-inc' })
|
|
28
|
+
await holdy.flush() // ou holdy.shutdown() no teardown
|
|
97
29
|
```
|
|
98
30
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
## Auto-capture (sem código)
|
|
102
|
-
|
|
103
|
-
Quando `autoCapture: true` (recomendado em desenvolvimento e produção), o SDK monitora navegação do SPA automaticamente:
|
|
104
|
-
|
|
105
|
-
- Carregamento inicial da página
|
|
106
|
-
- `pushState` / `replaceState` (Next.js, React Router, Vue Router)
|
|
107
|
-
- `popstate` (botão voltar)
|
|
108
|
-
|
|
109
|
-
Cada navegação gera `memory.append` com `{kind: 'route', path, title, referrer}`. Você vê dados no Holdy assim que o cliente carregar a página — sem precisar instrumentar nada manualmente.
|
|
110
|
-
|
|
111
|
-
Track manual continua disponível para os sinais semânticos (workflow/friction/expansion/outcome) que o auto-capture não pode adivinhar.
|
|
112
|
-
|
|
113
|
-
---
|
|
114
|
-
|
|
115
|
-
## Render in-product
|
|
116
|
-
|
|
117
|
-
Quando o backend de decisão sugere uma intervenção (tooltip, banner, modal, checklist, spotlight, CTA), o SDK renderiza automaticamente. Desabilitar:
|
|
118
|
-
|
|
119
|
-
```ts
|
|
120
|
-
Holdy.init({
|
|
121
|
-
apiKey: '...',
|
|
122
|
-
renderInterventions: false, // SDK só captura, não renderiza nada
|
|
123
|
-
})
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
---
|
|
127
|
-
|
|
128
|
-
## Reportar outcome
|
|
129
|
-
|
|
130
|
-
```ts
|
|
131
|
-
Holdy.onDecision((decision) => {
|
|
132
|
-
// ... seu código pode reagir
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
// Após o user agir em consequência:
|
|
136
|
-
Holdy.reportOutcome(decisionId, 'converted', { value: 99 })
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
---
|
|
140
|
-
|
|
141
|
-
## Configuração completa
|
|
142
|
-
|
|
143
|
-
```ts
|
|
144
|
-
interface HoldyConfig {
|
|
145
|
-
apiKey: string // hol_live_… ou hol_test_…
|
|
146
|
-
apiUrl?: string // default: https://app.holdy.com.br/api/v1
|
|
147
|
-
accountId?: string // ID Holdy ou external_id (use identify pra rico)
|
|
148
|
-
userId?: string
|
|
149
|
-
sessionId?: string
|
|
150
|
-
consumer?: 'ui' | 'csm' | 'agent'
|
|
151
|
-
debug?: boolean // console.warn em erros
|
|
152
|
-
renderInterventions?: boolean // default: true
|
|
153
|
-
autoCapture?: boolean // default: false · auto page.view tracking
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
---
|
|
158
|
-
|
|
159
|
-
## Tamanho
|
|
160
|
-
|
|
161
|
-
Bundle IIFE gzip < 15kb (budget enforced no build).
|
|
31
|
+
Server é stateless por chamada: passe `userId`/`accountKey` em overrides (estado de sessão em server multi-tenant vaza identidade).
|
|
162
32
|
|
|
163
|
-
##
|
|
33
|
+
## Semântica
|
|
164
34
|
|
|
165
|
-
|
|
35
|
+
- Fila em memória com flush automático (`flushAt` default 20 · `flushIntervalMs` default 5000) e batches de até 500.
|
|
36
|
+
- Retry é seguro: o servidor deduplica e aceita parcialmente; 5xx/network re-enfileira, 4xx dropa o batch.
|
|
37
|
+
- Nenhum método público lança — telemetria nunca derruba o app.
|
|
38
|
+
- A chave de account do cliente viaja em `context.account_external_id`; o Holdy resolve pro UUID interno (a porta nunca resolve identity).
|
package/dist/auto-capture.d.ts
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Auto-capture
|
|
2
|
+
* Auto-capture de navegação · @holdyapp/sdk/web.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Emite pageview no carregamento e a cada mudança de rota, inclusive nas que
|
|
5
|
+
* não recarregam a página (SPA). Sem isto, instrumentar exige que o cliente
|
|
6
|
+
* chame `page()` em cada tela — e o primeiro dado só aparece depois de um
|
|
7
|
+
* trabalho que ele ainda não fez.
|
|
6
8
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* · visibility_change · página voltou a ficar visível
|
|
10
|
-
*
|
|
11
|
-
* Tudo client-side · não precisa que o customer declare nada. Permite que
|
|
12
|
-
* o widget de install do Holdy mostre "✓ conectado" segundos após o user
|
|
13
|
-
* carregar a página.
|
|
14
|
-
*
|
|
15
|
-
* Idempotente · re-rodar `installAutoCapture` é no-op via guard global.
|
|
16
|
-
* Cleanup ao destroy via array de unsubscribers retornado.
|
|
9
|
+
* Idempotente: reinstalar é no-op (guard global). O retorno desfaz o patch de
|
|
10
|
+
* `history`, então `destroy()` devolve o browser como estava.
|
|
17
11
|
*/
|
|
18
|
-
export
|
|
19
|
-
|
|
12
|
+
export type NavigationKind = 'initial_load' | 'route_change';
|
|
13
|
+
export interface AutoCaptureOptions {
|
|
14
|
+
onNavigate: (kind: NavigationKind) => void;
|
|
20
15
|
}
|
|
21
|
-
export declare function installAutoCapture(
|
|
16
|
+
export declare function installAutoCapture(opts: AutoCaptureOptions): () => void;
|
|
17
|
+
//# sourceMappingURL=auto-capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-capture.d.ts","sourceRoot":"","sources":["../src/auto-capture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,cAAc,CAAA;AAE5D,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAA;CAC3C;AAMD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,IAAI,CAoEvE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entrada do bundle IIFE · global `Holdy` pra instalação por `<script>`.
|
|
3
|
+
*
|
|
4
|
+
* Existe pro cliente que não tem bundler (ou não quer mexer no build) poder
|
|
5
|
+
* instrumentar colando duas linhas no HTML. A API é a mesma de `@holdyapp/sdk/web`
|
|
6
|
+
* em forma de funções soltas, porque `Holdy.track(...)` é o que se espera de um
|
|
7
|
+
* global.
|
|
8
|
+
*
|
|
9
|
+
* Chamar antes de `init` não lança: telemetria não derruba a página do cliente.
|
|
10
|
+
*/
|
|
11
|
+
import { HoldyWeb, type HoldyWebOptions } from './web';
|
|
12
|
+
import type { Properties } from './types';
|
|
13
|
+
export declare function init(opts: HoldyWebOptions): HoldyWeb;
|
|
14
|
+
export declare function track(name: string, properties?: Properties): void;
|
|
15
|
+
export declare function identify(userId: string, traits?: Properties): void;
|
|
16
|
+
export declare function group(accountKey: string, traits?: Properties): void;
|
|
17
|
+
export declare function page(name?: string, properties?: Properties): void;
|
|
18
|
+
export declare function reset(): void;
|
|
19
|
+
export declare function flush(): Promise<void>;
|
|
20
|
+
export declare function shutdown(): Promise<void>;
|
|
21
|
+
/** Instância corrente · útil pra depurar no console do browser. */
|
|
22
|
+
export declare function getInstance(): HoldyWeb | null;
|
|
23
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,OAAO,CAAA;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAIzC,wBAAgB,IAAI,CAAC,IAAI,EAAE,eAAe,GAAG,QAAQ,CAIpD;AAED,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CAEjE;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI,CAElE;AAED,wBAAgB,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI,CAEnE;AAED,wBAAgB,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CAEjE;AAED,wBAAgB,KAAK,IAAI,IAAI,CAE5B;AAED,wBAAsB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3C;AAED,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAG9C;AAED,mEAAmE;AACnE,wBAAgB,WAAW,IAAI,QAAQ,GAAG,IAAI,CAE7C"}
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core do @holdyapp/sdk · fila + flush pra POST /api/v1/events/bulk.
|
|
3
|
+
*
|
|
4
|
+
* Retry de batch inteiro é seguro (partial accept + dedupe server-side).
|
|
5
|
+
* Fila é bounded: acima de MAX_QUEUE dropa os mais antigos (telemetria nunca
|
|
6
|
+
* derruba o app do cliente — nenhum método público lança).
|
|
7
|
+
*/
|
|
8
|
+
import type { HoldyOptions, Properties, TrackOverrides } from './types';
|
|
9
|
+
export declare const DEFAULT_BASE_URL = "https://holdy.chat/api/v1";
|
|
10
|
+
export declare class HoldyCore {
|
|
11
|
+
private readonly apiKey;
|
|
12
|
+
private readonly baseUrl;
|
|
13
|
+
private readonly flushAt;
|
|
14
|
+
private readonly debug;
|
|
15
|
+
private readonly source;
|
|
16
|
+
private queue;
|
|
17
|
+
private timer;
|
|
18
|
+
private flushing;
|
|
19
|
+
protected userId: string | null;
|
|
20
|
+
protected anonymousId: string | null;
|
|
21
|
+
protected accountKey: string | null;
|
|
22
|
+
constructor(source: 'sdk_web' | 'sdk_server', opts: HoldyOptions);
|
|
23
|
+
track(name: string, properties?: Properties, overrides?: TrackOverrides): void;
|
|
24
|
+
identify(userId: string, traits?: Properties): void;
|
|
25
|
+
group(accountKey: string, traits?: Properties): void;
|
|
26
|
+
/** Drena a fila. Nunca lança — falha re-enfileira e loga em debug. */
|
|
27
|
+
flush(): Promise<void>;
|
|
28
|
+
/** Flush final + para o timer. Chamar em shutdown do server / unload do web. */
|
|
29
|
+
shutdown(): Promise<void>;
|
|
30
|
+
private drain;
|
|
31
|
+
private send;
|
|
32
|
+
private log;
|
|
33
|
+
/**
|
|
34
|
+
* Ambiente que o SDK observa sozinho (url, referrer, browser, utm…).
|
|
35
|
+
* Vazio no core; `HoldyWeb` preenche. O server não infere ambiente — não
|
|
36
|
+
* existe "página atual" num worker.
|
|
37
|
+
*/
|
|
38
|
+
protected environmentContext(): Properties;
|
|
39
|
+
/** Interno · exposto pra testes. */
|
|
40
|
+
get pending(): number;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAkB,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAEvF,eAAO,MAAM,gBAAgB,8BAA8B,CAAA;AAM3D,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IAEjD,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,QAAQ,CAA6B;IAE7C,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAO;IACtC,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAO;IAC3C,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAO;gBAE9B,MAAM,EAAE,SAAS,GAAG,YAAY,EAAE,IAAI,EAAE,YAAY;IAkBhE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,cAAc,GAAG,IAAI;IAiC9E,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI;IAMnD,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI;IAMpD,sEAAsE;IAChE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAW5B,gFAAgF;IAC1E,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAQjB,KAAK;YAYL,IAAI;IA2BlB,OAAO,CAAC,GAAG;IAIX;;;;OAIG;IACH,SAAS,CAAC,kBAAkB,IAAI,UAAU;IAI1C,oCAAoC;IACpC,IAAI,OAAO,IAAI,MAAM,CAEpB;CACF"}
|
package/dist/holdy.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var Holdy=(()=>{var g=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var T=(n,t)=>{for(var e in t)g(n,e,{get:t[e],enumerable:!0})},k=(n,t,e,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of C(t))!P.call(n,i)&&i!==e&&g(n,i,{get:()=>t[i],enumerable:!(o=O(t,i))||o.enumerable});return n};var H=n=>k(g({},"__esModule",{value:!0}),n);var z={};T(z,{flush:()=>Q,getInstance:()=>V,group:()=>Y,identify:()=>G,init:()=>q,page:()=>j,reset:()=>J,shutdown:()=>X,track:()=>B});var W="https://holdy.chat/api/v1";var d=class{apiKey;baseUrl;flushAt;debug;source;queue=[];timer=null;flushing=null;userId=null;anonymousId=null;accountKey=null;constructor(t,e){this.source=t,this.apiKey=e.apiKey,this.accountKey=e.accountKey??null,this.baseUrl=(e.baseUrl??W).replace(/\/$/,""),this.flushAt=Math.max(1,e.flushAt??20),this.debug=e.debug??!1;let o=e.flushIntervalMs??5e3;o>0&&(this.timer=setInterval(()=>void this.flush(),o),typeof this.timer.unref=="function"&&this.timer.unref())}track(t,e,o){if(!t||typeof t!="string")return;let i=o?.userId??this.userId??void 0,r=o?.accountKey??this.accountKey??void 0,s=o?.occurredAt?new Date(o.occurredAt).toISOString():new Date().toISOString(),u={...this.environmentContext(),...o?.context??{}};r&&(u.account_external_id=r);let c={event:t,occurred_at:s,source:this.source,...i?{user_id:i}:{},...this.anonymousId?{anonymous_id:this.anonymousId}:{},properties:e??{},...Object.keys(u).length>0?{context:u}:{}};this.queue.push(c),this.queue.length>1e3&&this.queue.splice(0,this.queue.length-1e3),this.queue.length>=this.flushAt&&this.flush()}identify(t,e){!t||typeof t!="string"||(this.userId=t,this.track("$identify",e??{}))}group(t,e){!t||typeof t!="string"||(this.accountKey=t,this.track("$group",e??{}))}async flush(){if(this.flushing)return this.flushing;if(this.queue.length!==0)return this.flushing=this.drain().finally(()=>{this.flushing=null}),this.flushing}async shutdown(){this.timer&&(clearInterval(this.timer),this.timer=null),await this.flush()}async drain(){for(;this.queue.length>0;){let t=this.queue.splice(0,500);if(!await this.send(t)){this.queue.unshift(...t);return}}}async send(t){try{let e=await fetch(`${this.baseUrl}/events/bulk`,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({events:t}),...this.source==="sdk_web"?{keepalive:!0}:{}});return e.ok?!0:e.status>=400&&e.status<500?(this.log(`bulk rejected ${e.status} \xB7 batch dropped`),!0):(this.log(`bulk failed ${e.status} \xB7 will retry`),!1)}catch(e){return this.log(`bulk network error: ${e.message} \xB7 will retry`),!1}}log(t){this.debug&&console.warn(`[holdy] ${t}`)}environmentContext(){return{}}get pending(){return this.queue.length}};var y="__holdy_autocapture__";function _(n){if(typeof window>"u"||typeof history>"u")return()=>{};let t=window;if(t[y])return()=>{};t[y]=!0;let e=[],o=b(),i=null;function r(l){try{n.onNavigate(l)}catch{}}function s(){i&&clearTimeout(i),i=setTimeout(()=>{i=null;let l=b();l!==o&&(o=l,r("route_change"))},50)}r("initial_load");let u=history.pushState,c=history.replaceState;history.pushState=function(...h){let f=u.apply(this,h);return s(),f},history.replaceState=function(...h){let f=c.apply(this,h);return s(),f},e.push(()=>{history.pushState=u,history.replaceState=c});let v=()=>s();window.addEventListener("popstate",v),e.push(()=>window.removeEventListener("popstate",v));let w=()=>s();return window.addEventListener("hashchange",w),e.push(()=>window.removeEventListener("hashchange",w)),()=>{i&&clearTimeout(i);for(let l of e)l();t[y]=!1}}function b(){return typeof location>"u"?"/":`${location.pathname}${location.search}${location.hash}`}var K=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],S="holdy_session",x="holdy_attribution";function A(n){try{let t=new URL(n);return`${t.origin}${t.pathname}`}catch{return null}}function N(n){let t={};try{let e=new URL(n).searchParams;for(let o of K){let i=e.get(o)?.trim();i&&(t[o]=i.toLowerCase().slice(0,120))}}catch{}return t}function R(n){if(!n)return{};let t=n.toLowerCase(),e=t.includes("edg/")?"Edge":t.includes("opr/")||t.includes("opera")?"Opera":t.includes("samsungbrowser")?"Samsung Internet":t.includes("firefox")||t.includes("fxios")?"Firefox":t.includes("chrome")||t.includes("crios")?"Chrome":t.includes("safari")?"Safari":void 0,o=t.includes("iphone")||t.includes("ipad")||t.includes("ipod")?"iOS":t.includes("android")?"Android":t.includes("windows")?"Windows":t.includes("mac os")||t.includes("macintosh")?"macOS":t.includes("cros")?"ChromeOS":t.includes("linux")?"Linux":void 0,i=t.includes("ipad")||t.includes("android")&&!t.includes("mobile")?"tablet":t.includes("mobi")||t.includes("iphone")||t.includes("ipod")?"mobile":"desktop";return{...e?{browser:e}:{},...o?{os:o}:{},device:i}}function E(n,t){try{return(n==="session"?sessionStorage:localStorage).getItem(t)}catch{return null}}function U(n,t,e){try{(n==="session"?sessionStorage:localStorage).setItem(t,e)}catch{}}function $(){try{return crypto.randomUUID()}catch{return`hol-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,10)}`}}function D(){let n=E("session",S);if(n)return n;let t=$();return U("session",S,t),t}function F(n,t,e){let o=E("session",x);if(o)try{return JSON.parse(o)}catch{}let i={...N(n)},r=M(t);if(r&&r!==e){let s=A(t);s&&(i.referrer=s),i.referrer_host=r}return U("session",x,JSON.stringify(i)),i}function M(n){try{return new URL(n).hostname.toLowerCase()||null}catch{return null}}function I(){if(typeof window>"u"||typeof location>"u")return{};let n=location.href,t=typeof document<"u"?document.referrer:"",e=location.hostname.toLowerCase(),o=typeof navigator<"u"?navigator.userAgent:"",{browser:i,os:r,device:s}=R(o),u=A(n);return{...u?{url:u}:{},path:location.pathname||"/",host:e,...i?{browser:i}:{},...r?{os:r}:{},...s?{device:s}:{},...typeof navigator<"u"&&navigator.language?{locale:navigator.language}:{},session_id:D(),...F(n,t,e)}}var m="holdy_anon_id",p=class extends d{teardownAutoCapture=null;constructor(t){super("sdk_web",t),this.anonymousId=L(),typeof window<"u"&&(window.addEventListener("pagehide",()=>void this.flush()),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flush()})),t.autoCapture!==!1&&(this.teardownAutoCapture=_({onNavigate:e=>this.page(void 0,{navigation:e})}))}page(t,e){this.track("$pageview",{...e??{},...t?{page_name:t}:{},...typeof document<"u"&&document.title?{title:document.title}:{}})}reset(){this.userId=null,this.accountKey=null;try{localStorage.removeItem(m)}catch{}this.anonymousId=L()}async shutdown(){this.teardownAutoCapture?.(),this.teardownAutoCapture=null,await super.shutdown()}environmentContext(){return I()}};function L(){try{let n=localStorage.getItem(m);if(n)return n;let t=crypto.randomUUID();return localStorage.setItem(m,t),t}catch{return crypto.randomUUID()}}var a=null;function q(n){return a||(a=new p(n)),a}function B(n,t){a?.track(n,t)}function G(n,t){a?.identify(n,t)}function Y(n,t){a?.group(n,t)}function j(n,t){a?.page(n,t)}function J(){a?.reset()}async function Q(){await a?.flush()}async function X(){await a?.shutdown(),a=null}function V(){return a}return H(z);})();
|
|
2
|
+
//# sourceMappingURL=holdy.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/browser.ts", "../src/core.ts", "../src/auto-capture.ts", "../src/web-context.ts", "../src/web.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Entrada do bundle IIFE \u00B7 global `Holdy` pra instala\u00E7\u00E3o por `<script>`.\n *\n * Existe pro cliente que n\u00E3o tem bundler (ou n\u00E3o quer mexer no build) poder\n * instrumentar colando duas linhas no HTML. A API \u00E9 a mesma de `@holdyapp/sdk/web`\n * em forma de fun\u00E7\u00F5es soltas, porque `Holdy.track(...)` \u00E9 o que se espera de um\n * global.\n *\n * Chamar antes de `init` n\u00E3o lan\u00E7a: telemetria n\u00E3o derruba a p\u00E1gina do cliente.\n */\n\nimport { HoldyWeb, type HoldyWebOptions } from './web'\nimport type { Properties } from './types'\n\nlet instance: HoldyWeb | null = null\n\nexport function init(opts: HoldyWebOptions): HoldyWeb {\n // Idempotente: script duplicado na p\u00E1gina n\u00E3o duplica evento.\n if (!instance) instance = new HoldyWeb(opts)\n return instance\n}\n\nexport function track(name: string, properties?: Properties): void {\n instance?.track(name, properties)\n}\n\nexport function identify(userId: string, traits?: Properties): void {\n instance?.identify(userId, traits)\n}\n\nexport function group(accountKey: string, traits?: Properties): void {\n instance?.group(accountKey, traits)\n}\n\nexport function page(name?: string, properties?: Properties): void {\n instance?.page(name, properties)\n}\n\nexport function reset(): void {\n instance?.reset()\n}\n\nexport async function flush(): Promise<void> {\n await instance?.flush()\n}\n\nexport async function shutdown(): Promise<void> {\n await instance?.shutdown()\n instance = null\n}\n\n/** Inst\u00E2ncia corrente \u00B7 \u00FAtil pra depurar no console do browser. */\nexport function getInstance(): HoldyWeb | null {\n return instance\n}\n", "/**\n * Core do @holdyapp/sdk \u00B7 fila + flush pra POST /api/v1/events/bulk.\n *\n * Retry de batch inteiro \u00E9 seguro (partial accept + dedupe server-side).\n * Fila \u00E9 bounded: acima de MAX_QUEUE dropa os mais antigos (telemetria nunca\n * derruba o app do cliente \u2014 nenhum m\u00E9todo p\u00FAblico lan\u00E7a).\n */\n\nimport type { CanonicalEvent, HoldyOptions, Properties, TrackOverrides } from './types'\n\nexport const DEFAULT_BASE_URL = 'https://holdy.chat/api/v1'\nconst DEFAULT_FLUSH_AT = 20\nconst DEFAULT_FLUSH_INTERVAL_MS = 5000\nconst MAX_QUEUE = 1000\nconst BULK_LIMIT = 500\n\nexport class HoldyCore {\n private readonly apiKey: string\n private readonly baseUrl: string\n private readonly flushAt: number\n private readonly debug: boolean\n private readonly source: 'sdk_web' | 'sdk_server'\n\n private queue: CanonicalEvent[] = []\n private timer: ReturnType<typeof setInterval> | null = null\n private flushing: Promise<void> | null = null\n\n protected userId: string | null = null\n protected anonymousId: string | null = null\n protected accountKey: string | null = null\n\n constructor(source: 'sdk_web' | 'sdk_server', opts: HoldyOptions) {\n this.source = source\n this.apiKey = opts.apiKey\n this.accountKey = opts.accountKey ?? null\n this.baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '')\n this.flushAt = Math.max(1, opts.flushAt ?? DEFAULT_FLUSH_AT)\n this.debug = opts.debug ?? false\n\n const intervalMs = opts.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS\n if (intervalMs > 0) {\n this.timer = setInterval(() => void this.flush(), intervalMs)\n // Node: n\u00E3o segura o processo vivo s\u00F3 pelo timer do SDK.\n if (typeof (this.timer as { unref?: () => void }).unref === 'function') {\n ;(this.timer as unknown as { unref: () => void }).unref()\n }\n }\n }\n\n track(name: string, properties?: Properties, overrides?: TrackOverrides): void {\n if (!name || typeof name !== 'string') return\n\n const userId = overrides?.userId ?? this.userId ?? undefined\n const accountKey = overrides?.accountKey ?? this.accountKey ?? undefined\n const occurredAt = overrides?.occurredAt\n ? new Date(overrides.occurredAt).toISOString()\n : new Date().toISOString()\n\n // Ordem: ambiente observado pelo SDK \u2192 context da chamada \u2192 carrier de\n // account. O que o caller passou vence o inferido; o carrier vence tudo,\n // porque \u00E9 o que a Data layer usa pra resolver a conta.\n const context: Properties = {\n ...this.environmentContext(),\n ...(overrides?.context ?? {}),\n }\n if (accountKey) context.account_external_id = accountKey\n\n const event: CanonicalEvent = {\n event: name,\n occurred_at: occurredAt,\n source: this.source,\n ...(userId ? { user_id: userId } : {}),\n ...(this.anonymousId ? { anonymous_id: this.anonymousId } : {}),\n properties: properties ?? {},\n ...(Object.keys(context).length > 0 ? { context } : {}),\n }\n\n this.queue.push(event)\n if (this.queue.length > MAX_QUEUE) this.queue.splice(0, this.queue.length - MAX_QUEUE)\n if (this.queue.length >= this.flushAt) void this.flush()\n }\n\n identify(userId: string, traits?: Properties): void {\n if (!userId || typeof userId !== 'string') return\n this.userId = userId\n this.track('$identify', traits ?? {})\n }\n\n group(accountKey: string, traits?: Properties): void {\n if (!accountKey || typeof accountKey !== 'string') return\n this.accountKey = accountKey\n this.track('$group', traits ?? {})\n }\n\n /** Drena a fila. Nunca lan\u00E7a \u2014 falha re-enfileira e loga em debug. */\n async flush(): Promise<void> {\n // Serializa flushes concorrentes (timer + flushAt + manual).\n if (this.flushing) return this.flushing\n if (this.queue.length === 0) return\n\n this.flushing = this.drain().finally(() => {\n this.flushing = null\n })\n return this.flushing\n }\n\n /** Flush final + para o timer. Chamar em shutdown do server / unload do web. */\n async shutdown(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer)\n this.timer = null\n }\n await this.flush()\n }\n\n private async drain(): Promise<void> {\n while (this.queue.length > 0) {\n const batch = this.queue.splice(0, BULK_LIMIT)\n const ok = await this.send(batch)\n if (!ok) {\n // Devolve pra frente da fila e para \u2014 pr\u00F3ximo flush re-tenta (dedupe-safe).\n this.queue.unshift(...batch)\n return\n }\n }\n }\n\n private async send(events: CanonicalEvent[]): Promise<boolean> {\n try {\n const res = await fetch(`${this.baseUrl}/events/bulk`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ events }),\n ...(this.source === 'sdk_web' ? { keepalive: true } : {}),\n })\n if (!res.ok) {\n // 4xx: batch estruturalmente inv\u00E1lido n\u00E3o re-tenta (evita loop infinito).\n if (res.status >= 400 && res.status < 500) {\n this.log(`bulk rejected ${res.status} \u00B7 batch dropped`)\n return true\n }\n this.log(`bulk failed ${res.status} \u00B7 will retry`)\n return false\n }\n return true\n } catch (err) {\n this.log(`bulk network error: ${(err as Error).message} \u00B7 will retry`)\n return false\n }\n }\n\n private log(msg: string): void {\n if (this.debug) console.warn(`[holdy] ${msg}`)\n }\n\n /**\n * Ambiente que o SDK observa sozinho (url, referrer, browser, utm\u2026).\n * Vazio no core; `HoldyWeb` preenche. O server n\u00E3o infere ambiente \u2014 n\u00E3o\n * existe \"p\u00E1gina atual\" num worker.\n */\n protected environmentContext(): Properties {\n return {}\n }\n\n /** Interno \u00B7 exposto pra testes. */\n get pending(): number {\n return this.queue.length\n }\n}\n", "/**\n * Auto-capture de navega\u00E7\u00E3o \u00B7 @holdyapp/sdk/web.\n *\n * Emite pageview no carregamento e a cada mudan\u00E7a de rota, inclusive nas que\n * n\u00E3o recarregam a p\u00E1gina (SPA). Sem isto, instrumentar exige que o cliente\n * chame `page()` em cada tela \u2014 e o primeiro dado s\u00F3 aparece depois de um\n * trabalho que ele ainda n\u00E3o fez.\n *\n * Idempotente: reinstalar \u00E9 no-op (guard global). O retorno desfaz o patch de\n * `history`, ent\u00E3o `destroy()` devolve o browser como estava.\n */\n\nconst INSTALLED_FLAG = '__holdy_autocapture__'\n\n/** pushState seguido de replaceState \u00E9 uma mudan\u00E7a l\u00F3gica s\u00F3. */\nconst ROUTE_DEBOUNCE_MS = 50\n\nexport type NavigationKind = 'initial_load' | 'route_change'\n\nexport interface AutoCaptureOptions {\n onNavigate: (kind: NavigationKind) => void\n}\n\ninterface GlobalWithFlag {\n [INSTALLED_FLAG]?: boolean\n}\n\nexport function installAutoCapture(opts: AutoCaptureOptions): () => void {\n if (typeof window === 'undefined' || typeof history === 'undefined') {\n return () => undefined\n }\n\n const w = window as unknown as GlobalWithFlag\n if (w[INSTALLED_FLAG]) return () => undefined\n w[INSTALLED_FLAG] = true\n\n const cleanups: Array<() => void> = []\n let lastPath = currentPath()\n let pending: ReturnType<typeof setTimeout> | null = null\n\n function emit(kind: NavigationKind): void {\n try {\n opts.onNavigate(kind)\n } catch {\n // Telemetria nunca derruba a app do cliente.\n }\n }\n\n function scheduleRouteEmit(): void {\n if (pending) clearTimeout(pending)\n pending = setTimeout(() => {\n pending = null\n const path = currentPath()\n // S\u00F3 conta como pageview se a rota mudou de fato \u2014 replaceState de\n // estado interno (filtro, scroll) n\u00E3o \u00E9 navega\u00E7\u00E3o.\n if (path === lastPath) return\n lastPath = path\n emit('route_change')\n }, ROUTE_DEBOUNCE_MS)\n }\n\n emit('initial_load')\n\n const originalPush = history.pushState\n const originalReplace = history.replaceState\n\n history.pushState = function patchedPush(...args: Parameters<History['pushState']>) {\n const result = originalPush.apply(this, args)\n scheduleRouteEmit()\n return result\n }\n history.replaceState = function patchedReplace(...args: Parameters<History['replaceState']>) {\n const result = originalReplace.apply(this, args)\n scheduleRouteEmit()\n return result\n }\n cleanups.push(() => {\n history.pushState = originalPush\n history.replaceState = originalReplace\n })\n\n // Voltar/avan\u00E7ar do browser e routers baseados em hash.\n const onPop = (): void => scheduleRouteEmit()\n window.addEventListener('popstate', onPop)\n cleanups.push(() => window.removeEventListener('popstate', onPop))\n\n const onHash = (): void => scheduleRouteEmit()\n window.addEventListener('hashchange', onHash)\n cleanups.push(() => window.removeEventListener('hashchange', onHash))\n\n return () => {\n if (pending) clearTimeout(pending)\n for (const cleanup of cleanups) cleanup()\n w[INSTALLED_FLAG] = false\n }\n}\n\n/** Rota compar\u00E1vel \u00B7 path + query, sem hash de \u00E2ncora. */\nfunction currentPath(): string {\n if (typeof location === 'undefined') return '/'\n return `${location.pathname}${location.search}${location.hash}`\n}\n", "/**\n * Contexto de ambiente do browser \u00B7 @holdyapp/sdk/web.\n *\n * Vai em `context` de todo evento, nunca em `properties`: properties \u00E9 o\n * vocabul\u00E1rio do produto do cliente (`workflow_key`, `plan`), context \u00E9 o que o\n * SDK observou sozinho. A Data layer persiste os dois (facts.context, 00247) e\n * a leitura de dimens\u00E3o consulta os dois.\n *\n * Tr\u00EAs decis\u00F5es que definem se esse dado \u00E9 \u00FAtil ou lixo:\n * \u00B7 `url` sai sem query e sem hash. Href cru tem uma varia\u00E7\u00E3o por sess\u00E3o, e\n * dimens\u00E3o com cardinalidade quase-\u00FAnica \u00E9 descartada na leitura \u2014 a\n * p\u00E1gina vira ru\u00EDdo exatamente por excesso de precis\u00E3o.\n * \u00B7 utm \u00E9 capturado na primeira p\u00E1gina e carregado pelo resto da sess\u00E3o.\n * Atribui\u00E7\u00E3o que s\u00F3 existe no primeiro evento n\u00E3o responde nada.\n * \u00B7 referrer s\u00F3 quando \u00E9 externo. Navega\u00E7\u00E3o interna preenchendo \"Origem do\n * tr\u00E1fego\" com o pr\u00F3prio dom\u00EDnio esconde a origem real.\n *\n * Tudo aqui \u00E9 best-effort: browser em modo privativo, SSR e webview sem\n * storage precisam funcionar. Nenhuma fun\u00E7\u00E3o lan\u00E7a.\n */\n\nexport type WebContext = Record<string, string>\n\nconst UTM_KEYS = [\n 'utm_source',\n 'utm_medium',\n 'utm_campaign',\n 'utm_term',\n 'utm_content',\n] as const\n\nconst SESSION_STORAGE_KEY = 'holdy_session'\nconst ATTRIBUTION_STORAGE_KEY = 'holdy_attribution'\n\n/* \u2500\u2500 URL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/** `https://app.x.com/a/b?q=1#z` \u2192 `https://app.x.com/a/b`. */\nexport function stripUrl(href: string): string | null {\n try {\n const url = new URL(href)\n return `${url.origin}${url.pathname}`\n } catch {\n return null\n }\n}\n\n/** Par\u00E2metros de campanha presentes na URL, j\u00E1 em lowercase. */\nexport function parseUtm(href: string): WebContext {\n const out: WebContext = {}\n try {\n const params = new URL(href).searchParams\n for (const key of UTM_KEYS) {\n const value = params.get(key)?.trim()\n if (value) out[key] = value.toLowerCase().slice(0, 120)\n }\n } catch {\n // href inv\u00E1lido \u2014 sem utm, sem drama.\n }\n return out\n}\n\n/* \u2500\u2500 User agent \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\ninterface UaParts {\n browser?: string\n os?: string\n device?: string\n}\n\n/**\n * Fatia m\u00EDnima do UA: navegador, sistema e classe de dispositivo.\n *\n * N\u00E3o \u00E9 biblioteca de UA parsing e n\u00E3o deve virar uma \u2014 o valor est\u00E1 em\n * separar mobile de desktop e Safari de Chrome, n\u00E3o em cravar a vers\u00E3o.\n */\nexport function parseUserAgent(ua: string): UaParts {\n if (!ua) return {}\n const s = ua.toLowerCase()\n\n const browser = s.includes('edg/')\n ? 'Edge'\n : s.includes('opr/') || s.includes('opera')\n ? 'Opera'\n : s.includes('samsungbrowser')\n ? 'Samsung Internet'\n : s.includes('firefox') || s.includes('fxios')\n ? 'Firefox'\n : s.includes('chrome') || s.includes('crios')\n ? 'Chrome'\n : s.includes('safari')\n ? 'Safari'\n : undefined\n\n const os = s.includes('iphone') || s.includes('ipad') || s.includes('ipod')\n ? 'iOS'\n : s.includes('android')\n ? 'Android'\n : s.includes('windows')\n ? 'Windows'\n : s.includes('mac os') || s.includes('macintosh')\n ? 'macOS'\n : s.includes('cros')\n ? 'ChromeOS'\n : s.includes('linux')\n ? 'Linux'\n : undefined\n\n // iPad moderno se declara macOS; a pista que sobra \u00E9 o suporte a toque, que\n // o parser puro n\u00E3o v\u00EA. Tablet aqui cobre o caso declarado no UA.\n const device = s.includes('ipad') || (s.includes('android') && !s.includes('mobile'))\n ? 'tablet'\n : s.includes('mobi') || s.includes('iphone') || s.includes('ipod')\n ? 'mobile'\n : 'desktop'\n\n return {\n ...(browser ? { browser } : {}),\n ...(os ? { os } : {}),\n device,\n }\n}\n\n/* \u2500\u2500 Storage \u00B7 nunca lan\u00E7a \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfunction readStorage(storage: 'session' | 'local', key: string): string | null {\n try {\n const s = storage === 'session' ? sessionStorage : localStorage\n return s.getItem(key)\n } catch {\n return null\n }\n}\n\nfunction writeStorage(storage: 'session' | 'local', key: string, value: string): void {\n try {\n const s = storage === 'session' ? sessionStorage : localStorage\n s.setItem(key, value)\n } catch {\n // Modo privativo / storage bloqueado \u2014 o contexto degrada, o evento segue.\n }\n}\n\nfunction randomId(): string {\n try {\n return crypto.randomUUID()\n } catch {\n return `hol-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`\n }\n}\n\n/** ID da sess\u00E3o de browser \u00B7 vive enquanto a aba viver. */\nexport function resolveSessionId(): string {\n const existing = readStorage('session', SESSION_STORAGE_KEY)\n if (existing) return existing\n const fresh = randomId()\n writeStorage('session', SESSION_STORAGE_KEY, fresh)\n return fresh\n}\n\n/**\n * Atribui\u00E7\u00E3o da sess\u00E3o: utm da primeira p\u00E1gina + referrer externo de entrada.\n * Gravada uma vez e repetida em todo evento da sess\u00E3o.\n */\nexport function resolveAttribution(href: string, referrer: string, currentHost: string): WebContext {\n const stored = readStorage('session', ATTRIBUTION_STORAGE_KEY)\n if (stored) {\n try {\n return JSON.parse(stored) as WebContext\n } catch {\n // Valor corrompido \u2014 recalcula por cima.\n }\n }\n\n const attribution: WebContext = { ...parseUtm(href) }\n\n const referrerHost = hostOf(referrer)\n // Referrer interno n\u00E3o \u00E9 origem de tr\u00E1fego, \u00E9 navega\u00E7\u00E3o.\n if (referrerHost && referrerHost !== currentHost) {\n const clean = stripUrl(referrer)\n if (clean) attribution.referrer = clean\n attribution.referrer_host = referrerHost\n }\n\n writeStorage('session', ATTRIBUTION_STORAGE_KEY, JSON.stringify(attribution))\n return attribution\n}\n\nfunction hostOf(href: string): string | null {\n try {\n return new URL(href).hostname.toLowerCase() || null\n } catch {\n return null\n }\n}\n\n/* \u2500\u2500 Montagem \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/**\n * Contexto completo do evento atual. Fora do browser devolve `{}` \u2014 o SDK\n * server tem o pr\u00F3prio caminho e n\u00E3o deve inventar ambiente.\n */\nexport function buildWebContext(): WebContext {\n if (typeof window === 'undefined' || typeof location === 'undefined') return {}\n\n const href = location.href\n const referrer = typeof document !== 'undefined' ? document.referrer : ''\n const host = location.hostname.toLowerCase()\n\n const ua = typeof navigator !== 'undefined' ? navigator.userAgent : ''\n const { browser, os, device } = parseUserAgent(ua)\n\n const url = stripUrl(href)\n\n return {\n ...(url ? { url } : {}),\n path: location.pathname || '/',\n host,\n ...(browser ? { browser } : {}),\n ...(os ? { os } : {}),\n ...(device ? { device } : {}),\n ...(typeof navigator !== 'undefined' && navigator.language\n ? { locale: navigator.language }\n : {}),\n session_id: resolveSessionId(),\n ...resolveAttribution(href, referrer, host),\n }\n}\n", "/**\n * @holdyapp/sdk/web \u00B7 browser (source: sdk_web).\n *\n * anonymous_id gerado e persistido em localStorage (holdy_anon_id) \u2014 enviado em\n * todo event at\u00E9 identify() promover pra user_id (identity graph faz o merge\n * downstream, nunca o SDK). Flush no pagehide via fetch keepalive.\n *\n * Todo evento carrega o contexto de ambiente (url, path, referrer externo,\n * utm da sess\u00E3o, browser, os, device, locale, session_id) em `context` \u2014\n * separado das properties do produto. Pageview \u00E9 autom\u00E1tico por default:\n * carregamento inicial e cada mudan\u00E7a de rota, inclusive em SPA.\n */\n\nimport { HoldyCore } from './core'\nimport { installAutoCapture, type NavigationKind } from './auto-capture'\nimport { buildWebContext } from './web-context'\nimport type { HoldyOptions, Properties } from './types'\n\nconst ANON_STORAGE_KEY = 'holdy_anon_id'\n\nexport interface HoldyWebOptions extends HoldyOptions {\n /**\n * Emitir `$pageview` sozinho no load e a cada mudan\u00E7a de rota \u00B7 default true.\n * Desligue s\u00F3 se o produto j\u00E1 chama `page()` na m\u00E3o em cada tela.\n */\n autoCapture?: boolean\n}\n\nexport class HoldyWeb extends HoldyCore {\n private teardownAutoCapture: (() => void) | null = null\n\n constructor(opts: HoldyWebOptions) {\n super('sdk_web', opts)\n this.anonymousId = resolveAnonymousId()\n\n if (typeof window !== 'undefined') {\n window.addEventListener('pagehide', () => void this.flush())\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') void this.flush()\n })\n }\n\n if (opts.autoCapture !== false) {\n this.teardownAutoCapture = installAutoCapture({\n onNavigate: (kind: NavigationKind) => this.page(undefined, { navigation: kind }),\n })\n }\n }\n\n /**\n * Pageview. O `$` marca evento emitido pelo SDK, n\u00E3o pelo produto (mesma\n * conven\u00E7\u00E3o de `$identify`/`$group`); a canonicaliza\u00E7\u00E3o downstream remove o\n * sigilo, ent\u00E3o `$pageview` e `pageview` s\u00E3o o mesmo evento na leitura.\n * url/path/referrer/utm j\u00E1 v\u00EAm no context de todo evento \u2014 aqui s\u00F3 entra o\n * que \u00E9 da p\u00E1gina como conte\u00FAdo (nome e t\u00EDtulo).\n */\n page(name?: string, properties?: Properties): void {\n this.track('$pageview', {\n ...(properties ?? {}),\n ...(name ? { page_name: name } : {}),\n ...(typeof document !== 'undefined' && document.title ? { title: document.title } : {}),\n })\n }\n\n reset(): void {\n this.userId = null\n this.accountKey = null\n try {\n localStorage.removeItem(ANON_STORAGE_KEY)\n } catch {\n // storage indispon\u00EDvel (SSR/privacy mode) \u2014 ok\n }\n this.anonymousId = resolveAnonymousId()\n }\n\n /** Flush final + desinstala o auto-capture (devolve `history` ao original). */\n override async shutdown(): Promise<void> {\n this.teardownAutoCapture?.()\n this.teardownAutoCapture = null\n await super.shutdown()\n }\n\n protected override environmentContext(): Properties {\n return buildWebContext()\n }\n}\n\nfunction resolveAnonymousId(): string {\n try {\n const existing = localStorage.getItem(ANON_STORAGE_KEY)\n if (existing) return existing\n const fresh = crypto.randomUUID()\n localStorage.setItem(ANON_STORAGE_KEY, fresh)\n return fresh\n } catch {\n return crypto.randomUUID()\n }\n}\n\nlet singleton: HoldyWeb | null = null\n\n/** Init idempotente \u2014 segunda chamada retorna a inst\u00E2ncia existente. */\nexport function initHoldy(opts: HoldyWebOptions): HoldyWeb {\n if (!singleton) singleton = new HoldyWeb(opts)\n return singleton\n}\n\nexport type { HoldyOptions, Properties }\n"],
|
|
5
|
+
"mappings": "ybAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,WAAAE,EAAA,gBAAAC,EAAA,UAAAC,EAAA,aAAAC,EAAA,SAAAC,EAAA,SAAAC,EAAA,UAAAC,EAAA,aAAAC,EAAA,UAAAC,ICUO,IAAMC,EAAmB,4BAMzB,IAAMC,EAAN,KAAgB,CACJ,OACA,QACA,QACA,MACA,OAET,MAA0B,CAAC,EAC3B,MAA+C,KAC/C,SAAiC,KAE/B,OAAwB,KACxB,YAA6B,KAC7B,WAA4B,KAEtC,YAAYC,EAAkCC,EAAoB,CAChE,KAAK,OAASD,EACd,KAAK,OAASC,EAAK,OACnB,KAAK,WAAaA,EAAK,YAAc,KACrC,KAAK,SAAWA,EAAK,SAAWC,GAAkB,QAAQ,MAAO,EAAE,EACnE,KAAK,QAAU,KAAK,IAAI,EAAGD,EAAK,SAAW,EAAgB,EAC3D,KAAK,MAAQA,EAAK,OAAS,GAE3B,IAAME,EAAaF,EAAK,iBAAmB,IACvCE,EAAa,IACf,KAAK,MAAQ,YAAY,IAAM,KAAK,KAAK,MAAM,EAAGA,CAAU,EAExD,OAAQ,KAAK,MAAiC,OAAU,YACxD,KAAK,MAA2C,MAAM,EAG9D,CAEA,MAAMC,EAAcC,EAAyBC,EAAkC,CAC7E,GAAI,CAACF,GAAQ,OAAOA,GAAS,SAAU,OAEvC,IAAMG,EAASD,GAAW,QAAU,KAAK,QAAU,OAC7CE,EAAaF,GAAW,YAAc,KAAK,YAAc,OACzDG,EAAaH,GAAW,WAC1B,IAAI,KAAKA,EAAU,UAAU,EAAE,YAAY,EAC3C,IAAI,KAAK,EAAE,YAAY,EAKrBI,EAAsB,CAC1B,GAAG,KAAK,mBAAmB,EAC3B,GAAIJ,GAAW,SAAW,CAAC,CAC7B,EACIE,IAAYE,EAAQ,oBAAsBF,GAE9C,IAAMG,EAAwB,CAC5B,MAAOP,EACP,YAAaK,EACb,OAAQ,KAAK,OACb,GAAIF,EAAS,CAAE,QAASA,CAAO,EAAI,CAAC,EACpC,GAAI,KAAK,YAAc,CAAE,aAAc,KAAK,WAAY,EAAI,CAAC,EAC7D,WAAYF,GAAc,CAAC,EAC3B,GAAI,OAAO,KAAKK,CAAO,EAAE,OAAS,EAAI,CAAE,QAAAA,CAAQ,EAAI,CAAC,CACvD,EAEA,KAAK,MAAM,KAAKC,CAAK,EACjB,KAAK,MAAM,OAAS,KAAW,KAAK,MAAM,OAAO,EAAG,KAAK,MAAM,OAAS,GAAS,EACjF,KAAK,MAAM,QAAU,KAAK,SAAc,KAAK,MAAM,CACzD,CAEA,SAASJ,EAAgBK,EAA2B,CAC9C,CAACL,GAAU,OAAOA,GAAW,WACjC,KAAK,OAASA,EACd,KAAK,MAAM,YAAaK,GAAU,CAAC,CAAC,EACtC,CAEA,MAAMJ,EAAoBI,EAA2B,CAC/C,CAACJ,GAAc,OAAOA,GAAe,WACzC,KAAK,WAAaA,EAClB,KAAK,MAAM,SAAUI,GAAU,CAAC,CAAC,EACnC,CAGA,MAAM,OAAuB,CAE3B,GAAI,KAAK,SAAU,OAAO,KAAK,SAC/B,GAAI,KAAK,MAAM,SAAW,EAE1B,YAAK,SAAW,KAAK,MAAM,EAAE,QAAQ,IAAM,CACzC,KAAK,SAAW,IAClB,CAAC,EACM,KAAK,QACd,CAGA,MAAM,UAA0B,CAC1B,KAAK,QACP,cAAc,KAAK,KAAK,EACxB,KAAK,MAAQ,MAEf,MAAM,KAAK,MAAM,CACnB,CAEA,MAAc,OAAuB,CACnC,KAAO,KAAK,MAAM,OAAS,GAAG,CAC5B,IAAMC,EAAQ,KAAK,MAAM,OAAO,EAAG,GAAU,EAE7C,GAAI,CADO,MAAM,KAAK,KAAKA,CAAK,EACvB,CAEP,KAAK,MAAM,QAAQ,GAAGA,CAAK,EAC3B,MACF,CACF,CACF,CAEA,MAAc,KAAKC,EAA4C,CAC7D,GAAI,CACF,IAAMC,EAAM,MAAM,MAAM,GAAG,KAAK,OAAO,eAAgB,CACrD,OAAQ,OACR,QAAS,CACP,cAAe,UAAU,KAAK,MAAM,GACpC,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CAAE,OAAAD,CAAO,CAAC,EAC/B,GAAI,KAAK,SAAW,UAAY,CAAE,UAAW,EAAK,EAAI,CAAC,CACzD,CAAC,EACD,OAAKC,EAAI,GASF,GAPDA,EAAI,QAAU,KAAOA,EAAI,OAAS,KACpC,KAAK,IAAI,iBAAiBA,EAAI,MAAM,qBAAkB,EAC/C,KAET,KAAK,IAAI,eAAeA,EAAI,MAAM,kBAAe,EAC1C,GAGX,OAASC,EAAK,CACZ,YAAK,IAAI,uBAAwBA,EAAc,OAAO,kBAAe,EAC9D,EACT,CACF,CAEQ,IAAIC,EAAmB,CACzB,KAAK,OAAO,QAAQ,KAAK,WAAWA,CAAG,EAAE,CAC/C,CAOU,oBAAiC,CACzC,MAAO,CAAC,CACV,CAGA,IAAI,SAAkB,CACpB,OAAO,KAAK,MAAM,MACpB,CACF,EC/JA,IAAMC,EAAiB,wBAehB,SAASC,EAAmBC,EAAsC,CACvE,GAAI,OAAO,OAAW,KAAe,OAAO,QAAY,IACtD,MAAO,IAAG,GAGZ,IAAMC,EAAI,OACV,GAAIA,EAAEC,CAAc,EAAG,MAAO,IAAG,GACjCD,EAAEC,CAAc,EAAI,GAEpB,IAAMC,EAA8B,CAAC,EACjCC,EAAWC,EAAY,EACvBC,EAAgD,KAEpD,SAASC,EAAKC,EAA4B,CACxC,GAAI,CACFR,EAAK,WAAWQ,CAAI,CACtB,MAAQ,CAER,CACF,CAEA,SAASC,GAA0B,CAC7BH,GAAS,aAAaA,CAAO,EACjCA,EAAU,WAAW,IAAM,CACzBA,EAAU,KACV,IAAMI,EAAOL,EAAY,EAGrBK,IAASN,IACbA,EAAWM,EACXH,EAAK,cAAc,EACrB,EAAG,EAAiB,CACtB,CAEAA,EAAK,cAAc,EAEnB,IAAMI,EAAe,QAAQ,UACvBC,EAAkB,QAAQ,aAEhC,QAAQ,UAAY,YAAwBC,EAAwC,CAClF,IAAMC,EAASH,EAAa,MAAM,KAAME,CAAI,EAC5C,OAAAJ,EAAkB,EACXK,CACT,EACA,QAAQ,aAAe,YAA2BD,EAA2C,CAC3F,IAAMC,EAASF,EAAgB,MAAM,KAAMC,CAAI,EAC/C,OAAAJ,EAAkB,EACXK,CACT,EACAX,EAAS,KAAK,IAAM,CAClB,QAAQ,UAAYQ,EACpB,QAAQ,aAAeC,CACzB,CAAC,EAGD,IAAMG,EAAQ,IAAYN,EAAkB,EAC5C,OAAO,iBAAiB,WAAYM,CAAK,EACzCZ,EAAS,KAAK,IAAM,OAAO,oBAAoB,WAAYY,CAAK,CAAC,EAEjE,IAAMC,EAAS,IAAYP,EAAkB,EAC7C,cAAO,iBAAiB,aAAcO,CAAM,EAC5Cb,EAAS,KAAK,IAAM,OAAO,oBAAoB,aAAca,CAAM,CAAC,EAE7D,IAAM,CACPV,GAAS,aAAaA,CAAO,EACjC,QAAWW,KAAWd,EAAUc,EAAQ,EACxChB,EAAEC,CAAc,EAAI,EACtB,CACF,CAGA,SAASG,GAAsB,CAC7B,OAAI,OAAO,SAAa,IAAoB,IACrC,GAAG,SAAS,QAAQ,GAAG,SAAS,MAAM,GAAG,SAAS,IAAI,EAC/D,CC9EA,IAAMa,EAAW,CACf,aACA,aACA,eACA,WACA,aACF,EAEMC,EAAsB,gBACtBC,EAA0B,oBAKzB,SAASC,EAASC,EAA6B,CACpD,GAAI,CACF,IAAMC,EAAM,IAAI,IAAID,CAAI,EACxB,MAAO,GAAGC,EAAI,MAAM,GAAGA,EAAI,QAAQ,EACrC,MAAQ,CACN,OAAO,IACT,CACF,CAGO,SAASC,EAASF,EAA0B,CACjD,IAAMG,EAAkB,CAAC,EACzB,GAAI,CACF,IAAMC,EAAS,IAAI,IAAIJ,CAAI,EAAE,aAC7B,QAAWK,KAAOT,EAAU,CAC1B,IAAMU,EAAQF,EAAO,IAAIC,CAAG,GAAG,KAAK,EAChCC,IAAOH,EAAIE,CAAG,EAAIC,EAAM,YAAY,EAAE,MAAM,EAAG,GAAG,EACxD,CACF,MAAQ,CAER,CACA,OAAOH,CACT,CAgBO,SAASI,EAAeC,EAAqB,CAClD,GAAI,CAACA,EAAI,MAAO,CAAC,EACjB,IAAMC,EAAID,EAAG,YAAY,EAEnBE,EAAUD,EAAE,SAAS,MAAM,EAC7B,OACAA,EAAE,SAAS,MAAM,GAAKA,EAAE,SAAS,OAAO,EACtC,QACAA,EAAE,SAAS,gBAAgB,EACzB,mBACAA,EAAE,SAAS,SAAS,GAAKA,EAAE,SAAS,OAAO,EACzC,UACAA,EAAE,SAAS,QAAQ,GAAKA,EAAE,SAAS,OAAO,EACxC,SACAA,EAAE,SAAS,QAAQ,EACjB,SACA,OAERE,EAAKF,EAAE,SAAS,QAAQ,GAAKA,EAAE,SAAS,MAAM,GAAKA,EAAE,SAAS,MAAM,EACtE,MACAA,EAAE,SAAS,SAAS,EAClB,UACAA,EAAE,SAAS,SAAS,EAClB,UACAA,EAAE,SAAS,QAAQ,GAAKA,EAAE,SAAS,WAAW,EAC5C,QACAA,EAAE,SAAS,MAAM,EACf,WACAA,EAAE,SAAS,OAAO,EAChB,QACA,OAIRG,EAASH,EAAE,SAAS,MAAM,GAAMA,EAAE,SAAS,SAAS,GAAK,CAACA,EAAE,SAAS,QAAQ,EAC/E,SACAA,EAAE,SAAS,MAAM,GAAKA,EAAE,SAAS,QAAQ,GAAKA,EAAE,SAAS,MAAM,EAC7D,SACA,UAEN,MAAO,CACL,GAAIC,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,EAC7B,GAAIC,EAAK,CAAE,GAAAA,CAAG,EAAI,CAAC,EACnB,OAAAC,CACF,CACF,CAIA,SAASC,EAAYC,EAA8BT,EAA4B,CAC7E,GAAI,CAEF,OADUS,IAAY,UAAY,eAAiB,cAC1C,QAAQT,CAAG,CACtB,MAAQ,CACN,OAAO,IACT,CACF,CAEA,SAASU,EAAaD,EAA8BT,EAAaC,EAAqB,CACpF,GAAI,EACQQ,IAAY,UAAY,eAAiB,cACjD,QAAQT,EAAKC,CAAK,CACtB,MAAQ,CAER,CACF,CAEA,SAASU,GAAmB,CAC1B,GAAI,CACF,OAAO,OAAO,WAAW,CAC3B,MAAQ,CACN,MAAO,OAAO,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,EAAG,EAAE,CAAC,EAClF,CACF,CAGO,SAASC,GAA2B,CACzC,IAAMC,EAAWL,EAAY,UAAWhB,CAAmB,EAC3D,GAAIqB,EAAU,OAAOA,EACrB,IAAMC,EAAQH,EAAS,EACvB,OAAAD,EAAa,UAAWlB,EAAqBsB,CAAK,EAC3CA,CACT,CAMO,SAASC,EAAmBpB,EAAcqB,EAAkBC,EAAiC,CAClG,IAAMC,EAASV,EAAY,UAAWf,CAAuB,EAC7D,GAAIyB,EACF,GAAI,CACF,OAAO,KAAK,MAAMA,CAAM,CAC1B,MAAQ,CAER,CAGF,IAAMC,EAA0B,CAAE,GAAGtB,EAASF,CAAI,CAAE,EAE9CyB,EAAeC,EAAOL,CAAQ,EAEpC,GAAII,GAAgBA,IAAiBH,EAAa,CAChD,IAAMK,EAAQ5B,EAASsB,CAAQ,EAC3BM,IAAOH,EAAY,SAAWG,GAClCH,EAAY,cAAgBC,CAC9B,CAEA,OAAAV,EAAa,UAAWjB,EAAyB,KAAK,UAAU0B,CAAW,CAAC,EACrEA,CACT,CAEA,SAASE,EAAO1B,EAA6B,CAC3C,GAAI,CACF,OAAO,IAAI,IAAIA,CAAI,EAAE,SAAS,YAAY,GAAK,IACjD,MAAQ,CACN,OAAO,IACT,CACF,CAQO,SAAS4B,GAA8B,CAC5C,GAAI,OAAO,OAAW,KAAe,OAAO,SAAa,IAAa,MAAO,CAAC,EAE9E,IAAM5B,EAAO,SAAS,KAChBqB,EAAW,OAAO,SAAa,IAAc,SAAS,SAAW,GACjEQ,EAAO,SAAS,SAAS,YAAY,EAErCrB,EAAK,OAAO,UAAc,IAAc,UAAU,UAAY,GAC9D,CAAE,QAAAE,EAAS,GAAAC,EAAI,OAAAC,CAAO,EAAIL,EAAeC,CAAE,EAE3CP,EAAMF,EAASC,CAAI,EAEzB,MAAO,CACL,GAAIC,EAAM,CAAE,IAAAA,CAAI,EAAI,CAAC,EACrB,KAAM,SAAS,UAAY,IAC3B,KAAA4B,EACA,GAAInB,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,EAC7B,GAAIC,EAAK,CAAE,GAAAA,CAAG,EAAI,CAAC,EACnB,GAAIC,EAAS,CAAE,OAAAA,CAAO,EAAI,CAAC,EAC3B,GAAI,OAAO,UAAc,KAAe,UAAU,SAC9C,CAAE,OAAQ,UAAU,QAAS,EAC7B,CAAC,EACL,WAAYK,EAAiB,EAC7B,GAAGG,EAAmBpB,EAAMqB,EAAUQ,CAAI,CAC5C,CACF,CChNA,IAAMC,EAAmB,gBAUZC,EAAN,cAAuBC,CAAU,CAC9B,oBAA2C,KAEnD,YAAYC,EAAuB,CACjC,MAAM,UAAWA,CAAI,EACrB,KAAK,YAAcC,EAAmB,EAElC,OAAO,OAAW,MACpB,OAAO,iBAAiB,WAAY,IAAM,KAAK,KAAK,MAAM,CAAC,EAC3D,SAAS,iBAAiB,mBAAoB,IAAM,CAC9C,SAAS,kBAAoB,UAAe,KAAK,MAAM,CAC7D,CAAC,GAGCD,EAAK,cAAgB,KACvB,KAAK,oBAAsBE,EAAmB,CAC5C,WAAaC,GAAyB,KAAK,KAAK,OAAW,CAAE,WAAYA,CAAK,CAAC,CACjF,CAAC,EAEL,CASA,KAAKC,EAAeC,EAA+B,CACjD,KAAK,MAAM,YAAa,CACtB,GAAIA,GAAc,CAAC,EACnB,GAAID,EAAO,CAAE,UAAWA,CAAK,EAAI,CAAC,EAClC,GAAI,OAAO,SAAa,KAAe,SAAS,MAAQ,CAAE,MAAO,SAAS,KAAM,EAAI,CAAC,CACvF,CAAC,CACH,CAEA,OAAc,CACZ,KAAK,OAAS,KACd,KAAK,WAAa,KAClB,GAAI,CACF,aAAa,WAAWP,CAAgB,CAC1C,MAAQ,CAER,CACA,KAAK,YAAcI,EAAmB,CACxC,CAGA,MAAe,UAA0B,CACvC,KAAK,sBAAsB,EAC3B,KAAK,oBAAsB,KAC3B,MAAM,MAAM,SAAS,CACvB,CAEmB,oBAAiC,CAClD,OAAOK,EAAgB,CACzB,CACF,EAEA,SAASL,GAA6B,CACpC,GAAI,CACF,IAAMM,EAAW,aAAa,QAAQV,CAAgB,EACtD,GAAIU,EAAU,OAAOA,EACrB,IAAMC,EAAQ,OAAO,WAAW,EAChC,oBAAa,QAAQX,EAAkBW,CAAK,EACrCA,CACT,MAAQ,CACN,OAAO,OAAO,WAAW,CAC3B,CACF,CJnFA,IAAIC,EAA4B,KAEzB,SAASC,EAAKC,EAAiC,CAEpD,OAAKF,IAAUA,EAAW,IAAIG,EAASD,CAAI,GACpCF,CACT,CAEO,SAASI,EAAMC,EAAcC,EAA+B,CACjEN,GAAU,MAAMK,EAAMC,CAAU,CAClC,CAEO,SAASC,EAASC,EAAgBC,EAA2B,CAClET,GAAU,SAASQ,EAAQC,CAAM,CACnC,CAEO,SAASC,EAAMC,EAAoBF,EAA2B,CACnET,GAAU,MAAMW,EAAYF,CAAM,CACpC,CAEO,SAASG,EAAKP,EAAeC,EAA+B,CACjEN,GAAU,KAAKK,EAAMC,CAAU,CACjC,CAEO,SAASO,GAAc,CAC5Bb,GAAU,MAAM,CAClB,CAEA,eAAsBc,GAAuB,CAC3C,MAAMd,GAAU,MAAM,CACxB,CAEA,eAAsBe,GAA0B,CAC9C,MAAMf,GAAU,SAAS,EACzBA,EAAW,IACb,CAGO,SAASgB,GAA+B,CAC7C,OAAOhB,CACT",
|
|
6
|
+
"names": ["browser_exports", "__export", "flush", "getInstance", "group", "identify", "init", "page", "reset", "shutdown", "track", "DEFAULT_BASE_URL", "HoldyCore", "source", "opts", "DEFAULT_BASE_URL", "intervalMs", "name", "properties", "overrides", "userId", "accountKey", "occurredAt", "context", "event", "traits", "batch", "events", "res", "err", "msg", "INSTALLED_FLAG", "installAutoCapture", "opts", "w", "INSTALLED_FLAG", "cleanups", "lastPath", "currentPath", "pending", "emit", "kind", "scheduleRouteEmit", "path", "originalPush", "originalReplace", "args", "result", "onPop", "onHash", "cleanup", "UTM_KEYS", "SESSION_STORAGE_KEY", "ATTRIBUTION_STORAGE_KEY", "stripUrl", "href", "url", "parseUtm", "out", "params", "key", "value", "parseUserAgent", "ua", "s", "browser", "os", "device", "readStorage", "storage", "writeStorage", "randomId", "resolveSessionId", "existing", "fresh", "resolveAttribution", "referrer", "currentHost", "stored", "attribution", "referrerHost", "hostOf", "clean", "buildWebContext", "host", "ANON_STORAGE_KEY", "HoldyWeb", "HoldyCore", "opts", "resolveAnonymousId", "installAutoCapture", "kind", "name", "properties", "buildWebContext", "existing", "fresh", "instance", "init", "opts", "HoldyWeb", "track", "name", "properties", "identify", "userId", "traits", "group", "accountKey", "page", "reset", "flush", "shutdown", "getInstance"]
|
|
7
|
+
}
|