sng-nexus 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/README.md +240 -0
- package/client.d.ts +21 -0
- package/client.d.ts.map +1 -0
- package/client.js +175 -0
- package/client.js.map +1 -0
- package/index.d.ts +3 -0
- package/index.d.ts.map +1 -0
- package/index.js +2 -0
- package/index.js.map +1 -0
- package/network.d.ts +8 -0
- package/network.d.ts.map +1 -0
- package/network.js +22 -0
- package/network.js.map +1 -0
- package/package.json +30 -0
- package/queue.d.ts +18 -0
- package/queue.d.ts.map +1 -0
- package/queue.js +78 -0
- package/queue.js.map +1 -0
- package/retry.d.ts +2 -0
- package/retry.d.ts.map +1 -0
- package/retry.js +9 -0
- package/retry.js.map +1 -0
- package/types.d.ts +52 -0
- package/types.d.ts.map +1 -0
- package/types.js +2 -0
- package/types.js.map +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# sng-nexus
|
|
2
|
+
|
|
3
|
+
React Native client SDK for the Nexus event tracking platform. Collects events from mobile apps and sends them to the Nexus API with automatic offline queuing and retry.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add sng-nexus
|
|
9
|
+
|
|
10
|
+
# Peer dependencies (required)
|
|
11
|
+
pnpm add @react-native-async-storage/async-storage @react-native-community/netinfo
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
For iOS, run `npx pod-install` after installing.
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { NexusClient } from 'sng-nexus';
|
|
20
|
+
|
|
21
|
+
// Initialize once at app startup
|
|
22
|
+
await NexusClient.init({
|
|
23
|
+
endpoint: 'https://nexus.yourserver.com',
|
|
24
|
+
apiKey: 'mathit:your-secret',
|
|
25
|
+
debug: __DEV__,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Identify user after login
|
|
29
|
+
NexusClient.identify({ userId: 'u_123' });
|
|
30
|
+
|
|
31
|
+
// Track events
|
|
32
|
+
NexusClient.track('purchase', {
|
|
33
|
+
value: 4.99,
|
|
34
|
+
currency: 'USD',
|
|
35
|
+
email: 'user@example.com',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Reset identity on logout
|
|
39
|
+
NexusClient.resetIdentity();
|
|
40
|
+
|
|
41
|
+
// Force flush before app backgrounds
|
|
42
|
+
await NexusClient.flush();
|
|
43
|
+
|
|
44
|
+
// Shut down (cleanup timers and listeners)
|
|
45
|
+
await NexusClient.shutdown();
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## API
|
|
49
|
+
|
|
50
|
+
### `NexusClient.init(config)`
|
|
51
|
+
|
|
52
|
+
Initializes the SDK. Must be called before any other method. Loads any persisted events from a previous session and starts the flush timer and network listener.
|
|
53
|
+
|
|
54
|
+
**Config options:**
|
|
55
|
+
|
|
56
|
+
| Option | Type | Default | Description |
|
|
57
|
+
|---|---|---|---|
|
|
58
|
+
| `endpoint` | `string` | *required* | Nexus server base URL (e.g., `https://nexus.example.com`) |
|
|
59
|
+
| `apiKey` | `string` | *required* | API key in `appname:secret` format |
|
|
60
|
+
| `flushInterval` | `number` | `30` | Seconds between automatic flush attempts |
|
|
61
|
+
| `maxQueueSize` | `number` | `1000` | Max events stored in offline queue. Oldest dropped when exceeded |
|
|
62
|
+
| `maxRetries` | `number` | `5` | Max retry attempts per event before it's dropped |
|
|
63
|
+
| `debug` | `boolean` | `false` | Log SDK activity to console |
|
|
64
|
+
|
|
65
|
+
### `NexusClient.identify(identity)`
|
|
66
|
+
|
|
67
|
+
Sets the user identity. Persisted in memory and attached to all subsequent `track()` calls.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
NexusClient.identify({ userId: 'u_123' });
|
|
71
|
+
NexusClient.identify({ anonymousId: 'anon_456' });
|
|
72
|
+
NexusClient.identify({ userId: 'u_123', anonymousId: 'anon_456' });
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### `NexusClient.track(eventName, properties?, context?)`
|
|
76
|
+
|
|
77
|
+
Queues an event for delivery. Returns immediately (fire-and-forget).
|
|
78
|
+
|
|
79
|
+
- `eventName` — required, 1-256 characters
|
|
80
|
+
- `properties` — optional key-value data (e.g., `{ value: 4.99, currency: 'USD' }`)
|
|
81
|
+
- `context` — optional event context (e.g., `{ locale: 'en_US' }`)
|
|
82
|
+
|
|
83
|
+
The SDK automatically adds:
|
|
84
|
+
- `timestamp` — current Unix time in seconds
|
|
85
|
+
- `context.platform` — `'ios'` or `'android'` via `Platform.OS`
|
|
86
|
+
- `user_id` / `anonymous_id` — from the current identity
|
|
87
|
+
|
|
88
|
+
The server further enriches with `event_id`, `context.ip`, and `context.user_agent`.
|
|
89
|
+
|
|
90
|
+
#### Destination-specific properties
|
|
91
|
+
|
|
92
|
+
To trigger conversions in Meta or Reddit, include these in `properties`:
|
|
93
|
+
|
|
94
|
+
| Property | Type | Purpose |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| `value` | `number` | Conversion value |
|
|
97
|
+
| `currency` | `string` | ISO currency code (e.g., `USD`) |
|
|
98
|
+
| `email` | `string` | User email (sent raw; server hashes for Meta/Reddit) |
|
|
99
|
+
| `phone` | `string` | User phone (sent raw; server hashes for Meta/Reddit) |
|
|
100
|
+
| `fbc` | `string` | Meta click ID |
|
|
101
|
+
| `fbp` | `string` | Meta browser ID |
|
|
102
|
+
|
|
103
|
+
### `NexusClient.flush()`
|
|
104
|
+
|
|
105
|
+
Forces an immediate flush of all queued events. Returns a promise that resolves when the flush cycle completes. Skips silently if offline or already flushing.
|
|
106
|
+
|
|
107
|
+
### `NexusClient.resetIdentity()`
|
|
108
|
+
|
|
109
|
+
Clears the current user identity. Call on logout.
|
|
110
|
+
|
|
111
|
+
### `NexusClient.shutdown()`
|
|
112
|
+
|
|
113
|
+
Stops the flush timer and network listener, persists the queue, and marks the client as uninitialized. Call when the app is terminating.
|
|
114
|
+
|
|
115
|
+
## Offline Queue & Retry Behavior
|
|
116
|
+
|
|
117
|
+
Events are persisted to `AsyncStorage` under the key `@nexus:event_queue`. This means events survive app kills, crashes, and reboots.
|
|
118
|
+
|
|
119
|
+
### Flush triggers
|
|
120
|
+
|
|
121
|
+
A flush is attempted when:
|
|
122
|
+
1. The flush timer fires (every `flushInterval` seconds)
|
|
123
|
+
2. The device transitions from offline to online
|
|
124
|
+
3. `flush()` is called manually
|
|
125
|
+
4. The queue reaches 10 events (batch threshold)
|
|
126
|
+
|
|
127
|
+
### Retry strategy
|
|
128
|
+
|
|
129
|
+
| Scenario | Behavior |
|
|
130
|
+
|---|---|
|
|
131
|
+
| **Network offline** | Events stay in queue, flushed on reconnect |
|
|
132
|
+
| **Server 5xx** | Retry with exponential backoff (1s, 2s, 4s, 8s, 16s... capped at 30s) + jitter |
|
|
133
|
+
| **Server 4xx** (validation/auth error) | Event dropped immediately, no retry |
|
|
134
|
+
| **App killed** | Events persist in AsyncStorage, flushed on next `init()` |
|
|
135
|
+
| **Max retries exceeded** | Event dropped from queue |
|
|
136
|
+
| **Queue full** | Oldest events dropped to make room for new ones |
|
|
137
|
+
|
|
138
|
+
### Backoff formula
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
delay = min(1000ms * 2^attempt, 30000ms) + random jitter (0-25%)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Architecture
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
src/
|
|
148
|
+
├── index.ts — Public exports
|
|
149
|
+
├── client.ts — NexusClient singleton (init, track, identify, flush, shutdown)
|
|
150
|
+
├── types.ts — TypeScript interfaces (NexusConfig, TrackPayload, etc.)
|
|
151
|
+
├── queue.ts — AsyncStorage-backed persistent event queue
|
|
152
|
+
├── network.ts — NetInfo connectivity monitor with reconnect callback
|
|
153
|
+
└── retry.ts — Exponential backoff delay calculator
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Data flow
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
track() → EventQueue (in-memory + AsyncStorage)
|
|
160
|
+
↓ flush trigger
|
|
161
|
+
fetch POST /api/track
|
|
162
|
+
↓ success
|
|
163
|
+
remove from queue
|
|
164
|
+
↓ 5xx failure
|
|
165
|
+
increment retry, keep in queue
|
|
166
|
+
↓ 4xx failure
|
|
167
|
+
drop from queue (bad request)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Integration Example
|
|
171
|
+
|
|
172
|
+
### App.tsx (Expo / bare RN)
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
import { useEffect } from 'react';
|
|
176
|
+
import { AppState } from 'react-native';
|
|
177
|
+
import { NexusClient } from 'sng-nexus';
|
|
178
|
+
|
|
179
|
+
export default function App() {
|
|
180
|
+
useEffect(() => {
|
|
181
|
+
NexusClient.init({
|
|
182
|
+
endpoint: 'https://nexus.yourserver.com',
|
|
183
|
+
apiKey: 'kidteller:your-secret',
|
|
184
|
+
debug: __DEV__,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const sub = AppState.addEventListener('change', (state) => {
|
|
188
|
+
if (state === 'background') {
|
|
189
|
+
NexusClient.flush();
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
return () => {
|
|
194
|
+
sub.remove();
|
|
195
|
+
NexusClient.shutdown();
|
|
196
|
+
};
|
|
197
|
+
}, []);
|
|
198
|
+
|
|
199
|
+
return <MainNavigator />;
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### After login
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
NexusClient.identify({ userId: user.id });
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Tracking events
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
// Simple event
|
|
213
|
+
NexusClient.track('screen_view', { screen: 'home' });
|
|
214
|
+
|
|
215
|
+
// Purchase with Meta/Reddit conversion data
|
|
216
|
+
NexusClient.track('purchase', {
|
|
217
|
+
value: 4.99,
|
|
218
|
+
currency: 'USD',
|
|
219
|
+
email: user.email,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Custom event with context
|
|
223
|
+
NexusClient.track('level_complete', { level: 5, score: 100 }, { locale: 'en_US' });
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### On logout
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
NexusClient.resetIdentity();
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Development
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
# From the monorepo root
|
|
236
|
+
pnpm --filter sng-nexus build # Compile TypeScript
|
|
237
|
+
pnpm --filter sng-nexus clean # Remove dist/
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The package uses the monorepo's shared `tsconfig.base.json` and builds with `tsc` to `dist/`.
|
package/client.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { NexusConfig, Identity, EventContext } from './types.js';
|
|
2
|
+
declare class NexusClientImpl {
|
|
3
|
+
private config;
|
|
4
|
+
private queue;
|
|
5
|
+
private network;
|
|
6
|
+
private identity;
|
|
7
|
+
private flushTimer;
|
|
8
|
+
private flushing;
|
|
9
|
+
private initialized;
|
|
10
|
+
init(config: NexusConfig): Promise<void>;
|
|
11
|
+
identify(identity: Identity): void;
|
|
12
|
+
track(eventName: string, properties?: Record<string, unknown>, context?: EventContext): void;
|
|
13
|
+
flush(): Promise<void>;
|
|
14
|
+
resetIdentity(): void;
|
|
15
|
+
shutdown(): Promise<void>;
|
|
16
|
+
private send;
|
|
17
|
+
private log;
|
|
18
|
+
}
|
|
19
|
+
export declare const NexusClient: NexusClientImpl;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=client.d.ts.map
|
package/client.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,WAAW,EAEX,QAAQ,EAIR,YAAY,EACb,MAAM,YAAY,CAAC;AAIpB,cAAM,eAAe;IACnB,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAS;IAEtB,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAsC9C,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAKlC,KAAK,CACH,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,OAAO,CAAC,EAAE,YAAY,GACrB,IAAI;IAkCD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA6D5B,aAAa,IAAI,IAAI;IAKf,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAWjB,IAAI;IAsBlB,OAAO,CAAC,GAAG;CAKZ;AAED,eAAO,MAAM,WAAW,iBAAwB,CAAC"}
|
package/client.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
import { EventQueue } from './queue.js';
|
|
3
|
+
import { NetworkMonitor } from './network.js';
|
|
4
|
+
import { getRetryDelay } from './retry.js';
|
|
5
|
+
const FLUSH_BATCH_SIZE = 10;
|
|
6
|
+
class NexusClientImpl {
|
|
7
|
+
config = null;
|
|
8
|
+
queue = null;
|
|
9
|
+
network = null;
|
|
10
|
+
identity = {};
|
|
11
|
+
flushTimer = null;
|
|
12
|
+
flushing = false;
|
|
13
|
+
initialized = false;
|
|
14
|
+
async init(config) {
|
|
15
|
+
if (this.initialized) {
|
|
16
|
+
this.log('Already initialized, call reset() first to re-initialize');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
this.config = {
|
|
20
|
+
endpoint: config.endpoint.replace(/\/$/, ''),
|
|
21
|
+
apiKey: config.apiKey,
|
|
22
|
+
flushInterval: config.flushInterval ?? 30,
|
|
23
|
+
maxQueueSize: config.maxQueueSize ?? 1000,
|
|
24
|
+
maxRetries: config.maxRetries ?? 5,
|
|
25
|
+
debug: config.debug ?? false,
|
|
26
|
+
};
|
|
27
|
+
this.queue = new EventQueue(this.config.maxQueueSize);
|
|
28
|
+
await this.queue.load();
|
|
29
|
+
this.network = new NetworkMonitor();
|
|
30
|
+
this.network.start(() => {
|
|
31
|
+
this.log('Network reconnected, flushing queue');
|
|
32
|
+
this.flush();
|
|
33
|
+
});
|
|
34
|
+
this.flushTimer = setInterval(() => {
|
|
35
|
+
this.flush();
|
|
36
|
+
}, this.config.flushInterval * 1000);
|
|
37
|
+
this.initialized = true;
|
|
38
|
+
this.log('Initialized');
|
|
39
|
+
// Flush any events persisted from a previous session
|
|
40
|
+
if (this.queue.size > 0) {
|
|
41
|
+
this.log(`Found ${this.queue.size} queued events from previous session`);
|
|
42
|
+
this.flush();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
identify(identity) {
|
|
46
|
+
this.identity = { ...this.identity, ...identity };
|
|
47
|
+
this.log('Identity updated', this.identity);
|
|
48
|
+
}
|
|
49
|
+
track(eventName, properties, context) {
|
|
50
|
+
if (!this.config || !this.queue) {
|
|
51
|
+
console.warn('[Nexus] Not initialized. Call NexusClient.init() first.');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const payload = {
|
|
55
|
+
event_name: eventName,
|
|
56
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
57
|
+
properties,
|
|
58
|
+
context: {
|
|
59
|
+
platform: Platform.OS,
|
|
60
|
+
...context,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
if (this.identity.userId)
|
|
64
|
+
payload.user_id = this.identity.userId;
|
|
65
|
+
if (this.identity.anonymousId)
|
|
66
|
+
payload.anonymous_id = this.identity.anonymousId;
|
|
67
|
+
const queued = {
|
|
68
|
+
payload,
|
|
69
|
+
retries: 0,
|
|
70
|
+
createdAt: Date.now(),
|
|
71
|
+
};
|
|
72
|
+
this.queue.add(queued);
|
|
73
|
+
this.log('Event queued', eventName);
|
|
74
|
+
// Auto-flush when batch size reached
|
|
75
|
+
if (this.queue.size >= FLUSH_BATCH_SIZE) {
|
|
76
|
+
this.flush();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async flush() {
|
|
80
|
+
if (!this.config || !this.queue)
|
|
81
|
+
return;
|
|
82
|
+
if (this.flushing)
|
|
83
|
+
return;
|
|
84
|
+
if (this.queue.size === 0)
|
|
85
|
+
return;
|
|
86
|
+
if (this.network && !this.network.isConnected) {
|
|
87
|
+
this.log('Offline, skipping flush');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
this.flushing = true;
|
|
91
|
+
try {
|
|
92
|
+
const events = this.queue.peek();
|
|
93
|
+
const sent = [];
|
|
94
|
+
const retried = [];
|
|
95
|
+
for (let i = 0; i < events.length; i++) {
|
|
96
|
+
const event = events[i];
|
|
97
|
+
// Skip events with backoff not yet elapsed
|
|
98
|
+
if (event.retries > 0 && event.lastFailedAt) {
|
|
99
|
+
const delay = getRetryDelay(event.retries);
|
|
100
|
+
if (Date.now() - event.lastFailedAt < delay) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const response = await this.send(event.payload);
|
|
106
|
+
if (response.success) {
|
|
107
|
+
sent.push(i);
|
|
108
|
+
this.log('Event sent', event.payload.event_name, response.event_id);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
// Server rejected (400/401) — don't retry
|
|
112
|
+
this.log('Event rejected', event.payload.event_name, response.error);
|
|
113
|
+
sent.push(i);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Network/server error — retry later
|
|
118
|
+
retried.push(i);
|
|
119
|
+
this.queue.incrementRetry(i);
|
|
120
|
+
this.log('Event send failed, will retry', event.payload.event_name, `attempt ${event.retries + 1}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Remove sent events in reverse order to preserve indices
|
|
124
|
+
for (let i = sent.length - 1; i >= 0; i--) {
|
|
125
|
+
this.queue.remove(sent[i]);
|
|
126
|
+
}
|
|
127
|
+
this.queue.pruneExpired(this.config.maxRetries);
|
|
128
|
+
await this.queue.persist();
|
|
129
|
+
if (sent.length > 0) {
|
|
130
|
+
this.log(`Flushed ${sent.length} events, ${this.queue.size} remaining`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
this.flushing = false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
resetIdentity() {
|
|
138
|
+
this.identity = {};
|
|
139
|
+
this.log('Identity reset');
|
|
140
|
+
}
|
|
141
|
+
async shutdown() {
|
|
142
|
+
if (this.flushTimer) {
|
|
143
|
+
clearInterval(this.flushTimer);
|
|
144
|
+
this.flushTimer = null;
|
|
145
|
+
}
|
|
146
|
+
this.network?.stop();
|
|
147
|
+
await this.queue?.persist();
|
|
148
|
+
this.initialized = false;
|
|
149
|
+
this.log('Shut down');
|
|
150
|
+
}
|
|
151
|
+
async send(payload) {
|
|
152
|
+
const url = `${this.config.endpoint}/api/track`;
|
|
153
|
+
const response = await fetch(url, {
|
|
154
|
+
method: 'POST',
|
|
155
|
+
headers: {
|
|
156
|
+
'Content-Type': 'application/json',
|
|
157
|
+
'x-api-key': this.config.apiKey,
|
|
158
|
+
},
|
|
159
|
+
body: JSON.stringify(payload),
|
|
160
|
+
});
|
|
161
|
+
const data = (await response.json());
|
|
162
|
+
if (!response.ok && response.status >= 500) {
|
|
163
|
+
// Server error — throw to trigger retry
|
|
164
|
+
throw new Error(`Server error: ${response.status}`);
|
|
165
|
+
}
|
|
166
|
+
return data;
|
|
167
|
+
}
|
|
168
|
+
log(...args) {
|
|
169
|
+
if (this.config?.debug) {
|
|
170
|
+
console.log('[Nexus]', ...args);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
export const NexusClient = new NexusClientImpl();
|
|
175
|
+
//# sourceMappingURL=client.js.map
|
package/client.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/index.d.ts
ADDED
package/index.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,YAAY,EACV,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,aAAa,GACd,MAAM,YAAY,CAAC"}
|
package/index.js
ADDED
package/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/network.d.ts
ADDED
package/network.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"AAEA,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,YAAY,CAAQ;IAE5B,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,KAAK,CAAC,WAAW,EAAE,MAAM,IAAI,GAAG,IAAI;IAWpC,IAAI,IAAI,IAAI;CAIb"}
|
package/network.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import NetInfo from '@react-native-community/netinfo';
|
|
2
|
+
export class NetworkMonitor {
|
|
3
|
+
unsubscribe = null;
|
|
4
|
+
_isConnected = true;
|
|
5
|
+
get isConnected() {
|
|
6
|
+
return this._isConnected;
|
|
7
|
+
}
|
|
8
|
+
start(onReconnect) {
|
|
9
|
+
this.unsubscribe = NetInfo.addEventListener((state) => {
|
|
10
|
+
const wasDisconnected = !this._isConnected;
|
|
11
|
+
this._isConnected = state.isConnected ?? true;
|
|
12
|
+
if (wasDisconnected && this._isConnected) {
|
|
13
|
+
onReconnect();
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
stop() {
|
|
18
|
+
this.unsubscribe?.();
|
|
19
|
+
this.unsubscribe = null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=network.js.map
|
package/network.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.js","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"AAAA,OAAO,OAA8B,MAAM,iCAAiC,CAAC;AAE7E,MAAM,OAAO,cAAc;IACjB,WAAW,GAAwB,IAAI,CAAC;IACxC,YAAY,GAAG,IAAI,CAAC;IAE5B,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,WAAuB;QAC3B,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC,KAAmB,EAAE,EAAE;YAClE,MAAM,eAAe,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC;YAC3C,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC;YAE9C,IAAI,eAAe,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;gBACzC,WAAW,EAAE,CAAC;YAChB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI;QACF,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACrB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAC1B,CAAC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sng-nexus",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"clean": "rm -rf dist"
|
|
16
|
+
},
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"react-native": ">=0.70.0",
|
|
19
|
+
"@react-native-async-storage/async-storage": ">=1.0.0",
|
|
20
|
+
"@react-native-community/netinfo": ">=9.0.0"
|
|
21
|
+
},
|
|
22
|
+
"peerDependenciesMeta": {
|
|
23
|
+
"@react-native-async-storage/async-storage": {
|
|
24
|
+
"optional": false
|
|
25
|
+
},
|
|
26
|
+
"@react-native-community/netinfo": {
|
|
27
|
+
"optional": false
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
package/queue.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { QueuedEvent } from './types.js';
|
|
2
|
+
export declare class EventQueue {
|
|
3
|
+
private events;
|
|
4
|
+
private maxSize;
|
|
5
|
+
private dirty;
|
|
6
|
+
constructor(maxSize: number);
|
|
7
|
+
load(): Promise<void>;
|
|
8
|
+
add(event: QueuedEvent): void;
|
|
9
|
+
remove(index: number): void;
|
|
10
|
+
peek(): QueuedEvent[];
|
|
11
|
+
get size(): number;
|
|
12
|
+
incrementRetry(index: number): void;
|
|
13
|
+
/** Remove events that exceeded max retries */
|
|
14
|
+
pruneExpired(maxRetries: number): void;
|
|
15
|
+
persist(): Promise<void>;
|
|
16
|
+
clear(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=queue.d.ts.map
|
package/queue.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI9C,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAAS;gBAEV,OAAO,EAAE,MAAM;IAIrB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,GAAG,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAS7B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK3B,IAAI,IAAI,WAAW,EAAE;IAIrB,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IASnC,8CAA8C;IAC9C,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAQhC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAUxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAS7B"}
|
package/queue.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
2
|
+
const STORAGE_KEY = '@nexus:event_queue';
|
|
3
|
+
export class EventQueue {
|
|
4
|
+
events = [];
|
|
5
|
+
maxSize;
|
|
6
|
+
dirty = false;
|
|
7
|
+
constructor(maxSize) {
|
|
8
|
+
this.maxSize = maxSize;
|
|
9
|
+
}
|
|
10
|
+
async load() {
|
|
11
|
+
try {
|
|
12
|
+
const raw = await AsyncStorage.getItem(STORAGE_KEY);
|
|
13
|
+
if (raw) {
|
|
14
|
+
this.events = JSON.parse(raw);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// Corrupted storage — start fresh
|
|
19
|
+
this.events = [];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
add(event) {
|
|
23
|
+
// Drop oldest events if queue is full
|
|
24
|
+
while (this.events.length >= this.maxSize) {
|
|
25
|
+
this.events.shift();
|
|
26
|
+
}
|
|
27
|
+
this.events.push(event);
|
|
28
|
+
this.dirty = true;
|
|
29
|
+
}
|
|
30
|
+
remove(index) {
|
|
31
|
+
this.events.splice(index, 1);
|
|
32
|
+
this.dirty = true;
|
|
33
|
+
}
|
|
34
|
+
peek() {
|
|
35
|
+
return [...this.events];
|
|
36
|
+
}
|
|
37
|
+
get size() {
|
|
38
|
+
return this.events.length;
|
|
39
|
+
}
|
|
40
|
+
incrementRetry(index) {
|
|
41
|
+
const event = this.events[index];
|
|
42
|
+
if (event) {
|
|
43
|
+
event.retries++;
|
|
44
|
+
event.lastFailedAt = Date.now();
|
|
45
|
+
this.dirty = true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Remove events that exceeded max retries */
|
|
49
|
+
pruneExpired(maxRetries) {
|
|
50
|
+
const before = this.events.length;
|
|
51
|
+
this.events = this.events.filter((e) => e.retries < maxRetries);
|
|
52
|
+
if (this.events.length !== before) {
|
|
53
|
+
this.dirty = true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async persist() {
|
|
57
|
+
if (!this.dirty)
|
|
58
|
+
return;
|
|
59
|
+
try {
|
|
60
|
+
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(this.events));
|
|
61
|
+
this.dirty = false;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Storage write failed — events remain in memory, will retry next persist
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async clear() {
|
|
68
|
+
this.events = [];
|
|
69
|
+
this.dirty = false;
|
|
70
|
+
try {
|
|
71
|
+
await AsyncStorage.removeItem(STORAGE_KEY);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Ignore
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=queue.js.map
|
package/queue.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"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"}
|
package/retry.d.ts
ADDED
package/retry.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAGA,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKrD"}
|
package/retry.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const BASE_DELAY_MS = 1000;
|
|
2
|
+
const MAX_DELAY_MS = 30000;
|
|
3
|
+
export function getRetryDelay(attempt) {
|
|
4
|
+
const delay = Math.min(BASE_DELAY_MS * Math.pow(2, attempt), MAX_DELAY_MS);
|
|
5
|
+
// Add jitter (0-25% of delay) to avoid thundering herd
|
|
6
|
+
const jitter = delay * 0.25 * Math.random();
|
|
7
|
+
return delay + jitter;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=retry.js.map
|
package/retry.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA,MAAM,aAAa,GAAG,IAAI,CAAC;AAC3B,MAAM,YAAY,GAAG,KAAK,CAAC;AAE3B,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,YAAY,CAAC,CAAC;IAC3E,uDAAuD;IACvD,MAAM,MAAM,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;IAC5C,OAAO,KAAK,GAAG,MAAM,CAAC;AACxB,CAAC"}
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface NexusConfig {
|
|
2
|
+
/** Base URL of the Nexus server (e.g., https://nexus.example.com) */
|
|
3
|
+
endpoint: string;
|
|
4
|
+
/** API key in format "appname:secret" */
|
|
5
|
+
apiKey: string;
|
|
6
|
+
/** Flush interval in seconds (default: 30) */
|
|
7
|
+
flushInterval?: number;
|
|
8
|
+
/** Max events persisted in offline queue (default: 1000) */
|
|
9
|
+
maxQueueSize?: number;
|
|
10
|
+
/** Max retry attempts per event (default: 5) */
|
|
11
|
+
maxRetries?: number;
|
|
12
|
+
/** Enable debug logging (default: false) */
|
|
13
|
+
debug?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface ResolvedConfig {
|
|
16
|
+
endpoint: string;
|
|
17
|
+
apiKey: string;
|
|
18
|
+
flushInterval: number;
|
|
19
|
+
maxQueueSize: number;
|
|
20
|
+
maxRetries: number;
|
|
21
|
+
debug: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface Identity {
|
|
24
|
+
userId?: string;
|
|
25
|
+
anonymousId?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface EventContext {
|
|
28
|
+
app?: string;
|
|
29
|
+
platform?: string;
|
|
30
|
+
locale?: string;
|
|
31
|
+
[key: string]: unknown;
|
|
32
|
+
}
|
|
33
|
+
export interface TrackPayload {
|
|
34
|
+
event_name: string;
|
|
35
|
+
user_id?: string;
|
|
36
|
+
anonymous_id?: string;
|
|
37
|
+
timestamp?: number;
|
|
38
|
+
properties?: Record<string, unknown>;
|
|
39
|
+
context?: EventContext;
|
|
40
|
+
}
|
|
41
|
+
export interface QueuedEvent {
|
|
42
|
+
payload: TrackPayload;
|
|
43
|
+
retries: number;
|
|
44
|
+
createdAt: number;
|
|
45
|
+
lastFailedAt?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface TrackResponse {
|
|
48
|
+
success: boolean;
|
|
49
|
+
event_id?: string;
|
|
50
|
+
error?: string;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=types.d.ts.map
|
package/types.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
|
package/types.js
ADDED
package/types.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|