Skip to content

feat: add registerOptions and unregisterOptions - #715

Merged
turnipdabeets merged 3 commits into
chore/v6-posthog-ios-4from
feat/v6-register-options
Nov 9, 2026
Merged

turnipdabeets merged 3 commits into
chore/v6-posthog-ios-4from
feat/v6-register-options

Conversation

@turnipdabeets

Copy link
Copy Markdown
Contributor

💡 Motivation and Context

Capture v1 moved $process_person_profile, $cookieless_mode, $ignore_sent_at and $product_tour_id into per-event options, but 6.0 had no way to set an option for every event: the migration guide told apps to register the legacy property instead, which is the shim we want docs and the wizard to move people off. Eli asked for global options on the client SDKs too (capture v1 tracking thread; SD-17 in the v1 rubric; it reverses the client exemption on sdk-specs#146).

This adds Posthog().registerOptions(Map) and unregisterOptions(String key), named like posthog-node's:

  • An event's own options win over registered ones key by key, beforeSend sees and can change the result, they persist across restarts, and reset() clears them, like register().
  • iOS, macOS, Android: the plugin calls wrapper-only native APIs (feat: let wrapper SDKs register capture options for every event posthog-ios#1045 behind @_spi(PostHogInternal), feat: let wrapper SDKs register capture options for every event posthog-android#954 behind @PostHogInternal), so the native SDKs gain no public API and every natively built event gets them, including lifecycle, $feature_flag_called and crash events.
  • Windows, Linux: stored in the Dart core and filled under the event's options before beforeSend, and on $identify, $create_alias and $groupidentify, which don't pass through beforeSend.
  • Web: posthog-js has no options, so known keys are registered as their legacy properties (which posthog-js sends under each event's own properties) and other keys are dropped, as for per-event options.

The migration guide's "There are no global options" line now points to registerOptions, and the 6.0 changeset has a line for it.

💚 How did you test it?

  • New tests: desktop core (precedence, $identify/$create_alias/$groupidentify, registered process_person_profile replaces the personless default, unregister and reset, persistence), desktop plugin (beforeSend sees registered options and can remove them), method channel (registerOptions/unregisterOptions arguments, null values dropped), web (legacy property mapping, unregister).
  • flutter test passes for core/posthog_core_test.dart, posthog_flutter_io_test.dart and posthog_flutter_desktop_test.dart; flutter test --platform chrome test/posthog_flutter_web_handler_test.dart passes the new tests (three existing app-version tests there need the build-injected app version and fail locally without it).
  • Built the example app for Android (posthog-android #954 published to mavenLocal as 4.1.81) and for the iOS simulator (posthog-ios #1045 as an SPM path dependency), so both plugin calls compile.
  • flutter analyze, make checkFormatKotlin and make checkFormatSwift are clean; scripts/check-api-dart.sh --update regenerated the API snapshot.

How to test locally

CI can't build iOS or Android until posthog-ios and posthog-android 4.0 ship. To try it:

  1. Android: in posthog-android on feat/v4-register-options-wrapper, run CI=false ./gradlew :posthog:publishToMavenLocal :posthog-android:publishToMavenLocal -PcoreVersion=7.1.81 -PandroidVersion=4.1.81 -x signMavenPublication (any version above the highest 4.x in ~/.m2), and add mavenLocal() to example/android/build.gradle.kts's repositories.
  2. iOS: point posthog_flutter/darwin/posthog_flutter/Package.swift at a local checkout of posthog-ios on feat/v4-register-options-wrapper with .package(path: ".../posthog-ios") (the folder must be named posthog-ios).
  3. In the example app, call Posthog().registerOptions({'cookieless_mode': true}), capture an event, and check its options in a beforeSend callback or the request body.

📝 Checklist

  • All commits are signed
  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Added a line to .changeset/v6-major-release.md

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Written with Claude Code under Anna's direction. Native support stays wrapper-only by design: iOS and Android apps keep register() super properties. On web, unregisterOptions('cookieless_mode') also removes a $cookieless_mode the app registered with register(), since both are the same posthog-js super property.

🤖 Generated with Claude Code

Global capture options, the options counterpart of register(), so apps
stop relying on registering legacy option properties such as
$cookieless_mode. An event's own options win key by key, beforeSend sees
the result, and reset() clears them.

iOS, macOS and Android call wrapper-only native APIs, so the native SDKs
gain no public API. Windows and Linux keep them in the Dart core. Web
registers known options as their legacy properties, since posthog-js has
no options.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@turnipdabeets
turnipdabeets requested a review from a team as a code owner November 9, 2026 17:33
@turnipdabeets turnipdabeets self-assigned this Nov 9, 2026
@veria-ai

veria-ai Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

PR overview

No security issues were found in this pull request.

Reviews

@posthog posthog Bot 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.

PostHog Review · Standard · reviewed 6b34cdc: 1 Should fix.

See this review and its findings in PostHog Review. Comment @posthog review --deep for a Deep review. A Deep review takes longer and looks deeper than a Standard review.

@github-actions

github-actions Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

posthog-flutter Compliance Report

Date: 2026-11-09T18:47:23.854347+00:00
Duration: 370105ms

✅ All Tests Passed!

232/232 tests passed


Capture_V1 Tests

✅ 105/105 tests passed

View Details
Test Status Duration
Endpoint And Method.Targets V1 Endpoint ✅ 195ms
Endpoint And Method.Does Not Use Legacy Endpoints ✅ 123ms
Required Headers.Has Authorization Bearer Header ✅ 121ms
Required Headers.Has Content Type Json ✅ 124ms
Required Headers.Has Posthog Sdk Info Format ✅ 121ms
Required Headers.Has Posthog Attempt Header ✅ 120ms
Required Headers.Has Posthog Request Id ✅ 120ms
Required Headers.Has Posthog Request Timestamp ✅ 121ms
Required Headers.Has User Agent ✅ 122ms
Body Format.Body Has Created At And Batch ✅ 119ms
Body Format.No Api Key In Body ✅ 119ms
Body Format.No Sent At In Body ✅ 119ms
Event Format.Event Has Required Root Fields ✅ 118ms
Event Format.Event Uuid Is Valid ✅ 118ms
Event Format.Event Timestamp Is Rfc3339 ✅ 120ms
Event Format.Non Utc Event Timestamp Is Converted To Utc ✅ 119ms
Event Format.Distinct Id Is String ✅ 119ms
Event Format.Distinct Id At Root Not Properties ✅ 119ms
Event Format.Custom Properties Preserved ✅ 120ms
Event Format.Set Properties Preserved ✅ 121ms
Event Format.Set Once Properties Preserved ✅ 120ms
Event Format.Groups Properties Preserved ✅ 118ms
Event Format.Sdk Generates Uuid If Not Provided ✅ 118ms
Event Format.Event Has Required Root Fields Batch ✅ 122ms
Event Format.Event Uuid Is Valid Batch ✅ 124ms
Event Format.Event Timestamp Is Rfc3339 Batch ✅ 123ms
Event Format.Distinct Id Is String Batch ✅ 123ms
Event Format.Distinct Id At Root Not Properties Batch ✅ 124ms
Event Format.Custom Properties Preserved Batch ✅ 127ms
Event Format.Set Properties Preserved Batch ✅ 126ms
Event Format.Set Once Properties Preserved Batch ✅ 125ms
Event Format.Groups Properties Preserved Batch ✅ 124ms
Event Format.Sdk Generates Uuid If Not Provided Batch ✅ 122ms
Batch Behavior.Multiple Events In Single Batch ✅ 130ms
Batch Behavior.Batch Envelope Smoke ✅ 128ms
Batch Behavior.Flush With No Events Sends Nothing ✅ 109ms
Batch Behavior.Flush At Triggers Batch ✅ 1017ms
Batch Behavior.Created At Reflects Batch Creation Time ✅ 120ms
Deduplication.Generates Unique Uuids ✅ 128ms
Deduplication.Different Events Same Content Different Uuids ✅ 120ms
Deduplication.Preserves Uuid On Retry ✅ 5127ms
Deduplication.Preserves Timestamp On Retry ✅ 5125ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 5129ms
Deduplication.No Duplicate Events In Batch ✅ 128ms
Header Behavior On Retry.Attempt Header Starts At One ✅ 116ms
Header Behavior On Retry.Attempt Header Increments On Retry ✅ 10128ms
Header Behavior On Retry.Request Id Preserved On Retry ✅ 5122ms
Header Behavior On Retry.Different Requests Have Different Request Ids ✅ 2232ms
Header Behavior On Retry.Request Timestamp Changes On Retry ✅ 5125ms
Response Format Validation.Success Response Has Uuid Keyed Results ✅ 118ms
Response Format Validation.Success Response Has Ok For Each Event ✅ 121ms
Response Format Validation.Success No Retry After When All Ok ✅ 119ms
Response Format Validation.Success Retry After Present When Retry Events ✅ 118ms
Response Format Validation.Success No Retry After When Drop Only ✅ 119ms
Response Format Validation.Response Echoes Request Id ✅ 116ms
Retry Behavior.Retries On 408 ✅ 5120ms
Retry Behavior.Retries On 500 ✅ 5124ms
Retry Behavior.Retries On 503 ✅ 5123ms
Retry Behavior.Retries On 504 ✅ 5123ms
Retry Behavior.Retries On 404 ✅ 5127ms
Retry Behavior.Retries On 429 ✅ 5123ms
Retry Behavior.Retries On 502 ✅ 5123ms
Retry Behavior.Retryable Errors Have Retry After ✅ 2121ms
Retry Behavior.Respects Retry After On Retryable Error ✅ 8129ms
Retry Behavior.Does Not Retry On 400 ✅ 2120ms
Retry Behavior.Does Not Retry On 401 ✅ 2121ms
Retry Behavior.Does Not Retry On 402 ✅ 2120ms
Retry Behavior.Does Not Retry On 413 ✅ 2120ms
Retry Behavior.Does Not Retry On 415 ✅ 2121ms
Retry Behavior.Non Retryable Errors Have No Retry After ✅ 2121ms
Retry Behavior.Implements Backoff ✅ 12127ms
Partial Batch Handling.Handles 200 Full Success ✅ 2120ms
Partial Batch Handling.Handles 200 With All Ok ✅ 3126ms
Partial Batch Handling.Does Not Retry Dropped Events ✅ 3123ms
Partial Batch Handling.Does Not Retry Warning Events ✅ 3124ms
Partial Batch Handling.Prunes Ok Events On Partial Retry ✅ 5123ms
Partial Batch Handling.Prunes Dropped Events On Partial Retry ✅ 5126ms
Partial Batch Handling.Retries Only Retry Events From Partial ✅ 5129ms
Partial Batch Handling.Partial Retry Preserves Uuids ✅ 5125ms
Partial Batch Handling.Partial Retry Attempt Header Increments ✅ 5126ms
Partial Batch Handling.Partial Retry Request Id Preserved ✅ 5126ms
Partial Batch Handling.Respects Retry After On Partial ✅ 5126ms
Partial Batch Handling.Unknown Result Treated As Terminal ✅ 3123ms
Partial Batch Handling.Mixed Ok Drop Warning No Retry ✅ 3129ms
Compression.Sends Gzip Content Encoding ✅ 120ms
Compression.No Content Encoding When Disabled ✅ 116ms
Compression.Compressed Body Is Decompressible ✅ 115ms
Error Handling.Does Not Retry On Unknown 4Xx ✅ 2118ms
Event Options.Cookieless Mode Override ✅ 117ms
Event Options.Disable Skew Correction Override ✅ 116ms
Event Options.Process Person Profile Override ✅ 116ms
Event Options.Product Tour Id Override ✅ 116ms
Event Options.Unset Options Omitted ✅ 116ms
Event Options.Options Override In Batch ✅ 120ms
Event Options.Unknown Option Passes Through ✅ 117ms
Event Options.Option Value Is Not Converted ✅ 116ms
Event Options.Lenient Bool Option Values Are Not Converted ✅ 116ms
Event Options.Option Wins Over Legacy Property ✅ 115ms
Event Options.Legacy Property Fills Unset Option ✅ 114ms
Event Options.Null Option Falls Back To Legacy Property ✅ 115ms
Property Layers.Caller Property Beats Registered Property ✅ 116ms
Property Layers.Before Send Runs Last ✅ 117ms
Fail Open.Empty Api Key Sends Nothing ✅ 1111ms
Fail Open.Personal Api Key Sends Nothing ✅ 1111ms
Geoip And Historical Migration.Historical Migration Absent By Default ✅ 115ms

Capture_Ai_V1 Tests

✅ 110/110 tests passed

View Details
Test Status Duration
Endpoint And Method.Targets V1 Endpoint ✅ 115ms
Endpoint And Method.Does Not Use Legacy Endpoints ✅ 116ms
Endpoint And Method.Capture Does Not Reroute Ai Named Events ✅ 115ms
Endpoint And Method.Capture Ai Does Not Reroute Non Ai Events ✅ 116ms
Lane Isolation.Failing Ai Lane Does Not Delay Analytics ✅ 5127ms
Required Headers.Has Authorization Bearer Header ✅ 117ms
Required Headers.Has Content Type Json ✅ 115ms
Required Headers.Has Posthog Sdk Info Format ✅ 116ms
Required Headers.Has Posthog Attempt Header ✅ 115ms
Required Headers.Has Posthog Request Id ✅ 116ms
Required Headers.Has Posthog Request Timestamp ✅ 116ms
Required Headers.Has User Agent ✅ 114ms
Body Format.Body Has Created At And Batch ✅ 115ms
Body Format.No Api Key In Body ✅ 116ms
Body Format.No Sent At In Body ✅ 116ms
Event Format.Event Has Required Root Fields ✅ 115ms
Event Format.Event Uuid Is Valid ✅ 116ms
Event Format.Event Timestamp Is Rfc3339 ✅ 115ms
Event Format.Non Utc Event Timestamp Is Converted To Utc ✅ 116ms
Event Format.Distinct Id Is String ✅ 117ms
Event Format.Distinct Id At Root Not Properties ✅ 115ms
Event Format.Custom Properties Preserved ✅ 115ms
Event Format.Set Properties Preserved ✅ 116ms
Event Format.Set Once Properties Preserved ✅ 115ms
Event Format.Groups Properties Preserved ✅ 116ms
Event Format.Sdk Generates Uuid If Not Provided ✅ 115ms
Event Format.Event Has Required Root Fields Batch ✅ 120ms
Event Format.Event Uuid Is Valid Batch ✅ 119ms
Event Format.Event Timestamp Is Rfc3339 Batch ✅ 119ms
Event Format.Distinct Id Is String Batch ✅ 120ms
Event Format.Distinct Id At Root Not Properties Batch ✅ 121ms
Event Format.Custom Properties Preserved Batch ✅ 120ms
Event Format.Set Properties Preserved Batch ✅ 120ms
Event Format.Set Once Properties Preserved Batch ✅ 122ms
Event Format.Groups Properties Preserved Batch ✅ 120ms
Event Format.Sdk Generates Uuid If Not Provided Batch ✅ 120ms
Batch Behavior.Multiple Events In Single Batch ✅ 125ms
Batch Behavior.Batch Envelope Smoke ✅ 122ms
Batch Behavior.Flush At Triggers Batch ✅ 1014ms
Batch Behavior.Created At Reflects Batch Creation Time ✅ 117ms
Deduplication.Generates Unique Uuids ✅ 124ms
Deduplication.Different Events Same Content Different Uuids ✅ 123ms
Deduplication.Preserves Uuid On Retry ✅ 5123ms
Deduplication.Preserves Timestamp On Retry ✅ 5121ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 5127ms
Deduplication.No Duplicate Events In Batch ✅ 125ms
Header Behavior On Retry.Attempt Header Starts At One ✅ 120ms
Header Behavior On Retry.Attempt Header Increments On Retry ✅ 10126ms
Header Behavior On Retry.Request Id Preserved On Retry ✅ 5124ms
Header Behavior On Retry.Different Requests Have Different Request Ids ✅ 2230ms
Header Behavior On Retry.Request Timestamp Changes On Retry ✅ 5124ms
Response Format Validation.Success Response Has Uuid Keyed Results ✅ 117ms
Response Format Validation.Success Response Has Ok For Each Event ✅ 120ms
Response Format Validation.Success No Retry After When All Ok ✅ 118ms
Response Format Validation.Success Retry After Present When Retry Events ✅ 118ms
Response Format Validation.Success No Retry After When Drop Only ✅ 119ms
Response Format Validation.Response Echoes Request Id ✅ 116ms
Retry Behavior.Retries On 408 ✅ 5122ms
Retry Behavior.Retries On 500 ✅ 5122ms
Retry Behavior.Retries On 503 ✅ 5119ms
Retry Behavior.Retries On 504 ✅ 5124ms
Retry Behavior.Retries On 404 ✅ 5122ms
Retry Behavior.Retries On 429 ✅ 5123ms
Retry Behavior.Retries On 502 ✅ 5124ms
Retry Behavior.Retryable Errors Have Retry After ✅ 2120ms
Retry Behavior.Respects Retry After On Retryable Error ✅ 8126ms
Retry Behavior.Does Not Retry On 400 ✅ 2120ms
Retry Behavior.Does Not Retry On 401 ✅ 2121ms
Retry Behavior.Does Not Retry On 402 ✅ 2120ms
Retry Behavior.Does Not Retry On 413 ✅ 2120ms
Retry Behavior.Does Not Retry On 415 ✅ 2119ms
Retry Behavior.Non Retryable Errors Have No Retry After ✅ 2119ms
Retry Behavior.Implements Backoff ✅ 12130ms
Partial Batch Handling.Handles 200 Full Success ✅ 2121ms
Partial Batch Handling.Handles 200 With All Ok ✅ 3125ms
Partial Batch Handling.Does Not Retry Dropped Events ✅ 3125ms
Partial Batch Handling.Does Not Retry Warning Events ✅ 3124ms
Partial Batch Handling.Prunes Ok Events On Partial Retry ✅ 5127ms
Partial Batch Handling.Prunes Dropped Events On Partial Retry ✅ 5125ms
Partial Batch Handling.Retries Only Retry Events From Partial ✅ 5127ms
Partial Batch Handling.Partial Retry Preserves Uuids ✅ 5127ms
Partial Batch Handling.Partial Retry Attempt Header Increments ✅ 5126ms
Partial Batch Handling.Partial Retry Request Id Preserved ✅ 5125ms
Partial Batch Handling.Respects Retry After On Partial ✅ 5127ms
Partial Batch Handling.Unknown Result Treated As Terminal ✅ 3124ms
Partial Batch Handling.Mixed Ok Drop Warning No Retry ✅ 3130ms
Compression.Sends Gzip Content Encoding ✅ 116ms
Compression.No Content Encoding When Disabled ✅ 116ms
Compression.Compressed Body Is Decompressible ✅ 115ms
Error Handling.Does Not Retry On Unknown 4Xx ✅ 2119ms
Event Options.Cookieless Mode Override ✅ 118ms
Event Options.Disable Skew Correction Override ✅ 116ms
Event Options.Process Person Profile Override ✅ 115ms
Event Options.Product Tour Id Override ✅ 116ms
Event Options.Unset Options Omitted ✅ 117ms
Event Options.Options Override In Batch ✅ 121ms
Event Options.Unknown Option Passes Through ✅ 116ms
Event Options.Option Value Is Not Converted ✅ 116ms
Event Options.Lenient Bool Option Values Are Not Converted ✅ 116ms
Event Options.Option Wins Over Legacy Property ✅ 117ms
Event Options.Legacy Property Fills Unset Option ✅ 116ms
Event Options.Null Option Falls Back To Legacy Property ✅ 117ms
Event Size.Event Over Ceiling Is Not Sent ✅ 3898ms
Event Size.Over Ceiling Drop Is Reported Or Logged Without Content ✅ 2172ms
Event Size.Batches Close Before Byte Target ✅ 5995ms
Geoip And Historical Migration.Historical Migration Absent By Default ✅ 118ms
Property Layers.Caller Property Beats Registered Property ✅ 116ms
Property Layers.Before Send Runs Last ✅ 116ms
Fail Open.Empty Api Key Sends Nothing ✅ 1111ms
Fail Open.Personal Api Key Sends Nothing ✅ 1112ms

Feature_Flags Tests

✅ 17/17 tests passed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id ✅ 21ms
Request Payload.Flags Request Uses V2 Query Param ✅ 15ms
Request Payload.Flags Request Hits Flags Path Not Decide ✅ 12ms
Request Payload.Flags Request Omits Authorization Header ✅ 11ms
Request Payload.Token In Flags Body Matches Init ✅ 11ms
Request Payload.Groups Round Trip ✅ 12ms
Request Payload.Groups Default To Empty Object ✅ 11ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False ✅ 11ms
Request Payload.Disable Geoip Omitted Defaults To False ✅ 10ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key ✅ 12ms
Request Lifecycle.No Flags Request On Init Alone ✅ 4ms
Request Lifecycle.No Flags Request On Normal Capture ✅ 114ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests ✅ 20ms
Request Lifecycle.Mock Response Value Is Returned To Caller ✅ 13ms
Retry Behavior.Retries Flags On 502 ✅ 319ms
Retry Behavior.Retries Flags On 504 ✅ 320ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event ✅ 120ms

@eli-r-ph eli-r-ph left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

human and assorted robots think this looks great thanks for the updates! 🚀

@turnipdabeets turnipdabeets mentioned this pull request Nov 9, 2026
6 of 17 tasks
@turnipdabeets
turnipdabeets merged commit 962a28c into chore/v6-posthog-ios-4 Nov 9, 2026
34 of 40 checks passed
@turnipdabeets
turnipdabeets deleted the feat/v6-register-options branch November 9, 2026 20:46
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.

2 participants