@databuddy/sdk 2.1.78 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.mjs +3 -3
- package/dist/node/index.d.mts +132 -0
- package/dist/node/index.d.ts +132 -0
- package/dist/node/index.mjs +344 -0
- package/dist/react/index.mjs +2 -2
- package/dist/shared/@databuddy/{sdk.CHuEu0ou.mjs → sdk.aVQee-4k.mjs} +1 -1
- package/dist/shared/@databuddy/{sdk.ItWNeH-Y.mjs → sdk.tFAHtL2M.mjs} +1 -1
- package/dist/vue/index.mjs +1 -1
- package/package.json +10 -2
package/dist/core/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { D as Databuddy$1 } from '../shared/@databuddy/sdk.
|
|
2
|
-
export { d as detectClientId } from '../shared/@databuddy/sdk.
|
|
3
|
-
export { B as BrowserFlagStorage, C as CoreFlagsManager, c as createScript, i as isScriptInjected } from '../shared/@databuddy/sdk.
|
|
1
|
+
import { D as Databuddy$1 } from '../shared/@databuddy/sdk.aVQee-4k.mjs';
|
|
2
|
+
export { d as detectClientId } from '../shared/@databuddy/sdk.aVQee-4k.mjs';
|
|
3
|
+
export { B as BrowserFlagStorage, C as CoreFlagsManager, c as createScript, i as isScriptInjected } from '../shared/@databuddy/sdk.tFAHtL2M.mjs';
|
|
4
4
|
|
|
5
5
|
function isTrackerAvailable() {
|
|
6
6
|
return typeof window !== "undefined" && (!!window.databuddy || !!window.db);
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
interface Logger {
|
|
2
|
+
info(msg: string, data?: Record<string, unknown>): void;
|
|
3
|
+
error(msg: string, data?: Record<string, unknown>): void;
|
|
4
|
+
warn(msg: string, data?: Record<string, unknown>): void;
|
|
5
|
+
debug(msg: string, data?: Record<string, unknown>): void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface DatabuddyConfig {
|
|
9
|
+
clientId: string;
|
|
10
|
+
apiUrl?: string;
|
|
11
|
+
debug?: boolean;
|
|
12
|
+
logger?: Logger;
|
|
13
|
+
/**
|
|
14
|
+
* Enable automatic batching of events
|
|
15
|
+
* Default: true
|
|
16
|
+
*/
|
|
17
|
+
enableBatching?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Number of events to batch before auto-flushing
|
|
20
|
+
* Default: 10, Max: 100
|
|
21
|
+
*/
|
|
22
|
+
batchSize?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Time in milliseconds before auto-flushing batched events
|
|
25
|
+
* Default: 2000ms (2 seconds)
|
|
26
|
+
*/
|
|
27
|
+
batchTimeout?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Maximum number of events to queue before auto-flushing
|
|
30
|
+
* Default: 1000
|
|
31
|
+
*/
|
|
32
|
+
maxQueueSize?: number;
|
|
33
|
+
}
|
|
34
|
+
interface CustomEventInput {
|
|
35
|
+
name: string;
|
|
36
|
+
eventId?: string;
|
|
37
|
+
anonymousId?: string | null;
|
|
38
|
+
sessionId?: string | null;
|
|
39
|
+
timestamp?: number | null;
|
|
40
|
+
properties?: Record<string, unknown> | null;
|
|
41
|
+
}
|
|
42
|
+
interface EventResponse {
|
|
43
|
+
success: boolean;
|
|
44
|
+
eventId?: string;
|
|
45
|
+
error?: string;
|
|
46
|
+
}
|
|
47
|
+
interface BatchEventInput {
|
|
48
|
+
type: 'custom';
|
|
49
|
+
name: string;
|
|
50
|
+
eventId?: string;
|
|
51
|
+
anonymousId?: string | null;
|
|
52
|
+
sessionId?: string | null;
|
|
53
|
+
timestamp?: number | null;
|
|
54
|
+
properties?: Record<string, unknown> | null;
|
|
55
|
+
}
|
|
56
|
+
interface BatchEventResponse {
|
|
57
|
+
success: boolean;
|
|
58
|
+
processed?: number;
|
|
59
|
+
results?: Array<{
|
|
60
|
+
status: string;
|
|
61
|
+
type?: string;
|
|
62
|
+
eventId?: string;
|
|
63
|
+
message?: string;
|
|
64
|
+
error?: string;
|
|
65
|
+
}>;
|
|
66
|
+
error?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
declare class Databuddy {
|
|
70
|
+
private clientId;
|
|
71
|
+
private apiUrl;
|
|
72
|
+
private logger;
|
|
73
|
+
private enableBatching;
|
|
74
|
+
private batchSize;
|
|
75
|
+
private batchTimeout;
|
|
76
|
+
private queue;
|
|
77
|
+
private flushTimer;
|
|
78
|
+
constructor(config: DatabuddyConfig);
|
|
79
|
+
/**
|
|
80
|
+
* Track a custom event
|
|
81
|
+
* If batching is enabled, queues the event and auto-flushes when batch size is reached or timeout expires
|
|
82
|
+
* If batching is disabled, sends the event immediately
|
|
83
|
+
*
|
|
84
|
+
* @param event - Custom event data
|
|
85
|
+
* @returns Response indicating success or failure
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* await client.track({
|
|
90
|
+
* name: 'user_signup',
|
|
91
|
+
* properties: { plan: 'pro' }
|
|
92
|
+
* });
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
track(event: CustomEventInput): Promise<EventResponse>;
|
|
96
|
+
private send;
|
|
97
|
+
private scheduleFlush;
|
|
98
|
+
/**
|
|
99
|
+
* Manually flush all queued events
|
|
100
|
+
* Important for serverless/stateless environments where you need to ensure events are sent before the process exits
|
|
101
|
+
*
|
|
102
|
+
* @returns Response with batch results
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* // In serverless function
|
|
107
|
+
* await client.track({ name: 'api_call' });
|
|
108
|
+
* await client.flush(); // Ensure events are sent before function exits
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
flush(): Promise<BatchEventResponse>;
|
|
112
|
+
/**
|
|
113
|
+
* Send multiple custom events in a single batch request
|
|
114
|
+
* Max 100 events per batch
|
|
115
|
+
* Note: Usually you don't need to call this directly - use track() with batching enabled instead
|
|
116
|
+
*
|
|
117
|
+
* @param events - Array of custom events
|
|
118
|
+
* @returns Response with results for each event
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```typescript
|
|
122
|
+
* await client.batch([
|
|
123
|
+
* { type: 'custom', name: 'event1', properties: { foo: 'bar' } },
|
|
124
|
+
* { type: 'custom', name: 'event2', properties: { baz: 'qux' } }
|
|
125
|
+
* ]);
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
batch(events: BatchEventInput[]): Promise<BatchEventResponse>;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export { Databuddy, Databuddy as db };
|
|
132
|
+
export type { BatchEventInput, BatchEventResponse, CustomEventInput, DatabuddyConfig, EventResponse, Logger };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
interface Logger {
|
|
2
|
+
info(msg: string, data?: Record<string, unknown>): void;
|
|
3
|
+
error(msg: string, data?: Record<string, unknown>): void;
|
|
4
|
+
warn(msg: string, data?: Record<string, unknown>): void;
|
|
5
|
+
debug(msg: string, data?: Record<string, unknown>): void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface DatabuddyConfig {
|
|
9
|
+
clientId: string;
|
|
10
|
+
apiUrl?: string;
|
|
11
|
+
debug?: boolean;
|
|
12
|
+
logger?: Logger;
|
|
13
|
+
/**
|
|
14
|
+
* Enable automatic batching of events
|
|
15
|
+
* Default: true
|
|
16
|
+
*/
|
|
17
|
+
enableBatching?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Number of events to batch before auto-flushing
|
|
20
|
+
* Default: 10, Max: 100
|
|
21
|
+
*/
|
|
22
|
+
batchSize?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Time in milliseconds before auto-flushing batched events
|
|
25
|
+
* Default: 2000ms (2 seconds)
|
|
26
|
+
*/
|
|
27
|
+
batchTimeout?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Maximum number of events to queue before auto-flushing
|
|
30
|
+
* Default: 1000
|
|
31
|
+
*/
|
|
32
|
+
maxQueueSize?: number;
|
|
33
|
+
}
|
|
34
|
+
interface CustomEventInput {
|
|
35
|
+
name: string;
|
|
36
|
+
eventId?: string;
|
|
37
|
+
anonymousId?: string | null;
|
|
38
|
+
sessionId?: string | null;
|
|
39
|
+
timestamp?: number | null;
|
|
40
|
+
properties?: Record<string, unknown> | null;
|
|
41
|
+
}
|
|
42
|
+
interface EventResponse {
|
|
43
|
+
success: boolean;
|
|
44
|
+
eventId?: string;
|
|
45
|
+
error?: string;
|
|
46
|
+
}
|
|
47
|
+
interface BatchEventInput {
|
|
48
|
+
type: 'custom';
|
|
49
|
+
name: string;
|
|
50
|
+
eventId?: string;
|
|
51
|
+
anonymousId?: string | null;
|
|
52
|
+
sessionId?: string | null;
|
|
53
|
+
timestamp?: number | null;
|
|
54
|
+
properties?: Record<string, unknown> | null;
|
|
55
|
+
}
|
|
56
|
+
interface BatchEventResponse {
|
|
57
|
+
success: boolean;
|
|
58
|
+
processed?: number;
|
|
59
|
+
results?: Array<{
|
|
60
|
+
status: string;
|
|
61
|
+
type?: string;
|
|
62
|
+
eventId?: string;
|
|
63
|
+
message?: string;
|
|
64
|
+
error?: string;
|
|
65
|
+
}>;
|
|
66
|
+
error?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
declare class Databuddy {
|
|
70
|
+
private clientId;
|
|
71
|
+
private apiUrl;
|
|
72
|
+
private logger;
|
|
73
|
+
private enableBatching;
|
|
74
|
+
private batchSize;
|
|
75
|
+
private batchTimeout;
|
|
76
|
+
private queue;
|
|
77
|
+
private flushTimer;
|
|
78
|
+
constructor(config: DatabuddyConfig);
|
|
79
|
+
/**
|
|
80
|
+
* Track a custom event
|
|
81
|
+
* If batching is enabled, queues the event and auto-flushes when batch size is reached or timeout expires
|
|
82
|
+
* If batching is disabled, sends the event immediately
|
|
83
|
+
*
|
|
84
|
+
* @param event - Custom event data
|
|
85
|
+
* @returns Response indicating success or failure
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* await client.track({
|
|
90
|
+
* name: 'user_signup',
|
|
91
|
+
* properties: { plan: 'pro' }
|
|
92
|
+
* });
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
track(event: CustomEventInput): Promise<EventResponse>;
|
|
96
|
+
private send;
|
|
97
|
+
private scheduleFlush;
|
|
98
|
+
/**
|
|
99
|
+
* Manually flush all queued events
|
|
100
|
+
* Important for serverless/stateless environments where you need to ensure events are sent before the process exits
|
|
101
|
+
*
|
|
102
|
+
* @returns Response with batch results
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* // In serverless function
|
|
107
|
+
* await client.track({ name: 'api_call' });
|
|
108
|
+
* await client.flush(); // Ensure events are sent before function exits
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
flush(): Promise<BatchEventResponse>;
|
|
112
|
+
/**
|
|
113
|
+
* Send multiple custom events in a single batch request
|
|
114
|
+
* Max 100 events per batch
|
|
115
|
+
* Note: Usually you don't need to call this directly - use track() with batching enabled instead
|
|
116
|
+
*
|
|
117
|
+
* @param events - Array of custom events
|
|
118
|
+
* @returns Response with results for each event
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```typescript
|
|
122
|
+
* await client.batch([
|
|
123
|
+
* { type: 'custom', name: 'event1', properties: { foo: 'bar' } },
|
|
124
|
+
* { type: 'custom', name: 'event2', properties: { baz: 'qux' } }
|
|
125
|
+
* ]);
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
batch(events: BatchEventInput[]): Promise<BatchEventResponse>;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export { Databuddy, Databuddy as db };
|
|
132
|
+
export type { BatchEventInput, BatchEventResponse, CustomEventInput, DatabuddyConfig, EventResponse, Logger };
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
function createLogger(debug = false) {
|
|
2
|
+
try {
|
|
3
|
+
const pino = require("pino");
|
|
4
|
+
return pino({
|
|
5
|
+
level: debug ? "debug" : "info",
|
|
6
|
+
name: "databuddy"
|
|
7
|
+
});
|
|
8
|
+
} catch {
|
|
9
|
+
return createConsoleLogger(debug);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function createConsoleLogger(debug) {
|
|
13
|
+
const noop = () => {
|
|
14
|
+
};
|
|
15
|
+
return {
|
|
16
|
+
info(msg, data) {
|
|
17
|
+
if (debug) {
|
|
18
|
+
console.info(`[Databuddy] ${msg}`, data ? JSON.stringify(data) : "");
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
error(msg, data) {
|
|
22
|
+
if (debug) {
|
|
23
|
+
console.error(
|
|
24
|
+
`[Databuddy] ${msg}`,
|
|
25
|
+
data ? JSON.stringify(data) : ""
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
warn(msg, data) {
|
|
30
|
+
if (debug) {
|
|
31
|
+
console.warn(`[Databuddy] ${msg}`, data ? JSON.stringify(data) : "");
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
debug: debug ? (msg, data) => {
|
|
35
|
+
console.debug(
|
|
36
|
+
`[Databuddy] ${msg}`,
|
|
37
|
+
data ? JSON.stringify(data) : ""
|
|
38
|
+
);
|
|
39
|
+
} : noop
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function createNoopLogger() {
|
|
43
|
+
const noop = () => {
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
info: noop,
|
|
47
|
+
error: noop,
|
|
48
|
+
warn: noop,
|
|
49
|
+
debug: noop
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class EventQueue {
|
|
54
|
+
queue = [];
|
|
55
|
+
maxSize;
|
|
56
|
+
constructor(maxSize) {
|
|
57
|
+
this.maxSize = maxSize;
|
|
58
|
+
}
|
|
59
|
+
add(event) {
|
|
60
|
+
this.queue.push(event);
|
|
61
|
+
return this.queue.length >= this.maxSize;
|
|
62
|
+
}
|
|
63
|
+
getAll() {
|
|
64
|
+
return [...this.queue];
|
|
65
|
+
}
|
|
66
|
+
clear() {
|
|
67
|
+
this.queue = [];
|
|
68
|
+
}
|
|
69
|
+
size() {
|
|
70
|
+
return this.queue.length;
|
|
71
|
+
}
|
|
72
|
+
isEmpty() {
|
|
73
|
+
return this.queue.length === 0;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const DEFAULT_API_URL = "https://basket.databuddy.cc";
|
|
78
|
+
const DEFAULT_BATCH_SIZE = 10;
|
|
79
|
+
const DEFAULT_BATCH_TIMEOUT = 2e3;
|
|
80
|
+
const DEFAULT_MAX_QUEUE_SIZE = 1e3;
|
|
81
|
+
class Databuddy {
|
|
82
|
+
clientId;
|
|
83
|
+
apiUrl;
|
|
84
|
+
logger;
|
|
85
|
+
enableBatching;
|
|
86
|
+
batchSize;
|
|
87
|
+
batchTimeout;
|
|
88
|
+
queue;
|
|
89
|
+
flushTimer = null;
|
|
90
|
+
constructor(config) {
|
|
91
|
+
if (!config.clientId || typeof config.clientId !== "string") {
|
|
92
|
+
throw new Error("clientId is required and must be a string");
|
|
93
|
+
}
|
|
94
|
+
this.clientId = config.clientId.trim();
|
|
95
|
+
this.apiUrl = config.apiUrl?.trim() || DEFAULT_API_URL;
|
|
96
|
+
this.enableBatching = config.enableBatching !== false;
|
|
97
|
+
this.batchSize = Math.min(config.batchSize || DEFAULT_BATCH_SIZE, 100);
|
|
98
|
+
this.batchTimeout = config.batchTimeout || DEFAULT_BATCH_TIMEOUT;
|
|
99
|
+
this.queue = new EventQueue(config.maxQueueSize || DEFAULT_MAX_QUEUE_SIZE);
|
|
100
|
+
if (config.logger) {
|
|
101
|
+
this.logger = config.logger;
|
|
102
|
+
} else if (config.debug) {
|
|
103
|
+
this.logger = createLogger(true);
|
|
104
|
+
} else {
|
|
105
|
+
this.logger = createNoopLogger();
|
|
106
|
+
}
|
|
107
|
+
this.logger.info("Initialized", {
|
|
108
|
+
clientId: this.clientId,
|
|
109
|
+
apiUrl: this.apiUrl,
|
|
110
|
+
enableBatching: this.enableBatching,
|
|
111
|
+
batchSize: this.batchSize,
|
|
112
|
+
batchTimeout: this.batchTimeout
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Track a custom event
|
|
117
|
+
* If batching is enabled, queues the event and auto-flushes when batch size is reached or timeout expires
|
|
118
|
+
* If batching is disabled, sends the event immediately
|
|
119
|
+
*
|
|
120
|
+
* @param event - Custom event data
|
|
121
|
+
* @returns Response indicating success or failure
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* await client.track({
|
|
126
|
+
* name: 'user_signup',
|
|
127
|
+
* properties: { plan: 'pro' }
|
|
128
|
+
* });
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
async track(event) {
|
|
132
|
+
if (!event.name || typeof event.name !== "string") {
|
|
133
|
+
return {
|
|
134
|
+
success: false,
|
|
135
|
+
error: "Event name is required and must be a string"
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const batchEvent = {
|
|
139
|
+
type: "custom",
|
|
140
|
+
name: event.name,
|
|
141
|
+
eventId: event.eventId,
|
|
142
|
+
anonymousId: event.anonymousId,
|
|
143
|
+
sessionId: event.sessionId,
|
|
144
|
+
timestamp: event.timestamp,
|
|
145
|
+
properties: event.properties || null
|
|
146
|
+
};
|
|
147
|
+
if (!this.enableBatching) {
|
|
148
|
+
return this.send(batchEvent);
|
|
149
|
+
}
|
|
150
|
+
const shouldFlush = this.queue.add(batchEvent);
|
|
151
|
+
this.logger.debug("Event queued", { queueSize: this.queue.size() });
|
|
152
|
+
this.scheduleFlush();
|
|
153
|
+
if (shouldFlush || this.queue.size() >= this.batchSize) {
|
|
154
|
+
await this.flush();
|
|
155
|
+
}
|
|
156
|
+
return { success: true };
|
|
157
|
+
}
|
|
158
|
+
async send(event) {
|
|
159
|
+
try {
|
|
160
|
+
const url = `${this.apiUrl}/?client_id=${encodeURIComponent(this.clientId)}`;
|
|
161
|
+
this.logger.info("\u{1F4E4} SENDING SINGLE EVENT:", {
|
|
162
|
+
name: event.name,
|
|
163
|
+
properties: JSON.stringify(event.properties, null, 2),
|
|
164
|
+
propertiesCount: Object.keys(event.properties || {}).length
|
|
165
|
+
});
|
|
166
|
+
const response = await fetch(url, {
|
|
167
|
+
method: "POST",
|
|
168
|
+
headers: {
|
|
169
|
+
"Content-Type": "application/json"
|
|
170
|
+
},
|
|
171
|
+
body: JSON.stringify(event)
|
|
172
|
+
});
|
|
173
|
+
if (!response.ok) {
|
|
174
|
+
const errorText = await response.text().catch(() => "Unknown error");
|
|
175
|
+
this.logger.error("Request failed", {
|
|
176
|
+
status: response.status,
|
|
177
|
+
statusText: response.statusText,
|
|
178
|
+
body: errorText
|
|
179
|
+
});
|
|
180
|
+
return {
|
|
181
|
+
success: false,
|
|
182
|
+
error: `HTTP ${response.status}: ${response.statusText}`
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
const data = await response.json();
|
|
186
|
+
this.logger.info("Response received", data);
|
|
187
|
+
if (data.status === "success") {
|
|
188
|
+
return {
|
|
189
|
+
success: true,
|
|
190
|
+
eventId: data.eventId
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
success: false,
|
|
195
|
+
error: data.message || "Unknown error from server"
|
|
196
|
+
};
|
|
197
|
+
} catch (error) {
|
|
198
|
+
this.logger.error("Request error", {
|
|
199
|
+
error: error instanceof Error ? error.message : String(error)
|
|
200
|
+
});
|
|
201
|
+
return {
|
|
202
|
+
success: false,
|
|
203
|
+
error: error instanceof Error ? error.message : "Network request failed"
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
scheduleFlush() {
|
|
208
|
+
if (this.flushTimer) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
this.flushTimer = setTimeout(() => {
|
|
212
|
+
this.flush().catch((error) => {
|
|
213
|
+
this.logger.error("Auto-flush error", {
|
|
214
|
+
error: error instanceof Error ? error.message : String(error)
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
}, this.batchTimeout);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Manually flush all queued events
|
|
221
|
+
* Important for serverless/stateless environments where you need to ensure events are sent before the process exits
|
|
222
|
+
*
|
|
223
|
+
* @returns Response with batch results
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* // In serverless function
|
|
228
|
+
* await client.track({ name: 'api_call' });
|
|
229
|
+
* await client.flush(); // Ensure events are sent before function exits
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
async flush() {
|
|
233
|
+
if (this.flushTimer) {
|
|
234
|
+
clearTimeout(this.flushTimer);
|
|
235
|
+
this.flushTimer = null;
|
|
236
|
+
}
|
|
237
|
+
if (this.queue.isEmpty()) {
|
|
238
|
+
return {
|
|
239
|
+
success: true,
|
|
240
|
+
processed: 0,
|
|
241
|
+
results: []
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
const events = this.queue.getAll();
|
|
245
|
+
this.queue.clear();
|
|
246
|
+
this.logger.info("Flushing events", { count: events.length });
|
|
247
|
+
return this.batch(events);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Send multiple custom events in a single batch request
|
|
251
|
+
* Max 100 events per batch
|
|
252
|
+
* Note: Usually you don't need to call this directly - use track() with batching enabled instead
|
|
253
|
+
*
|
|
254
|
+
* @param events - Array of custom events
|
|
255
|
+
* @returns Response with results for each event
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```typescript
|
|
259
|
+
* await client.batch([
|
|
260
|
+
* { type: 'custom', name: 'event1', properties: { foo: 'bar' } },
|
|
261
|
+
* { type: 'custom', name: 'event2', properties: { baz: 'qux' } }
|
|
262
|
+
* ]);
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
async batch(events) {
|
|
266
|
+
if (!Array.isArray(events)) {
|
|
267
|
+
return {
|
|
268
|
+
success: false,
|
|
269
|
+
error: "Events must be an array"
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
if (events.length === 0) {
|
|
273
|
+
return {
|
|
274
|
+
success: false,
|
|
275
|
+
error: "Events array cannot be empty"
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
if (events.length > 100) {
|
|
279
|
+
return {
|
|
280
|
+
success: false,
|
|
281
|
+
error: "Batch size cannot exceed 100 events"
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
for (const event of events) {
|
|
285
|
+
if (!event.name || typeof event.name !== "string") {
|
|
286
|
+
return {
|
|
287
|
+
success: false,
|
|
288
|
+
error: "All events must have a valid name"
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
const url = `${this.apiUrl}/batch?client_id=${encodeURIComponent(this.clientId)}`;
|
|
294
|
+
this.logger.info("\u{1F4E6} SENDING BATCH EVENTS:", {
|
|
295
|
+
count: events.length,
|
|
296
|
+
firstEventName: events[0]?.name,
|
|
297
|
+
firstEventProperties: JSON.stringify(events[0]?.properties, null, 2),
|
|
298
|
+
firstEventPropertiesCount: Object.keys(events[0]?.properties || {}).length
|
|
299
|
+
});
|
|
300
|
+
const response = await fetch(url, {
|
|
301
|
+
method: "POST",
|
|
302
|
+
headers: {
|
|
303
|
+
"Content-Type": "application/json"
|
|
304
|
+
},
|
|
305
|
+
body: JSON.stringify(events)
|
|
306
|
+
});
|
|
307
|
+
if (!response.ok) {
|
|
308
|
+
const errorText = await response.text().catch(() => "Unknown error");
|
|
309
|
+
this.logger.error("Batch request failed", {
|
|
310
|
+
status: response.status,
|
|
311
|
+
statusText: response.statusText,
|
|
312
|
+
body: errorText
|
|
313
|
+
});
|
|
314
|
+
return {
|
|
315
|
+
success: false,
|
|
316
|
+
error: `HTTP ${response.status}: ${response.statusText}`
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
const data = await response.json();
|
|
320
|
+
this.logger.info("Batch response received", data);
|
|
321
|
+
if (data.status === "success") {
|
|
322
|
+
return {
|
|
323
|
+
success: true,
|
|
324
|
+
processed: data.processed,
|
|
325
|
+
results: data.results
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
success: false,
|
|
330
|
+
error: data.message || "Unknown error from server"
|
|
331
|
+
};
|
|
332
|
+
} catch (error) {
|
|
333
|
+
this.logger.error("Batch request error", {
|
|
334
|
+
error: error instanceof Error ? error.message : String(error)
|
|
335
|
+
});
|
|
336
|
+
return {
|
|
337
|
+
success: false,
|
|
338
|
+
error: error instanceof Error ? error.message : "Network request failed"
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export { Databuddy, Databuddy as db };
|
package/dist/react/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
export { D as Databuddy } from '../shared/@databuddy/sdk.
|
|
3
|
+
export { D as Databuddy } from '../shared/@databuddy/sdk.aVQee-4k.mjs';
|
|
4
4
|
import { createStore, atom, Provider, useAtom } from 'jotai';
|
|
5
5
|
import { useRef, useEffect, createElement } from 'react';
|
|
6
|
-
import { B as BrowserFlagStorage, C as CoreFlagsManager, l as logger } from '../shared/@databuddy/sdk.
|
|
6
|
+
import { B as BrowserFlagStorage, C as CoreFlagsManager, l as logger } from '../shared/@databuddy/sdk.tFAHtL2M.mjs';
|
|
7
7
|
|
|
8
8
|
const flagsStore = createStore();
|
|
9
9
|
const managerAtom = atom(null);
|
package/dist/vue/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineComponent, ref, onMounted, onUnmounted, watch, reactive, watchEffect, computed } from 'vue';
|
|
2
|
-
import { i as isScriptInjected, c as createScript, B as BrowserFlagStorage, C as CoreFlagsManager } from '../shared/@databuddy/sdk.
|
|
2
|
+
import { i as isScriptInjected, c as createScript, B as BrowserFlagStorage, C as CoreFlagsManager } from '../shared/@databuddy/sdk.tFAHtL2M.mjs';
|
|
3
3
|
|
|
4
4
|
const Databuddy = defineComponent({
|
|
5
5
|
props: {},
|
package/package.json
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@databuddy/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Official Databuddy Analytics SDK",
|
|
5
5
|
"main": "./dist/core/index.mjs",
|
|
6
6
|
"types": "./dist/core/index.d.ts",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"private": false,
|
|
9
9
|
"scripts": {
|
|
10
|
-
"build": "unbuild"
|
|
10
|
+
"build": "unbuild",
|
|
11
|
+
"test": "bun test"
|
|
11
12
|
},
|
|
12
13
|
"dependencies": {
|
|
13
14
|
"jotai": ">=2.0.0"
|
|
14
15
|
},
|
|
16
|
+
"optionalDependencies": {
|
|
17
|
+
"pino": "^9.0.0"
|
|
18
|
+
},
|
|
15
19
|
"devDependencies": {
|
|
16
20
|
"@types/node": "^20.0.0",
|
|
17
21
|
"@vitejs/plugin-react": "^5.0.0",
|
|
@@ -42,6 +46,10 @@
|
|
|
42
46
|
"./vue": {
|
|
43
47
|
"types": "./dist/vue/index.d.ts",
|
|
44
48
|
"import": "./dist/vue/index.mjs"
|
|
49
|
+
},
|
|
50
|
+
"./node": {
|
|
51
|
+
"types": "./dist/node/index.d.ts",
|
|
52
|
+
"import": "./dist/node/index.mjs"
|
|
45
53
|
}
|
|
46
54
|
},
|
|
47
55
|
"files": [
|