@cherrydotfun/collector-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Cherry
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,497 @@
1
+ # @cherrydotfun/collector-sdk
2
+
3
+ TypeScript SDK for Cherry Collector — event tracking, logging, and metrics.
4
+
5
+ Collect errors, logs, and analytics events from your application and stream them to the Collector backend. Zero dependencies, works on browser, React Native, and Node.js.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @cherrydotfun/collector-sdk
11
+ ```
12
+
13
+ ```bash
14
+ yarn add @cherrydotfun/collector-sdk
15
+ ```
16
+
17
+ ```bash
18
+ bun add @cherrydotfun/collector-sdk
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ Initialize the client, send events, and flush before exit:
24
+
25
+ ```typescript
26
+ import { CollectorClient } from '@cherrydotfun/collector-sdk';
27
+
28
+ const collector = new CollectorClient({
29
+ baseUrl: 'https://collector.cherry.fun',
30
+ apiKey: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
31
+ platform: 'ios',
32
+ version: '2.1.0',
33
+ });
34
+
35
+ // Initialize: fetch filter config and start refresh timer
36
+ await collector.init();
37
+
38
+ // Set context (attached to all subsequent events)
39
+ collector.setUser('wallet_address_or_user_id');
40
+ collector.setSession('session_uuid');
41
+ collector.setModule('messaging');
42
+ collector.setDevice('device_id');
43
+
44
+ // Send events
45
+ collector.error('WebSocket connection failed', { url: '/ws' });
46
+ collector.info('User logged in', { userId: 'wallet123' });
47
+ collector.debug('Cache hit', { key: 'messages_dm_123' });
48
+ collector.analytics('screen_view', 'ChatScreen', { fromScreen: 'RoomList' });
49
+ collector.analytics('button_click', 'Send message', { hasAttachment: true });
50
+
51
+ // Set metrics
52
+ await collector.metric('ws_connections', 42);
53
+ await collector.metricBatch([
54
+ { key: 'ws_connections', value: 42 },
55
+ { key: 'active_rooms', value: 15 },
56
+ ]);
57
+
58
+ // Flush remaining events and stop timers
59
+ await collector.flush();
60
+ collector.destroy();
61
+ ```
62
+
63
+ ## Configuration
64
+
65
+ Pass a `CollectorConfig` object to the constructor:
66
+
67
+ | Option | Type | Default | Description |
68
+ |--------|------|---------|-------------|
69
+ | `baseUrl` | string | required | Base URL of the Collector backend (e.g. `https://collector.cherry.fun`). |
70
+ | `apiKey` | string | required | Project API key from the admin panel. |
71
+ | `platform` | string | undefined | Client platform (e.g. `ios`, `android`, `web`, `server`). Attached to all events. |
72
+ | `version` | string | undefined | Application version string (e.g. `2.1.0`). Attached to all events. |
73
+ | `batchSize` | number | 10 | Number of events per batch before auto-flush. |
74
+ | `flushIntervalMs` | number | 5000 | Milliseconds between automatic batch flushes. |
75
+ | `configRefreshSec` | number | 300 | Seconds between config refreshes from server. |
76
+ | `debug` | boolean | false | Enable debug logging to `console.debug`. |
77
+ | `onError` | function | undefined | Called when network or API errors occur. Receives `(error, context)`. |
78
+
79
+ ## API Reference
80
+
81
+ ### Lifecycle
82
+
83
+ #### `init(): Promise<void>`
84
+
85
+ Initialize the client. Fetches filter configuration from the server and starts the periodic config refresh timer. Must be called before sending events.
86
+
87
+ ```typescript
88
+ await collector.init();
89
+ ```
90
+
91
+ #### `flush(): Promise<void>`
92
+
93
+ Flush all queued events to the server immediately. Returns when the send completes (or fails silently).
94
+
95
+ ```typescript
96
+ await collector.flush();
97
+ ```
98
+
99
+ #### `destroy(): void`
100
+
101
+ Stop all timers and flush remaining events. Call before application exit or when the client is no longer needed.
102
+
103
+ ```typescript
104
+ collector.destroy();
105
+ ```
106
+
107
+ ### Context
108
+
109
+ These setters attach context to all subsequent events:
110
+
111
+ #### `setUser(userId: string): void`
112
+
113
+ Set the user ID.
114
+
115
+ ```typescript
116
+ collector.setUser('wallet_address_or_user_id');
117
+ ```
118
+
119
+ #### `setSession(sessionId: string): void`
120
+
121
+ Set the session ID.
122
+
123
+ ```typescript
124
+ collector.setSession('session_uuid');
125
+ ```
126
+
127
+ #### `setModule(module: string): void`
128
+
129
+ Set the default module name.
130
+
131
+ ```typescript
132
+ collector.setModule('messaging');
133
+ ```
134
+
135
+ #### `setDevice(deviceId: string): void`
136
+
137
+ Set the device ID.
138
+
139
+ ```typescript
140
+ collector.setDevice('device_uuid_or_id');
141
+ ```
142
+
143
+ ### Errors
144
+
145
+ #### `error(message: string, params?: Record<string, unknown> & { stackTrace?: string }): void`
146
+
147
+ Send an error event. The special `stackTrace` key is extracted and sent as the structured `stackTrace.raw` field.
148
+
149
+ ```typescript
150
+ try {
151
+ await fetchData();
152
+ } catch (e) {
153
+ collector.error('Failed to fetch data', {
154
+ url: '/api/data',
155
+ stackTrace: e instanceof Error ? e.stack : undefined,
156
+ });
157
+ }
158
+ ```
159
+
160
+ ### Logging
161
+
162
+ #### `log(level: LogLevel, message: string, params?: Record<string, unknown>): void`
163
+
164
+ Send a log event with an explicit level (`fatal`, `error`, `warn`, `info`, or `debug`).
165
+
166
+ ```typescript
167
+ collector.log('warn', 'Slow query detected', { duration: 2500 });
168
+ ```
169
+
170
+ #### `fatal(message: string, params?: Record<string, unknown>): void`
171
+
172
+ Shorthand for `log('fatal', message, params)`.
173
+
174
+ ```typescript
175
+ collector.fatal('Critical system failure');
176
+ ```
177
+
178
+ #### `error(message: string, params?: Record<string, unknown>): void`
179
+
180
+ Shorthand for `log('error', message, params)`. Note: this is different from the `error()` method above, which has special stackTrace handling.
181
+
182
+ #### `warn(message: string, params?: Record<string, unknown>): void`
183
+
184
+ Shorthand for `log('warn', message, params)`.
185
+
186
+ ```typescript
187
+ collector.warn('High memory usage', { mb: 512 });
188
+ ```
189
+
190
+ #### `info(message: string, params?: Record<string, unknown>): void`
191
+
192
+ Shorthand for `log('info', message, params)`.
193
+
194
+ ```typescript
195
+ collector.info('Server started', { port: 3000 });
196
+ ```
197
+
198
+ #### `debug(message: string, params?: Record<string, unknown>): void`
199
+
200
+ Shorthand for `log('debug', message, params)`.
201
+
202
+ ```typescript
203
+ collector.debug('Cache miss', { key: 'user_settings_123' });
204
+ ```
205
+
206
+ ### Analytics
207
+
208
+ #### `analytics(event: string, message: string, params?: Record<string, unknown>): void`
209
+
210
+ Send an analytics event. The `event` parameter is an event name (e.g. `screen_view`, `button_click`), and `message` is a human-readable description.
211
+
212
+ ```typescript
213
+ collector.analytics('screen_view', 'ChatScreen', {
214
+ fromScreen: 'RoomList',
215
+ roomType: 'dm',
216
+ });
217
+
218
+ collector.analytics('button_click', 'Send message', {
219
+ hasAttachment: true,
220
+ messageLength: 42,
221
+ });
222
+ ```
223
+
224
+ ### Metrics
225
+
226
+ Metrics are sent immediately (not batched).
227
+
228
+ #### `metric(key: string, value: number): Promise<void>`
229
+
230
+ Set a single metric value.
231
+
232
+ ```typescript
233
+ await collector.metric('ws_connections', 42);
234
+ await collector.metric('active_rooms', 15);
235
+ ```
236
+
237
+ #### `metricBatch(ops: MetricOp[]): Promise<void>`
238
+
239
+ Set multiple metric values in one request. The backend accepts up to 100 operations per call.
240
+
241
+ ```typescript
242
+ await collector.metricBatch([
243
+ { key: 'ws_connections', value: 42 },
244
+ { key: 'active_rooms', value: 15 },
245
+ { key: 'cpu_usage', value: 34.5 },
246
+ ]);
247
+ ```
248
+
249
+ ### Direct Send
250
+
251
+ #### `send(event: Partial<CollectorEvent> & { type: CollectorEvent['type']; message: string }): Promise<void>`
252
+
253
+ Send a single event immediately via `POST /api/collect`, bypassing the batch queue. Useful for critical events that must not be delayed.
254
+
255
+ The event is enriched with context (userId, sessionId, platform, etc.) and filtered against disabled patterns, same as queued events.
256
+
257
+ ```typescript
258
+ await collector.send({
259
+ type: 'error',
260
+ level: 'error',
261
+ message: 'Critical error',
262
+ params: { severity: 'high' },
263
+ });
264
+ ```
265
+
266
+ ### Configuration
267
+
268
+ #### `getConfig(): ConfigResponse | null`
269
+
270
+ Returns the last successfully fetched server configuration, or `null` if no config has been loaded yet.
271
+
272
+ ```typescript
273
+ const config = collector.getConfig();
274
+ if (config) {
275
+ console.log('Disabled patterns:', config.disabled);
276
+ }
277
+ ```
278
+
279
+ #### `refreshConfig(): Promise<void>`
280
+
281
+ Force-refresh the filter configuration from the server. Normally called automatically on the configured interval.
282
+
283
+ ```typescript
284
+ await collector.refreshConfig();
285
+ ```
286
+
287
+ ### Observability
288
+
289
+ #### `getStats(): CollectorStats`
290
+
291
+ Returns SDK telemetry counters: events sent, dropped (filtered), failed, and current queue size.
292
+
293
+ ```typescript
294
+ const stats = collector.getStats();
295
+ console.log(`Sent: ${stats.sent}, Dropped: ${stats.dropped}, Failed: ${stats.failed}`);
296
+ ```
297
+
298
+ #### `isInitialized: boolean`
299
+
300
+ Read-only getter. Whether `init()` has been called and completed successfully.
301
+
302
+ ```typescript
303
+ if (collector.isInitialized) {
304
+ console.log('Ready to send events');
305
+ }
306
+ ```
307
+
308
+ ## Error Handling
309
+
310
+ Pass an `onError` callback to handle network and API errors:
311
+
312
+ ```typescript
313
+ const collector = new CollectorClient({
314
+ baseUrl: 'https://collector.cherry.fun',
315
+ apiKey: 'xxxxx',
316
+ onError: (error, context) => {
317
+ console.error(`Collector error in ${context}:`, error.message);
318
+ // Log to your error tracking service, retry, etc.
319
+ },
320
+ });
321
+
322
+ await collector.init();
323
+ ```
324
+
325
+ The `context` parameter indicates where the error occurred:
326
+ - `sendBatch` - batch event send
327
+ - `send` - direct event send
328
+ - `metric` - metric set
329
+ - `metricBatch` - batch metric set
330
+ - `refreshConfig` - config refresh from server
331
+
332
+ ## Event Filtering
333
+
334
+ The SDK fetches a filter configuration from the server on init and periodically refreshes it. Events matching disabled patterns are dropped client-side and not sent.
335
+
336
+ Patterns are hierarchical:
337
+ - `log.debug` — disable debug-level log events
338
+ - `log.*` — disable all log events
339
+ - `log` — disable all log events (alternate syntax)
340
+ - `*` — disable all events
341
+
342
+ When you call `collector.debug(...)`, the SDK checks if `log.debug` is in the disabled set. If so, the event is dropped locally (not sent), and the dropped counter is incremented.
343
+
344
+ ```typescript
345
+ const stats = collector.getStats();
346
+ console.log(`Events dropped: ${stats.dropped}`);
347
+ ```
348
+
349
+ ## Platform Support
350
+
351
+ The SDK requires `globalThis.fetch` to be available:
352
+
353
+ - **Browser:** Native fetch, or polyfill
354
+ - **React Native:** Built-in fetch (works with `http://` and `https://`)
355
+ - **Node.js 18+:** Native fetch
356
+
357
+ For Node.js < 18, provide a fetch polyfill (e.g. `node-fetch`):
358
+
359
+ ```typescript
360
+ import fetch from 'node-fetch';
361
+ globalThis.fetch = fetch;
362
+
363
+ const collector = new CollectorClient({ /* ... */ });
364
+ ```
365
+
366
+ ## Types
367
+
368
+ All types are exported from `@cherrydotfun/collector-sdk`:
369
+
370
+ ```typescript
371
+ import {
372
+ CollectorClient,
373
+ CollectorConfig,
374
+ CollectorEvent,
375
+ CollectorStats,
376
+ ConfigResponse,
377
+ EventType,
378
+ LogLevel,
379
+ AnalyticsLevel,
380
+ MetricOp,
381
+ IngestResponse,
382
+ BatchIngestResponse,
383
+ } from '@cherrydotfun/collector-sdk';
384
+ ```
385
+
386
+ ### Type Reference
387
+
388
+ | Type | Description |
389
+ |------|-------------|
390
+ | `CollectorClient` | Main client class for sending events and metrics. |
391
+ | `CollectorConfig` | Configuration options for the client. |
392
+ | `CollectorEvent` | Event payload sent to the backend. |
393
+ | `CollectorStats` | SDK telemetry counters (sent, dropped, failed, queueSize). |
394
+ | `ConfigResponse` | Server response from `GET /api/config` with disabled patterns. |
395
+ | `EventType` | Union of `'error' \| 'log' \| 'analytics'`. |
396
+ | `LogLevel` | Union of `'fatal' \| 'error' \| 'warn' \| 'info' \| 'debug'`. |
397
+ | `AnalyticsLevel` | Union of well-known analytics events or any custom string. |
398
+ | `MetricOp` | Single metric key-value pair for batch operations. |
399
+ | `IngestResponse` | Response from single event send. |
400
+ | `BatchIngestResponse` | Response from batch event send. |
401
+
402
+ ## React Native Example
403
+
404
+ ```typescript
405
+ import { useEffect } from 'react';
406
+ import { AppState } from 'react-native';
407
+ import { CollectorClient } from '@cherrydotfun/collector-sdk';
408
+
409
+ const collector = new CollectorClient({
410
+ baseUrl: 'https://collector.cherry.fun',
411
+ apiKey: 'xxxxx',
412
+ platform: 'android', // or 'ios'
413
+ version: '2.1.0',
414
+ debug: false,
415
+ onError: (error, context) => {
416
+ console.error(`Collector error [${context}]:`, error);
417
+ },
418
+ });
419
+
420
+ export function App() {
421
+ useEffect(() => {
422
+ const init = async () => {
423
+ await collector.init();
424
+ collector.setUser(userWallet);
425
+ collector.setSession(sessionId);
426
+ collector.setModule('chat');
427
+ };
428
+
429
+ init();
430
+
431
+ const subscription = AppState.addEventListener('change', (state) => {
432
+ if (state === 'background') {
433
+ collector.flush();
434
+ }
435
+ });
436
+
437
+ return () => {
438
+ subscription.remove();
439
+ collector.flush();
440
+ collector.destroy();
441
+ };
442
+ }, []);
443
+
444
+ return (
445
+ // Your app components
446
+ );
447
+ }
448
+ ```
449
+
450
+ ## Node.js Server Example
451
+
452
+ ```typescript
453
+ import { CollectorClient } from '@cherrydotfun/collector-sdk';
454
+
455
+ const collector = new CollectorClient({
456
+ baseUrl: 'https://collector.cherry.fun',
457
+ apiKey: 'xxxxx',
458
+ platform: 'server',
459
+ version: '1.0.0',
460
+ debug: process.env.DEBUG === 'true',
461
+ });
462
+
463
+ await collector.init();
464
+
465
+ // Log application startup
466
+ collector.info('Server started', { port: 3000, env: process.env.NODE_ENV });
467
+
468
+ // Track unhandled errors
469
+ process.on('uncaughtException', (error) => {
470
+ collector.error('Uncaught exception', {
471
+ name: error.name,
472
+ message: error.message,
473
+ stackTrace: error.stack,
474
+ });
475
+ collector.flush().finally(() => process.exit(1));
476
+ });
477
+
478
+ // On graceful shutdown
479
+ process.on('SIGTERM', async () => {
480
+ console.log('Shutting down...');
481
+ await collector.flush();
482
+ collector.destroy();
483
+ process.exit(0);
484
+ });
485
+ ```
486
+
487
+ ## Build Distribution
488
+
489
+ The SDK is published in multiple formats:
490
+
491
+ - **CommonJS** — `dist/index.js` for Node.js
492
+ - **ES Modules** — `dist/index.mjs` for bundlers
493
+ - **TypeScript Declarations** — `dist/index.d.ts`
494
+
495
+ ## License
496
+
497
+ MIT