@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.

Files changed (60) hide show
  1. package/.github/copilot-instructions.md +95 -0
  2. package/README.md +79 -0
  3. package/TRACKING.md +105 -0
  4. package/USER_CONTEXT_README.md +284 -0
  5. package/package.json +154 -0
  6. package/src/config.ts +25 -0
  7. package/src/core/flowEngine.ts +1833 -0
  8. package/src/core/triggerManager.ts +1011 -0
  9. package/src/experiences/banner.ts +366 -0
  10. package/src/experiences/beacon.ts +668 -0
  11. package/src/experiences/hotspotTour.ts +654 -0
  12. package/src/experiences/hotspots.ts +566 -0
  13. package/src/experiences/modal.ts +1337 -0
  14. package/src/experiences/modalSequence.ts +1247 -0
  15. package/src/experiences/popover.ts +652 -0
  16. package/src/experiences/registry.ts +21 -0
  17. package/src/experiences/survey.ts +1639 -0
  18. package/src/experiences/taskList.ts +625 -0
  19. package/src/experiences/tooltip.ts +740 -0
  20. package/src/experiences/types.ts +395 -0
  21. package/src/experiences/walkthrough.ts +670 -0
  22. package/src/flow-sequence.ts +177 -0
  23. package/src/flows.ts +512 -0
  24. package/src/http.ts +61 -0
  25. package/src/index.ts +355 -0
  26. package/src/services/flowManager.ts +905 -0
  27. package/src/services/flowNormalizer.ts +74 -0
  28. package/src/services/locationContextService.ts +189 -0
  29. package/src/services/pageContextService.ts +221 -0
  30. package/src/services/userContextService.ts +286 -0
  31. package/src/state/appState.ts +0 -0
  32. package/src/state/hooks.ts +0 -0
  33. package/src/state/index.ts +0 -0
  34. package/src/state/migration.ts +0 -0
  35. package/src/state/store.ts +0 -0
  36. package/src/styles/banner.css.ts +0 -0
  37. package/src/styles/hotspot.css.ts +0 -0
  38. package/src/styles/hotspotTour.css.ts +0 -0
  39. package/src/styles/modal.css.ts +564 -0
  40. package/src/styles/survey.css.ts +1013 -0
  41. package/src/styles/taskList.css.ts +0 -0
  42. package/src/styles/tooltip.css.ts +149 -0
  43. package/src/styles/walkthrough.css.ts +0 -0
  44. package/src/tourUtils.ts +0 -0
  45. package/src/tracking.ts +223 -0
  46. package/src/utils/debounce.ts +66 -0
  47. package/src/utils/eventSequenceValidator.ts +124 -0
  48. package/src/utils/flowTrackingSystem.ts +524 -0
  49. package/src/utils/idGenerator.ts +155 -0
  50. package/src/utils/immediateValidationPrevention.ts +184 -0
  51. package/src/utils/normalize.ts +50 -0
  52. package/src/utils/privacyManager.ts +166 -0
  53. package/src/utils/ruleEvaluator.ts +199 -0
  54. package/src/utils/sanitize.ts +79 -0
  55. package/src/utils/selectors.ts +107 -0
  56. package/src/utils/stepExecutor.ts +345 -0
  57. package/src/utils/triggerNormalizer.ts +149 -0
  58. package/src/utils/validationInterceptor.ts +650 -0
  59. package/tsconfig.json +13 -0
  60. 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