mixpanel-browser 2.77.0 → 2.79.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/.claude/settings.local.json +6 -9
  2. package/.eslintrc.json +12 -0
  3. package/.github/workflows/openfeature-provider-tests.yml +31 -0
  4. package/CHANGELOG.md +11 -0
  5. package/build.sh +2 -2
  6. package/dist/async-modules/{mixpanel-recorder-wIWnMDLA.min.js → mixpanel-recorder-D5HJyV2E.min.js} +2 -2
  7. package/dist/async-modules/mixpanel-recorder-D5HJyV2E.min.js.map +1 -0
  8. package/dist/async-modules/{mixpanel-recorder-DLKbUIEE.js → mixpanel-recorder-P6SEnnPV.js} +57 -33
  9. package/dist/async-modules/mixpanel-targeting-1L9FyetZ.min.js +2 -0
  10. package/dist/async-modules/mixpanel-targeting-1L9FyetZ.min.js.map +1 -0
  11. package/dist/async-modules/{mixpanel-targeting-CmVvUyFM.js → mixpanel-targeting-BBMVbgJF.js} +24 -13
  12. package/dist/mixpanel-core.cjs.d.ts +46 -1
  13. package/dist/mixpanel-core.cjs.js +671 -272
  14. package/dist/mixpanel-recorder.js +57 -33
  15. package/dist/mixpanel-recorder.min.js +1 -1
  16. package/dist/mixpanel-recorder.min.js.map +1 -1
  17. package/dist/mixpanel-targeting.js +24 -13
  18. package/dist/mixpanel-targeting.min.js +1 -1
  19. package/dist/mixpanel-targeting.min.js.map +1 -1
  20. package/dist/mixpanel-with-async-modules.cjs.d.ts +46 -1
  21. package/dist/mixpanel-with-async-modules.cjs.js +673 -274
  22. package/dist/mixpanel-with-async-recorder.cjs.d.ts +46 -1
  23. package/dist/mixpanel-with-async-recorder.cjs.js +673 -274
  24. package/dist/mixpanel-with-recorder.d.ts +46 -1
  25. package/dist/mixpanel-with-recorder.js +596 -197
  26. package/dist/mixpanel-with-recorder.min.d.ts +46 -1
  27. package/dist/mixpanel-with-recorder.min.js +1 -1
  28. package/dist/mixpanel.amd.d.ts +46 -1
  29. package/dist/mixpanel.amd.js +596 -197
  30. package/dist/mixpanel.cjs.d.ts +46 -1
  31. package/dist/mixpanel.cjs.js +596 -197
  32. package/dist/mixpanel.globals.js +673 -274
  33. package/dist/mixpanel.min.js +200 -189
  34. package/dist/mixpanel.module.d.ts +46 -1
  35. package/dist/mixpanel.module.js +596 -197
  36. package/dist/mixpanel.umd.d.ts +46 -1
  37. package/dist/mixpanel.umd.js +596 -197
  38. package/package.json +1 -1
  39. package/packages/openfeature-web-provider/README.md +357 -0
  40. package/packages/openfeature-web-provider/package-lock.json +1636 -0
  41. package/packages/openfeature-web-provider/package.json +51 -0
  42. package/packages/openfeature-web-provider/rollup.config.browser.mjs +26 -0
  43. package/packages/openfeature-web-provider/src/MixpanelProvider.ts +302 -0
  44. package/packages/openfeature-web-provider/src/index.ts +1 -0
  45. package/packages/openfeature-web-provider/src/types.ts +72 -0
  46. package/packages/openfeature-web-provider/test/MixpanelProvider.spec.ts +484 -0
  47. package/packages/openfeature-web-provider/tsconfig.json +15 -0
  48. package/src/autocapture/index.js +7 -2
  49. package/src/config.js +1 -1
  50. package/src/flags/CLAUDE.md +24 -0
  51. package/src/flags/flags-persistence.js +176 -0
  52. package/src/flags/index.js +278 -98
  53. package/src/index.d.ts +46 -1
  54. package/src/mixpanel-core.js +27 -8
  55. package/src/recorder/idb-config.js +16 -0
  56. package/src/recorder/recording-registry.js +7 -2
  57. package/src/recorder/session-recording.js +9 -4
  58. package/src/recorder-manager.js +7 -2
  59. package/src/request-queue.js +1 -2
  60. package/src/shared-lock.js +2 -3
  61. package/src/storage/indexed-db.js +16 -15
  62. package/src/storage/local-storage.js +5 -3
  63. package/src/utils.js +25 -12
  64. package/testServer.js +2 -0
  65. package/tsconfig.base.json +9 -0
  66. package/dist/async-modules/mixpanel-recorder-wIWnMDLA.min.js.map +0 -1
  67. package/dist/async-modules/mixpanel-targeting-CTcftSJC.min.js +0 -2
  68. package/dist/async-modules/mixpanel-targeting-CTcftSJC.min.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixpanel-browser",
