@od-oneapp/analytics 2026.1.1301
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 +509 -0
- package/dist/ai-YMnynb-t.mjs +3347 -0
- package/dist/ai-YMnynb-t.mjs.map +1 -0
- package/dist/chunk-DQk6qfdC.mjs +18 -0
- package/dist/client-CTzJVFU5.mjs +9 -0
- package/dist/client-CTzJVFU5.mjs.map +1 -0
- package/dist/client-CcFTauAh.mjs +54 -0
- package/dist/client-CcFTauAh.mjs.map +1 -0
- package/dist/client-CeOLjbac.mjs +281 -0
- package/dist/client-CeOLjbac.mjs.map +1 -0
- package/dist/client-D339NFJS.mjs +267 -0
- package/dist/client-D339NFJS.mjs.map +1 -0
- package/dist/client-next.d.mts +62 -0
- package/dist/client-next.d.mts.map +1 -0
- package/dist/client-next.mjs +525 -0
- package/dist/client-next.mjs.map +1 -0
- package/dist/client.d.mts +30 -0
- package/dist/client.d.mts.map +1 -0
- package/dist/client.mjs +186 -0
- package/dist/client.mjs.map +1 -0
- package/dist/config-DPS6bSYo.d.mts +34 -0
- package/dist/config-DPS6bSYo.d.mts.map +1 -0
- package/dist/config-P6P5adJg.mjs +287 -0
- package/dist/config-P6P5adJg.mjs.map +1 -0
- package/dist/console-8bND3mMU.mjs +128 -0
- package/dist/console-8bND3mMU.mjs.map +1 -0
- package/dist/ecommerce-Cgu4wlux.mjs +993 -0
- package/dist/ecommerce-Cgu4wlux.mjs.map +1 -0
- package/dist/emitters-6-nKo8i-.mjs +208 -0
- package/dist/emitters-6-nKo8i-.mjs.map +1 -0
- package/dist/emitters-DldkVSPp.d.mts +12 -0
- package/dist/emitters-DldkVSPp.d.mts.map +1 -0
- package/dist/index-BfNWgfa5.d.mts +1494 -0
- package/dist/index-BfNWgfa5.d.mts.map +1 -0
- package/dist/index-BkIWe--N.d.mts +953 -0
- package/dist/index-BkIWe--N.d.mts.map +1 -0
- package/dist/index-jPzXRn52.d.mts +184 -0
- package/dist/index-jPzXRn52.d.mts.map +1 -0
- package/dist/manager-DvRRjza6.d.mts +76 -0
- package/dist/manager-DvRRjza6.d.mts.map +1 -0
- package/dist/posthog-bootstrap-CYfIy_WS.mjs +1769 -0
- package/dist/posthog-bootstrap-CYfIy_WS.mjs.map +1 -0
- package/dist/posthog-bootstrap-DWxFrxlt.d.mts +81 -0
- package/dist/posthog-bootstrap-DWxFrxlt.d.mts.map +1 -0
- package/dist/providers-http-client.d.mts +37 -0
- package/dist/providers-http-client.d.mts.map +1 -0
- package/dist/providers-http-client.mjs +320 -0
- package/dist/providers-http-client.mjs.map +1 -0
- package/dist/providers-http-server.d.mts +31 -0
- package/dist/providers-http-server.d.mts.map +1 -0
- package/dist/providers-http-server.mjs +297 -0
- package/dist/providers-http-server.mjs.map +1 -0
- package/dist/providers-http.d.mts +46 -0
- package/dist/providers-http.d.mts.map +1 -0
- package/dist/providers-http.mjs +4 -0
- package/dist/server-edge.d.mts +9 -0
- package/dist/server-edge.d.mts.map +1 -0
- package/dist/server-edge.mjs +373 -0
- package/dist/server-edge.mjs.map +1 -0
- package/dist/server-next.d.mts +67 -0
- package/dist/server-next.d.mts.map +1 -0
- package/dist/server-next.mjs +193 -0
- package/dist/server-next.mjs.map +1 -0
- package/dist/server.d.mts +10 -0
- package/dist/server.mjs +7 -0
- package/dist/service-cYtBBL8x.mjs +945 -0
- package/dist/service-cYtBBL8x.mjs.map +1 -0
- package/dist/shared.d.mts +16 -0
- package/dist/shared.d.mts.map +1 -0
- package/dist/shared.mjs +93 -0
- package/dist/shared.mjs.map +1 -0
- package/dist/types-BxBnNQ0V.d.mts +354 -0
- package/dist/types-BxBnNQ0V.d.mts.map +1 -0
- package/dist/types-CBvxUEaF.d.mts +216 -0
- package/dist/types-CBvxUEaF.d.mts.map +1 -0
- package/dist/types.d.mts +4 -0
- package/dist/types.mjs +0 -0
- package/dist/vercel-types-lwakUfoI.d.mts +102 -0
- package/dist/vercel-types-lwakUfoI.d.mts.map +1 -0
- package/package.json +129 -0
- package/src/client/index.ts +164 -0
- package/src/client/manager.ts +71 -0
- package/src/client/next/components.tsx +270 -0
- package/src/client/next/hooks.ts +217 -0
- package/src/client/next/manager.ts +141 -0
- package/src/client/next.ts +144 -0
- package/src/client-next.ts +101 -0
- package/src/client.ts +89 -0
- package/src/examples/ai-sdk-patterns.ts +583 -0
- package/src/examples/emitter-patterns.ts +476 -0
- package/src/examples/nextjs-emitter-patterns.tsx +403 -0
- package/src/next/app-router.tsx +564 -0
- package/src/next/client.ts +419 -0
- package/src/next/index.ts +84 -0
- package/src/next/middleware.ts +429 -0
- package/src/next/rsc.tsx +300 -0
- package/src/next/server.ts +253 -0
- package/src/next/types.d.ts +220 -0
- package/src/providers/base-provider.ts +419 -0
- package/src/providers/console/client.ts +10 -0
- package/src/providers/console/index.ts +152 -0
- package/src/providers/console/server.ts +6 -0
- package/src/providers/console/types.ts +15 -0
- package/src/providers/http/client.ts +464 -0
- package/src/providers/http/index.ts +30 -0
- package/src/providers/http/server.ts +396 -0
- package/src/providers/http/types.ts +135 -0
- package/src/providers/posthog/client.ts +518 -0
- package/src/providers/posthog/index.ts +11 -0
- package/src/providers/posthog/server.ts +329 -0
- package/src/providers/posthog/types.ts +104 -0
- package/src/providers/segment/client.ts +113 -0
- package/src/providers/segment/index.ts +11 -0
- package/src/providers/segment/server.ts +115 -0
- package/src/providers/segment/types.ts +51 -0
- package/src/providers/vercel/client.ts +102 -0
- package/src/providers/vercel/index.ts +11 -0
- package/src/providers/vercel/server.ts +89 -0
- package/src/providers/vercel/types.ts +27 -0
- package/src/server/index.ts +103 -0
- package/src/server/manager.ts +62 -0
- package/src/server/next.ts +210 -0
- package/src/server-edge.ts +442 -0
- package/src/server-next.ts +39 -0
- package/src/server.ts +106 -0
- package/src/shared/emitters/ai/README.md +981 -0
- package/src/shared/emitters/ai/events/agent.ts +130 -0
- package/src/shared/emitters/ai/events/artifacts.ts +167 -0
- package/src/shared/emitters/ai/events/chat.ts +126 -0
- package/src/shared/emitters/ai/events/chatbot-ecommerce.ts +133 -0
- package/src/shared/emitters/ai/events/completion.ts +103 -0
- package/src/shared/emitters/ai/events/content-generation.ts +347 -0
- package/src/shared/emitters/ai/events/conversation.ts +332 -0
- package/src/shared/emitters/ai/events/product-features.ts +1402 -0
- package/src/shared/emitters/ai/events/streaming.ts +114 -0
- package/src/shared/emitters/ai/events/tool.ts +93 -0
- package/src/shared/emitters/ai/index.ts +69 -0
- package/src/shared/emitters/ai/track-ai-sdk.ts +74 -0
- package/src/shared/emitters/ai/track-ai.ts +50 -0
- package/src/shared/emitters/ai/types.ts +1041 -0
- package/src/shared/emitters/ai/utils.ts +468 -0
- package/src/shared/emitters/ecommerce/events/cart-checkout.ts +106 -0
- package/src/shared/emitters/ecommerce/events/coupon.ts +49 -0
- package/src/shared/emitters/ecommerce/events/engagement.ts +61 -0
- package/src/shared/emitters/ecommerce/events/marketplace.ts +119 -0
- package/src/shared/emitters/ecommerce/events/order.ts +199 -0
- package/src/shared/emitters/ecommerce/events/product.ts +205 -0
- package/src/shared/emitters/ecommerce/events/registry.ts +123 -0
- package/src/shared/emitters/ecommerce/events/wishlist-sharing.ts +140 -0
- package/src/shared/emitters/ecommerce/index.ts +46 -0
- package/src/shared/emitters/ecommerce/track-ecommerce.ts +53 -0
- package/src/shared/emitters/ecommerce/types.ts +314 -0
- package/src/shared/emitters/ecommerce/utils.ts +216 -0
- package/src/shared/emitters/emitter-types.ts +974 -0
- package/src/shared/emitters/emitters.ts +292 -0
- package/src/shared/emitters/helpers.ts +419 -0
- package/src/shared/emitters/index.ts +66 -0
- package/src/shared/index.ts +142 -0
- package/src/shared/ingestion/index.ts +66 -0
- package/src/shared/ingestion/schemas.ts +386 -0
- package/src/shared/ingestion/service.ts +628 -0
- package/src/shared/node22-features.ts +848 -0
- package/src/shared/providers/console-provider.ts +160 -0
- package/src/shared/types/base-types.ts +54 -0
- package/src/shared/types/console-types.ts +19 -0
- package/src/shared/types/posthog-types.ts +131 -0
- package/src/shared/types/segment-types.ts +15 -0
- package/src/shared/types/types.ts +397 -0
- package/src/shared/types/vercel-types.ts +19 -0
- package/src/shared/utils/config-client.ts +19 -0
- package/src/shared/utils/config.ts +250 -0
- package/src/shared/utils/emitter-adapter.ts +212 -0
- package/src/shared/utils/manager.test.ts +36 -0
- package/src/shared/utils/manager.ts +1322 -0
- package/src/shared/utils/posthog-bootstrap.ts +136 -0
- package/src/shared/utils/posthog-client-utils.ts +48 -0
- package/src/shared/utils/posthog-next-utils.ts +282 -0
- package/src/shared/utils/posthog-server-utils.ts +210 -0
- package/src/shared/utils/rate-limit.ts +289 -0
- package/src/shared/utils/security.ts +545 -0
- package/src/shared/utils/validation-client.ts +161 -0
- package/src/shared/utils/validation.ts +399 -0
- package/src/shared.ts +155 -0
- package/src/types/index.ts +62 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Rate limiting utilities for analytics calls
|
|
3
|
+
* Rate limiting utilities for analytics calls
|
|
4
|
+
*
|
|
5
|
+
* Prevents overwhelming provider APIs, hitting rate limits, and excessive costs.
|
|
6
|
+
* Uses token bucket algorithm for smooth rate limiting with bursts.
|
|
7
|
+
*
|
|
8
|
+
* @module rate-limit
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { logWarn } from '@repo/shared/logger';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Rate limiter configuration
|
|
15
|
+
*/
|
|
16
|
+
export interface RateLimiterConfig {
|
|
17
|
+
/** Maximum number of calls per window */
|
|
18
|
+
maxCalls: number;
|
|
19
|
+
/** Window size in milliseconds */
|
|
20
|
+
windowMs: number;
|
|
21
|
+
/** Maximum number of concurrent calls */
|
|
22
|
+
maxConcurrent?: number;
|
|
23
|
+
/** Whether to queue excess calls or drop them */
|
|
24
|
+
queueExcess?: boolean;
|
|
25
|
+
/** Maximum queue size */
|
|
26
|
+
maxQueueSize?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Rate limiter for analytics calls
|
|
31
|
+
*
|
|
32
|
+
* Implements token bucket algorithm with optional call queuing.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const limiter = new RateLimiter({
|
|
37
|
+
* maxCalls: 100,
|
|
38
|
+
* windowMs: 1000, // 100 calls per second
|
|
39
|
+
* maxConcurrent: 10,
|
|
40
|
+
* });
|
|
41
|
+
*
|
|
42
|
+
* // Check if call is allowed
|
|
43
|
+
* if (await limiter.tryAcquire()) {
|
|
44
|
+
* await analytics.track('Event');
|
|
45
|
+
* limiter.release();
|
|
46
|
+
* }
|
|
47
|
+
*
|
|
48
|
+
* // Or use wrapper
|
|
49
|
+
* await limiter.execute(() => analytics.track('Event'));
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export class RateLimiter {
|
|
53
|
+
private callCount = 0;
|
|
54
|
+
private concurrentCount = 0;
|
|
55
|
+
private windowStart = Date.now();
|
|
56
|
+
private queue: Array<() => void> = [];
|
|
57
|
+
private readonly config: Required<RateLimiterConfig>;
|
|
58
|
+
|
|
59
|
+
constructor(config: RateLimiterConfig) {
|
|
60
|
+
this.config = {
|
|
61
|
+
maxCalls: config.maxCalls,
|
|
62
|
+
windowMs: config.windowMs,
|
|
63
|
+
maxConcurrent: config.maxConcurrent ?? Infinity,
|
|
64
|
+
queueExcess: config.queueExcess ?? false,
|
|
65
|
+
maxQueueSize: config.maxQueueSize ?? 1000,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Try to acquire a rate limit token
|
|
71
|
+
*
|
|
72
|
+
* @returns Promise resolving to whether the call is allowed
|
|
73
|
+
*/
|
|
74
|
+
async tryAcquire(): Promise<boolean> {
|
|
75
|
+
// Reset window if needed
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
if (now - this.windowStart >= this.config.windowMs) {
|
|
78
|
+
this.callCount = 0;
|
|
79
|
+
this.windowStart = now;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Check rate limit
|
|
83
|
+
if (this.callCount >= this.config.maxCalls) {
|
|
84
|
+
if (this.config.queueExcess && this.queue.length < this.config.maxQueueSize) {
|
|
85
|
+
// Queue the call - will re-check rate limit when dequeued
|
|
86
|
+
return new Promise<boolean>(resolve => {
|
|
87
|
+
this.queue.push(() => {
|
|
88
|
+
// Re-check rate limit when dequeued to prevent bypassing
|
|
89
|
+
const now = Date.now();
|
|
90
|
+
if (now - this.windowStart >= this.config.windowMs) {
|
|
91
|
+
this.callCount = 0;
|
|
92
|
+
this.windowStart = now;
|
|
93
|
+
}
|
|
94
|
+
if (this.callCount < this.config.maxCalls) {
|
|
95
|
+
this.callCount++;
|
|
96
|
+
resolve(true);
|
|
97
|
+
} else {
|
|
98
|
+
resolve(false);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (process.env.NODE_ENV === 'development') {
|
|
105
|
+
logWarn('Rate limit exceeded', {
|
|
106
|
+
maxCalls: this.config.maxCalls,
|
|
107
|
+
windowMs: this.config.windowMs,
|
|
108
|
+
currentCount: this.callCount,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Check concurrency limit
|
|
116
|
+
if (this.concurrentCount >= this.config.maxConcurrent) {
|
|
117
|
+
if (this.config.queueExcess && this.queue.length < this.config.maxQueueSize) {
|
|
118
|
+
// Queue the call
|
|
119
|
+
return new Promise<boolean>(resolve => {
|
|
120
|
+
this.queue.push(() => resolve(true));
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
this.callCount++;
|
|
128
|
+
this.concurrentCount++;
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Release a rate limit token
|
|
134
|
+
*/
|
|
135
|
+
release(): void {
|
|
136
|
+
this.concurrentCount = Math.max(0, this.concurrentCount - 1);
|
|
137
|
+
|
|
138
|
+
// Process queue - only process if concurrency allows and rate limit allows
|
|
139
|
+
if (this.queue.length > 0 && this.concurrentCount < this.config.maxConcurrent) {
|
|
140
|
+
// Reset rate limit window if needed before processing queue
|
|
141
|
+
const now = Date.now();
|
|
142
|
+
if (now - this.windowStart >= this.config.windowMs) {
|
|
143
|
+
this.callCount = 0;
|
|
144
|
+
this.windowStart = now;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Only process if rate limit allows
|
|
148
|
+
if (this.callCount < this.config.maxCalls) {
|
|
149
|
+
const next = this.queue.shift();
|
|
150
|
+
if (next) {
|
|
151
|
+
this.concurrentCount++;
|
|
152
|
+
next();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Execute a function with rate limiting
|
|
160
|
+
*
|
|
161
|
+
* @param fn - Function to execute
|
|
162
|
+
* @returns Promise resolving to function result
|
|
163
|
+
*/
|
|
164
|
+
async execute<T>(fn: () => Promise<T>): Promise<T | null> {
|
|
165
|
+
const allowed = await this.tryAcquire();
|
|
166
|
+
|
|
167
|
+
if (!allowed) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
return await fn();
|
|
173
|
+
} finally {
|
|
174
|
+
this.release();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get current rate limiter stats
|
|
180
|
+
*/
|
|
181
|
+
getStats(): {
|
|
182
|
+
callCount: number;
|
|
183
|
+
concurrentCount: number;
|
|
184
|
+
queueSize: number;
|
|
185
|
+
remainingCalls: number;
|
|
186
|
+
windowStart: number;
|
|
187
|
+
} {
|
|
188
|
+
return {
|
|
189
|
+
callCount: this.callCount,
|
|
190
|
+
concurrentCount: this.concurrentCount,
|
|
191
|
+
queueSize: this.queue.length,
|
|
192
|
+
remainingCalls: Math.max(0, this.config.maxCalls - this.callCount),
|
|
193
|
+
windowStart: this.windowStart,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Reset the rate limiter
|
|
199
|
+
*/
|
|
200
|
+
reset(): void {
|
|
201
|
+
this.callCount = 0;
|
|
202
|
+
this.concurrentCount = 0;
|
|
203
|
+
this.windowStart = Date.now();
|
|
204
|
+
this.queue = [];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Simple in-memory rate limiter using Map
|
|
210
|
+
*
|
|
211
|
+
* Useful for per-provider or per-user rate limiting.
|
|
212
|
+
*/
|
|
213
|
+
export class MapRateLimiter {
|
|
214
|
+
private limiters = new Map<string, RateLimiter>();
|
|
215
|
+
private readonly config: RateLimiterConfig;
|
|
216
|
+
|
|
217
|
+
constructor(config: RateLimiterConfig) {
|
|
218
|
+
this.config = config;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Get or create a rate limiter for a key
|
|
223
|
+
*/
|
|
224
|
+
private getLimiter(key: string): RateLimiter {
|
|
225
|
+
let limiter = this.limiters.get(key);
|
|
226
|
+
|
|
227
|
+
if (!limiter) {
|
|
228
|
+
limiter = new RateLimiter(this.config);
|
|
229
|
+
this.limiters.set(key, limiter);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return limiter;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Try to acquire a rate limit token for a key
|
|
237
|
+
*/
|
|
238
|
+
async tryAcquire(key: string): Promise<boolean> {
|
|
239
|
+
return this.getLimiter(key).tryAcquire();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Release a rate limit token for a key
|
|
244
|
+
*/
|
|
245
|
+
release(key: string): void {
|
|
246
|
+
this.getLimiter(key).release();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Execute a function with rate limiting for a key
|
|
251
|
+
*/
|
|
252
|
+
async execute<T>(key: string, fn: () => Promise<T>): Promise<T | null> {
|
|
253
|
+
return this.getLimiter(key).execute(fn);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Get stats for a key
|
|
258
|
+
*/
|
|
259
|
+
getStats(key: string): ReturnType<RateLimiter['getStats']> | null {
|
|
260
|
+
const limiter = this.limiters.get(key);
|
|
261
|
+
return limiter ? limiter.getStats() : null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Reset rate limiter for a key
|
|
266
|
+
*/
|
|
267
|
+
reset(key: string): void {
|
|
268
|
+
const limiter = this.limiters.get(key);
|
|
269
|
+
if (limiter) {
|
|
270
|
+
limiter.reset();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Clean up old limiters (call periodically)
|
|
276
|
+
*/
|
|
277
|
+
cleanup(maxAge: number = 60000): void {
|
|
278
|
+
const now = Date.now();
|
|
279
|
+
|
|
280
|
+
for (const [key, limiter] of this.limiters.entries()) {
|
|
281
|
+
const stats = limiter.getStats();
|
|
282
|
+
|
|
283
|
+
// Remove if inactive for maxAge
|
|
284
|
+
if (now - stats.windowStart > maxAge && stats.callCount === 0 && stats.queueSize === 0) {
|
|
285
|
+
this.limiters.delete(key);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|