userplex 0.1.0 → 1.0.1
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 -201
- package/README.md +228 -273
- 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 +27 -130
- package/CHANGELOG.md +0 -11
- package/api-promise.d.mts +0 -2
- package/api-promise.d.mts.map +0 -1
- package/api-promise.d.ts +0 -2
- package/api-promise.d.ts.map +0 -1
- package/api-promise.js +0 -6
- package/api-promise.js.map +0 -1
- package/api-promise.mjs +0 -2
- package/api-promise.mjs.map +0 -1
- package/client.d.mts +0 -183
- package/client.d.mts.map +0 -1
- package/client.d.ts +0 -183
- package/client.d.ts.map +0 -1
- package/client.js +0 -464
- package/client.js.map +0 -1
- package/client.mjs +0 -460
- package/client.mjs.map +0 -1
- package/core/api-promise.d.mts +0 -46
- package/core/api-promise.d.mts.map +0 -1
- package/core/api-promise.d.ts +0 -46
- package/core/api-promise.d.ts.map +0 -1
- package/core/api-promise.js +0 -74
- package/core/api-promise.js.map +0 -1
- package/core/api-promise.mjs +0 -70
- package/core/api-promise.mjs.map +0 -1
- package/core/error.d.mts +0 -46
- package/core/error.d.mts.map +0 -1
- package/core/error.d.ts +0 -46
- package/core/error.d.ts.map +0 -1
- package/core/error.js +0 -113
- package/core/error.js.map +0 -1
- package/core/error.mjs +0 -97
- package/core/error.mjs.map +0 -1
- package/core/resource.d.mts +0 -6
- package/core/resource.d.mts.map +0 -1
- package/core/resource.d.ts +0 -6
- package/core/resource.d.ts.map +0 -1
- package/core/resource.js +0 -11
- package/core/resource.js.map +0 -1
- package/core/resource.mjs +0 -7
- package/core/resource.mjs.map +0 -1
- package/core/uploads.d.mts +0 -3
- package/core/uploads.d.mts.map +0 -1
- package/core/uploads.d.ts +0 -3
- package/core/uploads.d.ts.map +0 -1
- package/core/uploads.js +0 -6
- package/core/uploads.js.map +0 -1
- package/core/uploads.mjs +0 -2
- package/core/uploads.mjs.map +0 -1
- package/error.d.mts +0 -2
- package/error.d.mts.map +0 -1
- package/error.d.ts +0 -2
- package/error.d.ts.map +0 -1
- package/error.js +0 -6
- package/error.js.map +0 -1
- package/error.mjs +0 -2
- package/error.mjs.map +0 -1
- package/index.d.mts +0 -6
- package/index.d.mts.map +0 -1
- package/index.d.ts +0 -6
- package/index.d.ts.map +0 -1
- package/index.js +0 -30
- package/index.js.map +0 -1
- package/index.mjs +0 -7
- package/index.mjs.map +0 -1
- package/internal/builtin-types.d.mts +0 -73
- package/internal/builtin-types.d.mts.map +0 -1
- package/internal/builtin-types.d.ts +0 -73
- package/internal/builtin-types.d.ts.map +0 -1
- package/internal/builtin-types.js +0 -4
- package/internal/builtin-types.js.map +0 -1
- package/internal/builtin-types.mjs +0 -3
- package/internal/builtin-types.mjs.map +0 -1
- package/internal/detect-platform.d.mts +0 -15
- package/internal/detect-platform.d.mts.map +0 -1
- package/internal/detect-platform.d.ts +0 -15
- package/internal/detect-platform.d.ts.map +0 -1
- package/internal/detect-platform.js +0 -162
- package/internal/detect-platform.js.map +0 -1
- package/internal/detect-platform.mjs +0 -157
- package/internal/detect-platform.mjs.map +0 -1
- package/internal/errors.d.mts +0 -3
- package/internal/errors.d.mts.map +0 -1
- package/internal/errors.d.ts +0 -3
- package/internal/errors.d.ts.map +0 -1
- package/internal/errors.js +0 -41
- package/internal/errors.js.map +0 -1
- package/internal/errors.mjs +0 -36
- package/internal/errors.mjs.map +0 -1
- package/internal/headers.d.mts +0 -20
- package/internal/headers.d.mts.map +0 -1
- package/internal/headers.d.ts +0 -20
- package/internal/headers.d.ts.map +0 -1
- package/internal/headers.js +0 -79
- package/internal/headers.js.map +0 -1
- package/internal/headers.mjs +0 -74
- package/internal/headers.mjs.map +0 -1
- package/internal/parse.d.mts +0 -12
- package/internal/parse.d.mts.map +0 -1
- package/internal/parse.d.ts +0 -12
- package/internal/parse.d.ts.map +0 -1
- package/internal/parse.js +0 -35
- package/internal/parse.js.map +0 -1
- package/internal/parse.mjs +0 -32
- package/internal/parse.mjs.map +0 -1
- package/internal/request-options.d.mts +0 -75
- package/internal/request-options.d.mts.map +0 -1
- package/internal/request-options.d.ts +0 -75
- package/internal/request-options.d.ts.map +0 -1
- package/internal/request-options.js +0 -14
- package/internal/request-options.js.map +0 -1
- package/internal/request-options.mjs +0 -10
- package/internal/request-options.mjs.map +0 -1
- package/internal/shim-types.d.mts +0 -17
- package/internal/shim-types.d.mts.map +0 -1
- package/internal/shim-types.d.ts +0 -17
- package/internal/shim-types.d.ts.map +0 -1
- package/internal/shim-types.js +0 -4
- package/internal/shim-types.js.map +0 -1
- package/internal/shim-types.mjs +0 -3
- package/internal/shim-types.mjs.map +0 -1
- package/internal/shims.d.mts +0 -20
- package/internal/shims.d.mts.map +0 -1
- package/internal/shims.d.ts +0 -20
- package/internal/shims.d.ts.map +0 -1
- package/internal/shims.js +0 -92
- package/internal/shims.js.map +0 -1
- package/internal/shims.mjs +0 -85
- package/internal/shims.mjs.map +0 -1
- package/internal/to-file.d.mts +0 -45
- package/internal/to-file.d.mts.map +0 -1
- package/internal/to-file.d.ts +0 -45
- package/internal/to-file.d.ts.map +0 -1
- package/internal/to-file.js +0 -91
- package/internal/to-file.js.map +0 -1
- package/internal/to-file.mjs +0 -88
- package/internal/to-file.mjs.map +0 -1
- package/internal/tslib.js +0 -81
- package/internal/tslib.mjs +0 -17
- package/internal/types.d.mts +0 -69
- package/internal/types.d.mts.map +0 -1
- package/internal/types.d.ts +0 -69
- package/internal/types.d.ts.map +0 -1
- package/internal/types.js +0 -4
- package/internal/types.js.map +0 -1
- package/internal/types.mjs +0 -3
- package/internal/types.mjs.map +0 -1
- package/internal/uploads.d.mts +0 -42
- package/internal/uploads.d.mts.map +0 -1
- package/internal/uploads.d.ts +0 -42
- package/internal/uploads.d.ts.map +0 -1
- package/internal/uploads.js +0 -141
- package/internal/uploads.js.map +0 -1
- package/internal/uploads.mjs +0 -131
- package/internal/uploads.mjs.map +0 -1
- package/internal/utils/base64.d.mts +0 -3
- package/internal/utils/base64.d.mts.map +0 -1
- package/internal/utils/base64.d.ts +0 -3
- package/internal/utils/base64.d.ts.map +0 -1
- package/internal/utils/base64.js +0 -38
- package/internal/utils/base64.js.map +0 -1
- package/internal/utils/base64.mjs +0 -33
- package/internal/utils/base64.mjs.map +0 -1
- package/internal/utils/bytes.d.mts +0 -4
- package/internal/utils/bytes.d.mts.map +0 -1
- package/internal/utils/bytes.d.ts +0 -4
- package/internal/utils/bytes.d.ts.map +0 -1
- package/internal/utils/bytes.js +0 -31
- package/internal/utils/bytes.js.map +0 -1
- package/internal/utils/bytes.mjs +0 -26
- package/internal/utils/bytes.mjs.map +0 -1
- package/internal/utils/env.d.mts +0 -9
- package/internal/utils/env.d.mts.map +0 -1
- package/internal/utils/env.d.ts +0 -9
- package/internal/utils/env.d.ts.map +0 -1
- package/internal/utils/env.js +0 -22
- package/internal/utils/env.js.map +0 -1
- package/internal/utils/env.mjs +0 -18
- package/internal/utils/env.mjs.map +0 -1
- package/internal/utils/log.d.mts +0 -37
- package/internal/utils/log.d.mts.map +0 -1
- package/internal/utils/log.d.ts +0 -37
- package/internal/utils/log.d.ts.map +0 -1
- package/internal/utils/log.js +0 -85
- package/internal/utils/log.js.map +0 -1
- package/internal/utils/log.mjs +0 -79
- package/internal/utils/log.mjs.map +0 -1
- package/internal/utils/path.d.mts +0 -15
- package/internal/utils/path.d.mts.map +0 -1
- package/internal/utils/path.d.ts +0 -15
- package/internal/utils/path.d.ts.map +0 -1
- package/internal/utils/path.js +0 -79
- package/internal/utils/path.js.map +0 -1
- package/internal/utils/path.mjs +0 -74
- package/internal/utils/path.mjs.map +0 -1
- package/internal/utils/sleep.d.mts +0 -2
- package/internal/utils/sleep.d.mts.map +0 -1
- package/internal/utils/sleep.d.ts +0 -2
- package/internal/utils/sleep.d.ts.map +0 -1
- package/internal/utils/sleep.js +0 -7
- package/internal/utils/sleep.js.map +0 -1
- package/internal/utils/sleep.mjs +0 -3
- package/internal/utils/sleep.mjs.map +0 -1
- package/internal/utils/uuid.d.mts +0 -5
- package/internal/utils/uuid.d.mts.map +0 -1
- package/internal/utils/uuid.d.ts +0 -5
- package/internal/utils/uuid.d.ts.map +0 -1
- package/internal/utils/uuid.js +0 -19
- package/internal/utils/uuid.js.map +0 -1
- package/internal/utils/uuid.mjs +0 -15
- package/internal/utils/uuid.mjs.map +0 -1
- package/internal/utils/values.d.mts +0 -18
- package/internal/utils/values.d.mts.map +0 -1
- package/internal/utils/values.d.ts +0 -18
- package/internal/utils/values.d.ts.map +0 -1
- package/internal/utils/values.js +0 -112
- package/internal/utils/values.js.map +0 -1
- package/internal/utils/values.mjs +0 -94
- package/internal/utils/values.mjs.map +0 -1
- package/internal/utils.d.mts +0 -7
- package/internal/utils.d.mts.map +0 -1
- package/internal/utils.d.ts +0 -7
- package/internal/utils.d.ts.map +0 -1
- package/internal/utils.js +0 -11
- package/internal/utils.js.map +0 -1
- package/internal/utils.mjs +0 -8
- package/internal/utils.mjs.map +0 -1
- package/resource.d.mts +0 -2
- package/resource.d.mts.map +0 -1
- package/resource.d.ts +0 -2
- package/resource.d.ts.map +0 -1
- package/resource.js +0 -6
- package/resource.js.map +0 -1
- package/resource.mjs +0 -2
- package/resource.mjs.map +0 -1
- package/resources/events.d.mts +0 -48
- package/resources/events.d.mts.map +0 -1
- package/resources/events.d.ts +0 -48
- package/resources/events.d.ts.map +0 -1
- package/resources/events.js +0 -16
- package/resources/events.js.map +0 -1
- package/resources/events.mjs +0 -12
- package/resources/events.mjs.map +0 -1
- package/resources/index.d.mts +0 -3
- package/resources/index.d.mts.map +0 -1
- package/resources/index.d.ts +0 -3
- package/resources/index.d.ts.map +0 -1
- package/resources/index.js +0 -9
- package/resources/index.js.map +0 -1
- package/resources/index.mjs +0 -4
- package/resources/index.mjs.map +0 -1
- package/resources/users.d.mts +0 -44
- package/resources/users.d.mts.map +0 -1
- package/resources/users.d.ts +0 -44
- package/resources/users.d.ts.map +0 -1
- package/resources/users.js +0 -16
- package/resources/users.js.map +0 -1
- package/resources/users.mjs +0 -12
- package/resources/users.mjs.map +0 -1
- package/resources.d.mts +0 -2
- package/resources.d.mts.map +0 -1
- package/resources.d.ts +0 -2
- package/resources.d.ts.map +0 -1
- package/resources.js +0 -5
- package/resources.js.map +0 -1
- package/resources.mjs +0 -2
- package/resources.mjs.map +0 -1
- package/src/api-promise.ts +0 -2
- package/src/client.ts +0 -739
- package/src/core/README.md +0 -3
- package/src/core/api-promise.ts +0 -92
- package/src/core/error.ts +0 -130
- package/src/core/resource.ts +0 -11
- package/src/core/uploads.ts +0 -2
- package/src/error.ts +0 -2
- package/src/index.ts +0 -22
- package/src/internal/README.md +0 -3
- package/src/internal/builtin-types.ts +0 -93
- package/src/internal/detect-platform.ts +0 -196
- package/src/internal/errors.ts +0 -33
- package/src/internal/headers.ts +0 -97
- package/src/internal/parse.ts +0 -50
- package/src/internal/request-options.ts +0 -91
- package/src/internal/shim-types.ts +0 -26
- package/src/internal/shims.ts +0 -107
- package/src/internal/to-file.ts +0 -154
- package/src/internal/types.ts +0 -95
- package/src/internal/uploads.ts +0 -187
- package/src/internal/utils/base64.ts +0 -40
- package/src/internal/utils/bytes.ts +0 -32
- package/src/internal/utils/env.ts +0 -18
- package/src/internal/utils/log.ts +0 -126
- package/src/internal/utils/path.ts +0 -88
- package/src/internal/utils/sleep.ts +0 -3
- package/src/internal/utils/uuid.ts +0 -17
- package/src/internal/utils/values.ts +0 -105
- package/src/internal/utils.ts +0 -8
- package/src/lib/.keep +0 -4
- package/src/resource.ts +0 -2
- package/src/resources/events.ts +0 -58
- package/src/resources/index.ts +0 -4
- package/src/resources/users.ts +0 -53
- package/src/resources.ts +0 -1
- package/src/tsconfig.json +0 -11
- package/src/uploads.ts +0 -2
- package/src/version.ts +0 -1
- package/uploads.d.mts +0 -2
- package/uploads.d.mts.map +0 -1
- package/uploads.d.ts +0 -2
- package/uploads.d.ts.map +0 -1
- package/uploads.js +0 -6
- package/uploads.js.map +0 -1
- package/uploads.mjs +0 -2
- package/uploads.mjs.map +0 -1
- package/version.d.mts +0 -2
- package/version.d.mts.map +0 -1
- package/version.d.ts +0 -2
- package/version.d.ts.map +0 -1
- package/version.js +0 -5
- package/version.js.map +0 -1
- package/version.mjs +0 -2
- package/version.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,369 +1,324 @@
|
|
|
1
|
-
# Userplex
|
|
1
|
+
# Userplex
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
This library provides convenient access to the Userplex REST API from server-side TypeScript or JavaScript.
|
|
6
|
-
|
|
7
|
-
The full API of this library can be found in [api.md](api.md).
|
|
8
|
-
|
|
9
|
-
It is generated with [Stainless](https://www.stainless.com/).
|
|
3
|
+
Dead simple analytics SDK. No complex setup. Just track.
|
|
10
4
|
|
|
11
5
|
## Installation
|
|
12
6
|
|
|
13
|
-
```
|
|
7
|
+
```bash
|
|
14
8
|
npm install userplex
|
|
15
9
|
```
|
|
16
10
|
|
|
17
11
|
## Usage
|
|
18
12
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<!-- prettier-ignore -->
|
|
22
|
-
```js
|
|
13
|
+
```javascript
|
|
23
14
|
import Userplex from 'userplex';
|
|
24
15
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
});
|
|
16
|
+
// Initialize with your API key (connects to userplex.vercel.app by default)
|
|
17
|
+
const userplex = new Userplex('upx_your_api_key_here');
|
|
28
18
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
userId: 'REPLACE_ME',
|
|
19
|
+
// Or use your own Userplex instance
|
|
20
|
+
const customUserplex = new Userplex('upx_your_api_key_here', {
|
|
21
|
+
baseUrl: 'https://your-userplex-instance.com'
|
|
33
22
|
});
|
|
34
23
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
This library includes TypeScript definitions for all request params and response fields. You may import and use them like so:
|
|
41
|
-
|
|
42
|
-
<!-- prettier-ignore -->
|
|
43
|
-
```ts
|
|
44
|
-
import Userplex from 'userplex';
|
|
45
|
-
|
|
46
|
-
const client = new Userplex({
|
|
47
|
-
apiKey: process.env['USERPLEX_API_KEY'], // This is the default and can be omitted
|
|
24
|
+
// Track events
|
|
25
|
+
await userplex.track('user123', 'button_clicked', {
|
|
26
|
+
button: 'signup',
|
|
27
|
+
page: '/pricing'
|
|
48
28
|
});
|
|
49
29
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
30
|
+
// Identify users
|
|
31
|
+
await userplex.identify('user123', {
|
|
32
|
+
email: 'user@example.com',
|
|
33
|
+
name: 'John Doe',
|
|
34
|
+
plan: 'premium'
|
|
35
|
+
});
|
|
53
36
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<!-- prettier-ignore -->
|
|
63
|
-
```ts
|
|
64
|
-
const response = await client.users
|
|
65
|
-
.identify({ email: 'REPLACE_ME', name: 'REPLACE_ME', userId: 'REPLACE_ME' })
|
|
66
|
-
.catch(async (err) => {
|
|
67
|
-
if (err instanceof Userplex.APIError) {
|
|
68
|
-
console.log(err.status); // 400
|
|
69
|
-
console.log(err.name); // BadRequestError
|
|
70
|
-
console.log(err.headers); // {server: 'nginx', ...}
|
|
71
|
-
} else {
|
|
72
|
-
throw err;
|
|
73
|
-
}
|
|
74
|
-
});
|
|
37
|
+
// Log LLM conversations
|
|
38
|
+
await userplex.logConversation({
|
|
39
|
+
userId: 'user123',
|
|
40
|
+
messages: [
|
|
41
|
+
{ role: 'user', content: 'How do I reset my password?' },
|
|
42
|
+
{ role: 'assistant', content: 'Click on "Forgot Password" on the login page.' }
|
|
43
|
+
]
|
|
44
|
+
});
|
|
75
45
|
```
|
|
76
46
|
|
|
77
|
-
|
|
47
|
+
That's it. No route setup. No configuration files. Just analytics.
|
|
78
48
|
|
|
79
|
-
|
|
80
|
-
| ----------- | -------------------------- |
|
|
81
|
-
| 400 | `BadRequestError` |
|
|
82
|
-
| 401 | `AuthenticationError` |
|
|
83
|
-
| 403 | `PermissionDeniedError` |
|
|
84
|
-
| 404 | `NotFoundError` |
|
|
85
|
-
| 422 | `UnprocessableEntityError` |
|
|
86
|
-
| 429 | `RateLimitError` |
|
|
87
|
-
| >=500 | `InternalServerError` |
|
|
88
|
-
| N/A | `APIConnectionError` |
|
|
49
|
+
## API
|
|
89
50
|
|
|
90
|
-
###
|
|
51
|
+
### `new Userplex(apiKey, options?)`
|
|
91
52
|
|
|
92
|
-
|
|
93
|
-
Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,
|
|
94
|
-
429 Rate Limit, and >=500 Internal errors will all be retried by default.
|
|
53
|
+
Create a new Userplex instance.
|
|
95
54
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const client = new Userplex({
|
|
102
|
-
maxRetries: 0, // default is 2
|
|
103
|
-
});
|
|
55
|
+
- `apiKey` - Your Userplex API key (required)
|
|
56
|
+
- `options` - Optional configuration
|
|
57
|
+
- `baseUrl` - Your Userplex server URL (default: `https://userplex.vercel.app`)
|
|
58
|
+
- `timeout` - Request timeout in ms (default: `10000`)
|
|
59
|
+
- `debug` - Enable debug logging (default: `false`)
|
|
104
60
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
61
|
+
```javascript
|
|
62
|
+
const userplex = new Userplex('upx_key', {
|
|
63
|
+
baseUrl: 'https://analytics.myapp.com',
|
|
64
|
+
timeout: 30000,
|
|
65
|
+
debug: true
|
|
108
66
|
});
|
|
109
67
|
```
|
|
110
68
|
|
|
111
|
-
###
|
|
69
|
+
### `track(userId, event, properties?)`
|
|
112
70
|
|
|
113
|
-
|
|
71
|
+
Track an event.
|
|
114
72
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// Override per-request:
|
|
123
|
-
await client.users.identify({ email: 'REPLACE_ME', name: 'REPLACE_ME', userId: 'REPLACE_ME' }, {
|
|
124
|
-
timeout: 5 * 1000,
|
|
73
|
+
```javascript
|
|
74
|
+
await userplex.track('user123', 'purchase_completed', {
|
|
75
|
+
amount: 99.99,
|
|
76
|
+
currency: 'USD',
|
|
77
|
+
items: ['product1', 'product2']
|
|
125
78
|
});
|
|
126
79
|
```
|
|
127
80
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
Note that requests which time out will be [retried twice by default](#retries).
|
|
131
|
-
|
|
132
|
-
## Advanced Usage
|
|
133
|
-
|
|
134
|
-
### Accessing raw Response data (e.g., headers)
|
|
135
|
-
|
|
136
|
-
The "raw" `Response` returned by `fetch()` can be accessed through the `.asResponse()` method on the `APIPromise` type that all methods return.
|
|
137
|
-
This method returns as soon as the headers for a successful response are received and does not consume the response body, so you are free to write custom parsing or streaming logic.
|
|
138
|
-
|
|
139
|
-
You can also use the `.withResponse()` method to get the raw `Response` along with the parsed data.
|
|
140
|
-
Unlike `.asResponse()` this method consumes the body, returning once it is parsed.
|
|
141
|
-
|
|
142
|
-
<!-- prettier-ignore -->
|
|
143
|
-
```ts
|
|
144
|
-
const client = new Userplex();
|
|
81
|
+
### `identify(userId, properties?)`
|
|
145
82
|
|
|
146
|
-
|
|
147
|
-
.identify({ email: 'REPLACE_ME', name: 'REPLACE_ME', userId: 'REPLACE_ME' })
|
|
148
|
-
.asResponse();
|
|
149
|
-
console.log(response.headers.get('X-My-Header'));
|
|
150
|
-
console.log(response.statusText); // access the underlying Response object
|
|
83
|
+
Identify a user and set their properties.
|
|
151
84
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
.
|
|
155
|
-
|
|
156
|
-
|
|
85
|
+
```javascript
|
|
86
|
+
await userplex.identify('user123', {
|
|
87
|
+
email: 'john@example.com',
|
|
88
|
+
company: 'Acme Corp',
|
|
89
|
+
role: 'admin',
|
|
90
|
+
plan: 'enterprise'
|
|
91
|
+
});
|
|
157
92
|
```
|
|
158
93
|
|
|
159
|
-
###
|
|
160
|
-
|
|
161
|
-
> [!IMPORTANT]
|
|
162
|
-
> All log messages are intended for debugging only. The format and content of log messages
|
|
163
|
-
> may change between releases.
|
|
164
|
-
|
|
165
|
-
#### Log levels
|
|
166
|
-
|
|
167
|
-
The log level can be configured in two ways:
|
|
94
|
+
### `logConversation(options)`
|
|
168
95
|
|
|
169
|
-
|
|
170
|
-
2. Using the `logLevel` client option (overrides the environment variable if set)
|
|
96
|
+
Log an LLM conversation for observability.
|
|
171
97
|
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
98
|
+
```javascript
|
|
99
|
+
await userplex.logConversation({
|
|
100
|
+
userId: 'user123', // optional
|
|
101
|
+
conversationId: 'conv_abc', // optional
|
|
102
|
+
conversationName: 'Support Chat', // optional
|
|
103
|
+
messages: [ // required
|
|
104
|
+
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
105
|
+
{ role: 'user', content: 'Hello!' },
|
|
106
|
+
{ role: 'assistant', content: 'Hi! How can I help?' }
|
|
107
|
+
]
|
|
177
108
|
});
|
|
178
109
|
```
|
|
179
110
|
|
|
180
|
-
|
|
111
|
+
### `trackBatch(events)`
|
|
181
112
|
|
|
182
|
-
|
|
183
|
-
- `'info'` - Show info messages, warnings, and errors
|
|
184
|
-
- `'warn'` - Show warnings and errors (default)
|
|
185
|
-
- `'error'` - Show only errors
|
|
186
|
-
- `'off'` - Disable all logging
|
|
113
|
+
Track multiple events at once.
|
|
187
114
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
115
|
+
```javascript
|
|
116
|
+
await userplex.trackBatch([
|
|
117
|
+
{ userId: 'user1', event: 'login', properties: { method: 'google' } },
|
|
118
|
+
{ userId: 'user2', event: 'signup', properties: { plan: 'free' } },
|
|
119
|
+
{ userId: 'user3', event: 'upgrade', properties: { plan: 'pro' } }
|
|
120
|
+
]);
|
|
121
|
+
```
|
|
191
122
|
|
|
192
|
-
|
|
123
|
+
## Using in Different Environments
|
|
193
124
|
|
|
194
|
-
|
|
195
|
-
Most logging libraries are supported, including [pino](https://www.npmjs.com/package/pino), [winston](https://www.npmjs.com/package/winston), [bunyan](https://www.npmjs.com/package/bunyan), [consola](https://www.npmjs.com/package/consola), [signale](https://www.npmjs.com/package/signale), and [@std/log](https://jsr.io/@std/log). If your logger doesn't work, please open an issue.
|
|
125
|
+
### Node.js / Server-side
|
|
196
126
|
|
|
197
|
-
|
|
198
|
-
below the configured level will not be sent to your logger.
|
|
127
|
+
Use it directly with your API key:
|
|
199
128
|
|
|
200
|
-
```
|
|
129
|
+
```javascript
|
|
130
|
+
// server.js
|
|
201
131
|
import Userplex from 'userplex';
|
|
202
|
-
import pino from 'pino';
|
|
203
132
|
|
|
204
|
-
const
|
|
133
|
+
const userplex = new Userplex(process.env.USERPLEX_API_KEY);
|
|
205
134
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
135
|
+
app.post('/api/checkout', async (req, res) => {
|
|
136
|
+
// Process payment...
|
|
137
|
+
|
|
138
|
+
// Track purchase
|
|
139
|
+
await userplex.track(req.user.id, 'purchase_completed', {
|
|
140
|
+
amount: req.body.amount
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
res.json({ success: true });
|
|
209
144
|
});
|
|
210
145
|
```
|
|
211
146
|
|
|
212
|
-
###
|
|
147
|
+
### Next.js API Routes
|
|
213
148
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
#### Undocumented endpoints
|
|
218
|
-
|
|
219
|
-
To make requests to undocumented endpoints, you can use `client.get`, `client.post`, and other HTTP verbs.
|
|
220
|
-
Options on the client, such as retries, will be respected when making these requests.
|
|
221
|
-
|
|
222
|
-
```ts
|
|
223
|
-
await client.post('/some/path', {
|
|
224
|
-
body: { some_prop: 'foo' },
|
|
225
|
-
query: { some_query_arg: 'bar' },
|
|
226
|
-
});
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
#### Undocumented request params
|
|
149
|
+
```javascript
|
|
150
|
+
// app/api/checkout/route.js
|
|
151
|
+
import Userplex from 'userplex';
|
|
230
152
|
|
|
231
|
-
|
|
232
|
-
parameter. This library doesn't validate at runtime that the request matches the type, so any extra values you
|
|
233
|
-
send will be sent as-is.
|
|
153
|
+
const userplex = new Userplex(process.env.USERPLEX_API_KEY);
|
|
234
154
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
});
|
|
155
|
+
export async function POST(request) {
|
|
156
|
+
const body = await request.json();
|
|
157
|
+
|
|
158
|
+
await userplex.track(body.userId, 'purchase_completed', {
|
|
159
|
+
amount: body.amount
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
return Response.json({ success: true });
|
|
163
|
+
}
|
|
241
164
|
```
|
|
242
165
|
|
|
243
|
-
|
|
244
|
-
extra param in the body.
|
|
166
|
+
### Client-side (Browser)
|
|
245
167
|
|
|
246
|
-
|
|
247
|
-
options.
|
|
168
|
+
⚠️ **Never put your API key in client-side code!**
|
|
248
169
|
|
|
249
|
-
|
|
170
|
+
For client-side tracking, you have two options:
|
|
250
171
|
|
|
251
|
-
|
|
252
|
-
the response object, or cast the response object to the requisite type. Like the request params, we do not
|
|
253
|
-
validate or strip extra properties from the response from the API.
|
|
172
|
+
#### Option 1: Create a simple API endpoint
|
|
254
173
|
|
|
255
|
-
|
|
174
|
+
```javascript
|
|
175
|
+
// app/api/track/route.js (Next.js example)
|
|
176
|
+
import Userplex from 'userplex';
|
|
256
177
|
|
|
257
|
-
|
|
178
|
+
const userplex = new Userplex(process.env.USERPLEX_API_KEY);
|
|
258
179
|
|
|
259
|
-
|
|
180
|
+
export async function POST(request) {
|
|
181
|
+
const { userId, event, properties } = await request.json();
|
|
182
|
+
await userplex.track(userId, event, properties);
|
|
183
|
+
return Response.json({ success: true });
|
|
184
|
+
}
|
|
185
|
+
```
|
|
260
186
|
|
|
261
|
-
|
|
262
|
-
|
|
187
|
+
Then in your client:
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
// In your React component
|
|
191
|
+
async function trackEvent(event, properties) {
|
|
192
|
+
await fetch('/api/track', {
|
|
193
|
+
method: 'POST',
|
|
194
|
+
headers: { 'Content-Type': 'application/json' },
|
|
195
|
+
body: JSON.stringify({
|
|
196
|
+
userId: currentUser.id,
|
|
197
|
+
event,
|
|
198
|
+
properties
|
|
199
|
+
})
|
|
200
|
+
});
|
|
201
|
+
}
|
|
263
202
|
|
|
264
|
-
|
|
203
|
+
// Use it
|
|
204
|
+
trackEvent('button_clicked', { button: 'signup' });
|
|
265
205
|
```
|
|
266
206
|
|
|
267
|
-
|
|
207
|
+
#### Option 2: Use environment-specific initialization
|
|
268
208
|
|
|
269
|
-
```
|
|
209
|
+
```javascript
|
|
210
|
+
// lib/analytics.js
|
|
270
211
|
import Userplex from 'userplex';
|
|
271
|
-
import fetch from 'my-fetch';
|
|
272
212
|
|
|
273
|
-
|
|
213
|
+
// Server-side: use real instance
|
|
214
|
+
// Client-side: use a proxy that calls your API
|
|
215
|
+
const userplex = typeof window === 'undefined'
|
|
216
|
+
? new Userplex(process.env.USERPLEX_API_KEY)
|
|
217
|
+
: {
|
|
218
|
+
track: async (userId, event, properties) => {
|
|
219
|
+
await fetch('/api/track', {
|
|
220
|
+
method: 'POST',
|
|
221
|
+
headers: { 'Content-Type': 'application/json' },
|
|
222
|
+
body: JSON.stringify({ userId, event, properties })
|
|
223
|
+
});
|
|
224
|
+
},
|
|
225
|
+
identify: async (userId, properties) => {
|
|
226
|
+
await fetch('/api/identify', {
|
|
227
|
+
method: 'POST',
|
|
228
|
+
headers: { 'Content-Type': 'application/json' },
|
|
229
|
+
body: JSON.stringify({ userId, properties })
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
export default userplex;
|
|
274
235
|
```
|
|
275
236
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
});
|
|
237
|
+
## Error Handling
|
|
238
|
+
|
|
239
|
+
```javascript
|
|
240
|
+
try {
|
|
241
|
+
await userplex.track('user123', 'event');
|
|
242
|
+
} catch (error) {
|
|
243
|
+
if (error.name === 'UserplexError') {
|
|
244
|
+
console.error('Analytics error:', error.message);
|
|
245
|
+
console.error('Status code:', error.statusCode);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
288
248
|
```
|
|
289
249
|
|
|
290
|
-
|
|
250
|
+
## TypeScript
|
|
291
251
|
|
|
292
|
-
|
|
293
|
-
options to requests:
|
|
252
|
+
Full TypeScript support included:
|
|
294
253
|
|
|
295
|
-
|
|
254
|
+
```typescript
|
|
255
|
+
import Userplex, { ConversationMessage, UserplexError } from 'userplex';
|
|
296
256
|
|
|
297
|
-
|
|
298
|
-
import Userplex from 'userplex';
|
|
299
|
-
import * as undici from 'undici';
|
|
257
|
+
const userplex = new Userplex('upx_key');
|
|
300
258
|
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
259
|
+
const messages: ConversationMessage[] = [
|
|
260
|
+
{ role: 'user', content: 'Hello' },
|
|
261
|
+
{ role: 'assistant', content: 'Hi!' }
|
|
262
|
+
];
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
await userplex.logConversation({ messages });
|
|
266
|
+
} catch (error) {
|
|
267
|
+
if (error instanceof UserplexError) {
|
|
268
|
+
// Typed error handling
|
|
269
|
+
}
|
|
270
|
+
}
|
|
307
271
|
```
|
|
308
272
|
|
|
309
|
-
|
|
273
|
+
## Examples
|
|
310
274
|
|
|
311
|
-
|
|
312
|
-
import Userplex from 'userplex';
|
|
275
|
+
### E-commerce Tracking
|
|
313
276
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
});
|
|
277
|
+
```javascript
|
|
278
|
+
// Track the full customer journey
|
|
279
|
+
await userplex.track(userId, 'product_viewed', { productId, price });
|
|
280
|
+
await userplex.track(userId, 'add_to_cart', { productId, quantity });
|
|
281
|
+
await userplex.track(userId, 'checkout_started', { cartValue });
|
|
282
|
+
await userplex.track(userId, 'purchase_completed', { orderId, amount });
|
|
319
283
|
```
|
|
320
284
|
|
|
321
|
-
|
|
285
|
+
### SaaS Application
|
|
322
286
|
|
|
323
|
-
```
|
|
324
|
-
|
|
287
|
+
```javascript
|
|
288
|
+
// Track feature usage
|
|
289
|
+
await userplex.track(userId, 'feature_used', {
|
|
290
|
+
feature: 'csv_export',
|
|
291
|
+
recordCount: 1000
|
|
292
|
+
});
|
|
325
293
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
},
|
|
294
|
+
// Track subscription changes
|
|
295
|
+
await userplex.identify(userId, {
|
|
296
|
+
plan: 'enterprise',
|
|
297
|
+
mrr: 499
|
|
331
298
|
});
|
|
332
299
|
```
|
|
333
300
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
## Semantic versioning
|
|
301
|
+
### AI Application
|
|
337
302
|
|
|
338
|
-
|
|
303
|
+
```javascript
|
|
304
|
+
// Log every conversation for debugging and analytics
|
|
305
|
+
const messages = [];
|
|
339
306
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
3. Changes that we do not expect to impact the vast majority of users in practice.
|
|
307
|
+
// User asks a question
|
|
308
|
+
messages.push({ role: 'user', content: userInput });
|
|
343
309
|
|
|
344
|
-
|
|
310
|
+
// Get AI response
|
|
311
|
+
const response = await callOpenAI(messages);
|
|
312
|
+
messages.push({ role: 'assistant', content: response });
|
|
345
313
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
- Web browsers (Up-to-date Chrome, Firefox, Safari, Edge, and more)
|
|
355
|
-
- Node.js 20 LTS or later ([non-EOL](https://endoflife.date/nodejs)) versions.
|
|
356
|
-
- Deno v1.28.0 or higher.
|
|
357
|
-
- Bun 1.0 or later.
|
|
358
|
-
- Cloudflare Workers.
|
|
359
|
-
- Vercel Edge Runtime.
|
|
360
|
-
- Jest 28 or greater with the `"node"` environment (`"jsdom"` is not supported at this time).
|
|
361
|
-
- Nitro v2.6 or greater.
|
|
362
|
-
|
|
363
|
-
Note that React Native is not supported at this time.
|
|
364
|
-
|
|
365
|
-
If you are interested in other runtime environments, please open or upvote an issue on GitHub.
|
|
314
|
+
// Log to Userplex
|
|
315
|
+
await userplex.logConversation({
|
|
316
|
+
userId: currentUser.id,
|
|
317
|
+
conversationName: 'Chat Session',
|
|
318
|
+
messages
|
|
319
|
+
});
|
|
320
|
+
```
|
|
366
321
|
|
|
367
|
-
##
|
|
322
|
+
## License
|
|
368
323
|
|
|
369
|
-
|
|
324
|
+
MIT
|