@posthog/next 0.1.0 → 0.4.44

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.
Files changed (83) hide show
  1. package/dist/app/PostHogProvider.js +7 -7
  2. package/dist/app/PostHogProvider.js.map +1 -1
  3. package/dist/client/ClientPostHogProvider.js +2 -2
  4. package/dist/client/ClientPostHogProvider.js.map +1 -1
  5. package/dist/client/PostHogPageView.js +2 -2
  6. package/dist/client/PostHogPageView.js.map +1 -1
  7. package/dist/client/hooks.d.ts +1 -1
  8. package/dist/client/hooks.d.ts.map +1 -1
  9. package/dist/client/hooks.js +1 -1
  10. package/dist/client/hooks.js.map +1 -1
  11. package/dist/index.client.d.ts +5 -5
  12. package/dist/index.client.d.ts.map +1 -1
  13. package/dist/index.client.js +3 -3
  14. package/dist/index.client.js.map +1 -1
  15. package/dist/index.d.ts +8 -8
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.edge.d.ts +6 -6
  18. package/dist/index.edge.d.ts.map +1 -1
  19. package/dist/index.edge.js +4 -4
  20. package/dist/index.edge.js.map +1 -1
  21. package/dist/index.js +6 -6
  22. package/dist/index.js.map +1 -1
  23. package/dist/index.react-server.d.ts +4 -4
  24. package/dist/index.react-server.d.ts.map +1 -1
  25. package/dist/index.react-server.js +3 -3
  26. package/dist/index.react-server.js.map +1 -1
  27. package/dist/middleware/postHogMiddleware.d.ts +12 -5
  28. package/dist/middleware/postHogMiddleware.d.ts.map +1 -1
  29. package/dist/middleware/postHogMiddleware.js +8 -7
  30. package/dist/middleware/postHogMiddleware.js.map +1 -1
  31. package/dist/pages/PostHogPageView.js +2 -2
  32. package/dist/pages/PostHogPageView.js.map +1 -1
  33. package/dist/pages/PostHogProvider.js +3 -3
  34. package/dist/pages/PostHogProvider.js.map +1 -1
  35. package/dist/pages/getServerSidePostHog.d.ts.map +1 -1
  36. package/dist/pages/getServerSidePostHog.js +8 -7
  37. package/dist/pages/getServerSidePostHog.js.map +1 -1
  38. package/dist/pages.client.d.ts +6 -0
  39. package/dist/pages.client.d.ts.map +1 -0
  40. package/dist/pages.client.js +8 -0
  41. package/dist/pages.client.js.map +1 -0
  42. package/dist/pages.d.ts +8 -8
  43. package/dist/pages.d.ts.map +1 -1
  44. package/dist/pages.edge.d.ts +7 -0
  45. package/dist/pages.edge.d.ts.map +1 -0
  46. package/dist/pages.edge.js +9 -0
  47. package/dist/pages.edge.js.map +1 -0
  48. package/dist/pages.js +6 -6
  49. package/dist/pages.js.map +1 -1
  50. package/dist/server/getPostHog.d.ts +1 -1
  51. package/dist/server/getPostHog.d.ts.map +1 -1
  52. package/dist/server/getPostHog.js +11 -9
  53. package/dist/server/getPostHog.js.map +1 -1
  54. package/dist/shared/config.d.ts +3 -0
  55. package/dist/shared/config.d.ts.map +1 -1
  56. package/dist/shared/config.js +12 -1
  57. package/dist/shared/config.js.map +1 -1
  58. package/dist/shared/cookie.js +1 -1
  59. package/dist/shared/cookie.js.map +1 -1
  60. package/dist/shared/tracing-headers.d.ts +38 -0
  61. package/dist/shared/tracing-headers.d.ts.map +1 -0
  62. package/dist/shared/tracing-headers.js +52 -0
  63. package/dist/shared/tracing-headers.js.map +1 -0
  64. package/package.json +13 -6
  65. package/src/app/PostHogProvider.tsx +8 -8
  66. package/src/client/ClientPostHogProvider.tsx +2 -2
  67. package/src/client/PostHogPageView.tsx +2 -2
  68. package/src/client/hooks.ts +1 -1
  69. package/src/index.client.ts +5 -5
  70. package/src/index.edge.ts +6 -6
  71. package/src/index.react-server.ts +4 -4
  72. package/src/index.ts +8 -8
  73. package/src/middleware/postHogMiddleware.ts +19 -11
  74. package/src/pages/PostHogPageView.tsx +2 -2
  75. package/src/pages/PostHogProvider.tsx +3 -3
  76. package/src/pages/getServerSidePostHog.ts +8 -7
  77. package/src/pages.client.ts +9 -0
  78. package/src/pages.edge.ts +10 -0
  79. package/src/pages.ts +8 -8
  80. package/src/server/getPostHog.ts +11 -9
  81. package/src/shared/config.ts +15 -1
  82. package/src/shared/cookie.ts +1 -1
  83. package/src/shared/tracing-headers.ts +67 -0
