|
1 | 1 | import pkg from "node-machine-id"; |
2 | 2 | const { machineIdSync } = pkg; |
| 3 | +import https from "https"; |
3 | 4 |
|
4 | | -export async function createPosthogClient() { |
5 | | - const { PostHog } = await import("posthog-node"); |
6 | | - const posthog = new PostHog( |
7 | | - "phc_eR0iSoQufBxNY36k0f0T15UvHJdTfHlh8rJcxsfhfXk", |
8 | | - { |
9 | | - host: "https://eu.i.posthog.com", |
10 | | - flushAt: 1, |
11 | | - flushInterval: 0, |
12 | | - }, |
13 | | - ); |
14 | | - |
15 | | - return posthog; |
16 | | -} |
| 5 | +const POSTHOG_API_KEY = "phc_eR0iSoQufBxNY36k0f0T15UvHJdTfHlh8rJcxsfhfXk"; |
| 6 | +const POSTHOG_HOST = "eu.i.posthog.com"; |
| 7 | +const POSTHOG_PATH = "/i/v0/e/"; // Correct PostHog capture endpoint |
| 8 | +const REQUEST_TIMEOUT_MS = 1000; |
17 | 9 |
|
18 | | -export default async function trackEvent( |
| 10 | +/** |
| 11 | + * Sends an analytics event to PostHog using direct HTTPS API. |
| 12 | + * This is a fire-and-forget implementation that won't block the process. |
| 13 | + * |
| 14 | + * @param distinctId - Unique identifier for the user/device |
| 15 | + * @param event - Name of the event to track |
| 16 | + * @param properties - Additional properties to attach to the event |
| 17 | + */ |
| 18 | +export default function trackEvent( |
19 | 19 | distinctId: string | null | undefined, |
20 | 20 | event: string, |
21 | 21 | properties?: Record<string, any>, |
22 | | -) { |
23 | | - if (process.env.DO_NOT_TRACK) { |
| 22 | +): void { |
| 23 | + // Skip tracking if explicitly disabled or in CI environment |
| 24 | + if (process.env.DO_NOT_TRACK === "1") { |
24 | 25 | return; |
25 | 26 | } |
26 | 27 |
|
27 | | - try { |
28 | | - const actualId = distinctId || `device-${machineIdSync()}`; |
| 28 | + // Defer execution to next tick to avoid blocking |
| 29 | + setImmediate(() => { |
| 30 | + try { |
| 31 | + const actualId = distinctId || `device-${machineIdSync()}`; |
| 32 | + |
| 33 | + // PostHog expects distinct_id at the root level, not nested in properties |
| 34 | + const eventData = { |
| 35 | + api_key: POSTHOG_API_KEY, |
| 36 | + event, |
| 37 | + distinct_id: actualId, |
| 38 | + properties: { |
| 39 | + ...properties, |
| 40 | + $lib: "lingo.dev-cli", |
| 41 | + $lib_version: process.env.npm_package_version || "unknown", |
| 42 | + }, |
| 43 | + timestamp: new Date().toISOString(), |
| 44 | + }; |
29 | 45 |
|
30 | | - const posthog = await createPosthogClient(); |
| 46 | + const payload = JSON.stringify(eventData); |
31 | 47 |
|
32 | | - await posthog.capture({ |
33 | | - distinctId: actualId, |
34 | | - event, |
35 | | - properties: { |
36 | | - ...properties, |
37 | | - meta: { |
38 | | - version: process.env.npm_package_version, |
39 | | - isCi: process.env.CI === "true", |
| 48 | + const options: https.RequestOptions = { |
| 49 | + hostname: POSTHOG_HOST, |
| 50 | + path: POSTHOG_PATH, |
| 51 | + method: "POST", |
| 52 | + headers: { |
| 53 | + "Content-Type": "application/json", |
| 54 | + "Content-Length": Buffer.byteLength(payload).toString(), |
40 | 55 | }, |
41 | | - }, |
42 | | - }); |
43 | | - } catch (error) { |
44 | | - if (process.env.DEBUG) { |
45 | | - console.error(error); |
| 56 | + timeout: REQUEST_TIMEOUT_MS, |
| 57 | + }; |
| 58 | + |
| 59 | + const req = https.request(options); |
| 60 | + |
| 61 | + // Handle timeout by destroying the request |
| 62 | + req.on("timeout", () => { |
| 63 | + req.destroy(); |
| 64 | + }); |
| 65 | + |
| 66 | + // Silently ignore errors to prevent crashes |
| 67 | + req.on("error", (error) => { |
| 68 | + if (process.env.DEBUG === "true") { |
| 69 | + console.error("[Tracking] Error ignored:", error.message); |
| 70 | + } |
| 71 | + }); |
| 72 | + |
| 73 | + // Send payload and close the request |
| 74 | + req.write(payload); |
| 75 | + req.end(); |
| 76 | + |
| 77 | + // Ensure cleanup after timeout |
| 78 | + setTimeout(() => { |
| 79 | + if (!req.destroyed) { |
| 80 | + req.destroy(); |
| 81 | + } |
| 82 | + }, REQUEST_TIMEOUT_MS); |
| 83 | + } catch (error) { |
| 84 | + // Catch-all for any synchronous errors |
| 85 | + if (process.env.DEBUG === "true") { |
| 86 | + console.error("[Tracking] Failed to send event:", error); |
| 87 | + } |
46 | 88 | } |
47 | | - } |
| 89 | + }); |
48 | 90 | } |
0 commit comments