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 +21 -0
- package/README.md +319 -0
- package/dist/index.d.mts +78 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.js +132 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +126 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +35 -0
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
|
package/dist/index.d.mts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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
|
+
}
|