11import { FlagOverridePlugin } from '@launchdarkly/toolbar' ;
22
3+ // Clear mode symbols for type-safe configuration
4+ export const CLEAR_MODE_EXPLICIT = Symbol ( 'explicit' ) ;
5+ export const CLEAR_MODE_ALWAYS = Symbol ( 'always' ) ;
6+ export const CLEAR_MODE_AUTO = Symbol ( 'auto' ) ;
7+
38/**
49 * Creates a FlagOverridePlugin wrapper that syncs overrides to/from URL parameters
510 *
611 * @param {Object } options - Configuration options
712 * @param {string } options.parameterPrefix - Prefix for URL parameters (default: 'ld_override_')
13+ * @param {Symbol } options.clearMode - When to clear existing overrides (default: CLEAR_MODE_AUTO)
14+ * - CLEAR_MODE_EXPLICIT: Only clear if ld_override__clear is present
15+ * - CLEAR_MODE_ALWAYS: Always clear before loading from URL
16+ * - CLEAR_MODE_AUTO: Auto-clear if any override parameters are present
817 * @param {Object } options.overrideOptions - Options to pass to the underlying FlagOverridePlugin
918 * @param {Function } options.logger - Optional logger function for debugging
1019 * @returns {Object } A wrapped FlagOverridePlugin with URL sync capabilities
1120 */
1221export function createFlagUrlOverridePlugin ( options = { } ) {
1322 const {
1423 parameterPrefix = 'ld_override_' ,
24+ clearMode = CLEAR_MODE_AUTO ,
1525 overrideOptions = { } ,
1626 logger = console
1727 } = options ;
@@ -21,25 +31,57 @@ export function createFlagUrlOverridePlugin(options = {}) {
2131
2232 /**
2333 * Load overrides from URL query parameters
34+ * Returns an object with:
35+ * - overrides: map of flag keys to values
36+ * - shouldClear: boolean indicating if all overrides should be cleared first
37+ * - removeFlags: array of flag keys to explicitly remove
2438 */
2539 function loadOverridesFromUrl ( ) {
2640 const urlParams = new URLSearchParams ( window . location . search ) ;
2741 const overrides = { } ;
42+ const removeFlags = [ ] ;
43+ let hasOverrideParams = false ;
44+
45+ // Check for special _clear flag (just needs to be present)
46+ const hasClearFlag = urlParams . has ( `${ parameterPrefix } _clear` ) ;
2847
2948 for ( const [ key , value ] of urlParams . entries ( ) ) {
3049 if ( key . startsWith ( parameterPrefix ) ) {
3150 const flagKey = key . replace ( parameterPrefix , '' ) ;
32- try {
33- // Try to parse as JSON
34- overrides [ flagKey ] = JSON . parse ( value ) ;
35- } catch ( e ) {
36- // If parsing fails, treat as string
37- overrides [ flagKey ] = value ;
51+
52+ // Skip the special _clear flag
53+ if ( flagKey === '_clear' ) {
54+ continue ;
55+ }
56+
57+ hasOverrideParams = true ;
58+
59+ // Empty value means remove this override
60+ if ( value === '' ) {
61+ removeFlags . push ( flagKey ) ;
62+ } else {
63+ try {
64+ // Try to parse as JSON
65+ overrides [ flagKey ] = JSON . parse ( value ) ;
66+ } catch ( e ) {
67+ // If parsing fails, treat as string
68+ overrides [ flagKey ] = value ;
69+ }
3870 }
3971 }
4072 }
4173
42- return overrides ;
74+ // Determine if we should clear based on clearMode
75+ let shouldClear = false ;
76+ if ( clearMode === CLEAR_MODE_ALWAYS ) {
77+ shouldClear = true ;
78+ } else if ( clearMode === CLEAR_MODE_AUTO ) {
79+ shouldClear = hasOverrideParams || hasClearFlag ;
80+ } else if ( clearMode === CLEAR_MODE_EXPLICIT ) {
81+ shouldClear = hasClearFlag ;
82+ }
83+
84+ return { overrides, shouldClear, removeFlags } ;
4385 }
4486
4587 /**
@@ -118,18 +160,37 @@ export function createFlagUrlOverridePlugin(options = {}) {
118160 // Monkey patch registerDebug to load URL overrides
119161 const originalRegisterDebug = plugin . registerDebug . bind ( plugin ) ;
120162 plugin . registerDebug = function ( debugOverride ) {
121- // Call original implementation first
163+ // Call original implementation first (loads from localStorage)
122164 originalRegisterDebug ( debugOverride ) ;
123165
124- // Load overrides from URL and apply them (using original method to avoid recursion)
125- const urlOverrides = loadOverridesFromUrl ( ) ;
126- Object . entries ( urlOverrides ) . forEach ( ( [ flagKey , value ] ) => {
166+ // Load override instructions from URL
167+ const { overrides, shouldClear, removeFlags } = loadOverridesFromUrl ( ) ;
168+
169+ // Clear all existing overrides if needed (based on clearMode)
170+ if ( shouldClear ) {
171+ const reason = clearMode === CLEAR_MODE_ALWAYS
172+ ? 'clearMode=ALWAYS'
173+ : clearMode === CLEAR_MODE_AUTO
174+ ? 'clearMode=AUTO (URL params present)'
175+ : 'ld_override__clear flag' ;
176+ logger . info ( `Clearing all existing overrides (${ reason } )` ) ;
177+ originalClearAllOverrides ( ) ;
178+ }
179+
180+ // Remove specific flags if requested
181+ removeFlags . forEach ( flagKey => {
182+ logger . info ( `Removing override from URL: ${ flagKey } ` ) ;
183+ originalRemoveOverride ( flagKey ) ;
184+ } ) ;
185+
186+ // Apply overrides from URL (using original method to avoid recursion)
187+ Object . entries ( overrides ) . forEach ( ( [ flagKey , value ] ) => {
127188 logger . info ( `Loading override from URL: ${ flagKey } = ${ JSON . stringify ( value ) } ` ) ;
128189 originalSetOverride ( flagKey , value ) ;
129190 } ) ;
130191
131- if ( Object . keys ( urlOverrides ) . length > 0 ) {
132- logger . info ( `Loaded ${ Object . keys ( urlOverrides ) . length } overrides from URL` ) ;
192+ if ( Object . keys ( overrides ) . length > 0 ) {
193+ logger . info ( `Loaded ${ Object . keys ( overrides ) . length } overrides from URL` ) ;
133194 }
134195 } ;
135196
0 commit comments