Skip to content

feat: add official PostHog OpenFeature server provider (Node) - #3994

Merged
gustavohstrassburger merged 4 commits into
mainfrom
posthog-code/openfeature-provider
Jul 9, 2026
Merged

feat: add official PostHog OpenFeature server provider (Node)#3994
gustavohstrassburger merged 4 commits into
mainfrom
posthog-code/openfeature-provider

Conversation

@gustavohstrassburger

@gustavohstrassburger gustavohstrassburger commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Based on PostHog/posthog-python#695, adds @posthog/openfeature-node-provider — the official PostHog provider for the OpenFeature server SDK (@openfeature/server-sdk), backed by posthog-node.

Split into two PRs per review (@marandaneto: "could be 2 PRs tbh"). This PR is the server/Node provider; the web/browser provider (@posthog/openfeature-web-provider, backed by posthog-js) is in #4069.

OpenFeature ships two SDKs with incompatible Provider contracts — the server SDK is async with a per-call evaluation context (distinct id from targetingKey), versus the web SDK's synchronous, static-context model. So the two providers are separate packages, each pulling in only its own paradigm's SDK. The Python parent PR is server-only (no browser runtime); this pair covers both.

Behaviour

The server model is stateless and multi-user: the distinct id arrives per evaluation from the context's targetingKey, and resolution is async. Context mapping: targetingKeydistinctId, reserved groups/groupProperties → PostHog equivalents, every other attribute → personProperties.

Flag-type mapping (via getFeatureFlagResult): boolean → enabled, string → multivariate variant, number → parsed variant, object → JSON payload. A missing flag resolves to FLAG_NOT_FOUND; asking for a type the flag can't provide resolves to TYPE_MISMATCH (caller gets its default, per spec).

One deliberate divergence from Python: posthog-node's FeatureFlagResult has no reason field, so reason mapping simplifies to enabled ? TARGETING_MATCH : DEFAULT and there's no posthog_reason metadata.

Options

  • sendFeatureFlagEvents (default true) — control $feature_flag_called capture.
  • defaultDistinctId — when set, evaluations without a targetingKey use it instead of raising TARGETING_KEY_MISSING.

Publishing

Marked for release (changeset + entry in the release.yml publish matrix); created on npm under the @posthog scope on first publish, gated on the standard merge + NPM Release approval flow.

Docs

