@meet-sudo/sdk 0.1.2 → 0.1.4
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/README.md +24 -24
- package/dist/chunk-2H6T3CDL.mjs +127 -0
- package/dist/chunk-2H6T3CDL.mjs.map +1 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +123 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5 -0
- package/dist/index.mjs.map +1 -1
- package/dist/server.d.mts +89 -0
- package/dist/server.d.ts +89 -0
- package/dist/server.js +151 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +9 -0
- package/dist/server.mjs.map +1 -0
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Official JavaScript SDK for MeetSudo - the unified customer platform with chat, analytics, and session replay.
|
|
4
4
|
|
|
5
|
-
**[Documentation](https
|
|
5
|
+
**[Documentation](https://meetsudo.com/docs)** | **[Website](https://meetsudo.com)** | **[Dashboard](https://meetsudo.com/login)**
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -19,7 +19,7 @@ pnpm add @meet-sudo/sdk
|
|
|
19
19
|
### Script Tag (CDN)
|
|
20
20
|
|
|
21
21
|
```html
|
|
22
|
-
<script src="https://
|
|
22
|
+
<script src="https://unpkg.com/@meet-sudo/sdk/dist/meetsudo.global.js"></script>
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
## Quick Start
|
|
@@ -27,18 +27,18 @@ pnpm add @meet-sudo/sdk
|
|
|
27
27
|
### ES Modules
|
|
28
28
|
|
|
29
29
|
```javascript
|
|
30
|
-
import {
|
|
30
|
+
import { meetsudo } from '@meet-sudo/sdk';
|
|
31
31
|
|
|
32
32
|
// Initialize with your API key
|
|
33
|
-
|
|
33
|
+
meetsudo.init({
|
|
34
34
|
apiKey: 'your-api-key'
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
// Track events
|
|
38
|
-
|
|
38
|
+
meetsudo.track('button_clicked', { buttonId: 'signup' });
|
|
39
39
|
|
|
40
40
|
// Identify users
|
|
41
|
-
|
|
41
|
+
meetsudo.identify('user-123', {
|
|
42
42
|
email: 'user@example.com',
|
|
43
43
|
name: 'John Doe'
|
|
44
44
|
});
|
|
@@ -47,13 +47,13 @@ await @meet-sudo/sdk.identify('user-123', {
|
|
|
47
47
|
### Script Tag
|
|
48
48
|
|
|
49
49
|
```html
|
|
50
|
-
<script src="https://
|
|
50
|
+
<script src="https://unpkg.com/@meet-sudo/sdk/dist/meetsudo.global.js"></script>
|
|
51
51
|
<script>
|
|
52
|
-
|
|
52
|
+
meetsudo.init({
|
|
53
53
|
apiKey: 'your-api-key'
|
|
54
|
-
}).then(function() {
|
|
55
|
-
MeetSudo.track('page_loaded');
|
|
56
54
|
});
|
|
55
|
+
|
|
56
|
+
meetsudo.track('page_loaded');
|
|
57
57
|
</script>
|
|
58
58
|
```
|
|
59
59
|
|
|
@@ -66,8 +66,8 @@ Initialize the SDK. Must be called before any other methods.
|
|
|
66
66
|
```typescript
|
|
67
67
|
interface MeetSudoConfig {
|
|
68
68
|
apiKey: string; // Required: Your MeetSudo API key
|
|
69
|
-
apiUrl?: string; // Optional: API URL (default: https://api
|
|
70
|
-
widgetUrl?: string; // Optional: Widget URL (default: https://widget
|
|
69
|
+
apiUrl?: string; // Optional: API URL (default: https://api.meetsudo.com)
|
|
70
|
+
widgetUrl?: string; // Optional: Widget URL (default: https://widget.meetsudo.com)
|
|
71
71
|
disableChat?: boolean; // Optional: Disable chat widget
|
|
72
72
|
disableReplay?: boolean; // Optional: Disable session replay
|
|
73
73
|
disableEvents?: boolean; // Optional: Disable event tracking
|
|
@@ -80,7 +80,7 @@ interface MeetSudoConfig {
|
|
|
80
80
|
Identify a user with their unique ID and optional traits.
|
|
81
81
|
|
|
82
82
|
```javascript
|
|
83
|
-
|
|
83
|
+
meetsudo.identify('user-123', {
|
|
84
84
|
email: 'user@example.com',
|
|
85
85
|
name: 'John Doe',
|
|
86
86
|
plan: 'pro',
|
|
@@ -93,7 +93,7 @@ await @meet-sudo/sdk.identify('user-123', {
|
|
|
93
93
|
Track a custom event with optional properties.
|
|
94
94
|
|
|
95
95
|
```javascript
|
|
96
|
-
|
|
96
|
+
meetsudo.track('purchase_completed', {
|
|
97
97
|
orderId: 'ord-123',
|
|
98
98
|
amount: 99.99,
|
|
99
99
|
currency: 'USD'
|
|
@@ -105,7 +105,7 @@ Track a custom event with optional properties.
|
|
|
105
105
|
Track a page view. Called automatically on init unless `disableAutoPageView` is set.
|
|
106
106
|
|
|
107
107
|
```javascript
|
|
108
|
-
|
|
108
|
+
meetsudo.page({
|
|
109
109
|
title: 'Product Page',
|
|
110
110
|
category: 'E-commerce'
|
|
111
111
|
});
|
|
@@ -117,29 +117,29 @@ Control the chat widget programmatically.
|
|
|
117
117
|
|
|
118
118
|
```javascript
|
|
119
119
|
// Open the chat widget
|
|
120
|
-
|
|
120
|
+
meetsudo.chat('open');
|
|
121
121
|
|
|
122
122
|
// Close the chat widget
|
|
123
|
-
|
|
123
|
+
meetsudo.chat('close');
|
|
124
124
|
|
|
125
125
|
// Hide the chat widget
|
|
126
|
-
|
|
126
|
+
meetsudo.chat('hide');
|
|
127
127
|
|
|
128
128
|
// Show the chat widget
|
|
129
|
-
|
|
129
|
+
meetsudo.chat('show');
|
|
130
130
|
```
|
|
131
131
|
|
|
132
132
|
### Helper Methods
|
|
133
133
|
|
|
134
134
|
```javascript
|
|
135
135
|
// Check if SDK is initialized
|
|
136
|
-
|
|
136
|
+
meetsudo.isInitialized(); // boolean
|
|
137
137
|
|
|
138
138
|
// Get anonymous ID
|
|
139
|
-
|
|
139
|
+
meetsudo.getAnonymousId(); // string | null
|
|
140
140
|
|
|
141
141
|
// Get customer ID (after identify)
|
|
142
|
-
|
|
142
|
+
meetsudo.getCustomerId(); // string | null
|
|
143
143
|
```
|
|
144
144
|
|
|
145
145
|
## Features
|
|
@@ -160,10 +160,10 @@ To exclude specific elements from recording:
|
|
|
160
160
|
|
|
161
161
|
```html
|
|
162
162
|
<!-- Using class -->
|
|
163
|
-
<div class="
|
|
163
|
+
<div class="ms-no-record">Sensitive content</div>
|
|
164
164
|
|
|
165
165
|
<!-- Using data attribute -->
|
|
166
|
-
<div data
|
|
166
|
+
<div data-ms-no-record>Sensitive content</div>
|
|
167
167
|
```
|
|
168
168
|
|
|
169
169
|
## TypeScript
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// src/server.ts
|
|
2
|
+
var MeetSudoServer = class {
|
|
3
|
+
constructor(config) {
|
|
4
|
+
if (!config.apiKey) {
|
|
5
|
+
throw new Error("MeetSudoServer: apiKey is required");
|
|
6
|
+
}
|
|
7
|
+
this.apiKey = config.apiKey;
|
|
8
|
+
this.apiUrl = config.apiUrl || "https://api.meetsudo.com";
|
|
9
|
+
}
|
|
10
|
+
async request(method, path, body) {
|
|
11
|
+
const res = await fetch(`${this.apiUrl}${path}`, {
|
|
12
|
+
method,
|
|
13
|
+
headers: {
|
|
14
|
+
"Content-Type": "application/json",
|
|
15
|
+
"X-API-Key": this.apiKey
|
|
16
|
+
},
|
|
17
|
+
body: body ? JSON.stringify(body) : void 0
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
const error = await res.json().catch(() => ({ error: "Request failed" }));
|
|
21
|
+
throw new Error(error.error || `Request failed: ${res.status}`);
|
|
22
|
+
}
|
|
23
|
+
return res.json();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Identify a user with traits.
|
|
27
|
+
* Creates or updates the customer in MeetSudo.
|
|
28
|
+
*
|
|
29
|
+
* @param userId - Unique identifier for the user (your internal user ID)
|
|
30
|
+
* @param traits - User properties like email, name, plan, etc.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* await meetsudo.identify('user_123', {
|
|
34
|
+
* email: 'jane@example.com',
|
|
35
|
+
* name: 'Jane Smith',
|
|
36
|
+
* plan: 'pro'
|
|
37
|
+
* });
|
|
38
|
+
*/
|
|
39
|
+
async identify(userId, traits) {
|
|
40
|
+
return this.request("POST", "/widget/identify", {
|
|
41
|
+
anonymous_id: `server_${userId}`,
|
|
42
|
+
external_id: userId,
|
|
43
|
+
properties: traits || {}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Track a custom event.
|
|
48
|
+
*
|
|
49
|
+
* @param userId - The user ID (same as used in identify)
|
|
50
|
+
* @param eventName - Name of the event (e.g., 'purchase_completed')
|
|
51
|
+
* @param properties - Additional event properties
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* await meetsudo.track('user_123', 'purchase_completed', {
|
|
55
|
+
* order_id: 'ord_456',
|
|
56
|
+
* amount: 99.00,
|
|
57
|
+
* currency: 'USD'
|
|
58
|
+
* });
|
|
59
|
+
*/
|
|
60
|
+
async track(userId, eventName, properties) {
|
|
61
|
+
await this.request("POST", "/widget/init", {
|
|
62
|
+
anonymous_id: `server_${userId}`
|
|
63
|
+
});
|
|
64
|
+
return this.request("POST", "/widget/events", {
|
|
65
|
+
anonymous_id: `server_${userId}`,
|
|
66
|
+
events: [
|
|
67
|
+
{
|
|
68
|
+
name: eventName,
|
|
69
|
+
properties: properties || {},
|
|
70
|
+
page_url: "server",
|
|
71
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Track multiple events at once (batch).
|
|
78
|
+
*
|
|
79
|
+
* @param userId - The user ID
|
|
80
|
+
* @param events - Array of events to track
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* await meetsudo.trackBatch('user_123', [
|
|
84
|
+
* { name: 'page_viewed', properties: { page: '/pricing' } },
|
|
85
|
+
* { name: 'button_clicked', properties: { button: 'cta' } }
|
|
86
|
+
* ]);
|
|
87
|
+
*/
|
|
88
|
+
async trackBatch(userId, events) {
|
|
89
|
+
await this.request("POST", "/widget/init", {
|
|
90
|
+
anonymous_id: `server_${userId}`
|
|
91
|
+
});
|
|
92
|
+
return this.request("POST", "/widget/events", {
|
|
93
|
+
anonymous_id: `server_${userId}`,
|
|
94
|
+
events: events.map((e) => ({
|
|
95
|
+
name: e.name,
|
|
96
|
+
properties: e.properties || {},
|
|
97
|
+
page_url: "server",
|
|
98
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
99
|
+
}))
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Get customer information.
|
|
104
|
+
*
|
|
105
|
+
* @param customerId - The MeetSudo customer ID
|
|
106
|
+
*/
|
|
107
|
+
async getCustomer(customerId) {
|
|
108
|
+
return this.request("GET", `/customers/${customerId}`);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get customer timeline (events, messages, sessions).
|
|
112
|
+
*
|
|
113
|
+
* @param customerId - The MeetSudo customer ID
|
|
114
|
+
* @param limit - Maximum number of items to return
|
|
115
|
+
*/
|
|
116
|
+
async getCustomerTimeline(customerId, limit) {
|
|
117
|
+
const qs = limit ? `?limit=${limit}` : "";
|
|
118
|
+
return this.request("GET", `/customers/${customerId}/timeline${qs}`);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
var server_default = MeetSudoServer;
|
|
122
|
+
|
|
123
|
+
export {
|
|
124
|
+
MeetSudoServer,
|
|
125
|
+
server_default
|
|
126
|
+
};
|
|
127
|
+
//# sourceMappingURL=chunk-2H6T3CDL.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["/**\n * MeetSudo Server SDK\n * Use this for server-side event tracking, user identification, and API calls.\n */\n\nexport interface MeetSudoServerConfig {\n apiKey: string;\n apiUrl?: string;\n}\n\nexport interface UserTraits {\n email?: string;\n name?: string;\n [key: string]: unknown;\n}\n\nexport interface EventProperties {\n [key: string]: unknown;\n}\n\nexport class MeetSudoServer {\n private apiKey: string;\n private apiUrl: string;\n\n constructor(config: MeetSudoServerConfig) {\n if (!config.apiKey) {\n throw new Error(\"MeetSudoServer: apiKey is required\");\n }\n this.apiKey = config.apiKey;\n this.apiUrl = config.apiUrl || \"https://api.meetsudo.com\";\n }\n\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const res = await fetch(`${this.apiUrl}${path}`, {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!res.ok) {\n const error = await res.json().catch(() => ({ error: \"Request failed\" }));\n throw new Error(error.error || `Request failed: ${res.status}`);\n }\n\n return res.json();\n }\n\n /**\n * Identify a user with traits.\n * Creates or updates the customer in MeetSudo.\n *\n * @param userId - Unique identifier for the user (your internal user ID)\n * @param traits - User properties like email, name, plan, etc.\n *\n * @example\n * await meetsudo.identify('user_123', {\n * email: 'jane@example.com',\n * name: 'Jane Smith',\n * plan: 'pro'\n * });\n */\n async identify(userId: string, traits?: UserTraits): Promise<{ customer_id: string }> {\n return this.request(\"POST\", \"/widget/identify\", {\n anonymous_id: `server_${userId}`,\n external_id: userId,\n properties: traits || {},\n });\n }\n\n /**\n * Track a custom event.\n *\n * @param userId - The user ID (same as used in identify)\n * @param eventName - Name of the event (e.g., 'purchase_completed')\n * @param properties - Additional event properties\n *\n * @example\n * await meetsudo.track('user_123', 'purchase_completed', {\n * order_id: 'ord_456',\n * amount: 99.00,\n * currency: 'USD'\n * });\n */\n async track(\n userId: string,\n eventName: string,\n properties?: EventProperties\n ): Promise<{ received: number }> {\n // First ensure customer exists\n await this.request(\"POST\", \"/widget/init\", {\n anonymous_id: `server_${userId}`,\n });\n\n return this.request(\"POST\", \"/widget/events\", {\n anonymous_id: `server_${userId}`,\n events: [\n {\n name: eventName,\n properties: properties || {},\n page_url: \"server\",\n timestamp: new Date().toISOString(),\n },\n ],\n });\n }\n\n /**\n * Track multiple events at once (batch).\n *\n * @param userId - The user ID\n * @param events - Array of events to track\n *\n * @example\n * await meetsudo.trackBatch('user_123', [\n * { name: 'page_viewed', properties: { page: '/pricing' } },\n * { name: 'button_clicked', properties: { button: 'cta' } }\n * ]);\n */\n async trackBatch(\n userId: string,\n events: Array<{ name: string; properties?: EventProperties }>\n ): Promise<{ received: number }> {\n // First ensure customer exists\n await this.request(\"POST\", \"/widget/init\", {\n anonymous_id: `server_${userId}`,\n });\n\n return this.request(\"POST\", \"/widget/events\", {\n anonymous_id: `server_${userId}`,\n events: events.map((e) => ({\n name: e.name,\n properties: e.properties || {},\n page_url: \"server\",\n timestamp: new Date().toISOString(),\n })),\n });\n }\n\n /**\n * Get customer information.\n *\n * @param customerId - The MeetSudo customer ID\n */\n async getCustomer(customerId: string): Promise<unknown> {\n return this.request(\"GET\", `/customers/${customerId}`);\n }\n\n /**\n * Get customer timeline (events, messages, sessions).\n *\n * @param customerId - The MeetSudo customer ID\n * @param limit - Maximum number of items to return\n */\n async getCustomerTimeline(\n customerId: string,\n limit?: number\n ): Promise<unknown> {\n const qs = limit ? `?limit=${limit}` : \"\";\n return this.request(\"GET\", `/customers/${customerId}/timeline${qs}`);\n }\n}\n\n// Default export for convenience\nexport default MeetSudoServer;\n"],"mappings":";AAoBO,IAAM,iBAAN,MAAqB;AAAA,EAI1B,YAAY,QAA8B;AACxC,QAAI,CAAC,OAAO,QAAQ;AAClB,YAAM,IAAI,MAAM,oCAAoC;AAAA,IACtD;AACA,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS,OAAO,UAAU;AAAA,EACjC;AAAA,EAEA,MAAc,QACZ,QACA,MACA,MACY;AACZ,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,MAAM,GAAG,IAAI,IAAI;AAAA,MAC/C;AAAA,MACA,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,aAAa,KAAK;AAAA,MACpB;AAAA,MACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IACtC,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,QAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,EAAE,OAAO,iBAAiB,EAAE;AACxE,YAAM,IAAI,MAAM,MAAM,SAAS,mBAAmB,IAAI,MAAM,EAAE;AAAA,IAChE;AAEA,WAAO,IAAI,KAAK;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,SAAS,QAAgB,QAAuD;AACpF,WAAO,KAAK,QAAQ,QAAQ,oBAAoB;AAAA,MAC9C,cAAc,UAAU,MAAM;AAAA,MAC9B,aAAa;AAAA,MACb,YAAY,UAAU,CAAC;AAAA,IACzB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,MACJ,QACA,WACA,YAC+B;AAE/B,UAAM,KAAK,QAAQ,QAAQ,gBAAgB;AAAA,MACzC,cAAc,UAAU,MAAM;AAAA,IAChC,CAAC;AAED,WAAO,KAAK,QAAQ,QAAQ,kBAAkB;AAAA,MAC5C,cAAc,UAAU,MAAM;AAAA,MAC9B,QAAQ;AAAA,QACN;AAAA,UACE,MAAM;AAAA,UACN,YAAY,cAAc,CAAC;AAAA,UAC3B,UAAU;AAAA,UACV,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QACpC;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,WACJ,QACA,QAC+B;AAE/B,UAAM,KAAK,QAAQ,QAAQ,gBAAgB;AAAA,MACzC,cAAc,UAAU,MAAM;AAAA,IAChC,CAAC;AAED,WAAO,KAAK,QAAQ,QAAQ,kBAAkB;AAAA,MAC5C,cAAc,UAAU,MAAM;AAAA,MAC9B,QAAQ,OAAO,IAAI,CAAC,OAAO;AAAA,QACzB,MAAM,EAAE;AAAA,QACR,YAAY,EAAE,cAAc,CAAC;AAAA,QAC7B,UAAU;AAAA,QACV,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,EAAE;AAAA,IACJ,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YAAY,YAAsC;AACtD,WAAO,KAAK,QAAQ,OAAO,cAAc,UAAU,EAAE;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,oBACJ,YACA,OACkB;AAClB,UAAM,KAAK,QAAQ,UAAU,KAAK,KAAK;AACvC,WAAO,KAAK,QAAQ,OAAO,cAAc,UAAU,YAAY,EAAE,EAAE;AAAA,EACrE;AACF;AAGA,IAAO,iBAAQ;","names":[]}
|
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
MeetSudo: () => MeetSudo,
|
|
24
|
+
MeetSudoServer: () => MeetSudoServer,
|
|
24
25
|
meetsudo: () => meetsudo
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -572,11 +573,133 @@ var MeetSudo = class {
|
|
|
572
573
|
}
|
|
573
574
|
};
|
|
574
575
|
|
|
576
|
+
// src/server.ts
|
|
577
|
+
var MeetSudoServer = class {
|
|
578
|
+
constructor(config) {
|
|
579
|
+
if (!config.apiKey) {
|
|
580
|
+
throw new Error("MeetSudoServer: apiKey is required");
|
|
581
|
+
}
|
|
582
|
+
this.apiKey = config.apiKey;
|
|
583
|
+
this.apiUrl = config.apiUrl || "https://api.meetsudo.com";
|
|
584
|
+
}
|
|
585
|
+
async request(method, path, body) {
|
|
586
|
+
const res = await fetch(`${this.apiUrl}${path}`, {
|
|
587
|
+
method,
|
|
588
|
+
headers: {
|
|
589
|
+
"Content-Type": "application/json",
|
|
590
|
+
"X-API-Key": this.apiKey
|
|
591
|
+
},
|
|
592
|
+
body: body ? JSON.stringify(body) : void 0
|
|
593
|
+
});
|
|
594
|
+
if (!res.ok) {
|
|
595
|
+
const error = await res.json().catch(() => ({ error: "Request failed" }));
|
|
596
|
+
throw new Error(error.error || `Request failed: ${res.status}`);
|
|
597
|
+
}
|
|
598
|
+
return res.json();
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Identify a user with traits.
|
|
602
|
+
* Creates or updates the customer in MeetSudo.
|
|
603
|
+
*
|
|
604
|
+
* @param userId - Unique identifier for the user (your internal user ID)
|
|
605
|
+
* @param traits - User properties like email, name, plan, etc.
|
|
606
|
+
*
|
|
607
|
+
* @example
|
|
608
|
+
* await meetsudo.identify('user_123', {
|
|
609
|
+
* email: 'jane@example.com',
|
|
610
|
+
* name: 'Jane Smith',
|
|
611
|
+
* plan: 'pro'
|
|
612
|
+
* });
|
|
613
|
+
*/
|
|
614
|
+
async identify(userId, traits) {
|
|
615
|
+
return this.request("POST", "/widget/identify", {
|
|
616
|
+
anonymous_id: `server_${userId}`,
|
|
617
|
+
external_id: userId,
|
|
618
|
+
properties: traits || {}
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Track a custom event.
|
|
623
|
+
*
|
|
624
|
+
* @param userId - The user ID (same as used in identify)
|
|
625
|
+
* @param eventName - Name of the event (e.g., 'purchase_completed')
|
|
626
|
+
* @param properties - Additional event properties
|
|
627
|
+
*
|
|
628
|
+
* @example
|
|
629
|
+
* await meetsudo.track('user_123', 'purchase_completed', {
|
|
630
|
+
* order_id: 'ord_456',
|
|
631
|
+
* amount: 99.00,
|
|
632
|
+
* currency: 'USD'
|
|
633
|
+
* });
|
|
634
|
+
*/
|
|
635
|
+
async track(userId, eventName, properties) {
|
|
636
|
+
await this.request("POST", "/widget/init", {
|
|
637
|
+
anonymous_id: `server_${userId}`
|
|
638
|
+
});
|
|
639
|
+
return this.request("POST", "/widget/events", {
|
|
640
|
+
anonymous_id: `server_${userId}`,
|
|
641
|
+
events: [
|
|
642
|
+
{
|
|
643
|
+
name: eventName,
|
|
644
|
+
properties: properties || {},
|
|
645
|
+
page_url: "server",
|
|
646
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
647
|
+
}
|
|
648
|
+
]
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Track multiple events at once (batch).
|
|
653
|
+
*
|
|
654
|
+
* @param userId - The user ID
|
|
655
|
+
* @param events - Array of events to track
|
|
656
|
+
*
|
|
657
|
+
* @example
|
|
658
|
+
* await meetsudo.trackBatch('user_123', [
|
|
659
|
+
* { name: 'page_viewed', properties: { page: '/pricing' } },
|
|
660
|
+
* { name: 'button_clicked', properties: { button: 'cta' } }
|
|
661
|
+
* ]);
|
|
662
|
+
*/
|
|
663
|
+
async trackBatch(userId, events) {
|
|
664
|
+
await this.request("POST", "/widget/init", {
|
|
665
|
+
anonymous_id: `server_${userId}`
|
|
666
|
+
});
|
|
667
|
+
return this.request("POST", "/widget/events", {
|
|
668
|
+
anonymous_id: `server_${userId}`,
|
|
669
|
+
events: events.map((e) => ({
|
|
670
|
+
name: e.name,
|
|
671
|
+
properties: e.properties || {},
|
|
672
|
+
page_url: "server",
|
|
673
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
674
|
+
}))
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Get customer information.
|
|
679
|
+
*
|
|
680
|
+
* @param customerId - The MeetSudo customer ID
|
|
681
|
+
*/
|
|
682
|
+
async getCustomer(customerId) {
|
|
683
|
+
return this.request("GET", `/customers/${customerId}`);
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Get customer timeline (events, messages, sessions).
|
|
687
|
+
*
|
|
688
|
+
* @param customerId - The MeetSudo customer ID
|
|
689
|
+
* @param limit - Maximum number of items to return
|
|
690
|
+
*/
|
|
691
|
+
async getCustomerTimeline(customerId, limit) {
|
|
692
|
+
const qs = limit ? `?limit=${limit}` : "";
|
|
693
|
+
return this.request("GET", `/customers/${customerId}/timeline${qs}`);
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
|
|
575
697
|
// src/index.ts
|
|
576
698
|
var meetsudo = new MeetSudo();
|
|
577
699
|
// Annotate the CommonJS export names for ESM import in node:
|
|
578
700
|
0 && (module.exports = {
|
|
579
701
|
MeetSudo,
|
|
702
|
+
MeetSudoServer,
|
|
580
703
|
meetsudo
|
|
581
704
|
});
|
|
582
705
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/sdk.ts"],"sourcesContent":["export { MeetSudo } from \"./sdk\";\nexport type {\n MeetSudoConfig,\n UserTraits,\n EventProperties,\n PageProperties,\n ChatAction,\n} from \"./types\";\n\n// Default instance for convenience\nimport { MeetSudo } from \"./sdk\";\nexport const meetsudo = new MeetSudo();\n","import type {\n MeetSudoConfig,\n UserTraits,\n EventProperties,\n PageProperties,\n Features,\n InitResponse,\n IdentifyResponse,\n ChatAction,\n} from \"./types\";\n\nconst STORAGE_KEY = \"meetsudo_anon_id\";\nconst FLUSH_INTERVAL = 5000;\nconst REPLAY_FLUSH_INTERVAL = 10000;\nconst INACTIVITY_TIMEOUT = 60000; // 1 minute\n\ninterface EventData {\n name: string;\n properties: EventProperties;\n page_url: string;\n timestamp: string;\n}\n\ninterface State {\n apiKey: string | null;\n apiUrl: string;\n widgetUrl: string;\n anonId: string | null;\n custId: string | null;\n features: Features | null;\n workspaceName: string | null;\n initialized: boolean;\n iframe: HTMLIFrameElement | null;\n eventQ: EventData[];\n flushTimer: ReturnType<typeof setInterval> | null;\n replaySessionId: string | null;\n replaySequence: number;\n replayEvents: unknown[];\n replayFlushTimer: ReturnType<typeof setInterval> | null;\n replayStopFn: (() => void) | null;\n replayTotalEvents: number;\n replayStartedAt: string | null;\n replayRageClickCount: number;\n replayErrorCount: number;\n lastClickTime: number;\n lastClickTarget: EventTarget | null;\n clickCount: number;\n capturedEmails: Record<string, boolean>;\n lastActivityTime: number;\n inactivityTimer: ReturnType<typeof setTimeout> | null;\n replayPaused: boolean;\n}\n\nexport class MeetSudo {\n private state: State = {\n apiKey: null,\n apiUrl: \"https://api.meetsudo.com\",\n widgetUrl: \"https://widget.meetsudo.com\",\n anonId: null,\n custId: null,\n features: null,\n workspaceName: null,\n initialized: false,\n iframe: null,\n eventQ: [],\n flushTimer: null,\n replaySessionId: null,\n replaySequence: 0,\n replayEvents: [],\n replayFlushTimer: null,\n replayStopFn: null,\n replayTotalEvents: 0,\n replayStartedAt: null,\n replayRageClickCount: 0,\n replayErrorCount: 0,\n lastClickTime: 0,\n lastClickTarget: null,\n clickCount: 0,\n capturedEmails: {},\n lastActivityTime: Date.now(),\n inactivityTimer: null,\n replayPaused: false,\n };\n\n private config: MeetSudoConfig | null = null;\n\n /**\n * Initialize the MeetSudo SDK\n */\n async init(config: MeetSudoConfig): Promise<void> {\n if (!config.apiKey) {\n throw new Error(\"MeetSudo: apiKey is required\");\n }\n\n this.config = config;\n this.state.apiKey = config.apiKey;\n this.state.apiUrl = config.apiUrl || \"https://api.meetsudo.com\";\n this.state.widgetUrl = config.widgetUrl || \"https://widget.meetsudo.com\";\n this.state.anonId = this.getAnonId();\n\n try {\n const res = await this.api<InitResponse>(\"/widget/init\", {\n anonymous_id: this.state.anonId,\n url: window.location.href,\n referrer: document.referrer,\n });\n\n this.state.custId = res.customer_id;\n this.state.features = res.features;\n this.state.workspaceName = res.workspace_name;\n this.state.initialized = true;\n\n // Create chat iframe if enabled\n if (res.features.chat_enabled && !config.disableChat) {\n this.createIframe(res);\n }\n\n // Start event tracking if enabled\n if (res.features.events_enabled && !config.disableEvents) {\n this.state.flushTimer = setInterval(() => this.flushEvents(), FLUSH_INTERVAL);\n if (res.features.events_auto_page && !config.disableAutoPageView) {\n this.page();\n }\n this.setupEmailCapture();\n }\n\n // Start session replay if enabled and sampled in\n if (this.shouldSampleReplay() && !config.disableReplay) {\n this.startReplay();\n }\n\n // Setup beforeunload handler\n window.addEventListener(\"beforeunload\", () => this.handleUnload());\n } catch (error) {\n console.error(\"MeetSudo: init failed\", error);\n throw error;\n }\n }\n\n /**\n * Identify a user\n */\n async identify(userId: string, traits?: UserTraits): Promise<void> {\n if (!this.state.initialized) {\n console.warn(\"MeetSudo: call init() before identify()\");\n return;\n }\n\n try {\n const res = await this.api<IdentifyResponse>(\"/widget/identify\", {\n anonymous_id: this.state.anonId,\n external_id: userId,\n properties: traits || {},\n });\n\n this.state.custId = res.customer_id;\n\n // Notify chat iframe about identity change\n if (this.state.iframe?.contentWindow) {\n this.state.iframe.contentWindow.postMessage(\n {\n type: \"fi:identify\",\n customerId: res.customer_id,\n merged: res.merged,\n },\n \"*\"\n );\n }\n } catch (error) {\n console.error(\"MeetSudo: identify failed\", error);\n }\n }\n\n /**\n * Track a custom event\n */\n track(eventName: string, properties?: EventProperties): void {\n if (!this.state.initialized || !this.state.features?.events_enabled) {\n return;\n }\n\n this.state.eventQ.push({\n name: eventName,\n properties: properties || {},\n page_url: window.location.href,\n timestamp: new Date().toISOString(),\n });\n }\n\n /**\n * Track a page view\n */\n page(properties?: PageProperties): void {\n const props: PageProperties = {\n url: window.location.href,\n title: document.title,\n referrer: document.referrer,\n ...properties,\n };\n this.track(\"page_view\", props);\n }\n\n /**\n * Control the chat widget\n */\n chat(action: ChatAction): void {\n if (!this.state.iframe?.contentWindow) return;\n\n switch (action) {\n case \"open\":\n this.state.iframe.contentWindow.postMessage({ type: \"fi:chat\", action: \"open\" }, \"*\");\n this.state.iframe.style.width = \"400px\";\n this.state.iframe.style.height = \"560px\";\n break;\n case \"close\":\n this.state.iframe.contentWindow.postMessage({ type: \"fi:chat\", action: \"close\" }, \"*\");\n this.state.iframe.style.width = \"80px\";\n this.state.iframe.style.height = \"80px\";\n break;\n case \"hide\":\n this.state.iframe.style.display = \"none\";\n break;\n case \"show\":\n this.state.iframe.style.display = \"\";\n break;\n }\n }\n\n /**\n * Check if SDK is initialized\n */\n isInitialized(): boolean {\n return this.state.initialized;\n }\n\n /**\n * Get the anonymous ID\n */\n getAnonymousId(): string | null {\n return this.state.anonId;\n }\n\n /**\n * Get the customer ID (after identify)\n */\n getCustomerId(): string | null {\n return this.state.custId;\n }\n\n // Private methods\n\n private getAnonId(): string {\n try {\n const stored = localStorage.getItem(STORAGE_KEY);\n if (stored) return stored;\n } catch {\n /* private browsing */\n }\n\n const id = crypto.randomUUID?.() || this.generateUUID();\n try {\n localStorage.setItem(STORAGE_KEY, id);\n } catch {\n /* ignore */\n }\n return id;\n }\n\n private generateUUID(): string {\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n return (c === \"x\" ? r : (r & 0x3) | 0x8).toString(16);\n });\n }\n\n private async api<T>(path: string, data: unknown): Promise<T> {\n const res = await fetch(this.state.apiUrl + path, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.state.apiKey!,\n },\n body: JSON.stringify(data),\n });\n\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}`);\n }\n\n return res.json();\n }\n\n private createIframe(initData: InitResponse): void {\n if (this.state.iframe) return;\n\n const pos = this.state.features?.chat_position || \"bottom-right\";\n const iframe = document.createElement(\"iframe\");\n iframe.id = \"meetsudo-widget\";\n const posCSS = pos === \"bottom-left\" ? \"left:0;\" : \"right:0;\";\n iframe.style.cssText = `position:fixed;bottom:0;${posCSS}width:80px;height:80px;border:none;z-index:999999;background:transparent;color-scheme:normal;`;\n iframe.allow = \"clipboard-write\";\n iframe.src = this.state.widgetUrl;\n\n document.body.appendChild(iframe);\n this.state.iframe = iframe;\n\n iframe.onload = () => {\n iframe.contentWindow?.postMessage(\n {\n type: \"fi:config\",\n apiKey: this.state.apiKey,\n apiUrl: this.state.apiUrl,\n anonymousId: this.state.anonId,\n customerId: this.state.custId,\n workspaceName: this.state.workspaceName,\n features: this.state.features,\n conversationId: initData.open_conversation_id,\n messages: initData.messages || [],\n },\n \"*\"\n );\n };\n\n window.addEventListener(\"message\", (e) => {\n if (e.data?.type === \"fi:resize\") {\n if (e.data.state === \"open\") {\n iframe.style.width = \"400px\";\n iframe.style.height = \"560px\";\n } else {\n iframe.style.width = \"80px\";\n iframe.style.height = \"80px\";\n }\n }\n });\n }\n\n private flushEvents(): void {\n if (!this.state.eventQ.length) return;\n\n const batch = this.state.eventQ.splice(0);\n this.api(\"/widget/events\", {\n anonymous_id: this.state.anonId,\n events: batch,\n }).catch(() => {\n // Put events back on failure\n this.state.eventQ = batch.concat(this.state.eventQ);\n });\n }\n\n private shouldSampleReplay(): boolean {\n if (!this.state.features?.replay_enabled) return false;\n return Math.random() < (this.state.features.replay_sample_rate || 0);\n }\n\n private startReplay(): void {\n this.state.replaySessionId = crypto.randomUUID?.() || this.generateUUID();\n this.state.replaySequence = 0;\n this.state.replayEvents = [];\n this.state.replayTotalEvents = 0;\n this.state.replayStartedAt = new Date().toISOString();\n this.state.replayErrorCount = 0;\n this.state.replayRageClickCount = 0;\n\n this.setupRageClickDetection();\n this.setupErrorCapture();\n\n // Load rrweb dynamically\n const script = document.createElement(\"script\");\n script.type = \"module\";\n script.src = this.state.widgetUrl.replace(/\\/?(index\\.html)?$/, \"\") + \"/replay.js\";\n document.head.appendChild(script);\n\n // Wait for rrweb to load\n let attempts = 0;\n const checkRrweb = setInterval(() => {\n attempts++;\n // @ts-expect-error rrweb is loaded dynamically\n if (window.rrweb?.record) {\n clearInterval(checkRrweb);\n this.initRrweb();\n } else if (attempts > 50) {\n clearInterval(checkRrweb);\n console.warn(\"MeetSudo: replay failed to load\");\n }\n }, 100);\n }\n\n private initRrweb(): void {\n // @ts-expect-error rrweb is loaded dynamically\n this.state.replayStopFn = window.rrweb.record({\n emit: (event: unknown) => {\n this.state.replayEvents.push(event);\n this.state.replayTotalEvents++;\n },\n maskAllInputs: false,\n maskInputFn: (text: string, element: HTMLElement) => {\n if (this.isSensitiveInput(element)) {\n return \"*\".repeat(text.length || 8);\n }\n return text;\n },\n blockSelector: \".meetsudo-no-record, [data-meetsudo-no-record]\",\n inlineStylesheet: true,\n sampling: {\n mousemove: 50,\n mouseInteraction: true,\n scroll: 150,\n input: \"last\",\n },\n });\n\n this.state.replayFlushTimer = setInterval(() => this.flushReplayChunk(), REPLAY_FLUSH_INTERVAL);\n\n // Setup inactivity tracking\n this.setupActivityTracking();\n }\n\n private setupActivityTracking(): void {\n const activityEvents = [\"mousemove\", \"keydown\", \"click\", \"scroll\", \"touchstart\"];\n\n const handleActivity = () => {\n this.state.lastActivityTime = Date.now();\n\n // Resume replay if it was paused\n if (this.state.replayPaused) {\n this.resumeReplay();\n }\n\n // Reset inactivity timer\n if (this.state.inactivityTimer) {\n clearTimeout(this.state.inactivityTimer);\n }\n\n this.state.inactivityTimer = setTimeout(() => {\n this.pauseReplayDueToInactivity();\n }, INACTIVITY_TIMEOUT);\n };\n\n // Add event listeners\n activityEvents.forEach((event) => {\n document.addEventListener(event, handleActivity, { passive: true });\n });\n\n // Start initial inactivity timer\n this.state.inactivityTimer = setTimeout(() => {\n this.pauseReplayDueToInactivity();\n }, INACTIVITY_TIMEOUT);\n }\n\n private pauseReplayDueToInactivity(): void {\n if (this.state.replayPaused || !this.state.replaySessionId) return;\n\n this.state.replayPaused = true;\n\n // Stop rrweb recording\n if (this.state.replayStopFn) {\n this.state.replayStopFn();\n this.state.replayStopFn = null;\n }\n\n // Flush remaining events\n this.flushReplayChunk();\n\n // Stop flush timer\n if (this.state.replayFlushTimer) {\n clearInterval(this.state.replayFlushTimer);\n this.state.replayFlushTimer = null;\n }\n\n // Send end signal with inactivity flag\n if (this.state.apiKey) {\n this.api(\"/widget/replay/end\", {\n anonymous_id: this.state.anonId,\n session_id: this.state.replaySessionId,\n ended_at: new Date().toISOString(),\n event_count: this.state.replayTotalEvents,\n error_count: this.state.replayErrorCount,\n rage_click_count: this.state.replayRageClickCount,\n end_reason: \"inactivity\",\n }).catch(() => {});\n }\n }\n\n private resumeReplay(): void {\n if (!this.state.replayPaused || !this.state.features?.replay_enabled) return;\n\n this.state.replayPaused = false;\n\n // Start a new replay session\n this.state.replaySessionId = crypto.randomUUID?.() || this.generateUUID();\n this.state.replaySequence = 0;\n this.state.replayEvents = [];\n this.state.replayTotalEvents = 0;\n this.state.replayStartedAt = new Date().toISOString();\n this.state.replayErrorCount = 0;\n this.state.replayRageClickCount = 0;\n\n // Restart rrweb recording\n // @ts-expect-error rrweb is loaded dynamically\n if (window.rrweb?.record) {\n // @ts-expect-error rrweb is loaded dynamically\n this.state.replayStopFn = window.rrweb.record({\n emit: (event: unknown) => {\n this.state.replayEvents.push(event);\n this.state.replayTotalEvents++;\n },\n maskAllInputs: false,\n maskInputFn: (text: string, element: HTMLElement) => {\n if (this.isSensitiveInput(element)) {\n return \"*\".repeat(text.length || 8);\n }\n return text;\n },\n blockSelector: \".meetsudo-no-record, [data-meetsudo-no-record]\",\n inlineStylesheet: true,\n sampling: {\n mousemove: 50,\n mouseInteraction: true,\n scroll: 150,\n input: \"last\",\n },\n });\n\n this.state.replayFlushTimer = setInterval(() => this.flushReplayChunk(), REPLAY_FLUSH_INTERVAL);\n }\n }\n\n private isSensitiveInput(el: HTMLElement): boolean {\n if (!el) return true;\n const input = el as HTMLInputElement;\n const type = (input.type || \"\").toLowerCase();\n const name = (input.name || \"\").toLowerCase();\n const id = (input.id || \"\").toLowerCase();\n\n if (type === \"password\") return true;\n\n const sensitivePatterns = [/password/i, /card/i, /cvv/i, /cvc/i, /ssn/i, /secret/i, /token/i];\n const identifiers = name + \" \" + id;\n return sensitivePatterns.some((p) => p.test(identifiers));\n }\n\n private flushReplayChunk(): void {\n if (!this.state.replayEvents.length || this.state.replayPaused) return;\n\n const events = this.state.replayEvents.splice(0);\n const seq = this.state.replaySequence++;\n\n const payload: Record<string, unknown> = {\n anonymous_id: this.state.anonId,\n session_id: this.state.replaySessionId,\n sequence: seq,\n events,\n error_count: this.state.replayErrorCount,\n rage_click_count: this.state.replayRageClickCount,\n };\n\n if (seq === 0) {\n payload.page_url = window.location.href;\n payload.user_agent = navigator.userAgent;\n payload.screen_width = window.innerWidth;\n payload.screen_height = window.innerHeight;\n payload.started_at = this.state.replayStartedAt;\n }\n\n this.api(\"/widget/replay/chunk\", payload).catch(() => {\n this.state.replayEvents = events.concat(this.state.replayEvents);\n this.state.replaySequence--;\n });\n }\n\n private setupRageClickDetection(): void {\n document.addEventListener(\n \"click\",\n (e) => {\n if (!this.state.replaySessionId) return;\n\n const now = Date.now();\n const target = e.target;\n\n if (target === this.state.lastClickTarget && now - this.state.lastClickTime < 500) {\n this.state.clickCount++;\n if (this.state.clickCount >= 3) {\n this.state.replayRageClickCount++;\n this.state.clickCount = 0;\n this.track(\"rage_click\", { page_url: window.location.href });\n }\n } else {\n this.state.clickCount = 1;\n }\n\n this.state.lastClickTime = now;\n this.state.lastClickTarget = target;\n },\n true\n );\n }\n\n private setupErrorCapture(): void {\n window.onerror = (message, source, lineno, colno) => {\n if (this.state.replaySessionId) {\n this.state.replayErrorCount++;\n this.track(\"js_error\", {\n message: String(message).substring(0, 500),\n source: source || \"\",\n lineno: lineno || 0,\n });\n }\n return false;\n };\n\n window.addEventListener(\"unhandledrejection\", (e) => {\n if (this.state.replaySessionId) {\n this.state.replayErrorCount++;\n const message = e.reason instanceof Error ? e.reason.message : String(e.reason);\n this.track(\"js_error\", { message: message.substring(0, 500), type: \"unhandledrejection\" });\n }\n });\n }\n\n private setupEmailCapture(): void {\n document.addEventListener(\n \"blur\",\n (e) => {\n const el = e.target as HTMLInputElement;\n if (!el || el.tagName !== \"INPUT\") return;\n\n const type = (el.type || \"\").toLowerCase();\n const name = (el.name || \"\").toLowerCase();\n if (type === \"email\" || /email/.test(name)) {\n const value = el.value?.trim();\n if (value && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value) && !this.state.capturedEmails[value]) {\n this.state.capturedEmails[value] = true;\n this.track(\"email_captured\", { email: value });\n }\n }\n },\n true\n );\n }\n\n private handleUnload(): void {\n if (this.state.flushTimer) {\n clearInterval(this.state.flushTimer);\n }\n\n // Flush remaining events\n if (this.state.eventQ.length && this.state.apiKey) {\n const apiKey = this.state.apiKey;\n try {\n fetch(this.state.apiUrl + \"/widget/events\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": apiKey,\n },\n body: JSON.stringify({\n anonymous_id: this.state.anonId,\n events: this.state.eventQ,\n }),\n keepalive: true,\n }).catch(() => {});\n } catch {\n /* ignore */\n }\n }\n\n // End replay\n if (this.state.replaySessionId && this.state.apiKey) {\n const replayApiKey = this.state.apiKey;\n if (this.state.replayStopFn) {\n this.state.replayStopFn();\n }\n if (this.state.replayFlushTimer) {\n clearInterval(this.state.replayFlushTimer);\n }\n\n try {\n fetch(this.state.apiUrl + \"/widget/replay/end\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": replayApiKey,\n },\n body: JSON.stringify({\n anonymous_id: this.state.anonId,\n session_id: this.state.replaySessionId,\n ended_at: new Date().toISOString(),\n event_count: this.state.replayTotalEvents,\n error_count: this.state.replayErrorCount,\n rage_click_count: this.state.replayRageClickCount,\n }),\n keepalive: true,\n }).catch(() => {});\n } catch {\n /* ignore */\n }\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/sdk.ts","../src/server.ts"],"sourcesContent":["export { MeetSudo } from \"./sdk\";\nexport { MeetSudoServer } from \"./server\";\nexport type {\n MeetSudoConfig,\n UserTraits,\n EventProperties,\n PageProperties,\n ChatAction,\n} from \"./types\";\nexport type { MeetSudoServerConfig } from \"./server\";\n\n// Default instance for convenience\nimport { MeetSudo } from \"./sdk\";\nexport const meetsudo = new MeetSudo();\n","import type {\n MeetSudoConfig,\n UserTraits,\n EventProperties,\n PageProperties,\n Features,\n InitResponse,\n IdentifyResponse,\n ChatAction,\n} from \"./types\";\n\nconst STORAGE_KEY = \"meetsudo_anon_id\";\nconst FLUSH_INTERVAL = 5000;\nconst REPLAY_FLUSH_INTERVAL = 10000;\nconst INACTIVITY_TIMEOUT = 60000; // 1 minute\n\ninterface EventData {\n name: string;\n properties: EventProperties;\n page_url: string;\n timestamp: string;\n}\n\ninterface State {\n apiKey: string | null;\n apiUrl: string;\n widgetUrl: string;\n anonId: string | null;\n custId: string | null;\n features: Features | null;\n workspaceName: string | null;\n initialized: boolean;\n iframe: HTMLIFrameElement | null;\n eventQ: EventData[];\n flushTimer: ReturnType<typeof setInterval> | null;\n replaySessionId: string | null;\n replaySequence: number;\n replayEvents: unknown[];\n replayFlushTimer: ReturnType<typeof setInterval> | null;\n replayStopFn: (() => void) | null;\n replayTotalEvents: number;\n replayStartedAt: string | null;\n replayRageClickCount: number;\n replayErrorCount: number;\n lastClickTime: number;\n lastClickTarget: EventTarget | null;\n clickCount: number;\n capturedEmails: Record<string, boolean>;\n lastActivityTime: number;\n inactivityTimer: ReturnType<typeof setTimeout> | null;\n replayPaused: boolean;\n}\n\nexport class MeetSudo {\n private state: State = {\n apiKey: null,\n apiUrl: \"https://api.meetsudo.com\",\n widgetUrl: \"https://widget.meetsudo.com\",\n anonId: null,\n custId: null,\n features: null,\n workspaceName: null,\n initialized: false,\n iframe: null,\n eventQ: [],\n flushTimer: null,\n replaySessionId: null,\n replaySequence: 0,\n replayEvents: [],\n replayFlushTimer: null,\n replayStopFn: null,\n replayTotalEvents: 0,\n replayStartedAt: null,\n replayRageClickCount: 0,\n replayErrorCount: 0,\n lastClickTime: 0,\n lastClickTarget: null,\n clickCount: 0,\n capturedEmails: {},\n lastActivityTime: Date.now(),\n inactivityTimer: null,\n replayPaused: false,\n };\n\n private config: MeetSudoConfig | null = null;\n\n /**\n * Initialize the MeetSudo SDK\n */\n async init(config: MeetSudoConfig): Promise<void> {\n if (!config.apiKey) {\n throw new Error(\"MeetSudo: apiKey is required\");\n }\n\n this.config = config;\n this.state.apiKey = config.apiKey;\n this.state.apiUrl = config.apiUrl || \"https://api.meetsudo.com\";\n this.state.widgetUrl = config.widgetUrl || \"https://widget.meetsudo.com\";\n this.state.anonId = this.getAnonId();\n\n try {\n const res = await this.api<InitResponse>(\"/widget/init\", {\n anonymous_id: this.state.anonId,\n url: window.location.href,\n referrer: document.referrer,\n });\n\n this.state.custId = res.customer_id;\n this.state.features = res.features;\n this.state.workspaceName = res.workspace_name;\n this.state.initialized = true;\n\n // Create chat iframe if enabled\n if (res.features.chat_enabled && !config.disableChat) {\n this.createIframe(res);\n }\n\n // Start event tracking if enabled\n if (res.features.events_enabled && !config.disableEvents) {\n this.state.flushTimer = setInterval(() => this.flushEvents(), FLUSH_INTERVAL);\n if (res.features.events_auto_page && !config.disableAutoPageView) {\n this.page();\n }\n this.setupEmailCapture();\n }\n\n // Start session replay if enabled and sampled in\n if (this.shouldSampleReplay() && !config.disableReplay) {\n this.startReplay();\n }\n\n // Setup beforeunload handler\n window.addEventListener(\"beforeunload\", () => this.handleUnload());\n } catch (error) {\n console.error(\"MeetSudo: init failed\", error);\n throw error;\n }\n }\n\n /**\n * Identify a user\n */\n async identify(userId: string, traits?: UserTraits): Promise<void> {\n if (!this.state.initialized) {\n console.warn(\"MeetSudo: call init() before identify()\");\n return;\n }\n\n try {\n const res = await this.api<IdentifyResponse>(\"/widget/identify\", {\n anonymous_id: this.state.anonId,\n external_id: userId,\n properties: traits || {},\n });\n\n this.state.custId = res.customer_id;\n\n // Notify chat iframe about identity change\n if (this.state.iframe?.contentWindow) {\n this.state.iframe.contentWindow.postMessage(\n {\n type: \"fi:identify\",\n customerId: res.customer_id,\n merged: res.merged,\n },\n \"*\"\n );\n }\n } catch (error) {\n console.error(\"MeetSudo: identify failed\", error);\n }\n }\n\n /**\n * Track a custom event\n */\n track(eventName: string, properties?: EventProperties): void {\n if (!this.state.initialized || !this.state.features?.events_enabled) {\n return;\n }\n\n this.state.eventQ.push({\n name: eventName,\n properties: properties || {},\n page_url: window.location.href,\n timestamp: new Date().toISOString(),\n });\n }\n\n /**\n * Track a page view\n */\n page(properties?: PageProperties): void {\n const props: PageProperties = {\n url: window.location.href,\n title: document.title,\n referrer: document.referrer,\n ...properties,\n };\n this.track(\"page_view\", props);\n }\n\n /**\n * Control the chat widget\n */\n chat(action: ChatAction): void {\n if (!this.state.iframe?.contentWindow) return;\n\n switch (action) {\n case \"open\":\n this.state.iframe.contentWindow.postMessage({ type: \"fi:chat\", action: \"open\" }, \"*\");\n this.state.iframe.style.width = \"400px\";\n this.state.iframe.style.height = \"560px\";\n break;\n case \"close\":\n this.state.iframe.contentWindow.postMessage({ type: \"fi:chat\", action: \"close\" }, \"*\");\n this.state.iframe.style.width = \"80px\";\n this.state.iframe.style.height = \"80px\";\n break;\n case \"hide\":\n this.state.iframe.style.display = \"none\";\n break;\n case \"show\":\n this.state.iframe.style.display = \"\";\n break;\n }\n }\n\n /**\n * Check if SDK is initialized\n */\n isInitialized(): boolean {\n return this.state.initialized;\n }\n\n /**\n * Get the anonymous ID\n */\n getAnonymousId(): string | null {\n return this.state.anonId;\n }\n\n /**\n * Get the customer ID (after identify)\n */\n getCustomerId(): string | null {\n return this.state.custId;\n }\n\n // Private methods\n\n private getAnonId(): string {\n try {\n const stored = localStorage.getItem(STORAGE_KEY);\n if (stored) return stored;\n } catch {\n /* private browsing */\n }\n\n const id = crypto.randomUUID?.() || this.generateUUID();\n try {\n localStorage.setItem(STORAGE_KEY, id);\n } catch {\n /* ignore */\n }\n return id;\n }\n\n private generateUUID(): string {\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n return (c === \"x\" ? r : (r & 0x3) | 0x8).toString(16);\n });\n }\n\n private async api<T>(path: string, data: unknown): Promise<T> {\n const res = await fetch(this.state.apiUrl + path, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.state.apiKey!,\n },\n body: JSON.stringify(data),\n });\n\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}`);\n }\n\n return res.json();\n }\n\n private createIframe(initData: InitResponse): void {\n if (this.state.iframe) return;\n\n const pos = this.state.features?.chat_position || \"bottom-right\";\n const iframe = document.createElement(\"iframe\");\n iframe.id = \"meetsudo-widget\";\n const posCSS = pos === \"bottom-left\" ? \"left:0;\" : \"right:0;\";\n iframe.style.cssText = `position:fixed;bottom:0;${posCSS}width:80px;height:80px;border:none;z-index:999999;background:transparent;color-scheme:normal;`;\n iframe.allow = \"clipboard-write\";\n iframe.src = this.state.widgetUrl;\n\n document.body.appendChild(iframe);\n this.state.iframe = iframe;\n\n iframe.onload = () => {\n iframe.contentWindow?.postMessage(\n {\n type: \"fi:config\",\n apiKey: this.state.apiKey,\n apiUrl: this.state.apiUrl,\n anonymousId: this.state.anonId,\n customerId: this.state.custId,\n workspaceName: this.state.workspaceName,\n features: this.state.features,\n conversationId: initData.open_conversation_id,\n messages: initData.messages || [],\n },\n \"*\"\n );\n };\n\n window.addEventListener(\"message\", (e) => {\n if (e.data?.type === \"fi:resize\") {\n if (e.data.state === \"open\") {\n iframe.style.width = \"400px\";\n iframe.style.height = \"560px\";\n } else {\n iframe.style.width = \"80px\";\n iframe.style.height = \"80px\";\n }\n }\n });\n }\n\n private flushEvents(): void {\n if (!this.state.eventQ.length) return;\n\n const batch = this.state.eventQ.splice(0);\n this.api(\"/widget/events\", {\n anonymous_id: this.state.anonId,\n events: batch,\n }).catch(() => {\n // Put events back on failure\n this.state.eventQ = batch.concat(this.state.eventQ);\n });\n }\n\n private shouldSampleReplay(): boolean {\n if (!this.state.features?.replay_enabled) return false;\n return Math.random() < (this.state.features.replay_sample_rate || 0);\n }\n\n private startReplay(): void {\n this.state.replaySessionId = crypto.randomUUID?.() || this.generateUUID();\n this.state.replaySequence = 0;\n this.state.replayEvents = [];\n this.state.replayTotalEvents = 0;\n this.state.replayStartedAt = new Date().toISOString();\n this.state.replayErrorCount = 0;\n this.state.replayRageClickCount = 0;\n\n this.setupRageClickDetection();\n this.setupErrorCapture();\n\n // Load rrweb dynamically\n const script = document.createElement(\"script\");\n script.type = \"module\";\n script.src = this.state.widgetUrl.replace(/\\/?(index\\.html)?$/, \"\") + \"/replay.js\";\n document.head.appendChild(script);\n\n // Wait for rrweb to load\n let attempts = 0;\n const checkRrweb = setInterval(() => {\n attempts++;\n // @ts-expect-error rrweb is loaded dynamically\n if (window.rrweb?.record) {\n clearInterval(checkRrweb);\n this.initRrweb();\n } else if (attempts > 50) {\n clearInterval(checkRrweb);\n console.warn(\"MeetSudo: replay failed to load\");\n }\n }, 100);\n }\n\n private initRrweb(): void {\n // @ts-expect-error rrweb is loaded dynamically\n this.state.replayStopFn = window.rrweb.record({\n emit: (event: unknown) => {\n this.state.replayEvents.push(event);\n this.state.replayTotalEvents++;\n },\n maskAllInputs: false,\n maskInputFn: (text: string, element: HTMLElement) => {\n if (this.isSensitiveInput(element)) {\n return \"*\".repeat(text.length || 8);\n }\n return text;\n },\n blockSelector: \".meetsudo-no-record, [data-meetsudo-no-record]\",\n inlineStylesheet: true,\n sampling: {\n mousemove: 50,\n mouseInteraction: true,\n scroll: 150,\n input: \"last\",\n },\n });\n\n this.state.replayFlushTimer = setInterval(() => this.flushReplayChunk(), REPLAY_FLUSH_INTERVAL);\n\n // Setup inactivity tracking\n this.setupActivityTracking();\n }\n\n private setupActivityTracking(): void {\n const activityEvents = [\"mousemove\", \"keydown\", \"click\", \"scroll\", \"touchstart\"];\n\n const handleActivity = () => {\n this.state.lastActivityTime = Date.now();\n\n // Resume replay if it was paused\n if (this.state.replayPaused) {\n this.resumeReplay();\n }\n\n // Reset inactivity timer\n if (this.state.inactivityTimer) {\n clearTimeout(this.state.inactivityTimer);\n }\n\n this.state.inactivityTimer = setTimeout(() => {\n this.pauseReplayDueToInactivity();\n }, INACTIVITY_TIMEOUT);\n };\n\n // Add event listeners\n activityEvents.forEach((event) => {\n document.addEventListener(event, handleActivity, { passive: true });\n });\n\n // Start initial inactivity timer\n this.state.inactivityTimer = setTimeout(() => {\n this.pauseReplayDueToInactivity();\n }, INACTIVITY_TIMEOUT);\n }\n\n private pauseReplayDueToInactivity(): void {\n if (this.state.replayPaused || !this.state.replaySessionId) return;\n\n this.state.replayPaused = true;\n\n // Stop rrweb recording\n if (this.state.replayStopFn) {\n this.state.replayStopFn();\n this.state.replayStopFn = null;\n }\n\n // Flush remaining events\n this.flushReplayChunk();\n\n // Stop flush timer\n if (this.state.replayFlushTimer) {\n clearInterval(this.state.replayFlushTimer);\n this.state.replayFlushTimer = null;\n }\n\n // Send end signal with inactivity flag\n if (this.state.apiKey) {\n this.api(\"/widget/replay/end\", {\n anonymous_id: this.state.anonId,\n session_id: this.state.replaySessionId,\n ended_at: new Date().toISOString(),\n event_count: this.state.replayTotalEvents,\n error_count: this.state.replayErrorCount,\n rage_click_count: this.state.replayRageClickCount,\n end_reason: \"inactivity\",\n }).catch(() => {});\n }\n }\n\n private resumeReplay(): void {\n if (!this.state.replayPaused || !this.state.features?.replay_enabled) return;\n\n this.state.replayPaused = false;\n\n // Start a new replay session\n this.state.replaySessionId = crypto.randomUUID?.() || this.generateUUID();\n this.state.replaySequence = 0;\n this.state.replayEvents = [];\n this.state.replayTotalEvents = 0;\n this.state.replayStartedAt = new Date().toISOString();\n this.state.replayErrorCount = 0;\n this.state.replayRageClickCount = 0;\n\n // Restart rrweb recording\n // @ts-expect-error rrweb is loaded dynamically\n if (window.rrweb?.record) {\n // @ts-expect-error rrweb is loaded dynamically\n this.state.replayStopFn = window.rrweb.record({\n emit: (event: unknown) => {\n this.state.replayEvents.push(event);\n this.state.replayTotalEvents++;\n },\n maskAllInputs: false,\n maskInputFn: (text: string, element: HTMLElement) => {\n if (this.isSensitiveInput(element)) {\n return \"*\".repeat(text.length || 8);\n }\n return text;\n },\n blockSelector: \".meetsudo-no-record, [data-meetsudo-no-record]\",\n inlineStylesheet: true,\n sampling: {\n mousemove: 50,\n mouseInteraction: true,\n scroll: 150,\n input: \"last\",\n },\n });\n\n this.state.replayFlushTimer = setInterval(() => this.flushReplayChunk(), REPLAY_FLUSH_INTERVAL);\n }\n }\n\n private isSensitiveInput(el: HTMLElement): boolean {\n if (!el) return true;\n const input = el as HTMLInputElement;\n const type = (input.type || \"\").toLowerCase();\n const name = (input.name || \"\").toLowerCase();\n const id = (input.id || \"\").toLowerCase();\n\n if (type === \"password\") return true;\n\n const sensitivePatterns = [/password/i, /card/i, /cvv/i, /cvc/i, /ssn/i, /secret/i, /token/i];\n const identifiers = name + \" \" + id;\n return sensitivePatterns.some((p) => p.test(identifiers));\n }\n\n private flushReplayChunk(): void {\n if (!this.state.replayEvents.length || this.state.replayPaused) return;\n\n const events = this.state.replayEvents.splice(0);\n const seq = this.state.replaySequence++;\n\n const payload: Record<string, unknown> = {\n anonymous_id: this.state.anonId,\n session_id: this.state.replaySessionId,\n sequence: seq,\n events,\n error_count: this.state.replayErrorCount,\n rage_click_count: this.state.replayRageClickCount,\n };\n\n if (seq === 0) {\n payload.page_url = window.location.href;\n payload.user_agent = navigator.userAgent;\n payload.screen_width = window.innerWidth;\n payload.screen_height = window.innerHeight;\n payload.started_at = this.state.replayStartedAt;\n }\n\n this.api(\"/widget/replay/chunk\", payload).catch(() => {\n this.state.replayEvents = events.concat(this.state.replayEvents);\n this.state.replaySequence--;\n });\n }\n\n private setupRageClickDetection(): void {\n document.addEventListener(\n \"click\",\n (e) => {\n if (!this.state.replaySessionId) return;\n\n const now = Date.now();\n const target = e.target;\n\n if (target === this.state.lastClickTarget && now - this.state.lastClickTime < 500) {\n this.state.clickCount++;\n if (this.state.clickCount >= 3) {\n this.state.replayRageClickCount++;\n this.state.clickCount = 0;\n this.track(\"rage_click\", { page_url: window.location.href });\n }\n } else {\n this.state.clickCount = 1;\n }\n\n this.state.lastClickTime = now;\n this.state.lastClickTarget = target;\n },\n true\n );\n }\n\n private setupErrorCapture(): void {\n window.onerror = (message, source, lineno, colno) => {\n if (this.state.replaySessionId) {\n this.state.replayErrorCount++;\n this.track(\"js_error\", {\n message: String(message).substring(0, 500),\n source: source || \"\",\n lineno: lineno || 0,\n });\n }\n return false;\n };\n\n window.addEventListener(\"unhandledrejection\", (e) => {\n if (this.state.replaySessionId) {\n this.state.replayErrorCount++;\n const message = e.reason instanceof Error ? e.reason.message : String(e.reason);\n this.track(\"js_error\", { message: message.substring(0, 500), type: \"unhandledrejection\" });\n }\n });\n }\n\n private setupEmailCapture(): void {\n document.addEventListener(\n \"blur\",\n (e) => {\n const el = e.target as HTMLInputElement;\n if (!el || el.tagName !== \"INPUT\") return;\n\n const type = (el.type || \"\").toLowerCase();\n const name = (el.name || \"\").toLowerCase();\n if (type === \"email\" || /email/.test(name)) {\n const value = el.value?.trim();\n if (value && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value) && !this.state.capturedEmails[value]) {\n this.state.capturedEmails[value] = true;\n this.track(\"email_captured\", { email: value });\n }\n }\n },\n true\n );\n }\n\n private handleUnload(): void {\n if (this.state.flushTimer) {\n clearInterval(this.state.flushTimer);\n }\n\n // Flush remaining events\n if (this.state.eventQ.length && this.state.apiKey) {\n const apiKey = this.state.apiKey;\n try {\n fetch(this.state.apiUrl + \"/widget/events\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": apiKey,\n },\n body: JSON.stringify({\n anonymous_id: this.state.anonId,\n events: this.state.eventQ,\n }),\n keepalive: true,\n }).catch(() => {});\n } catch {\n /* ignore */\n }\n }\n\n // End replay\n if (this.state.replaySessionId && this.state.apiKey) {\n const replayApiKey = this.state.apiKey;\n if (this.state.replayStopFn) {\n this.state.replayStopFn();\n }\n if (this.state.replayFlushTimer) {\n clearInterval(this.state.replayFlushTimer);\n }\n\n try {\n fetch(this.state.apiUrl + \"/widget/replay/end\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": replayApiKey,\n },\n body: JSON.stringify({\n anonymous_id: this.state.anonId,\n session_id: this.state.replaySessionId,\n ended_at: new Date().toISOString(),\n event_count: this.state.replayTotalEvents,\n error_count: this.state.replayErrorCount,\n rage_click_count: this.state.replayRageClickCount,\n }),\n keepalive: true,\n }).catch(() => {});\n } catch {\n /* ignore */\n }\n }\n }\n}\n","/**\n * MeetSudo Server SDK\n * Use this for server-side event tracking, user identification, and API calls.\n */\n\nexport interface MeetSudoServerConfig {\n apiKey: string;\n apiUrl?: string;\n}\n\nexport interface UserTraits {\n email?: string;\n name?: string;\n [key: string]: unknown;\n}\n\nexport interface EventProperties {\n [key: string]: unknown;\n}\n\nexport class MeetSudoServer {\n private apiKey: string;\n private apiUrl: string;\n\n constructor(config: MeetSudoServerConfig) {\n if (!config.apiKey) {\n throw new Error(\"MeetSudoServer: apiKey is required\");\n }\n this.apiKey = config.apiKey;\n this.apiUrl = config.apiUrl || \"https://api.meetsudo.com\";\n }\n\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const res = await fetch(`${this.apiUrl}${path}`, {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!res.ok) {\n const error = await res.json().catch(() => ({ error: \"Request failed\" }));\n throw new Error(error.error || `Request failed: ${res.status}`);\n }\n\n return res.json();\n }\n\n /**\n * Identify a user with traits.\n * Creates or updates the customer in MeetSudo.\n *\n * @param userId - Unique identifier for the user (your internal user ID)\n * @param traits - User properties like email, name, plan, etc.\n *\n * @example\n * await meetsudo.identify('user_123', {\n * email: 'jane@example.com',\n * name: 'Jane Smith',\n * plan: 'pro'\n * });\n */\n async identify(userId: string, traits?: UserTraits): Promise<{ customer_id: string }> {\n return this.request(\"POST\", \"/widget/identify\", {\n anonymous_id: `server_${userId}`,\n external_id: userId,\n properties: traits || {},\n });\n }\n\n /**\n * Track a custom event.\n *\n * @param userId - The user ID (same as used in identify)\n * @param eventName - Name of the event (e.g., 'purchase_completed')\n * @param properties - Additional event properties\n *\n * @example\n * await meetsudo.track('user_123', 'purchase_completed', {\n * order_id: 'ord_456',\n * amount: 99.00,\n * currency: 'USD'\n * });\n */\n async track(\n userId: string,\n eventName: string,\n properties?: EventProperties\n ): Promise<{ received: number }> {\n // First ensure customer exists\n await this.request(\"POST\", \"/widget/init\", {\n anonymous_id: `server_${userId}`,\n });\n\n return this.request(\"POST\", \"/widget/events\", {\n anonymous_id: `server_${userId}`,\n events: [\n {\n name: eventName,\n properties: properties || {},\n page_url: \"server\",\n timestamp: new Date().toISOString(),\n },\n ],\n });\n }\n\n /**\n * Track multiple events at once (batch).\n *\n * @param userId - The user ID\n * @param events - Array of events to track\n *\n * @example\n * await meetsudo.trackBatch('user_123', [\n * { name: 'page_viewed', properties: { page: '/pricing' } },\n * { name: 'button_clicked', properties: { button: 'cta' } }\n * ]);\n */\n async trackBatch(\n userId: string,\n events: Array<{ name: string; properties?: EventProperties }>\n ): Promise<{ received: number }> {\n // First ensure customer exists\n await this.request(\"POST\", \"/widget/init\", {\n anonymous_id: `server_${userId}`,\n });\n\n return this.request(\"POST\", \"/widget/events\", {\n anonymous_id: `server_${userId}`,\n events: events.map((e) => ({\n name: e.name,\n properties: e.properties || {},\n page_url: \"server\",\n timestamp: new Date().toISOString(),\n })),\n });\n }\n\n /**\n * Get customer information.\n *\n * @param customerId - The MeetSudo customer ID\n */\n async getCustomer(customerId: string): Promise<unknown> {\n return this.request(\"GET\", `/customers/${customerId}`);\n }\n\n /**\n * Get customer timeline (events, messages, sessions).\n *\n * @param customerId - The MeetSudo customer ID\n * @param limit - Maximum number of items to return\n */\n async getCustomerTimeline(\n customerId: string,\n limit?: number\n ): Promise<unknown> {\n const qs = limit ? `?limit=${limit}` : \"\";\n return this.request(\"GET\", `/customers/${customerId}/timeline${qs}`);\n }\n}\n\n// Default export for convenience\nexport default MeetSudoServer;\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MeetSudoServer
|
|
3
|
+
} from "./chunk-2H6T3CDL.mjs";
|
|
4
|
+
|
|
1
5
|
// src/sdk.ts
|
|
2
6
|
var STORAGE_KEY = "meetsudo_anon_id";
|
|
3
7
|
var FLUSH_INTERVAL = 5e3;
|
|
@@ -549,6 +553,7 @@ var MeetSudo = class {
|
|
|
549
553
|
var meetsudo = new MeetSudo();
|
|
550
554
|
export {
|
|
551
555
|
MeetSudo,
|
|
556
|
+
MeetSudoServer,
|
|
552
557
|
meetsudo
|
|
553
558
|
};
|
|
554
559
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sdk.ts","../src/index.ts"],"sourcesContent":["import type {\n MeetSudoConfig,\n UserTraits,\n EventProperties,\n PageProperties,\n Features,\n InitResponse,\n IdentifyResponse,\n ChatAction,\n} from \"./types\";\n\nconst STORAGE_KEY = \"meetsudo_anon_id\";\nconst FLUSH_INTERVAL = 5000;\nconst REPLAY_FLUSH_INTERVAL = 10000;\nconst INACTIVITY_TIMEOUT = 60000; // 1 minute\n\ninterface EventData {\n name: string;\n properties: EventProperties;\n page_url: string;\n timestamp: string;\n}\n\ninterface State {\n apiKey: string | null;\n apiUrl: string;\n widgetUrl: string;\n anonId: string | null;\n custId: string | null;\n features: Features | null;\n workspaceName: string | null;\n initialized: boolean;\n iframe: HTMLIFrameElement | null;\n eventQ: EventData[];\n flushTimer: ReturnType<typeof setInterval> | null;\n replaySessionId: string | null;\n replaySequence: number;\n replayEvents: unknown[];\n replayFlushTimer: ReturnType<typeof setInterval> | null;\n replayStopFn: (() => void) | null;\n replayTotalEvents: number;\n replayStartedAt: string | null;\n replayRageClickCount: number;\n replayErrorCount: number;\n lastClickTime: number;\n lastClickTarget: EventTarget | null;\n clickCount: number;\n capturedEmails: Record<string, boolean>;\n lastActivityTime: number;\n inactivityTimer: ReturnType<typeof setTimeout> | null;\n replayPaused: boolean;\n}\n\nexport class MeetSudo {\n private state: State = {\n apiKey: null,\n apiUrl: \"https://api.meetsudo.com\",\n widgetUrl: \"https://widget.meetsudo.com\",\n anonId: null,\n custId: null,\n features: null,\n workspaceName: null,\n initialized: false,\n iframe: null,\n eventQ: [],\n flushTimer: null,\n replaySessionId: null,\n replaySequence: 0,\n replayEvents: [],\n replayFlushTimer: null,\n replayStopFn: null,\n replayTotalEvents: 0,\n replayStartedAt: null,\n replayRageClickCount: 0,\n replayErrorCount: 0,\n lastClickTime: 0,\n lastClickTarget: null,\n clickCount: 0,\n capturedEmails: {},\n lastActivityTime: Date.now(),\n inactivityTimer: null,\n replayPaused: false,\n };\n\n private config: MeetSudoConfig | null = null;\n\n /**\n * Initialize the MeetSudo SDK\n */\n async init(config: MeetSudoConfig): Promise<void> {\n if (!config.apiKey) {\n throw new Error(\"MeetSudo: apiKey is required\");\n }\n\n this.config = config;\n this.state.apiKey = config.apiKey;\n this.state.apiUrl = config.apiUrl || \"https://api.meetsudo.com\";\n this.state.widgetUrl = config.widgetUrl || \"https://widget.meetsudo.com\";\n this.state.anonId = this.getAnonId();\n\n try {\n const res = await this.api<InitResponse>(\"/widget/init\", {\n anonymous_id: this.state.anonId,\n url: window.location.href,\n referrer: document.referrer,\n });\n\n this.state.custId = res.customer_id;\n this.state.features = res.features;\n this.state.workspaceName = res.workspace_name;\n this.state.initialized = true;\n\n // Create chat iframe if enabled\n if (res.features.chat_enabled && !config.disableChat) {\n this.createIframe(res);\n }\n\n // Start event tracking if enabled\n if (res.features.events_enabled && !config.disableEvents) {\n this.state.flushTimer = setInterval(() => this.flushEvents(), FLUSH_INTERVAL);\n if (res.features.events_auto_page && !config.disableAutoPageView) {\n this.page();\n }\n this.setupEmailCapture();\n }\n\n // Start session replay if enabled and sampled in\n if (this.shouldSampleReplay() && !config.disableReplay) {\n this.startReplay();\n }\n\n // Setup beforeunload handler\n window.addEventListener(\"beforeunload\", () => this.handleUnload());\n } catch (error) {\n console.error(\"MeetSudo: init failed\", error);\n throw error;\n }\n }\n\n /**\n * Identify a user\n */\n async identify(userId: string, traits?: UserTraits): Promise<void> {\n if (!this.state.initialized) {\n console.warn(\"MeetSudo: call init() before identify()\");\n return;\n }\n\n try {\n const res = await this.api<IdentifyResponse>(\"/widget/identify\", {\n anonymous_id: this.state.anonId,\n external_id: userId,\n properties: traits || {},\n });\n\n this.state.custId = res.customer_id;\n\n // Notify chat iframe about identity change\n if (this.state.iframe?.contentWindow) {\n this.state.iframe.contentWindow.postMessage(\n {\n type: \"fi:identify\",\n customerId: res.customer_id,\n merged: res.merged,\n },\n \"*\"\n );\n }\n } catch (error) {\n console.error(\"MeetSudo: identify failed\", error);\n }\n }\n\n /**\n * Track a custom event\n */\n track(eventName: string, properties?: EventProperties): void {\n if (!this.state.initialized || !this.state.features?.events_enabled) {\n return;\n }\n\n this.state.eventQ.push({\n name: eventName,\n properties: properties || {},\n page_url: window.location.href,\n timestamp: new Date().toISOString(),\n });\n }\n\n /**\n * Track a page view\n */\n page(properties?: PageProperties): void {\n const props: PageProperties = {\n url: window.location.href,\n title: document.title,\n referrer: document.referrer,\n ...properties,\n };\n this.track(\"page_view\", props);\n }\n\n /**\n * Control the chat widget\n */\n chat(action: ChatAction): void {\n if (!this.state.iframe?.contentWindow) return;\n\n switch (action) {\n case \"open\":\n this.state.iframe.contentWindow.postMessage({ type: \"fi:chat\", action: \"open\" }, \"*\");\n this.state.iframe.style.width = \"400px\";\n this.state.iframe.style.height = \"560px\";\n break;\n case \"close\":\n this.state.iframe.contentWindow.postMessage({ type: \"fi:chat\", action: \"close\" }, \"*\");\n this.state.iframe.style.width = \"80px\";\n this.state.iframe.style.height = \"80px\";\n break;\n case \"hide\":\n this.state.iframe.style.display = \"none\";\n break;\n case \"show\":\n this.state.iframe.style.display = \"\";\n break;\n }\n }\n\n /**\n * Check if SDK is initialized\n */\n isInitialized(): boolean {\n return this.state.initialized;\n }\n\n /**\n * Get the anonymous ID\n */\n getAnonymousId(): string | null {\n return this.state.anonId;\n }\n\n /**\n * Get the customer ID (after identify)\n */\n getCustomerId(): string | null {\n return this.state.custId;\n }\n\n // Private methods\n\n private getAnonId(): string {\n try {\n const stored = localStorage.getItem(STORAGE_KEY);\n if (stored) return stored;\n } catch {\n /* private browsing */\n }\n\n const id = crypto.randomUUID?.() || this.generateUUID();\n try {\n localStorage.setItem(STORAGE_KEY, id);\n } catch {\n /* ignore */\n }\n return id;\n }\n\n private generateUUID(): string {\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n return (c === \"x\" ? r : (r & 0x3) | 0x8).toString(16);\n });\n }\n\n private async api<T>(path: string, data: unknown): Promise<T> {\n const res = await fetch(this.state.apiUrl + path, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.state.apiKey!,\n },\n body: JSON.stringify(data),\n });\n\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}`);\n }\n\n return res.json();\n }\n\n private createIframe(initData: InitResponse): void {\n if (this.state.iframe) return;\n\n const pos = this.state.features?.chat_position || \"bottom-right\";\n const iframe = document.createElement(\"iframe\");\n iframe.id = \"meetsudo-widget\";\n const posCSS = pos === \"bottom-left\" ? \"left:0;\" : \"right:0;\";\n iframe.style.cssText = `position:fixed;bottom:0;${posCSS}width:80px;height:80px;border:none;z-index:999999;background:transparent;color-scheme:normal;`;\n iframe.allow = \"clipboard-write\";\n iframe.src = this.state.widgetUrl;\n\n document.body.appendChild(iframe);\n this.state.iframe = iframe;\n\n iframe.onload = () => {\n iframe.contentWindow?.postMessage(\n {\n type: \"fi:config\",\n apiKey: this.state.apiKey,\n apiUrl: this.state.apiUrl,\n anonymousId: this.state.anonId,\n customerId: this.state.custId,\n workspaceName: this.state.workspaceName,\n features: this.state.features,\n conversationId: initData.open_conversation_id,\n messages: initData.messages || [],\n },\n \"*\"\n );\n };\n\n window.addEventListener(\"message\", (e) => {\n if (e.data?.type === \"fi:resize\") {\n if (e.data.state === \"open\") {\n iframe.style.width = \"400px\";\n iframe.style.height = \"560px\";\n } else {\n iframe.style.width = \"80px\";\n iframe.style.height = \"80px\";\n }\n }\n });\n }\n\n private flushEvents(): void {\n if (!this.state.eventQ.length) return;\n\n const batch = this.state.eventQ.splice(0);\n this.api(\"/widget/events\", {\n anonymous_id: this.state.anonId,\n events: batch,\n }).catch(() => {\n // Put events back on failure\n this.state.eventQ = batch.concat(this.state.eventQ);\n });\n }\n\n private shouldSampleReplay(): boolean {\n if (!this.state.features?.replay_enabled) return false;\n return Math.random() < (this.state.features.replay_sample_rate || 0);\n }\n\n private startReplay(): void {\n this.state.replaySessionId = crypto.randomUUID?.() || this.generateUUID();\n this.state.replaySequence = 0;\n this.state.replayEvents = [];\n this.state.replayTotalEvents = 0;\n this.state.replayStartedAt = new Date().toISOString();\n this.state.replayErrorCount = 0;\n this.state.replayRageClickCount = 0;\n\n this.setupRageClickDetection();\n this.setupErrorCapture();\n\n // Load rrweb dynamically\n const script = document.createElement(\"script\");\n script.type = \"module\";\n script.src = this.state.widgetUrl.replace(/\\/?(index\\.html)?$/, \"\") + \"/replay.js\";\n document.head.appendChild(script);\n\n // Wait for rrweb to load\n let attempts = 0;\n const checkRrweb = setInterval(() => {\n attempts++;\n // @ts-expect-error rrweb is loaded dynamically\n if (window.rrweb?.record) {\n clearInterval(checkRrweb);\n this.initRrweb();\n } else if (attempts > 50) {\n clearInterval(checkRrweb);\n console.warn(\"MeetSudo: replay failed to load\");\n }\n }, 100);\n }\n\n private initRrweb(): void {\n // @ts-expect-error rrweb is loaded dynamically\n this.state.replayStopFn = window.rrweb.record({\n emit: (event: unknown) => {\n this.state.replayEvents.push(event);\n this.state.replayTotalEvents++;\n },\n maskAllInputs: false,\n maskInputFn: (text: string, element: HTMLElement) => {\n if (this.isSensitiveInput(element)) {\n return \"*\".repeat(text.length || 8);\n }\n return text;\n },\n blockSelector: \".meetsudo-no-record, [data-meetsudo-no-record]\",\n inlineStylesheet: true,\n sampling: {\n mousemove: 50,\n mouseInteraction: true,\n scroll: 150,\n input: \"last\",\n },\n });\n\n this.state.replayFlushTimer = setInterval(() => this.flushReplayChunk(), REPLAY_FLUSH_INTERVAL);\n\n // Setup inactivity tracking\n this.setupActivityTracking();\n }\n\n private setupActivityTracking(): void {\n const activityEvents = [\"mousemove\", \"keydown\", \"click\", \"scroll\", \"touchstart\"];\n\n const handleActivity = () => {\n this.state.lastActivityTime = Date.now();\n\n // Resume replay if it was paused\n if (this.state.replayPaused) {\n this.resumeReplay();\n }\n\n // Reset inactivity timer\n if (this.state.inactivityTimer) {\n clearTimeout(this.state.inactivityTimer);\n }\n\n this.state.inactivityTimer = setTimeout(() => {\n this.pauseReplayDueToInactivity();\n }, INACTIVITY_TIMEOUT);\n };\n\n // Add event listeners\n activityEvents.forEach((event) => {\n document.addEventListener(event, handleActivity, { passive: true });\n });\n\n // Start initial inactivity timer\n this.state.inactivityTimer = setTimeout(() => {\n this.pauseReplayDueToInactivity();\n }, INACTIVITY_TIMEOUT);\n }\n\n private pauseReplayDueToInactivity(): void {\n if (this.state.replayPaused || !this.state.replaySessionId) return;\n\n this.state.replayPaused = true;\n\n // Stop rrweb recording\n if (this.state.replayStopFn) {\n this.state.replayStopFn();\n this.state.replayStopFn = null;\n }\n\n // Flush remaining events\n this.flushReplayChunk();\n\n // Stop flush timer\n if (this.state.replayFlushTimer) {\n clearInterval(this.state.replayFlushTimer);\n this.state.replayFlushTimer = null;\n }\n\n // Send end signal with inactivity flag\n if (this.state.apiKey) {\n this.api(\"/widget/replay/end\", {\n anonymous_id: this.state.anonId,\n session_id: this.state.replaySessionId,\n ended_at: new Date().toISOString(),\n event_count: this.state.replayTotalEvents,\n error_count: this.state.replayErrorCount,\n rage_click_count: this.state.replayRageClickCount,\n end_reason: \"inactivity\",\n }).catch(() => {});\n }\n }\n\n private resumeReplay(): void {\n if (!this.state.replayPaused || !this.state.features?.replay_enabled) return;\n\n this.state.replayPaused = false;\n\n // Start a new replay session\n this.state.replaySessionId = crypto.randomUUID?.() || this.generateUUID();\n this.state.replaySequence = 0;\n this.state.replayEvents = [];\n this.state.replayTotalEvents = 0;\n this.state.replayStartedAt = new Date().toISOString();\n this.state.replayErrorCount = 0;\n this.state.replayRageClickCount = 0;\n\n // Restart rrweb recording\n // @ts-expect-error rrweb is loaded dynamically\n if (window.rrweb?.record) {\n // @ts-expect-error rrweb is loaded dynamically\n this.state.replayStopFn = window.rrweb.record({\n emit: (event: unknown) => {\n this.state.replayEvents.push(event);\n this.state.replayTotalEvents++;\n },\n maskAllInputs: false,\n maskInputFn: (text: string, element: HTMLElement) => {\n if (this.isSensitiveInput(element)) {\n return \"*\".repeat(text.length || 8);\n }\n return text;\n },\n blockSelector: \".meetsudo-no-record, [data-meetsudo-no-record]\",\n inlineStylesheet: true,\n sampling: {\n mousemove: 50,\n mouseInteraction: true,\n scroll: 150,\n input: \"last\",\n },\n });\n\n this.state.replayFlushTimer = setInterval(() => this.flushReplayChunk(), REPLAY_FLUSH_INTERVAL);\n }\n }\n\n private isSensitiveInput(el: HTMLElement): boolean {\n if (!el) return true;\n const input = el as HTMLInputElement;\n const type = (input.type || \"\").toLowerCase();\n const name = (input.name || \"\").toLowerCase();\n const id = (input.id || \"\").toLowerCase();\n\n if (type === \"password\") return true;\n\n const sensitivePatterns = [/password/i, /card/i, /cvv/i, /cvc/i, /ssn/i, /secret/i, /token/i];\n const identifiers = name + \" \" + id;\n return sensitivePatterns.some((p) => p.test(identifiers));\n }\n\n private flushReplayChunk(): void {\n if (!this.state.replayEvents.length || this.state.replayPaused) return;\n\n const events = this.state.replayEvents.splice(0);\n const seq = this.state.replaySequence++;\n\n const payload: Record<string, unknown> = {\n anonymous_id: this.state.anonId,\n session_id: this.state.replaySessionId,\n sequence: seq,\n events,\n error_count: this.state.replayErrorCount,\n rage_click_count: this.state.replayRageClickCount,\n };\n\n if (seq === 0) {\n payload.page_url = window.location.href;\n payload.user_agent = navigator.userAgent;\n payload.screen_width = window.innerWidth;\n payload.screen_height = window.innerHeight;\n payload.started_at = this.state.replayStartedAt;\n }\n\n this.api(\"/widget/replay/chunk\", payload).catch(() => {\n this.state.replayEvents = events.concat(this.state.replayEvents);\n this.state.replaySequence--;\n });\n }\n\n private setupRageClickDetection(): void {\n document.addEventListener(\n \"click\",\n (e) => {\n if (!this.state.replaySessionId) return;\n\n const now = Date.now();\n const target = e.target;\n\n if (target === this.state.lastClickTarget && now - this.state.lastClickTime < 500) {\n this.state.clickCount++;\n if (this.state.clickCount >= 3) {\n this.state.replayRageClickCount++;\n this.state.clickCount = 0;\n this.track(\"rage_click\", { page_url: window.location.href });\n }\n } else {\n this.state.clickCount = 1;\n }\n\n this.state.lastClickTime = now;\n this.state.lastClickTarget = target;\n },\n true\n );\n }\n\n private setupErrorCapture(): void {\n window.onerror = (message, source, lineno, colno) => {\n if (this.state.replaySessionId) {\n this.state.replayErrorCount++;\n this.track(\"js_error\", {\n message: String(message).substring(0, 500),\n source: source || \"\",\n lineno: lineno || 0,\n });\n }\n return false;\n };\n\n window.addEventListener(\"unhandledrejection\", (e) => {\n if (this.state.replaySessionId) {\n this.state.replayErrorCount++;\n const message = e.reason instanceof Error ? e.reason.message : String(e.reason);\n this.track(\"js_error\", { message: message.substring(0, 500), type: \"unhandledrejection\" });\n }\n });\n }\n\n private setupEmailCapture(): void {\n document.addEventListener(\n \"blur\",\n (e) => {\n const el = e.target as HTMLInputElement;\n if (!el || el.tagName !== \"INPUT\") return;\n\n const type = (el.type || \"\").toLowerCase();\n const name = (el.name || \"\").toLowerCase();\n if (type === \"email\" || /email/.test(name)) {\n const value = el.value?.trim();\n if (value && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value) && !this.state.capturedEmails[value]) {\n this.state.capturedEmails[value] = true;\n this.track(\"email_captured\", { email: value });\n }\n }\n },\n true\n );\n }\n\n private handleUnload(): void {\n if (this.state.flushTimer) {\n clearInterval(this.state.flushTimer);\n }\n\n // Flush remaining events\n if (this.state.eventQ.length && this.state.apiKey) {\n const apiKey = this.state.apiKey;\n try {\n fetch(this.state.apiUrl + \"/widget/events\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": apiKey,\n },\n body: JSON.stringify({\n anonymous_id: this.state.anonId,\n events: this.state.eventQ,\n }),\n keepalive: true,\n }).catch(() => {});\n } catch {\n /* ignore */\n }\n }\n\n // End replay\n if (this.state.replaySessionId && this.state.apiKey) {\n const replayApiKey = this.state.apiKey;\n if (this.state.replayStopFn) {\n this.state.replayStopFn();\n }\n if (this.state.replayFlushTimer) {\n clearInterval(this.state.replayFlushTimer);\n }\n\n try {\n fetch(this.state.apiUrl + \"/widget/replay/end\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": replayApiKey,\n },\n body: JSON.stringify({\n anonymous_id: this.state.anonId,\n session_id: this.state.replaySessionId,\n ended_at: new Date().toISOString(),\n event_count: this.state.replayTotalEvents,\n error_count: this.state.replayErrorCount,\n rage_click_count: this.state.replayRageClickCount,\n }),\n keepalive: true,\n }).catch(() => {});\n } catch {\n /* ignore */\n }\n }\n }\n}\n","export { MeetSudo } from \"./sdk\";\nexport type {\n MeetSudoConfig,\n UserTraits,\n EventProperties,\n PageProperties,\n ChatAction,\n} from \"./types\";\n\n// Default instance for convenience\nimport { MeetSudo } from \"./sdk\";\nexport const meetsudo = new MeetSudo();\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/sdk.ts","../src/index.ts"],"sourcesContent":["import type {\n MeetSudoConfig,\n UserTraits,\n EventProperties,\n PageProperties,\n Features,\n InitResponse,\n IdentifyResponse,\n ChatAction,\n} from \"./types\";\n\nconst STORAGE_KEY = \"meetsudo_anon_id\";\nconst FLUSH_INTERVAL = 5000;\nconst REPLAY_FLUSH_INTERVAL = 10000;\nconst INACTIVITY_TIMEOUT = 60000; // 1 minute\n\ninterface EventData {\n name: string;\n properties: EventProperties;\n page_url: string;\n timestamp: string;\n}\n\ninterface State {\n apiKey: string | null;\n apiUrl: string;\n widgetUrl: string;\n anonId: string | null;\n custId: string | null;\n features: Features | null;\n workspaceName: string | null;\n initialized: boolean;\n iframe: HTMLIFrameElement | null;\n eventQ: EventData[];\n flushTimer: ReturnType<typeof setInterval> | null;\n replaySessionId: string | null;\n replaySequence: number;\n replayEvents: unknown[];\n replayFlushTimer: ReturnType<typeof setInterval> | null;\n replayStopFn: (() => void) | null;\n replayTotalEvents: number;\n replayStartedAt: string | null;\n replayRageClickCount: number;\n replayErrorCount: number;\n lastClickTime: number;\n lastClickTarget: EventTarget | null;\n clickCount: number;\n capturedEmails: Record<string, boolean>;\n lastActivityTime: number;\n inactivityTimer: ReturnType<typeof setTimeout> | null;\n replayPaused: boolean;\n}\n\nexport class MeetSudo {\n private state: State = {\n apiKey: null,\n apiUrl: \"https://api.meetsudo.com\",\n widgetUrl: \"https://widget.meetsudo.com\",\n anonId: null,\n custId: null,\n features: null,\n workspaceName: null,\n initialized: false,\n iframe: null,\n eventQ: [],\n flushTimer: null,\n replaySessionId: null,\n replaySequence: 0,\n replayEvents: [],\n replayFlushTimer: null,\n replayStopFn: null,\n replayTotalEvents: 0,\n replayStartedAt: null,\n replayRageClickCount: 0,\n replayErrorCount: 0,\n lastClickTime: 0,\n lastClickTarget: null,\n clickCount: 0,\n capturedEmails: {},\n lastActivityTime: Date.now(),\n inactivityTimer: null,\n replayPaused: false,\n };\n\n private config: MeetSudoConfig | null = null;\n\n /**\n * Initialize the MeetSudo SDK\n */\n async init(config: MeetSudoConfig): Promise<void> {\n if (!config.apiKey) {\n throw new Error(\"MeetSudo: apiKey is required\");\n }\n\n this.config = config;\n this.state.apiKey = config.apiKey;\n this.state.apiUrl = config.apiUrl || \"https://api.meetsudo.com\";\n this.state.widgetUrl = config.widgetUrl || \"https://widget.meetsudo.com\";\n this.state.anonId = this.getAnonId();\n\n try {\n const res = await this.api<InitResponse>(\"/widget/init\", {\n anonymous_id: this.state.anonId,\n url: window.location.href,\n referrer: document.referrer,\n });\n\n this.state.custId = res.customer_id;\n this.state.features = res.features;\n this.state.workspaceName = res.workspace_name;\n this.state.initialized = true;\n\n // Create chat iframe if enabled\n if (res.features.chat_enabled && !config.disableChat) {\n this.createIframe(res);\n }\n\n // Start event tracking if enabled\n if (res.features.events_enabled && !config.disableEvents) {\n this.state.flushTimer = setInterval(() => this.flushEvents(), FLUSH_INTERVAL);\n if (res.features.events_auto_page && !config.disableAutoPageView) {\n this.page();\n }\n this.setupEmailCapture();\n }\n\n // Start session replay if enabled and sampled in\n if (this.shouldSampleReplay() && !config.disableReplay) {\n this.startReplay();\n }\n\n // Setup beforeunload handler\n window.addEventListener(\"beforeunload\", () => this.handleUnload());\n } catch (error) {\n console.error(\"MeetSudo: init failed\", error);\n throw error;\n }\n }\n\n /**\n * Identify a user\n */\n async identify(userId: string, traits?: UserTraits): Promise<void> {\n if (!this.state.initialized) {\n console.warn(\"MeetSudo: call init() before identify()\");\n return;\n }\n\n try {\n const res = await this.api<IdentifyResponse>(\"/widget/identify\", {\n anonymous_id: this.state.anonId,\n external_id: userId,\n properties: traits || {},\n });\n\n this.state.custId = res.customer_id;\n\n // Notify chat iframe about identity change\n if (this.state.iframe?.contentWindow) {\n this.state.iframe.contentWindow.postMessage(\n {\n type: \"fi:identify\",\n customerId: res.customer_id,\n merged: res.merged,\n },\n \"*\"\n );\n }\n } catch (error) {\n console.error(\"MeetSudo: identify failed\", error);\n }\n }\n\n /**\n * Track a custom event\n */\n track(eventName: string, properties?: EventProperties): void {\n if (!this.state.initialized || !this.state.features?.events_enabled) {\n return;\n }\n\n this.state.eventQ.push({\n name: eventName,\n properties: properties || {},\n page_url: window.location.href,\n timestamp: new Date().toISOString(),\n });\n }\n\n /**\n * Track a page view\n */\n page(properties?: PageProperties): void {\n const props: PageProperties = {\n url: window.location.href,\n title: document.title,\n referrer: document.referrer,\n ...properties,\n };\n this.track(\"page_view\", props);\n }\n\n /**\n * Control the chat widget\n */\n chat(action: ChatAction): void {\n if (!this.state.iframe?.contentWindow) return;\n\n switch (action) {\n case \"open\":\n this.state.iframe.contentWindow.postMessage({ type: \"fi:chat\", action: \"open\" }, \"*\");\n this.state.iframe.style.width = \"400px\";\n this.state.iframe.style.height = \"560px\";\n break;\n case \"close\":\n this.state.iframe.contentWindow.postMessage({ type: \"fi:chat\", action: \"close\" }, \"*\");\n this.state.iframe.style.width = \"80px\";\n this.state.iframe.style.height = \"80px\";\n break;\n case \"hide\":\n this.state.iframe.style.display = \"none\";\n break;\n case \"show\":\n this.state.iframe.style.display = \"\";\n break;\n }\n }\n\n /**\n * Check if SDK is initialized\n */\n isInitialized(): boolean {\n return this.state.initialized;\n }\n\n /**\n * Get the anonymous ID\n */\n getAnonymousId(): string | null {\n return this.state.anonId;\n }\n\n /**\n * Get the customer ID (after identify)\n */\n getCustomerId(): string | null {\n return this.state.custId;\n }\n\n // Private methods\n\n private getAnonId(): string {\n try {\n const stored = localStorage.getItem(STORAGE_KEY);\n if (stored) return stored;\n } catch {\n /* private browsing */\n }\n\n const id = crypto.randomUUID?.() || this.generateUUID();\n try {\n localStorage.setItem(STORAGE_KEY, id);\n } catch {\n /* ignore */\n }\n return id;\n }\n\n private generateUUID(): string {\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n return (c === \"x\" ? r : (r & 0x3) | 0x8).toString(16);\n });\n }\n\n private async api<T>(path: string, data: unknown): Promise<T> {\n const res = await fetch(this.state.apiUrl + path, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.state.apiKey!,\n },\n body: JSON.stringify(data),\n });\n\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}`);\n }\n\n return res.json();\n }\n\n private createIframe(initData: InitResponse): void {\n if (this.state.iframe) return;\n\n const pos = this.state.features?.chat_position || \"bottom-right\";\n const iframe = document.createElement(\"iframe\");\n iframe.id = \"meetsudo-widget\";\n const posCSS = pos === \"bottom-left\" ? \"left:0;\" : \"right:0;\";\n iframe.style.cssText = `position:fixed;bottom:0;${posCSS}width:80px;height:80px;border:none;z-index:999999;background:transparent;color-scheme:normal;`;\n iframe.allow = \"clipboard-write\";\n iframe.src = this.state.widgetUrl;\n\n document.body.appendChild(iframe);\n this.state.iframe = iframe;\n\n iframe.onload = () => {\n iframe.contentWindow?.postMessage(\n {\n type: \"fi:config\",\n apiKey: this.state.apiKey,\n apiUrl: this.state.apiUrl,\n anonymousId: this.state.anonId,\n customerId: this.state.custId,\n workspaceName: this.state.workspaceName,\n features: this.state.features,\n conversationId: initData.open_conversation_id,\n messages: initData.messages || [],\n },\n \"*\"\n );\n };\n\n window.addEventListener(\"message\", (e) => {\n if (e.data?.type === \"fi:resize\") {\n if (e.data.state === \"open\") {\n iframe.style.width = \"400px\";\n iframe.style.height = \"560px\";\n } else {\n iframe.style.width = \"80px\";\n iframe.style.height = \"80px\";\n }\n }\n });\n }\n\n private flushEvents(): void {\n if (!this.state.eventQ.length) return;\n\n const batch = this.state.eventQ.splice(0);\n this.api(\"/widget/events\", {\n anonymous_id: this.state.anonId,\n events: batch,\n }).catch(() => {\n // Put events back on failure\n this.state.eventQ = batch.concat(this.state.eventQ);\n });\n }\n\n private shouldSampleReplay(): boolean {\n if (!this.state.features?.replay_enabled) return false;\n return Math.random() < (this.state.features.replay_sample_rate || 0);\n }\n\n private startReplay(): void {\n this.state.replaySessionId = crypto.randomUUID?.() || this.generateUUID();\n this.state.replaySequence = 0;\n this.state.replayEvents = [];\n this.state.replayTotalEvents = 0;\n this.state.replayStartedAt = new Date().toISOString();\n this.state.replayErrorCount = 0;\n this.state.replayRageClickCount = 0;\n\n this.setupRageClickDetection();\n this.setupErrorCapture();\n\n // Load rrweb dynamically\n const script = document.createElement(\"script\");\n script.type = \"module\";\n script.src = this.state.widgetUrl.replace(/\\/?(index\\.html)?$/, \"\") + \"/replay.js\";\n document.head.appendChild(script);\n\n // Wait for rrweb to load\n let attempts = 0;\n const checkRrweb = setInterval(() => {\n attempts++;\n // @ts-expect-error rrweb is loaded dynamically\n if (window.rrweb?.record) {\n clearInterval(checkRrweb);\n this.initRrweb();\n } else if (attempts > 50) {\n clearInterval(checkRrweb);\n console.warn(\"MeetSudo: replay failed to load\");\n }\n }, 100);\n }\n\n private initRrweb(): void {\n // @ts-expect-error rrweb is loaded dynamically\n this.state.replayStopFn = window.rrweb.record({\n emit: (event: unknown) => {\n this.state.replayEvents.push(event);\n this.state.replayTotalEvents++;\n },\n maskAllInputs: false,\n maskInputFn: (text: string, element: HTMLElement) => {\n if (this.isSensitiveInput(element)) {\n return \"*\".repeat(text.length || 8);\n }\n return text;\n },\n blockSelector: \".meetsudo-no-record, [data-meetsudo-no-record]\",\n inlineStylesheet: true,\n sampling: {\n mousemove: 50,\n mouseInteraction: true,\n scroll: 150,\n input: \"last\",\n },\n });\n\n this.state.replayFlushTimer = setInterval(() => this.flushReplayChunk(), REPLAY_FLUSH_INTERVAL);\n\n // Setup inactivity tracking\n this.setupActivityTracking();\n }\n\n private setupActivityTracking(): void {\n const activityEvents = [\"mousemove\", \"keydown\", \"click\", \"scroll\", \"touchstart\"];\n\n const handleActivity = () => {\n this.state.lastActivityTime = Date.now();\n\n // Resume replay if it was paused\n if (this.state.replayPaused) {\n this.resumeReplay();\n }\n\n // Reset inactivity timer\n if (this.state.inactivityTimer) {\n clearTimeout(this.state.inactivityTimer);\n }\n\n this.state.inactivityTimer = setTimeout(() => {\n this.pauseReplayDueToInactivity();\n }, INACTIVITY_TIMEOUT);\n };\n\n // Add event listeners\n activityEvents.forEach((event) => {\n document.addEventListener(event, handleActivity, { passive: true });\n });\n\n // Start initial inactivity timer\n this.state.inactivityTimer = setTimeout(() => {\n this.pauseReplayDueToInactivity();\n }, INACTIVITY_TIMEOUT);\n }\n\n private pauseReplayDueToInactivity(): void {\n if (this.state.replayPaused || !this.state.replaySessionId) return;\n\n this.state.replayPaused = true;\n\n // Stop rrweb recording\n if (this.state.replayStopFn) {\n this.state.replayStopFn();\n this.state.replayStopFn = null;\n }\n\n // Flush remaining events\n this.flushReplayChunk();\n\n // Stop flush timer\n if (this.state.replayFlushTimer) {\n clearInterval(this.state.replayFlushTimer);\n this.state.replayFlushTimer = null;\n }\n\n // Send end signal with inactivity flag\n if (this.state.apiKey) {\n this.api(\"/widget/replay/end\", {\n anonymous_id: this.state.anonId,\n session_id: this.state.replaySessionId,\n ended_at: new Date().toISOString(),\n event_count: this.state.replayTotalEvents,\n error_count: this.state.replayErrorCount,\n rage_click_count: this.state.replayRageClickCount,\n end_reason: \"inactivity\",\n }).catch(() => {});\n }\n }\n\n private resumeReplay(): void {\n if (!this.state.replayPaused || !this.state.features?.replay_enabled) return;\n\n this.state.replayPaused = false;\n\n // Start a new replay session\n this.state.replaySessionId = crypto.randomUUID?.() || this.generateUUID();\n this.state.replaySequence = 0;\n this.state.replayEvents = [];\n this.state.replayTotalEvents = 0;\n this.state.replayStartedAt = new Date().toISOString();\n this.state.replayErrorCount = 0;\n this.state.replayRageClickCount = 0;\n\n // Restart rrweb recording\n // @ts-expect-error rrweb is loaded dynamically\n if (window.rrweb?.record) {\n // @ts-expect-error rrweb is loaded dynamically\n this.state.replayStopFn = window.rrweb.record({\n emit: (event: unknown) => {\n this.state.replayEvents.push(event);\n this.state.replayTotalEvents++;\n },\n maskAllInputs: false,\n maskInputFn: (text: string, element: HTMLElement) => {\n if (this.isSensitiveInput(element)) {\n return \"*\".repeat(text.length || 8);\n }\n return text;\n },\n blockSelector: \".meetsudo-no-record, [data-meetsudo-no-record]\",\n inlineStylesheet: true,\n sampling: {\n mousemove: 50,\n mouseInteraction: true,\n scroll: 150,\n input: \"last\",\n },\n });\n\n this.state.replayFlushTimer = setInterval(() => this.flushReplayChunk(), REPLAY_FLUSH_INTERVAL);\n }\n }\n\n private isSensitiveInput(el: HTMLElement): boolean {\n if (!el) return true;\n const input = el as HTMLInputElement;\n const type = (input.type || \"\").toLowerCase();\n const name = (input.name || \"\").toLowerCase();\n const id = (input.id || \"\").toLowerCase();\n\n if (type === \"password\") return true;\n\n const sensitivePatterns = [/password/i, /card/i, /cvv/i, /cvc/i, /ssn/i, /secret/i, /token/i];\n const identifiers = name + \" \" + id;\n return sensitivePatterns.some((p) => p.test(identifiers));\n }\n\n private flushReplayChunk(): void {\n if (!this.state.replayEvents.length || this.state.replayPaused) return;\n\n const events = this.state.replayEvents.splice(0);\n const seq = this.state.replaySequence++;\n\n const payload: Record<string, unknown> = {\n anonymous_id: this.state.anonId,\n session_id: this.state.replaySessionId,\n sequence: seq,\n events,\n error_count: this.state.replayErrorCount,\n rage_click_count: this.state.replayRageClickCount,\n };\n\n if (seq === 0) {\n payload.page_url = window.location.href;\n payload.user_agent = navigator.userAgent;\n payload.screen_width = window.innerWidth;\n payload.screen_height = window.innerHeight;\n payload.started_at = this.state.replayStartedAt;\n }\n\n this.api(\"/widget/replay/chunk\", payload).catch(() => {\n this.state.replayEvents = events.concat(this.state.replayEvents);\n this.state.replaySequence--;\n });\n }\n\n private setupRageClickDetection(): void {\n document.addEventListener(\n \"click\",\n (e) => {\n if (!this.state.replaySessionId) return;\n\n const now = Date.now();\n const target = e.target;\n\n if (target === this.state.lastClickTarget && now - this.state.lastClickTime < 500) {\n this.state.clickCount++;\n if (this.state.clickCount >= 3) {\n this.state.replayRageClickCount++;\n this.state.clickCount = 0;\n this.track(\"rage_click\", { page_url: window.location.href });\n }\n } else {\n this.state.clickCount = 1;\n }\n\n this.state.lastClickTime = now;\n this.state.lastClickTarget = target;\n },\n true\n );\n }\n\n private setupErrorCapture(): void {\n window.onerror = (message, source, lineno, colno) => {\n if (this.state.replaySessionId) {\n this.state.replayErrorCount++;\n this.track(\"js_error\", {\n message: String(message).substring(0, 500),\n source: source || \"\",\n lineno: lineno || 0,\n });\n }\n return false;\n };\n\n window.addEventListener(\"unhandledrejection\", (e) => {\n if (this.state.replaySessionId) {\n this.state.replayErrorCount++;\n const message = e.reason instanceof Error ? e.reason.message : String(e.reason);\n this.track(\"js_error\", { message: message.substring(0, 500), type: \"unhandledrejection\" });\n }\n });\n }\n\n private setupEmailCapture(): void {\n document.addEventListener(\n \"blur\",\n (e) => {\n const el = e.target as HTMLInputElement;\n if (!el || el.tagName !== \"INPUT\") return;\n\n const type = (el.type || \"\").toLowerCase();\n const name = (el.name || \"\").toLowerCase();\n if (type === \"email\" || /email/.test(name)) {\n const value = el.value?.trim();\n if (value && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value) && !this.state.capturedEmails[value]) {\n this.state.capturedEmails[value] = true;\n this.track(\"email_captured\", { email: value });\n }\n }\n },\n true\n );\n }\n\n private handleUnload(): void {\n if (this.state.flushTimer) {\n clearInterval(this.state.flushTimer);\n }\n\n // Flush remaining events\n if (this.state.eventQ.length && this.state.apiKey) {\n const apiKey = this.state.apiKey;\n try {\n fetch(this.state.apiUrl + \"/widget/events\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": apiKey,\n },\n body: JSON.stringify({\n anonymous_id: this.state.anonId,\n events: this.state.eventQ,\n }),\n keepalive: true,\n }).catch(() => {});\n } catch {\n /* ignore */\n }\n }\n\n // End replay\n if (this.state.replaySessionId && this.state.apiKey) {\n const replayApiKey = this.state.apiKey;\n if (this.state.replayStopFn) {\n this.state.replayStopFn();\n }\n if (this.state.replayFlushTimer) {\n clearInterval(this.state.replayFlushTimer);\n }\n\n try {\n fetch(this.state.apiUrl + \"/widget/replay/end\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": replayApiKey,\n },\n body: JSON.stringify({\n anonymous_id: this.state.anonId,\n session_id: this.state.replaySessionId,\n ended_at: new Date().toISOString(),\n event_count: this.state.replayTotalEvents,\n error_count: this.state.replayErrorCount,\n rage_click_count: this.state.replayRageClickCount,\n }),\n keepalive: true,\n }).catch(() => {});\n } catch {\n /* ignore */\n }\n }\n }\n}\n","export { MeetSudo } from \"./sdk\";\nexport { MeetSudoServer } from \"./server\";\nexport type {\n MeetSudoConfig,\n UserTraits,\n EventProperties,\n PageProperties,\n ChatAction,\n} from \"./types\";\nexport type { MeetSudoServerConfig } from \"./server\";\n\n// Default instance for convenience\nimport { MeetSudo } from \"./sdk\";\nexport const meetsudo = new MeetSudo();\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MeetSudo Server SDK
|
|
3
|
+
* Use this for server-side event tracking, user identification, and API calls.
|
|
4
|
+
*/
|
|
5
|
+
interface MeetSudoServerConfig {
|
|
6
|
+
apiKey: string;
|
|
7
|
+
apiUrl?: string;
|
|
8
|
+
}
|
|
9
|
+
interface UserTraits {
|
|
10
|
+
email?: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
interface EventProperties {
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
declare class MeetSudoServer {
|
|
18
|
+
private apiKey;
|
|
19
|
+
private apiUrl;
|
|
20
|
+
constructor(config: MeetSudoServerConfig);
|
|
21
|
+
private request;
|
|
22
|
+
/**
|
|
23
|
+
* Identify a user with traits.
|
|
24
|
+
* Creates or updates the customer in MeetSudo.
|
|
25
|
+
*
|
|
26
|
+
* @param userId - Unique identifier for the user (your internal user ID)
|
|
27
|
+
* @param traits - User properties like email, name, plan, etc.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* await meetsudo.identify('user_123', {
|
|
31
|
+
* email: 'jane@example.com',
|
|
32
|
+
* name: 'Jane Smith',
|
|
33
|
+
* plan: 'pro'
|
|
34
|
+
* });
|
|
35
|
+
*/
|
|
36
|
+
identify(userId: string, traits?: UserTraits): Promise<{
|
|
37
|
+
customer_id: string;
|
|
38
|
+
}>;
|
|
39
|
+
/**
|
|
40
|
+
* Track a custom event.
|
|
41
|
+
*
|
|
42
|
+
* @param userId - The user ID (same as used in identify)
|
|
43
|
+
* @param eventName - Name of the event (e.g., 'purchase_completed')
|
|
44
|
+
* @param properties - Additional event properties
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* await meetsudo.track('user_123', 'purchase_completed', {
|
|
48
|
+
* order_id: 'ord_456',
|
|
49
|
+
* amount: 99.00,
|
|
50
|
+
* currency: 'USD'
|
|
51
|
+
* });
|
|
52
|
+
*/
|
|
53
|
+
track(userId: string, eventName: string, properties?: EventProperties): Promise<{
|
|
54
|
+
received: number;
|
|
55
|
+
}>;
|
|
56
|
+
/**
|
|
57
|
+
* Track multiple events at once (batch).
|
|
58
|
+
*
|
|
59
|
+
* @param userId - The user ID
|
|
60
|
+
* @param events - Array of events to track
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* await meetsudo.trackBatch('user_123', [
|
|
64
|
+
* { name: 'page_viewed', properties: { page: '/pricing' } },
|
|
65
|
+
* { name: 'button_clicked', properties: { button: 'cta' } }
|
|
66
|
+
* ]);
|
|
67
|
+
*/
|
|
68
|
+
trackBatch(userId: string, events: Array<{
|
|
69
|
+
name: string;
|
|
70
|
+
properties?: EventProperties;
|
|
71
|
+
}>): Promise<{
|
|
72
|
+
received: number;
|
|
73
|
+
}>;
|
|
74
|
+
/**
|
|
75
|
+
* Get customer information.
|
|
76
|
+
*
|
|
77
|
+
* @param customerId - The MeetSudo customer ID
|
|
78
|
+
*/
|
|
79
|
+
getCustomer(customerId: string): Promise<unknown>;
|
|
80
|
+
/**
|
|
81
|
+
* Get customer timeline (events, messages, sessions).
|
|
82
|
+
*
|
|
83
|
+
* @param customerId - The MeetSudo customer ID
|
|
84
|
+
* @param limit - Maximum number of items to return
|
|
85
|
+
*/
|
|
86
|
+
getCustomerTimeline(customerId: string, limit?: number): Promise<unknown>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { type EventProperties, MeetSudoServer, type MeetSudoServerConfig, type UserTraits, MeetSudoServer as default };
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MeetSudo Server SDK
|
|
3
|
+
* Use this for server-side event tracking, user identification, and API calls.
|
|
4
|
+
*/
|
|
5
|
+
interface MeetSudoServerConfig {
|
|
6
|
+
apiKey: string;
|
|
7
|
+
apiUrl?: string;
|
|
8
|
+
}
|
|
9
|
+
interface UserTraits {
|
|
10
|
+
email?: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
interface EventProperties {
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
declare class MeetSudoServer {
|
|
18
|
+
private apiKey;
|
|
19
|
+
private apiUrl;
|
|
20
|
+
constructor(config: MeetSudoServerConfig);
|
|
21
|
+
private request;
|
|
22
|
+
/**
|
|
23
|
+
* Identify a user with traits.
|
|
24
|
+
* Creates or updates the customer in MeetSudo.
|
|
25
|
+
*
|
|
26
|
+
* @param userId - Unique identifier for the user (your internal user ID)
|
|
27
|
+
* @param traits - User properties like email, name, plan, etc.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* await meetsudo.identify('user_123', {
|
|
31
|
+
* email: 'jane@example.com',
|
|
32
|
+
* name: 'Jane Smith',
|
|
33
|
+
* plan: 'pro'
|
|
34
|
+
* });
|
|
35
|
+
*/
|
|
36
|
+
identify(userId: string, traits?: UserTraits): Promise<{
|
|
37
|
+
customer_id: string;
|
|
38
|
+
}>;
|
|
39
|
+
/**
|
|
40
|
+
* Track a custom event.
|
|
41
|
+
*
|
|
42
|
+
* @param userId - The user ID (same as used in identify)
|
|
43
|
+
* @param eventName - Name of the event (e.g., 'purchase_completed')
|
|
44
|
+
* @param properties - Additional event properties
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* await meetsudo.track('user_123', 'purchase_completed', {
|
|
48
|
+
* order_id: 'ord_456',
|
|
49
|
+
* amount: 99.00,
|
|
50
|
+
* currency: 'USD'
|
|
51
|
+
* });
|
|
52
|
+
*/
|
|
53
|
+
track(userId: string, eventName: string, properties?: EventProperties): Promise<{
|
|
54
|
+
received: number;
|
|
55
|
+
}>;
|
|
56
|
+
/**
|
|
57
|
+
* Track multiple events at once (batch).
|
|
58
|
+
*
|
|
59
|
+
* @param userId - The user ID
|
|
60
|
+
* @param events - Array of events to track
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* await meetsudo.trackBatch('user_123', [
|
|
64
|
+
* { name: 'page_viewed', properties: { page: '/pricing' } },
|
|
65
|
+
* { name: 'button_clicked', properties: { button: 'cta' } }
|
|
66
|
+
* ]);
|
|
67
|
+
*/
|
|
68
|
+
trackBatch(userId: string, events: Array<{
|
|
69
|
+
name: string;
|
|
70
|
+
properties?: EventProperties;
|
|
71
|
+
}>): Promise<{
|
|
72
|
+
received: number;
|
|
73
|
+
}>;
|
|
74
|
+
/**
|
|
75
|
+
* Get customer information.
|
|
76
|
+
*
|
|
77
|
+
* @param customerId - The MeetSudo customer ID
|
|
78
|
+
*/
|
|
79
|
+
getCustomer(customerId: string): Promise<unknown>;
|
|
80
|
+
/**
|
|
81
|
+
* Get customer timeline (events, messages, sessions).
|
|
82
|
+
*
|
|
83
|
+
* @param customerId - The MeetSudo customer ID
|
|
84
|
+
* @param limit - Maximum number of items to return
|
|
85
|
+
*/
|
|
86
|
+
getCustomerTimeline(customerId: string, limit?: number): Promise<unknown>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { type EventProperties, MeetSudoServer, type MeetSudoServerConfig, type UserTraits, MeetSudoServer as default };
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/server.ts
|
|
21
|
+
var server_exports = {};
|
|
22
|
+
__export(server_exports, {
|
|
23
|
+
MeetSudoServer: () => MeetSudoServer,
|
|
24
|
+
default: () => server_default
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(server_exports);
|
|
27
|
+
var MeetSudoServer = class {
|
|
28
|
+
constructor(config) {
|
|
29
|
+
if (!config.apiKey) {
|
|
30
|
+
throw new Error("MeetSudoServer: apiKey is required");
|
|
31
|
+
}
|
|
32
|
+
this.apiKey = config.apiKey;
|
|
33
|
+
this.apiUrl = config.apiUrl || "https://api.meetsudo.com";
|
|
34
|
+
}
|
|
35
|
+
async request(method, path, body) {
|
|
36
|
+
const res = await fetch(`${this.apiUrl}${path}`, {
|
|
37
|
+
method,
|
|
38
|
+
headers: {
|
|
39
|
+
"Content-Type": "application/json",
|
|
40
|
+
"X-API-Key": this.apiKey
|
|
41
|
+
},
|
|
42
|
+
body: body ? JSON.stringify(body) : void 0
|
|
43
|
+
});
|
|
44
|
+
if (!res.ok) {
|
|
45
|
+
const error = await res.json().catch(() => ({ error: "Request failed" }));
|
|
46
|
+
throw new Error(error.error || `Request failed: ${res.status}`);
|
|
47
|
+
}
|
|
48
|
+
return res.json();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Identify a user with traits.
|
|
52
|
+
* Creates or updates the customer in MeetSudo.
|
|
53
|
+
*
|
|
54
|
+
* @param userId - Unique identifier for the user (your internal user ID)
|
|
55
|
+
* @param traits - User properties like email, name, plan, etc.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* await meetsudo.identify('user_123', {
|
|
59
|
+
* email: 'jane@example.com',
|
|
60
|
+
* name: 'Jane Smith',
|
|
61
|
+
* plan: 'pro'
|
|
62
|
+
* });
|
|
63
|
+
*/
|
|
64
|
+
async identify(userId, traits) {
|
|
65
|
+
return this.request("POST", "/widget/identify", {
|
|
66
|
+
anonymous_id: `server_${userId}`,
|
|
67
|
+
external_id: userId,
|
|
68
|
+
properties: traits || {}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Track a custom event.
|
|
73
|
+
*
|
|
74
|
+
* @param userId - The user ID (same as used in identify)
|
|
75
|
+
* @param eventName - Name of the event (e.g., 'purchase_completed')
|
|
76
|
+
* @param properties - Additional event properties
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* await meetsudo.track('user_123', 'purchase_completed', {
|
|
80
|
+
* order_id: 'ord_456',
|
|
81
|
+
* amount: 99.00,
|
|
82
|
+
* currency: 'USD'
|
|
83
|
+
* });
|
|
84
|
+
*/
|
|
85
|
+
async track(userId, eventName, properties) {
|
|
86
|
+
await this.request("POST", "/widget/init", {
|
|
87
|
+
anonymous_id: `server_${userId}`
|
|
88
|
+
});
|
|
89
|
+
return this.request("POST", "/widget/events", {
|
|
90
|
+
anonymous_id: `server_${userId}`,
|
|
91
|
+
events: [
|
|
92
|
+
{
|
|
93
|
+
name: eventName,
|
|
94
|
+
properties: properties || {},
|
|
95
|
+
page_url: "server",
|
|
96
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Track multiple events at once (batch).
|
|
103
|
+
*
|
|
104
|
+
* @param userId - The user ID
|
|
105
|
+
* @param events - Array of events to track
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* await meetsudo.trackBatch('user_123', [
|
|
109
|
+
* { name: 'page_viewed', properties: { page: '/pricing' } },
|
|
110
|
+
* { name: 'button_clicked', properties: { button: 'cta' } }
|
|
111
|
+
* ]);
|
|
112
|
+
*/
|
|
113
|
+
async trackBatch(userId, events) {
|
|
114
|
+
await this.request("POST", "/widget/init", {
|
|
115
|
+
anonymous_id: `server_${userId}`
|
|
116
|
+
});
|
|
117
|
+
return this.request("POST", "/widget/events", {
|
|
118
|
+
anonymous_id: `server_${userId}`,
|
|
119
|
+
events: events.map((e) => ({
|
|
120
|
+
name: e.name,
|
|
121
|
+
properties: e.properties || {},
|
|
122
|
+
page_url: "server",
|
|
123
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
124
|
+
}))
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get customer information.
|
|
129
|
+
*
|
|
130
|
+
* @param customerId - The MeetSudo customer ID
|
|
131
|
+
*/
|
|
132
|
+
async getCustomer(customerId) {
|
|
133
|
+
return this.request("GET", `/customers/${customerId}`);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Get customer timeline (events, messages, sessions).
|
|
137
|
+
*
|
|
138
|
+
* @param customerId - The MeetSudo customer ID
|
|
139
|
+
* @param limit - Maximum number of items to return
|
|
140
|
+
*/
|
|
141
|
+
async getCustomerTimeline(customerId, limit) {
|
|
142
|
+
const qs = limit ? `?limit=${limit}` : "";
|
|
143
|
+
return this.request("GET", `/customers/${customerId}/timeline${qs}`);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
var server_default = MeetSudoServer;
|
|
147
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
148
|
+
0 && (module.exports = {
|
|
149
|
+
MeetSudoServer
|
|
150
|
+
});
|
|
151
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["/**\n * MeetSudo Server SDK\n * Use this for server-side event tracking, user identification, and API calls.\n */\n\nexport interface MeetSudoServerConfig {\n apiKey: string;\n apiUrl?: string;\n}\n\nexport interface UserTraits {\n email?: string;\n name?: string;\n [key: string]: unknown;\n}\n\nexport interface EventProperties {\n [key: string]: unknown;\n}\n\nexport class MeetSudoServer {\n private apiKey: string;\n private apiUrl: string;\n\n constructor(config: MeetSudoServerConfig) {\n if (!config.apiKey) {\n throw new Error(\"MeetSudoServer: apiKey is required\");\n }\n this.apiKey = config.apiKey;\n this.apiUrl = config.apiUrl || \"https://api.meetsudo.com\";\n }\n\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const res = await fetch(`${this.apiUrl}${path}`, {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!res.ok) {\n const error = await res.json().catch(() => ({ error: \"Request failed\" }));\n throw new Error(error.error || `Request failed: ${res.status}`);\n }\n\n return res.json();\n }\n\n /**\n * Identify a user with traits.\n * Creates or updates the customer in MeetSudo.\n *\n * @param userId - Unique identifier for the user (your internal user ID)\n * @param traits - User properties like email, name, plan, etc.\n *\n * @example\n * await meetsudo.identify('user_123', {\n * email: 'jane@example.com',\n * name: 'Jane Smith',\n * plan: 'pro'\n * });\n */\n async identify(userId: string, traits?: UserTraits): Promise<{ customer_id: string }> {\n return this.request(\"POST\", \"/widget/identify\", {\n anonymous_id: `server_${userId}`,\n external_id: userId,\n properties: traits || {},\n });\n }\n\n /**\n * Track a custom event.\n *\n * @param userId - The user ID (same as used in identify)\n * @param eventName - Name of the event (e.g., 'purchase_completed')\n * @param properties - Additional event properties\n *\n * @example\n * await meetsudo.track('user_123', 'purchase_completed', {\n * order_id: 'ord_456',\n * amount: 99.00,\n * currency: 'USD'\n * });\n */\n async track(\n userId: string,\n eventName: string,\n properties?: EventProperties\n ): Promise<{ received: number }> {\n // First ensure customer exists\n await this.request(\"POST\", \"/widget/init\", {\n anonymous_id: `server_${userId}`,\n });\n\n return this.request(\"POST\", \"/widget/events\", {\n anonymous_id: `server_${userId}`,\n events: [\n {\n name: eventName,\n properties: properties || {},\n page_url: \"server\",\n timestamp: new Date().toISOString(),\n },\n ],\n });\n }\n\n /**\n * Track multiple events at once (batch).\n *\n * @param userId - The user ID\n * @param events - Array of events to track\n *\n * @example\n * await meetsudo.trackBatch('user_123', [\n * { name: 'page_viewed', properties: { page: '/pricing' } },\n * { name: 'button_clicked', properties: { button: 'cta' } }\n * ]);\n */\n async trackBatch(\n userId: string,\n events: Array<{ name: string; properties?: EventProperties }>\n ): Promise<{ received: number }> {\n // First ensure customer exists\n await this.request(\"POST\", \"/widget/init\", {\n anonymous_id: `server_${userId}`,\n });\n\n return this.request(\"POST\", \"/widget/events\", {\n anonymous_id: `server_${userId}`,\n events: events.map((e) => ({\n name: e.name,\n properties: e.properties || {},\n page_url: \"server\",\n timestamp: new Date().toISOString(),\n })),\n });\n }\n\n /**\n * Get customer information.\n *\n * @param customerId - The MeetSudo customer ID\n */\n async getCustomer(customerId: string): Promise<unknown> {\n return this.request(\"GET\", `/customers/${customerId}`);\n }\n\n /**\n * Get customer timeline (events, messages, sessions).\n *\n * @param customerId - The MeetSudo customer ID\n * @param limit - Maximum number of items to return\n */\n async getCustomerTimeline(\n customerId: string,\n limit?: number\n ): Promise<unknown> {\n const qs = limit ? `?limit=${limit}` : \"\";\n return this.request(\"GET\", `/customers/${customerId}/timeline${qs}`);\n }\n}\n\n// Default export for convenience\nexport default MeetSudoServer;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoBO,IAAM,iBAAN,MAAqB;AAAA,EAI1B,YAAY,QAA8B;AACxC,QAAI,CAAC,OAAO,QAAQ;AAClB,YAAM,IAAI,MAAM,oCAAoC;AAAA,IACtD;AACA,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS,OAAO,UAAU;AAAA,EACjC;AAAA,EAEA,MAAc,QACZ,QACA,MACA,MACY;AACZ,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,MAAM,GAAG,IAAI,IAAI;AAAA,MAC/C;AAAA,MACA,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,aAAa,KAAK;AAAA,MACpB;AAAA,MACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IACtC,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,QAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,EAAE,OAAO,iBAAiB,EAAE;AACxE,YAAM,IAAI,MAAM,MAAM,SAAS,mBAAmB,IAAI,MAAM,EAAE;AAAA,IAChE;AAEA,WAAO,IAAI,KAAK;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,SAAS,QAAgB,QAAuD;AACpF,WAAO,KAAK,QAAQ,QAAQ,oBAAoB;AAAA,MAC9C,cAAc,UAAU,MAAM;AAAA,MAC9B,aAAa;AAAA,MACb,YAAY,UAAU,CAAC;AAAA,IACzB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,MACJ,QACA,WACA,YAC+B;AAE/B,UAAM,KAAK,QAAQ,QAAQ,gBAAgB;AAAA,MACzC,cAAc,UAAU,MAAM;AAAA,IAChC,CAAC;AAED,WAAO,KAAK,QAAQ,QAAQ,kBAAkB;AAAA,MAC5C,cAAc,UAAU,MAAM;AAAA,MAC9B,QAAQ;AAAA,QACN;AAAA,UACE,MAAM;AAAA,UACN,YAAY,cAAc,CAAC;AAAA,UAC3B,UAAU;AAAA,UACV,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QACpC;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,WACJ,QACA,QAC+B;AAE/B,UAAM,KAAK,QAAQ,QAAQ,gBAAgB;AAAA,MACzC,cAAc,UAAU,MAAM;AAAA,IAChC,CAAC;AAED,WAAO,KAAK,QAAQ,QAAQ,kBAAkB;AAAA,MAC5C,cAAc,UAAU,MAAM;AAAA,MAC9B,QAAQ,OAAO,IAAI,CAAC,OAAO;AAAA,QACzB,MAAM,EAAE;AAAA,QACR,YAAY,EAAE,cAAc,CAAC;AAAA,QAC7B,UAAU;AAAA,QACV,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,EAAE;AAAA,IACJ,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YAAY,YAAsC;AACtD,WAAO,KAAK,QAAQ,OAAO,cAAc,UAAU,EAAE;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,oBACJ,YACA,OACkB;AAClB,UAAM,KAAK,QAAQ,UAAU,KAAK,KAAK;AACvC,WAAO,KAAK,QAAQ,OAAO,cAAc,UAAU,YAAY,EAAE,EAAE;AAAA,EACrE;AACF;AAGA,IAAO,iBAAQ;","names":[]}
|
package/dist/server.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meet-sudo/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "MeetSudo SDK - Chat, Analytics, Session Replay",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
"import": "./dist/index.mjs",
|
|
15
15
|
"require": "./dist/index.js"
|
|
16
16
|
},
|
|
17
|
+
"./server": {
|
|
18
|
+
"types": "./dist/server.d.ts",
|
|
19
|
+
"import": "./dist/server.mjs",
|
|
20
|
+
"require": "./dist/server.js"
|
|
21
|
+
},
|
|
17
22
|
"./script": "./dist/meetsudo.global.js"
|
|
18
23
|
},
|
|
19
24
|
"files": [
|