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.
Files changed (331) hide show
  1. package/LICENSE +21 -201
  2. package/README.md +228 -273
  3. package/dist/index.d.mts +78 -0
  4. package/dist/index.d.ts +78 -0
  5. package/dist/index.js +132 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/index.mjs +126 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/package.json +27 -130
  10. package/CHANGELOG.md +0 -11
  11. package/api-promise.d.mts +0 -2
  12. package/api-promise.d.mts.map +0 -1
  13. package/api-promise.d.ts +0 -2
  14. package/api-promise.d.ts.map +0 -1
  15. package/api-promise.js +0 -6
  16. package/api-promise.js.map +0 -1
  17. package/api-promise.mjs +0 -2
  18. package/api-promise.mjs.map +0 -1
  19. package/client.d.mts +0 -183
  20. package/client.d.mts.map +0 -1
  21. package/client.d.ts +0 -183
  22. package/client.d.ts.map +0 -1
  23. package/client.js +0 -464
  24. package/client.js.map +0 -1
  25. package/client.mjs +0 -460
  26. package/client.mjs.map +0 -1
  27. package/core/api-promise.d.mts +0 -46
  28. package/core/api-promise.d.mts.map +0 -1
  29. package/core/api-promise.d.ts +0 -46
  30. package/core/api-promise.d.ts.map +0 -1
  31. package/core/api-promise.js +0 -74
  32. package/core/api-promise.js.map +0 -1
  33. package/core/api-promise.mjs +0 -70
  34. package/core/api-promise.mjs.map +0 -1
  35. package/core/error.d.mts +0 -46
  36. package/core/error.d.mts.map +0 -1
  37. package/core/error.d.ts +0 -46
  38. package/core/error.d.ts.map +0 -1
  39. package/core/error.js +0 -113
  40. package/core/error.js.map +0 -1
  41. package/core/error.mjs +0 -97
  42. package/core/error.mjs.map +0 -1
  43. package/core/resource.d.mts +0 -6
  44. package/core/resource.d.mts.map +0 -1
  45. package/core/resource.d.ts +0 -6
  46. package/core/resource.d.ts.map +0 -1
  47. package/core/resource.js +0 -11
  48. package/core/resource.js.map +0 -1
  49. package/core/resource.mjs +0 -7
  50. package/core/resource.mjs.map +0 -1
  51. package/core/uploads.d.mts +0 -3
  52. package/core/uploads.d.mts.map +0 -1
  53. package/core/uploads.d.ts +0 -3
  54. package/core/uploads.d.ts.map +0 -1
  55. package/core/uploads.js +0 -6
  56. package/core/uploads.js.map +0 -1
  57. package/core/uploads.mjs +0 -2
  58. package/core/uploads.mjs.map +0 -1
  59. package/error.d.mts +0 -2
  60. package/error.d.mts.map +0 -1
  61. package/error.d.ts +0 -2
  62. package/error.d.ts.map +0 -1
  63. package/error.js +0 -6
  64. package/error.js.map +0 -1
  65. package/error.mjs +0 -2
  66. package/error.mjs.map +0 -1
  67. package/index.d.mts +0 -6
  68. package/index.d.mts.map +0 -1
  69. package/index.d.ts +0 -6
  70. package/index.d.ts.map +0 -1
  71. package/index.js +0 -30
  72. package/index.js.map +0 -1
  73. package/index.mjs +0 -7
  74. package/index.mjs.map +0 -1
  75. package/internal/builtin-types.d.mts +0 -73
  76. package/internal/builtin-types.d.mts.map +0 -1
  77. package/internal/builtin-types.d.ts +0 -73
  78. package/internal/builtin-types.d.ts.map +0 -1
  79. package/internal/builtin-types.js +0 -4
  80. package/internal/builtin-types.js.map +0 -1
  81. package/internal/builtin-types.mjs +0 -3
  82. package/internal/builtin-types.mjs.map +0 -1
  83. package/internal/detect-platform.d.mts +0 -15
  84. package/internal/detect-platform.d.mts.map +0 -1
  85. package/internal/detect-platform.d.ts +0 -15
  86. package/internal/detect-platform.d.ts.map +0 -1
  87. package/internal/detect-platform.js +0 -162
  88. package/internal/detect-platform.js.map +0 -1
  89. package/internal/detect-platform.mjs +0 -157
  90. package/internal/detect-platform.mjs.map +0 -1
  91. package/internal/errors.d.mts +0 -3
  92. package/internal/errors.d.mts.map +0 -1
  93. package/internal/errors.d.ts +0 -3
  94. package/internal/errors.d.ts.map +0 -1
  95. package/internal/errors.js +0 -41
  96. package/internal/errors.js.map +0 -1
  97. package/internal/errors.mjs +0 -36
  98. package/internal/errors.mjs.map +0 -1
  99. package/internal/headers.d.mts +0 -20
  100. package/internal/headers.d.mts.map +0 -1
  101. package/internal/headers.d.ts +0 -20
  102. package/internal/headers.d.ts.map +0 -1
  103. package/internal/headers.js +0 -79
  104. package/internal/headers.js.map +0 -1
  105. package/internal/headers.mjs +0 -74
  106. package/internal/headers.mjs.map +0 -1
  107. package/internal/parse.d.mts +0 -12
  108. package/internal/parse.d.mts.map +0 -1
  109. package/internal/parse.d.ts +0 -12
  110. package/internal/parse.d.ts.map +0 -1
  111. package/internal/parse.js +0 -35
  112. package/internal/parse.js.map +0 -1
  113. package/internal/parse.mjs +0 -32
  114. package/internal/parse.mjs.map +0 -1
  115. package/internal/request-options.d.mts +0 -75
  116. package/internal/request-options.d.mts.map +0 -1
  117. package/internal/request-options.d.ts +0 -75
  118. package/internal/request-options.d.ts.map +0 -1
  119. package/internal/request-options.js +0 -14
  120. package/internal/request-options.js.map +0 -1
  121. package/internal/request-options.mjs +0 -10
  122. package/internal/request-options.mjs.map +0 -1
  123. package/internal/shim-types.d.mts +0 -17
  124. package/internal/shim-types.d.mts.map +0 -1
  125. package/internal/shim-types.d.ts +0 -17
  126. package/internal/shim-types.d.ts.map +0 -1
  127. package/internal/shim-types.js +0 -4
  128. package/internal/shim-types.js.map +0 -1
  129. package/internal/shim-types.mjs +0 -3
  130. package/internal/shim-types.mjs.map +0 -1
  131. package/internal/shims.d.mts +0 -20
  132. package/internal/shims.d.mts.map +0 -1
  133. package/internal/shims.d.ts +0 -20
  134. package/internal/shims.d.ts.map +0 -1
  135. package/internal/shims.js +0 -92
  136. package/internal/shims.js.map +0 -1
  137. package/internal/shims.mjs +0 -85
  138. package/internal/shims.mjs.map +0 -1
  139. package/internal/to-file.d.mts +0 -45
  140. package/internal/to-file.d.mts.map +0 -1
  141. package/internal/to-file.d.ts +0 -45
  142. package/internal/to-file.d.ts.map +0 -1
  143. package/internal/to-file.js +0 -91
  144. package/internal/to-file.js.map +0 -1
  145. package/internal/to-file.mjs +0 -88
  146. package/internal/to-file.mjs.map +0 -1
  147. package/internal/tslib.js +0 -81
  148. package/internal/tslib.mjs +0 -17
  149. package/internal/types.d.mts +0 -69
  150. package/internal/types.d.mts.map +0 -1
  151. package/internal/types.d.ts +0 -69
  152. package/internal/types.d.ts.map +0 -1
  153. package/internal/types.js +0 -4
  154. package/internal/types.js.map +0 -1
  155. package/internal/types.mjs +0 -3
  156. package/internal/types.mjs.map +0 -1
  157. package/internal/uploads.d.mts +0 -42
  158. package/internal/uploads.d.mts.map +0 -1
  159. package/internal/uploads.d.ts +0 -42
  160. package/internal/uploads.d.ts.map +0 -1
  161. package/internal/uploads.js +0 -141
  162. package/internal/uploads.js.map +0 -1
  163. package/internal/uploads.mjs +0 -131
  164. package/internal/uploads.mjs.map +0 -1
  165. package/internal/utils/base64.d.mts +0 -3
  166. package/internal/utils/base64.d.mts.map +0 -1
  167. package/internal/utils/base64.d.ts +0 -3
  168. package/internal/utils/base64.d.ts.map +0 -1
  169. package/internal/utils/base64.js +0 -38
  170. package/internal/utils/base64.js.map +0 -1
  171. package/internal/utils/base64.mjs +0 -33
  172. package/internal/utils/base64.mjs.map +0 -1
  173. package/internal/utils/bytes.d.mts +0 -4
  174. package/internal/utils/bytes.d.mts.map +0 -1
  175. package/internal/utils/bytes.d.ts +0 -4
  176. package/internal/utils/bytes.d.ts.map +0 -1
  177. package/internal/utils/bytes.js +0 -31
  178. package/internal/utils/bytes.js.map +0 -1
  179. package/internal/utils/bytes.mjs +0 -26
  180. package/internal/utils/bytes.mjs.map +0 -1
  181. package/internal/utils/env.d.mts +0 -9
  182. package/internal/utils/env.d.mts.map +0 -1
  183. package/internal/utils/env.d.ts +0 -9
  184. package/internal/utils/env.d.ts.map +0 -1
  185. package/internal/utils/env.js +0 -22
  186. package/internal/utils/env.js.map +0 -1
  187. package/internal/utils/env.mjs +0 -18
  188. package/internal/utils/env.mjs.map +0 -1
  189. package/internal/utils/log.d.mts +0 -37
  190. package/internal/utils/log.d.mts.map +0 -1
  191. package/internal/utils/log.d.ts +0 -37
  192. package/internal/utils/log.d.ts.map +0 -1
  193. package/internal/utils/log.js +0 -85
  194. package/internal/utils/log.js.map +0 -1
  195. package/internal/utils/log.mjs +0 -79
  196. package/internal/utils/log.mjs.map +0 -1
  197. package/internal/utils/path.d.mts +0 -15
  198. package/internal/utils/path.d.mts.map +0 -1
  199. package/internal/utils/path.d.ts +0 -15
  200. package/internal/utils/path.d.ts.map +0 -1
  201. package/internal/utils/path.js +0 -79
  202. package/internal/utils/path.js.map +0 -1
  203. package/internal/utils/path.mjs +0 -74
  204. package/internal/utils/path.mjs.map +0 -1
  205. package/internal/utils/sleep.d.mts +0 -2
  206. package/internal/utils/sleep.d.mts.map +0 -1
  207. package/internal/utils/sleep.d.ts +0 -2
  208. package/internal/utils/sleep.d.ts.map +0 -1
  209. package/internal/utils/sleep.js +0 -7
  210. package/internal/utils/sleep.js.map +0 -1
  211. package/internal/utils/sleep.mjs +0 -3
  212. package/internal/utils/sleep.mjs.map +0 -1
  213. package/internal/utils/uuid.d.mts +0 -5
  214. package/internal/utils/uuid.d.mts.map +0 -1
  215. package/internal/utils/uuid.d.ts +0 -5
  216. package/internal/utils/uuid.d.ts.map +0 -1
  217. package/internal/utils/uuid.js +0 -19
  218. package/internal/utils/uuid.js.map +0 -1
  219. package/internal/utils/uuid.mjs +0 -15
  220. package/internal/utils/uuid.mjs.map +0 -1
  221. package/internal/utils/values.d.mts +0 -18
  222. package/internal/utils/values.d.mts.map +0 -1
  223. package/internal/utils/values.d.ts +0 -18
  224. package/internal/utils/values.d.ts.map +0 -1
  225. package/internal/utils/values.js +0 -112
  226. package/internal/utils/values.js.map +0 -1
  227. package/internal/utils/values.mjs +0 -94
  228. package/internal/utils/values.mjs.map +0 -1
  229. package/internal/utils.d.mts +0 -7
  230. package/internal/utils.d.mts.map +0 -1
  231. package/internal/utils.d.ts +0 -7
  232. package/internal/utils.d.ts.map +0 -1
  233. package/internal/utils.js +0 -11
  234. package/internal/utils.js.map +0 -1
  235. package/internal/utils.mjs +0 -8
  236. package/internal/utils.mjs.map +0 -1
  237. package/resource.d.mts +0 -2
  238. package/resource.d.mts.map +0 -1
  239. package/resource.d.ts +0 -2
  240. package/resource.d.ts.map +0 -1
  241. package/resource.js +0 -6
  242. package/resource.js.map +0 -1
  243. package/resource.mjs +0 -2
  244. package/resource.mjs.map +0 -1
  245. package/resources/events.d.mts +0 -48
  246. package/resources/events.d.mts.map +0 -1
  247. package/resources/events.d.ts +0 -48
  248. package/resources/events.d.ts.map +0 -1
  249. package/resources/events.js +0 -16
  250. package/resources/events.js.map +0 -1
  251. package/resources/events.mjs +0 -12
  252. package/resources/events.mjs.map +0 -1
  253. package/resources/index.d.mts +0 -3
  254. package/resources/index.d.mts.map +0 -1
  255. package/resources/index.d.ts +0 -3
  256. package/resources/index.d.ts.map +0 -1
  257. package/resources/index.js +0 -9
  258. package/resources/index.js.map +0 -1
  259. package/resources/index.mjs +0 -4
  260. package/resources/index.mjs.map +0 -1
  261. package/resources/users.d.mts +0 -44
  262. package/resources/users.d.mts.map +0 -1
  263. package/resources/users.d.ts +0 -44
  264. package/resources/users.d.ts.map +0 -1
  265. package/resources/users.js +0 -16
  266. package/resources/users.js.map +0 -1
  267. package/resources/users.mjs +0 -12
  268. package/resources/users.mjs.map +0 -1
  269. package/resources.d.mts +0 -2
  270. package/resources.d.mts.map +0 -1
  271. package/resources.d.ts +0 -2
  272. package/resources.d.ts.map +0 -1
  273. package/resources.js +0 -5
  274. package/resources.js.map +0 -1
  275. package/resources.mjs +0 -2
  276. package/resources.mjs.map +0 -1
  277. package/src/api-promise.ts +0 -2
  278. package/src/client.ts +0 -739
  279. package/src/core/README.md +0 -3
  280. package/src/core/api-promise.ts +0 -92
  281. package/src/core/error.ts +0 -130
  282. package/src/core/resource.ts +0 -11
  283. package/src/core/uploads.ts +0 -2
  284. package/src/error.ts +0 -2
  285. package/src/index.ts +0 -22
  286. package/src/internal/README.md +0 -3
  287. package/src/internal/builtin-types.ts +0 -93
  288. package/src/internal/detect-platform.ts +0 -196
  289. package/src/internal/errors.ts +0 -33
  290. package/src/internal/headers.ts +0 -97
  291. package/src/internal/parse.ts +0 -50
  292. package/src/internal/request-options.ts +0 -91
  293. package/src/internal/shim-types.ts +0 -26
  294. package/src/internal/shims.ts +0 -107
  295. package/src/internal/to-file.ts +0 -154
  296. package/src/internal/types.ts +0 -95
  297. package/src/internal/uploads.ts +0 -187
  298. package/src/internal/utils/base64.ts +0 -40
  299. package/src/internal/utils/bytes.ts +0 -32
  300. package/src/internal/utils/env.ts +0 -18
  301. package/src/internal/utils/log.ts +0 -126
  302. package/src/internal/utils/path.ts +0 -88
  303. package/src/internal/utils/sleep.ts +0 -3
  304. package/src/internal/utils/uuid.ts +0 -17
  305. package/src/internal/utils/values.ts +0 -105
  306. package/src/internal/utils.ts +0 -8
  307. package/src/lib/.keep +0 -4
  308. package/src/resource.ts +0 -2
  309. package/src/resources/events.ts +0 -58
  310. package/src/resources/index.ts +0 -4
  311. package/src/resources/users.ts +0 -53
  312. package/src/resources.ts +0 -1
  313. package/src/tsconfig.json +0 -11
  314. package/src/uploads.ts +0 -2
  315. package/src/version.ts +0 -1
  316. package/uploads.d.mts +0 -2
  317. package/uploads.d.mts.map +0 -1
  318. package/uploads.d.ts +0 -2
  319. package/uploads.d.ts.map +0 -1
  320. package/uploads.js +0 -6
  321. package/uploads.js.map +0 -1
  322. package/uploads.mjs +0 -2
  323. package/uploads.mjs.map +0 -1
  324. package/version.d.mts +0 -2
  325. package/version.d.mts.map +0 -1
  326. package/version.d.ts +0 -2
  327. package/version.d.ts.map +0 -1
  328. package/version.js +0 -5
  329. package/version.js.map +0 -1
  330. package/version.mjs +0 -2
  331. package/version.mjs.map +0 -1
