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