userplex 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Userplex
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,319 @@
1
+ # Userplex
2
+
3
+ Dead simple analytics SDK. No complex setup. Just track.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install userplex
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```javascript
14
+ import Userplex from 'userplex';
15
+
16
+ // Initialize with your API key
17
+ const userplex = new Userplex('upx_your_api_key_here');
18
+
19
+ // Track events
20
+ await userplex.track('user123', 'button_clicked', {
21
+ button: 'signup',
22
+ page: '/pricing'
23
+ });
24
+
25
+ // Identify users
26
+ await userplex.identify('user123', {
27
+ email: 'user@example.com',
28
+ name: 'John Doe',
29
+ plan: 'premium'
30
+ });
31
+
32
+ // Log LLM conversations
33
+ await userplex.logConversation({
34
+ userId: 'user123',
35
+ messages: [
36
+ { role: 'user', content: 'How do I reset my password?' },
37
+ { role: 'assistant', content: 'Click on "Forgot Password" on the login page.' }
38
+ ]
39
+ });
40
+ ```
41
+
42
+ That's it. No route setup. No configuration files. Just analytics.
43
+
44
+ ## API
45
+
46
+ ### `new Userplex(apiKey, options?)`
47
+
48
+ Create a new Userplex instance.
49
+
50
+ - `apiKey` - Your Userplex API key (required)
51
+ - `options` - Optional configuration
52
+ - `baseUrl` - Your Userplex server URL (default: `http://localhost:3000`)
53
+ - `timeout` - Request timeout in ms (default: `10000`)
54
+ - `debug` - Enable debug logging (default: `false`)
55
+
56
+ ```javascript
57
+ const userplex = new Userplex('upx_key', {
58
+ baseUrl: 'https://analytics.myapp.com',
59
+ timeout: 30000,
60
+ debug: true
61
+ });
62
+ ```
63
+
64
+ ### `track(userId, event, properties?)`
65
+
66
+ Track an event.
67
+
68
+ ```javascript
69
+ await userplex.track('user123', 'purchase_completed', {
70
+ amount: 99.99,
71
+ currency: 'USD',
72
+ items: ['product1', 'product2']
73
+ });
74
+ ```
75
+
76
+ ### `identify(userId, properties?)`
77
+
78
+ Identify a user and set their properties.
79
+
80
+ ```javascript
81
+ await userplex.identify('user123', {
82
+ email: 'john@example.com',
83
+ company: 'Acme Corp',
84
+ role: 'admin',
85
+ plan: 'enterprise'
86
+ });
87
+ ```
88
+
89
+ ### `logConversation(options)`
90
+
91
+ Log an LLM conversation for observability.
92
+
93
+ ```javascript
94
+ await userplex.logConversation({
95
+ userId: 'user123', // optional
96
+ conversationId: 'conv_abc', // optional
97
+ conversationName: 'Support Chat', // optional
98
+ messages: [ // required
99
+ { role: 'system', content: 'You are a helpful assistant.' },
100
+ { role: 'user', content: 'Hello!' },
101
+ { role: 'assistant', content: 'Hi! How can I help?' }
102
+ ]
103
+ });
104
+ ```
105
+
106
+ ### `trackBatch(events)`
107
+
108
+ Track multiple events at once.
109
+
110
+ ```javascript
111
+ await userplex.trackBatch([
112
+ { userId: 'user1', event: 'login', properties: { method: 'google' } },
113
+ { userId: 'user2', event: 'signup', properties: { plan: 'free' } },
114
+ { userId: 'user3', event: 'upgrade', properties: { plan: 'pro' } }
115
+ ]);
116
+ ```
117
+
118
+ ## Using in Different Environments
119
+
120
+ ### Node.js / Server-side
121
+
122
+ Use it directly with your API key:
123
+
124
+ ```javascript
125
+ // server.js
126
+ import Userplex from 'userplex';
127
+
128
+ const userplex = new Userplex(process.env.USERPLEX_API_KEY);
129
+
130
+ app.post('/api/checkout', async (req, res) => {
131
+ // Process payment...
132
+
133
+ // Track purchase
134
+ await userplex.track(req.user.id, 'purchase_completed', {
135
+ amount: req.body.amount
136
+ });
137
+
138
+ res.json({ success: true });
139
+ });
140
+ ```
141
+
142
+ ### Next.js API Routes
143
+
144
+ ```javascript
145
+ // app/api/checkout/route.js
146
+ import Userplex from 'userplex';
147
+
148
+ const userplex = new Userplex(process.env.USERPLEX_API_KEY);
149
+
150
+ export async function POST(request) {
151
+ const body = await request.json();
152
+
153
+ await userplex.track(body.userId, 'purchase_completed', {
154
+ amount: body.amount
155
+ });
156
+
157
+ return Response.json({ success: true });
158
+ }
159
+ ```
160
+
161
+ ### Client-side (Browser)
162
+
163
+ ⚠️ **Never put your API key in client-side code!**
164
+
165
+ For client-side tracking, you have two options:
166
+
167
+ #### Option 1: Create a simple API endpoint
168
+
169
+ ```javascript
170
+ // app/api/track/route.js (Next.js example)
171
+ import Userplex from 'userplex';
172
+
173
+ const userplex = new Userplex(process.env.USERPLEX_API_KEY);
174
+
175
+ export async function POST(request) {
176
+ const { userId, event, properties } = await request.json();
177
+ await userplex.track(userId, event, properties);
178
+ return Response.json({ success: true });
179
+ }
180
+ ```
181
+
182
+ Then in your client:
183
+
184
+ ```javascript
185
+ // In your React component
186
+ async function trackEvent(event, properties) {
187
+ await fetch('/api/track', {
188
+ method: 'POST',
189
+ headers: { 'Content-Type': 'application/json' },
190
+ body: JSON.stringify({
191
+ userId: currentUser.id,
192
+ event,
193
+ properties
194
+ })
195
+ });
196
+ }
197
+
198
+ // Use it
199
+ trackEvent('button_clicked', { button: 'signup' });
200
+ ```
201
+
202
+ #### Option 2: Use environment-specific initialization
203
+
204
+ ```javascript
205
+ // lib/analytics.js
206
+ import Userplex from 'userplex';
207
+
208
+ // Server-side: use real instance
209
+ // Client-side: use a proxy that calls your API
210
+ const userplex = typeof window === 'undefined'
211
+ ? new Userplex(process.env.USERPLEX_API_KEY)
212
+ : {
213
+ track: async (userId, event, properties) => {
214
+ await fetch('/api/track', {
215
+ method: 'POST',
216
+ headers: { 'Content-Type': 'application/json' },
217
+ body: JSON.stringify({ userId, event, properties })
218
+ });
219
+ },
220
+ identify: async (userId, properties) => {
221
+ await fetch('/api/identify', {
222
+ method: 'POST',
223
+ headers: { 'Content-Type': 'application/json' },
224
+ body: JSON.stringify({ userId, properties })
225
+ });
226
+ }
227
+ };
228
+
229
+ export default userplex;
230
+ ```
231
+
232
+ ## Error Handling
233
+
234
+ ```javascript
235
+ try {
236
+ await userplex.track('user123', 'event');
237
+ } catch (error) {
238
+ if (error.name === 'UserplexError') {
239
+ console.error('Analytics error:', error.message);
240
+ console.error('Status code:', error.statusCode);
241
+ }
242
+ }
243
+ ```
244
+
245
+ ## TypeScript
246
+
247
+ Full TypeScript support included:
248
+
249
+ ```typescript
250
+ import Userplex, { ConversationMessage, UserplexError } from 'userplex';
251
+
252
+ const userplex = new Userplex('upx_key');
253
+
254
+ const messages: ConversationMessage[] = [
255
+ { role: 'user', content: 'Hello' },
256
+ { role: 'assistant', content: 'Hi!' }
257
+ ];
258
+
259
+ try {
260
+ await userplex.logConversation({ messages });
261
+ } catch (error) {
262
+ if (error instanceof UserplexError) {
263
+ // Typed error handling
264
+ }
265
+ }
266
+ ```
267
+
268
+ ## Examples
269
+
270
+ ### E-commerce Tracking
271
+
272
+ ```javascript
273
+ // Track the full customer journey
274
+ await userplex.track(userId, 'product_viewed', { productId, price });
275
+ await userplex.track(userId, 'add_to_cart', { productId, quantity });
276
+ await userplex.track(userId, 'checkout_started', { cartValue });
277
+ await userplex.track(userId, 'purchase_completed', { orderId, amount });
278
+ ```
279
+
280
+ ### SaaS Application
281
+
282
+ ```javascript
283
+ // Track feature usage
284
+ await userplex.track(userId, 'feature_used', {
285
+ feature: 'csv_export',
286
+ recordCount: 1000
287
+ });
288
+
289
+ // Track subscription changes
290
+ await userplex.identify(userId, {
291
+ plan: 'enterprise',
292
+ mrr: 499
293
+ });
294
+ ```
295
+
296
+ ### AI Application
297
+
298
+ ```javascript
299
+ // Log every conversation for debugging and analytics
300
+ const messages = [];
301
+
302
+ // User asks a question
303
+ messages.push({ role: 'user', content: userInput });
304
+
305
+ // Get AI response
306
+ const response = await callOpenAI(messages);
307
+ messages.push({ role: 'assistant', content: response });
308
+
309
+ // Log to Userplex
310
+ await userplex.logConversation({
311
+ userId: currentUser.id,
312
+ conversationName: 'Chat Session',
313
+ messages
314
+ });
315
+ ```
316
+
317
+ ## License
318
+
319
+ MIT
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Userplex - Simple Analytics SDK
3
+ *
4
+ * Just install and use. No complex setup required.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import Userplex from 'userplex';
9
+ *
10
+ * const userplex = new Userplex('upx_your_api_key');
11
+ *
12
+ * // Track events
13
+ * await userplex.track('user123', 'button_clicked', { button: 'signup' });
14
+ *
15
+ * // Identify users
16
+ * await userplex.identify('user123', { email: 'user@example.com' });
17
+ *
18
+ * // Log conversations
19
+ * await userplex.logConversation({
20
+ * messages: [
21
+ * { role: 'user', content: 'Hello' },
22
+ * { role: 'assistant', content: 'Hi there!' }
23
+ * ]
24
+ * });
25
+ * ```
26
+ */
27
+ interface ConversationMessage {
28
+ role: 'user' | 'assistant' | 'system';
29
+ content: string;
30
+ }
31
+ interface UserplexOptions {
32
+ /** Your Userplex instance URL (default: http://localhost:3000) */
33
+ baseUrl?: string;
34
+ /** Request timeout in ms (default: 10000) */
35
+ timeout?: number;
36
+ /** Enable debug logging (default: false) */
37
+ debug?: boolean;
38
+ }
39
+ declare class UserplexError extends Error {
40
+ statusCode?: number | undefined;
41
+ response?: any | undefined;
42
+ constructor(message: string, statusCode?: number | undefined, response?: any | undefined);
43
+ }
44
+ declare class Userplex {
45
+ private apiKey;
46
+ private baseUrl;
47
+ private timeout;
48
+ private debug;
49
+ constructor(apiKey: string, options?: UserplexOptions);
50
+ /**
51
+ * Track an event
52
+ */
53
+ track(userId: string, event: string, properties?: Record<string, any>): Promise<any>;
54
+ /**
55
+ * Identify a user
56
+ */
57
+ identify(externalId: string, properties?: Record<string, any>): Promise<any>;
58
+ /**
59
+ * Log a conversation
60
+ */
61
+ logConversation(options: {
62
+ messages: ConversationMessage[];
63
+ userId?: string;
64
+ conversationId?: string;
65
+ conversationName?: string;
66
+ }): Promise<any>;
67
+ /**
68
+ * Batch track multiple events
69
+ */
70
+ trackBatch(events: Array<{
71
+ userId: string;
72
+ event: string;
73
+ properties?: Record<string, any>;
74
+ }>): Promise<any[]>;
75
+ private request;
76
+ }
77
+
78
+ export { type ConversationMessage, Userplex, UserplexError, type UserplexOptions, Userplex as default };
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Userplex - Simple Analytics SDK
3
+ *
4
+ * Just install and use. No complex setup required.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import Userplex from 'userplex';
9
+ *
10
+ * const userplex = new Userplex('upx_your_api_key');
11
+ *
12
+ * // Track events
13
+ * await userplex.track('user123', 'button_clicked', { button: 'signup' });
14
+ *
15
+ * // Identify users
16
+ * await userplex.identify('user123', { email: 'user@example.com' });
17
+ *
18
+ * // Log conversations
19
+ * await userplex.logConversation({
20
+ * messages: [
21
+ * { role: 'user', content: 'Hello' },
22
+ * { role: 'assistant', content: 'Hi there!' }
23
+ * ]
24
+ * });
25
+ * ```
26
+ */
27
+ interface ConversationMessage {
28
+ role: 'user' | 'assistant' | 'system';
29
+ content: string;
30
+ }
31
+ interface UserplexOptions {
32
+ /** Your Userplex instance URL (default: http://localhost:3000) */
33
+ baseUrl?: string;
34
+ /** Request timeout in ms (default: 10000) */
35
+ timeout?: number;
36
+ /** Enable debug logging (default: false) */
37
+ debug?: boolean;
38
+ }
39
+ declare class UserplexError extends Error {
40
+ statusCode?: number | undefined;
41
+ response?: any | undefined;
42
+ constructor(message: string, statusCode?: number | undefined, response?: any | undefined);
43
+ }
44
+ declare class Userplex {
45
+ private apiKey;
46
+ private baseUrl;
47
+ private timeout;
48
+ private debug;
49
+ constructor(apiKey: string, options?: UserplexOptions);
50
+ /**
51
+ * Track an event
52
+ */
53
+ track(userId: string, event: string, properties?: Record<string, any>): Promise<any>;
54
+ /**
55
+ * Identify a user
56
+ */
57
+ identify(externalId: string, properties?: Record<string, any>): Promise<any>;
58
+ /**
59
+ * Log a conversation
60
+ */
61
+ logConversation(options: {
62
+ messages: ConversationMessage[];
63
+ userId?: string;
64
+ conversationId?: string;
65
+ conversationName?: string;
66
+ }): Promise<any>;
67
+ /**
68
+ * Batch track multiple events
69
+ */
70
+ trackBatch(events: Array<{
71
+ userId: string;
72
+ event: string;
73
+ properties?: Record<string, any>;
74
+ }>): Promise<any[]>;
75
+ private request;
76
+ }
77
+
78
+ export { type ConversationMessage, Userplex, UserplexError, type UserplexOptions, Userplex as default };
package/dist/index.js ADDED
@@ -0,0 +1,132 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ // index.ts
6
+ var UserplexError = class extends Error {
7
+ constructor(message, statusCode, response) {
8
+ super(message);
9
+ this.statusCode = statusCode;
10
+ this.response = response;
11
+ this.name = "UserplexError";
12
+ }
13
+ };
14
+ var Userplex = class {
15
+ constructor(apiKey, options = {}) {
16
+ if (!apiKey) {
17
+ throw new Error("Userplex: API key is required");
18
+ }
19
+ if (!apiKey.startsWith("upx_")) {
20
+ throw new Error('Userplex: API key must start with "upx_"');
21
+ }
22
+ this.apiKey = apiKey;
23
+ this.baseUrl = (options.baseUrl || "http://localhost:3000").replace(/\/$/, "");
24
+ this.timeout = options.timeout || 1e4;
25
+ this.debug = options.debug || false;
26
+ }
27
+ /**
28
+ * Track an event
29
+ */
30
+ async track(userId, event, properties) {
31
+ if (!userId || !event) {
32
+ throw new Error("userId and event are required");
33
+ }
34
+ return this.request("/api/events", {
35
+ userId,
36
+ event,
37
+ properties: properties || {}
38
+ });
39
+ }
40
+ /**
41
+ * Identify a user
42
+ */
43
+ async identify(externalId, properties) {
44
+ if (!externalId) {
45
+ throw new Error("externalId is required");
46
+ }
47
+ return this.request("/api/identify", {
48
+ externalId,
49
+ ...properties
50
+ }, {
51
+ "x-api-key": this.apiKey
52
+ // identify endpoint uses this header too
53
+ });
54
+ }
55
+ /**
56
+ * Log a conversation
57
+ */
58
+ async logConversation(options) {
59
+ if (!options.messages || options.messages.length === 0) {
60
+ throw new Error("messages array is required and cannot be empty");
61
+ }
62
+ return this.request("/api/conversations", {
63
+ externalUserId: options.userId,
64
+ conversationId: options.conversationId,
65
+ conversationName: options.conversationName,
66
+ messages: options.messages
67
+ });
68
+ }
69
+ /**
70
+ * Batch track multiple events
71
+ */
72
+ async trackBatch(events) {
73
+ if (!events || events.length === 0) {
74
+ throw new Error("events array is required and cannot be empty");
75
+ }
76
+ const promises = events.map((e) => this.track(e.userId, e.event, e.properties));
77
+ return Promise.all(promises);
78
+ }
79
+ async request(endpoint, data, additionalHeaders) {
80
+ const url = `${this.baseUrl}${endpoint}`;
81
+ const controller = new AbortController();
82
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
83
+ if (this.debug) {
84
+ console.log(`[Userplex] POST ${url}`, JSON.stringify(data, null, 2));
85
+ }
86
+ try {
87
+ const response = await fetch(url, {
88
+ method: "POST",
89
+ headers: {
90
+ "Content-Type": "application/json",
91
+ "Authorization": `Bearer ${this.apiKey}`,
92
+ ...additionalHeaders
93
+ },
94
+ body: JSON.stringify(data),
95
+ signal: controller.signal
96
+ });
97
+ clearTimeout(timeoutId);
98
+ const responseData = await response.json();
99
+ if (this.debug) {
100
+ console.log(`[Userplex] Response:`, JSON.stringify(responseData, null, 2));
101
+ }
102
+ if (!response.ok) {
103
+ throw new UserplexError(
104
+ responseData.error || `Request failed with status ${response.status}`,
105
+ response.status,
106
+ responseData
107
+ );
108
+ }
109
+ return responseData;
110
+ } catch (error) {
111
+ clearTimeout(timeoutId);
112
+ if (error.name === "AbortError") {
113
+ throw new UserplexError("Request timeout", 408);
114
+ }
115
+ if (error instanceof UserplexError) {
116
+ throw error;
117
+ }
118
+ throw new UserplexError(
119
+ error.message || "An unexpected error occurred",
120
+ void 0,
121
+ error
122
+ );
123
+ }
124
+ }
125
+ };
126
+ var index_default = Userplex;
127
+
128
+ exports.Userplex = Userplex;
129
+ exports.UserplexError = UserplexError;
130
+ exports.default = index_default;
131
+ //# sourceMappingURL=index.js.map
132
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Userplex - Simple Analytics SDK\n * \n * Just install and use. No complex setup required.\n * \n * @example\n * ```ts\n * import Userplex from 'userplex';\n * \n * const userplex = new Userplex('upx_your_api_key');\n * \n * // Track events\n * await userplex.track('user123', 'button_clicked', { button: 'signup' });\n * \n * // Identify users\n * await userplex.identify('user123', { email: 'user@example.com' });\n * \n * // Log conversations\n * await userplex.logConversation({\n * messages: [\n * { role: 'user', content: 'Hello' },\n * { role: 'assistant', content: 'Hi there!' }\n * ]\n * });\n * ```\n */\n\nexport interface ConversationMessage {\n role: 'user' | 'assistant' | 'system';\n content: string;\n}\n\nexport interface UserplexOptions {\n /** Your Userplex instance URL (default: http://localhost:3000) */\n baseUrl?: string;\n /** Request timeout in ms (default: 10000) */\n timeout?: number;\n /** Enable debug logging (default: false) */\n debug?: boolean;\n}\n\nexport class UserplexError extends Error {\n constructor(\n message: string,\n public statusCode?: number,\n public response?: any\n ) {\n super(message);\n this.name = 'UserplexError';\n }\n}\n\nclass Userplex {\n private apiKey: string;\n private baseUrl: string;\n private timeout: number;\n private debug: boolean;\n\n constructor(apiKey: string, options: UserplexOptions = {}) {\n if (!apiKey) {\n throw new Error('Userplex: API key is required');\n }\n if (!apiKey.startsWith('upx_')) {\n throw new Error('Userplex: API key must start with \"upx_\"');\n }\n\n this.apiKey = apiKey;\n this.baseUrl = (options.baseUrl || 'http://localhost:3000').replace(/\\/$/, '');\n this.timeout = options.timeout || 10000;\n this.debug = options.debug || false;\n }\n\n /**\n * Track an event\n */\n async track(userId: string, event: string, properties?: Record<string, any>) {\n if (!userId || !event) {\n throw new Error('userId and event are required');\n }\n\n return this.request('/api/events', {\n userId,\n event,\n properties: properties || {}\n });\n }\n\n /**\n * Identify a user\n */\n async identify(externalId: string, properties?: Record<string, any>) {\n if (!externalId) {\n throw new Error('externalId is required');\n }\n\n return this.request('/api/identify', {\n externalId,\n ...properties\n }, {\n 'x-api-key': this.apiKey // identify endpoint uses this header too\n });\n }\n\n /**\n * Log a conversation\n */\n async logConversation(options: {\n messages: ConversationMessage[];\n userId?: string;\n conversationId?: string;\n conversationName?: string;\n }) {\n if (!options.messages || options.messages.length === 0) {\n throw new Error('messages array is required and cannot be empty');\n }\n\n return this.request('/api/conversations', {\n externalUserId: options.userId,\n conversationId: options.conversationId,\n conversationName: options.conversationName,\n messages: options.messages\n });\n }\n\n /**\n * Batch track multiple events\n */\n async trackBatch(events: Array<{\n userId: string;\n event: string;\n properties?: Record<string, any>;\n }>) {\n if (!events || events.length === 0) {\n throw new Error('events array is required and cannot be empty');\n }\n\n const promises = events.map(e => this.track(e.userId, e.event, e.properties));\n return Promise.all(promises);\n }\n\n private async request(endpoint: string, data: any, additionalHeaders?: Record<string, string>) {\n const url = `${this.baseUrl}${endpoint}`;\n \n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n if (this.debug) {\n console.log(`[Userplex] POST ${url}`, JSON.stringify(data, null, 2));\n }\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n ...additionalHeaders\n },\n body: JSON.stringify(data),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n const responseData = await response.json() as any;\n\n if (this.debug) {\n console.log(`[Userplex] Response:`, JSON.stringify(responseData, null, 2));\n }\n\n if (!response.ok) {\n throw new UserplexError(\n responseData.error || `Request failed with status ${response.status}`,\n response.status,\n responseData\n );\n }\n\n return responseData;\n } catch (error: any) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new UserplexError('Request timeout', 408);\n }\n\n if (error instanceof UserplexError) {\n throw error;\n }\n\n throw new UserplexError(\n error.message || 'An unexpected error occurred',\n undefined,\n error\n );\n }\n }\n}\n\nexport default Userplex;\n\n// For environments that need it separate\nexport { Userplex };\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,126 @@
1
+ // index.ts
2
+ var UserplexError = class extends Error {
3
+ constructor(message, statusCode, response) {
4
+ super(message);
5
+ this.statusCode = statusCode;
6
+ this.response = response;
7
+ this.name = "UserplexError";
8
+ }
9
+ };
10
+ var Userplex = class {
11
+ constructor(apiKey, options = {}) {
12
+ if (!apiKey) {
13
+ throw new Error("Userplex: API key is required");
14
+ }
15
+ if (!apiKey.startsWith("upx_")) {
16
+ throw new Error('Userplex: API key must start with "upx_"');
17
+ }
18
+ this.apiKey = apiKey;
19
+ this.baseUrl = (options.baseUrl || "http://localhost:3000").replace(/\/$/, "");
20
+ this.timeout = options.timeout || 1e4;
21
+ this.debug = options.debug || false;
22
+ }
23
+ /**
24
+ * Track an event
25
+ */
26
+ async track(userId, event, properties) {
27
+ if (!userId || !event) {
28
+ throw new Error("userId and event are required");
29
+ }
30
+ return this.request("/api/events", {
31
+ userId,
32
+ event,
33
+ properties: properties || {}
34
+ });
35
+ }
36
+ /**
37
+ * Identify a user
38
+ */
39
+ async identify(externalId, properties) {
40
+ if (!externalId) {
41
+ throw new Error("externalId is required");
42
+ }
43
+ return this.request("/api/identify", {
44
+ externalId,
45
+ ...properties
46
+ }, {
47
+ "x-api-key": this.apiKey
48
+ // identify endpoint uses this header too
49
+ });
50
+ }
51
+ /**
52
+ * Log a conversation
53
+ */
54
+ async logConversation(options) {
55
+ if (!options.messages || options.messages.length === 0) {
56
+ throw new Error("messages array is required and cannot be empty");
57
+ }
58
+ return this.request("/api/conversations", {
59
+ externalUserId: options.userId,
60
+ conversationId: options.conversationId,
61
+ conversationName: options.conversationName,
62
+ messages: options.messages
63
+ });
64
+ }
65
+ /**
66
+ * Batch track multiple events
67
+ */
68
+ async trackBatch(events) {
69
+ if (!events || events.length === 0) {
70
+ throw new Error("events array is required and cannot be empty");
71
+ }
72
+ const promises = events.map((e) => this.track(e.userId, e.event, e.properties));
73
+ return Promise.all(promises);
74
+ }
75
+ async request(endpoint, data, additionalHeaders) {
76
+ const url = `${this.baseUrl}${endpoint}`;
77
+ const controller = new AbortController();
78
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
79
+ if (this.debug) {
80
+ console.log(`[Userplex] POST ${url}`, JSON.stringify(data, null, 2));
81
+ }
82
+ try {
83
+ const response = await fetch(url, {
84
+ method: "POST",
85
+ headers: {
86
+ "Content-Type": "application/json",
87
+ "Authorization": `Bearer ${this.apiKey}`,
88
+ ...additionalHeaders
89
+ },
90
+ body: JSON.stringify(data),
91
+ signal: controller.signal
92
+ });
93
+ clearTimeout(timeoutId);
94
+ const responseData = await response.json();
95
+ if (this.debug) {
96
+ console.log(`[Userplex] Response:`, JSON.stringify(responseData, null, 2));
97
+ }
98
+ if (!response.ok) {
99
+ throw new UserplexError(
100
+ responseData.error || `Request failed with status ${response.status}`,
101
+ response.status,
102
+ responseData
103
+ );
104
+ }
105
+ return responseData;
106
+ } catch (error) {
107
+ clearTimeout(timeoutId);
108
+ if (error.name === "AbortError") {
109
+ throw new UserplexError("Request timeout", 408);
110
+ }
111
+ if (error instanceof UserplexError) {
112
+ throw error;
113
+ }
114
+ throw new UserplexError(
115
+ error.message || "An unexpected error occurred",
116
+ void 0,
117
+ error
118
+ );
119
+ }
120
+ }
121
+ };
122
+ var index_default = Userplex;
123
+
124
+ export { Userplex, UserplexError, index_default as default };
125
+ //# sourceMappingURL=index.mjs.map
126
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Userplex - Simple Analytics SDK\n * \n * Just install and use. No complex setup required.\n * \n * @example\n * ```ts\n * import Userplex from 'userplex';\n * \n * const userplex = new Userplex('upx_your_api_key');\n * \n * // Track events\n * await userplex.track('user123', 'button_clicked', { button: 'signup' });\n * \n * // Identify users\n * await userplex.identify('user123', { email: 'user@example.com' });\n * \n * // Log conversations\n * await userplex.logConversation({\n * messages: [\n * { role: 'user', content: 'Hello' },\n * { role: 'assistant', content: 'Hi there!' }\n * ]\n * });\n * ```\n */\n\nexport interface ConversationMessage {\n role: 'user' | 'assistant' | 'system';\n content: string;\n}\n\nexport interface UserplexOptions {\n /** Your Userplex instance URL (default: http://localhost:3000) */\n baseUrl?: string;\n /** Request timeout in ms (default: 10000) */\n timeout?: number;\n /** Enable debug logging (default: false) */\n debug?: boolean;\n}\n\nexport class UserplexError extends Error {\n constructor(\n message: string,\n public statusCode?: number,\n public response?: any\n ) {\n super(message);\n this.name = 'UserplexError';\n }\n}\n\nclass Userplex {\n private apiKey: string;\n private baseUrl: string;\n private timeout: number;\n private debug: boolean;\n\n constructor(apiKey: string, options: UserplexOptions = {}) {\n if (!apiKey) {\n throw new Error('Userplex: API key is required');\n }\n if (!apiKey.startsWith('upx_')) {\n throw new Error('Userplex: API key must start with \"upx_\"');\n }\n\n this.apiKey = apiKey;\n this.baseUrl = (options.baseUrl || 'http://localhost:3000').replace(/\\/$/, '');\n this.timeout = options.timeout || 10000;\n this.debug = options.debug || false;\n }\n\n /**\n * Track an event\n */\n async track(userId: string, event: string, properties?: Record<string, any>) {\n if (!userId || !event) {\n throw new Error('userId and event are required');\n }\n\n return this.request('/api/events', {\n userId,\n event,\n properties: properties || {}\n });\n }\n\n /**\n * Identify a user\n */\n async identify(externalId: string, properties?: Record<string, any>) {\n if (!externalId) {\n throw new Error('externalId is required');\n }\n\n return this.request('/api/identify', {\n externalId,\n ...properties\n }, {\n 'x-api-key': this.apiKey // identify endpoint uses this header too\n });\n }\n\n /**\n * Log a conversation\n */\n async logConversation(options: {\n messages: ConversationMessage[];\n userId?: string;\n conversationId?: string;\n conversationName?: string;\n }) {\n if (!options.messages || options.messages.length === 0) {\n throw new Error('messages array is required and cannot be empty');\n }\n\n return this.request('/api/conversations', {\n externalUserId: options.userId,\n conversationId: options.conversationId,\n conversationName: options.conversationName,\n messages: options.messages\n });\n }\n\n /**\n * Batch track multiple events\n */\n async trackBatch(events: Array<{\n userId: string;\n event: string;\n properties?: Record<string, any>;\n }>) {\n if (!events || events.length === 0) {\n throw new Error('events array is required and cannot be empty');\n }\n\n const promises = events.map(e => this.track(e.userId, e.event, e.properties));\n return Promise.all(promises);\n }\n\n private async request(endpoint: string, data: any, additionalHeaders?: Record<string, string>) {\n const url = `${this.baseUrl}${endpoint}`;\n \n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n if (this.debug) {\n console.log(`[Userplex] POST ${url}`, JSON.stringify(data, null, 2));\n }\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n ...additionalHeaders\n },\n body: JSON.stringify(data),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n const responseData = await response.json() as any;\n\n if (this.debug) {\n console.log(`[Userplex] Response:`, JSON.stringify(responseData, null, 2));\n }\n\n if (!response.ok) {\n throw new UserplexError(\n responseData.error || `Request failed with status ${response.status}`,\n response.status,\n responseData\n );\n }\n\n return responseData;\n } catch (error: any) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new UserplexError('Request timeout', 408);\n }\n\n if (error instanceof UserplexError) {\n throw error;\n }\n\n throw new UserplexError(\n error.message || 'An unexpected error occurred',\n undefined,\n error\n );\n }\n }\n}\n\nexport default Userplex;\n\n// For environments that need it separate\nexport { Userplex };\n"]}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "userplex",
3
+ "version": "1.0.0",
4
+ "description": "Simple analytics SDK for Userplex - Track events, identify users, and log conversations",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "README.md"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsup index.ts --format cjs,esm --dts --clean",
14
+ "dev": "tsup index.ts --format cjs,esm --dts --watch",
15
+ "prepublishOnly": "npm run build"
16
+ },
17
+ "keywords": [
18
+ "analytics",
19
+ "tracking",
20
+ "events",
21
+ "userplex",
22
+ "observability",
23
+ "llm",
24
+ "simple"
25
+ ],
26
+ "author": "Userplex",
27
+ "license": "MIT",
28
+ "devDependencies": {
29
+ "tsup": "^8.0.1",
30
+ "typescript": "^5.3.0"
31
+ },
32
+ "engines": {
33
+ "node": ">=14.0.0"
34
+ }
35
+ }