package/README.md CHANGED
@@ -1,369 +1,324 @@
1
- # Userplex TypeScript API Library
1
+ # Userplex
2
2
 
3
- [![NPM version](<https://img.shields.io/npm/v/userplex.svg?label=npm%20(stable)>)](https://npmjs.org/package/userplex) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/userplex)
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
- ```sh
7
+ ```bash
14
8
  npm install userplex
15
9
  ```
16
10
 
17
11
  ## Usage
18
12
 
19
- The full API of this library can be found in [api.md](api.md).
20
-
21
- <!-- prettier-ignore -->
22
- ```js
13
+ ```javascript
23
14
  import Userplex from 'userplex';
24
15
 
25
- const client = new Userplex({
26
- apiKey: process.env['USERPLEX_API_KEY'], // This is the default and can be omitted
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
- const response = await client.users.identify({
30
- email: 'REPLACE_ME',
31
- name: 'REPLACE_ME',
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
- console.log(response.success);
36
- ```
37
-
38
- ### Request & Response types
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
- const params: Userplex.UserIdentifyParams = { email: 'REPLACE_ME', name: 'REPLACE_ME', userId: 'REPLACE_ME' };
51
- const response: Userplex.UserIdentifyResponse = await client.users.identify(params);
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
- Documentation for each method, request param, and response field are available in docstrings and will appear on hover in most modern editors.
55
-
56
- ## Handling errors
57
-
58
- When the library is unable to connect to the API,
59
- or if the API returns a non-success status code (i.e., 4xx or 5xx response),
60
- a subclass of `APIError` will be thrown:
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
- Error codes are as follows:
47
+ That's it. No route setup. No configuration files. Just analytics.
78
48
 
79
- | Status Code | Error Type |
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
- ### Retries
51
+ ### `new Userplex(apiKey, options?)`
91
52
 
92
- Certain errors will be automatically retried 2 times by default, with a short exponential backoff.
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
- You can use the `maxRetries` option to configure or disable this:
97
-
98
- <!-- prettier-ignore -->
99
- ```js
100
- // Configure the default for all requests:
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
- // Or, configure per-request:
106
- await client.users.identify({ email: 'REPLACE_ME', name: 'REPLACE_ME', userId: 'REPLACE_ME' }, {
107
- maxRetries: 5,
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
- ### Timeouts
69
+ ### `track(userId, event, properties?)`
112
70
 
113
- Requests time out after 1 minute by default. You can configure this with a `timeout` option:
71
+ Track an event.
114
72
 
115
- <!-- prettier-ignore -->
116
- ```ts
117
- // Configure the default for all requests:
118
- const client = new Userplex({
119
- timeout: 20 * 1000, // 20 seconds (default is 1 minute)
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
- On timeout, an `APIConnectionTimeoutError` is thrown.
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
- const response = await client.users
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
- const { data: response, response: raw } = await client.users
153
- .identify({ email: 'REPLACE_ME', name: 'REPLACE_ME', userId: 'REPLACE_ME' })
154
- .withResponse();
155
- console.log(raw.headers.get('X-My-Header'));
156
- console.log(response.success);
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
- ### Logging
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
- 1. Via the `USERPLEX_LOG` environment variable
170
- 2. Using the `logLevel` client option (overrides the environment variable if set)
96
+ Log an LLM conversation for observability.
171
97
 
172
- ```ts
173
- import Userplex from 'userplex';
174
-
175
- const client = new Userplex({
176
- logLevel: 'debug', // Show all log messages
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
- Available log levels, from most to least verbose:
111
+ ### `trackBatch(events)`
181
112
 
182
- - `'debug'` - Show debug messages, info, warnings, and errors
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
- At the `'debug'` level, all HTTP requests and responses are logged, including headers and bodies.
189
- Some authentication-related headers are redacted, but sensitive data in request and response bodies
190
- may still be visible.
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
- #### Custom logger
123
+ ## Using in Different Environments
193
124
 
194
- By default, this library logs to `globalThis.console`. You can also provide a custom logger.
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
- When providing a custom logger, the `logLevel` option still controls which messages are emitted, messages
198
- below the configured level will not be sent to your logger.
127
+ Use it directly with your API key:
199
128
 
200
- ```ts
129
+ ```javascript
130
+ // server.js
201
131
  import Userplex from 'userplex';
202
- import pino from 'pino';
203
132
 
204
- const logger = pino();
133
+ const userplex = new Userplex(process.env.USERPLEX_API_KEY);
205
134
 
206
- const client = new Userplex({
207
- logger: logger.child({ name: 'Userplex' }),
208
- logLevel: 'debug', // Send all messages to pino, allowing it to filter
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
- ### Making custom/undocumented requests
147
+ ### Next.js API Routes
213
148
 
214
- This library is typed for convenient access to the documented API. If you need to access undocumented
215
- endpoints, params, or response properties, the library can still be used.
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
- To make requests using undocumented parameters, you may use `// @ts-expect-error` on the undocumented
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
- ```ts
236
- client.users.identify({
237
- // ...
238
- // @ts-expect-error baz is not yet public
239
- baz: 'undocumented option',
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
- For requests with the `GET` verb, any extra params will be in the query, all other requests will send the
244
- extra param in the body.
166
+ ### Client-side (Browser)
245
167
 
246
- If you want to explicitly send an extra argument, you can do so with the `query`, `body`, and `headers` request
247
- options.
168
+ ⚠️ **Never put your API key in client-side code!**
248
169
 
249
- #### Undocumented response properties
170
+ For client-side tracking, you have two options:
250
171
 
251
- To access undocumented response properties, you may access the response object with `// @ts-expect-error` on
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
- ### Customizing the fetch client
174
+ ```javascript
175
+ // app/api/track/route.js (Next.js example)
176
+ import Userplex from 'userplex';
256
177
 
257
- By default, this library expects a global `fetch` function is defined.
178
+ const userplex = new Userplex(process.env.USERPLEX_API_KEY);
258
179
 
259
- If you want to use a different `fetch` function, you can either polyfill the global:
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
- ```ts
262
- import fetch from 'my-fetch';
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
- globalThis.fetch = fetch;
203
+ // Use it
204
+ trackEvent('button_clicked', { button: 'signup' });
265
205
  ```
266
206
 
267
- Or pass it to the client:
207
+ #### Option 2: Use environment-specific initialization
268
208
 
269
- ```ts
209
+ ```javascript
210
+ // lib/analytics.js
270
211
  import Userplex from 'userplex';
271
- import fetch from 'my-fetch';
272
212
 
273
- const client = new Userplex({ fetch });
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
- ### Fetch options
277
-
278
- If you want to set custom `fetch` options without overriding the `fetch` function, you can provide a `fetchOptions` object when instantiating the client or making a request. (Request-specific options override client options.)
279
-
280
- ```ts
281
- import Userplex from 'userplex';
282
-
283
- const client = new Userplex({
284
- fetchOptions: {
285
- // `RequestInit` options
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
- #### Configuring proxies
250
+ ## TypeScript
291
251
 
292
- To modify proxy behavior, you can provide custom `fetchOptions` that add runtime-specific proxy
293
- options to requests:
252
+ Full TypeScript support included:
294
253
 
295
- <img src="https://raw.githubusercontent.com/stainless-api/sdk-assets/refs/heads/main/node.svg" align="top" width="18" height="21"> **Node** <sup>[[docs](https://github.com/nodejs/undici/blob/main/docs/docs/api/ProxyAgent.md#example---proxyagent-with-fetch)]</sup>
254
+ ```typescript
255
+ import Userplex, { ConversationMessage, UserplexError } from 'userplex';
296
256
 
297
- ```ts
298
- import Userplex from 'userplex';
299
- import * as undici from 'undici';
257
+ const userplex = new Userplex('upx_key');
300
258
 
301
- const proxyAgent = new undici.ProxyAgent('http://localhost:8888');
302
- const client = new Userplex({
303
- fetchOptions: {
304
- dispatcher: proxyAgent,
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
- <img src="https://raw.githubusercontent.com/stainless-api/sdk-assets/refs/heads/main/bun.svg" align="top" width="18" height="21"> **Bun** <sup>[[docs](https://bun.sh/guides/http/proxy)]</sup>
273
+ ## Examples
310
274
 
311
- ```ts
312
- import Userplex from 'userplex';
275
+ ### E-commerce Tracking
313
276
 
314
- const client = new Userplex({
315
- fetchOptions: {
316
- proxy: 'http://localhost:8888',
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
- <img src="https://raw.githubusercontent.com/stainless-api/sdk-assets/refs/heads/main/deno.svg" align="top" width="18" height="21"> **Deno** <sup>[[docs](https://docs.deno.com/api/deno/~/Deno.createHttpClient)]</sup>
285
+ ### SaaS Application
322
286
 
323
- ```ts
324
- import Userplex from 'npm:userplex';
287
+ ```javascript
288
+ // Track feature usage
289
+ await userplex.track(userId, 'feature_used', {
290
+ feature: 'csv_export',
291
+ recordCount: 1000
292
+ });
325
293
 
326
- const httpClient = Deno.createHttpClient({ proxy: { url: 'http://localhost:8888' } });
327
- const client = new Userplex({
328
- fetchOptions: {
329
- client: httpClient,
330
- },
294
+ // Track subscription changes
295
+ await userplex.identify(userId, {
296
+ plan: 'enterprise',
297
+ mrr: 499
331
298
  });
332
299
  ```
333
300
 
334
- ## Frequently Asked Questions
335
-
336
- ## Semantic versioning
301
+ ### AI Application
337
302
 
338
- This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
303
+ ```javascript
304
+ // Log every conversation for debugging and analytics
305
+ const messages = [];
339
306
 
340
- 1. Changes that only affect static types, without breaking runtime behavior.
341
- 2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_
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
- We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
310
+ // Get AI response
311
+ const response = await callOpenAI(messages);
312
+ messages.push({ role: 'assistant', content: response });
345
313
 
346
- We are keen for your feedback; please open an [issue](https://www.github.com/dqnamo/userplex-typescript/issues) with questions, bugs, or suggestions.
347
-
348
- ## Requirements
349
-
350
- TypeScript >= 4.9 is supported.
351
-
352
- The following runtimes are supported:
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
- ## Contributing
322
+ ## License
368
323
 
369
- See [the contributing documentation](./CONTRIBUTING.md).
324
+ MIT