ChemPal Documentation - v1.7.0
    Preparing search index...

    Function trackEvent

    • Sends one GA4 event to the /g/collect endpoint. No-op (and no network call) until a measurement id is configured in config.json (analytics). Text params go through as ep.<key> (truncated to GA4's limit), numeric params as epn.<key>. Never throws.

      Parameters

      • name: string

        GA4 event name (e.g. "render_error").

      • params: Record<string, string | number> = {}

        Non-PII event parameters.

      Returns Promise<void>

      A promise that resolves once the send settles.

      await trackEvent("render_error", { app_version: "1.6.1" });
      
      export async function trackEvent(
      name: string,
      params: Record<string, string | number> = {},
      ): Promise<void> {
      const { measurementId } = analyticsConfig;
      if (!measurementId) return;
      if (!(await analyticsEnabled())) return;

      try {
      const query = new URLSearchParams({
      v: '2', // GA4 protocol version
      tid: measurementId, // Measurement ID
      cid: await getClientId(), // stable client id
      en: name, // event name
      _et: '1', // engagement time (ms)
      });
      for (const [key, value] of Object.entries(params)) {
      if (typeof value === 'number') query.set(`epn.${key}`, String(value));
      else query.set(`ep.${key}`, value.slice(0, PARAM_VALUE_LIMIT));
      }
      // no-cors: an extension page may POST cross-origin without a host permission;
      // keepalive lets it complete even if the page is tearing down after a crash.
      await fetch(`${GA_ENDPOINT}?${query.toString()}`, {
      method: 'POST',
      mode: 'no-cors',
      keepalive: true,
      });
      } catch {
      // Best-effort telemetry: swallow all failures.
      }
      }