@@ -2,10 +2,11 @@ import 'server-only'
2
2
 
3
3
  import { isFunction } from '@posthog/core'
4
4
  import type { PostHogOptions, IPostHog } from 'posthog-node'
5
- import { cookies } from 'next/headers'
6
- import { getOrCreateNodeClient } from './nodeClientCache'
7
- import { readPostHogCookie, cookieStateToProperties, isOptedOut } from '../shared/cookie'
8
- import { resolveApiKey } from '../shared/config'
5
+ import { cookies, headers } from 'next/headers.js'
6
+ import { getOrCreateNodeClient } from './nodeClientCache.js'
7
+ import { readPostHogCookie, isOptedOut } from '../shared/cookie.js'
8
+ import { resolveApiKey, resolveHostOrDefault } from '../shared/config.js'
9
+ import { readTracingHeaders, buildContextData } from '../shared/tracing-headers.js'
9
10
 
10
11
  /**
11
12
  * Returns a PostHog server client scoped to the current request.
@@ -14,7 +15,7 @@ import { resolveApiKey } from '../shared/config'
14
15
  * request-scoped client. Methods like `getAllFlags()`, `getFeatureFlagResult()`,
15
16
  * and `capture()` automatically use the current user's identity.
16
17
  *
17
- * Calls `cookies()` internally, which opts the route into dynamic rendering.
18
+ * Calls `cookies()` and `headers()` internally, which opts the route into dynamic rendering.
18
19
  *
19
20
  * @param apiKey - PostHog project API key. If omitted, reads from `NEXT_PUBLIC_POSTHOG_KEY`.
20
21
  * @param options - Optional `posthog-node` configuration (e.g., `{ host: '...' }`).
@@ -34,8 +35,8 @@ import { resolveApiKey } from '../shared/config'
34
35
  */
