ras-stack 1.0.1 → 1.1.1

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.
package/README.md CHANGED
@@ -39,7 +39,7 @@ These combinations are tested here and in production. [Sealed Lists](https://git
39
39
  | Data | Drizzle, `better-sqlite3`, Postgres.js | Connection lifecycle, safety defaults, migrations, target selection, and conformance checks |
40
40
  | Realtime | Centrifuge, Centrifugo, Caddy | Publishing, tokens, browser/React lifecycle, presence, proxy configuration, binaries, and supervision |
41
41
  | Email and uploads | Nodemailer, `tus-js-client` | SMTP configuration/delivery, auth callbacks, and promise-based resumable uploads |
42
- | Observability | PostHog JS, React, and Node SDKs | Initialization, error defaults, request correlation, proxy routes, shutdown, and coverage decisions |
42
+ | Observability | PostHog JS, React, Node, and OpenTelemetry | Analytics, replay, errors, logs, metrics, tracing, correlation, proxying, shutdown, and coverage |
43
43
  | Delivery | GitHub Actions, Changesets, Dokploy, Docker | Checks, releases, preview lifecycle/status, production assets, and runtime binaries |
44
44
 
45
45
  Applications still configure every upstream library directly. This table describes what is tested together, not a replacement API.
@@ -266,6 +266,9 @@ export function assertPostHogBrowserConformance(options) {
266
266
  if (options.capture_pageview !== 'history_change') {
267
267
  throw new ConformanceError('PostHog browser initialization', 'SPA pageviews must follow history changes');
268
268
  }
269
+ if (options.capture_performance !== true) {
270
+ throw new ConformanceError('PostHog browser initialization', 'browser performance capture must be enabled');
271
+ }
269
272
  if (options.person_profiles !== 'identified_only') {
270
273
  throw new ConformanceError('PostHog browser initialization', 'person profiles must be limited to identified users');
271
274
  }
@@ -279,6 +282,16 @@ export function assertPostHogBrowserConformance(options) {
279
282
  if (!recording || typeof recording !== 'object' || !('maskAllInputs' in recording) || recording.maskAllInputs !== true) {
280
283
  throw new ConformanceError('PostHog browser initialization', 'session replay must mask all inputs by default');
281
284
  }
285
+ const logs = serviceName(options.logs);
286
+ const metrics = serviceName(options.metrics);
287
+ if ((logs || metrics) && (!logs || logs !== metrics)) {
288
+ throw new ConformanceError('PostHog browser initialization', 'logs and metrics must share one service name');
289
+ }
290
+ }
291
+ function serviceName(value) {
292
+ if (!value || typeof value !== 'object' || !('serviceName' in value) || typeof value.serviceName !== 'string')
293
+ return undefined;
294
+ return value.serviceName.trim() || undefined;
282
295
  }
283
296
  export function assertPostHogRequestConformance(parse) {
284
297
  const matched = parse(postHogRequest('person-123', 'session-456'), { authenticatedDistinctId: 'person-123' });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/conformance/index.ts"],"names":[],"mappings":"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","sourcesContent":["export class ConformanceError extends Error {\n constructor(\n readonly scenario: string,\n message: string,\n options: { cause?: unknown } = {},\n ) {\n super(`${scenario}: ${message}`, options.cause === undefined ? undefined : { cause: options.cause })\n this.name = 'ConformanceError'\n }\n}\n\nexport async function assertMutationOriginConformance(\n guard: (request: Request) => void | Promise<void>,\n options: { trustForwardedHeaders?: boolean } = {},\n) {\n await accepted('same-origin request', () =>\n guard(\n new Request('https://app.example/action', {\n method: 'POST',\n headers: { origin: 'https://app.example', 'sec-fetch-site': 'same-origin' },\n }),\n ),\n )\n await rejected('cross-origin request', () =>\n guard(\n new Request('https://app.example/action', {\n method: 'POST',\n headers: { origin: 'https://attacker.example', 'sec-fetch-site': 'cross-site' },\n }),\n ),\n )\n await rejected('missing-origin request', () => guard(new Request('https://app.example/action', { method: 'POST' })))\n if (!options.trustForwardedHeaders) {\n await rejected('spoofed forwarded-origin request', () =>\n guard(\n new Request('https://app.example/action', {\n method: 'POST',\n headers: {\n origin: 'https://attacker.example',\n 'x-forwarded-host': 'attacker.example',\n 'x-forwarded-proto': 'https',\n },\n }),\n ),\n )\n }\n}\n\nexport type HealthHandlerFactory = (check: () => void | Promise<void>) => () => Response | Promise<Response>\n\nexport async function assertHealthHandlerConformance(createHandler: HealthHandlerFactory) {\n const healthy = await createHandler(() => undefined)()\n if (healthy.status !== 200) throw new ConformanceError('healthy dependency', `expected status 200, received ${healthy.status}`)\n const healthyBody = await responseBody(healthy, 'healthy dependency')\n if (healthyBody.ok !== true) throw new ConformanceError('healthy dependency', 'response body must contain ok: true')\n\n const privateMessage = 'password=private-diagnostic'\n const unavailable = await createHandler(() => Promise.reject(new Error(privateMessage)))()\n if (unavailable.status !== 503) {\n throw new ConformanceError('unavailable dependency', `expected status 503, received ${unavailable.status}`)\n }\n const unavailableText = await unavailable.text()\n if (unavailableText.includes(privateMessage)) {\n throw new ConformanceError('unavailable dependency', 'response exposed the private diagnostic message')\n }\n let unavailableBody: unknown\n try {\n unavailableBody = JSON.parse(unavailableText)\n } catch (error) {\n throw new ConformanceError('unavailable dependency', 'response body must be JSON', { cause: error })\n }\n if (!unavailableBody || typeof unavailableBody !== 'object' || !('ok' in unavailableBody) || unavailableBody.ok !== false) {\n throw new ConformanceError('unavailable dependency', 'response body must contain ok: false')\n }\n}\n\nexport type SqlitePragmaReader = (name: 'journal_mode' | 'synchronous' | 'busy_timeout' | 'foreign_keys') => unknown\n\nexport async function assertSqliteConformance(readPragma: SqlitePragmaReader) {\n const values = {\n journalMode: String(await readPragma('journal_mode')).toLowerCase(),\n synchronous: Number(await readPragma('synchronous')),\n busyTimeout: Number(await readPragma('busy_timeout')),\n foreignKeys: Number(await readPragma('foreign_keys')),\n }\n if (values.journalMode !== 'wal' && values.journalMode !== 'memory') {\n throw new ConformanceError('SQLite journal mode', `expected wal or memory, received ${values.journalMode}`)\n }\n if (values.synchronous !== 2) {\n throw new ConformanceError('SQLite synchronous mode', `expected FULL (2), received ${values.synchronous}`)\n }\n if (values.busyTimeout !== 5000) {\n throw new ConformanceError('SQLite busy timeout', `expected 5000, received ${values.busyTimeout}`)\n }\n if (values.foreignKeys !== 1) {\n throw new ConformanceError('SQLite foreign keys', `expected enabled (1), received ${values.foreignKeys}`)\n }\n}\n\nexport type RealtimeTokenSigner = (subject: string, claims: Record<string, unknown>) => string | Promise<string>\n\nexport type RealtimeTokenConformanceOptions = { secret: string; maxTtlSeconds?: number; now?: number }\n\nexport async function assertRealtimeTokenConformance(sign: RealtimeTokenSigner, options: RealtimeTokenConformanceOptions) {\n const now = options.now ?? Math.floor(Date.now() / 1000)\n const maxTtlSeconds = options.maxTtlSeconds ?? 60 * 60\n const token = await sign('person-123', { channel: 'room:1' })\n const { header, payload, signed, signature } = decodeToken(token)\n\n if (header.alg !== 'HS256') {\n throw new ConformanceError('realtime token algorithm', `expected HS256, received ${JSON.stringify(header.alg)}`)\n }\n if (payload.sub !== 'person-123') {\n throw new ConformanceError('realtime token subject', 'token must bind the subject it was signed for')\n }\n if (payload.channel !== 'room:1') {\n throw new ConformanceError('realtime token claims', 'token must carry the claims it was signed with')\n }\n if (typeof payload.exp !== 'number') {\n throw new ConformanceError('realtime token expiry', 'token must expire')\n }\n if (payload.exp <= now) {\n throw new ConformanceError('realtime token expiry', 'token expired before it was issued')\n }\n if (payload.exp - now > maxTtlSeconds) {\n throw new ConformanceError('realtime token expiry', `token outlives the ${maxTtlSeconds} second maximum`)\n }\n if (!(await verifyHmac(signed, signature, options.secret))) {\n throw new ConformanceError('realtime token signature', 'token is not signed with the shared Centrifugo secret')\n }\n\n const other = decodeToken(await sign('person-456', { channel: 'room:1' })).payload\n if (other.sub === payload.sub) {\n throw new ConformanceError('realtime token subject', 'every subject received the same identity')\n }\n}\n\nfunction decodeToken(token: string) {\n const segments = token.split('.')\n if (segments.length !== 3) throw new ConformanceError('realtime token format', 'expected a three-segment JWT')\n const [header, claims, signature] = segments as [string, string, string]\n return {\n header: decodeSegment(header, 'header'),\n payload: decodeSegment(claims, 'payload'),\n signed: `${header}.${claims}`,\n signature,\n }\n}\n\nfunction decodeSegment(segment: string, name: string): Record<string, unknown> {\n try {\n const padded = segment\n .replaceAll('-', '+')\n .replaceAll('_', '/')\n .padEnd(Math.ceil(segment.length / 4) * 4, '=')\n const value: unknown = JSON.parse(atob(padded))\n if (value && typeof value === 'object') return value as Record<string, unknown>\n } catch (error) {\n throw new ConformanceError('realtime token format', `${name} is not base64url JSON`, { cause: error })\n }\n throw new ConformanceError('realtime token format', `${name} is not an object`)\n}\n\nasync function verifyHmac(signed: string, signature: string, secret: string) {\n const encoder = new TextEncoder()\n const key = await crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'])\n const digest = new Uint8Array(await crypto.subtle.sign('HMAC', key, encoder.encode(signed)))\n const expected = btoa(String.fromCharCode(...digest))\n .replaceAll('+', '-')\n .replaceAll('/', '_')\n .replaceAll('=', '')\n return expected === signature\n}\n\nexport type RateLimitStoreProbe = {\n increment: (\n key: string,\n windowSeconds: number,\n now: number,\n ) => Promise<{ count: number; resetAt: number }> | { count: number; resetAt: number }\n}\n\n// The package cannot reach a consumer's PostgreSQL, so the store contract travels to wherever the real database is.\nexport async function assertRateLimitStoreConformance(store: RateLimitStoreProbe, options: { now?: number } = {}) {\n const now = options.now ?? Math.floor(Date.now() / 1000)\n const window = 60\n const key = `conformance:${crypto.randomUUID()}`\n\n const first = await store.increment(key, window, now)\n if (first.count !== 1) throw new ConformanceError('rate limit store', `expected a new key to start at 1, received ${first.count}`)\n if (first.resetAt !== now + window) {\n throw new ConformanceError('rate limit store', `expected the window to end at ${now + window}, received ${first.resetAt}`)\n }\n\n const second = await store.increment(key, window, now + 1)\n if (second.count !== 2) throw new ConformanceError('rate limit store', `expected the second request to count 2, received ${second.count}`)\n if (second.resetAt !== first.resetAt) {\n throw new ConformanceError('rate limit store', 'a request inside the window must not extend it')\n }\n\n const other = await store.increment(`conformance:${crypto.randomUUID()}`, window, now)\n if (other.count !== 1) throw new ConformanceError('rate limit store', 'one key consumed another key budget')\n\n const expired = await store.increment(key, window, first.resetAt)\n if (expired.count !== 1) throw new ConformanceError('rate limit store', 'an elapsed window must start a new count')\n if (expired.resetAt !== first.resetAt + window) {\n throw new ConformanceError('rate limit store', 'an elapsed window must start a new window')\n }\n}\n\nexport type AuthSecretProvider = (environment: NodeJS.ProcessEnv) => string | Promise<string>\n\n// A secret that changes between restarts signs out every session, and a short one is guessable. Neither shows up\n// until it is already in production, so the contract is checked against the provider an application wired.\nexport async function assertAuthSecretConformance(secret: AuthSecretProvider, options: { environmentKey?: string } = {}) {\n const key = options.environmentKey ?? 'AUTH_SECRET'\n const configured = 'configured-secret-value-that-is-long-enough'\n\n const first = await secret({})\n if (typeof first !== 'string' || !first.trim()) throw new ConformanceError('auth secret', 'provider returned no secret')\n if (first.length < 32) throw new ConformanceError('auth secret', `expected at least 32 characters, received ${first.length}`)\n if (new Set(first).size < 8) throw new ConformanceError('auth secret', 'secret does not look randomly generated')\n\n if ((await secret({})) !== first) {\n throw new ConformanceError('auth secret', 'secret changed between calls, which signs out every session on restart')\n }\n if ((await secret({ [key]: configured })) !== configured) {\n throw new ConformanceError('auth secret', `${key} must take precedence over a generated secret`)\n }\n}\n\nexport type RealtimePublisherProbe = { publish: (channel: string, data: unknown) => boolean; close: () => Promise<void> }\n\n// A publisher that accepts every channel turns a burst into unbounded memory, and one that accepts work after close\n// loses publications during shutdown. Both appear only under load.\nexport async function assertRealtimePublisherConformance(create: () => RealtimePublisherProbe, options: { probes?: number } = {}) {\n const probes = options.probes ?? 5_000\n const bounded = create()\n let admitted = 0\n for (let attempt = 0; attempt < probes; attempt++) if (bounded.publish(`conformance-${attempt}`, { attempt })) admitted += 1\n if (admitted === probes) throw new ConformanceError('realtime publisher', `accepted all ${probes} channels without a capacity limit`)\n if (admitted === 0) throw new ConformanceError('realtime publisher', 'rejected every publication')\n await bounded.close()\n\n const closed = create()\n await closed.close()\n if (closed.publish('conformance', {})) throw new ConformanceError('realtime publisher', 'accepted work after it was closed')\n}\n\nexport type SmtpConfigReader = (environment: NodeJS.ProcessEnv) => { host: string; port: number; from: string } | undefined\n\n// Half-configured SMTP is the failure that reaches production, because nothing sends mail until something needs to.\nexport function assertSmtpConfigConformance(read: SmtpConfigReader) {\n if (read({}) !== undefined) throw new ConformanceError('SMTP configuration', 'expected no configuration when nothing is set')\n\n const configured = read({ SMTP_HOST: 'smtp.example', EMAIL_FROM: 'app@example.com' })\n if (!configured) throw new ConformanceError('SMTP configuration', 'expected a configuration from a host and sender')\n if (configured.port !== 587)\n throw new ConformanceError('SMTP configuration', `expected the default port 587, received ${configured.port}`)\n\n rejects(read, { SMTP_HOST: 'smtp.example' }, 'a host without a sender')\n rejects(read, { EMAIL_FROM: 'app@example.com' }, 'a sender without a host')\n rejects(read, { SMTP_HOST: 'smtp.example', EMAIL_FROM: 'app@example.com', SMTP_PORT: '70000' }, 'a port outside the valid range')\n rejects(read, { SMTP_HOST: 'smtp.example', EMAIL_FROM: 'app@example.com', SMTP_USER: 'user' }, 'a user without a password')\n}\n\nfunction rejects(read: SmtpConfigReader, environment: NodeJS.ProcessEnv, scenario: string) {\n try {\n read(environment)\n } catch {\n return\n }\n throw new ConformanceError('SMTP configuration', `expected ${scenario} to be rejected`)\n}\n\nexport type DatabaseTarget = { provider: 'sqlite'; file: string } | { provider: 'postgres'; url: string }\n\nexport type DatabaseTargetResolver = (options: { databaseUrl?: string; sqliteFile: string }) => DatabaseTarget\n\nexport function assertDatabaseTargetConformance(resolve: DatabaseTargetResolver) {\n const sqliteFile = '/data/application.sqlite'\n const sqlite = resolve({ sqliteFile })\n if (sqlite.provider !== 'sqlite' || sqlite.file !== sqliteFile) {\n throw new ConformanceError('default database target', 'expected the configured SQLite file')\n }\n\n for (const url of ['postgres://user:secret@database/application', 'postgresql://user:secret@database/application']) {\n const target = resolve({ databaseUrl: url, sqliteFile })\n if (target.provider !== 'postgres' || target.url !== url) {\n throw new ConformanceError('PostgreSQL database target', `expected the configured ${new URL(url).protocol} URL`)\n }\n }\n\n try {\n resolve({ databaseUrl: 'https://database.example/application', sqliteFile })\n } catch {\n return\n }\n throw new ConformanceError('invalid database target', 'expected a non-PostgreSQL URL to be rejected')\n}\n\nexport function assertPostHogBrowserConformance(options: Record<string, unknown>) {\n if (typeof options.api_host !== 'string' || !options.api_host.trim()) {\n throw new ConformanceError('PostHog browser initialization', 'api_host must be configured')\n }\n if (typeof options.ui_host !== 'string' || !options.ui_host.trim()) {\n throw new ConformanceError('PostHog browser initialization', 'ui_host must be configured')\n }\n if (typeof options.defaults !== 'string' || !options.defaults.trim()) {\n throw new ConformanceError('PostHog browser initialization', 'SDK defaults must be pinned')\n }\n if (!options.capture_exceptions) {\n throw new ConformanceError('PostHog browser initialization', 'exception autocapture must be enabled')\n }\n if (options.capture_pageview !== 'history_change') {\n throw new ConformanceError('PostHog browser initialization', 'SPA pageviews must follow history changes')\n }\n if (options.person_profiles !== 'identified_only') {\n throw new ConformanceError('PostHog browser initialization', 'person profiles must be limited to identified users')\n }\n if (options.mask_personal_data_properties !== true) {\n throw new ConformanceError('PostHog browser initialization', 'personal-data URL properties must be masked')\n }\n if (!Array.isArray(options.custom_personal_data_properties) || !options.custom_personal_data_properties.includes('token')) {\n throw new ConformanceError('PostHog browser initialization', 'token query parameters must be masked')\n }\n const recording = options.session_recording\n if (!recording || typeof recording !== 'object' || !('maskAllInputs' in recording) || recording.maskAllInputs !== true) {\n throw new ConformanceError('PostHog browser initialization', 'session replay must mask all inputs by default')\n }\n}\n\ntype PostHogContextParser = (\n request: Request,\n options?: { authenticatedDistinctId?: string; allowAnonymousDistinctId?: boolean },\n) => { distinctId?: string; sessionId?: string; properties: { $session_id?: string } }\n\nexport function assertPostHogRequestConformance(parse: PostHogContextParser) {\n const matched = parse(postHogRequest('person-123', 'session-456'), { authenticatedDistinctId: 'person-123' })\n if (matched.distinctId !== 'person-123' || matched.sessionId !== 'session-456' || matched.properties.$session_id !== 'session-456') {\n throw new ConformanceError('authenticated PostHog request', 'expected verified identity and session propagation')\n }\n const spoofed = parse(postHogRequest('attacker', 'session-456'), { authenticatedDistinctId: 'person-123' })\n if (spoofed.distinctId !== undefined) {\n throw new ConformanceError('spoofed PostHog request', 'unverified distinct id was trusted')\n }\n const malformed = parse(postHogRequest('person-123', 'x'.repeat(129)), { authenticatedDistinctId: 'person-123' })\n if (malformed.sessionId !== undefined || malformed.properties.$session_id !== undefined) {\n throw new ConformanceError('malformed PostHog request', 'unbounded session id was propagated')\n }\n}\n\nfunction postHogRequest(distinctId: string, sessionId: string) {\n return new Request('https://app.example/action', {\n headers: { 'x-posthog-distinct-id': distinctId, 'x-posthog-session-id': sessionId },\n })\n}\n\nasync function accepted(scenario: string, work: () => void | Promise<void>) {\n try {\n await work()\n } catch (error) {\n throw new ConformanceError(scenario, 'expected request to be accepted', { cause: error })\n }\n}\n\nasync function rejected(scenario: string, work: () => void | Promise<void>) {\n try {\n await work()\n } catch {\n return\n }\n throw new ConformanceError(scenario, 'expected request to be rejected')\n}\n\nasync function responseBody(response: Response, scenario: string): Promise<Record<string, unknown>> {\n try {\n const body: unknown = await response.json()\n if (body && typeof body === 'object') return body as Record<string, unknown>\n } catch (error) {\n throw new ConformanceError(scenario, 'response body must be JSON', { cause: error })\n }\n throw new ConformanceError(scenario, 'response body must be an object')\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/conformance/index.ts"],"names":[],"mappings":"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","sourcesContent":["export class ConformanceError extends Error {\n constructor(\n readonly scenario: string,\n message: string,\n options: { cause?: unknown } = {},\n ) {\n super(`${scenario}: ${message}`, options.cause === undefined ? undefined : { cause: options.cause })\n this.name = 'ConformanceError'\n }\n}\n\nexport async function assertMutationOriginConformance(\n guard: (request: Request) => void | Promise<void>,\n options: { trustForwardedHeaders?: boolean } = {},\n) {\n await accepted('same-origin request', () =>\n guard(\n new Request('https://app.example/action', {\n method: 'POST',\n headers: { origin: 'https://app.example', 'sec-fetch-site': 'same-origin' },\n }),\n ),\n )\n await rejected('cross-origin request', () =>\n guard(\n new Request('https://app.example/action', {\n method: 'POST',\n headers: { origin: 'https://attacker.example', 'sec-fetch-site': 'cross-site' },\n }),\n ),\n )\n await rejected('missing-origin request', () => guard(new Request('https://app.example/action', { method: 'POST' })))\n if (!options.trustForwardedHeaders) {\n await rejected('spoofed forwarded-origin request', () =>\n guard(\n new Request('https://app.example/action', {\n method: 'POST',\n headers: {\n origin: 'https://attacker.example',\n 'x-forwarded-host': 'attacker.example',\n 'x-forwarded-proto': 'https',\n },\n }),\n ),\n )\n }\n}\n\nexport type HealthHandlerFactory = (check: () => void | Promise<void>) => () => Response | Promise<Response>\n\nexport async function assertHealthHandlerConformance(createHandler: HealthHandlerFactory) {\n const healthy = await createHandler(() => undefined)()\n if (healthy.status !== 200) throw new ConformanceError('healthy dependency', `expected status 200, received ${healthy.status}`)\n const healthyBody = await responseBody(healthy, 'healthy dependency')\n if (healthyBody.ok !== true) throw new ConformanceError('healthy dependency', 'response body must contain ok: true')\n\n const privateMessage = 'password=private-diagnostic'\n const unavailable = await createHandler(() => Promise.reject(new Error(privateMessage)))()\n if (unavailable.status !== 503) {\n throw new ConformanceError('unavailable dependency', `expected status 503, received ${unavailable.status}`)\n }\n const unavailableText = await unavailable.text()\n if (unavailableText.includes(privateMessage)) {\n throw new ConformanceError('unavailable dependency', 'response exposed the private diagnostic message')\n }\n let unavailableBody: unknown\n try {\n unavailableBody = JSON.parse(unavailableText)\n } catch (error) {\n throw new ConformanceError('unavailable dependency', 'response body must be JSON', { cause: error })\n }\n if (!unavailableBody || typeof unavailableBody !== 'object' || !('ok' in unavailableBody) || unavailableBody.ok !== false) {\n throw new ConformanceError('unavailable dependency', 'response body must contain ok: false')\n }\n}\n\nexport type SqlitePragmaReader = (name: 'journal_mode' | 'synchronous' | 'busy_timeout' | 'foreign_keys') => unknown\n\nexport async function assertSqliteConformance(readPragma: SqlitePragmaReader) {\n const values = {\n journalMode: String(await readPragma('journal_mode')).toLowerCase(),\n synchronous: Number(await readPragma('synchronous')),\n busyTimeout: Number(await readPragma('busy_timeout')),\n foreignKeys: Number(await readPragma('foreign_keys')),\n }\n if (values.journalMode !== 'wal' && values.journalMode !== 'memory') {\n throw new ConformanceError('SQLite journal mode', `expected wal or memory, received ${values.journalMode}`)\n }\n if (values.synchronous !== 2) {\n throw new ConformanceError('SQLite synchronous mode', `expected FULL (2), received ${values.synchronous}`)\n }\n if (values.busyTimeout !== 5000) {\n throw new ConformanceError('SQLite busy timeout', `expected 5000, received ${values.busyTimeout}`)\n }\n if (values.foreignKeys !== 1) {\n throw new ConformanceError('SQLite foreign keys', `expected enabled (1), received ${values.foreignKeys}`)\n }\n}\n\nexport type RealtimeTokenSigner = (subject: string, claims: Record<string, unknown>) => string | Promise<string>\n\nexport type RealtimeTokenConformanceOptions = { secret: string; maxTtlSeconds?: number; now?: number }\n\nexport async function assertRealtimeTokenConformance(sign: RealtimeTokenSigner, options: RealtimeTokenConformanceOptions) {\n const now = options.now ?? Math.floor(Date.now() / 1000)\n const maxTtlSeconds = options.maxTtlSeconds ?? 60 * 60\n const token = await sign('person-123', { channel: 'room:1' })\n const { header, payload, signed, signature } = decodeToken(token)\n\n if (header.alg !== 'HS256') {\n throw new ConformanceError('realtime token algorithm', `expected HS256, received ${JSON.stringify(header.alg)}`)\n }\n if (payload.sub !== 'person-123') {\n throw new ConformanceError('realtime token subject', 'token must bind the subject it was signed for')\n }\n if (payload.channel !== 'room:1') {\n throw new ConformanceError('realtime token claims', 'token must carry the claims it was signed with')\n }\n if (typeof payload.exp !== 'number') {\n throw new ConformanceError('realtime token expiry', 'token must expire')\n }\n if (payload.exp <= now) {\n throw new ConformanceError('realtime token expiry', 'token expired before it was issued')\n }\n if (payload.exp - now > maxTtlSeconds) {\n throw new ConformanceError('realtime token expiry', `token outlives the ${maxTtlSeconds} second maximum`)\n }\n if (!(await verifyHmac(signed, signature, options.secret))) {\n throw new ConformanceError('realtime token signature', 'token is not signed with the shared Centrifugo secret')\n }\n\n const other = decodeToken(await sign('person-456', { channel: 'room:1' })).payload\n if (other.sub === payload.sub) {\n throw new ConformanceError('realtime token subject', 'every subject received the same identity')\n }\n}\n\nfunction decodeToken(token: string) {\n const segments = token.split('.')\n if (segments.length !== 3) throw new ConformanceError('realtime token format', 'expected a three-segment JWT')\n const [header, claims, signature] = segments as [string, string, string]\n return {\n header: decodeSegment(header, 'header'),\n payload: decodeSegment(claims, 'payload'),\n signed: `${header}.${claims}`,\n signature,\n }\n}\n\nfunction decodeSegment(segment: string, name: string): Record<string, unknown> {\n try {\n const padded = segment\n .replaceAll('-', '+')\n .replaceAll('_', '/')\n .padEnd(Math.ceil(segment.length / 4) * 4, '=')\n const value: unknown = JSON.parse(atob(padded))\n if (value && typeof value === 'object') return value as Record<string, unknown>\n } catch (error) {\n throw new ConformanceError('realtime token format', `${name} is not base64url JSON`, { cause: error })\n }\n throw new ConformanceError('realtime token format', `${name} is not an object`)\n}\n\nasync function verifyHmac(signed: string, signature: string, secret: string) {\n const encoder = new TextEncoder()\n const key = await crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'])\n const digest = new Uint8Array(await crypto.subtle.sign('HMAC', key, encoder.encode(signed)))\n const expected = btoa(String.fromCharCode(...digest))\n .replaceAll('+', '-')\n .replaceAll('/', '_')\n .replaceAll('=', '')\n return expected === signature\n}\n\nexport type RateLimitStoreProbe = {\n increment: (\n key: string,\n windowSeconds: number,\n now: number,\n ) => Promise<{ count: number; resetAt: number }> | { count: number; resetAt: number }\n}\n\n// The package cannot reach a consumer's PostgreSQL, so the store contract travels to wherever the real database is.\nexport async function assertRateLimitStoreConformance(store: RateLimitStoreProbe, options: { now?: number } = {}) {\n const now = options.now ?? Math.floor(Date.now() / 1000)\n const window = 60\n const key = `conformance:${crypto.randomUUID()}`\n\n const first = await store.increment(key, window, now)\n if (first.count !== 1) throw new ConformanceError('rate limit store', `expected a new key to start at 1, received ${first.count}`)\n if (first.resetAt !== now + window) {\n throw new ConformanceError('rate limit store', `expected the window to end at ${now + window}, received ${first.resetAt}`)\n }\n\n const second = await store.increment(key, window, now + 1)\n if (second.count !== 2) throw new ConformanceError('rate limit store', `expected the second request to count 2, received ${second.count}`)\n if (second.resetAt !== first.resetAt) {\n throw new ConformanceError('rate limit store', 'a request inside the window must not extend it')\n }\n\n const other = await store.increment(`conformance:${crypto.randomUUID()}`, window, now)\n if (other.count !== 1) throw new ConformanceError('rate limit store', 'one key consumed another key budget')\n\n const expired = await store.increment(key, window, first.resetAt)\n if (expired.count !== 1) throw new ConformanceError('rate limit store', 'an elapsed window must start a new count')\n if (expired.resetAt !== first.resetAt + window) {\n throw new ConformanceError('rate limit store', 'an elapsed window must start a new window')\n }\n}\n\nexport type AuthSecretProvider = (environment: NodeJS.ProcessEnv) => string | Promise<string>\n\n// A secret that changes between restarts signs out every session, and a short one is guessable. Neither shows up\n// until it is already in production, so the contract is checked against the provider an application wired.\nexport async function assertAuthSecretConformance(secret: AuthSecretProvider, options: { environmentKey?: string } = {}) {\n const key = options.environmentKey ?? 'AUTH_SECRET'\n const configured = 'configured-secret-value-that-is-long-enough'\n\n const first = await secret({})\n if (typeof first !== 'string' || !first.trim()) throw new ConformanceError('auth secret', 'provider returned no secret')\n if (first.length < 32) throw new ConformanceError('auth secret', `expected at least 32 characters, received ${first.length}`)\n if (new Set(first).size < 8) throw new ConformanceError('auth secret', 'secret does not look randomly generated')\n\n if ((await secret({})) !== first) {\n throw new ConformanceError('auth secret', 'secret changed between calls, which signs out every session on restart')\n }\n if ((await secret({ [key]: configured })) !== configured) {\n throw new ConformanceError('auth secret', `${key} must take precedence over a generated secret`)\n }\n}\n\nexport type RealtimePublisherProbe = { publish: (channel: string, data: unknown) => boolean; close: () => Promise<void> }\n\n// A publisher that accepts every channel turns a burst into unbounded memory, and one that accepts work after close\n// loses publications during shutdown. Both appear only under load.\nexport async function assertRealtimePublisherConformance(create: () => RealtimePublisherProbe, options: { probes?: number } = {}) {\n const probes = options.probes ?? 5_000\n const bounded = create()\n let admitted = 0\n for (let attempt = 0; attempt < probes; attempt++) if (bounded.publish(`conformance-${attempt}`, { attempt })) admitted += 1\n if (admitted === probes) throw new ConformanceError('realtime publisher', `accepted all ${probes} channels without a capacity limit`)\n if (admitted === 0) throw new ConformanceError('realtime publisher', 'rejected every publication')\n await bounded.close()\n\n const closed = create()\n await closed.close()\n if (closed.publish('conformance', {})) throw new ConformanceError('realtime publisher', 'accepted work after it was closed')\n}\n\nexport type SmtpConfigReader = (environment: NodeJS.ProcessEnv) => { host: string; port: number; from: string } | undefined\n\n// Half-configured SMTP is the failure that reaches production, because nothing sends mail until something needs to.\nexport function assertSmtpConfigConformance(read: SmtpConfigReader) {\n if (read({}) !== undefined) throw new ConformanceError('SMTP configuration', 'expected no configuration when nothing is set')\n\n const configured = read({ SMTP_HOST: 'smtp.example', EMAIL_FROM: 'app@example.com' })\n if (!configured) throw new ConformanceError('SMTP configuration', 'expected a configuration from a host and sender')\n if (configured.port !== 587)\n throw new ConformanceError('SMTP configuration', `expected the default port 587, received ${configured.port}`)\n\n rejects(read, { SMTP_HOST: 'smtp.example' }, 'a host without a sender')\n rejects(read, { EMAIL_FROM: 'app@example.com' }, 'a sender without a host')\n rejects(read, { SMTP_HOST: 'smtp.example', EMAIL_FROM: 'app@example.com', SMTP_PORT: '70000' }, 'a port outside the valid range')\n rejects(read, { SMTP_HOST: 'smtp.example', EMAIL_FROM: 'app@example.com', SMTP_USER: 'user' }, 'a user without a password')\n}\n\nfunction rejects(read: SmtpConfigReader, environment: NodeJS.ProcessEnv, scenario: string) {\n try {\n read(environment)\n } catch {\n return\n }\n throw new ConformanceError('SMTP configuration', `expected ${scenario} to be rejected`)\n}\n\nexport type DatabaseTarget = { provider: 'sqlite'; file: string } | { provider: 'postgres'; url: string }\n\nexport type DatabaseTargetResolver = (options: { databaseUrl?: string; sqliteFile: string }) => DatabaseTarget\n\nexport function assertDatabaseTargetConformance(resolve: DatabaseTargetResolver) {\n const sqliteFile = '/data/application.sqlite'\n const sqlite = resolve({ sqliteFile })\n if (sqlite.provider !== 'sqlite' || sqlite.file !== sqliteFile) {\n throw new ConformanceError('default database target', 'expected the configured SQLite file')\n }\n\n for (const url of ['postgres://user:secret@database/application', 'postgresql://user:secret@database/application']) {\n const target = resolve({ databaseUrl: url, sqliteFile })\n if (target.provider !== 'postgres' || target.url !== url) {\n throw new ConformanceError('PostgreSQL database target', `expected the configured ${new URL(url).protocol} URL`)\n }\n }\n\n try {\n resolve({ databaseUrl: 'https://database.example/application', sqliteFile })\n } catch {\n return\n }\n throw new ConformanceError('invalid database target', 'expected a non-PostgreSQL URL to be rejected')\n}\n\nexport function assertPostHogBrowserConformance(options: Record<string, unknown>) {\n if (typeof options.api_host !== 'string' || !options.api_host.trim()) {\n throw new ConformanceError('PostHog browser initialization', 'api_host must be configured')\n }\n if (typeof options.ui_host !== 'string' || !options.ui_host.trim()) {\n throw new ConformanceError('PostHog browser initialization', 'ui_host must be configured')\n }\n if (typeof options.defaults !== 'string' || !options.defaults.trim()) {\n throw new ConformanceError('PostHog browser initialization', 'SDK defaults must be pinned')\n }\n if (!options.capture_exceptions) {\n throw new ConformanceError('PostHog browser initialization', 'exception autocapture must be enabled')\n }\n if (options.capture_pageview !== 'history_change') {\n throw new ConformanceError('PostHog browser initialization', 'SPA pageviews must follow history changes')\n }\n if (options.capture_performance !== true) {\n throw new ConformanceError('PostHog browser initialization', 'browser performance capture must be enabled')\n }\n if (options.person_profiles !== 'identified_only') {\n throw new ConformanceError('PostHog browser initialization', 'person profiles must be limited to identified users')\n }\n if (options.mask_personal_data_properties !== true) {\n throw new ConformanceError('PostHog browser initialization', 'personal-data URL properties must be masked')\n }\n if (!Array.isArray(options.custom_personal_data_properties) || !options.custom_personal_data_properties.includes('token')) {\n throw new ConformanceError('PostHog browser initialization', 'token query parameters must be masked')\n }\n const recording = options.session_recording\n if (!recording || typeof recording !== 'object' || !('maskAllInputs' in recording) || recording.maskAllInputs !== true) {\n throw new ConformanceError('PostHog browser initialization', 'session replay must mask all inputs by default')\n }\n const logs = serviceName(options.logs)\n const metrics = serviceName(options.metrics)\n if ((logs || metrics) && (!logs || logs !== metrics)) {\n throw new ConformanceError('PostHog browser initialization', 'logs and metrics must share one service name')\n }\n}\n\nfunction serviceName(value: unknown) {\n if (!value || typeof value !== 'object' || !('serviceName' in value) || typeof value.serviceName !== 'string') return undefined\n return value.serviceName.trim() || undefined\n}\n\ntype PostHogContextParser = (\n request: Request,\n options?: { authenticatedDistinctId?: string; allowAnonymousDistinctId?: boolean },\n) => { distinctId?: string; sessionId?: string; properties: { $session_id?: string } }\n\nexport function assertPostHogRequestConformance(parse: PostHogContextParser) {\n const matched = parse(postHogRequest('person-123', 'session-456'), { authenticatedDistinctId: 'person-123' })\n if (matched.distinctId !== 'person-123' || matched.sessionId !== 'session-456' || matched.properties.$session_id !== 'session-456') {\n throw new ConformanceError('authenticated PostHog request', 'expected verified identity and session propagation')\n }\n const spoofed = parse(postHogRequest('attacker', 'session-456'), { authenticatedDistinctId: 'person-123' })\n if (spoofed.distinctId !== undefined) {\n throw new ConformanceError('spoofed PostHog request', 'unverified distinct id was trusted')\n }\n const malformed = parse(postHogRequest('person-123', 'x'.repeat(129)), { authenticatedDistinctId: 'person-123' })\n if (malformed.sessionId !== undefined || malformed.properties.$session_id !== undefined) {\n throw new ConformanceError('malformed PostHog request', 'unbounded session id was propagated')\n }\n}\n\nfunction postHogRequest(distinctId: string, sessionId: string) {\n return new Request('https://app.example/action', {\n headers: { 'x-posthog-distinct-id': distinctId, 'x-posthog-session-id': sessionId },\n })\n}\n\nasync function accepted(scenario: string, work: () => void | Promise<void>) {\n try {\n await work()\n } catch (error) {\n throw new ConformanceError(scenario, 'expected request to be accepted', { cause: error })\n }\n}\n\nasync function rejected(scenario: string, work: () => void | Promise<void>) {\n try {\n await work()\n } catch {\n return\n }\n throw new ConformanceError(scenario, 'expected request to be rejected')\n}\n\nasync function responseBody(response: Response, scenario: string): Promise<Record<string, unknown>> {\n try {\n const body: unknown = await response.json()\n if (body && typeof body === 'object') return body as Record<string, unknown>\n } catch (error) {\n throw new ConformanceError(scenario, 'response body must be JSON', { cause: error })\n }\n throw new ConformanceError(scenario, 'response body must be an object')\n}\n"]}
@@ -23,7 +23,7 @@
23
23
  "@opentelemetry/exporter-logs-otlp-http": "^0.221.0",
24
24
  "@opentelemetry/resources": "^2.10.0",
25
25
  "@opentelemetry/sdk-logs": "^0.221.0",
26
- "@posthog/react": "^1.10.3",
26
+ "@posthog/react": "^1.10.6",
27
27
  "@tanstack/react-query": "^5.101.4",
28
28
  "@tanstack/react-router": "^1.170.18",
29
29
  "@tanstack/react-router-ssr-query": "^1.167.1",
@@ -33,8 +33,8 @@
33
33
  "centrifuge": "5.7.1",
34
34
  "drizzle-orm": "0.45.2",
35
35
  "nodemailer": "^9.0.3",
36
- "posthog-js": "^1.414.0",
37
- "posthog-node": "^5.48.1",
36
+ "posthog-js": "^1.430.3",
37
+ "posthog-node": "^5.52.1",
38
38
  "ras-stack": "workspace:*",
39
39
  "react": "19.2.8",
40
40
  "react-dom": "19.2.8",
@@ -5,6 +5,8 @@ export const postHogCoverage = definePostHogCoverage({
5
5
  analytics: true,
6
6
  errorTracking: true,
7
7
  identity: true,
8
+ logs: true,
9
+ metrics: true,
8
10
  sessionReplay: { disabled: 'The example does not handle user data worth replaying' },
9
11
  featureFlags: { disabled: 'The example has no rollout-controlled behavior' },
10
12
  },
@@ -12,6 +14,8 @@ export const postHogCoverage = definePostHogCoverage({
12
14
  analytics: true,
13
15
  errorTracking: true,
14
16
  logs: true,
17
+ metrics: true,
18
+ tracing: true,
15
19
  },
16
20
  sourceMaps: { disabled: 'The example is not deployed as a user-facing application' },
17
21
  })
@@ -9,6 +9,8 @@ const posthog = postHogEnvironment({
9
9
  host: import.meta.env.VITE_POSTHOG_HOST,
10
10
  })
11
11
 
12
+ const posthogService = { name: 'ras-stack-example-web', environment: import.meta.env.MODE }
13
+
12
14
  export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({
13
15
  head: () => ({
14
16
  meta: [
@@ -28,7 +30,7 @@ function Root() {
28
30
  <HeadContent />
29
31
  </head>
30
32
  <body>
31
- <PostHogIntegration environment={posthog}>
33
+ <PostHogIntegration environment={posthog} service={posthogService}>
32
34
  <Outlet />
33
35
  </PostHogIntegration>
34
36
  <Scripts />
@@ -24,7 +24,11 @@ describe('PostHog integration boundary', () => {
24
24
  const telemetry = createManagedPostHogServerTelemetry({ environment: postHogEnvironment({}), serviceName: 'example' })
25
25
  await telemetry.start()
26
26
  await telemetry.log({ body: 'request completed', severityText: 'info' })
27
+ await telemetry.metrics.count('request.completed')
28
+ expect(await telemetry.withSpan('request', () => 'completed')).toBe('completed')
27
29
  await telemetry.shutdown()
28
30
  expect(postHogCoverage.server.logs).toBe(true)
31
+ expect(postHogCoverage.server.metrics).toBe(true)
32
+ expect(postHogCoverage.server.tracing).toBe(true)
29
33
  })
30
34
  })
@@ -1,10 +1,12 @@
1
1
  import type { PostHogConfig } from 'posthog-js';
2
+ import type { PostHogBrowserService } from './config.js';
2
3
  export { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER } from './request.js';
3
4
  export declare const POSTHOG_BROWSER_DEFAULTS = "2026-05-30";
4
5
  export declare function postHogBrowserOptions(input: {
5
6
  apiHost: string;
6
7
  uiHost: string;
7
8
  tracingHostnames?: string[];
9
+ service?: PostHogBrowserService;
8
10
  options?: Partial<PostHogConfig>;
9
11
  }): Partial<PostHogConfig>;
10
12
  export declare function postHogBrowserHeaders(client: {
@@ -2,16 +2,26 @@ import { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER, postHogIdentifie
2
2
  export { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER } from './request.js';
3
3
  export const POSTHOG_BROWSER_DEFAULTS = '2026-05-30';
4
4
  export function postHogBrowserOptions(input) {
5
+ const service = input.service
6
+ ? {
7
+ serviceName: required(input.service.name, 'service.name'),
8
+ ...(input.service.version ? { serviceVersion: input.service.version } : {}),
9
+ ...(input.service.environment ? { environment: input.service.environment } : {}),
10
+ ...(input.service.resourceAttributes ? { resourceAttributes: input.service.resourceAttributes } : {}),
11
+ }
12
+ : undefined;
5
13
  return {
6
14
  api_host: required(input.apiHost, 'apiHost'),
7
15
  ui_host: required(input.uiHost, 'uiHost'),
8
16
  defaults: POSTHOG_BROWSER_DEFAULTS,
9
17
  capture_exceptions: true,
10
18
  capture_pageview: 'history_change',
19
+ capture_performance: true,
11
20
  custom_personal_data_properties: ['token'],
12
21
  mask_personal_data_properties: true,
13
22
  person_profiles: 'identified_only',
14
23
  session_recording: { maskAllInputs: true, blockSelector: '.ph-no-capture' },
24
+ ...(service ? { logs: service, metrics: service } : {}),
15
25
  ...(input.tracingHostnames ? { tracing_headers: input.tracingHostnames } : {}),
16
26
  ...input.options,
17
27
  };
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/posthog/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAEvG,OAAO,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AACpF,MAAM,CAAC,MAAM,wBAAwB,GAAG,YAAY,CAAA;AAEpD,MAAM,UAAU,qBAAqB,CAAC,KAKrC;IACC,OAAO;QACL,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC;QAC5C,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC;QACzC,QAAQ,EAAE,wBAAwB;QAClC,kBAAkB,EAAE,IAAI;QACxB,gBAAgB,EAAE,gBAAgB;QAClC,+BAA+B,EAAE,CAAC,OAAO,CAAC;QAC1C,6BAA6B,EAAE,IAAI;QACnC,eAAe,EAAE,iBAAiB;QAClC,iBAAiB,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAE,gBAAgB,EAAE;QAC3E,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9E,GAAG,KAAK,CAAC,OAAO;KACjB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAA2E;IAC/G,MAAM,UAAU,GAAG,iBAAiB,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAA;IAC9D,MAAM,SAAS,GAAG,iBAAiB,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAA;IAC5D,OAAO;QACL,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,0BAA0B,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,yBAAyB,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACjE,CAAA;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAa,EAAE,IAAY;IAC3C,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAC/B,IAAI,CAAC,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,cAAc,CAAC,CAAA;IACvD,OAAO,UAAU,CAAA;AACnB,CAAC","sourcesContent":["import type { PostHogConfig } from 'posthog-js'\nimport { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER, postHogIdentifier } from './request.js'\n\nexport { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER } from './request.js'\nexport const POSTHOG_BROWSER_DEFAULTS = '2026-05-30'\n\nexport function postHogBrowserOptions(input: {\n apiHost: string\n uiHost: string\n tracingHostnames?: string[]\n options?: Partial<PostHogConfig>\n}): Partial<PostHogConfig> {\n return {\n api_host: required(input.apiHost, 'apiHost'),\n ui_host: required(input.uiHost, 'uiHost'),\n defaults: POSTHOG_BROWSER_DEFAULTS,\n capture_exceptions: true,\n capture_pageview: 'history_change',\n custom_personal_data_properties: ['token'],\n mask_personal_data_properties: true,\n person_profiles: 'identified_only',\n session_recording: { maskAllInputs: true, blockSelector: '.ph-no-capture' },\n ...(input.tracingHostnames ? { tracing_headers: input.tracingHostnames } : {}),\n ...input.options,\n }\n}\n\nexport function postHogBrowserHeaders(client: { get_distinct_id(): string; get_session_id(): string | undefined }): Record<string, string> {\n const distinctId = postHogIdentifier(client.get_distinct_id())\n const sessionId = postHogIdentifier(client.get_session_id())\n return {\n ...(distinctId ? { [POSTHOG_DISTINCT_ID_HEADER]: distinctId } : {}),\n ...(sessionId ? { [POSTHOG_SESSION_ID_HEADER]: sessionId } : {}),\n }\n}\n\nfunction required(value: string, name: string) {\n const normalized = value.trim()\n if (!normalized) throw new Error(`${name} is required`)\n return normalized\n}\n"]}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/posthog/client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAEvG,OAAO,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AACpF,MAAM,CAAC,MAAM,wBAAwB,GAAG,YAAY,CAAA;AAEpD,MAAM,UAAU,qBAAqB,CAAC,KAMrC;IACC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO;QAC3B,CAAC,CAAC;YACE,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,cAAc,CAAC;YACzD,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3E,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChF,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,KAAK,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACtG;QACH,CAAC,CAAC,SAAS,CAAA;IACb,OAAO;QACL,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC;QAC5C,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC;QACzC,QAAQ,EAAE,wBAAwB;QAClC,kBAAkB,EAAE,IAAI;QACxB,gBAAgB,EAAE,gBAAgB;QAClC,mBAAmB,EAAE,IAAI;QACzB,+BAA+B,EAAE,CAAC,OAAO,CAAC;QAC1C,6BAA6B,EAAE,IAAI;QACnC,eAAe,EAAE,iBAAiB;QAClC,iBAAiB,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAE,gBAAgB,EAAE;QAC3E,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9E,GAAG,KAAK,CAAC,OAAO;KACjB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAA2E;IAC/G,MAAM,UAAU,GAAG,iBAAiB,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAA;IAC9D,MAAM,SAAS,GAAG,iBAAiB,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAA;IAC5D,OAAO;QACL,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,0BAA0B,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,yBAAyB,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACjE,CAAA;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAa,EAAE,IAAY;IAC3C,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAC/B,IAAI,CAAC,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,cAAc,CAAC,CAAA;IACvD,OAAO,UAAU,CAAA;AACnB,CAAC","sourcesContent":["import type { PostHogConfig } from 'posthog-js'\nimport type { PostHogBrowserService } from './config.js'\nimport { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER, postHogIdentifier } from './request.js'\n\nexport { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER } from './request.js'\nexport const POSTHOG_BROWSER_DEFAULTS = '2026-05-30'\n\nexport function postHogBrowserOptions(input: {\n apiHost: string\n uiHost: string\n tracingHostnames?: string[]\n service?: PostHogBrowserService\n options?: Partial<PostHogConfig>\n}): Partial<PostHogConfig> {\n const service = input.service\n ? {\n serviceName: required(input.service.name, 'service.name'),\n ...(input.service.version ? { serviceVersion: input.service.version } : {}),\n ...(input.service.environment ? { environment: input.service.environment } : {}),\n ...(input.service.resourceAttributes ? { resourceAttributes: input.service.resourceAttributes } : {}),\n }\n : undefined\n return {\n api_host: required(input.apiHost, 'apiHost'),\n ui_host: required(input.uiHost, 'uiHost'),\n defaults: POSTHOG_BROWSER_DEFAULTS,\n capture_exceptions: true,\n capture_pageview: 'history_change',\n capture_performance: true,\n custom_personal_data_properties: ['token'],\n mask_personal_data_properties: true,\n person_profiles: 'identified_only',\n session_recording: { maskAllInputs: true, blockSelector: '.ph-no-capture' },\n ...(service ? { logs: service, metrics: service } : {}),\n ...(input.tracingHostnames ? { tracing_headers: input.tracingHostnames } : {}),\n ...input.options,\n }\n}\n\nexport function postHogBrowserHeaders(client: { get_distinct_id(): string; get_session_id(): string | undefined }): Record<string, string> {\n const distinctId = postHogIdentifier(client.get_distinct_id())\n const sessionId = postHogIdentifier(client.get_session_id())\n return {\n ...(distinctId ? { [POSTHOG_DISTINCT_ID_HEADER]: distinctId } : {}),\n ...(sessionId ? { [POSTHOG_SESSION_ID_HEADER]: sessionId } : {}),\n }\n}\n\nfunction required(value: string, name: string) {\n const normalized = value.trim()\n if (!normalized) throw new Error(`${name} is required`)\n return normalized\n}\n"]}
@@ -4,6 +4,12 @@ export type PostHogEnvironment = {
4
4
  uiHost: string;
5
5
  assetsHost: string;
6
6
  };
7
+ export type PostHogBrowserService = {
8
+ name: string;
9
+ version?: string;
10
+ environment?: string;
11
+ resourceAttributes?: Record<string, string>;
12
+ };
7
13
  export declare function postHogEnvironment(input: {
8
14
  projectToken?: string;
9
15
  host?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/posthog/config.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,kBAAkB,CAAC,KAAoE;IACrG,MAAM,YAAY,GAAG,KAAK,CAAC,YAAY,EAAE,IAAI,EAAE,CAAA;IAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,CAAA;IAC/B,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU;QAAE,OAAO,SAAS,CAAA;IACjE,IAAI,CAAC,YAAY;QAAE,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAA;IAChG,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,IAAI,0BAA0B,EAAE,cAAc,CAAC,CAAA;IAC1E,OAAO;QACL,YAAY;QACZ,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC;QAC3B,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC;QAC7B,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,UAAU,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,MAAM,CAAC;KAClH,CAAA;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,IAAY;IACxD,OAAO,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAA;AAC5C,CAAC;AAED,SAAS,OAAO,CAAC,KAAa,EAAE,IAAY;IAC1C,IAAI,MAAW,CAAA;IACf,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,8DAA8D,CAAC,CAAA;IACxF,CAAC;IACD,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QACzH,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,8DAA8D,CAAC,CAAA;IACxF,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,aAAa,CAAC,GAAQ;IAC7B,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;AAC1C,CAAC;AAED,SAAS,aAAa,CAAC,IAAS;IAC9B,IAAI,IAAI,CAAC,QAAQ,KAAK,kBAAkB;QAAE,OAAO,wBAAwB,CAAA;IACzE,IAAI,IAAI,CAAC,QAAQ,KAAK,kBAAkB;QAAE,OAAO,wBAAwB,CAAA;IACzE,OAAO,IAAI,CAAC,MAAM,CAAA;AACpB,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAS;IAClC,IAAI,IAAI,CAAC,QAAQ,KAAK,kBAAkB;QAAE,OAAO,iCAAiC,CAAA;IAClF,IAAI,IAAI,CAAC,QAAQ,KAAK,kBAAkB;QAAE,OAAO,iCAAiC,CAAA;IAClF,OAAO,aAAa,CAAC,IAAI,CAAC,CAAA;AAC5B,CAAC","sourcesContent":["export type PostHogEnvironment = {\n projectToken: string\n host: string\n uiHost: string\n assetsHost: string\n}\n\nexport function postHogEnvironment(input: { projectToken?: string; host?: string; assetsHost?: string }): PostHogEnvironment | undefined {\n const projectToken = input.projectToken?.trim()\n const host = input.host?.trim()\n if (!projectToken && !host && !input.assetsHost) return undefined\n if (!projectToken) throw new Error('PostHog projectToken is required when a host is configured')\n const parsed = httpUrl(host || 'https://us.i.posthog.com', 'PostHog host')\n return {\n projectToken,\n host: normalizedUrl(parsed),\n uiHost: postHogUiHost(parsed),\n assetsHost: input.assetsHost ? postHogHttpUrl(input.assetsHost, 'PostHog assetsHost') : postHogAssetsHost(parsed),\n }\n}\n\nexport function postHogHttpUrl(value: string, name: string) {\n return normalizedUrl(httpUrl(value, name))\n}\n\nfunction httpUrl(value: string, name: string) {\n let parsed: URL\n try {\n parsed = new URL(value)\n } catch {\n throw new Error(`${name} must be an HTTP URL without credentials, query, or fragment`)\n }\n if (!['http:', 'https:'].includes(parsed.protocol) || parsed.username || parsed.password || parsed.search || parsed.hash) {\n throw new Error(`${name} must be an HTTP URL without credentials, query, or fragment`)\n }\n return parsed\n}\n\nfunction normalizedUrl(url: URL) {\n return url.toString().replace(/\\/$/, '')\n}\n\nfunction postHogUiHost(host: URL) {\n if (host.hostname === 'us.i.posthog.com') return 'https://us.posthog.com'\n if (host.hostname === 'eu.i.posthog.com') return 'https://eu.posthog.com'\n return host.origin\n}\n\nfunction postHogAssetsHost(host: URL) {\n if (host.hostname === 'us.i.posthog.com') return 'https://us-assets.i.posthog.com'\n if (host.hostname === 'eu.i.posthog.com') return 'https://eu-assets.i.posthog.com'\n return normalizedUrl(host)\n}\n"]}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/posthog/config.ts"],"names":[],"mappings":"AAcA,MAAM,UAAU,kBAAkB,CAAC,KAAoE;IACrG,MAAM,YAAY,GAAG,KAAK,CAAC,YAAY,EAAE,IAAI,EAAE,CAAA;IAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,CAAA;IAC/B,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU;QAAE,OAAO,SAAS,CAAA;IACjE,IAAI,CAAC,YAAY;QAAE,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAA;IAChG,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,IAAI,0BAA0B,EAAE,cAAc,CAAC,CAAA;IAC1E,OAAO;QACL,YAAY;QACZ,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC;QAC3B,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC;QAC7B,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,UAAU,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,MAAM,CAAC;KAClH,CAAA;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,IAAY;IACxD,OAAO,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAA;AAC5C,CAAC;AAED,SAAS,OAAO,CAAC,KAAa,EAAE,IAAY;IAC1C,IAAI,MAAW,CAAA;IACf,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,8DAA8D,CAAC,CAAA;IACxF,CAAC;IACD,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QACzH,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,8DAA8D,CAAC,CAAA;IACxF,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,aAAa,CAAC,GAAQ;IAC7B,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;AAC1C,CAAC;AAED,SAAS,aAAa,CAAC,IAAS;IAC9B,IAAI,IAAI,CAAC,QAAQ,KAAK,kBAAkB;QAAE,OAAO,wBAAwB,CAAA;IACzE,IAAI,IAAI,CAAC,QAAQ,KAAK,kBAAkB;QAAE,OAAO,wBAAwB,CAAA;IACzE,OAAO,IAAI,CAAC,MAAM,CAAA;AACpB,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAS;IAClC,IAAI,IAAI,CAAC,QAAQ,KAAK,kBAAkB;QAAE,OAAO,iCAAiC,CAAA;IAClF,IAAI,IAAI,CAAC,QAAQ,KAAK,kBAAkB;QAAE,OAAO,iCAAiC,CAAA;IAClF,OAAO,aAAa,CAAC,IAAI,CAAC,CAAA;AAC5B,CAAC","sourcesContent":["export type PostHogEnvironment = {\n projectToken: string\n host: string\n uiHost: string\n assetsHost: string\n}\n\nexport type PostHogBrowserService = {\n name: string\n version?: string\n environment?: string\n resourceAttributes?: Record<string, string>\n}\n\nexport function postHogEnvironment(input: { projectToken?: string; host?: string; assetsHost?: string }): PostHogEnvironment | undefined {\n const projectToken = input.projectToken?.trim()\n const host = input.host?.trim()\n if (!projectToken && !host && !input.assetsHost) return undefined\n if (!projectToken) throw new Error('PostHog projectToken is required when a host is configured')\n const parsed = httpUrl(host || 'https://us.i.posthog.com', 'PostHog host')\n return {\n projectToken,\n host: normalizedUrl(parsed),\n uiHost: postHogUiHost(parsed),\n assetsHost: input.assetsHost ? postHogHttpUrl(input.assetsHost, 'PostHog assetsHost') : postHogAssetsHost(parsed),\n }\n}\n\nexport function postHogHttpUrl(value: string, name: string) {\n return normalizedUrl(httpUrl(value, name))\n}\n\nfunction httpUrl(value: string, name: string) {\n let parsed: URL\n try {\n parsed = new URL(value)\n } catch {\n throw new Error(`${name} must be an HTTP URL without credentials, query, or fragment`)\n }\n if (!['http:', 'https:'].includes(parsed.protocol) || parsed.username || parsed.password || parsed.search || parsed.hash) {\n throw new Error(`${name} must be an HTTP URL without credentials, query, or fragment`)\n }\n return parsed\n}\n\nfunction normalizedUrl(url: URL) {\n return url.toString().replace(/\\/$/, '')\n}\n\nfunction postHogUiHost(host: URL) {\n if (host.hostname === 'us.i.posthog.com') return 'https://us.posthog.com'\n if (host.hostname === 'eu.i.posthog.com') return 'https://eu.posthog.com'\n return host.origin\n}\n\nfunction postHogAssetsHost(host: URL) {\n if (host.hostname === 'us.i.posthog.com') return 'https://us-assets.i.posthog.com'\n if (host.hostname === 'eu.i.posthog.com') return 'https://eu-assets.i.posthog.com'\n return normalizedUrl(host)\n}\n"]}
@@ -7,12 +7,16 @@ export type PostHogCoverage = {
7
7
  errorTracking: PostHogCoverageDecision;
8
8
  featureFlags: PostHogCoverageDecision;
9
9
  identity: PostHogCoverageDecision;
10
+ logs?: PostHogCoverageDecision;
11
+ metrics?: PostHogCoverageDecision;
10
12
  sessionReplay: PostHogCoverageDecision;
11
13
  };
12
14
  server: {
13
15
  analytics: PostHogCoverageDecision;
14
16
  errorTracking: PostHogCoverageDecision;
15
17
  logs: PostHogCoverageDecision;
18
+ metrics?: PostHogCoverageDecision;
19
+ tracing?: PostHogCoverageDecision;
16
20
  };
17
21
  sourceMaps: PostHogCoverageDecision;
18
22
  };
@@ -16,10 +16,14 @@ function coverageDecisions(coverage) {
16
16
  ['browser.errorTracking', coverage.browser.errorTracking],
17
17
  ['browser.featureFlags', coverage.browser.featureFlags],
18
18
  ['browser.identity', coverage.browser.identity],
19
+ ...(coverage.browser.logs ? [['browser.logs', coverage.browser.logs]] : []),
20
+ ...(coverage.browser.metrics ? [['browser.metrics', coverage.browser.metrics]] : []),
19
21
  ['browser.sessionReplay', coverage.browser.sessionReplay],
20
22
  ['server.analytics', coverage.server.analytics],
21
23
  ['server.errorTracking', coverage.server.errorTracking],
22
24
  ['server.logs', coverage.server.logs],
25
+ ...(coverage.server.metrics ? [['server.metrics', coverage.server.metrics]] : []),
26
+ ...(coverage.server.tracing ? [['server.tracing', coverage.server.tracing]] : []),
23
27
  ['sourceMaps', coverage.sourceMaps],
24
28
  ];
25
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"coverage.js","sourceRoot":"","sources":["../../src/posthog/coverage.ts"],"names":[],"mappings":"AAkBA,MAAM,UAAU,qBAAqB,CAAC,QAAyB;IAC7D,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,IAAI,QAAQ,KAAK,IAAI;YAAE,SAAQ;QAC/B,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,OAAO,QAAQ,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACvF,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,+CAA+C,CAAC,CAAA;QACzE,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,oCAAoC,CAAC,CAAA;IAC7F,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAyB;IAClD,OAAO;QACL,CAAC,mBAAmB,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC;QACjD,CAAC,uBAAuB,EAAE,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC;QACzD,CAAC,sBAAsB,EAAE,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC;QACvD,CAAC,kBAAkB,EAAE,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC;QAC/C,CAAC,uBAAuB,EAAE,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC;QACzD,CAAC,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC;QAC/C,CAAC,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC;QACvD,CAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC;QACrC,CAAC,YAAY,EAAE,QAAQ,CAAC,UAAU,CAAC;KACpC,CAAA;AACH,CAAC","sourcesContent":["export type PostHogCoverageDecision = true | { disabled: string }\n\nexport type PostHogCoverage = {\n browser: {\n analytics: PostHogCoverageDecision\n errorTracking: PostHogCoverageDecision\n featureFlags: PostHogCoverageDecision\n identity: PostHogCoverageDecision\n sessionReplay: PostHogCoverageDecision\n }\n server: {\n analytics: PostHogCoverageDecision\n errorTracking: PostHogCoverageDecision\n logs: PostHogCoverageDecision\n }\n sourceMaps: PostHogCoverageDecision\n}\n\nexport function definePostHogCoverage(coverage: PostHogCoverage) {\n for (const [area, decision] of coverageDecisions(coverage)) {\n if (decision === true) continue\n if (!decision || typeof decision !== 'object' || typeof decision.disabled !== 'string') {\n throw new Error(`${area} must be enabled or include a disabled reason`)\n }\n if (!decision.disabled.trim()) throw new Error(`${area} disabled reason must not be empty`)\n }\n return coverage\n}\n\nfunction coverageDecisions(coverage: PostHogCoverage): Array<[string, PostHogCoverageDecision]> {\n return [\n ['browser.analytics', coverage.browser.analytics],\n ['browser.errorTracking', coverage.browser.errorTracking],\n ['browser.featureFlags', coverage.browser.featureFlags],\n ['browser.identity', coverage.browser.identity],\n ['browser.sessionReplay', coverage.browser.sessionReplay],\n ['server.analytics', coverage.server.analytics],\n ['server.errorTracking', coverage.server.errorTracking],\n ['server.logs', coverage.server.logs],\n ['sourceMaps', coverage.sourceMaps],\n ]\n}\n"]}
1
+ {"version":3,"file":"coverage.js","sourceRoot":"","sources":["../../src/posthog/coverage.ts"],"names":[],"mappings":"AAsBA,MAAM,UAAU,qBAAqB,CAAC,QAAyB;IAC7D,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,IAAI,QAAQ,KAAK,IAAI;YAAE,SAAQ;QAC/B,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,OAAO,QAAQ,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACvF,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,+CAA+C,CAAC,CAAA;QACzE,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,oCAAoC,CAAC,CAAA;IAC7F,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAyB;IAClD,OAAO;QACL,CAAC,mBAAmB,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC;QACjD,CAAC,uBAAuB,EAAE,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC;QACzD,CAAC,sBAAsB,EAAE,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC;QACvD,CAAC,kBAAkB,EAAE,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC;QAC/C,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC,cAAc,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAA8C,CAAC,CAAC,CAAC,EAAE,CAAC;QACzH,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,CAAC,iBAAiB,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAA8C,CAAC,CAAC,CAAC,EAAE,CAAC;QAClI,CAAC,uBAAuB,EAAE,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC;QACzD,CAAC,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC;QAC/C,CAAC,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC;QACvD,CAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC;QACrC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,CAAC,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAA8C,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/H,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,CAAC,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAA8C,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/H,CAAC,YAAY,EAAE,QAAQ,CAAC,UAAU,CAAC;KACpC,CAAA;AACH,CAAC","sourcesContent":["export type PostHogCoverageDecision = true | { disabled: string }\n\nexport type PostHogCoverage = {\n browser: {\n analytics: PostHogCoverageDecision\n errorTracking: PostHogCoverageDecision\n featureFlags: PostHogCoverageDecision\n identity: PostHogCoverageDecision\n logs?: PostHogCoverageDecision\n metrics?: PostHogCoverageDecision\n sessionReplay: PostHogCoverageDecision\n }\n server: {\n analytics: PostHogCoverageDecision\n errorTracking: PostHogCoverageDecision\n logs: PostHogCoverageDecision\n metrics?: PostHogCoverageDecision\n tracing?: PostHogCoverageDecision\n }\n sourceMaps: PostHogCoverageDecision\n}\n\nexport function definePostHogCoverage(coverage: PostHogCoverage) {\n for (const [area, decision] of coverageDecisions(coverage)) {\n if (decision === true) continue\n if (!decision || typeof decision !== 'object' || typeof decision.disabled !== 'string') {\n throw new Error(`${area} must be enabled or include a disabled reason`)\n }\n if (!decision.disabled.trim()) throw new Error(`${area} disabled reason must not be empty`)\n }\n return coverage\n}\n\nfunction coverageDecisions(coverage: PostHogCoverage): Array<[string, PostHogCoverageDecision]> {\n return [\n ['browser.analytics', coverage.browser.analytics],\n ['browser.errorTracking', coverage.browser.errorTracking],\n ['browser.featureFlags', coverage.browser.featureFlags],\n ['browser.identity', coverage.browser.identity],\n ...(coverage.browser.logs ? ([['browser.logs', coverage.browser.logs]] as Array<[string, PostHogCoverageDecision]>) : []),\n ...(coverage.browser.metrics ? ([['browser.metrics', coverage.browser.metrics]] as Array<[string, PostHogCoverageDecision]>) : []),\n ['browser.sessionReplay', coverage.browser.sessionReplay],\n ['server.analytics', coverage.server.analytics],\n ['server.errorTracking', coverage.server.errorTracking],\n ['server.logs', coverage.server.logs],\n ...(coverage.server.metrics ? ([['server.metrics', coverage.server.metrics]] as Array<[string, PostHogCoverageDecision]>) : []),\n ...(coverage.server.tracing ? ([['server.tracing', coverage.server.tracing]] as Array<[string, PostHogCoverageDecision]>) : []),\n ['sourceMaps', coverage.sourceMaps],\n ]\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  export { postHogEnvironment, postHogHttpUrl } from './config.js';
2
- export type { PostHogEnvironment } from './config.js';
2
+ export type { PostHogBrowserService, PostHogEnvironment } from './config.js';
3
3
  export { definePostHogCoverage } from './coverage.js';
4
4
  export type { PostHogCoverage, PostHogCoverageDecision } from './coverage.js';
5
5
  export { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER, postHogRequestContext } from './request.js';
6
- export type { PostHogRequestContext } from './request.js';
6
+ export type { PostHogRequestContext, PostHogRequestContextOptions } from './request.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/posthog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEhE,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAErD,OAAO,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA","sourcesContent":["export { postHogEnvironment, postHogHttpUrl } from './config.js'\nexport type { PostHogEnvironment } from './config.js'\nexport { definePostHogCoverage } from './coverage.js'\nexport type { PostHogCoverage, PostHogCoverageDecision } from './coverage.js'\nexport { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER, postHogRequestContext } from './request.js'\nexport type { PostHogRequestContext } from './request.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/posthog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEhE,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAErD,OAAO,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA","sourcesContent":["export { postHogEnvironment, postHogHttpUrl } from './config.js'\nexport type { PostHogBrowserService, PostHogEnvironment } from './config.js'\nexport { definePostHogCoverage } from './coverage.js'\nexport type { PostHogCoverage, PostHogCoverageDecision } from './coverage.js'\nexport { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER, postHogRequestContext } from './request.js'\nexport type { PostHogRequestContext, PostHogRequestContextOptions } from './request.js'\n"]}
@@ -1,12 +1,14 @@
1
1
  import { type ReactNode } from 'react';
2
2
  import type { PostHogConfig, Properties } from 'posthog-js';
3
- import type { PostHogEnvironment } from './config.js';
4
- export declare function PostHogIntegration({ children, environment, fallback, ingestPath, options, }: {
3
+ import type { PostHogBrowserService, PostHogEnvironment } from './config.js';
4
+ export { usePostHog } from '@posthog/react';
5
+ export declare function PostHogIntegration({ children, environment, fallback, ingestPath, options, service, }: {
5
6
  children?: ReactNode;
6
7
  environment: PostHogEnvironment | undefined;
7
8
  fallback?: ReactNode;
8
9
  ingestPath?: string;
9
10
  options?: Partial<PostHogConfig>;
11
+ service?: PostHogBrowserService;
10
12
  }): string | number | bigint | boolean | import("react").FunctionComponentElement<({
11
13
  client: import("posthog-js").PostHog;
12
14
  apiKey?: never;
@@ -16,7 +18,7 @@ export declare function PostHogIntegration({ children, environment, fallback, in
16
18
  options?: Partial<PostHogConfig>;
17
19
  client?: never;
18
20
  }) & {
19
- children?: ReactNode | undefined;
21
+ children?: React.ReactNode | undefined;
20
22
  }> | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | null | undefined;
21
23
  export type BetterAuthUser = {
22
24
  id: string;
@@ -2,8 +2,9 @@ import { PostHogErrorBoundary, PostHogProvider, usePostHog } from '@posthog/reac
2
2
  import { createContext, createElement, useContext, useEffect, useRef, useState } from 'react';
3
3
  import { postHogBrowserOptions } from './client.js';
4
4
  import { POSTHOG_DEFAULT_INGEST_PATH } from './proxy.js';
5
+ export { usePostHog } from '@posthog/react';
5
6
  const PostHogLoadedContext = createContext(true);
6
- export function PostHogIntegration({ children, environment, fallback = createElement('main', null, 'Something went wrong. Refresh the page to try again.'), ingestPath = POSTHOG_DEFAULT_INGEST_PATH, options, }) {
7
+ export function PostHogIntegration({ children, environment, fallback = createElement('main', null, 'Something went wrong. Refresh the page to try again.'), ingestPath = POSTHOG_DEFAULT_INGEST_PATH, options, service, }) {
7
8
  const [isLoaded, setIsLoaded] = useState(false);
8
9
  const loadedRef = useRef(options?.loaded);
9
10
  loadedRef.current = options?.loaded;
@@ -16,6 +17,7 @@ export function PostHogIntegration({ children, environment, fallback = createEle
16
17
  apiHost: ingestPath,
17
18
  uiHost: environment.uiHost,
18
19
  ...(tracingHostnames ? { tracingHostnames } : {}),
20
+ ...(service ? { service } : {}),
19
21
  options: {
20
22
  ...options,
21
23
  loaded: (posthog) => {
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","sourceRoot":"","sources":["../../src/posthog/react.ts"],"names":[],"mappings":"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","sourcesContent":["import { PostHogErrorBoundary, PostHogProvider, usePostHog } from '@posthog/react'\nimport { createContext, createElement, type ReactNode, useContext, useEffect, useRef, useState } from 'react'\nimport type { PostHogConfig, Properties } from 'posthog-js'\nimport { postHogBrowserOptions } from './client.js'\nimport type { PostHogEnvironment } from './config.js'\nimport { POSTHOG_DEFAULT_INGEST_PATH } from './proxy.js'\n\nconst PostHogLoadedContext = createContext(true)\n\nexport function PostHogIntegration({\n children,\n environment,\n fallback = createElement('main', null, 'Something went wrong. Refresh the page to try again.'),\n ingestPath = POSTHOG_DEFAULT_INGEST_PATH,\n options,\n}: {\n children?: ReactNode\n environment: PostHogEnvironment | undefined\n fallback?: ReactNode\n ingestPath?: string\n options?: Partial<PostHogConfig>\n}) {\n const [isLoaded, setIsLoaded] = useState(false)\n const loadedRef = useRef(options?.loaded)\n loadedRef.current = options?.loaded\n if (!environment) return children\n const tracingHostnames = typeof window === 'undefined' ? undefined : [window.location.hostname]\n return createElement(\n PostHogProvider,\n {\n apiKey: environment.projectToken,\n options: postHogBrowserOptions({\n apiHost: ingestPath,\n uiHost: environment.uiHost,\n ...(tracingHostnames ? { tracingHostnames } : {}),\n options: {\n ...options,\n loaded: (posthog) => {\n setIsLoaded(true)\n loadedRef.current?.(posthog)\n },\n },\n }),\n },\n createElement(PostHogLoadedContext.Provider, { value: isLoaded }, createElement(PostHogErrorBoundary, { fallback }, children)),\n )\n}\n\nexport type BetterAuthUser = { id: string }\nexport type BetterAuthSession<User extends BetterAuthUser = BetterAuthUser> = { user: User }\nexport type BetterAuthSessionState<User extends BetterAuthUser = BetterAuthUser> = {\n data?: BetterAuthSession<User> | null\n error?: unknown\n isPending: boolean\n}\nexport type BetterAuthReactClient<User extends BetterAuthUser = BetterAuthUser> = {\n useSession: () => BetterAuthSessionState<User>\n}\n\nexport function PostHogBetterAuthIdentity<User extends BetterAuthUser>({\n authClient,\n properties,\n}: {\n authClient: BetterAuthReactClient<User>\n properties?: (user: User) => Properties\n}) {\n const session = authClient.useSession()\n const posthog = usePostHog()\n const isLoaded = useContext(PostHogLoadedContext)\n const isReady = isLoaded || posthog['__loaded']\n const identified = useRef<string | undefined>(undefined)\n const propertiesRef = useRef(properties)\n propertiesRef.current = properties\n\n useEffect(() => {\n if (!isReady || session.isPending || session.error) return\n const user = session.data?.user\n const persistedUserId = posthog.get_property('$user_id')\n if (user) {\n if (persistedUserId && persistedUserId !== user.id) posthog.reset()\n posthog.identify(user.id, propertiesRef.current?.(user))\n identified.current = user.id\n } else if (identified.current || persistedUserId) {\n posthog.reset()\n identified.current = undefined\n }\n }, [isReady, posthog, session.data?.user, session.error, session.isPending])\n\n return null\n}\n"]}
1
+ {"version":3,"file":"react.js","sourceRoot":"","sources":["../../src/posthog/react.ts"],"names":[],"mappings":"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","sourcesContent":["import { PostHogErrorBoundary, PostHogProvider, usePostHog } from '@posthog/react'\nimport { createContext, createElement, type ReactNode, useContext, useEffect, useRef, useState } from 'react'\nimport type { PostHogConfig, Properties } from 'posthog-js'\nimport { postHogBrowserOptions } from './client.js'\nimport type { PostHogBrowserService, PostHogEnvironment } from './config.js'\nimport { POSTHOG_DEFAULT_INGEST_PATH } from './proxy.js'\n\nexport { usePostHog } from '@posthog/react'\n\nconst PostHogLoadedContext = createContext(true)\n\nexport function PostHogIntegration({\n children,\n environment,\n fallback = createElement('main', null, 'Something went wrong. Refresh the page to try again.'),\n ingestPath = POSTHOG_DEFAULT_INGEST_PATH,\n options,\n service,\n}: {\n children?: ReactNode\n environment: PostHogEnvironment | undefined\n fallback?: ReactNode\n ingestPath?: string\n options?: Partial<PostHogConfig>\n service?: PostHogBrowserService\n}) {\n const [isLoaded, setIsLoaded] = useState(false)\n const loadedRef = useRef(options?.loaded)\n loadedRef.current = options?.loaded\n if (!environment) return children\n const tracingHostnames = typeof window === 'undefined' ? undefined : [window.location.hostname]\n return createElement(\n PostHogProvider,\n {\n apiKey: environment.projectToken,\n options: postHogBrowserOptions({\n apiHost: ingestPath,\n uiHost: environment.uiHost,\n ...(tracingHostnames ? { tracingHostnames } : {}),\n ...(service ? { service } : {}),\n options: {\n ...options,\n loaded: (posthog) => {\n setIsLoaded(true)\n loadedRef.current?.(posthog)\n },\n },\n }),\n },\n createElement(PostHogLoadedContext.Provider, { value: isLoaded }, createElement(PostHogErrorBoundary, { fallback }, children)),\n )\n}\n\nexport type BetterAuthUser = { id: string }\nexport type BetterAuthSession<User extends BetterAuthUser = BetterAuthUser> = { user: User }\nexport type BetterAuthSessionState<User extends BetterAuthUser = BetterAuthUser> = {\n data?: BetterAuthSession<User> | null\n error?: unknown\n isPending: boolean\n}\nexport type BetterAuthReactClient<User extends BetterAuthUser = BetterAuthUser> = {\n useSession: () => BetterAuthSessionState<User>\n}\n\nexport function PostHogBetterAuthIdentity<User extends BetterAuthUser>({\n authClient,\n properties,\n}: {\n authClient: BetterAuthReactClient<User>\n properties?: (user: User) => Properties\n}) {\n const session = authClient.useSession()\n const posthog = usePostHog()\n const isLoaded = useContext(PostHogLoadedContext)\n const isReady = isLoaded || posthog['__loaded']\n const identified = useRef<string | undefined>(undefined)\n const propertiesRef = useRef(properties)\n propertiesRef.current = properties\n\n useEffect(() => {\n if (!isReady || session.isPending || session.error) return\n const user = session.data?.user\n const persistedUserId = posthog.get_property('$user_id')\n if (user) {\n if (persistedUserId && persistedUserId !== user.id) posthog.reset()\n posthog.identify(user.id, propertiesRef.current?.(user))\n identified.current = user.id\n } else if (identified.current || persistedUserId) {\n posthog.reset()\n identified.current = undefined\n }\n }, [isReady, posthog, session.data?.user, session.error, session.isPending])\n\n return null\n}\n"]}
@@ -7,8 +7,9 @@ export type PostHogRequestContext = {
7
7
  $session_id?: string;
8
8
  };
9
9
  };
10
- export declare function postHogRequestContext(request: Request, options?: {
10
+ export type PostHogRequestContextOptions = {
11
11
  authenticatedDistinctId?: string;
12
12
  allowAnonymousDistinctId?: boolean;
13
- }): PostHogRequestContext;
13
+ };
14
+ export declare function postHogRequestContext(request: Request, options?: PostHogRequestContextOptions): PostHogRequestContext;
14
15
  export declare function postHogIdentifier(value: string | null | undefined): string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"request.js","sourceRoot":"","sources":["../../src/posthog/request.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,0BAA0B,GAAG,uBAAuB,CAAA;AACjE,MAAM,CAAC,MAAM,yBAAyB,GAAG,sBAAsB,CAAA;AAQ/D,MAAM,UAAU,qBAAqB,CACnC,OAAgB,EAChB,OAAO,GAA6E,EAAE;IAEtF,MAAM,iBAAiB,GAAG,iBAAiB,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC,CAAA;IAC5F,MAAM,SAAS,GAAG,iBAAiB,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,CAAA;IACnF,MAAM,UAAU,GAAG,OAAO,CAAC,uBAAuB;QAChD,CAAC,CAAC,iBAAiB,KAAK,OAAO,CAAC,uBAAuB;YACrD,CAAC,CAAC,iBAAiB;YACnB,CAAC,CAAC,SAAS;QACb,CAAC,CAAC,OAAO,CAAC,wBAAwB;YAChC,CAAC,CAAC,iBAAiB;YACnB,CAAC,CAAC,SAAS,CAAA;IACf,OAAO;QACL,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnC,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE;KACxD,CAAA;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAgC;IAChE,OAAO,KAAK,IAAI,6BAA6B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AAC/E,CAAC","sourcesContent":["export const POSTHOG_DISTINCT_ID_HEADER = 'x-posthog-distinct-id'\nexport const POSTHOG_SESSION_ID_HEADER = 'x-posthog-session-id'\n\nexport type PostHogRequestContext = {\n distinctId?: string\n sessionId?: string\n properties: { $session_id?: string }\n}\n\nexport function postHogRequestContext(\n request: Request,\n options: { authenticatedDistinctId?: string; allowAnonymousDistinctId?: boolean } = {},\n): PostHogRequestContext {\n const claimedDistinctId = postHogIdentifier(request.headers.get(POSTHOG_DISTINCT_ID_HEADER))\n const sessionId = postHogIdentifier(request.headers.get(POSTHOG_SESSION_ID_HEADER))\n const distinctId = options.authenticatedDistinctId\n ? claimedDistinctId === options.authenticatedDistinctId\n ? claimedDistinctId\n : undefined\n : options.allowAnonymousDistinctId\n ? claimedDistinctId\n : undefined\n return {\n ...(distinctId ? { distinctId } : {}),\n ...(sessionId ? { sessionId } : {}),\n properties: sessionId ? { $session_id: sessionId } : {},\n }\n}\n\nexport function postHogIdentifier(value: string | null | undefined) {\n return value && /^[A-Za-z0-9._:@|+-]{1,128}$/.test(value) ? value : undefined\n}\n"]}
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../../src/posthog/request.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,0BAA0B,GAAG,uBAAuB,CAAA;AACjE,MAAM,CAAC,MAAM,yBAAyB,GAAG,sBAAsB,CAAA;AAa/D,MAAM,UAAU,qBAAqB,CAAC,OAAgB,EAAE,OAAO,GAAiC,EAAE;IAChG,MAAM,iBAAiB,GAAG,iBAAiB,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC,CAAA;IAC5F,MAAM,SAAS,GAAG,iBAAiB,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,CAAA;IACnF,MAAM,UAAU,GAAG,OAAO,CAAC,uBAAuB;QAChD,CAAC,CAAC,iBAAiB,KAAK,OAAO,CAAC,uBAAuB;YACrD,CAAC,CAAC,iBAAiB;YACnB,CAAC,CAAC,SAAS;QACb,CAAC,CAAC,OAAO,CAAC,wBAAwB;YAChC,CAAC,CAAC,iBAAiB;YACnB,CAAC,CAAC,SAAS,CAAA;IACf,OAAO;QACL,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnC,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE;KACxD,CAAA;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAgC;IAChE,OAAO,KAAK,IAAI,6BAA6B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AAC/E,CAAC","sourcesContent":["export const POSTHOG_DISTINCT_ID_HEADER = 'x-posthog-distinct-id'\nexport const POSTHOG_SESSION_ID_HEADER = 'x-posthog-session-id'\n\nexport type PostHogRequestContext = {\n distinctId?: string\n sessionId?: string\n properties: { $session_id?: string }\n}\n\nexport type PostHogRequestContextOptions = {\n authenticatedDistinctId?: string\n allowAnonymousDistinctId?: boolean\n}\n\nexport function postHogRequestContext(request: Request, options: PostHogRequestContextOptions = {}): PostHogRequestContext {\n const claimedDistinctId = postHogIdentifier(request.headers.get(POSTHOG_DISTINCT_ID_HEADER))\n const sessionId = postHogIdentifier(request.headers.get(POSTHOG_SESSION_ID_HEADER))\n const distinctId = options.authenticatedDistinctId\n ? claimedDistinctId === options.authenticatedDistinctId\n ? claimedDistinctId\n : undefined\n : options.allowAnonymousDistinctId\n ? claimedDistinctId\n : undefined\n return {\n ...(distinctId ? { distinctId } : {}),\n ...(sessionId ? { sessionId } : {}),\n properties: sessionId ? { $session_id: sessionId } : {},\n }\n}\n\nexport function postHogIdentifier(value: string | null | undefined) {\n return value && /^[A-Za-z0-9._:@|+-]{1,128}$/.test(value) ? value : undefined\n}\n"]}
@@ -1,6 +1,7 @@
1
- import type { PostHog, PostHogOptions } from 'posthog-node';
1
+ import type { CaptureMetricOptions, PostHog, PostHogOptions, Span, StartSpanOptions } from 'posthog-node';
2
2
  import type { RpcErrorContext, RpcLogger } from '../server/rpc.js';
3
3
  import type { PostHogEnvironment } from './config.js';
4
+ import { type PostHogRequestContextOptions } from './request.js';
4
5
  export type PostHogLogValue = string | number | boolean | null | PostHogLogValue[];
5
6
  export type PostHogLogRecord = {
6
7
  body: string;
@@ -13,10 +14,17 @@ export type ManagedPostHogServerTelemetryOptions = {
13
14
  serviceName: string;
14
15
  serviceVersion?: string;
15
16
  deploymentEnvironment?: string;
17
+ resourceAttributes?: Record<string, string>;
16
18
  clientOptions?: Omit<PostHogOptions, 'host'>;
17
19
  onError?: (error: unknown) => void;
18
20
  };
19
21
  export type PostHogServerTelemetry = ReturnType<typeof createManagedPostHogServerTelemetry>;
22
+ export type PostHogTelemetryContext = {
23
+ distinctId?: string;
24
+ sessionId?: string;
25
+ properties?: Record<string, unknown>;
26
+ };
27
+ export type PostHogTraceSpan = Span | undefined;
20
28
  export type PostHogRpcLoggerOptions = {
21
29
  logError?: (error: unknown, context: RpcErrorContext) => void;
22
30
  resolveAuthenticatedDistinctId?: (request: Request) => string | undefined | Promise<string | undefined>;
@@ -32,10 +40,23 @@ type PostHogShutdownProcess = {
32
40
  export declare function createPostHogServerClient(environment: PostHogEnvironment | undefined, options?: Omit<PostHogOptions, 'host'>): Promise<PostHog | undefined>;
33
41
  export declare function shutdownPostHogServerClient(client: PostHog | undefined, timeoutMs?: number): Promise<void>;
34
42
  export declare function createManagedPostHogServerTelemetry(options: ManagedPostHogServerTelemetryOptions): {
43
+ metrics: {
44
+ count(name: string, value?: number, metricOptions?: CaptureMetricOptions): Promise<void>;
45
+ gauge(name: string, value: number, metricOptions?: CaptureMetricOptions): Promise<void>;
46
+ histogram(name: string, value: number, metricOptions?: CaptureMetricOptions): Promise<void>;
47
+ };
35
48
  start(): Promise<void>;
36
49
  capture(distinctId: string, event: string, properties?: Record<string, unknown>): Promise<void>;
37
50
  exception(error: unknown, distinctId?: string, properties?: Record<string, unknown>): Promise<void>;
38
51
  log(record: PostHogLogRecord): Promise<void>;
52
+ flush(): Promise<void>;
53
+ startSpan(name: string, spanOptions?: StartSpanOptions): Promise<Span | undefined>;
54
+ withContext: <T>(telemetryContext: PostHogTelemetryContext, work: () => T) => Promise<Awaited<T>>;
55
+ withRequestContext: <T>(request: Request, requestOptions: PostHogRequestContextOptions, work: () => T) => Promise<Awaited<T>>;
56
+ withSpan: {
57
+ <T>(name: string, work: (span: PostHogTraceSpan) => T): Promise<Awaited<T>>;
58
+ <T>(name: string, spanOptions: StartSpanOptions, work: (span: PostHogTraceSpan) => T): Promise<Awaited<T>>;
59
+ };
39
60
  shutdown(timeoutMs?: number): Promise<void>;
40
61
  };
41
62
  export declare function createPostHogRpcLogger(telemetry: PostHogServerTelemetry, options?: PostHogRpcLoggerOptions): RpcLogger;
@@ -1,3 +1,4 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
1
2
  import { postHogRequestContext } from './request.js';
2
3
  export async function createPostHogServerClient(environment, options = {}) {
3
4
  if (!environment)
@@ -18,6 +19,7 @@ export function createManagedPostHogServerTelemetry(options) {
18
19
  let client;
19
20
  let logProvider;
20
21
  let closed = false;
22
+ const context = new AsyncLocalStorage();
21
23
  const report = (error) => {
22
24
  try {
23
25
  options.onError?.(error);
@@ -28,7 +30,7 @@ export function createManagedPostHogServerTelemetry(options) {
28
30
  if (closed || !options.environment)
29
31
  return undefined;
30
32
  if (!client) {
31
- const pending = createPostHogServerClient(options.environment, options.clientOptions);
33
+ const pending = createPostHogServerClient(options.environment, managedClientOptions(options));
32
34
  client = pending;
33
35
  void pending.catch(() => {
34
36
  if (client === pending)
@@ -60,7 +62,63 @@ export function createManagedPostHogServerTelemetry(options) {
60
62
  report(error);
61
63
  }
62
64
  };
65
+ const loadClient = async () => {
66
+ try {
67
+ return await getClient();
68
+ }
69
+ catch (error) {
70
+ report(error);
71
+ return undefined;
72
+ }
73
+ };
74
+ async function withContext(telemetryContext, work) {
75
+ const inherited = context.getStore();
76
+ const correlation = {
77
+ ...(inherited?.distinctId ? { distinctId: inherited.distinctId } : {}),
78
+ ...(inherited?.sessionId ? { sessionId: inherited.sessionId } : {}),
79
+ ...telemetryContext,
80
+ };
81
+ const requestContext = {
82
+ ...(correlation.distinctId ? { distinctId: correlation.distinctId } : {}),
83
+ ...(correlation.sessionId ? { sessionId: correlation.sessionId } : {}),
84
+ properties: {
85
+ ...inherited?.properties,
86
+ ...telemetryContext.properties,
87
+ ...(correlation.sessionId ? { $session_id: correlation.sessionId } : {}),
88
+ },
89
+ };
90
+ const run = () => context.run(requestContext, work);
91
+ const value = await loadClient();
92
+ return (value ? value.withContext(telemetryContext, run) : run());
93
+ }
94
+ async function withRequestContext(request, requestOptions, work) {
95
+ const correlation = postHogRequestContext(request, requestOptions);
96
+ return withContext({
97
+ ...(correlation.distinctId ? { distinctId: correlation.distinctId } : {}),
98
+ ...(correlation.sessionId ? { sessionId: correlation.sessionId } : {}),
99
+ }, work);
100
+ }
101
+ async function withSpan(name, optionsOrWork, maybeWork) {
102
+ const spanOptions = typeof optionsOrWork === 'function' ? undefined : optionsOrWork;
103
+ const work = typeof optionsOrWork === 'function' ? optionsOrWork : maybeWork;
104
+ const value = await loadClient();
105
+ if (!value)
106
+ return work(undefined);
107
+ return (spanOptions ? value.withSpan(name, spanOptions, work) : value.withSpan(name, work));
108
+ }
109
+ const metrics = {
110
+ count(name, value = 1, metricOptions) {
111
+ return safely(async () => (await getClient())?.metrics.count(name, value, metricOptions));
112
+ },
113
+ gauge(name, value, metricOptions) {
114
+ return safely(async () => (await getClient())?.metrics.gauge(name, value, metricOptions));
115
+ },
116
+ histogram(name, value, metricOptions) {
117
+ return safely(async () => (await getClient())?.metrics.histogram(name, value, metricOptions));
118
+ },
119
+ };
63
120
  return {
121
+ metrics,
64
122
  async start() {
65
123
  await safely(async () => {
66
124
  await Promise.all([getClient(), getLogProvider()]);
@@ -72,16 +130,22 @@ export function createManagedPostHogServerTelemetry(options) {
72
130
  value?.capture({ distinctId, event, ...(properties ? { properties } : {}) });
73
131
  });
74
132
  },
75
- async exception(error, distinctId = 'server', properties) {
133
+ async exception(error, distinctId, properties) {
76
134
  await safely(async () => {
77
135
  const value = await getClient();
78
- value?.captureException(error, distinctId, properties);
136
+ const correlation = context.getStore();
137
+ const capturedProperties = mergeProperties(properties, correlation?.properties);
138
+ value?.captureException(error, distinctId ?? correlation?.distinctId ?? 'server', capturedProperties);
79
139
  });
80
140
  },
81
141
  async log(record) {
82
142
  await safely(async () => {
83
143
  const provider = await getLogProvider();
84
- const attributes = boundedAttributes(record.attributes);
144
+ const correlation = context.getStore();
145
+ const attributes = boundedAttributes(mergeProperties(record.attributes, {
146
+ ...(correlation?.distinctId ? { posthogDistinctId: correlation.distinctId } : {}),
147
+ ...(correlation?.sessionId ? { sessionId: correlation.sessionId } : {}),
148
+ }));
85
149
  provider?.getLogger(options.serviceName).emit({
86
150
  body: boundedString(record.body),
87
151
  ...(record.severityText ? { severityText: record.severityText } : {}),
@@ -90,6 +154,18 @@ export function createManagedPostHogServerTelemetry(options) {
90
154
  });
91
155
  });
92
156
  },
157
+ async flush() {
158
+ await safely(async () => {
159
+ const [value, provider] = await Promise.all([getClient(), getLogProvider()]);
160
+ await Promise.all([value?.flush(), provider?.forceFlush()]);
161
+ });
162
+ },
163
+ async startSpan(name, spanOptions) {
164
+ return (await loadClient())?.startSpan(name, spanOptions);
165
+ },
166
+ withContext,
167
+ withRequestContext,
168
+ withSpan,
93
169
  async shutdown(timeoutMs = 10_000) {
94
170
  if (closed)
95
171
  return;
@@ -105,6 +181,20 @@ export function createManagedPostHogServerTelemetry(options) {
105
181
  },
106
182
  };
107
183
  }
184
+ function managedClientOptions(options) {
185
+ const { metrics, traces, ...clientOptions } = options.clientOptions ?? {};
186
+ const service = {
187
+ serviceName: options.serviceName,
188
+ ...(options.serviceVersion ? { serviceVersion: options.serviceVersion } : {}),
189
+ ...(options.deploymentEnvironment ? { environment: options.deploymentEnvironment } : {}),
190
+ ...(options.resourceAttributes ? { resourceAttributes: options.resourceAttributes } : {}),
191
+ };
192
+ return {
193
+ ...clientOptions,
194
+ metrics: { ...service, ...metrics },
195
+ traces: { ...service, ...traces },
196
+ };
197
+ }
108
198
  export function createPostHogRpcLogger(telemetry, options = {}) {
109
199
  return async (error, context, request) => {
110
200
  try {
@@ -183,10 +273,15 @@ async function createPostHogLogProvider(options) {
183
273
  'service.name': options.serviceName,
184
274
  ...(options.serviceVersion ? { 'service.version': options.serviceVersion } : {}),
185
275
  ...(options.deploymentEnvironment ? { 'deployment.environment': options.deploymentEnvironment } : {}),
276
+ ...options.resourceAttributes,
186
277
  }),
187
278
  processors: [new BatchLogRecordProcessor({ exporter })],
188
279
  });
189
280
  }
281
+ function mergeProperties(...values) {
282
+ const merged = Object.assign({}, ...values.filter(Boolean));
283
+ return Object.keys(merged).length ? merged : undefined;
284
+ }
190
285
  function boundedAttributes(attributes) {
191
286
  if (!attributes)
192
287
  return undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/posthog/server.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PostHog, PostHogOptions } from 'posthog-node'\nimport type { RpcErrorContext, RpcLogger } from '../server/rpc.js'\nimport type { PostHogEnvironment } from './config.js'\nimport { postHogRequestContext } from './request.js'\n\ntype LogProvider = InstanceType<(typeof import('@opentelemetry/sdk-logs'))['LoggerProvider']>\n\nexport type PostHogLogValue = string | number | boolean | null | PostHogLogValue[]\n\nexport type PostHogLogRecord = {\n body: string\n severityText?: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'\n timestamp?: number\n attributes?: Record<string, unknown>\n}\n\nexport type ManagedPostHogServerTelemetryOptions = {\n environment: PostHogEnvironment | undefined\n serviceName: string\n serviceVersion?: string\n deploymentEnvironment?: string\n clientOptions?: Omit<PostHogOptions, 'host'>\n onError?: (error: unknown) => void\n}\n\nexport type PostHogServerTelemetry = ReturnType<typeof createManagedPostHogServerTelemetry>\n\nexport type PostHogRpcLoggerOptions = {\n logError?: (error: unknown, context: RpcErrorContext) => void\n resolveAuthenticatedDistinctId?: (request: Request) => string | undefined | Promise<string | undefined>\n allowAnonymousDistinctId?: boolean\n fallbackDistinctId?: string\n}\n\ntype PostHogShutdownProcess = {\n pid: number\n on(signal: 'SIGINT' | 'SIGTERM', listener: () => void): void\n off(signal: 'SIGINT' | 'SIGTERM', listener: () => void): void\n kill(pid: number, signal: 'SIGINT' | 'SIGTERM'): void\n}\n\nexport async function createPostHogServerClient(\n environment: PostHogEnvironment | undefined,\n options: Omit<PostHogOptions, 'host'> = {},\n): Promise<PostHog | undefined> {\n if (!environment) return undefined\n const { PostHog } = await import('posthog-node')\n return new PostHog(environment.projectToken, {\n host: environment.host,\n enableExceptionAutocapture: true,\n ...options,\n })\n}\n\nexport async function shutdownPostHogServerClient(client: PostHog | undefined, timeoutMs = 10_000) {\n assertShutdownTimeout(timeoutMs)\n // oxlint-disable-next-line no-underscore-dangle -- posthog-node's async shutdown API is named `_shutdown`.\n await client?._shutdown(timeoutMs)\n}\n\nexport function createManagedPostHogServerTelemetry(options: ManagedPostHogServerTelemetryOptions) {\n let client: Promise<PostHog | undefined> | undefined\n let logProvider: Promise<LogProvider | undefined> | undefined\n let closed = false\n\n const report = (error: unknown) => {\n try {\n options.onError?.(error)\n } catch {}\n }\n\n const getClient = () => {\n if (closed || !options.environment) return undefined\n if (!client) {\n const pending = createPostHogServerClient(options.environment, options.clientOptions)\n client = pending\n void pending.catch(() => {\n if (client === pending) client = undefined\n })\n }\n return client\n }\n\n const getLogProvider = () => {\n if (closed || !options.environment) return undefined\n if (!logProvider) {\n const pending = createPostHogLogProvider(options)\n logProvider = pending\n void pending.catch(() => {\n if (logProvider === pending) logProvider = undefined\n })\n }\n return logProvider\n }\n\n const safely = async (work: () => void | Promise<void>) => {\n if (closed || !options.environment) return\n try {\n await work()\n } catch (error) {\n report(error)\n }\n }\n\n return {\n async start() {\n await safely(async () => {\n await Promise.all([getClient(), getLogProvider()])\n })\n },\n async capture(distinctId: string, event: string, properties?: Record<string, unknown>) {\n await safely(async () => {\n const value = await getClient()\n value?.capture({ distinctId, event, ...(properties ? { properties } : {}) })\n })\n },\n async exception(error: unknown, distinctId = 'server', properties?: Record<string, unknown>) {\n await safely(async () => {\n const value = await getClient()\n value?.captureException(error, distinctId, properties)\n })\n },\n async log(record: PostHogLogRecord) {\n await safely(async () => {\n const provider = await getLogProvider()\n const attributes = boundedAttributes(record.attributes)\n provider?.getLogger(options.serviceName).emit({\n body: boundedString(record.body),\n ...(record.severityText ? { severityText: record.severityText } : {}),\n ...(record.timestamp === undefined ? {} : { timestamp: record.timestamp }),\n ...(attributes ? { attributes } : {}),\n })\n })\n },\n async shutdown(timeoutMs = 10_000) {\n if (closed) return\n assertShutdownTimeout(timeoutMs)\n closed = true\n const results = await Promise.allSettled([\n client?.then((value) => shutdownPostHogServerClient(value, timeoutMs)),\n logProvider?.then((value) => shutdownPostHogLogProvider(value, timeoutMs)),\n ])\n for (const result of results) if (result.status === 'rejected') report(result.reason)\n },\n }\n}\n\nexport function createPostHogRpcLogger(telemetry: PostHogServerTelemetry, options: PostHogRpcLoggerOptions = {}): RpcLogger {\n return async (error, context, request) => {\n try {\n options.logError?.(error, context)\n } catch {}\n let authenticatedDistinctId: string | undefined\n if (request && options.resolveAuthenticatedDistinctId) {\n try {\n authenticatedDistinctId = await options.resolveAuthenticatedDistinctId(request)\n } catch {}\n }\n const correlation = request\n ? postHogRequestContext(request, {\n ...(authenticatedDistinctId ? { authenticatedDistinctId } : {}),\n ...(options.allowAnonymousDistinctId === undefined ? {} : { allowAnonymousDistinctId: options.allowAnonymousDistinctId }),\n })\n : { properties: {} }\n const distinctId = correlation.distinctId ?? options.fallbackDistinctId ?? 'server'\n const properties = {\n ...correlation.properties,\n ...(context.method ? { request_method: context.method } : {}),\n ...(context.path ? { request_path: context.path } : {}),\n }\n await Promise.all([\n telemetry.exception(error, distinctId, properties),\n telemetry.log({\n body: 'server function failed',\n severityText: 'error',\n attributes: { ...properties, posthogDistinctId: distinctId },\n }),\n ])\n }\n}\n\nexport function installPostHogServerTelemetryShutdown(\n telemetry: Pick<PostHogServerTelemetry, 'shutdown'>,\n target: PostHogShutdownProcess = process,\n) {\n let shuttingDown = false\n const listeners = new Map<'SIGINT' | 'SIGTERM', () => void>()\n const remove = () => {\n for (const [signal, listener] of listeners) target.off(signal, listener)\n listeners.clear()\n }\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n const listener = () => {\n if (shuttingDown) return\n shuttingDown = true\n void telemetry\n .shutdown()\n .catch(() => undefined)\n .finally(() => {\n remove()\n target.kill(target.pid, signal)\n })\n }\n listeners.set(signal, listener)\n target.on(signal, listener)\n }\n return remove\n}\n\nasync function createPostHogLogProvider(options: ManagedPostHogServerTelemetryOptions): Promise<LogProvider | undefined> {\n if (!options.environment) return undefined\n const [{ OTLPLogExporter }, { resourceFromAttributes }, { BatchLogRecordProcessor, LoggerProvider }] = await Promise.all([\n import('@opentelemetry/exporter-logs-otlp-http'),\n import('@opentelemetry/resources'),\n import('@opentelemetry/sdk-logs'),\n ])\n const exporter = new OTLPLogExporter({\n url: `${options.environment.host.replace(/\\/$/, '')}/i/v1/logs`,\n headers: { Authorization: `Bearer ${options.environment.projectToken}` },\n })\n return new LoggerProvider({\n resource: resourceFromAttributes({\n 'service.name': options.serviceName,\n ...(options.serviceVersion ? { 'service.version': options.serviceVersion } : {}),\n ...(options.deploymentEnvironment ? { 'deployment.environment': options.deploymentEnvironment } : {}),\n }),\n processors: [new BatchLogRecordProcessor({ exporter })],\n })\n}\n\nfunction boundedAttributes(attributes: Record<string, unknown> | undefined) {\n if (!attributes) return undefined\n return Object.fromEntries(\n Object.entries(attributes)\n .slice(0, 64)\n .map(([key, value]) => [key.slice(0, 128), boundedLogValue(value, 0)])\n .filter((entry): entry is [string, PostHogLogValue] => entry[1] !== undefined),\n )\n}\n\nfunction boundedLogValue(value: unknown, depth: number): PostHogLogValue | undefined {\n if (value === null || typeof value === 'number' || typeof value === 'boolean') return value\n if (typeof value === 'string') return boundedString(value)\n if (depth >= 3) return '[Truncated]'\n if (Array.isArray(value)) {\n return value\n .slice(0, 32)\n .map((entry) => boundedLogValue(entry, depth + 1))\n .filter((entry): entry is PostHogLogValue => entry !== undefined)\n }\n if (value === undefined) return undefined\n try {\n return boundedString(JSON.stringify(value))\n } catch {\n return '[Unserializable value]'\n }\n}\n\nfunction boundedString(value: string) {\n return value.slice(0, 2_048)\n}\n\nasync function shutdownPostHogLogProvider(provider: LogProvider | undefined, timeoutMs: number) {\n if (!provider) return\n let timeout: ReturnType<typeof setTimeout> | undefined\n try {\n await Promise.race([\n provider.shutdown(),\n new Promise<never>((_resolve, reject) => {\n timeout = setTimeout(() => reject(new Error(`PostHog log shutdown timed out after ${timeoutMs}ms`)), timeoutMs)\n timeout.unref?.()\n }),\n ])\n } finally {\n if (timeout) clearTimeout(timeout)\n }\n}\n\nfunction assertShutdownTimeout(timeoutMs: number) {\n if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0) throw new Error('timeoutMs must be a non-negative integer')\n}\n"]}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/posthog/server.ts"],"names":[],"mappings":"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","sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks'\nimport type { CaptureMetricOptions, PostHog, PostHogOptions, Span, StartSpanOptions } from 'posthog-node'\nimport type { RpcErrorContext, RpcLogger } from '../server/rpc.js'\nimport type { PostHogEnvironment } from './config.js'\nimport { postHogRequestContext, type PostHogRequestContextOptions } from './request.js'\n\ntype LogProvider = InstanceType<(typeof import('@opentelemetry/sdk-logs'))['LoggerProvider']>\n\nexport type PostHogLogValue = string | number | boolean | null | PostHogLogValue[]\n\nexport type PostHogLogRecord = {\n body: string\n severityText?: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'\n timestamp?: number\n attributes?: Record<string, unknown>\n}\n\nexport type ManagedPostHogServerTelemetryOptions = {\n environment: PostHogEnvironment | undefined\n serviceName: string\n serviceVersion?: string\n deploymentEnvironment?: string\n resourceAttributes?: Record<string, string>\n clientOptions?: Omit<PostHogOptions, 'host'>\n onError?: (error: unknown) => void\n}\n\nexport type PostHogServerTelemetry = ReturnType<typeof createManagedPostHogServerTelemetry>\n\nexport type PostHogTelemetryContext = {\n distinctId?: string\n sessionId?: string\n properties?: Record<string, unknown>\n}\n\nexport type PostHogTraceSpan = Span | undefined\n\nexport type PostHogRpcLoggerOptions = {\n logError?: (error: unknown, context: RpcErrorContext) => void\n resolveAuthenticatedDistinctId?: (request: Request) => string | undefined | Promise<string | undefined>\n allowAnonymousDistinctId?: boolean\n fallbackDistinctId?: string\n}\n\ntype PostHogShutdownProcess = {\n pid: number\n on(signal: 'SIGINT' | 'SIGTERM', listener: () => void): void\n off(signal: 'SIGINT' | 'SIGTERM', listener: () => void): void\n kill(pid: number, signal: 'SIGINT' | 'SIGTERM'): void\n}\n\nexport async function createPostHogServerClient(\n environment: PostHogEnvironment | undefined,\n options: Omit<PostHogOptions, 'host'> = {},\n): Promise<PostHog | undefined> {\n if (!environment) return undefined\n const { PostHog } = await import('posthog-node')\n return new PostHog(environment.projectToken, {\n host: environment.host,\n enableExceptionAutocapture: true,\n ...options,\n })\n}\n\nexport async function shutdownPostHogServerClient(client: PostHog | undefined, timeoutMs = 10_000) {\n assertShutdownTimeout(timeoutMs)\n // oxlint-disable-next-line no-underscore-dangle -- posthog-node's async shutdown API is named `_shutdown`.\n await client?._shutdown(timeoutMs)\n}\n\nexport function createManagedPostHogServerTelemetry(options: ManagedPostHogServerTelemetryOptions) {\n let client: Promise<PostHog | undefined> | undefined\n let logProvider: Promise<LogProvider | undefined> | undefined\n let closed = false\n const context = new AsyncLocalStorage<PostHogTelemetryContext>()\n\n const report = (error: unknown) => {\n try {\n options.onError?.(error)\n } catch {}\n }\n\n const getClient = () => {\n if (closed || !options.environment) return undefined\n if (!client) {\n const pending = createPostHogServerClient(options.environment, managedClientOptions(options))\n client = pending\n void pending.catch(() => {\n if (client === pending) client = undefined\n })\n }\n return client\n }\n\n const getLogProvider = () => {\n if (closed || !options.environment) return undefined\n if (!logProvider) {\n const pending = createPostHogLogProvider(options)\n logProvider = pending\n void pending.catch(() => {\n if (logProvider === pending) logProvider = undefined\n })\n }\n return logProvider\n }\n\n const safely = async (work: () => void | Promise<void>) => {\n if (closed || !options.environment) return\n try {\n await work()\n } catch (error) {\n report(error)\n }\n }\n\n const loadClient = async () => {\n try {\n return await getClient()\n } catch (error) {\n report(error)\n return undefined\n }\n }\n\n async function withContext<T>(telemetryContext: PostHogTelemetryContext, work: () => T): Promise<Awaited<T>> {\n const inherited = context.getStore()\n const correlation = {\n ...(inherited?.distinctId ? { distinctId: inherited.distinctId } : {}),\n ...(inherited?.sessionId ? { sessionId: inherited.sessionId } : {}),\n ...telemetryContext,\n }\n const requestContext: PostHogTelemetryContext = {\n ...(correlation.distinctId ? { distinctId: correlation.distinctId } : {}),\n ...(correlation.sessionId ? { sessionId: correlation.sessionId } : {}),\n properties: {\n ...inherited?.properties,\n ...telemetryContext.properties,\n ...(correlation.sessionId ? { $session_id: correlation.sessionId } : {}),\n },\n }\n const run = () => context.run(requestContext, work)\n const value = await loadClient()\n return (value ? value.withContext(telemetryContext, run) : run()) as Awaited<T>\n }\n\n async function withRequestContext<T>(request: Request, requestOptions: PostHogRequestContextOptions, work: () => T): Promise<Awaited<T>> {\n const correlation = postHogRequestContext(request, requestOptions)\n return withContext(\n {\n ...(correlation.distinctId ? { distinctId: correlation.distinctId } : {}),\n ...(correlation.sessionId ? { sessionId: correlation.sessionId } : {}),\n },\n work,\n )\n }\n\n async function withSpan<T>(name: string, work: (span: PostHogTraceSpan) => T): Promise<Awaited<T>>\n async function withSpan<T>(name: string, spanOptions: StartSpanOptions, work: (span: PostHogTraceSpan) => T): Promise<Awaited<T>>\n async function withSpan<T>(\n name: string,\n optionsOrWork: StartSpanOptions | ((span: PostHogTraceSpan) => T),\n maybeWork?: (span: PostHogTraceSpan) => T,\n ): Promise<Awaited<T>> {\n const spanOptions = typeof optionsOrWork === 'function' ? undefined : optionsOrWork\n const work = typeof optionsOrWork === 'function' ? optionsOrWork : maybeWork!\n const value = await loadClient()\n if (!value) return work(undefined) as Awaited<T>\n return (spanOptions ? value.withSpan(name, spanOptions, work) : value.withSpan(name, work)) as Awaited<T>\n }\n\n const metrics = {\n count(name: string, value = 1, metricOptions?: CaptureMetricOptions) {\n return safely(async () => (await getClient())?.metrics.count(name, value, metricOptions))\n },\n gauge(name: string, value: number, metricOptions?: CaptureMetricOptions) {\n return safely(async () => (await getClient())?.metrics.gauge(name, value, metricOptions))\n },\n histogram(name: string, value: number, metricOptions?: CaptureMetricOptions) {\n return safely(async () => (await getClient())?.metrics.histogram(name, value, metricOptions))\n },\n }\n\n return {\n metrics,\n async start() {\n await safely(async () => {\n await Promise.all([getClient(), getLogProvider()])\n })\n },\n async capture(distinctId: string, event: string, properties?: Record<string, unknown>) {\n await safely(async () => {\n const value = await getClient()\n value?.capture({ distinctId, event, ...(properties ? { properties } : {}) })\n })\n },\n async exception(error: unknown, distinctId?: string, properties?: Record<string, unknown>) {\n await safely(async () => {\n const value = await getClient()\n const correlation = context.getStore()\n const capturedProperties = mergeProperties(properties, correlation?.properties)\n value?.captureException(error, distinctId ?? correlation?.distinctId ?? 'server', capturedProperties)\n })\n },\n async log(record: PostHogLogRecord) {\n await safely(async () => {\n const provider = await getLogProvider()\n const correlation = context.getStore()\n const attributes = boundedAttributes(\n mergeProperties(record.attributes, {\n ...(correlation?.distinctId ? { posthogDistinctId: correlation.distinctId } : {}),\n ...(correlation?.sessionId ? { sessionId: correlation.sessionId } : {}),\n }),\n )\n provider?.getLogger(options.serviceName).emit({\n body: boundedString(record.body),\n ...(record.severityText ? { severityText: record.severityText } : {}),\n ...(record.timestamp === undefined ? {} : { timestamp: record.timestamp }),\n ...(attributes ? { attributes } : {}),\n })\n })\n },\n async flush() {\n await safely(async () => {\n const [value, provider] = await Promise.all([getClient(), getLogProvider()])\n await Promise.all([value?.flush(), provider?.forceFlush()])\n })\n },\n async startSpan(name: string, spanOptions?: StartSpanOptions) {\n return (await loadClient())?.startSpan(name, spanOptions)\n },\n withContext,\n withRequestContext,\n withSpan,\n async shutdown(timeoutMs = 10_000) {\n if (closed) return\n assertShutdownTimeout(timeoutMs)\n closed = true\n const results = await Promise.allSettled([\n client?.then((value) => shutdownPostHogServerClient(value, timeoutMs)),\n logProvider?.then((value) => shutdownPostHogLogProvider(value, timeoutMs)),\n ])\n for (const result of results) if (result.status === 'rejected') report(result.reason)\n },\n }\n}\n\nfunction managedClientOptions(options: ManagedPostHogServerTelemetryOptions): Omit<PostHogOptions, 'host'> {\n const { metrics, traces, ...clientOptions } = options.clientOptions ?? {}\n const service = {\n serviceName: options.serviceName,\n ...(options.serviceVersion ? { serviceVersion: options.serviceVersion } : {}),\n ...(options.deploymentEnvironment ? { environment: options.deploymentEnvironment } : {}),\n ...(options.resourceAttributes ? { resourceAttributes: options.resourceAttributes } : {}),\n }\n return {\n ...clientOptions,\n metrics: { ...service, ...metrics },\n traces: { ...service, ...traces },\n }\n}\n\nexport function createPostHogRpcLogger(telemetry: PostHogServerTelemetry, options: PostHogRpcLoggerOptions = {}): RpcLogger {\n return async (error, context, request) => {\n try {\n options.logError?.(error, context)\n } catch {}\n let authenticatedDistinctId: string | undefined\n if (request && options.resolveAuthenticatedDistinctId) {\n try {\n authenticatedDistinctId = await options.resolveAuthenticatedDistinctId(request)\n } catch {}\n }\n const correlation = request\n ? postHogRequestContext(request, {\n ...(authenticatedDistinctId ? { authenticatedDistinctId } : {}),\n ...(options.allowAnonymousDistinctId === undefined ? {} : { allowAnonymousDistinctId: options.allowAnonymousDistinctId }),\n })\n : { properties: {} }\n const distinctId = correlation.distinctId ?? options.fallbackDistinctId ?? 'server'\n const properties = {\n ...correlation.properties,\n ...(context.method ? { request_method: context.method } : {}),\n ...(context.path ? { request_path: context.path } : {}),\n }\n await Promise.all([\n telemetry.exception(error, distinctId, properties),\n telemetry.log({\n body: 'server function failed',\n severityText: 'error',\n attributes: { ...properties, posthogDistinctId: distinctId },\n }),\n ])\n }\n}\n\nexport function installPostHogServerTelemetryShutdown(\n telemetry: Pick<PostHogServerTelemetry, 'shutdown'>,\n target: PostHogShutdownProcess = process,\n) {\n let shuttingDown = false\n const listeners = new Map<'SIGINT' | 'SIGTERM', () => void>()\n const remove = () => {\n for (const [signal, listener] of listeners) target.off(signal, listener)\n listeners.clear()\n }\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n const listener = () => {\n if (shuttingDown) return\n shuttingDown = true\n void telemetry\n .shutdown()\n .catch(() => undefined)\n .finally(() => {\n remove()\n target.kill(target.pid, signal)\n })\n }\n listeners.set(signal, listener)\n target.on(signal, listener)\n }\n return remove\n}\n\nasync function createPostHogLogProvider(options: ManagedPostHogServerTelemetryOptions): Promise<LogProvider | undefined> {\n if (!options.environment) return undefined\n const [{ OTLPLogExporter }, { resourceFromAttributes }, { BatchLogRecordProcessor, LoggerProvider }] = await Promise.all([\n import('@opentelemetry/exporter-logs-otlp-http'),\n import('@opentelemetry/resources'),\n import('@opentelemetry/sdk-logs'),\n ])\n const exporter = new OTLPLogExporter({\n url: `${options.environment.host.replace(/\\/$/, '')}/i/v1/logs`,\n headers: { Authorization: `Bearer ${options.environment.projectToken}` },\n })\n return new LoggerProvider({\n resource: resourceFromAttributes({\n 'service.name': options.serviceName,\n ...(options.serviceVersion ? { 'service.version': options.serviceVersion } : {}),\n ...(options.deploymentEnvironment ? { 'deployment.environment': options.deploymentEnvironment } : {}),\n ...options.resourceAttributes,\n }),\n processors: [new BatchLogRecordProcessor({ exporter })],\n })\n}\n\nfunction mergeProperties(...values: Array<Record<string, unknown> | undefined>) {\n const merged = Object.assign({}, ...values.filter(Boolean))\n return Object.keys(merged).length ? merged : undefined\n}\n\nfunction boundedAttributes(attributes: Record<string, unknown> | undefined) {\n if (!attributes) return undefined\n return Object.fromEntries(\n Object.entries(attributes)\n .slice(0, 64)\n .map(([key, value]) => [key.slice(0, 128), boundedLogValue(value, 0)])\n .filter((entry): entry is [string, PostHogLogValue] => entry[1] !== undefined),\n )\n}\n\nfunction boundedLogValue(value: unknown, depth: number): PostHogLogValue | undefined {\n if (value === null || typeof value === 'number' || typeof value === 'boolean') return value\n if (typeof value === 'string') return boundedString(value)\n if (depth >= 3) return '[Truncated]'\n if (Array.isArray(value)) {\n return value\n .slice(0, 32)\n .map((entry) => boundedLogValue(entry, depth + 1))\n .filter((entry): entry is PostHogLogValue => entry !== undefined)\n }\n if (value === undefined) return undefined\n try {\n return boundedString(JSON.stringify(value))\n } catch {\n return '[Unserializable value]'\n }\n}\n\nfunction boundedString(value: string) {\n return value.slice(0, 2_048)\n}\n\nasync function shutdownPostHogLogProvider(provider: LogProvider | undefined, timeoutMs: number) {\n if (!provider) return\n let timeout: ReturnType<typeof setTimeout> | undefined\n try {\n await Promise.race([\n provider.shutdown(),\n new Promise<never>((_resolve, reject) => {\n timeout = setTimeout(() => reject(new Error(`PostHog log shutdown timed out after ${timeoutMs}ms`)), timeoutMs)\n timeout.unref?.()\n }),\n ])\n } finally {\n if (timeout) clearTimeout(timeout)\n }\n}\n\nfunction assertShutdownTimeout(timeoutMs: number) {\n if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0) throw new Error('timeoutMs must be a non-negative integer')\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ras-stack",
3
- "version": "1.0.1",
3
+ "version": "1.1.1",
4
4
  "description": "Composable full-stack primitives shared across Richard Solomou's applications.",
5
5
  "keywords": [
6
6
  "authentication",
@@ -158,7 +158,7 @@
158
158
  "@opentelemetry/exporter-logs-otlp-http": "^0.221.0",
159
159
  "@opentelemetry/resources": "^2.10.0",
160
160
  "@opentelemetry/sdk-logs": "^0.221.0",
161
- "@posthog/react": "^1.10.3",
161
+ "@posthog/react": "1.10.6",
162
162
  "@tanstack/react-query": "^5.101.4",
163
163
  "@tanstack/react-start": "^1.168.32",
164
164
  "@types/better-sqlite3": "9.6.0",
@@ -175,8 +175,8 @@
175
175
  "oxlint": "^1.74.0",
176
176
  "oxlint-tsgolint": "^7.0.2001",
177
177
  "postgres": "3.4.9",
178
- "posthog-js": "^1.414.0",
179
- "posthog-node": "^5.48.1",
178
+ "posthog-js": "1.430.3",
179
+ "posthog-node": "5.52.1",
180
180
  "react": "19.2.8",
181
181
  "react-test-renderer": "19.2.8",
182
182
  "tus-js-client": "^4.3.1",
@@ -197,8 +197,8 @@
197
197
  "drizzle-orm": ">=0.45 <1",
198
198
  "nodemailer": ">=9 <10",
199
199
  "postgres": ">=3.4 <4",
200
- "posthog-js": ">=1.407 <2",
201
- "posthog-node": ">=5.47 <6",
200
+ "posthog-js": ">=1.430.3 <2",
201
+ "posthog-node": ">=5.52 <6",
202
202
  "react": ">=19 <20",
203
203
  "tus-js-client": ">=4 <5"
204
204
  },