@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 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 request(method, path, body) {
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": this.apiKey
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-2H6T3CDL.mjs.map
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
@@ -1,4 +1,4 @@
1
- export { MeetSudoServer, MeetSudoServerConfig } from './server.mjs';
1
+ export { LogAssistResponse, LogEntry, LogQueryRequest, LogSchemaHintsResponse, MeetSudoServer, MeetSudoServerConfig } from './server.mjs';
2
2
 
3
3
  interface MeetSudoConfig {
4
4
  /** Your MeetSudo API key */
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { MeetSudoServer, MeetSudoServerConfig } from './server.js';
1
+ export { LogAssistResponse, LogEntry, LogQueryRequest, LogSchemaHintsResponse, MeetSudoServer, MeetSudoServerConfig } from './server.js';
2
2
 
3
3
  interface MeetSudoConfig {
4
4
  /** Your MeetSudo API key */
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 request(method, path, body) {
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": this.apiKey
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
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  MeetSudoServer
3
- } from "./chunk-2H6T3CDL.mjs";
3
+ } from "./chunk-F3QN3VVZ.mjs";
4
4
 
5
5
  // src/sdk.ts
6
6
  var STORAGE_KEY = "meetsudo_anon_id";
@@ -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: string;
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: string;
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 request(method, path, body) {
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": this.apiKey
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:
@@ -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":"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","names":[]}
package/dist/server.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  MeetSudoServer,
3
3
  server_default
4
- } from "./chunk-2H6T3CDL.mjs";
4
+ } from "./chunk-F3QN3VVZ.mjs";
5
5
  export {
6
6
  MeetSudoServer,
7
7
  server_default as default
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@meet-sudo/sdk",
3
- "version": "0.1.5",
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": "./dist/cli.js"
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":[]}