@appspro-dev/sdk-js 0.1.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 +205 -0
- package/dist/client.d.ts +111 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +214 -0
- package/dist/client.js.map +1 -0
- package/dist/context.d.ts +14 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +90 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/queue.d.ts +50 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +105 -0
- package/dist/queue.js.map +1 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +44 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +105 -0
- package/dist/utils.js.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# @appspro/sdk-js
|
|
2
|
+
|
|
3
|
+
> Zero-dependency analytics SDK for the **AppsPro** platform.
|
|
4
|
+
> Works in browsers **and** Node.js (18+).
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @appspro/sdk-js
|
|
12
|
+
# or
|
|
13
|
+
yarn add @appspro/sdk-js
|
|
14
|
+
# or
|
|
15
|
+
pnpm add @appspro/sdk-js
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { AppsPro } from '@appspro/sdk-js';
|
|
22
|
+
|
|
23
|
+
const analytics = new AppsPro({
|
|
24
|
+
apiKey: 'ws_abc123.your-secret-key',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Track a custom event
|
|
28
|
+
analytics.track('button_clicked', { label: 'Sign Up' });
|
|
29
|
+
|
|
30
|
+
// Track a page view
|
|
31
|
+
analytics.page('Home');
|
|
32
|
+
|
|
33
|
+
// Identify a user
|
|
34
|
+
analytics.identify('user_42', {
|
|
35
|
+
email: 'jane@example.com',
|
|
36
|
+
plan: 'pro',
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Configuration
|
|
41
|
+
|
|
42
|
+
| Option | Type | Default | Description |
|
|
43
|
+
| --------------- | -------- | ------------------------------ | --------------------------------------------- |
|
|
44
|
+
| `apiKey` | `string` | **(required)** | Your AppsPro API key |
|
|
45
|
+
| `endpoint` | `string` | `https://collect.appspro.io` | Collector endpoint URL |
|
|
46
|
+
| `flushInterval` | `number` | `10000` | Auto-flush interval in milliseconds |
|
|
47
|
+
| `flushSize` | `number` | `20` | Number of queued events that triggers a flush |
|
|
48
|
+
| `maxRetries` | `number` | `3` | Max retry attempts on network failure |
|
|
49
|
+
| `workspaceId` | `string` | Extracted from `apiKey` prefix | Override the workspace ID |
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
const analytics = new AppsPro({
|
|
53
|
+
apiKey: 'ws_abc123.your-secret-key',
|
|
54
|
+
endpoint: 'https://your-collector.example.com',
|
|
55
|
+
flushInterval: 5000,
|
|
56
|
+
flushSize: 10,
|
|
57
|
+
maxRetries: 5,
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## API Reference
|
|
62
|
+
|
|
63
|
+
### `new AppsPro(config)`
|
|
64
|
+
|
|
65
|
+
Creates a new SDK instance. An anonymous ID is generated automatically.
|
|
66
|
+
|
|
67
|
+
### `analytics.track(event, properties?)`
|
|
68
|
+
|
|
69
|
+
Track a custom event.
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
analytics.track('purchase', {
|
|
73
|
+
amount: 49.99,
|
|
74
|
+
currency: 'USD',
|
|
75
|
+
item_id: 'sku_001',
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### `analytics.identify(userId, traits?)`
|
|
80
|
+
|
|
81
|
+
Identify a user and merge the anonymous session with a known user ID. Sends
|
|
82
|
+
an identify call to `POST /v1/identify` immediately and sets the `user_id`
|
|
83
|
+
for all subsequent events.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
analytics.identify('user_42', {
|
|
87
|
+
email: 'jane@example.com',
|
|
88
|
+
name: 'Jane Doe',
|
|
89
|
+
plan: 'pro',
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### `analytics.page(name?, properties?)`
|
|
94
|
+
|
|
95
|
+
Track a page view. Automatically captures `page_url` and `referrer`.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
analytics.page('Pricing', { experiment: 'variant_b' });
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### `analytics.setAnonymousId(id)`
|
|
102
|
+
|
|
103
|
+
Override the auto-generated anonymous ID (e.g. restore from a cookie).
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
analytics.setAnonymousId('anon_from_cookie_abc');
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### `analytics.flush()`
|
|
110
|
+
|
|
111
|
+
Flush all pending events to the collector immediately. Returns a `Promise`.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
await analytics.flush();
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### `analytics.shutdown()`
|
|
118
|
+
|
|
119
|
+
Stop the automatic flush timer and send remaining events. Call this when your
|
|
120
|
+
application is shutting down.
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
await analytics.shutdown();
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### `analytics.reset()`
|
|
127
|
+
|
|
128
|
+
Reset identity state (e.g. on user logout). Generates a new anonymous ID and
|
|
129
|
+
clears the known user ID.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
analytics.reset();
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Server-Side Usage (Node.js)
|
|
136
|
+
|
|
137
|
+
The SDK works in Node.js 18+ (which ships with global `fetch`). Browser-only
|
|
138
|
+
context fields (page URL, referrer, user agent, screen size, UTM params) are
|
|
139
|
+
gracefully skipped in server environments.
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
import { AppsPro } from '@appspro/sdk-js';
|
|
143
|
+
|
|
144
|
+
const analytics = new AppsPro({
|
|
145
|
+
apiKey: 'ws_abc123.your-secret-key',
|
|
146
|
+
endpoint: 'https://collect.appspro.io',
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Server-side tracking
|
|
150
|
+
analytics.track('api_request', {
|
|
151
|
+
path: '/api/users',
|
|
152
|
+
method: 'GET',
|
|
153
|
+
status: 200,
|
|
154
|
+
duration_ms: 42,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Important: flush before process exits
|
|
158
|
+
process.on('SIGTERM', async () => {
|
|
159
|
+
await analytics.shutdown();
|
|
160
|
+
process.exit(0);
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Event Payload
|
|
165
|
+
|
|
166
|
+
Each event sent to the collector follows this shape:
|
|
167
|
+
|
|
168
|
+
```jsonc
|
|
169
|
+
{
|
|
170
|
+
"event_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
171
|
+
"event": "button_clicked",
|
|
172
|
+
"anonymous_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
173
|
+
"user_id": "user_42", // present after identify()
|
|
174
|
+
"properties": { "label": "Sign Up" },
|
|
175
|
+
"context": {
|
|
176
|
+
"page_url": "https://example.com/pricing",
|
|
177
|
+
"referrer": "https://google.com",
|
|
178
|
+
"user_agent": "Mozilla/5.0 ...",
|
|
179
|
+
"locale": "en-US",
|
|
180
|
+
"timezone": "America/New_York",
|
|
181
|
+
"screen_width": 1920,
|
|
182
|
+
"screen_height": 1080,
|
|
183
|
+
"utm_source": "google",
|
|
184
|
+
"utm_medium": "cpc",
|
|
185
|
+
},
|
|
186
|
+
"timestamp": "2026-02-06T12:00:00.000Z",
|
|
187
|
+
"workspace_id": "ws_abc123",
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## How It Works
|
|
192
|
+
|
|
193
|
+
1. **Queueing** — Events are queued in memory.
|
|
194
|
+
2. **Auto-flush** — The queue flushes when it reaches `flushSize` events or
|
|
195
|
+
every `flushInterval` milliseconds, whichever comes first.
|
|
196
|
+
3. **Batching** — Flushed events are sent as a single `POST /v1/events/batch`
|
|
197
|
+
request with `Authorization: Bearer <apiKey>`.
|
|
198
|
+
4. **Retry** — Failed requests are retried with exponential backoff (up to
|
|
199
|
+
`maxRetries` times). Events are re-queued on failure.
|
|
200
|
+
5. **Identify** — `identify()` sends to `POST /v1/identify` immediately with
|
|
201
|
+
its own retry logic.
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { AppsProConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The main AppsPro analytics client.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { AppsPro } from '@appspro/sdk-js';
|
|
8
|
+
*
|
|
9
|
+
* const analytics = new AppsPro({ apiKey: 'ws_abc123.secret' });
|
|
10
|
+
*
|
|
11
|
+
* analytics.track('button_clicked', { label: 'Sign Up' });
|
|
12
|
+
* analytics.page();
|
|
13
|
+
* analytics.identify('user_42', { email: 'user@example.com' });
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare class AppsPro {
|
|
17
|
+
private readonly apiKey;
|
|
18
|
+
private readonly endpoint;
|
|
19
|
+
private readonly maxRetries;
|
|
20
|
+
private readonly workspaceId;
|
|
21
|
+
private anonymousId;
|
|
22
|
+
private userId;
|
|
23
|
+
private queue;
|
|
24
|
+
/**
|
|
25
|
+
* Create a new AppsPro analytics client.
|
|
26
|
+
*
|
|
27
|
+
* @param config - SDK configuration. At minimum `apiKey` is required.
|
|
28
|
+
*/
|
|
29
|
+
constructor(config: AppsProConfig);
|
|
30
|
+
/**
|
|
31
|
+
* Track a custom event.
|
|
32
|
+
*
|
|
33
|
+
* @param event - The event name (e.g. `"button_clicked"`).
|
|
34
|
+
* @param properties - Optional key-value properties attached to the event.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* analytics.track('purchase', { amount: 49.99, currency: 'USD' });
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
track(event: string, properties?: Record<string, unknown>): void;
|
|
42
|
+
/**
|
|
43
|
+
* Identify a user and merge the anonymous session with a known user ID.
|
|
44
|
+
*
|
|
45
|
+
* The identify call is sent to `POST /v1/identify` immediately (with
|
|
46
|
+
* retry). Subsequent `track()` / `page()` calls will include the
|
|
47
|
+
* `user_id`.
|
|
48
|
+
*
|
|
49
|
+
* @param userId - The unique user identifier in your system.
|
|
50
|
+
* @param traits - Optional traits describing the user (email, name, etc.).
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* analytics.identify('user_42', { email: 'j@example.com', plan: 'pro' });
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
identify(userId: string, traits?: Record<string, unknown>): void;
|
|
58
|
+
/**
|
|
59
|
+
* Track a page view.
|
|
60
|
+
*
|
|
61
|
+
* This is sugar over {@link track} with event name `"page_view"` and
|
|
62
|
+
* automatic `page_name`, `page_url`, and `referrer` properties.
|
|
63
|
+
*
|
|
64
|
+
* @param name - Optional human-readable page name.
|
|
65
|
+
* @param properties - Additional properties to attach.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* analytics.page('Home');
|
|
70
|
+
* analytics.page('Pricing', { experiment: 'v2' });
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
page(name?: string, properties?: Record<string, unknown>): void;
|
|
74
|
+
/**
|
|
75
|
+
* Override the anonymous ID.
|
|
76
|
+
*
|
|
77
|
+
* By default a random UUID is generated on construction. Use this method
|
|
78
|
+
* to restore a previously persisted anonymous ID (e.g. from a cookie).
|
|
79
|
+
*
|
|
80
|
+
* @param id - The anonymous ID to use.
|
|
81
|
+
*/
|
|
82
|
+
setAnonymousId(id: string): void;
|
|
83
|
+
/**
|
|
84
|
+
* Flush all pending events to the collector immediately.
|
|
85
|
+
*
|
|
86
|
+
* @returns A promise that resolves once the flush network call completes
|
|
87
|
+
* (or rejects if all retries are exhausted).
|
|
88
|
+
*/
|
|
89
|
+
flush(): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Gracefully shut down the SDK.
|
|
92
|
+
*
|
|
93
|
+
* Stops the automatic flush timer and sends any remaining queued events.
|
|
94
|
+
* After calling `shutdown()` the client should not be used again.
|
|
95
|
+
*/
|
|
96
|
+
shutdown(): Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* Reset identity state (e.g. on user logout).
|
|
99
|
+
*
|
|
100
|
+
* Generates a new anonymous ID and clears the known user ID. Pending
|
|
101
|
+
* events already in the queue are **not** affected.
|
|
102
|
+
*/
|
|
103
|
+
reset(): void;
|
|
104
|
+
/** Build a {@link TrackEvent} payload. */
|
|
105
|
+
private buildEvent;
|
|
106
|
+
/** Capture the current environment context. */
|
|
107
|
+
private getContext;
|
|
108
|
+
/** Send an identify payload to POST /v1/identify with retry. */
|
|
109
|
+
private sendIdentify;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAA6C,MAAM,YAAY,CAAC;AAa3F;;;;;;;;;;;;;GAaG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IAEjD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,KAAK,CAAa;IAE1B;;;;OAIG;gBACS,MAAM,EAAE,aAAa;IAwBjC;;;;;;;;;;OAUG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAKhE;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAgBhE;;;;;;;;;;;;;;OAcG;IACH,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAa/D;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIhC;;;;;OAKG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;;;OAKG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAK/B;;;;;OAKG;IACH,KAAK,IAAI,IAAI;IASb,0CAA0C;IAC1C,OAAO,CAAC,UAAU;IAqBlB,+CAA+C;IAC/C,OAAO,CAAC,UAAU;IAIlB,gEAAgE;YAClD,YAAY;CA0B3B"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// AppsPro SDK — Main Client
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
import { EventQueue } from './queue.js';
|
|
5
|
+
import { captureContext } from './context.js';
|
|
6
|
+
import { generateUUID, extractWorkspaceId, retryWithBackoff } from './utils.js';
|
|
7
|
+
/** Default configuration values. */
|
|
8
|
+
const DEFAULTS = {
|
|
9
|
+
endpoint: 'https://collect.appspro.io',
|
|
10
|
+
flushInterval: 10000,
|
|
11
|
+
flushSize: 20,
|
|
12
|
+
maxRetries: 3,
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* The main AppsPro analytics client.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { AppsPro } from '@appspro/sdk-js';
|
|
20
|
+
*
|
|
21
|
+
* const analytics = new AppsPro({ apiKey: 'ws_abc123.secret' });
|
|
22
|
+
*
|
|
23
|
+
* analytics.track('button_clicked', { label: 'Sign Up' });
|
|
24
|
+
* analytics.page();
|
|
25
|
+
* analytics.identify('user_42', { email: 'user@example.com' });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class AppsPro {
|
|
29
|
+
/**
|
|
30
|
+
* Create a new AppsPro analytics client.
|
|
31
|
+
*
|
|
32
|
+
* @param config - SDK configuration. At minimum `apiKey` is required.
|
|
33
|
+
*/
|
|
34
|
+
constructor(config) {
|
|
35
|
+
if (!config.apiKey) {
|
|
36
|
+
throw new Error('AppsPro: apiKey is required');
|
|
37
|
+
}
|
|
38
|
+
this.apiKey = config.apiKey;
|
|
39
|
+
this.endpoint = (config.endpoint ?? DEFAULTS.endpoint).replace(/\/+$/, '');
|
|
40
|
+
this.maxRetries = config.maxRetries ?? DEFAULTS.maxRetries;
|
|
41
|
+
this.workspaceId = config.workspaceId ?? extractWorkspaceId(config.apiKey);
|
|
42
|
+
this.anonymousId = generateUUID();
|
|
43
|
+
this.queue = new EventQueue({
|
|
44
|
+
endpoint: this.endpoint,
|
|
45
|
+
apiKey: this.apiKey,
|
|
46
|
+
flushSize: config.flushSize ?? DEFAULTS.flushSize,
|
|
47
|
+
flushInterval: config.flushInterval ?? DEFAULTS.flushInterval,
|
|
48
|
+
maxRetries: this.maxRetries,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
// -----------------------------------------------------------------------
|
|
52
|
+
// Public API
|
|
53
|
+
// -----------------------------------------------------------------------
|
|
54
|
+
/**
|
|
55
|
+
* Track a custom event.
|
|
56
|
+
*
|
|
57
|
+
* @param event - The event name (e.g. `"button_clicked"`).
|
|
58
|
+
* @param properties - Optional key-value properties attached to the event.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* analytics.track('purchase', { amount: 49.99, currency: 'USD' });
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
track(event, properties) {
|
|
66
|
+
const payload = this.buildEvent(event, properties);
|
|
67
|
+
this.queue.enqueue(payload);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Identify a user and merge the anonymous session with a known user ID.
|
|
71
|
+
*
|
|
72
|
+
* The identify call is sent to `POST /v1/identify` immediately (with
|
|
73
|
+
* retry). Subsequent `track()` / `page()` calls will include the
|
|
74
|
+
* `user_id`.
|
|
75
|
+
*
|
|
76
|
+
* @param userId - The unique user identifier in your system.
|
|
77
|
+
* @param traits - Optional traits describing the user (email, name, etc.).
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts
|
|
81
|
+
* analytics.identify('user_42', { email: 'j@example.com', plan: 'pro' });
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
identify(userId, traits) {
|
|
85
|
+
this.userId = userId;
|
|
86
|
+
const payload = {
|
|
87
|
+
anonymous_id: this.anonymousId,
|
|
88
|
+
user_id: userId,
|
|
89
|
+
traits,
|
|
90
|
+
context: this.getContext(),
|
|
91
|
+
timestamp: new Date().toISOString(),
|
|
92
|
+
workspace_id: this.workspaceId,
|
|
93
|
+
};
|
|
94
|
+
// Fire-and-forget with retry
|
|
95
|
+
void this.sendIdentify(payload);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Track a page view.
|
|
99
|
+
*
|
|
100
|
+
* This is sugar over {@link track} with event name `"page_view"` and
|
|
101
|
+
* automatic `page_name`, `page_url`, and `referrer` properties.
|
|
102
|
+
*
|
|
103
|
+
* @param name - Optional human-readable page name.
|
|
104
|
+
* @param properties - Additional properties to attach.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* analytics.page('Home');
|
|
109
|
+
* analytics.page('Pricing', { experiment: 'v2' });
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
page(name, properties) {
|
|
113
|
+
const ctx = this.getContext();
|
|
114
|
+
const pageProps = {
|
|
115
|
+
...properties,
|
|
116
|
+
};
|
|
117
|
+
if (name)
|
|
118
|
+
pageProps['page_name'] = name;
|
|
119
|
+
if (ctx.page_url)
|
|
120
|
+
pageProps['page_url'] = ctx.page_url;
|
|
121
|
+
if (ctx.referrer)
|
|
122
|
+
pageProps['referrer'] = ctx.referrer;
|
|
123
|
+
this.track('page_view', pageProps);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Override the anonymous ID.
|
|
127
|
+
*
|
|
128
|
+
* By default a random UUID is generated on construction. Use this method
|
|
129
|
+
* to restore a previously persisted anonymous ID (e.g. from a cookie).
|
|
130
|
+
*
|
|
131
|
+
* @param id - The anonymous ID to use.
|
|
132
|
+
*/
|
|
133
|
+
setAnonymousId(id) {
|
|
134
|
+
this.anonymousId = id;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Flush all pending events to the collector immediately.
|
|
138
|
+
*
|
|
139
|
+
* @returns A promise that resolves once the flush network call completes
|
|
140
|
+
* (or rejects if all retries are exhausted).
|
|
141
|
+
*/
|
|
142
|
+
async flush() {
|
|
143
|
+
await this.queue.flush();
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Gracefully shut down the SDK.
|
|
147
|
+
*
|
|
148
|
+
* Stops the automatic flush timer and sends any remaining queued events.
|
|
149
|
+
* After calling `shutdown()` the client should not be used again.
|
|
150
|
+
*/
|
|
151
|
+
async shutdown() {
|
|
152
|
+
this.queue.stopTimer();
|
|
153
|
+
await this.queue.flush();
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Reset identity state (e.g. on user logout).
|
|
157
|
+
*
|
|
158
|
+
* Generates a new anonymous ID and clears the known user ID. Pending
|
|
159
|
+
* events already in the queue are **not** affected.
|
|
160
|
+
*/
|
|
161
|
+
reset() {
|
|
162
|
+
this.anonymousId = generateUUID();
|
|
163
|
+
this.userId = undefined;
|
|
164
|
+
}
|
|
165
|
+
// -----------------------------------------------------------------------
|
|
166
|
+
// Internals
|
|
167
|
+
// -----------------------------------------------------------------------
|
|
168
|
+
/** Build a {@link TrackEvent} payload. */
|
|
169
|
+
buildEvent(event, properties) {
|
|
170
|
+
const payload = {
|
|
171
|
+
event_id: generateUUID(),
|
|
172
|
+
event,
|
|
173
|
+
anonymous_id: this.anonymousId,
|
|
174
|
+
properties,
|
|
175
|
+
context: this.getContext(),
|
|
176
|
+
timestamp: new Date().toISOString(),
|
|
177
|
+
};
|
|
178
|
+
if (this.userId) {
|
|
179
|
+
payload.user_id = this.userId;
|
|
180
|
+
}
|
|
181
|
+
if (this.workspaceId) {
|
|
182
|
+
payload.workspace_id = this.workspaceId;
|
|
183
|
+
}
|
|
184
|
+
return payload;
|
|
185
|
+
}
|
|
186
|
+
/** Capture the current environment context. */
|
|
187
|
+
getContext() {
|
|
188
|
+
return captureContext();
|
|
189
|
+
}
|
|
190
|
+
/** Send an identify payload to POST /v1/identify with retry. */
|
|
191
|
+
async sendIdentify(payload) {
|
|
192
|
+
const url = `${this.endpoint}/v1/identify`;
|
|
193
|
+
try {
|
|
194
|
+
await retryWithBackoff(async () => {
|
|
195
|
+
const res = await fetch(url, {
|
|
196
|
+
method: 'POST',
|
|
197
|
+
headers: {
|
|
198
|
+
'Content-Type': 'application/json',
|
|
199
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
200
|
+
},
|
|
201
|
+
body: JSON.stringify(payload),
|
|
202
|
+
});
|
|
203
|
+
if (!res.ok) {
|
|
204
|
+
throw new Error(`AppsPro identify responded with ${res.status}: ${res.statusText}`);
|
|
205
|
+
}
|
|
206
|
+
}, { maxRetries: this.maxRetries });
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
// Silently drop after retries exhausted — analytics should never
|
|
210
|
+
// break the host application.
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { EventContext } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Capture contextual information from the current environment.
|
|
4
|
+
*
|
|
5
|
+
* **Browser** — includes page URL, referrer, user agent, locale, screen
|
|
6
|
+
* dimensions, timezone, and UTM parameters.
|
|
7
|
+
*
|
|
8
|
+
* **Node.js / SSR** — only fields available from the runtime are populated
|
|
9
|
+
* (e.g. locale, timezone).
|
|
10
|
+
*
|
|
11
|
+
* @returns An {@link EventContext} with all available fields populated.
|
|
12
|
+
*/
|
|
13
|
+
export declare function captureContext(): EventContext;
|
|
14
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAc/C;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,IAAI,YAAY,CAgE7C"}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// AppsPro SDK — Automatic Context Capture
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
import { parseUtmParams } from './utils.js';
|
|
5
|
+
/**
|
|
6
|
+
* Detect whether the current runtime is a browser environment.
|
|
7
|
+
*/
|
|
8
|
+
function isBrowser() {
|
|
9
|
+
return (typeof window !== 'undefined' &&
|
|
10
|
+
typeof document !== 'undefined' &&
|
|
11
|
+
typeof window.location !== 'undefined');
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Capture contextual information from the current environment.
|
|
15
|
+
*
|
|
16
|
+
* **Browser** — includes page URL, referrer, user agent, locale, screen
|
|
17
|
+
* dimensions, timezone, and UTM parameters.
|
|
18
|
+
*
|
|
19
|
+
* **Node.js / SSR** — only fields available from the runtime are populated
|
|
20
|
+
* (e.g. locale, timezone).
|
|
21
|
+
*
|
|
22
|
+
* @returns An {@link EventContext} with all available fields populated.
|
|
23
|
+
*/
|
|
24
|
+
export function captureContext() {
|
|
25
|
+
const ctx = {};
|
|
26
|
+
// Timezone (available in both browser & Node)
|
|
27
|
+
try {
|
|
28
|
+
ctx.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// ignore
|
|
32
|
+
}
|
|
33
|
+
// Locale
|
|
34
|
+
if (typeof navigator !== 'undefined' && navigator.language) {
|
|
35
|
+
ctx.locale = navigator.language;
|
|
36
|
+
}
|
|
37
|
+
if (!isBrowser()) {
|
|
38
|
+
return ctx;
|
|
39
|
+
}
|
|
40
|
+
// --- Browser-only fields ---------------------------------------------------
|
|
41
|
+
try {
|
|
42
|
+
ctx.page_url = window.location.href;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// ignore
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
if (document.referrer) {
|
|
49
|
+
ctx.referrer = document.referrer;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// ignore
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
ctx.user_agent = navigator.userAgent;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// ignore
|
|
60
|
+
}
|
|
61
|
+
// Screen dimensions
|
|
62
|
+
try {
|
|
63
|
+
if (typeof screen !== 'undefined') {
|
|
64
|
+
ctx.screen_width = screen.width;
|
|
65
|
+
ctx.screen_height = screen.height;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// ignore
|
|
70
|
+
}
|
|
71
|
+
// UTM parameters from current URL
|
|
72
|
+
try {
|
|
73
|
+
const utms = parseUtmParams(window.location.href);
|
|
74
|
+
if (utms.utm_source)
|
|
75
|
+
ctx.utm_source = utms.utm_source;
|
|
76
|
+
if (utms.utm_medium)
|
|
77
|
+
ctx.utm_medium = utms.utm_medium;
|
|
78
|
+
if (utms.utm_campaign)
|
|
79
|
+
ctx.utm_campaign = utms.utm_campaign;
|
|
80
|
+
if (utms.utm_content)
|
|
81
|
+
ctx.utm_content = utms.utm_content;
|
|
82
|
+
if (utms.utm_term)
|
|
83
|
+
ctx.utm_term = utms.utm_term;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// ignore
|
|
87
|
+
}
|
|
88
|
+
return ctx;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,0CAA0C;AAC1C,8EAA8E;AAG9E,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C;;GAEG;AACH,SAAS,SAAS;IAChB,OAAO,CACL,OAAO,MAAM,KAAK,WAAW;QAC7B,OAAO,QAAQ,KAAK,WAAW;QAC/B,OAAO,MAAM,CAAC,QAAQ,KAAK,WAAW,CACvC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,GAAG,GAAiB,EAAE,CAAC;IAE7B,8CAA8C;IAC9C,IAAI,CAAC;QACH,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC,eAAe,EAAE,CAAC,QAAQ,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;IAED,SAAS;IACT,IAAI,OAAO,SAAS,KAAK,WAAW,IAAI,SAAS,CAAC,QAAQ,EAAE,CAAC;QAC3D,GAAG,CAAC,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC;IAClC,CAAC;IAED,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;QACjB,OAAO,GAAG,CAAC;IACb,CAAC;IAED,8EAA8E;IAE9E,IAAI,CAAC;QACH,GAAG,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;IAED,IAAI,CAAC;QACH,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACtB,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC;QACnC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;IAED,IAAI,CAAC;QACH,GAAG,CAAC,UAAU,GAAG,SAAS,CAAC,SAAS,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;IAED,oBAAoB;IACpB,IAAI,CAAC;QACH,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,GAAG,CAAC,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC;YAChC,GAAG,CAAC,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC;QACpC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;IAED,kCAAkC;IAClC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,IAAI,CAAC,UAAU;YAAE,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QACtD,IAAI,IAAI,CAAC,UAAU;YAAE,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QACtD,IAAI,IAAI,CAAC,YAAY;YAAE,GAAG,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;QAC5D,IAAI,IAAI,CAAC,WAAW;YAAE,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QACzD,IAAI,IAAI,CAAC,QAAQ;YAAE,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { AppsPro } from './client.js';
|
|
2
|
+
export type { AppsProConfig, EventContext, TrackEvent, BatchPayload, IdentifyPayload, } from './types.js';
|
|
3
|
+
export { generateUUID, parseUtmParams } from './utils.js';
|
|
4
|
+
export type { UtmParams } from './utils.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,YAAY,EACV,aAAa,EACb,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC1D,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// AppsPro SDK — Public API
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
//
|
|
5
|
+
// This is the main entry point for `@appspro/sdk-js`.
|
|
6
|
+
// Only symbols that are part of the public API are re-exported here.
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
export { AppsPro } from './client.js';
|
|
9
|
+
export { generateUUID, parseUtmParams } from './utils.js';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAC9E,EAAE;AACF,sDAAsD;AACtD,qEAAqE;AACrE,8EAA8E;AAE9E,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAUtC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/queue.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { TrackEvent } from './types.js';
|
|
2
|
+
export interface QueueOptions {
|
|
3
|
+
/** Collector endpoint (no trailing slash). */
|
|
4
|
+
endpoint: string;
|
|
5
|
+
/** API key sent as Bearer token. */
|
|
6
|
+
apiKey: string;
|
|
7
|
+
/** Max events before an automatic flush. */
|
|
8
|
+
flushSize: number;
|
|
9
|
+
/** Interval (ms) between automatic flushes. */
|
|
10
|
+
flushInterval: number;
|
|
11
|
+
/** Max retry attempts per network request. */
|
|
12
|
+
maxRetries: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* In-memory event queue that batches events and flushes them to the
|
|
16
|
+
* AppsPro collector at configurable size/time thresholds.
|
|
17
|
+
*/
|
|
18
|
+
export declare class EventQueue {
|
|
19
|
+
private queue;
|
|
20
|
+
private timer;
|
|
21
|
+
private flushing;
|
|
22
|
+
private readonly opts;
|
|
23
|
+
constructor(opts: QueueOptions);
|
|
24
|
+
/**
|
|
25
|
+
* Add an event to the queue. Triggers an automatic flush when
|
|
26
|
+
* `flushSize` is reached.
|
|
27
|
+
*/
|
|
28
|
+
enqueue(event: TrackEvent): void;
|
|
29
|
+
/**
|
|
30
|
+
* Flush all queued events to the collector immediately.
|
|
31
|
+
*
|
|
32
|
+
* Safe to call concurrently — concurrent flushes are serialised
|
|
33
|
+
* internally so events are never sent twice.
|
|
34
|
+
*/
|
|
35
|
+
flush(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Stop the automatic flush timer. Call this during shutdown.
|
|
38
|
+
*/
|
|
39
|
+
stopTimer(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Returns the number of events currently queued.
|
|
42
|
+
*/
|
|
43
|
+
get length(): number;
|
|
44
|
+
private startTimer;
|
|
45
|
+
/**
|
|
46
|
+
* Send a batch of events to POST /v1/events/batch with retry logic.
|
|
47
|
+
*/
|
|
48
|
+
private sendBatch;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,YAAY,CAAC;AAG3D,MAAM,WAAW,YAAY;IAC3B,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,aAAa,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;gBAExB,IAAI,EAAE,YAAY;IAS9B;;;OAGG;IACH,OAAO,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAShC;;;;;OAKG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB5B;;OAEG;IACH,SAAS,IAAI,IAAI;IAOjB;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAEnB;IAMD,OAAO,CAAC,UAAU;IAalB;;OAEG;YACW,SAAS;CAsBxB"}
|
package/dist/queue.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// AppsPro SDK — Event Queue
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
import { retryWithBackoff } from './utils.js';
|
|
5
|
+
/**
|
|
6
|
+
* In-memory event queue that batches events and flushes them to the
|
|
7
|
+
* AppsPro collector at configurable size/time thresholds.
|
|
8
|
+
*/
|
|
9
|
+
export class EventQueue {
|
|
10
|
+
constructor(opts) {
|
|
11
|
+
this.queue = [];
|
|
12
|
+
this.timer = null;
|
|
13
|
+
this.flushing = false;
|
|
14
|
+
this.opts = opts;
|
|
15
|
+
this.startTimer();
|
|
16
|
+
}
|
|
17
|
+
// -------------------------------------------------------------------------
|
|
18
|
+
// Public API
|
|
19
|
+
// -------------------------------------------------------------------------
|
|
20
|
+
/**
|
|
21
|
+
* Add an event to the queue. Triggers an automatic flush when
|
|
22
|
+
* `flushSize` is reached.
|
|
23
|
+
*/
|
|
24
|
+
enqueue(event) {
|
|
25
|
+
this.queue.push(event);
|
|
26
|
+
if (this.queue.length >= this.opts.flushSize) {
|
|
27
|
+
// Fire-and-forget; errors are swallowed internally
|
|
28
|
+
void this.flush();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Flush all queued events to the collector immediately.
|
|
33
|
+
*
|
|
34
|
+
* Safe to call concurrently — concurrent flushes are serialised
|
|
35
|
+
* internally so events are never sent twice.
|
|
36
|
+
*/
|
|
37
|
+
async flush() {
|
|
38
|
+
if (this.flushing || this.queue.length === 0)
|
|
39
|
+
return;
|
|
40
|
+
this.flushing = true;
|
|
41
|
+
// Snapshot the current queue and clear it so new events can accumulate
|
|
42
|
+
// while the network request is in flight.
|
|
43
|
+
const batch = this.queue.splice(0, this.queue.length);
|
|
44
|
+
try {
|
|
45
|
+
await this.sendBatch(batch);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Re-enqueue events that failed to send (prepend to preserve order)
|
|
49
|
+
this.queue.unshift(...batch);
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
this.flushing = false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Stop the automatic flush timer. Call this during shutdown.
|
|
57
|
+
*/
|
|
58
|
+
stopTimer() {
|
|
59
|
+
if (this.timer !== null) {
|
|
60
|
+
clearInterval(this.timer);
|
|
61
|
+
this.timer = null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Returns the number of events currently queued.
|
|
66
|
+
*/
|
|
67
|
+
get length() {
|
|
68
|
+
return this.queue.length;
|
|
69
|
+
}
|
|
70
|
+
// -------------------------------------------------------------------------
|
|
71
|
+
// Internals
|
|
72
|
+
// -------------------------------------------------------------------------
|
|
73
|
+
startTimer() {
|
|
74
|
+
if (this.opts.flushInterval > 0) {
|
|
75
|
+
this.timer = setInterval(() => {
|
|
76
|
+
void this.flush();
|
|
77
|
+
}, this.opts.flushInterval);
|
|
78
|
+
// Allow the Node.js process to exit even if the timer is active.
|
|
79
|
+
if (typeof this.timer === 'object' && 'unref' in this.timer) {
|
|
80
|
+
this.timer.unref();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Send a batch of events to POST /v1/events/batch with retry logic.
|
|
86
|
+
*/
|
|
87
|
+
async sendBatch(batch) {
|
|
88
|
+
const url = `${this.opts.endpoint}/v1/events/batch`;
|
|
89
|
+
const payload = { batch };
|
|
90
|
+
await retryWithBackoff(async () => {
|
|
91
|
+
const res = await fetch(url, {
|
|
92
|
+
method: 'POST',
|
|
93
|
+
headers: {
|
|
94
|
+
'Content-Type': 'application/json',
|
|
95
|
+
Authorization: `Bearer ${this.opts.apiKey}`,
|
|
96
|
+
},
|
|
97
|
+
body: JSON.stringify(payload),
|
|
98
|
+
});
|
|
99
|
+
if (!res.ok) {
|
|
100
|
+
throw new Error(`AppsPro collector responded with ${res.status}: ${res.statusText}`);
|
|
101
|
+
}
|
|
102
|
+
}, { maxRetries: this.opts.maxRetries });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for the AppsPro SDK client.
|
|
3
|
+
*/
|
|
4
|
+
export interface AppsProConfig {
|
|
5
|
+
/** Your AppsPro API key (required). */
|
|
6
|
+
apiKey: string;
|
|
7
|
+
/**
|
|
8
|
+
* Collector endpoint URL.
|
|
9
|
+
* @default "https://collect.appspro.io"
|
|
10
|
+
*/
|
|
11
|
+
endpoint?: string;
|
|
12
|
+
/**
|
|
13
|
+
* How often (in ms) the queue is automatically flushed.
|
|
14
|
+
* @default 10000
|
|
15
|
+
*/
|
|
16
|
+
flushInterval?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Number of events that triggers an automatic flush.
|
|
19
|
+
* @default 20
|
|
20
|
+
*/
|
|
21
|
+
flushSize?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Maximum number of retries for a failed network request.
|
|
24
|
+
* @default 3
|
|
25
|
+
*/
|
|
26
|
+
maxRetries?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Optional workspace ID. When omitted the SDK attempts to extract it from
|
|
29
|
+
* the apiKey prefix (everything before the first dot).
|
|
30
|
+
*/
|
|
31
|
+
workspaceId?: string;
|
|
32
|
+
}
|
|
33
|
+
/** Context automatically captured by the SDK. */
|
|
34
|
+
export interface EventContext {
|
|
35
|
+
page_url?: string;
|
|
36
|
+
referrer?: string;
|
|
37
|
+
user_agent?: string;
|
|
38
|
+
locale?: string;
|
|
39
|
+
timezone?: string;
|
|
40
|
+
screen_width?: number;
|
|
41
|
+
screen_height?: number;
|
|
42
|
+
utm_source?: string;
|
|
43
|
+
utm_medium?: string;
|
|
44
|
+
utm_campaign?: string;
|
|
45
|
+
utm_content?: string;
|
|
46
|
+
utm_term?: string;
|
|
47
|
+
[key: string]: unknown;
|
|
48
|
+
}
|
|
49
|
+
/** Shape of a single event sent to the collector. */
|
|
50
|
+
export interface TrackEvent {
|
|
51
|
+
event_id: string;
|
|
52
|
+
event: string;
|
|
53
|
+
anonymous_id: string;
|
|
54
|
+
user_id?: string;
|
|
55
|
+
properties?: Record<string, unknown>;
|
|
56
|
+
context: EventContext;
|
|
57
|
+
timestamp: string;
|
|
58
|
+
workspace_id?: string;
|
|
59
|
+
}
|
|
60
|
+
/** Payload for the /v1/events/batch endpoint. */
|
|
61
|
+
export interface BatchPayload {
|
|
62
|
+
batch: TrackEvent[];
|
|
63
|
+
}
|
|
64
|
+
/** Payload for the /v1/identify endpoint. */
|
|
65
|
+
export interface IdentifyPayload {
|
|
66
|
+
anonymous_id: string;
|
|
67
|
+
user_id: string;
|
|
68
|
+
traits?: Record<string, unknown>;
|
|
69
|
+
context: EventContext;
|
|
70
|
+
timestamp: string;
|
|
71
|
+
workspace_id?: string;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAMD,iDAAiD;AACjD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,qDAAqD;AACrD,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,iDAAiD;AACjD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,6CAA6C;AAC7C,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,6BAA6B;AAC7B,8EAA8E"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate a RFC 4122 v4 UUID.
|
|
3
|
+
*
|
|
4
|
+
* Uses `crypto.randomUUID()` when available (modern browsers, Node >= 19).
|
|
5
|
+
* Falls back to a Math.random-based implementation for older environments.
|
|
6
|
+
*/
|
|
7
|
+
export declare function generateUUID(): string;
|
|
8
|
+
declare const UTM_PARAMS: readonly ["utm_source", "utm_medium", "utm_campaign", "utm_content", "utm_term"];
|
|
9
|
+
export type UtmParams = Partial<Record<(typeof UTM_PARAMS)[number], string>>;
|
|
10
|
+
/**
|
|
11
|
+
* Extract UTM parameters from a URL string.
|
|
12
|
+
*
|
|
13
|
+
* @param url - Full URL or query string to parse.
|
|
14
|
+
* @returns Object containing found UTM values (keys omitted if absent).
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseUtmParams(url: string): UtmParams;
|
|
17
|
+
export interface RetryOptions {
|
|
18
|
+
/** Maximum number of retry attempts (not counting the initial try). */
|
|
19
|
+
maxRetries: number;
|
|
20
|
+
/** Base delay in ms (doubled each retry). @default 1000 */
|
|
21
|
+
baseDelay?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Execute an async function with exponential-backoff retries.
|
|
25
|
+
*
|
|
26
|
+
* @param fn - The async operation to attempt.
|
|
27
|
+
* @param options - Retry configuration.
|
|
28
|
+
* @returns The resolved value of `fn`.
|
|
29
|
+
* @throws The last error if all retries are exhausted.
|
|
30
|
+
*/
|
|
31
|
+
export declare function retryWithBackoff<T>(fn: () => Promise<T>, options: RetryOptions): Promise<T>;
|
|
32
|
+
/**
|
|
33
|
+
* Returns a promise that resolves after `ms` milliseconds.
|
|
34
|
+
*/
|
|
35
|
+
export declare function sleep(ms: number): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Attempt to extract a workspace ID from an API key.
|
|
38
|
+
*
|
|
39
|
+
* Convention: keys are formatted as `ws_<workspaceId>.<secret>`.
|
|
40
|
+
* If the key does not match this pattern, returns `undefined`.
|
|
41
|
+
*/
|
|
42
|
+
export declare function extractWorkspaceId(apiKey: string): string | undefined;
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAYrC;AAMD,QAAA,MAAM,UAAU,kFAAmF,CAAC;AAEpG,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAE7E;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAsBrD;AAMD,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,CAkBjG;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAMD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOrE"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// AppsPro SDK — Utilities
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
/**
|
|
5
|
+
* Generate a RFC 4122 v4 UUID.
|
|
6
|
+
*
|
|
7
|
+
* Uses `crypto.randomUUID()` when available (modern browsers, Node >= 19).
|
|
8
|
+
* Falls back to a Math.random-based implementation for older environments.
|
|
9
|
+
*/
|
|
10
|
+
export function generateUUID() {
|
|
11
|
+
// Modern environments (browsers with secure context, Node >= 19)
|
|
12
|
+
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
|
13
|
+
return crypto.randomUUID();
|
|
14
|
+
}
|
|
15
|
+
// Fallback — RFC 4122 v4 compliant
|
|
16
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
17
|
+
const r = (Math.random() * 16) | 0;
|
|
18
|
+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
19
|
+
return v.toString(16);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// UTM parameter extraction
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
const UTM_PARAMS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'];
|
|
26
|
+
/**
|
|
27
|
+
* Extract UTM parameters from a URL string.
|
|
28
|
+
*
|
|
29
|
+
* @param url - Full URL or query string to parse.
|
|
30
|
+
* @returns Object containing found UTM values (keys omitted if absent).
|
|
31
|
+
*/
|
|
32
|
+
export function parseUtmParams(url) {
|
|
33
|
+
const params = {};
|
|
34
|
+
try {
|
|
35
|
+
const searchString = url.includes('?') ? (url.split('?')[1] ?? '') : '';
|
|
36
|
+
if (!searchString)
|
|
37
|
+
return params;
|
|
38
|
+
const pairs = searchString.split('#')[0]?.split('&') ?? [];
|
|
39
|
+
for (const pair of pairs) {
|
|
40
|
+
const eqIdx = pair.indexOf('=');
|
|
41
|
+
if (eqIdx === -1)
|
|
42
|
+
continue;
|
|
43
|
+
const key = decodeURIComponent(pair.slice(0, eqIdx));
|
|
44
|
+
const val = decodeURIComponent(pair.slice(eqIdx + 1));
|
|
45
|
+
if (UTM_PARAMS.includes(key)) {
|
|
46
|
+
params[key] = val;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Swallow malformed URLs silently
|
|
52
|
+
}
|
|
53
|
+
return params;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Execute an async function with exponential-backoff retries.
|
|
57
|
+
*
|
|
58
|
+
* @param fn - The async operation to attempt.
|
|
59
|
+
* @param options - Retry configuration.
|
|
60
|
+
* @returns The resolved value of `fn`.
|
|
61
|
+
* @throws The last error if all retries are exhausted.
|
|
62
|
+
*/
|
|
63
|
+
export async function retryWithBackoff(fn, options) {
|
|
64
|
+
const { maxRetries, baseDelay = 1000 } = options;
|
|
65
|
+
let lastError;
|
|
66
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
67
|
+
try {
|
|
68
|
+
return await fn();
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
lastError = err;
|
|
72
|
+
if (attempt < maxRetries) {
|
|
73
|
+
const jitter = Math.random() * 0.3 + 0.85; // 0.85 – 1.15
|
|
74
|
+
const delay = baseDelay * Math.pow(2, attempt) * jitter;
|
|
75
|
+
await sleep(delay);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
throw lastError;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Returns a promise that resolves after `ms` milliseconds.
|
|
83
|
+
*/
|
|
84
|
+
export function sleep(ms) {
|
|
85
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
86
|
+
}
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Workspace ID extraction
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
/**
|
|
91
|
+
* Attempt to extract a workspace ID from an API key.
|
|
92
|
+
*
|
|
93
|
+
* Convention: keys are formatted as `ws_<workspaceId>.<secret>`.
|
|
94
|
+
* If the key does not match this pattern, returns `undefined`.
|
|
95
|
+
*/
|
|
96
|
+
export function extractWorkspaceId(apiKey) {
|
|
97
|
+
if (!apiKey)
|
|
98
|
+
return undefined;
|
|
99
|
+
const dotIndex = apiKey.indexOf('.');
|
|
100
|
+
if (dotIndex > 0) {
|
|
101
|
+
return apiKey.slice(0, dotIndex);
|
|
102
|
+
}
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@appspro-dev/sdk-js",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "AppsPro Analytics SDK for browser and Node.js — zero dependencies",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.mjs",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/appspro-dev/apps-pro.git",
|
|
12
|
+
"directory": "packages/sdk-js"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"import": "./dist/index.mjs",
|
|
20
|
+
"require": "./dist/index.js",
|
|
21
|
+
"types": "./dist/index.d.ts"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"lint": "tsc --noEmit --pretty"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^20.11.0",
|
|
36
|
+
"typescript": "^5.3.0"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"analytics",
|
|
40
|
+
"tracking",
|
|
41
|
+
"appspro",
|
|
42
|
+
"events",
|
|
43
|
+
"sdk"
|
|
44
|
+
],
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18.0.0"
|
|
47
|
+
}
|
|
48
|
+
}
|