Skip to content

Commit 7691707

Browse files
frankieyanclaude
andauthored
feat: allow isEnabled() to return undefined to defer to sampleRate (#26)
* feat: allow isEnabled() to return undefined to defer to sampleRate When isEnabled() returns true or false, it force-enables or force-disables as before. Returning undefined now falls through to the sampleRate sampling logic, enabling patterns like force-enabling for internal users while sampling everyone else. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: enable global mock restoration in Vitest config Add `restoreMocks: true` to vitest.config.ts so vi.restoreAllMocks() runs after every test, preventing mocked state (e.g. Math.random spies) from leaking between tests. Remove redundant manual mockRestore() call. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 776dce7 commit 7691707

5 files changed

Lines changed: 45 additions & 7 deletions

File tree

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ const cleanup = initInteractionTraceMonitor({
2626
},
2727
enrollment: {
2828
sampleRate: 10, // 10% of sessions
29-
isEnabled: () => user.isInternal, // Optional override
29+
isEnabled: () => {
30+
if (user.isInternal) return true // force enable for internal users
31+
return undefined // everyone else: use sampleRate
32+
},
3033
},
3134
abortSignal: controller.signal, // Optional: auto-cleanup when aborted
3235
})
@@ -103,7 +106,7 @@ function SettingsModal() {
103106
|--------|------|-------------|
104107
| `sampleRate` | `number` | Percentage of sessions to enroll (0-100) |
105108
| `persistKey` | `string` | sessionStorage key for enrollment state. Default: `'interaction-trace-enrolled'` |
106-
| `isEnabled` | `() => boolean` | Override function (takes precedence over sampleRate) |
109+
| `isEnabled` | `() => boolean \| undefined` | Override function. `true`/`false` override sampling, `undefined` defers to `sampleRate` |
107110

108111
### Reporter Interface
109112

src/core/trace-controller.test.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,6 @@ describe('trace-controller', () => {
9494
)
9595
cleanup2()
9696
expect(isMonitorActive()).toBe(true)
97-
98-
consoleWarnSpy.mockRestore()
9997
})
10098
})
10199

@@ -142,6 +140,34 @@ describe('trace-controller', () => {
142140
expect(isMonitorActive()).toBe(true)
143141
})
144142

143+
it('force disables when isEnabled returns false', () => {
144+
const reporter = vi.fn()
145+
initInteractionTraceMonitor({
146+
reporter,
147+
enrollment: {
148+
sampleRate: 100,
149+
isEnabled: () => false,
150+
},
151+
})
152+
153+
expect(isMonitorActive()).toBe(false)
154+
})
155+
156+
it('falls back to sampleRate when isEnabled returns undefined', () => {
157+
vi.spyOn(Math, 'random').mockReturnValue(0.05)
158+
159+
const reporter = vi.fn()
160+
initInteractionTraceMonitor({
161+
reporter,
162+
enrollment: {
163+
sampleRate: 10,
164+
isEnabled: () => undefined,
165+
},
166+
})
167+
168+
expect(isMonitorActive()).toBe(true)
169+
})
170+
145171
it('reads existing enrollment from sessionStorage', () => {
146172
sessionStorage.setItem('interaction-trace-enrolled', 'false')
147173

src/core/trace-controller.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,10 @@ signInteractionTrace.withTypes = function withTypes<
186186

187187
function checkEnrollment(config: InteractionTraceConfig['enrollment']): boolean {
188188
if (config?.isEnabled) {
189-
return config.isEnabled()
189+
const result = config.isEnabled()
190+
if (result !== undefined) {
191+
return result
192+
}
190193
}
191194

192195
const sampleRate = config?.sampleRate ?? DEFAULT_SAMPLE_RATE

src/types.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,13 @@ export type EnrollmentConfig = {
6161
sampleRate?: number
6262
/** sessionStorage key for enrollment state. Default: 'interaction-trace-enrolled' */
6363
persistKey?: string
64-
/** Override function - takes precedence over sampleRate if provided */
65-
isEnabled?: () => boolean
64+
/**
65+
* Override function for enrollment.
66+
* - `true` — force enable (skip sampling)
67+
* - `false` — force disable (skip sampling)
68+
* - `undefined` — defer to `sampleRate`
69+
*/
70+
isEnabled?: () => boolean | undefined
6671
}
6772

6873
/**

vitest.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ export default defineConfig({
66
include: ['src/**/*.test.{ts,tsx}'],
77
watchExclude: ['node_modules', 'dist'],
88
passWithNoTests: true,
9+
restoreMocks: true,
910
},
1011
})

0 commit comments

Comments
 (0)