{"version":3,"sources":["../../src/utils.ts","../../src/server/index.ts"],"sourcesContent":["import type { AllowedPropertyValues, AnalyticsProps, Mode } from './types';\n\nexport function isBrowser(): boolean {\n  return typeof window !== 'undefined';\n}\n\nfunction detectEnvironment(): 'development' | 'production' {\n  try {\n    const env = process.env.NODE_ENV;\n    if (env === 'development' || env === 'test') {\n      return 'development';\n    }\n  } catch (e) {\n    // do nothing, this is okay\n  }\n  return 'production';\n}\n\nexport function setMode(mode: Mode = 'auto'): void {\n  if (mode === 'auto') {\n    window.vam = detectEnvironment();\n    return;\n  }\n\n  window.vam = mode;\n}\n\nexport function getMode(): Mode {\n  const mode = isBrowser() ? window.vam : detectEnvironment();\n  return mode || 'production';\n}\n\nexport function isProduction(): boolean {\n  return getMode() === 'production';\n}\n\nexport function isDevelopment(): boolean {\n  return getMode() === 'development';\n}\n\nfunction removeKey(\n  key: string,\n  { [key]: _, ...rest }\n): Record<string, unknown> {\n  return rest;\n}\n\nexport function parseProperties(\n  properties: Record<string, unknown> | undefined,\n  options: {\n    strip?: boolean;\n  }\n): Error | Record<string, AllowedPropertyValues> | undefined {\n  if (!properties) return undefined;\n  let props = properties;\n  const errorProperties: string[] = [];\n  for (const [key, value] of Object.entries(properties)) {\n    if (typeof value === 'object' && value !== null) {\n      if (options.strip) {\n        props = removeKey(key, props);\n      } else {\n        errorProperties.push(key);\n      }\n    }\n  }\n\n  if (errorProperties.length > 0 && !options.strip) {\n    throw Error(\n      `The following properties are not valid: ${errorProperties.join(\n        ', '\n      )}. Only strings, numbers, booleans, and null are allowed.`\n    );\n  }\n  return props as Record<string, AllowedPropertyValues>;\n}\n\nexport function computeRoute(\n  pathname: string | null,\n  pathParams: Record<string, string | string[]> | null\n): string | null {\n  if (!pathname || !pathParams) {\n    return pathname;\n  }\n\n  let result = pathname;\n  try {\n    const entries = Object.entries(pathParams);\n    // simple keys must be handled first\n    for (const [key, value] of entries) {\n      if (!Array.isArray(value)) {\n        const matcher = turnValueToRegExp(value);\n        if (matcher.test(result)) {\n          result = result.replace(matcher, `/[${key}]`);\n        }\n      }\n    }\n    // array values next\n    for (const [key, value] of entries) {\n      if (Array.isArray(value)) {\n        const matcher = turnValueToRegExp(value.join('/'));\n        if (matcher.test(result)) {\n          result = result.replace(matcher, `/[...${key}]`);\n        }\n      }\n    }\n    return result;\n  } catch (e) {\n    return pathname;\n  }\n}\n\nfunction turnValueToRegExp(value: string): RegExp {\n  return new RegExp(`/${escapeRegExp(value)}(?=[/?#]|$)`);\n}\n\nfunction escapeRegExp(string: string): string {\n  return string.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nexport function getScriptSrc(\n  props: AnalyticsProps & { basePath?: string }\n): string {\n  if (props.scriptSrc) {\n    return props.scriptSrc;\n  }\n  if (isDevelopment()) {\n    return 'https://va.vercel-scripts.com/v1/script.debug.js';\n  }\n  if (props.basePath) {\n    return `${props.basePath}/insights/script.js`;\n  }\n  return '/_vercel/insights/script.js';\n}\n","/* eslint-disable no-console -- Allow logging on the server */\nimport type {\n  AllowedPropertyValues,\n  FlagsDataInput,\n  PlainFlags,\n} from '../types';\nimport { isProduction, parseProperties } from '../utils';\n\ntype HeadersObject = Record<string, string | string[] | undefined>;\ntype AllowedHeaders = Headers | HeadersObject;\n\nfunction isHeaders(headers?: AllowedHeaders): headers is Headers {\n  if (!headers) return false;\n  return typeof (headers as HeadersObject).entries === 'function';\n}\n\ninterface Options {\n  flags?: FlagsDataInput;\n  headers?: AllowedHeaders;\n  request?: { headers: AllowedHeaders };\n}\n\ninterface RequestContext {\n  get: () => {\n    headers: Record<string, string | undefined>;\n    url: string;\n    waitUntil?: (promise: Promise<unknown>) => void;\n    flags?: {\n      getValues: () => PlainFlags;\n      reportValue: (key: string, value: unknown) => void;\n    };\n  };\n}\n\nconst symbol = Symbol.for('@vercel/request-context');\nconst logPrefix = '[Vercel Web Analytics]';\n\nexport async function track(\n  eventName: string,\n  properties?: Record<string, AllowedPropertyValues>,\n  options?: Options\n): Promise<void> {\n  const ENDPOINT =\n    process.env.VERCEL_WEB_ANALYTICS_ENDPOINT || process.env.VERCEL_URL;\n  const DISABLE_LOGS = Boolean(process.env.VERCEL_WEB_ANALYTICS_DISABLE_LOGS);\n  const BYPASS_SECRET = process.env.VERCEL_AUTOMATION_BYPASS_SECRET;\n\n  if (typeof window !== 'undefined') {\n    if (!isProduction()) {\n      throw new Error(\n        `${logPrefix} It seems like you imported the \\`track\\` function from \\`@vercel/web-analytics/server\\` in a browser environment. This function is only meant to be used in a server environment.`\n      );\n    }\n\n    return;\n  }\n\n  const props = parseProperties(properties, {\n    strip: isProduction(),\n  });\n\n  if (!ENDPOINT) {\n    if (isProduction()) {\n      console.log(\n        `${logPrefix} Can't find VERCEL_URL in environment variables.`\n      );\n    } else if (!DISABLE_LOGS) {\n      console.log(\n        `${logPrefix} Track \"${eventName}\" ${\n          props ? `with data ${JSON.stringify(props)}` : ''\n        }`\n      );\n    }\n    return;\n  }\n  try {\n    const requestContext = (\n      (globalThis as never)[symbol] as RequestContext | undefined\n    )?.get();\n\n    let headers: AllowedHeaders | undefined;\n\n    if (options && 'headers' in options) {\n      headers = options.headers;\n    } else if (options?.request) {\n      headers = options.request.headers;\n    } else if (requestContext?.headers) {\n      // not explicitly passed in context, so take it from async storage\n      headers = requestContext.headers;\n    }\n\n    let tmp: HeadersObject = {};\n    if (headers && isHeaders(headers)) {\n      headers.forEach((value, key) => {\n        tmp[key] = value;\n      });\n    } else if (headers) {\n      tmp = headers;\n    }\n\n    const origin =\n      requestContext?.url || (tmp.referer as string) || `https://${ENDPOINT}`;\n\n    const url = new URL(origin);\n\n    const body = {\n      o: origin,\n      ts: new Date().getTime(),\n      r: '',\n      en: eventName,\n      ed: props,\n      f: safeGetFlags(options?.flags, requestContext),\n    };\n\n    const hasHeaders = Boolean(headers);\n\n    if (!hasHeaders) {\n      throw new Error(\n        'No session context found. Pass `request` or `headers` to the `track` function.'\n      );\n    }\n\n    const promise = fetch(`${url.origin}/_vercel/insights/event`, {\n      headers: {\n        'content-type': 'application/json',\n        // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- The throwing is temporary until we add support for non Vercel hosted environments\n        ...(hasHeaders\n          ? {\n              'user-agent': tmp['user-agent'] as string,\n              'x-vercel-ip': tmp['x-forwarded-for'] as string,\n              'x-va-server': '1',\n              cookie: tmp.cookie as string,\n            }\n          : {\n              'x-va-server': '2',\n            }),\n        ...(BYPASS_SECRET\n          ? { 'x-vercel-protection-bypass': BYPASS_SECRET }\n          : {}),\n      },\n      body: JSON.stringify(body),\n      method: 'POST',\n    })\n      // We want to always consume the body; some cloud providers track fetch concurrency\n      // and may not release the connection until the body is consumed.\n      .then((response) => response.text())\n      .catch((err: unknown) => {\n        if (err instanceof Error && 'response' in err) {\n          console.error(err.response);\n        } else {\n          console.error(err);\n        }\n      });\n\n    if (requestContext?.waitUntil) {\n      requestContext.waitUntil(promise);\n    } else {\n      await promise;\n    }\n\n    return void 0;\n  } catch (err) {\n    console.error(err);\n  }\n}\n\nfunction safeGetFlags(\n  flags: Options['flags'],\n  requestContext?: ReturnType<RequestContext['get']>\n):\n  | {\n      p: PlainFlags;\n    }\n  | undefined {\n  try {\n    if (!requestContext || !flags) return;\n    // In the case plain flags are passed, just return them\n    if (!Array.isArray(flags)) {\n      return { p: flags };\n    }\n\n    const plainFlags: Record<string, unknown> = {};\n    // returns all available plain flags\n    const resolvedPlainFlags = requestContext.flags?.getValues() ?? {};\n\n    for (const flag of flags) {\n      if (typeof flag === 'string') {\n        // only picks the desired flags\n        plainFlags[flag] = resolvedPlainFlags[flag];\n      } else {\n        // merge user-provided values with resolved values\n        Object.assign(plainFlags, flag);\n      }\n    }\n\n    return { p: plainFlags };\n  } catch {\n    /* empty */\n  }\n}\n"],"mappings":"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","names":[]}