Skip to content

feat(node): add evaluateFlags() API for single-call flag evaluation#3476

Open
dmarticus wants to merge 7 commits intomainfrom
posthog-code/node-evaluate-flags-api
Open

feat(node): add evaluateFlags() API for single-call flag evaluation#3476
dmarticus wants to merge 7 commits intomainfrom
posthog-code/node-evaluate-flags-api

Conversation

@dmarticus
Copy link
Copy Markdown
Contributor

@dmarticus dmarticus commented Apr 24, 2026

Problem

Phase 1 + Phase 2 of the Server SDK Feature Flag Evaluations RFC for posthog-node. Companion to the Python SDK PR (PostHog/posthog-python#539).

Today every flag check fires its own /flags request, and capture({ sendFeatureFlags: true }) silently fires yet another on every captured event. The flag values on a captured event can diverge from the ones the code actually branched on when person/group properties differ between calls. sendFeatureFlags also attaches every evaluated flag to every event, which bloats properties on high-volume events.

Changes

New API (Phase 1)

posthog.evaluateFlags(distinctId, ...) returns a FeatureFlagEvaluations snapshot:

const flags = await posthog.evaluateFlags(distinctId, { personProperties: { plan: 'enterprise' } })
if (flags.isEnabled('new-dashboard')) {
  renderNewDashboard()
}
posthog.capture({ distinctId, event: 'page_viewed', flags })

A single /flags request powers both branching and event enrichment. isEnabled() and getFlag() fire $feature_flag_called events (deduped through the existing cache) with the full metadata — $feature_flag_id, $feature_flag_version, $feature_flag_reason, $feature_flag_request_id — so experiment exposure tracking keeps working.

Two layers of scoping

  • Network-level (flagKeys option): scopes the underlying /flags request itself.

    const flags = await posthog.evaluateFlags(distinctId, { flagKeys: ['new-dashboard', 'checkout-flow'] })
  • Event-level (filter helpers): narrow which flags get attached to a captured event without re-fetching.

    posthog.capture({ ..., flags: flags.onlyAccessed() })           // only flags the developer checked
    posthog.capture({ ..., flags: flags.only(['new-dashboard']) })  // specific keys

    onlyAccessed() honors its name — if nothing has been accessed, it returns an empty snapshot (no fallback to all flags). only([...]) warns and drops unknown keys; the warning is silenceable via the featureFlagsLogWarnings: false SDK option.

Slices are for capture, not branching

onlyAccessed() / only([...]) return filtered snapshots intended for capture(). Calling isEnabled() / getFlag() on a slice for a key that was filtered out is a no-op (no event fires) — the flag wasn't actually missing, it was excluded from the slice.

Granular $feature_flag_error reporting

Response-level errors (errors_while_computing_flags, quota_limited) are propagated into $feature_flag_called events from the snapshot. A missing flag during a quota-limited response now reports quota_limited,flag_missing instead of just flag_missing, matching the single-flag path's granularity.

Exception captures carry flag context

captureException() and captureExceptionImmediate() accept an optional flags argument so $exception events carry the same flag context as the rest of your request's events:

const flags = await posthog.evaluateFlags(distinctId)
try {
  riskyThing()
} catch (e) {
  posthog.captureException(e, distinctId, undefined, undefined, flags)
}

flags vs sendFeatureFlags precedence

When both are passed, flags always wins and we log a warning so the precedence isn't surprising:

[PostHog] Both flags and sendFeatureFlags were passed to capture(); using flags and ignoring sendFeatureFlags.

Deprecation warnings (Phase 2)

The legacy single-flag surface keeps working but now emits a deduped [PostHog] ... is deprecated console warning the first time it's used:

  • getFeatureFlag()
  • isFeatureEnabled()
  • getFeatureFlagPayload()
  • capture({ sendFeatureFlags }) (only when truthy)

isFeatureEnabled is restructured to call _getFeatureFlagResult directly instead of routing through getFeatureFlag, so a single user-level call emits exactly one warning instead of cascading two. The dedup is process-wide via emitDeprecationWarningOnce, matching Python's warnings.warn default-dedup behavior. Phase 3 (removal in next major) ships separately.

Local evaluation

Transparent. When the poller resolves a flag, the snapshot carries locally_evaluated: true and reason "Evaluated locally", matching what getFeatureFlag() emits today.

Backwards compatibility

No breaking changes. All existing call paths return the same values they did before — the only behavior changes are:

  1. The new deprecation console warnings (deduped per-process)
  2. onlyAccessed() no longer falls back to all flags when nothing was accessed (empty input → empty output)

Internals

_getFeatureFlagResult was refactored: the dedup + capture portion is extracted into _captureFlagCalledEventIfNeeded, which is shared between the single-flag path and the new FeatureFlagEvaluations object. Both paths now dedupe identically. evaluateFlags uses getFeatureFlagDetailsStateless (the rich-detail endpoint) rather than the bare values endpoint so the snapshot carries full per-flag metadata.

Tests

packages/node/src/__tests__/evaluate-flags.spec.ts — 32 tests covering remote evaluation, local evaluation, filtering helpers, capture integration, flagKeys round-trip, empty-distinctId safety, error-granularity propagation, deprecation warning emission (with no-cascade verification), and captureException/captureExceptionImmediate flag forwarding.

Full Node SDK suite: zero regressions on this branch (the same pre-existing failures from main remain). pnpm lint clean.


Created with PostHog Code

Introduce `posthog.evaluateFlags(distinctId, options)` returning a
`FeatureFlagEvaluations` snapshot. Branch on `isEnabled()` / `getFlag()`
and pass the snapshot to `capture()` via a new `flags` option so events
carry the exact values the code branched on, with no extra /flags
request per capture.

Filtering helpers `onlyAccessed()` and `only([keys])` let callers shrink
the flag set attached to events. A new `featureFlagsLogWarnings` option
toggles the associated user-facing warnings.

Existing `isFeatureEnabled` / `getFeatureFlag` / `sendFeatureFlags`
continue to work unchanged; `sendFeatureFlags` is marked deprecated in
JSDoc ahead of a future major-version removal.

Generated-By: PostHog Code
Task-Id: b8a45b11-b41c-4995-8622-acea525e7703
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
posthog-example-next-app-router Ready Ready Preview Apr 29, 2026 9:27pm
posthog-js Ready Ready Preview Apr 29, 2026 9:27pm
posthog-nextjs-config Ready Ready Preview Apr 29, 2026 9:27pm

Request Review

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 24, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 24, 2026

Size Change: +28.3 kB (+0.4%)

Total Size: 7.03 MB

Filename Size Change
packages/node/dist/client.js 45.3 kB +8.55 kB (+23.26%) 🚨
packages/node/dist/client.mjs 43 kB +8.23 kB (+23.7%) 🚨
packages/node/dist/exports.js 4.78 kB +567 B (+13.45%) ⚠️
packages/node/dist/exports.mjs 300 B +97 B (+47.78%) 🚨
packages/node/dist/feature-flag-evaluations.js 6.09 kB +6.09 kB (new file) 🆕
packages/node/dist/feature-flag-evaluations.mjs 4.75 kB +4.75 kB (new file) 🆕
ℹ️ View Unchanged
Filename Size Change
packages/ai/dist/anthropic/index.cjs 20.5 kB 0 B
packages/ai/dist/anthropic/index.mjs 20.2 kB 0 B
packages/ai/dist/gemini/index.cjs 29.4 kB 0 B
packages/ai/dist/gemini/index.mjs 29.2 kB 0 B
packages/ai/dist/index.cjs 169 kB 0 B
packages/ai/dist/index.mjs 168 kB 0 B
packages/ai/dist/langchain/index.cjs 44 kB 0 B
packages/ai/dist/langchain/index.mjs 43.5 kB 0 B
packages/ai/dist/openai-agents/index.cjs 22.6 kB 0 B
packages/ai/dist/openai-agents/index.mjs 22.6 kB 0 B
packages/ai/dist/openai/index.cjs 47.3 kB 0 B
packages/ai/dist/openai/index.mjs 47 kB 0 B
packages/ai/dist/otel/index.cjs 4.97 kB 0 B
packages/ai/dist/otel/index.mjs 4.86 kB 0 B
packages/ai/dist/vercel/index.cjs 38.3 kB 0 B
packages/ai/dist/vercel/index.mjs 38.3 kB 0 B
packages/browser/dist/all-external-dependencies.js 270 kB 0 B
packages/browser/dist/array.full.es5.js 344 kB 0 B
packages/browser/dist/array.full.js 437 kB 0 B
packages/browser/dist/array.full.no-external.js 506 kB 0 B
packages/browser/dist/array.js 189 kB 0 B
packages/browser/dist/array.no-external.js 207 kB 0 B
packages/browser/dist/conversations.js 67.2 kB 0 B
packages/browser/dist/crisp-chat-integration.js 1.88 kB 0 B
packages/browser/dist/customizations.full.js 17.9 kB 0 B
packages/browser/dist/dead-clicks-autocapture.js 13.1 kB 0 B
packages/browser/dist/default-extensions.js 188 kB 0 B
packages/browser/dist/element-inference.js 5.59 kB 0 B
packages/browser/dist/exception-autocapture.js 11.7 kB 0 B
packages/browser/dist/extension-bundles.js 106 kB 0 B
packages/browser/dist/external-scripts-loader.js 3.03 kB 0 B
packages/browser/dist/intercom-integration.js 1.93 kB 0 B
packages/browser/dist/lazy-recorder.js 159 kB 0 B
packages/browser/dist/logs.js 38.5 kB 0 B
packages/browser/dist/main.js 194 kB 0 B
packages/browser/dist/module.full.js 440 kB 0 B
packages/browser/dist/module.full.no-external.js 508 kB 0 B
packages/browser/dist/module.js 193 kB 0 B
packages/browser/dist/module.no-external.js 211 kB 0 B
packages/browser/dist/module.slim.js 100 kB 0 B
packages/browser/dist/module.slim.no-external.js 105 kB 0 B
packages/browser/dist/posthog-recorder.js 145 kB 0 B
packages/browser/dist/product-tours-preview.js 76.2 kB 0 B
packages/browser/dist/product-tours.js 115 kB 0 B
packages/browser/dist/recorder-v2.js 111 kB 0 B
packages/browser/dist/recorder.js 111 kB 0 B
packages/browser/dist/surveys-preview.js 76.1 kB 0 B
packages/browser/dist/surveys.js 94.3 kB 0 B
packages/browser/dist/tracing-headers.js 1.74 kB 0 B
packages/browser/dist/web-vitals-with-attribution.js 11.8 kB 0 B
packages/browser/dist/web-vitals.js 6.39 kB 0 B
packages/browser/react/dist/esm/index.js 21.2 kB 0 B
packages/browser/react/dist/esm/slim/index.js 17.6 kB 0 B
packages/browser/react/dist/esm/surveys/index.js 4.68 kB 0 B
packages/browser/react/dist/umd/index.js 24.4 kB 0 B
packages/browser/react/dist/umd/slim/index.js 20.4 kB 0 B
packages/browser/react/dist/umd/surveys/index.js 5.45 kB 0 B
packages/convex/dist/client/index.js 7.96 kB 0 B
packages/convex/dist/component/_generated/api.js 712 B 0 B
packages/convex/dist/component/_generated/component.js 212 B 0 B
packages/convex/dist/component/_generated/dataModel.js 230 B 0 B
packages/convex/dist/component/_generated/server.js 3.71 kB 0 B
packages/convex/dist/component/convex.config.js 133 B 0 B
packages/convex/dist/component/lib.js 7.95 kB 0 B
packages/convex/dist/component/schema.js 113 B 0 B
packages/core/dist/error-tracking/chunk-ids.js 2.54 kB 0 B
packages/core/dist/error-tracking/chunk-ids.mjs 1.31 kB 0 B
packages/core/dist/error-tracking/coercers/dom-exception-coercer.js 2.3 kB 0 B
packages/core/dist/error-tracking/coercers/dom-exception-coercer.mjs 993 B 0 B
packages/core/dist/error-tracking/coercers/error-coercer.js 2.02 kB 0 B
packages/core/dist/error-tracking/coercers/error-coercer.mjs 794 B 0 B
packages/core/dist/error-tracking/coercers/error-event-coercer.js 1.76 kB 0 B
packages/core/dist/error-tracking/coercers/error-event-coercer.mjs 513 B 0 B
packages/core/dist/error-tracking/coercers/event-coercer.js 1.82 kB 0 B
packages/core/dist/error-tracking/coercers/event-coercer.mjs 548 B 0 B
packages/core/dist/error-tracking/coercers/index.js 6.79 kB 0 B
packages/core/dist/error-tracking/coercers/index.mjs 326 B 0 B
packages/core/dist/error-tracking/coercers/object-coercer.js 3.46 kB 0 B
packages/core/dist/error-tracking/coercers/object-coercer.mjs 2.07 kB 0 B
packages/core/dist/error-tracking/coercers/primitive-coercer.js 1.67 kB 0 B
packages/core/dist/error-tracking/coercers/primitive-coercer.mjs 419 B 0 B
packages/core/dist/error-tracking/coercers/promise-rejection-event.js 2.59 kB 0 B
packages/core/dist/error-tracking/coercers/promise-rejection-event.mjs 1.25 kB 0 B
packages/core/dist/error-tracking/coercers/string-coercer.js 2.01 kB 0 B
packages/core/dist/error-tracking/coercers/string-coercer.mjs 820 B 0 B
packages/core/dist/error-tracking/coercers/utils.js 2.06 kB 0 B
packages/core/dist/error-tracking/coercers/utils.mjs 716 B 0 B
packages/core/dist/error-tracking/error-properties-builder.js 5.56 kB 0 B
packages/core/dist/error-tracking/error-properties-builder.mjs 4.23 kB 0 B
packages/core/dist/error-tracking/exception-steps.js 6.87 kB 0 B
packages/core/dist/error-tracking/exception-steps.mjs 4.71 kB 0 B
packages/core/dist/error-tracking/index.js 4.74 kB 0 B
packages/core/dist/error-tracking/index.mjs 191 B 0 B
packages/core/dist/error-tracking/parsers/base.js 1.83 kB 0 B
packages/core/dist/error-tracking/parsers/base.mjs 464 B 0 B
packages/core/dist/error-tracking/parsers/chrome.js 2.73 kB 0 B
packages/core/dist/error-tracking/parsers/chrome.mjs 1.32 kB 0 B
packages/core/dist/error-tracking/parsers/gecko.js 2.47 kB 0 B
packages/core/dist/error-tracking/parsers/gecko.mjs 1.13 kB 0 B
packages/core/dist/error-tracking/parsers/index.js 4.75 kB 0 B
packages/core/dist/error-tracking/parsers/index.mjs 2.1 kB 0 B
packages/core/dist/error-tracking/parsers/node.js 3.94 kB 0 B
packages/core/dist/error-tracking/parsers/node.mjs 2.68 kB 0 B
packages/core/dist/error-tracking/parsers/opera.js 2.26 kB 0 B
packages/core/dist/error-tracking/parsers/opera.mjs 746 B 0 B
packages/core/dist/error-tracking/parsers/safari.js 1.88 kB 0 B
packages/core/dist/error-tracking/parsers/safari.mjs 574 B 0 B
packages/core/dist/error-tracking/parsers/winjs.js 1.72 kB 0 B
packages/core/dist/error-tracking/parsers/winjs.mjs 426 B 0 B
packages/core/dist/error-tracking/types.js 1.33 kB 0 B
packages/core/dist/error-tracking/types.mjs 131 B 0 B
packages/core/dist/error-tracking/utils.js 1.8 kB 0 B
packages/core/dist/error-tracking/utils.mjs 604 B 0 B
packages/core/dist/eventemitter.js 1.78 kB 0 B
packages/core/dist/eventemitter.mjs 571 B 0 B
packages/core/dist/featureFlagUtils.js 6.8 kB 0 B
packages/core/dist/featureFlagUtils.mjs 4.32 kB 0 B
packages/core/dist/gzip.js 2.74 kB 0 B
packages/core/dist/gzip.mjs 1.29 kB 0 B
packages/core/dist/index.js 11.3 kB 0 B
packages/core/dist/index.mjs 1.15 kB 0 B
packages/core/dist/logs/index.js 2.62 kB 0 B
packages/core/dist/logs/index.mjs 1.3 kB 0 B
packages/core/dist/logs/logs-utils.js 5.96 kB 0 B
packages/core/dist/logs/logs-utils.mjs 3.99 kB 0 B
packages/core/dist/logs/types.js 603 B 0 B
packages/core/dist/logs/types.mjs 0 B 0 B 🆕
packages/core/dist/posthog-core-stateless.js 31.7 kB 0 B
packages/core/dist/posthog-core-stateless.mjs 29.2 kB 0 B
packages/core/dist/posthog-core.js 41.9 kB 0 B
packages/core/dist/posthog-core.mjs 36.9 kB 0 B
packages/core/dist/surveys/validation.js 3.06 kB 0 B
packages/core/dist/surveys/validation.mjs 1.51 kB 0 B
packages/core/dist/testing/index.js 2.93 kB 0 B
packages/core/dist/testing/index.mjs 79 B 0 B
packages/core/dist/testing/PostHogCoreTestClient.js 3.15 kB 0 B
packages/core/dist/testing/PostHogCoreTestClient.mjs 1.74 kB 0 B
packages/core/dist/testing/test-utils.js 2.77 kB 0 B
packages/core/dist/testing/test-utils.mjs 1.09 kB 0 B
packages/core/dist/tracing-headers.js 3.38 kB 0 B
packages/core/dist/tracing-headers.mjs 2.08 kB 0 B
packages/core/dist/types.js 9.62 kB 0 B
packages/core/dist/types.mjs 7.07 kB 0 B
packages/core/dist/utils/bot-detection.js 3.28 kB 0 B
packages/core/dist/utils/bot-detection.mjs 1.95 kB 0 B
packages/core/dist/utils/bucketed-rate-limiter.js 3 kB 0 B
packages/core/dist/utils/bucketed-rate-limiter.mjs 1.62 kB 0 B
packages/core/dist/utils/index.js 11.9 kB 0 B
packages/core/dist/utils/index.mjs 1.98 kB 0 B
packages/core/dist/utils/logger.js 2.5 kB 0 B
packages/core/dist/utils/logger.mjs 1.22 kB 0 B
packages/core/dist/utils/number-utils.js 3.32 kB 0 B
packages/core/dist/utils/number-utils.mjs 1.68 kB 0 B
packages/core/dist/utils/promise-queue.js 2 kB 0 B
packages/core/dist/utils/promise-queue.mjs 768 B 0 B
packages/core/dist/utils/string-utils.js 2.73 kB 0 B
packages/core/dist/utils/string-utils.mjs 1.09 kB 0 B
packages/core/dist/utils/type-utils.js 7.04 kB 0 B
packages/core/dist/utils/type-utils.mjs 3.11 kB 0 B
packages/core/dist/utils/user-agent-utils.js 15.2 kB 0 B
packages/core/dist/utils/user-agent-utils.mjs 12.2 kB 0 B
packages/core/dist/vendor/uuidv7.js 8.29 kB 0 B
packages/core/dist/vendor/uuidv7.mjs 6.72 kB 0 B
packages/next/dist/app/PostHogProvider.js 3.23 kB 0 B
packages/next/dist/client/ClientPostHogProvider.js 1.76 kB 0 B
packages/next/dist/client/hooks.js 172 B 0 B
packages/next/dist/client/PostHogPageView.js 1.7 kB 0 B
packages/next/dist/index.client.js 401 B 0 B
packages/next/dist/index.edge.js 447 B 0 B
packages/next/dist/index.js 444 B 0 B
packages/next/dist/index.react-server.js 420 B 0 B
packages/next/dist/middleware/postHogMiddleware.js 3.64 kB 0 B
packages/next/dist/pages.js 414 B 0 B
packages/next/dist/pages/getServerSidePostHog.js 1.93 kB 0 B
packages/next/dist/pages/PostHogPageView.js 1.2 kB 0 B
packages/next/dist/pages/PostHogProvider.js 1.51 kB 0 B
packages/next/dist/server/getPostHog.js 2.73 kB 0 B
packages/next/dist/server/nodeClientCache.js 1.31 kB 0 B
packages/next/dist/shared/config.js 2.06 kB 0 B
packages/next/dist/shared/constants.js 278 B 0 B
packages/next/dist/shared/cookie.js 4.49 kB 0 B
packages/next/dist/shared/identity.js 264 B 0 B
packages/next/dist/shared/tracing-headers.js 2.18 kB 0 B
packages/nextjs-config/dist/config.js 4.97 kB 0 B
packages/nextjs-config/dist/config.mjs 3.49 kB 0 B
packages/nextjs-config/dist/index.js 2.24 kB 0 B
packages/nextjs-config/dist/index.mjs 30 B 0 B
packages/nextjs-config/dist/utils.js 2.94 kB 0 B
packages/nextjs-config/dist/utils.mjs 826 B 0 B
packages/node/dist/entrypoints/index.edge.js 4.25 kB 0 B
packages/node/dist/entrypoints/index.edge.mjs 723 B 0 B
packages/node/dist/entrypoints/index.node.js 6.04 kB 0 B
packages/node/dist/entrypoints/index.node.mjs 1.22 kB 0 B
packages/node/dist/entrypoints/nestjs.js 2.31 kB 0 B
packages/node/dist/entrypoints/nestjs.mjs 42 B 0 B
packages/node/dist/experimental.js 603 B 0 B
packages/node/dist/experimental.mjs 0 B 0 B 🆕
packages/node/dist/extensions/context/context.js 2.13 kB 0 B
packages/node/dist/extensions/context/context.mjs 863 B 0 B
packages/node/dist/extensions/context/types.js 603 B 0 B
packages/node/dist/extensions/context/types.mjs 0 B 0 B 🆕
packages/node/dist/extensions/error-tracking/autocapture.js 2.66 kB 0 B
packages/node/dist/extensions/error-tracking/autocapture.mjs 1.24 kB 0 B
packages/node/dist/extensions/error-tracking/index.js 4.14 kB 0 B
packages/node/dist/extensions/error-tracking/index.mjs 2.87 kB 0 B
packages/node/dist/extensions/error-tracking/modifiers/context-lines.node.js 8.81 kB 0 B
packages/node/dist/extensions/error-tracking/modifiers/context-lines.node.mjs 7.15 kB 0 B
packages/node/dist/extensions/error-tracking/modifiers/module.node.js 2.78 kB 0 B
packages/node/dist/extensions/error-tracking/modifiers/module.node.mjs 1.45 kB 0 B
packages/node/dist/extensions/error-tracking/modifiers/relative-path.node.js 1.97 kB 0 B
packages/node/dist/extensions/error-tracking/modifiers/relative-path.node.mjs 624 B 0 B
packages/node/dist/extensions/express.js 4.56 kB 0 B
packages/node/dist/extensions/express.mjs 2.45 kB 0 B
packages/node/dist/extensions/feature-flags/cache.js 603 B 0 B
packages/node/dist/extensions/feature-flags/cache.mjs 0 B 0 B 🆕
packages/node/dist/extensions/feature-flags/crypto.js 1.57 kB 0 B
packages/node/dist/extensions/feature-flags/crypto.mjs 395 B 0 B
packages/node/dist/extensions/feature-flags/feature-flags.js 38.8 kB 0 B
packages/node/dist/extensions/feature-flags/feature-flags.mjs 36.7 kB 0 B
packages/node/dist/extensions/nestjs.js 5 kB 0 B
packages/node/dist/extensions/nestjs.mjs 2.9 kB 0 B
packages/node/dist/extensions/sentry-integration.js 4.66 kB 0 B
packages/node/dist/extensions/sentry-integration.mjs 3.17 kB 0 B
packages/node/dist/extensions/tracing-headers.js 3.31 kB 0 B
packages/node/dist/extensions/tracing-headers.mjs 1.53 kB 0 B
packages/node/dist/storage-memory.js 1.52 kB 0 B
packages/node/dist/storage-memory.mjs 297 B 0 B
packages/node/dist/types.js 1.43 kB 0 B
packages/node/dist/types.mjs 224 B 0 B
packages/node/dist/version.js 1.21 kB 0 B
packages/node/dist/version.mjs 46 B 0 B
packages/nuxt/dist/module.mjs 5.29 kB 0 B
packages/nuxt/dist/runtime/composables/useFeatureFlagEnabled.js 566 B 0 B
packages/nuxt/dist/runtime/composables/useFeatureFlagPayload.js 690 B 0 B
packages/nuxt/dist/runtime/composables/useFeatureFlagVariantKey.js 591 B 0 B
packages/nuxt/dist/runtime/composables/usePostHog.js 128 B 0 B
packages/nuxt/dist/runtime/nitro-plugin.js 1.08 kB 0 B
packages/nuxt/dist/runtime/vue-plugin.js 1.14 kB 0 B
packages/plugin-utils/dist/cli.js 3.14 kB 0 B
packages/plugin-utils/dist/cli.mjs 1.64 kB 0 B
packages/plugin-utils/dist/config.js 3.07 kB 0 B
packages/plugin-utils/dist/config.mjs 1.83 kB 0 B
packages/plugin-utils/dist/index.js 4.3 kB 0 B
packages/plugin-utils/dist/index.mjs 217 B 0 B
packages/plugin-utils/dist/spawn-local.js 2.17 kB 0 B
packages/plugin-utils/dist/spawn-local.mjs 918 B 0 B
packages/plugin-utils/dist/utils.js 3.27 kB 0 B
packages/plugin-utils/dist/utils.mjs 1.3 kB 0 B
packages/react-native/dist/autocapture.js 5.05 kB 0 B
packages/react-native/dist/error-tracking/index.js 7.24 kB 0 B
packages/react-native/dist/error-tracking/utils.js 2.58 kB 0 B
packages/react-native/dist/frameworks/wix-navigation.js 1.3 kB 0 B
packages/react-native/dist/hooks/useFeatureFlag.js 1.7 kB 0 B
packages/react-native/dist/hooks/useFeatureFlagResult.js 963 B 0 B
packages/react-native/dist/hooks/useFeatureFlags.js 921 B 0 B
packages/react-native/dist/hooks/useNavigationTracker.js 2.45 kB 0 B
packages/react-native/dist/hooks/usePostHog.js 544 B 0 B
packages/react-native/dist/hooks/utils.js 988 B 0 B
packages/react-native/dist/index.js 4.33 kB 0 B
packages/react-native/dist/logs-defaults.js 1.97 kB 0 B
packages/react-native/dist/native-deps.js 8.77 kB 0 B
packages/react-native/dist/optional/OptionalAsyncStorage.js 299 B 0 B
packages/react-native/dist/optional/OptionalExpoApplication.js 377 B 0 B
packages/react-native/dist/optional/OptionalExpoDevice.js 347 B 0 B
packages/react-native/dist/optional/OptionalExpoFileSystem.js 386 B 0 B
packages/react-native/dist/optional/OptionalExpoFileSystemLegacy.js 423 B 0 B
packages/react-native/dist/optional/OptionalExpoLocalization.js 383 B 0 B
packages/react-native/dist/optional/OptionalReactNativeDeviceInfo.js 415 B 0 B
packages/react-native/dist/optional/OptionalReactNativeLocalize.js 303 B 0 B
packages/react-native/dist/optional/OptionalReactNativeNavigation.js 415 B 0 B
packages/react-native/dist/optional/OptionalReactNativeNavigationWix.js 443 B 0 B
packages/react-native/dist/optional/OptionalReactNativeSafeArea.js 644 B 0 B
packages/react-native/dist/optional/OptionalReactNativeSvg.js 872 B 0 B
packages/react-native/dist/optional/OptionalSessionReplay.js 455 B 0 B
packages/react-native/dist/posthog-rn.js 42.2 kB 0 B
packages/react-native/dist/PostHogContext.js 329 B 0 B
packages/react-native/dist/PostHogErrorBoundary.js 3.19 kB 0 B
packages/react-native/dist/PostHogMaskView.js 1.68 kB 0 B
packages/react-native/dist/PostHogProvider.js 4.76 kB 0 B
packages/react-native/dist/storage.js 5.2 kB 0 B
packages/react-native/dist/surveys/components/BottomSection.js 1.46 kB 0 B
packages/react-native/dist/surveys/components/Cancel.js 909 B 0 B
packages/react-native/dist/surveys/components/ConfirmationMessage.js 1.65 kB 0 B
packages/react-native/dist/surveys/components/QuestionHeader.js 1.37 kB 0 B
packages/react-native/dist/surveys/components/QuestionTypes.js 13.2 kB 0 B
packages/react-native/dist/surveys/components/SurveyModal.js 5.4 kB 0 B
packages/react-native/dist/surveys/components/Surveys.js 7.8 kB 0 B
packages/react-native/dist/surveys/getActiveMatchingSurveys.js 2.64 kB 0 B
packages/react-native/dist/surveys/icons.js 9.97 kB 0 B
packages/react-native/dist/surveys/index.js 600 B 0 B
packages/react-native/dist/surveys/PostHogSurveyProvider.js 5.74 kB 0 B
packages/react-native/dist/surveys/surveys-utils.js 14.2 kB 0 B
packages/react-native/dist/surveys/useActivatedSurveys.js 3.67 kB 0 B
packages/react-native/dist/surveys/useSurveyStorage.js 2.16 kB 0 B
packages/react-native/dist/tooling/expoconfig.js 4.02 kB 0 B
packages/react-native/dist/tooling/metroconfig.js 2.32 kB 0 B
packages/react-native/dist/tooling/posthogMetroSerializer.js 4.86 kB 0 B
packages/react-native/dist/tooling/utils.js 4.05 kB 0 B
packages/react-native/dist/tooling/vendor/expo/expoconfig.js 70 B 0 B
packages/react-native/dist/tooling/vendor/metro/countLines.js 237 B 0 B
packages/react-native/dist/tooling/vendor/metro/utils.js 3.35 kB 0 B
packages/react-native/dist/types.js 70 B 0 B
packages/react-native/dist/utils.js 1.14 kB 0 B
packages/react-native/dist/version.js 131 B 0 B
packages/react/dist/esm/index.js 21.2 kB 0 B
packages/react/dist/esm/slim/index.js 17.6 kB 0 B
packages/react/dist/esm/surveys/index.js 4.68 kB 0 B
packages/react/dist/umd/index.js 24.4 kB 0 B
packages/react/dist/umd/slim/index.js 20.4 kB 0 B
packages/react/dist/umd/surveys/index.js 5.45 kB 0 B
packages/rollup-plugin/dist/index.js 2.11 kB 0 B
packages/types/dist/capture-log.js 603 B 0 B
packages/types/dist/capture-log.mjs 0 B 0 B 🆕
packages/types/dist/capture.js 603 B 0 B
packages/types/dist/capture.mjs 0 B 0 B 🆕
packages/types/dist/common.js 603 B 0 B
packages/types/dist/common.mjs 0 B 0 B 🆕
packages/types/dist/feature-flags.js 603 B 0 B
packages/types/dist/feature-flags.mjs 0 B 0 B 🆕
packages/types/dist/index.js 603 B 0 B
packages/types/dist/index.mjs 0 B 0 B 🆕
packages/types/dist/posthog-config.js 603 B 0 B
packages/types/dist/posthog-config.mjs 0 B 0 B 🆕
packages/types/dist/posthog.js 603 B 0 B
packages/types/dist/posthog.mjs 0 B 0 B 🆕
packages/types/dist/request.js 603 B 0 B
packages/types/dist/request.mjs 0 B 0 B 🆕
packages/types/dist/segment.js 603 B 0 B
packages/types/dist/segment.mjs 0 B 0 B 🆕
packages/types/dist/session-recording.js 603 B 0 B
packages/types/dist/session-recording.mjs 0 B 0 B 🆕
packages/types/dist/survey.js 603 B 0 B
packages/types/dist/survey.mjs 0 B 0 B 🆕
packages/types/dist/toolbar.js 603 B 0 B
packages/types/dist/toolbar.mjs 0 B 0 B 🆕
packages/types/dist/tree-shakeable.js 603 B 0 B
packages/types/dist/tree-shakeable.mjs 0 B 0 B 🆕
packages/web/dist/index.cjs 13.8 kB 0 B
packages/web/dist/index.mjs 13.7 kB 0 B
packages/webpack-plugin/dist/config.js 1.53 kB 0 B
packages/webpack-plugin/dist/config.mjs 543 B 0 B
packages/webpack-plugin/dist/index.js 5.38 kB 0 B
packages/webpack-plugin/dist/index.mjs 2.04 kB 0 B
tooling/changelog/dist/index.js 3.31 kB 0 B
tooling/rollup-utils/dist/index.js 1.17 kB 0 B

compressed-size-action

Allow callers to scope the underlying /flags request to a subset of
flags. The chained `flags.only([...])` filter still exists for
event-attachment scoping after evaluation; `flagKeys` reduces the
network payload itself.

Generated-By: PostHog Code
Task-Id: b8a45b11-b41c-4995-8622-acea525e7703
…examples

Generated-By: PostHog Code
Task-Id: b8a45b11-b41c-4995-8622-acea525e7703
…called events

- Plumb $feature_flag_definitions_loaded_at into the snapshot at
  construction so locally-evaluated flag access via the new API emits
  the same event schema as the existing single-flag path.
- Short-circuit $feature_flag_called emission when the snapshot has
  no resolvable distinctId, so the safety-fallback empty snapshot
  doesn't leak events with empty distinct_id values.
- Demote the shared dedup helper from public to protected; the only
  external caller is a closure with `this`-scoped access.
- Document the onlyAccessed() empty-fallback behavior and clarify that
  the local-evaluation flag definition has no version field.

Generated-By: PostHog Code
Task-Id: b8a45b11-b41c-4995-8622-acea525e7703
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 27, 2026

Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/node/src/feature-flag-evaluations.ts
Line: 248-260

Comment:
**Filtered snapshot branching fires misleading `flag_missing` events**

When `_recordAccess` is called on a filtered clone (returned by `onlyAccessed()` or `only()`) for a key that was excluded from the filter but actually exists in the original snapshot, `this._flags[key]` is `undefined` here — triggering the `$feature_flag_error: 'flag_missing'` path even though the flag was fully evaluated. This pollutes the analytics stream with spurious error events.

The test at line 341 of `evaluate-flags.spec.ts` exercises exactly this path (`filtered.isEnabled('variant-flag')` on a clone that only holds `boolean-flag`) but doesn't assert on the resulting events, so the misleading capture goes undetected.

Filtered views are intended for `capture()`, not for further branching. Consider guarding this in one of two ways: either document it explicitly and add an assertion in the test, or short-circuit `_recordAccess` when the key is absent from `_flags` on a filtered clone (e.g. by tracking whether the instance is a slice).

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/node/src/__tests__/evaluate-flags.spec.ts
Line: 100-113

Comment:
**Repeated PostHog initialisation violates OnceAndOnlyOnce**

The pattern

```ts
posthog = new PostHog('TEST_API_KEY', {
  host: 'http://example.com',
  ...posthogImmediateResolveOptions,
})
```

appears verbatim in eight tests inside the `remote evaluation` describe block. Similarly, the `captures: any[]` + `posthog.on('capture', …)` setup repeats in four of them. A shared `beforeEach` for the common case would remove the duplication and make per-test deviations (e.g. `featureFlagsLogWarnings: false`, `personalApiKey`) stand out clearly.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(node): close parity gaps on FeatureF..." | Re-trigger Greptile

Comment thread packages/node/src/feature-flag-evaluations.ts
Comment thread packages/node/src/__tests__/evaluate-flags.spec.ts Outdated
…tions slices

Address review feedback on PR #3476:

- Filtered snapshots from `only()` / `onlyAccessed()` no longer fire
  misleading `$feature_flag_called` events with `flag_missing` when
  branching on a key that was excluded from the slice. The slice tracks
  whether it's a filtered view via an `_isSlice` flag and short-circuits
  `_recordAccess` for absent keys. Document this behavior on the filter
  helpers' JSDoc — slices are intended for `capture()`, not branching.
  Add a regression test covering the path.

- Refactor `evaluate-flags.spec.ts` to extract a `setup(overrides)`
  helper used by all suites, replacing eight repeated `new PostHog(...)`
  blocks plus four duplicated capture-listener setups. Per-test
  deviations (`featureFlagsLogWarnings: false`, `personalApiKey: ...`)
  now stand out as explicit overrides.

Generated-By: PostHog Code
Task-Id: b8a45b11-b41c-4995-8622-acea525e7703
…ularity, captureException flags)

