@clianta/sdk 1.5.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/angular.cjs.js +259 -45
- package/dist/angular.cjs.js.map +1 -1
- package/dist/angular.d.ts +69 -0
- package/dist/angular.esm.js +259 -45
- package/dist/angular.esm.js.map +1 -1
- package/dist/clianta.cjs.js +259 -45
- package/dist/clianta.cjs.js.map +1 -1
- package/dist/clianta.esm.js +259 -45
- package/dist/clianta.esm.js.map +1 -1
- package/dist/clianta.umd.js +259 -45
- 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 +150 -7
- package/dist/react.cjs.js +326 -57
- package/dist/react.cjs.js.map +1 -1
- package/dist/react.d.ts +89 -3
- package/dist/react.esm.js +327 -59
- package/dist/react.esm.js.map +1 -1
- package/dist/svelte.cjs.js +259 -45
- package/dist/svelte.cjs.js.map +1 -1
- package/dist/svelte.d.ts +69 -0
- package/dist/svelte.esm.js +259 -45
- package/dist/svelte.esm.js.map +1 -1
- package/dist/vue.cjs.js +259 -45
- package/dist/vue.cjs.js.map +1 -1
- package/dist/vue.d.ts +69 -0
- package/dist/vue.esm.js +259 -45
- package/dist/vue.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/react.cjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Clianta SDK v1.
|
|
2
|
+
* Clianta SDK v1.6.0
|
|
3
3
|
* (c) 2026 Clianta
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
@@ -13,7 +13,7 @@ var react = require('react');
|
|
|
13
13
|
* @see SDK_VERSION in core/config.ts
|
|
14
14
|
*/
|
|
15
15
|
/** SDK Version */
|
|
16
|
-
const SDK_VERSION = '1.
|
|
16
|
+
const SDK_VERSION = '1.6.0';
|
|
17
17
|
/** Default API endpoint — reads from env or falls back to localhost */
|
|
18
18
|
const getDefaultApiEndpoint = () => {
|
|
19
19
|
// Build-time env var (works with Next.js, Vite, CRA, etc.)
|
|
@@ -3695,6 +3695,85 @@ class CRMClient {
|
|
|
3695
3695
|
}
|
|
3696
3696
|
}
|
|
3697
3697
|
|
|
3698
|
+
/**
|
|
3699
|
+
* Privacy-safe visitor API client.
|
|
3700
|
+
* All methods return data for the current visitor only (no cross-visitor access).
|
|
3701
|
+
*/
|
|
3702
|
+
class VisitorClient {
|
|
3703
|
+
constructor(transport, workspaceId, visitorId) {
|
|
3704
|
+
this.transport = transport;
|
|
3705
|
+
this.workspaceId = workspaceId;
|
|
3706
|
+
this.visitorId = visitorId;
|
|
3707
|
+
}
|
|
3708
|
+
/** Update visitorId (e.g. after reset) */
|
|
3709
|
+
setVisitorId(id) {
|
|
3710
|
+
this.visitorId = id;
|
|
3711
|
+
}
|
|
3712
|
+
basePath() {
|
|
3713
|
+
return `/api/public/track/visitor/${this.workspaceId}/${this.visitorId}`;
|
|
3714
|
+
}
|
|
3715
|
+
/**
|
|
3716
|
+
* Get the current visitor's profile from the CRM.
|
|
3717
|
+
* Returns visitor data and linked contact info if identified.
|
|
3718
|
+
*/
|
|
3719
|
+
async getProfile() {
|
|
3720
|
+
const result = await this.transport.fetchData(`${this.basePath()}/profile`);
|
|
3721
|
+
if (result.success && result.data) {
|
|
3722
|
+
logger.debug('Visitor profile fetched:', result.data);
|
|
3723
|
+
return result.data;
|
|
3724
|
+
}
|
|
3725
|
+
logger.warn('Failed to fetch visitor profile:', result.error);
|
|
3726
|
+
return null;
|
|
3727
|
+
}
|
|
3728
|
+
/**
|
|
3729
|
+
* Get the current visitor's recent activity/events.
|
|
3730
|
+
* Returns paginated list of tracking events.
|
|
3731
|
+
*/
|
|
3732
|
+
async getActivity(options) {
|
|
3733
|
+
const params = {};
|
|
3734
|
+
if (options?.page)
|
|
3735
|
+
params.page = options.page.toString();
|
|
3736
|
+
if (options?.limit)
|
|
3737
|
+
params.limit = options.limit.toString();
|
|
3738
|
+
if (options?.eventType)
|
|
3739
|
+
params.eventType = options.eventType;
|
|
3740
|
+
if (options?.startDate)
|
|
3741
|
+
params.startDate = options.startDate;
|
|
3742
|
+
if (options?.endDate)
|
|
3743
|
+
params.endDate = options.endDate;
|
|
3744
|
+
const result = await this.transport.fetchData(`${this.basePath()}/activity`, params);
|
|
3745
|
+
if (result.success && result.data) {
|
|
3746
|
+
return result.data;
|
|
3747
|
+
}
|
|
3748
|
+
logger.warn('Failed to fetch visitor activity:', result.error);
|
|
3749
|
+
return null;
|
|
3750
|
+
}
|
|
3751
|
+
/**
|
|
3752
|
+
* Get a summarized journey timeline for the current visitor.
|
|
3753
|
+
* Includes top pages, sessions, time spent, and recent activities.
|
|
3754
|
+
*/
|
|
3755
|
+
async getTimeline() {
|
|
3756
|
+
const result = await this.transport.fetchData(`${this.basePath()}/timeline`);
|
|
3757
|
+
if (result.success && result.data) {
|
|
3758
|
+
return result.data;
|
|
3759
|
+
}
|
|
3760
|
+
logger.warn('Failed to fetch visitor timeline:', result.error);
|
|
3761
|
+
return null;
|
|
3762
|
+
}
|
|
3763
|
+
/**
|
|
3764
|
+
* Get engagement metrics for the current visitor.
|
|
3765
|
+
* Includes time on site, page views, bounce rate, and engagement score.
|
|
3766
|
+
*/
|
|
3767
|
+
async getEngagement() {
|
|
3768
|
+
const result = await this.transport.fetchData(`${this.basePath()}/engagement`);
|
|
3769
|
+
if (result.success && result.data) {
|
|
3770
|
+
return result.data;
|
|
3771
|
+
}
|
|
3772
|
+
logger.warn('Failed to fetch visitor engagement:', result.error);
|
|
3773
|
+
return null;
|
|
3774
|
+
}
|
|
3775
|
+
}
|
|
3776
|
+
|
|
3698
3777
|
/**
|
|
3699
3778
|
* Clianta SDK - Main Tracker Class
|
|
3700
3779
|
* @see SDK_VERSION in core/config.ts
|
|
@@ -3708,10 +3787,16 @@ class Tracker {
|
|
|
3708
3787
|
this.isInitialized = false;
|
|
3709
3788
|
/** contactId after a successful identify() call */
|
|
3710
3789
|
this.contactId = null;
|
|
3790
|
+
/** groupId after a successful group() call */
|
|
3791
|
+
this.groupId = null;
|
|
3711
3792
|
/** Pending identify retry on next flush */
|
|
3712
3793
|
this.pendingIdentify = null;
|
|
3713
3794
|
/** Registered event schemas for validation */
|
|
3714
3795
|
this.eventSchemas = new Map();
|
|
3796
|
+
/** Event middleware pipeline */
|
|
3797
|
+
this.middlewares = [];
|
|
3798
|
+
/** Ready callbacks */
|
|
3799
|
+
this.readyCallbacks = [];
|
|
3715
3800
|
if (!workspaceId) {
|
|
3716
3801
|
throw new Error('[Clianta] Workspace ID is required');
|
|
3717
3802
|
}
|
|
@@ -3737,6 +3822,8 @@ class Tracker {
|
|
|
3737
3822
|
this.visitorId = this.createVisitorId();
|
|
3738
3823
|
this.sessionId = this.createSessionId();
|
|
3739
3824
|
logger.debug('IDs created', { visitorId: this.visitorId, sessionId: this.sessionId });
|
|
3825
|
+
// Initialize visitor API client
|
|
3826
|
+
this.visitor = new VisitorClient(this.transport, this.workspaceId, this.visitorId);
|
|
3740
3827
|
// Security warnings
|
|
3741
3828
|
if (this.config.apiEndpoint.startsWith('http://') &&
|
|
3742
3829
|
typeof window !== 'undefined' &&
|
|
@@ -3751,6 +3838,16 @@ class Tracker {
|
|
|
3751
3838
|
this.initPlugins();
|
|
3752
3839
|
this.isInitialized = true;
|
|
3753
3840
|
logger.info('SDK initialized successfully');
|
|
3841
|
+
// Fire ready callbacks
|
|
3842
|
+
for (const cb of this.readyCallbacks) {
|
|
3843
|
+
try {
|
|
3844
|
+
cb();
|
|
3845
|
+
}
|
|
3846
|
+
catch (e) {
|
|
3847
|
+
logger.error('onReady callback error:', e);
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
this.readyCallbacks = [];
|
|
3754
3851
|
}
|
|
3755
3852
|
/**
|
|
3756
3853
|
* Create visitor ID based on storage mode
|
|
@@ -3863,6 +3960,10 @@ class Tracker {
|
|
|
3863
3960
|
if (this.contactId) {
|
|
3864
3961
|
event.contactId = this.contactId;
|
|
3865
3962
|
}
|
|
3963
|
+
// Attach groupId if known (from a prior group() call)
|
|
3964
|
+
if (this.groupId) {
|
|
3965
|
+
event.groupId = this.groupId;
|
|
3966
|
+
}
|
|
3866
3967
|
// Validate event against registered schema (debug mode only)
|
|
3867
3968
|
this.validateEventSchema(eventType, properties);
|
|
3868
3969
|
// Check consent before tracking
|
|
@@ -3876,8 +3977,11 @@ class Tracker {
|
|
|
3876
3977
|
logger.debug('Event dropped (no consent):', eventName);
|
|
3877
3978
|
return;
|
|
3878
3979
|
}
|
|
3879
|
-
|
|
3880
|
-
|
|
3980
|
+
// Run event through middleware pipeline
|
|
3981
|
+
this.runMiddleware(event, () => {
|
|
3982
|
+
this.queue.push(event);
|
|
3983
|
+
logger.debug('Event tracked:', eventName, properties);
|
|
3984
|
+
});
|
|
3881
3985
|
}
|
|
3882
3986
|
/**
|
|
3883
3987
|
* Track a page view
|
|
@@ -3939,80 +4043,47 @@ class Tracker {
|
|
|
3939
4043
|
}
|
|
3940
4044
|
/**
|
|
3941
4045
|
* Get the current visitor's profile from the CRM.
|
|
3942
|
-
*
|
|
3943
|
-
* Only returns data for the current visitor (privacy-safe for frontend).
|
|
4046
|
+
* @deprecated Use `tracker.visitor.getProfile()` instead.
|
|
3944
4047
|
*/
|
|
3945
4048
|
async getVisitorProfile() {
|
|
3946
4049
|
if (!this.isInitialized) {
|
|
3947
4050
|
logger.warn('SDK not initialized');
|
|
3948
4051
|
return null;
|
|
3949
4052
|
}
|
|
3950
|
-
|
|
3951
|
-
if (result.success && result.data) {
|
|
3952
|
-
logger.debug('Visitor profile fetched:', result.data);
|
|
3953
|
-
return result.data;
|
|
3954
|
-
}
|
|
3955
|
-
logger.warn('Failed to fetch visitor profile:', result.error);
|
|
3956
|
-
return null;
|
|
4053
|
+
return this.visitor.getProfile();
|
|
3957
4054
|
}
|
|
3958
4055
|
/**
|
|
3959
4056
|
* Get the current visitor's recent activity/events.
|
|
3960
|
-
*
|
|
4057
|
+
* @deprecated Use `tracker.visitor.getActivity()` instead.
|
|
3961
4058
|
*/
|
|
3962
4059
|
async getVisitorActivity(options) {
|
|
3963
4060
|
if (!this.isInitialized) {
|
|
3964
4061
|
logger.warn('SDK not initialized');
|
|
3965
4062
|
return null;
|
|
3966
4063
|
}
|
|
3967
|
-
|
|
3968
|
-
if (options?.page)
|
|
3969
|
-
params.page = options.page.toString();
|
|
3970
|
-
if (options?.limit)
|
|
3971
|
-
params.limit = options.limit.toString();
|
|
3972
|
-
if (options?.eventType)
|
|
3973
|
-
params.eventType = options.eventType;
|
|
3974
|
-
if (options?.startDate)
|
|
3975
|
-
params.startDate = options.startDate;
|
|
3976
|
-
if (options?.endDate)
|
|
3977
|
-
params.endDate = options.endDate;
|
|
3978
|
-
const result = await this.transport.fetchData(`/api/public/track/visitor/${this.workspaceId}/${this.visitorId}/activity`, params);
|
|
3979
|
-
if (result.success && result.data) {
|
|
3980
|
-
return result.data;
|
|
3981
|
-
}
|
|
3982
|
-
logger.warn('Failed to fetch visitor activity:', result.error);
|
|
3983
|
-
return null;
|
|
4064
|
+
return this.visitor.getActivity(options);
|
|
3984
4065
|
}
|
|
3985
4066
|
/**
|
|
3986
4067
|
* Get a summarized journey timeline for the current visitor.
|
|
3987
|
-
*
|
|
4068
|
+
* @deprecated Use `tracker.visitor.getTimeline()` instead.
|
|
3988
4069
|
*/
|
|
3989
4070
|
async getVisitorTimeline() {
|
|
3990
4071
|
if (!this.isInitialized) {
|
|
3991
4072
|
logger.warn('SDK not initialized');
|
|
3992
4073
|
return null;
|
|
3993
4074
|
}
|
|
3994
|
-
|
|
3995
|
-
if (result.success && result.data) {
|
|
3996
|
-
return result.data;
|
|
3997
|
-
}
|
|
3998
|
-
logger.warn('Failed to fetch visitor timeline:', result.error);
|
|
3999
|
-
return null;
|
|
4075
|
+
return this.visitor.getTimeline();
|
|
4000
4076
|
}
|
|
4001
4077
|
/**
|
|
4002
4078
|
* Get engagement metrics for the current visitor.
|
|
4003
|
-
*
|
|
4079
|
+
* @deprecated Use `tracker.visitor.getEngagement()` instead.
|
|
4004
4080
|
*/
|
|
4005
4081
|
async getVisitorEngagement() {
|
|
4006
4082
|
if (!this.isInitialized) {
|
|
4007
4083
|
logger.warn('SDK not initialized');
|
|
4008
4084
|
return null;
|
|
4009
4085
|
}
|
|
4010
|
-
|
|
4011
|
-
if (result.success && result.data) {
|
|
4012
|
-
return result.data;
|
|
4013
|
-
}
|
|
4014
|
-
logger.warn('Failed to fetch visitor engagement:', result.error);
|
|
4015
|
-
return null;
|
|
4086
|
+
return this.visitor.getEngagement();
|
|
4016
4087
|
}
|
|
4017
4088
|
/**
|
|
4018
4089
|
* Retry pending identify call
|
|
@@ -4043,6 +4114,149 @@ class Tracker {
|
|
|
4043
4114
|
logger.enabled = enabled;
|
|
4044
4115
|
logger.info(`Debug mode ${enabled ? 'enabled' : 'disabled'}`);
|
|
4045
4116
|
}
|
|
4117
|
+
// ============================================
|
|
4118
|
+
// GROUP, ALIAS, SCREEN
|
|
4119
|
+
// ============================================
|
|
4120
|
+
/**
|
|
4121
|
+
* Associate the current visitor with a group (company/account).
|
|
4122
|
+
* The groupId will be attached to all subsequent track() calls.
|
|
4123
|
+
*/
|
|
4124
|
+
group(groupId, traits = {}) {
|
|
4125
|
+
if (!groupId) {
|
|
4126
|
+
logger.warn('groupId is required for group()');
|
|
4127
|
+
return;
|
|
4128
|
+
}
|
|
4129
|
+
this.groupId = groupId;
|
|
4130
|
+
logger.info('Visitor grouped:', groupId);
|
|
4131
|
+
this.track('group', 'Group Identified', {
|
|
4132
|
+
groupId,
|
|
4133
|
+
...traits,
|
|
4134
|
+
});
|
|
4135
|
+
}
|
|
4136
|
+
/**
|
|
4137
|
+
* Merge two visitor identities.
|
|
4138
|
+
* Links `previousId` (typically the anonymous visitor) to `newId` (the known user).
|
|
4139
|
+
* If `previousId` is omitted, the current visitorId is used.
|
|
4140
|
+
*/
|
|
4141
|
+
async alias(newId, previousId) {
|
|
4142
|
+
if (!newId) {
|
|
4143
|
+
logger.warn('newId is required for alias()');
|
|
4144
|
+
return false;
|
|
4145
|
+
}
|
|
4146
|
+
const prevId = previousId || this.visitorId;
|
|
4147
|
+
logger.info('Aliasing visitor:', { from: prevId, to: newId });
|
|
4148
|
+
try {
|
|
4149
|
+
const url = `${this.config.apiEndpoint}/api/public/track/alias`;
|
|
4150
|
+
const response = await fetch(url, {
|
|
4151
|
+
method: 'POST',
|
|
4152
|
+
headers: { 'Content-Type': 'application/json' },
|
|
4153
|
+
body: JSON.stringify({
|
|
4154
|
+
workspaceId: this.workspaceId,
|
|
4155
|
+
previousId: prevId,
|
|
4156
|
+
newId,
|
|
4157
|
+
}),
|
|
4158
|
+
});
|
|
4159
|
+
if (response.ok) {
|
|
4160
|
+
logger.info('Alias successful');
|
|
4161
|
+
return true;
|
|
4162
|
+
}
|
|
4163
|
+
logger.error('Alias failed:', response.status);
|
|
4164
|
+
return false;
|
|
4165
|
+
}
|
|
4166
|
+
catch (error) {
|
|
4167
|
+
logger.error('Alias request failed:', error);
|
|
4168
|
+
return false;
|
|
4169
|
+
}
|
|
4170
|
+
}
|
|
4171
|
+
/**
|
|
4172
|
+
* Track a screen view (for mobile-first PWAs and SPAs).
|
|
4173
|
+
* Similar to page() but semantically for app screens.
|
|
4174
|
+
*/
|
|
4175
|
+
screen(name, properties = {}) {
|
|
4176
|
+
this.track('screen_view', name, {
|
|
4177
|
+
...properties,
|
|
4178
|
+
screenName: name,
|
|
4179
|
+
});
|
|
4180
|
+
}
|
|
4181
|
+
// ============================================
|
|
4182
|
+
// MIDDLEWARE
|
|
4183
|
+
// ============================================
|
|
4184
|
+
/**
|
|
4185
|
+
* Register event middleware.
|
|
4186
|
+
* Middleware functions receive the event and a `next` callback.
|
|
4187
|
+
* Call `next()` to pass the event through, or don't call it to drop the event.
|
|
4188
|
+
*
|
|
4189
|
+
* @example
|
|
4190
|
+
* tracker.use((event, next) => {
|
|
4191
|
+
* // Strip PII from events
|
|
4192
|
+
* delete event.properties.email;
|
|
4193
|
+
* next(); // pass it through
|
|
4194
|
+
* });
|
|
4195
|
+
*/
|
|
4196
|
+
use(middleware) {
|
|
4197
|
+
this.middlewares.push(middleware);
|
|
4198
|
+
logger.debug('Middleware registered');
|
|
4199
|
+
}
|
|
4200
|
+
/**
|
|
4201
|
+
* Run event through the middleware pipeline.
|
|
4202
|
+
* Executes each middleware in order; if any skips `next()`, the event is dropped.
|
|
4203
|
+
*/
|
|
4204
|
+
runMiddleware(event, finalCallback) {
|
|
4205
|
+
if (this.middlewares.length === 0) {
|
|
4206
|
+
finalCallback();
|
|
4207
|
+
return;
|
|
4208
|
+
}
|
|
4209
|
+
let index = 0;
|
|
4210
|
+
const middlewares = this.middlewares;
|
|
4211
|
+
const next = () => {
|
|
4212
|
+
index++;
|
|
4213
|
+
if (index < middlewares.length) {
|
|
4214
|
+
try {
|
|
4215
|
+
middlewares[index](event, next);
|
|
4216
|
+
}
|
|
4217
|
+
catch (e) {
|
|
4218
|
+
logger.error('Middleware error:', e);
|
|
4219
|
+
finalCallback();
|
|
4220
|
+
}
|
|
4221
|
+
}
|
|
4222
|
+
else {
|
|
4223
|
+
finalCallback();
|
|
4224
|
+
}
|
|
4225
|
+
};
|
|
4226
|
+
try {
|
|
4227
|
+
middlewares[0](event, next);
|
|
4228
|
+
}
|
|
4229
|
+
catch (e) {
|
|
4230
|
+
logger.error('Middleware error:', e);
|
|
4231
|
+
finalCallback();
|
|
4232
|
+
}
|
|
4233
|
+
}
|
|
4234
|
+
// ============================================
|
|
4235
|
+
// LIFECYCLE
|
|
4236
|
+
// ============================================
|
|
4237
|
+
/**
|
|
4238
|
+
* Register a callback to be invoked when the SDK is fully initialized.
|
|
4239
|
+
* If already initialized, the callback fires immediately.
|
|
4240
|
+
*/
|
|
4241
|
+
onReady(callback) {
|
|
4242
|
+
if (this.isInitialized) {
|
|
4243
|
+
try {
|
|
4244
|
+
callback();
|
|
4245
|
+
}
|
|
4246
|
+
catch (e) {
|
|
4247
|
+
logger.error('onReady callback error:', e);
|
|
4248
|
+
}
|
|
4249
|
+
}
|
|
4250
|
+
else {
|
|
4251
|
+
this.readyCallbacks.push(callback);
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
/**
|
|
4255
|
+
* Check if the SDK is fully initialized and ready.
|
|
4256
|
+
*/
|
|
4257
|
+
isReady() {
|
|
4258
|
+
return this.isInitialized;
|
|
4259
|
+
}
|
|
4046
4260
|
/**
|
|
4047
4261
|
* Register a schema for event validation.
|
|
4048
4262
|
* When debug mode is enabled, events will be validated against registered schemas.
|
|
@@ -4337,14 +4551,42 @@ if (typeof window !== 'undefined') {
|
|
|
4337
4551
|
/**
|
|
4338
4552
|
* Clianta SDK - React Integration
|
|
4339
4553
|
*
|
|
4340
|
-
* Provides CliantaProvider component for easy
|
|
4341
|
-
* using the clianta.config.ts pattern.
|
|
4554
|
+
* Provides CliantaProvider component (with ErrorBoundary) for easy
|
|
4555
|
+
* React/Next.js integration using the clianta.config.ts pattern.
|
|
4556
|
+
*/
|
|
4557
|
+
const CliantaContext = react.createContext({
|
|
4558
|
+
tracker: null,
|
|
4559
|
+
isReady: false,
|
|
4560
|
+
});
|
|
4561
|
+
/**
|
|
4562
|
+
* Internal ErrorBoundary to prevent SDK errors from crashing the host app.
|
|
4563
|
+
* Catches render-time errors in the provider tree.
|
|
4342
4564
|
*/
|
|
4343
|
-
|
|
4344
|
-
|
|
4565
|
+
class CliantaErrorBoundary extends react.Component {
|
|
4566
|
+
constructor(props) {
|
|
4567
|
+
super(props);
|
|
4568
|
+
this.state = { hasError: false };
|
|
4569
|
+
}
|
|
4570
|
+
static getDerivedStateFromError() {
|
|
4571
|
+
return { hasError: true };
|
|
4572
|
+
}
|
|
4573
|
+
componentDidCatch(error, errorInfo) {
|
|
4574
|
+
console.error('[Clianta] SDK error caught by ErrorBoundary:', error);
|
|
4575
|
+
this.props.onError?.(error, errorInfo);
|
|
4576
|
+
}
|
|
4577
|
+
render() {
|
|
4578
|
+
if (this.state.hasError) {
|
|
4579
|
+
// Render children anyway — SDK failure shouldn't break the host UI
|
|
4580
|
+
return this.props.fallback ?? this.props.children;
|
|
4581
|
+
}
|
|
4582
|
+
return this.props.children;
|
|
4583
|
+
}
|
|
4584
|
+
}
|
|
4345
4585
|
/**
|
|
4346
4586
|
* CliantaProvider - Wrap your app to enable tracking
|
|
4347
4587
|
*
|
|
4588
|
+
* Includes an ErrorBoundary so SDK failures never crash the host app.
|
|
4589
|
+
*
|
|
4348
4590
|
* @example
|
|
4349
4591
|
* // In clianta.config.ts:
|
|
4350
4592
|
* import { CliantaConfig } from '@clianta/sdk';
|
|
@@ -4365,8 +4607,9 @@ const CliantaContext = react.createContext(null);
|
|
|
4365
4607
|
* {children}
|
|
4366
4608
|
* </CliantaProvider>
|
|
4367
4609
|
*/
|
|
4368
|
-
function CliantaProvider({ config, children }) {
|
|
4610
|
+
function CliantaProvider({ config, children, onError }) {
|
|
4369
4611
|
const [tracker, setTracker] = react.useState(null);
|
|
4612
|
+
const [isReady, setIsReady] = react.useState(false);
|
|
4370
4613
|
// Stable ref to projectId — the only value that truly identifies the tracker
|
|
4371
4614
|
const projectIdRef = react.useRef(config.projectId);
|
|
4372
4615
|
react.useEffect(() => {
|
|
@@ -4380,18 +4623,29 @@ function CliantaProvider({ config, children }) {
|
|
|
4380
4623
|
if (projectIdRef.current !== projectId) {
|
|
4381
4624
|
projectIdRef.current = projectId;
|
|
4382
4625
|
}
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4626
|
+
try {
|
|
4627
|
+
// Extract projectId (handled separately) and pass rest as options
|
|
4628
|
+
const { projectId: _, ...options } = config;
|
|
4629
|
+
const instance = clianta(projectId, options);
|
|
4630
|
+
setTracker(instance);
|
|
4631
|
+
setIsReady(true);
|
|
4632
|
+
}
|
|
4633
|
+
catch (error) {
|
|
4634
|
+
console.error('[Clianta] Failed to initialize SDK:', error);
|
|
4635
|
+
onError?.(error, { componentStack: '' });
|
|
4636
|
+
}
|
|
4387
4637
|
// Cleanup: flush pending events on unmount
|
|
4388
4638
|
return () => {
|
|
4389
|
-
|
|
4639
|
+
tracker?.flush();
|
|
4640
|
+
setIsReady(false);
|
|
4390
4641
|
};
|
|
4391
4642
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
4392
4643
|
}, [config.projectId]);
|
|
4393
|
-
return (jsxRuntime.jsx(CliantaContext.Provider, { value: tracker, children: children }));
|
|
4644
|
+
return (jsxRuntime.jsx(CliantaErrorBoundary, { onError: onError, children: jsxRuntime.jsx(CliantaContext.Provider, { value: { tracker, isReady }, children: children }) }));
|
|
4394
4645
|
}
|
|
4646
|
+
// ============================================
|
|
4647
|
+
// HOOKS
|
|
4648
|
+
// ============================================
|
|
4395
4649
|
/**
|
|
4396
4650
|
* useClianta - Hook to access tracker in any component
|
|
4397
4651
|
*
|
|
@@ -4400,7 +4654,21 @@ function CliantaProvider({ config, children }) {
|
|
|
4400
4654
|
* tracker?.track('button_click', 'CTA Button');
|
|
4401
4655
|
*/
|
|
4402
4656
|
function useClianta() {
|
|
4403
|
-
|
|
4657
|
+
const { tracker } = react.useContext(CliantaContext);
|
|
4658
|
+
return tracker;
|
|
4659
|
+
}
|
|
4660
|
+
/**
|
|
4661
|
+
* useCliantaReady - Hook to check if SDK is initialized
|
|
4662
|
+
*
|
|
4663
|
+
* @example
|
|
4664
|
+
* const { isReady, tracker } = useCliantaReady();
|
|
4665
|
+
* if (isReady) {
|
|
4666
|
+
* tracker.track('purchase', 'Order', { value: 99 });
|
|
4667
|
+
* }
|
|
4668
|
+
*/
|
|
4669
|
+
function useCliantaReady() {
|
|
4670
|
+
const { tracker, isReady } = react.useContext(CliantaContext);
|
|
4671
|
+
return { isReady, tracker };
|
|
4404
4672
|
}
|
|
4405
4673
|
/**
|
|
4406
4674
|
* useCliantaTrack - Convenience hook for tracking events
|
|
@@ -4418,5 +4686,6 @@ function useCliantaTrack() {
|
|
|
4418
4686
|
|
|
4419
4687
|
exports.CliantaProvider = CliantaProvider;
|
|
4420
4688
|
exports.useClianta = useClianta;
|
|
4689
|
+
exports.useCliantaReady = useCliantaReady;
|
|
4421
4690
|
exports.useCliantaTrack = useCliantaTrack;
|
|
4422
4691
|
//# sourceMappingURL=react.cjs.js.map
|