@encatch/web-sdk 0.0.13

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 ADDED
@@ -0,0 +1,756 @@
1
+ # Encatch Web SDK
2
+
3
+ A powerful, lightweight JavaScript SDK for collecting user feedback and running surveys on web applications. Built with Preact and TypeScript, the Encatch Web SDK provides intelligent trigger-based feedback collection with advanced targeting and frequency control.
4
+
5
+ ## Features
6
+
7
+ - **Intelligent Triggers**: Automatically display feedback based on user behavior
8
+ - Page load triggers
9
+ - Custom event triggers
10
+ - Scroll-based triggers
11
+ - Time-delayed triggers
12
+ - **User Session Management**: Track and manage user sessions with device fingerprinting
13
+ - **Frequency Control**: Configure feedback display frequency and scheduling
14
+ - **Multi-language Support**: Display feedback in multiple languages
15
+ - **Theme Support**: Light and dark mode with custom CSS
16
+ - **Prepopulated Answers**: Pre-fill feedback forms with known data
17
+ - **Event Tracking**: Track user interactions and custom events
18
+ - **TypeScript Support**: Fully typed API for better developer experience
19
+ - **Lightweight**: Minimal bundle size with optimized performance
20
+
21
+ ## Installation
22
+
23
+ ### Via Script Tag (Recommended)
24
+
25
+ Add the following script to your HTML file before the closing `</head>` tag:
26
+
27
+ ```html
28
+ <script>
29
+ !function(){if(!window.encatch){var e={_i:[],apiKey:"",config:{host:"https://your-host.com",autoStartEnabled:!1,autoStartSessionDisabled:!0,themeMode:"light",language:"en",customCssLink:""},initialized:!1,chunkUrlLoader:function(e){return window.encatch.config.host+e},init:function(n,t){if(!("initialized"in window.encatch&&window.encatch.initialized)){if(window.encatch.apiKey=n,t?.host&&(window.encatch.config.host=t.host),t?.identity&&(window.encatch.config.identity=t.identity),void 0!==t?.autoStartSessionDisabled&&(window.encatch.config.autoStartSessionDisabled=t.autoStartSessionDisabled),void 0!==t?.processAfterIdentitySet?window.encatch.config.processAfterIdentitySet=t.processAfterIdentitySet:window.encatch.config.processAfterIdentitySet=!1,void 0!==t?.autoStartEnabled&&(window.encatch.config.autoStartEnabled=t.autoStartEnabled),t?.themeMode?window.encatch.config.themeMode=t.themeMode:window.encatch.config.themeMode="light",t?.language?window.encatch.config.language=t.language:window.encatch.config.language="en",t?.customCssLink&&(window.encatch.config.customCssLink=t.customCssLink),t?.setUser&&(window.encatch.config.setUser=t.setUser),window.encatch.initialized=!0,!import.meta.env.VITE_PATH_PREFIX||import.meta.env.VITE_PATH_PREFIX.startsWith("/")||(import.meta.env.VITE_PATH_PREFIX="/"+import.meta.env.VITE_PATH_PREFIX),import.meta.env.VITE_PATH_PREFIX=import.meta.env.VITE_PATH_PREFIX.replace(/\/+$/,""),scriptSrc=`${window.encatch.config.host}${import.meta.env.VITE_PATH_PREFIX||""}/encatch-web-sdk.js`,(script=document.createElement("script")).src=scriptSrc,script.type="module",script.async=!0,firstHeadElement=document.head.firstChild)firstHeadElement?document.head.insertBefore(script,firstHeadElement):document.head.appendChild(script);var o=document.createElement("div");o.id="enisght-root",document.body.appendChild(o)}}};window.encatch=e,["trackEvent","stop","start","setUser","setThemeMode","setLanguage","openFeedbackById","openFeedbackByName","verifyFeedbackIds","forceFetchEligibleFeedbacks","capturePageScrollEvent"].forEach((function(n){e[n]=function(){for(var t=arguments.length,o=new Array(t),i=0;i<t;i++)o[i]=arguments[i];window.encatch._i.push([n].concat(o))}}))}}();
30
+ </script>
31
+ ```
32
+
33
+ Then initialize the SDK:
34
+
35
+ ```html
36
+ <script>
37
+ encatch.init('YOUR_API_KEY', {
38
+ host: 'https://your-host.com',
39
+ autoStartEnabled: true
40
+ });
41
+ </script>
42
+ ```
43
+
44
+ ### Via NPM (For Module Bundlers)
45
+
46
+ ```bash
47
+ npm install @encatch/web-sdk
48
+ ```
49
+
50
+ ```javascript
51
+ import '@encatch/web-sdk';
52
+
53
+ encatch.init('YOUR_API_KEY', {
54
+ host: 'https://your-host.com',
55
+ autoStartEnabled: true
56
+ });
57
+ ```
58
+
59
+ ## Quick Start
60
+
61
+ ### Basic Setup
62
+
63
+ ```javascript
64
+ // Initialize the SDK
65
+ encatch.init('YOUR_API_KEY', {
66
+ host: 'https://your-host.com',
67
+ autoStartEnabled: true,
68
+ themeMode: 'light',
69
+ language: 'en'
70
+ });
71
+
72
+ // Set user identity (optional)
73
+ encatch.setUser('user-123', {
74
+ $set: {
75
+ email: 'user@example.com',
76
+ name: 'John Doe',
77
+ plan: 'premium'
78
+ }
79
+ });
80
+
81
+ // Track custom events
82
+ encatch.trackEvent('button_clicked', {
83
+ path: '/dashboard',
84
+ button_id: 'upgrade-button'
85
+ });
86
+ ```
87
+
88
+ ### Manual Trigger
89
+
90
+ ```javascript
91
+ // Open feedback by ID
92
+ encatch.openFeedbackById('feedback-config-id-123');
93
+
94
+ // Open feedback by name
95
+ encatch.openFeedbackByName('Customer Satisfaction Survey');
96
+
97
+ // With theme and language override
98
+ encatch.openFeedbackById('feedback-config-id-123', 'dark', 'es');
99
+ ```
100
+
101
+ ## Configuration Options
102
+
103
+ ### Initialization Options
104
+
105
+ | Option | Type | Default | Description |
106
+ |--------|------|---------|-------------|
107
+ | `host` | `string` | - | **Required**. The host URL for the Encatch API |
108
+ | `autoStartEnabled` | `boolean` | `false` | Automatically start fetching eligible feedbacks |
109
+ | `autoStartSessionDisabled` | `boolean` | `true` | Disable automatic session management |
110
+ | `processAfterIdentitySet` | `boolean` | `false` | Wait for user identity before processing |
111
+ | `themeMode` | `'light' \| 'dark'` | `'light'` | Default theme mode |
112
+ | `language` | `string` | `'en'` | Default language code |
113
+ | `customCssLink` | `string` | - | URL to custom CSS file |
114
+ | `setUser` | `UserInfo` | - | Initial user identity |
115
+ | `customProperties` | `Record<string, string>` | - | Custom properties for targeting |
116
+ | `onSessionDisabled` | `(action, message) => void` | - | Callback when session functions are disabled |
117
+
118
+ ### Example: Complete Configuration
119
+
120
+ ```javascript
121
+ encatch.init('YOUR_API_KEY', {
122
+ host: 'https://your-host.com',
123
+ autoStartEnabled: true,
124
+ autoStartSessionDisabled: false,
125
+ themeMode: 'light',
126
+ language: 'en',
127
+ customCssLink: 'https://your-cdn.com/custom-theme.css',
128
+ setUser: {
129
+ userId: 'user-123',
130
+ traits: {
131
+ $set: {
132
+ email: 'user@example.com',
133
+ plan: 'premium'
134
+ }
135
+ }
136
+ },
137
+ onSessionDisabled: (action, message) => {
138
+ console.warn(`Session action blocked: ${action} - ${message}`);
139
+ }
140
+ });
141
+ ```
142
+
143
+ ## API Reference
144
+
145
+ ### `init(apiKey, options)`
146
+
147
+ Initialize the Encatch SDK.
148
+
149
+ ```javascript
150
+ encatch.init('YOUR_API_KEY', {
151
+ host: 'https://your-host.com',
152
+ autoStartEnabled: true
153
+ });
154
+ ```
155
+
156
+ ### `start(userId?, traits?)`
157
+
158
+ Start the SDK and optionally set user identity. Useful when `autoStartEnabled: false`.
159
+
160
+ ```javascript
161
+ // Start without user
162
+ encatch.start();
163
+
164
+ // Start with user identity
165
+ encatch.start('user-123', {
166
+ $set: {
167
+ email: 'user@example.com',
168
+ name: 'John Doe'
169
+ }
170
+ });
171
+ ```
172
+
173
+ ### `stop()`
174
+
175
+ Stop the SDK. Behavior depends on initialization:
176
+
177
+ - If initialized with `autoStartEnabled: false`: Clears all data, feedbacks, and frequency tracking
178
+ - If initialized with `autoStartEnabled: true`: Makes user anonymous but retains feedbacks
179
+
180
+ ```javascript
181
+ encatch.stop();
182
+ ```
183
+
184
+ ### `setUser(userId?, traits?)`
185
+
186
+ Set or update user identity.
187
+
188
+ ```javascript
189
+ // Set user identity
190
+ encatch.setUser('user-123', {
191
+ $set: { email: 'user@example.com', plan: 'premium' },
192
+ $set_once: { signup_date: '2024-01-01' },
193
+ $counter: { logins: 1 },
194
+ $unset: ['temp_property']
195
+ });
196
+
197
+ // Make user anonymous
198
+ encatch.setUser();
199
+ ```
200
+
201
+ #### User Traits Operators
202
+
203
+ - `$set`: Set property value (overwrites existing)
204
+ - `$set_once`: Set only if property doesn't exist
205
+ - `$counter`: Increment counter by value
206
+ - `$unset`: Remove properties (array of property names)
207
+
208
+ ### `trackEvent(eventName, properties?)`
209
+
210
+ Track custom events for trigger matching.
211
+
212
+ ```javascript
213
+ encatch.trackEvent('purchase_completed', {
214
+ path: '/checkout/success',
215
+ product_id: 'prod-123',
216
+ amount: 99.99
217
+ });
218
+ ```
219
+
220
+ ### `openFeedbackById(feedbackConfigId, theme?, language?, event?, customProperties?, prepopulatedAnswers?)`
221
+
222
+ Manually open a feedback form by its configuration ID.
223
+
224
+ ```javascript
225
+ // Basic usage
226
+ encatch.openFeedbackById('feedback-config-id-123');
227
+
228
+ // With theme and language override
229
+ encatch.openFeedbackById('feedback-config-id-123', 'dark', 'es');
230
+
231
+ // With prepopulated answers
232
+ encatch.openFeedbackById('feedback-config-id-123', 'light', 'en', undefined, undefined, [
233
+ {
234
+ sectionIndex: 0,
235
+ questionIndex: 0,
236
+ value: { text: 'Pre-filled answer' }
237
+ }
238
+ ]);
239
+ ```
240
+
241
+ ### `openFeedbackByName(feedbackName, theme?, language?, event?, customProperties?, prepopulatedAnswers?)`
242
+
243
+ Manually open a feedback form by its name.
244
+
245
+ ```javascript
246
+ encatch.openFeedbackByName('Customer Satisfaction Survey');
247
+
248
+ // With options
249
+ encatch.openFeedbackByName('NPS Survey', 'dark', 'es');
250
+ ```
251
+
252
+ ### `setThemeMode(theme)`
253
+
254
+ Change the theme mode dynamically.
255
+
256
+ ```javascript
257
+ encatch.setThemeMode('dark');
258
+ encatch.setThemeMode('light');
259
+ ```
260
+
261
+ ### `setLanguage(language)`
262
+
263
+ Change the language dynamically.
264
+
265
+ ```javascript
266
+ encatch.setLanguage('es'); // Spanish
267
+ encatch.setLanguage('fr'); // French
268
+ encatch.setLanguage('en'); // English
269
+ ```
270
+
271
+ ### `forceFetchEligibleFeedbacks()`
272
+
273
+ Force refresh the list of eligible feedbacks.
274
+
275
+ ```javascript
276
+ await encatch.forceFetchEligibleFeedbacks();
277
+ ```
278
+
279
+ ### `verifyFeedbackIds(feedbackIds)`
280
+
281
+ Verify which feedback IDs are currently eligible.
282
+
283
+ ```javascript
284
+ const validIds = encatch.verifyFeedbackIds([
285
+ 'feedback-id-1',
286
+ 'feedback-id-2',
287
+ 'feedback-id-3'
288
+ ]);
289
+ console.log('Valid feedback IDs:', validIds);
290
+ ```
291
+
292
+ ### `capturePageScrollEvent(scrollPercent)`
293
+
294
+ Manually trigger scroll-based feedback.
295
+
296
+ ```javascript
297
+ // Track scroll percentage
298
+ window.addEventListener('scroll', () => {
299
+ const scrollPercent = (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100;
300
+ encatch.capturePageScrollEvent(`${scrollPercent}%`);
301
+ });
302
+ ```
303
+
304
+ ## Event Tracking
305
+
306
+ ### Automatic Page Tracking
307
+
308
+ The SDK automatically tracks page changes in single-page applications:
309
+
310
+ ```javascript
311
+ // Page changes are tracked automatically
312
+ // Triggers "pageLoad" event for configured triggers
313
+ ```
314
+
315
+ ### Custom Events
316
+
317
+ Track custom events for business logic:
318
+
319
+ ```javascript
320
+ // E-commerce example
321
+ encatch.trackEvent('product_viewed', {
322
+ path: '/products/123',
323
+ category: 'electronics',
324
+ price: 299.99
325
+ });
326
+
327
+ encatch.trackEvent('add_to_cart', {
328
+ path: '/products/123',
329
+ product_id: 'prod-123'
330
+ });
331
+
332
+ encatch.trackEvent('checkout_started', {
333
+ path: '/checkout',
334
+ cart_value: 599.98
335
+ });
336
+ ```
337
+
338
+ ### Scroll Tracking
339
+
340
+ ```javascript
341
+ // Automatic scroll tracking (if configured in feedback triggers)
342
+ // Or manual tracking:
343
+ let scrollTriggered = false;
344
+
345
+ window.addEventListener('scroll', () => {
346
+ const scrollPercent = (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100;
347
+
348
+ if (scrollPercent > 50 && !scrollTriggered) {
349
+ encatch.capturePageScrollEvent('50%');
350
+ scrollTriggered = true;
351
+ }
352
+ });
353
+ ```
354
+
355
+ ## Advanced Usage
356
+
357
+ ### Prepopulated Answers
358
+
359
+ Pre-fill feedback forms with known data:
360
+
361
+ ```javascript
362
+ encatch.openFeedbackById('feedback-config-id', 'light', 'en', undefined, undefined, [
363
+ {
364
+ sectionIndex: 0, // First section
365
+ questionIndex: 0, // First question
366
+ value: { text: 'Pre-filled text answer' }
367
+ },
368
+ {
369
+ sectionIndex: 0,
370
+ questionIndex: 1,
371
+ value: { rating: 5 }
372
+ }
373
+ ]);
374
+ ```
375
+
376
+ ### Custom Properties for Targeting
377
+
378
+ Pass custom properties for advanced targeting:
379
+
380
+ ```javascript
381
+ encatch.openFeedbackById(
382
+ 'feedback-config-id',
383
+ 'light',
384
+ 'en',
385
+ undefined,
386
+ {
387
+ user_segment: 'premium',
388
+ feature_flag: 'beta_features',
389
+ experiment_group: 'variant_a'
390
+ }
391
+ );
392
+ ```
393
+
394
+ ### Session Management
395
+
396
+ ```javascript
397
+ // Disable automatic session management
398
+ encatch.init('YOUR_API_KEY', {
399
+ host: 'https://your-host.com',
400
+ autoStartSessionDisabled: false
401
+ });
402
+
403
+ // Manual session control (when autoStartSessionDisabled: false)
404
+ encatch.start(); // Start a new session
405
+ encatch.stop(); // End current session
406
+ ```
407
+
408
+ ### Dynamic Theme Switching
409
+
410
+ ```javascript
411
+ // Listen to system theme changes
412
+ const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)');
413
+
414
+ darkModeQuery.addEventListener('change', (e) => {
415
+ encatch.setThemeMode(e.matches ? 'dark' : 'light');
416
+ });
417
+
418
+ // Or use a theme toggle
419
+ function toggleTheme() {
420
+ const currentTheme = getCurrentTheme(); // Your theme logic
421
+ encatch.setThemeMode(currentTheme === 'dark' ? 'light' : 'dark');
422
+ }
423
+ ```
424
+
425
+ ### Custom CSS Styling
426
+
427
+ ```javascript
428
+ // Option 1: Provide custom CSS URL during initialization
429
+ encatch.init('YOUR_API_KEY', {
430
+ host: 'https://your-host.com',
431
+ customCssLink: 'https://your-cdn.com/custom-encatch-theme.css'
432
+ });
433
+
434
+ // Option 2: Configure custom CSS per feedback in the Encatch dashboard
435
+ ```
436
+
437
+ ### Conditional Feedback Display
438
+
439
+ ```javascript
440
+ // Only show feedback to premium users
441
+ if (user.plan === 'premium') {
442
+ encatch.openFeedbackById('premium-user-feedback');
443
+ }
444
+
445
+ // Show different feedback based on user journey
446
+ if (isNewUser) {
447
+ encatch.openFeedbackByName('Onboarding Feedback');
448
+ } else {
449
+ encatch.openFeedbackByName('Feature Usage Survey');
450
+ }
451
+ ```
452
+
453
+ ## Form Event Listeners
454
+
455
+ The SDK provides two approaches for listening to feedback form events: **config-based** and **runtime-based**.
456
+
457
+ ### Available Form Events
458
+
459
+ | Event | Payload | Description |
460
+ |-------|---------|-------------|
461
+ | `form:submit` | `{ feedbackIdentifier: string, feedbackConfigurationId: string, completionTimeInSeconds: number, response: Object }` | Emitted when the user completes and submits the feedback form |
462
+ | `form:view` | `{ feedbackIdentifier: string, feedbackConfigurationId: string }` | Emitted when the feedback form is displayed to the user |
463
+ | `form:close` | `{ feedbackIdentifier: string, feedbackConfigurationId: string }` | Emitted when the user closes the feedback form (with or without submitting) |
464
+ | `form:questionAnswered` | `{ questionId: string, answer: Object }` | Emitted when the user answers a question |
465
+ | `form:sectionChange` | `{ sectionIndex: number }` | Emitted when the user navigates to a different section |
466
+ | `form:error` | `{ error: string }` | Emitted when an error occurs during form interaction |
467
+
468
+ ### Approach 1: Config-Based (Recommended)
469
+
470
+ Define event handlers during SDK initialization using the `onFormEvent` callback:
471
+
472
+ ```javascript
473
+ encatch.init('YOUR_API_KEY', {
474
+ host: 'https://your-host.com',
475
+ autoStartEnabled: true,
476
+ onFormEvent: (formEvent) => {
477
+ // Listen to form submission
478
+ formEvent.onSubmit((data) => {
479
+ console.log('Form submitted:', data);
480
+ console.log('Feedback ID:', data.feedbackIdentifier);
481
+ console.log('Config ID:', data.feedbackConfigurationId);
482
+ console.log('Completion time:', data.completionTimeInSeconds);
483
+
484
+ // Send to analytics
485
+ analytics.track('Feedback Submitted', {
486
+ feedbackId: data.feedbackIdentifier,
487
+ configId: data.feedbackConfigurationId
488
+ });
489
+ });
490
+
491
+ // Listen to form view
492
+ formEvent.onView((data) => {
493
+ console.log('Form viewed:', data);
494
+ });
495
+
496
+ // Listen to form close
497
+ formEvent.onClose((data) => {
498
+ console.log('Form closed:', data);
499
+ });
500
+
501
+ // Listen to question answered
502
+ formEvent.onQuestionAnswered((data) => {
503
+ console.log('Question answered:', data.questionId, data.answer);
504
+ });
505
+
506
+ // Listen to section change
507
+ formEvent.onSectionChange((data) => {
508
+ console.log('Section changed to:', data.sectionIndex);
509
+ });
510
+
511
+ // Listen to errors
512
+ formEvent.onError((data) => {
513
+ console.error('Form error:', data.error);
514
+ });
515
+ }
516
+ });
517
+ ```
518
+
519
+ ### Approach 2: Runtime-Based
520
+
521
+ Subscribe to events at runtime using the `encatch.on()` method:
522
+
523
+ ```javascript
524
+ // Subscribe to form submission
525
+ const unsubscribeSubmit = encatch.on('form:submit', (data) => {
526
+ console.log('Form submitted:', data);
527
+ alert(`Feedback submitted! ID: ${data.feedbackIdentifier}`);
528
+ });
529
+
530
+ // Subscribe to form view
531
+ const unsubscribeView = encatch.on('form:view', (data) => {
532
+ console.log('Form viewed:', data);
533
+ });
534
+
535
+ // Subscribe to form close
536
+ const unsubscribeClose = encatch.on('form:close', (data) => {
537
+ console.log('Form closed:', data);
538
+ });
539
+
540
+ // Subscribe to question answered
541
+ const unsubscribeQuestion = encatch.on('form:questionAnswered', (data) => {
542
+ console.log('Question answered:', data);
543
+ });
544
+
545
+ // Subscribe to section change
546
+ const unsubscribeSection = encatch.on('form:sectionChange', (data) => {
547
+ console.log('Section changed:', data);
548
+ });
549
+
550
+ // Subscribe to errors
551
+ const unsubscribeError = encatch.on('form:error', (data) => {
552
+ console.error('Form error:', data);
553
+ });
554
+
555
+ // Unsubscribe when no longer needed
556
+ unsubscribeSubmit();
557
+ unsubscribeView();
558
+ ```
559
+
560
+ ### Integration Examples
561
+
562
+ #### Google Analytics Integration
563
+
564
+ ```javascript
565
+ encatch.init('YOUR_API_KEY', {
566
+ host: 'https://your-host.com',
567
+ autoStartEnabled: true,
568
+ onFormEvent: (formEvent) => {
569
+ formEvent.onView((data) => {
570
+ gtag('event', 'feedback_viewed', {
571
+ feedback_id: data.feedbackIdentifier,
572
+ config_id: data.feedbackConfigurationId
573
+ });
574
+ });
575
+
576
+ formEvent.onSubmit((data) => {
577
+ gtag('event', 'feedback_submitted', {
578
+ feedback_id: data.feedbackIdentifier,
579
+ config_id: data.feedbackConfigurationId,
580
+ completion_time: data.completionTimeInSeconds
581
+ });
582
+ });
583
+
584
+ formEvent.onClose((data) => {
585
+ gtag('event', 'feedback_closed', {
586
+ feedback_id: data.feedbackIdentifier
587
+ });
588
+ });
589
+ }
590
+ });
591
+ ```
592
+
593
+ #### Segment Analytics Integration
594
+
595
+ ```javascript
596
+ encatch.init('YOUR_API_KEY', {
597
+ host: 'https://your-host.com',
598
+ autoStartEnabled: true,
599
+ onFormEvent: (formEvent) => {
600
+ formEvent.onSubmit((data) => {
601
+ analytics.track('Feedback Completed', {
602
+ feedbackId: data.feedbackIdentifier,
603
+ configId: data.feedbackConfigurationId,
604
+ duration: data.completionTimeInSeconds
605
+ });
606
+ });
607
+
608
+ formEvent.onQuestionAnswered((data) => {
609
+ analytics.track('Question Answered', {
610
+ questionId: data.questionId,
611
+ answerType: data.answer.answer_type
612
+ });
613
+ });
614
+ }
615
+ });
616
+ ```
617
+
618
+ #### Custom Data Layer (GTM)
619
+
620
+ ```javascript
621
+ encatch.init('YOUR_API_KEY', {
622
+ host: 'https://your-host.com',
623
+ autoStartEnabled: true,
624
+ onFormEvent: (formEvent) => {
625
+ formEvent.onSubmit((data) => {
626
+ window.dataLayer = window.dataLayer || [];
627
+ window.dataLayer.push({
628
+ event: 'feedback_submitted',
629
+ feedbackId: data.feedbackIdentifier,
630
+ configId: data.feedbackConfigurationId,
631
+ completionTime: data.completionTimeInSeconds
632
+ });
633
+ });
634
+ }
635
+ });
636
+ ```
637
+
638
+ #### CRM Integration (e.g., Salesforce, HubSpot)
639
+
640
+ ```javascript
641
+ encatch.init('YOUR_API_KEY', {
642
+ host: 'https://your-host.com',
643
+ autoStartEnabled: true,
644
+ onFormEvent: (formEvent) => {
645
+ formEvent.onSubmit(async (data) => {
646
+ // Send to your CRM
647
+ await fetch('https://your-crm.com/api/feedback', {
648
+ method: 'POST',
649
+ headers: { 'Content-Type': 'application/json' },
650
+ body: JSON.stringify({
651
+ feedbackId: data.feedbackIdentifier,
652
+ userId: window.currentUserId,
653
+ response: data.response,
654
+ timestamp: new Date().toISOString()
655
+ })
656
+ });
657
+ });
658
+ }
659
+ });
660
+ ```
661
+
662
+ ## TypeScript Support
663
+
664
+ The SDK is fully typed. TypeScript definitions are included:
665
+
666
+ ```typescript
667
+ import '@encatch/web-sdk';
668
+
669
+ // All methods are typed
670
+ encatch.init('YOUR_API_KEY', {
671
+ host: 'https://your-host.com',
672
+ autoStartEnabled: true,
673
+ themeMode: 'light', // Type: 'light' | 'dark'
674
+ language: 'en'
675
+ });
676
+
677
+ // User traits are typed
678
+ encatch.setUser('user-123', {
679
+ $set: { email: 'user@example.com' },
680
+ $set_once: { signup_date: '2024-01-01' },
681
+ $counter: { logins: 1 },
682
+ $unset: ['temp_field']
683
+ });
684
+
685
+ // Event properties are typed
686
+ encatch.trackEvent('custom_event', {
687
+ path: '/page',
688
+ event: 'click'
689
+ });
690
+ ```
691
+
692
+ ## Development
693
+
694
+ ### Prerequisites
695
+
696
+ - Node.js 18+
697
+ - pnpm
698
+
699
+ ### Setup
700
+
701
+ ```bash
702
+ # Install dependencies
703
+ pnpm install
704
+
705
+ # Start development server
706
+ pnpm dev
707
+
708
+ # Build for production
709
+ pnpm build
710
+
711
+ # Preview production build
712
+ pnpm preview
713
+ ```
714
+
715
+ ### Project Structure
716
+
717
+ ```
718
+ packages/web-sdk/
719
+ ├── src/
720
+ │ ├── @types/ # TypeScript type definitions
721
+ │ ├── hooks/ # Preact hooks for functionality
722
+ │ ├── utils/ # Utility functions
723
+ │ ├── index.tsx # Main SDK initialization
724
+ │ ├── core-wrapper.tsx # Core SDK logic
725
+ │ └── surveySDK.ts # Event SDK
726
+ ├── dist-sdk/ # Build output
727
+ ├── package.json
728
+ ├── vite.config.ts # Vite configuration
729
+ └── README.md
730
+ ```
731
+
732
+ ### Build Output
733
+
734
+ The build produces:
735
+
736
+ - `encatch-web-sdk.js` - Main SDK bundle
737
+ - `encatch-web-sdk.css` - Default styles
738
+ - `preview-sdk.js` - Preview/demo version
739
+ - `embedded-sdk.js` - Embedded version
740
+
741
+ ## Browser Support
742
+
743
+ - Chrome 62+
744
+ - Firefox 59+
745
+ - Safari 12+
746
+ - iOS Safari 10.3+
747
+ - Opera 50+
748
+ - Edge (Chromium-based)
749
+
750
+ ## License
751
+
752
+ Private - Copyright (c) Encatch
753
+
754
+ ## Support
755
+
756
+ For issues, questions, or feature requests, please contact your Encatch account manager or visit the [Encatch Dashboard](https://app.encatch.com).