Usage lives in the PostHog docs (kept out of the package README so it doesn't drift): PostHog/posthog.com#18105/docs/feature-flags/installation/openfeature-js (covers both the node and web providers).

Testing

  • 20 unit tests (type mappings, error cases, context split, distinct-id resolution, initialize preload) + end-to-end tests through the real @openfeature/server-sdk client.
  • ✅ Build (rslib esm + cjs + declarations) · ✅ Lint · ✅ Tests · ✅ pnpm install --frozen-lockfile consistent

🤖 Generated with Claude Code

@greptile-apps

greptile-apps Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "feat: add official PostHog OpenFeature p..." | Re-trigger Greptile

Comment thread packages/openfeature-web-provider/src/provider.ts
Comment thread packages/openfeature-provider/src/mapping.ts Outdated
Comment thread packages/openfeature-provider/src/__tests__/server-provider.spec.ts Outdated
@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Size Change: 0 B

Total Size: 17.3 MB

ℹ️ View Unchanged
Filename Size Change
packages/ai/dist/anthropic/index.cjs 28.2 kB 0 B
packages/ai/dist/anthropic/index.mjs 27.9 kB 0 B
packages/ai/dist/gemini/index.cjs 36.5 kB 0 B
packages/ai/dist/gemini/index.mjs 36.3 kB 0 B
packages/ai/dist/index.cjs 55.2 kB 0 B
packages/ai/dist/index.mjs 55.1 kB 0 B
packages/ai/dist/langchain/index.cjs 34.7 kB 0 B
packages/ai/dist/langchain/index.mjs 34.7 kB 0 B
packages/ai/dist/openai-agents/index.cjs 29.5 kB 0 B
packages/ai/dist/openai-agents/index.mjs 29.4 kB 0 B
packages/ai/dist/openai/index.cjs 78.9 kB 0 B
packages/ai/dist/openai/index.mjs 78.6 kB 0 B
packages/ai/dist/otel/index.cjs 17.2 kB 0 B
packages/ai/dist/otel/index.mjs 17.1 kB 0 B
packages/ai/dist/vercel/index.cjs 47.3 kB 0 B
packages/ai/dist/vercel/index.mjs 47.3 kB 0 B
packages/browser/dist/all-external-dependencies.js 238 kB 0 B
packages/browser/dist/array.full.es5.js 413 kB 0 B
packages/browser/dist/array.full.js 439 kB 0 B
packages/browser/dist/array.full.no-********.js 518 kB 0 B
packages/browser/dist/array.js 226 kB 0 B
packages/browser/dist/array.no-********.js 250 kB 0 B
packages/browser/dist/conversations.js 68.5 kB 0 B
packages/browser/dist/crisp-chat-integration.js 1.97 kB 0 B
packages/browser/dist/customizations.full.js 19.1 kB 0 B
packages/browser/dist/customizations.js 19.1 kB 0 B
packages/browser/dist/dead-clicks-autocapture.js 14.7 kB 0 B
packages/browser/dist/default-extensions.js 224 kB 0 B
packages/browser/dist/element-inference.js 5.69 kB 0 B
packages/browser/dist/exception-autocapture.js 11.8 kB 0 B
packages/browser/dist/extension-bundles.js 131 kB 0 B
packages/browser/dist/external-scripts-loader.js 3.11 kB 0 B
packages/browser/dist/intercom-integration.js 2.03 kB 0 B
packages/browser/dist/lazy-********.js 162 kB 0 B
packages/browser/dist/logs.js 4.35 kB 0 B
packages/browser/dist/main.js 230 kB 0 B
packages/browser/dist/module.full.js 442 kB 0 B
packages/browser/dist/module.full.no-********.js 521 kB 0 B
packages/browser/dist/module.js 230 kB 0 B
packages/browser/dist/module.no-********.js 254 kB 0 B
packages/browser/dist/module.slim.js 112 kB 0 B
packages/browser/dist/module.slim.no-********.js 119 kB 0 B
packages/browser/dist/posthog-********.js 162 kB 0 B
packages/browser/dist/product-tours-preview.js 76.6 kB 0 B
packages/browser/dist/product-tours.js 116 kB 0 B
packages/browser/dist/recorder-v2.js 107 kB 0 B
packages/browser/dist/recorder.js 107 kB 0 B
packages/browser/dist/rrweb-plugin-console-record.js 6.67 kB 0 B
packages/browser/dist/rrweb-types.js 2.33 kB 0 B
packages/browser/dist/rrweb.js 288 kB 0 B
packages/browser/dist/surveys-preview.js 78.3 kB 0 B
packages/browser/dist/surveys.js 97.2 kB 0 B
packages/browser/dist/tracing-headers.js 3.65 kB 0 B
packages/browser/dist/web-vitals-with-attribution.js 12.2 kB 0 B
packages/browser/dist/web-vitals.js 6.66 kB 0 B
packages/browser/react/dist/esm/index.js 21.1 kB 0 B
packages/browser/react/dist/esm/slim/index.js 17.5 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.3 kB 0 B
packages/browser/react/dist/umd/surveys/index.js 5.45 kB 0 B
packages/convex/dist/client/feature-flags/crypto.js 461 B 0 B
packages/convex/dist/client/feature-flags/evaluator.js 16.5 kB 0 B
packages/convex/dist/client/feature-flags/index.js 196 B 0 B
packages/convex/dist/client/feature-flags/match-********.js 14.8 kB 0 B
packages/convex/dist/client/feature-flags/types.js 44 B 0 B
packages/convex/dist/client/index.js 14.6 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.74 kB 0 B
packages/convex/dist/component/convex.config.js 1.66 kB 0 B
packages/convex/dist/component/crons.js 969 B 0 B
packages/convex/dist/component/lib.js 24.7 kB 0 B
packages/convex/dist/component/schema.js 1.17 kB 0 B
packages/convex/dist/component/version.js 68 B 0 B
packages/core/dist/cookie.js 5.34 kB 0 B
packages/core/dist/cookie.mjs 3.12 kB 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.01 kB 0 B
packages/core/dist/error-tracking/exception-steps.mjs 3.84 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.42 kB 0 B
packages/core/dist/featureFlagUtils.mjs 3.99 kB 0 B
packages/core/dist/gzip.js 5.72 kB 0 B
packages/core/dist/gzip.mjs 3.84 kB 0 B
packages/core/dist/index.js 17.4 kB 0 B
packages/core/dist/index.mjs 1.75 kB 0 B
packages/core/dist/logs/index.js 10.9 kB 0 B
packages/core/dist/logs/index.mjs 9.22 kB 0 B
packages/core/dist/logs/logs-utils.js 7.01 kB 0 B
packages/core/dist/logs/logs-utils.mjs 4.85 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/metrics/index.js 13.6 kB 0 B
packages/core/dist/metrics/index.mjs 11.4 kB 0 B
packages/core/dist/metrics/metrics-utils.js 3.86 kB 0 B
packages/core/dist/metrics/metrics-utils.mjs 1.97 kB 0 B
packages/core/dist/metrics/types.js 603 B 0 B
packages/core/dist/metrics/types.mjs 0 B 0 B 🆕
packages/core/dist/posthog-core-stateless.js 38.9 kB 0 B
packages/core/dist/posthog-core-stateless.mjs 35.7 kB 0 B
packages/core/dist/posthog-core.js 47.1 kB 0 B
packages/core/dist/posthog-core.mjs 41.7 kB 0 B
packages/core/dist/surveys/events.js 4.21 kB 0 B
packages/core/dist/surveys/events.mjs 1.99 kB 0 B
packages/core/dist/surveys/index.js 4.57 kB 0 B
packages/core/dist/surveys/index.mjs 894 B 0 B
packages/core/dist/surveys/translations.js 10 kB 0 B
packages/core/dist/surveys/translations.mjs 7.55 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.24 kB 0 B
packages/core/dist/testing/PostHogCoreTestClient.mjs 1.83 kB 0 B
packages/core/dist/testing/test-utils.js 2.83 kB 0 B
packages/core/dist/testing/test-utils.mjs 1.15 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 10 kB 0 B
packages/core/dist/types.mjs 7.28 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 13.5 kB 0 B
packages/core/dist/utils/index.mjs 2.4 kB 0 B
packages/core/dist/utils/logger.js 2.58 kB 0 B
packages/core/dist/utils/logger.mjs 1.29 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.48 kB 0 B
packages/core/dist/utils/promise-queue.mjs 1.25 kB 0 B
packages/core/dist/utils/string-utils.js 3.73 kB 0 B
packages/core/dist/utils/string-utils.mjs 1.97 kB 0 B
packages/core/dist/utils/type-utils.js 7.43 kB 0 B
packages/core/dist/utils/type-utils.mjs 3.28 kB 0 B
packages/core/dist/utils/user-agent-utils.js 17.7 kB 0 B
packages/core/dist/utils/user-agent-utils.mjs 14.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/mcp/dist/extensions/capture.js 3.67 kB 0 B
packages/mcp/dist/extensions/capture.mjs 2.23 kB 0 B
packages/mcp/dist/extensions/compatibility.js 5.63 kB 0 B
packages/mcp/dist/extensions/compatibility.mjs 3.79 kB 0 B
packages/mcp/dist/extensions/constants.js 4.64 kB 0 B
packages/mcp/dist/extensions/constants.mjs 2.39 kB 0 B
packages/mcp/dist/extensions/context-parameters.js 3.9 kB 0 B
packages/mcp/dist/extensions/context-parameters.mjs 2.11 kB 0 B
packages/mcp/dist/extensions/conversation-id.js 7.12 kB 0 B
packages/mcp/dist/extensions/conversation-id.mjs 4.21 kB 0 B
packages/mcp/dist/extensions/event-types.js 1.68 kB 0 B
packages/mcp/dist/extensions/event-types.mjs 459 B 0 B
packages/mcp/dist/extensions/exceptions.js 2.57 kB 0 B
packages/mcp/dist/extensions/exceptions.mjs 1.19 kB 0 B
packages/mcp/dist/extensions/ids.js 2.01 kB 0 B
packages/mcp/dist/extensions/ids.mjs 637 B 0 B
packages/mcp/dist/extensions/instrument-highlevel.js 11.3 kB 0 B
packages/mcp/dist/extensions/instrument-highlevel.mjs 8.71 kB 0 B
packages/mcp/dist/extensions/instrument-********.js 4.25 kB 0 B
packages/mcp/dist/extensions/instrument-********.mjs 2.43 kB 0 B
packages/mcp/dist/extensions/instrumentation.js 14.8 kB 0 B
packages/mcp/dist/extensions/instrumentation.mjs 10.6 kB 0 B
packages/mcp/dist/extensions/intent.js 3.31 kB 0 B
packages/mcp/dist/extensions/intent.mjs 1.68 kB 0 B
packages/mcp/dist/extensions/internal.js 6.39 kB 0 B
packages/mcp/dist/extensions/internal.mjs 4.14 kB 0 B
packages/mcp/dist/extensions/lib-********.js 1.6 kB 0 B
packages/mcp/dist/extensions/lib-********.mjs 267 B 0 B
packages/mcp/dist/extensions/logger.js 1.44 kB 0 B
packages/mcp/dist/extensions/logger.mjs 204 B 0 B
packages/mcp/dist/extensions/mcp-********.js 3.93 kB 0 B
packages/mcp/dist/extensions/mcp-********.mjs 2.56 kB 0 B
packages/mcp/dist/extensions/mcp-sdk-compat.js 4.08 kB 0 B
packages/mcp/dist/extensions/mcp-sdk-compat.mjs 2.22 kB 0 B
packages/mcp/dist/extensions/posthog-events.js 10.5 kB 0 B
packages/mcp/dist/extensions/posthog-events.mjs 7.13 kB 0 B
packages/mcp/dist/extensions/posthog-mcp.js 6.6 kB 0 B
packages/mcp/dist/extensions/posthog-mcp.mjs 4.69 kB 0 B
packages/mcp/dist/extensions/sanitization.js 4.04 kB 0 B
packages/mcp/dist/extensions/sanitization.mjs 2.46 kB 0 B
packages/mcp/dist/extensions/session.js 4.78 kB 0 B
packages/mcp/dist/extensions/session.mjs 2.67 kB 0 B
packages/mcp/dist/extensions/sink.js 4.38 kB 0 B
packages/mcp/dist/extensions/sink.mjs 2.54 kB 0 B
packages/mcp/dist/extensions/tools.js 3.5 kB 0 B
packages/mcp/dist/extensions/tools.mjs 1.61 kB 0 B
packages/mcp/dist/extensions/tracing-helpers.js 2.41 kB 0 B
packages/mcp/dist/extensions/tracing-helpers.mjs 772 B 0 B
packages/mcp/dist/extensions/truncation.js 11.2 kB 0 B
packages/mcp/dist/extensions/truncation.mjs 9.54 kB 0 B
packages/mcp/dist/index.js 7.72 kB 0 B
packages/mcp/dist/index.mjs 4.46 kB 0 B
packages/mcp/dist/types.js 603 B 0 B
packages/mcp/dist/types.mjs 0 B 0 B 🆕
packages/mcp/dist/version.js 1.21 kB 0 B
packages/mcp/dist/version.mjs 45 B 0 B
packages/next/dist/app/PostHogProvider.js 3.34 kB 0 B
packages/next/dist/client/ClientPostHogProvider.js 2.37 kB 0 B
packages/next/dist/client/hooks.js 172 B 0 B
packages/next/dist/client/PostHogPageView.js 1.76 kB 0 B
packages/next/dist/index.client.js 401 B 0 B
packages/next/dist/index.edge.js 570 B 0 B
packages/next/dist/index.js 548 B 0 B
packages/next/dist/index.react-server.js 420 B 0 B
packages/next/dist/middleware/postHogMiddleware.js 3.68 kB 0 B
packages/next/dist/pages.client.js 225 B 0 B
packages/next/dist/pages.edge.js 570 B 0 B
packages/next/dist/pages.js 414 B 0 B
packages/next/dist/pages/getServerSidePostHog.js 1.99 kB 0 B
packages/next/dist/pages/PostHogPageView.js 1.55 kB 0 B
packages/next/dist/pages/PostHogProvider.js 1.61 kB 0 B
packages/next/dist/server.edge.js 795 B 0 B
packages/next/dist/server/captureRequestError.js 5.26 kB 0 B
packages/next/dist/server/clientCache.edge.js 305 B 0 B
packages/next/dist/server/clientCache.js 1.27 kB 0 B
packages/next/dist/server/clientCache.node.js 305 B 0 B
packages/next/dist/server/getPostHog.js 2.77 kB 0 B
packages/next/dist/server/onRequestError.js 1.39 kB 0 B
packages/next/dist/server/onRequestError.types.js 59 B 0 B
packages/next/dist/shared/browser.js 195 B 0 B
packages/next/dist/shared/config.js 2.23 kB 0 B
packages/next/dist/shared/constants.js 201 B 0 B
packages/next/dist/shared/cookie.js 540 B 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 6.1 kB 0 B
packages/nextjs-config/dist/config.mjs 4.53 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/strip-sourcemap-********.js 3.63 kB 0 B
packages/nextjs-config/dist/strip-sourcemap-********.mjs 1.77 kB 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/client.js 50.4 kB 0 B
packages/node/dist/client.mjs 48 kB 0 B
packages/node/dist/entrypoints/index.edge.js 3.85 kB 0 B
packages/node/dist/entrypoints/index.edge.mjs 720 B 0 B
packages/node/dist/entrypoints/index.node.js 5.67 kB 0 B
packages/node/dist/entrypoints/index.node.mjs 1.25 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 870 B 0 B
packages/node/dist/experimental.mjs 267 B 0 B
packages/node/dist/exports.js 6.75 kB 0 B
packages/node/dist/exports.mjs 582 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.17 kB 0 B
packages/node/dist/extensions/error-tracking/index.mjs 2.9 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.97 kB 0 B
packages/node/dist/extensions/express.mjs 2.78 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 41 kB 0 B
packages/node/dist/extensions/feature-flags/feature-flags.mjs 38.9 kB 0 B
packages/node/dist/extensions/nestjs.js 5.36 kB 0 B
packages/node/dist/extensions/nestjs.mjs 3.18 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/extensions/url-utils.js 2.22 kB 0 B
packages/node/dist/extensions/url-utils.mjs 785 B 0 B
packages/node/dist/feature-flag-evaluations.js 5.97 kB 0 B
packages/node/dist/feature-flag-evaluations.mjs 4.63 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.35 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/openfeature-node-provider/dist/index.js 1.76 kB 0 B
packages/openfeature-node-provider/dist/index.mjs 190 B 0 B
packages/openfeature-node-provider/dist/mapping.js 5.32 kB 0 B
packages/openfeature-node-provider/dist/mapping.mjs 3.21 kB 0 B
packages/openfeature-node-provider/dist/provider.js 3.99 kB 0 B
packages/openfeature-node-provider/dist/provider.mjs 2.6 kB 0 B
packages/openfeature-web-provider/dist/index.js 1.75 kB 0 B
packages/openfeature-web-provider/dist/index.mjs 184 B 0 B
packages/openfeature-web-provider/dist/mapping.js 5.29 kB 0 B
packages/openfeature-web-provider/dist/mapping.mjs 3.18 kB 0 B
packages/openfeature-web-provider/dist/provider.js 4.41 kB 0 B
packages/openfeature-web-provider/dist/provider.mjs 3.06 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 6.27 kB 0 B
packages/react-native/dist/error-tracking/index.js 9.97 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.84 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-********.js 3.66 kB 0 B
packages/react-native/dist/native-deps.js 8.73 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/OptionalPlugin.js 569 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/posthog-rn.js 55 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.55 kB 0 B
packages/react-native/dist/storage.js 5.76 kB 0 B
packages/react-native/dist/surveys/components/BottomSection.js 1.51 kB 0 B
packages/react-native/dist/surveys/components/Cancel.js 968 B 0 B
packages/react-native/dist/surveys/components/ConfirmationMessage.js 1.71 kB 0 B
packages/react-native/dist/surveys/components/QuestionHeader.js 1.43 kB 0 B
packages/react-native/dist/surveys/components/QuestionTypes.js 14 kB 0 B
packages/react-native/dist/surveys/components/SurveyModal.js 6.59 kB 0 B
packages/react-native/dist/surveys/components/Surveys.js 6.58 kB 0 B
packages/react-native/dist/surveys/getActiveMatchingSurveys.js 2.64 kB 0 B
packages/react-native/dist/surveys/icons.js 10 kB 0 B
packages/react-native/dist/surveys/index.js 600 B 0 B
packages/react-native/dist/surveys/PostHogSurveyProvider.js 6.28 kB 0 B
packages/react-native/dist/surveys/safeStyleSheet.js 448 B 0 B
packages/react-native/dist/surveys/survey-translations.js 1.11 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 11.9 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.3 kB 0 B
packages/react-native/dist/version.js 130 B 0 B
packages/react/dist/esm/index.js 21.1 kB 0 B
packages/react/dist/esm/slim/index.js 17.5 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.3 kB 0 B
packages/react/dist/umd/surveys/index.js 5.45 kB 0 B
packages/rollup-plugin/dist/index.js 2.44 kB 0 B
packages/rrweb/all/dist/rrweb-all.cjs 633 kB 0 B
packages/rrweb/all/dist/rrweb-all.js 633 kB 0 B
packages/rrweb/all/dist/rrweb-all.umd.cjs 636 kB 0 B
packages/rrweb/all/dist/rrweb-all.umd.min.cjs 299 kB 0 B
packages/rrweb/packer/dist/base-********.js 18.2 kB 0 B
packages/rrweb/packer/dist/base-********.cjs 18.3 kB 0 B
packages/rrweb/packer/dist/base-********.umd.cjs 18.7 kB 0 B
packages/rrweb/packer/dist/base-********.umd.min.cjs 9.5 kB 0 B
packages/rrweb/packer/dist/pack.cjs 347 B 0 B
packages/rrweb/packer/dist/pack.js 285 B 0 B
packages/rrweb/packer/dist/pack.umd.cjs 1.63 kB 0 B
packages/rrweb/packer/dist/pack.umd.min.cjs 1.11 kB 0 B
packages/rrweb/packer/dist/packer.cjs 257 B 0 B
packages/rrweb/packer/dist/packer.js 136 B 0 B
packages/rrweb/packer/dist/packer.umd.cjs 662 B 0 B
packages/rrweb/packer/dist/packer.umd.min.cjs 626 B 0 B
packages/rrweb/packer/dist/unpack.cjs 769 B 0 B
packages/rrweb/packer/dist/unpack.js 702 B 0 B
packages/rrweb/packer/dist/unpack.umd.cjs 1.17 kB 0 B
packages/rrweb/packer/dist/unpack.umd.min.cjs 955 B 0 B
packages/rrweb/plugins/rrweb-plugin-canvas-webrtc-record/dist/rrweb-plugin-canvas-webrtc-record.cjs 37.6 kB 0 B
packages/rrweb/plugins/rrweb-plugin-canvas-webrtc-record/dist/rrweb-plugin-canvas-webrtc-record.js 37.5 kB 0 B
packages/rrweb/plugins/rrweb-plugin-canvas-webrtc-record/dist/rrweb-plugin-canvas-webrtc-record.umd.cjs 38 kB 0 B
packages/rrweb/plugins/rrweb-plugin-canvas-webrtc-record/dist/rrweb-plugin-canvas-webrtc-record.umd.min.cjs 22.2 kB 0 B
packages/rrweb/plugins/rrweb-plugin-canvas-webrtc-replay/dist/rrweb-plugin-canvas-webrtc-replay.cjs 34.3 kB 0 B
packages/rrweb/plugins/rrweb-plugin-canvas-webrtc-replay/dist/rrweb-plugin-canvas-webrtc-replay.js 34.2 kB 0 B
packages/rrweb/plugins/rrweb-plugin-canvas-webrtc-replay/dist/rrweb-plugin-canvas-webrtc-replay.umd.cjs 34.7 kB 0 B
packages/rrweb/plugins/rrweb-plugin-canvas-webrtc-replay/dist/rrweb-plugin-canvas-webrtc-replay.umd.min.cjs 20.5 kB 0 B
packages/rrweb/plugins/rrweb-plugin-console-record/dist/rrweb-plugin-console-record.cjs 14.9 kB 0 B
packages/rrweb/plugins/rrweb-plugin-console-record/dist/rrweb-plugin-console-record.js 14.8 kB 0 B
packages/rrweb/plugins/rrweb-plugin-console-record/dist/rrweb-plugin-console-record.umd.cjs 15.4 kB 0 B
packages/rrweb/plugins/rrweb-plugin-console-record/dist/rrweb-plugin-console-record.umd.min.cjs 7.33 kB 0 B
packages/rrweb/plugins/rrweb-plugin-console-replay/dist/rrweb-plugin-console-replay.cjs 5.01 kB 0 B
packages/rrweb/plugins/rrweb-plugin-console-replay/dist/rrweb-plugin-console-replay.js 4.9 kB 0 B
packages/rrweb/plugins/rrweb-plugin-console-replay/dist/rrweb-plugin-console-replay.umd.cjs 5.44 kB 0 B
packages/rrweb/plugins/rrweb-plugin-console-replay/dist/rrweb-plugin-console-replay.umd.min.cjs 2.64 kB 0 B
packages/rrweb/plugins/rrweb-plugin-sequential-id-record/dist/rrweb-plugin-sequential-id-record.cjs 681 B 0 B
packages/rrweb/plugins/rrweb-plugin-sequential-id-record/dist/rrweb-plugin-sequential-id-record.js 548 B 0 B
packages/rrweb/plugins/rrweb-plugin-sequential-id-record/dist/rrweb-plugin-sequential-id-record.umd.cjs 1.12 kB 0 B
packages/rrweb/plugins/rrweb-plugin-sequential-id-record/dist/rrweb-plugin-sequential-id-record.umd.min.cjs 829 B 0 B
packages/rrweb/plugins/rrweb-plugin-sequential-id-replay/dist/rrweb-plugin-sequential-id-replay.cjs 933 B 0 B
packages/rrweb/plugins/rrweb-plugin-sequential-id-replay/dist/rrweb-plugin-sequential-id-replay.js 820 B 0 B
packages/rrweb/plugins/rrweb-plugin-sequential-id-replay/dist/rrweb-plugin-sequential-id-replay.umd.cjs 1.37 kB 0 B
packages/rrweb/plugins/rrweb-plugin-sequential-id-replay/dist/rrweb-plugin-sequential-id-replay.umd.min.cjs 968 B 0 B
packages/rrweb/record/dist/rrweb-record.cjs 187 kB 0 B
packages/rrweb/record/dist/rrweb-record.js 187 kB 0 B
packages/rrweb/record/dist/rrweb-record.umd.cjs 188 kB 0 B
packages/rrweb/record/dist/rrweb-record.umd.min.cjs 89.1 kB 0 B
packages/rrweb/replay/dist/rrweb-replay.cjs 448 kB 0 B
packages/rrweb/replay/dist/rrweb-replay.js 448 kB 0 B
packages/rrweb/replay/dist/rrweb-replay.umd.cjs 451 kB 0 B
packages/rrweb/replay/dist/rrweb-replay.umd.min.cjs 213 kB 0 B
packages/rrweb/rrdom-nodejs/dist/rrdom-nodejs.cjs 153 kB 0 B
packages/rrweb/rrdom-nodejs/dist/rrdom-nodejs.js 152 kB 0 B
packages/rrweb/rrdom-nodejs/dist/rrdom-nodejs.umd.cjs 154 kB 0 B
packages/rrweb/rrdom-nodejs/dist/rrdom-nodejs.umd.min.cjs 71.7 kB 0 B
packages/rrweb/rrdom/dist/rrdom.cjs 177 kB 0 B
packages/rrweb/rrdom/dist/rrdom.js 176 kB 0 B
packages/rrweb/rrdom/dist/rrdom.umd.cjs 178 kB 0 B
packages/rrweb/rrdom/dist/rrdom.umd.min.cjs 81.8 kB 0 B
packages/rrweb/rrweb-snapshot/dist/record.cjs 34.5 kB 0 B
packages/rrweb/rrweb-snapshot/dist/record.js 33.4 kB 0 B
packages/rrweb/rrweb-snapshot/dist/record.umd.cjs 55.8 kB 0 B
packages/rrweb/rrweb-snapshot/dist/record.umd.min.cjs 26.4 kB 0 B
packages/rrweb/rrweb-snapshot/dist/replay.cjs 141 kB 0 B
packages/rrweb/rrweb-snapshot/dist/replay.js 140 kB 0 B
packages/rrweb/rrweb-snapshot/dist/replay.umd.cjs 163 kB 0 B
packages/rrweb/rrweb-snapshot/dist/replay.umd.min.cjs 75 kB 0 B
packages/rrweb/rrweb-snapshot/dist/rrweb-********.cjs 2.27 kB 0 B
packages/rrweb/rrweb-snapshot/dist/rrweb-********.js 1.42 kB 0 B
packages/rrweb/rrweb-snapshot/dist/rrweb-********.umd.cjs 223 kB 0 B
packages/rrweb/rrweb-snapshot/dist/rrweb-********.umd.min.cjs 93.6 kB 0 B
packages/rrweb/rrweb-snapshot/dist/types-********.cjs 18.3 kB 0 B
packages/rrweb/rrweb-snapshot/dist/types-********.umd.cjs 18.8 kB 0 B
packages/rrweb/rrweb-snapshot/dist/types-********.umd.min.cjs 9.31 kB 0 B
packages/rrweb/rrweb-snapshot/dist/types-********.js 17.8 kB 0 B
packages/rrweb/rrweb/dist/rrweb.cjs 617 kB 0 B
packages/rrweb/rrweb/dist/rrweb.js 617 kB 0 B
packages/rrweb/rrweb/dist/rrweb.umd.cjs 617 kB 0 B
packages/rrweb/rrweb/dist/rrweb.umd.min.cjs 290 kB 0 B
packages/rrweb/types/dist/rrweb-types.cjs 5.75 kB 0 B
packages/rrweb/types/dist/rrweb-types.js 5.46 kB 0 B
packages/rrweb/types/dist/rrweb-types.umd.cjs 6.16 kB 0 B
packages/rrweb/types/dist/rrweb-types.umd.min.cjs 2.86 kB 0 B
packages/rrweb/utils/dist/rrweb-utils.cjs 6.41 kB 0 B
packages/rrweb/utils/dist/rrweb-utils.js 5.95 kB 0 B
packages/rrweb/utils/dist/rrweb-utils.umd.cjs 6.82 kB 0 B
packages/rrweb/utils/dist/rrweb-utils.umd.min.cjs 3.51 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-metric.js 1.37 kB 0 B
packages/types/dist/capture-metric.mjs 93 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 1.45 kB 0 B
packages/types/dist/index.mjs 122 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 14.1 kB 0 B
packages/web/dist/index.mjs 14 kB 0 B
packages/webpack-plugin/dist/config.js 1.47 kB 0 B
packages/webpack-plugin/dist/config.mjs 480 B 0 B
packages/webpack-plugin/dist/index.js 6.7 kB 0 B
packages/webpack-plugin/dist/index.mjs 2.94 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

@gustavohstrassburger
gustavohstrassburger marked this pull request as draft June 26, 2026 15:55
@gustavohstrassburger gustavohstrassburger moved this to In Progress in Feature Flags Jul 1, 2026
gustavohstrassburger added a commit to PostHog/posthog.com that referenced this pull request Jul 1, 2026
Documents the two official PostHog OpenFeature providers for JavaScript
(@posthog/openfeature-node and @posthog/openfeature-web): installation,
usage, evaluation-context mapping, supported flag types, and options.

Companion to the posthog-js implementation PR (PostHog/posthog-js#3994),
mirroring the existing OpenFeature (Python) page.

Generated-By: PostHog Code
Task-Id: 46a3f8c9-fbcd-460e-9457-fca583955e5a
@gustavohstrassburger
gustavohstrassburger marked this pull request as ready for review July 1, 2026 19:33
@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Reviews (2): Last reviewed commit: "refactor(openfeature): rename packages t..." | Re-trigger Greptile

@haacked haacked left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice addition! Two blocking issues inline: disabled flags throw instead of resolving to the default, and the node package's server-sdk peer range can't actually be satisfied. A few non-blocking suggestions too.

flagKey: string
): ResolutionDetails<string> {
const resolved = ensureResolved(result, flagKey)
if (resolved.variant === undefined) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: A disabled or unmatched flag ({ enabled: false, variant: undefined }) throws TypeMismatchError instead of resolving to the caller's default. OpenFeature's SDK catches the throw, sets errorCode: TYPE_MISMATCH and reason: ERROR, and fires every registered error hook. So a consumer with error hooks wired to their error tracker gets paged on every ordinary disabled-flag read.

The Python provider that this PR mirrors returns the caller's default with Reason.DEFAULT and no error in exactly this case, only raising TypeMismatchError when enabled is true. The identical bug exists in packages/openfeature-web-provider/src/mapping.ts. Neither provider.spec.ts has a test for disabled flags through these resolvers.

Add the !resolved.enabled guard before the throw in all three resolvers in both packages, and thread the caller's default value through:

 export function resolveStringDetails(
   result: PostHogFlagResult | undefined,
-  flagKey: string
+  flagKey: string,
+  defaultValue: string
 ): ResolutionDetails<string> {
   const resolved = ensureResolved(result, flagKey)
   if (resolved.variant === undefined) {
-    // A boolean flag has no string variant. Surface a type mismatch so the
-    // caller gets its default value (per the OpenFeature spec) rather than a
-    // surprising "true"/"false" string.
+    if (!resolved.enabled) {
+      return { value: defaultValue, reason: StandardResolutionReasons.DEFAULT }
+    }
+    // An enabled boolean flag has no string variant: this is a genuine mismatch.
     throw new TypeMismatchError(`Flag '${flagKey}' has no string variant (boolean flag).`)
   }
   return { value: resolved.variant, variant: resolved.variant, reason: reasonFor(resolved) }
 }

Apply the same guard in resolveNumberDetails (before the variant === undefined throw) and resolveObjectDetails (before the payload-type throw). Pass _defaultValue through at the call sites in both provider.ts files. Add disabled-flag cases to each it.each table asserting reason: DEFAULT and no errorCode.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7fffb7c (node) and 85f34db (web) — added the !resolved.enabled guard to all three resolvers, so a disabled/unmatched flag resolves to the caller's default with reason: DEFAULT instead of throwing. Threaded the default value through resolveString/Number/Object and their provider.ts call sites. Added disabled-flag test cases asserting reason: DEFAULT and no errorCode.

"@openfeature/core": "^1.11.0",
"@openfeature/server-sdk": "^1.7.0",
"posthog-node": ">=5.21.2"
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: The @openfeature/server-sdk@^1.7.0 lower bound conflicts with the @openfeature/core@^1.11.0 peer declared above. Every server-sdk version from 1.7.0 through 1.16.x requires @openfeature/core below 1.11.0, so no single installed version satisfies both constraints. A consumer on any server-sdk in that range hits an ERESOLVE failure on npm or yarn install; pnpm only warns unless strict-peer-dependencies is set.

server-sdk@1.17.0 is the first version whose own core peer (^1.6.0) overlaps with 1.11.0:

   "peerDependencies": {
     "@openfeature/core": "^1.11.0",
-    "@openfeature/server-sdk": "^1.7.0",
+    "@openfeature/server-sdk": "^1.17.0",
     "posthog-node": ">=5.21.2"
   },

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7fffb7c — bumped the @openfeature/server-sdk peer floor to ^1.17.0, the first version whose own @openfeature/core peer overlaps ^1.11.0.

}

async onContextChange(_oldContext: EvaluationContext, newContext: EvaluationContext): Promise<void> {
await this._reconcile(newContext)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: onContextChange reconciles on every call even when the context is unchanged. @openfeature/web-sdk's setContext runs the provider's handler on every call with no equality check, and _reconcile doesn't diff against the previous context either. So a host that re-sets an equivalent context (a React integration passing a freshly-constructed object on every render, say) triggers a $groupidentify event and a reloadFeatureFlags() request on every call, since consecutive calls are typically more than 5ms apart and don't fold into the SDK's debounce.

Diff newContext against the previous context and skip reconciliation when nothing changed:

async onContextChange(oldContext: EvaluationContext, newContext: EvaluationContext): Promise<void> {
  if (contextsEqual(oldContext, newContext)) {
    return
  }
  await this._reconcile(newContext)
}

groupProperties is nested, so this needs a deep compare, not a shallow one. initialize() should keep calling _reconcile unconditionally since first load always needs it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed on the web PR #4069 in 85f34dbonContextChange now deep-compares old vs new context (handling nested groupProperties) and skips reconciliation when nothing changed. initialize() still reconciles unconditionally.

try {
await this._client.reloadFeatureFlags()
} catch {
// Intentionally ignored: remote evaluation remains available.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: This swallows every preload error with no log, not just the documented no-personalApiKey no-op case. A genuine misconfiguration during reloadFeatureFlags() (bad host, network failure) disappears silently, so a user whose local evaluation never warms up has no visibility. The Python reference that this PR mirrors logs a warning with the exception in the equivalent spot.

     try {
       await this._client.reloadFeatureFlags()
-    } catch {
-      // Intentionally ignored: remote evaluation remains available.
+    } catch (err) {
+      // Remote evaluation still works, so don't block readiness, but surface
+      // the failure so a real misconfiguration isn't invisible.
+      console.warn('[PostHogServerProvider] initialize() flag preload failed; remote evaluation still available.', err)
     }

no-console is already disabled for this package in .eslintrc.cjs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7fffb7cinitialize() now console.warns on a preload failure (with the error) instead of swallowing it, so a genuine misconfiguration on a client set up for local evaluation is visible. Remote evaluation still works, so readiness isn't blocked.

})

it.each<[string, FlagResult | undefined, Resolve, ErrorCode]>([
[

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: This table only covers a string read on a boolean flag and a missing flag. The node package's equivalent table also has a "number from a non-numeric variant" case and an "object from a non-object payload" case, exercising resolveNumberDetails's !Number.isFinite throw and resolveObjectDetails's non-object-payload throw. Neither branch is exercised here, so a regression in either check on the web side ships silently.

Add the two missing rows, mirroring the node spec:

[
  'number from a non-numeric variant',
  { key: 'flag', enabled: true, variant: 'not-a-number' },
  (p) => p.resolveNumberEvaluation('flag', 0),
  ErrorCode.TYPE_MISMATCH,
],
[
  'object from a non-object payload',
  { key: 'flag', enabled: true, variant: 'x', payload: 'not-an-object' },
  (p) => p.resolveObjectEvaluation('flag', {}),
  ErrorCode.TYPE_MISMATCH,
],

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed on the web PR #4069 in 85f34db — added the missing number from a non-numeric variant and object from a non-object payload cases (plus disabled-flag cases) to the parameterised tables.

@@ -0,0 +1,150 @@
/**

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: This file is identical to openfeature-node-provider/src/mapping.ts apart from two words in the header comment, and imports only from @openfeature/core, not from posthog-js, posthog-node, or any runtime SDK. So the dependency-isolation argument that justifies splitting the providers into two packages doesn't apply to the mapping layer: a shared internal module would peer on @openfeature/core, which both already require, adding no extra peer constraints. The concrete cost of duplication is drift: the disabled-flag type-mismatch bug exists identically in both files, and any fix lands twice and stays in sync.

Was a shared internal module considered and set aside deliberately? The likely counter-argument is that with bundle: false in both rslib.config.ts, extracting means either publishing a third @posthog/openfeature-* package or setting bundle: true here, which would deviate from the @posthog/mcp convention these packages otherwise follow. For 150 lines with two consumers that's a defensible tradeoff, just want to confirm it was weighed rather than copy-paste.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considered and set aside deliberately. With bundle: false (the @posthog/mcp convention both packages follow), sharing the mapping means either publishing a third @posthog/openfeature-* package or switching to bundle: true — both deviate from convention for ~150 lines. We accept the duplication; the drift risk you flagged (the disabled-flag bug) is now fixed in both packages in lockstep (7fffb7c / 85f34db). Happy to revisit if a third consumer appears.

"package": "pnpm pack --out $PACKAGE_DEST/%s.tgz"
},
"files": [
"dist/"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: No engines block here, while the node provider and every other sibling package (@posthog/mcp, @posthog/nextjs-config, @posthog/nuxt) declare one despite targeting the web. Without it, consumers on unsupported Node versions get no install-time warning when building or testing this package.

     "package": "pnpm pack --out $PACKAGE_DEST/%s.tgz"
   },
+  "engines": {
+    "node": "^20.20.0 || >=22.22.0"
+  },
   "files": [
     "dist/"
   ],

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed on the web PR #4069 in 85f34db — added an engines block (^20.20.0 || >=22.22.0) matching the node package.

let subscribed = false

const finish = (): void => {
if (settled) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: finish references timer and unsubscribe before either const below it is declared. It works today only because finish never actually runs until after both are assigned, so a future edit that lets it run earlier would hit a temporal-dead-zone throw instead of a type error. Declaring unsubscribe and timer above finish would let the closure read top-to-bottom.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed on the web PR #4069 in 85f34db — restructured _reloadFlags to collect cleanups in an array declared above finish, so the closure no longer forward-references timer/unsubscribe.

Comment thread .changeset/openfeature-providers.md Outdated
Comment on lines +2 to +3
'@posthog/openfeature-node-provider': minor
'@posthog/openfeature-web-provider': minor

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could be 2 PRs tbh

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — split into two PRs. This one (#3994) now ships only the server provider (@posthog/openfeature-node-provider); the web provider (@posthog/openfeature-web-provider) moved to #4069. Each has its own changeset and release-matrix entry.

@marandaneto

Copy link
Copy Markdown
Member

@marandaneto

Copy link
Copy Markdown
Member

also append to https://github.com/PostHog/posthog-js#packages

@marandaneto

Copy link
Copy Markdown
Member

@marandaneto

Copy link
Copy Markdown
Member

can we add examples to https://github.com/PostHog/posthog-js/tree/main/examples so we know/can test this outside unit tests? or extend one of our examples for node/web

@gustavohstrassburger gustavohstrassburger moved this from In Review to In Progress in Feature Flags Jul 3, 2026
gustavohstrassburger added a commit that referenced this pull request Jul 3, 2026
Official PostHog provider for the OpenFeature web SDK (@openfeature/web-sdk),
backed by posthog-js. Split out from the combined providers PR per review
(marandaneto) — the server provider (@posthog/openfeature-node-provider)
ships in #3994.

The browser model is single-user and synchronous: posthog-js owns the user
identity and holds flags in memory, so evaluation is synchronous and the
static evaluation context is reconciled into the SDK on
initialize()/onContextChange(). Resolves booleans from `enabled`, strings
from the multivariate variant, numbers from the parsed variant, and objects
from the JSON payload; missing flag -> FLAG_NOT_FOUND, wrong type ->
TYPE_MISMATCH.

Not published yet at the workflow level beyond being wired into the release
matrix and carrying a changeset; publishing still requires the standard
merge + NPM Release approval flow.

Generated-By: PostHog Code
Task-Id: 46a3f8c9-fbcd-460e-9457-fca583955e5a
@gustavohstrassburger gustavohstrassburger changed the title feat: add official PostHog OpenFeature providers (server + web) feat: add official PostHog OpenFeature server provider (Node) Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

@marandaneto addressed your follow-ups:

@gustavohstrassburger gustavohstrassburger moved this from In Progress to In Review in Feature Flags Jul 3, 2026
gustavohstrassburger added a commit that referenced this pull request Jul 6, 2026
* feat(openfeature): add web provider (@posthog/openfeature-web-provider)

Official PostHog provider for the OpenFeature web SDK (@openfeature/web-sdk),
backed by posthog-js. Split out from the combined providers PR per review
(marandaneto) — the server provider (@posthog/openfeature-node-provider)
ships in #3994.

The browser model is single-user and synchronous: posthog-js owns the user
identity and holds flags in memory, so evaluation is synchronous and the
static evaluation context is reconciled into the SDK on
initialize()/onContextChange(). Resolves booleans from `enabled`, strings
from the multivariate variant, numbers from the parsed variant, and objects
from the JSON payload; missing flag -> FLAG_NOT_FOUND, wrong type ->
TYPE_MISMATCH.

Not published yet at the workflow level beyond being wired into the release
matrix and carrying a changeset; publishing still requires the standard
merge + NPM Release approval flow.

Generated-By: PostHog Code
Task-Id: 46a3f8c9-fbcd-460e-9457-fca583955e5a

* fix(openfeature-web): address review feedback (haacked, Greptile, manoel)

Provider/mapping fixes (haacked):
- Disabled or unmatched flags now resolve to the caller's default with
  reason=DEFAULT instead of throwing TypeMismatchError (which set reason=ERROR
  and fired error hooks on every ordinary disabled-flag read). Threaded the
  default value through resolveString/Number/Object.
- onContextChange now deep-compares old vs new context and skips reconciliation
  (group()/reloadFeatureFlags()) when nothing changed.
- Added an engines block.
- Restructured _reloadFlags so the cleanup closure no longer references
  timer/unsubscribe before their declarations (temporal-dead-zone fragility).

Tests:
- Added disabled-flag resolution cases (reason=DEFAULT, no errorCode) and the
  missing number/object TypeMismatchError branches to the parameterised tables.
- Converted the reload-timeout test to fake timers (Greptile) so it's
  deterministic and instant instead of waiting on real wall-clock time.

Repo wiring (manoel):
- Listed the package in the root README and CHANGELOG package lists and the PR
  template's "Libraries affected" checklist.
- Added examples/example-openfeature-web (Vite) demonstrating the provider.

Generated-By: PostHog Code
Task-Id: 46a3f8c9-fbcd-460e-9457-fca583955e5a
Rebuilt on top of current main after the web provider (#4069) merged, resolving conflicts in the shared registration files by adding the node entries alongside the now-merged web entries.

Adds @posthog/openfeature-node-provider (OpenFeature server SDK, backed by posthog-node), an example (examples/example-openfeature-node), a changeset, and registration entries. Includes review fixes: disabled/unmatched flags resolve to the caller's default with reason=DEFAULT instead of throwing; @openfeature/server-sdk peer floor raised to ^1.17.0; initialize() logs a warning on preload failure.
@gustavohstrassburger
gustavohstrassburger force-pushed the posthog-code/openfeature-provider branch from 7fffb7c to 92704a5 Compare July 7, 2026 14:38

@haacked haacked left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both blocking issues from before are fixed. One follow-up suggestion inline on initialize()'s error handling, plus a couple of nits.

try {
await this._client.reloadFeatureFlags()
} catch (err) {
// Remote evaluation still works, so don't block readiness — but surface

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: This catch can't fire for the misconfigurations the comment names. reloadFeatureFlags() awaits featureFlagsPoller.loadFeatureFlags(true), which wraps its internal _loadFeatureFlags() in its own .catch() and only debug-logs. Every real failure (bad host, invalid key, network error, 401/403/429) routes through posthog-node's onError callback instead of a rejection, but that callback's event emitter drops the event when nothing's listening. So the promise here never rejects in production, and console.warn never runs for the cases this comment describes.

To actually surface those failures, listen on the channel posthog-node uses instead:

   async initialize(): Promise<void> {
-    // Preload locally-evaluated flag definitions. reloadFeatureFlags() safely
-    // no-ops without a personalApiKey, and posthog-node catches its own poller
-    // errors, so a preload failure must not make the OpenFeature client
-    // un-ready — remote evaluation still works.
-    try {
-      await this._client.reloadFeatureFlags()
-    } catch (err) {
-      // Remote evaluation still works, so don't block readiness — but surface
-      // the failure so a genuine misconfiguration (bad host, network error) on
-      // a client set up for local evaluation isn't invisible.
-      // eslint-disable-next-line no-console
-      console.warn('[PostHogServerProvider] initialize() flag preload failed; remote evaluation still available.', err)
-    }
+    // posthog-node swallows poller errors internally and surfaces the
+    // client/network-error class via its `error` event, so listen on that
+    // channel around the reload. Remote evaluation still works either way,
+    // so this never blocks readiness.
+    let preloadError: Error | undefined
+    const unsubscribe = this._client.on('error', (err: Error) => {
+      preloadError = err
+    })
+    try {
+      await this._client.reloadFeatureFlags()
+    } finally {
+      unsubscribe()
+    }
+    if (preloadError) {
+      // eslint-disable-next-line no-console
+      console.warn('[PostHogServerProvider] initialize() flag preload failed; remote evaluation still available.', preloadError)
+    }
   }

One caveat: posthog-node only routes ClientError (401/403/429, bad response) through that event, so a raw network/DNS failure on a bad host still won't surface either way.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — confirmed reloadFeatureFlags() never rejects (poller .catch() at feature-flags.ts:748 only debug-logs), so the try/catch was dead code. Fixed in c2ad670 by listening on the client's error event around the reload, per your suggestion. Kept your caveat in the comment: only ClientError (401/403/429, bad response) routes through that event, so a raw network/DNS failure on a bad host still won't surface.

expect(reloadFeatureFlags).toHaveBeenCalledTimes(1)
})

it('does not reject when preloading fails', async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: This test proves initialize() doesn't throw when reloadFeatureFlags rejects, but the real posthog-node client never rejects here (see provider.ts:84), so it doesn't cover the actual failure path.

If you switch to listening on the client's error event there, test that instead: emit an error on 'error' and assert console.warn fires with it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c2ad670 — the test now emits the client's error event while the reload is in flight and asserts console.warn fires with it, plus a companion case asserting no warning on a clean preload.

}
throw new TypeMismatchError(`Flag '${flagKey}' has no variant to parse as a number.`)
}
const value = Number(resolved.variant)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Number('') evaluates to 0, not NaN, so a multivariate flag with an empty-string variant resolves to 0 instead of throwing TypeMismatchError. The 'not-a-number' test case only covers the NaN branch; worth an added case if you want this edge nailed down, though real variant keys are unlikely to be empty strings.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c2ad670resolveNumberDetails now rejects a blank variant (variant.trim() === '') before the Number.isFinite check, so an empty/whitespace variant throws TypeMismatchError instead of resolving to 0. Added an empty-string variant case to the throws table.

(The web package's mapping has the same line; since it's already merged I left it for a follow-up rather than reintroduce cross-package churn here.)

// id comes from the evaluation context's targetingKey; other attributes
// map to person/group properties. Swap these keys for real flags.
const context = { targetingKey: 'user_distinct_id', plan: 'enterprise' }
const result = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: These three flag reads run sequentially even though they're independent. Promise.all would model the better pattern here since example code tends to get copy-pasted, though at 3 flags in a one-shot demo the actual cost is negligible.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in c2ad670 — the three independent reads now run via Promise.all.

@github-project-automation github-project-automation Bot moved this from In Review to Approved in Feature Flags Jul 8, 2026
- initialize(): reloadFeatureFlags() never rejects — posthog-node swallows
  its poller errors and surfaces the client/network-error class via its
  `error` event. Listen on that event around the reload so a genuine
  misconfiguration is surfaced via console.warn; the previous try/catch was
  dead code. Remote evaluation still works, so this never blocks readiness.
- mapping: guard blank number variants. Number('') and Number('   ') are 0
  (not NaN), so an empty/whitespace variant previously resolved to 0 instead
  of throwing TypeMismatchError.
- tests: replace the unrealistic reject-based preload test with one that
  emits the client's `error` event and asserts console.warn fires (plus a
  no-warn-on-success case); add an empty-string number-variant case.
- example: evaluate the three independent flag reads with Promise.all.

Generated-By: PostHog Code
Task-Id: 46a3f8c9-fbcd-460e-9457-fca583955e5a
Comment on lines +140 to +149
return this._client.getFeatureFlagResult(flagKey, distinctId, {
groups: Object.keys(groups).length > 0 ? groups : undefined,
personProperties:
Object.keys(personProperties).length > 0 ? (personProperties as Record<string, string>) : undefined,
groupProperties:
Object.keys(groupProperties).length > 0
? (groupProperties as Record<string, Record<string, string>>)
: undefined,
sendFeatureFlagEvents: this._sendFeatureFlagEvents,
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect type casts will cause runtime type mismatches. The personProperties is Record<string, unknown> but is cast to Record<string, string>, and groupProperties is Record<string, Record<string, unknown>> but is cast to Record<string, Record<string, string>>. OpenFeature contexts can contain any JSON value (numbers, booleans, objects, arrays, etc.), not just strings.

If getFeatureFlagResult truly requires string values, these should be converted/serialized, not just cast. If it accepts any values, remove the incorrect casts:

personProperties:
  Object.keys(personProperties).length > 0 ? personProperties : undefined,
groupProperties:
  Object.keys(groupProperties).length > 0 ? groupProperties : undefined,

The current casts only hide the type mismatch at compile time but will pass invalid types at runtime when non-string values are present in the evaluation context.

Suggested change
return this._client.getFeatureFlagResult(flagKey, distinctId, {
groups: Object.keys(groups).length > 0 ? groups : undefined,
personProperties:
Object.keys(personProperties).length > 0 ? (personProperties as Record<string, string>) : undefined,
groupProperties:
Object.keys(groupProperties).length > 0
? (groupProperties as Record<string, Record<string, string>>)
: undefined,
sendFeatureFlagEvents: this._sendFeatureFlagEvents,
})
return this._client.getFeatureFlagResult(flagKey, distinctId, {
groups: Object.keys(groups).length > 0 ? groups : undefined,
personProperties:
Object.keys(personProperties).length > 0 ? personProperties : undefined,
groupProperties:
Object.keys(groupProperties).length > 0 ? groupProperties : undefined,
sendFeatureFlagEvents: this._sendFeatureFlagEvents,
})

Spotted by Graphite

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch on the intent — you're right that the values must reach getFeatureFlagResult unchanged, since the flags API matches on the native JSON type (e.g. a numeric age >= 21 filter), so serializing to strings would silently break comparisons.

The one wrinkle is that removing the casts outright doesn't compile: posthog-node's public option type narrows personProperties to Record<string, string> (and groupProperties to Record<string, Record<string, string>>), even though the internal type and the flags API accept any JSON value. So the assertion is needed purely to bridge that too-narrow upstream type — it changes nothing at runtime; the values are forwarded exactly as received.

Fixed in 9f8a7fe: values are passed through unchanged with a documented comment, empty records collapse to undefined via a nonEmpty() helper, and a new test (forwards non-string property values unchanged) asserts that age: 42, beta: true, and seats: 25 reach the client without coercion.

The flags API matches person/group properties on their native JSON type
(e.g. a numeric `age >= 21` filter), but posthog-node's public option type
narrows them to `Record<string, string>`. Forward context values through
unchanged with a documented assertion that only bridges the too-narrow
upstream type — coercing to strings here would break native-type matching.
Add a `nonEmpty` helper and a test proving no coercion happens.

Also repair the pnpm-lock.yaml entry for the openfeature-node-provider
importer, which the main merge left pointing at stale peer resolutions
(api-extractor 7.55.1 -> 7.58.9, babel/core 7.28.5 -> 7.29.7).

Generated-By: PostHog Code
Task-Id: 46a3f8c9-fbcd-460e-9457-fca583955e5a
@gustavohstrassburger
gustavohstrassburger merged commit 759b66f into main Jul 9, 2026
50 checks passed
@gustavohstrassburger
gustavohstrassburger deleted the posthog-code/openfeature-provider branch July 9, 2026 02:39
@github-project-automation github-project-automation Bot moved this from Approved to Done in Feature Flags Jul 9, 2026
gustavohstrassburger added a commit to PostHog/posthog.com that referenced this pull request Jul 9, 2026
* Add OpenFeature (JavaScript) feature flags provider docs

Documents the two official PostHog OpenFeature providers for JavaScript
(@posthog/openfeature-node and @posthog/openfeature-web): installation,
usage, evaluation-context mapping, supported flag types, and options.

Companion to the posthog-js implementation PR (PostHog/posthog-js#3994),
mirroring the existing OpenFeature (Python) page.

Generated-By: PostHog Code
Task-Id: 46a3f8c9-fbcd-460e-9457-fca583955e5a

* Update package names to add -provider suffix

Match the renamed packages: @posthog/openfeature-node-provider and
@posthog/openfeature-web-provider (install commands and import snippets).

Generated-By: PostHog Code
Task-Id: 46a3f8c9-fbcd-460e-9457-fca583955e5a

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants