@supashiphq/javascript-sdk 0.7.15 → 0.7.16
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/dist/index.js +28 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +28 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -195,6 +195,7 @@ var SupaToolbarPlugin = class {
|
|
|
195
195
|
const panelId = `supaship-toolbar-panel-${this.clientId}`;
|
|
196
196
|
const searchId = `supaship-search-input-${this.clientId}`;
|
|
197
197
|
const clearId = `supaship-clear-all-${this.clientId}`;
|
|
198
|
+
const closeId = `supaship-close-toolbar-${this.clientId}`;
|
|
198
199
|
const contentId = `supaship-toolbar-content-${this.clientId}`;
|
|
199
200
|
const badgeId = `supaship-toolbar-badge-${this.clientId}`;
|
|
200
201
|
const headerOverrideCountId = `supaship-header-override-count-${this.clientId}`;
|
|
@@ -204,7 +205,7 @@ var SupaToolbarPlugin = class {
|
|
|
204
205
|
<svg
|
|
205
206
|
xmlns="http://www.w3.org/2000/svg"
|
|
206
207
|
viewBox="0 0 256 256"
|
|
207
|
-
width="
|
|
208
|
+
width="32"
|
|
208
209
|
style="vertical-align: middle;">
|
|
209
210
|
<rect width='256' height='256' rx='128' fill='#e4f222'></rect>
|
|
210
211
|
<line
|
|
@@ -255,8 +256,22 @@ var SupaToolbarPlugin = class {
|
|
|
255
256
|
aria-label="Reset all overrides"
|
|
256
257
|
title="Reset all overrides to default"
|
|
257
258
|
>
|
|
258
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0
|
|
259
|
-
<path d="
|
|
259
|
+
<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">
|
|
260
|
+
<path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
|
|
261
|
+
<path d="M3 3v5h5"/>
|
|
262
|
+
<path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/>
|
|
263
|
+
<path d="M16 16h5v5"/>
|
|
264
|
+
</svg>
|
|
265
|
+
</button>
|
|
266
|
+
<button
|
|
267
|
+
class="supaship-header-btn"
|
|
268
|
+
id="${closeId}"
|
|
269
|
+
aria-label="Close toolbar"
|
|
270
|
+
title="Close toolbar"
|
|
271
|
+
>
|
|
272
|
+
<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">
|
|
273
|
+
<path d="M18 6 6 18"/>
|
|
274
|
+
<path d="m6 6 12 12"/>
|
|
260
275
|
</svg>
|
|
261
276
|
</button>
|
|
262
277
|
</div>
|
|
@@ -663,16 +678,21 @@ var SupaToolbarPlugin = class {
|
|
|
663
678
|
const toggleId = `supaship-toolbar-toggle-${this.clientId}`;
|
|
664
679
|
const panelId = `supaship-toolbar-panel-${this.clientId}`;
|
|
665
680
|
const clearId = `supaship-clear-all-${this.clientId}`;
|
|
681
|
+
const closeId = `supaship-close-toolbar-${this.clientId}`;
|
|
666
682
|
const searchId = `supaship-search-input-${this.clientId}`;
|
|
667
683
|
const contentId = `supaship-toolbar-content-${this.clientId}`;
|
|
668
684
|
const toggle = document.getElementById(toggleId);
|
|
669
685
|
const panel = document.getElementById(panelId);
|
|
670
686
|
const clearAll = document.getElementById(clearId);
|
|
687
|
+
const close = document.getElementById(closeId);
|
|
671
688
|
const searchInput = document.getElementById(searchId);
|
|
672
689
|
const content = document.getElementById(contentId);
|
|
673
690
|
toggle?.addEventListener("click", () => {
|
|
674
691
|
panel?.classList.toggle("open");
|
|
675
692
|
});
|
|
693
|
+
close?.addEventListener("click", () => {
|
|
694
|
+
panel?.classList.remove("open");
|
|
695
|
+
});
|
|
676
696
|
clearAll?.addEventListener("click", () => {
|
|
677
697
|
this.clearAllOverrides();
|
|
678
698
|
});
|
|
@@ -842,8 +862,11 @@ var SupaToolbarPlugin = class {
|
|
|
842
862
|
data-action="remove"
|
|
843
863
|
title="Reset to default"
|
|
844
864
|
>
|
|
845
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0
|
|
846
|
-
<path d="
|
|
865
|
+
<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">
|
|
866
|
+
<path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
|
|
867
|
+
<path d="M3 3v5h5"/>
|
|
868
|
+
<path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/>
|
|
869
|
+
<path d="M16 16h5v5"/>
|
|
847
870
|
</svg>
|
|
848
871
|
</button>
|
|
849
872
|
` : ""}
|
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 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=\"24\"\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 256 256\" width=\"18\" height=\"18\">\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 </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 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 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 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 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 <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 '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\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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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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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 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 '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\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"]}
|
package/dist/index.mjs
CHANGED
|
@@ -168,6 +168,7 @@ var SupaToolbarPlugin = class {
|
|
|
168
168
|
const panelId = `supaship-toolbar-panel-${this.clientId}`;
|
|
169
169
|
const searchId = `supaship-search-input-${this.clientId}`;
|
|
170
170
|
const clearId = `supaship-clear-all-${this.clientId}`;
|
|
171
|
+
const closeId = `supaship-close-toolbar-${this.clientId}`;
|
|
171
172
|
const contentId = `supaship-toolbar-content-${this.clientId}`;
|
|
172
173
|
const badgeId = `supaship-toolbar-badge-${this.clientId}`;
|
|
173
174
|
const headerOverrideCountId = `supaship-header-override-count-${this.clientId}`;
|
|
@@ -177,7 +178,7 @@ var SupaToolbarPlugin = class {
|
|
|
177
178
|
<svg
|
|
178
179
|
xmlns="http://www.w3.org/2000/svg"
|
|
179
180
|
viewBox="0 0 256 256"
|
|
180
|
-
width="
|
|
181
|
+
width="32"
|
|
181
182
|
style="vertical-align: middle;">
|
|
182
183
|
<rect width='256' height='256' rx='128' fill='#e4f222'></rect>
|
|
183
184
|
<line
|
|
@@ -228,8 +229,22 @@ var SupaToolbarPlugin = class {
|
|
|
228
229
|
aria-label="Reset all overrides"
|
|
229
230
|
title="Reset all overrides to default"
|
|
230
231
|
>
|
|
231
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0
|
|
232
|
-
<path d="
|
|
232
|
+
<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">
|
|
233
|
+
<path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
|
|
234
|
+
<path d="M3 3v5h5"/>
|
|
235
|
+
<path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/>
|
|
236
|
+
<path d="M16 16h5v5"/>
|
|
237
|
+
</svg>
|
|
238
|
+
</button>
|
|
239
|
+
<button
|
|
240
|
+
class="supaship-header-btn"
|
|
241
|
+
id="${closeId}"
|
|
242
|
+
aria-label="Close toolbar"
|
|
243
|
+
title="Close toolbar"
|
|
244
|
+
>
|
|
245
|
+
<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">
|
|
246
|
+
<path d="M18 6 6 18"/>
|
|
247
|
+
<path d="m6 6 12 12"/>
|
|
233
248
|
</svg>
|
|
234
249
|
</button>
|
|
235
250
|
</div>
|
|
@@ -636,16 +651,21 @@ var SupaToolbarPlugin = class {
|
|
|
636
651
|
const toggleId = `supaship-toolbar-toggle-${this.clientId}`;
|
|
637
652
|
const panelId = `supaship-toolbar-panel-${this.clientId}`;
|
|
638
653
|
const clearId = `supaship-clear-all-${this.clientId}`;
|
|
654
|
+
const closeId = `supaship-close-toolbar-${this.clientId}`;
|
|
639
655
|
const searchId = `supaship-search-input-${this.clientId}`;
|
|
640
656
|
const contentId = `supaship-toolbar-content-${this.clientId}`;
|
|
641
657
|
const toggle = document.getElementById(toggleId);
|
|
642
658
|
const panel = document.getElementById(panelId);
|
|
643
659
|
const clearAll = document.getElementById(clearId);
|
|
660
|
+
const close = document.getElementById(closeId);
|
|
644
661
|
const searchInput = document.getElementById(searchId);
|
|
645
662
|
const content = document.getElementById(contentId);
|
|
646
663
|
toggle?.addEventListener("click", () => {
|
|
647
664
|
panel?.classList.toggle("open");
|
|
648
665
|
});
|
|
666
|
+
close?.addEventListener("click", () => {
|
|
667
|
+
panel?.classList.remove("open");
|
|
668
|
+
});
|
|
649
669
|
clearAll?.addEventListener("click", () => {
|
|
650
670
|
this.clearAllOverrides();
|
|
651
671
|
});
|
|
@@ -815,8 +835,11 @@ var SupaToolbarPlugin = class {
|
|
|
815
835
|
data-action="remove"
|
|
816
836
|
title="Reset to default"
|
|
817
837
|
>
|
|
818
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0
|
|
819
|
-
<path d="
|
|
838
|
+
<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">
|
|
839
|
+
<path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
|
|
840
|
+
<path d="M3 3v5h5"/>
|
|
841
|
+
<path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/>
|
|
842
|
+
<path d="M16 16h5v5"/>
|
|
820
843
|
</svg>
|
|
821
844
|
</button>
|
|
822
845
|
` : ""}
|
package/dist/index.mjs.map
CHANGED
|
@@ -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 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=\"24\"\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 256 256\" width=\"18\" height=\"18\">\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 </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 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 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 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 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 <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 '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\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/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 '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\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"]}
|