@meet-sudo/sdk 0.1.5 → 0.1.7
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 +118 -1
- package/dist/{chunk-2H6T3CDL.mjs → chunk-F3QN3VVZ.mjs} +55 -6
- package/dist/chunk-F3QN3VVZ.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +54 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/server.d.mts +65 -2
- package/dist/server.d.ts +65 -2
- package/dist/server.js +54 -5
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-2H6T3CDL.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -57,6 +57,28 @@ meetsudo.identify('user-123', {
|
|
|
57
57
|
</script>
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
+
### Server SDK (Node.js)
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
import { MeetSudoServer } from '@meet-sudo/sdk';
|
|
64
|
+
|
|
65
|
+
const ms = new MeetSudoServer({
|
|
66
|
+
apiKey: process.env.MEETSUDO_API_KEY, // for identify/track/customer APIs
|
|
67
|
+
secretKey: process.env.MEETSUDO_SECRET_KEY // for logs APIs, must start with sk_
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await ms.identify('user-123', {
|
|
71
|
+
email: 'user@example.com',
|
|
72
|
+
name: 'John Doe',
|
|
73
|
+
plan: 'pro'
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
await ms.track('user-123', 'invoice_paid', {
|
|
77
|
+
amount: 120,
|
|
78
|
+
currency: 'USD'
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
60
82
|
## API Reference
|
|
61
83
|
|
|
62
84
|
### `init(config)`
|
|
@@ -141,6 +163,96 @@ meetsudo.changelog('open');
|
|
|
141
163
|
meetsudo.changelog('close');
|
|
142
164
|
```
|
|
143
165
|
|
|
166
|
+
## Server API Reference
|
|
167
|
+
|
|
168
|
+
### `new MeetSudoServer(config)`
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
interface MeetSudoServerConfig {
|
|
172
|
+
apiKey?: string;
|
|
173
|
+
secretKey?: string;
|
|
174
|
+
apiUrl?: string;
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
- Provide `apiKey` for customer/event APIs.
|
|
179
|
+
- Provide `secretKey` for log ingest/query APIs.
|
|
180
|
+
|
|
181
|
+
### `identify(userId, traits?)`
|
|
182
|
+
|
|
183
|
+
Create or update a user from backend code.
|
|
184
|
+
|
|
185
|
+
### `track(userId, eventName, properties?)`
|
|
186
|
+
|
|
187
|
+
Track one backend event for a user.
|
|
188
|
+
|
|
189
|
+
### `trackBatch(userId, events[])`
|
|
190
|
+
|
|
191
|
+
Track multiple backend events in one call.
|
|
192
|
+
|
|
193
|
+
### `getCustomer(customerId)`
|
|
194
|
+
|
|
195
|
+
Fetch customer details.
|
|
196
|
+
|
|
197
|
+
### `getCustomerTimeline(customerId, limit?)`
|
|
198
|
+
|
|
199
|
+
Fetch customer timeline (events, messages, sessions).
|
|
200
|
+
|
|
201
|
+
## Logging API (Server)
|
|
202
|
+
|
|
203
|
+
### `log(entry)`
|
|
204
|
+
|
|
205
|
+
Ingest a single backend log line.
|
|
206
|
+
|
|
207
|
+
```javascript
|
|
208
|
+
await ms.log({
|
|
209
|
+
level: 'error',
|
|
210
|
+
source: 'backend',
|
|
211
|
+
service: 'api',
|
|
212
|
+
event: 'checkout_failed',
|
|
213
|
+
message: 'Stripe returned 402',
|
|
214
|
+
user_id: 'user-123',
|
|
215
|
+
props: { duration_ms: 742, amount: 99 }
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### `logBatch(entries[])`
|
|
220
|
+
|
|
221
|
+
Ingest multiple logs in one request.
|
|
222
|
+
|
|
223
|
+
```javascript
|
|
224
|
+
await ms.logBatch([
|
|
225
|
+
{ level: 'info', source: 'backend', service: 'api', message: 'request started' },
|
|
226
|
+
{ level: 'warn', source: 'backend', service: 'api', message: 'retrying payment' }
|
|
227
|
+
]);
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### `queryLogs({ query, from, to, limit })`
|
|
231
|
+
|
|
232
|
+
Run Splunk-like queries.
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
const r1 = await ms.queryLogs({ query: 'count' });
|
|
236
|
+
const r2 = await ms.queryLogs({ query: 'stats count by service' });
|
|
237
|
+
const r3 = await ms.queryLogs({ query: 'stats p95(prop.duration_ms) by service' });
|
|
238
|
+
const r4 = await ms.queryLogs({ query: 'timechart span=1h' });
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### `assistLogQuery(prompt)`
|
|
242
|
+
|
|
243
|
+
Generate a valid logs query from English text.
|
|
244
|
+
|
|
245
|
+
```javascript
|
|
246
|
+
const assist = await ms.assistLogQuery(
|
|
247
|
+
'show p99 checkout latency by service in last 24 hours'
|
|
248
|
+
);
|
|
249
|
+
// assist.query => stats p99(prop.duration_ms) by service
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### `getLogSchemaHints()`
|
|
253
|
+
|
|
254
|
+
Get observed workspace fields (services, events, prop keys, levels, sources).
|
|
255
|
+
|
|
144
256
|
### Helper Methods
|
|
145
257
|
|
|
146
258
|
```javascript
|
|
@@ -193,7 +305,12 @@ import type {
|
|
|
193
305
|
EventProperties,
|
|
194
306
|
PageProperties,
|
|
195
307
|
ChatAction,
|
|
196
|
-
ChangelogAction
|
|
308
|
+
ChangelogAction,
|
|
309
|
+
MeetSudoServerConfig,
|
|
310
|
+
LogEntry,
|
|
311
|
+
LogQueryRequest,
|
|
312
|
+
LogAssistResponse,
|
|
313
|
+
LogSchemaHintsResponse
|
|
197
314
|
} from '@meet-sudo/sdk';
|
|
198
315
|
```
|
|
199
316
|
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
// src/server.ts
|
|
2
2
|
var MeetSudoServer = class {
|
|
3
3
|
constructor(config) {
|
|
4
|
-
if (!config.apiKey) {
|
|
5
|
-
throw new Error("MeetSudoServer: apiKey is required");
|
|
4
|
+
if (!config.apiKey && !config.secretKey) {
|
|
5
|
+
throw new Error("MeetSudoServer: apiKey or secretKey is required");
|
|
6
6
|
}
|
|
7
|
-
this.apiKey = config.apiKey;
|
|
7
|
+
this.apiKey = config.apiKey ?? null;
|
|
8
|
+
this.secretKey = config.secretKey ?? null;
|
|
8
9
|
this.apiUrl = config.apiUrl || "https://api.meetsudo.com";
|
|
9
10
|
}
|
|
10
|
-
async
|
|
11
|
+
async requestWithKey(method, path, key, body) {
|
|
11
12
|
const res = await fetch(`${this.apiUrl}${path}`, {
|
|
12
13
|
method,
|
|
13
14
|
headers: {
|
|
14
15
|
"Content-Type": "application/json",
|
|
15
|
-
"X-API-Key":
|
|
16
|
+
"X-API-Key": key
|
|
16
17
|
},
|
|
17
18
|
body: body ? JSON.stringify(body) : void 0
|
|
18
19
|
});
|
|
@@ -22,6 +23,21 @@ var MeetSudoServer = class {
|
|
|
22
23
|
}
|
|
23
24
|
return res.json();
|
|
24
25
|
}
|
|
26
|
+
async request(method, path, body) {
|
|
27
|
+
if (!this.apiKey) {
|
|
28
|
+
throw new Error("MeetSudoServer: apiKey is required for this call");
|
|
29
|
+
}
|
|
30
|
+
return this.requestWithKey(method, path, this.apiKey, body);
|
|
31
|
+
}
|
|
32
|
+
async requestSecret(method, path, body) {
|
|
33
|
+
if (!this.secretKey) {
|
|
34
|
+
throw new Error("MeetSudoServer: secretKey is required for logs");
|
|
35
|
+
}
|
|
36
|
+
if (!this.secretKey.startsWith("sk_")) {
|
|
37
|
+
throw new Error("MeetSudoServer: secretKey must start with sk_");
|
|
38
|
+
}
|
|
39
|
+
return this.requestWithKey(method, path, this.secretKey, body);
|
|
40
|
+
}
|
|
25
41
|
/**
|
|
26
42
|
* Identify a user with traits.
|
|
27
43
|
* Creates or updates the customer in MeetSudo.
|
|
@@ -117,6 +133,39 @@ var MeetSudoServer = class {
|
|
|
117
133
|
const qs = limit ? `?limit=${limit}` : "";
|
|
118
134
|
return this.request("GET", `/customers/${customerId}/timeline${qs}`);
|
|
119
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Ingest one backend log entry (requires secretKey).
|
|
138
|
+
*/
|
|
139
|
+
async log(entry) {
|
|
140
|
+
return this.requestSecret("POST", "/logs/ingest", { logs: [entry] });
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Ingest multiple backend log entries (requires secretKey).
|
|
144
|
+
*/
|
|
145
|
+
async logBatch(entries) {
|
|
146
|
+
return this.requestSecret("POST", "/logs/ingest", { logs: entries });
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Query logs with Splunk-like commands:
|
|
150
|
+
* - count
|
|
151
|
+
* - stats count by level|source|service|event|user_id
|
|
152
|
+
* - timechart span=1h
|
|
153
|
+
*/
|
|
154
|
+
async queryLogs(params) {
|
|
155
|
+
return this.requestSecret("POST", "/logs/query/secret", params);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Convert plain English into a MeetSudo logs query (requires secretKey).
|
|
159
|
+
*/
|
|
160
|
+
async assistLogQuery(prompt) {
|
|
161
|
+
return this.requestSecret("POST", "/logs/assist/secret", { prompt });
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Get observed workspace log schema hints (services/events/prop keys).
|
|
165
|
+
*/
|
|
166
|
+
async getLogSchemaHints() {
|
|
167
|
+
return this.requestSecret("GET", "/logs/schema-hints/secret");
|
|
168
|
+
}
|
|
120
169
|
};
|
|
121
170
|
var server_default = MeetSudoServer;
|
|
122
171
|
|
|
@@ -124,4 +173,4 @@ export {
|
|
|
124
173
|
MeetSudoServer,
|
|
125
174
|
server_default
|
|
126
175
|
};
|
|
127
|
-
//# sourceMappingURL=chunk-
|
|
176
|
+
//# sourceMappingURL=chunk-F3QN3VVZ.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 secretKey?: 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 interface LogEntry {\n ts?: string;\n level?: \"debug\" | \"info\" | \"warn\" | \"error\";\n source?: \"backend\" | \"frontend\" | \"sdk\" | \"system\";\n service?: string;\n event?: string;\n message: string;\n user_id?: string;\n request_id?: string;\n trace_id?: string;\n tags?: Record<string, string>;\n props?: Record<string, unknown>;\n}\n\nexport interface LogQueryRequest {\n query?: string;\n from?: string;\n to?: string;\n limit?: number;\n}\n\nexport interface LogAssistResponse {\n query: string;\n explanation: string;\n}\n\nexport interface LogSchemaHintsResponse {\n services: string[];\n events: string[];\n prop_keys: string[];\n levels: string[];\n sources: string[];\n}\n\nexport class MeetSudoServer {\n private apiKey: string | null;\n private secretKey: string | null;\n private apiUrl: string;\n\n constructor(config: MeetSudoServerConfig) {\n if (!config.apiKey && !config.secretKey) {\n throw new Error(\"MeetSudoServer: apiKey or secretKey is required\");\n }\n this.apiKey = config.apiKey ?? null;\n this.secretKey = config.secretKey ?? null;\n this.apiUrl = config.apiUrl || \"https://api.meetsudo.com\";\n }\n\n private async requestWithKey<T>(\n method: string,\n path: string,\n key: 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\": key,\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 private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n if (!this.apiKey) {\n throw new Error(\"MeetSudoServer: apiKey is required for this call\");\n }\n return this.requestWithKey(method, path, this.apiKey, body);\n }\n\n private async requestSecret<T>(method: string, path: string, body?: unknown): Promise<T> {\n if (!this.secretKey) {\n throw new Error(\"MeetSudoServer: secretKey is required for logs\");\n }\n if (!this.secretKey.startsWith(\"sk_\")) {\n throw new Error(\"MeetSudoServer: secretKey must start with sk_\");\n }\n return this.requestWithKey(method, path, this.secretKey, body);\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 * Ingest one backend log entry (requires secretKey).\n */\n async log(entry: LogEntry): Promise<{ ok: boolean; inserted: number }> {\n return this.requestSecret(\"POST\", \"/logs/ingest\", { logs: [entry] });\n }\n\n /**\n * Ingest multiple backend log entries (requires secretKey).\n */\n async logBatch(entries: LogEntry[]): Promise<{ ok: boolean; inserted: number }> {\n return this.requestSecret(\"POST\", \"/logs/ingest\", { logs: entries });\n }\n\n /**\n * Query logs with Splunk-like commands:\n * - count\n * - stats count by level|source|service|event|user_id\n * - timechart span=1h\n */\n async queryLogs(params: LogQueryRequest): Promise<unknown> {\n return this.requestSecret(\"POST\", \"/logs/query/secret\", params);\n }\n\n /**\n * Convert plain English into a MeetSudo logs query (requires secretKey).\n */\n async assistLogQuery(prompt: string): Promise<LogAssistResponse> {\n return this.requestSecret(\"POST\", \"/logs/assist/secret\", { prompt });\n }\n\n /**\n * Get observed workspace log schema hints (services/events/prop keys).\n */\n async getLogSchemaHints(): Promise<LogSchemaHintsResponse> {\n return this.requestSecret(\"GET\", \"/logs/schema-hints/secret\");\n }\n}\n\n// Default export for convenience\nexport default MeetSudoServer;\n"],"mappings":"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","names":[]}
|
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -590,18 +590,19 @@ var MeetSudo = class {
|
|
|
590
590
|
// src/server.ts
|
|
591
591
|
var MeetSudoServer = class {
|
|
592
592
|
constructor(config) {
|
|
593
|
-
if (!config.apiKey) {
|
|
594
|
-
throw new Error("MeetSudoServer: apiKey is required");
|
|
593
|
+
if (!config.apiKey && !config.secretKey) {
|
|
594
|
+
throw new Error("MeetSudoServer: apiKey or secretKey is required");
|
|
595
595
|
}
|
|
596
|
-
this.apiKey = config.apiKey;
|
|
596
|
+
this.apiKey = config.apiKey ?? null;
|
|
597
|
+
this.secretKey = config.secretKey ?? null;
|
|
597
598
|
this.apiUrl = config.apiUrl || "https://api.meetsudo.com";
|
|
598
599
|
}
|
|
599
|
-
async
|
|
600
|
+
async requestWithKey(method, path, key, body) {
|
|
600
601
|
const res = await fetch(`${this.apiUrl}${path}`, {
|
|
601
602
|
method,
|
|
602
603
|
headers: {
|
|
603
604
|
"Content-Type": "application/json",
|
|
604
|
-
"X-API-Key":
|
|
605
|
+
"X-API-Key": key
|
|
605
606
|
},
|
|
606
607
|
body: body ? JSON.stringify(body) : void 0
|
|
607
608
|
});
|
|
@@ -611,6 +612,21 @@ var MeetSudoServer = class {
|
|
|
611
612
|
}
|
|
612
613
|
return res.json();
|
|
613
614
|
}
|
|
615
|
+
async request(method, path, body) {
|
|
616
|
+
if (!this.apiKey) {
|
|
617
|
+
throw new Error("MeetSudoServer: apiKey is required for this call");
|
|
618
|
+
}
|
|
619
|
+
return this.requestWithKey(method, path, this.apiKey, body);
|
|
620
|
+
}
|
|
621
|
+
async requestSecret(method, path, body) {
|
|
622
|
+
if (!this.secretKey) {
|
|
623
|
+
throw new Error("MeetSudoServer: secretKey is required for logs");
|
|
624
|
+
}
|
|
625
|
+
if (!this.secretKey.startsWith("sk_")) {
|
|
626
|
+
throw new Error("MeetSudoServer: secretKey must start with sk_");
|
|
627
|
+
}
|
|
628
|
+
return this.requestWithKey(method, path, this.secretKey, body);
|
|
629
|
+
}
|
|
614
630
|
/**
|
|
615
631
|
* Identify a user with traits.
|
|
616
632
|
* Creates or updates the customer in MeetSudo.
|
|
@@ -706,6 +722,39 @@ var MeetSudoServer = class {
|
|
|
706
722
|
const qs = limit ? `?limit=${limit}` : "";
|
|
707
723
|
return this.request("GET", `/customers/${customerId}/timeline${qs}`);
|
|
708
724
|
}
|
|
725
|
+
/**
|
|
726
|
+
* Ingest one backend log entry (requires secretKey).
|
|
727
|
+
*/
|
|
728
|
+
async log(entry) {
|
|
729
|
+
return this.requestSecret("POST", "/logs/ingest", { logs: [entry] });
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Ingest multiple backend log entries (requires secretKey).
|
|
733
|
+
*/
|
|
734
|
+
async logBatch(entries) {
|
|
735
|
+
return this.requestSecret("POST", "/logs/ingest", { logs: entries });
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Query logs with Splunk-like commands:
|
|
739
|
+
* - count
|
|
740
|
+
* - stats count by level|source|service|event|user_id
|
|
741
|
+
* - timechart span=1h
|
|
742
|
+
*/
|
|
743
|
+
async queryLogs(params) {
|
|
744
|
+
return this.requestSecret("POST", "/logs/query/secret", params);
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Convert plain English into a MeetSudo logs query (requires secretKey).
|
|
748
|
+
*/
|
|
749
|
+
async assistLogQuery(prompt) {
|
|
750
|
+
return this.requestSecret("POST", "/logs/assist/secret", { prompt });
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Get observed workspace log schema hints (services/events/prop keys).
|
|
754
|
+
*/
|
|
755
|
+
async getLogSchemaHints() {
|
|
756
|
+
return this.requestSecret("GET", "/logs/schema-hints/secret");
|
|
757
|
+
}
|
|
709
758
|
};
|
|
710
759
|
|
|
711
760
|
// src/index.ts
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 ChangelogAction,\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 ChangelogAction,\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 unreadChangelogCount: number;\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 unreadChangelogCount: 0,\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.unreadChangelogCount = ((res as unknown) as Record<string, number>).unread_changelog_count || 0;\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 * Control the changelog view\n */\n changelog(action: ChangelogAction): void {\n if (!this.state.iframe?.contentWindow) return;\n this.state.iframe.contentWindow.postMessage({ type: \"fi:changelog\", action }, \"*\");\n if (action === \"open\") {\n this.state.iframe.style.width = \"400px\";\n this.state.iframe.style.height = \"560px\";\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 unreadChangelogCount: this.state.unreadChangelogCount,\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":[]}
|
|
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 ChangelogAction,\n} from \"./types\";\nexport type {\n MeetSudoServerConfig,\n LogEntry,\n LogQueryRequest,\n LogAssistResponse,\n LogSchemaHintsResponse,\n} 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 ChangelogAction,\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 unreadChangelogCount: number;\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 unreadChangelogCount: 0,\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.unreadChangelogCount = ((res as unknown) as Record<string, number>).unread_changelog_count || 0;\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 * Control the changelog view\n */\n changelog(action: ChangelogAction): void {\n if (!this.state.iframe?.contentWindow) return;\n this.state.iframe.contentWindow.postMessage({ type: \"fi:changelog\", action }, \"*\");\n if (action === \"open\") {\n this.state.iframe.style.width = \"400px\";\n this.state.iframe.style.height = \"560px\";\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 unreadChangelogCount: this.state.unreadChangelogCount,\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 secretKey?: 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 interface LogEntry {\n ts?: string;\n level?: \"debug\" | \"info\" | \"warn\" | \"error\";\n source?: \"backend\" | \"frontend\" | \"sdk\" | \"system\";\n service?: string;\n event?: string;\n message: string;\n user_id?: string;\n request_id?: string;\n trace_id?: string;\n tags?: Record<string, string>;\n props?: Record<string, unknown>;\n}\n\nexport interface LogQueryRequest {\n query?: string;\n from?: string;\n to?: string;\n limit?: number;\n}\n\nexport interface LogAssistResponse {\n query: string;\n explanation: string;\n}\n\nexport interface LogSchemaHintsResponse {\n services: string[];\n events: string[];\n prop_keys: string[];\n levels: string[];\n sources: string[];\n}\n\nexport class MeetSudoServer {\n private apiKey: string | null;\n private secretKey: string | null;\n private apiUrl: string;\n\n constructor(config: MeetSudoServerConfig) {\n if (!config.apiKey && !config.secretKey) {\n throw new Error(\"MeetSudoServer: apiKey or secretKey is required\");\n }\n this.apiKey = config.apiKey ?? null;\n this.secretKey = config.secretKey ?? null;\n this.apiUrl = config.apiUrl || \"https://api.meetsudo.com\";\n }\n\n private async requestWithKey<T>(\n method: string,\n path: string,\n key: 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\": key,\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 private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n if (!this.apiKey) {\n throw new Error(\"MeetSudoServer: apiKey is required for this call\");\n }\n return this.requestWithKey(method, path, this.apiKey, body);\n }\n\n private async requestSecret<T>(method: string, path: string, body?: unknown): Promise<T> {\n if (!this.secretKey) {\n throw new Error(\"MeetSudoServer: secretKey is required for logs\");\n }\n if (!this.secretKey.startsWith(\"sk_\")) {\n throw new Error(\"MeetSudoServer: secretKey must start with sk_\");\n }\n return this.requestWithKey(method, path, this.secretKey, body);\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 * Ingest one backend log entry (requires secretKey).\n */\n async log(entry: LogEntry): Promise<{ ok: boolean; inserted: number }> {\n return this.requestSecret(\"POST\", \"/logs/ingest\", { logs: [entry] });\n }\n\n /**\n * Ingest multiple backend log entries (requires secretKey).\n */\n async logBatch(entries: LogEntry[]): Promise<{ ok: boolean; inserted: number }> {\n return this.requestSecret(\"POST\", \"/logs/ingest\", { logs: entries });\n }\n\n /**\n * Query logs with Splunk-like commands:\n * - count\n * - stats count by level|source|service|event|user_id\n * - timechart span=1h\n */\n async queryLogs(params: LogQueryRequest): Promise<unknown> {\n return this.requestSecret(\"POST\", \"/logs/query/secret\", params);\n }\n\n /**\n * Convert plain English into a MeetSudo logs query (requires secretKey).\n */\n async assistLogQuery(prompt: string): Promise<LogAssistResponse> {\n return this.requestSecret(\"POST\", \"/logs/assist/secret\", { prompt });\n }\n\n /**\n * Get observed workspace log schema hints (services/events/prop keys).\n */\n async getLogSchemaHints(): Promise<LogSchemaHintsResponse> {\n return this.requestSecret(\"GET\", \"/logs/schema-hints/secret\");\n }\n}\n\n// Default export for convenience\nexport default MeetSudoServer;\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
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 ChangelogAction,\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 unreadChangelogCount: number;\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 unreadChangelogCount: 0,\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.unreadChangelogCount = ((res as unknown) as Record<string, number>).unread_changelog_count || 0;\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 * Control the changelog view\n */\n changelog(action: ChangelogAction): void {\n if (!this.state.iframe?.contentWindow) return;\n this.state.iframe.contentWindow.postMessage({ type: \"fi:changelog\", action }, \"*\");\n if (action === \"open\") {\n this.state.iframe.style.width = \"400px\";\n this.state.iframe.style.height = \"560px\";\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 unreadChangelogCount: this.state.unreadChangelogCount,\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 ChangelogAction,\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":[]}
|
|
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 ChangelogAction,\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 unreadChangelogCount: number;\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 unreadChangelogCount: 0,\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.unreadChangelogCount = ((res as unknown) as Record<string, number>).unread_changelog_count || 0;\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 * Control the changelog view\n */\n changelog(action: ChangelogAction): void {\n if (!this.state.iframe?.contentWindow) return;\n this.state.iframe.contentWindow.postMessage({ type: \"fi:changelog\", action }, \"*\");\n if (action === \"open\") {\n this.state.iframe.style.width = \"400px\";\n this.state.iframe.style.height = \"560px\";\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 unreadChangelogCount: this.state.unreadChangelogCount,\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 ChangelogAction,\n} from \"./types\";\nexport type {\n MeetSudoServerConfig,\n LogEntry,\n LogQueryRequest,\n LogAssistResponse,\n LogSchemaHintsResponse,\n} from \"./server\";\n\n// Default instance for convenience\nimport { MeetSudo } from \"./sdk\";\nexport const meetsudo = new MeetSudo();\n"],"mappings":"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","names":[]}
|
package/dist/server.d.mts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Use this for server-side event tracking, user identification, and API calls.
|
|
4
4
|
*/
|
|
5
5
|
interface MeetSudoServerConfig {
|
|
6
|
-
apiKey
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
secretKey?: string;
|
|
7
8
|
apiUrl?: string;
|
|
8
9
|
}
|
|
9
10
|
interface UserTraits {
|
|
@@ -14,11 +15,44 @@ interface UserTraits {
|
|
|
14
15
|
interface EventProperties {
|
|
15
16
|
[key: string]: unknown;
|
|
16
17
|
}
|
|
18
|
+
interface LogEntry {
|
|
19
|
+
ts?: string;
|
|
20
|
+
level?: "debug" | "info" | "warn" | "error";
|
|
21
|
+
source?: "backend" | "frontend" | "sdk" | "system";
|
|
22
|
+
service?: string;
|
|
23
|
+
event?: string;
|
|
24
|
+
message: string;
|
|
25
|
+
user_id?: string;
|
|
26
|
+
request_id?: string;
|
|
27
|
+
trace_id?: string;
|
|
28
|
+
tags?: Record<string, string>;
|
|
29
|
+
props?: Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
interface LogQueryRequest {
|
|
32
|
+
query?: string;
|
|
33
|
+
from?: string;
|
|
34
|
+
to?: string;
|
|
35
|
+
limit?: number;
|
|
36
|
+
}
|
|
37
|
+
interface LogAssistResponse {
|
|
38
|
+
query: string;
|
|
39
|
+
explanation: string;
|
|
40
|
+
}
|
|
41
|
+
interface LogSchemaHintsResponse {
|
|
42
|
+
services: string[];
|
|
43
|
+
events: string[];
|
|
44
|
+
prop_keys: string[];
|
|
45
|
+
levels: string[];
|
|
46
|
+
sources: string[];
|
|
47
|
+
}
|
|
17
48
|
declare class MeetSudoServer {
|
|
18
49
|
private apiKey;
|
|
50
|
+
private secretKey;
|
|
19
51
|
private apiUrl;
|
|
20
52
|
constructor(config: MeetSudoServerConfig);
|
|
53
|
+
private requestWithKey;
|
|
21
54
|
private request;
|
|
55
|
+
private requestSecret;
|
|
22
56
|
/**
|
|
23
57
|
* Identify a user with traits.
|
|
24
58
|
* Creates or updates the customer in MeetSudo.
|
|
@@ -84,6 +118,35 @@ declare class MeetSudoServer {
|
|
|
84
118
|
* @param limit - Maximum number of items to return
|
|
85
119
|
*/
|
|
86
120
|
getCustomerTimeline(customerId: string, limit?: number): Promise<unknown>;
|
|
121
|
+
/**
|
|
122
|
+
* Ingest one backend log entry (requires secretKey).
|
|
123
|
+
*/
|
|
124
|
+
log(entry: LogEntry): Promise<{
|
|
125
|
+
ok: boolean;
|
|
126
|
+
inserted: number;
|
|
127
|
+
}>;
|
|
128
|
+
/**
|
|
129
|
+
* Ingest multiple backend log entries (requires secretKey).
|
|
130
|
+
*/
|
|
131
|
+
logBatch(entries: LogEntry[]): Promise<{
|
|
132
|
+
ok: boolean;
|
|
133
|
+
inserted: number;
|
|
134
|
+
}>;
|
|
135
|
+
/**
|
|
136
|
+
* Query logs with Splunk-like commands:
|
|
137
|
+
* - count
|
|
138
|
+
* - stats count by level|source|service|event|user_id
|
|
139
|
+
* - timechart span=1h
|
|
140
|
+
*/
|
|
141
|
+
queryLogs(params: LogQueryRequest): Promise<unknown>;
|
|
142
|
+
/**
|
|
143
|
+
* Convert plain English into a MeetSudo logs query (requires secretKey).
|
|
144
|
+
*/
|
|
145
|
+
assistLogQuery(prompt: string): Promise<LogAssistResponse>;
|
|
146
|
+
/**
|
|
147
|
+
* Get observed workspace log schema hints (services/events/prop keys).
|
|
148
|
+
*/
|
|
149
|
+
getLogSchemaHints(): Promise<LogSchemaHintsResponse>;
|
|
87
150
|
}
|
|
88
151
|
|
|
89
|
-
export { type EventProperties, MeetSudoServer, type MeetSudoServerConfig, type UserTraits, MeetSudoServer as default };
|
|
152
|
+
export { type EventProperties, type LogAssistResponse, type LogEntry, type LogQueryRequest, type LogSchemaHintsResponse, MeetSudoServer, type MeetSudoServerConfig, type UserTraits, MeetSudoServer as default };
|
package/dist/server.d.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Use this for server-side event tracking, user identification, and API calls.
|
|
4
4
|
*/
|
|
5
5
|
interface MeetSudoServerConfig {
|
|
6
|
-
apiKey
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
secretKey?: string;
|
|
7
8
|
apiUrl?: string;
|
|
8
9
|
}
|
|
9
10
|
interface UserTraits {
|
|
@@ -14,11 +15,44 @@ interface UserTraits {
|
|
|
14
15
|
interface EventProperties {
|
|
15
16
|
[key: string]: unknown;
|
|
16
17
|
}
|
|
18
|
+
interface LogEntry {
|
|
19
|
+
ts?: string;
|
|
20
|
+
level?: "debug" | "info" | "warn" | "error";
|
|
21
|
+
source?: "backend" | "frontend" | "sdk" | "system";
|
|
22
|
+
service?: string;
|
|
23
|
+
event?: string;
|
|
24
|
+
message: string;
|
|
25
|
+
user_id?: string;
|
|
26
|
+
request_id?: string;
|
|
27
|
+
trace_id?: string;
|
|
28
|
+
tags?: Record<string, string>;
|
|
29
|
+
props?: Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
interface LogQueryRequest {
|
|
32
|
+
query?: string;
|
|
33
|
+
from?: string;
|
|
34
|
+
to?: string;
|
|
35
|
+
limit?: number;
|
|
36
|
+
}
|
|
37
|
+
interface LogAssistResponse {
|
|
38
|
+
query: string;
|
|
39
|
+
explanation: string;
|
|
40
|
+
}
|
|
41
|
+
interface LogSchemaHintsResponse {
|
|
42
|
+
services: string[];
|
|
43
|
+
events: string[];
|
|
44
|
+
prop_keys: string[];
|
|
45
|
+
levels: string[];
|
|
46
|
+
sources: string[];
|
|
47
|
+
}
|
|
17
48
|
declare class MeetSudoServer {
|
|
18
49
|
private apiKey;
|
|
50
|
+
private secretKey;
|
|
19
51
|
private apiUrl;
|
|
20
52
|
constructor(config: MeetSudoServerConfig);
|
|
53
|
+
private requestWithKey;
|
|
21
54
|
private request;
|
|
55
|
+
private requestSecret;
|
|
22
56
|
/**
|
|
23
57
|
* Identify a user with traits.
|
|
24
58
|
* Creates or updates the customer in MeetSudo.
|
|
@@ -84,6 +118,35 @@ declare class MeetSudoServer {
|
|
|
84
118
|
* @param limit - Maximum number of items to return
|
|
85
119
|
*/
|
|
86
120
|
getCustomerTimeline(customerId: string, limit?: number): Promise<unknown>;
|
|
121
|
+
/**
|
|
122
|
+
* Ingest one backend log entry (requires secretKey).
|
|
123
|
+
*/
|
|
124
|
+
log(entry: LogEntry): Promise<{
|
|
125
|
+
ok: boolean;
|
|
126
|
+
inserted: number;
|
|
127
|
+
}>;
|
|
128
|
+
/**
|
|
129
|
+
* Ingest multiple backend log entries (requires secretKey).
|
|
130
|
+
*/
|
|
131
|
+
logBatch(entries: LogEntry[]): Promise<{
|
|
132
|
+
ok: boolean;
|
|
133
|
+
inserted: number;
|
|
134
|
+
}>;
|
|
135
|
+
/**
|
|
136
|
+
* Query logs with Splunk-like commands:
|
|
137
|
+
* - count
|
|
138
|
+
* - stats count by level|source|service|event|user_id
|
|
139
|
+
* - timechart span=1h
|
|
140
|
+
*/
|
|
141
|
+
queryLogs(params: LogQueryRequest): Promise<unknown>;
|
|
142
|
+
/**
|
|
143
|
+
* Convert plain English into a MeetSudo logs query (requires secretKey).
|
|
144
|
+
*/
|
|
145
|
+
assistLogQuery(prompt: string): Promise<LogAssistResponse>;
|
|
146
|
+
/**
|
|
147
|
+
* Get observed workspace log schema hints (services/events/prop keys).
|
|
148
|
+
*/
|
|
149
|
+
getLogSchemaHints(): Promise<LogSchemaHintsResponse>;
|
|
87
150
|
}
|
|
88
151
|
|
|
89
|
-
export { type EventProperties, MeetSudoServer, type MeetSudoServerConfig, type UserTraits, MeetSudoServer as default };
|
|
152
|
+
export { type EventProperties, type LogAssistResponse, type LogEntry, type LogQueryRequest, type LogSchemaHintsResponse, MeetSudoServer, type MeetSudoServerConfig, type UserTraits, MeetSudoServer as default };
|
package/dist/server.js
CHANGED
|
@@ -26,18 +26,19 @@ __export(server_exports, {
|
|
|
26
26
|
module.exports = __toCommonJS(server_exports);
|
|
27
27
|
var MeetSudoServer = class {
|
|
28
28
|
constructor(config) {
|
|
29
|
-
if (!config.apiKey) {
|
|
30
|
-
throw new Error("MeetSudoServer: apiKey is required");
|
|
29
|
+
if (!config.apiKey && !config.secretKey) {
|
|
30
|
+
throw new Error("MeetSudoServer: apiKey or secretKey is required");
|
|
31
31
|
}
|
|
32
|
-
this.apiKey = config.apiKey;
|
|
32
|
+
this.apiKey = config.apiKey ?? null;
|
|
33
|
+
this.secretKey = config.secretKey ?? null;
|
|
33
34
|
this.apiUrl = config.apiUrl || "https://api.meetsudo.com";
|
|
34
35
|
}
|
|
35
|
-
async
|
|
36
|
+
async requestWithKey(method, path, key, body) {
|
|
36
37
|
const res = await fetch(`${this.apiUrl}${path}`, {
|
|
37
38
|
method,
|
|
38
39
|
headers: {
|
|
39
40
|
"Content-Type": "application/json",
|
|
40
|
-
"X-API-Key":
|
|
41
|
+
"X-API-Key": key
|
|
41
42
|
},
|
|
42
43
|
body: body ? JSON.stringify(body) : void 0
|
|
43
44
|
});
|
|
@@ -47,6 +48,21 @@ var MeetSudoServer = class {
|
|
|
47
48
|
}
|
|
48
49
|
return res.json();
|
|
49
50
|
}
|
|
51
|
+
async request(method, path, body) {
|
|
52
|
+
if (!this.apiKey) {
|
|
53
|
+
throw new Error("MeetSudoServer: apiKey is required for this call");
|
|
54
|
+
}
|
|
55
|
+
return this.requestWithKey(method, path, this.apiKey, body);
|
|
56
|
+
}
|
|
57
|
+
async requestSecret(method, path, body) {
|
|
58
|
+
if (!this.secretKey) {
|
|
59
|
+
throw new Error("MeetSudoServer: secretKey is required for logs");
|
|
60
|
+
}
|
|
61
|
+
if (!this.secretKey.startsWith("sk_")) {
|
|
62
|
+
throw new Error("MeetSudoServer: secretKey must start with sk_");
|
|
63
|
+
}
|
|
64
|
+
return this.requestWithKey(method, path, this.secretKey, body);
|
|
65
|
+
}
|
|
50
66
|
/**
|
|
51
67
|
* Identify a user with traits.
|
|
52
68
|
* Creates or updates the customer in MeetSudo.
|
|
@@ -142,6 +158,39 @@ var MeetSudoServer = class {
|
|
|
142
158
|
const qs = limit ? `?limit=${limit}` : "";
|
|
143
159
|
return this.request("GET", `/customers/${customerId}/timeline${qs}`);
|
|
144
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Ingest one backend log entry (requires secretKey).
|
|
163
|
+
*/
|
|
164
|
+
async log(entry) {
|
|
165
|
+
return this.requestSecret("POST", "/logs/ingest", { logs: [entry] });
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Ingest multiple backend log entries (requires secretKey).
|
|
169
|
+
*/
|
|
170
|
+
async logBatch(entries) {
|
|
171
|
+
return this.requestSecret("POST", "/logs/ingest", { logs: entries });
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Query logs with Splunk-like commands:
|
|
175
|
+
* - count
|
|
176
|
+
* - stats count by level|source|service|event|user_id
|
|
177
|
+
* - timechart span=1h
|
|
178
|
+
*/
|
|
179
|
+
async queryLogs(params) {
|
|
180
|
+
return this.requestSecret("POST", "/logs/query/secret", params);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Convert plain English into a MeetSudo logs query (requires secretKey).
|
|
184
|
+
*/
|
|
185
|
+
async assistLogQuery(prompt) {
|
|
186
|
+
return this.requestSecret("POST", "/logs/assist/secret", { prompt });
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get observed workspace log schema hints (services/events/prop keys).
|
|
190
|
+
*/
|
|
191
|
+
async getLogSchemaHints() {
|
|
192
|
+
return this.requestSecret("GET", "/logs/schema-hints/secret");
|
|
193
|
+
}
|
|
145
194
|
};
|
|
146
195
|
var server_default = MeetSudoServer;
|
|
147
196
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/server.js.map
CHANGED
|
@@ -1 +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":[]}
|
|
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 secretKey?: 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 interface LogEntry {\n ts?: string;\n level?: \"debug\" | \"info\" | \"warn\" | \"error\";\n source?: \"backend\" | \"frontend\" | \"sdk\" | \"system\";\n service?: string;\n event?: string;\n message: string;\n user_id?: string;\n request_id?: string;\n trace_id?: string;\n tags?: Record<string, string>;\n props?: Record<string, unknown>;\n}\n\nexport interface LogQueryRequest {\n query?: string;\n from?: string;\n to?: string;\n limit?: number;\n}\n\nexport interface LogAssistResponse {\n query: string;\n explanation: string;\n}\n\nexport interface LogSchemaHintsResponse {\n services: string[];\n events: string[];\n prop_keys: string[];\n levels: string[];\n sources: string[];\n}\n\nexport class MeetSudoServer {\n private apiKey: string | null;\n private secretKey: string | null;\n private apiUrl: string;\n\n constructor(config: MeetSudoServerConfig) {\n if (!config.apiKey && !config.secretKey) {\n throw new Error(\"MeetSudoServer: apiKey or secretKey is required\");\n }\n this.apiKey = config.apiKey ?? null;\n this.secretKey = config.secretKey ?? null;\n this.apiUrl = config.apiUrl || \"https://api.meetsudo.com\";\n }\n\n private async requestWithKey<T>(\n method: string,\n path: string,\n key: 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\": key,\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 private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n if (!this.apiKey) {\n throw new Error(\"MeetSudoServer: apiKey is required for this call\");\n }\n return this.requestWithKey(method, path, this.apiKey, body);\n }\n\n private async requestSecret<T>(method: string, path: string, body?: unknown): Promise<T> {\n if (!this.secretKey) {\n throw new Error(\"MeetSudoServer: secretKey is required for logs\");\n }\n if (!this.secretKey.startsWith(\"sk_\")) {\n throw new Error(\"MeetSudoServer: secretKey must start with sk_\");\n }\n return this.requestWithKey(method, path, this.secretKey, body);\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 * Ingest one backend log entry (requires secretKey).\n */\n async log(entry: LogEntry): Promise<{ ok: boolean; inserted: number }> {\n return this.requestSecret(\"POST\", \"/logs/ingest\", { logs: [entry] });\n }\n\n /**\n * Ingest multiple backend log entries (requires secretKey).\n */\n async logBatch(entries: LogEntry[]): Promise<{ ok: boolean; inserted: number }> {\n return this.requestSecret(\"POST\", \"/logs/ingest\", { logs: entries });\n }\n\n /**\n * Query logs with Splunk-like commands:\n * - count\n * - stats count by level|source|service|event|user_id\n * - timechart span=1h\n */\n async queryLogs(params: LogQueryRequest): Promise<unknown> {\n return this.requestSecret(\"POST\", \"/logs/query/secret\", params);\n }\n\n /**\n * Convert plain English into a MeetSudo logs query (requires secretKey).\n */\n async assistLogQuery(prompt: string): Promise<LogAssistResponse> {\n return this.requestSecret(\"POST\", \"/logs/assist/secret\", { prompt });\n }\n\n /**\n * Get observed workspace log schema hints (services/events/prop keys).\n */\n async getLogSchemaHints(): Promise<LogSchemaHintsResponse> {\n return this.requestSecret(\"GET\", \"/logs/schema-hints/secret\");\n }\n}\n\n// Default export for convenience\nexport default MeetSudoServer;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuDO,IAAM,iBAAN,MAAqB;AAAA,EAK1B,YAAY,QAA8B;AACxC,QAAI,CAAC,OAAO,UAAU,CAAC,OAAO,WAAW;AACvC,YAAM,IAAI,MAAM,iDAAiD;AAAA,IACnE;AACA,SAAK,SAAS,OAAO,UAAU;AAC/B,SAAK,YAAY,OAAO,aAAa;AACrC,SAAK,SAAS,OAAO,UAAU;AAAA,EACjC;AAAA,EAEA,MAAc,eACZ,QACA,MACA,KACA,MACY;AACZ,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,MAAM,GAAG,IAAI,IAAI;AAAA,MAC/C;AAAA,MACA,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,aAAa;AAAA,MACf;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,EAEA,MAAc,QAAW,QAAgB,MAAc,MAA4B;AACjF,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI,MAAM,kDAAkD;AAAA,IACpE;AACA,WAAO,KAAK,eAAe,QAAQ,MAAM,KAAK,QAAQ,IAAI;AAAA,EAC5D;AAAA,EAEA,MAAc,cAAiB,QAAgB,MAAc,MAA4B;AACvF,QAAI,CAAC,KAAK,WAAW;AACnB,YAAM,IAAI,MAAM,gDAAgD;AAAA,IAClE;AACA,QAAI,CAAC,KAAK,UAAU,WAAW,KAAK,GAAG;AACrC,YAAM,IAAI,MAAM,+CAA+C;AAAA,IACjE;AACA,WAAO,KAAK,eAAe,QAAQ,MAAM,KAAK,WAAW,IAAI;AAAA,EAC/D;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;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAI,OAA6D;AACrE,WAAO,KAAK,cAAc,QAAQ,gBAAgB,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAS,SAAiE;AAC9E,WAAO,KAAK,cAAc,QAAQ,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,UAAU,QAA2C;AACzD,WAAO,KAAK,cAAc,QAAQ,sBAAsB,MAAM;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,QAA4C;AAC/D,WAAO,KAAK,cAAc,QAAQ,uBAAuB,EAAE,OAAO,CAAC;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBAAqD;AACzD,WAAO,KAAK,cAAc,OAAO,2BAA2B;AAAA,EAC9D;AACF;AAGA,IAAO,iBAAQ;","names":[]}
|
package/dist/server.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meet-sudo/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "MeetSudo SDK - Chat, Analytics, Session Replay",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"bin": {
|
|
9
|
-
"meet-sudo": "
|
|
9
|
+
"meet-sudo": "dist/cli.js"
|
|
10
10
|
},
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
@@ -1 +0,0 @@
|
|
|
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":[]}
|