@clianta/sdk 1.6.0 → 1.6.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.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Clianta SDK v1.6.0
2
+ * Clianta SDK v1.6.2
3
3
  * (c) 2026 Clianta
4
4
  * Released under the MIT License.
5
5
  */
@@ -10,7 +10,7 @@
10
10
  * @see SDK_VERSION in core/config.ts
11
11
  */
12
12
  /** SDK Version */
13
- const SDK_VERSION = '1.6.0';
13
+ const SDK_VERSION = '1.6.2';
14
14
  /** Default API endpoint — reads from env or falls back to localhost */
15
15
  const getDefaultApiEndpoint = () => {
16
16
  // Build-time env var (works with Next.js, Vite, CRA, etc.)
@@ -28,7 +28,7 @@ const getDefaultApiEndpoint = () => {
28
28
  }
29
29
  return 'http://localhost:5000';
30
30
  };
31
- /** Core plugins enabled by default */
31
+ /** Core plugins enabled by default — all auto-track with zero config */
32
32
  const DEFAULT_PLUGINS = [
33
33
  'pageView',
34
34
  'forms',
@@ -37,13 +37,13 @@ const DEFAULT_PLUGINS = [
37
37
  'engagement',
38
38
  'downloads',
39
39
  'exitIntent',
40
+ 'errors',
41
+ 'performance',
40
42
  ];
41
43
  /** Default configuration values */
42
44
  const DEFAULT_CONFIG = {
43
45
  projectId: '',
44
46
  apiEndpoint: getDefaultApiEndpoint(),
45
- authToken: '',
46
- apiKey: '',
47
47
  debug: false,
48
48
  autoPageView: true,
49
49
  plugins: DEFAULT_PLUGINS,
@@ -2509,1268 +2509,6 @@ class ConsentManager {
2509
2509
  }
2510
2510
  }
2511
2511
 
2512
- /**
2513
- * Clianta SDK - Event Triggers Manager
2514
- * Manages event-driven automation and email notifications
2515
- */
2516
- /**
2517
- * Event Triggers Manager
2518
- * Handles event-driven automation based on CRM actions
2519
- *
2520
- * Similar to:
2521
- * - Salesforce: Process Builder, Flow Automation
2522
- * - HubSpot: Workflows, Email Sequences
2523
- * - Pipedrive: Workflow Automation
2524
- */
2525
- class EventTriggersManager {
2526
- constructor(apiEndpoint, workspaceId, authToken) {
2527
- this.triggers = new Map();
2528
- this.listeners = new Map();
2529
- this.apiEndpoint = apiEndpoint;
2530
- this.workspaceId = workspaceId;
2531
- this.authToken = authToken;
2532
- }
2533
- /**
2534
- * Set authentication token
2535
- */
2536
- setAuthToken(token) {
2537
- this.authToken = token;
2538
- }
2539
- /**
2540
- * Make authenticated API request
2541
- */
2542
- async request(endpoint, options = {}) {
2543
- const url = `${this.apiEndpoint}${endpoint}`;
2544
- const headers = {
2545
- 'Content-Type': 'application/json',
2546
- ...(options.headers || {}),
2547
- };
2548
- if (this.authToken) {
2549
- headers['Authorization'] = `Bearer ${this.authToken}`;
2550
- }
2551
- try {
2552
- const response = await fetch(url, {
2553
- ...options,
2554
- headers,
2555
- });
2556
- const data = await response.json();
2557
- if (!response.ok) {
2558
- return {
2559
- success: false,
2560
- error: data.message || 'Request failed',
2561
- status: response.status,
2562
- };
2563
- }
2564
- return {
2565
- success: true,
2566
- data: data.data || data,
2567
- status: response.status,
2568
- };
2569
- }
2570
- catch (error) {
2571
- return {
2572
- success: false,
2573
- error: error instanceof Error ? error.message : 'Network error',
2574
- status: 0,
2575
- };
2576
- }
2577
- }
2578
- // ============================================
2579
- // TRIGGER MANAGEMENT
2580
- // ============================================
2581
- /**
2582
- * Get all event triggers
2583
- */
2584
- async getTriggers() {
2585
- return this.request(`/api/workspaces/${this.workspaceId}/triggers`);
2586
- }
2587
- /**
2588
- * Get a single trigger by ID
2589
- */
2590
- async getTrigger(triggerId) {
2591
- return this.request(`/api/workspaces/${this.workspaceId}/triggers/${triggerId}`);
2592
- }
2593
- /**
2594
- * Create a new event trigger
2595
- */
2596
- async createTrigger(trigger) {
2597
- const result = await this.request(`/api/workspaces/${this.workspaceId}/triggers`, {
2598
- method: 'POST',
2599
- body: JSON.stringify(trigger),
2600
- });
2601
- // Cache the trigger locally if successful
2602
- if (result.success && result.data?._id) {
2603
- this.triggers.set(result.data._id, result.data);
2604
- }
2605
- return result;
2606
- }
2607
- /**
2608
- * Update an existing trigger
2609
- */
2610
- async updateTrigger(triggerId, updates) {
2611
- const result = await this.request(`/api/workspaces/${this.workspaceId}/triggers/${triggerId}`, {
2612
- method: 'PUT',
2613
- body: JSON.stringify(updates),
2614
- });
2615
- // Update cache if successful
2616
- if (result.success && result.data?._id) {
2617
- this.triggers.set(result.data._id, result.data);
2618
- }
2619
- return result;
2620
- }
2621
- /**
2622
- * Delete a trigger
2623
- */
2624
- async deleteTrigger(triggerId) {
2625
- const result = await this.request(`/api/workspaces/${this.workspaceId}/triggers/${triggerId}`, {
2626
- method: 'DELETE',
2627
- });
2628
- // Remove from cache if successful
2629
- if (result.success) {
2630
- this.triggers.delete(triggerId);
2631
- }
2632
- return result;
2633
- }
2634
- /**
2635
- * Activate a trigger
2636
- */
2637
- async activateTrigger(triggerId) {
2638
- return this.updateTrigger(triggerId, { isActive: true });
2639
- }
2640
- /**
2641
- * Deactivate a trigger
2642
- */
2643
- async deactivateTrigger(triggerId) {
2644
- return this.updateTrigger(triggerId, { isActive: false });
2645
- }
2646
- // ============================================
2647
- // EVENT HANDLING (CLIENT-SIDE)
2648
- // ============================================
2649
- /**
2650
- * Register a local event listener for client-side triggers
2651
- * This allows immediate client-side reactions to events
2652
- */
2653
- on(eventType, callback) {
2654
- if (!this.listeners.has(eventType)) {
2655
- this.listeners.set(eventType, new Set());
2656
- }
2657
- this.listeners.get(eventType).add(callback);
2658
- logger.debug(`Event listener registered: ${eventType}`);
2659
- }
2660
- /**
2661
- * Remove an event listener
2662
- */
2663
- off(eventType, callback) {
2664
- const listeners = this.listeners.get(eventType);
2665
- if (listeners) {
2666
- listeners.delete(callback);
2667
- }
2668
- }
2669
- /**
2670
- * Emit an event (client-side only)
2671
- * This will trigger any registered local listeners
2672
- */
2673
- emit(eventType, data) {
2674
- logger.debug(`Event emitted: ${eventType}`, data);
2675
- const listeners = this.listeners.get(eventType);
2676
- if (listeners) {
2677
- listeners.forEach(callback => {
2678
- try {
2679
- callback(data);
2680
- }
2681
- catch (error) {
2682
- logger.error(`Error in event listener for ${eventType}:`, error);
2683
- }
2684
- });
2685
- }
2686
- }
2687
- /**
2688
- * Check if conditions are met for a trigger
2689
- * Supports dynamic field evaluation including custom fields and nested paths
2690
- */
2691
- evaluateConditions(conditions, data) {
2692
- if (!conditions || conditions.length === 0) {
2693
- return true; // No conditions means always fire
2694
- }
2695
- return conditions.every(condition => {
2696
- // Support dot notation for nested fields (e.g., 'customFields.industry')
2697
- const fieldValue = condition.field.includes('.')
2698
- ? this.getNestedValue(data, condition.field)
2699
- : data[condition.field];
2700
- const targetValue = condition.value;
2701
- switch (condition.operator) {
2702
- case 'equals':
2703
- return fieldValue === targetValue;
2704
- case 'not_equals':
2705
- return fieldValue !== targetValue;
2706
- case 'contains':
2707
- return String(fieldValue).includes(String(targetValue));
2708
- case 'greater_than':
2709
- return Number(fieldValue) > Number(targetValue);
2710
- case 'less_than':
2711
- return Number(fieldValue) < Number(targetValue);
2712
- case 'in':
2713
- return Array.isArray(targetValue) && targetValue.includes(fieldValue);
2714
- case 'not_in':
2715
- return Array.isArray(targetValue) && !targetValue.includes(fieldValue);
2716
- default:
2717
- return false;
2718
- }
2719
- });
2720
- }
2721
- /**
2722
- * Execute actions for a triggered event (client-side preview)
2723
- * Note: Actual execution happens on the backend
2724
- */
2725
- async executeActions(trigger, data) {
2726
- logger.info(`Executing actions for trigger: ${trigger.name}`);
2727
- for (const action of trigger.actions) {
2728
- try {
2729
- await this.executeAction(action, data);
2730
- }
2731
- catch (error) {
2732
- logger.error(`Failed to execute action:`, error);
2733
- }
2734
- }
2735
- }
2736
- /**
2737
- * Execute a single action
2738
- */
2739
- async executeAction(action, data) {
2740
- switch (action.type) {
2741
- case 'send_email':
2742
- await this.executeSendEmail(action, data);
2743
- break;
2744
- case 'webhook':
2745
- await this.executeWebhook(action, data);
2746
- break;
2747
- case 'create_task':
2748
- await this.executeCreateTask(action, data);
2749
- break;
2750
- case 'update_contact':
2751
- await this.executeUpdateContact(action, data);
2752
- break;
2753
- default:
2754
- logger.warn(`Unknown action type:`, action);
2755
- }
2756
- }
2757
- /**
2758
- * Execute send email action (via backend API)
2759
- */
2760
- async executeSendEmail(action, data) {
2761
- logger.debug('Sending email:', action);
2762
- const payload = {
2763
- to: this.replaceVariables(action.to, data),
2764
- subject: action.subject ? this.replaceVariables(action.subject, data) : undefined,
2765
- body: action.body ? this.replaceVariables(action.body, data) : undefined,
2766
- templateId: action.templateId,
2767
- cc: action.cc,
2768
- bcc: action.bcc,
2769
- from: action.from,
2770
- delayMinutes: action.delayMinutes,
2771
- };
2772
- await this.request(`/api/workspaces/${this.workspaceId}/emails/send`, {
2773
- method: 'POST',
2774
- body: JSON.stringify(payload),
2775
- });
2776
- }
2777
- /**
2778
- * Execute webhook action
2779
- */
2780
- async executeWebhook(action, data) {
2781
- logger.debug('Calling webhook:', action.url);
2782
- const body = action.body ? this.replaceVariables(action.body, data) : JSON.stringify(data);
2783
- await fetch(action.url, {
2784
- method: action.method,
2785
- headers: {
2786
- 'Content-Type': 'application/json',
2787
- ...action.headers,
2788
- },
2789
- body,
2790
- });
2791
- }
2792
- /**
2793
- * Execute create task action
2794
- */
2795
- async executeCreateTask(action, data) {
2796
- logger.debug('Creating task:', action.title);
2797
- const dueDate = action.dueDays
2798
- ? new Date(Date.now() + action.dueDays * 24 * 60 * 60 * 1000).toISOString()
2799
- : undefined;
2800
- await this.request(`/api/workspaces/${this.workspaceId}/tasks`, {
2801
- method: 'POST',
2802
- body: JSON.stringify({
2803
- title: this.replaceVariables(action.title, data),
2804
- description: action.description ? this.replaceVariables(action.description, data) : undefined,
2805
- priority: action.priority,
2806
- dueDate,
2807
- assignedTo: action.assignedTo,
2808
- relatedContactId: typeof data.contactId === 'string' ? data.contactId : undefined,
2809
- }),
2810
- });
2811
- }
2812
- /**
2813
- * Execute update contact action
2814
- */
2815
- async executeUpdateContact(action, data) {
2816
- const contactId = data.contactId || data._id;
2817
- if (!contactId) {
2818
- logger.warn('Cannot update contact: no contactId in data');
2819
- return;
2820
- }
2821
- logger.debug('Updating contact:', contactId);
2822
- await this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}`, {
2823
- method: 'PUT',
2824
- body: JSON.stringify(action.updates),
2825
- });
2826
- }
2827
- /**
2828
- * Replace variables in a string template
2829
- * Supports syntax like {{contact.email}}, {{opportunity.value}}
2830
- */
2831
- replaceVariables(template, data) {
2832
- return template.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
2833
- const value = this.getNestedValue(data, path.trim());
2834
- return value !== undefined ? String(value) : match;
2835
- });
2836
- }
2837
- /**
2838
- * Get nested value from object using dot notation
2839
- * Supports dynamic field access including custom fields
2840
- */
2841
- getNestedValue(obj, path) {
2842
- return path.split('.').reduce((current, key) => {
2843
- return current !== null && current !== undefined && typeof current === 'object'
2844
- ? current[key]
2845
- : undefined;
2846
- }, obj);
2847
- }
2848
- /**
2849
- * Extract all available field paths from a data object
2850
- * Useful for dynamic field discovery based on platform-specific attributes
2851
- * @param obj - The data object to extract fields from
2852
- * @param prefix - Internal use for nested paths
2853
- * @param maxDepth - Maximum depth to traverse (default: 3)
2854
- * @returns Array of field paths (e.g., ['email', 'contact.firstName', 'customFields.industry'])
2855
- */
2856
- extractAvailableFields(obj, prefix = '', maxDepth = 3) {
2857
- if (maxDepth <= 0)
2858
- return [];
2859
- const fields = [];
2860
- for (const key in obj) {
2861
- if (!obj.hasOwnProperty(key))
2862
- continue;
2863
- const value = obj[key];
2864
- const fieldPath = prefix ? `${prefix}.${key}` : key;
2865
- fields.push(fieldPath);
2866
- // Recursively traverse nested objects
2867
- if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
2868
- const nestedFields = this.extractAvailableFields(value, fieldPath, maxDepth - 1);
2869
- fields.push(...nestedFields);
2870
- }
2871
- }
2872
- return fields;
2873
- }
2874
- /**
2875
- * Get available fields from sample data
2876
- * Helps with dynamic field detection for platform-specific attributes
2877
- * @param sampleData - Sample data object to analyze
2878
- * @returns Array of available field paths
2879
- */
2880
- getAvailableFields(sampleData) {
2881
- return this.extractAvailableFields(sampleData);
2882
- }
2883
- // ============================================
2884
- // HELPER METHODS FOR COMMON PATTERNS
2885
- // ============================================
2886
- /**
2887
- * Create a simple email trigger
2888
- * Helper method for common use case
2889
- */
2890
- async createEmailTrigger(config) {
2891
- return this.createTrigger({
2892
- name: config.name,
2893
- eventType: config.eventType,
2894
- conditions: config.conditions,
2895
- actions: [
2896
- {
2897
- type: 'send_email',
2898
- to: config.to,
2899
- subject: config.subject,
2900
- body: config.body,
2901
- },
2902
- ],
2903
- isActive: true,
2904
- });
2905
- }
2906
- /**
2907
- * Create a task creation trigger
2908
- */
2909
- async createTaskTrigger(config) {
2910
- return this.createTrigger({
2911
- name: config.name,
2912
- eventType: config.eventType,
2913
- conditions: config.conditions,
2914
- actions: [
2915
- {
2916
- type: 'create_task',
2917
- title: config.taskTitle,
2918
- description: config.taskDescription,
2919
- priority: config.priority,
2920
- dueDays: config.dueDays,
2921
- },
2922
- ],
2923
- isActive: true,
2924
- });
2925
- }
2926
- /**
2927
- * Create a webhook trigger
2928
- */
2929
- async createWebhookTrigger(config) {
2930
- return this.createTrigger({
2931
- name: config.name,
2932
- eventType: config.eventType,
2933
- conditions: config.conditions,
2934
- actions: [
2935
- {
2936
- type: 'webhook',
2937
- url: config.webhookUrl,
2938
- method: config.method || 'POST',
2939
- },
2940
- ],
2941
- isActive: true,
2942
- });
2943
- }
2944
- }
2945
-
2946
- /**
2947
- * Clianta SDK - CRM API Client
2948
- * @see SDK_VERSION in core/config.ts
2949
- */
2950
- /**
2951
- * CRM API Client for managing contacts and opportunities
2952
- */
2953
- class CRMClient {
2954
- constructor(apiEndpoint, workspaceId, authToken, apiKey) {
2955
- this.apiEndpoint = apiEndpoint;
2956
- this.workspaceId = workspaceId;
2957
- this.authToken = authToken;
2958
- this.apiKey = apiKey;
2959
- this.triggers = new EventTriggersManager(apiEndpoint, workspaceId, authToken);
2960
- }
2961
- /**
2962
- * Set authentication token for API requests (user JWT)
2963
- */
2964
- setAuthToken(token) {
2965
- this.authToken = token;
2966
- this.apiKey = undefined;
2967
- this.triggers.setAuthToken(token);
2968
- }
2969
- /**
2970
- * Set workspace API key for server-to-server requests.
2971
- * Use this instead of setAuthToken when integrating from an external app.
2972
- */
2973
- setApiKey(key) {
2974
- this.apiKey = key;
2975
- this.authToken = undefined;
2976
- }
2977
- /**
2978
- * Validate required parameter exists
2979
- * @throws {Error} if value is null/undefined or empty string
2980
- */
2981
- validateRequired(param, value, methodName) {
2982
- if (value === null || value === undefined || value === '') {
2983
- throw new Error(`[CRMClient.${methodName}] ${param} is required`);
2984
- }
2985
- }
2986
- /**
2987
- * Make authenticated API request
2988
- */
2989
- async request(endpoint, options = {}) {
2990
- const url = `${this.apiEndpoint}${endpoint}`;
2991
- const headers = {
2992
- 'Content-Type': 'application/json',
2993
- ...(options.headers || {}),
2994
- };
2995
- if (this.apiKey) {
2996
- headers['X-Api-Key'] = this.apiKey;
2997
- }
2998
- else if (this.authToken) {
2999
- headers['Authorization'] = `Bearer ${this.authToken}`;
3000
- }
3001
- try {
3002
- const response = await fetch(url, {
3003
- ...options,
3004
- headers,
3005
- });
3006
- const data = await response.json();
3007
- if (!response.ok) {
3008
- return {
3009
- success: false,
3010
- error: data.message || 'Request failed',
3011
- status: response.status,
3012
- };
3013
- }
3014
- return {
3015
- success: true,
3016
- data: data.data || data,
3017
- status: response.status,
3018
- };
3019
- }
3020
- catch (error) {
3021
- return {
3022
- success: false,
3023
- error: error instanceof Error ? error.message : 'Network error',
3024
- status: 0,
3025
- };
3026
- }
3027
- }
3028
- // ============================================
3029
- // INBOUND EVENTS API (API-key authenticated)
3030
- // ============================================
3031
- /**
3032
- * Send an inbound event from an external app (e.g. user signup on client website).
3033
- * Requires the client to be initialized with an API key via setApiKey() or the constructor.
3034
- *
3035
- * The contact is upserted in the CRM and matching workflow automations fire automatically.
3036
- *
3037
- * @example
3038
- * const crm = new CRMClient('http://localhost:5000', 'WORKSPACE_ID');
3039
- * crm.setApiKey('mm_live_...');
3040
- *
3041
- * await crm.sendEvent({
3042
- * event: 'user.registered',
3043
- * contact: { email: 'alice@example.com', firstName: 'Alice' },
3044
- * data: { plan: 'free', signupSource: 'homepage' },
3045
- * });
3046
- */
3047
- async sendEvent(payload) {
3048
- const url = `${this.apiEndpoint}/api/public/events`;
3049
- const headers = { 'Content-Type': 'application/json' };
3050
- if (this.apiKey) {
3051
- headers['X-Api-Key'] = this.apiKey;
3052
- }
3053
- else if (this.authToken) {
3054
- headers['Authorization'] = `Bearer ${this.authToken}`;
3055
- }
3056
- try {
3057
- const response = await fetch(url, {
3058
- method: 'POST',
3059
- headers,
3060
- body: JSON.stringify(payload),
3061
- });
3062
- const data = await response.json();
3063
- if (!response.ok) {
3064
- return {
3065
- success: false,
3066
- contactCreated: false,
3067
- event: payload.event,
3068
- error: data.error || 'Request failed',
3069
- };
3070
- }
3071
- return {
3072
- success: data.success,
3073
- contactCreated: data.contactCreated,
3074
- contactId: data.contactId,
3075
- event: data.event,
3076
- };
3077
- }
3078
- catch (error) {
3079
- return {
3080
- success: false,
3081
- contactCreated: false,
3082
- event: payload.event,
3083
- error: error instanceof Error ? error.message : 'Network error',
3084
- };
3085
- }
3086
- }
3087
- // ============================================
3088
- // CONTACTS API
3089
- // ============================================
3090
- /**
3091
- * Get all contacts with pagination
3092
- */
3093
- async getContacts(params) {
3094
- const queryParams = new URLSearchParams();
3095
- if (params?.page)
3096
- queryParams.set('page', params.page.toString());
3097
- if (params?.limit)
3098
- queryParams.set('limit', params.limit.toString());
3099
- if (params?.search)
3100
- queryParams.set('search', params.search);
3101
- if (params?.status)
3102
- queryParams.set('status', params.status);
3103
- const query = queryParams.toString();
3104
- const endpoint = `/api/workspaces/${this.workspaceId}/contacts${query ? `?${query}` : ''}`;
3105
- return this.request(endpoint);
3106
- }
3107
- /**
3108
- * Get a single contact by ID
3109
- */
3110
- async getContact(contactId) {
3111
- this.validateRequired('contactId', contactId, 'getContact');
3112
- return this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}`);
3113
- }
3114
- /**
3115
- * Create a new contact
3116
- */
3117
- async createContact(contact) {
3118
- return this.request(`/api/workspaces/${this.workspaceId}/contacts`, {
3119
- method: 'POST',
3120
- body: JSON.stringify(contact),
3121
- });
3122
- }
3123
- /**
3124
- * Update an existing contact
3125
- */
3126
- async updateContact(contactId, updates) {
3127
- this.validateRequired('contactId', contactId, 'updateContact');
3128
- return this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}`, {
3129
- method: 'PUT',
3130
- body: JSON.stringify(updates),
3131
- });
3132
- }
3133
- /**
3134
- * Delete a contact
3135
- */
3136
- async deleteContact(contactId) {
3137
- this.validateRequired('contactId', contactId, 'deleteContact');
3138
- return this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}`, {
3139
- method: 'DELETE',
3140
- });
3141
- }
3142
- // ============================================
3143
- // OPPORTUNITIES API
3144
- // ============================================
3145
- /**
3146
- * Get all opportunities with pagination
3147
- */
3148
- async getOpportunities(params) {
3149
- const queryParams = new URLSearchParams();
3150
- if (params?.page)
3151
- queryParams.set('page', params.page.toString());
3152
- if (params?.limit)
3153
- queryParams.set('limit', params.limit.toString());
3154
- if (params?.pipelineId)
3155
- queryParams.set('pipelineId', params.pipelineId);
3156
- if (params?.stageId)
3157
- queryParams.set('stageId', params.stageId);
3158
- const query = queryParams.toString();
3159
- const endpoint = `/api/workspaces/${this.workspaceId}/opportunities${query ? `?${query}` : ''}`;
3160
- return this.request(endpoint);
3161
- }
3162
- /**
3163
- * Get a single opportunity by ID
3164
- */
3165
- async getOpportunity(opportunityId) {
3166
- return this.request(`/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}`);
3167
- }
3168
- /**
3169
- * Create a new opportunity
3170
- */
3171
- async createOpportunity(opportunity) {
3172
- return this.request(`/api/workspaces/${this.workspaceId}/opportunities`, {
3173
- method: 'POST',
3174
- body: JSON.stringify(opportunity),
3175
- });
3176
- }
3177
- /**
3178
- * Update an existing opportunity
3179
- */
3180
- async updateOpportunity(opportunityId, updates) {
3181
- return this.request(`/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}`, {
3182
- method: 'PUT',
3183
- body: JSON.stringify(updates),
3184
- });
3185
- }
3186
- /**
3187
- * Delete an opportunity
3188
- */
3189
- async deleteOpportunity(opportunityId) {
3190
- return this.request(`/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}`, {
3191
- method: 'DELETE',
3192
- });
3193
- }
3194
- /**
3195
- * Move opportunity to a different stage
3196
- */
3197
- async moveOpportunity(opportunityId, stageId) {
3198
- return this.request(`/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}/move`, {
3199
- method: 'POST',
3200
- body: JSON.stringify({ stageId }),
3201
- });
3202
- }
3203
- // ============================================
3204
- // COMPANIES API
3205
- // ============================================
3206
- /**
3207
- * Get all companies with pagination
3208
- */
3209
- async getCompanies(params) {
3210
- const queryParams = new URLSearchParams();
3211
- if (params?.page)
3212
- queryParams.set('page', params.page.toString());
3213
- if (params?.limit)
3214
- queryParams.set('limit', params.limit.toString());
3215
- if (params?.search)
3216
- queryParams.set('search', params.search);
3217
- if (params?.status)
3218
- queryParams.set('status', params.status);
3219
- if (params?.industry)
3220
- queryParams.set('industry', params.industry);
3221
- const query = queryParams.toString();
3222
- const endpoint = `/api/workspaces/${this.workspaceId}/companies${query ? `?${query}` : ''}`;
3223
- return this.request(endpoint);
3224
- }
3225
- /**
3226
- * Get a single company by ID
3227
- */
3228
- async getCompany(companyId) {
3229
- return this.request(`/api/workspaces/${this.workspaceId}/companies/${companyId}`);
3230
- }
3231
- /**
3232
- * Create a new company
3233
- */
3234
- async createCompany(company) {
3235
- return this.request(`/api/workspaces/${this.workspaceId}/companies`, {
3236
- method: 'POST',
3237
- body: JSON.stringify(company),
3238
- });
3239
- }
3240
- /**
3241
- * Update an existing company
3242
- */
3243
- async updateCompany(companyId, updates) {
3244
- return this.request(`/api/workspaces/${this.workspaceId}/companies/${companyId}`, {
3245
- method: 'PUT',
3246
- body: JSON.stringify(updates),
3247
- });
3248
- }
3249
- /**
3250
- * Delete a company
3251
- */
3252
- async deleteCompany(companyId) {
3253
- return this.request(`/api/workspaces/${this.workspaceId}/companies/${companyId}`, {
3254
- method: 'DELETE',
3255
- });
3256
- }
3257
- /**
3258
- * Get contacts belonging to a company
3259
- */
3260
- async getCompanyContacts(companyId, params) {
3261
- const queryParams = new URLSearchParams();
3262
- if (params?.page)
3263
- queryParams.set('page', params.page.toString());
3264
- if (params?.limit)
3265
- queryParams.set('limit', params.limit.toString());
3266
- const query = queryParams.toString();
3267
- const endpoint = `/api/workspaces/${this.workspaceId}/companies/${companyId}/contacts${query ? `?${query}` : ''}`;
3268
- return this.request(endpoint);
3269
- }
3270
- /**
3271
- * Get deals/opportunities belonging to a company
3272
- */
3273
- async getCompanyDeals(companyId, params) {
3274
- const queryParams = new URLSearchParams();
3275
- if (params?.page)
3276
- queryParams.set('page', params.page.toString());
3277
- if (params?.limit)
3278
- queryParams.set('limit', params.limit.toString());
3279
- const query = queryParams.toString();
3280
- const endpoint = `/api/workspaces/${this.workspaceId}/companies/${companyId}/deals${query ? `?${query}` : ''}`;
3281
- return this.request(endpoint);
3282
- }
3283
- // ============================================
3284
- // PIPELINES API
3285
- // ============================================
3286
- /**
3287
- * Get all pipelines
3288
- */
3289
- async getPipelines() {
3290
- return this.request(`/api/workspaces/${this.workspaceId}/pipelines`);
3291
- }
3292
- /**
3293
- * Get a single pipeline by ID
3294
- */
3295
- async getPipeline(pipelineId) {
3296
- return this.request(`/api/workspaces/${this.workspaceId}/pipelines/${pipelineId}`);
3297
- }
3298
- /**
3299
- * Create a new pipeline
3300
- */
3301
- async createPipeline(pipeline) {
3302
- return this.request(`/api/workspaces/${this.workspaceId}/pipelines`, {
3303
- method: 'POST',
3304
- body: JSON.stringify(pipeline),
3305
- });
3306
- }
3307
- /**
3308
- * Update an existing pipeline
3309
- */
3310
- async updatePipeline(pipelineId, updates) {
3311
- return this.request(`/api/workspaces/${this.workspaceId}/pipelines/${pipelineId}`, {
3312
- method: 'PUT',
3313
- body: JSON.stringify(updates),
3314
- });
3315
- }
3316
- /**
3317
- * Delete a pipeline
3318
- */
3319
- async deletePipeline(pipelineId) {
3320
- return this.request(`/api/workspaces/${this.workspaceId}/pipelines/${pipelineId}`, {
3321
- method: 'DELETE',
3322
- });
3323
- }
3324
- // ============================================
3325
- // TASKS API
3326
- // ============================================
3327
- /**
3328
- * Get all tasks with pagination
3329
- */
3330
- async getTasks(params) {
3331
- const queryParams = new URLSearchParams();
3332
- if (params?.page)
3333
- queryParams.set('page', params.page.toString());
3334
- if (params?.limit)
3335
- queryParams.set('limit', params.limit.toString());
3336
- if (params?.status)
3337
- queryParams.set('status', params.status);
3338
- if (params?.priority)
3339
- queryParams.set('priority', params.priority);
3340
- if (params?.contactId)
3341
- queryParams.set('contactId', params.contactId);
3342
- if (params?.companyId)
3343
- queryParams.set('companyId', params.companyId);
3344
- if (params?.opportunityId)
3345
- queryParams.set('opportunityId', params.opportunityId);
3346
- const query = queryParams.toString();
3347
- const endpoint = `/api/workspaces/${this.workspaceId}/tasks${query ? `?${query}` : ''}`;
3348
- return this.request(endpoint);
3349
- }
3350
- /**
3351
- * Get a single task by ID
3352
- */
3353
- async getTask(taskId) {
3354
- return this.request(`/api/workspaces/${this.workspaceId}/tasks/${taskId}`);
3355
- }
3356
- /**
3357
- * Create a new task
3358
- */
3359
- async createTask(task) {
3360
- return this.request(`/api/workspaces/${this.workspaceId}/tasks`, {
3361
- method: 'POST',
3362
- body: JSON.stringify(task),
3363
- });
3364
- }
3365
- /**
3366
- * Update an existing task
3367
- */
3368
- async updateTask(taskId, updates) {
3369
- return this.request(`/api/workspaces/${this.workspaceId}/tasks/${taskId}`, {
3370
- method: 'PUT',
3371
- body: JSON.stringify(updates),
3372
- });
3373
- }
3374
- /**
3375
- * Mark a task as completed
3376
- */
3377
- async completeTask(taskId) {
3378
- return this.request(`/api/workspaces/${this.workspaceId}/tasks/${taskId}/complete`, {
3379
- method: 'PATCH',
3380
- });
3381
- }
3382
- /**
3383
- * Delete a task
3384
- */
3385
- async deleteTask(taskId) {
3386
- return this.request(`/api/workspaces/${this.workspaceId}/tasks/${taskId}`, {
3387
- method: 'DELETE',
3388
- });
3389
- }
3390
- // ============================================
3391
- // ACTIVITIES API
3392
- // ============================================
3393
- /**
3394
- * Get activities for a contact
3395
- */
3396
- async getContactActivities(contactId, params) {
3397
- const queryParams = new URLSearchParams();
3398
- if (params?.page)
3399
- queryParams.set('page', params.page.toString());
3400
- if (params?.limit)
3401
- queryParams.set('limit', params.limit.toString());
3402
- if (params?.type)
3403
- queryParams.set('type', params.type);
3404
- const query = queryParams.toString();
3405
- const endpoint = `/api/workspaces/${this.workspaceId}/contacts/${contactId}/activities${query ? `?${query}` : ''}`;
3406
- return this.request(endpoint);
3407
- }
3408
- /**
3409
- * Get activities for an opportunity/deal
3410
- */
3411
- async getOpportunityActivities(opportunityId, params) {
3412
- const queryParams = new URLSearchParams();
3413
- if (params?.page)
3414
- queryParams.set('page', params.page.toString());
3415
- if (params?.limit)
3416
- queryParams.set('limit', params.limit.toString());
3417
- if (params?.type)
3418
- queryParams.set('type', params.type);
3419
- const query = queryParams.toString();
3420
- const endpoint = `/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}/activities${query ? `?${query}` : ''}`;
3421
- return this.request(endpoint);
3422
- }
3423
- /**
3424
- * Create a new activity
3425
- */
3426
- async createActivity(activity) {
3427
- // Determine the correct endpoint based on related entity
3428
- let endpoint;
3429
- if (activity.opportunityId) {
3430
- endpoint = `/api/workspaces/${this.workspaceId}/opportunities/${activity.opportunityId}/activities`;
3431
- }
3432
- else if (activity.contactId) {
3433
- endpoint = `/api/workspaces/${this.workspaceId}/contacts/${activity.contactId}/activities`;
3434
- }
3435
- else {
3436
- endpoint = `/api/workspaces/${this.workspaceId}/activities`;
3437
- }
3438
- return this.request(endpoint, {
3439
- method: 'POST',
3440
- body: JSON.stringify(activity),
3441
- });
3442
- }
3443
- /**
3444
- * Update an existing activity
3445
- */
3446
- async updateActivity(activityId, updates) {
3447
- return this.request(`/api/workspaces/${this.workspaceId}/activities/${activityId}`, {
3448
- method: 'PATCH',
3449
- body: JSON.stringify(updates),
3450
- });
3451
- }
3452
- /**
3453
- * Delete an activity
3454
- */
3455
- async deleteActivity(activityId) {
3456
- return this.request(`/api/workspaces/${this.workspaceId}/activities/${activityId}`, {
3457
- method: 'DELETE',
3458
- });
3459
- }
3460
- /**
3461
- * Log a call activity
3462
- */
3463
- async logCall(data) {
3464
- return this.createActivity({
3465
- type: 'call',
3466
- title: `${data.direction === 'inbound' ? 'Inbound' : 'Outbound'} Call`,
3467
- direction: data.direction,
3468
- duration: data.duration,
3469
- outcome: data.outcome,
3470
- description: data.notes,
3471
- contactId: data.contactId,
3472
- opportunityId: data.opportunityId,
3473
- });
3474
- }
3475
- /**
3476
- * Log a meeting activity
3477
- */
3478
- async logMeeting(data) {
3479
- return this.createActivity({
3480
- type: 'meeting',
3481
- title: data.title,
3482
- duration: data.duration,
3483
- outcome: data.outcome,
3484
- description: data.notes,
3485
- contactId: data.contactId,
3486
- opportunityId: data.opportunityId,
3487
- });
3488
- }
3489
- /**
3490
- * Add a note to a contact or opportunity
3491
- */
3492
- async addNote(data) {
3493
- return this.createActivity({
3494
- type: 'note',
3495
- title: 'Note',
3496
- description: data.content,
3497
- contactId: data.contactId,
3498
- opportunityId: data.opportunityId,
3499
- });
3500
- }
3501
- // ============================================
3502
- // EMAIL TEMPLATES API
3503
- // ============================================
3504
- /**
3505
- * Get all email templates
3506
- */
3507
- async getEmailTemplates(params) {
3508
- const queryParams = new URLSearchParams();
3509
- if (params?.page)
3510
- queryParams.set('page', params.page.toString());
3511
- if (params?.limit)
3512
- queryParams.set('limit', params.limit.toString());
3513
- const query = queryParams.toString();
3514
- const endpoint = `/api/workspaces/${this.workspaceId}/email-templates${query ? `?${query}` : ''}`;
3515
- return this.request(endpoint);
3516
- }
3517
- /**
3518
- * Get a single email template by ID
3519
- */
3520
- async getEmailTemplate(templateId) {
3521
- return this.request(`/api/workspaces/${this.workspaceId}/email-templates/${templateId}`);
3522
- }
3523
- /**
3524
- * Create a new email template
3525
- */
3526
- async createEmailTemplate(template) {
3527
- return this.request(`/api/workspaces/${this.workspaceId}/email-templates`, {
3528
- method: 'POST',
3529
- body: JSON.stringify(template),
3530
- });
3531
- }
3532
- /**
3533
- * Update an email template
3534
- */
3535
- async updateEmailTemplate(templateId, updates) {
3536
- return this.request(`/api/workspaces/${this.workspaceId}/email-templates/${templateId}`, {
3537
- method: 'PUT',
3538
- body: JSON.stringify(updates),
3539
- });
3540
- }
3541
- /**
3542
- * Delete an email template
3543
- */
3544
- async deleteEmailTemplate(templateId) {
3545
- return this.request(`/api/workspaces/${this.workspaceId}/email-templates/${templateId}`, {
3546
- method: 'DELETE',
3547
- });
3548
- }
3549
- /**
3550
- * Send an email using a template
3551
- */
3552
- async sendEmail(data) {
3553
- return this.request(`/api/workspaces/${this.workspaceId}/emails/send`, {
3554
- method: 'POST',
3555
- body: JSON.stringify(data),
3556
- });
3557
- }
3558
- // ============================================
3559
- // READ-BACK / DATA RETRIEVAL API
3560
- // ============================================
3561
- /**
3562
- * Get a contact by email address.
3563
- * Returns the first matching contact from a search query.
3564
- */
3565
- async getContactByEmail(email) {
3566
- this.validateRequired('email', email, 'getContactByEmail');
3567
- const queryParams = new URLSearchParams({ search: email, limit: '1' });
3568
- return this.request(`/api/workspaces/${this.workspaceId}/contacts?${queryParams.toString()}`);
3569
- }
3570
- /**
3571
- * Get activity timeline for a contact
3572
- */
3573
- async getContactActivity(contactId, params) {
3574
- this.validateRequired('contactId', contactId, 'getContactActivity');
3575
- const queryParams = new URLSearchParams();
3576
- if (params?.page)
3577
- queryParams.set('page', params.page.toString());
3578
- if (params?.limit)
3579
- queryParams.set('limit', params.limit.toString());
3580
- if (params?.type)
3581
- queryParams.set('type', params.type);
3582
- if (params?.startDate)
3583
- queryParams.set('startDate', params.startDate);
3584
- if (params?.endDate)
3585
- queryParams.set('endDate', params.endDate);
3586
- const query = queryParams.toString();
3587
- const endpoint = `/api/workspaces/${this.workspaceId}/contacts/${contactId}/activities${query ? `?${query}` : ''}`;
3588
- return this.request(endpoint);
3589
- }
3590
- /**
3591
- * Get engagement metrics for a contact (via their linked visitor data)
3592
- */
3593
- async getContactEngagement(contactId) {
3594
- this.validateRequired('contactId', contactId, 'getContactEngagement');
3595
- return this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}/engagement`);
3596
- }
3597
- /**
3598
- * Get a full timeline for a contact including events, activities, and opportunities
3599
- */
3600
- async getContactTimeline(contactId, params) {
3601
- this.validateRequired('contactId', contactId, 'getContactTimeline');
3602
- const queryParams = new URLSearchParams();
3603
- if (params?.page)
3604
- queryParams.set('page', params.page.toString());
3605
- if (params?.limit)
3606
- queryParams.set('limit', params.limit.toString());
3607
- const query = queryParams.toString();
3608
- const endpoint = `/api/workspaces/${this.workspaceId}/contacts/${contactId}/timeline${query ? `?${query}` : ''}`;
3609
- return this.request(endpoint);
3610
- }
3611
- /**
3612
- * Search contacts with advanced filters
3613
- */
3614
- async searchContacts(query, filters) {
3615
- const queryParams = new URLSearchParams();
3616
- queryParams.set('search', query);
3617
- if (filters?.status)
3618
- queryParams.set('status', filters.status);
3619
- if (filters?.lifecycleStage)
3620
- queryParams.set('lifecycleStage', filters.lifecycleStage);
3621
- if (filters?.source)
3622
- queryParams.set('source', filters.source);
3623
- if (filters?.tags)
3624
- queryParams.set('tags', filters.tags.join(','));
3625
- if (filters?.page)
3626
- queryParams.set('page', filters.page.toString());
3627
- if (filters?.limit)
3628
- queryParams.set('limit', filters.limit.toString());
3629
- const qs = queryParams.toString();
3630
- const endpoint = `/api/workspaces/${this.workspaceId}/contacts${qs ? `?${qs}` : ''}`;
3631
- return this.request(endpoint);
3632
- }
3633
- // ============================================
3634
- // WEBHOOK MANAGEMENT API
3635
- // ============================================
3636
- /**
3637
- * List all webhook subscriptions
3638
- */
3639
- async listWebhooks(params) {
3640
- const queryParams = new URLSearchParams();
3641
- if (params?.page)
3642
- queryParams.set('page', params.page.toString());
3643
- if (params?.limit)
3644
- queryParams.set('limit', params.limit.toString());
3645
- const query = queryParams.toString();
3646
- return this.request(`/api/workspaces/${this.workspaceId}/webhooks${query ? `?${query}` : ''}`);
3647
- }
3648
- /**
3649
- * Create a new webhook subscription
3650
- */
3651
- async createWebhook(data) {
3652
- return this.request(`/api/workspaces/${this.workspaceId}/webhooks`, {
3653
- method: 'POST',
3654
- body: JSON.stringify(data),
3655
- });
3656
- }
3657
- /**
3658
- * Delete a webhook subscription
3659
- */
3660
- async deleteWebhook(webhookId) {
3661
- this.validateRequired('webhookId', webhookId, 'deleteWebhook');
3662
- return this.request(`/api/workspaces/${this.workspaceId}/webhooks/${webhookId}`, {
3663
- method: 'DELETE',
3664
- });
3665
- }
3666
- // ============================================
3667
- // EVENT TRIGGERS API (delegated to triggers manager)
3668
- // ============================================
3669
- /**
3670
- * Get all event triggers
3671
- */
3672
- async getEventTriggers() {
3673
- return this.triggers.getTriggers();
3674
- }
3675
- /**
3676
- * Create a new event trigger
3677
- */
3678
- async createEventTrigger(trigger) {
3679
- return this.triggers.createTrigger(trigger);
3680
- }
3681
- /**
3682
- * Update an event trigger
3683
- */
3684
- async updateEventTrigger(triggerId, updates) {
3685
- return this.triggers.updateTrigger(triggerId, updates);
3686
- }
3687
- /**
3688
- * Delete an event trigger
3689
- */
3690
- async deleteEventTrigger(triggerId) {
3691
- return this.triggers.deleteTrigger(triggerId);
3692
- }
3693
- }
3694
-
3695
- /**
3696
- * Privacy-safe visitor API client.
3697
- * All methods return data for the current visitor only (no cross-visitor access).
3698
- */
3699
- class VisitorClient {
3700
- constructor(transport, workspaceId, visitorId) {
3701
- this.transport = transport;
3702
- this.workspaceId = workspaceId;
3703
- this.visitorId = visitorId;
3704
- }
3705
- /** Update visitorId (e.g. after reset) */
3706
- setVisitorId(id) {
3707
- this.visitorId = id;
3708
- }
3709
- basePath() {
3710
- return `/api/public/track/visitor/${this.workspaceId}/${this.visitorId}`;
3711
- }
3712
- /**
3713
- * Get the current visitor's profile from the CRM.
3714
- * Returns visitor data and linked contact info if identified.
3715
- */
3716
- async getProfile() {
3717
- const result = await this.transport.fetchData(`${this.basePath()}/profile`);
3718
- if (result.success && result.data) {
3719
- logger.debug('Visitor profile fetched:', result.data);
3720
- return result.data;
3721
- }
3722
- logger.warn('Failed to fetch visitor profile:', result.error);
3723
- return null;
3724
- }
3725
- /**
3726
- * Get the current visitor's recent activity/events.
3727
- * Returns paginated list of tracking events.
3728
- */
3729
- async getActivity(options) {
3730
- const params = {};
3731
- if (options?.page)
3732
- params.page = options.page.toString();
3733
- if (options?.limit)
3734
- params.limit = options.limit.toString();
3735
- if (options?.eventType)
3736
- params.eventType = options.eventType;
3737
- if (options?.startDate)
3738
- params.startDate = options.startDate;
3739
- if (options?.endDate)
3740
- params.endDate = options.endDate;
3741
- const result = await this.transport.fetchData(`${this.basePath()}/activity`, params);
3742
- if (result.success && result.data) {
3743
- return result.data;
3744
- }
3745
- logger.warn('Failed to fetch visitor activity:', result.error);
3746
- return null;
3747
- }
3748
- /**
3749
- * Get a summarized journey timeline for the current visitor.
3750
- * Includes top pages, sessions, time spent, and recent activities.
3751
- */
3752
- async getTimeline() {
3753
- const result = await this.transport.fetchData(`${this.basePath()}/timeline`);
3754
- if (result.success && result.data) {
3755
- return result.data;
3756
- }
3757
- logger.warn('Failed to fetch visitor timeline:', result.error);
3758
- return null;
3759
- }
3760
- /**
3761
- * Get engagement metrics for the current visitor.
3762
- * Includes time on site, page views, bounce rate, and engagement score.
3763
- */
3764
- async getEngagement() {
3765
- const result = await this.transport.fetchData(`${this.basePath()}/engagement`);
3766
- if (result.success && result.data) {
3767
- return result.data;
3768
- }
3769
- logger.warn('Failed to fetch visitor engagement:', result.error);
3770
- return null;
3771
- }
3772
- }
3773
-
3774
2512
  /**
3775
2513
  * Clianta SDK - Main Tracker Class
3776
2514
  * @see SDK_VERSION in core/config.ts
@@ -3819,17 +2557,12 @@ class Tracker {
3819
2557
  this.visitorId = this.createVisitorId();
3820
2558
  this.sessionId = this.createSessionId();
3821
2559
  logger.debug('IDs created', { visitorId: this.visitorId, sessionId: this.sessionId });
3822
- // Initialize visitor API client
3823
- this.visitor = new VisitorClient(this.transport, this.workspaceId, this.visitorId);
3824
2560
  // Security warnings
3825
2561
  if (this.config.apiEndpoint.startsWith('http://') &&
3826
2562
  typeof window !== 'undefined' &&
3827
2563
  !window.location.hostname.includes('localhost') &&
3828
2564
  !window.location.hostname.includes('127.0.0.1')) {
3829
- logger.warn('apiEndpoint uses HTTP — events and visitor data will be sent unencrypted. Use HTTPS in production.');
3830
- }
3831
- if (this.config.apiKey && typeof window !== 'undefined') {
3832
- logger.warn('API key is exposed in client-side code. Use API keys only in server-side (Node.js) environments.');
2565
+ logger.warn('apiEndpoint uses HTTP — events will be sent unencrypted. Use HTTPS in production.');
3833
2566
  }
3834
2567
  // Initialize plugins
3835
2568
  this.initPlugins();
@@ -4025,63 +2758,6 @@ class Tracker {
4025
2758
  return null;
4026
2759
  }
4027
2760
  }
4028
- /**
4029
- * Send a server-side inbound event via the API key endpoint.
4030
- * Convenience proxy to CRMClient.sendEvent() — requires apiKey in config.
4031
- */
4032
- async sendEvent(payload) {
4033
- const apiKey = this.config.apiKey;
4034
- if (!apiKey) {
4035
- logger.error('sendEvent() requires an apiKey in the SDK config');
4036
- return { success: false, contactCreated: false, event: payload.event, error: 'No API key configured' };
4037
- }
4038
- const client = new CRMClient(this.config.apiEndpoint, this.workspaceId, undefined, apiKey);
4039
- return client.sendEvent(payload);
4040
- }
4041
- /**
4042
- * Get the current visitor's profile from the CRM.
4043
- * @deprecated Use `tracker.visitor.getProfile()` instead.
4044
- */
4045
- async getVisitorProfile() {
4046
- if (!this.isInitialized) {
4047
- logger.warn('SDK not initialized');
4048
- return null;
4049
- }
4050
- return this.visitor.getProfile();
4051
- }
4052
- /**
4053
- * Get the current visitor's recent activity/events.
4054
- * @deprecated Use `tracker.visitor.getActivity()` instead.
4055
- */
4056
- async getVisitorActivity(options) {
4057
- if (!this.isInitialized) {
4058
- logger.warn('SDK not initialized');
4059
- return null;
4060
- }
4061
- return this.visitor.getActivity(options);
4062
- }
4063
- /**
4064
- * Get a summarized journey timeline for the current visitor.
4065
- * @deprecated Use `tracker.visitor.getTimeline()` instead.
4066
- */
4067
- async getVisitorTimeline() {
4068
- if (!this.isInitialized) {
4069
- logger.warn('SDK not initialized');
4070
- return null;
4071
- }
4072
- return this.visitor.getTimeline();
4073
- }
4074
- /**
4075
- * Get engagement metrics for the current visitor.
4076
- * @deprecated Use `tracker.visitor.getEngagement()` instead.
4077
- */
4078
- async getVisitorEngagement() {
4079
- if (!this.isInitialized) {
4080
- logger.warn('SDK not initialized');
4081
- return null;
4082
- }
4083
- return this.visitor.getEngagement();
4084
- }
4085
2761
  /**
4086
2762
  * Retry pending identify call
4087
2763
  */
@@ -4491,7 +3167,12 @@ class Tracker {
4491
3167
 
4492
3168
  /**
4493
3169
  * Clianta SDK
4494
- * Professional CRM and tracking SDK for lead generation
3170
+ * Client-side tracking SDK for CRM — tracks visitors, identifies contacts,
3171
+ * captures forms, and writes CRM data from client websites.
3172
+ *
3173
+ * This SDK is designed to run on CLIENT WEBSITES (React, Next.js, Vue, etc.)
3174
+ * It only SENDS data to your CRM — it never reads CRM data back.
3175
+ *
4495
3176
  * @see SDK_VERSION in core/config.ts
4496
3177
  */
4497
3178
  // Global instance cache
@@ -4533,16 +3214,40 @@ function clianta(workspaceId, config) {
4533
3214
  globalInstance = new Tracker(workspaceId, config);
4534
3215
  return globalInstance;
4535
3216
  }
4536
- // Attach to window for <script> usage
3217
+ // Attach to window for <script> tag usage + AUTO-INIT
4537
3218
  if (typeof window !== 'undefined') {
4538
3219
  window.clianta = clianta;
4539
3220
  window.Clianta = {
4540
3221
  clianta,
4541
3222
  Tracker,
4542
- CRMClient,
4543
3223
  ConsentManager,
4544
- EventTriggersManager,
4545
3224
  };
3225
+ // ============================================
3226
+ // AUTO-INIT FROM SCRIPT TAG
3227
+ // ============================================
3228
+ // Enables true plug-and-play:
3229
+ // <script src="clianta.min.js" data-project-id="YOUR_ID"></script>
3230
+ // That's it — everything auto-tracks.
3231
+ const autoInit = () => {
3232
+ const scripts = document.querySelectorAll('script[data-project-id]');
3233
+ const script = scripts[scripts.length - 1]; // last matching script
3234
+ if (!script)
3235
+ return;
3236
+ const projectId = script.getAttribute('data-project-id');
3237
+ if (!projectId)
3238
+ return;
3239
+ const debug = script.hasAttribute('data-debug');
3240
+ const instance = clianta(projectId, { debug });
3241
+ // Expose the auto-initialized instance globally
3242
+ window.__clianta = instance;
3243
+ };
3244
+ // Run after DOM is ready
3245
+ if (document.readyState === 'loading') {
3246
+ document.addEventListener('DOMContentLoaded', autoInit);
3247
+ }
3248
+ else {
3249
+ autoInit();
3250
+ }
4546
3251
  }
4547
3252
 
4548
3253
  /**
@@ -4564,8 +3269,6 @@ if (typeof window !== 'undefined') {
4564
3269
  * constructor() {
4565
3270
  * this.instance = createCliantaTracker({
4566
3271
  * projectId: environment.cliantaProjectId,
4567
- * apiEndpoint: environment.cliantaApiEndpoint,
4568
- * debug: !environment.production,
4569
3272
  * });
4570
3273
  * }
4571
3274
  *
@@ -4593,7 +3296,6 @@ if (typeof window !== 'undefined') {
4593
3296
  * @example
4594
3297
  * const instance = createCliantaTracker({
4595
3298
  * projectId: 'your-project-id',
4596
- * apiEndpoint: environment.cliantaApiEndpoint || 'http://localhost:5000',
4597
3299
  * });
4598
3300
  *
4599
3301
  * instance.tracker?.track('page_view', 'Home Page');