@clianta/sdk 1.5.1 → 1.6.1
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/CHANGELOG.md +20 -0
- package/README.md +250 -384
- package/dist/angular.cjs.js +177 -1286
- package/dist/angular.cjs.js.map +1 -1
- package/dist/angular.d.ts +69 -122
- package/dist/angular.esm.js +177 -1286
- package/dist/angular.esm.js.map +1 -1
- package/dist/clianta.cjs.js +177 -1288
- package/dist/clianta.cjs.js.map +1 -1
- package/dist/clianta.esm.js +178 -1287
- package/dist/clianta.esm.js.map +1 -1
- package/dist/clianta.umd.js +177 -1288
- package/dist/clianta.umd.js.map +1 -1
- package/dist/clianta.umd.min.js +2 -2
- package/dist/clianta.umd.min.js.map +1 -1
- package/dist/index.d.ts +91 -970
- package/dist/react.cjs.js +244 -1298
- package/dist/react.cjs.js.map +1 -1
- package/dist/react.d.ts +89 -125
- package/dist/react.esm.js +245 -1300
- package/dist/react.esm.js.map +1 -1
- package/dist/svelte.cjs.js +177 -1286
- package/dist/svelte.cjs.js.map +1 -1
- package/dist/svelte.d.ts +69 -122
- package/dist/svelte.esm.js +177 -1286
- package/dist/svelte.esm.js.map +1 -1
- package/dist/vue.cjs.js +177 -1286
- package/dist/vue.cjs.js.map +1 -1
- package/dist/vue.d.ts +69 -122
- package/dist/vue.esm.js +177 -1286
- package/dist/vue.esm.js.map +1 -1
- package/package.json +2 -2
package/dist/clianta.cjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Clianta SDK v1.
|
|
2
|
+
* Clianta SDK v1.6.1
|
|
3
3
|
* (c) 2026 Clianta
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
@@ -12,7 +12,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
12
12
|
* @see SDK_VERSION in core/config.ts
|
|
13
13
|
*/
|
|
14
14
|
/** SDK Version */
|
|
15
|
-
const SDK_VERSION = '1.
|
|
15
|
+
const SDK_VERSION = '1.6.1';
|
|
16
16
|
/** Default API endpoint — reads from env or falls back to localhost */
|
|
17
17
|
const getDefaultApiEndpoint = () => {
|
|
18
18
|
// Build-time env var (works with Next.js, Vite, CRA, etc.)
|
|
@@ -44,8 +44,6 @@ const DEFAULT_PLUGINS = [
|
|
|
44
44
|
const DEFAULT_CONFIG = {
|
|
45
45
|
projectId: '',
|
|
46
46
|
apiEndpoint: getDefaultApiEndpoint(),
|
|
47
|
-
authToken: '',
|
|
48
|
-
apiKey: '',
|
|
49
47
|
debug: false,
|
|
50
48
|
autoPageView: true,
|
|
51
49
|
plugins: DEFAULT_PLUGINS,
|
|
@@ -2511,1189 +2509,6 @@ class ConsentManager {
|
|
|
2511
2509
|
}
|
|
2512
2510
|
}
|
|
2513
2511
|
|
|
2514
|
-
/**
|
|
2515
|
-
* Clianta SDK - Event Triggers Manager
|
|
2516
|
-
* Manages event-driven automation and email notifications
|
|
2517
|
-
*/
|
|
2518
|
-
/**
|
|
2519
|
-
* Event Triggers Manager
|
|
2520
|
-
* Handles event-driven automation based on CRM actions
|
|
2521
|
-
*
|
|
2522
|
-
* Similar to:
|
|
2523
|
-
* - Salesforce: Process Builder, Flow Automation
|
|
2524
|
-
* - HubSpot: Workflows, Email Sequences
|
|
2525
|
-
* - Pipedrive: Workflow Automation
|
|
2526
|
-
*/
|
|
2527
|
-
class EventTriggersManager {
|
|
2528
|
-
constructor(apiEndpoint, workspaceId, authToken) {
|
|
2529
|
-
this.triggers = new Map();
|
|
2530
|
-
this.listeners = new Map();
|
|
2531
|
-
this.apiEndpoint = apiEndpoint;
|
|
2532
|
-
this.workspaceId = workspaceId;
|
|
2533
|
-
this.authToken = authToken;
|
|
2534
|
-
}
|
|
2535
|
-
/**
|
|
2536
|
-
* Set authentication token
|
|
2537
|
-
*/
|
|
2538
|
-
setAuthToken(token) {
|
|
2539
|
-
this.authToken = token;
|
|
2540
|
-
}
|
|
2541
|
-
/**
|
|
2542
|
-
* Make authenticated API request
|
|
2543
|
-
*/
|
|
2544
|
-
async request(endpoint, options = {}) {
|
|
2545
|
-
const url = `${this.apiEndpoint}${endpoint}`;
|
|
2546
|
-
const headers = {
|
|
2547
|
-
'Content-Type': 'application/json',
|
|
2548
|
-
...(options.headers || {}),
|
|
2549
|
-
};
|
|
2550
|
-
if (this.authToken) {
|
|
2551
|
-
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
2552
|
-
}
|
|
2553
|
-
try {
|
|
2554
|
-
const response = await fetch(url, {
|
|
2555
|
-
...options,
|
|
2556
|
-
headers,
|
|
2557
|
-
});
|
|
2558
|
-
const data = await response.json();
|
|
2559
|
-
if (!response.ok) {
|
|
2560
|
-
return {
|
|
2561
|
-
success: false,
|
|
2562
|
-
error: data.message || 'Request failed',
|
|
2563
|
-
status: response.status,
|
|
2564
|
-
};
|
|
2565
|
-
}
|
|
2566
|
-
return {
|
|
2567
|
-
success: true,
|
|
2568
|
-
data: data.data || data,
|
|
2569
|
-
status: response.status,
|
|
2570
|
-
};
|
|
2571
|
-
}
|
|
2572
|
-
catch (error) {
|
|
2573
|
-
return {
|
|
2574
|
-
success: false,
|
|
2575
|
-
error: error instanceof Error ? error.message : 'Network error',
|
|
2576
|
-
status: 0,
|
|
2577
|
-
};
|
|
2578
|
-
}
|
|
2579
|
-
}
|
|
2580
|
-
// ============================================
|
|
2581
|
-
// TRIGGER MANAGEMENT
|
|
2582
|
-
// ============================================
|
|
2583
|
-
/**
|
|
2584
|
-
* Get all event triggers
|
|
2585
|
-
*/
|
|
2586
|
-
async getTriggers() {
|
|
2587
|
-
return this.request(`/api/workspaces/${this.workspaceId}/triggers`);
|
|
2588
|
-
}
|
|
2589
|
-
/**
|
|
2590
|
-
* Get a single trigger by ID
|
|
2591
|
-
*/
|
|
2592
|
-
async getTrigger(triggerId) {
|
|
2593
|
-
return this.request(`/api/workspaces/${this.workspaceId}/triggers/${triggerId}`);
|
|
2594
|
-
}
|
|
2595
|
-
/**
|
|
2596
|
-
* Create a new event trigger
|
|
2597
|
-
*/
|
|
2598
|
-
async createTrigger(trigger) {
|
|
2599
|
-
const result = await this.request(`/api/workspaces/${this.workspaceId}/triggers`, {
|
|
2600
|
-
method: 'POST',
|
|
2601
|
-
body: JSON.stringify(trigger),
|
|
2602
|
-
});
|
|
2603
|
-
// Cache the trigger locally if successful
|
|
2604
|
-
if (result.success && result.data?._id) {
|
|
2605
|
-
this.triggers.set(result.data._id, result.data);
|
|
2606
|
-
}
|
|
2607
|
-
return result;
|
|
2608
|
-
}
|
|
2609
|
-
/**
|
|
2610
|
-
* Update an existing trigger
|
|
2611
|
-
*/
|
|
2612
|
-
async updateTrigger(triggerId, updates) {
|
|
2613
|
-
const result = await this.request(`/api/workspaces/${this.workspaceId}/triggers/${triggerId}`, {
|
|
2614
|
-
method: 'PUT',
|
|
2615
|
-
body: JSON.stringify(updates),
|
|
2616
|
-
});
|
|
2617
|
-
// Update cache if successful
|
|
2618
|
-
if (result.success && result.data?._id) {
|
|
2619
|
-
this.triggers.set(result.data._id, result.data);
|
|
2620
|
-
}
|
|
2621
|
-
return result;
|
|
2622
|
-
}
|
|
2623
|
-
/**
|
|
2624
|
-
* Delete a trigger
|
|
2625
|
-
*/
|
|
2626
|
-
async deleteTrigger(triggerId) {
|
|
2627
|
-
const result = await this.request(`/api/workspaces/${this.workspaceId}/triggers/${triggerId}`, {
|
|
2628
|
-
method: 'DELETE',
|
|
2629
|
-
});
|
|
2630
|
-
// Remove from cache if successful
|
|
2631
|
-
if (result.success) {
|
|
2632
|
-
this.triggers.delete(triggerId);
|
|
2633
|
-
}
|
|
2634
|
-
return result;
|
|
2635
|
-
}
|
|
2636
|
-
/**
|
|
2637
|
-
* Activate a trigger
|
|
2638
|
-
*/
|
|
2639
|
-
async activateTrigger(triggerId) {
|
|
2640
|
-
return this.updateTrigger(triggerId, { isActive: true });
|
|
2641
|
-
}
|
|
2642
|
-
/**
|
|
2643
|
-
* Deactivate a trigger
|
|
2644
|
-
*/
|
|
2645
|
-
async deactivateTrigger(triggerId) {
|
|
2646
|
-
return this.updateTrigger(triggerId, { isActive: false });
|
|
2647
|
-
}
|
|
2648
|
-
// ============================================
|
|
2649
|
-
// EVENT HANDLING (CLIENT-SIDE)
|
|
2650
|
-
// ============================================
|
|
2651
|
-
/**
|
|
2652
|
-
* Register a local event listener for client-side triggers
|
|
2653
|
-
* This allows immediate client-side reactions to events
|
|
2654
|
-
*/
|
|
2655
|
-
on(eventType, callback) {
|
|
2656
|
-
if (!this.listeners.has(eventType)) {
|
|
2657
|
-
this.listeners.set(eventType, new Set());
|
|
2658
|
-
}
|
|
2659
|
-
this.listeners.get(eventType).add(callback);
|
|
2660
|
-
logger.debug(`Event listener registered: ${eventType}`);
|
|
2661
|
-
}
|
|
2662
|
-
/**
|
|
2663
|
-
* Remove an event listener
|
|
2664
|
-
*/
|
|
2665
|
-
off(eventType, callback) {
|
|
2666
|
-
const listeners = this.listeners.get(eventType);
|
|
2667
|
-
if (listeners) {
|
|
2668
|
-
listeners.delete(callback);
|
|
2669
|
-
}
|
|
2670
|
-
}
|
|
2671
|
-
/**
|
|
2672
|
-
* Emit an event (client-side only)
|
|
2673
|
-
* This will trigger any registered local listeners
|
|
2674
|
-
*/
|
|
2675
|
-
emit(eventType, data) {
|
|
2676
|
-
logger.debug(`Event emitted: ${eventType}`, data);
|
|
2677
|
-
const listeners = this.listeners.get(eventType);
|
|
2678
|
-
if (listeners) {
|
|
2679
|
-
listeners.forEach(callback => {
|
|
2680
|
-
try {
|
|
2681
|
-
callback(data);
|
|
2682
|
-
}
|
|
2683
|
-
catch (error) {
|
|
2684
|
-
logger.error(`Error in event listener for ${eventType}:`, error);
|
|
2685
|
-
}
|
|
2686
|
-
});
|
|
2687
|
-
}
|
|
2688
|
-
}
|
|
2689
|
-
/**
|
|
2690
|
-
* Check if conditions are met for a trigger
|
|
2691
|
-
* Supports dynamic field evaluation including custom fields and nested paths
|
|
2692
|
-
*/
|
|
2693
|
-
evaluateConditions(conditions, data) {
|
|
2694
|
-
if (!conditions || conditions.length === 0) {
|
|
2695
|
-
return true; // No conditions means always fire
|
|
2696
|
-
}
|
|
2697
|
-
return conditions.every(condition => {
|
|
2698
|
-
// Support dot notation for nested fields (e.g., 'customFields.industry')
|
|
2699
|
-
const fieldValue = condition.field.includes('.')
|
|
2700
|
-
? this.getNestedValue(data, condition.field)
|
|
2701
|
-
: data[condition.field];
|
|
2702
|
-
const targetValue = condition.value;
|
|
2703
|
-
switch (condition.operator) {
|
|
2704
|
-
case 'equals':
|
|
2705
|
-
return fieldValue === targetValue;
|
|
2706
|
-
case 'not_equals':
|
|
2707
|
-
return fieldValue !== targetValue;
|
|
2708
|
-
case 'contains':
|
|
2709
|
-
return String(fieldValue).includes(String(targetValue));
|
|
2710
|
-
case 'greater_than':
|
|
2711
|
-
return Number(fieldValue) > Number(targetValue);
|
|
2712
|
-
case 'less_than':
|
|
2713
|
-
return Number(fieldValue) < Number(targetValue);
|
|
2714
|
-
case 'in':
|
|
2715
|
-
return Array.isArray(targetValue) && targetValue.includes(fieldValue);
|
|
2716
|
-
case 'not_in':
|
|
2717
|
-
return Array.isArray(targetValue) && !targetValue.includes(fieldValue);
|
|
2718
|
-
default:
|
|
2719
|
-
return false;
|
|
2720
|
-
}
|
|
2721
|
-
});
|
|
2722
|
-
}
|
|
2723
|
-
/**
|
|
2724
|
-
* Execute actions for a triggered event (client-side preview)
|
|
2725
|
-
* Note: Actual execution happens on the backend
|
|
2726
|
-
*/
|
|
2727
|
-
async executeActions(trigger, data) {
|
|
2728
|
-
logger.info(`Executing actions for trigger: ${trigger.name}`);
|
|
2729
|
-
for (const action of trigger.actions) {
|
|
2730
|
-
try {
|
|
2731
|
-
await this.executeAction(action, data);
|
|
2732
|
-
}
|
|
2733
|
-
catch (error) {
|
|
2734
|
-
logger.error(`Failed to execute action:`, error);
|
|
2735
|
-
}
|
|
2736
|
-
}
|
|
2737
|
-
}
|
|
2738
|
-
/**
|
|
2739
|
-
* Execute a single action
|
|
2740
|
-
*/
|
|
2741
|
-
async executeAction(action, data) {
|
|
2742
|
-
switch (action.type) {
|
|
2743
|
-
case 'send_email':
|
|
2744
|
-
await this.executeSendEmail(action, data);
|
|
2745
|
-
break;
|
|
2746
|
-
case 'webhook':
|
|
2747
|
-
await this.executeWebhook(action, data);
|
|
2748
|
-
break;
|
|
2749
|
-
case 'create_task':
|
|
2750
|
-
await this.executeCreateTask(action, data);
|
|
2751
|
-
break;
|
|
2752
|
-
case 'update_contact':
|
|
2753
|
-
await this.executeUpdateContact(action, data);
|
|
2754
|
-
break;
|
|
2755
|
-
default:
|
|
2756
|
-
logger.warn(`Unknown action type:`, action);
|
|
2757
|
-
}
|
|
2758
|
-
}
|
|
2759
|
-
/**
|
|
2760
|
-
* Execute send email action (via backend API)
|
|
2761
|
-
*/
|
|
2762
|
-
async executeSendEmail(action, data) {
|
|
2763
|
-
logger.debug('Sending email:', action);
|
|
2764
|
-
const payload = {
|
|
2765
|
-
to: this.replaceVariables(action.to, data),
|
|
2766
|
-
subject: action.subject ? this.replaceVariables(action.subject, data) : undefined,
|
|
2767
|
-
body: action.body ? this.replaceVariables(action.body, data) : undefined,
|
|
2768
|
-
templateId: action.templateId,
|
|
2769
|
-
cc: action.cc,
|
|
2770
|
-
bcc: action.bcc,
|
|
2771
|
-
from: action.from,
|
|
2772
|
-
delayMinutes: action.delayMinutes,
|
|
2773
|
-
};
|
|
2774
|
-
await this.request(`/api/workspaces/${this.workspaceId}/emails/send`, {
|
|
2775
|
-
method: 'POST',
|
|
2776
|
-
body: JSON.stringify(payload),
|
|
2777
|
-
});
|
|
2778
|
-
}
|
|
2779
|
-
/**
|
|
2780
|
-
* Execute webhook action
|
|
2781
|
-
*/
|
|
2782
|
-
async executeWebhook(action, data) {
|
|
2783
|
-
logger.debug('Calling webhook:', action.url);
|
|
2784
|
-
const body = action.body ? this.replaceVariables(action.body, data) : JSON.stringify(data);
|
|
2785
|
-
await fetch(action.url, {
|
|
2786
|
-
method: action.method,
|
|
2787
|
-
headers: {
|
|
2788
|
-
'Content-Type': 'application/json',
|
|
2789
|
-
...action.headers,
|
|
2790
|
-
},
|
|
2791
|
-
body,
|
|
2792
|
-
});
|
|
2793
|
-
}
|
|
2794
|
-
/**
|
|
2795
|
-
* Execute create task action
|
|
2796
|
-
*/
|
|
2797
|
-
async executeCreateTask(action, data) {
|
|
2798
|
-
logger.debug('Creating task:', action.title);
|
|
2799
|
-
const dueDate = action.dueDays
|
|
2800
|
-
? new Date(Date.now() + action.dueDays * 24 * 60 * 60 * 1000).toISOString()
|
|
2801
|
-
: undefined;
|
|
2802
|
-
await this.request(`/api/workspaces/${this.workspaceId}/tasks`, {
|
|
2803
|
-
method: 'POST',
|
|
2804
|
-
body: JSON.stringify({
|
|
2805
|
-
title: this.replaceVariables(action.title, data),
|
|
2806
|
-
description: action.description ? this.replaceVariables(action.description, data) : undefined,
|
|
2807
|
-
priority: action.priority,
|
|
2808
|
-
dueDate,
|
|
2809
|
-
assignedTo: action.assignedTo,
|
|
2810
|
-
relatedContactId: typeof data.contactId === 'string' ? data.contactId : undefined,
|
|
2811
|
-
}),
|
|
2812
|
-
});
|
|
2813
|
-
}
|
|
2814
|
-
/**
|
|
2815
|
-
* Execute update contact action
|
|
2816
|
-
*/
|
|
2817
|
-
async executeUpdateContact(action, data) {
|
|
2818
|
-
const contactId = data.contactId || data._id;
|
|
2819
|
-
if (!contactId) {
|
|
2820
|
-
logger.warn('Cannot update contact: no contactId in data');
|
|
2821
|
-
return;
|
|
2822
|
-
}
|
|
2823
|
-
logger.debug('Updating contact:', contactId);
|
|
2824
|
-
await this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}`, {
|
|
2825
|
-
method: 'PUT',
|
|
2826
|
-
body: JSON.stringify(action.updates),
|
|
2827
|
-
});
|
|
2828
|
-
}
|
|
2829
|
-
/**
|
|
2830
|
-
* Replace variables in a string template
|
|
2831
|
-
* Supports syntax like {{contact.email}}, {{opportunity.value}}
|
|
2832
|
-
*/
|
|
2833
|
-
replaceVariables(template, data) {
|
|
2834
|
-
return template.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
|
|
2835
|
-
const value = this.getNestedValue(data, path.trim());
|
|
2836
|
-
return value !== undefined ? String(value) : match;
|
|
2837
|
-
});
|
|
2838
|
-
}
|
|
2839
|
-
/**
|
|
2840
|
-
* Get nested value from object using dot notation
|
|
2841
|
-
* Supports dynamic field access including custom fields
|
|
2842
|
-
*/
|
|
2843
|
-
getNestedValue(obj, path) {
|
|
2844
|
-
return path.split('.').reduce((current, key) => {
|
|
2845
|
-
return current !== null && current !== undefined && typeof current === 'object'
|
|
2846
|
-
? current[key]
|
|
2847
|
-
: undefined;
|
|
2848
|
-
}, obj);
|
|
2849
|
-
}
|
|
2850
|
-
/**
|
|
2851
|
-
* Extract all available field paths from a data object
|
|
2852
|
-
* Useful for dynamic field discovery based on platform-specific attributes
|
|
2853
|
-
* @param obj - The data object to extract fields from
|
|
2854
|
-
* @param prefix - Internal use for nested paths
|
|
2855
|
-
* @param maxDepth - Maximum depth to traverse (default: 3)
|
|
2856
|
-
* @returns Array of field paths (e.g., ['email', 'contact.firstName', 'customFields.industry'])
|
|
2857
|
-
*/
|
|
2858
|
-
extractAvailableFields(obj, prefix = '', maxDepth = 3) {
|
|
2859
|
-
if (maxDepth <= 0)
|
|
2860
|
-
return [];
|
|
2861
|
-
const fields = [];
|
|
2862
|
-
for (const key in obj) {
|
|
2863
|
-
if (!obj.hasOwnProperty(key))
|
|
2864
|
-
continue;
|
|
2865
|
-
const value = obj[key];
|
|
2866
|
-
const fieldPath = prefix ? `${prefix}.${key}` : key;
|
|
2867
|
-
fields.push(fieldPath);
|
|
2868
|
-
// Recursively traverse nested objects
|
|
2869
|
-
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
2870
|
-
const nestedFields = this.extractAvailableFields(value, fieldPath, maxDepth - 1);
|
|
2871
|
-
fields.push(...nestedFields);
|
|
2872
|
-
}
|
|
2873
|
-
}
|
|
2874
|
-
return fields;
|
|
2875
|
-
}
|
|
2876
|
-
/**
|
|
2877
|
-
* Get available fields from sample data
|
|
2878
|
-
* Helps with dynamic field detection for platform-specific attributes
|
|
2879
|
-
* @param sampleData - Sample data object to analyze
|
|
2880
|
-
* @returns Array of available field paths
|
|
2881
|
-
*/
|
|
2882
|
-
getAvailableFields(sampleData) {
|
|
2883
|
-
return this.extractAvailableFields(sampleData);
|
|
2884
|
-
}
|
|
2885
|
-
// ============================================
|
|
2886
|
-
// HELPER METHODS FOR COMMON PATTERNS
|
|
2887
|
-
// ============================================
|
|
2888
|
-
/**
|
|
2889
|
-
* Create a simple email trigger
|
|
2890
|
-
* Helper method for common use case
|
|
2891
|
-
*/
|
|
2892
|
-
async createEmailTrigger(config) {
|
|
2893
|
-
return this.createTrigger({
|
|
2894
|
-
name: config.name,
|
|
2895
|
-
eventType: config.eventType,
|
|
2896
|
-
conditions: config.conditions,
|
|
2897
|
-
actions: [
|
|
2898
|
-
{
|
|
2899
|
-
type: 'send_email',
|
|
2900
|
-
to: config.to,
|
|
2901
|
-
subject: config.subject,
|
|
2902
|
-
body: config.body,
|
|
2903
|
-
},
|
|
2904
|
-
],
|
|
2905
|
-
isActive: true,
|
|
2906
|
-
});
|
|
2907
|
-
}
|
|
2908
|
-
/**
|
|
2909
|
-
* Create a task creation trigger
|
|
2910
|
-
*/
|
|
2911
|
-
async createTaskTrigger(config) {
|
|
2912
|
-
return this.createTrigger({
|
|
2913
|
-
name: config.name,
|
|
2914
|
-
eventType: config.eventType,
|
|
2915
|
-
conditions: config.conditions,
|
|
2916
|
-
actions: [
|
|
2917
|
-
{
|
|
2918
|
-
type: 'create_task',
|
|
2919
|
-
title: config.taskTitle,
|
|
2920
|
-
description: config.taskDescription,
|
|
2921
|
-
priority: config.priority,
|
|
2922
|
-
dueDays: config.dueDays,
|
|
2923
|
-
},
|
|
2924
|
-
],
|
|
2925
|
-
isActive: true,
|
|
2926
|
-
});
|
|
2927
|
-
}
|
|
2928
|
-
/**
|
|
2929
|
-
* Create a webhook trigger
|
|
2930
|
-
*/
|
|
2931
|
-
async createWebhookTrigger(config) {
|
|
2932
|
-
return this.createTrigger({
|
|
2933
|
-
name: config.name,
|
|
2934
|
-
eventType: config.eventType,
|
|
2935
|
-
conditions: config.conditions,
|
|
2936
|
-
actions: [
|
|
2937
|
-
{
|
|
2938
|
-
type: 'webhook',
|
|
2939
|
-
url: config.webhookUrl,
|
|
2940
|
-
method: config.method || 'POST',
|
|
2941
|
-
},
|
|
2942
|
-
],
|
|
2943
|
-
isActive: true,
|
|
2944
|
-
});
|
|
2945
|
-
}
|
|
2946
|
-
}
|
|
2947
|
-
|
|
2948
|
-
/**
|
|
2949
|
-
* Clianta SDK - CRM API Client
|
|
2950
|
-
* @see SDK_VERSION in core/config.ts
|
|
2951
|
-
*/
|
|
2952
|
-
/**
|
|
2953
|
-
* CRM API Client for managing contacts and opportunities
|
|
2954
|
-
*/
|
|
2955
|
-
class CRMClient {
|
|
2956
|
-
constructor(apiEndpoint, workspaceId, authToken, apiKey) {
|
|
2957
|
-
this.apiEndpoint = apiEndpoint;
|
|
2958
|
-
this.workspaceId = workspaceId;
|
|
2959
|
-
this.authToken = authToken;
|
|
2960
|
-
this.apiKey = apiKey;
|
|
2961
|
-
this.triggers = new EventTriggersManager(apiEndpoint, workspaceId, authToken);
|
|
2962
|
-
}
|
|
2963
|
-
/**
|
|
2964
|
-
* Set authentication token for API requests (user JWT)
|
|
2965
|
-
*/
|
|
2966
|
-
setAuthToken(token) {
|
|
2967
|
-
this.authToken = token;
|
|
2968
|
-
this.apiKey = undefined;
|
|
2969
|
-
this.triggers.setAuthToken(token);
|
|
2970
|
-
}
|
|
2971
|
-
/**
|
|
2972
|
-
* Set workspace API key for server-to-server requests.
|
|
2973
|
-
* Use this instead of setAuthToken when integrating from an external app.
|
|
2974
|
-
*/
|
|
2975
|
-
setApiKey(key) {
|
|
2976
|
-
this.apiKey = key;
|
|
2977
|
-
this.authToken = undefined;
|
|
2978
|
-
}
|
|
2979
|
-
/**
|
|
2980
|
-
* Validate required parameter exists
|
|
2981
|
-
* @throws {Error} if value is null/undefined or empty string
|
|
2982
|
-
*/
|
|
2983
|
-
validateRequired(param, value, methodName) {
|
|
2984
|
-
if (value === null || value === undefined || value === '') {
|
|
2985
|
-
throw new Error(`[CRMClient.${methodName}] ${param} is required`);
|
|
2986
|
-
}
|
|
2987
|
-
}
|
|
2988
|
-
/**
|
|
2989
|
-
* Make authenticated API request
|
|
2990
|
-
*/
|
|
2991
|
-
async request(endpoint, options = {}) {
|
|
2992
|
-
const url = `${this.apiEndpoint}${endpoint}`;
|
|
2993
|
-
const headers = {
|
|
2994
|
-
'Content-Type': 'application/json',
|
|
2995
|
-
...(options.headers || {}),
|
|
2996
|
-
};
|
|
2997
|
-
if (this.apiKey) {
|
|
2998
|
-
headers['X-Api-Key'] = this.apiKey;
|
|
2999
|
-
}
|
|
3000
|
-
else if (this.authToken) {
|
|
3001
|
-
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
3002
|
-
}
|
|
3003
|
-
try {
|
|
3004
|
-
const response = await fetch(url, {
|
|
3005
|
-
...options,
|
|
3006
|
-
headers,
|
|
3007
|
-
});
|
|
3008
|
-
const data = await response.json();
|
|
3009
|
-
if (!response.ok) {
|
|
3010
|
-
return {
|
|
3011
|
-
success: false,
|
|
3012
|
-
error: data.message || 'Request failed',
|
|
3013
|
-
status: response.status,
|
|
3014
|
-
};
|
|
3015
|
-
}
|
|
3016
|
-
return {
|
|
3017
|
-
success: true,
|
|
3018
|
-
data: data.data || data,
|
|
3019
|
-
status: response.status,
|
|
3020
|
-
};
|
|
3021
|
-
}
|
|
3022
|
-
catch (error) {
|
|
3023
|
-
return {
|
|
3024
|
-
success: false,
|
|
3025
|
-
error: error instanceof Error ? error.message : 'Network error',
|
|
3026
|
-
status: 0,
|
|
3027
|
-
};
|
|
3028
|
-
}
|
|
3029
|
-
}
|
|
3030
|
-
// ============================================
|
|
3031
|
-
// INBOUND EVENTS API (API-key authenticated)
|
|
3032
|
-
// ============================================
|
|
3033
|
-
/**
|
|
3034
|
-
* Send an inbound event from an external app (e.g. user signup on client website).
|
|
3035
|
-
* Requires the client to be initialized with an API key via setApiKey() or the constructor.
|
|
3036
|
-
*
|
|
3037
|
-
* The contact is upserted in the CRM and matching workflow automations fire automatically.
|
|
3038
|
-
*
|
|
3039
|
-
* @example
|
|
3040
|
-
* const crm = new CRMClient('http://localhost:5000', 'WORKSPACE_ID');
|
|
3041
|
-
* crm.setApiKey('mm_live_...');
|
|
3042
|
-
*
|
|
3043
|
-
* await crm.sendEvent({
|
|
3044
|
-
* event: 'user.registered',
|
|
3045
|
-
* contact: { email: 'alice@example.com', firstName: 'Alice' },
|
|
3046
|
-
* data: { plan: 'free', signupSource: 'homepage' },
|
|
3047
|
-
* });
|
|
3048
|
-
*/
|
|
3049
|
-
async sendEvent(payload) {
|
|
3050
|
-
const url = `${this.apiEndpoint}/api/public/events`;
|
|
3051
|
-
const headers = { 'Content-Type': 'application/json' };
|
|
3052
|
-
if (this.apiKey) {
|
|
3053
|
-
headers['X-Api-Key'] = this.apiKey;
|
|
3054
|
-
}
|
|
3055
|
-
else if (this.authToken) {
|
|
3056
|
-
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
3057
|
-
}
|
|
3058
|
-
try {
|
|
3059
|
-
const response = await fetch(url, {
|
|
3060
|
-
method: 'POST',
|
|
3061
|
-
headers,
|
|
3062
|
-
body: JSON.stringify(payload),
|
|
3063
|
-
});
|
|
3064
|
-
const data = await response.json();
|
|
3065
|
-
if (!response.ok) {
|
|
3066
|
-
return {
|
|
3067
|
-
success: false,
|
|
3068
|
-
contactCreated: false,
|
|
3069
|
-
event: payload.event,
|
|
3070
|
-
error: data.error || 'Request failed',
|
|
3071
|
-
};
|
|
3072
|
-
}
|
|
3073
|
-
return {
|
|
3074
|
-
success: data.success,
|
|
3075
|
-
contactCreated: data.contactCreated,
|
|
3076
|
-
contactId: data.contactId,
|
|
3077
|
-
event: data.event,
|
|
3078
|
-
};
|
|
3079
|
-
}
|
|
3080
|
-
catch (error) {
|
|
3081
|
-
return {
|
|
3082
|
-
success: false,
|
|
3083
|
-
contactCreated: false,
|
|
3084
|
-
event: payload.event,
|
|
3085
|
-
error: error instanceof Error ? error.message : 'Network error',
|
|
3086
|
-
};
|
|
3087
|
-
}
|
|
3088
|
-
}
|
|
3089
|
-
// ============================================
|
|
3090
|
-
// CONTACTS API
|
|
3091
|
-
// ============================================
|
|
3092
|
-
/**
|
|
3093
|
-
* Get all contacts with pagination
|
|
3094
|
-
*/
|
|
3095
|
-
async getContacts(params) {
|
|
3096
|
-
const queryParams = new URLSearchParams();
|
|
3097
|
-
if (params?.page)
|
|
3098
|
-
queryParams.set('page', params.page.toString());
|
|
3099
|
-
if (params?.limit)
|
|
3100
|
-
queryParams.set('limit', params.limit.toString());
|
|
3101
|
-
if (params?.search)
|
|
3102
|
-
queryParams.set('search', params.search);
|
|
3103
|
-
if (params?.status)
|
|
3104
|
-
queryParams.set('status', params.status);
|
|
3105
|
-
const query = queryParams.toString();
|
|
3106
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/contacts${query ? `?${query}` : ''}`;
|
|
3107
|
-
return this.request(endpoint);
|
|
3108
|
-
}
|
|
3109
|
-
/**
|
|
3110
|
-
* Get a single contact by ID
|
|
3111
|
-
*/
|
|
3112
|
-
async getContact(contactId) {
|
|
3113
|
-
this.validateRequired('contactId', contactId, 'getContact');
|
|
3114
|
-
return this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}`);
|
|
3115
|
-
}
|
|
3116
|
-
/**
|
|
3117
|
-
* Create a new contact
|
|
3118
|
-
*/
|
|
3119
|
-
async createContact(contact) {
|
|
3120
|
-
return this.request(`/api/workspaces/${this.workspaceId}/contacts`, {
|
|
3121
|
-
method: 'POST',
|
|
3122
|
-
body: JSON.stringify(contact),
|
|
3123
|
-
});
|
|
3124
|
-
}
|
|
3125
|
-
/**
|
|
3126
|
-
* Update an existing contact
|
|
3127
|
-
*/
|
|
3128
|
-
async updateContact(contactId, updates) {
|
|
3129
|
-
this.validateRequired('contactId', contactId, 'updateContact');
|
|
3130
|
-
return this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}`, {
|
|
3131
|
-
method: 'PUT',
|
|
3132
|
-
body: JSON.stringify(updates),
|
|
3133
|
-
});
|
|
3134
|
-
}
|
|
3135
|
-
/**
|
|
3136
|
-
* Delete a contact
|
|
3137
|
-
*/
|
|
3138
|
-
async deleteContact(contactId) {
|
|
3139
|
-
this.validateRequired('contactId', contactId, 'deleteContact');
|
|
3140
|
-
return this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}`, {
|
|
3141
|
-
method: 'DELETE',
|
|
3142
|
-
});
|
|
3143
|
-
}
|
|
3144
|
-
// ============================================
|
|
3145
|
-
// OPPORTUNITIES API
|
|
3146
|
-
// ============================================
|
|
3147
|
-
/**
|
|
3148
|
-
* Get all opportunities with pagination
|
|
3149
|
-
*/
|
|
3150
|
-
async getOpportunities(params) {
|
|
3151
|
-
const queryParams = new URLSearchParams();
|
|
3152
|
-
if (params?.page)
|
|
3153
|
-
queryParams.set('page', params.page.toString());
|
|
3154
|
-
if (params?.limit)
|
|
3155
|
-
queryParams.set('limit', params.limit.toString());
|
|
3156
|
-
if (params?.pipelineId)
|
|
3157
|
-
queryParams.set('pipelineId', params.pipelineId);
|
|
3158
|
-
if (params?.stageId)
|
|
3159
|
-
queryParams.set('stageId', params.stageId);
|
|
3160
|
-
const query = queryParams.toString();
|
|
3161
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/opportunities${query ? `?${query}` : ''}`;
|
|
3162
|
-
return this.request(endpoint);
|
|
3163
|
-
}
|
|
3164
|
-
/**
|
|
3165
|
-
* Get a single opportunity by ID
|
|
3166
|
-
*/
|
|
3167
|
-
async getOpportunity(opportunityId) {
|
|
3168
|
-
return this.request(`/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}`);
|
|
3169
|
-
}
|
|
3170
|
-
/**
|
|
3171
|
-
* Create a new opportunity
|
|
3172
|
-
*/
|
|
3173
|
-
async createOpportunity(opportunity) {
|
|
3174
|
-
return this.request(`/api/workspaces/${this.workspaceId}/opportunities`, {
|
|
3175
|
-
method: 'POST',
|
|
3176
|
-
body: JSON.stringify(opportunity),
|
|
3177
|
-
});
|
|
3178
|
-
}
|
|
3179
|
-
/**
|
|
3180
|
-
* Update an existing opportunity
|
|
3181
|
-
*/
|
|
3182
|
-
async updateOpportunity(opportunityId, updates) {
|
|
3183
|
-
return this.request(`/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}`, {
|
|
3184
|
-
method: 'PUT',
|
|
3185
|
-
body: JSON.stringify(updates),
|
|
3186
|
-
});
|
|
3187
|
-
}
|
|
3188
|
-
/**
|
|
3189
|
-
* Delete an opportunity
|
|
3190
|
-
*/
|
|
3191
|
-
async deleteOpportunity(opportunityId) {
|
|
3192
|
-
return this.request(`/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}`, {
|
|
3193
|
-
method: 'DELETE',
|
|
3194
|
-
});
|
|
3195
|
-
}
|
|
3196
|
-
/**
|
|
3197
|
-
* Move opportunity to a different stage
|
|
3198
|
-
*/
|
|
3199
|
-
async moveOpportunity(opportunityId, stageId) {
|
|
3200
|
-
return this.request(`/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}/move`, {
|
|
3201
|
-
method: 'POST',
|
|
3202
|
-
body: JSON.stringify({ stageId }),
|
|
3203
|
-
});
|
|
3204
|
-
}
|
|
3205
|
-
// ============================================
|
|
3206
|
-
// COMPANIES API
|
|
3207
|
-
// ============================================
|
|
3208
|
-
/**
|
|
3209
|
-
* Get all companies with pagination
|
|
3210
|
-
*/
|
|
3211
|
-
async getCompanies(params) {
|
|
3212
|
-
const queryParams = new URLSearchParams();
|
|
3213
|
-
if (params?.page)
|
|
3214
|
-
queryParams.set('page', params.page.toString());
|
|
3215
|
-
if (params?.limit)
|
|
3216
|
-
queryParams.set('limit', params.limit.toString());
|
|
3217
|
-
if (params?.search)
|
|
3218
|
-
queryParams.set('search', params.search);
|
|
3219
|
-
if (params?.status)
|
|
3220
|
-
queryParams.set('status', params.status);
|
|
3221
|
-
if (params?.industry)
|
|
3222
|
-
queryParams.set('industry', params.industry);
|
|
3223
|
-
const query = queryParams.toString();
|
|
3224
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/companies${query ? `?${query}` : ''}`;
|
|
3225
|
-
return this.request(endpoint);
|
|
3226
|
-
}
|
|
3227
|
-
/**
|
|
3228
|
-
* Get a single company by ID
|
|
3229
|
-
*/
|
|
3230
|
-
async getCompany(companyId) {
|
|
3231
|
-
return this.request(`/api/workspaces/${this.workspaceId}/companies/${companyId}`);
|
|
3232
|
-
}
|
|
3233
|
-
/**
|
|
3234
|
-
* Create a new company
|
|
3235
|
-
*/
|
|
3236
|
-
async createCompany(company) {
|
|
3237
|
-
return this.request(`/api/workspaces/${this.workspaceId}/companies`, {
|
|
3238
|
-
method: 'POST',
|
|
3239
|
-
body: JSON.stringify(company),
|
|
3240
|
-
});
|
|
3241
|
-
}
|
|
3242
|
-
/**
|
|
3243
|
-
* Update an existing company
|
|
3244
|
-
*/
|
|
3245
|
-
async updateCompany(companyId, updates) {
|
|
3246
|
-
return this.request(`/api/workspaces/${this.workspaceId}/companies/${companyId}`, {
|
|
3247
|
-
method: 'PUT',
|
|
3248
|
-
body: JSON.stringify(updates),
|
|
3249
|
-
});
|
|
3250
|
-
}
|
|
3251
|
-
/**
|
|
3252
|
-
* Delete a company
|
|
3253
|
-
*/
|
|
3254
|
-
async deleteCompany(companyId) {
|
|
3255
|
-
return this.request(`/api/workspaces/${this.workspaceId}/companies/${companyId}`, {
|
|
3256
|
-
method: 'DELETE',
|
|
3257
|
-
});
|
|
3258
|
-
}
|
|
3259
|
-
/**
|
|
3260
|
-
* Get contacts belonging to a company
|
|
3261
|
-
*/
|
|
3262
|
-
async getCompanyContacts(companyId, params) {
|
|
3263
|
-
const queryParams = new URLSearchParams();
|
|
3264
|
-
if (params?.page)
|
|
3265
|
-
queryParams.set('page', params.page.toString());
|
|
3266
|
-
if (params?.limit)
|
|
3267
|
-
queryParams.set('limit', params.limit.toString());
|
|
3268
|
-
const query = queryParams.toString();
|
|
3269
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/companies/${companyId}/contacts${query ? `?${query}` : ''}`;
|
|
3270
|
-
return this.request(endpoint);
|
|
3271
|
-
}
|
|
3272
|
-
/**
|
|
3273
|
-
* Get deals/opportunities belonging to a company
|
|
3274
|
-
*/
|
|
3275
|
-
async getCompanyDeals(companyId, params) {
|
|
3276
|
-
const queryParams = new URLSearchParams();
|
|
3277
|
-
if (params?.page)
|
|
3278
|
-
queryParams.set('page', params.page.toString());
|
|
3279
|
-
if (params?.limit)
|
|
3280
|
-
queryParams.set('limit', params.limit.toString());
|
|
3281
|
-
const query = queryParams.toString();
|
|
3282
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/companies/${companyId}/deals${query ? `?${query}` : ''}`;
|
|
3283
|
-
return this.request(endpoint);
|
|
3284
|
-
}
|
|
3285
|
-
// ============================================
|
|
3286
|
-
// PIPELINES API
|
|
3287
|
-
// ============================================
|
|
3288
|
-
/**
|
|
3289
|
-
* Get all pipelines
|
|
3290
|
-
*/
|
|
3291
|
-
async getPipelines() {
|
|
3292
|
-
return this.request(`/api/workspaces/${this.workspaceId}/pipelines`);
|
|
3293
|
-
}
|
|
3294
|
-
/**
|
|
3295
|
-
* Get a single pipeline by ID
|
|
3296
|
-
*/
|
|
3297
|
-
async getPipeline(pipelineId) {
|
|
3298
|
-
return this.request(`/api/workspaces/${this.workspaceId}/pipelines/${pipelineId}`);
|
|
3299
|
-
}
|
|
3300
|
-
/**
|
|
3301
|
-
* Create a new pipeline
|
|
3302
|
-
*/
|
|
3303
|
-
async createPipeline(pipeline) {
|
|
3304
|
-
return this.request(`/api/workspaces/${this.workspaceId}/pipelines`, {
|
|
3305
|
-
method: 'POST',
|
|
3306
|
-
body: JSON.stringify(pipeline),
|
|
3307
|
-
});
|
|
3308
|
-
}
|
|
3309
|
-
/**
|
|
3310
|
-
* Update an existing pipeline
|
|
3311
|
-
*/
|
|
3312
|
-
async updatePipeline(pipelineId, updates) {
|
|
3313
|
-
return this.request(`/api/workspaces/${this.workspaceId}/pipelines/${pipelineId}`, {
|
|
3314
|
-
method: 'PUT',
|
|
3315
|
-
body: JSON.stringify(updates),
|
|
3316
|
-
});
|
|
3317
|
-
}
|
|
3318
|
-
/**
|
|
3319
|
-
* Delete a pipeline
|
|
3320
|
-
*/
|
|
3321
|
-
async deletePipeline(pipelineId) {
|
|
3322
|
-
return this.request(`/api/workspaces/${this.workspaceId}/pipelines/${pipelineId}`, {
|
|
3323
|
-
method: 'DELETE',
|
|
3324
|
-
});
|
|
3325
|
-
}
|
|
3326
|
-
// ============================================
|
|
3327
|
-
// TASKS API
|
|
3328
|
-
// ============================================
|
|
3329
|
-
/**
|
|
3330
|
-
* Get all tasks with pagination
|
|
3331
|
-
*/
|
|
3332
|
-
async getTasks(params) {
|
|
3333
|
-
const queryParams = new URLSearchParams();
|
|
3334
|
-
if (params?.page)
|
|
3335
|
-
queryParams.set('page', params.page.toString());
|
|
3336
|
-
if (params?.limit)
|
|
3337
|
-
queryParams.set('limit', params.limit.toString());
|
|
3338
|
-
if (params?.status)
|
|
3339
|
-
queryParams.set('status', params.status);
|
|
3340
|
-
if (params?.priority)
|
|
3341
|
-
queryParams.set('priority', params.priority);
|
|
3342
|
-
if (params?.contactId)
|
|
3343
|
-
queryParams.set('contactId', params.contactId);
|
|
3344
|
-
if (params?.companyId)
|
|
3345
|
-
queryParams.set('companyId', params.companyId);
|
|
3346
|
-
if (params?.opportunityId)
|
|
3347
|
-
queryParams.set('opportunityId', params.opportunityId);
|
|
3348
|
-
const query = queryParams.toString();
|
|
3349
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/tasks${query ? `?${query}` : ''}`;
|
|
3350
|
-
return this.request(endpoint);
|
|
3351
|
-
}
|
|
3352
|
-
/**
|
|
3353
|
-
* Get a single task by ID
|
|
3354
|
-
*/
|
|
3355
|
-
async getTask(taskId) {
|
|
3356
|
-
return this.request(`/api/workspaces/${this.workspaceId}/tasks/${taskId}`);
|
|
3357
|
-
}
|
|
3358
|
-
/**
|
|
3359
|
-
* Create a new task
|
|
3360
|
-
*/
|
|
3361
|
-
async createTask(task) {
|
|
3362
|
-
return this.request(`/api/workspaces/${this.workspaceId}/tasks`, {
|
|
3363
|
-
method: 'POST',
|
|
3364
|
-
body: JSON.stringify(task),
|
|
3365
|
-
});
|
|
3366
|
-
}
|
|
3367
|
-
/**
|
|
3368
|
-
* Update an existing task
|
|
3369
|
-
*/
|
|
3370
|
-
async updateTask(taskId, updates) {
|
|
3371
|
-
return this.request(`/api/workspaces/${this.workspaceId}/tasks/${taskId}`, {
|
|
3372
|
-
method: 'PUT',
|
|
3373
|
-
body: JSON.stringify(updates),
|
|
3374
|
-
});
|
|
3375
|
-
}
|
|
3376
|
-
/**
|
|
3377
|
-
* Mark a task as completed
|
|
3378
|
-
*/
|
|
3379
|
-
async completeTask(taskId) {
|
|
3380
|
-
return this.request(`/api/workspaces/${this.workspaceId}/tasks/${taskId}/complete`, {
|
|
3381
|
-
method: 'PATCH',
|
|
3382
|
-
});
|
|
3383
|
-
}
|
|
3384
|
-
/**
|
|
3385
|
-
* Delete a task
|
|
3386
|
-
*/
|
|
3387
|
-
async deleteTask(taskId) {
|
|
3388
|
-
return this.request(`/api/workspaces/${this.workspaceId}/tasks/${taskId}`, {
|
|
3389
|
-
method: 'DELETE',
|
|
3390
|
-
});
|
|
3391
|
-
}
|
|
3392
|
-
// ============================================
|
|
3393
|
-
// ACTIVITIES API
|
|
3394
|
-
// ============================================
|
|
3395
|
-
/**
|
|
3396
|
-
* Get activities for a contact
|
|
3397
|
-
*/
|
|
3398
|
-
async getContactActivities(contactId, params) {
|
|
3399
|
-
const queryParams = new URLSearchParams();
|
|
3400
|
-
if (params?.page)
|
|
3401
|
-
queryParams.set('page', params.page.toString());
|
|
3402
|
-
if (params?.limit)
|
|
3403
|
-
queryParams.set('limit', params.limit.toString());
|
|
3404
|
-
if (params?.type)
|
|
3405
|
-
queryParams.set('type', params.type);
|
|
3406
|
-
const query = queryParams.toString();
|
|
3407
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/contacts/${contactId}/activities${query ? `?${query}` : ''}`;
|
|
3408
|
-
return this.request(endpoint);
|
|
3409
|
-
}
|
|
3410
|
-
/**
|
|
3411
|
-
* Get activities for an opportunity/deal
|
|
3412
|
-
*/
|
|
3413
|
-
async getOpportunityActivities(opportunityId, params) {
|
|
3414
|
-
const queryParams = new URLSearchParams();
|
|
3415
|
-
if (params?.page)
|
|
3416
|
-
queryParams.set('page', params.page.toString());
|
|
3417
|
-
if (params?.limit)
|
|
3418
|
-
queryParams.set('limit', params.limit.toString());
|
|
3419
|
-
if (params?.type)
|
|
3420
|
-
queryParams.set('type', params.type);
|
|
3421
|
-
const query = queryParams.toString();
|
|
3422
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}/activities${query ? `?${query}` : ''}`;
|
|
3423
|
-
return this.request(endpoint);
|
|
3424
|
-
}
|
|
3425
|
-
/**
|
|
3426
|
-
* Create a new activity
|
|
3427
|
-
*/
|
|
3428
|
-
async createActivity(activity) {
|
|
3429
|
-
// Determine the correct endpoint based on related entity
|
|
3430
|
-
let endpoint;
|
|
3431
|
-
if (activity.opportunityId) {
|
|
3432
|
-
endpoint = `/api/workspaces/${this.workspaceId}/opportunities/${activity.opportunityId}/activities`;
|
|
3433
|
-
}
|
|
3434
|
-
else if (activity.contactId) {
|
|
3435
|
-
endpoint = `/api/workspaces/${this.workspaceId}/contacts/${activity.contactId}/activities`;
|
|
3436
|
-
}
|
|
3437
|
-
else {
|
|
3438
|
-
endpoint = `/api/workspaces/${this.workspaceId}/activities`;
|
|
3439
|
-
}
|
|
3440
|
-
return this.request(endpoint, {
|
|
3441
|
-
method: 'POST',
|
|
3442
|
-
body: JSON.stringify(activity),
|
|
3443
|
-
});
|
|
3444
|
-
}
|
|
3445
|
-
/**
|
|
3446
|
-
* Update an existing activity
|
|
3447
|
-
*/
|
|
3448
|
-
async updateActivity(activityId, updates) {
|
|
3449
|
-
return this.request(`/api/workspaces/${this.workspaceId}/activities/${activityId}`, {
|
|
3450
|
-
method: 'PATCH',
|
|
3451
|
-
body: JSON.stringify(updates),
|
|
3452
|
-
});
|
|
3453
|
-
}
|
|
3454
|
-
/**
|
|
3455
|
-
* Delete an activity
|
|
3456
|
-
*/
|
|
3457
|
-
async deleteActivity(activityId) {
|
|
3458
|
-
return this.request(`/api/workspaces/${this.workspaceId}/activities/${activityId}`, {
|
|
3459
|
-
method: 'DELETE',
|
|
3460
|
-
});
|
|
3461
|
-
}
|
|
3462
|
-
/**
|
|
3463
|
-
* Log a call activity
|
|
3464
|
-
*/
|
|
3465
|
-
async logCall(data) {
|
|
3466
|
-
return this.createActivity({
|
|
3467
|
-
type: 'call',
|
|
3468
|
-
title: `${data.direction === 'inbound' ? 'Inbound' : 'Outbound'} Call`,
|
|
3469
|
-
direction: data.direction,
|
|
3470
|
-
duration: data.duration,
|
|
3471
|
-
outcome: data.outcome,
|
|
3472
|
-
description: data.notes,
|
|
3473
|
-
contactId: data.contactId,
|
|
3474
|
-
opportunityId: data.opportunityId,
|
|
3475
|
-
});
|
|
3476
|
-
}
|
|
3477
|
-
/**
|
|
3478
|
-
* Log a meeting activity
|
|
3479
|
-
*/
|
|
3480
|
-
async logMeeting(data) {
|
|
3481
|
-
return this.createActivity({
|
|
3482
|
-
type: 'meeting',
|
|
3483
|
-
title: data.title,
|
|
3484
|
-
duration: data.duration,
|
|
3485
|
-
outcome: data.outcome,
|
|
3486
|
-
description: data.notes,
|
|
3487
|
-
contactId: data.contactId,
|
|
3488
|
-
opportunityId: data.opportunityId,
|
|
3489
|
-
});
|
|
3490
|
-
}
|
|
3491
|
-
/**
|
|
3492
|
-
* Add a note to a contact or opportunity
|
|
3493
|
-
*/
|
|
3494
|
-
async addNote(data) {
|
|
3495
|
-
return this.createActivity({
|
|
3496
|
-
type: 'note',
|
|
3497
|
-
title: 'Note',
|
|
3498
|
-
description: data.content,
|
|
3499
|
-
contactId: data.contactId,
|
|
3500
|
-
opportunityId: data.opportunityId,
|
|
3501
|
-
});
|
|
3502
|
-
}
|
|
3503
|
-
// ============================================
|
|
3504
|
-
// EMAIL TEMPLATES API
|
|
3505
|
-
// ============================================
|
|
3506
|
-
/**
|
|
3507
|
-
* Get all email templates
|
|
3508
|
-
*/
|
|
3509
|
-
async getEmailTemplates(params) {
|
|
3510
|
-
const queryParams = new URLSearchParams();
|
|
3511
|
-
if (params?.page)
|
|
3512
|
-
queryParams.set('page', params.page.toString());
|
|
3513
|
-
if (params?.limit)
|
|
3514
|
-
queryParams.set('limit', params.limit.toString());
|
|
3515
|
-
const query = queryParams.toString();
|
|
3516
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/email-templates${query ? `?${query}` : ''}`;
|
|
3517
|
-
return this.request(endpoint);
|
|
3518
|
-
}
|
|
3519
|
-
/**
|
|
3520
|
-
* Get a single email template by ID
|
|
3521
|
-
*/
|
|
3522
|
-
async getEmailTemplate(templateId) {
|
|
3523
|
-
return this.request(`/api/workspaces/${this.workspaceId}/email-templates/${templateId}`);
|
|
3524
|
-
}
|
|
3525
|
-
/**
|
|
3526
|
-
* Create a new email template
|
|
3527
|
-
*/
|
|
3528
|
-
async createEmailTemplate(template) {
|
|
3529
|
-
return this.request(`/api/workspaces/${this.workspaceId}/email-templates`, {
|
|
3530
|
-
method: 'POST',
|
|
3531
|
-
body: JSON.stringify(template),
|
|
3532
|
-
});
|
|
3533
|
-
}
|
|
3534
|
-
/**
|
|
3535
|
-
* Update an email template
|
|
3536
|
-
*/
|
|
3537
|
-
async updateEmailTemplate(templateId, updates) {
|
|
3538
|
-
return this.request(`/api/workspaces/${this.workspaceId}/email-templates/${templateId}`, {
|
|
3539
|
-
method: 'PUT',
|
|
3540
|
-
body: JSON.stringify(updates),
|
|
3541
|
-
});
|
|
3542
|
-
}
|
|
3543
|
-
/**
|
|
3544
|
-
* Delete an email template
|
|
3545
|
-
*/
|
|
3546
|
-
async deleteEmailTemplate(templateId) {
|
|
3547
|
-
return this.request(`/api/workspaces/${this.workspaceId}/email-templates/${templateId}`, {
|
|
3548
|
-
method: 'DELETE',
|
|
3549
|
-
});
|
|
3550
|
-
}
|
|
3551
|
-
/**
|
|
3552
|
-
* Send an email using a template
|
|
3553
|
-
*/
|
|
3554
|
-
async sendEmail(data) {
|
|
3555
|
-
return this.request(`/api/workspaces/${this.workspaceId}/emails/send`, {
|
|
3556
|
-
method: 'POST',
|
|
3557
|
-
body: JSON.stringify(data),
|
|
3558
|
-
});
|
|
3559
|
-
}
|
|
3560
|
-
// ============================================
|
|
3561
|
-
// READ-BACK / DATA RETRIEVAL API
|
|
3562
|
-
// ============================================
|
|
3563
|
-
/**
|
|
3564
|
-
* Get a contact by email address.
|
|
3565
|
-
* Returns the first matching contact from a search query.
|
|
3566
|
-
*/
|
|
3567
|
-
async getContactByEmail(email) {
|
|
3568
|
-
this.validateRequired('email', email, 'getContactByEmail');
|
|
3569
|
-
const queryParams = new URLSearchParams({ search: email, limit: '1' });
|
|
3570
|
-
return this.request(`/api/workspaces/${this.workspaceId}/contacts?${queryParams.toString()}`);
|
|
3571
|
-
}
|
|
3572
|
-
/**
|
|
3573
|
-
* Get activity timeline for a contact
|
|
3574
|
-
*/
|
|
3575
|
-
async getContactActivity(contactId, params) {
|
|
3576
|
-
this.validateRequired('contactId', contactId, 'getContactActivity');
|
|
3577
|
-
const queryParams = new URLSearchParams();
|
|
3578
|
-
if (params?.page)
|
|
3579
|
-
queryParams.set('page', params.page.toString());
|
|
3580
|
-
if (params?.limit)
|
|
3581
|
-
queryParams.set('limit', params.limit.toString());
|
|
3582
|
-
if (params?.type)
|
|
3583
|
-
queryParams.set('type', params.type);
|
|
3584
|
-
if (params?.startDate)
|
|
3585
|
-
queryParams.set('startDate', params.startDate);
|
|
3586
|
-
if (params?.endDate)
|
|
3587
|
-
queryParams.set('endDate', params.endDate);
|
|
3588
|
-
const query = queryParams.toString();
|
|
3589
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/contacts/${contactId}/activities${query ? `?${query}` : ''}`;
|
|
3590
|
-
return this.request(endpoint);
|
|
3591
|
-
}
|
|
3592
|
-
/**
|
|
3593
|
-
* Get engagement metrics for a contact (via their linked visitor data)
|
|
3594
|
-
*/
|
|
3595
|
-
async getContactEngagement(contactId) {
|
|
3596
|
-
this.validateRequired('contactId', contactId, 'getContactEngagement');
|
|
3597
|
-
return this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}/engagement`);
|
|
3598
|
-
}
|
|
3599
|
-
/**
|
|
3600
|
-
* Get a full timeline for a contact including events, activities, and opportunities
|
|
3601
|
-
*/
|
|
3602
|
-
async getContactTimeline(contactId, params) {
|
|
3603
|
-
this.validateRequired('contactId', contactId, 'getContactTimeline');
|
|
3604
|
-
const queryParams = new URLSearchParams();
|
|
3605
|
-
if (params?.page)
|
|
3606
|
-
queryParams.set('page', params.page.toString());
|
|
3607
|
-
if (params?.limit)
|
|
3608
|
-
queryParams.set('limit', params.limit.toString());
|
|
3609
|
-
const query = queryParams.toString();
|
|
3610
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/contacts/${contactId}/timeline${query ? `?${query}` : ''}`;
|
|
3611
|
-
return this.request(endpoint);
|
|
3612
|
-
}
|
|
3613
|
-
/**
|
|
3614
|
-
* Search contacts with advanced filters
|
|
3615
|
-
*/
|
|
3616
|
-
async searchContacts(query, filters) {
|
|
3617
|
-
const queryParams = new URLSearchParams();
|
|
3618
|
-
queryParams.set('search', query);
|
|
3619
|
-
if (filters?.status)
|
|
3620
|
-
queryParams.set('status', filters.status);
|
|
3621
|
-
if (filters?.lifecycleStage)
|
|
3622
|
-
queryParams.set('lifecycleStage', filters.lifecycleStage);
|
|
3623
|
-
if (filters?.source)
|
|
3624
|
-
queryParams.set('source', filters.source);
|
|
3625
|
-
if (filters?.tags)
|
|
3626
|
-
queryParams.set('tags', filters.tags.join(','));
|
|
3627
|
-
if (filters?.page)
|
|
3628
|
-
queryParams.set('page', filters.page.toString());
|
|
3629
|
-
if (filters?.limit)
|
|
3630
|
-
queryParams.set('limit', filters.limit.toString());
|
|
3631
|
-
const qs = queryParams.toString();
|
|
3632
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/contacts${qs ? `?${qs}` : ''}`;
|
|
3633
|
-
return this.request(endpoint);
|
|
3634
|
-
}
|
|
3635
|
-
// ============================================
|
|
3636
|
-
// WEBHOOK MANAGEMENT API
|
|
3637
|
-
// ============================================
|
|
3638
|
-
/**
|
|
3639
|
-
* List all webhook subscriptions
|
|
3640
|
-
*/
|
|
3641
|
-
async listWebhooks(params) {
|
|
3642
|
-
const queryParams = new URLSearchParams();
|
|
3643
|
-
if (params?.page)
|
|
3644
|
-
queryParams.set('page', params.page.toString());
|
|
3645
|
-
if (params?.limit)
|
|
3646
|
-
queryParams.set('limit', params.limit.toString());
|
|
3647
|
-
const query = queryParams.toString();
|
|
3648
|
-
return this.request(`/api/workspaces/${this.workspaceId}/webhooks${query ? `?${query}` : ''}`);
|
|
3649
|
-
}
|
|
3650
|
-
/**
|
|
3651
|
-
* Create a new webhook subscription
|
|
3652
|
-
*/
|
|
3653
|
-
async createWebhook(data) {
|
|
3654
|
-
return this.request(`/api/workspaces/${this.workspaceId}/webhooks`, {
|
|
3655
|
-
method: 'POST',
|
|
3656
|
-
body: JSON.stringify(data),
|
|
3657
|
-
});
|
|
3658
|
-
}
|
|
3659
|
-
/**
|
|
3660
|
-
* Delete a webhook subscription
|
|
3661
|
-
*/
|
|
3662
|
-
async deleteWebhook(webhookId) {
|
|
3663
|
-
this.validateRequired('webhookId', webhookId, 'deleteWebhook');
|
|
3664
|
-
return this.request(`/api/workspaces/${this.workspaceId}/webhooks/${webhookId}`, {
|
|
3665
|
-
method: 'DELETE',
|
|
3666
|
-
});
|
|
3667
|
-
}
|
|
3668
|
-
// ============================================
|
|
3669
|
-
// EVENT TRIGGERS API (delegated to triggers manager)
|
|
3670
|
-
// ============================================
|
|
3671
|
-
/**
|
|
3672
|
-
* Get all event triggers
|
|
3673
|
-
*/
|
|
3674
|
-
async getEventTriggers() {
|
|
3675
|
-
return this.triggers.getTriggers();
|
|
3676
|
-
}
|
|
3677
|
-
/**
|
|
3678
|
-
* Create a new event trigger
|
|
3679
|
-
*/
|
|
3680
|
-
async createEventTrigger(trigger) {
|
|
3681
|
-
return this.triggers.createTrigger(trigger);
|
|
3682
|
-
}
|
|
3683
|
-
/**
|
|
3684
|
-
* Update an event trigger
|
|
3685
|
-
*/
|
|
3686
|
-
async updateEventTrigger(triggerId, updates) {
|
|
3687
|
-
return this.triggers.updateTrigger(triggerId, updates);
|
|
3688
|
-
}
|
|
3689
|
-
/**
|
|
3690
|
-
* Delete an event trigger
|
|
3691
|
-
*/
|
|
3692
|
-
async deleteEventTrigger(triggerId) {
|
|
3693
|
-
return this.triggers.deleteTrigger(triggerId);
|
|
3694
|
-
}
|
|
3695
|
-
}
|
|
3696
|
-
|
|
3697
2512
|
/**
|
|
3698
2513
|
* Clianta SDK - Main Tracker Class
|
|
3699
2514
|
* @see SDK_VERSION in core/config.ts
|
|
@@ -3707,10 +2522,16 @@ class Tracker {
|
|
|
3707
2522
|
this.isInitialized = false;
|
|
3708
2523
|
/** contactId after a successful identify() call */
|
|
3709
2524
|
this.contactId = null;
|
|
2525
|
+
/** groupId after a successful group() call */
|
|
2526
|
+
this.groupId = null;
|
|
3710
2527
|
/** Pending identify retry on next flush */
|
|
3711
2528
|
this.pendingIdentify = null;
|
|
3712
2529
|
/** Registered event schemas for validation */
|
|
3713
2530
|
this.eventSchemas = new Map();
|
|
2531
|
+
/** Event middleware pipeline */
|
|
2532
|
+
this.middlewares = [];
|
|
2533
|
+
/** Ready callbacks */
|
|
2534
|
+
this.readyCallbacks = [];
|
|
3714
2535
|
if (!workspaceId) {
|
|
3715
2536
|
throw new Error('[Clianta] Workspace ID is required');
|
|
3716
2537
|
}
|
|
@@ -3741,15 +2562,22 @@ class Tracker {
|
|
|
3741
2562
|
typeof window !== 'undefined' &&
|
|
3742
2563
|
!window.location.hostname.includes('localhost') &&
|
|
3743
2564
|
!window.location.hostname.includes('127.0.0.1')) {
|
|
3744
|
-
logger.warn('apiEndpoint uses HTTP — events
|
|
3745
|
-
}
|
|
3746
|
-
if (this.config.apiKey && typeof window !== 'undefined') {
|
|
3747
|
-
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.');
|
|
3748
2566
|
}
|
|
3749
2567
|
// Initialize plugins
|
|
3750
2568
|
this.initPlugins();
|
|
3751
2569
|
this.isInitialized = true;
|
|
3752
2570
|
logger.info('SDK initialized successfully');
|
|
2571
|
+
// Fire ready callbacks
|
|
2572
|
+
for (const cb of this.readyCallbacks) {
|
|
2573
|
+
try {
|
|
2574
|
+
cb();
|
|
2575
|
+
}
|
|
2576
|
+
catch (e) {
|
|
2577
|
+
logger.error('onReady callback error:', e);
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
this.readyCallbacks = [];
|
|
3753
2581
|
}
|
|
3754
2582
|
/**
|
|
3755
2583
|
* Create visitor ID based on storage mode
|
|
@@ -3862,6 +2690,10 @@ class Tracker {
|
|
|
3862
2690
|
if (this.contactId) {
|
|
3863
2691
|
event.contactId = this.contactId;
|
|
3864
2692
|
}
|
|
2693
|
+
// Attach groupId if known (from a prior group() call)
|
|
2694
|
+
if (this.groupId) {
|
|
2695
|
+
event.groupId = this.groupId;
|
|
2696
|
+
}
|
|
3865
2697
|
// Validate event against registered schema (debug mode only)
|
|
3866
2698
|
this.validateEventSchema(eventType, properties);
|
|
3867
2699
|
// Check consent before tracking
|
|
@@ -3875,8 +2707,11 @@ class Tracker {
|
|
|
3875
2707
|
logger.debug('Event dropped (no consent):', eventName);
|
|
3876
2708
|
return;
|
|
3877
2709
|
}
|
|
3878
|
-
|
|
3879
|
-
|
|
2710
|
+
// Run event through middleware pipeline
|
|
2711
|
+
this.runMiddleware(event, () => {
|
|
2712
|
+
this.queue.push(event);
|
|
2713
|
+
logger.debug('Event tracked:', eventName, properties);
|
|
2714
|
+
});
|
|
3880
2715
|
}
|
|
3881
2716
|
/**
|
|
3882
2717
|
* Track a page view
|
|
@@ -3924,123 +2759,176 @@ class Tracker {
|
|
|
3924
2759
|
}
|
|
3925
2760
|
}
|
|
3926
2761
|
/**
|
|
3927
|
-
*
|
|
3928
|
-
* Convenience proxy to CRMClient.sendEvent() — requires apiKey in config.
|
|
2762
|
+
* Retry pending identify call
|
|
3929
2763
|
*/
|
|
3930
|
-
async
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
2764
|
+
async retryPendingIdentify() {
|
|
2765
|
+
if (!this.pendingIdentify)
|
|
2766
|
+
return;
|
|
2767
|
+
const { email, traits } = this.pendingIdentify;
|
|
2768
|
+
this.pendingIdentify = null;
|
|
2769
|
+
await this.identify(email, traits);
|
|
2770
|
+
}
|
|
2771
|
+
/**
|
|
2772
|
+
* Update consent state
|
|
2773
|
+
*/
|
|
2774
|
+
consent(state) {
|
|
2775
|
+
this.consentManager.update(state);
|
|
3938
2776
|
}
|
|
3939
2777
|
/**
|
|
3940
|
-
* Get
|
|
3941
|
-
* Returns visitor data and linked contact info if identified.
|
|
3942
|
-
* Only returns data for the current visitor (privacy-safe for frontend).
|
|
2778
|
+
* Get current consent state
|
|
3943
2779
|
*/
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
logger.warn('SDK not initialized');
|
|
3947
|
-
return null;
|
|
3948
|
-
}
|
|
3949
|
-
const result = await this.transport.fetchData(`/api/public/track/visitor/${this.workspaceId}/${this.visitorId}/profile`);
|
|
3950
|
-
if (result.success && result.data) {
|
|
3951
|
-
logger.debug('Visitor profile fetched:', result.data);
|
|
3952
|
-
return result.data;
|
|
3953
|
-
}
|
|
3954
|
-
logger.warn('Failed to fetch visitor profile:', result.error);
|
|
3955
|
-
return null;
|
|
2780
|
+
getConsentState() {
|
|
2781
|
+
return this.consentManager.getState();
|
|
3956
2782
|
}
|
|
3957
2783
|
/**
|
|
3958
|
-
*
|
|
3959
|
-
* Returns paginated list of tracking events for this visitor.
|
|
2784
|
+
* Toggle debug mode
|
|
3960
2785
|
*/
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
return null;
|
|
3965
|
-
}
|
|
3966
|
-
const params = {};
|
|
3967
|
-
if (options?.page)
|
|
3968
|
-
params.page = options.page.toString();
|
|
3969
|
-
if (options?.limit)
|
|
3970
|
-
params.limit = options.limit.toString();
|
|
3971
|
-
if (options?.eventType)
|
|
3972
|
-
params.eventType = options.eventType;
|
|
3973
|
-
if (options?.startDate)
|
|
3974
|
-
params.startDate = options.startDate;
|
|
3975
|
-
if (options?.endDate)
|
|
3976
|
-
params.endDate = options.endDate;
|
|
3977
|
-
const result = await this.transport.fetchData(`/api/public/track/visitor/${this.workspaceId}/${this.visitorId}/activity`, params);
|
|
3978
|
-
if (result.success && result.data) {
|
|
3979
|
-
return result.data;
|
|
3980
|
-
}
|
|
3981
|
-
logger.warn('Failed to fetch visitor activity:', result.error);
|
|
3982
|
-
return null;
|
|
2786
|
+
debug(enabled) {
|
|
2787
|
+
logger.enabled = enabled;
|
|
2788
|
+
logger.info(`Debug mode ${enabled ? 'enabled' : 'disabled'}`);
|
|
3983
2789
|
}
|
|
2790
|
+
// ============================================
|
|
2791
|
+
// GROUP, ALIAS, SCREEN
|
|
2792
|
+
// ============================================
|
|
3984
2793
|
/**
|
|
3985
|
-
*
|
|
3986
|
-
*
|
|
2794
|
+
* Associate the current visitor with a group (company/account).
|
|
2795
|
+
* The groupId will be attached to all subsequent track() calls.
|
|
3987
2796
|
*/
|
|
3988
|
-
|
|
3989
|
-
if (!
|
|
3990
|
-
logger.warn('
|
|
3991
|
-
return
|
|
3992
|
-
}
|
|
3993
|
-
const result = await this.transport.fetchData(`/api/public/track/visitor/${this.workspaceId}/${this.visitorId}/timeline`);
|
|
3994
|
-
if (result.success && result.data) {
|
|
3995
|
-
return result.data;
|
|
2797
|
+
group(groupId, traits = {}) {
|
|
2798
|
+
if (!groupId) {
|
|
2799
|
+
logger.warn('groupId is required for group()');
|
|
2800
|
+
return;
|
|
3996
2801
|
}
|
|
3997
|
-
|
|
3998
|
-
|
|
2802
|
+
this.groupId = groupId;
|
|
2803
|
+
logger.info('Visitor grouped:', groupId);
|
|
2804
|
+
this.track('group', 'Group Identified', {
|
|
2805
|
+
groupId,
|
|
2806
|
+
...traits,
|
|
2807
|
+
});
|
|
3999
2808
|
}
|
|
4000
2809
|
/**
|
|
4001
|
-
*
|
|
4002
|
-
*
|
|
2810
|
+
* Merge two visitor identities.
|
|
2811
|
+
* Links `previousId` (typically the anonymous visitor) to `newId` (the known user).
|
|
2812
|
+
* If `previousId` is omitted, the current visitorId is used.
|
|
4003
2813
|
*/
|
|
4004
|
-
async
|
|
4005
|
-
if (!
|
|
4006
|
-
logger.warn('
|
|
4007
|
-
return
|
|
2814
|
+
async alias(newId, previousId) {
|
|
2815
|
+
if (!newId) {
|
|
2816
|
+
logger.warn('newId is required for alias()');
|
|
2817
|
+
return false;
|
|
4008
2818
|
}
|
|
4009
|
-
const
|
|
4010
|
-
|
|
4011
|
-
|
|
2819
|
+
const prevId = previousId || this.visitorId;
|
|
2820
|
+
logger.info('Aliasing visitor:', { from: prevId, to: newId });
|
|
2821
|
+
try {
|
|
2822
|
+
const url = `${this.config.apiEndpoint}/api/public/track/alias`;
|
|
2823
|
+
const response = await fetch(url, {
|
|
2824
|
+
method: 'POST',
|
|
2825
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2826
|
+
body: JSON.stringify({
|
|
2827
|
+
workspaceId: this.workspaceId,
|
|
2828
|
+
previousId: prevId,
|
|
2829
|
+
newId,
|
|
2830
|
+
}),
|
|
2831
|
+
});
|
|
2832
|
+
if (response.ok) {
|
|
2833
|
+
logger.info('Alias successful');
|
|
2834
|
+
return true;
|
|
2835
|
+
}
|
|
2836
|
+
logger.error('Alias failed:', response.status);
|
|
2837
|
+
return false;
|
|
2838
|
+
}
|
|
2839
|
+
catch (error) {
|
|
2840
|
+
logger.error('Alias request failed:', error);
|
|
2841
|
+
return false;
|
|
4012
2842
|
}
|
|
4013
|
-
logger.warn('Failed to fetch visitor engagement:', result.error);
|
|
4014
|
-
return null;
|
|
4015
2843
|
}
|
|
4016
2844
|
/**
|
|
4017
|
-
*
|
|
2845
|
+
* Track a screen view (for mobile-first PWAs and SPAs).
|
|
2846
|
+
* Similar to page() but semantically for app screens.
|
|
4018
2847
|
*/
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
await this.identify(email, traits);
|
|
2848
|
+
screen(name, properties = {}) {
|
|
2849
|
+
this.track('screen_view', name, {
|
|
2850
|
+
...properties,
|
|
2851
|
+
screenName: name,
|
|
2852
|
+
});
|
|
4025
2853
|
}
|
|
2854
|
+
// ============================================
|
|
2855
|
+
// MIDDLEWARE
|
|
2856
|
+
// ============================================
|
|
4026
2857
|
/**
|
|
4027
|
-
*
|
|
2858
|
+
* Register event middleware.
|
|
2859
|
+
* Middleware functions receive the event and a `next` callback.
|
|
2860
|
+
* Call `next()` to pass the event through, or don't call it to drop the event.
|
|
2861
|
+
*
|
|
2862
|
+
* @example
|
|
2863
|
+
* tracker.use((event, next) => {
|
|
2864
|
+
* // Strip PII from events
|
|
2865
|
+
* delete event.properties.email;
|
|
2866
|
+
* next(); // pass it through
|
|
2867
|
+
* });
|
|
4028
2868
|
*/
|
|
4029
|
-
|
|
4030
|
-
this.
|
|
2869
|
+
use(middleware) {
|
|
2870
|
+
this.middlewares.push(middleware);
|
|
2871
|
+
logger.debug('Middleware registered');
|
|
4031
2872
|
}
|
|
4032
2873
|
/**
|
|
4033
|
-
*
|
|
2874
|
+
* Run event through the middleware pipeline.
|
|
2875
|
+
* Executes each middleware in order; if any skips `next()`, the event is dropped.
|
|
4034
2876
|
*/
|
|
4035
|
-
|
|
4036
|
-
|
|
2877
|
+
runMiddleware(event, finalCallback) {
|
|
2878
|
+
if (this.middlewares.length === 0) {
|
|
2879
|
+
finalCallback();
|
|
2880
|
+
return;
|
|
2881
|
+
}
|
|
2882
|
+
let index = 0;
|
|
2883
|
+
const middlewares = this.middlewares;
|
|
2884
|
+
const next = () => {
|
|
2885
|
+
index++;
|
|
2886
|
+
if (index < middlewares.length) {
|
|
2887
|
+
try {
|
|
2888
|
+
middlewares[index](event, next);
|
|
2889
|
+
}
|
|
2890
|
+
catch (e) {
|
|
2891
|
+
logger.error('Middleware error:', e);
|
|
2892
|
+
finalCallback();
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
else {
|
|
2896
|
+
finalCallback();
|
|
2897
|
+
}
|
|
2898
|
+
};
|
|
2899
|
+
try {
|
|
2900
|
+
middlewares[0](event, next);
|
|
2901
|
+
}
|
|
2902
|
+
catch (e) {
|
|
2903
|
+
logger.error('Middleware error:', e);
|
|
2904
|
+
finalCallback();
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
// ============================================
|
|
2908
|
+
// LIFECYCLE
|
|
2909
|
+
// ============================================
|
|
2910
|
+
/**
|
|
2911
|
+
* Register a callback to be invoked when the SDK is fully initialized.
|
|
2912
|
+
* If already initialized, the callback fires immediately.
|
|
2913
|
+
*/
|
|
2914
|
+
onReady(callback) {
|
|
2915
|
+
if (this.isInitialized) {
|
|
2916
|
+
try {
|
|
2917
|
+
callback();
|
|
2918
|
+
}
|
|
2919
|
+
catch (e) {
|
|
2920
|
+
logger.error('onReady callback error:', e);
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
else {
|
|
2924
|
+
this.readyCallbacks.push(callback);
|
|
2925
|
+
}
|
|
4037
2926
|
}
|
|
4038
2927
|
/**
|
|
4039
|
-
*
|
|
2928
|
+
* Check if the SDK is fully initialized and ready.
|
|
4040
2929
|
*/
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
logger.info(`Debug mode ${enabled ? 'enabled' : 'disabled'}`);
|
|
2930
|
+
isReady() {
|
|
2931
|
+
return this.isInitialized;
|
|
4044
2932
|
}
|
|
4045
2933
|
/**
|
|
4046
2934
|
* Register a schema for event validation.
|
|
@@ -4279,7 +3167,12 @@ class Tracker {
|
|
|
4279
3167
|
|
|
4280
3168
|
/**
|
|
4281
3169
|
* Clianta SDK
|
|
4282
|
-
*
|
|
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
|
+
*
|
|
4283
3176
|
* @see SDK_VERSION in core/config.ts
|
|
4284
3177
|
*/
|
|
4285
3178
|
// Global instance cache
|
|
@@ -4321,21 +3214,17 @@ function clianta(workspaceId, config) {
|
|
|
4321
3214
|
globalInstance = new Tracker(workspaceId, config);
|
|
4322
3215
|
return globalInstance;
|
|
4323
3216
|
}
|
|
4324
|
-
// Attach to window for <script> usage
|
|
3217
|
+
// Attach to window for <script> tag usage
|
|
4325
3218
|
if (typeof window !== 'undefined') {
|
|
4326
3219
|
window.clianta = clianta;
|
|
4327
3220
|
window.Clianta = {
|
|
4328
3221
|
clianta,
|
|
4329
3222
|
Tracker,
|
|
4330
|
-
CRMClient,
|
|
4331
3223
|
ConsentManager,
|
|
4332
|
-
EventTriggersManager,
|
|
4333
3224
|
};
|
|
4334
3225
|
}
|
|
4335
3226
|
|
|
4336
|
-
exports.CRMClient = CRMClient;
|
|
4337
3227
|
exports.ConsentManager = ConsentManager;
|
|
4338
|
-
exports.EventTriggersManager = EventTriggersManager;
|
|
4339
3228
|
exports.SDK_VERSION = SDK_VERSION;
|
|
4340
3229
|
exports.Tracker = Tracker;
|
|
4341
3230
|
exports.clianta = clianta;
|