Mirrors fixes from PostHog/posthog-python#539:

- `onlyAccessed()` returns empty when nothing has been accessed (no
  fallback to all flags). The previous fallback contradicted the method
  name and surprised reviewers.
- Propagate response-level errors (`errors_while_computing_flags`,
  `quota_limited`) into `$feature_flag_called` events so each access
  carries the granular error code(s) the single-flag path emits.
- Make `flags` vs `sendFeatureFlags` precedence explicit on `capture()`:
  `flags` always wins, and we log a warning when both are passed.
- Phase 2 deprecation warnings: `getFeatureFlag`, `isFeatureEnabled`,
  `getFeatureFlagPayload`, and `capture({ sendFeatureFlags })` now log a
  deduped `[PostHog] ... is deprecated` console warning the first time
  they're used. `isFeatureEnabled` is restructured to call
  `_getFeatureFlagResult` directly so a single user-level call emits
  exactly one warning instead of cascading.
- `captureException` and `captureExceptionImmediate` accept an optional
  `flags` snapshot so `$exception` events carry the same flag context
  as the rest of the request's events.

Adds a process-wide dedup helper `emitDeprecationWarningOnce` matching
Python's `warnings.warn` default-dedup behavior.

Generated-By: PostHog Code
Task-Id: b8a45b11-b41c-4995-8622-acea525e7703
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant