@jami-studio/core 0.92.27 → 0.92.29
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +34 -0
- package/corpus/core/package.json +2 -1
- package/corpus/core/src/deploy/build.ts +120 -2
- package/corpus/core/src/event-bus/bus.ts +138 -140
- package/corpus/core/src/event-bus/registry.ts +81 -79
- package/corpus/core/src/file-upload/registry.ts +135 -125
- package/corpus/core/src/notifications/registry.ts +218 -216
- package/corpus/core/src/private-blob/registry.ts +246 -242
- package/corpus/core/src/secrets/register.ts +132 -129
- package/corpus/core/src/shared/global-scope.ts +75 -0
- package/corpus/core/src/sharing/registry.ts +193 -194
- package/corpus/core/src/tracking/providers.ts +425 -422
- package/corpus/core/src/tracking/registry.ts +102 -102
- package/dist/collab/routes.d.ts +1 -1
- package/dist/deploy/build.d.ts +44 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +91 -2
- package/dist/deploy/build.js.map +1 -1
- package/dist/event-bus/bus.d.ts.map +1 -1
- package/dist/event-bus/bus.js +8 -6
- package/dist/event-bus/bus.js.map +1 -1
- package/dist/event-bus/registry.d.ts.map +1 -1
- package/dist/event-bus/registry.js +10 -6
- package/dist/event-bus/registry.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/file-upload/registry.d.ts.map +1 -1
- package/dist/file-upload/registry.js +28 -8
- package/dist/file-upload/registry.js.map +1 -1
- package/dist/notifications/registry.d.ts.map +1 -1
- package/dist/notifications/registry.js +6 -5
- package/dist/notifications/registry.js.map +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/private-blob/registry.d.ts.map +1 -1
- package/dist/private-blob/registry.js +18 -13
- package/dist/private-blob/registry.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/secrets/register.d.ts.map +1 -1
- package/dist/secrets/register.js +11 -7
- package/dist/secrets/register.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/shared/global-scope.d.ts +55 -0
- package/dist/shared/global-scope.d.ts.map +1 -0
- package/dist/shared/global-scope.js +70 -0
- package/dist/shared/global-scope.js.map +1 -0
- package/dist/sharing/registry.d.ts.map +1 -1
- package/dist/sharing/registry.js +2 -16
- package/dist/sharing/registry.js.map +1 -1
- package/dist/tracking/providers.d.ts.map +1 -1
- package/dist/tracking/providers.js +11 -9
- package/dist/tracking/providers.js.map +1 -1
- package/dist/tracking/registry.d.ts.map +1 -1
- package/dist/tracking/registry.js +5 -5
- package/dist/tracking/registry.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,422 +1,425 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Built-in tracking providers that auto-register from env vars.
|
|
3
|
-
*
|
|
4
|
-
* No SDK dependencies — uses raw HTTP to keep core lightweight.
|
|
5
|
-
* Set the env var and tracking starts automatically.
|
|
6
|
-
*
|
|
7
|
-
* POSTHOG_API_KEY + POSTHOG_HOST → PostHog
|
|
8
|
-
* MIXPANEL_TOKEN → Mixpanel
|
|
9
|
-
* AMPLITUDE_API_KEY → Amplitude
|
|
10
|
-
* AGENT_NATIVE_ANALYTICS_PUBLIC_KEY → Agent Native Analytics
|
|
11
|
-
*
|
|
12
|
-
* Call `registerBuiltinProviders()` at server startup (done
|
|
13
|
-
* automatically by the core-routes plugin).
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import {
|
|
17
|
-
import
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
h === "
|
|
117
|
-
h
|
|
118
|
-
h
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (process.env.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
process.env.
|
|
135
|
-
process.env.
|
|
136
|
-
process.env.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
process.env.
|
|
146
|
-
process.env.
|
|
147
|
-
process.env.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
},
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
},
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
},
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
},
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
},
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
)
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Built-in tracking providers that auto-register from env vars.
|
|
3
|
+
*
|
|
4
|
+
* No SDK dependencies — uses raw HTTP to keep core lightweight.
|
|
5
|
+
* Set the env var and tracking starts automatically.
|
|
6
|
+
*
|
|
7
|
+
* POSTHOG_API_KEY + POSTHOG_HOST → PostHog
|
|
8
|
+
* MIXPANEL_TOKEN → Mixpanel
|
|
9
|
+
* AMPLITUDE_API_KEY → Amplitude
|
|
10
|
+
* AGENT_NATIVE_ANALYTICS_PUBLIC_KEY → Agent Native Analytics
|
|
11
|
+
*
|
|
12
|
+
* Call `registerBuiltinProviders()` at server startup (done
|
|
13
|
+
* automatically by the core-routes plugin).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { getScopedGlobal } from "../shared/global-scope.js";
|
|
17
|
+
import { registerTrackingProvider } from "./registry.js";
|
|
18
|
+
import type { TrackingProvider, TrackingEvent } from "./types.js";
|
|
19
|
+
|
|
20
|
+
const POSTHOG_DEFAULT_HOST = "https://us.i.posthog.com";
|
|
21
|
+
const AGENT_NATIVE_ANALYTICS_DEFAULT_ENDPOINT =
|
|
22
|
+
"https://analytics.jami.studio/track";
|
|
23
|
+
const BATCH_INTERVAL_MS = 10_000;
|
|
24
|
+
const MAX_BATCH_SIZE = 50;
|
|
25
|
+
|
|
26
|
+
// ─── Batched sender ────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
interface QueuedEvent {
|
|
29
|
+
url: string;
|
|
30
|
+
body: string;
|
|
31
|
+
headers?: Record<string, string>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface EnqueueOptions {
|
|
35
|
+
flushImmediately?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Use globalThis so multiple ESM graph instances (Vite dev + Nitro symlinks)
|
|
39
|
+
// share one queue, matching the same pattern as the tracking registry.
|
|
40
|
+
// Scope-aware + lazily resolved so unified workspace deployments keep
|
|
41
|
+
// per-app queues/timers. See shared/global-scope.
|
|
42
|
+
const QUEUE_BASE_KEY = "agent-native.tracking.queue";
|
|
43
|
+
const TIMER_BASE_KEY = "agent-native.tracking.timer";
|
|
44
|
+
|
|
45
|
+
interface TimerBox {
|
|
46
|
+
timer: ReturnType<typeof setTimeout> | null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getQueue(): QueuedEvent[] {
|
|
50
|
+
return getScopedGlobal(QUEUE_BASE_KEY, () => [] as QueuedEvent[]);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getTimerBox(): TimerBox {
|
|
54
|
+
return getScopedGlobal(TIMER_BASE_KEY, () => ({ timer: null }) as TimerBox);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getTimer(): ReturnType<typeof setTimeout> | null {
|
|
58
|
+
return getTimerBox().timer;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function setTimer(t: ReturnType<typeof setTimeout> | null): void {
|
|
62
|
+
getTimerBox().timer = t;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function enqueue(
|
|
66
|
+
url: string,
|
|
67
|
+
body: string,
|
|
68
|
+
headers?: Record<string, string>,
|
|
69
|
+
options?: EnqueueOptions,
|
|
70
|
+
): void {
|
|
71
|
+
const queue = getQueue();
|
|
72
|
+
queue.push({ url, body, headers });
|
|
73
|
+
if (options?.flushImmediately || queue.length >= MAX_BATCH_SIZE) {
|
|
74
|
+
void drainQueue();
|
|
75
|
+
} else if (!getTimer()) {
|
|
76
|
+
const timer = setTimeout(() => {
|
|
77
|
+
void drainQueue();
|
|
78
|
+
}, BATCH_INTERVAL_MS);
|
|
79
|
+
if (timer.unref) timer.unref();
|
|
80
|
+
setTimer(timer);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function drainQueue(): Promise<void[]> {
|
|
85
|
+
const t = getTimer();
|
|
86
|
+
if (t) {
|
|
87
|
+
clearTimeout(t);
|
|
88
|
+
setTimer(null);
|
|
89
|
+
}
|
|
90
|
+
const queue = getQueue();
|
|
91
|
+
const batch = queue.splice(0, queue.length);
|
|
92
|
+
return Promise.all(
|
|
93
|
+
batch.map((item) =>
|
|
94
|
+
fetch(item.url, {
|
|
95
|
+
method: "POST",
|
|
96
|
+
headers: { "Content-Type": "application/json", ...item.headers },
|
|
97
|
+
body: item.body,
|
|
98
|
+
}).then(
|
|
99
|
+
() => undefined,
|
|
100
|
+
() => undefined,
|
|
101
|
+
),
|
|
102
|
+
),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function isLocalhostUrl(value: string | undefined): boolean {
|
|
107
|
+
if (!value || !value.trim()) return false;
|
|
108
|
+
const raw = value.trim();
|
|
109
|
+
const withProtocol = /^[a-z][a-z0-9+.-]*:\/\//i.test(raw)
|
|
110
|
+
? raw
|
|
111
|
+
: `https://${raw}`;
|
|
112
|
+
try {
|
|
113
|
+
const { hostname } = new URL(withProtocol);
|
|
114
|
+
const h = hostname.toLowerCase();
|
|
115
|
+
return (
|
|
116
|
+
h === "localhost" ||
|
|
117
|
+
h === "127.0.0.1" ||
|
|
118
|
+
h === "::1" ||
|
|
119
|
+
h === "[::1]" ||
|
|
120
|
+
h.endsWith(".localhost") ||
|
|
121
|
+
h.endsWith(".local")
|
|
122
|
+
);
|
|
123
|
+
} catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function shouldSkipAgentNativeAnalyticsForLocalhost(): boolean {
|
|
129
|
+
if (process.env.AGENT_NATIVE_ANALYTICS_ALLOW_LOCALHOST === "true") {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
if (process.env.NODE_ENV === "development") return true;
|
|
133
|
+
return [
|
|
134
|
+
process.env.APP_URL,
|
|
135
|
+
process.env.BETTER_AUTH_URL,
|
|
136
|
+
process.env.URL,
|
|
137
|
+
process.env.DEPLOY_URL,
|
|
138
|
+
process.env.VERCEL_PROJECT_PRODUCTION_URL,
|
|
139
|
+
process.env.VERCEL_URL,
|
|
140
|
+
].some(isLocalhostUrl);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function isServerlessRuntime(): boolean {
|
|
144
|
+
return Boolean(
|
|
145
|
+
process.env.NETLIFY ||
|
|
146
|
+
process.env.VERCEL ||
|
|
147
|
+
process.env.AWS_LAMBDA_FUNCTION_NAME ||
|
|
148
|
+
process.env.AWS_EXECUTION_ENV ||
|
|
149
|
+
process.env.LAMBDA_TASK_ROOT ||
|
|
150
|
+
process.env.FUNCTION_NAME,
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function agentNativeAnalyticsFlushesImmediately(): boolean {
|
|
155
|
+
const mode =
|
|
156
|
+
process.env.AGENT_NATIVE_ANALYTICS_FLUSH_MODE?.trim().toLowerCase();
|
|
157
|
+
if (mode === "batch") return false;
|
|
158
|
+
if (mode === "immediate") return true;
|
|
159
|
+
return isServerlessRuntime();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ─── PostHog ───────────────────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
function isPostHogAiObservabilityEvent(eventName: string): boolean {
|
|
165
|
+
return eventName.startsWith("$ai_");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function createPostHogProvider(apiKey: string, host: string): TrackingProvider {
|
|
169
|
+
return {
|
|
170
|
+
name: "posthog",
|
|
171
|
+
track(event: TrackingEvent) {
|
|
172
|
+
const distinctId = event.userId || "anonymous";
|
|
173
|
+
if (isPostHogAiObservabilityEvent(event.name)) {
|
|
174
|
+
enqueue(
|
|
175
|
+
`${host}/i/v0/e/`,
|
|
176
|
+
JSON.stringify({
|
|
177
|
+
api_key: apiKey,
|
|
178
|
+
event: event.name,
|
|
179
|
+
properties: {
|
|
180
|
+
distinct_id: distinctId,
|
|
181
|
+
...event.properties,
|
|
182
|
+
timestamp: event.timestamp,
|
|
183
|
+
},
|
|
184
|
+
}),
|
|
185
|
+
);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
enqueue(
|
|
190
|
+
`${host}/capture/`,
|
|
191
|
+
JSON.stringify({
|
|
192
|
+
api_key: apiKey,
|
|
193
|
+
event: event.name,
|
|
194
|
+
distinct_id: distinctId,
|
|
195
|
+
properties: {
|
|
196
|
+
...event.properties,
|
|
197
|
+
timestamp: event.timestamp,
|
|
198
|
+
},
|
|
199
|
+
}),
|
|
200
|
+
);
|
|
201
|
+
},
|
|
202
|
+
identify(userId, traits) {
|
|
203
|
+
enqueue(
|
|
204
|
+
`${host}/capture/`,
|
|
205
|
+
JSON.stringify({
|
|
206
|
+
api_key: apiKey,
|
|
207
|
+
event: "$identify",
|
|
208
|
+
distinct_id: userId,
|
|
209
|
+
properties: { $set: traits },
|
|
210
|
+
}),
|
|
211
|
+
);
|
|
212
|
+
},
|
|
213
|
+
flush: () => {
|
|
214
|
+
return drainQueue().then(() => undefined);
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ─── Mixpanel ──────────────────────────────────────────────────────────────
|
|
220
|
+
|
|
221
|
+
function createMixpanelProvider(token: string): TrackingProvider {
|
|
222
|
+
return {
|
|
223
|
+
name: "mixpanel",
|
|
224
|
+
track(event: TrackingEvent) {
|
|
225
|
+
const data = {
|
|
226
|
+
event: event.name,
|
|
227
|
+
properties: {
|
|
228
|
+
token,
|
|
229
|
+
distinct_id: event.userId || "anonymous",
|
|
230
|
+
time: event.timestamp
|
|
231
|
+
? new Date(event.timestamp).getTime() / 1000
|
|
232
|
+
: undefined,
|
|
233
|
+
...event.properties,
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
enqueue("https://api.mixpanel.com/track", JSON.stringify([data]));
|
|
237
|
+
},
|
|
238
|
+
identify(userId, traits) {
|
|
239
|
+
const data = {
|
|
240
|
+
$token: token,
|
|
241
|
+
$distinct_id: userId,
|
|
242
|
+
$set: traits,
|
|
243
|
+
};
|
|
244
|
+
enqueue("https://api.mixpanel.com/engage", JSON.stringify([data]));
|
|
245
|
+
},
|
|
246
|
+
flush: () => {
|
|
247
|
+
return drainQueue().then(() => undefined);
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ─── Amplitude ─────────────────────────────────────────────────────────────
|
|
253
|
+
|
|
254
|
+
function createAmplitudeProvider(apiKey: string): TrackingProvider {
|
|
255
|
+
return {
|
|
256
|
+
name: "amplitude",
|
|
257
|
+
track(event: TrackingEvent) {
|
|
258
|
+
const data = {
|
|
259
|
+
api_key: apiKey,
|
|
260
|
+
events: [
|
|
261
|
+
{
|
|
262
|
+
event_type: event.name,
|
|
263
|
+
user_id: event.userId || "anonymous",
|
|
264
|
+
event_properties: event.properties,
|
|
265
|
+
time: event.timestamp
|
|
266
|
+
? new Date(event.timestamp).getTime()
|
|
267
|
+
: undefined,
|
|
268
|
+
},
|
|
269
|
+
],
|
|
270
|
+
};
|
|
271
|
+
enqueue("https://api2.amplitude.com/2/httpapi", JSON.stringify(data));
|
|
272
|
+
},
|
|
273
|
+
identify(userId, traits) {
|
|
274
|
+
const data = {
|
|
275
|
+
api_key: apiKey,
|
|
276
|
+
events: [
|
|
277
|
+
{
|
|
278
|
+
event_type: "$identify",
|
|
279
|
+
user_id: userId,
|
|
280
|
+
user_properties: { $set: traits },
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
};
|
|
284
|
+
enqueue("https://api2.amplitude.com/2/httpapi", JSON.stringify(data));
|
|
285
|
+
},
|
|
286
|
+
flush: () => {
|
|
287
|
+
return drainQueue().then(() => undefined);
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// ─── Webhook (custom HTTP endpoint) ───────────────────────────────────────
|
|
293
|
+
|
|
294
|
+
function createWebhookProvider(
|
|
295
|
+
url: string,
|
|
296
|
+
authHeader?: string,
|
|
297
|
+
): TrackingProvider {
|
|
298
|
+
const extra = authHeader ? { Authorization: authHeader } : undefined;
|
|
299
|
+
return {
|
|
300
|
+
name: "webhook",
|
|
301
|
+
track(event: TrackingEvent) {
|
|
302
|
+
enqueue(
|
|
303
|
+
url,
|
|
304
|
+
JSON.stringify({
|
|
305
|
+
event: event.name,
|
|
306
|
+
properties: event.properties,
|
|
307
|
+
userId: event.userId,
|
|
308
|
+
timestamp: event.timestamp,
|
|
309
|
+
}),
|
|
310
|
+
extra,
|
|
311
|
+
);
|
|
312
|
+
},
|
|
313
|
+
identify(userId, traits) {
|
|
314
|
+
enqueue(
|
|
315
|
+
url,
|
|
316
|
+
JSON.stringify({
|
|
317
|
+
event: "$identify",
|
|
318
|
+
userId,
|
|
319
|
+
traits,
|
|
320
|
+
timestamp: new Date().toISOString(),
|
|
321
|
+
}),
|
|
322
|
+
extra,
|
|
323
|
+
);
|
|
324
|
+
},
|
|
325
|
+
flush: () => {
|
|
326
|
+
return drainQueue().then(() => undefined);
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// ─── Agent Native Analytics ───────────────────────────────────────────────
|
|
332
|
+
|
|
333
|
+
function createAgentNativeAnalyticsProvider(
|
|
334
|
+
publicKey: string,
|
|
335
|
+
endpoint: string,
|
|
336
|
+
): TrackingProvider {
|
|
337
|
+
const flushImmediately = agentNativeAnalyticsFlushesImmediately();
|
|
338
|
+
return {
|
|
339
|
+
name: "agent-native-analytics",
|
|
340
|
+
track(event: TrackingEvent) {
|
|
341
|
+
enqueue(
|
|
342
|
+
endpoint,
|
|
343
|
+
JSON.stringify({
|
|
344
|
+
publicKey,
|
|
345
|
+
event: event.name,
|
|
346
|
+
properties: event.properties ?? {},
|
|
347
|
+
userId: event.userId,
|
|
348
|
+
timestamp: event.timestamp,
|
|
349
|
+
}),
|
|
350
|
+
undefined,
|
|
351
|
+
{ flushImmediately },
|
|
352
|
+
);
|
|
353
|
+
},
|
|
354
|
+
identify(userId, traits) {
|
|
355
|
+
enqueue(
|
|
356
|
+
endpoint,
|
|
357
|
+
JSON.stringify({
|
|
358
|
+
publicKey,
|
|
359
|
+
event: "$identify",
|
|
360
|
+
userId,
|
|
361
|
+
properties: traits ?? {},
|
|
362
|
+
timestamp: new Date().toISOString(),
|
|
363
|
+
}),
|
|
364
|
+
undefined,
|
|
365
|
+
{ flushImmediately },
|
|
366
|
+
);
|
|
367
|
+
},
|
|
368
|
+
flush: () => {
|
|
369
|
+
return drainQueue().then(() => undefined);
|
|
370
|
+
},
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ─── Auto-registration ────────────────────────────────────────────────────
|
|
375
|
+
|
|
376
|
+
let _registered = false;
|
|
377
|
+
|
|
378
|
+
export function registerBuiltinProviders(): void {
|
|
379
|
+
if (_registered) return;
|
|
380
|
+
_registered = true;
|
|
381
|
+
|
|
382
|
+
const posthogKey = process.env.POSTHOG_API_KEY;
|
|
383
|
+
if (posthogKey) {
|
|
384
|
+
const host = (process.env.POSTHOG_HOST || POSTHOG_DEFAULT_HOST).replace(
|
|
385
|
+
/\/+$/,
|
|
386
|
+
"",
|
|
387
|
+
);
|
|
388
|
+
registerTrackingProvider(createPostHogProvider(posthogKey, host));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const mixpanelToken = process.env.MIXPANEL_TOKEN;
|
|
392
|
+
if (mixpanelToken) {
|
|
393
|
+
registerTrackingProvider(createMixpanelProvider(mixpanelToken));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const amplitudeKey = process.env.AMPLITUDE_API_KEY;
|
|
397
|
+
if (amplitudeKey) {
|
|
398
|
+
registerTrackingProvider(createAmplitudeProvider(amplitudeKey));
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const agentNativeAnalyticsKey =
|
|
402
|
+
process.env.AGENT_NATIVE_ANALYTICS_PUBLIC_KEY ||
|
|
403
|
+
process.env.VITE_AGENT_NATIVE_ANALYTICS_PUBLIC_KEY;
|
|
404
|
+
if (
|
|
405
|
+
agentNativeAnalyticsKey &&
|
|
406
|
+
!shouldSkipAgentNativeAnalyticsForLocalhost()
|
|
407
|
+
) {
|
|
408
|
+
registerTrackingProvider(
|
|
409
|
+
createAgentNativeAnalyticsProvider(
|
|
410
|
+
agentNativeAnalyticsKey,
|
|
411
|
+
(
|
|
412
|
+
process.env.AGENT_NATIVE_ANALYTICS_ENDPOINT ||
|
|
413
|
+
AGENT_NATIVE_ANALYTICS_DEFAULT_ENDPOINT
|
|
414
|
+
).replace(/\/+$/, ""),
|
|
415
|
+
),
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const webhookUrl = process.env.TRACKING_WEBHOOK_URL;
|
|
420
|
+
if (webhookUrl) {
|
|
421
|
+
registerTrackingProvider(
|
|
422
|
+
createWebhookProvider(webhookUrl, process.env.TRACKING_WEBHOOK_AUTH),
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
}
|