@bananalytics/react-native 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/dist/context/app.d.ts +12 -0
  4. package/dist/context/app.js +34 -0
  5. package/dist/context/device.d.ts +12 -0
  6. package/dist/context/device.js +38 -0
  7. package/dist/context/session.d.ts +49 -0
  8. package/dist/context/session.js +99 -0
  9. package/dist/core/client.d.ts +112 -0
  10. package/dist/core/client.js +283 -0
  11. package/dist/core/config.d.ts +27 -0
  12. package/dist/core/config.js +41 -0
  13. package/dist/core/errors.d.ts +19 -0
  14. package/dist/core/errors.js +40 -0
  15. package/dist/hooks/BananalyticsProvider.d.ts +22 -0
  16. package/dist/hooks/BananalyticsProvider.js +72 -0
  17. package/dist/hooks/RochadeProvider.d.ts +22 -0
  18. package/dist/hooks/RochadeProvider.js +72 -0
  19. package/dist/hooks/useBananalytics.d.ts +16 -0
  20. package/dist/hooks/useBananalytics.js +26 -0
  21. package/dist/hooks/useRochade.d.ts +16 -0
  22. package/dist/hooks/useRochade.js +26 -0
  23. package/dist/hooks/useTrackScreen.d.ts +14 -0
  24. package/dist/hooks/useTrackScreen.js +24 -0
  25. package/dist/index.d.ts +61 -0
  26. package/dist/index.js +97 -0
  27. package/dist/privacy/consent.d.ts +39 -0
  28. package/dist/privacy/consent.js +57 -0
  29. package/dist/privacy/sanitizer.d.ts +16 -0
  30. package/dist/privacy/sanitizer.js +40 -0
  31. package/dist/tracking/event-builder.d.ts +47 -0
  32. package/dist/tracking/event-builder.js +73 -0
  33. package/dist/tracking/lifecycle-tracker.d.ts +24 -0
  34. package/dist/tracking/lifecycle-tracker.js +51 -0
  35. package/dist/tracking/screen-tracker.d.ts +20 -0
  36. package/dist/tracking/screen-tracker.js +34 -0
  37. package/dist/tracking/user-identity.d.ts +40 -0
  38. package/dist/tracking/user-identity.js +75 -0
  39. package/dist/transport/batcher.d.ts +44 -0
  40. package/dist/transport/batcher.js +91 -0
  41. package/dist/transport/persister.d.ts +49 -0
  42. package/dist/transport/persister.js +148 -0
  43. package/dist/transport/queue.d.ts +41 -0
  44. package/dist/transport/queue.js +67 -0
  45. package/dist/transport/retry.d.ts +15 -0
  46. package/dist/transport/retry.js +52 -0
  47. package/dist/transport/transport.d.ts +23 -0
  48. package/dist/transport/transport.js +51 -0
  49. package/dist/types/common.d.ts +6 -0
  50. package/dist/types/common.js +2 -0
  51. package/dist/types/config.d.ts +25 -0
  52. package/dist/types/config.js +2 -0
  53. package/dist/types/events.d.ts +53 -0
  54. package/dist/types/events.js +2 -0
  55. package/dist/utils/id.d.ts +13 -0
  56. package/dist/utils/id.js +18 -0
  57. package/dist/utils/logger.d.ts +11 -0
  58. package/dist/utils/logger.js +24 -0
  59. package/dist/utils/network.d.ts +7 -0
  60. package/dist/utils/network.js +33 -0
  61. package/dist/utils/time.d.ts +13 -0
  62. package/dist/utils/time.js +17 -0
  63. package/dist/utils/validation.d.ts +26 -0
  64. package/dist/utils/validation.js +60 -0
  65. package/package.json +64 -0
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UserIdentity = void 0;
4
+ const id_1 = require("../utils/id");
5
+ /**
6
+ * Manages user identity — anonymous ID, user ID, and identity lifecycle.
7
+ */
8
+ class UserIdentity {
9
+ constructor(persister, logger) {
10
+ this.userId = null;
11
+ this.persister = persister;
12
+ this.logger = logger;
13
+ this.anonymousId = (0, id_1.generateId)();
14
+ }
15
+ /**
16
+ * Initializes identity, loading persisted anonymous and user IDs.
17
+ */
18
+ async initialize() {
19
+ const storedAnonId = await this.persister.loadAnonymousId();
20
+ if (storedAnonId) {
21
+ this.anonymousId = storedAnonId;
22
+ this.logger.debug('Restored anonymous ID', this.anonymousId);
23
+ }
24
+ else {
25
+ await this.persister.saveAnonymousId(this.anonymousId);
26
+ this.logger.debug('Generated new anonymous ID', this.anonymousId);
27
+ }
28
+ const storedUserId = await this.persister.loadUserId();
29
+ if (storedUserId) {
30
+ this.userId = storedUserId;
31
+ this.logger.debug('Restored user ID', this.userId);
32
+ }
33
+ }
34
+ /**
35
+ * Sets the user ID for all subsequent events.
36
+ *
37
+ * @param userId - The user identifier
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * identity.identify('user-123');
42
+ * ```
43
+ */
44
+ async identify(userId) {
45
+ this.userId = userId;
46
+ await this.persister.saveUserId(userId);
47
+ this.logger.debug('User identified', userId);
48
+ }
49
+ /**
50
+ * Clears the user ID and generates a new anonymous ID.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * identity.reset();
55
+ * ```
56
+ */
57
+ async reset() {
58
+ this.userId = null;
59
+ this.anonymousId = (0, id_1.generateId)();
60
+ await Promise.all([
61
+ this.persister.saveUserId(null),
62
+ this.persister.saveAnonymousId(this.anonymousId),
63
+ ]);
64
+ this.logger.debug('Identity reset, new anonymous ID', this.anonymousId);
65
+ }
66
+ /** Returns the current anonymous ID. */
67
+ getAnonymousId() {
68
+ return this.anonymousId;
69
+ }
70
+ /** Returns the current user ID, or null if not identified. */
71
+ getUserId() {
72
+ return this.userId;
73
+ }
74
+ }
75
+ exports.UserIdentity = UserIdentity;
@@ -0,0 +1,44 @@
1
+ import { EventPayload } from '../types/events';
2
+ import { Logger } from '../utils/logger';
3
+ import { EventQueue } from './queue';
4
+ import { Transport } from './transport';
5
+ /**
6
+ * Manages automatic batching and flushing of events.
7
+ * Flushes on a timer interval or when the queue reaches the threshold.
8
+ */
9
+ export declare class Batcher {
10
+ private readonly queue;
11
+ private readonly transport;
12
+ private readonly logger;
13
+ private readonly flushInterval;
14
+ private readonly flushAt;
15
+ private readonly maxRetries;
16
+ private timer;
17
+ private flushing;
18
+ constructor(queue: EventQueue, transport: Transport, logger: Logger, flushInterval: number, flushAt: number, maxRetries: number);
19
+ /**
20
+ * Starts the automatic flush timer.
21
+ */
22
+ start(): void;
23
+ /**
24
+ * Stops the automatic flush timer.
25
+ */
26
+ stop(): void;
27
+ /**
28
+ * Adds an event and triggers a flush if the threshold is reached.
29
+ *
30
+ * @param event - Event payload to enqueue
31
+ */
32
+ enqueue(event: EventPayload): void;
33
+ /**
34
+ * Flushes all queued events to the backend.
35
+ *
36
+ * @returns Promise that resolves when the flush completes
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * await batcher.flush();
41
+ * ```
42
+ */
43
+ flush(): Promise<void>;
44
+ }
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Batcher = void 0;
4
+ const network_1 = require("../utils/network");
5
+ const retry_1 = require("./retry");
6
+ /**
7
+ * Manages automatic batching and flushing of events.
8
+ * Flushes on a timer interval or when the queue reaches the threshold.
9
+ */
10
+ class Batcher {
11
+ constructor(queue, transport, logger, flushInterval, flushAt, maxRetries) {
12
+ this.timer = null;
13
+ this.flushing = false;
14
+ this.queue = queue;
15
+ this.transport = transport;
16
+ this.logger = logger;
17
+ this.flushInterval = flushInterval;
18
+ this.flushAt = flushAt;
19
+ this.maxRetries = maxRetries;
20
+ }
21
+ /**
22
+ * Starts the automatic flush timer.
23
+ */
24
+ start() {
25
+ if (this.timer)
26
+ return;
27
+ this.timer = setInterval(() => {
28
+ this.flush().catch((err) => {
29
+ this.logger.error('Auto-flush failed', err);
30
+ });
31
+ }, this.flushInterval);
32
+ }
33
+ /**
34
+ * Stops the automatic flush timer.
35
+ */
36
+ stop() {
37
+ if (this.timer) {
38
+ clearInterval(this.timer);
39
+ this.timer = null;
40
+ }
41
+ }
42
+ /**
43
+ * Adds an event and triggers a flush if the threshold is reached.
44
+ *
45
+ * @param event - Event payload to enqueue
46
+ */
47
+ enqueue(event) {
48
+ this.queue.push(event);
49
+ if (this.queue.length >= this.flushAt) {
50
+ this.flush().catch((err) => {
51
+ this.logger.error('Threshold flush failed', err);
52
+ });
53
+ }
54
+ }
55
+ /**
56
+ * Flushes all queued events to the backend.
57
+ *
58
+ * @returns Promise that resolves when the flush completes
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * await batcher.flush();
63
+ * ```
64
+ */
65
+ async flush() {
66
+ if (this.flushing)
67
+ return;
68
+ if (this.queue.length === 0)
69
+ return;
70
+ // Skip flush if offline — events stay in queue for next attempt
71
+ const online = await (0, network_1.isOnline)();
72
+ if (!online) {
73
+ this.logger.debug('Skipping flush — device is offline');
74
+ return;
75
+ }
76
+ this.flushing = true;
77
+ const events = this.queue.flush();
78
+ try {
79
+ await (0, retry_1.withRetry)(() => this.transport.send(events), this.maxRetries, this.logger);
80
+ this.logger.debug(`Flushed ${events.length} events`);
81
+ }
82
+ catch (err) {
83
+ this.logger.error('Flush failed after retries, re-queueing events', err);
84
+ this.queue.unshift(events);
85
+ }
86
+ finally {
87
+ this.flushing = false;
88
+ }
89
+ }
90
+ }
91
+ exports.Batcher = Batcher;
@@ -0,0 +1,49 @@
1
+ import { EventPayload } from '../types/events';
2
+ import { Logger } from '../utils/logger';
3
+ /** AsyncStorage interface — matches @react-native-async-storage/async-storage. */
4
+ export interface AsyncStorageInterface {
5
+ getItem(key: string): Promise<string | null>;
6
+ setItem(key: string, value: string): Promise<void>;
7
+ removeItem(key: string): Promise<void>;
8
+ }
9
+ /**
10
+ * Handles persistence of SDK state to AsyncStorage.
11
+ * Persists the event queue, anonymous ID, user ID, opt-out state, and session.
12
+ */
13
+ export declare class Persister {
14
+ private readonly storage;
15
+ private readonly logger;
16
+ constructor(storage: AsyncStorageInterface, logger: Logger);
17
+ /** Maximum bytes to persist in AsyncStorage. Older events are dropped if exceeded. */
18
+ private static readonly MAX_PERSIST_BYTES;
19
+ /** Saves the event queue to storage, trimming oldest events if it exceeds the size limit. */
20
+ saveQueue(events: ReadonlyArray<EventPayload>): Promise<void>;
21
+ /** Loads the event queue from storage. */
22
+ loadQueue(): Promise<EventPayload[]>;
23
+ /** Clears the persisted queue. */
24
+ clearQueue(): Promise<void>;
25
+ /** Saves the anonymous ID. */
26
+ saveAnonymousId(id: string): Promise<void>;
27
+ /** Loads the anonymous ID. */
28
+ loadAnonymousId(): Promise<string | null>;
29
+ /** Saves the user ID. */
30
+ saveUserId(id: string | null): Promise<void>;
31
+ /** Loads the user ID. */
32
+ loadUserId(): Promise<string | null>;
33
+ /** Saves the opt-out state. */
34
+ saveOptOut(optedOut: boolean): Promise<void>;
35
+ /** Loads the opt-out state. */
36
+ loadOptOut(): Promise<boolean>;
37
+ /** Saves session state. */
38
+ saveSession(session: {
39
+ id: string;
40
+ startedAt: string;
41
+ lastActivity: string;
42
+ }): Promise<void>;
43
+ /** Loads session state. */
44
+ loadSession(): Promise<{
45
+ id: string;
46
+ startedAt: string;
47
+ lastActivity: string;
48
+ } | null>;
49
+ }
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Persister = void 0;
4
+ const QUEUE_STORAGE_KEY = '@bananalytics/queue';
5
+ const ANONYMOUS_ID_KEY = '@bananalytics/anonymous_id';
6
+ const USER_ID_KEY = '@bananalytics/user_id';
7
+ const OPT_OUT_KEY = '@bananalytics/opt_out';
8
+ const SESSION_KEY = '@bananalytics/session';
9
+ /**
10
+ * Handles persistence of SDK state to AsyncStorage.
11
+ * Persists the event queue, anonymous ID, user ID, opt-out state, and session.
12
+ */
13
+ class Persister {
14
+ constructor(storage, logger) {
15
+ this.storage = storage;
16
+ this.logger = logger;
17
+ }
18
+ /** Saves the event queue to storage, trimming oldest events if it exceeds the size limit. */
19
+ async saveQueue(events) {
20
+ try {
21
+ let toSave = [...events];
22
+ let serialized = JSON.stringify(toSave);
23
+ // Drop oldest events until we're under the size limit
24
+ while (serialized.length > Persister.MAX_PERSIST_BYTES && toSave.length > 0) {
25
+ const dropped = toSave.length;
26
+ toSave = toSave.slice(Math.ceil(toSave.length / 2));
27
+ this.logger.warn(`Persisted queue too large (${serialized.length} bytes), dropped ${dropped - toSave.length} oldest events`);
28
+ serialized = JSON.stringify(toSave);
29
+ }
30
+ await this.storage.setItem(QUEUE_STORAGE_KEY, serialized);
31
+ }
32
+ catch (err) {
33
+ this.logger.error('Failed to persist queue', err);
34
+ }
35
+ }
36
+ /** Loads the event queue from storage. */
37
+ async loadQueue() {
38
+ try {
39
+ const data = await this.storage.getItem(QUEUE_STORAGE_KEY);
40
+ if (data) {
41
+ return JSON.parse(data);
42
+ }
43
+ }
44
+ catch (err) {
45
+ this.logger.error('Failed to load persisted queue', err);
46
+ }
47
+ return [];
48
+ }
49
+ /** Clears the persisted queue. */
50
+ async clearQueue() {
51
+ try {
52
+ await this.storage.removeItem(QUEUE_STORAGE_KEY);
53
+ }
54
+ catch (err) {
55
+ this.logger.error('Failed to clear persisted queue', err);
56
+ }
57
+ }
58
+ /** Saves the anonymous ID. */
59
+ async saveAnonymousId(id) {
60
+ try {
61
+ await this.storage.setItem(ANONYMOUS_ID_KEY, id);
62
+ }
63
+ catch (err) {
64
+ this.logger.error('Failed to persist anonymous ID', err);
65
+ }
66
+ }
67
+ /** Loads the anonymous ID. */
68
+ async loadAnonymousId() {
69
+ try {
70
+ return await this.storage.getItem(ANONYMOUS_ID_KEY);
71
+ }
72
+ catch (err) {
73
+ this.logger.error('Failed to load anonymous ID', err);
74
+ return null;
75
+ }
76
+ }
77
+ /** Saves the user ID. */
78
+ async saveUserId(id) {
79
+ try {
80
+ if (id === null) {
81
+ await this.storage.removeItem(USER_ID_KEY);
82
+ }
83
+ else {
84
+ await this.storage.setItem(USER_ID_KEY, id);
85
+ }
86
+ }
87
+ catch (err) {
88
+ this.logger.error('Failed to persist user ID', err);
89
+ }
90
+ }
91
+ /** Loads the user ID. */
92
+ async loadUserId() {
93
+ try {
94
+ return await this.storage.getItem(USER_ID_KEY);
95
+ }
96
+ catch (err) {
97
+ this.logger.error('Failed to load user ID', err);
98
+ return null;
99
+ }
100
+ }
101
+ /** Saves the opt-out state. */
102
+ async saveOptOut(optedOut) {
103
+ try {
104
+ await this.storage.setItem(OPT_OUT_KEY, JSON.stringify(optedOut));
105
+ }
106
+ catch (err) {
107
+ this.logger.error('Failed to persist opt-out state', err);
108
+ }
109
+ }
110
+ /** Loads the opt-out state. */
111
+ async loadOptOut() {
112
+ try {
113
+ const data = await this.storage.getItem(OPT_OUT_KEY);
114
+ if (data !== null) {
115
+ return JSON.parse(data);
116
+ }
117
+ }
118
+ catch (err) {
119
+ this.logger.error('Failed to load opt-out state', err);
120
+ }
121
+ return false;
122
+ }
123
+ /** Saves session state. */
124
+ async saveSession(session) {
125
+ try {
126
+ await this.storage.setItem(SESSION_KEY, JSON.stringify(session));
127
+ }
128
+ catch (err) {
129
+ this.logger.error('Failed to persist session', err);
130
+ }
131
+ }
132
+ /** Loads session state. */
133
+ async loadSession() {
134
+ try {
135
+ const data = await this.storage.getItem(SESSION_KEY);
136
+ if (data) {
137
+ return JSON.parse(data);
138
+ }
139
+ }
140
+ catch (err) {
141
+ this.logger.error('Failed to load session', err);
142
+ }
143
+ return null;
144
+ }
145
+ }
146
+ exports.Persister = Persister;
147
+ /** Maximum bytes to persist in AsyncStorage. Older events are dropped if exceeded. */
148
+ Persister.MAX_PERSIST_BYTES = 512 * 1024; // 512KB
@@ -0,0 +1,41 @@
1
+ import { EventPayload } from '../types/events';
2
+ import { Logger } from '../utils/logger';
3
+ /**
4
+ * In-memory event queue with a configurable max size.
5
+ * Events exceeding the max size are dropped (oldest first).
6
+ */
7
+ export declare class EventQueue {
8
+ private items;
9
+ private readonly maxSize;
10
+ private readonly logger;
11
+ constructor(maxSize: number, logger: Logger);
12
+ /**
13
+ * Adds an event to the queue.
14
+ *
15
+ * @param event - The event payload to enqueue
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * queue.push(eventPayload);
20
+ * ```
21
+ */
22
+ push(event: EventPayload): void;
23
+ /**
24
+ * Adds events to the front of the queue (used for retry).
25
+ *
26
+ * @param events - Events to prepend
27
+ */
28
+ unshift(events: EventPayload[]): void;
29
+ /**
30
+ * Takes all events from the queue, clearing it.
31
+ *
32
+ * @returns All queued events
33
+ */
34
+ flush(): EventPayload[];
35
+ /** Returns the current number of events in the queue. */
36
+ get length(): number;
37
+ /** Returns all events without removing them. */
38
+ peek(): ReadonlyArray<EventPayload>;
39
+ /** Clears all events from the queue. */
40
+ clear(): void;
41
+ }
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EventQueue = void 0;
4
+ /**
5
+ * In-memory event queue with a configurable max size.
6
+ * Events exceeding the max size are dropped (oldest first).
7
+ */
8
+ class EventQueue {
9
+ constructor(maxSize, logger) {
10
+ this.items = [];
11
+ this.maxSize = maxSize;
12
+ this.logger = logger;
13
+ }
14
+ /**
15
+ * Adds an event to the queue.
16
+ *
17
+ * @param event - The event payload to enqueue
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * queue.push(eventPayload);
22
+ * ```
23
+ */
24
+ push(event) {
25
+ if (this.items.length >= this.maxSize) {
26
+ this.items.shift();
27
+ this.logger.warn(`Queue full (max ${this.maxSize}), dropping oldest event`);
28
+ }
29
+ this.items.push(event);
30
+ }
31
+ /**
32
+ * Adds events to the front of the queue (used for retry).
33
+ *
34
+ * @param events - Events to prepend
35
+ */
36
+ unshift(events) {
37
+ const available = this.maxSize - this.items.length;
38
+ const toAdd = events.slice(0, available);
39
+ this.items.unshift(...toAdd);
40
+ if (events.length > available) {
41
+ this.logger.warn(`Dropped ${events.length - available} events during retry (queue full)`);
42
+ }
43
+ }
44
+ /**
45
+ * Takes all events from the queue, clearing it.
46
+ *
47
+ * @returns All queued events
48
+ */
49
+ flush() {
50
+ const events = this.items;
51
+ this.items = [];
52
+ return events;
53
+ }
54
+ /** Returns the current number of events in the queue. */
55
+ get length() {
56
+ return this.items.length;
57
+ }
58
+ /** Returns all events without removing them. */
59
+ peek() {
60
+ return this.items;
61
+ }
62
+ /** Clears all events from the queue. */
63
+ clear() {
64
+ this.items = [];
65
+ }
66
+ }
67
+ exports.EventQueue = EventQueue;
@@ -0,0 +1,15 @@
1
+ import { Logger } from '../utils/logger';
2
+ /**
3
+ * Executes a function with exponential backoff retry logic.
4
+ *
5
+ * @param fn - The async function to retry
6
+ * @param maxRetries - Maximum number of retry attempts
7
+ * @param logger - Logger instance
8
+ * @returns The result of the function if successful
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * await withRetry(() => transport.send(events), 3, logger);
13
+ * ```
14
+ */
15
+ export declare function withRetry(fn: () => Promise<void>, maxRetries: number, logger: Logger): Promise<void>;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withRetry = withRetry;
4
+ const errors_1 = require("../core/errors");
5
+ const BASE_DELAY_MS = 1000;
6
+ /**
7
+ * Executes a function with exponential backoff retry logic.
8
+ *
9
+ * @param fn - The async function to retry
10
+ * @param maxRetries - Maximum number of retry attempts
11
+ * @param logger - Logger instance
12
+ * @returns The result of the function if successful
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * await withRetry(() => transport.send(events), 3, logger);
17
+ * ```
18
+ */
19
+ async function withRetry(fn, maxRetries, logger) {
20
+ let lastError;
21
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
22
+ try {
23
+ await fn();
24
+ return;
25
+ }
26
+ catch (err) {
27
+ lastError = err instanceof Error ? err : new Error(String(err));
28
+ // Don't retry client errors (400, 401, 403)
29
+ if (err instanceof errors_1.NetworkError && !err.isRetryable) {
30
+ logger.warn(`Non-retryable error (status ${err.statusCode}), dropping events: ${err.message}`);
31
+ return;
32
+ }
33
+ if (attempt < maxRetries) {
34
+ const delay = addJitter(BASE_DELAY_MS * Math.pow(2, attempt));
35
+ logger.debug(`Retry attempt ${attempt + 1}/${maxRetries} in ${delay}ms`);
36
+ await sleep(delay);
37
+ }
38
+ }
39
+ }
40
+ throw lastError;
41
+ }
42
+ /**
43
+ * Adds random jitter (0-50% of base) to prevent thundering herd.
44
+ * When many clients retry at the same time after an outage,
45
+ * jitter spreads them out so the server isn't overwhelmed.
46
+ */
47
+ function addJitter(baseMs) {
48
+ return Math.floor(baseMs + Math.random() * baseMs * 0.5);
49
+ }
50
+ function sleep(ms) {
51
+ return new Promise((resolve) => setTimeout(resolve, ms));
52
+ }
@@ -0,0 +1,23 @@
1
+ import { EventPayload } from '../types/events';
2
+ import { Logger } from '../utils/logger';
3
+ /**
4
+ * HTTP transport that sends event batches to the ingestion endpoint.
5
+ */
6
+ export declare class Transport {
7
+ private readonly endpoint;
8
+ private readonly apiKey;
9
+ private readonly logger;
10
+ constructor(endpoint: string, apiKey: string, logger: Logger);
11
+ /**
12
+ * Sends a batch of events to the ingestion endpoint.
13
+ *
14
+ * @param events - Array of event payloads to send
15
+ * @throws NetworkError on failure
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * await transport.send(eventBatch);
20
+ * ```
21
+ */
22
+ send(events: EventPayload[]): Promise<void>;
23
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Transport = void 0;
4
+ const errors_1 = require("../core/errors");
5
+ /**
6
+ * HTTP transport that sends event batches to the ingestion endpoint.
7
+ */
8
+ class Transport {
9
+ constructor(endpoint, apiKey, logger) {
10
+ this.endpoint = endpoint;
11
+ this.apiKey = apiKey;
12
+ this.logger = logger;
13
+ }
14
+ /**
15
+ * Sends a batch of events to the ingestion endpoint.
16
+ *
17
+ * @param events - Array of event payloads to send
18
+ * @throws NetworkError on failure
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * await transport.send(eventBatch);
23
+ * ```
24
+ */
25
+ async send(events) {
26
+ const url = `${this.endpoint}/v1/ingest`;
27
+ const body = JSON.stringify({ batch: events });
28
+ this.logger.debug(`Sending ${events.length} events to ${url}`);
29
+ try {
30
+ const response = await fetch(url, {
31
+ method: 'POST',
32
+ headers: {
33
+ 'Content-Type': 'application/json',
34
+ 'Authorization': `Bearer ${this.apiKey}`,
35
+ },
36
+ body,
37
+ });
38
+ if (!response.ok) {
39
+ throw new errors_1.NetworkError(`Ingestion failed with status ${response.status}`, response.status);
40
+ }
41
+ this.logger.debug(`Successfully sent ${events.length} events`);
42
+ }
43
+ catch (err) {
44
+ if (err instanceof errors_1.NetworkError) {
45
+ throw err;
46
+ }
47
+ throw new errors_1.NetworkError(`Network request failed: ${err instanceof Error ? err.message : String(err)}`);
48
+ }
49
+ }
50
+ }
51
+ exports.Transport = Transport;
@@ -0,0 +1,6 @@
1
+ /** Generic properties map for event data. */
2
+ export type Properties = Record<string, unknown>;
3
+ /** ISO 8601 timestamp string. */
4
+ export type Timestamp = string;
5
+ /** UUID v4 string identifier. */
6
+ export type UUID = string;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });