@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/dist/server.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @holdyapp/sdk (server) · Node/Edge (source: sdk_server).
|
|
3
|
+
*
|
|
4
|
+
* Stateless por default: user_id/accountKey vão por chamada em overrides.
|
|
5
|
+
* identify()/group() de sessão existem mas em server multi-tenant prefira
|
|
6
|
+
* overrides — estado compartilhado entre requests vaza identidade.
|
|
7
|
+
*/
|
|
8
|
+
import { HoldyCore } from './core';
|
|
9
|
+
import type { HoldyOptions } from './types';
|
|
10
|
+
export declare class Holdy extends HoldyCore {
|
|
11
|
+
constructor(opts: HoldyOptions);
|
|
12
|
+
}
|
|
13
|
+
export { HoldyCore, DEFAULT_BASE_URL } from './core';
|
|
14
|
+
export type { HoldyOptions, Properties, TrackOverrides, CanonicalEvent } from './types';
|
|
15
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE3C,qBAAa,KAAM,SAAQ,SAAS;gBACtB,IAAI,EAAE,YAAY;CAG/B;AAED,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAA;AACpD,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA"}
|
package/dist/server.mjs
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// src/core.ts
|
|
2
|
+
var DEFAULT_BASE_URL = "https://holdy.chat/api/v1";
|
|
3
|
+
var DEFAULT_FLUSH_AT = 20;
|
|
4
|
+
var DEFAULT_FLUSH_INTERVAL_MS = 5e3;
|
|
5
|
+
var MAX_QUEUE = 1e3;
|
|
6
|
+
var BULK_LIMIT = 500;
|
|
7
|
+
var HoldyCore = class {
|
|
8
|
+
apiKey;
|
|
9
|
+
baseUrl;
|
|
10
|
+
flushAt;
|
|
11
|
+
debug;
|
|
12
|
+
source;
|
|
13
|
+
queue = [];
|
|
14
|
+
timer = null;
|
|
15
|
+
flushing = null;
|
|
16
|
+
userId = null;
|
|
17
|
+
anonymousId = null;
|
|
18
|
+
accountKey = null;
|
|
19
|
+
constructor(source, opts) {
|
|
20
|
+
this.source = source;
|
|
21
|
+
this.apiKey = opts.apiKey;
|
|
22
|
+
this.accountKey = opts.accountKey ?? null;
|
|
23
|
+
this.baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
24
|
+
this.flushAt = Math.max(1, opts.flushAt ?? DEFAULT_FLUSH_AT);
|
|
25
|
+
this.debug = opts.debug ?? false;
|
|
26
|
+
const intervalMs = opts.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
|
|
27
|
+
if (intervalMs > 0) {
|
|
28
|
+
this.timer = setInterval(() => void this.flush(), intervalMs);
|
|
29
|
+
if (typeof this.timer.unref === "function") {
|
|
30
|
+
;
|
|
31
|
+
this.timer.unref();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
track(name, properties, overrides) {
|
|
36
|
+
if (!name || typeof name !== "string") return;
|
|
37
|
+
const userId = overrides?.userId ?? this.userId ?? void 0;
|
|
38
|
+
const accountKey = overrides?.accountKey ?? this.accountKey ?? void 0;
|
|
39
|
+
const occurredAt = overrides?.occurredAt ? new Date(overrides.occurredAt).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
|
|
40
|
+
const context = {
|
|
41
|
+
...this.environmentContext(),
|
|
42
|
+
...overrides?.context ?? {}
|
|
43
|
+
};
|
|
44
|
+
if (accountKey) context.account_external_id = accountKey;
|
|
45
|
+
const event = {
|
|
46
|
+
event: name,
|
|
47
|
+
occurred_at: occurredAt,
|
|
48
|
+
source: this.source,
|
|
49
|
+
...userId ? { user_id: userId } : {},
|
|
50
|
+
...this.anonymousId ? { anonymous_id: this.anonymousId } : {},
|
|
51
|
+
properties: properties ?? {},
|
|
52
|
+
...Object.keys(context).length > 0 ? { context } : {}
|
|
53
|
+
};
|
|
54
|
+
this.queue.push(event);
|
|
55
|
+
if (this.queue.length > MAX_QUEUE) this.queue.splice(0, this.queue.length - MAX_QUEUE);
|
|
56
|
+
if (this.queue.length >= this.flushAt) void this.flush();
|
|
57
|
+
}
|
|
58
|
+
identify(userId, traits) {
|
|
59
|
+
if (!userId || typeof userId !== "string") return;
|
|
60
|
+
this.userId = userId;
|
|
61
|
+
this.track("$identify", traits ?? {});
|
|
62
|
+
}
|
|
63
|
+
group(accountKey, traits) {
|
|
64
|
+
if (!accountKey || typeof accountKey !== "string") return;
|
|
65
|
+
this.accountKey = accountKey;
|
|
66
|
+
this.track("$group", traits ?? {});
|
|
67
|
+
}
|
|
68
|
+
/** Drena a fila. Nunca lança — falha re-enfileira e loga em debug. */
|
|
69
|
+
async flush() {
|
|
70
|
+
if (this.flushing) return this.flushing;
|
|
71
|
+
if (this.queue.length === 0) return;
|
|
72
|
+
this.flushing = this.drain().finally(() => {
|
|
73
|
+
this.flushing = null;
|
|
74
|
+
});
|
|
75
|
+
return this.flushing;
|
|
76
|
+
}
|
|
77
|
+
/** Flush final + para o timer. Chamar em shutdown do server / unload do web. */
|
|
78
|
+
async shutdown() {
|
|
79
|
+
if (this.timer) {
|
|
80
|
+
clearInterval(this.timer);
|
|
81
|
+
this.timer = null;
|
|
82
|
+
}
|
|
83
|
+
await this.flush();
|
|
84
|
+
}
|
|
85
|
+
async drain() {
|
|
86
|
+
while (this.queue.length > 0) {
|
|
87
|
+
const batch = this.queue.splice(0, BULK_LIMIT);
|
|
88
|
+
const ok = await this.send(batch);
|
|
89
|
+
if (!ok) {
|
|
90
|
+
this.queue.unshift(...batch);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async send(events) {
|
|
96
|
+
try {
|
|
97
|
+
const res = await fetch(`${this.baseUrl}/events/bulk`, {
|
|
98
|
+
method: "POST",
|
|
99
|
+
headers: {
|
|
100
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
101
|
+
"Content-Type": "application/json"
|
|
102
|
+
},
|
|
103
|
+
body: JSON.stringify({ events }),
|
|
104
|
+
...this.source === "sdk_web" ? { keepalive: true } : {}
|
|
105
|
+
});
|
|
106
|
+
if (!res.ok) {
|
|
107
|
+
if (res.status >= 400 && res.status < 500) {
|
|
108
|
+
this.log(`bulk rejected ${res.status} \xB7 batch dropped`);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
this.log(`bulk failed ${res.status} \xB7 will retry`);
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
return true;
|
|
115
|
+
} catch (err) {
|
|
116
|
+
this.log(`bulk network error: ${err.message} \xB7 will retry`);
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
log(msg) {
|
|
121
|
+
if (this.debug) console.warn(`[holdy] ${msg}`);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Ambiente que o SDK observa sozinho (url, referrer, browser, utm…).
|
|
125
|
+
* Vazio no core; `HoldyWeb` preenche. O server não infere ambiente — não
|
|
126
|
+
* existe "página atual" num worker.
|
|
127
|
+
*/
|
|
128
|
+
environmentContext() {
|
|
129
|
+
return {};
|
|
130
|
+
}
|
|
131
|
+
/** Interno · exposto pra testes. */
|
|
132
|
+
get pending() {
|
|
133
|
+
return this.queue.length;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// src/server.ts
|
|
138
|
+
var Holdy = class extends HoldyCore {
|
|
139
|
+
constructor(opts) {
|
|
140
|
+
super("sdk_server", opts);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
export {
|
|
144
|
+
DEFAULT_BASE_URL,
|
|
145
|
+
Holdy,
|
|
146
|
+
HoldyCore
|
|
147
|
+
};
|
|
148
|
+
//# sourceMappingURL=server.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/core.ts", "../src/server.ts"],
|
|
4
|
+
"sourcesContent": ["/**\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 * @holdyapp/sdk (server) \u00B7 Node/Edge (source: sdk_server).\n *\n * Stateless por default: user_id/accountKey v\u00E3o por chamada em overrides.\n * identify()/group() de sess\u00E3o existem mas em server multi-tenant prefira\n * overrides \u2014 estado compartilhado entre requests vaza identidade.\n */\n\nimport { HoldyCore } from './core'\nimport type { HoldyOptions } from './types'\n\nexport class Holdy extends HoldyCore {\n constructor(opts: HoldyOptions) {\n super('sdk_server', opts)\n }\n}\n\nexport { HoldyCore, DEFAULT_BASE_URL } from './core'\nexport type { HoldyOptions, Properties, TrackOverrides, CanonicalEvent } from './types'\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,114 +1,46 @@
|
|
|
1
|
-
export type CanonicalPrimitive = 'workflow.started' | 'workflow.progressed' | 'workflow.blocked' | 'workflow.completed' | 'workflow.abandoned' | 'outcome.progressed' | 'outcome.completed' | 'outcome.regressed' | 'friction.detected' | 'collaboration.invited' | 'collaboration.joined' | 'collaboration.deepened' | 'collaboration.weakened' | 'collaboration.champion_lost' | 'support.escalated' | 'support.sentiment_negative' | 'support.theme_detected' | 'expansion.intent_detected' | 'expansion.ceiling_hit' | 'memory.append' | 'session.activity';
|
|
2
|
-
export type DecisionType = 'intervention' | 'briefing' | 'cs_task' | 'track_journey' | 'no_action';
|
|
3
|
-
export type InterventionKind = 'tooltip' | 'banner' | 'modal' | 'checklist' | 'spotlight' | 'cta';
|
|
4
|
-
export interface ChecklistItem {
|
|
5
|
-
/** ID estável · persistência local + dedup. */
|
|
6
|
-
id: string;
|
|
7
|
-
label: string;
|
|
8
|
-
/** Opcional · link aberto ao clicar. Sem href, item só marca completed. */
|
|
9
|
-
href?: string;
|
|
10
|
-
/** Workflow ou outcome primitive que marca esse item como completed quando emitido. */
|
|
11
|
-
completed_event?: string;
|
|
12
|
-
/** Pré-marcado · server pode indicar que cliente já fez essa etapa. */
|
|
13
|
-
completed?: boolean;
|
|
14
|
-
}
|
|
15
|
-
export interface InterventionPayload {
|
|
16
|
-
kind: InterventionKind;
|
|
17
|
-
text: string;
|
|
18
|
-
/** Título opcional · usado em modal/checklist headers. */
|
|
19
|
-
title?: string;
|
|
20
|
-
cta_text?: string;
|
|
21
|
-
cta_href?: string;
|
|
22
|
-
anchor_selector?: string;
|
|
23
|
-
/** Items pro kind=checklist. */
|
|
24
|
-
items?: ChecklistItem[];
|
|
25
|
-
}
|
|
26
|
-
export interface DecisionResult {
|
|
27
|
-
decision_id: string;
|
|
28
|
-
decision_type: DecisionType;
|
|
29
|
-
decision_payload: InterventionPayload | Record<string, unknown>;
|
|
30
|
-
rule_key: string;
|
|
31
|
-
confidence: number;
|
|
32
|
-
reason: string;
|
|
33
|
-
decided_at: string;
|
|
34
|
-
}
|
|
35
|
-
export type OutcomeValue = 'executed' | 'dismissed' | 'converted' | 'no_show' | 'failed';
|
|
36
|
-
export interface RuntimeConfig {
|
|
37
|
-
api_version: string;
|
|
38
|
-
taxonomy_version: string;
|
|
39
|
-
organization_id: string;
|
|
40
|
-
thresholds: Record<string, number | string>;
|
|
41
|
-
features: {
|
|
42
|
-
auto_attribution: boolean;
|
|
43
|
-
interventions_enabled: boolean;
|
|
44
|
-
consent_required: boolean;
|
|
45
|
-
};
|
|
46
|
-
theme?: ThemeConfig;
|
|
47
|
-
}
|
|
48
|
-
export interface ThemeConfig {
|
|
49
|
-
primary_color: string;
|
|
50
|
-
font_family: string;
|
|
51
|
-
border_radius_px: number;
|
|
52
|
-
position_offset_px: number;
|
|
53
|
-
}
|
|
54
|
-
export declare const DEFAULT_THEME: ThemeConfig;
|
|
55
|
-
export interface HoldyConfig {
|
|
56
|
-
apiKey: string;
|
|
57
|
-
apiUrl?: string;
|
|
58
|
-
accountId?: string;
|
|
59
|
-
userId?: string;
|
|
60
|
-
sessionId?: string;
|
|
61
|
-
consumer?: 'ui' | 'csm' | 'agent';
|
|
62
|
-
debug?: boolean;
|
|
63
|
-
/** Disables render runtime · SDK só captura telemetry. */
|
|
64
|
-
renderInterventions?: boolean;
|
|
65
|
-
/**
|
|
66
|
-
* Stage 28 · auto-capture de page views (initial_load + route_change +
|
|
67
|
-
* visibility_change). Quando true, SDK monkeypatcha history.pushState/
|
|
68
|
-
* replaceState + escuta popstate/visibilitychange e emite session.activity
|
|
69
|
-
* sem código manual. Default false pra back-compat.
|
|
70
|
-
*/
|
|
71
|
-
autoCapture?: boolean;
|
|
72
|
-
}
|
|
73
1
|
/**
|
|
74
|
-
*
|
|
75
|
-
* Backend usa COALESCE — não sobrescreve campos preenchidos no wizard manual.
|
|
2
|
+
* Tipos públicos do @holdyapp/sdk (entry-contract.md §2 + §6).
|
|
76
3
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
4
|
+
* Envelope idêntico ao CanonicalEventInputSchema do servidor — o SDK nunca
|
|
5
|
+
* inventa campo. account_id (uuid Holdy) não existe aqui: a chave do cliente
|
|
6
|
+
* viaja em context.account_external_id e a Data layer resolve (regra 4).
|
|
79
7
|
*/
|
|
80
|
-
export
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
8
|
+
export type Properties = Record<string, unknown>;
|
|
9
|
+
export interface CanonicalEvent {
|
|
10
|
+
event: string;
|
|
11
|
+
occurred_at: string;
|
|
12
|
+
source: 'sdk_web' | 'sdk_server';
|
|
13
|
+
user_id?: string;
|
|
14
|
+
anonymous_id?: string;
|
|
15
|
+
properties: Properties;
|
|
16
|
+
context?: Properties;
|
|
17
|
+
}
|
|
18
|
+
export interface HoldyOptions {
|
|
19
|
+
/** API key `hol_{env}_...` com scope events:write. */
|
|
20
|
+
apiKey: string;
|
|
21
|
+
/** Override · default https://holdy.chat/api/v1 */
|
|
22
|
+
baseUrl?: string;
|
|
23
|
+
/** Flush automático quando a fila atinge este tamanho · default 20. */
|
|
24
|
+
flushAt?: number;
|
|
25
|
+
/** Flush automático a cada N ms · default 5000. 0 desliga o timer. */
|
|
26
|
+
flushIntervalMs?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Chave da account do cliente (`context.account_external_id`). Define o
|
|
29
|
+
* vínculo sem emitir evento — `group()` continua sendo a chamada pra
|
|
30
|
+
* declarar a account COM traits.
|
|
31
|
+
*/
|
|
32
|
+
accountKey?: string;
|
|
33
|
+
/** Logs de debug no console. */
|
|
34
|
+
debug?: boolean;
|
|
98
35
|
}
|
|
99
|
-
export interface
|
|
100
|
-
/**
|
|
101
|
-
accountId?: string;
|
|
36
|
+
export interface TrackOverrides {
|
|
37
|
+
/** Server-side: end-user do evento (stateless por chamada). */
|
|
102
38
|
userId?: string;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
/** Workflow corrente · override do estado interno. */
|
|
112
|
-
workflow?: string;
|
|
113
|
-
}
|
|
114
|
-
export type DecisionListener = (decision: DecisionResult) => void;
|
|
39
|
+
/** Server-side: chave de account do cliente (context.account_external_id). */
|
|
40
|
+
accountKey?: string;
|
|
41
|
+
/** Timestamp do evento · default agora. */
|
|
42
|
+
occurredAt?: Date | string;
|
|
43
|
+
/** Metadata adicional pro context. */
|
|
44
|
+
context?: Properties;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEhD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,SAAS,GAAG,YAAY,CAAA;IAChC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,UAAU,CAAA;IACtB,OAAO,CAAC,EAAE,UAAU,CAAA;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAA;IACd,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,sEAAsE;IACtE,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gCAAgC;IAChC,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2CAA2C;IAC3C,UAAU,CAAC,EAAE,IAAI,GAAG,MAAM,CAAA;IAC1B,sCAAsC;IACtC,OAAO,CAAC,EAAE,UAAU,CAAA;CACrB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contexto de ambiente do browser · @holdyapp/sdk/web.
|
|
3
|
+
*
|
|
4
|
+
* Vai em `context` de todo evento, nunca em `properties`: properties é o
|
|
5
|
+
* vocabulário do produto do cliente (`workflow_key`, `plan`), context é o que o
|
|
6
|
+
* SDK observou sozinho. A Data layer persiste os dois (facts.context, 00247) e
|
|
7
|
+
* a leitura de dimensão consulta os dois.
|
|
8
|
+
*
|
|
9
|
+
* Três decisões que definem se esse dado é útil ou lixo:
|
|
10
|
+
* · `url` sai sem query e sem hash. Href cru tem uma variação por sessão, e
|
|
11
|
+
* dimensão com cardinalidade quase-única é descartada na leitura — a
|
|
12
|
+
* página vira ruído exatamente por excesso de precisão.
|
|
13
|
+
* · utm é capturado na primeira página e carregado pelo resto da sessão.
|
|
14
|
+
* Atribuição que só existe no primeiro evento não responde nada.
|
|
15
|
+
* · referrer só quando é externo. Navegação interna preenchendo "Origem do
|
|
16
|
+
* tráfego" com o próprio domínio esconde a origem real.
|
|
17
|
+
*
|
|
18
|
+
* Tudo aqui é best-effort: browser em modo privativo, SSR e webview sem
|
|
19
|
+
* storage precisam funcionar. Nenhuma função lança.
|
|
20
|
+
*/
|
|
21
|
+
export type WebContext = Record<string, string>;
|
|
22
|
+
/** `https://app.x.com/a/b?q=1#z` → `https://app.x.com/a/b`. */
|
|
23
|
+
export declare function stripUrl(href: string): string | null;
|
|
24
|
+
/** Parâmetros de campanha presentes na URL, já em lowercase. */
|
|
25
|
+
export declare function parseUtm(href: string): WebContext;
|
|
26
|
+
interface UaParts {
|
|
27
|
+
browser?: string;
|
|
28
|
+
os?: string;
|
|
29
|
+
device?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Fatia mínima do UA: navegador, sistema e classe de dispositivo.
|
|
33
|
+
*
|
|
34
|
+
* Não é biblioteca de UA parsing e não deve virar uma — o valor está em
|
|
35
|
+
* separar mobile de desktop e Safari de Chrome, não em cravar a versão.
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseUserAgent(ua: string): UaParts;
|
|
38
|
+
/** ID da sessão de browser · vive enquanto a aba viver. */
|
|
39
|
+
export declare function resolveSessionId(): string;
|
|
40
|
+
/**
|
|
41
|
+
* Atribuição da sessão: utm da primeira página + referrer externo de entrada.
|
|
42
|
+
* Gravada uma vez e repetida em todo evento da sessão.
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveAttribution(href: string, referrer: string, currentHost: string): WebContext;
|
|
45
|
+
/**
|
|
46
|
+
* Contexto completo do evento atual. Fora do browser devolve `{}` — o SDK
|
|
47
|
+
* server tem o próprio caminho e não deve inventar ambiente.
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildWebContext(): WebContext;
|
|
50
|
+
export {};
|
|
51
|
+
//# sourceMappingURL=web-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-context.d.ts","sourceRoot":"","sources":["../src/web-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAe/C,+DAA+D;AAC/D,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOpD;AAED,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAYjD;AAID,UAAU,OAAO;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CA6ClD;AA8BD,2DAA2D;AAC3D,wBAAgB,gBAAgB,IAAI,MAAM,CAMzC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,UAAU,CAsBlG;AAYD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,UAAU,CAyB5C"}
|
package/dist/web.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @holdyapp/sdk/web · browser (source: sdk_web).
|
|
3
|
+
*
|
|
4
|
+
* anonymous_id gerado e persistido em localStorage (holdy_anon_id) — enviado em
|
|
5
|
+
* todo event até identify() promover pra user_id (identity graph faz o merge
|
|
6
|
+
* downstream, nunca o SDK). Flush no pagehide via fetch keepalive.
|
|
7
|
+
*
|
|
8
|
+
* Todo evento carrega o contexto de ambiente (url, path, referrer externo,
|
|
9
|
+
* utm da sessão, browser, os, device, locale, session_id) em `context` —
|
|
10
|
+
* separado das properties do produto. Pageview é automático por default:
|
|
11
|
+
* carregamento inicial e cada mudança de rota, inclusive em SPA.
|
|
12
|
+
*/
|
|
13
|
+
import { HoldyCore } from './core';
|
|
14
|
+
import type { HoldyOptions, Properties } from './types';
|
|
15
|
+
export interface HoldyWebOptions extends HoldyOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Emitir `$pageview` sozinho no load e a cada mudança de rota · default true.
|
|
18
|
+
* Desligue só se o produto já chama `page()` na mão em cada tela.
|
|
19
|
+
*/
|
|
20
|
+
autoCapture?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare class HoldyWeb extends HoldyCore {
|
|
23
|
+
private teardownAutoCapture;
|
|
24
|
+
constructor(opts: HoldyWebOptions);
|
|
25
|
+
/**
|
|
26
|
+
* Pageview. O `$` marca evento emitido pelo SDK, não pelo produto (mesma
|
|
27
|
+
* convenção de `$identify`/`$group`); a canonicalização downstream remove o
|
|
28
|
+
* sigilo, então `$pageview` e `pageview` são o mesmo evento na leitura.
|
|
29
|
+
* url/path/referrer/utm já vêm no context de todo evento — aqui só entra o
|
|
30
|
+
* que é da página como conteúdo (nome e título).
|
|
31
|
+
*/
|
|
32
|
+
page(name?: string, properties?: Properties): void;
|
|
33
|
+
reset(): void;
|
|
34
|
+
/** Flush final + desinstala o auto-capture (devolve `history` ao original). */
|
|
35
|
+
shutdown(): Promise<void>;
|
|
36
|
+
protected environmentContext(): Properties;
|
|
37
|
+
}
|
|
38
|
+
/** Init idempotente — segunda chamada retorna a instância existente. */
|
|
39
|
+
export declare function initHoldy(opts: HoldyWebOptions): HoldyWeb;
|
|
40
|
+
export type { HoldyOptions, Properties };
|
|
41
|
+
//# sourceMappingURL=web.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../src/web.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAGlC,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAIvD,MAAM,WAAW,eAAgB,SAAQ,YAAY;IACnD;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,qBAAa,QAAS,SAAQ,SAAS;IACrC,OAAO,CAAC,mBAAmB,CAA4B;gBAE3C,IAAI,EAAE,eAAe;IAkBjC;;;;;;OAMG;IACH,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI;IAQlD,KAAK,IAAI,IAAI;IAWb,+EAA+E;IAChE,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;cAMrB,kBAAkB,IAAI,UAAU;CAGpD;AAgBD,wEAAwE;AACxE,wBAAgB,SAAS,CAAC,IAAI,EAAE,eAAe,GAAG,QAAQ,CAGzD;AAED,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,CAAA"}
|