@cognior/iap-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @cognior/iap-sdk might be problematic. Click here for more details.
- package/.github/copilot-instructions.md +95 -0
- package/README.md +79 -0
- package/TRACKING.md +105 -0
- package/USER_CONTEXT_README.md +284 -0
- package/package.json +154 -0
- package/src/config.ts +25 -0
- package/src/core/flowEngine.ts +1833 -0
- package/src/core/triggerManager.ts +1011 -0
- package/src/experiences/banner.ts +366 -0
- package/src/experiences/beacon.ts +668 -0
- package/src/experiences/hotspotTour.ts +654 -0
- package/src/experiences/hotspots.ts +566 -0
- package/src/experiences/modal.ts +1337 -0
- package/src/experiences/modalSequence.ts +1247 -0
- package/src/experiences/popover.ts +652 -0
- package/src/experiences/registry.ts +21 -0
- package/src/experiences/survey.ts +1639 -0
- package/src/experiences/taskList.ts +625 -0
- package/src/experiences/tooltip.ts +740 -0
- package/src/experiences/types.ts +395 -0
- package/src/experiences/walkthrough.ts +670 -0
- package/src/flow-sequence.ts +177 -0
- package/src/flows.ts +512 -0
- package/src/http.ts +61 -0
- package/src/index.ts +355 -0
- package/src/services/flowManager.ts +905 -0
- package/src/services/flowNormalizer.ts +74 -0
- package/src/services/locationContextService.ts +189 -0
- package/src/services/pageContextService.ts +221 -0
- package/src/services/userContextService.ts +286 -0
- package/src/state/appState.ts +0 -0
- package/src/state/hooks.ts +0 -0
- package/src/state/index.ts +0 -0
- package/src/state/migration.ts +0 -0
- package/src/state/store.ts +0 -0
- package/src/styles/banner.css.ts +0 -0
- package/src/styles/hotspot.css.ts +0 -0
- package/src/styles/hotspotTour.css.ts +0 -0
- package/src/styles/modal.css.ts +564 -0
- package/src/styles/survey.css.ts +1013 -0
- package/src/styles/taskList.css.ts +0 -0
- package/src/styles/tooltip.css.ts +149 -0
- package/src/styles/walkthrough.css.ts +0 -0
- package/src/tourUtils.ts +0 -0
- package/src/tracking.ts +223 -0
- package/src/utils/debounce.ts +66 -0
- package/src/utils/eventSequenceValidator.ts +124 -0
- package/src/utils/flowTrackingSystem.ts +524 -0
- package/src/utils/idGenerator.ts +155 -0
- package/src/utils/immediateValidationPrevention.ts +184 -0
- package/src/utils/normalize.ts +50 -0
- package/src/utils/privacyManager.ts +166 -0
- package/src/utils/ruleEvaluator.ts +199 -0
- package/src/utils/sanitize.ts +79 -0
- package/src/utils/selectors.ts +107 -0
- package/src/utils/stepExecutor.ts +345 -0
- package/src/utils/triggerNormalizer.ts +149 -0
- package/src/utils/validationInterceptor.ts +650 -0
- package/tsconfig.json +13 -0
- package/tsup.config.ts +13 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
// src/services/userContextService.ts
|
|
2
|
+
// Standard User Context Handling for DAP SDK
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* User interface - follows DAP standard user model
|
|
6
|
+
*/
|
|
7
|
+
export interface DapUser {
|
|
8
|
+
id: string;
|
|
9
|
+
role?: string;
|
|
10
|
+
email?: string;
|
|
11
|
+
attributes?: Record<string, string>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Analytics context structure
|
|
16
|
+
*/
|
|
17
|
+
export interface UserAnalyticsContext {
|
|
18
|
+
userId: string;
|
|
19
|
+
role?: string;
|
|
20
|
+
attributes?: Record<string, string>;
|
|
21
|
+
isAnonymous: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* User context change event
|
|
26
|
+
*/
|
|
27
|
+
export interface UserContextChangeEvent {
|
|
28
|
+
type: 'user-changed' | 'user-cleared';
|
|
29
|
+
user: DapUser | null;
|
|
30
|
+
previousUser: DapUser | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* UserContextService - Singleton service for managing user context in DAP SDK
|
|
35
|
+
*
|
|
36
|
+
* SECURITY CONSTRAINTS:
|
|
37
|
+
* - NEVER infer user identity
|
|
38
|
+
* - Only accept explicitly provided data
|
|
39
|
+
* - Host application is source of truth
|
|
40
|
+
*/
|
|
41
|
+
export class UserContextService {
|
|
42
|
+
private static _instance: UserContextService | null = null;
|
|
43
|
+
private _user: DapUser | null = null;
|
|
44
|
+
private _fallbackUserId: string | null = null;
|
|
45
|
+
private _eventListeners: ((event: UserContextChangeEvent) => void)[] = [];
|
|
46
|
+
|
|
47
|
+
private readonly SESSION_STORAGE_KEY = 'dap_user_context';
|
|
48
|
+
private readonly FALLBACK_USER_KEY = 'dap_anonymous_user_id';
|
|
49
|
+
|
|
50
|
+
private constructor() {
|
|
51
|
+
this.initializeFromStorage();
|
|
52
|
+
console.debug('[DAP UserContext] Service initialized');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get singleton instance
|
|
57
|
+
*/
|
|
58
|
+
public static getInstance(): UserContextService {
|
|
59
|
+
if (!this._instance) {
|
|
60
|
+
this._instance = new UserContextService();
|
|
61
|
+
}
|
|
62
|
+
return this._instance;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Initialize user context from sessionStorage
|
|
67
|
+
*/
|
|
68
|
+
private initializeFromStorage(): void {
|
|
69
|
+
try {
|
|
70
|
+
// Restore user context
|
|
71
|
+
const storedUser = sessionStorage.getItem(this.SESSION_STORAGE_KEY);
|
|
72
|
+
if (storedUser) {
|
|
73
|
+
this._user = JSON.parse(storedUser);
|
|
74
|
+
console.debug('[DAP UserContext] Restored user from storage:', this._user?.id);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Restore or generate fallback user ID
|
|
78
|
+
this._fallbackUserId = sessionStorage.getItem(this.FALLBACK_USER_KEY);
|
|
79
|
+
if (!this._fallbackUserId) {
|
|
80
|
+
this._fallbackUserId = this.generateFallbackUserId();
|
|
81
|
+
sessionStorage.setItem(this.FALLBACK_USER_KEY, this._fallbackUserId);
|
|
82
|
+
console.debug('[DAP UserContext] Generated fallback user ID:', this._fallbackUserId);
|
|
83
|
+
}
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.error('[DAP UserContext] Error initializing from storage:', error);
|
|
86
|
+
this._fallbackUserId = this.generateFallbackUserId();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Generate a stable unique fallback user ID
|
|
92
|
+
*/
|
|
93
|
+
private generateFallbackUserId(): string {
|
|
94
|
+
const timestamp = Date.now();
|
|
95
|
+
const random = Math.random().toString(36).substring(2, 15);
|
|
96
|
+
return `dap-anon-${timestamp}-${random}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Set user context (replaces existing user)
|
|
101
|
+
*/
|
|
102
|
+
public setUser(user: DapUser): void {
|
|
103
|
+
if (!user || !user.id) {
|
|
104
|
+
console.error('[DAP UserContext] Invalid user - id is required');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const previousUser = this._user;
|
|
109
|
+
this._user = { ...user };
|
|
110
|
+
|
|
111
|
+
// Persist to sessionStorage
|
|
112
|
+
try {
|
|
113
|
+
sessionStorage.setItem(this.SESSION_STORAGE_KEY, JSON.stringify(this._user));
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error('[DAP UserContext] Error persisting user:', error);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
console.debug('[DAP UserContext] User set:', this._user.id);
|
|
119
|
+
this.notifyListeners({ type: 'user-changed', user: this._user, previousUser });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Update user context (merge with existing data)
|
|
124
|
+
*/
|
|
125
|
+
public updateUser(partialUser: Partial<DapUser>): void {
|
|
126
|
+
if (!this._user) {
|
|
127
|
+
console.warn('[DAP UserContext] Cannot update - no user context available');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const previousUser = { ...this._user };
|
|
132
|
+
this._user = {
|
|
133
|
+
...this._user,
|
|
134
|
+
...partialUser,
|
|
135
|
+
// Merge attributes specifically
|
|
136
|
+
attributes: {
|
|
137
|
+
...this._user.attributes,
|
|
138
|
+
...partialUser.attributes
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Persist to sessionStorage
|
|
143
|
+
try {
|
|
144
|
+
sessionStorage.setItem(this.SESSION_STORAGE_KEY, JSON.stringify(this._user));
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error('[DAP UserContext] Error persisting updated user:', error);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
console.debug('[DAP UserContext] User updated:', this._user.id);
|
|
150
|
+
this.notifyListeners({ type: 'user-changed', user: this._user, previousUser });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get current user context
|
|
155
|
+
*/
|
|
156
|
+
public getUser(): DapUser | null {
|
|
157
|
+
return this._user ? { ...this._user } : null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Clear user context (reverts to anonymous)
|
|
162
|
+
*/
|
|
163
|
+
public clearUser(): void {
|
|
164
|
+
const previousUser = this._user;
|
|
165
|
+
this._user = null;
|
|
166
|
+
|
|
167
|
+
// Remove from sessionStorage
|
|
168
|
+
try {
|
|
169
|
+
sessionStorage.removeItem(this.SESSION_STORAGE_KEY);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error('[DAP UserContext] Error clearing user storage:', error);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
console.debug('[DAP UserContext] User context cleared');
|
|
175
|
+
this.notifyListeners({ type: 'user-cleared', user: null, previousUser });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Check if real user context is available (not anonymous)
|
|
180
|
+
*/
|
|
181
|
+
public hasRealUser(): boolean {
|
|
182
|
+
return this._user !== null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Get analytics context for tracking
|
|
187
|
+
*/
|
|
188
|
+
public getAnalyticsContext(): UserAnalyticsContext {
|
|
189
|
+
if (this._user) {
|
|
190
|
+
return {
|
|
191
|
+
userId: this._user.id,
|
|
192
|
+
role: this._user.role,
|
|
193
|
+
attributes: this._user.attributes,
|
|
194
|
+
isAnonymous: false
|
|
195
|
+
};
|
|
196
|
+
} else {
|
|
197
|
+
return {
|
|
198
|
+
userId: this._fallbackUserId || 'unknown',
|
|
199
|
+
isAnonymous: true
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Get user property for rule evaluation
|
|
206
|
+
* Supports: user.id, user.role, user.email, user.attributes.*
|
|
207
|
+
*/
|
|
208
|
+
public getUserProperty(propertyPath: string): string | null {
|
|
209
|
+
if (!propertyPath.startsWith('user.')) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const path = propertyPath.substring(5); // Remove 'user.' prefix
|
|
214
|
+
|
|
215
|
+
// Handle anonymous user - only allow id
|
|
216
|
+
if (!this._user) {
|
|
217
|
+
if (path === 'id') {
|
|
218
|
+
return this._fallbackUserId;
|
|
219
|
+
}
|
|
220
|
+
console.debug(`[DAP UserContext] Property ${propertyPath} not available - no user context`);
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Handle direct properties
|
|
225
|
+
switch (path) {
|
|
226
|
+
case 'id':
|
|
227
|
+
return this._user.id;
|
|
228
|
+
case 'role':
|
|
229
|
+
return this._user.role || null;
|
|
230
|
+
case 'email':
|
|
231
|
+
return this._user.email || null;
|
|
232
|
+
default:
|
|
233
|
+
// Handle attributes
|
|
234
|
+
if (path.startsWith('attributes.')) {
|
|
235
|
+
const attributeKey = path.substring(11); // Remove 'attributes.' prefix
|
|
236
|
+
return this._user.attributes?.[attributeKey] || null;
|
|
237
|
+
}
|
|
238
|
+
console.warn(`[DAP UserContext] Unknown property path: ${propertyPath}`);
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Subscribe to user context changes
|
|
245
|
+
*/
|
|
246
|
+
public onUserChange(callback: (event: UserContextChangeEvent) => void): () => void {
|
|
247
|
+
this._eventListeners.push(callback);
|
|
248
|
+
|
|
249
|
+
// Return unsubscribe function
|
|
250
|
+
return () => {
|
|
251
|
+
const index = this._eventListeners.indexOf(callback);
|
|
252
|
+
if (index > -1) {
|
|
253
|
+
this._eventListeners.splice(index, 1);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Notify all listeners of user context changes
|
|
260
|
+
*/
|
|
261
|
+
private notifyListeners(event: UserContextChangeEvent): void {
|
|
262
|
+
this._eventListeners.forEach(callback => {
|
|
263
|
+
try {
|
|
264
|
+
callback(event);
|
|
265
|
+
} catch (error) {
|
|
266
|
+
console.error('[DAP UserContext] Error in change listener:', error);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Debug method to get current state
|
|
273
|
+
*/
|
|
274
|
+
public getDebugState(): any {
|
|
275
|
+
return {
|
|
276
|
+
hasUser: this.hasRealUser(),
|
|
277
|
+
userId: this._user?.id || this._fallbackUserId,
|
|
278
|
+
userRole: this._user?.role,
|
|
279
|
+
isAnonymous: !this.hasRealUser(),
|
|
280
|
+
attributeCount: Object.keys(this._user?.attributes || {}).length
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Export singleton instance
|
|
286
|
+
export const userContextService = UserContextService.getInstance();
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|