@supashiphq/javascript-sdk 0.7.16 → 0.7.17

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.
package/README.md CHANGED
@@ -33,7 +33,7 @@ const client = new SupaClient({
33
33
  environment: 'production',
34
34
  features,
35
35
  context: {
36
- userID: '123',
36
+ userId: '123',
37
37
  email: 'user@example.com',
38
38
  plan: 'premium',
39
39
  },
@@ -197,7 +197,7 @@ const theme = await client.getFeature('theme-config')
197
197
 
198
198
  // With context override
199
199
  const darkMode = await client.getFeature('dark-mode', {
200
- context: { userID: '123', plan: 'premium' },
200
+ context: { userId: '123', plan: 'premium' },
201
201
  })
202
202
  ```
203
203
 
@@ -239,7 +239,7 @@ const results = await client.getFeatures(['new-ui', 'premium-content'])
239
239
 
240
240
  // With context override
241
241
  const results = await client.getFeatures(['new-ui', 'beta-mode'], {
242
- context: { userID: '123', plan: 'premium' },
242
+ context: { userId: '123', plan: 'premium' },
243
243
  })
244
244
  ```
245
245
 
@@ -260,10 +260,10 @@ updateContext(context: FeatureContext, mergeWithExisting?: boolean): void
260
260
 
261
261
  ```typescript
262
262
  // Merge with existing context
263
- client.updateContext({ userID: '456' })
263
+ client.updateContext({ userId: '456' })
264
264
 
265
265
  // Replace entire context
266
- client.updateContext({ userID: '456', newField: 'value' }, false)
266
+ client.updateContext({ userId: '456', newField: 'value' }, false)
267
267
  ```
268
268
 
269
269
  ##### getContext()
@@ -303,7 +303,7 @@ interface FeatureContext {
303
303
 
304
304
  Common context properties:
305
305
 
306
- - `userID`: User identifier
306
+ - `userId`: User identifier
307
307
  - `email`: User email
308
308
  - `plan`: Membership plan (e.g., 'premium', 'free')
309
309
  - `version`: Application version (e.g., 1.0.0)
@@ -386,7 +386,7 @@ const client = new SupaClient({
386
386
  environment: 'production',
387
387
  features,
388
388
  context: {
389
- userID: user.id,
389
+ userId: user.id,
390
390
  email: user.email,
391
391
  plan: user.subscriptionPlan,
392
392
  version: process.env.APP_VERSION!,
@@ -396,7 +396,7 @@ const client = new SupaClient({
396
396
  // Update context when user state changes
397
397
  function onUserLogin(user) {
398
398
  client.updateContext({
399
- userID: user.id,
399
+ userId: user.id,
400
400
  email: user.email,
401
401
  plan: user.plan,
402
402
  })
@@ -519,7 +519,7 @@ app.get('/api/config', async (req, res) => {
519
519
  app.get('/api/user-features/:userId', async (req, res) => {
520
520
  const results = await featureClient.getFeatures(['new-api', 'cache-enabled'], {
521
521
  context: {
522
- userID: req.params.userId,
522
+ userId: req.params.userId,
523
523
  plan: req.user.plan,
524
524
  region: req.headers['cloudfront-viewer-country'],
525
525
  },
@@ -604,7 +604,7 @@ export class FeatureFlagService {
604
604
  environment: 'production',
605
605
  features,
606
606
  context: {
607
- userID: this.getCurrentUserId(),
607
+ userId: this.getCurrentUserId(),
608
608
  version: environment.version,
609
609
  },
610
610
  })
@@ -623,7 +623,7 @@ export class FeatureFlagService {
623
623
  }
624
624
 
625
625
  private getCurrentUserId(): string {
626
- return localStorage.getItem('userID') || 'anonymous'
626
+ return localStorage.getItem('userId') || 'anonymous'
627
627
  }
628
628
  }
629
629
 
package/dist/index.js CHANGED
@@ -324,7 +324,6 @@ var SupaToolbarPlugin = class {
324
324
  width: 36px;
325
325
  height: 36px;
326
326
  border-radius: 100%;
327
- background: #000;
328
327
  border: none;
329
328
  color: white;
330
329
  cursor: pointer;
@@ -903,8 +902,11 @@ var SupaToolbarPlugin = class {
903
902
  data-action="remove"
904
903
  title="Reset to default"
905
904
  >
906
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="14" height="14">
907
- <path d="M240,56v48a8,8,0,0,1-8,8H184a8,8,0,0,1,0-16H211.4L184.81,71.64l-.25-.24a80,80,0,1,0-1.67,114.78,8,8,0,0,1,11,11.63A95.44,95.44,0,0,1,128,224h-1.32A96,96,0,1,1,195.75,60L224,85.8V56a8,8,0,1,1,16,0Z" fill="currentColor"/>
905
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
906
+ <path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
907
+ <path d="M3 3v5h5"/>
908
+ <path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/>
909
+ <path d="M16 16h5v5"/>
908
910
  </svg>
909
911
  </button>
910
912
  ` : ""}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/plugins/toolbar-plugin.ts","../src/constants.ts","../src/client.ts"],"sourcesContent":["export type * from './types'\nexport { SupaClient } from './client'\nexport type { SupaPlugin, SupaPluginConfig } from './plugins/types'\nexport { SupaToolbarPlugin as ToolbarPlugin } from './plugins/toolbar-plugin'\nexport type {\n SupaToolbarPluginConfig,\n SupaToolbarPosition,\n SupaToolbarOverrideChange,\n SupaToolbarOverrideChangeCallback,\n} from './plugins/toolbar-plugin'\n// export { LoggingPlugin } from \"./plugins/logging-plugin\";\n// export { CachingPlugin } from \"./plugins/caching-plugin\";\n// export { AnalyticsPlugin } from \"./plugins/analytics-plugin\";\n// export { LocalDevPlugin } from \"./plugins/local-dev-plugin\";\n","export function sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nexport async function retry<T>(\n fn: () => Promise<T>,\n maxAttempts: number = 3,\n backoff: number = 1000,\n onRetry?: (attempt: number, error: Error, willRetry: boolean) => void\n): Promise<T> {\n let lastError: Error\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn()\n } catch (error) {\n lastError = error as Error\n const willRetry = attempt < maxAttempts\n\n if (onRetry) {\n onRetry(attempt, lastError, willRetry)\n }\n\n if (!willRetry) break\n await sleep(backoff * Math.pow(2, attempt - 1))\n }\n }\n\n throw lastError!\n}\n","import { SupaPlugin, SupaPluginConfig } from './types'\nimport { FeatureContext, FeatureValue } from '../types'\n\nexport interface SupaToolbarPosition {\n placement?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'\n offset?: { x: string; y: string }\n}\n\nexport type SupaToolbarOverrideChange = {\n feature: string\n value: FeatureValue\n}\n\nexport type SupaToolbarOverrideChangeCallback = (\n featureOverride: SupaToolbarOverrideChange,\n allOverrides: Record<string, FeatureValue>\n) => void\n\nexport interface SupaToolbarPluginConfig extends Omit<SupaPluginConfig, 'enabled'> {\n enabled?: boolean | 'auto' // auto means show only on localhost\n position?: SupaToolbarPosition\n onOverrideChange?: SupaToolbarOverrideChangeCallback\n}\n\ninterface SupaToolbarState {\n overrides: Record<string, FeatureValue>\n features: Set<string>\n featureValues: Record<string, FeatureValue>\n context?: FeatureContext\n searchQuery: string\n useLocalOverrides: boolean\n}\n\nconst DEFAULT_STORAGE_KEY = 'supaship-feature-overrides'\n\nconst NO_FEATURES_MESSAGE = `No feature flags configured in the client.`\n\n/**\n * Toolbar plugin for local feature flag testing\n * Provides a visual interface to override feature flags during development\n */\nexport class SupaToolbarPlugin implements SupaPlugin {\n name = 'toolbar-plugin'\n private config: {\n enabled: boolean | 'auto'\n position: Required<SupaToolbarPosition>\n onOverrideChange?: SupaToolbarOverrideChangeCallback\n }\n private state: SupaToolbarState\n private clientId?: string\n private storageKey: string = DEFAULT_STORAGE_KEY\n\n constructor(config: SupaToolbarPluginConfig = {}) {\n this.config = {\n enabled: config.enabled ?? 'auto',\n position: {\n placement: config.position?.placement ?? 'bottom-right',\n offset: config.position?.offset ?? { x: '1rem', y: '1rem' },\n },\n onOverrideChange: config.onOverrideChange,\n }\n\n this.state = {\n overrides: {},\n features: new Set(),\n featureValues: {},\n searchQuery: '',\n useLocalOverrides: true,\n }\n }\n\n cleanup(): void {\n this.removeToolbar()\n }\n\n private shouldShowToolbar(): boolean {\n if (this.config.enabled === true) return true\n if (this.config.enabled === false) return false\n\n // Auto mode: show only on localhost\n if (typeof window !== 'undefined') {\n return (\n window.location.hostname === 'localhost' ||\n window.location.hostname === '127.0.0.1' ||\n window.location.hostname === '' ||\n window.location.hostname.endsWith('.local') ||\n window.location.hostname.endsWith('.localhost')\n )\n }\n return false\n }\n\n onInit(params: {\n availableFeatures: Record<string, FeatureValue>\n context?: FeatureContext\n clientId: string\n }): void {\n const { availableFeatures, context, clientId } = params\n\n // Set client ID for DOM element IDs\n this.clientId = clientId\n\n // Use shared storage key (not client-specific) to persist across refreshes\n this.storageKey = DEFAULT_STORAGE_KEY\n\n // Load overrides from shared storage\n this.state.overrides = this.loadOverrides()\n\n // Initialize with all available features and their fallback values from config\n this.state.features = new Set(Object.keys(availableFeatures))\n this.state.featureValues = { ...availableFeatures }\n this.state.context = context\n\n // Inject toolbar if conditions are met\n if (this.shouldShowToolbar()) {\n this.injectToolbar()\n }\n\n // Update toolbar UI if it exists\n this.updateToolbarUI()\n }\n\n async beforeGetFeatures(_featureNames: string[], context?: FeatureContext): Promise<void> {\n // Update context if it changed\n this.state.context = context\n\n // Load overrides from shared storage\n this.state.overrides = this.loadOverrides()\n\n // Update toolbar UI if it exists\n this.updateToolbarUI()\n }\n\n async afterGetFeatures(\n results: Record<string, FeatureValue>,\n context?: FeatureContext\n ): Promise<void> {\n // Update feature values with fetched results (this replaces config fallback values)\n Object.keys(results).forEach(name => {\n this.state.featureValues[name] = results[name]\n })\n\n // Apply overrides to results only if local overrides are enabled\n if (this.state.useLocalOverrides) {\n Object.keys(this.state.overrides).forEach(featureName => {\n if (featureName in results) {\n results[featureName] = this.state.overrides[featureName]\n }\n })\n }\n\n // Track features and update UI\n Object.keys(results).forEach(name => this.state.features.add(name))\n this.state.context = context\n this.updateToolbarUI()\n }\n\n private loadOverrides(): Record<string, FeatureValue> {\n if (typeof window === 'undefined' || !window.localStorage) {\n return {}\n }\n\n try {\n const stored = window.localStorage.getItem(this.storageKey)\n return stored ? JSON.parse(stored) : {}\n } catch {\n return {}\n }\n }\n\n private saveOverrides(\n feature?: string,\n value?: FeatureValue,\n allOverrides?: Record<string, FeatureValue>\n ): void {\n if (typeof window === 'undefined' || !window.localStorage) {\n return\n }\n\n try {\n window.localStorage.setItem(this.storageKey, JSON.stringify(allOverrides))\n this.config.onOverrideChange?.(\n { feature: feature ?? '', value: value ?? null },\n allOverrides ?? {}\n )\n } catch (error) {\n console.error('Supaship: Failed to save feature overrides:', error)\n }\n }\n\n public setOverride(featureName: string, value: FeatureValue): void {\n this.state.overrides[featureName] = value\n this.saveOverrides(featureName, value, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public removeOverride(featureName: string): void {\n delete this.state.overrides[featureName]\n this.saveOverrides(featureName, null, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public clearAllOverrides(): void {\n this.state.overrides = {}\n this.saveOverrides('', null, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public getOverrides(): Record<string, FeatureValue> {\n return { ...this.state.overrides }\n }\n\n private injectToolbar(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return\n }\n\n // Check if toolbar with this client ID already exists\n const toolbarId = `supaship-toolbar-${this.clientId}`\n if (document.getElementById(toolbarId)) {\n return\n }\n\n // Create toolbar container\n const toolbar = document.createElement('div')\n toolbar.id = toolbarId\n toolbar.setAttribute('data-supaship-client', this.clientId || '')\n toolbar.innerHTML = this.getToolbarHTML()\n\n // Add styles\n this.injectStyles()\n\n // Add to DOM\n document.body.appendChild(toolbar)\n\n // Add event listeners\n this.attachEventListeners()\n }\n\n private removeToolbar(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const toolbar = document.getElementById(`supaship-toolbar-${this.clientId}`)\n if (toolbar) {\n toolbar.remove()\n }\n\n const styles = document.getElementById('supaship-toolbar-styles')\n if (styles) {\n styles.remove()\n }\n }\n\n private getToolbarHTML(): string {\n const { placement, offset } = this.config.position\n const positionClass = `supaship-toolbar-${placement}`\n const offsetX = offset?.x ?? '1rem'\n const offsetY = offset?.y ?? '1rem'\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n const panelId = `supaship-toolbar-panel-${this.clientId}`\n const searchId = `supaship-search-input-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const closeId = `supaship-close-toolbar-${this.clientId}`\n const contentId = `supaship-toolbar-content-${this.clientId}`\n const badgeId = `supaship-toolbar-badge-${this.clientId}`\n const headerOverrideCountId = `supaship-header-override-count-${this.clientId}`\n\n return `\n <div class=\"supaship-toolbar-container ${positionClass}\" style=\"--offset-x: ${offsetX}; --offset-y: ${offsetY};\">\n <button class=\"supaship-toolbar-toggle\" id=\"${toggleId}\" aria-label=\"Supaship Toolbar\" title=\"Supaship Toolbar\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 256 256\"\n width=\"32\"\n style=\"vertical-align: middle;\">\n <rect width='256' height='256' rx='128' fill='#e4f222'></rect>\n <line\n x1='66'\n y1='118'\n x2='118'\n y2='66'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n <line\n x1='186'\n y1='70'\n x2='70'\n y2='186'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n <line\n x1='190'\n y1='138'\n x2='138'\n y2='190'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n </svg>\n <span class=\"supaship-toolbar-badge\" id=\"${badgeId}\"></span>\n </button>\n <div class=\"supaship-toolbar-panel\" id=\"${panelId}\">\n <div class=\"supaship-toolbar-header\">\n <input\n type=\"text\"\n class=\"supaship-search-input\"\n id=\"${searchId}\"\n placeholder=\"Search features\"\n />\n <span class=\"supaship-toolbar-overrides-label\" id=\"${headerOverrideCountId}\">0 overrides</span>\n <button\n class=\"supaship-header-btn\"\n id=\"${clearId}\"\n aria-label=\"Reset all overrides\"\n title=\"Reset all overrides to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/>\n <path d=\"M3 3v5h5\"/>\n <path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/>\n <path d=\"M16 16h5v5\"/>\n </svg>\n </button>\n <button\n class=\"supaship-header-btn\"\n id=\"${closeId}\"\n aria-label=\"Close toolbar\"\n title=\"Close toolbar\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6 6 18\"/>\n <path d=\"m6 6 12 12\"/>\n </svg>\n </button>\n </div>\n <div class=\"supaship-toolbar-content\" id=\"${contentId}\">\n <div class=\"supaship-toolbar-empty\">${NO_FEATURES_MESSAGE}</div>\n </div>\n </div>\n </div>\n `\n }\n\n private injectStyles(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n if (document.getElementById('supaship-toolbar-styles')) {\n return\n }\n\n const styles = document.createElement('style')\n styles.id = 'supaship-toolbar-styles'\n styles.textContent = `\n .supaship-toolbar-container {\n position: fixed;\n z-index: 999999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n font-size: 14px;\n }\n\n .supaship-toolbar-bottom-right {\n bottom: var(--offset-y);\n right: var(--offset-x);\n }\n\n .supaship-toolbar-bottom-left {\n bottom: var(--offset-y);\n left: var(--offset-x);\n }\n\n .supaship-toolbar-top-right {\n top: var(--offset-y);\n right: var(--offset-x);\n }\n\n .supaship-toolbar-top-left {\n top: var(--offset-y);\n left: var(--offset-x);\n }\n\n .supaship-toolbar-toggle {\n position: relative;\n width: 36px;\n height: 36px;\n border-radius: 100%;\n background: #000;\n border: none;\n color: white;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n transition: transform 0.2s, box-shadow 0.2s;\n }\n\n .supaship-toolbar-toggle:hover {\n transform: scale(1.05);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);\n }\n\n .supaship-toolbar-badge {\n position: absolute;\n top: -4px;\n right: -4px;\n background: #ef4444;\n color: white;\n font-size: 10px;\n font-weight: 600;\n min-width: 18px;\n height: 18px;\n border-radius: 9px;\n display: none;\n align-items: center;\n justify-content: center;\n padding: 0 5px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n pointer-events: none;\n }\n\n .supaship-toolbar-badge.show {\n display: flex;\n }\n\n .supaship-toolbar-panel {\n position: absolute;\n bottom: 48px;\n right: 0;\n width: 300px;\n max-height: 600px;\n background: #1a1a1a;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);\n display: none;\n flex-direction: column;\n overflow: hidden;\n border: 1px solid #333;\n }\n\n .supaship-toolbar-bottom-left .supaship-toolbar-panel,\n .supaship-toolbar-top-left .supaship-toolbar-panel {\n right: auto;\n left: 0;\n }\n\n .supaship-toolbar-top-right .supaship-toolbar-panel,\n .supaship-toolbar-top-left .supaship-toolbar-panel {\n bottom: auto;\n top: 60px;\n }\n\n .supaship-toolbar-panel.open {\n display: flex;\n }\n\n .supaship-toolbar-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n border-bottom: 1px solid #333;\n background: #0f0f0f;\n min-width: 0;\n overflow: hidden;\n }\n\n .supaship-search-input {\n flex: 1;\n min-width: 0;\n background: transparent;\n border: none;\n color: #e5e5e5;\n padding: 0;\n font-size: 13px;\n outline: none;\n }\n\n .supaship-search-input::placeholder {\n color: #888;\n }\n\n .supaship-header-btn {\n background: transparent;\n border: none;\n color: #e5e5e5;\n width: 24px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.2s;\n padding: 0;\n }\n\n .supaship-header-btn:hover {\n color: #ef4444;\n }\n\n .supaship-toolbar-content {\n flex: 1;\n overflow-y: auto;\n padding: 8px;\n min-height: 300px;\n max-height: 300px;\n }\n\n .supaship-toolbar-empty {\n padding: 32px 16px;\n text-align: center;\n color: #888;\n }\n\n .supaship-feature-item {\n padding: 0 6px;\n }\n\n .supaship-feature-item.disabled {\n opacity: 0.5;\n pointer-events: none;\n }\n\n .supaship-feature-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n min-height: 32px;\n }\n\n .supaship-feature-name {\n font-weight: 500;\n color: #e5e5e5;\n font-size: 13px;\n flex: 1;\n min-width: 0;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n }\n\n .supaship-feature-override-indicator {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #ef4444;\n flex-shrink: 0;\n }\n\n .supaship-toolbar-overrides-label {\n font-size: 12px;\n color: #888;\n white-space: nowrap;\n flex-shrink: 0;\n transition: all 0.2s;\n }\n\n .supaship-toolbar-overrides-label-count.has-overrides {\n background: #ef4444;\n color: white;\n font-size: 10px;\n font-weight: 600;\n min-width: 18px;\n height: 18px;\n border-radius: 9px;\n padding: 2px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n .supaship-feature-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n min-height: 20px;\n }\n\n .supaship-feature-content {\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n .supaship-feature-input {\n flex: 1;\n padding: 6px 8px;\n background: #1a1a1a;\n border: 1px solid #555;\n color: #e5e5e5;\n border-radius: 4px;\n font-size: 13px;\n font-family: 'Monaco', 'Courier New', monospace;\n outline: none;\n resize: vertical;\n min-height: 60px;\n margin-bottom: 8px;\n }\n\n .supaship-feature-input:focus {\n border-color: #667eea;\n }\n\n .supaship-btn {\n padding: 4px 12px;\n border: none;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n }\n\n .supaship-btn-primary {\n background: #444;\n color: white;\n }\n\n .supaship-btn-primary:hover {\n background: #555;\n }\n\n .supaship-btn-secondary {\n background: #444;\n color: #e5e5e5;\n }\n\n .supaship-btn-secondary:hover {\n background: #555;\n }\n\n .supaship-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .supaship-btn:disabled:hover {\n background: #444;\n }\n\n .supaship-header-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n }\n\n .supaship-header-btn:disabled:hover {\n color: #e5e5e5;\n }\n\n .supaship-toggle {\n position: relative;\n display: inline-block;\n width: 32px;\n height: 18px;\n flex-shrink: 0;\n }\n\n .supaship-toggle input {\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .supaship-toggle-slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: #333;\n border: 1px solid #555;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 20px;\n }\n\n .supaship-toggle-slider:before {\n position: absolute;\n content: \"\";\n height: 14px;\n width: 14px;\n left: 2px;\n bottom: 1px;\n background-color: #666;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 50%;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n }\n\n .supaship-toggle input:checked + .supaship-toggle-slider {\n background-color: #fff;\n border-color: #fff;\n }\n\n .supaship-toggle input:checked + .supaship-toggle-slider:before {\n transform: translateX(13px);\n background-color: #000;\n }\n\n .supaship-toggle input:disabled + .supaship-toggle-slider {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .supaship-toggle:hover .supaship-toggle-slider:before {\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);\n }\n\n .supaship-btn-icon {\n background: transparent;\n border: none;\n color: #e5e5e5;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n cursor: pointer;\n padding: 0;\n transition: all 0.2s;\n flex-shrink: 0;\n }\n\n .supaship-btn-icon:hover {\n background: #444;\n color: #ef4444;\n }\n `\n document.head.appendChild(styles)\n }\n\n private attachEventListeners(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n const panelId = `supaship-toolbar-panel-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const closeId = `supaship-close-toolbar-${this.clientId}`\n const searchId = `supaship-search-input-${this.clientId}`\n const contentId = `supaship-toolbar-content-${this.clientId}`\n\n const toggle = document.getElementById(toggleId)\n const panel = document.getElementById(panelId)\n const clearAll = document.getElementById(clearId)\n const close = document.getElementById(closeId)\n const searchInput = document.getElementById(searchId) as HTMLInputElement\n const content = document.getElementById(contentId)\n\n toggle?.addEventListener('click', () => {\n panel?.classList.toggle('open')\n })\n\n close?.addEventListener('click', () => {\n panel?.classList.remove('open')\n })\n\n clearAll?.addEventListener('click', () => {\n this.clearAllOverrides()\n })\n\n searchInput?.addEventListener('input', e => {\n this.state.searchQuery = (e.target as HTMLInputElement).value.toLowerCase()\n this.updateToolbarUI()\n })\n\n // Use event delegation on content element - survives innerHTML updates\n if (content) {\n // Handle button clicks (remove and set actions)\n content.addEventListener('click', (e: Event) => {\n const target = e.target as HTMLElement\n const buttonElement = target.closest('button[data-action]') as HTMLButtonElement\n if (!buttonElement) return\n\n e.preventDefault()\n e.stopPropagation()\n\n const featureName = buttonElement.dataset.feature!\n const action = buttonElement.dataset.action\n\n if (action === 'remove') {\n this.removeOverride(featureName)\n } else if (action === 'set') {\n const textarea = content.querySelector(\n `textarea[data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLTextAreaElement\n if (textarea && textarea.value.trim()) {\n try {\n const value = JSON.parse(textarea.value)\n this.setOverride(featureName, value)\n } catch {\n // If not valid JSON, wrap string in object\n this.setOverride(featureName, { value: textarea.value })\n }\n }\n }\n })\n\n // Handle checkbox changes for boolean toggles\n content.addEventListener('change', (e: Event) => {\n const target = e.target as HTMLInputElement\n if (target.type === 'checkbox' && target.dataset.type === 'boolean') {\n const featureName = target.dataset.feature!\n const newValue = target.checked\n this.setOverride(featureName, newValue)\n }\n })\n\n // Handle textarea input to update button states\n content.addEventListener('input', (e: Event) => {\n const target = e.target as HTMLTextAreaElement\n if (target.tagName === 'TEXTAREA' && target.dataset.feature) {\n const featureName = target.dataset.feature!\n const originalValue = target.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = target.value !== originalValue\n const hasContent = target.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n }\n })\n\n // Handle textarea paste events\n content.addEventListener('paste', (e: Event) => {\n const target = e.target as HTMLTextAreaElement\n if (target.tagName === 'TEXTAREA' && target.dataset.feature) {\n setTimeout(() => {\n const featureName = target.dataset.feature!\n const originalValue = target.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${featureName}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = target.value !== originalValue\n const hasContent = target.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n }, 0)\n }\n })\n\n // Handle Ctrl/Cmd+Enter to set override\n content.addEventListener('keydown', (e: KeyboardEvent) => {\n const target = e.target as HTMLTextAreaElement\n if (\n target.tagName === 'TEXTAREA' &&\n target.dataset.feature &&\n (e.ctrlKey || e.metaKey) &&\n e.key === 'Enter'\n ) {\n e.preventDefault()\n const featureName = target.dataset.feature!\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn && !overrideBtn.disabled) {\n overrideBtn.click()\n }\n }\n })\n }\n }\n\n private updateToolbarUI(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const contentId = `supaship-toolbar-content-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const badgeId = `supaship-toolbar-badge-${this.clientId}`\n const headerOverrideCountId = `supaship-header-override-count-${this.clientId}`\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n\n const content = document.getElementById(contentId)\n const clearAllBtn = document.getElementById(clearId) as HTMLButtonElement\n const badge = document.getElementById(badgeId)\n const headerOverrideCount = document.getElementById(headerOverrideCountId)\n const toggleBtn = document.getElementById(toggleId) as HTMLButtonElement\n\n if (!content) {\n console.warn('[Toolbar] Content element not found:', contentId)\n return\n }\n\n // Update clear all button state and badge\n const overrideCount = Object.keys(this.state.overrides).length\n const hasOverrides = overrideCount > 0\n if (clearAllBtn) {\n clearAllBtn.disabled = !hasOverrides\n }\n\n // Update badge on toggle button\n if (badge) {\n if (hasOverrides) {\n badge.textContent = overrideCount > 99 ? '99+' : String(overrideCount)\n badge.classList.add('show')\n } else {\n badge.classList.remove('show')\n }\n }\n\n // Update tooltip on toggle button\n if (toggleBtn) {\n if (hasOverrides) {\n toggleBtn.setAttribute(\n 'title',\n `Supaship Toolbar, ${overrideCount} override${overrideCount === 1 ? '' : 's'}`\n )\n toggleBtn.setAttribute(\n 'aria-label',\n `Supaship Toolbar, ${overrideCount} override${overrideCount === 1 ? '' : 's'}`\n )\n } else {\n toggleBtn.setAttribute('title', 'Supaship Toolbar')\n toggleBtn.setAttribute('aria-label', 'Supaship Toolbar')\n }\n }\n\n // Update override count in header\n if (headerOverrideCount) {\n // Escape overrideCount to prevent any potential XSS (defense in depth)\n const escapedCount = this.escapeHtml(String(overrideCount))\n headerOverrideCount.innerHTML = `<span class=\"supaship-toolbar-overrides-label-count ${hasOverrides ? 'has-overrides' : ''}\">${escapedCount}</span> override${overrideCount === 1 ? '' : 's'}`\n }\n\n const features = Array.from(this.state.features).sort()\n\n // Filter features based on search query\n const filteredFeatures = features.filter(name =>\n name.toLowerCase().includes(this.state.searchQuery)\n )\n\n if (filteredFeatures.length === 0) {\n content.innerHTML = this.state.searchQuery\n ? '<div class=\"supaship-toolbar-empty\">No matching features found</div>'\n : `<div class=\"supaship-toolbar-empty\">${NO_FEATURES_MESSAGE}</div>`\n return\n }\n\n const htmlContent = filteredFeatures\n .map(featureName => {\n const hasOverride = featureName in this.state.overrides\n const currentValue = this.state.featureValues[featureName]\n const overrideValue = hasOverride ? this.state.overrides[featureName] : currentValue\n const isDisabled = !this.state.useLocalOverrides\n const itemClass = `supaship-feature-item ${isDisabled ? 'disabled' : ''}`\n\n // Check if the feature is boolean\n const isBoolean =\n typeof currentValue === 'boolean' || (hasOverride && typeof overrideValue === 'boolean')\n\n if (isBoolean) {\n // Render toggle switch for boolean values (single row layout)\n const isChecked = hasOverride ? overrideValue === true : currentValue === true\n return `\n <div class=\"${itemClass}\">\n <div class=\"supaship-feature-row\">\n <span class=\"supaship-feature-name\">\n ${this.escapeHtml(featureName)}\n ${hasOverride ? '<span class=\"supaship-feature-override-indicator\" title=\"Serving local override\"></span>' : ''}\n </span>\n <div class=\"supaship-feature-actions\">\n ${\n hasOverride\n ? `\n <button\n class=\"supaship-btn-icon\"\n data-feature=\"${this.escapeHtml(featureName)}\"\n data-action=\"remove\"\n title=\"Reset to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/>\n <path d=\"M3 3v5h5\"/>\n <path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/>\n <path d=\"M16 16h5v5\"/>\n </svg>\n </button>\n `\n : ''\n }\n <label class=\"supaship-toggle\">\n <input\n type=\"checkbox\"\n ${isChecked ? 'checked' : ''}\n data-feature=\"${this.escapeHtml(featureName)}\"\n data-type=\"boolean\"\n />\n <span class=\"supaship-toggle-slider\"></span>\n </label>\n </div>\n </div>\n </div>\n `\n } else {\n // Render textarea for non-boolean values\n const currentDisplayValue = hasOverride\n ? JSON.stringify(overrideValue)\n : currentValue !== undefined\n ? JSON.stringify(currentValue)\n : ''\n const escapedFeatureName = this.escapeHtml(featureName)\n const escapedCurrentDisplayValue = this.escapeHtml(currentDisplayValue)\n const escapedTextareaContent = hasOverride\n ? this.escapeHtml(JSON.stringify(overrideValue))\n : escapedCurrentDisplayValue\n\n return `\n <div class=\"${itemClass}\">\n <div class=\"supaship-feature-row\">\n <span class=\"supaship-feature-name\">\n ${escapedFeatureName}\n ${hasOverride ? '<span class=\"supaship-feature-override-indicator\" title=\"Serving local override\"></span>' : ''}\n </span>\n <div class=\"supaship-feature-actions\">\n ${\n hasOverride\n ? `\n <button\n class=\"supaship-btn-icon\"\n data-feature=\"${escapedFeatureName}\"\n data-action=\"remove\"\n title=\"Reset to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 256\" width=\"14\" height=\"14\">\n <path d=\"M240,56v48a8,8,0,0,1-8,8H184a8,8,0,0,1,0-16H211.4L184.81,71.64l-.25-.24a80,80,0,1,0-1.67,114.78,8,8,0,0,1,11,11.63A95.44,95.44,0,0,1,128,224h-1.32A96,96,0,1,1,195.75,60L224,85.8V56a8,8,0,1,1,16,0Z\" fill=\"currentColor\"/>\n </svg>\n </button>\n `\n : ''\n }\n <button\n class=\"supaship-btn supaship-btn-primary\"\n data-feature=\"${escapedFeatureName}\"\n data-action=\"set\"\n disabled>\n Override\n </button>\n </div>\n </div>\n <div class=\"supaship-feature-content\">\n <textarea\n class=\"supaship-feature-input\"\n placeholder=\"Override JSON value\"\n data-feature=\"${escapedFeatureName}\"\n data-original=\"${escapedCurrentDisplayValue}\"\n >${escapedTextareaContent}</textarea>\n </div>\n </div>\n `\n }\n })\n .join('')\n\n requestAnimationFrame(() => {\n // Set innerHTML - event listeners are handled via delegation in attachEventListeners()\n content.innerHTML = htmlContent\n\n // Update button states for textareas that already have values\n content.querySelectorAll('textarea[data-feature]').forEach(textarea => {\n const textareaElement = textarea as HTMLTextAreaElement\n const featureName = textareaElement.dataset.feature!\n const originalValue = textareaElement.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = textareaElement.value !== originalValue\n const hasContent = textareaElement.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n })\n })\n }\n\n private escapeHtml(text: string): string {\n const div = typeof document !== 'undefined' ? document.createElement('div') : null\n if (div) {\n div.textContent = text\n return div.innerHTML\n }\n return text.replace(/[&<>\"']/g, char => {\n const escapeMap: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n }\n return escapeMap[char]\n })\n }\n\n /**\n * Escapes special characters in CSS attribute selectors to prevent CSS injection\n * @param value The value to escape for use in CSS attribute selectors\n */\n private escapeCssSelector(value: string): string {\n // Escape special CSS selector characters: \", ', ], \\\n return value.replace(/[\"'\\\\\\]]/g, '\\\\$&')\n }\n}\n","export const DEFAULT_FEATURES_URL = 'https://edge.supaship.com/v1/features'\nexport const DEFAULT_EVENTS_URL = 'https://edge.supaship.com/v1/events'\n","import {\n SupaClientConfig,\n FeatureContext,\n FeatureValue,\n NetworkConfig,\n Features,\n FeaturesWithFallbacks,\n} from './types'\nimport { retry } from './utils'\nimport { SupaPlugin } from './plugins/types'\nimport { SupaToolbarPlugin } from './plugins/toolbar-plugin'\nimport { DEFAULT_FEATURES_URL, DEFAULT_EVENTS_URL } from './constants'\n\ntype RequiredRetryConfig = Required<NonNullable<NetworkConfig['retry']>>\ntype ResolvedNetworkConfig = {\n featuresAPIUrl: string\n eventsAPIUrl: string\n retry: RequiredRetryConfig\n requestTimeoutMs: number\n}\n\nexport class SupaClient<TFeatures extends FeaturesWithFallbacks> {\n private apiKey: string\n private environment: string\n private defaultContext?: FeatureContext\n private plugins: SupaPlugin[]\n private featureDefinitions: Features<TFeatures>\n private clientId: string\n\n private fetchImpl: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>\n private networkConfig: ResolvedNetworkConfig\n\n constructor(config: SupaClientConfig & { features: TFeatures }) {\n this.apiKey = config.apiKey\n this.environment = config.environment\n this.defaultContext = config.context\n this.featureDefinitions = config.features as Features<TFeatures>\n\n // Generate unique client ID\n this.clientId = this.generateClientId()\n\n this.networkConfig = {\n featuresAPIUrl: config.networkConfig?.featuresAPIUrl || DEFAULT_FEATURES_URL,\n eventsAPIUrl: config.networkConfig?.eventsAPIUrl || DEFAULT_EVENTS_URL,\n retry: {\n enabled: config.networkConfig?.retry?.enabled ?? true,\n maxAttempts: config.networkConfig?.retry?.maxAttempts ?? 3,\n backoff: config.networkConfig?.retry?.backoff ?? 1000,\n },\n requestTimeoutMs: config.networkConfig?.requestTimeoutMs ?? 10000,\n }\n\n // Prefer injected fetch, then global fetch if available\n const globalFetch: typeof fetch | undefined =\n typeof globalThis !== 'undefined'\n ? (globalThis as unknown as { fetch?: typeof fetch }).fetch\n : undefined\n if (config.networkConfig?.fetchFn) {\n this.fetchImpl = config.networkConfig.fetchFn\n } else if (typeof globalFetch === 'function') {\n this.fetchImpl = globalFetch.bind(globalThis)\n } else {\n throw new Error(\n 'No fetch implementation available. Provide fetchFn in config or use a runtime with global fetch (e.g., Node 18+, browsers).'\n )\n }\n\n // Initialize plugins with automatic toolbar plugin in browser\n this.plugins = this.initializePlugins(config)\n\n // Initialize plugins with available features and their fallback values\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onInit?.({\n clientId: this.clientId,\n availableFeatures: this.featureDefinitions,\n context: this.defaultContext,\n })\n )\n ).catch(console.error)\n }\n\n /**\n * Generate a unique client ID\n */\n private generateClientId(): string {\n return `supaship-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`\n }\n\n /**\n * Initialize plugins with automatic toolbar plugin in browser environments\n */\n private initializePlugins(config: SupaClientConfig & { features: TFeatures }): SupaPlugin[] {\n const plugins = config.plugins || []\n\n // Check if we're in a browser environment\n const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined'\n\n // If toolbar is explicitly disabled, don't add it\n if (config.toolbar === false) {\n return plugins\n }\n\n // If in browser and toolbar not disabled, add it automatically\n if (isBrowser) {\n // Check if user already added toolbar plugin manually\n const hasToolbarPlugin = plugins.some(p => p.name === 'toolbar-plugin')\n\n if (!hasToolbarPlugin) {\n // Add toolbar with user config or defaults\n const toolbarConfig = config.toolbar || { enabled: 'auto' }\n const toolbarPlugin = new SupaToolbarPlugin(toolbarConfig)\n return [toolbarPlugin, ...plugins]\n }\n }\n\n return plugins\n }\n\n /**\n * Updates the default context for the client\n * @param context - New context to merge with or replace the existing context\n * @param mergeWithExisting - Whether to merge with existing context (default: true)\n */\n updateContext(context: FeatureContext, mergeWithExisting: boolean = true): void {\n const oldContext = this.defaultContext\n\n if (mergeWithExisting && this.defaultContext) {\n this.defaultContext = { ...this.defaultContext, ...context }\n } else {\n this.defaultContext = context\n }\n\n // Notify plugins of context change\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onContextUpdate?.(oldContext, this.defaultContext!, 'updateContext')\n )\n ).catch(console.error)\n }\n\n /**\n * Gets the current default context\n */\n getContext(): FeatureContext | undefined {\n return this.defaultContext\n }\n\n /**\n * Gets the fallback value for a feature from its definition\n */\n getFeatureFallback<TKey extends keyof TFeatures>(featureName: TKey): Features<TFeatures>[TKey] {\n return this.featureDefinitions[featureName]\n }\n\n private getVariationValue(variation: FeatureValue, fallback: FeatureValue): FeatureValue {\n if (variation !== undefined && variation !== null) {\n return variation\n }\n\n return fallback ?? null\n }\n\n async getFeature<TKey extends keyof TFeatures>(\n featureName: TKey,\n options?: { context?: FeatureContext }\n ): Promise<Features<TFeatures>[TKey]> {\n const { context } = options ?? {}\n\n // Only merge context if it's defined and not null\n const mergedContext: FeatureContext | undefined =\n typeof context === 'object' && context !== null\n ? { ...(this.defaultContext ?? {}), ...context }\n : this.defaultContext\n\n try {\n const response = await this.getFeatures([featureName as string], {\n context: mergedContext,\n })\n\n // Get the specific feature value\n const value = response[featureName as string]\n return value as Features<TFeatures>[TKey]\n } catch (error) {\n // Run onError hooks\n await Promise.all(this.plugins.map(plugin => plugin.onError?.(error as Error, mergedContext)))\n\n // Use fallback feature value when API fails\n const fallbackValue = this.featureDefinitions[featureName]\n\n // Notify plugins that fallback was used\n await Promise.all(\n this.plugins.map(plugin =>\n plugin.onFallbackUsed?.(\n featureName as string,\n fallbackValue as FeatureValue,\n error as Error\n )\n )\n )\n return fallbackValue as Features<TFeatures>[TKey]\n }\n }\n\n async getFeatures<TKeys extends readonly (keyof TFeatures)[]>(\n featureNames: TKeys,\n options?: { context?: FeatureContext }\n ): Promise<{ [K in TKeys[number]]: Features<TFeatures>[K] }> {\n const { context: contextOverride } = options ?? {}\n\n // Only merge context if it's defined and not null\n const mergedContext: FeatureContext | undefined =\n typeof contextOverride === 'object' && contextOverride !== null\n ? { ...(this.defaultContext ?? {}), ...contextOverride }\n : this.defaultContext\n\n // Notify plugins of context update for this request\n if (contextOverride) {\n await Promise.all(\n this.plugins.map(plugin =>\n plugin.onContextUpdate?.(this.defaultContext, mergedContext!, 'request')\n )\n )\n }\n\n // Convert feature names to strings for API call\n const featureNamesArray = featureNames.map(name => name as string)\n\n try {\n // Run beforeGetFeatures hooks\n await Promise.all(\n this.plugins.map(plugin => plugin.beforeGetFeatures?.(featureNamesArray, mergedContext))\n )\n\n type FeaturesResponse = { features: Record<string, { variation: FeatureValue }> }\n const fetchFeatures = async (): Promise<Record<string, FeatureValue>> => {\n const url = this.networkConfig.featuresAPIUrl\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n }\n const body = JSON.stringify({\n environment: this.environment,\n features: featureNamesArray,\n context: mergedContext,\n })\n\n // Notify plugins before request\n await Promise.all(this.plugins.map(plugin => plugin.beforeRequest?.(url, body, headers)))\n\n const startTime = Date.now()\n // Support timeout via AbortController when available\n const AbortCtrl: typeof AbortController | undefined =\n typeof globalThis !== 'undefined'\n ? (globalThis as unknown as { AbortController?: typeof AbortController })\n .AbortController\n : undefined\n let controller: AbortController | undefined\n let timeoutId: ReturnType<typeof setTimeout> | undefined\n if (this.networkConfig.requestTimeoutMs && typeof AbortCtrl === 'function') {\n controller = new AbortCtrl()\n timeoutId = setTimeout(() => controller?.abort(), this.networkConfig.requestTimeoutMs)\n }\n let response: Response\n try {\n response = await this.fetchImpl(url, {\n method: 'POST',\n headers,\n body,\n signal: controller?.signal,\n })\n } finally {\n if (timeoutId) clearTimeout(timeoutId)\n }\n const duration = Date.now() - startTime\n\n // Notify plugins after response\n await Promise.all(\n this.plugins.map(plugin => plugin.afterResponse?.(response, { duration }))\n )\n\n if (!response.ok) {\n throw new Error(`Failed to fetch features: ${response.statusText}`)\n }\n\n const data = (await response.json()) as FeaturesResponse\n const result: Record<string, FeatureValue> = {}\n\n featureNamesArray.forEach(name => {\n const variation = data.features[name]?.variation\n result[name] = this.getVariationValue(\n variation,\n this.featureDefinitions[name as keyof TFeatures]\n )\n })\n\n return result\n }\n\n const result = this.networkConfig.retry.enabled\n ? await retry(\n fetchFeatures,\n this.networkConfig.retry.maxAttempts,\n this.networkConfig.retry.backoff,\n (attempt, error, willRetry) => {\n // Notify plugins of retry attempts\n Promise.all(\n this.plugins.map(plugin => plugin.onRetryAttempt?.(attempt, error, willRetry))\n ).catch(console.error)\n }\n )\n : await fetchFeatures()\n\n // Run afterGetFeatures hooks\n await Promise.all(\n this.plugins.map(plugin => plugin.afterGetFeatures?.(result, mergedContext))\n )\n\n // Return the fetched features\n return result as { [K in TKeys[number]]: Features<TFeatures>[K] }\n } catch (error) {\n // Run onError hooks\n await Promise.all(this.plugins.map(plugin => plugin.onError?.(error as Error, mergedContext)))\n\n // Create fallback result with requested feature names\n const fallbackResult: Record<string, FeatureValue> = {}\n\n featureNamesArray.forEach(featureName => {\n fallbackResult[featureName] = this.featureDefinitions[featureName as keyof TFeatures]\n\n // Notify plugins that fallback was used for each feature\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onFallbackUsed?.(\n featureName,\n this.featureDefinitions[featureName as keyof TFeatures],\n error as Error\n )\n )\n ).catch(console.error)\n })\n\n return fallbackResult as { [K in TKeys[number]]: Features<TFeatures>[K] }\n }\n }\n}\n"],"mappings":"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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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","names":["result"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/plugins/toolbar-plugin.ts","../src/constants.ts","../src/client.ts"],"sourcesContent":["export type * from './types'\nexport { SupaClient } from './client'\nexport type { SupaPlugin, SupaPluginConfig } from './plugins/types'\nexport { SupaToolbarPlugin as ToolbarPlugin } from './plugins/toolbar-plugin'\nexport type {\n SupaToolbarPluginConfig,\n SupaToolbarPosition,\n SupaToolbarOverrideChange,\n SupaToolbarOverrideChangeCallback,\n} from './plugins/toolbar-plugin'\n// export { LoggingPlugin } from \"./plugins/logging-plugin\";\n// export { CachingPlugin } from \"./plugins/caching-plugin\";\n// export { AnalyticsPlugin } from \"./plugins/analytics-plugin\";\n// export { LocalDevPlugin } from \"./plugins/local-dev-plugin\";\n","export function sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nexport async function retry<T>(\n fn: () => Promise<T>,\n maxAttempts: number = 3,\n backoff: number = 1000,\n onRetry?: (attempt: number, error: Error, willRetry: boolean) => void\n): Promise<T> {\n let lastError: Error\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn()\n } catch (error) {\n lastError = error as Error\n const willRetry = attempt < maxAttempts\n\n if (onRetry) {\n onRetry(attempt, lastError, willRetry)\n }\n\n if (!willRetry) break\n await sleep(backoff * Math.pow(2, attempt - 1))\n }\n }\n\n throw lastError!\n}\n","import { SupaPlugin, SupaPluginConfig } from './types'\nimport { FeatureContext, FeatureValue } from '../types'\n\nexport interface SupaToolbarPosition {\n placement?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'\n offset?: { x: string; y: string }\n}\n\nexport type SupaToolbarOverrideChange = {\n feature: string\n value: FeatureValue\n}\n\nexport type SupaToolbarOverrideChangeCallback = (\n featureOverride: SupaToolbarOverrideChange,\n allOverrides: Record<string, FeatureValue>\n) => void\n\nexport interface SupaToolbarPluginConfig extends Omit<SupaPluginConfig, 'enabled'> {\n enabled?: boolean | 'auto' // auto means show only on localhost\n position?: SupaToolbarPosition\n onOverrideChange?: SupaToolbarOverrideChangeCallback\n}\n\ninterface SupaToolbarState {\n overrides: Record<string, FeatureValue>\n features: Set<string>\n featureValues: Record<string, FeatureValue>\n context?: FeatureContext\n searchQuery: string\n useLocalOverrides: boolean\n}\n\nconst DEFAULT_STORAGE_KEY = 'supaship-feature-overrides'\n\nconst NO_FEATURES_MESSAGE = `No feature flags configured in the client.`\n\n/**\n * Toolbar plugin for local feature flag testing\n * Provides a visual interface to override feature flags during development\n */\nexport class SupaToolbarPlugin implements SupaPlugin {\n name = 'toolbar-plugin'\n private config: {\n enabled: boolean | 'auto'\n position: Required<SupaToolbarPosition>\n onOverrideChange?: SupaToolbarOverrideChangeCallback\n }\n private state: SupaToolbarState\n private clientId?: string\n private storageKey: string = DEFAULT_STORAGE_KEY\n\n constructor(config: SupaToolbarPluginConfig = {}) {\n this.config = {\n enabled: config.enabled ?? 'auto',\n position: {\n placement: config.position?.placement ?? 'bottom-right',\n offset: config.position?.offset ?? { x: '1rem', y: '1rem' },\n },\n onOverrideChange: config.onOverrideChange,\n }\n\n this.state = {\n overrides: {},\n features: new Set(),\n featureValues: {},\n searchQuery: '',\n useLocalOverrides: true,\n }\n }\n\n cleanup(): void {\n this.removeToolbar()\n }\n\n private shouldShowToolbar(): boolean {\n if (this.config.enabled === true) return true\n if (this.config.enabled === false) return false\n\n // Auto mode: show only on localhost\n if (typeof window !== 'undefined') {\n return (\n window.location.hostname === 'localhost' ||\n window.location.hostname === '127.0.0.1' ||\n window.location.hostname === '' ||\n window.location.hostname.endsWith('.local') ||\n window.location.hostname.endsWith('.localhost')\n )\n }\n return false\n }\n\n onInit(params: {\n availableFeatures: Record<string, FeatureValue>\n context?: FeatureContext\n clientId: string\n }): void {\n const { availableFeatures, context, clientId } = params\n\n // Set client ID for DOM element IDs\n this.clientId = clientId\n\n // Use shared storage key (not client-specific) to persist across refreshes\n this.storageKey = DEFAULT_STORAGE_KEY\n\n // Load overrides from shared storage\n this.state.overrides = this.loadOverrides()\n\n // Initialize with all available features and their fallback values from config\n this.state.features = new Set(Object.keys(availableFeatures))\n this.state.featureValues = { ...availableFeatures }\n this.state.context = context\n\n // Inject toolbar if conditions are met\n if (this.shouldShowToolbar()) {\n this.injectToolbar()\n }\n\n // Update toolbar UI if it exists\n this.updateToolbarUI()\n }\n\n async beforeGetFeatures(_featureNames: string[], context?: FeatureContext): Promise<void> {\n // Update context if it changed\n this.state.context = context\n\n // Load overrides from shared storage\n this.state.overrides = this.loadOverrides()\n\n // Update toolbar UI if it exists\n this.updateToolbarUI()\n }\n\n async afterGetFeatures(\n results: Record<string, FeatureValue>,\n context?: FeatureContext\n ): Promise<void> {\n // Update feature values with fetched results (this replaces config fallback values)\n Object.keys(results).forEach(name => {\n this.state.featureValues[name] = results[name]\n })\n\n // Apply overrides to results only if local overrides are enabled\n if (this.state.useLocalOverrides) {\n Object.keys(this.state.overrides).forEach(featureName => {\n if (featureName in results) {\n results[featureName] = this.state.overrides[featureName]\n }\n })\n }\n\n // Track features and update UI\n Object.keys(results).forEach(name => this.state.features.add(name))\n this.state.context = context\n this.updateToolbarUI()\n }\n\n private loadOverrides(): Record<string, FeatureValue> {\n if (typeof window === 'undefined' || !window.localStorage) {\n return {}\n }\n\n try {\n const stored = window.localStorage.getItem(this.storageKey)\n return stored ? JSON.parse(stored) : {}\n } catch {\n return {}\n }\n }\n\n private saveOverrides(\n feature?: string,\n value?: FeatureValue,\n allOverrides?: Record<string, FeatureValue>\n ): void {\n if (typeof window === 'undefined' || !window.localStorage) {\n return\n }\n\n try {\n window.localStorage.setItem(this.storageKey, JSON.stringify(allOverrides))\n this.config.onOverrideChange?.(\n { feature: feature ?? '', value: value ?? null },\n allOverrides ?? {}\n )\n } catch (error) {\n console.error('Supaship: Failed to save feature overrides:', error)\n }\n }\n\n public setOverride(featureName: string, value: FeatureValue): void {\n this.state.overrides[featureName] = value\n this.saveOverrides(featureName, value, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public removeOverride(featureName: string): void {\n delete this.state.overrides[featureName]\n this.saveOverrides(featureName, null, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public clearAllOverrides(): void {\n this.state.overrides = {}\n this.saveOverrides('', null, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public getOverrides(): Record<string, FeatureValue> {\n return { ...this.state.overrides }\n }\n\n private injectToolbar(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return\n }\n\n // Check if toolbar with this client ID already exists\n const toolbarId = `supaship-toolbar-${this.clientId}`\n if (document.getElementById(toolbarId)) {\n return\n }\n\n // Create toolbar container\n const toolbar = document.createElement('div')\n toolbar.id = toolbarId\n toolbar.setAttribute('data-supaship-client', this.clientId || '')\n toolbar.innerHTML = this.getToolbarHTML()\n\n // Add styles\n this.injectStyles()\n\n // Add to DOM\n document.body.appendChild(toolbar)\n\n // Add event listeners\n this.attachEventListeners()\n }\n\n private removeToolbar(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const toolbar = document.getElementById(`supaship-toolbar-${this.clientId}`)\n if (toolbar) {\n toolbar.remove()\n }\n\n const styles = document.getElementById('supaship-toolbar-styles')\n if (styles) {\n styles.remove()\n }\n }\n\n private getToolbarHTML(): string {\n const { placement, offset } = this.config.position\n const positionClass = `supaship-toolbar-${placement}`\n const offsetX = offset?.x ?? '1rem'\n const offsetY = offset?.y ?? '1rem'\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n const panelId = `supaship-toolbar-panel-${this.clientId}`\n const searchId = `supaship-search-input-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const closeId = `supaship-close-toolbar-${this.clientId}`\n const contentId = `supaship-toolbar-content-${this.clientId}`\n const badgeId = `supaship-toolbar-badge-${this.clientId}`\n const headerOverrideCountId = `supaship-header-override-count-${this.clientId}`\n\n return `\n <div class=\"supaship-toolbar-container ${positionClass}\" style=\"--offset-x: ${offsetX}; --offset-y: ${offsetY};\">\n <button class=\"supaship-toolbar-toggle\" id=\"${toggleId}\" aria-label=\"Supaship Toolbar\" title=\"Supaship Toolbar\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 256 256\"\n width=\"32\"\n style=\"vertical-align: middle;\">\n <rect width='256' height='256' rx='128' fill='#e4f222'></rect>\n <line\n x1='66'\n y1='118'\n x2='118'\n y2='66'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n <line\n x1='186'\n y1='70'\n x2='70'\n y2='186'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n <line\n x1='190'\n y1='138'\n x2='138'\n y2='190'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n </svg>\n <span class=\"supaship-toolbar-badge\" id=\"${badgeId}\"></span>\n </button>\n <div class=\"supaship-toolbar-panel\" id=\"${panelId}\">\n <div class=\"supaship-toolbar-header\">\n <input\n type=\"text\"\n class=\"supaship-search-input\"\n id=\"${searchId}\"\n placeholder=\"Search features\"\n />\n <span class=\"supaship-toolbar-overrides-label\" id=\"${headerOverrideCountId}\">0 overrides</span>\n <button\n class=\"supaship-header-btn\"\n id=\"${clearId}\"\n aria-label=\"Reset all overrides\"\n title=\"Reset all overrides to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/>\n <path d=\"M3 3v5h5\"/>\n <path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/>\n <path d=\"M16 16h5v5\"/>\n </svg>\n </button>\n <button\n class=\"supaship-header-btn\"\n id=\"${closeId}\"\n aria-label=\"Close toolbar\"\n title=\"Close toolbar\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6 6 18\"/>\n <path d=\"m6 6 12 12\"/>\n </svg>\n </button>\n </div>\n <div class=\"supaship-toolbar-content\" id=\"${contentId}\">\n <div class=\"supaship-toolbar-empty\">${NO_FEATURES_MESSAGE}</div>\n </div>\n </div>\n </div>\n `\n }\n\n private injectStyles(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n if (document.getElementById('supaship-toolbar-styles')) {\n return\n }\n\n const styles = document.createElement('style')\n styles.id = 'supaship-toolbar-styles'\n styles.textContent = `\n .supaship-toolbar-container {\n position: fixed;\n z-index: 999999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n font-size: 14px;\n }\n\n .supaship-toolbar-bottom-right {\n bottom: var(--offset-y);\n right: var(--offset-x);\n }\n\n .supaship-toolbar-bottom-left {\n bottom: var(--offset-y);\n left: var(--offset-x);\n }\n\n .supaship-toolbar-top-right {\n top: var(--offset-y);\n right: var(--offset-x);\n }\n\n .supaship-toolbar-top-left {\n top: var(--offset-y);\n left: var(--offset-x);\n }\n\n .supaship-toolbar-toggle {\n position: relative;\n width: 36px;\n height: 36px;\n border-radius: 100%;\n border: none;\n color: white;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n transition: transform 0.2s, box-shadow 0.2s;\n }\n\n .supaship-toolbar-toggle:hover {\n transform: scale(1.05);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);\n }\n\n .supaship-toolbar-badge {\n position: absolute;\n top: -4px;\n right: -4px;\n background: #ef4444;\n color: white;\n font-size: 10px;\n font-weight: 600;\n min-width: 18px;\n height: 18px;\n border-radius: 9px;\n display: none;\n align-items: center;\n justify-content: center;\n padding: 0 5px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n pointer-events: none;\n }\n\n .supaship-toolbar-badge.show {\n display: flex;\n }\n\n .supaship-toolbar-panel {\n position: absolute;\n bottom: 48px;\n right: 0;\n width: 300px;\n max-height: 600px;\n background: #1a1a1a;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);\n display: none;\n flex-direction: column;\n overflow: hidden;\n border: 1px solid #333;\n }\n\n .supaship-toolbar-bottom-left .supaship-toolbar-panel,\n .supaship-toolbar-top-left .supaship-toolbar-panel {\n right: auto;\n left: 0;\n }\n\n .supaship-toolbar-top-right .supaship-toolbar-panel,\n .supaship-toolbar-top-left .supaship-toolbar-panel {\n bottom: auto;\n top: 60px;\n }\n\n .supaship-toolbar-panel.open {\n display: flex;\n }\n\n .supaship-toolbar-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n border-bottom: 1px solid #333;\n background: #0f0f0f;\n min-width: 0;\n overflow: hidden;\n }\n\n .supaship-search-input {\n flex: 1;\n min-width: 0;\n background: transparent;\n border: none;\n color: #e5e5e5;\n padding: 0;\n font-size: 13px;\n outline: none;\n }\n\n .supaship-search-input::placeholder {\n color: #888;\n }\n\n .supaship-header-btn {\n background: transparent;\n border: none;\n color: #e5e5e5;\n width: 24px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.2s;\n padding: 0;\n }\n\n .supaship-header-btn:hover {\n color: #ef4444;\n }\n\n .supaship-toolbar-content {\n flex: 1;\n overflow-y: auto;\n padding: 8px;\n min-height: 300px;\n max-height: 300px;\n }\n\n .supaship-toolbar-empty {\n padding: 32px 16px;\n text-align: center;\n color: #888;\n }\n\n .supaship-feature-item {\n padding: 0 6px;\n }\n\n .supaship-feature-item.disabled {\n opacity: 0.5;\n pointer-events: none;\n }\n\n .supaship-feature-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n min-height: 32px;\n }\n\n .supaship-feature-name {\n font-weight: 500;\n color: #e5e5e5;\n font-size: 13px;\n flex: 1;\n min-width: 0;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n }\n\n .supaship-feature-override-indicator {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #ef4444;\n flex-shrink: 0;\n }\n\n .supaship-toolbar-overrides-label {\n font-size: 12px;\n color: #888;\n white-space: nowrap;\n flex-shrink: 0;\n transition: all 0.2s;\n }\n\n .supaship-toolbar-overrides-label-count.has-overrides {\n background: #ef4444;\n color: white;\n font-size: 10px;\n font-weight: 600;\n min-width: 18px;\n height: 18px;\n border-radius: 9px;\n padding: 2px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n .supaship-feature-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n min-height: 20px;\n }\n\n .supaship-feature-content {\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n .supaship-feature-input {\n flex: 1;\n padding: 6px 8px;\n background: #1a1a1a;\n border: 1px solid #555;\n color: #e5e5e5;\n border-radius: 4px;\n font-size: 13px;\n font-family: 'Monaco', 'Courier New', monospace;\n outline: none;\n resize: vertical;\n min-height: 60px;\n margin-bottom: 8px;\n }\n\n .supaship-feature-input:focus {\n border-color: #667eea;\n }\n\n .supaship-btn {\n padding: 4px 12px;\n border: none;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n }\n\n .supaship-btn-primary {\n background: #444;\n color: white;\n }\n\n .supaship-btn-primary:hover {\n background: #555;\n }\n\n .supaship-btn-secondary {\n background: #444;\n color: #e5e5e5;\n }\n\n .supaship-btn-secondary:hover {\n background: #555;\n }\n\n .supaship-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .supaship-btn:disabled:hover {\n background: #444;\n }\n\n .supaship-header-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n }\n\n .supaship-header-btn:disabled:hover {\n color: #e5e5e5;\n }\n\n .supaship-toggle {\n position: relative;\n display: inline-block;\n width: 32px;\n height: 18px;\n flex-shrink: 0;\n }\n\n .supaship-toggle input {\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .supaship-toggle-slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: #333;\n border: 1px solid #555;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 20px;\n }\n\n .supaship-toggle-slider:before {\n position: absolute;\n content: \"\";\n height: 14px;\n width: 14px;\n left: 2px;\n bottom: 1px;\n background-color: #666;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 50%;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n }\n\n .supaship-toggle input:checked + .supaship-toggle-slider {\n background-color: #fff;\n border-color: #fff;\n }\n\n .supaship-toggle input:checked + .supaship-toggle-slider:before {\n transform: translateX(13px);\n background-color: #000;\n }\n\n .supaship-toggle input:disabled + .supaship-toggle-slider {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .supaship-toggle:hover .supaship-toggle-slider:before {\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);\n }\n\n .supaship-btn-icon {\n background: transparent;\n border: none;\n color: #e5e5e5;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n cursor: pointer;\n padding: 0;\n transition: all 0.2s;\n flex-shrink: 0;\n }\n\n .supaship-btn-icon:hover {\n background: #444;\n color: #ef4444;\n }\n `\n document.head.appendChild(styles)\n }\n\n private attachEventListeners(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n const panelId = `supaship-toolbar-panel-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const closeId = `supaship-close-toolbar-${this.clientId}`\n const searchId = `supaship-search-input-${this.clientId}`\n const contentId = `supaship-toolbar-content-${this.clientId}`\n\n const toggle = document.getElementById(toggleId)\n const panel = document.getElementById(panelId)\n const clearAll = document.getElementById(clearId)\n const close = document.getElementById(closeId)\n const searchInput = document.getElementById(searchId) as HTMLInputElement\n const content = document.getElementById(contentId)\n\n toggle?.addEventListener('click', () => {\n panel?.classList.toggle('open')\n })\n\n close?.addEventListener('click', () => {\n panel?.classList.remove('open')\n })\n\n clearAll?.addEventListener('click', () => {\n this.clearAllOverrides()\n })\n\n searchInput?.addEventListener('input', e => {\n this.state.searchQuery = (e.target as HTMLInputElement).value.toLowerCase()\n this.updateToolbarUI()\n })\n\n // Use event delegation on content element - survives innerHTML updates\n if (content) {\n // Handle button clicks (remove and set actions)\n content.addEventListener('click', (e: Event) => {\n const target = e.target as HTMLElement\n const buttonElement = target.closest('button[data-action]') as HTMLButtonElement\n if (!buttonElement) return\n\n e.preventDefault()\n e.stopPropagation()\n\n const featureName = buttonElement.dataset.feature!\n const action = buttonElement.dataset.action\n\n if (action === 'remove') {\n this.removeOverride(featureName)\n } else if (action === 'set') {\n const textarea = content.querySelector(\n `textarea[data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLTextAreaElement\n if (textarea && textarea.value.trim()) {\n try {\n const value = JSON.parse(textarea.value)\n this.setOverride(featureName, value)\n } catch {\n // If not valid JSON, wrap string in object\n this.setOverride(featureName, { value: textarea.value })\n }\n }\n }\n })\n\n // Handle checkbox changes for boolean toggles\n content.addEventListener('change', (e: Event) => {\n const target = e.target as HTMLInputElement\n if (target.type === 'checkbox' && target.dataset.type === 'boolean') {\n const featureName = target.dataset.feature!\n const newValue = target.checked\n this.setOverride(featureName, newValue)\n }\n })\n\n // Handle textarea input to update button states\n content.addEventListener('input', (e: Event) => {\n const target = e.target as HTMLTextAreaElement\n if (target.tagName === 'TEXTAREA' && target.dataset.feature) {\n const featureName = target.dataset.feature!\n const originalValue = target.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = target.value !== originalValue\n const hasContent = target.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n }\n })\n\n // Handle textarea paste events\n content.addEventListener('paste', (e: Event) => {\n const target = e.target as HTMLTextAreaElement\n if (target.tagName === 'TEXTAREA' && target.dataset.feature) {\n setTimeout(() => {\n const featureName = target.dataset.feature!\n const originalValue = target.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${featureName}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = target.value !== originalValue\n const hasContent = target.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n }, 0)\n }\n })\n\n // Handle Ctrl/Cmd+Enter to set override\n content.addEventListener('keydown', (e: KeyboardEvent) => {\n const target = e.target as HTMLTextAreaElement\n if (\n target.tagName === 'TEXTAREA' &&\n target.dataset.feature &&\n (e.ctrlKey || e.metaKey) &&\n e.key === 'Enter'\n ) {\n e.preventDefault()\n const featureName = target.dataset.feature!\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn && !overrideBtn.disabled) {\n overrideBtn.click()\n }\n }\n })\n }\n }\n\n private updateToolbarUI(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const contentId = `supaship-toolbar-content-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const badgeId = `supaship-toolbar-badge-${this.clientId}`\n const headerOverrideCountId = `supaship-header-override-count-${this.clientId}`\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n\n const content = document.getElementById(contentId)\n const clearAllBtn = document.getElementById(clearId) as HTMLButtonElement\n const badge = document.getElementById(badgeId)\n const headerOverrideCount = document.getElementById(headerOverrideCountId)\n const toggleBtn = document.getElementById(toggleId) as HTMLButtonElement\n\n if (!content) {\n console.warn('[Toolbar] Content element not found:', contentId)\n return\n }\n\n // Update clear all button state and badge\n const overrideCount = Object.keys(this.state.overrides).length\n const hasOverrides = overrideCount > 0\n if (clearAllBtn) {\n clearAllBtn.disabled = !hasOverrides\n }\n\n // Update badge on toggle button\n if (badge) {\n if (hasOverrides) {\n badge.textContent = overrideCount > 99 ? '99+' : String(overrideCount)\n badge.classList.add('show')\n } else {\n badge.classList.remove('show')\n }\n }\n\n // Update tooltip on toggle button\n if (toggleBtn) {\n if (hasOverrides) {\n toggleBtn.setAttribute(\n 'title',\n `Supaship Toolbar, ${overrideCount} override${overrideCount === 1 ? '' : 's'}`\n )\n toggleBtn.setAttribute(\n 'aria-label',\n `Supaship Toolbar, ${overrideCount} override${overrideCount === 1 ? '' : 's'}`\n )\n } else {\n toggleBtn.setAttribute('title', 'Supaship Toolbar')\n toggleBtn.setAttribute('aria-label', 'Supaship Toolbar')\n }\n }\n\n // Update override count in header\n if (headerOverrideCount) {\n // Escape overrideCount to prevent any potential XSS (defense in depth)\n const escapedCount = this.escapeHtml(String(overrideCount))\n headerOverrideCount.innerHTML = `<span class=\"supaship-toolbar-overrides-label-count ${hasOverrides ? 'has-overrides' : ''}\">${escapedCount}</span> override${overrideCount === 1 ? '' : 's'}`\n }\n\n const features = Array.from(this.state.features).sort()\n\n // Filter features based on search query\n const filteredFeatures = features.filter(name =>\n name.toLowerCase().includes(this.state.searchQuery)\n )\n\n if (filteredFeatures.length === 0) {\n content.innerHTML = this.state.searchQuery\n ? '<div class=\"supaship-toolbar-empty\">No matching features found</div>'\n : `<div class=\"supaship-toolbar-empty\">${NO_FEATURES_MESSAGE}</div>`\n return\n }\n\n const htmlContent = filteredFeatures\n .map(featureName => {\n const hasOverride = featureName in this.state.overrides\n const currentValue = this.state.featureValues[featureName]\n const overrideValue = hasOverride ? this.state.overrides[featureName] : currentValue\n const isDisabled = !this.state.useLocalOverrides\n const itemClass = `supaship-feature-item ${isDisabled ? 'disabled' : ''}`\n\n // Check if the feature is boolean\n const isBoolean =\n typeof currentValue === 'boolean' || (hasOverride && typeof overrideValue === 'boolean')\n\n if (isBoolean) {\n // Render toggle switch for boolean values (single row layout)\n const isChecked = hasOverride ? overrideValue === true : currentValue === true\n return `\n <div class=\"${itemClass}\">\n <div class=\"supaship-feature-row\">\n <span class=\"supaship-feature-name\">\n ${this.escapeHtml(featureName)}\n ${hasOverride ? '<span class=\"supaship-feature-override-indicator\" title=\"Serving local override\"></span>' : ''}\n </span>\n <div class=\"supaship-feature-actions\">\n ${\n hasOverride\n ? `\n <button\n class=\"supaship-btn-icon\"\n data-feature=\"${this.escapeHtml(featureName)}\"\n data-action=\"remove\"\n title=\"Reset to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/>\n <path d=\"M3 3v5h5\"/>\n <path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/>\n <path d=\"M16 16h5v5\"/>\n </svg>\n </button>\n `\n : ''\n }\n <label class=\"supaship-toggle\">\n <input\n type=\"checkbox\"\n ${isChecked ? 'checked' : ''}\n data-feature=\"${this.escapeHtml(featureName)}\"\n data-type=\"boolean\"\n />\n <span class=\"supaship-toggle-slider\"></span>\n </label>\n </div>\n </div>\n </div>\n `\n } else {\n // Render textarea for non-boolean values\n const currentDisplayValue = hasOverride\n ? JSON.stringify(overrideValue)\n : currentValue !== undefined\n ? JSON.stringify(currentValue)\n : ''\n const escapedFeatureName = this.escapeHtml(featureName)\n const escapedCurrentDisplayValue = this.escapeHtml(currentDisplayValue)\n const escapedTextareaContent = hasOverride\n ? this.escapeHtml(JSON.stringify(overrideValue))\n : escapedCurrentDisplayValue\n\n return `\n <div class=\"${itemClass}\">\n <div class=\"supaship-feature-row\">\n <span class=\"supaship-feature-name\">\n ${escapedFeatureName}\n ${hasOverride ? '<span class=\"supaship-feature-override-indicator\" title=\"Serving local override\"></span>' : ''}\n </span>\n <div class=\"supaship-feature-actions\">\n ${\n hasOverride\n ? `\n <button\n class=\"supaship-btn-icon\"\n data-feature=\"${escapedFeatureName}\"\n data-action=\"remove\"\n title=\"Reset to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/>\n <path d=\"M3 3v5h5\"/>\n <path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/>\n <path d=\"M16 16h5v5\"/>\n </svg>\n </button>\n `\n : ''\n }\n <button\n class=\"supaship-btn supaship-btn-primary\"\n data-feature=\"${escapedFeatureName}\"\n data-action=\"set\"\n disabled>\n Override\n </button>\n </div>\n </div>\n <div class=\"supaship-feature-content\">\n <textarea\n class=\"supaship-feature-input\"\n placeholder=\"Override JSON value\"\n data-feature=\"${escapedFeatureName}\"\n data-original=\"${escapedCurrentDisplayValue}\"\n >${escapedTextareaContent}</textarea>\n </div>\n </div>\n `\n }\n })\n .join('')\n\n requestAnimationFrame(() => {\n // Set innerHTML - event listeners are handled via delegation in attachEventListeners()\n content.innerHTML = htmlContent\n\n // Update button states for textareas that already have values\n content.querySelectorAll('textarea[data-feature]').forEach(textarea => {\n const textareaElement = textarea as HTMLTextAreaElement\n const featureName = textareaElement.dataset.feature!\n const originalValue = textareaElement.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = textareaElement.value !== originalValue\n const hasContent = textareaElement.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n })\n })\n }\n\n private escapeHtml(text: string): string {\n const div = typeof document !== 'undefined' ? document.createElement('div') : null\n if (div) {\n div.textContent = text\n return div.innerHTML\n }\n return text.replace(/[&<>\"']/g, char => {\n const escapeMap: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n }\n return escapeMap[char]\n })\n }\n\n /**\n * Escapes special characters in CSS attribute selectors to prevent CSS injection\n * @param value The value to escape for use in CSS attribute selectors\n */\n private escapeCssSelector(value: string): string {\n // Escape special CSS selector characters: \", ', ], \\\n return value.replace(/[\"'\\\\\\]]/g, '\\\\$&')\n }\n}\n","export const DEFAULT_FEATURES_URL = 'https://edge.supaship.com/v1/features'\nexport const DEFAULT_EVENTS_URL = 'https://edge.supaship.com/v1/events'\n","import {\n SupaClientConfig,\n FeatureContext,\n FeatureValue,\n NetworkConfig,\n Features,\n FeaturesWithFallbacks,\n} from './types'\nimport { retry } from './utils'\nimport { SupaPlugin } from './plugins/types'\nimport { SupaToolbarPlugin } from './plugins/toolbar-plugin'\nimport { DEFAULT_FEATURES_URL, DEFAULT_EVENTS_URL } from './constants'\n\ntype RequiredRetryConfig = Required<NonNullable<NetworkConfig['retry']>>\ntype ResolvedNetworkConfig = {\n featuresAPIUrl: string\n eventsAPIUrl: string\n retry: RequiredRetryConfig\n requestTimeoutMs: number\n}\n\nexport class SupaClient<TFeatures extends FeaturesWithFallbacks> {\n private apiKey: string\n private environment: string\n private defaultContext?: FeatureContext\n private plugins: SupaPlugin[]\n private featureDefinitions: Features<TFeatures>\n private clientId: string\n\n private fetchImpl: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>\n private networkConfig: ResolvedNetworkConfig\n\n constructor(config: SupaClientConfig & { features: TFeatures }) {\n this.apiKey = config.apiKey\n this.environment = config.environment\n this.defaultContext = config.context\n this.featureDefinitions = config.features as Features<TFeatures>\n\n // Generate unique client ID\n this.clientId = this.generateClientId()\n\n this.networkConfig = {\n featuresAPIUrl: config.networkConfig?.featuresAPIUrl || DEFAULT_FEATURES_URL,\n eventsAPIUrl: config.networkConfig?.eventsAPIUrl || DEFAULT_EVENTS_URL,\n retry: {\n enabled: config.networkConfig?.retry?.enabled ?? true,\n maxAttempts: config.networkConfig?.retry?.maxAttempts ?? 3,\n backoff: config.networkConfig?.retry?.backoff ?? 1000,\n },\n requestTimeoutMs: config.networkConfig?.requestTimeoutMs ?? 10000,\n }\n\n // Prefer injected fetch, then global fetch if available\n const globalFetch: typeof fetch | undefined =\n typeof globalThis !== 'undefined'\n ? (globalThis as unknown as { fetch?: typeof fetch }).fetch\n : undefined\n if (config.networkConfig?.fetchFn) {\n this.fetchImpl = config.networkConfig.fetchFn\n } else if (typeof globalFetch === 'function') {\n this.fetchImpl = globalFetch.bind(globalThis)\n } else {\n throw new Error(\n 'No fetch implementation available. Provide fetchFn in config or use a runtime with global fetch (e.g., Node 18+, browsers).'\n )\n }\n\n // Initialize plugins with automatic toolbar plugin in browser\n this.plugins = this.initializePlugins(config)\n\n // Initialize plugins with available features and their fallback values\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onInit?.({\n clientId: this.clientId,\n availableFeatures: this.featureDefinitions,\n context: this.defaultContext,\n })\n )\n ).catch(console.error)\n }\n\n /**\n * Generate a unique client ID\n */\n private generateClientId(): string {\n return `supaship-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`\n }\n\n /**\n * Initialize plugins with automatic toolbar plugin in browser environments\n */\n private initializePlugins(config: SupaClientConfig & { features: TFeatures }): SupaPlugin[] {\n const plugins = config.plugins || []\n\n // Check if we're in a browser environment\n const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined'\n\n // If toolbar is explicitly disabled, don't add it\n if (config.toolbar === false) {\n return plugins\n }\n\n // If in browser and toolbar not disabled, add it automatically\n if (isBrowser) {\n // Check if user already added toolbar plugin manually\n const hasToolbarPlugin = plugins.some(p => p.name === 'toolbar-plugin')\n\n if (!hasToolbarPlugin) {\n // Add toolbar with user config or defaults\n const toolbarConfig = config.toolbar || { enabled: 'auto' }\n const toolbarPlugin = new SupaToolbarPlugin(toolbarConfig)\n return [toolbarPlugin, ...plugins]\n }\n }\n\n return plugins\n }\n\n /**\n * Updates the default context for the client\n * @param context - New context to merge with or replace the existing context\n * @param mergeWithExisting - Whether to merge with existing context (default: true)\n */\n updateContext(context: FeatureContext, mergeWithExisting: boolean = true): void {\n const oldContext = this.defaultContext\n\n if (mergeWithExisting && this.defaultContext) {\n this.defaultContext = { ...this.defaultContext, ...context }\n } else {\n this.defaultContext = context\n }\n\n // Notify plugins of context change\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onContextUpdate?.(oldContext, this.defaultContext!, 'updateContext')\n )\n ).catch(console.error)\n }\n\n /**\n * Gets the current default context\n */\n getContext(): FeatureContext | undefined {\n return this.defaultContext\n }\n\n /**\n * Gets the fallback value for a feature from its definition\n */\n getFeatureFallback<TKey extends keyof TFeatures>(featureName: TKey): Features<TFeatures>[TKey] {\n return this.featureDefinitions[featureName]\n }\n\n private getVariationValue(variation: FeatureValue, fallback: FeatureValue): FeatureValue {\n if (variation !== undefined && variation !== null) {\n return variation\n }\n\n return fallback ?? null\n }\n\n async getFeature<TKey extends keyof TFeatures>(\n featureName: TKey,\n options?: { context?: FeatureContext }\n ): Promise<Features<TFeatures>[TKey]> {\n const { context } = options ?? {}\n\n // Only merge context if it's defined and not null\n const mergedContext: FeatureContext | undefined =\n typeof context === 'object' && context !== null\n ? { ...(this.defaultContext ?? {}), ...context }\n : this.defaultContext\n\n try {\n const response = await this.getFeatures([featureName as string], {\n context: mergedContext,\n })\n\n // Get the specific feature value\n const value = response[featureName as string]\n return value as Features<TFeatures>[TKey]\n } catch (error) {\n // Run onError hooks\n await Promise.all(this.plugins.map(plugin => plugin.onError?.(error as Error, mergedContext)))\n\n // Use fallback feature value when API fails\n const fallbackValue = this.featureDefinitions[featureName]\n\n // Notify plugins that fallback was used\n await Promise.all(\n this.plugins.map(plugin =>\n plugin.onFallbackUsed?.(\n featureName as string,\n fallbackValue as FeatureValue,\n error as Error\n )\n )\n )\n return fallbackValue as Features<TFeatures>[TKey]\n }\n }\n\n async getFeatures<TKeys extends readonly (keyof TFeatures)[]>(\n featureNames: TKeys,\n options?: { context?: FeatureContext }\n ): Promise<{ [K in TKeys[number]]: Features<TFeatures>[K] }> {\n const { context: contextOverride } = options ?? {}\n\n // Only merge context if it's defined and not null\n const mergedContext: FeatureContext | undefined =\n typeof contextOverride === 'object' && contextOverride !== null\n ? { ...(this.defaultContext ?? {}), ...contextOverride }\n : this.defaultContext\n\n // Notify plugins of context update for this request\n if (contextOverride) {\n await Promise.all(\n this.plugins.map(plugin =>\n plugin.onContextUpdate?.(this.defaultContext, mergedContext!, 'request')\n )\n )\n }\n\n // Convert feature names to strings for API call\n const featureNamesArray = featureNames.map(name => name as string)\n\n try {\n // Run beforeGetFeatures hooks\n await Promise.all(\n this.plugins.map(plugin => plugin.beforeGetFeatures?.(featureNamesArray, mergedContext))\n )\n\n type FeaturesResponse = { features: Record<string, { variation: FeatureValue }> }\n const fetchFeatures = async (): Promise<Record<string, FeatureValue>> => {\n const url = this.networkConfig.featuresAPIUrl\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n }\n const body = JSON.stringify({\n environment: this.environment,\n features: featureNamesArray,\n context: mergedContext,\n })\n\n // Notify plugins before request\n await Promise.all(this.plugins.map(plugin => plugin.beforeRequest?.(url, body, headers)))\n\n const startTime = Date.now()\n // Support timeout via AbortController when available\n const AbortCtrl: typeof AbortController | undefined =\n typeof globalThis !== 'undefined'\n ? (globalThis as unknown as { AbortController?: typeof AbortController })\n .AbortController\n : undefined\n let controller: AbortController | undefined\n let timeoutId: ReturnType<typeof setTimeout> | undefined\n if (this.networkConfig.requestTimeoutMs && typeof AbortCtrl === 'function') {\n controller = new AbortCtrl()\n timeoutId = setTimeout(() => controller?.abort(), this.networkConfig.requestTimeoutMs)\n }\n let response: Response\n try {\n response = await this.fetchImpl(url, {\n method: 'POST',\n headers,\n body,\n signal: controller?.signal,\n })\n } finally {\n if (timeoutId) clearTimeout(timeoutId)\n }\n const duration = Date.now() - startTime\n\n // Notify plugins after response\n await Promise.all(\n this.plugins.map(plugin => plugin.afterResponse?.(response, { duration }))\n )\n\n if (!response.ok) {\n throw new Error(`Failed to fetch features: ${response.statusText}`)\n }\n\n const data = (await response.json()) as FeaturesResponse\n const result: Record<string, FeatureValue> = {}\n\n featureNamesArray.forEach(name => {\n const variation = data.features[name]?.variation\n result[name] = this.getVariationValue(\n variation,\n this.featureDefinitions[name as keyof TFeatures]\n )\n })\n\n return result\n }\n\n const result = this.networkConfig.retry.enabled\n ? await retry(\n fetchFeatures,\n this.networkConfig.retry.maxAttempts,\n this.networkConfig.retry.backoff,\n (attempt, error, willRetry) => {\n // Notify plugins of retry attempts\n Promise.all(\n this.plugins.map(plugin => plugin.onRetryAttempt?.(attempt, error, willRetry))\n ).catch(console.error)\n }\n )\n : await fetchFeatures()\n\n // Run afterGetFeatures hooks\n await Promise.all(\n this.plugins.map(plugin => plugin.afterGetFeatures?.(result, mergedContext))\n )\n\n // Return the fetched features\n return result as { [K in TKeys[number]]: Features<TFeatures>[K] }\n } catch (error) {\n // Run onError hooks\n await Promise.all(this.plugins.map(plugin => plugin.onError?.(error as Error, mergedContext)))\n\n // Create fallback result with requested feature names\n const fallbackResult: Record<string, FeatureValue> = {}\n\n featureNamesArray.forEach(featureName => {\n fallbackResult[featureName] = this.featureDefinitions[featureName as keyof TFeatures]\n\n // Notify plugins that fallback was used for each feature\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onFallbackUsed?.(\n featureName,\n this.featureDefinitions[featureName as keyof TFeatures],\n error as Error\n )\n )\n ).catch(console.error)\n })\n\n return fallbackResult as { [K in TKeys[number]]: Features<TFeatures>[K] }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,SAAS,MAAM,IAA2B;AAC/C,SAAO,IAAI,QAAQ,aAAW,WAAW,SAAS,EAAE,CAAC;AACvD;AAEA,eAAsB,MACpB,IACA,cAAsB,GACtB,UAAkB,KAClB,SACY;AACZ,MAAI;AAEJ,WAAS,UAAU,GAAG,WAAW,aAAa,WAAW;AACvD,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,SAAS,OAAO;AACd,kBAAY;AACZ,YAAM,YAAY,UAAU;AAE5B,UAAI,SAAS;AACX,gBAAQ,SAAS,WAAW,SAAS;AAAA,MACvC;AAEA,UAAI,CAAC,UAAW;AAChB,YAAM,MAAM,UAAU,KAAK,IAAI,GAAG,UAAU,CAAC,CAAC;AAAA,IAChD;AAAA,EACF;AAEA,QAAM;AACR;;;ACIA,IAAM,sBAAsB;AAE5B,IAAM,sBAAsB;AAMrB,IAAM,oBAAN,MAA8C;AAAA,EAWnD,YAAY,SAAkC,CAAC,GAAG;AAVlD,gBAAO;AAQP,SAAQ,aAAqB;AAG3B,SAAK,SAAS;AAAA,MACZ,SAAS,OAAO,WAAW;AAAA,MAC3B,UAAU;AAAA,QACR,WAAW,OAAO,UAAU,aAAa;AAAA,QACzC,QAAQ,OAAO,UAAU,UAAU,EAAE,GAAG,QAAQ,GAAG,OAAO;AAAA,MAC5D;AAAA,MACA,kBAAkB,OAAO;AAAA,IAC3B;AAEA,SAAK,QAAQ;AAAA,MACX,WAAW,CAAC;AAAA,MACZ,UAAU,oBAAI,IAAI;AAAA,MAClB,eAAe,CAAC;AAAA,MAChB,aAAa;AAAA,MACb,mBAAmB;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,UAAgB;AACd,SAAK,cAAc;AAAA,EACrB;AAAA,EAEQ,oBAA6B;AACnC,QAAI,KAAK,OAAO,YAAY,KAAM,QAAO;AACzC,QAAI,KAAK,OAAO,YAAY,MAAO,QAAO;AAG1C,QAAI,OAAO,WAAW,aAAa;AACjC,aACE,OAAO,SAAS,aAAa,eAC7B,OAAO,SAAS,aAAa,eAC7B,OAAO,SAAS,aAAa,MAC7B,OAAO,SAAS,SAAS,SAAS,QAAQ,KAC1C,OAAO,SAAS,SAAS,SAAS,YAAY;AAAA,IAElD;AACA,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,QAIE;AACP,UAAM,EAAE,mBAAmB,SAAS,SAAS,IAAI;AAGjD,SAAK,WAAW;AAGhB,SAAK,aAAa;AAGlB,SAAK,MAAM,YAAY,KAAK,cAAc;AAG1C,SAAK,MAAM,WAAW,IAAI,IAAI,OAAO,KAAK,iBAAiB,CAAC;AAC5D,SAAK,MAAM,gBAAgB,EAAE,GAAG,kBAAkB;AAClD,SAAK,MAAM,UAAU;AAGrB,QAAI,KAAK,kBAAkB,GAAG;AAC5B,WAAK,cAAc;AAAA,IACrB;AAGA,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,MAAM,kBAAkB,eAAyB,SAAyC;AAExF,SAAK,MAAM,UAAU;AAGrB,SAAK,MAAM,YAAY,KAAK,cAAc;AAG1C,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,MAAM,iBACJ,SACA,SACe;AAEf,WAAO,KAAK,OAAO,EAAE,QAAQ,UAAQ;AACnC,WAAK,MAAM,cAAc,IAAI,IAAI,QAAQ,IAAI;AAAA,IAC/C,CAAC;AAGD,QAAI,KAAK,MAAM,mBAAmB;AAChC,aAAO,KAAK,KAAK,MAAM,SAAS,EAAE,QAAQ,iBAAe;AACvD,YAAI,eAAe,SAAS;AAC1B,kBAAQ,WAAW,IAAI,KAAK,MAAM,UAAU,WAAW;AAAA,QACzD;AAAA,MACF,CAAC;AAAA,IACH;AAGA,WAAO,KAAK,OAAO,EAAE,QAAQ,UAAQ,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC;AAClE,SAAK,MAAM,UAAU;AACrB,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEQ,gBAA8C;AACpD,QAAI,OAAO,WAAW,eAAe,CAAC,OAAO,cAAc;AACzD,aAAO,CAAC;AAAA,IACV;AAEA,QAAI;AACF,YAAM,SAAS,OAAO,aAAa,QAAQ,KAAK,UAAU;AAC1D,aAAO,SAAS,KAAK,MAAM,MAAM,IAAI,CAAC;AAAA,IACxC,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EAEQ,cACN,SACA,OACA,cACM;AACN,QAAI,OAAO,WAAW,eAAe,CAAC,OAAO,cAAc;AACzD;AAAA,IACF;AAEA,QAAI;AACF,aAAO,aAAa,QAAQ,KAAK,YAAY,KAAK,UAAU,YAAY,CAAC;AACzE,WAAK,OAAO;AAAA,QACV,EAAE,SAAS,WAAW,IAAI,OAAO,SAAS,KAAK;AAAA,QAC/C,gBAAgB,CAAC;AAAA,MACnB;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,+CAA+C,KAAK;AAAA,IACpE;AAAA,EACF;AAAA,EAEO,YAAY,aAAqB,OAA2B;AACjE,SAAK,MAAM,UAAU,WAAW,IAAI;AACpC,SAAK,cAAc,aAAa,OAAO,KAAK,MAAM,SAAS;AAC3D,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEO,eAAe,aAA2B;AAC/C,WAAO,KAAK,MAAM,UAAU,WAAW;AACvC,SAAK,cAAc,aAAa,MAAM,KAAK,MAAM,SAAS;AAC1D,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEO,oBAA0B;AAC/B,SAAK,MAAM,YAAY,CAAC;AACxB,SAAK,cAAc,IAAI,MAAM,KAAK,MAAM,SAAS;AACjD,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEO,eAA6C;AAClD,WAAO,EAAE,GAAG,KAAK,MAAM,UAAU;AAAA,EACnC;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,OAAO,WAAW,eAAe,OAAO,aAAa,aAAa;AACpE;AAAA,IACF;AAGA,UAAM,YAAY,oBAAoB,KAAK,QAAQ;AACnD,QAAI,SAAS,eAAe,SAAS,GAAG;AACtC;AAAA,IACF;AAGA,UAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,YAAQ,KAAK;AACb,YAAQ,aAAa,wBAAwB,KAAK,YAAY,EAAE;AAChE,YAAQ,YAAY,KAAK,eAAe;AAGxC,SAAK,aAAa;AAGlB,aAAS,KAAK,YAAY,OAAO;AAGjC,SAAK,qBAAqB;AAAA,EAC5B;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,OAAO,aAAa,aAAa;AACnC;AAAA,IACF;AAEA,UAAM,UAAU,SAAS,eAAe,oBAAoB,KAAK,QAAQ,EAAE;AAC3E,QAAI,SAAS;AACX,cAAQ,OAAO;AAAA,IACjB;AAEA,UAAM,SAAS,SAAS,eAAe,yBAAyB;AAChE,QAAI,QAAQ;AACV,aAAO,OAAO;AAAA,IAChB;AAAA,EACF;AAAA,EAEQ,iBAAyB;AAC/B,UAAM,EAAE,WAAW,OAAO,IAAI,KAAK,OAAO;AAC1C,UAAM,gBAAgB,oBAAoB,SAAS;AACnD,UAAM,UAAU,QAAQ,KAAK;AAC7B,UAAM,UAAU,QAAQ,KAAK;AAC7B,UAAM,WAAW,2BAA2B,KAAK,QAAQ;AACzD,UAAM,UAAU,0BAA0B,KAAK,QAAQ;AACvD,UAAM,WAAW,yBAAyB,KAAK,QAAQ;AACvD,UAAM,UAAU,sBAAsB,KAAK,QAAQ;AACnD,UAAM,UAAU,0BAA0B,KAAK,QAAQ;AACvD,UAAM,YAAY,4BAA4B,KAAK,QAAQ;AAC3D,UAAM,UAAU,0BAA0B,KAAK,QAAQ;AACvD,UAAM,wBAAwB,kCAAkC,KAAK,QAAQ;AAE7E,WAAO;AAAA,+CACoC,aAAa,wBAAwB,OAAO,iBAAiB,OAAO;AAAA,sDAC7D,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qDAsCT,OAAO;AAAA;AAAA,kDAEV,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,oBAKrC,QAAQ;AAAA;AAAA;AAAA,iEAGqC,qBAAqB;AAAA;AAAA;AAAA,oBAGlE,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAaP,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sDAU2B,SAAS;AAAA,kDACb,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKnE;AAAA,EAEQ,eAAqB;AAC3B,QAAI,OAAO,aAAa,aAAa;AACnC;AAAA,IACF;AAEA,QAAI,SAAS,eAAe,yBAAyB,GAAG;AACtD;AAAA,IACF;AAEA,UAAM,SAAS,SAAS,cAAc,OAAO;AAC7C,WAAO,KAAK;AACZ,WAAO,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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","names":["result"]}
package/dist/index.mjs CHANGED
@@ -297,7 +297,6 @@ var SupaToolbarPlugin = class {
297
297
  width: 36px;
298
298
  height: 36px;
299
299
  border-radius: 100%;
300
- background: #000;
301
300
  border: none;
302
301
  color: white;
303
302
  cursor: pointer;
@@ -876,8 +875,11 @@ var SupaToolbarPlugin = class {
876
875
  data-action="remove"
877
876
  title="Reset to default"
878
877
  >
879
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="14" height="14">
880
- <path d="M240,56v48a8,8,0,0,1-8,8H184a8,8,0,0,1,0-16H211.4L184.81,71.64l-.25-.24a80,80,0,1,0-1.67,114.78,8,8,0,0,1,11,11.63A95.44,95.44,0,0,1,128,224h-1.32A96,96,0,1,1,195.75,60L224,85.8V56a8,8,0,1,1,16,0Z" fill="currentColor"/>
878
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
879
+ <path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
880
+ <path d="M3 3v5h5"/>
881
+ <path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/>
882
+ <path d="M16 16h5v5"/>
881
883
  </svg>
882
884
  </button>
883
885
  ` : ""}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils.ts","../src/plugins/toolbar-plugin.ts","../src/constants.ts","../src/client.ts"],"sourcesContent":["export function sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nexport async function retry<T>(\n fn: () => Promise<T>,\n maxAttempts: number = 3,\n backoff: number = 1000,\n onRetry?: (attempt: number, error: Error, willRetry: boolean) => void\n): Promise<T> {\n let lastError: Error\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn()\n } catch (error) {\n lastError = error as Error\n const willRetry = attempt < maxAttempts\n\n if (onRetry) {\n onRetry(attempt, lastError, willRetry)\n }\n\n if (!willRetry) break\n await sleep(backoff * Math.pow(2, attempt - 1))\n }\n }\n\n throw lastError!\n}\n","import { SupaPlugin, SupaPluginConfig } from './types'\nimport { FeatureContext, FeatureValue } from '../types'\n\nexport interface SupaToolbarPosition {\n placement?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'\n offset?: { x: string; y: string }\n}\n\nexport type SupaToolbarOverrideChange = {\n feature: string\n value: FeatureValue\n}\n\nexport type SupaToolbarOverrideChangeCallback = (\n featureOverride: SupaToolbarOverrideChange,\n allOverrides: Record<string, FeatureValue>\n) => void\n\nexport interface SupaToolbarPluginConfig extends Omit<SupaPluginConfig, 'enabled'> {\n enabled?: boolean | 'auto' // auto means show only on localhost\n position?: SupaToolbarPosition\n onOverrideChange?: SupaToolbarOverrideChangeCallback\n}\n\ninterface SupaToolbarState {\n overrides: Record<string, FeatureValue>\n features: Set<string>\n featureValues: Record<string, FeatureValue>\n context?: FeatureContext\n searchQuery: string\n useLocalOverrides: boolean\n}\n\nconst DEFAULT_STORAGE_KEY = 'supaship-feature-overrides'\n\nconst NO_FEATURES_MESSAGE = `No feature flags configured in the client.`\n\n/**\n * Toolbar plugin for local feature flag testing\n * Provides a visual interface to override feature flags during development\n */\nexport class SupaToolbarPlugin implements SupaPlugin {\n name = 'toolbar-plugin'\n private config: {\n enabled: boolean | 'auto'\n position: Required<SupaToolbarPosition>\n onOverrideChange?: SupaToolbarOverrideChangeCallback\n }\n private state: SupaToolbarState\n private clientId?: string\n private storageKey: string = DEFAULT_STORAGE_KEY\n\n constructor(config: SupaToolbarPluginConfig = {}) {\n this.config = {\n enabled: config.enabled ?? 'auto',\n position: {\n placement: config.position?.placement ?? 'bottom-right',\n offset: config.position?.offset ?? { x: '1rem', y: '1rem' },\n },\n onOverrideChange: config.onOverrideChange,\n }\n\n this.state = {\n overrides: {},\n features: new Set(),\n featureValues: {},\n searchQuery: '',\n useLocalOverrides: true,\n }\n }\n\n cleanup(): void {\n this.removeToolbar()\n }\n\n private shouldShowToolbar(): boolean {\n if (this.config.enabled === true) return true\n if (this.config.enabled === false) return false\n\n // Auto mode: show only on localhost\n if (typeof window !== 'undefined') {\n return (\n window.location.hostname === 'localhost' ||\n window.location.hostname === '127.0.0.1' ||\n window.location.hostname === '' ||\n window.location.hostname.endsWith('.local') ||\n window.location.hostname.endsWith('.localhost')\n )\n }\n return false\n }\n\n onInit(params: {\n availableFeatures: Record<string, FeatureValue>\n context?: FeatureContext\n clientId: string\n }): void {\n const { availableFeatures, context, clientId } = params\n\n // Set client ID for DOM element IDs\n this.clientId = clientId\n\n // Use shared storage key (not client-specific) to persist across refreshes\n this.storageKey = DEFAULT_STORAGE_KEY\n\n // Load overrides from shared storage\n this.state.overrides = this.loadOverrides()\n\n // Initialize with all available features and their fallback values from config\n this.state.features = new Set(Object.keys(availableFeatures))\n this.state.featureValues = { ...availableFeatures }\n this.state.context = context\n\n // Inject toolbar if conditions are met\n if (this.shouldShowToolbar()) {\n this.injectToolbar()\n }\n\n // Update toolbar UI if it exists\n this.updateToolbarUI()\n }\n\n async beforeGetFeatures(_featureNames: string[], context?: FeatureContext): Promise<void> {\n // Update context if it changed\n this.state.context = context\n\n // Load overrides from shared storage\n this.state.overrides = this.loadOverrides()\n\n // Update toolbar UI if it exists\n this.updateToolbarUI()\n }\n\n async afterGetFeatures(\n results: Record<string, FeatureValue>,\n context?: FeatureContext\n ): Promise<void> {\n // Update feature values with fetched results (this replaces config fallback values)\n Object.keys(results).forEach(name => {\n this.state.featureValues[name] = results[name]\n })\n\n // Apply overrides to results only if local overrides are enabled\n if (this.state.useLocalOverrides) {\n Object.keys(this.state.overrides).forEach(featureName => {\n if (featureName in results) {\n results[featureName] = this.state.overrides[featureName]\n }\n })\n }\n\n // Track features and update UI\n Object.keys(results).forEach(name => this.state.features.add(name))\n this.state.context = context\n this.updateToolbarUI()\n }\n\n private loadOverrides(): Record<string, FeatureValue> {\n if (typeof window === 'undefined' || !window.localStorage) {\n return {}\n }\n\n try {\n const stored = window.localStorage.getItem(this.storageKey)\n return stored ? JSON.parse(stored) : {}\n } catch {\n return {}\n }\n }\n\n private saveOverrides(\n feature?: string,\n value?: FeatureValue,\n allOverrides?: Record<string, FeatureValue>\n ): void {\n if (typeof window === 'undefined' || !window.localStorage) {\n return\n }\n\n try {\n window.localStorage.setItem(this.storageKey, JSON.stringify(allOverrides))\n this.config.onOverrideChange?.(\n { feature: feature ?? '', value: value ?? null },\n allOverrides ?? {}\n )\n } catch (error) {\n console.error('Supaship: Failed to save feature overrides:', error)\n }\n }\n\n public setOverride(featureName: string, value: FeatureValue): void {\n this.state.overrides[featureName] = value\n this.saveOverrides(featureName, value, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public removeOverride(featureName: string): void {\n delete this.state.overrides[featureName]\n this.saveOverrides(featureName, null, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public clearAllOverrides(): void {\n this.state.overrides = {}\n this.saveOverrides('', null, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public getOverrides(): Record<string, FeatureValue> {\n return { ...this.state.overrides }\n }\n\n private injectToolbar(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return\n }\n\n // Check if toolbar with this client ID already exists\n const toolbarId = `supaship-toolbar-${this.clientId}`\n if (document.getElementById(toolbarId)) {\n return\n }\n\n // Create toolbar container\n const toolbar = document.createElement('div')\n toolbar.id = toolbarId\n toolbar.setAttribute('data-supaship-client', this.clientId || '')\n toolbar.innerHTML = this.getToolbarHTML()\n\n // Add styles\n this.injectStyles()\n\n // Add to DOM\n document.body.appendChild(toolbar)\n\n // Add event listeners\n this.attachEventListeners()\n }\n\n private removeToolbar(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const toolbar = document.getElementById(`supaship-toolbar-${this.clientId}`)\n if (toolbar) {\n toolbar.remove()\n }\n\n const styles = document.getElementById('supaship-toolbar-styles')\n if (styles) {\n styles.remove()\n }\n }\n\n private getToolbarHTML(): string {\n const { placement, offset } = this.config.position\n const positionClass = `supaship-toolbar-${placement}`\n const offsetX = offset?.x ?? '1rem'\n const offsetY = offset?.y ?? '1rem'\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n const panelId = `supaship-toolbar-panel-${this.clientId}`\n const searchId = `supaship-search-input-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const closeId = `supaship-close-toolbar-${this.clientId}`\n const contentId = `supaship-toolbar-content-${this.clientId}`\n const badgeId = `supaship-toolbar-badge-${this.clientId}`\n const headerOverrideCountId = `supaship-header-override-count-${this.clientId}`\n\n return `\n <div class=\"supaship-toolbar-container ${positionClass}\" style=\"--offset-x: ${offsetX}; --offset-y: ${offsetY};\">\n <button class=\"supaship-toolbar-toggle\" id=\"${toggleId}\" aria-label=\"Supaship Toolbar\" title=\"Supaship Toolbar\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 256 256\"\n width=\"32\"\n style=\"vertical-align: middle;\">\n <rect width='256' height='256' rx='128' fill='#e4f222'></rect>\n <line\n x1='66'\n y1='118'\n x2='118'\n y2='66'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n <line\n x1='186'\n y1='70'\n x2='70'\n y2='186'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n <line\n x1='190'\n y1='138'\n x2='138'\n y2='190'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n </svg>\n <span class=\"supaship-toolbar-badge\" id=\"${badgeId}\"></span>\n </button>\n <div class=\"supaship-toolbar-panel\" id=\"${panelId}\">\n <div class=\"supaship-toolbar-header\">\n <input\n type=\"text\"\n class=\"supaship-search-input\"\n id=\"${searchId}\"\n placeholder=\"Search features\"\n />\n <span class=\"supaship-toolbar-overrides-label\" id=\"${headerOverrideCountId}\">0 overrides</span>\n <button\n class=\"supaship-header-btn\"\n id=\"${clearId}\"\n aria-label=\"Reset all overrides\"\n title=\"Reset all overrides to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/>\n <path d=\"M3 3v5h5\"/>\n <path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/>\n <path d=\"M16 16h5v5\"/>\n </svg>\n </button>\n <button\n class=\"supaship-header-btn\"\n id=\"${closeId}\"\n aria-label=\"Close toolbar\"\n title=\"Close toolbar\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6 6 18\"/>\n <path d=\"m6 6 12 12\"/>\n </svg>\n </button>\n </div>\n <div class=\"supaship-toolbar-content\" id=\"${contentId}\">\n <div class=\"supaship-toolbar-empty\">${NO_FEATURES_MESSAGE}</div>\n </div>\n </div>\n </div>\n `\n }\n\n private injectStyles(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n if (document.getElementById('supaship-toolbar-styles')) {\n return\n }\n\n const styles = document.createElement('style')\n styles.id = 'supaship-toolbar-styles'\n styles.textContent = `\n .supaship-toolbar-container {\n position: fixed;\n z-index: 999999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n font-size: 14px;\n }\n\n .supaship-toolbar-bottom-right {\n bottom: var(--offset-y);\n right: var(--offset-x);\n }\n\n .supaship-toolbar-bottom-left {\n bottom: var(--offset-y);\n left: var(--offset-x);\n }\n\n .supaship-toolbar-top-right {\n top: var(--offset-y);\n right: var(--offset-x);\n }\n\n .supaship-toolbar-top-left {\n top: var(--offset-y);\n left: var(--offset-x);\n }\n\n .supaship-toolbar-toggle {\n position: relative;\n width: 36px;\n height: 36px;\n border-radius: 100%;\n background: #000;\n border: none;\n color: white;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n transition: transform 0.2s, box-shadow 0.2s;\n }\n\n .supaship-toolbar-toggle:hover {\n transform: scale(1.05);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);\n }\n\n .supaship-toolbar-badge {\n position: absolute;\n top: -4px;\n right: -4px;\n background: #ef4444;\n color: white;\n font-size: 10px;\n font-weight: 600;\n min-width: 18px;\n height: 18px;\n border-radius: 9px;\n display: none;\n align-items: center;\n justify-content: center;\n padding: 0 5px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n pointer-events: none;\n }\n\n .supaship-toolbar-badge.show {\n display: flex;\n }\n\n .supaship-toolbar-panel {\n position: absolute;\n bottom: 48px;\n right: 0;\n width: 300px;\n max-height: 600px;\n background: #1a1a1a;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);\n display: none;\n flex-direction: column;\n overflow: hidden;\n border: 1px solid #333;\n }\n\n .supaship-toolbar-bottom-left .supaship-toolbar-panel,\n .supaship-toolbar-top-left .supaship-toolbar-panel {\n right: auto;\n left: 0;\n }\n\n .supaship-toolbar-top-right .supaship-toolbar-panel,\n .supaship-toolbar-top-left .supaship-toolbar-panel {\n bottom: auto;\n top: 60px;\n }\n\n .supaship-toolbar-panel.open {\n display: flex;\n }\n\n .supaship-toolbar-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n border-bottom: 1px solid #333;\n background: #0f0f0f;\n min-width: 0;\n overflow: hidden;\n }\n\n .supaship-search-input {\n flex: 1;\n min-width: 0;\n background: transparent;\n border: none;\n color: #e5e5e5;\n padding: 0;\n font-size: 13px;\n outline: none;\n }\n\n .supaship-search-input::placeholder {\n color: #888;\n }\n\n .supaship-header-btn {\n background: transparent;\n border: none;\n color: #e5e5e5;\n width: 24px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.2s;\n padding: 0;\n }\n\n .supaship-header-btn:hover {\n color: #ef4444;\n }\n\n .supaship-toolbar-content {\n flex: 1;\n overflow-y: auto;\n padding: 8px;\n min-height: 300px;\n max-height: 300px;\n }\n\n .supaship-toolbar-empty {\n padding: 32px 16px;\n text-align: center;\n color: #888;\n }\n\n .supaship-feature-item {\n padding: 0 6px;\n }\n\n .supaship-feature-item.disabled {\n opacity: 0.5;\n pointer-events: none;\n }\n\n .supaship-feature-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n min-height: 32px;\n }\n\n .supaship-feature-name {\n font-weight: 500;\n color: #e5e5e5;\n font-size: 13px;\n flex: 1;\n min-width: 0;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n }\n\n .supaship-feature-override-indicator {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #ef4444;\n flex-shrink: 0;\n }\n\n .supaship-toolbar-overrides-label {\n font-size: 12px;\n color: #888;\n white-space: nowrap;\n flex-shrink: 0;\n transition: all 0.2s;\n }\n\n .supaship-toolbar-overrides-label-count.has-overrides {\n background: #ef4444;\n color: white;\n font-size: 10px;\n font-weight: 600;\n min-width: 18px;\n height: 18px;\n border-radius: 9px;\n padding: 2px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n .supaship-feature-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n min-height: 20px;\n }\n\n .supaship-feature-content {\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n .supaship-feature-input {\n flex: 1;\n padding: 6px 8px;\n background: #1a1a1a;\n border: 1px solid #555;\n color: #e5e5e5;\n border-radius: 4px;\n font-size: 13px;\n font-family: 'Monaco', 'Courier New', monospace;\n outline: none;\n resize: vertical;\n min-height: 60px;\n margin-bottom: 8px;\n }\n\n .supaship-feature-input:focus {\n border-color: #667eea;\n }\n\n .supaship-btn {\n padding: 4px 12px;\n border: none;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n }\n\n .supaship-btn-primary {\n background: #444;\n color: white;\n }\n\n .supaship-btn-primary:hover {\n background: #555;\n }\n\n .supaship-btn-secondary {\n background: #444;\n color: #e5e5e5;\n }\n\n .supaship-btn-secondary:hover {\n background: #555;\n }\n\n .supaship-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .supaship-btn:disabled:hover {\n background: #444;\n }\n\n .supaship-header-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n }\n\n .supaship-header-btn:disabled:hover {\n color: #e5e5e5;\n }\n\n .supaship-toggle {\n position: relative;\n display: inline-block;\n width: 32px;\n height: 18px;\n flex-shrink: 0;\n }\n\n .supaship-toggle input {\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .supaship-toggle-slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: #333;\n border: 1px solid #555;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 20px;\n }\n\n .supaship-toggle-slider:before {\n position: absolute;\n content: \"\";\n height: 14px;\n width: 14px;\n left: 2px;\n bottom: 1px;\n background-color: #666;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 50%;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n }\n\n .supaship-toggle input:checked + .supaship-toggle-slider {\n background-color: #fff;\n border-color: #fff;\n }\n\n .supaship-toggle input:checked + .supaship-toggle-slider:before {\n transform: translateX(13px);\n background-color: #000;\n }\n\n .supaship-toggle input:disabled + .supaship-toggle-slider {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .supaship-toggle:hover .supaship-toggle-slider:before {\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);\n }\n\n .supaship-btn-icon {\n background: transparent;\n border: none;\n color: #e5e5e5;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n cursor: pointer;\n padding: 0;\n transition: all 0.2s;\n flex-shrink: 0;\n }\n\n .supaship-btn-icon:hover {\n background: #444;\n color: #ef4444;\n }\n `\n document.head.appendChild(styles)\n }\n\n private attachEventListeners(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n const panelId = `supaship-toolbar-panel-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const closeId = `supaship-close-toolbar-${this.clientId}`\n const searchId = `supaship-search-input-${this.clientId}`\n const contentId = `supaship-toolbar-content-${this.clientId}`\n\n const toggle = document.getElementById(toggleId)\n const panel = document.getElementById(panelId)\n const clearAll = document.getElementById(clearId)\n const close = document.getElementById(closeId)\n const searchInput = document.getElementById(searchId) as HTMLInputElement\n const content = document.getElementById(contentId)\n\n toggle?.addEventListener('click', () => {\n panel?.classList.toggle('open')\n })\n\n close?.addEventListener('click', () => {\n panel?.classList.remove('open')\n })\n\n clearAll?.addEventListener('click', () => {\n this.clearAllOverrides()\n })\n\n searchInput?.addEventListener('input', e => {\n this.state.searchQuery = (e.target as HTMLInputElement).value.toLowerCase()\n this.updateToolbarUI()\n })\n\n // Use event delegation on content element - survives innerHTML updates\n if (content) {\n // Handle button clicks (remove and set actions)\n content.addEventListener('click', (e: Event) => {\n const target = e.target as HTMLElement\n const buttonElement = target.closest('button[data-action]') as HTMLButtonElement\n if (!buttonElement) return\n\n e.preventDefault()\n e.stopPropagation()\n\n const featureName = buttonElement.dataset.feature!\n const action = buttonElement.dataset.action\n\n if (action === 'remove') {\n this.removeOverride(featureName)\n } else if (action === 'set') {\n const textarea = content.querySelector(\n `textarea[data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLTextAreaElement\n if (textarea && textarea.value.trim()) {\n try {\n const value = JSON.parse(textarea.value)\n this.setOverride(featureName, value)\n } catch {\n // If not valid JSON, wrap string in object\n this.setOverride(featureName, { value: textarea.value })\n }\n }\n }\n })\n\n // Handle checkbox changes for boolean toggles\n content.addEventListener('change', (e: Event) => {\n const target = e.target as HTMLInputElement\n if (target.type === 'checkbox' && target.dataset.type === 'boolean') {\n const featureName = target.dataset.feature!\n const newValue = target.checked\n this.setOverride(featureName, newValue)\n }\n })\n\n // Handle textarea input to update button states\n content.addEventListener('input', (e: Event) => {\n const target = e.target as HTMLTextAreaElement\n if (target.tagName === 'TEXTAREA' && target.dataset.feature) {\n const featureName = target.dataset.feature!\n const originalValue = target.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = target.value !== originalValue\n const hasContent = target.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n }\n })\n\n // Handle textarea paste events\n content.addEventListener('paste', (e: Event) => {\n const target = e.target as HTMLTextAreaElement\n if (target.tagName === 'TEXTAREA' && target.dataset.feature) {\n setTimeout(() => {\n const featureName = target.dataset.feature!\n const originalValue = target.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${featureName}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = target.value !== originalValue\n const hasContent = target.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n }, 0)\n }\n })\n\n // Handle Ctrl/Cmd+Enter to set override\n content.addEventListener('keydown', (e: KeyboardEvent) => {\n const target = e.target as HTMLTextAreaElement\n if (\n target.tagName === 'TEXTAREA' &&\n target.dataset.feature &&\n (e.ctrlKey || e.metaKey) &&\n e.key === 'Enter'\n ) {\n e.preventDefault()\n const featureName = target.dataset.feature!\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn && !overrideBtn.disabled) {\n overrideBtn.click()\n }\n }\n })\n }\n }\n\n private updateToolbarUI(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const contentId = `supaship-toolbar-content-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const badgeId = `supaship-toolbar-badge-${this.clientId}`\n const headerOverrideCountId = `supaship-header-override-count-${this.clientId}`\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n\n const content = document.getElementById(contentId)\n const clearAllBtn = document.getElementById(clearId) as HTMLButtonElement\n const badge = document.getElementById(badgeId)\n const headerOverrideCount = document.getElementById(headerOverrideCountId)\n const toggleBtn = document.getElementById(toggleId) as HTMLButtonElement\n\n if (!content) {\n console.warn('[Toolbar] Content element not found:', contentId)\n return\n }\n\n // Update clear all button state and badge\n const overrideCount = Object.keys(this.state.overrides).length\n const hasOverrides = overrideCount > 0\n if (clearAllBtn) {\n clearAllBtn.disabled = !hasOverrides\n }\n\n // Update badge on toggle button\n if (badge) {\n if (hasOverrides) {\n badge.textContent = overrideCount > 99 ? '99+' : String(overrideCount)\n badge.classList.add('show')\n } else {\n badge.classList.remove('show')\n }\n }\n\n // Update tooltip on toggle button\n if (toggleBtn) {\n if (hasOverrides) {\n toggleBtn.setAttribute(\n 'title',\n `Supaship Toolbar, ${overrideCount} override${overrideCount === 1 ? '' : 's'}`\n )\n toggleBtn.setAttribute(\n 'aria-label',\n `Supaship Toolbar, ${overrideCount} override${overrideCount === 1 ? '' : 's'}`\n )\n } else {\n toggleBtn.setAttribute('title', 'Supaship Toolbar')\n toggleBtn.setAttribute('aria-label', 'Supaship Toolbar')\n }\n }\n\n // Update override count in header\n if (headerOverrideCount) {\n // Escape overrideCount to prevent any potential XSS (defense in depth)\n const escapedCount = this.escapeHtml(String(overrideCount))\n headerOverrideCount.innerHTML = `<span class=\"supaship-toolbar-overrides-label-count ${hasOverrides ? 'has-overrides' : ''}\">${escapedCount}</span> override${overrideCount === 1 ? '' : 's'}`\n }\n\n const features = Array.from(this.state.features).sort()\n\n // Filter features based on search query\n const filteredFeatures = features.filter(name =>\n name.toLowerCase().includes(this.state.searchQuery)\n )\n\n if (filteredFeatures.length === 0) {\n content.innerHTML = this.state.searchQuery\n ? '<div class=\"supaship-toolbar-empty\">No matching features found</div>'\n : `<div class=\"supaship-toolbar-empty\">${NO_FEATURES_MESSAGE}</div>`\n return\n }\n\n const htmlContent = filteredFeatures\n .map(featureName => {\n const hasOverride = featureName in this.state.overrides\n const currentValue = this.state.featureValues[featureName]\n const overrideValue = hasOverride ? this.state.overrides[featureName] : currentValue\n const isDisabled = !this.state.useLocalOverrides\n const itemClass = `supaship-feature-item ${isDisabled ? 'disabled' : ''}`\n\n // Check if the feature is boolean\n const isBoolean =\n typeof currentValue === 'boolean' || (hasOverride && typeof overrideValue === 'boolean')\n\n if (isBoolean) {\n // Render toggle switch for boolean values (single row layout)\n const isChecked = hasOverride ? overrideValue === true : currentValue === true\n return `\n <div class=\"${itemClass}\">\n <div class=\"supaship-feature-row\">\n <span class=\"supaship-feature-name\">\n ${this.escapeHtml(featureName)}\n ${hasOverride ? '<span class=\"supaship-feature-override-indicator\" title=\"Serving local override\"></span>' : ''}\n </span>\n <div class=\"supaship-feature-actions\">\n ${\n hasOverride\n ? `\n <button\n class=\"supaship-btn-icon\"\n data-feature=\"${this.escapeHtml(featureName)}\"\n data-action=\"remove\"\n title=\"Reset to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/>\n <path d=\"M3 3v5h5\"/>\n <path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/>\n <path d=\"M16 16h5v5\"/>\n </svg>\n </button>\n `\n : ''\n }\n <label class=\"supaship-toggle\">\n <input\n type=\"checkbox\"\n ${isChecked ? 'checked' : ''}\n data-feature=\"${this.escapeHtml(featureName)}\"\n data-type=\"boolean\"\n />\n <span class=\"supaship-toggle-slider\"></span>\n </label>\n </div>\n </div>\n </div>\n `\n } else {\n // Render textarea for non-boolean values\n const currentDisplayValue = hasOverride\n ? JSON.stringify(overrideValue)\n : currentValue !== undefined\n ? JSON.stringify(currentValue)\n : ''\n const escapedFeatureName = this.escapeHtml(featureName)\n const escapedCurrentDisplayValue = this.escapeHtml(currentDisplayValue)\n const escapedTextareaContent = hasOverride\n ? this.escapeHtml(JSON.stringify(overrideValue))\n : escapedCurrentDisplayValue\n\n return `\n <div class=\"${itemClass}\">\n <div class=\"supaship-feature-row\">\n <span class=\"supaship-feature-name\">\n ${escapedFeatureName}\n ${hasOverride ? '<span class=\"supaship-feature-override-indicator\" title=\"Serving local override\"></span>' : ''}\n </span>\n <div class=\"supaship-feature-actions\">\n ${\n hasOverride\n ? `\n <button\n class=\"supaship-btn-icon\"\n data-feature=\"${escapedFeatureName}\"\n data-action=\"remove\"\n title=\"Reset to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 256\" width=\"14\" height=\"14\">\n <path d=\"M240,56v48a8,8,0,0,1-8,8H184a8,8,0,0,1,0-16H211.4L184.81,71.64l-.25-.24a80,80,0,1,0-1.67,114.78,8,8,0,0,1,11,11.63A95.44,95.44,0,0,1,128,224h-1.32A96,96,0,1,1,195.75,60L224,85.8V56a8,8,0,1,1,16,0Z\" fill=\"currentColor\"/>\n </svg>\n </button>\n `\n : ''\n }\n <button\n class=\"supaship-btn supaship-btn-primary\"\n data-feature=\"${escapedFeatureName}\"\n data-action=\"set\"\n disabled>\n Override\n </button>\n </div>\n </div>\n <div class=\"supaship-feature-content\">\n <textarea\n class=\"supaship-feature-input\"\n placeholder=\"Override JSON value\"\n data-feature=\"${escapedFeatureName}\"\n data-original=\"${escapedCurrentDisplayValue}\"\n >${escapedTextareaContent}</textarea>\n </div>\n </div>\n `\n }\n })\n .join('')\n\n requestAnimationFrame(() => {\n // Set innerHTML - event listeners are handled via delegation in attachEventListeners()\n content.innerHTML = htmlContent\n\n // Update button states for textareas that already have values\n content.querySelectorAll('textarea[data-feature]').forEach(textarea => {\n const textareaElement = textarea as HTMLTextAreaElement\n const featureName = textareaElement.dataset.feature!\n const originalValue = textareaElement.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = textareaElement.value !== originalValue\n const hasContent = textareaElement.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n })\n })\n }\n\n private escapeHtml(text: string): string {\n const div = typeof document !== 'undefined' ? document.createElement('div') : null\n if (div) {\n div.textContent = text\n return div.innerHTML\n }\n return text.replace(/[&<>\"']/g, char => {\n const escapeMap: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n }\n return escapeMap[char]\n })\n }\n\n /**\n * Escapes special characters in CSS attribute selectors to prevent CSS injection\n * @param value The value to escape for use in CSS attribute selectors\n */\n private escapeCssSelector(value: string): string {\n // Escape special CSS selector characters: \", ', ], \\\n return value.replace(/[\"'\\\\\\]]/g, '\\\\$&')\n }\n}\n","export const DEFAULT_FEATURES_URL = 'https://edge.supaship.com/v1/features'\nexport const DEFAULT_EVENTS_URL = 'https://edge.supaship.com/v1/events'\n","import {\n SupaClientConfig,\n FeatureContext,\n FeatureValue,\n NetworkConfig,\n Features,\n FeaturesWithFallbacks,\n} from './types'\nimport { retry } from './utils'\nimport { SupaPlugin } from './plugins/types'\nimport { SupaToolbarPlugin } from './plugins/toolbar-plugin'\nimport { DEFAULT_FEATURES_URL, DEFAULT_EVENTS_URL } from './constants'\n\ntype RequiredRetryConfig = Required<NonNullable<NetworkConfig['retry']>>\ntype ResolvedNetworkConfig = {\n featuresAPIUrl: string\n eventsAPIUrl: string\n retry: RequiredRetryConfig\n requestTimeoutMs: number\n}\n\nexport class SupaClient<TFeatures extends FeaturesWithFallbacks> {\n private apiKey: string\n private environment: string\n private defaultContext?: FeatureContext\n private plugins: SupaPlugin[]\n private featureDefinitions: Features<TFeatures>\n private clientId: string\n\n private fetchImpl: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>\n private networkConfig: ResolvedNetworkConfig\n\n constructor(config: SupaClientConfig & { features: TFeatures }) {\n this.apiKey = config.apiKey\n this.environment = config.environment\n this.defaultContext = config.context\n this.featureDefinitions = config.features as Features<TFeatures>\n\n // Generate unique client ID\n this.clientId = this.generateClientId()\n\n this.networkConfig = {\n featuresAPIUrl: config.networkConfig?.featuresAPIUrl || DEFAULT_FEATURES_URL,\n eventsAPIUrl: config.networkConfig?.eventsAPIUrl || DEFAULT_EVENTS_URL,\n retry: {\n enabled: config.networkConfig?.retry?.enabled ?? true,\n maxAttempts: config.networkConfig?.retry?.maxAttempts ?? 3,\n backoff: config.networkConfig?.retry?.backoff ?? 1000,\n },\n requestTimeoutMs: config.networkConfig?.requestTimeoutMs ?? 10000,\n }\n\n // Prefer injected fetch, then global fetch if available\n const globalFetch: typeof fetch | undefined =\n typeof globalThis !== 'undefined'\n ? (globalThis as unknown as { fetch?: typeof fetch }).fetch\n : undefined\n if (config.networkConfig?.fetchFn) {\n this.fetchImpl = config.networkConfig.fetchFn\n } else if (typeof globalFetch === 'function') {\n this.fetchImpl = globalFetch.bind(globalThis)\n } else {\n throw new Error(\n 'No fetch implementation available. Provide fetchFn in config or use a runtime with global fetch (e.g., Node 18+, browsers).'\n )\n }\n\n // Initialize plugins with automatic toolbar plugin in browser\n this.plugins = this.initializePlugins(config)\n\n // Initialize plugins with available features and their fallback values\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onInit?.({\n clientId: this.clientId,\n availableFeatures: this.featureDefinitions,\n context: this.defaultContext,\n })\n )\n ).catch(console.error)\n }\n\n /**\n * Generate a unique client ID\n */\n private generateClientId(): string {\n return `supaship-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`\n }\n\n /**\n * Initialize plugins with automatic toolbar plugin in browser environments\n */\n private initializePlugins(config: SupaClientConfig & { features: TFeatures }): SupaPlugin[] {\n const plugins = config.plugins || []\n\n // Check if we're in a browser environment\n const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined'\n\n // If toolbar is explicitly disabled, don't add it\n if (config.toolbar === false) {\n return plugins\n }\n\n // If in browser and toolbar not disabled, add it automatically\n if (isBrowser) {\n // Check if user already added toolbar plugin manually\n const hasToolbarPlugin = plugins.some(p => p.name === 'toolbar-plugin')\n\n if (!hasToolbarPlugin) {\n // Add toolbar with user config or defaults\n const toolbarConfig = config.toolbar || { enabled: 'auto' }\n const toolbarPlugin = new SupaToolbarPlugin(toolbarConfig)\n return [toolbarPlugin, ...plugins]\n }\n }\n\n return plugins\n }\n\n /**\n * Updates the default context for the client\n * @param context - New context to merge with or replace the existing context\n * @param mergeWithExisting - Whether to merge with existing context (default: true)\n */\n updateContext(context: FeatureContext, mergeWithExisting: boolean = true): void {\n const oldContext = this.defaultContext\n\n if (mergeWithExisting && this.defaultContext) {\n this.defaultContext = { ...this.defaultContext, ...context }\n } else {\n this.defaultContext = context\n }\n\n // Notify plugins of context change\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onContextUpdate?.(oldContext, this.defaultContext!, 'updateContext')\n )\n ).catch(console.error)\n }\n\n /**\n * Gets the current default context\n */\n getContext(): FeatureContext | undefined {\n return this.defaultContext\n }\n\n /**\n * Gets the fallback value for a feature from its definition\n */\n getFeatureFallback<TKey extends keyof TFeatures>(featureName: TKey): Features<TFeatures>[TKey] {\n return this.featureDefinitions[featureName]\n }\n\n private getVariationValue(variation: FeatureValue, fallback: FeatureValue): FeatureValue {\n if (variation !== undefined && variation !== null) {\n return variation\n }\n\n return fallback ?? null\n }\n\n async getFeature<TKey extends keyof TFeatures>(\n featureName: TKey,\n options?: { context?: FeatureContext }\n ): Promise<Features<TFeatures>[TKey]> {\n const { context } = options ?? {}\n\n // Only merge context if it's defined and not null\n const mergedContext: FeatureContext | undefined =\n typeof context === 'object' && context !== null\n ? { ...(this.defaultContext ?? {}), ...context }\n : this.defaultContext\n\n try {\n const response = await this.getFeatures([featureName as string], {\n context: mergedContext,\n })\n\n // Get the specific feature value\n const value = response[featureName as string]\n return value as Features<TFeatures>[TKey]\n } catch (error) {\n // Run onError hooks\n await Promise.all(this.plugins.map(plugin => plugin.onError?.(error as Error, mergedContext)))\n\n // Use fallback feature value when API fails\n const fallbackValue = this.featureDefinitions[featureName]\n\n // Notify plugins that fallback was used\n await Promise.all(\n this.plugins.map(plugin =>\n plugin.onFallbackUsed?.(\n featureName as string,\n fallbackValue as FeatureValue,\n error as Error\n )\n )\n )\n return fallbackValue as Features<TFeatures>[TKey]\n }\n }\n\n async getFeatures<TKeys extends readonly (keyof TFeatures)[]>(\n featureNames: TKeys,\n options?: { context?: FeatureContext }\n ): Promise<{ [K in TKeys[number]]: Features<TFeatures>[K] }> {\n const { context: contextOverride } = options ?? {}\n\n // Only merge context if it's defined and not null\n const mergedContext: FeatureContext | undefined =\n typeof contextOverride === 'object' && contextOverride !== null\n ? { ...(this.defaultContext ?? {}), ...contextOverride }\n : this.defaultContext\n\n // Notify plugins of context update for this request\n if (contextOverride) {\n await Promise.all(\n this.plugins.map(plugin =>\n plugin.onContextUpdate?.(this.defaultContext, mergedContext!, 'request')\n )\n )\n }\n\n // Convert feature names to strings for API call\n const featureNamesArray = featureNames.map(name => name as string)\n\n try {\n // Run beforeGetFeatures hooks\n await Promise.all(\n this.plugins.map(plugin => plugin.beforeGetFeatures?.(featureNamesArray, mergedContext))\n )\n\n type FeaturesResponse = { features: Record<string, { variation: FeatureValue }> }\n const fetchFeatures = async (): Promise<Record<string, FeatureValue>> => {\n const url = this.networkConfig.featuresAPIUrl\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n }\n const body = JSON.stringify({\n environment: this.environment,\n features: featureNamesArray,\n context: mergedContext,\n })\n\n // Notify plugins before request\n await Promise.all(this.plugins.map(plugin => plugin.beforeRequest?.(url, body, headers)))\n\n const startTime = Date.now()\n // Support timeout via AbortController when available\n const AbortCtrl: typeof AbortController | undefined =\n typeof globalThis !== 'undefined'\n ? (globalThis as unknown as { AbortController?: typeof AbortController })\n .AbortController\n : undefined\n let controller: AbortController | undefined\n let timeoutId: ReturnType<typeof setTimeout> | undefined\n if (this.networkConfig.requestTimeoutMs && typeof AbortCtrl === 'function') {\n controller = new AbortCtrl()\n timeoutId = setTimeout(() => controller?.abort(), this.networkConfig.requestTimeoutMs)\n }\n let response: Response\n try {\n response = await this.fetchImpl(url, {\n method: 'POST',\n headers,\n body,\n signal: controller?.signal,\n })\n } finally {\n if (timeoutId) clearTimeout(timeoutId)\n }\n const duration = Date.now() - startTime\n\n // Notify plugins after response\n await Promise.all(\n this.plugins.map(plugin => plugin.afterResponse?.(response, { duration }))\n )\n\n if (!response.ok) {\n throw new Error(`Failed to fetch features: ${response.statusText}`)\n }\n\n const data = (await response.json()) as FeaturesResponse\n const result: Record<string, FeatureValue> = {}\n\n featureNamesArray.forEach(name => {\n const variation = data.features[name]?.variation\n result[name] = this.getVariationValue(\n variation,\n this.featureDefinitions[name as keyof TFeatures]\n )\n })\n\n return result\n }\n\n const result = this.networkConfig.retry.enabled\n ? await retry(\n fetchFeatures,\n this.networkConfig.retry.maxAttempts,\n this.networkConfig.retry.backoff,\n (attempt, error, willRetry) => {\n // Notify plugins of retry attempts\n Promise.all(\n this.plugins.map(plugin => plugin.onRetryAttempt?.(attempt, error, willRetry))\n ).catch(console.error)\n }\n )\n : await fetchFeatures()\n\n // Run afterGetFeatures hooks\n await Promise.all(\n this.plugins.map(plugin => plugin.afterGetFeatures?.(result, mergedContext))\n )\n\n // Return the fetched features\n return result as { [K in TKeys[number]]: Features<TFeatures>[K] }\n } catch (error) {\n // Run onError hooks\n await Promise.all(this.plugins.map(plugin => plugin.onError?.(error as Error, mergedContext)))\n\n // Create fallback result with requested feature names\n const fallbackResult: Record<string, FeatureValue> = {}\n\n featureNamesArray.forEach(featureName => {\n fallbackResult[featureName] = this.featureDefinitions[featureName as keyof TFeatures]\n\n // Notify plugins that fallback was used for each feature\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onFallbackUsed?.(\n featureName,\n this.featureDefinitions[featureName as keyof TFeatures],\n error as Error\n )\n )\n ).catch(console.error)\n })\n\n return fallbackResult as { [K in TKeys[number]]: Features<TFeatures>[K] }\n }\n }\n}\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["result"]}
1
+ {"version":3,"sources":["../src/utils.ts","../src/plugins/toolbar-plugin.ts","../src/constants.ts","../src/client.ts"],"sourcesContent":["export function sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nexport async function retry<T>(\n fn: () => Promise<T>,\n maxAttempts: number = 3,\n backoff: number = 1000,\n onRetry?: (attempt: number, error: Error, willRetry: boolean) => void\n): Promise<T> {\n let lastError: Error\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn()\n } catch (error) {\n lastError = error as Error\n const willRetry = attempt < maxAttempts\n\n if (onRetry) {\n onRetry(attempt, lastError, willRetry)\n }\n\n if (!willRetry) break\n await sleep(backoff * Math.pow(2, attempt - 1))\n }\n }\n\n throw lastError!\n}\n","import { SupaPlugin, SupaPluginConfig } from './types'\nimport { FeatureContext, FeatureValue } from '../types'\n\nexport interface SupaToolbarPosition {\n placement?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'\n offset?: { x: string; y: string }\n}\n\nexport type SupaToolbarOverrideChange = {\n feature: string\n value: FeatureValue\n}\n\nexport type SupaToolbarOverrideChangeCallback = (\n featureOverride: SupaToolbarOverrideChange,\n allOverrides: Record<string, FeatureValue>\n) => void\n\nexport interface SupaToolbarPluginConfig extends Omit<SupaPluginConfig, 'enabled'> {\n enabled?: boolean | 'auto' // auto means show only on localhost\n position?: SupaToolbarPosition\n onOverrideChange?: SupaToolbarOverrideChangeCallback\n}\n\ninterface SupaToolbarState {\n overrides: Record<string, FeatureValue>\n features: Set<string>\n featureValues: Record<string, FeatureValue>\n context?: FeatureContext\n searchQuery: string\n useLocalOverrides: boolean\n}\n\nconst DEFAULT_STORAGE_KEY = 'supaship-feature-overrides'\n\nconst NO_FEATURES_MESSAGE = `No feature flags configured in the client.`\n\n/**\n * Toolbar plugin for local feature flag testing\n * Provides a visual interface to override feature flags during development\n */\nexport class SupaToolbarPlugin implements SupaPlugin {\n name = 'toolbar-plugin'\n private config: {\n enabled: boolean | 'auto'\n position: Required<SupaToolbarPosition>\n onOverrideChange?: SupaToolbarOverrideChangeCallback\n }\n private state: SupaToolbarState\n private clientId?: string\n private storageKey: string = DEFAULT_STORAGE_KEY\n\n constructor(config: SupaToolbarPluginConfig = {}) {\n this.config = {\n enabled: config.enabled ?? 'auto',\n position: {\n placement: config.position?.placement ?? 'bottom-right',\n offset: config.position?.offset ?? { x: '1rem', y: '1rem' },\n },\n onOverrideChange: config.onOverrideChange,\n }\n\n this.state = {\n overrides: {},\n features: new Set(),\n featureValues: {},\n searchQuery: '',\n useLocalOverrides: true,\n }\n }\n\n cleanup(): void {\n this.removeToolbar()\n }\n\n private shouldShowToolbar(): boolean {\n if (this.config.enabled === true) return true\n if (this.config.enabled === false) return false\n\n // Auto mode: show only on localhost\n if (typeof window !== 'undefined') {\n return (\n window.location.hostname === 'localhost' ||\n window.location.hostname === '127.0.0.1' ||\n window.location.hostname === '' ||\n window.location.hostname.endsWith('.local') ||\n window.location.hostname.endsWith('.localhost')\n )\n }\n return false\n }\n\n onInit(params: {\n availableFeatures: Record<string, FeatureValue>\n context?: FeatureContext\n clientId: string\n }): void {\n const { availableFeatures, context, clientId } = params\n\n // Set client ID for DOM element IDs\n this.clientId = clientId\n\n // Use shared storage key (not client-specific) to persist across refreshes\n this.storageKey = DEFAULT_STORAGE_KEY\n\n // Load overrides from shared storage\n this.state.overrides = this.loadOverrides()\n\n // Initialize with all available features and their fallback values from config\n this.state.features = new Set(Object.keys(availableFeatures))\n this.state.featureValues = { ...availableFeatures }\n this.state.context = context\n\n // Inject toolbar if conditions are met\n if (this.shouldShowToolbar()) {\n this.injectToolbar()\n }\n\n // Update toolbar UI if it exists\n this.updateToolbarUI()\n }\n\n async beforeGetFeatures(_featureNames: string[], context?: FeatureContext): Promise<void> {\n // Update context if it changed\n this.state.context = context\n\n // Load overrides from shared storage\n this.state.overrides = this.loadOverrides()\n\n // Update toolbar UI if it exists\n this.updateToolbarUI()\n }\n\n async afterGetFeatures(\n results: Record<string, FeatureValue>,\n context?: FeatureContext\n ): Promise<void> {\n // Update feature values with fetched results (this replaces config fallback values)\n Object.keys(results).forEach(name => {\n this.state.featureValues[name] = results[name]\n })\n\n // Apply overrides to results only if local overrides are enabled\n if (this.state.useLocalOverrides) {\n Object.keys(this.state.overrides).forEach(featureName => {\n if (featureName in results) {\n results[featureName] = this.state.overrides[featureName]\n }\n })\n }\n\n // Track features and update UI\n Object.keys(results).forEach(name => this.state.features.add(name))\n this.state.context = context\n this.updateToolbarUI()\n }\n\n private loadOverrides(): Record<string, FeatureValue> {\n if (typeof window === 'undefined' || !window.localStorage) {\n return {}\n }\n\n try {\n const stored = window.localStorage.getItem(this.storageKey)\n return stored ? JSON.parse(stored) : {}\n } catch {\n return {}\n }\n }\n\n private saveOverrides(\n feature?: string,\n value?: FeatureValue,\n allOverrides?: Record<string, FeatureValue>\n ): void {\n if (typeof window === 'undefined' || !window.localStorage) {\n return\n }\n\n try {\n window.localStorage.setItem(this.storageKey, JSON.stringify(allOverrides))\n this.config.onOverrideChange?.(\n { feature: feature ?? '', value: value ?? null },\n allOverrides ?? {}\n )\n } catch (error) {\n console.error('Supaship: Failed to save feature overrides:', error)\n }\n }\n\n public setOverride(featureName: string, value: FeatureValue): void {\n this.state.overrides[featureName] = value\n this.saveOverrides(featureName, value, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public removeOverride(featureName: string): void {\n delete this.state.overrides[featureName]\n this.saveOverrides(featureName, null, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public clearAllOverrides(): void {\n this.state.overrides = {}\n this.saveOverrides('', null, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public getOverrides(): Record<string, FeatureValue> {\n return { ...this.state.overrides }\n }\n\n private injectToolbar(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return\n }\n\n // Check if toolbar with this client ID already exists\n const toolbarId = `supaship-toolbar-${this.clientId}`\n if (document.getElementById(toolbarId)) {\n return\n }\n\n // Create toolbar container\n const toolbar = document.createElement('div')\n toolbar.id = toolbarId\n toolbar.setAttribute('data-supaship-client', this.clientId || '')\n toolbar.innerHTML = this.getToolbarHTML()\n\n // Add styles\n this.injectStyles()\n\n // Add to DOM\n document.body.appendChild(toolbar)\n\n // Add event listeners\n this.attachEventListeners()\n }\n\n private removeToolbar(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const toolbar = document.getElementById(`supaship-toolbar-${this.clientId}`)\n if (toolbar) {\n toolbar.remove()\n }\n\n const styles = document.getElementById('supaship-toolbar-styles')\n if (styles) {\n styles.remove()\n }\n }\n\n private getToolbarHTML(): string {\n const { placement, offset } = this.config.position\n const positionClass = `supaship-toolbar-${placement}`\n const offsetX = offset?.x ?? '1rem'\n const offsetY = offset?.y ?? '1rem'\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n const panelId = `supaship-toolbar-panel-${this.clientId}`\n const searchId = `supaship-search-input-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const closeId = `supaship-close-toolbar-${this.clientId}`\n const contentId = `supaship-toolbar-content-${this.clientId}`\n const badgeId = `supaship-toolbar-badge-${this.clientId}`\n const headerOverrideCountId = `supaship-header-override-count-${this.clientId}`\n\n return `\n <div class=\"supaship-toolbar-container ${positionClass}\" style=\"--offset-x: ${offsetX}; --offset-y: ${offsetY};\">\n <button class=\"supaship-toolbar-toggle\" id=\"${toggleId}\" aria-label=\"Supaship Toolbar\" title=\"Supaship Toolbar\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 256 256\"\n width=\"32\"\n style=\"vertical-align: middle;\">\n <rect width='256' height='256' rx='128' fill='#e4f222'></rect>\n <line\n x1='66'\n y1='118'\n x2='118'\n y2='66'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n <line\n x1='186'\n y1='70'\n x2='70'\n y2='186'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n <line\n x1='190'\n y1='138'\n x2='138'\n y2='190'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n </svg>\n <span class=\"supaship-toolbar-badge\" id=\"${badgeId}\"></span>\n </button>\n <div class=\"supaship-toolbar-panel\" id=\"${panelId}\">\n <div class=\"supaship-toolbar-header\">\n <input\n type=\"text\"\n class=\"supaship-search-input\"\n id=\"${searchId}\"\n placeholder=\"Search features\"\n />\n <span class=\"supaship-toolbar-overrides-label\" id=\"${headerOverrideCountId}\">0 overrides</span>\n <button\n class=\"supaship-header-btn\"\n id=\"${clearId}\"\n aria-label=\"Reset all overrides\"\n title=\"Reset all overrides to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/>\n <path d=\"M3 3v5h5\"/>\n <path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/>\n <path d=\"M16 16h5v5\"/>\n </svg>\n </button>\n <button\n class=\"supaship-header-btn\"\n id=\"${closeId}\"\n aria-label=\"Close toolbar\"\n title=\"Close toolbar\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6 6 18\"/>\n <path d=\"m6 6 12 12\"/>\n </svg>\n </button>\n </div>\n <div class=\"supaship-toolbar-content\" id=\"${contentId}\">\n <div class=\"supaship-toolbar-empty\">${NO_FEATURES_MESSAGE}</div>\n </div>\n </div>\n </div>\n `\n }\n\n private injectStyles(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n if (document.getElementById('supaship-toolbar-styles')) {\n return\n }\n\n const styles = document.createElement('style')\n styles.id = 'supaship-toolbar-styles'\n styles.textContent = `\n .supaship-toolbar-container {\n position: fixed;\n z-index: 999999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n font-size: 14px;\n }\n\n .supaship-toolbar-bottom-right {\n bottom: var(--offset-y);\n right: var(--offset-x);\n }\n\n .supaship-toolbar-bottom-left {\n bottom: var(--offset-y);\n left: var(--offset-x);\n }\n\n .supaship-toolbar-top-right {\n top: var(--offset-y);\n right: var(--offset-x);\n }\n\n .supaship-toolbar-top-left {\n top: var(--offset-y);\n left: var(--offset-x);\n }\n\n .supaship-toolbar-toggle {\n position: relative;\n width: 36px;\n height: 36px;\n border-radius: 100%;\n border: none;\n color: white;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n transition: transform 0.2s, box-shadow 0.2s;\n }\n\n .supaship-toolbar-toggle:hover {\n transform: scale(1.05);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);\n }\n\n .supaship-toolbar-badge {\n position: absolute;\n top: -4px;\n right: -4px;\n background: #ef4444;\n color: white;\n font-size: 10px;\n font-weight: 600;\n min-width: 18px;\n height: 18px;\n border-radius: 9px;\n display: none;\n align-items: center;\n justify-content: center;\n padding: 0 5px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n pointer-events: none;\n }\n\n .supaship-toolbar-badge.show {\n display: flex;\n }\n\n .supaship-toolbar-panel {\n position: absolute;\n bottom: 48px;\n right: 0;\n width: 300px;\n max-height: 600px;\n background: #1a1a1a;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);\n display: none;\n flex-direction: column;\n overflow: hidden;\n border: 1px solid #333;\n }\n\n .supaship-toolbar-bottom-left .supaship-toolbar-panel,\n .supaship-toolbar-top-left .supaship-toolbar-panel {\n right: auto;\n left: 0;\n }\n\n .supaship-toolbar-top-right .supaship-toolbar-panel,\n .supaship-toolbar-top-left .supaship-toolbar-panel {\n bottom: auto;\n top: 60px;\n }\n\n .supaship-toolbar-panel.open {\n display: flex;\n }\n\n .supaship-toolbar-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n border-bottom: 1px solid #333;\n background: #0f0f0f;\n min-width: 0;\n overflow: hidden;\n }\n\n .supaship-search-input {\n flex: 1;\n min-width: 0;\n background: transparent;\n border: none;\n color: #e5e5e5;\n padding: 0;\n font-size: 13px;\n outline: none;\n }\n\n .supaship-search-input::placeholder {\n color: #888;\n }\n\n .supaship-header-btn {\n background: transparent;\n border: none;\n color: #e5e5e5;\n width: 24px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.2s;\n padding: 0;\n }\n\n .supaship-header-btn:hover {\n color: #ef4444;\n }\n\n .supaship-toolbar-content {\n flex: 1;\n overflow-y: auto;\n padding: 8px;\n min-height: 300px;\n max-height: 300px;\n }\n\n .supaship-toolbar-empty {\n padding: 32px 16px;\n text-align: center;\n color: #888;\n }\n\n .supaship-feature-item {\n padding: 0 6px;\n }\n\n .supaship-feature-item.disabled {\n opacity: 0.5;\n pointer-events: none;\n }\n\n .supaship-feature-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n min-height: 32px;\n }\n\n .supaship-feature-name {\n font-weight: 500;\n color: #e5e5e5;\n font-size: 13px;\n flex: 1;\n min-width: 0;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n }\n\n .supaship-feature-override-indicator {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #ef4444;\n flex-shrink: 0;\n }\n\n .supaship-toolbar-overrides-label {\n font-size: 12px;\n color: #888;\n white-space: nowrap;\n flex-shrink: 0;\n transition: all 0.2s;\n }\n\n .supaship-toolbar-overrides-label-count.has-overrides {\n background: #ef4444;\n color: white;\n font-size: 10px;\n font-weight: 600;\n min-width: 18px;\n height: 18px;\n border-radius: 9px;\n padding: 2px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n .supaship-feature-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n min-height: 20px;\n }\n\n .supaship-feature-content {\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n .supaship-feature-input {\n flex: 1;\n padding: 6px 8px;\n background: #1a1a1a;\n border: 1px solid #555;\n color: #e5e5e5;\n border-radius: 4px;\n font-size: 13px;\n font-family: 'Monaco', 'Courier New', monospace;\n outline: none;\n resize: vertical;\n min-height: 60px;\n margin-bottom: 8px;\n }\n\n .supaship-feature-input:focus {\n border-color: #667eea;\n }\n\n .supaship-btn {\n padding: 4px 12px;\n border: none;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n }\n\n .supaship-btn-primary {\n background: #444;\n color: white;\n }\n\n .supaship-btn-primary:hover {\n background: #555;\n }\n\n .supaship-btn-secondary {\n background: #444;\n color: #e5e5e5;\n }\n\n .supaship-btn-secondary:hover {\n background: #555;\n }\n\n .supaship-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .supaship-btn:disabled:hover {\n background: #444;\n }\n\n .supaship-header-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n }\n\n .supaship-header-btn:disabled:hover {\n color: #e5e5e5;\n }\n\n .supaship-toggle {\n position: relative;\n display: inline-block;\n width: 32px;\n height: 18px;\n flex-shrink: 0;\n }\n\n .supaship-toggle input {\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .supaship-toggle-slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: #333;\n border: 1px solid #555;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 20px;\n }\n\n .supaship-toggle-slider:before {\n position: absolute;\n content: \"\";\n height: 14px;\n width: 14px;\n left: 2px;\n bottom: 1px;\n background-color: #666;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 50%;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n }\n\n .supaship-toggle input:checked + .supaship-toggle-slider {\n background-color: #fff;\n border-color: #fff;\n }\n\n .supaship-toggle input:checked + .supaship-toggle-slider:before {\n transform: translateX(13px);\n background-color: #000;\n }\n\n .supaship-toggle input:disabled + .supaship-toggle-slider {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .supaship-toggle:hover .supaship-toggle-slider:before {\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);\n }\n\n .supaship-btn-icon {\n background: transparent;\n border: none;\n color: #e5e5e5;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n cursor: pointer;\n padding: 0;\n transition: all 0.2s;\n flex-shrink: 0;\n }\n\n .supaship-btn-icon:hover {\n background: #444;\n color: #ef4444;\n }\n `\n document.head.appendChild(styles)\n }\n\n private attachEventListeners(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n const panelId = `supaship-toolbar-panel-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const closeId = `supaship-close-toolbar-${this.clientId}`\n const searchId = `supaship-search-input-${this.clientId}`\n const contentId = `supaship-toolbar-content-${this.clientId}`\n\n const toggle = document.getElementById(toggleId)\n const panel = document.getElementById(panelId)\n const clearAll = document.getElementById(clearId)\n const close = document.getElementById(closeId)\n const searchInput = document.getElementById(searchId) as HTMLInputElement\n const content = document.getElementById(contentId)\n\n toggle?.addEventListener('click', () => {\n panel?.classList.toggle('open')\n })\n\n close?.addEventListener('click', () => {\n panel?.classList.remove('open')\n })\n\n clearAll?.addEventListener('click', () => {\n this.clearAllOverrides()\n })\n\n searchInput?.addEventListener('input', e => {\n this.state.searchQuery = (e.target as HTMLInputElement).value.toLowerCase()\n this.updateToolbarUI()\n })\n\n // Use event delegation on content element - survives innerHTML updates\n if (content) {\n // Handle button clicks (remove and set actions)\n content.addEventListener('click', (e: Event) => {\n const target = e.target as HTMLElement\n const buttonElement = target.closest('button[data-action]') as HTMLButtonElement\n if (!buttonElement) return\n\n e.preventDefault()\n e.stopPropagation()\n\n const featureName = buttonElement.dataset.feature!\n const action = buttonElement.dataset.action\n\n if (action === 'remove') {\n this.removeOverride(featureName)\n } else if (action === 'set') {\n const textarea = content.querySelector(\n `textarea[data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLTextAreaElement\n if (textarea && textarea.value.trim()) {\n try {\n const value = JSON.parse(textarea.value)\n this.setOverride(featureName, value)\n } catch {\n // If not valid JSON, wrap string in object\n this.setOverride(featureName, { value: textarea.value })\n }\n }\n }\n })\n\n // Handle checkbox changes for boolean toggles\n content.addEventListener('change', (e: Event) => {\n const target = e.target as HTMLInputElement\n if (target.type === 'checkbox' && target.dataset.type === 'boolean') {\n const featureName = target.dataset.feature!\n const newValue = target.checked\n this.setOverride(featureName, newValue)\n }\n })\n\n // Handle textarea input to update button states\n content.addEventListener('input', (e: Event) => {\n const target = e.target as HTMLTextAreaElement\n if (target.tagName === 'TEXTAREA' && target.dataset.feature) {\n const featureName = target.dataset.feature!\n const originalValue = target.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = target.value !== originalValue\n const hasContent = target.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n }\n })\n\n // Handle textarea paste events\n content.addEventListener('paste', (e: Event) => {\n const target = e.target as HTMLTextAreaElement\n if (target.tagName === 'TEXTAREA' && target.dataset.feature) {\n setTimeout(() => {\n const featureName = target.dataset.feature!\n const originalValue = target.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${featureName}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = target.value !== originalValue\n const hasContent = target.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n }, 0)\n }\n })\n\n // Handle Ctrl/Cmd+Enter to set override\n content.addEventListener('keydown', (e: KeyboardEvent) => {\n const target = e.target as HTMLTextAreaElement\n if (\n target.tagName === 'TEXTAREA' &&\n target.dataset.feature &&\n (e.ctrlKey || e.metaKey) &&\n e.key === 'Enter'\n ) {\n e.preventDefault()\n const featureName = target.dataset.feature!\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn && !overrideBtn.disabled) {\n overrideBtn.click()\n }\n }\n })\n }\n }\n\n private updateToolbarUI(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const contentId = `supaship-toolbar-content-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const badgeId = `supaship-toolbar-badge-${this.clientId}`\n const headerOverrideCountId = `supaship-header-override-count-${this.clientId}`\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n\n const content = document.getElementById(contentId)\n const clearAllBtn = document.getElementById(clearId) as HTMLButtonElement\n const badge = document.getElementById(badgeId)\n const headerOverrideCount = document.getElementById(headerOverrideCountId)\n const toggleBtn = document.getElementById(toggleId) as HTMLButtonElement\n\n if (!content) {\n console.warn('[Toolbar] Content element not found:', contentId)\n return\n }\n\n // Update clear all button state and badge\n const overrideCount = Object.keys(this.state.overrides).length\n const hasOverrides = overrideCount > 0\n if (clearAllBtn) {\n clearAllBtn.disabled = !hasOverrides\n }\n\n // Update badge on toggle button\n if (badge) {\n if (hasOverrides) {\n badge.textContent = overrideCount > 99 ? '99+' : String(overrideCount)\n badge.classList.add('show')\n } else {\n badge.classList.remove('show')\n }\n }\n\n // Update tooltip on toggle button\n if (toggleBtn) {\n if (hasOverrides) {\n toggleBtn.setAttribute(\n 'title',\n `Supaship Toolbar, ${overrideCount} override${overrideCount === 1 ? '' : 's'}`\n )\n toggleBtn.setAttribute(\n 'aria-label',\n `Supaship Toolbar, ${overrideCount} override${overrideCount === 1 ? '' : 's'}`\n )\n } else {\n toggleBtn.setAttribute('title', 'Supaship Toolbar')\n toggleBtn.setAttribute('aria-label', 'Supaship Toolbar')\n }\n }\n\n // Update override count in header\n if (headerOverrideCount) {\n // Escape overrideCount to prevent any potential XSS (defense in depth)\n const escapedCount = this.escapeHtml(String(overrideCount))\n headerOverrideCount.innerHTML = `<span class=\"supaship-toolbar-overrides-label-count ${hasOverrides ? 'has-overrides' : ''}\">${escapedCount}</span> override${overrideCount === 1 ? '' : 's'}`\n }\n\n const features = Array.from(this.state.features).sort()\n\n // Filter features based on search query\n const filteredFeatures = features.filter(name =>\n name.toLowerCase().includes(this.state.searchQuery)\n )\n\n if (filteredFeatures.length === 0) {\n content.innerHTML = this.state.searchQuery\n ? '<div class=\"supaship-toolbar-empty\">No matching features found</div>'\n : `<div class=\"supaship-toolbar-empty\">${NO_FEATURES_MESSAGE}</div>`\n return\n }\n\n const htmlContent = filteredFeatures\n .map(featureName => {\n const hasOverride = featureName in this.state.overrides\n const currentValue = this.state.featureValues[featureName]\n const overrideValue = hasOverride ? this.state.overrides[featureName] : currentValue\n const isDisabled = !this.state.useLocalOverrides\n const itemClass = `supaship-feature-item ${isDisabled ? 'disabled' : ''}`\n\n // Check if the feature is boolean\n const isBoolean =\n typeof currentValue === 'boolean' || (hasOverride && typeof overrideValue === 'boolean')\n\n if (isBoolean) {\n // Render toggle switch for boolean values (single row layout)\n const isChecked = hasOverride ? overrideValue === true : currentValue === true\n return `\n <div class=\"${itemClass}\">\n <div class=\"supaship-feature-row\">\n <span class=\"supaship-feature-name\">\n ${this.escapeHtml(featureName)}\n ${hasOverride ? '<span class=\"supaship-feature-override-indicator\" title=\"Serving local override\"></span>' : ''}\n </span>\n <div class=\"supaship-feature-actions\">\n ${\n hasOverride\n ? `\n <button\n class=\"supaship-btn-icon\"\n data-feature=\"${this.escapeHtml(featureName)}\"\n data-action=\"remove\"\n title=\"Reset to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/>\n <path d=\"M3 3v5h5\"/>\n <path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/>\n <path d=\"M16 16h5v5\"/>\n </svg>\n </button>\n `\n : ''\n }\n <label class=\"supaship-toggle\">\n <input\n type=\"checkbox\"\n ${isChecked ? 'checked' : ''}\n data-feature=\"${this.escapeHtml(featureName)}\"\n data-type=\"boolean\"\n />\n <span class=\"supaship-toggle-slider\"></span>\n </label>\n </div>\n </div>\n </div>\n `\n } else {\n // Render textarea for non-boolean values\n const currentDisplayValue = hasOverride\n ? JSON.stringify(overrideValue)\n : currentValue !== undefined\n ? JSON.stringify(currentValue)\n : ''\n const escapedFeatureName = this.escapeHtml(featureName)\n const escapedCurrentDisplayValue = this.escapeHtml(currentDisplayValue)\n const escapedTextareaContent = hasOverride\n ? this.escapeHtml(JSON.stringify(overrideValue))\n : escapedCurrentDisplayValue\n\n return `\n <div class=\"${itemClass}\">\n <div class=\"supaship-feature-row\">\n <span class=\"supaship-feature-name\">\n ${escapedFeatureName}\n ${hasOverride ? '<span class=\"supaship-feature-override-indicator\" title=\"Serving local override\"></span>' : ''}\n </span>\n <div class=\"supaship-feature-actions\">\n ${\n hasOverride\n ? `\n <button\n class=\"supaship-btn-icon\"\n data-feature=\"${escapedFeatureName}\"\n data-action=\"remove\"\n title=\"Reset to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/>\n <path d=\"M3 3v5h5\"/>\n <path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/>\n <path d=\"M16 16h5v5\"/>\n </svg>\n </button>\n `\n : ''\n }\n <button\n class=\"supaship-btn supaship-btn-primary\"\n data-feature=\"${escapedFeatureName}\"\n data-action=\"set\"\n disabled>\n Override\n </button>\n </div>\n </div>\n <div class=\"supaship-feature-content\">\n <textarea\n class=\"supaship-feature-input\"\n placeholder=\"Override JSON value\"\n data-feature=\"${escapedFeatureName}\"\n data-original=\"${escapedCurrentDisplayValue}\"\n >${escapedTextareaContent}</textarea>\n </div>\n </div>\n `\n }\n })\n .join('')\n\n requestAnimationFrame(() => {\n // Set innerHTML - event listeners are handled via delegation in attachEventListeners()\n content.innerHTML = htmlContent\n\n // Update button states for textareas that already have values\n content.querySelectorAll('textarea[data-feature]').forEach(textarea => {\n const textareaElement = textarea as HTMLTextAreaElement\n const featureName = textareaElement.dataset.feature!\n const originalValue = textareaElement.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = textareaElement.value !== originalValue\n const hasContent = textareaElement.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n })\n })\n }\n\n private escapeHtml(text: string): string {\n const div = typeof document !== 'undefined' ? document.createElement('div') : null\n if (div) {\n div.textContent = text\n return div.innerHTML\n }\n return text.replace(/[&<>\"']/g, char => {\n const escapeMap: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n }\n return escapeMap[char]\n })\n }\n\n /**\n * Escapes special characters in CSS attribute selectors to prevent CSS injection\n * @param value The value to escape for use in CSS attribute selectors\n */\n private escapeCssSelector(value: string): string {\n // Escape special CSS selector characters: \", ', ], \\\n return value.replace(/[\"'\\\\\\]]/g, '\\\\$&')\n }\n}\n","export const DEFAULT_FEATURES_URL = 'https://edge.supaship.com/v1/features'\nexport const DEFAULT_EVENTS_URL = 'https://edge.supaship.com/v1/events'\n","import {\n SupaClientConfig,\n FeatureContext,\n FeatureValue,\n NetworkConfig,\n Features,\n FeaturesWithFallbacks,\n} from './types'\nimport { retry } from './utils'\nimport { SupaPlugin } from './plugins/types'\nimport { SupaToolbarPlugin } from './plugins/toolbar-plugin'\nimport { DEFAULT_FEATURES_URL, DEFAULT_EVENTS_URL } from './constants'\n\ntype RequiredRetryConfig = Required<NonNullable<NetworkConfig['retry']>>\ntype ResolvedNetworkConfig = {\n featuresAPIUrl: string\n eventsAPIUrl: string\n retry: RequiredRetryConfig\n requestTimeoutMs: number\n}\n\nexport class SupaClient<TFeatures extends FeaturesWithFallbacks> {\n private apiKey: string\n private environment: string\n private defaultContext?: FeatureContext\n private plugins: SupaPlugin[]\n private featureDefinitions: Features<TFeatures>\n private clientId: string\n\n private fetchImpl: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>\n private networkConfig: ResolvedNetworkConfig\n\n constructor(config: SupaClientConfig & { features: TFeatures }) {\n this.apiKey = config.apiKey\n this.environment = config.environment\n this.defaultContext = config.context\n this.featureDefinitions = config.features as Features<TFeatures>\n\n // Generate unique client ID\n this.clientId = this.generateClientId()\n\n this.networkConfig = {\n featuresAPIUrl: config.networkConfig?.featuresAPIUrl || DEFAULT_FEATURES_URL,\n eventsAPIUrl: config.networkConfig?.eventsAPIUrl || DEFAULT_EVENTS_URL,\n retry: {\n enabled: config.networkConfig?.retry?.enabled ?? true,\n maxAttempts: config.networkConfig?.retry?.maxAttempts ?? 3,\n backoff: config.networkConfig?.retry?.backoff ?? 1000,\n },\n requestTimeoutMs: config.networkConfig?.requestTimeoutMs ?? 10000,\n }\n\n // Prefer injected fetch, then global fetch if available\n const globalFetch: typeof fetch | undefined =\n typeof globalThis !== 'undefined'\n ? (globalThis as unknown as { fetch?: typeof fetch }).fetch\n : undefined\n if (config.networkConfig?.fetchFn) {\n this.fetchImpl = config.networkConfig.fetchFn\n } else if (typeof globalFetch === 'function') {\n this.fetchImpl = globalFetch.bind(globalThis)\n } else {\n throw new Error(\n 'No fetch implementation available. Provide fetchFn in config or use a runtime with global fetch (e.g., Node 18+, browsers).'\n )\n }\n\n // Initialize plugins with automatic toolbar plugin in browser\n this.plugins = this.initializePlugins(config)\n\n // Initialize plugins with available features and their fallback values\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onInit?.({\n clientId: this.clientId,\n availableFeatures: this.featureDefinitions,\n context: this.defaultContext,\n })\n )\n ).catch(console.error)\n }\n\n /**\n * Generate a unique client ID\n */\n private generateClientId(): string {\n return `supaship-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`\n }\n\n /**\n * Initialize plugins with automatic toolbar plugin in browser environments\n */\n private initializePlugins(config: SupaClientConfig & { features: TFeatures }): SupaPlugin[] {\n const plugins = config.plugins || []\n\n // Check if we're in a browser environment\n const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined'\n\n // If toolbar is explicitly disabled, don't add it\n if (config.toolbar === false) {\n return plugins\n }\n\n // If in browser and toolbar not disabled, add it automatically\n if (isBrowser) {\n // Check if user already added toolbar plugin manually\n const hasToolbarPlugin = plugins.some(p => p.name === 'toolbar-plugin')\n\n if (!hasToolbarPlugin) {\n // Add toolbar with user config or defaults\n const toolbarConfig = config.toolbar || { enabled: 'auto' }\n const toolbarPlugin = new SupaToolbarPlugin(toolbarConfig)\n return [toolbarPlugin, ...plugins]\n }\n }\n\n return plugins\n }\n\n /**\n * Updates the default context for the client\n * @param context - New context to merge with or replace the existing context\n * @param mergeWithExisting - Whether to merge with existing context (default: true)\n */\n updateContext(context: FeatureContext, mergeWithExisting: boolean = true): void {\n const oldContext = this.defaultContext\n\n if (mergeWithExisting && this.defaultContext) {\n this.defaultContext = { ...this.defaultContext, ...context }\n } else {\n this.defaultContext = context\n }\n\n // Notify plugins of context change\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onContextUpdate?.(oldContext, this.defaultContext!, 'updateContext')\n )\n ).catch(console.error)\n }\n\n /**\n * Gets the current default context\n */\n getContext(): FeatureContext | undefined {\n return this.defaultContext\n }\n\n /**\n * Gets the fallback value for a feature from its definition\n */\n getFeatureFallback<TKey extends keyof TFeatures>(featureName: TKey): Features<TFeatures>[TKey] {\n return this.featureDefinitions[featureName]\n }\n\n private getVariationValue(variation: FeatureValue, fallback: FeatureValue): FeatureValue {\n if (variation !== undefined && variation !== null) {\n return variation\n }\n\n return fallback ?? null\n }\n\n async getFeature<TKey extends keyof TFeatures>(\n featureName: TKey,\n options?: { context?: FeatureContext }\n ): Promise<Features<TFeatures>[TKey]> {\n const { context } = options ?? {}\n\n // Only merge context if it's defined and not null\n const mergedContext: FeatureContext | undefined =\n typeof context === 'object' && context !== null\n ? { ...(this.defaultContext ?? {}), ...context }\n : this.defaultContext\n\n try {\n const response = await this.getFeatures([featureName as string], {\n context: mergedContext,\n })\n\n // Get the specific feature value\n const value = response[featureName as string]\n return value as Features<TFeatures>[TKey]\n } catch (error) {\n // Run onError hooks\n await Promise.all(this.plugins.map(plugin => plugin.onError?.(error as Error, mergedContext)))\n\n // Use fallback feature value when API fails\n const fallbackValue = this.featureDefinitions[featureName]\n\n // Notify plugins that fallback was used\n await Promise.all(\n this.plugins.map(plugin =>\n plugin.onFallbackUsed?.(\n featureName as string,\n fallbackValue as FeatureValue,\n error as Error\n )\n )\n )\n return fallbackValue as Features<TFeatures>[TKey]\n }\n }\n\n async getFeatures<TKeys extends readonly (keyof TFeatures)[]>(\n featureNames: TKeys,\n options?: { context?: FeatureContext }\n ): Promise<{ [K in TKeys[number]]: Features<TFeatures>[K] }> {\n const { context: contextOverride } = options ?? {}\n\n // Only merge context if it's defined and not null\n const mergedContext: FeatureContext | undefined =\n typeof contextOverride === 'object' && contextOverride !== null\n ? { ...(this.defaultContext ?? {}), ...contextOverride }\n : this.defaultContext\n\n // Notify plugins of context update for this request\n if (contextOverride) {\n await Promise.all(\n this.plugins.map(plugin =>\n plugin.onContextUpdate?.(this.defaultContext, mergedContext!, 'request')\n )\n )\n }\n\n // Convert feature names to strings for API call\n const featureNamesArray = featureNames.map(name => name as string)\n\n try {\n // Run beforeGetFeatures hooks\n await Promise.all(\n this.plugins.map(plugin => plugin.beforeGetFeatures?.(featureNamesArray, mergedContext))\n )\n\n type FeaturesResponse = { features: Record<string, { variation: FeatureValue }> }\n const fetchFeatures = async (): Promise<Record<string, FeatureValue>> => {\n const url = this.networkConfig.featuresAPIUrl\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n }\n const body = JSON.stringify({\n environment: this.environment,\n features: featureNamesArray,\n context: mergedContext,\n })\n\n // Notify plugins before request\n await Promise.all(this.plugins.map(plugin => plugin.beforeRequest?.(url, body, headers)))\n\n const startTime = Date.now()\n // Support timeout via AbortController when available\n const AbortCtrl: typeof AbortController | undefined =\n typeof globalThis !== 'undefined'\n ? (globalThis as unknown as { AbortController?: typeof AbortController })\n .AbortController\n : undefined\n let controller: AbortController | undefined\n let timeoutId: ReturnType<typeof setTimeout> | undefined\n if (this.networkConfig.requestTimeoutMs && typeof AbortCtrl === 'function') {\n controller = new AbortCtrl()\n timeoutId = setTimeout(() => controller?.abort(), this.networkConfig.requestTimeoutMs)\n }\n let response: Response\n try {\n response = await this.fetchImpl(url, {\n method: 'POST',\n headers,\n body,\n signal: controller?.signal,\n })\n } finally {\n if (timeoutId) clearTimeout(timeoutId)\n }\n const duration = Date.now() - startTime\n\n // Notify plugins after response\n await Promise.all(\n this.plugins.map(plugin => plugin.afterResponse?.(response, { duration }))\n )\n\n if (!response.ok) {\n throw new Error(`Failed to fetch features: ${response.statusText}`)\n }\n\n const data = (await response.json()) as FeaturesResponse\n const result: Record<string, FeatureValue> = {}\n\n featureNamesArray.forEach(name => {\n const variation = data.features[name]?.variation\n result[name] = this.getVariationValue(\n variation,\n this.featureDefinitions[name as keyof TFeatures]\n )\n })\n\n return result\n }\n\n const result = this.networkConfig.retry.enabled\n ? await retry(\n fetchFeatures,\n this.networkConfig.retry.maxAttempts,\n this.networkConfig.retry.backoff,\n (attempt, error, willRetry) => {\n // Notify plugins of retry attempts\n Promise.all(\n this.plugins.map(plugin => plugin.onRetryAttempt?.(attempt, error, willRetry))\n ).catch(console.error)\n }\n )\n : await fetchFeatures()\n\n // Run afterGetFeatures hooks\n await Promise.all(\n this.plugins.map(plugin => plugin.afterGetFeatures?.(result, mergedContext))\n )\n\n // Return the fetched features\n return result as { [K in TKeys[number]]: Features<TFeatures>[K] }\n } catch (error) {\n // Run onError hooks\n await Promise.all(this.plugins.map(plugin => plugin.onError?.(error as Error, mergedContext)))\n\n // Create fallback result with requested feature names\n const fallbackResult: Record<string, FeatureValue> = {}\n\n featureNamesArray.forEach(featureName => {\n fallbackResult[featureName] = this.featureDefinitions[featureName as keyof TFeatures]\n\n // Notify plugins that fallback was used for each feature\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onFallbackUsed?.(\n featureName,\n this.featureDefinitions[featureName as keyof TFeatures],\n error as Error\n )\n )\n ).catch(console.error)\n })\n\n return fallbackResult as { [K in TKeys[number]]: Features<TFeatures>[K] }\n }\n }\n}\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["result"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supashiphq/javascript-sdk",
3
- "version": "0.7.16",
3
+ "version": "0.7.17",
4
4
  "description": "JavaScript SDK for Supaship",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",