@eventra_dev/eventra-sdk 0.1.2
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 +197 -0
- package/dist/index.cjs +262 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +61 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.js +235 -0
- package/dist/index.js.map +1 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Eventra SDK
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
Production-grade TypeScript SDK for tracking product events across **browser, server, and edge runtimes**.
|
|
9
|
+
|
|
10
|
+
Eventra is designed for **deterministic ingestion**, batching efficiency, and near-zero runtime overhead.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- UUID v4 idempotency keys
|
|
17
|
+
- Safe retry policy
|
|
18
|
+
- Exponential backoff with jitter
|
|
19
|
+
- sendBeacon support for browsers
|
|
20
|
+
- Multi-runtime support (Browser / Node / Bun / Deno / Edge)
|
|
21
|
+
- Optional multi-tab leader mode
|
|
22
|
+
- Queue overflow protection
|
|
23
|
+
- Zero dependencies
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pnpm add @eventra/sdk
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
or
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install @eventra/sdk
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
# Quick Start
|
|
42
|
+
|
|
43
|
+
## Browser
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { Eventra } from "@eventra/sdk";
|
|
47
|
+
|
|
48
|
+
const tracker = new Eventra({
|
|
49
|
+
apiKey: "YOUR_API_KEY"
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
tracker.track("checkout.completed", {
|
|
53
|
+
userId: "user_123"
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Node / NestJS
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { Eventra } from "@eventra/sdk";
|
|
63
|
+
|
|
64
|
+
const tracker = new Eventra({
|
|
65
|
+
apiKey: process.env.EVENTRA_API_KEY!
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
tracker.track("invoice.created", {
|
|
69
|
+
userId: "user_123"
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
# Configuration
|
|
76
|
+
|
|
77
|
+
| Option | Default |
|
|
78
|
+
|------|------|
|
|
79
|
+
| flushInterval | 2000 ms |
|
|
80
|
+
| maxBatchSize | 50 |
|
|
81
|
+
| maxRetries | 3 |
|
|
82
|
+
| maxQueueSize | 10000 |
|
|
83
|
+
| retryBaseDelayMs | 300 |
|
|
84
|
+
|
|
85
|
+
Example:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
const tracker = new Eventra({
|
|
89
|
+
apiKey: "API_KEY",
|
|
90
|
+
flushInterval: 2000,
|
|
91
|
+
maxBatchSize: 50
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
# Reliability Model
|
|
98
|
+
|
|
99
|
+
### Idempotency
|
|
100
|
+
|
|
101
|
+
Each event receives a **UUID v4 idempotency key**, ensuring:
|
|
102
|
+
|
|
103
|
+
- safe retries
|
|
104
|
+
- duplicate suppression
|
|
105
|
+
- multi-tab safety
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
### Retry Policy
|
|
110
|
+
|
|
111
|
+
The SDK retries only when safe:
|
|
112
|
+
|
|
113
|
+
✔ network failures
|
|
114
|
+
✔ HTTP 5xx
|
|
115
|
+
|
|
116
|
+
It does **not retry**:
|
|
117
|
+
|
|
118
|
+
✖ HTTP 4xx
|
|
119
|
+
✖ validation errors
|
|
120
|
+
✖ authentication errors
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
### Backoff Strategy
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
delay = base * 2^attempt * jitter(0.5–1.5)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
# Multi-Tab Mode
|
|
133
|
+
|
|
134
|
+
Default:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
multiTabMode: "independent"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Leader mode:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
const tracker = new Eventra({
|
|
144
|
+
apiKey: "API_KEY",
|
|
145
|
+
multiTabMode: "leader"
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
One tab becomes the sender and reduces duplicate traffic.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
# Architecture
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
Application
|
|
157
|
+
│
|
|
158
|
+
▼
|
|
159
|
+
Eventra SDK
|
|
160
|
+
│
|
|
161
|
+
batching + retry
|
|
162
|
+
│
|
|
163
|
+
▼
|
|
164
|
+
Eventra Ingest API
|
|
165
|
+
│
|
|
166
|
+
idempotent pipeline
|
|
167
|
+
│
|
|
168
|
+
▼
|
|
169
|
+
Eventra Event Store
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
# Best Practices
|
|
175
|
+
|
|
176
|
+
Use semantic event names.
|
|
177
|
+
|
|
178
|
+
Good:
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
invoice.created
|
|
182
|
+
checkout.completed
|
|
183
|
+
team.invited
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Bad:
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
button_clicked
|
|
190
|
+
modal_opened
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
# License
|
|
196
|
+
|
|
197
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
Eventra: () => Eventra
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/client.ts
|
|
28
|
+
var SDK_VERSION = "0.1.2";
|
|
29
|
+
var DEFAULTS = {
|
|
30
|
+
endpoint: "http://api.eventra.dev/api/v1/ingest/batch",
|
|
31
|
+
flushInterval: 2e3,
|
|
32
|
+
maxBatchSize: 50,
|
|
33
|
+
maxQueueSize: 1e4,
|
|
34
|
+
maxRetries: 3,
|
|
35
|
+
retryBaseDelay: 300
|
|
36
|
+
};
|
|
37
|
+
function getGlobalFetch() {
|
|
38
|
+
if (typeof fetch !== "undefined") {
|
|
39
|
+
return fetch.bind(globalThis);
|
|
40
|
+
}
|
|
41
|
+
return void 0;
|
|
42
|
+
}
|
|
43
|
+
function generateUUIDv4() {
|
|
44
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
45
|
+
return crypto.randomUUID();
|
|
46
|
+
}
|
|
47
|
+
const rnd = Math.random().toString(16).slice(2);
|
|
48
|
+
const ts = Date.now().toString(16);
|
|
49
|
+
return `${ts}-${rnd}`;
|
|
50
|
+
}
|
|
51
|
+
function isBrowser() {
|
|
52
|
+
return typeof window !== "undefined";
|
|
53
|
+
}
|
|
54
|
+
function sleep(ms) {
|
|
55
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
56
|
+
}
|
|
57
|
+
var LEADER_KEY = "__eventra_sdk_leader__";
|
|
58
|
+
var LEADER_TTL = 4e3;
|
|
59
|
+
function tryBecomeLeader() {
|
|
60
|
+
if (!isBrowser()) return true;
|
|
61
|
+
try {
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
const raw = localStorage.getItem(LEADER_KEY);
|
|
64
|
+
if (!raw) {
|
|
65
|
+
localStorage.setItem(
|
|
66
|
+
LEADER_KEY,
|
|
67
|
+
JSON.stringify({ ts: now })
|
|
68
|
+
);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
const parsed = JSON.parse(raw);
|
|
72
|
+
if (now - parsed.ts > LEADER_TTL) {
|
|
73
|
+
localStorage.setItem(
|
|
74
|
+
LEADER_KEY,
|
|
75
|
+
JSON.stringify({ ts: now })
|
|
76
|
+
);
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
} catch {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
var Eventra = class {
|
|
85
|
+
constructor(options) {
|
|
86
|
+
this.queue = [];
|
|
87
|
+
this.inFlight = false;
|
|
88
|
+
this.destroyed = false;
|
|
89
|
+
this.droppedEvents = 0;
|
|
90
|
+
if (!options.apiKey) {
|
|
91
|
+
throw new Error("Eventra: apiKey is required");
|
|
92
|
+
}
|
|
93
|
+
this.apiKey = options.apiKey;
|
|
94
|
+
this.endpoint = options.endpoint ?? DEFAULTS.endpoint ?? "";
|
|
95
|
+
if (!this.endpoint) {
|
|
96
|
+
throw new Error("Eventra: endpoint is not configured");
|
|
97
|
+
}
|
|
98
|
+
this.flushInterval = options.flushInterval ?? DEFAULTS.flushInterval;
|
|
99
|
+
this.maxBatchSize = options.maxBatchSize ?? DEFAULTS.maxBatchSize;
|
|
100
|
+
this.maxQueueSize = options.maxQueueSize ?? DEFAULTS.maxQueueSize;
|
|
101
|
+
this.maxRetries = options.maxRetries ?? DEFAULTS.maxRetries;
|
|
102
|
+
this.retryBaseDelay = options.retryBaseDelayMs ?? DEFAULTS.retryBaseDelay;
|
|
103
|
+
this.multiTabMode = options.multiTabMode ?? "independent";
|
|
104
|
+
this.fetchImpl = options.fetchImpl ?? getGlobalFetch();
|
|
105
|
+
if (!this.fetchImpl) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
"Eventra: fetch is not available. Provide fetchImpl."
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
this.sdkInfo = {
|
|
111
|
+
name: "@eventra/sdk",
|
|
112
|
+
version: SDK_VERSION,
|
|
113
|
+
runtime: this.detectRuntime()
|
|
114
|
+
};
|
|
115
|
+
if (!options.disableTimer) {
|
|
116
|
+
this.startTimer();
|
|
117
|
+
}
|
|
118
|
+
if (options.autoFlushOnExit !== false) {
|
|
119
|
+
this.setupExitHandlers();
|
|
120
|
+
}
|
|
121
|
+
this.onEventsDropped = options.onEventsDropped;
|
|
122
|
+
}
|
|
123
|
+
/* ---------------- Public API ---------------- */
|
|
124
|
+
track(name, options) {
|
|
125
|
+
if (this.destroyed) return;
|
|
126
|
+
if (this.queue.length >= this.maxQueueSize) {
|
|
127
|
+
this.droppedEvents++;
|
|
128
|
+
this.onEventsDropped?.(this.droppedEvents);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
this.queue.push({
|
|
132
|
+
idempotencyKey: generateUUIDv4(),
|
|
133
|
+
name,
|
|
134
|
+
userId: options?.userId,
|
|
135
|
+
properties: options?.properties ?? {},
|
|
136
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
137
|
+
});
|
|
138
|
+
if (this.queue.length >= this.maxBatchSize) {
|
|
139
|
+
void this.flush();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async flush() {
|
|
143
|
+
if (this.destroyed) return;
|
|
144
|
+
if (this.inFlight) return;
|
|
145
|
+
if (this.queue.length === 0) return;
|
|
146
|
+
if (this.multiTabMode === "leader" && !tryBecomeLeader()) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
this.inFlight = true;
|
|
150
|
+
const batch = this.queue.splice(0, this.queue.length);
|
|
151
|
+
try {
|
|
152
|
+
await this.send(batch);
|
|
153
|
+
} catch {
|
|
154
|
+
this.queue.unshift(...batch);
|
|
155
|
+
} finally {
|
|
156
|
+
this.inFlight = false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
destroy() {
|
|
160
|
+
this.destroyed = true;
|
|
161
|
+
if (this.timer) clearInterval(this.timer);
|
|
162
|
+
}
|
|
163
|
+
/* ---------------- Transport ---------------- */
|
|
164
|
+
trySendBeacon(payload) {
|
|
165
|
+
if (!isBrowser()) return false;
|
|
166
|
+
try {
|
|
167
|
+
if (navigator.sendBeacon) {
|
|
168
|
+
const blob = new Blob([payload], {
|
|
169
|
+
type: "application/json"
|
|
170
|
+
});
|
|
171
|
+
return navigator.sendBeacon(this.endpoint, blob);
|
|
172
|
+
}
|
|
173
|
+
} catch {
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
async send(events) {
|
|
178
|
+
const payload = JSON.stringify({
|
|
179
|
+
sentAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
180
|
+
sdk: this.sdkInfo,
|
|
181
|
+
events
|
|
182
|
+
});
|
|
183
|
+
if (isBrowser() && payload.length < 6e4 && document.visibilityState === "hidden") {
|
|
184
|
+
if (this.trySendBeacon(payload)) return;
|
|
185
|
+
}
|
|
186
|
+
let attempt = 0;
|
|
187
|
+
while (attempt <= this.maxRetries) {
|
|
188
|
+
try {
|
|
189
|
+
const res = await this.fetchImpl(this.endpoint, {
|
|
190
|
+
method: "POST",
|
|
191
|
+
headers: {
|
|
192
|
+
"Content-Type": "application/json",
|
|
193
|
+
"x-api-key": this.apiKey
|
|
194
|
+
},
|
|
195
|
+
body: payload
|
|
196
|
+
});
|
|
197
|
+
if (res.status >= 400 && res.status < 500) {
|
|
198
|
+
if (res.status === 401 || res.status === 403) {
|
|
199
|
+
console.error("Eventra: Invalid API key");
|
|
200
|
+
}
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (!res.ok) {
|
|
204
|
+
throw new Error(`HTTP ${res.status}`);
|
|
205
|
+
}
|
|
206
|
+
return;
|
|
207
|
+
} catch (err) {
|
|
208
|
+
attempt++;
|
|
209
|
+
if (attempt > this.maxRetries) {
|
|
210
|
+
throw err;
|
|
211
|
+
}
|
|
212
|
+
const jitter = 0.5 + Math.random();
|
|
213
|
+
const delay = this.retryBaseDelay * 2 ** (attempt - 1) * jitter;
|
|
214
|
+
await sleep(delay);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/* ---------------- Runtime ---------------- */
|
|
219
|
+
detectRuntime() {
|
|
220
|
+
if (typeof window !== "undefined") return "browser";
|
|
221
|
+
const g = globalThis;
|
|
222
|
+
if (g["Deno"]) return "deno";
|
|
223
|
+
if (g["Bun"]) return "bun";
|
|
224
|
+
const maybeProcess = g["process"];
|
|
225
|
+
if (maybeProcess?.versions?.node) return "node";
|
|
226
|
+
return "unknown";
|
|
227
|
+
}
|
|
228
|
+
/* ---------------- Timer ---------------- */
|
|
229
|
+
startTimer() {
|
|
230
|
+
this.timer = setInterval(() => {
|
|
231
|
+
void this.flush();
|
|
232
|
+
}, this.flushInterval);
|
|
233
|
+
}
|
|
234
|
+
/* ---------------- Exit handling ---------------- */
|
|
235
|
+
setupExitHandlers() {
|
|
236
|
+
if (typeof window !== "undefined") {
|
|
237
|
+
window.addEventListener("visibilitychange", () => {
|
|
238
|
+
if (document.visibilityState === "hidden") {
|
|
239
|
+
void this.flush();
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
const g = globalThis;
|
|
244
|
+
const maybeProcess = g["process"];
|
|
245
|
+
if (typeof maybeProcess?.once === "function") {
|
|
246
|
+
const shutdown = async () => {
|
|
247
|
+
try {
|
|
248
|
+
await this.flush();
|
|
249
|
+
} catch {
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
maybeProcess.once("SIGINT", shutdown);
|
|
253
|
+
maybeProcess.once("SIGTERM", shutdown);
|
|
254
|
+
maybeProcess.once("beforeExit", shutdown);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
259
|
+
0 && (module.exports = {
|
|
260
|
+
Eventra
|
|
261
|
+
});
|
|
262
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts"],"sourcesContent":["export * from \"./client\";\nexport * from \"./types\";\n","import {\n TrackerOptions,\n TrackEvent,\n SdkInfo,\n} from \"./types\";\n\n/* ---------------- Constants ---------------- */\n\n\ndeclare const __SDK_VERSION__: string;\ndeclare const __EVENTRA_ENDPOINT__: string | undefined;\n\nconst SDK_VERSION = __SDK_VERSION__;\n\nconst DEFAULTS = {\n endpoint: __EVENTRA_ENDPOINT__ ?? \"\",\n flushInterval: 2000,\n maxBatchSize: 50,\n maxQueueSize: 10_000,\n maxRetries: 3,\n retryBaseDelay: 300,\n};\n\n/* ---------------- Helpers ---------------- */\n\nfunction getGlobalFetch(): typeof fetch | undefined {\n if (typeof fetch !== \"undefined\") {\n return fetch.bind(globalThis);\n }\n return undefined;\n}\n\nfunction generateUUIDv4(): string {\n if (\n typeof crypto !== \"undefined\" &&\n typeof crypto.randomUUID === \"function\"\n ) {\n return crypto.randomUUID();\n }\n\n // ultra-rare fallback (не должен происходить в нормальной среде)\n const rnd = Math.random().toString(16).slice(2);\n const ts = Date.now().toString(16);\n return `${ts}-${rnd}`;\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== \"undefined\";\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\n/* ---------------- Leader Election (minimal) ---------------- */\n\nconst LEADER_KEY = \"__eventra_sdk_leader__\";\nconst LEADER_TTL = 4000;\n\nfunction tryBecomeLeader(): boolean {\n if (!isBrowser()) return true;\n\n try {\n const now = Date.now();\n const raw = localStorage.getItem(LEADER_KEY);\n\n if (!raw) {\n localStorage.setItem(\n LEADER_KEY,\n JSON.stringify({ ts: now })\n );\n return true;\n }\n\n const parsed = JSON.parse(raw) as { ts: number };\n\n if (now - parsed.ts > LEADER_TTL) {\n localStorage.setItem(\n LEADER_KEY,\n JSON.stringify({ ts: now })\n );\n return true;\n }\n\n return false;\n } catch {\n return true; // fail-open\n }\n}\n\n/* ============================================================ */\n\nexport class Eventra {\n private apiKey: string;\n private endpoint: string;\n private flushInterval: number;\n private maxBatchSize: number;\n private maxQueueSize: number;\n private maxRetries: number;\n private retryBaseDelay: number;\n private multiTabMode: \"independent\" | \"leader\";\n\n private fetchImpl?: typeof fetch;\n private queue: TrackEvent[] = [];\n private timer?: ReturnType<typeof setInterval>;\n private inFlight = false;\n private destroyed = false;\n private droppedEvents = 0;\n\n private sdkInfo: SdkInfo;\n\n constructor(options: TrackerOptions) {\n if (!options.apiKey) {\n throw new Error(\"Eventra: apiKey is required\");\n }\n\n this.apiKey = options.apiKey;\n\n this.endpoint = options.endpoint ?? DEFAULTS.endpoint ?? \"\";\n if (!this.endpoint) {\n throw new Error(\"Eventra: endpoint is not configured\");\n }\n\n this.flushInterval =\n options.flushInterval ?? DEFAULTS.flushInterval;\n\n this.maxBatchSize =\n options.maxBatchSize ?? DEFAULTS.maxBatchSize;\n\n this.maxQueueSize =\n options.maxQueueSize ?? DEFAULTS.maxQueueSize;\n\n this.maxRetries =\n options.maxRetries ?? DEFAULTS.maxRetries;\n\n this.retryBaseDelay =\n options.retryBaseDelayMs ?? DEFAULTS.retryBaseDelay;\n\n this.multiTabMode =\n options.multiTabMode ?? \"independent\";\n\n this.fetchImpl =\n options.fetchImpl ?? getGlobalFetch();\n\n if (!this.fetchImpl) {\n throw new Error(\n \"Eventra: fetch is not available. Provide fetchImpl.\"\n );\n }\n\n this.sdkInfo = {\n name: \"@eventra/sdk\",\n version: SDK_VERSION,\n runtime: this.detectRuntime(),\n };\n\n if (!options.disableTimer) {\n this.startTimer();\n }\n\n if (options.autoFlushOnExit !== false) {\n this.setupExitHandlers();\n }\n\n this.onEventsDropped = options.onEventsDropped;\n }\n\n private onEventsDropped?: (count: number) => void;\n\n /* ---------------- Public API ---------------- */\n\n track(\n name: string,\n options?: {\n userId?: string;\n properties?: Record<string, unknown>;\n }\n ) {\n if (this.destroyed) return;\n\n if (this.queue.length >= this.maxQueueSize) {\n this.droppedEvents++;\n this.onEventsDropped?.(this.droppedEvents);\n return;\n }\n\n this.queue.push({\n idempotencyKey: generateUUIDv4(),\n name,\n userId: options?.userId,\n properties: options?.properties ?? {},\n timestamp: new Date().toISOString(),\n });\n\n if (this.queue.length >= this.maxBatchSize) {\n void this.flush();\n }\n }\n\n async flush(): Promise<void> {\n if (this.destroyed) return;\n if (this.inFlight) return;\n if (this.queue.length === 0) return;\n\n if (\n this.multiTabMode === \"leader\" &&\n !tryBecomeLeader()\n ) {\n return;\n }\n\n this.inFlight = true;\n\n const batch = this.queue.splice(0, this.queue.length);\n\n try {\n await this.send(batch);\n } catch {\n this.queue.unshift(...batch);\n } finally {\n this.inFlight = false;\n }\n }\n\n destroy() {\n this.destroyed = true;\n if (this.timer) clearInterval(this.timer);\n }\n\n /* ---------------- Transport ---------------- */\n\n private trySendBeacon(payload: string): boolean {\n if (!isBrowser()) return false;\n\n try {\n if (navigator.sendBeacon) {\n const blob = new Blob([payload], {\n type: \"application/json\",\n });\n return navigator.sendBeacon(this.endpoint, blob);\n }\n } catch {\n // ignore\n }\n\n return false;\n }\n\n private async send(events: TrackEvent[]) {\n const payload = JSON.stringify({\n sentAt: new Date().toISOString(),\n sdk: this.sdkInfo,\n events,\n });\n\n // beacon fast-path (browser only)\n if (\n isBrowser() &&\n payload.length < 60_000 &&\n document.visibilityState === \"hidden\"\n ) {\n if (this.trySendBeacon(payload)) return;\n }\n\n let attempt = 0;\n\n while (attempt <= this.maxRetries) {\n try {\n const res = await this.fetchImpl!(this.endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n },\n body: payload,\n });\n\n // ❗ DO NOT retry client errors\n if (res.status >= 400 && res.status < 500) {\n if (res.status === 401 || res.status === 403) {\n console.error(\"Eventra: Invalid API key\");\n }\n return;\n }\n\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}`);\n }\n\n return;\n } catch (err) {\n attempt++;\n\n if (attempt > this.maxRetries) {\n throw err;\n }\n\n const jitter = 0.5 + Math.random(); // 0.5–1.5\n const delay =\n this.retryBaseDelay *\n 2 ** (attempt - 1) *\n jitter;\n\n await sleep(delay);\n }\n }\n }\n\n /* ---------------- Runtime ---------------- */\n\n private detectRuntime(): string {\n if (typeof window !== \"undefined\") return \"browser\";\n\n const g = globalThis as Record<string, unknown>;\n\n if (g[\"Deno\"]) return \"deno\";\n if (g[\"Bun\"]) return \"bun\";\n\n const maybeProcess = g[\"process\"] as\n | { versions?: { node?: string } }\n | undefined;\n\n if (maybeProcess?.versions?.node) return \"node\";\n\n return \"unknown\";\n }\n\n /* ---------------- Timer ---------------- */\n\n private startTimer() {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.flushInterval);\n }\n\n /* ---------------- Exit handling ---------------- */\n\n private setupExitHandlers() {\n // browser\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n void this.flush();\n }\n });\n }\n\n // node\n const g = globalThis as Record<string, unknown>;\n\n const maybeProcess = g[\"process\"] as\n | { once?: (event: string, cb: () => void) => void }\n | undefined;\n\n if (typeof maybeProcess?.once === \"function\") {\n const shutdown = async () => {\n try {\n await this.flush();\n } catch {}\n };\n\n maybeProcess.once(\"SIGINT\", shutdown);\n maybeProcess.once(\"SIGTERM\", shutdown);\n maybeProcess.once(\"beforeExit\", shutdown);\n }\n }\n}\n\nexport * from \"./types\";\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
interface TrackerOptions {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
endpoint?: string;
|
|
4
|
+
flushInterval?: number;
|
|
5
|
+
maxBatchSize?: number;
|
|
6
|
+
maxQueueSize?: number;
|
|
7
|
+
maxRetries?: number;
|
|
8
|
+
retryBaseDelayMs?: number;
|
|
9
|
+
fetchImpl?: typeof fetch;
|
|
10
|
+
autoFlushOnExit?: boolean;
|
|
11
|
+
disableTimer?: boolean;
|
|
12
|
+
/** Called when events are dropped due to queue overflow */
|
|
13
|
+
onEventsDropped?: (count: number) => void;
|
|
14
|
+
/** Multi-tab mode (browser only) */
|
|
15
|
+
multiTabMode?: "independent" | "leader";
|
|
16
|
+
}
|
|
17
|
+
interface TrackEvent {
|
|
18
|
+
idempotencyKey: string;
|
|
19
|
+
name: string;
|
|
20
|
+
userId?: string;
|
|
21
|
+
properties?: Record<string, unknown>;
|
|
22
|
+
timestamp: string;
|
|
23
|
+
}
|
|
24
|
+
interface SdkInfo {
|
|
25
|
+
name: string;
|
|
26
|
+
version: string;
|
|
27
|
+
runtime: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
declare class Eventra {
|
|
31
|
+
private apiKey;
|
|
32
|
+
private endpoint;
|
|
33
|
+
private flushInterval;
|
|
34
|
+
private maxBatchSize;
|
|
35
|
+
private maxQueueSize;
|
|
36
|
+
private maxRetries;
|
|
37
|
+
private retryBaseDelay;
|
|
38
|
+
private multiTabMode;
|
|
39
|
+
private fetchImpl?;
|
|
40
|
+
private queue;
|
|
41
|
+
private timer?;
|
|
42
|
+
private inFlight;
|
|
43
|
+
private destroyed;
|
|
44
|
+
private droppedEvents;
|
|
45
|
+
private sdkInfo;
|
|
46
|
+
constructor(options: TrackerOptions);
|
|
47
|
+
private onEventsDropped?;
|
|
48
|
+
track(name: string, options?: {
|
|
49
|
+
userId?: string;
|
|
50
|
+
properties?: Record<string, unknown>;
|
|
51
|
+
}): void;
|
|
52
|
+
flush(): Promise<void>;
|
|
53
|
+
destroy(): void;
|
|
54
|
+
private trySendBeacon;
|
|
55
|
+
private send;
|
|
56
|
+
private detectRuntime;
|
|
57
|
+
private startTimer;
|
|
58
|
+
private setupExitHandlers;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { Eventra, type SdkInfo, type TrackEvent, type TrackerOptions };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
interface TrackerOptions {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
endpoint?: string;
|
|
4
|
+
flushInterval?: number;
|
|
5
|
+
maxBatchSize?: number;
|
|
6
|
+
maxQueueSize?: number;
|
|
7
|
+
maxRetries?: number;
|
|
8
|
+
retryBaseDelayMs?: number;
|
|
9
|
+
fetchImpl?: typeof fetch;
|
|
10
|
+
autoFlushOnExit?: boolean;
|
|
11
|
+
disableTimer?: boolean;
|
|
12
|
+
/** Called when events are dropped due to queue overflow */
|
|
13
|
+
onEventsDropped?: (count: number) => void;
|
|
14
|
+
/** Multi-tab mode (browser only) */
|
|
15
|
+
multiTabMode?: "independent" | "leader";
|
|
16
|
+
}
|
|
17
|
+
interface TrackEvent {
|
|
18
|
+
idempotencyKey: string;
|
|
19
|
+
name: string;
|
|
20
|
+
userId?: string;
|
|
21
|
+
properties?: Record<string, unknown>;
|
|
22
|
+
timestamp: string;
|
|
23
|
+
}
|
|
24
|
+
interface SdkInfo {
|
|
25
|
+
name: string;
|
|
26
|
+
version: string;
|
|
27
|
+
runtime: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
declare class Eventra {
|
|
31
|
+
private apiKey;
|
|
32
|
+
private endpoint;
|
|
33
|
+
private flushInterval;
|
|
34
|
+
private maxBatchSize;
|
|
35
|
+
private maxQueueSize;
|
|
36
|
+
private maxRetries;
|
|
37
|
+
private retryBaseDelay;
|
|
38
|
+
private multiTabMode;
|
|
39
|
+
private fetchImpl?;
|
|
40
|
+
private queue;
|
|
41
|
+
private timer?;
|
|
42
|
+
private inFlight;
|
|
43
|
+
private destroyed;
|
|
44
|
+
private droppedEvents;
|
|
45
|
+
private sdkInfo;
|
|
46
|
+
constructor(options: TrackerOptions);
|
|
47
|
+
private onEventsDropped?;
|
|
48
|
+
track(name: string, options?: {
|
|
49
|
+
userId?: string;
|
|
50
|
+
properties?: Record<string, unknown>;
|
|
51
|
+
}): void;
|
|
52
|
+
flush(): Promise<void>;
|
|
53
|
+
destroy(): void;
|
|
54
|
+
private trySendBeacon;
|
|
55
|
+
private send;
|
|
56
|
+
private detectRuntime;
|
|
57
|
+
private startTimer;
|
|
58
|
+
private setupExitHandlers;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { Eventra, type SdkInfo, type TrackEvent, type TrackerOptions };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
var SDK_VERSION = "0.1.2";
|
|
3
|
+
var DEFAULTS = {
|
|
4
|
+
endpoint: "http://api.eventra.dev/api/v1/ingest/batch",
|
|
5
|
+
flushInterval: 2e3,
|
|
6
|
+
maxBatchSize: 50,
|
|
7
|
+
maxQueueSize: 1e4,
|
|
8
|
+
maxRetries: 3,
|
|
9
|
+
retryBaseDelay: 300
|
|
10
|
+
};
|
|
11
|
+
function getGlobalFetch() {
|
|
12
|
+
if (typeof fetch !== "undefined") {
|
|
13
|
+
return fetch.bind(globalThis);
|
|
14
|
+
}
|
|
15
|
+
return void 0;
|
|
16
|
+
}
|
|
17
|
+
function generateUUIDv4() {
|
|
18
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
19
|
+
return crypto.randomUUID();
|
|
20
|
+
}
|
|
21
|
+
const rnd = Math.random().toString(16).slice(2);
|
|
22
|
+
const ts = Date.now().toString(16);
|
|
23
|
+
return `${ts}-${rnd}`;
|
|
24
|
+
}
|
|
25
|
+
function isBrowser() {
|
|
26
|
+
return typeof window !== "undefined";
|
|
27
|
+
}
|
|
28
|
+
function sleep(ms) {
|
|
29
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
30
|
+
}
|
|
31
|
+
var LEADER_KEY = "__eventra_sdk_leader__";
|
|
32
|
+
var LEADER_TTL = 4e3;
|
|
33
|
+
function tryBecomeLeader() {
|
|
34
|
+
if (!isBrowser()) return true;
|
|
35
|
+
try {
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
const raw = localStorage.getItem(LEADER_KEY);
|
|
38
|
+
if (!raw) {
|
|
39
|
+
localStorage.setItem(
|
|
40
|
+
LEADER_KEY,
|
|
41
|
+
JSON.stringify({ ts: now })
|
|
42
|
+
);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
const parsed = JSON.parse(raw);
|
|
46
|
+
if (now - parsed.ts > LEADER_TTL) {
|
|
47
|
+
localStorage.setItem(
|
|
48
|
+
LEADER_KEY,
|
|
49
|
+
JSON.stringify({ ts: now })
|
|
50
|
+
);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
} catch {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
var Eventra = class {
|
|
59
|
+
constructor(options) {
|
|
60
|
+
this.queue = [];
|
|
61
|
+
this.inFlight = false;
|
|
62
|
+
this.destroyed = false;
|
|
63
|
+
this.droppedEvents = 0;
|
|
64
|
+
if (!options.apiKey) {
|
|
65
|
+
throw new Error("Eventra: apiKey is required");
|
|
66
|
+
}
|
|
67
|
+
this.apiKey = options.apiKey;
|
|
68
|
+
this.endpoint = options.endpoint ?? DEFAULTS.endpoint ?? "";
|
|
69
|
+
if (!this.endpoint) {
|
|
70
|
+
throw new Error("Eventra: endpoint is not configured");
|
|
71
|
+
}
|
|
72
|
+
this.flushInterval = options.flushInterval ?? DEFAULTS.flushInterval;
|
|
73
|
+
this.maxBatchSize = options.maxBatchSize ?? DEFAULTS.maxBatchSize;
|
|
74
|
+
this.maxQueueSize = options.maxQueueSize ?? DEFAULTS.maxQueueSize;
|
|
75
|
+
this.maxRetries = options.maxRetries ?? DEFAULTS.maxRetries;
|
|
76
|
+
this.retryBaseDelay = options.retryBaseDelayMs ?? DEFAULTS.retryBaseDelay;
|
|
77
|
+
this.multiTabMode = options.multiTabMode ?? "independent";
|
|
78
|
+
this.fetchImpl = options.fetchImpl ?? getGlobalFetch();
|
|
79
|
+
if (!this.fetchImpl) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
"Eventra: fetch is not available. Provide fetchImpl."
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
this.sdkInfo = {
|
|
85
|
+
name: "@eventra/sdk",
|
|
86
|
+
version: SDK_VERSION,
|
|
87
|
+
runtime: this.detectRuntime()
|
|
88
|
+
};
|
|
89
|
+
if (!options.disableTimer) {
|
|
90
|
+
this.startTimer();
|
|
91
|
+
}
|
|
92
|
+
if (options.autoFlushOnExit !== false) {
|
|
93
|
+
this.setupExitHandlers();
|
|
94
|
+
}
|
|
95
|
+
this.onEventsDropped = options.onEventsDropped;
|
|
96
|
+
}
|
|
97
|
+
/* ---------------- Public API ---------------- */
|
|
98
|
+
track(name, options) {
|
|
99
|
+
if (this.destroyed) return;
|
|
100
|
+
if (this.queue.length >= this.maxQueueSize) {
|
|
101
|
+
this.droppedEvents++;
|
|
102
|
+
this.onEventsDropped?.(this.droppedEvents);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
this.queue.push({
|
|
106
|
+
idempotencyKey: generateUUIDv4(),
|
|
107
|
+
name,
|
|
108
|
+
userId: options?.userId,
|
|
109
|
+
properties: options?.properties ?? {},
|
|
110
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
111
|
+
});
|
|
112
|
+
if (this.queue.length >= this.maxBatchSize) {
|
|
113
|
+
void this.flush();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async flush() {
|
|
117
|
+
if (this.destroyed) return;
|
|
118
|
+
if (this.inFlight) return;
|
|
119
|
+
if (this.queue.length === 0) return;
|
|
120
|
+
if (this.multiTabMode === "leader" && !tryBecomeLeader()) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
this.inFlight = true;
|
|
124
|
+
const batch = this.queue.splice(0, this.queue.length);
|
|
125
|
+
try {
|
|
126
|
+
await this.send(batch);
|
|
127
|
+
} catch {
|
|
128
|
+
this.queue.unshift(...batch);
|
|
129
|
+
} finally {
|
|
130
|
+
this.inFlight = false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
destroy() {
|
|
134
|
+
this.destroyed = true;
|
|
135
|
+
if (this.timer) clearInterval(this.timer);
|
|
136
|
+
}
|
|
137
|
+
/* ---------------- Transport ---------------- */
|
|
138
|
+
trySendBeacon(payload) {
|
|
139
|
+
if (!isBrowser()) return false;
|
|
140
|
+
try {
|
|
141
|
+
if (navigator.sendBeacon) {
|
|
142
|
+
const blob = new Blob([payload], {
|
|
143
|
+
type: "application/json"
|
|
144
|
+
});
|
|
145
|
+
return navigator.sendBeacon(this.endpoint, blob);
|
|
146
|
+
}
|
|
147
|
+
} catch {
|
|
148
|
+
}
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
async send(events) {
|
|
152
|
+
const payload = JSON.stringify({
|
|
153
|
+
sentAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
154
|
+
sdk: this.sdkInfo,
|
|
155
|
+
events
|
|
156
|
+
});
|
|
157
|
+
if (isBrowser() && payload.length < 6e4 && document.visibilityState === "hidden") {
|
|
158
|
+
if (this.trySendBeacon(payload)) return;
|
|
159
|
+
}
|
|
160
|
+
let attempt = 0;
|
|
161
|
+
while (attempt <= this.maxRetries) {
|
|
162
|
+
try {
|
|
163
|
+
const res = await this.fetchImpl(this.endpoint, {
|
|
164
|
+
method: "POST",
|
|
165
|
+
headers: {
|
|
166
|
+
"Content-Type": "application/json",
|
|
167
|
+
"x-api-key": this.apiKey
|
|
168
|
+
},
|
|
169
|
+
body: payload
|
|
170
|
+
});
|
|
171
|
+
if (res.status >= 400 && res.status < 500) {
|
|
172
|
+
if (res.status === 401 || res.status === 403) {
|
|
173
|
+
console.error("Eventra: Invalid API key");
|
|
174
|
+
}
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (!res.ok) {
|
|
178
|
+
throw new Error(`HTTP ${res.status}`);
|
|
179
|
+
}
|
|
180
|
+
return;
|
|
181
|
+
} catch (err) {
|
|
182
|
+
attempt++;
|
|
183
|
+
if (attempt > this.maxRetries) {
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
const jitter = 0.5 + Math.random();
|
|
187
|
+
const delay = this.retryBaseDelay * 2 ** (attempt - 1) * jitter;
|
|
188
|
+
await sleep(delay);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/* ---------------- Runtime ---------------- */
|
|
193
|
+
detectRuntime() {
|
|
194
|
+
if (typeof window !== "undefined") return "browser";
|
|
195
|
+
const g = globalThis;
|
|
196
|
+
if (g["Deno"]) return "deno";
|
|
197
|
+
if (g["Bun"]) return "bun";
|
|
198
|
+
const maybeProcess = g["process"];
|
|
199
|
+
if (maybeProcess?.versions?.node) return "node";
|
|
200
|
+
return "unknown";
|
|
201
|
+
}
|
|
202
|
+
/* ---------------- Timer ---------------- */
|
|
203
|
+
startTimer() {
|
|
204
|
+
this.timer = setInterval(() => {
|
|
205
|
+
void this.flush();
|
|
206
|
+
}, this.flushInterval);
|
|
207
|
+
}
|
|
208
|
+
/* ---------------- Exit handling ---------------- */
|
|
209
|
+
setupExitHandlers() {
|
|
210
|
+
if (typeof window !== "undefined") {
|
|
211
|
+
window.addEventListener("visibilitychange", () => {
|
|
212
|
+
if (document.visibilityState === "hidden") {
|
|
213
|
+
void this.flush();
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
const g = globalThis;
|
|
218
|
+
const maybeProcess = g["process"];
|
|
219
|
+
if (typeof maybeProcess?.once === "function") {
|
|
220
|
+
const shutdown = async () => {
|
|
221
|
+
try {
|
|
222
|
+
await this.flush();
|
|
223
|
+
} catch {
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
maybeProcess.once("SIGINT", shutdown);
|
|
227
|
+
maybeProcess.once("SIGTERM", shutdown);
|
|
228
|
+
maybeProcess.once("beforeExit", shutdown);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
export {
|
|
233
|
+
Eventra
|
|
234
|
+
};
|
|
235
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import {\n TrackerOptions,\n TrackEvent,\n SdkInfo,\n} from \"./types\";\n\n/* ---------------- Constants ---------------- */\n\n\ndeclare const __SDK_VERSION__: string;\ndeclare const __EVENTRA_ENDPOINT__: string | undefined;\n\nconst SDK_VERSION = __SDK_VERSION__;\n\nconst DEFAULTS = {\n endpoint: __EVENTRA_ENDPOINT__ ?? \"\",\n flushInterval: 2000,\n maxBatchSize: 50,\n maxQueueSize: 10_000,\n maxRetries: 3,\n retryBaseDelay: 300,\n};\n\n/* ---------------- Helpers ---------------- */\n\nfunction getGlobalFetch(): typeof fetch | undefined {\n if (typeof fetch !== \"undefined\") {\n return fetch.bind(globalThis);\n }\n return undefined;\n}\n\nfunction generateUUIDv4(): string {\n if (\n typeof crypto !== \"undefined\" &&\n typeof crypto.randomUUID === \"function\"\n ) {\n return crypto.randomUUID();\n }\n\n // ultra-rare fallback (не должен происходить в нормальной среде)\n const rnd = Math.random().toString(16).slice(2);\n const ts = Date.now().toString(16);\n return `${ts}-${rnd}`;\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== \"undefined\";\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\n/* ---------------- Leader Election (minimal) ---------------- */\n\nconst LEADER_KEY = \"__eventra_sdk_leader__\";\nconst LEADER_TTL = 4000;\n\nfunction tryBecomeLeader(): boolean {\n if (!isBrowser()) return true;\n\n try {\n const now = Date.now();\n const raw = localStorage.getItem(LEADER_KEY);\n\n if (!raw) {\n localStorage.setItem(\n LEADER_KEY,\n JSON.stringify({ ts: now })\n );\n return true;\n }\n\n const parsed = JSON.parse(raw) as { ts: number };\n\n if (now - parsed.ts > LEADER_TTL) {\n localStorage.setItem(\n LEADER_KEY,\n JSON.stringify({ ts: now })\n );\n return true;\n }\n\n return false;\n } catch {\n return true; // fail-open\n }\n}\n\n/* ============================================================ */\n\nexport class Eventra {\n private apiKey: string;\n private endpoint: string;\n private flushInterval: number;\n private maxBatchSize: number;\n private maxQueueSize: number;\n private maxRetries: number;\n private retryBaseDelay: number;\n private multiTabMode: \"independent\" | \"leader\";\n\n private fetchImpl?: typeof fetch;\n private queue: TrackEvent[] = [];\n private timer?: ReturnType<typeof setInterval>;\n private inFlight = false;\n private destroyed = false;\n private droppedEvents = 0;\n\n private sdkInfo: SdkInfo;\n\n constructor(options: TrackerOptions) {\n if (!options.apiKey) {\n throw new Error(\"Eventra: apiKey is required\");\n }\n\n this.apiKey = options.apiKey;\n\n this.endpoint = options.endpoint ?? DEFAULTS.endpoint ?? \"\";\n if (!this.endpoint) {\n throw new Error(\"Eventra: endpoint is not configured\");\n }\n\n this.flushInterval =\n options.flushInterval ?? DEFAULTS.flushInterval;\n\n this.maxBatchSize =\n options.maxBatchSize ?? DEFAULTS.maxBatchSize;\n\n this.maxQueueSize =\n options.maxQueueSize ?? DEFAULTS.maxQueueSize;\n\n this.maxRetries =\n options.maxRetries ?? DEFAULTS.maxRetries;\n\n this.retryBaseDelay =\n options.retryBaseDelayMs ?? DEFAULTS.retryBaseDelay;\n\n this.multiTabMode =\n options.multiTabMode ?? \"independent\";\n\n this.fetchImpl =\n options.fetchImpl ?? getGlobalFetch();\n\n if (!this.fetchImpl) {\n throw new Error(\n \"Eventra: fetch is not available. Provide fetchImpl.\"\n );\n }\n\n this.sdkInfo = {\n name: \"@eventra/sdk\",\n version: SDK_VERSION,\n runtime: this.detectRuntime(),\n };\n\n if (!options.disableTimer) {\n this.startTimer();\n }\n\n if (options.autoFlushOnExit !== false) {\n this.setupExitHandlers();\n }\n\n this.onEventsDropped = options.onEventsDropped;\n }\n\n private onEventsDropped?: (count: number) => void;\n\n /* ---------------- Public API ---------------- */\n\n track(\n name: string,\n options?: {\n userId?: string;\n properties?: Record<string, unknown>;\n }\n ) {\n if (this.destroyed) return;\n\n if (this.queue.length >= this.maxQueueSize) {\n this.droppedEvents++;\n this.onEventsDropped?.(this.droppedEvents);\n return;\n }\n\n this.queue.push({\n idempotencyKey: generateUUIDv4(),\n name,\n userId: options?.userId,\n properties: options?.properties ?? {},\n timestamp: new Date().toISOString(),\n });\n\n if (this.queue.length >= this.maxBatchSize) {\n void this.flush();\n }\n }\n\n async flush(): Promise<void> {\n if (this.destroyed) return;\n if (this.inFlight) return;\n if (this.queue.length === 0) return;\n\n if (\n this.multiTabMode === \"leader\" &&\n !tryBecomeLeader()\n ) {\n return;\n }\n\n this.inFlight = true;\n\n const batch = this.queue.splice(0, this.queue.length);\n\n try {\n await this.send(batch);\n } catch {\n this.queue.unshift(...batch);\n } finally {\n this.inFlight = false;\n }\n }\n\n destroy() {\n this.destroyed = true;\n if (this.timer) clearInterval(this.timer);\n }\n\n /* ---------------- Transport ---------------- */\n\n private trySendBeacon(payload: string): boolean {\n if (!isBrowser()) return false;\n\n try {\n if (navigator.sendBeacon) {\n const blob = new Blob([payload], {\n type: \"application/json\",\n });\n return navigator.sendBeacon(this.endpoint, blob);\n }\n } catch {\n // ignore\n }\n\n return false;\n }\n\n private async send(events: TrackEvent[]) {\n const payload = JSON.stringify({\n sentAt: new Date().toISOString(),\n sdk: this.sdkInfo,\n events,\n });\n\n // beacon fast-path (browser only)\n if (\n isBrowser() &&\n payload.length < 60_000 &&\n document.visibilityState === \"hidden\"\n ) {\n if (this.trySendBeacon(payload)) return;\n }\n\n let attempt = 0;\n\n while (attempt <= this.maxRetries) {\n try {\n const res = await this.fetchImpl!(this.endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n },\n body: payload,\n });\n\n // ❗ DO NOT retry client errors\n if (res.status >= 400 && res.status < 500) {\n if (res.status === 401 || res.status === 403) {\n console.error(\"Eventra: Invalid API key\");\n }\n return;\n }\n\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}`);\n }\n\n return;\n } catch (err) {\n attempt++;\n\n if (attempt > this.maxRetries) {\n throw err;\n }\n\n const jitter = 0.5 + Math.random(); // 0.5–1.5\n const delay =\n this.retryBaseDelay *\n 2 ** (attempt - 1) *\n jitter;\n\n await sleep(delay);\n }\n }\n }\n\n /* ---------------- Runtime ---------------- */\n\n private detectRuntime(): string {\n if (typeof window !== \"undefined\") return \"browser\";\n\n const g = globalThis as Record<string, unknown>;\n\n if (g[\"Deno\"]) return \"deno\";\n if (g[\"Bun\"]) return \"bun\";\n\n const maybeProcess = g[\"process\"] as\n | { versions?: { node?: string } }\n | undefined;\n\n if (maybeProcess?.versions?.node) return \"node\";\n\n return \"unknown\";\n }\n\n /* ---------------- Timer ---------------- */\n\n private startTimer() {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.flushInterval);\n }\n\n /* ---------------- Exit handling ---------------- */\n\n private setupExitHandlers() {\n // browser\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n void this.flush();\n }\n });\n }\n\n // node\n const g = globalThis as Record<string, unknown>;\n\n const maybeProcess = g[\"process\"] as\n | { once?: (event: string, cb: () => void) => void }\n | undefined;\n\n if (typeof maybeProcess?.once === \"function\") {\n const shutdown = async () => {\n try {\n await this.flush();\n } catch {}\n };\n\n maybeProcess.once(\"SIGINT\", shutdown);\n maybeProcess.once(\"SIGTERM\", shutdown);\n maybeProcess.once(\"beforeExit\", shutdown);\n }\n }\n}\n\nexport * from \"./types\";\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@eventra_dev/eventra-sdk",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"private": false,
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"module": "./dist/index.mjs",
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"clean": "rm -rf dist"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/and-1991/eventra-sdk"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://eventra.dev",
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"tsup": "^8.0.0",
|
|
33
|
+
"typescript": "^5.6.0"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18"
|
|
37
|
+
}
|
|
38
|
+
}
|