@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/svelte.d.ts
CHANGED
|
@@ -84,6 +84,63 @@ interface ConsentState {
|
|
|
84
84
|
personalization?: boolean;
|
|
85
85
|
}
|
|
86
86
|
type EventType = 'page_view' | 'button_click' | 'form_view' | 'form_submit' | 'form_interaction' | 'scroll_depth' | 'engagement' | 'download' | 'exit_intent' | 'error' | 'performance' | 'time_on_page' | 'custom';
|
|
87
|
+
interface TrackingEvent {
|
|
88
|
+
/** Workspace/project ID */
|
|
89
|
+
workspaceId: string;
|
|
90
|
+
/** Anonymous visitor identifier */
|
|
91
|
+
visitorId: string;
|
|
92
|
+
/** Session identifier */
|
|
93
|
+
sessionId: string;
|
|
94
|
+
/** Event type category */
|
|
95
|
+
eventType: EventType;
|
|
96
|
+
/** Human-readable event name */
|
|
97
|
+
eventName: string;
|
|
98
|
+
/** Current page URL */
|
|
99
|
+
url: string;
|
|
100
|
+
/** Referrer URL */
|
|
101
|
+
referrer?: string;
|
|
102
|
+
/** Event properties/metadata */
|
|
103
|
+
properties: Record<string, unknown>;
|
|
104
|
+
/** Device information */
|
|
105
|
+
device: DeviceInfo;
|
|
106
|
+
/** UTM parameters */
|
|
107
|
+
utm?: UTMParams;
|
|
108
|
+
/** ISO timestamp */
|
|
109
|
+
timestamp: string;
|
|
110
|
+
/** SDK version */
|
|
111
|
+
sdkVersion: string;
|
|
112
|
+
}
|
|
113
|
+
interface DeviceInfo {
|
|
114
|
+
userAgent: string;
|
|
115
|
+
screen: string;
|
|
116
|
+
language: string;
|
|
117
|
+
timezone?: string;
|
|
118
|
+
}
|
|
119
|
+
interface UTMParams {
|
|
120
|
+
utmSource?: string;
|
|
121
|
+
utmMedium?: string;
|
|
122
|
+
utmCampaign?: string;
|
|
123
|
+
utmTerm?: string;
|
|
124
|
+
utmContent?: string;
|
|
125
|
+
}
|
|
126
|
+
interface GroupTraits {
|
|
127
|
+
/** Company/account name */
|
|
128
|
+
name?: string;
|
|
129
|
+
/** Industry */
|
|
130
|
+
industry?: string;
|
|
131
|
+
/** Company size */
|
|
132
|
+
employees?: number;
|
|
133
|
+
/** Annual revenue */
|
|
134
|
+
revenue?: number;
|
|
135
|
+
/** Company website */
|
|
136
|
+
website?: string;
|
|
137
|
+
/** Company plan/tier */
|
|
138
|
+
plan?: string;
|
|
139
|
+
/** Additional custom properties */
|
|
140
|
+
[key: string]: unknown;
|
|
141
|
+
}
|
|
142
|
+
/** Event middleware function — intercept or transform events before they are sent */
|
|
143
|
+
type MiddlewareFn = (event: TrackingEvent, next: () => void) => void;
|
|
87
144
|
interface UserTraits {
|
|
88
145
|
firstName?: string;
|
|
89
146
|
lastName?: string;
|
|
@@ -119,6 +176,18 @@ interface TrackerCore {
|
|
|
119
176
|
deleteData(): void;
|
|
120
177
|
/** Get current consent state */
|
|
121
178
|
getConsentState(): ConsentState;
|
|
179
|
+
/** Associate the current visitor with a group (company/account) */
|
|
180
|
+
group(groupId: string, traits?: GroupTraits): void;
|
|
181
|
+
/** Merge two visitor identities (e.g., anonymous → logged-in) */
|
|
182
|
+
alias(newId: string, previousId?: string): Promise<boolean>;
|
|
183
|
+
/** Track a screen view (for mobile-first PWAs and SPAs) */
|
|
184
|
+
screen(name: string, properties?: Record<string, unknown>): void;
|
|
185
|
+
/** Register event middleware to intercept/transform events before sending */
|
|
186
|
+
use(middleware: MiddlewareFn): void;
|
|
187
|
+
/** Register a callback to be invoked when the SDK is fully initialized */
|
|
188
|
+
onReady(callback: () => void): void;
|
|
189
|
+
/** Check if the SDK is fully initialized and ready */
|
|
190
|
+
isReady(): boolean;
|
|
122
191
|
/** Get the current visitor's profile from the CRM */
|
|
123
192
|
getVisitorProfile(): Promise<VisitorProfile | null>;
|
|
124
193
|
/** Get the current visitor's recent activity */
|
package/dist/svelte.esm.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
|
*/
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @see SDK_VERSION in core/config.ts
|
|
9
9
|
*/
|
|
10
10
|
/** SDK Version */
|
|
11
|
-
const SDK_VERSION = '1.
|
|
11
|
+
const SDK_VERSION = '1.6.0';
|
|
12
12
|
/** Default API endpoint — reads from env or falls back to localhost */
|
|
13
13
|
const getDefaultApiEndpoint = () => {
|
|
14
14
|
// Build-time env var (works with Next.js, Vite, CRA, etc.)
|
|
@@ -3690,6 +3690,85 @@ class CRMClient {
|
|
|
3690
3690
|
}
|
|
3691
3691
|
}
|
|
3692
3692
|
|
|
3693
|
+
/**
|
|
3694
|
+
* Privacy-safe visitor API client.
|
|
3695
|
+
* All methods return data for the current visitor only (no cross-visitor access).
|
|
3696
|
+
*/
|
|
3697
|
+
class VisitorClient {
|
|
3698
|
+
constructor(transport, workspaceId, visitorId) {
|
|
3699
|
+
this.transport = transport;
|
|
3700
|
+
this.workspaceId = workspaceId;
|
|
3701
|
+
this.visitorId = visitorId;
|
|
3702
|
+
}
|
|
3703
|
+
/** Update visitorId (e.g. after reset) */
|
|
3704
|
+
setVisitorId(id) {
|
|
3705
|
+
this.visitorId = id;
|
|
3706
|
+
}
|
|
3707
|
+
basePath() {
|
|
3708
|
+
return `/api/public/track/visitor/${this.workspaceId}/${this.visitorId}`;
|
|
3709
|
+
}
|
|
3710
|
+
/**
|
|
3711
|
+
* Get the current visitor's profile from the CRM.
|
|
3712
|
+
* Returns visitor data and linked contact info if identified.
|
|
3713
|
+
*/
|
|
3714
|
+
async getProfile() {
|
|
3715
|
+
const result = await this.transport.fetchData(`${this.basePath()}/profile`);
|
|
3716
|
+
if (result.success && result.data) {
|
|
3717
|
+
logger.debug('Visitor profile fetched:', result.data);
|
|
3718
|
+
return result.data;
|
|
3719
|
+
}
|
|
3720
|
+
logger.warn('Failed to fetch visitor profile:', result.error);
|
|
3721
|
+
return null;
|
|
3722
|
+
}
|
|
3723
|
+
/**
|
|
3724
|
+
* Get the current visitor's recent activity/events.
|
|
3725
|
+
* Returns paginated list of tracking events.
|
|
3726
|
+
*/
|
|
3727
|
+
async getActivity(options) {
|
|
3728
|
+
const params = {};
|
|
3729
|
+
if (options?.page)
|
|
3730
|
+
params.page = options.page.toString();
|
|
3731
|
+
if (options?.limit)
|
|
3732
|
+
params.limit = options.limit.toString();
|
|
3733
|
+
if (options?.eventType)
|
|
3734
|
+
params.eventType = options.eventType;
|
|
3735
|
+
if (options?.startDate)
|
|
3736
|
+
params.startDate = options.startDate;
|
|
3737
|
+
if (options?.endDate)
|
|
3738
|
+
params.endDate = options.endDate;
|
|
3739
|
+
const result = await this.transport.fetchData(`${this.basePath()}/activity`, params);
|
|
3740
|
+
if (result.success && result.data) {
|
|
3741
|
+
return result.data;
|
|
3742
|
+
}
|
|
3743
|
+
logger.warn('Failed to fetch visitor activity:', result.error);
|
|
3744
|
+
return null;
|
|
3745
|
+
}
|
|
3746
|
+
/**
|
|
3747
|
+
* Get a summarized journey timeline for the current visitor.
|
|
3748
|
+
* Includes top pages, sessions, time spent, and recent activities.
|
|
3749
|
+
*/
|
|
3750
|
+
async getTimeline() {
|
|
3751
|
+
const result = await this.transport.fetchData(`${this.basePath()}/timeline`);
|
|
3752
|
+
if (result.success && result.data) {
|
|
3753
|
+
return result.data;
|
|
3754
|
+
}
|
|
3755
|
+
logger.warn('Failed to fetch visitor timeline:', result.error);
|
|
3756
|
+
return null;
|
|
3757
|
+
}
|
|
3758
|
+
/**
|
|
3759
|
+
* Get engagement metrics for the current visitor.
|
|
3760
|
+
* Includes time on site, page views, bounce rate, and engagement score.
|
|
3761
|
+
*/
|
|
3762
|
+
async getEngagement() {
|
|
3763
|
+
const result = await this.transport.fetchData(`${this.basePath()}/engagement`);
|
|
3764
|
+
if (result.success && result.data) {
|
|
3765
|
+
return result.data;
|
|
3766
|
+
}
|
|
3767
|
+
logger.warn('Failed to fetch visitor engagement:', result.error);
|
|
3768
|
+
return null;
|
|
3769
|
+
}
|
|
3770
|
+
}
|
|
3771
|
+
|
|
3693
3772
|
/**
|
|
3694
3773
|
* Clianta SDK - Main Tracker Class
|
|
3695
3774
|
* @see SDK_VERSION in core/config.ts
|
|
@@ -3703,10 +3782,16 @@ class Tracker {
|
|
|
3703
3782
|
this.isInitialized = false;
|
|
3704
3783
|
/** contactId after a successful identify() call */
|
|
3705
3784
|
this.contactId = null;
|
|
3785
|
+
/** groupId after a successful group() call */
|
|
3786
|
+
this.groupId = null;
|
|
3706
3787
|
/** Pending identify retry on next flush */
|
|
3707
3788
|
this.pendingIdentify = null;
|
|
3708
3789
|
/** Registered event schemas for validation */
|
|
3709
3790
|
this.eventSchemas = new Map();
|
|
3791
|
+
/** Event middleware pipeline */
|
|
3792
|
+
this.middlewares = [];
|
|
3793
|
+
/** Ready callbacks */
|
|
3794
|
+
this.readyCallbacks = [];
|
|
3710
3795
|
if (!workspaceId) {
|
|
3711
3796
|
throw new Error('[Clianta] Workspace ID is required');
|
|
3712
3797
|
}
|
|
@@ -3732,6 +3817,8 @@ class Tracker {
|
|
|
3732
3817
|
this.visitorId = this.createVisitorId();
|
|
3733
3818
|
this.sessionId = this.createSessionId();
|
|
3734
3819
|
logger.debug('IDs created', { visitorId: this.visitorId, sessionId: this.sessionId });
|
|
3820
|
+
// Initialize visitor API client
|
|
3821
|
+
this.visitor = new VisitorClient(this.transport, this.workspaceId, this.visitorId);
|
|
3735
3822
|
// Security warnings
|
|
3736
3823
|
if (this.config.apiEndpoint.startsWith('http://') &&
|
|
3737
3824
|
typeof window !== 'undefined' &&
|
|
@@ -3746,6 +3833,16 @@ class Tracker {
|
|
|
3746
3833
|
this.initPlugins();
|
|
3747
3834
|
this.isInitialized = true;
|
|
3748
3835
|
logger.info('SDK initialized successfully');
|
|
3836
|
+
// Fire ready callbacks
|
|
3837
|
+
for (const cb of this.readyCallbacks) {
|
|
3838
|
+
try {
|
|
3839
|
+
cb();
|
|
3840
|
+
}
|
|
3841
|
+
catch (e) {
|
|
3842
|
+
logger.error('onReady callback error:', e);
|
|
3843
|
+
}
|
|
3844
|
+
}
|
|
3845
|
+
this.readyCallbacks = [];
|
|
3749
3846
|
}
|
|
3750
3847
|
/**
|
|
3751
3848
|
* Create visitor ID based on storage mode
|
|
@@ -3858,6 +3955,10 @@ class Tracker {
|
|
|
3858
3955
|
if (this.contactId) {
|
|
3859
3956
|
event.contactId = this.contactId;
|
|
3860
3957
|
}
|
|
3958
|
+
// Attach groupId if known (from a prior group() call)
|
|
3959
|
+
if (this.groupId) {
|
|
3960
|
+
event.groupId = this.groupId;
|
|
3961
|
+
}
|
|
3861
3962
|
// Validate event against registered schema (debug mode only)
|
|
3862
3963
|
this.validateEventSchema(eventType, properties);
|
|
3863
3964
|
// Check consent before tracking
|
|
@@ -3871,8 +3972,11 @@ class Tracker {
|
|
|
3871
3972
|
logger.debug('Event dropped (no consent):', eventName);
|
|
3872
3973
|
return;
|
|
3873
3974
|
}
|
|
3874
|
-
|
|
3875
|
-
|
|
3975
|
+
// Run event through middleware pipeline
|
|
3976
|
+
this.runMiddleware(event, () => {
|
|
3977
|
+
this.queue.push(event);
|
|
3978
|
+
logger.debug('Event tracked:', eventName, properties);
|
|
3979
|
+
});
|
|
3876
3980
|
}
|
|
3877
3981
|
/**
|
|
3878
3982
|
* Track a page view
|
|
@@ -3934,80 +4038,47 @@ class Tracker {
|
|
|
3934
4038
|
}
|
|
3935
4039
|
/**
|
|
3936
4040
|
* Get the current visitor's profile from the CRM.
|
|
3937
|
-
*
|
|
3938
|
-
* Only returns data for the current visitor (privacy-safe for frontend).
|
|
4041
|
+
* @deprecated Use `tracker.visitor.getProfile()` instead.
|
|
3939
4042
|
*/
|
|
3940
4043
|
async getVisitorProfile() {
|
|
3941
4044
|
if (!this.isInitialized) {
|
|
3942
4045
|
logger.warn('SDK not initialized');
|
|
3943
4046
|
return null;
|
|
3944
4047
|
}
|
|
3945
|
-
|
|
3946
|
-
if (result.success && result.data) {
|
|
3947
|
-
logger.debug('Visitor profile fetched:', result.data);
|
|
3948
|
-
return result.data;
|
|
3949
|
-
}
|
|
3950
|
-
logger.warn('Failed to fetch visitor profile:', result.error);
|
|
3951
|
-
return null;
|
|
4048
|
+
return this.visitor.getProfile();
|
|
3952
4049
|
}
|
|
3953
4050
|
/**
|
|
3954
4051
|
* Get the current visitor's recent activity/events.
|
|
3955
|
-
*
|
|
4052
|
+
* @deprecated Use `tracker.visitor.getActivity()` instead.
|
|
3956
4053
|
*/
|
|
3957
4054
|
async getVisitorActivity(options) {
|
|
3958
4055
|
if (!this.isInitialized) {
|
|
3959
4056
|
logger.warn('SDK not initialized');
|
|
3960
4057
|
return null;
|
|
3961
4058
|
}
|
|
3962
|
-
|
|
3963
|
-
if (options?.page)
|
|
3964
|
-
params.page = options.page.toString();
|
|
3965
|
-
if (options?.limit)
|
|
3966
|
-
params.limit = options.limit.toString();
|
|
3967
|
-
if (options?.eventType)
|
|
3968
|
-
params.eventType = options.eventType;
|
|
3969
|
-
if (options?.startDate)
|
|
3970
|
-
params.startDate = options.startDate;
|
|
3971
|
-
if (options?.endDate)
|
|
3972
|
-
params.endDate = options.endDate;
|
|
3973
|
-
const result = await this.transport.fetchData(`/api/public/track/visitor/${this.workspaceId}/${this.visitorId}/activity`, params);
|
|
3974
|
-
if (result.success && result.data) {
|
|
3975
|
-
return result.data;
|
|
3976
|
-
}
|
|
3977
|
-
logger.warn('Failed to fetch visitor activity:', result.error);
|
|
3978
|
-
return null;
|
|
4059
|
+
return this.visitor.getActivity(options);
|
|
3979
4060
|
}
|
|
3980
4061
|
/**
|
|
3981
4062
|
* Get a summarized journey timeline for the current visitor.
|
|
3982
|
-
*
|
|
4063
|
+
* @deprecated Use `tracker.visitor.getTimeline()` instead.
|
|
3983
4064
|
*/
|
|
3984
4065
|
async getVisitorTimeline() {
|
|
3985
4066
|
if (!this.isInitialized) {
|
|
3986
4067
|
logger.warn('SDK not initialized');
|
|
3987
4068
|
return null;
|
|
3988
4069
|
}
|
|
3989
|
-
|
|
3990
|
-
if (result.success && result.data) {
|
|
3991
|
-
return result.data;
|
|
3992
|
-
}
|
|
3993
|
-
logger.warn('Failed to fetch visitor timeline:', result.error);
|
|
3994
|
-
return null;
|
|
4070
|
+
return this.visitor.getTimeline();
|
|
3995
4071
|
}
|
|
3996
4072
|
/**
|
|
3997
4073
|
* Get engagement metrics for the current visitor.
|
|
3998
|
-
*
|
|
4074
|
+
* @deprecated Use `tracker.visitor.getEngagement()` instead.
|
|
3999
4075
|
*/
|
|
4000
4076
|
async getVisitorEngagement() {
|
|
4001
4077
|
if (!this.isInitialized) {
|
|
4002
4078
|
logger.warn('SDK not initialized');
|
|
4003
4079
|
return null;
|
|
4004
4080
|
}
|
|
4005
|
-
|
|
4006
|
-
if (result.success && result.data) {
|
|
4007
|
-
return result.data;
|
|
4008
|
-
}
|
|
4009
|
-
logger.warn('Failed to fetch visitor engagement:', result.error);
|
|
4010
|
-
return null;
|
|
4081
|
+
return this.visitor.getEngagement();
|
|
4011
4082
|
}
|
|
4012
4083
|
/**
|
|
4013
4084
|
* Retry pending identify call
|
|
@@ -4038,6 +4109,149 @@ class Tracker {
|
|
|
4038
4109
|
logger.enabled = enabled;
|
|
4039
4110
|
logger.info(`Debug mode ${enabled ? 'enabled' : 'disabled'}`);
|
|
4040
4111
|
}
|
|
4112
|
+
// ============================================
|
|
4113
|
+
// GROUP, ALIAS, SCREEN
|
|
4114
|
+
// ============================================
|
|
4115
|
+
/**
|
|
4116
|
+
* Associate the current visitor with a group (company/account).
|
|
4117
|
+
* The groupId will be attached to all subsequent track() calls.
|
|
4118
|
+
*/
|
|
4119
|
+
group(groupId, traits = {}) {
|
|
4120
|
+
if (!groupId) {
|
|
4121
|
+
logger.warn('groupId is required for group()');
|
|
4122
|
+
return;
|
|
4123
|
+
}
|
|
4124
|
+
this.groupId = groupId;
|
|
4125
|
+
logger.info('Visitor grouped:', groupId);
|
|
4126
|
+
this.track('group', 'Group Identified', {
|
|
4127
|
+
groupId,
|
|
4128
|
+
...traits,
|
|
4129
|
+
});
|
|
4130
|
+
}
|
|
4131
|
+
/**
|
|
4132
|
+
* Merge two visitor identities.
|
|
4133
|
+
* Links `previousId` (typically the anonymous visitor) to `newId` (the known user).
|
|
4134
|
+
* If `previousId` is omitted, the current visitorId is used.
|
|
4135
|
+
*/
|
|
4136
|
+
async alias(newId, previousId) {
|
|
4137
|
+
if (!newId) {
|
|
4138
|
+
logger.warn('newId is required for alias()');
|
|
4139
|
+
return false;
|
|
4140
|
+
}
|
|
4141
|
+
const prevId = previousId || this.visitorId;
|
|
4142
|
+
logger.info('Aliasing visitor:', { from: prevId, to: newId });
|
|
4143
|
+
try {
|
|
4144
|
+
const url = `${this.config.apiEndpoint}/api/public/track/alias`;
|
|
4145
|
+
const response = await fetch(url, {
|
|
4146
|
+
method: 'POST',
|
|
4147
|
+
headers: { 'Content-Type': 'application/json' },
|
|
4148
|
+
body: JSON.stringify({
|
|
4149
|
+
workspaceId: this.workspaceId,
|
|
4150
|
+
previousId: prevId,
|
|
4151
|
+
newId,
|
|
4152
|
+
}),
|
|
4153
|
+
});
|
|
4154
|
+
if (response.ok) {
|
|
4155
|
+
logger.info('Alias successful');
|
|
4156
|
+
return true;
|
|
4157
|
+
}
|
|
4158
|
+
logger.error('Alias failed:', response.status);
|
|
4159
|
+
return false;
|
|
4160
|
+
}
|
|
4161
|
+
catch (error) {
|
|
4162
|
+
logger.error('Alias request failed:', error);
|
|
4163
|
+
return false;
|
|
4164
|
+
}
|
|
4165
|
+
}
|
|
4166
|
+
/**
|
|
4167
|
+
* Track a screen view (for mobile-first PWAs and SPAs).
|
|
4168
|
+
* Similar to page() but semantically for app screens.
|
|
4169
|
+
*/
|
|
4170
|
+
screen(name, properties = {}) {
|
|
4171
|
+
this.track('screen_view', name, {
|
|
4172
|
+
...properties,
|
|
4173
|
+
screenName: name,
|
|
4174
|
+
});
|
|
4175
|
+
}
|
|
4176
|
+
// ============================================
|
|
4177
|
+
// MIDDLEWARE
|
|
4178
|
+
// ============================================
|
|
4179
|
+
/**
|
|
4180
|
+
* Register event middleware.
|
|
4181
|
+
* Middleware functions receive the event and a `next` callback.
|
|
4182
|
+
* Call `next()` to pass the event through, or don't call it to drop the event.
|
|
4183
|
+
*
|
|
4184
|
+
* @example
|
|
4185
|
+
* tracker.use((event, next) => {
|
|
4186
|
+
* // Strip PII from events
|
|
4187
|
+
* delete event.properties.email;
|
|
4188
|
+
* next(); // pass it through
|
|
4189
|
+
* });
|
|
4190
|
+
*/
|
|
4191
|
+
use(middleware) {
|
|
4192
|
+
this.middlewares.push(middleware);
|
|
4193
|
+
logger.debug('Middleware registered');
|
|
4194
|
+
}
|
|
4195
|
+
/**
|
|
4196
|
+
* Run event through the middleware pipeline.
|
|
4197
|
+
* Executes each middleware in order; if any skips `next()`, the event is dropped.
|
|
4198
|
+
*/
|
|
4199
|
+
runMiddleware(event, finalCallback) {
|
|
4200
|
+
if (this.middlewares.length === 0) {
|
|
4201
|
+
finalCallback();
|
|
4202
|
+
return;
|
|
4203
|
+
}
|
|
4204
|
+
let index = 0;
|
|
4205
|
+
const middlewares = this.middlewares;
|
|
4206
|
+
const next = () => {
|
|
4207
|
+
index++;
|
|
4208
|
+
if (index < middlewares.length) {
|
|
4209
|
+
try {
|
|
4210
|
+
middlewares[index](event, next);
|
|
4211
|
+
}
|
|
4212
|
+
catch (e) {
|
|
4213
|
+
logger.error('Middleware error:', e);
|
|
4214
|
+
finalCallback();
|
|
4215
|
+
}
|
|
4216
|
+
}
|
|
4217
|
+
else {
|
|
4218
|
+
finalCallback();
|
|
4219
|
+
}
|
|
4220
|
+
};
|
|
4221
|
+
try {
|
|
4222
|
+
middlewares[0](event, next);
|
|
4223
|
+
}
|
|
4224
|
+
catch (e) {
|
|
4225
|
+
logger.error('Middleware error:', e);
|
|
4226
|
+
finalCallback();
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4229
|
+
// ============================================
|
|
4230
|
+
// LIFECYCLE
|
|
4231
|
+
// ============================================
|
|
4232
|
+
/**
|
|
4233
|
+
* Register a callback to be invoked when the SDK is fully initialized.
|
|
4234
|
+
* If already initialized, the callback fires immediately.
|
|
4235
|
+
*/
|
|
4236
|
+
onReady(callback) {
|
|
4237
|
+
if (this.isInitialized) {
|
|
4238
|
+
try {
|
|
4239
|
+
callback();
|
|
4240
|
+
}
|
|
4241
|
+
catch (e) {
|
|
4242
|
+
logger.error('onReady callback error:', e);
|
|
4243
|
+
}
|
|
4244
|
+
}
|
|
4245
|
+
else {
|
|
4246
|
+
this.readyCallbacks.push(callback);
|
|
4247
|
+
}
|
|
4248
|
+
}
|
|
4249
|
+
/**
|
|
4250
|
+
* Check if the SDK is fully initialized and ready.
|
|
4251
|
+
*/
|
|
4252
|
+
isReady() {
|
|
4253
|
+
return this.isInitialized;
|
|
4254
|
+
}
|
|
4041
4255
|
/**
|
|
4042
4256
|
* Register a schema for event validation.
|
|
4043
4257
|
* When debug mode is enabled, events will be validated against registered schemas.
|