@magicfeedback/popup-sdk 1.1.0 → 1.1.2
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 +0 -19
- package/dist/chunk-6WTOQGET.mjs +141 -0
- package/dist/chunk-6WTOQGET.mjs.map +1 -0
- package/dist/chunk-IOAZQPZE.mjs +120 -0
- package/dist/chunk-IOAZQPZE.mjs.map +1 -0
- package/dist/index.d.mts +417 -40
- package/dist/index.d.ts +417 -40
- package/dist/index.js +987 -106
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +841 -3411
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +486 -0
- package/dist/react-native.d.ts +486 -0
- package/dist/react-native.js +3286 -0
- package/dist/react-native.js.map +1 -0
- package/dist/react-native.mjs +1758 -0
- package/dist/react-native.mjs.map +1 -0
- package/dist/renderPopup-KBDWWSUU.mjs +1376 -0
- package/dist/renderPopup-KBDWWSUU.mjs.map +1 -0
- package/dist/renderPopup-YLFRGPQN.mjs +3303 -0
- package/dist/renderPopup-YLFRGPQN.mjs.map +1 -0
- package/package.json +36 -2
package/README.md
CHANGED
|
@@ -200,25 +200,6 @@ Example:
|
|
|
200
200
|
popups.triggerEvent('search');
|
|
201
201
|
```
|
|
202
202
|
|
|
203
|
-
### `show({ surveyId, productId })`
|
|
204
|
-
|
|
205
|
-
Shows a popup immediately without waiting for a trigger.
|
|
206
|
-
|
|
207
|
-
```ts
|
|
208
|
-
popups.show({
|
|
209
|
-
surveyId: 'survey-home-001',
|
|
210
|
-
productId: 'product-main',
|
|
211
|
-
});
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### `showByPopupId(popupId)`
|
|
215
|
-
|
|
216
|
-
Shows a popup definition by id. This is useful when multiple popups reuse the same `surveyId`.
|
|
217
|
-
|
|
218
|
-
```ts
|
|
219
|
-
popups.showByPopupId('popup-home-5s');
|
|
220
|
-
```
|
|
221
|
-
|
|
222
203
|
### `markSurveyAnswered(surveyId)`
|
|
223
204
|
|
|
224
205
|
Marks a survey as answered so cooldown rules like `{ answered: 'COMPLETED', cooldownDays: 30 }` start applying.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
8
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
19
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
20
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
21
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
22
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
23
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
24
|
+
mod
|
|
25
|
+
));
|
|
26
|
+
|
|
27
|
+
// src/tracking/tracking-manager.ts
|
|
28
|
+
var InMemoryStorage = class {
|
|
29
|
+
constructor() {
|
|
30
|
+
this.map = /* @__PURE__ */ new Map();
|
|
31
|
+
}
|
|
32
|
+
getItem(key) {
|
|
33
|
+
return this.map.has(key) ? this.map.get(key) : null;
|
|
34
|
+
}
|
|
35
|
+
setItem(key, value) {
|
|
36
|
+
this.map.set(key, value);
|
|
37
|
+
}
|
|
38
|
+
removeItem(key) {
|
|
39
|
+
this.map.delete(key);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
function createDefaultStorage() {
|
|
43
|
+
try {
|
|
44
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
45
|
+
const ls = window.localStorage;
|
|
46
|
+
const probe = "__deepdots_probe__";
|
|
47
|
+
ls.setItem(probe, "1");
|
|
48
|
+
ls.removeItem(probe);
|
|
49
|
+
return {
|
|
50
|
+
getItem: (k) => ls.getItem(k),
|
|
51
|
+
setItem: (k, v) => ls.setItem(k, v),
|
|
52
|
+
removeItem: (k) => ls.removeItem(k)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
return new InMemoryStorage();
|
|
58
|
+
}
|
|
59
|
+
var STORAGE_KEYS = {
|
|
60
|
+
userId: "deepdots.user_id",
|
|
61
|
+
firstSeen: "deepdots.user.first_seen",
|
|
62
|
+
contactAttributes: "deepdots.contact.attributes"
|
|
63
|
+
};
|
|
64
|
+
function buildSurveyIdentity(userId, sessionId, miniService = null, analyticsFeedbackSessionId = null) {
|
|
65
|
+
const profile = userId ? [{ key: "external-user-id", value: [userId] }] : [];
|
|
66
|
+
const metadata = [];
|
|
67
|
+
if (sessionId) metadata.push({ key: "session_id", value: [sessionId] });
|
|
68
|
+
if (userId) metadata.push({ key: "user_id", value: [userId] });
|
|
69
|
+
if (miniService) metadata.push({ key: "mini_service", value: [miniService] });
|
|
70
|
+
if (analyticsFeedbackSessionId) metadata.push({ key: "deepdots_analytics_feedback_session_id", value: [analyticsFeedbackSessionId] });
|
|
71
|
+
return { profile, metadata };
|
|
72
|
+
}
|
|
73
|
+
function defaultUuid() {
|
|
74
|
+
const g = typeof globalThis !== "undefined" ? globalThis : {};
|
|
75
|
+
if (g.crypto?.randomUUID) return g.crypto.randomUUID();
|
|
76
|
+
const bytes = new Uint8Array(16);
|
|
77
|
+
if (g.crypto?.getRandomValues) {
|
|
78
|
+
g.crypto.getRandomValues(bytes);
|
|
79
|
+
} else {
|
|
80
|
+
for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
81
|
+
}
|
|
82
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
83
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
84
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
|
|
85
|
+
return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`;
|
|
86
|
+
}
|
|
87
|
+
var TrackingManager = class {
|
|
88
|
+
constructor(options) {
|
|
89
|
+
/** session_id cacheado, provisto por el backend (no se genera ni se persiste). */
|
|
90
|
+
this.sessionId = null;
|
|
91
|
+
this.userWasJustCreated = false;
|
|
92
|
+
this.storage = options.storage;
|
|
93
|
+
this.clientUserId = options.clientUserId;
|
|
94
|
+
this.now = options.now ?? (() => Date.now());
|
|
95
|
+
this.uuid = options.uuid ?? defaultUuid;
|
|
96
|
+
this.enabled = options.enabled ?? true;
|
|
97
|
+
}
|
|
98
|
+
isTrackingEnabled() {
|
|
99
|
+
return this.enabled;
|
|
100
|
+
}
|
|
101
|
+
setTrackingEnabled(enabled) {
|
|
102
|
+
this.enabled = enabled;
|
|
103
|
+
}
|
|
104
|
+
/** Resuelve el user_id según la regla: cliente > persistido > generar+persistir. */
|
|
105
|
+
getUserId() {
|
|
106
|
+
if (!this.enabled) return null;
|
|
107
|
+
if (this.clientUserId) return this.clientUserId;
|
|
108
|
+
const persisted = this.storage.getItem(STORAGE_KEYS.userId);
|
|
109
|
+
if (persisted) return persisted;
|
|
110
|
+
const id = this.uuid();
|
|
111
|
+
this.storage.setItem(STORAGE_KEYS.userId, id);
|
|
112
|
+
this.storage.setItem(STORAGE_KEYS.firstSeen, String(this.now()));
|
|
113
|
+
this.userWasJustCreated = true;
|
|
114
|
+
return id;
|
|
115
|
+
}
|
|
116
|
+
/** `true` solo en la primera sesión tras crear el user_id propio del SDK. */
|
|
117
|
+
isNewUser() {
|
|
118
|
+
return this.userWasJustCreated;
|
|
119
|
+
}
|
|
120
|
+
/** session_id actual provisto por el backend, o null si aún no llegó. */
|
|
121
|
+
getSessionId() {
|
|
122
|
+
if (!this.enabled) return null;
|
|
123
|
+
return this.sessionId;
|
|
124
|
+
}
|
|
125
|
+
/** Cachea el session_id devuelto por el backend (respuesta de POST /sdk/popups). */
|
|
126
|
+
setSessionId(sessionId) {
|
|
127
|
+
if (!this.enabled) return;
|
|
128
|
+
this.sessionId = sessionId;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export {
|
|
133
|
+
__commonJS,
|
|
134
|
+
__toESM,
|
|
135
|
+
InMemoryStorage,
|
|
136
|
+
createDefaultStorage,
|
|
137
|
+
STORAGE_KEYS,
|
|
138
|
+
buildSurveyIdentity,
|
|
139
|
+
TrackingManager
|
|
140
|
+
};
|
|
141
|
+
//# sourceMappingURL=chunk-6WTOQGET.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tracking/tracking-manager.ts"],"sourcesContent":["/**\n * Identidad + sesión para el tracking de navegación (Fase 1).\n * Implementa el \"Contrato común Web+KMP\".\n *\n * Modelo: el SDK gestiona el `user_id` (persistente). El `session_id` es propiedad\n * del BACKEND: llega en la respuesta de `POST /sdk/popups` y se cachea; el backend\n * cose sesiones por `user_id` + ventana. El SDK NO genera ni expira sesiones.\n *\n * Sin dependencias de DOM: recibe el storage y (opcionalmente) el reloj y el\n * generador de uuid por inyección. El binding a `localStorage` vive en `createDefaultStorage`.\n */\n\n/** Store clave/valor mínimo (espejo conceptual del `KeyValueStorage` de KMP). */\nexport interface KeyValueStorage {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\n/** Impl en memoria — usada en tests y como fallback sin `localStorage`. */\nexport class InMemoryStorage implements KeyValueStorage {\n private map = new Map<string, string>();\n getItem(key: string): string | null {\n return this.map.has(key) ? (this.map.get(key) as string) : null;\n }\n setItem(key: string, value: string): void {\n this.map.set(key, value);\n }\n removeItem(key: string): void {\n this.map.delete(key);\n }\n}\n\n/** Storage por defecto en Web: `localStorage` si está disponible, si no in-memory. */\nexport function createDefaultStorage(): KeyValueStorage {\n try {\n if (typeof window !== 'undefined' && window.localStorage) {\n const ls = window.localStorage;\n const probe = '__deepdots_probe__';\n ls.setItem(probe, '1');\n ls.removeItem(probe);\n return {\n getItem: (k) => ls.getItem(k),\n setItem: (k, v) => ls.setItem(k, v),\n removeItem: (k) => ls.removeItem(k),\n };\n }\n } catch {\n // cae a in-memory\n }\n return new InMemoryStorage();\n}\n\n/** Claves de storage — namespace `deepdots.*`. Solo identidad (la sesión no se persiste). */\nexport const STORAGE_KEYS = {\n userId: 'deepdots.user_id',\n firstSeen: 'deepdots.user.first_seen',\n contactAttributes: 'deepdots.contact.attributes',\n} as const;\n\n/** Par clave/valor con el formato `NativeAnswer` que espera `@magicfeedback/native`. */\nexport interface IdentityAnswer {\n key: string;\n value: string[];\n}\n\n/**\n * Construye el `profile` y el `metadata` de identidad para inyectar en el survey\n * (`magicfeedback.form(appId, publicKey, profile, metadata)`), según el contrato §5.\n * `session_id` solo se incluye si el backend ya lo proveyó.\n */\nexport function buildSurveyIdentity(\n userId: string | null,\n sessionId: string | null,\n miniService: string | null = null,\n analyticsFeedbackSessionId: string | null = null,\n): { profile: IdentityAnswer[]; metadata: IdentityAnswer[] } {\n const profile: IdentityAnswer[] = userId ? [{ key: 'external-user-id', value: [userId] }] : [];\n const metadata: IdentityAnswer[] = [];\n if (sessionId) metadata.push({ key: 'session_id', value: [sessionId] });\n if (userId) metadata.push({ key: 'user_id', value: [userId] });\n if (miniService) metadata.push({ key: 'mini_service', value: [miniService] });\n if (analyticsFeedbackSessionId) metadata.push({ key: 'deepdots_analytics_feedback_session_id', value: [analyticsFeedbackSessionId] });\n return { profile, metadata };\n}\n\nexport interface TrackingManagerOptions {\n storage: KeyValueStorage;\n /** Id provisto por el cliente; si existe se usa y NO se persiste. */\n clientUserId?: string;\n /** Reloj inyectable (default `Date.now`). */\n now?: () => number;\n /** Generador de uuid inyectable (default uuid v4). */\n uuid?: () => string;\n /** Estado inicial del tracking (default `true`). */\n enabled?: boolean;\n}\n\nfunction defaultUuid(): string {\n const g = (typeof globalThis !== 'undefined' ? globalThis : {}) as {\n crypto?: { randomUUID?: () => string; getRandomValues?: (a: Uint8Array) => Uint8Array };\n };\n if (g.crypto?.randomUUID) return g.crypto.randomUUID();\n const bytes = new Uint8Array(16);\n if (g.crypto?.getRandomValues) {\n g.crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0'));\n return `${hex.slice(0, 4).join('')}-${hex.slice(4, 6).join('')}-${hex\n .slice(6, 8)\n .join('')}-${hex.slice(8, 10).join('')}-${hex.slice(10, 16).join('')}`;\n}\n\nexport class TrackingManager {\n private storage: KeyValueStorage;\n private clientUserId?: string;\n private now: () => number;\n private uuid: () => string;\n private enabled: boolean;\n\n /** session_id cacheado, provisto por el backend (no se genera ni se persiste). */\n private sessionId: string | null = null;\n private userWasJustCreated = false;\n\n constructor(options: TrackingManagerOptions) {\n this.storage = options.storage;\n this.clientUserId = options.clientUserId;\n this.now = options.now ?? (() => Date.now());\n this.uuid = options.uuid ?? defaultUuid;\n this.enabled = options.enabled ?? true;\n }\n\n isTrackingEnabled(): boolean {\n return this.enabled;\n }\n\n setTrackingEnabled(enabled: boolean): void {\n this.enabled = enabled;\n }\n\n /** Resuelve el user_id según la regla: cliente > persistido > generar+persistir. */\n getUserId(): string | null {\n if (!this.enabled) return null;\n if (this.clientUserId) return this.clientUserId;\n\n const persisted = this.storage.getItem(STORAGE_KEYS.userId);\n if (persisted) return persisted;\n\n const id = this.uuid();\n this.storage.setItem(STORAGE_KEYS.userId, id);\n this.storage.setItem(STORAGE_KEYS.firstSeen, String(this.now()));\n this.userWasJustCreated = true;\n return id;\n }\n\n /** `true` solo en la primera sesión tras crear el user_id propio del SDK. */\n isNewUser(): boolean {\n return this.userWasJustCreated;\n }\n\n /** session_id actual provisto por el backend, o null si aún no llegó. */\n getSessionId(): string | null {\n if (!this.enabled) return null;\n return this.sessionId;\n }\n\n /** Cachea el session_id devuelto por el backend (respuesta de POST /sdk/popups). */\n setSessionId(sessionId: string | null): void {\n if (!this.enabled) return;\n this.sessionId = sessionId;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/tracking/tracking-manager.ts
|
|
9
|
+
var InMemoryStorage = class {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.map = /* @__PURE__ */ new Map();
|
|
12
|
+
}
|
|
13
|
+
getItem(key) {
|
|
14
|
+
return this.map.has(key) ? this.map.get(key) : null;
|
|
15
|
+
}
|
|
16
|
+
setItem(key, value) {
|
|
17
|
+
this.map.set(key, value);
|
|
18
|
+
}
|
|
19
|
+
removeItem(key) {
|
|
20
|
+
this.map.delete(key);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
function createDefaultStorage() {
|
|
24
|
+
try {
|
|
25
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
26
|
+
const ls = window.localStorage;
|
|
27
|
+
const probe = "__deepdots_probe__";
|
|
28
|
+
ls.setItem(probe, "1");
|
|
29
|
+
ls.removeItem(probe);
|
|
30
|
+
return {
|
|
31
|
+
getItem: (k) => ls.getItem(k),
|
|
32
|
+
setItem: (k, v) => ls.setItem(k, v),
|
|
33
|
+
removeItem: (k) => ls.removeItem(k)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
return new InMemoryStorage();
|
|
39
|
+
}
|
|
40
|
+
var STORAGE_KEYS = {
|
|
41
|
+
userId: "deepdots.user_id",
|
|
42
|
+
firstSeen: "deepdots.user.first_seen",
|
|
43
|
+
contactAttributes: "deepdots.contact.attributes"
|
|
44
|
+
};
|
|
45
|
+
function buildSurveyIdentity(userId, sessionId, miniService = null, analyticsFeedbackSessionId = null) {
|
|
46
|
+
const profile = userId ? [{ key: "external-user-id", value: [userId] }] : [];
|
|
47
|
+
const metadata = [];
|
|
48
|
+
if (sessionId) metadata.push({ key: "session_id", value: [sessionId] });
|
|
49
|
+
if (userId) metadata.push({ key: "user_id", value: [userId] });
|
|
50
|
+
if (miniService) metadata.push({ key: "mini_service", value: [miniService] });
|
|
51
|
+
if (analyticsFeedbackSessionId) metadata.push({ key: "deepdots_analytics_feedback_session_id", value: [analyticsFeedbackSessionId] });
|
|
52
|
+
return { profile, metadata };
|
|
53
|
+
}
|
|
54
|
+
function defaultUuid() {
|
|
55
|
+
const g = typeof globalThis !== "undefined" ? globalThis : {};
|
|
56
|
+
if (g.crypto?.randomUUID) return g.crypto.randomUUID();
|
|
57
|
+
const bytes = new Uint8Array(16);
|
|
58
|
+
if (g.crypto?.getRandomValues) {
|
|
59
|
+
g.crypto.getRandomValues(bytes);
|
|
60
|
+
} else {
|
|
61
|
+
for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
62
|
+
}
|
|
63
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
64
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
65
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
|
|
66
|
+
return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`;
|
|
67
|
+
}
|
|
68
|
+
var TrackingManager = class {
|
|
69
|
+
constructor(options) {
|
|
70
|
+
/** session_id cacheado, provisto por el backend (no se genera ni se persiste). */
|
|
71
|
+
this.sessionId = null;
|
|
72
|
+
this.userWasJustCreated = false;
|
|
73
|
+
this.storage = options.storage;
|
|
74
|
+
this.clientUserId = options.clientUserId;
|
|
75
|
+
this.now = options.now ?? (() => Date.now());
|
|
76
|
+
this.uuid = options.uuid ?? defaultUuid;
|
|
77
|
+
this.enabled = options.enabled ?? true;
|
|
78
|
+
}
|
|
79
|
+
isTrackingEnabled() {
|
|
80
|
+
return this.enabled;
|
|
81
|
+
}
|
|
82
|
+
setTrackingEnabled(enabled) {
|
|
83
|
+
this.enabled = enabled;
|
|
84
|
+
}
|
|
85
|
+
/** Resuelve el user_id según la regla: cliente > persistido > generar+persistir. */
|
|
86
|
+
getUserId() {
|
|
87
|
+
if (!this.enabled) return null;
|
|
88
|
+
if (this.clientUserId) return this.clientUserId;
|
|
89
|
+
const persisted = this.storage.getItem(STORAGE_KEYS.userId);
|
|
90
|
+
if (persisted) return persisted;
|
|
91
|
+
const id = this.uuid();
|
|
92
|
+
this.storage.setItem(STORAGE_KEYS.userId, id);
|
|
93
|
+
this.storage.setItem(STORAGE_KEYS.firstSeen, String(this.now()));
|
|
94
|
+
this.userWasJustCreated = true;
|
|
95
|
+
return id;
|
|
96
|
+
}
|
|
97
|
+
/** `true` solo en la primera sesión tras crear el user_id propio del SDK. */
|
|
98
|
+
isNewUser() {
|
|
99
|
+
return this.userWasJustCreated;
|
|
100
|
+
}
|
|
101
|
+
/** session_id actual provisto por el backend, o null si aún no llegó. */
|
|
102
|
+
getSessionId() {
|
|
103
|
+
if (!this.enabled) return null;
|
|
104
|
+
return this.sessionId;
|
|
105
|
+
}
|
|
106
|
+
/** Cachea el session_id devuelto por el backend (respuesta de POST /sdk/popups). */
|
|
107
|
+
setSessionId(sessionId) {
|
|
108
|
+
if (!this.enabled) return;
|
|
109
|
+
this.sessionId = sessionId;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export {
|
|
114
|
+
__require,
|
|
115
|
+
createDefaultStorage,
|
|
116
|
+
STORAGE_KEYS,
|
|
117
|
+
buildSurveyIdentity,
|
|
118
|
+
TrackingManager
|
|
119
|
+
};
|
|
120
|
+
//# sourceMappingURL=chunk-IOAZQPZE.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tracking/tracking-manager.ts"],"sourcesContent":["/**\n * Identidad + sesión para el tracking de navegación (Fase 1).\n * Implementa el \"Contrato común Web+KMP\".\n *\n * Modelo: el SDK gestiona el `user_id` (persistente). El `session_id` es propiedad\n * del BACKEND: llega en la respuesta de `POST /sdk/popups` y se cachea; el backend\n * cose sesiones por `user_id` + ventana. El SDK NO genera ni expira sesiones.\n *\n * Sin dependencias de DOM: recibe el storage y (opcionalmente) el reloj y el\n * generador de uuid por inyección. El binding a `localStorage` vive en `createDefaultStorage`.\n */\n\n/** Store clave/valor mínimo (espejo conceptual del `KeyValueStorage` de KMP). */\nexport interface KeyValueStorage {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\n/** Impl en memoria — usada en tests y como fallback sin `localStorage`. */\nexport class InMemoryStorage implements KeyValueStorage {\n private map = new Map<string, string>();\n getItem(key: string): string | null {\n return this.map.has(key) ? (this.map.get(key) as string) : null;\n }\n setItem(key: string, value: string): void {\n this.map.set(key, value);\n }\n removeItem(key: string): void {\n this.map.delete(key);\n }\n}\n\n/** Storage por defecto en Web: `localStorage` si está disponible, si no in-memory. */\nexport function createDefaultStorage(): KeyValueStorage {\n try {\n if (typeof window !== 'undefined' && window.localStorage) {\n const ls = window.localStorage;\n const probe = '__deepdots_probe__';\n ls.setItem(probe, '1');\n ls.removeItem(probe);\n return {\n getItem: (k) => ls.getItem(k),\n setItem: (k, v) => ls.setItem(k, v),\n removeItem: (k) => ls.removeItem(k),\n };\n }\n } catch {\n // cae a in-memory\n }\n return new InMemoryStorage();\n}\n\n/** Claves de storage — namespace `deepdots.*`. Solo identidad (la sesión no se persiste). */\nexport const STORAGE_KEYS = {\n userId: 'deepdots.user_id',\n firstSeen: 'deepdots.user.first_seen',\n contactAttributes: 'deepdots.contact.attributes',\n} as const;\n\n/** Par clave/valor con el formato `NativeAnswer` que espera `@magicfeedback/native`. */\nexport interface IdentityAnswer {\n key: string;\n value: string[];\n}\n\n/**\n * Construye el `profile` y el `metadata` de identidad para inyectar en el survey\n * (`magicfeedback.form(appId, publicKey, profile, metadata)`), según el contrato §5.\n * `session_id` solo se incluye si el backend ya lo proveyó.\n */\nexport function buildSurveyIdentity(\n userId: string | null,\n sessionId: string | null,\n miniService: string | null = null,\n analyticsFeedbackSessionId: string | null = null,\n): { profile: IdentityAnswer[]; metadata: IdentityAnswer[] } {\n const profile: IdentityAnswer[] = userId ? [{ key: 'external-user-id', value: [userId] }] : [];\n const metadata: IdentityAnswer[] = [];\n if (sessionId) metadata.push({ key: 'session_id', value: [sessionId] });\n if (userId) metadata.push({ key: 'user_id', value: [userId] });\n if (miniService) metadata.push({ key: 'mini_service', value: [miniService] });\n if (analyticsFeedbackSessionId) metadata.push({ key: 'deepdots_analytics_feedback_session_id', value: [analyticsFeedbackSessionId] });\n return { profile, metadata };\n}\n\nexport interface TrackingManagerOptions {\n storage: KeyValueStorage;\n /** Id provisto por el cliente; si existe se usa y NO se persiste. */\n clientUserId?: string;\n /** Reloj inyectable (default `Date.now`). */\n now?: () => number;\n /** Generador de uuid inyectable (default uuid v4). */\n uuid?: () => string;\n /** Estado inicial del tracking (default `true`). */\n enabled?: boolean;\n}\n\nfunction defaultUuid(): string {\n const g = (typeof globalThis !== 'undefined' ? globalThis : {}) as {\n crypto?: { randomUUID?: () => string; getRandomValues?: (a: Uint8Array) => Uint8Array };\n };\n if (g.crypto?.randomUUID) return g.crypto.randomUUID();\n const bytes = new Uint8Array(16);\n if (g.crypto?.getRandomValues) {\n g.crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0'));\n return `${hex.slice(0, 4).join('')}-${hex.slice(4, 6).join('')}-${hex\n .slice(6, 8)\n .join('')}-${hex.slice(8, 10).join('')}-${hex.slice(10, 16).join('')}`;\n}\n\nexport class TrackingManager {\n private storage: KeyValueStorage;\n private clientUserId?: string;\n private now: () => number;\n private uuid: () => string;\n private enabled: boolean;\n\n /** session_id cacheado, provisto por el backend (no se genera ni se persiste). */\n private sessionId: string | null = null;\n private userWasJustCreated = false;\n\n constructor(options: TrackingManagerOptions) {\n this.storage = options.storage;\n this.clientUserId = options.clientUserId;\n this.now = options.now ?? (() => Date.now());\n this.uuid = options.uuid ?? defaultUuid;\n this.enabled = options.enabled ?? true;\n }\n\n isTrackingEnabled(): boolean {\n return this.enabled;\n }\n\n setTrackingEnabled(enabled: boolean): void {\n this.enabled = enabled;\n }\n\n /** Resuelve el user_id según la regla: cliente > persistido > generar+persistir. */\n getUserId(): string | null {\n if (!this.enabled) return null;\n if (this.clientUserId) return this.clientUserId;\n\n const persisted = this.storage.getItem(STORAGE_KEYS.userId);\n if (persisted) return persisted;\n\n const id = this.uuid();\n this.storage.setItem(STORAGE_KEYS.userId, id);\n this.storage.setItem(STORAGE_KEYS.firstSeen, String(this.now()));\n this.userWasJustCreated = true;\n return id;\n }\n\n /** `true` solo en la primera sesión tras crear el user_id propio del SDK. */\n isNewUser(): boolean {\n return this.userWasJustCreated;\n }\n\n /** session_id actual provisto por el backend, o null si aún no llegó. */\n getSessionId(): string | null {\n if (!this.enabled) return null;\n return this.sessionId;\n }\n\n /** Cachea el session_id devuelto por el backend (respuesta de POST /sdk/popups). */\n setSessionId(sessionId: string | null): void {\n if (!this.enabled) return;\n this.sessionId = sessionId;\n }\n}\n"],"mappings":"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","names":[]}
|