35
36
  export async function getPostHog(apiKey?: string, options?: Partial<PostHogOptions>): Promise<IPostHog> {
36
37
  const resolvedApiKey = resolveApiKey(apiKey)
37
- const host = options?.host ?? process.env.NEXT_PUBLIC_POSTHOG_HOST
38
- const resolvedOptions = host ? { ...options, host } : options
38
+ const host = resolveHostOrDefault(options?.host)
39
+ const resolvedOptions = { ...options, host }
39
40
  const client = await getOrCreateNodeClient(resolvedApiKey, resolvedOptions)
40
41
  const cookieStore = await cookies()
41
42
 
@@ -44,8 +45,9 @@ export async function getPostHog(apiKey?: string, options?: Partial<PostHogOptio
44
45
  }
45
46
 
46
47
  const state = readPostHogCookie(cookieStore, resolvedApiKey)
47
- const properties = cookieStateToProperties(state)
48
- const contextData = { distinctId: state?.distinctId, sessionId: state?.sessionId, properties }
48
+ const headerStore = await headers()
49
+ const tracing = readTracingHeaders(headerStore)
50
+ const contextData = buildContextData(tracing, state)
49
51
 
50
52
  // Wrap the shared client in a Proxy that applies request-scoped context
51
53
  // to every method call. We can't use enterContext() here because
@@ -1,5 +1,6 @@
1
1
  import type { PostHogConfig } from 'posthog-js'
2
2
  import type { PostHogOptions } from 'posthog-node'
3
+ import { DEFAULT_API_HOST } from './constants.js'
3
4
 
4
5
  /**
5
6
  * Configuration for the client-side PostHog provider.
@@ -19,8 +20,13 @@ export type PostHogServerConfig = PostHogOptions
19
20
  *
20
21
  * Throws if neither is available.
21
22
  */
23
+ export function normalizeConfigValue(value?: unknown): string | undefined {
24
+ const normalizedValue = typeof value === 'string' ? value.trim() : ''
25
+ return normalizedValue || undefined
26
+ }
27
+
22
28
  export function resolveApiKey(apiKey?: string): string {
23
- const resolved = apiKey || process.env.NEXT_PUBLIC_POSTHOG_KEY
29
+ const resolved = normalizeConfigValue(apiKey) ?? normalizeConfigValue(process.env.NEXT_PUBLIC_POSTHOG_KEY)
24
30
  if (!resolved) {
25
31
  throw new Error(
26
32
  '[PostHog Next.js] apiKey is required. Either pass it explicitly or set the NEXT_PUBLIC_POSTHOG_KEY environment variable.'
@@ -29,6 +35,14 @@ export function resolveApiKey(apiKey?: string): string {
29
35
  return resolved
30
36
  }
31
37
 
38
+ export function resolveHost(host?: string): string | undefined {
39
+ return normalizeConfigValue(host) ?? normalizeConfigValue(process.env.NEXT_PUBLIC_POSTHOG_HOST)
40
+ }
41
+
42
+ export function resolveHostOrDefault(host?: string): string {
43
+ return resolveHost(host) ?? DEFAULT_API_HOST
44
+ }
45
+
32
46
  /**
33
47
  * Next.js-specific defaults for the posthog-js client.
34
48
  *
@@ -1,5 +1,5 @@
1
1
  import { uuidv7, isNoLike, isArray } from '@posthog/core'
2
- import { COOKIE_PREFIX, COOKIE_SUFFIX } from './constants'
2
+ import { COOKIE_PREFIX, COOKIE_SUFFIX } from './constants.js'
3
3
 
4
4
  /**
5
5
  * Minimal cookie-reading interface compatible with Next.js `cookies()`,
@@ -0,0 +1,67 @@
1
+ import { isFunction, isArray } from '@posthog/core'
2
+ import type { PostHogCookieState } from './cookie.js'
3
+ import { cookieStateToProperties } from './cookie.js'
4
+
5
+ /**
6
+ * Header names used by the PostHog browser SDK's tracing headers feature.
7
+ *
8
+ * When `__add_tracing_headers` is enabled in the browser SDK, these headers
9
+ * are added to outgoing fetch/XHR requests so that server-side code can
10
+ * correlate events back to the browser session.
11
+ */
12
+ export const POSTHOG_SESSION_ID_HEADER = 'x-posthog-session-id'
13
+ export const POSTHOG_DISTINCT_ID_HEADER = 'x-posthog-distinct-id'
14
+ export const POSTHOG_WINDOW_ID_HEADER = 'x-posthog-window-id'
15
+
16
+ export interface TracingHeaderValues {
17
+ distinctId?: string
18
+ sessionId?: string
19
+ windowId?: string
20
+ }
21
+
22
+ /**
23
+ * Extracts PostHog tracing header values from request headers.
24
+ *
25
+ * Accepts either a Headers-like object with a `.get()` method (e.g. from
26
+ * `next/headers`) or a plain record (e.g. `ctx.req.headers` in Pages Router).
27
+ */
28
+ export function readTracingHeaders(
29
+ headers: { get(name: string): string | null } | Record<string, string | string[] | undefined>
30
+ ): TracingHeaderValues {
31
+ const getValue = (name: string): string | undefined => {
32
+ if (isFunction((headers as { get: unknown }).get)) {
33
+ return (headers as { get(name: string): string | null }).get(name) ?? undefined
34
+ }
35
+ const value = (headers as Record<string, string | string[] | undefined>)[name]
36
+ return typeof value === 'string' ? value : isArray(value) ? value[0] : undefined
37
+ }
38
+
39
+ return {
40
+ distinctId: getValue(POSTHOG_DISTINCT_ID_HEADER) || undefined,
41
+ sessionId: getValue(POSTHOG_SESSION_ID_HEADER) || undefined,
42
+ windowId: getValue(POSTHOG_WINDOW_ID_HEADER) || undefined,
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Builds context data by merging cookie state with tracing headers.
48
+ *
49
+ * Tracing headers take precedence over cookie values for `distinctId` and
50
+ * `sessionId` because they represent the browser's current state and are
51
+ * set per-request by the browser SDK.
52
+ */
53
+ export function buildContextData(
54
+ tracing: TracingHeaderValues,
55
+ state: PostHogCookieState | null
56
+ ): { distinctId: string | undefined; sessionId: string | undefined; properties: Record<string, string> | undefined } {
57
+ const mergedProperties: Record<string, string> = {
58
+ ...cookieStateToProperties(state),
59
+ ...(tracing.sessionId ? { $session_id: tracing.sessionId } : {}),
60
+ ...(tracing.windowId ? { $window_id: tracing.windowId } : {}),
61
+ }
62
+ return {
63
+ distinctId: tracing.distinctId || state?.distinctId,
64
+ sessionId: tracing.sessionId || state?.sessionId,
65
+ properties: Object.keys(mergedProperties).length > 0 ? mergedProperties : undefined,
66
+ }
67
+ }