3
- "version": "2.77.0",
3
+ "version": "2.79.0",
4
4
  "description": "The official Mixpanel JavaScript browser client library",
5
5
  "main": "dist/mixpanel.cjs.js",
6
6
  "module": "dist/mixpanel.module.js",
@@ -0,0 +1,357 @@
1
+ # @mixpanel/openfeature-web-provider
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@mixpanel/openfeature-web-provider.svg)](https://www.npmjs.com/package/@mixpanel/openfeature-web-provider)
4
+ [![OpenFeature](https://img.shields.io/badge/OpenFeature-compatible-green)](https://openfeature.dev/)
5
+ [![License](https://img.shields.io/npm/l/@mixpanel/openfeature-web-provider.svg)](https://github.com/mixpanel/mixpanel-js/blob/master/LICENSE)
6
+
7
+ An [OpenFeature](https://openfeature.dev/) provider that wraps Mixpanel's feature flags for use with the OpenFeature Web SDK. This allows you to use Mixpanel's feature flagging capabilities through OpenFeature's standardized, vendor-agnostic API.
8
+
9
+ ## Overview
10
+
11
+ This package provides a bridge between Mixpanel's native feature flags implementation and the OpenFeature specification. By using this provider, you can:
12
+
13
+ - Leverage Mixpanel's powerful feature flag and experimentation platform
14
+ - Use OpenFeature's standardized API for flag evaluation
15
+ - Easily switch between feature flag providers without changing your application code
16
+ - Integrate with OpenFeature's ecosystem of tools and frameworks
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @mixpanel/openfeature-web-provider @openfeature/web-sdk mixpanel-browser
22
+ ```
23
+
24
+ Or with yarn:
25
+
26
+ ```bash
27
+ yarn add @mixpanel/openfeature-web-provider @openfeature/web-sdk mixpanel-browser
28
+ ```
29
+
30
+ ## Quick Start
31
+
32
+ ```typescript
33
+ import mixpanel from 'mixpanel-browser';
34
+ import { OpenFeature } from '@openfeature/web-sdk';
35
+ import { MixpanelProvider } from '@mixpanel/openfeature-web-provider';
36
+
37
+ // 1. Initialize Mixpanel with feature flags and context
38
+ mixpanel.init('YOUR_PROJECT_TOKEN', {
39
+ flags: {
40
+ context: { plan: 'premium' }
41
+ }
42
+ });
43
+
44
+ // 2. Create and register the Mixpanel provider
45
+ const provider = new MixpanelProvider(mixpanel.flags);
46
+ await OpenFeature.setProviderAndWait(provider);
47
+
48
+ // 3. Get a client and evaluate flags
49
+ const client = OpenFeature.getClient();
50
+ const showNewFeature = client.getBooleanValue('new-feature-flag', false);
51
+
52
+ if (showNewFeature) {
53
+ console.log('New feature is enabled!');
54
+ }
55
+ ```
56
+
57
+ ## Usage Examples
58
+
59
+ ### Basic Boolean Flag
60
+
61
+ ```typescript
62
+ const client = OpenFeature.getClient();
63
+
64
+ // Get a boolean flag with a default value
65
+ const isFeatureEnabled = client.getBooleanValue('my-feature', false);
66
+
67
+ if (isFeatureEnabled) {
68
+ // Show the new feature
69
+ }
70
+ ```
71
+
72
+ ### Mixpanel Flag Types and OpenFeature Evaluation Methods
73
+
74
+ Mixpanel feature flags support three flag types. Use the corresponding OpenFeature evaluation method based on your flag's variant values:
75
+
76
+ | Mixpanel Flag Type | Variant Values | OpenFeature Method |
77
+ |---|---|---|
78
+ | Feature Gate | `true` / `false` | `getBooleanValue()` |
79
+ | Experiment | boolean, string, number, or JSON object | `getBooleanValue()`, `getStringValue()`, `getNumberValue()`, or `getObjectValue()` |
80
+ | Dynamic Config | JSON object | `getObjectValue()` |
81
+
82
+ ```typescript
83
+ const client = OpenFeature.getClient();
84
+
85
+ // Feature Gate — boolean variants
86
+ const isFeatureOn = client.getBooleanValue('new-checkout', false);
87
+
88
+ // Experiment with string variants
89
+ const buttonColor = client.getStringValue('button-color-test', 'blue');
90
+
91
+ // Experiment with number variants
92
+ const maxItems = client.getNumberValue('max-items', 10);
93
+
94
+ // Dynamic Config — JSON object variants
95
+ const featureConfig = client.getObjectValue('homepage-layout', {
96
+ layout: 'grid',
97
+ itemsPerRow: 3
98
+ });
99
+ ```
100
+
101
+ ### Getting Full Resolution Details
102
+
103
+ If you need additional metadata about the flag evaluation:
104
+
105
+ ```typescript
106
+ const client = OpenFeature.getClient();
107
+
108
+ const details = client.getBooleanDetails('my-feature', false);
109
+
110
+ console.log(details.value); // The resolved value
111
+ console.log(details.variant); // The variant key from Mixpanel
112
+ console.log(details.reason); // Why this value was returned
113
+ console.log(details.errorCode); // Error code if evaluation failed
114
+ ```
115
+
116
+ ### Setting Context
117
+
118
+ You can pass evaluation context that will be sent to Mixpanel for flag evaluation using `OpenFeature.setContext()`:
119
+
120
+ ```typescript
121
+ await OpenFeature.setContext({
122
+ email: 'user@example.com',
123
+ plan: 'premium'
124
+ });
125
+ ```
126
+
127
+ > **Note:** Per-evaluation context (the optional third argument to `getBooleanValue`, `getStringValue`, etc.) is **not supported** by this provider. Context must be set globally via `OpenFeature.setContext()`, which triggers a re-fetch of flag values from Mixpanel.
128
+
129
+ ### Using custom_properties for Runtime Properties
130
+
131
+ You can pass `custom_properties` in the evaluation context for use with Mixpanel's [Runtime Properties](https://docs.mixpanel.com/docs/feature-flags/runtime-properties) targeting rules. Values must be flat key-value pairs (no nested objects):
132
+
133
+ ```typescript
134
+ await OpenFeature.setContext({
135
+ custom_properties: {
136
+ tier: 'enterprise',
137
+ seats: 50,
138
+ industry: 'technology'
139
+ }
140
+ });
141
+ ```
142
+
143
+ ### React Integration
144
+
145
+ Using OpenFeature with React via the `@openfeature/react-sdk`:
146
+
147
+ ```tsx
148
+ import { OpenFeatureProvider, useBooleanFlagValue } from '@openfeature/react-sdk';
149
+ import { OpenFeature } from '@openfeature/web-sdk';
150
+ import mixpanel from 'mixpanel-browser';
151
+ import { MixpanelProvider } from '@mixpanel/openfeature-web-provider';
152
+
153
+ // Initialize outside of component
154
+ mixpanel.init('YOUR_PROJECT_TOKEN', {
155
+ flags: {
156
+ context: { plan: 'premium' }
157
+ }
158
+ });
159
+ const provider = new MixpanelProvider(mixpanel.flags);
160
+ OpenFeature.setProvider(provider);
161
+
162
+ function App() {
163
+ return (
164
+ <OpenFeatureProvider>
165
+ <MyComponent />
166
+ </OpenFeatureProvider>
167
+ );
168
+ }
169
+
170
+ function MyComponent() {
171
+ // Use the hook to get flag values reactively
172
+ const showBanner = useBooleanFlagValue('show-banner', false);
173
+
174
+ return (
175
+ <div>
176
+ {showBanner && <Banner message="Welcome to our new feature!" />}
177
+ </div>
178
+ );
179
+ }
180
+ ```
181
+
182
+ ## Context Mapping
183
+
184
+ Understanding how OpenFeature context maps to Mixpanel:
185
+
186
+ ### All Properties Passed Directly
187
+
188
+ All properties in the OpenFeature `EvaluationContext` are passed directly to Mixpanel's feature flag evaluation. There is no transformation or filtering of properties.
189
+
190
+ ```typescript
191
+ // This OpenFeature context...
192
+ await OpenFeature.setContext({
193
+ targetingKey: 'user-123',
194
+ email: 'user@example.com',
195
+ plan: 'premium',
196
+ beta_tester: true
197
+ });
198
+
199
+ // ...is passed to Mixpanel as-is for flag evaluation
200
+ ```
201
+
202
+ ### targetingKey is Not Special
203
+
204
+ Unlike some feature flag providers, `targetingKey` is **not** used as a special bucketing key in Mixpanel. It is simply passed as another context property. Mixpanel's server-side configuration determines which properties are used for:
205
+
206
+ - **Targeting rules**: Which users see which variants
207
+ - **Bucketing**: How users are consistently assigned to variants
208
+
209
+ ### User Identity is Managed Separately
210
+
211
+ **Important**: This provider does **not** call `mixpanel.identify()`. User identity should be managed separately through your normal Mixpanel integration:
212
+
213
+ ```typescript
214
+ // Manage identity through Mixpanel directly
215
+ mixpanel.identify('user-123');
216
+
217
+ // The provider will use Mixpanel's current distinct_id automatically
218
+ const client = OpenFeature.getClient();
219
+ const value = client.getBooleanValue('my-flag', false);
220
+ ```
221
+
222
+ ## API Reference
223
+
224
+ ### MixpanelProvider
225
+
226
+ The main provider class that implements the OpenFeature `Provider` interface.
227
+
228
+ #### Constructor
229
+
230
+ ```typescript
231
+ constructor(flagsManager: FlagsManager)
232
+ ```
233
+
234
+ **Parameters:**
235
+
236
+ - `flagsManager`: The Mixpanel FlagsManager instance (accessed via `mixpanel.flags`)
237
+
238
+ **Note:** Pass `mixpanel.flags` (the FlagsManager) to the provider, not the entire mixpanel instance. This reduces coupling and makes the provider only depend on the flags interface.
239
+
240
+ #### Properties
241
+
242
+ | Property | Type | Description |
243
+ |----------|------|-------------|
244
+ | `metadata` | `{ name: string }` | Provider metadata with the name "mixpanel-provider" |
245
+ | `runsOn` | `'client'` | Indicates this is a client-side provider |
246
+
247
+ #### Methods
248
+
249
+ | Method | Description |
250
+ |--------|-------------|
251
+ | `initialize(context?)` | Called when the provider is registered. Waits for Mixpanel flags to be ready. |
252
+ | `onClose()` | Called when the provider is shut down. |
253
+ | `resolveBooleanEvaluation(flagKey, defaultValue, context, logger)` | Evaluates a boolean flag |
254
+ | `resolveStringEvaluation(flagKey, defaultValue, context, logger)` | Evaluates a string flag |
255
+ | `resolveNumberEvaluation(flagKey, defaultValue, context, logger)` | Evaluates a number flag |
256
+ | `resolveObjectEvaluation(flagKey, defaultValue, context, logger)` | Evaluates an object flag |
257
+
258
+ ## Error Handling
259
+
260
+ The provider uses OpenFeature's standard error codes to indicate issues during flag evaluation:
261
+
262
+ ### PROVIDER_NOT_READY
263
+
264
+ Returned when flags are evaluated before the provider has finished initializing.
265
+
266
+ ```typescript
267
+ // To avoid this error, use setProviderAndWait
268
+ await OpenFeature.setProviderAndWait(provider);
269
+
270
+ // Or listen for the READY event
271
+ OpenFeature.addHandler(ProviderEvents.Ready, () => {
272
+ // Now safe to evaluate flags
273
+ });
274
+ ```
275
+
276
+ ### FLAG_NOT_FOUND
277
+
278
+ Returned when the requested flag does not exist in Mixpanel.
279
+
280
+ ```typescript
281
+ const details = client.getBooleanDetails('nonexistent-flag', false);
282
+
283
+ if (details.errorCode === 'FLAG_NOT_FOUND') {
284
+ console.log('Flag does not exist, using default value');
285
+ }
286
+ ```
287
+
288
+ ### TYPE_MISMATCH
289
+
290
+ Returned when the flag value type does not match the requested type.
291
+
292
+ ```typescript
293
+ // If 'my-flag' is configured as a string in Mixpanel...
294
+ const details = client.getBooleanDetails('my-flag', false);
295
+
296
+ if (details.errorCode === 'TYPE_MISMATCH') {
297
+ console.log('Flag is not a boolean, using default value');
298
+ }
299
+ ```
300
+
301
+ ## Troubleshooting
302
+
303
+ ### Flags Always Return Default Values
304
+
305
+ **Possible causes:**
306
+
307
+ 1. **Feature flags not enabled**: Ensure you initialized Mixpanel with `flags` enabled:
308
+ ```typescript
309
+ mixpanel.init('YOUR_TOKEN', { flags: { context: { plan: 'premium' } } });
310
+ ```
311
+
312
+ 2. **Provider not ready**: Make sure to wait for the provider to initialize:
313
+ ```typescript
314
+ await OpenFeature.setProviderAndWait(provider);
315
+ ```
316
+
317
+ 3. **Network issues**: Check the browser console for failed requests to Mixpanel's flags API.
318
+
319
+ 4. **Flag not configured**: Verify the flag exists in your Mixpanel project and is enabled.
320
+
321
+ ### Type Mismatch Errors
322
+
323
+ If you are getting `TYPE_MISMATCH` errors:
324
+
325
+ 1. **Check flag configuration**: Verify the flag's value type in Mixpanel matches how you are evaluating it:
326
+ ```typescript
327
+ // If flag value is a string like "true", use getStringValue, not getBooleanValue
328
+ const value = client.getStringValue('my-flag', 'default');
329
+ ```
330
+
331
+ 2. **Use getObjectValue for complex types**: For JSON objects or arrays, use `getObjectValue`.
332
+
333
+ ### Exposure Events Not Tracking
334
+
335
+ If `$experiment_started` events are not appearing in Mixpanel:
336
+
337
+ 1. **Verify Mixpanel tracking is working**: Test that other Mixpanel events are being tracked successfully.
338
+
339
+ 2. **Check for duplicate evaluations**: Mixpanel only tracks the first exposure per flag per session to avoid duplicate events.
340
+
341
+ ### Flags Not Updating After Context Change
342
+
343
+ When you update the OpenFeature context, the provider needs to fetch new flag values:
344
+
345
+ ```typescript
346
+ // Update context and wait for new flags
347
+ await OpenFeature.setContext({ plan: 'premium' });
348
+
349
+ // Now evaluate with new context
350
+ const value = client.getBooleanValue('premium-feature', false);
351
+ ```
352
+
353
+ If flags still are not updating, check that your targeting rules in Mixpanel are configured to use the context properties you are setting.
354
+
355
+ ## License
356
+
357
+ Apache-2.0