{"version":3,"file":"server.cjs","sources":["../src/server.ts"],"sourcesContent":["/**\n * @solid-analytics/solidstart - Server Functions\n *\n * Server-side analytics tracking with automatic request context enrichment\n */\n\n\"use server\";\n\nimport { getRequestEvent } from \"solid-js/web\";\nimport type { AnalyticsInstance } from \"analytics\";\n\nexport interface ServerTrackOptions {\n  /**\n   * Analytics client instance (must be isomorphic)\n   */\n  client?: AnalyticsInstance;\n\n  /**\n   * Enrich event with request context (IP, user agent, etc.)\n   * @default true\n   */\n  enrichContext?: boolean;\n\n  /**\n   * Extract session information from cookies\n   * @default true\n   */\n  includeSession?: boolean;\n\n  /**\n   * Debug logging\n   * @default false\n   */\n  debug?: boolean;\n}\n\nexport interface RequestContext {\n  ip?: string;\n  userAgent?: string;\n  referer?: string;\n  country?: string;\n  region?: string;\n  city?: string;\n  sessionId?: string;\n  userId?: string;\n  [key: string]: any;\n}\n\n/**\n * Extract request context from SolidStart request event\n */\nfunction extractRequestContext(options: ServerTrackOptions): RequestContext {\n  const event = getRequestEvent();\n  if (!event) return {};\n\n  const headers = event.request.headers;\n  const context: RequestContext = {};\n\n  // Extract IP address\n  context.ip =\n    headers.get(\"x-forwarded-for\")?.split(\",\")[0].trim() ||\n    headers.get(\"x-real-ip\") ||\n    headers.get(\"cf-connecting-ip\") || // Cloudflare\n    \"unknown\";\n\n  // Extract user agent\n  context.userAgent = headers.get(\"user-agent\") || undefined;\n\n  // Extract referer\n  context.referer = headers.get(\"referer\") || undefined;\n\n  // Extract geo information (if available from CDN/proxy)\n  context.country =\n    headers.get(\"cf-ipcountry\") ||\n    headers.get(\"x-vercel-ip-country\") ||\n    undefined;\n  context.region = headers.get(\"x-vercel-ip-country-region\") || undefined;\n  context.city = headers.get(\"x-vercel-ip-city\") || undefined;\n\n  // Extract session from cookies if enabled\n  if (options.includeSession !== false) {\n    const cookieHeader = headers.get(\"cookie\");\n    if (cookieHeader) {\n      const sessionMatch = cookieHeader.match(/session[_-]?id=([^;]+)/);\n      if (sessionMatch) {\n        context.sessionId = sessionMatch[1];\n      }\n      const userMatch = cookieHeader.match(/user[_-]?id=([^;]+)/);\n      if (userMatch) {\n        context.userId = userMatch[1];\n      }\n    }\n  }\n\n  return context;\n}\n\n/**\n * Server-side track function\n *\n * @example\n * ```tsx\n * \"use server\"\n * import { serverTrack } from '@solid-analytics/solidstart/server'\n *\n * export async function handlePurchase() {\n *   await serverTrack('purchase_completed', { amount: 99.99 })\n * }\n * ```\n */\nexport async function serverTrack(\n  eventName: string,\n  properties?: Record<string, any>,\n  options: ServerTrackOptions = {}\n): Promise<void> {\n  const { client, enrichContext = true, debug = false } = options;\n\n  if (!client) {\n    if (debug) {\n      console.warn(\"[Analytics] No client provided for server-side tracking\");\n    }\n    return;\n  }\n\n  try {\n    // Enrich with request context\n    let enrichedProperties = properties || {};\n\n    if (enrichContext) {\n      const context = extractRequestContext(options);\n      enrichedProperties = {\n        ...properties,\n        $context: context,\n        $server: true,\n        $timestamp: new Date().toISOString(),\n      };\n    }\n\n    // Track the event\n    await client.track(eventName, enrichedProperties);\n\n    if (debug) {\n      console.log(\n        `[Analytics] Server tracked: ${eventName}`,\n        enrichedProperties\n      );\n    }\n  } catch (error) {\n    console.error(\"[Analytics] Server track error:\", error);\n    // Don't throw - analytics failures shouldn't break the app\n  }\n}\n\n/**\n * Server-side identify function\n *\n * @example\n * ```tsx\n * \"use server\"\n * import { serverIdentify } from '@solid-analytics/solidstart/server'\n *\n * export async function handleLogin(userId: string) {\n *   await serverIdentify(userId, { email: 'user@example.com' })\n * }\n * ```\n */\nexport async function serverIdentify(\n  userId: string,\n  traits?: Record<string, any>,\n  options: ServerTrackOptions = {}\n): Promise<void> {\n  const { client, enrichContext = true, debug = false } = options;\n\n  if (!client) {\n    if (debug) {\n      console.warn(\"[Analytics] No client provided for server-side identify\");\n    }\n    return;\n  }\n\n  try {\n    let enrichedTraits = traits || {};\n\n    if (enrichContext) {\n      const context = extractRequestContext(options);\n      enrichedTraits = {\n        ...traits,\n        $context: context,\n        $server: true,\n        $identifiedAt: new Date().toISOString(),\n      };\n    }\n\n    await client.identify(userId, enrichedTraits);\n\n    if (debug) {\n      console.log(`[Analytics] Server identified: ${userId}`, enrichedTraits);\n    }\n  } catch (error) {\n    console.error(\"[Analytics] Server identify error:\", error);\n  }\n}\n\n/**\n * Server-side page function\n *\n * @example\n * ```tsx\n * \"use server\"\n * import { serverPage } from '@solid-analytics/solidstart/server'\n *\n * export async function trackPageView() {\n *   await serverPage('Home', { section: 'landing' })\n * }\n * ```\n */\nexport async function serverPage(\n  pageName?: string,\n  properties?: Record<string, any>,\n  options: ServerTrackOptions = {}\n): Promise<void> {\n  const { client, enrichContext = true, debug = false } = options;\n\n  if (!client) {\n    if (debug) {\n      console.warn(\"[Analytics] No client provided for server-side page\");\n    }\n    return;\n  }\n\n  try {\n    let enrichedProperties = properties || {};\n\n    if (enrichContext) {\n      const event = getRequestEvent();\n      const context = extractRequestContext(options);\n\n      enrichedProperties = {\n        ...properties,\n        $context: context,\n        $server: true,\n        $url: event?.request.url,\n        $path: new URL(event?.request.url || \"\").pathname,\n        $timestamp: new Date().toISOString(),\n      };\n    }\n\n    await client.page({ title: pageName, ...enrichedProperties });\n\n    if (debug) {\n      console.log(\n        `[Analytics] Server page: ${pageName || \"(none)\"}`,\n        enrichedProperties\n      );\n    }\n  } catch (error) {\n    console.error(\"[Analytics] Server page error:\", error);\n  }\n}\n\n/**\n * Batch server tracking for multiple events\n *\n * @example\n * ```tsx\n * \"use server\"\n * import { serverTrackBatch } from '@solid-analytics/solidstart/server'\n *\n * export async function trackUserFlow() {\n *   await serverTrackBatch([\n *     { type: 'track', event: 'page_view' },\n *     { type: 'track', event: 'button_click', properties: { button: 'cta' } },\n *   ])\n * }\n * ```\n */\nexport async function serverTrackBatch(\n  events: Array<{\n    type: \"track\" | \"identify\" | \"page\";\n    event?: string;\n    userId?: string;\n    traits?: Record<string, any>;\n    properties?: Record<string, any>;\n  }>,\n  options: ServerTrackOptions = {}\n): Promise<void> {\n  const promises = events.map(async (evt) => {\n    switch (evt.type) {\n      case \"track\":\n        if (evt.event) {\n          return serverTrack(evt.event, evt.properties, options);\n        }\n        break;\n      case \"identify\":\n        if (evt.userId) {\n          return serverIdentify(evt.userId, evt.traits, options);\n        }\n        break;\n      case \"page\":\n        return serverPage(evt.event, evt.properties, options);\n    }\n  });\n\n  await Promise.allSettled(promises);\n}\n\n/**\n * Create a server-side analytics instance with automatic context enrichment\n *\n * @example\n * ```tsx\n * \"use server\"\n * import { createServerAnalytics } from '@solid-analytics/solidstart/server'\n *\n * const analytics = createServerAnalytics(analyticsClient)\n *\n * export async function trackEvent(name: string, data: any) {\n *   await analytics.track(name, data)\n * }\n * ```\n */\nexport function createServerAnalytics(\n  client: AnalyticsInstance,\n  defaultOptions: Omit<ServerTrackOptions, \"client\"> = {}\n) {\n  return {\n    track: (eventName: string, properties?: Record<string, any>) =>\n      serverTrack(eventName, properties, { ...defaultOptions, client }),\n\n    identify: (userId: string, traits?: Record<string, any>) =>\n      serverIdentify(userId, traits, { ...defaultOptions, client }),\n\n    page: (pageName?: string, properties?: Record<string, any>) =>\n      serverPage(pageName, properties, { ...defaultOptions, client }),\n\n    batch: (events: Parameters<typeof serverTrackBatch>[0]) =>\n      serverTrackBatch(events, { ...defaultOptions, client }),\n  };\n}\n"],"names":["extractRequestContext","options","event","getRequestEvent","headers","context","cookieHeader","sessionMatch","userMatch","serverTrack","eventName","properties","client","enrichContext","debug","enrichedProperties","error","serverIdentify","userId","traits","enrichedTraits","serverPage","pageName","serverTrackBatch","events","promises","evt","createServerAnalytics","defaultOptions"],"mappings":"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"}