@supashiphq/javascript-sdk 0.7.15 → 0.7.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -33,7 +33,7 @@ const client = new SupaClient({
33
33
  environment: 'production',
34
34
  features,
35
35
  context: {
36
- userID: '123',
36
+ userId: '123',
37
37
  email: 'user@example.com',
38
38
  plan: 'premium',
39
39
  },
@@ -197,7 +197,7 @@ const theme = await client.getFeature('theme-config')
197
197
 
198
198
  // With context override
199
199
  const darkMode = await client.getFeature('dark-mode', {
200
- context: { userID: '123', plan: 'premium' },
200
+ context: { userId: '123', plan: 'premium' },
201
201
  })
202
202
  ```
203
203
 
@@ -239,7 +239,7 @@ const results = await client.getFeatures(['new-ui', 'premium-content'])
239
239
 
240
240
  // With context override
241
241
  const results = await client.getFeatures(['new-ui', 'beta-mode'], {
242
- context: { userID: '123', plan: 'premium' },
242
+ context: { userId: '123', plan: 'premium' },
243
243
  })
244
244
  ```
245
245
 
@@ -260,10 +260,10 @@ updateContext(context: FeatureContext, mergeWithExisting?: boolean): void
260
260
 
261
261
  ```typescript
262
262
  // Merge with existing context
263
- client.updateContext({ userID: '456' })
263
+ client.updateContext({ userId: '456' })
264
264
 
265
265
  // Replace entire context
266
- client.updateContext({ userID: '456', newField: 'value' }, false)
266
+ client.updateContext({ userId: '456', newField: 'value' }, false)
267
267
  ```
268
268
 
269
269
  ##### getContext()
@@ -303,7 +303,7 @@ interface FeatureContext {
303
303
 
304
304
  Common context properties:
305
305
 
306
- - `userID`: User identifier
306
+ - `userId`: User identifier
307
307
  - `email`: User email
308
308
  - `plan`: Membership plan (e.g., 'premium', 'free')
309
309
  - `version`: Application version (e.g., 1.0.0)
@@ -386,7 +386,7 @@ const client = new SupaClient({
386
386
  environment: 'production',
387
387
  features,
388
388
  context: {
389
- userID: user.id,
389
+ userId: user.id,
390
390
  email: user.email,
391
391
  plan: user.subscriptionPlan,
392
392
  version: process.env.APP_VERSION!,
@@ -396,7 +396,7 @@ const client = new SupaClient({
396
396
  // Update context when user state changes
397
397
  function onUserLogin(user) {
398
398
  client.updateContext({
399
- userID: user.id,
399
+ userId: user.id,
400
400
  email: user.email,
401
401
  plan: user.plan,
402
402
  })
@@ -519,7 +519,7 @@ app.get('/api/config', async (req, res) => {
519
519
  app.get('/api/user-features/:userId', async (req, res) => {
520
520
  const results = await featureClient.getFeatures(['new-api', 'cache-enabled'], {
521
521
  context: {
522
- userID: req.params.userId,
522
+ userId: req.params.userId,
523
523
  plan: req.user.plan,
524
524
  region: req.headers['cloudfront-viewer-country'],
525
525
  },
@@ -604,7 +604,7 @@ export class FeatureFlagService {
604
604
  environment: 'production',
605
605
  features,
606
606
  context: {
607
- userID: this.getCurrentUserId(),
607
+ userId: this.getCurrentUserId(),
608
608
  version: environment.version,
609
609
  },
610
610
  })
@@ -623,7 +623,7 @@ export class FeatureFlagService {
623
623
  }
624
624
 
625
625
  private getCurrentUserId(): string {
626
- return localStorage.getItem('userID') || 'anonymous'
626
+ return localStorage.getItem('userId') || 'anonymous'
627
627
  }
628
628
  }
629
629
 
package/dist/index.js CHANGED
@@ -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="24"
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 256 256" width="18" height="18">
259
- <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"/>
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>
@@ -309,7 +324,6 @@ var SupaToolbarPlugin = class {
309
324
  width: 36px;
310
325
  height: 36px;
311
326
  border-radius: 100%;
312
- background: #000;
313
327
  border: none;
314
328
  color: white;
315
329
  cursor: pointer;
@@ -663,16 +677,21 @@ var SupaToolbarPlugin = class {
663
677
  const toggleId = `supaship-toolbar-toggle-${this.clientId}`;
664
678
  const panelId = `supaship-toolbar-panel-${this.clientId}`;
665
679
  const clearId = `supaship-clear-all-${this.clientId}`;
680
+ const closeId = `supaship-close-toolbar-${this.clientId}`;
666
681
  const searchId = `supaship-search-input-${this.clientId}`;
667
682
  const contentId = `supaship-toolbar-content-${this.clientId}`;
668
683
  const toggle = document.getElementById(toggleId);
669
684
  const panel = document.getElementById(panelId);
670
685
  const clearAll = document.getElementById(clearId);
686
+ const close = document.getElementById(closeId);
671
687
  const searchInput = document.getElementById(searchId);
672
688
  const content = document.getElementById(contentId);
673
689
  toggle?.addEventListener("click", () => {
674
690
  panel?.classList.toggle("open");
675
691
  });
692
+ close?.addEventListener("click", () => {
693
+ panel?.classList.remove("open");
694
+ });
676
695
  clearAll?.addEventListener("click", () => {
677
696
  this.clearAllOverrides();
678
697
  });
@@ -842,8 +861,11 @@ var SupaToolbarPlugin = class {
842
861
  data-action="remove"
843
862
  title="Reset to default"
844
863
  >
845
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="14" height="14">
846
- <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"/>
864
+ <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">
865
+ <path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
866
+ <path d="M3 3v5h5"/>
867
+ <path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/>
868
+ <path d="M16 16h5v5"/>
847
869
  </svg>
848
870
  </button>
849
871
  ` : ""}
@@ -880,8 +902,11 @@ var SupaToolbarPlugin = class {
880
902
  data-action="remove"
881
903
  title="Reset to default"
882
904
  >
883
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="14" height="14">
884
- <path d="M240,56v48a8,8,0,0,1-8,8H184a8,8,0,0,1,0-16H211.4L184.81,71.64l-.25-.24a80,80,0,1,0-1.67,114.78,8,8,0,0,1,11,11.63A95.44,95.44,0,0,1,128,224h-1.32A96,96,0,1,1,195.75,60L224,85.8V56a8,8,0,1,1,16,0Z" fill="currentColor"/>
905
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
906
+ <path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
907
+ <path d="M3 3v5h5"/>
908
+ <path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/>
909
+ <path d="M16 16h5v5"/>
885
910
  </svg>
886
911
  </button>
887
912
  ` : ""}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/plugins/toolbar-plugin.ts","../src/constants.ts","../src/client.ts"],"sourcesContent":["export type * from './types'\nexport { SupaClient } from './client'\nexport type { SupaPlugin, SupaPluginConfig } from './plugins/types'\nexport { SupaToolbarPlugin as ToolbarPlugin } from './plugins/toolbar-plugin'\nexport type {\n SupaToolbarPluginConfig,\n SupaToolbarPosition,\n SupaToolbarOverrideChange,\n SupaToolbarOverrideChangeCallback,\n} from './plugins/toolbar-plugin'\n// export { LoggingPlugin } from \"./plugins/logging-plugin\";\n// export { CachingPlugin } from \"./plugins/caching-plugin\";\n// export { AnalyticsPlugin } from \"./plugins/analytics-plugin\";\n// export { LocalDevPlugin } from \"./plugins/local-dev-plugin\";\n","export function sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nexport async function retry<T>(\n fn: () => Promise<T>,\n maxAttempts: number = 3,\n backoff: number = 1000,\n onRetry?: (attempt: number, error: Error, willRetry: boolean) => void\n): Promise<T> {\n let lastError: Error\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn()\n } catch (error) {\n lastError = error as Error\n const willRetry = attempt < maxAttempts\n\n if (onRetry) {\n onRetry(attempt, lastError, willRetry)\n }\n\n if (!willRetry) break\n await sleep(backoff * Math.pow(2, attempt - 1))\n }\n }\n\n throw lastError!\n}\n","import { SupaPlugin, SupaPluginConfig } from './types'\nimport { FeatureContext, FeatureValue } from '../types'\n\nexport interface SupaToolbarPosition {\n placement?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'\n offset?: { x: string; y: string }\n}\n\nexport type SupaToolbarOverrideChange = {\n feature: string\n value: FeatureValue\n}\n\nexport type SupaToolbarOverrideChangeCallback = (\n featureOverride: SupaToolbarOverrideChange,\n allOverrides: Record<string, FeatureValue>\n) => void\n\nexport interface SupaToolbarPluginConfig extends Omit<SupaPluginConfig, 'enabled'> {\n enabled?: boolean | 'auto' // auto means show only on localhost\n position?: SupaToolbarPosition\n onOverrideChange?: SupaToolbarOverrideChangeCallback\n}\n\ninterface SupaToolbarState {\n overrides: Record<string, FeatureValue>\n features: Set<string>\n featureValues: Record<string, FeatureValue>\n context?: FeatureContext\n searchQuery: string\n useLocalOverrides: boolean\n}\n\nconst DEFAULT_STORAGE_KEY = 'supaship-feature-overrides'\n\nconst NO_FEATURES_MESSAGE = `No feature flags configured in the client.`\n\n/**\n * Toolbar plugin for local feature flag testing\n * Provides a visual interface to override feature flags during development\n */\nexport class SupaToolbarPlugin implements SupaPlugin {\n name = 'toolbar-plugin'\n private config: {\n enabled: boolean | 'auto'\n position: Required<SupaToolbarPosition>\n onOverrideChange?: SupaToolbarOverrideChangeCallback\n }\n private state: SupaToolbarState\n private clientId?: string\n private storageKey: string = DEFAULT_STORAGE_KEY\n\n constructor(config: SupaToolbarPluginConfig = {}) {\n this.config = {\n enabled: config.enabled ?? 'auto',\n position: {\n placement: config.position?.placement ?? 'bottom-right',\n offset: config.position?.offset ?? { x: '1rem', y: '1rem' },\n },\n onOverrideChange: config.onOverrideChange,\n }\n\n this.state = {\n overrides: {},\n features: new Set(),\n featureValues: {},\n searchQuery: '',\n useLocalOverrides: true,\n }\n }\n\n cleanup(): void {\n this.removeToolbar()\n }\n\n private shouldShowToolbar(): boolean {\n if (this.config.enabled === true) return true\n if (this.config.enabled === false) return false\n\n // Auto mode: show only on localhost\n if (typeof window !== 'undefined') {\n return (\n window.location.hostname === 'localhost' ||\n window.location.hostname === '127.0.0.1' ||\n window.location.hostname === '' ||\n window.location.hostname.endsWith('.local') ||\n window.location.hostname.endsWith('.localhost')\n )\n }\n return false\n }\n\n onInit(params: {\n availableFeatures: Record<string, FeatureValue>\n context?: FeatureContext\n clientId: string\n }): void {\n const { availableFeatures, context, clientId } = params\n\n // Set client ID for DOM element IDs\n this.clientId = clientId\n\n // Use shared storage key (not client-specific) to persist across refreshes\n this.storageKey = DEFAULT_STORAGE_KEY\n\n // Load overrides from shared storage\n this.state.overrides = this.loadOverrides()\n\n // Initialize with all available features and their fallback values from config\n this.state.features = new Set(Object.keys(availableFeatures))\n this.state.featureValues = { ...availableFeatures }\n this.state.context = context\n\n // Inject toolbar if conditions are met\n if (this.shouldShowToolbar()) {\n this.injectToolbar()\n }\n\n // Update toolbar UI if it exists\n this.updateToolbarUI()\n }\n\n async beforeGetFeatures(_featureNames: string[], context?: FeatureContext): Promise<void> {\n // Update context if it changed\n this.state.context = context\n\n // Load overrides from shared storage\n this.state.overrides = this.loadOverrides()\n\n // Update toolbar UI if it exists\n this.updateToolbarUI()\n }\n\n async afterGetFeatures(\n results: Record<string, FeatureValue>,\n context?: FeatureContext\n ): Promise<void> {\n // Update feature values with fetched results (this replaces config fallback values)\n Object.keys(results).forEach(name => {\n this.state.featureValues[name] = results[name]\n })\n\n // Apply overrides to results only if local overrides are enabled\n if (this.state.useLocalOverrides) {\n Object.keys(this.state.overrides).forEach(featureName => {\n if (featureName in results) {\n results[featureName] = this.state.overrides[featureName]\n }\n })\n }\n\n // Track features and update UI\n Object.keys(results).forEach(name => this.state.features.add(name))\n this.state.context = context\n this.updateToolbarUI()\n }\n\n private loadOverrides(): Record<string, FeatureValue> {\n if (typeof window === 'undefined' || !window.localStorage) {\n return {}\n }\n\n try {\n const stored = window.localStorage.getItem(this.storageKey)\n return stored ? JSON.parse(stored) : {}\n } catch {\n return {}\n }\n }\n\n private saveOverrides(\n feature?: string,\n value?: FeatureValue,\n allOverrides?: Record<string, FeatureValue>\n ): void {\n if (typeof window === 'undefined' || !window.localStorage) {\n return\n }\n\n try {\n window.localStorage.setItem(this.storageKey, JSON.stringify(allOverrides))\n this.config.onOverrideChange?.(\n { feature: feature ?? '', value: value ?? null },\n allOverrides ?? {}\n )\n } catch (error) {\n console.error('Supaship: Failed to save feature overrides:', error)\n }\n }\n\n public setOverride(featureName: string, value: FeatureValue): void {\n this.state.overrides[featureName] = value\n this.saveOverrides(featureName, value, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public removeOverride(featureName: string): void {\n delete this.state.overrides[featureName]\n this.saveOverrides(featureName, null, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public clearAllOverrides(): void {\n this.state.overrides = {}\n this.saveOverrides('', null, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public getOverrides(): Record<string, FeatureValue> {\n return { ...this.state.overrides }\n }\n\n private injectToolbar(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return\n }\n\n // Check if toolbar with this client ID already exists\n const toolbarId = `supaship-toolbar-${this.clientId}`\n if (document.getElementById(toolbarId)) {\n return\n }\n\n // Create toolbar container\n const toolbar = document.createElement('div')\n toolbar.id = toolbarId\n toolbar.setAttribute('data-supaship-client', this.clientId || '')\n toolbar.innerHTML = this.getToolbarHTML()\n\n // Add styles\n this.injectStyles()\n\n // Add to DOM\n document.body.appendChild(toolbar)\n\n // Add event listeners\n this.attachEventListeners()\n }\n\n private removeToolbar(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const toolbar = document.getElementById(`supaship-toolbar-${this.clientId}`)\n if (toolbar) {\n toolbar.remove()\n }\n\n const styles = document.getElementById('supaship-toolbar-styles')\n if (styles) {\n styles.remove()\n }\n }\n\n private getToolbarHTML(): string {\n const { placement, offset } = this.config.position\n const positionClass = `supaship-toolbar-${placement}`\n const offsetX = offset?.x ?? '1rem'\n const offsetY = offset?.y ?? '1rem'\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n const panelId = `supaship-toolbar-panel-${this.clientId}`\n const searchId = `supaship-search-input-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const 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 '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n }\n return escapeMap[char]\n })\n }\n\n /**\n * Escapes special characters in CSS attribute selectors to prevent CSS injection\n * @param value The value to escape for use in CSS attribute selectors\n */\n private escapeCssSelector(value: string): string {\n // Escape special CSS selector characters: \", ', ], \\\n return value.replace(/[\"'\\\\\\]]/g, '\\\\$&')\n }\n}\n","export const DEFAULT_FEATURES_URL = 'https://edge.supaship.com/v1/features'\nexport const DEFAULT_EVENTS_URL = 'https://edge.supaship.com/v1/events'\n","import {\n SupaClientConfig,\n FeatureContext,\n FeatureValue,\n NetworkConfig,\n Features,\n FeaturesWithFallbacks,\n} from './types'\nimport { retry } from './utils'\nimport { SupaPlugin } from './plugins/types'\nimport { SupaToolbarPlugin } from './plugins/toolbar-plugin'\nimport { DEFAULT_FEATURES_URL, DEFAULT_EVENTS_URL } from './constants'\n\ntype RequiredRetryConfig = Required<NonNullable<NetworkConfig['retry']>>\ntype ResolvedNetworkConfig = {\n featuresAPIUrl: string\n eventsAPIUrl: string\n retry: RequiredRetryConfig\n requestTimeoutMs: number\n}\n\nexport class SupaClient<TFeatures extends FeaturesWithFallbacks> {\n private apiKey: string\n private environment: string\n private defaultContext?: FeatureContext\n private plugins: SupaPlugin[]\n private featureDefinitions: Features<TFeatures>\n private clientId: string\n\n private fetchImpl: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>\n private networkConfig: ResolvedNetworkConfig\n\n constructor(config: SupaClientConfig & { features: TFeatures }) {\n this.apiKey = config.apiKey\n this.environment = config.environment\n this.defaultContext = config.context\n this.featureDefinitions = config.features as Features<TFeatures>\n\n // Generate unique client ID\n this.clientId = this.generateClientId()\n\n this.networkConfig = {\n featuresAPIUrl: config.networkConfig?.featuresAPIUrl || DEFAULT_FEATURES_URL,\n eventsAPIUrl: config.networkConfig?.eventsAPIUrl || DEFAULT_EVENTS_URL,\n retry: {\n enabled: config.networkConfig?.retry?.enabled ?? true,\n maxAttempts: config.networkConfig?.retry?.maxAttempts ?? 3,\n backoff: config.networkConfig?.retry?.backoff ?? 1000,\n },\n requestTimeoutMs: config.networkConfig?.requestTimeoutMs ?? 10000,\n }\n\n // Prefer injected fetch, then global fetch if available\n const globalFetch: typeof fetch | undefined =\n typeof globalThis !== 'undefined'\n ? (globalThis as unknown as { fetch?: typeof fetch }).fetch\n : undefined\n if (config.networkConfig?.fetchFn) {\n this.fetchImpl = config.networkConfig.fetchFn\n } else if (typeof globalFetch === 'function') {\n this.fetchImpl = globalFetch.bind(globalThis)\n } else {\n throw new Error(\n 'No fetch implementation available. Provide fetchFn in config or use a runtime with global fetch (e.g., Node 18+, browsers).'\n )\n }\n\n // Initialize plugins with automatic toolbar plugin in browser\n this.plugins = this.initializePlugins(config)\n\n // Initialize plugins with available features and their fallback values\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onInit?.({\n clientId: this.clientId,\n availableFeatures: this.featureDefinitions,\n context: this.defaultContext,\n })\n )\n ).catch(console.error)\n }\n\n /**\n * Generate a unique client ID\n */\n private generateClientId(): string {\n return `supaship-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`\n }\n\n /**\n * Initialize plugins with automatic toolbar plugin in browser environments\n */\n private initializePlugins(config: SupaClientConfig & { features: TFeatures }): SupaPlugin[] {\n const plugins = config.plugins || []\n\n // Check if we're in a browser environment\n const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined'\n\n // If toolbar is explicitly disabled, don't add it\n if (config.toolbar === false) {\n return plugins\n }\n\n // If in browser and toolbar not disabled, add it automatically\n if (isBrowser) {\n // Check if user already added toolbar plugin manually\n const hasToolbarPlugin = plugins.some(p => p.name === 'toolbar-plugin')\n\n if (!hasToolbarPlugin) {\n // Add toolbar with user config or defaults\n const toolbarConfig = config.toolbar || { enabled: 'auto' }\n const toolbarPlugin = new SupaToolbarPlugin(toolbarConfig)\n return [toolbarPlugin, ...plugins]\n }\n }\n\n return plugins\n }\n\n /**\n * Updates the default context for the client\n * @param context - New context to merge with or replace the existing context\n * @param mergeWithExisting - Whether to merge with existing context (default: true)\n */\n updateContext(context: FeatureContext, mergeWithExisting: boolean = true): void {\n const oldContext = this.defaultContext\n\n if (mergeWithExisting && this.defaultContext) {\n this.defaultContext = { ...this.defaultContext, ...context }\n } else {\n this.defaultContext = context\n }\n\n // Notify plugins of context change\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onContextUpdate?.(oldContext, this.defaultContext!, 'updateContext')\n )\n ).catch(console.error)\n }\n\n /**\n * Gets the current default context\n */\n getContext(): FeatureContext | undefined {\n return this.defaultContext\n }\n\n /**\n * Gets the fallback value for a feature from its definition\n */\n getFeatureFallback<TKey extends keyof TFeatures>(featureName: TKey): Features<TFeatures>[TKey] {\n return this.featureDefinitions[featureName]\n }\n\n private getVariationValue(variation: FeatureValue, fallback: FeatureValue): FeatureValue {\n if (variation !== undefined && variation !== null) {\n return variation\n }\n\n return fallback ?? null\n }\n\n async getFeature<TKey extends keyof TFeatures>(\n featureName: TKey,\n options?: { context?: FeatureContext }\n ): Promise<Features<TFeatures>[TKey]> {\n const { context } = options ?? {}\n\n // Only merge context if it's defined and not null\n const mergedContext: FeatureContext | undefined =\n typeof context === 'object' && context !== null\n ? { ...(this.defaultContext ?? {}), ...context }\n : this.defaultContext\n\n try {\n const response = await this.getFeatures([featureName as string], {\n context: mergedContext,\n })\n\n // Get the specific feature value\n const value = response[featureName as string]\n return value as Features<TFeatures>[TKey]\n } catch (error) {\n // Run onError hooks\n await Promise.all(this.plugins.map(plugin => plugin.onError?.(error as Error, mergedContext)))\n\n // Use fallback feature value when API fails\n const fallbackValue = this.featureDefinitions[featureName]\n\n // Notify plugins that fallback was used\n await Promise.all(\n this.plugins.map(plugin =>\n plugin.onFallbackUsed?.(\n featureName as string,\n fallbackValue as FeatureValue,\n error as Error\n )\n )\n )\n return fallbackValue as Features<TFeatures>[TKey]\n }\n }\n\n async getFeatures<TKeys extends readonly (keyof TFeatures)[]>(\n featureNames: TKeys,\n options?: { context?: FeatureContext }\n ): Promise<{ [K in TKeys[number]]: Features<TFeatures>[K] }> {\n const { context: contextOverride } = options ?? {}\n\n // Only merge context if it's defined and not null\n const mergedContext: FeatureContext | undefined =\n typeof contextOverride === 'object' && contextOverride !== null\n ? { ...(this.defaultContext ?? {}), ...contextOverride }\n : this.defaultContext\n\n // Notify plugins of context update for this request\n if (contextOverride) {\n await Promise.all(\n this.plugins.map(plugin =>\n plugin.onContextUpdate?.(this.defaultContext, mergedContext!, 'request')\n )\n )\n }\n\n // Convert feature names to strings for API call\n const featureNamesArray = featureNames.map(name => name as string)\n\n try {\n // Run beforeGetFeatures hooks\n await Promise.all(\n this.plugins.map(plugin => plugin.beforeGetFeatures?.(featureNamesArray, mergedContext))\n )\n\n type FeaturesResponse = { features: Record<string, { variation: FeatureValue }> }\n const fetchFeatures = async (): Promise<Record<string, FeatureValue>> => {\n const url = this.networkConfig.featuresAPIUrl\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n }\n const body = JSON.stringify({\n environment: this.environment,\n features: featureNamesArray,\n context: mergedContext,\n })\n\n // Notify plugins before request\n await Promise.all(this.plugins.map(plugin => plugin.beforeRequest?.(url, body, headers)))\n\n const startTime = Date.now()\n // Support timeout via AbortController when available\n const AbortCtrl: typeof AbortController | undefined =\n typeof globalThis !== 'undefined'\n ? (globalThis as unknown as { AbortController?: typeof AbortController })\n .AbortController\n : undefined\n let controller: AbortController | undefined\n let timeoutId: ReturnType<typeof setTimeout> | undefined\n if (this.networkConfig.requestTimeoutMs && typeof AbortCtrl === 'function') {\n controller = new AbortCtrl()\n timeoutId = setTimeout(() => controller?.abort(), this.networkConfig.requestTimeoutMs)\n }\n let response: Response\n try {\n response = await this.fetchImpl(url, {\n method: 'POST',\n headers,\n body,\n signal: controller?.signal,\n })\n } finally {\n if (timeoutId) clearTimeout(timeoutId)\n }\n const duration = Date.now() - startTime\n\n // Notify plugins after response\n await Promise.all(\n this.plugins.map(plugin => plugin.afterResponse?.(response, { duration }))\n )\n\n if (!response.ok) {\n throw new Error(`Failed to fetch features: ${response.statusText}`)\n }\n\n const data = (await response.json()) as FeaturesResponse\n const result: Record<string, FeatureValue> = {}\n\n featureNamesArray.forEach(name => {\n const variation = data.features[name]?.variation\n result[name] = this.getVariationValue(\n variation,\n this.featureDefinitions[name as keyof TFeatures]\n )\n })\n\n return result\n }\n\n const result = this.networkConfig.retry.enabled\n ? await retry(\n fetchFeatures,\n this.networkConfig.retry.maxAttempts,\n this.networkConfig.retry.backoff,\n (attempt, error, willRetry) => {\n // Notify plugins of retry attempts\n Promise.all(\n this.plugins.map(plugin => plugin.onRetryAttempt?.(attempt, error, willRetry))\n ).catch(console.error)\n }\n )\n : await fetchFeatures()\n\n // Run afterGetFeatures hooks\n await Promise.all(\n this.plugins.map(plugin => plugin.afterGetFeatures?.(result, mergedContext))\n )\n\n // Return the fetched features\n return result as { [K in TKeys[number]]: Features<TFeatures>[K] }\n } catch (error) {\n // Run onError hooks\n await Promise.all(this.plugins.map(plugin => plugin.onError?.(error as Error, mergedContext)))\n\n // Create fallback result with requested feature names\n const fallbackResult: Record<string, FeatureValue> = {}\n\n featureNamesArray.forEach(featureName => {\n fallbackResult[featureName] = this.featureDefinitions[featureName as keyof TFeatures]\n\n // Notify plugins that fallback was used for each feature\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onFallbackUsed?.(\n featureName,\n this.featureDefinitions[featureName as keyof TFeatures],\n error as Error\n )\n )\n ).catch(console.error)\n })\n\n return fallbackResult as { [K in TKeys[number]]: Features<TFeatures>[K] }\n }\n }\n}\n"],"mappings":"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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0XrB,aAAS,KAAK,YAAY,MAAM;AAAA,EAClC;AAAA,EAEQ,uBAA6B;AACnC,QAAI,OAAO,aAAa,aAAa;AACnC;AAAA,IACF;AAEA,UAAM,WAAW,2BAA2B,KAAK,QAAQ;AACzD,UAAM,UAAU,0BAA0B,KAAK,QAAQ;AACvD,UAAM,UAAU,sBAAsB,KAAK,QAAQ;AACnD,UAAM,WAAW,yBAAyB,KAAK,QAAQ;AACvD,UAAM,YAAY,4BAA4B,KAAK,QAAQ;AAE3D,UAAM,SAAS,SAAS,eAAe,QAAQ;AAC/C,UAAM,QAAQ,SAAS,eAAe,OAAO;AAC7C,UAAM,WAAW,SAAS,eAAe,OAAO;AAChD,UAAM,cAAc,SAAS,eAAe,QAAQ;AACpD,UAAM,UAAU,SAAS,eAAe,SAAS;AAEjD,YAAQ,iBAAiB,SAAS,MAAM;AACtC,aAAO,UAAU,OAAO,MAAM;AAAA,IAChC,CAAC;AAED,cAAU,iBAAiB,SAAS,MAAM;AACxC,WAAK,kBAAkB;AAAA,IACzB,CAAC;AAED,iBAAa,iBAAiB,SAAS,OAAK;AAC1C,WAAK,MAAM,cAAe,EAAE,OAA4B,MAAM,YAAY;AAC1E,WAAK,gBAAgB;AAAA,IACvB,CAAC;AAGD,QAAI,SAAS;AAEX,cAAQ,iBAAiB,SAAS,CAAC,MAAa;AAC9C,cAAM,SAAS,EAAE;AACjB,cAAM,gBAAgB,OAAO,QAAQ,qBAAqB;AAC1D,YAAI,CAAC,cAAe;AAEpB,UAAE,eAAe;AACjB,UAAE,gBAAgB;AAElB,cAAM,cAAc,cAAc,QAAQ;AAC1C,cAAM,SAAS,cAAc,QAAQ;AAErC,YAAI,WAAW,UAAU;AACvB,eAAK,eAAe,WAAW;AAAA,QACjC,WAAW,WAAW,OAAO;AAC3B,gBAAM,WAAW,QAAQ;AAAA,YACvB,0BAA0B,KAAK,kBAAkB,WAAW,CAAC;AAAA,UAC/D;AACA,cAAI,YAAY,SAAS,MAAM,KAAK,GAAG;AACrC,gBAAI;AACF,oBAAM,QAAQ,KAAK,MAAM,SAAS,KAAK;AACvC,mBAAK,YAAY,aAAa,KAAK;AAAA,YACrC,QAAQ;AAEN,mBAAK,YAAY,aAAa,EAAE,OAAO,SAAS,MAAM,CAAC;AAAA,YACzD;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAGD,cAAQ,iBAAiB,UAAU,CAAC,MAAa;AAC/C,cAAM,SAAS,EAAE;AACjB,YAAI,OAAO,SAAS,cAAc,OAAO,QAAQ,SAAS,WAAW;AACnE,gBAAM,cAAc,OAAO,QAAQ;AACnC,gBAAM,WAAW,OAAO;AACxB,eAAK,YAAY,aAAa,QAAQ;AAAA,QACxC;AAAA,MACF,CAAC;AAGD,cAAQ,iBAAiB,SAAS,CAAC,MAAa;AAC9C,cAAM,SAAS,EAAE;AACjB,YAAI,OAAO,YAAY,cAAc,OAAO,QAAQ,SAAS;AAC3D,gBAAM,cAAc,OAAO,QAAQ;AACnC,gBAAM,gBAAgB,OAAO,QAAQ,YAAY;AACjD,gBAAM,cAAc,QAAQ;AAAA,YAC1B,2CAA2C,KAAK,kBAAkB,WAAW,CAAC;AAAA,UAChF;AAEA,cAAI,aAAa;AACf,kBAAM,aAAa,OAAO,UAAU;AACpC,kBAAM,aAAa,OAAO,MAAM,KAAK,EAAE,SAAS;AAChD,wBAAY,WAAW,CAAC,cAAc,CAAC;AAAA,UACzC;AAAA,QACF;AAAA,MACF,CAAC;AAGD,cAAQ,iBAAiB,SAAS,CAAC,MAAa;AAC9C,cAAM,SAAS,EAAE;AACjB,YAAI,OAAO,YAAY,cAAc,OAAO,QAAQ,SAAS;AAC3D,qBAAW,MAAM;AACf,kBAAM,cAAc,OAAO,QAAQ;AACnC,kBAAM,gBAAgB,OAAO,QAAQ,YAAY;AACjD,kBAAM,cAAc,QAAQ;AAAA,cAC1B,2CAA2C,WAAW;AAAA,YACxD;AAEA,gBAAI,aAAa;AACf,oBAAM,aAAa,OAAO,UAAU;AACpC,oBAAM,aAAa,OAAO,MAAM,KAAK,EAAE,SAAS;AAChD,0BAAY,WAAW,CAAC,cAAc,CAAC;AAAA,YACzC;AAAA,UACF,GAAG,CAAC;AAAA,QACN;AAAA,MACF,CAAC;AAGD,cAAQ,iBAAiB,WAAW,CAAC,MAAqB;AACxD,cAAM,SAAS,EAAE;AACjB,YACE,OAAO,YAAY,cACnB,OAAO,QAAQ,YACd,EAAE,WAAW,EAAE,YAChB,EAAE,QAAQ,SACV;AACA,YAAE,eAAe;AACjB,gBAAM,cAAc,OAAO,QAAQ;AACnC,gBAAM,cAAc,QAAQ;AAAA,YAC1B,2CAA2C,KAAK,kBAAkB,WAAW,CAAC;AAAA,UAChF;AAEA,cAAI,eAAe,CAAC,YAAY,UAAU;AACxC,wBAAY,MAAM;AAAA,UACpB;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEQ,kBAAwB;AAC9B,QAAI,OAAO,aAAa,aAAa;AACnC;AAAA,IACF;AAEA,UAAM,YAAY,4BAA4B,KAAK,QAAQ;AAC3D,UAAM,UAAU,sBAAsB,KAAK,QAAQ;AACnD,UAAM,UAAU,0BAA0B,KAAK,QAAQ;AACvD,UAAM,wBAAwB,kCAAkC,KAAK,QAAQ;AAC7E,UAAM,WAAW,2BAA2B,KAAK,QAAQ;AAEzD,UAAM,UAAU,SAAS,eAAe,SAAS;AACjD,UAAM,cAAc,SAAS,eAAe,OAAO;AACnD,UAAM,QAAQ,SAAS,eAAe,OAAO;AAC7C,UAAM,sBAAsB,SAAS,eAAe,qBAAqB;AACzE,UAAM,YAAY,SAAS,eAAe,QAAQ;AAElD,QAAI,CAAC,SAAS;AACZ,cAAQ,KAAK,wCAAwC,SAAS;AAC9D;AAAA,IACF;AAGA,UAAM,gBAAgB,OAAO,KAAK,KAAK,MAAM,SAAS,EAAE;AACxD,UAAM,eAAe,gBAAgB;AACrC,QAAI,aAAa;AACf,kBAAY,WAAW,CAAC;AAAA,IAC1B;AAGA,QAAI,OAAO;AACT,UAAI,cAAc;AAChB,cAAM,cAAc,gBAAgB,KAAK,QAAQ,OAAO,aAAa;AACrE,cAAM,UAAU,IAAI,MAAM;AAAA,MAC5B,OAAO;AACL,cAAM,UAAU,OAAO,MAAM;AAAA,MAC/B;AAAA,IACF;AAGA,QAAI,WAAW;AACb,UAAI,cAAc;AAChB,kBAAU;AAAA,UACR;AAAA,UACA,qBAAqB,aAAa,YAAY,kBAAkB,IAAI,KAAK,GAAG;AAAA,QAC9E;AACA,kBAAU;AAAA,UACR;AAAA,UACA,qBAAqB,aAAa,YAAY,kBAAkB,IAAI,KAAK,GAAG;AAAA,QAC9E;AAAA,MACF,OAAO;AACL,kBAAU,aAAa,SAAS,kBAAkB;AAClD,kBAAU,aAAa,cAAc,kBAAkB;AAAA,MACzD;AAAA,IACF;AAGA,QAAI,qBAAqB;AAEvB,YAAM,eAAe,KAAK,WAAW,OAAO,aAAa,CAAC;AAC1D,0BAAoB,YAAY,uDAAuD,eAAe,kBAAkB,EAAE,KAAK,YAAY,mBAAmB,kBAAkB,IAAI,KAAK,GAAG;AAAA,IAC9L;AAEA,UAAM,WAAW,MAAM,KAAK,KAAK,MAAM,QAAQ,EAAE,KAAK;AAGtD,UAAM,mBAAmB,SAAS;AAAA,MAAO,UACvC,KAAK,YAAY,EAAE,SAAS,KAAK,MAAM,WAAW;AAAA,IACpD;AAEA,QAAI,iBAAiB,WAAW,GAAG;AACjC,cAAQ,YAAY,KAAK,MAAM,cAC3B,yEACA,uCAAuC,mBAAmB;AAC9D;AAAA,IACF;AAEA,UAAM,cAAc,iBACjB,IAAI,iBAAe;AAClB,YAAM,cAAc,eAAe,KAAK,MAAM;AAC9C,YAAM,eAAe,KAAK,MAAM,cAAc,WAAW;AACzD,YAAM,gBAAgB,cAAc,KAAK,MAAM,UAAU,WAAW,IAAI;AACxE,YAAM,aAAa,CAAC,KAAK,MAAM;AAC/B,YAAM,YAAY,yBAAyB,aAAa,aAAa,EAAE;AAGvE,YAAM,YACJ,OAAO,iBAAiB,aAAc,eAAe,OAAO,kBAAkB;AAEhF,UAAI,WAAW;AAEb,cAAM,YAAY,cAAc,kBAAkB,OAAO,iBAAiB;AAC1E,eAAO;AAAA,wBACO,SAAS;AAAA;AAAA;AAAA,kBAGf,KAAK,WAAW,WAAW,CAAC;AAAA,kBAC5B,cAAc,6FAA6F,EAAE;AAAA;AAAA;AAAA,kBAI7G,cACI;AAAA;AAAA;AAAA,oCAGc,KAAK,WAAW,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAS1C,EACN;AAAA;AAAA;AAAA;AAAA,sBAIM,YAAY,YAAY,EAAE;AAAA,oCACZ,KAAK,WAAW,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MASxD,OAAO;AAEL,cAAM,sBAAsB,cACxB,KAAK,UAAU,aAAa,IAC5B,iBAAiB,SACf,KAAK,UAAU,YAAY,IAC3B;AACN,cAAM,qBAAqB,KAAK,WAAW,WAAW;AACtD,cAAM,6BAA6B,KAAK,WAAW,mBAAmB;AACtE,cAAM,yBAAyB,cAC3B,KAAK,WAAW,KAAK,UAAU,aAAa,CAAC,IAC7C;AAEJ,eAAO;AAAA,wBACO,SAAS;AAAA;AAAA;AAAA,kBAGf,kBAAkB;AAAA,kBAClB,cAAc,6FAA6F,EAAE;AAAA;AAAA;AAAA,kBAI7G,cACI;AAAA;AAAA;AAAA,sCAGgB,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBASlC,EACN;AAAA;AAAA;AAAA,kCAGkB,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gCAWpB,kBAAkB;AAAA,iCACjB,0BAA0B;AAAA,iBAC1C,sBAAsB;AAAA;AAAA;AAAA;AAAA,MAI/B;AAAA,IACF,CAAC,EACA,KAAK,EAAE;AAEV,0BAAsB,MAAM;AAE1B,cAAQ,YAAY;AAGpB,cAAQ,iBAAiB,wBAAwB,EAAE,QAAQ,cAAY;AACrE,cAAM,kBAAkB;AACxB,cAAM,cAAc,gBAAgB,QAAQ;AAC5C,cAAM,gBAAgB,gBAAgB,QAAQ,YAAY;AAC1D,cAAM,cAAc,QAAQ;AAAA,UAC1B,2CAA2C,KAAK,kBAAkB,WAAW,CAAC;AAAA,QAChF;AAEA,YAAI,aAAa;AACf,gBAAM,aAAa,gBAAgB,UAAU;AAC7C,gBAAM,aAAa,gBAAgB,MAAM,KAAK,EAAE,SAAS;AACzD,sBAAY,WAAW,CAAC,cAAc,CAAC;AAAA,QACzC;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEQ,WAAW,MAAsB;AACvC,UAAM,MAAM,OAAO,aAAa,cAAc,SAAS,cAAc,KAAK,IAAI;AAC9E,QAAI,KAAK;AACP,UAAI,cAAc;AAClB,aAAO,IAAI;AAAA,IACb;AACA,WAAO,KAAK,QAAQ,YAAY,UAAQ;AACtC,YAAM,YAAoC;AAAA,QACxC,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,MACP;AACA,aAAO,UAAU,IAAI;AAAA,IACvB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,kBAAkB,OAAuB;AAE/C,WAAO,MAAM,QAAQ,aAAa,MAAM;AAAA,EAC1C;AACF;;;AC5kCO,IAAM,uBAAuB;AAC7B,IAAM,qBAAqB;;;ACoB3B,IAAM,aAAN,MAA0D;AAAA,EAW/D,YAAY,QAAoD;AAC9D,SAAK,SAAS,OAAO;AACrB,SAAK,cAAc,OAAO;AAC1B,SAAK,iBAAiB,OAAO;AAC7B,SAAK,qBAAqB,OAAO;AAGjC,SAAK,WAAW,KAAK,iBAAiB;AAEtC,SAAK,gBAAgB;AAAA,MACnB,gBAAgB,OAAO,eAAe,kBAAkB;AAAA,MACxD,cAAc,OAAO,eAAe,gBAAgB;AAAA,MACpD,OAAO;AAAA,QACL,SAAS,OAAO,eAAe,OAAO,WAAW;AAAA,QACjD,aAAa,OAAO,eAAe,OAAO,eAAe;AAAA,QACzD,SAAS,OAAO,eAAe,OAAO,WAAW;AAAA,MACnD;AAAA,MACA,kBAAkB,OAAO,eAAe,oBAAoB;AAAA,IAC9D;AAGA,UAAM,cACJ,OAAO,eAAe,cACjB,WAAmD,QACpD;AACN,QAAI,OAAO,eAAe,SAAS;AACjC,WAAK,YAAY,OAAO,cAAc;AAAA,IACxC,WAAW,OAAO,gBAAgB,YAAY;AAC5C,WAAK,YAAY,YAAY,KAAK,UAAU;AAAA,IAC9C,OAAO;AACL,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAGA,SAAK,UAAU,KAAK,kBAAkB,MAAM;AAG5C,YAAQ;AAAA,MACN,KAAK,QAAQ;AAAA,QAAI,YACf,OAAO,SAAS;AAAA,UACd,UAAU,KAAK;AAAA,UACf,mBAAmB,KAAK;AAAA,UACxB,SAAS,KAAK;AAAA,QAChB,CAAC;AAAA,MACH;AAAA,IACF,EAAE,MAAM,QAAQ,KAAK;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAA2B;AACjC,WAAO,YAAY,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,EAC7E;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,QAAkE;AAC1F,UAAM,UAAU,OAAO,WAAW,CAAC;AAGnC,UAAM,YAAY,OAAO,WAAW,eAAe,OAAO,aAAa;AAGvE,QAAI,OAAO,YAAY,OAAO;AAC5B,aAAO;AAAA,IACT;AAGA,QAAI,WAAW;AAEb,YAAM,mBAAmB,QAAQ,KAAK,OAAK,EAAE,SAAS,gBAAgB;AAEtE,UAAI,CAAC,kBAAkB;AAErB,cAAM,gBAAgB,OAAO,WAAW,EAAE,SAAS,OAAO;AAC1D,cAAM,gBAAgB,IAAI,kBAAkB,aAAa;AACzD,eAAO,CAAC,eAAe,GAAG,OAAO;AAAA,MACnC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,cAAc,SAAyB,oBAA6B,MAAY;AAC9E,UAAM,aAAa,KAAK;AAExB,QAAI,qBAAqB,KAAK,gBAAgB;AAC5C,WAAK,iBAAiB,EAAE,GAAG,KAAK,gBAAgB,GAAG,QAAQ;AAAA,IAC7D,OAAO;AACL,WAAK,iBAAiB;AAAA,IACxB;AAGA,YAAQ;AAAA,MACN,KAAK,QAAQ;AAAA,QAAI,YACf,OAAO,kBAAkB,YAAY,KAAK,gBAAiB,eAAe;AAAA,MAC5E;AAAA,IACF,EAAE,MAAM,QAAQ,KAAK;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,aAAyC;AACvC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAiD,aAA8C;AAC7F,WAAO,KAAK,mBAAmB,WAAW;AAAA,EAC5C;AAAA,EAEQ,kBAAkB,WAAyB,UAAsC;AACvF,QAAI,cAAc,UAAa,cAAc,MAAM;AACjD,aAAO;AAAA,IACT;AAEA,WAAO,YAAY;AAAA,EACrB;AAAA,EAEA,MAAM,WACJ,aACA,SACoC;AACpC,UAAM,EAAE,QAAQ,IAAI,WAAW,CAAC;AAGhC,UAAM,gBACJ,OAAO,YAAY,YAAY,YAAY,OACvC,EAAE,GAAI,KAAK,kBAAkB,CAAC,GAAI,GAAG,QAAQ,IAC7C,KAAK;AAEX,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,YAAY,CAAC,WAAqB,GAAG;AAAA,QAC/D,SAAS;AAAA,MACX,CAAC;AAGD,YAAM,QAAQ,SAAS,WAAqB;AAC5C,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,YAAM,QAAQ,IAAI,KAAK,QAAQ,IAAI,YAAU,OAAO,UAAU,OAAgB,aAAa,CAAC,CAAC;AAG7F,YAAM,gBAAgB,KAAK,mBAAmB,WAAW;AAGzD,YAAM,QAAQ;AAAA,QACZ,KAAK,QAAQ;AAAA,UAAI,YACf,OAAO;AAAA,YACL;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,YACJ,cACA,SAC2D;AAC3D,UAAM,EAAE,SAAS,gBAAgB,IAAI,WAAW,CAAC;AAGjD,UAAM,gBACJ,OAAO,oBAAoB,YAAY,oBAAoB,OACvD,EAAE,GAAI,KAAK,kBAAkB,CAAC,GAAI,GAAG,gBAAgB,IACrD,KAAK;AAGX,QAAI,iBAAiB;AACnB,YAAM,QAAQ;AAAA,QACZ,KAAK,QAAQ;AAAA,UAAI,YACf,OAAO,kBAAkB,KAAK,gBAAgB,eAAgB,SAAS;AAAA,QACzE;AAAA,MACF;AAAA,IACF;AAGA,UAAM,oBAAoB,aAAa,IAAI,UAAQ,IAAc;AAEjE,QAAI;AAEF,YAAM,QAAQ;AAAA,QACZ,KAAK,QAAQ,IAAI,YAAU,OAAO,oBAAoB,mBAAmB,aAAa,CAAC;AAAA,MACzF;AAGA,YAAM,gBAAgB,YAAmD;AACvE,cAAM,MAAM,KAAK,cAAc;AAC/B,cAAM,UAAkC;AAAA,UACtC,gBAAgB;AAAA,UAChB,eAAe,UAAU,KAAK,MAAM;AAAA,QACtC;AACA,cAAM,OAAO,KAAK,UAAU;AAAA,UAC1B,aAAa,KAAK;AAAA,UAClB,UAAU;AAAA,UACV,SAAS;AAAA,QACX,CAAC;AAGD,cAAM,QAAQ,IAAI,KAAK,QAAQ,IAAI,YAAU,OAAO,gBAAgB,KAAK,MAAM,OAAO,CAAC,CAAC;AAExF,cAAM,YAAY,KAAK,IAAI;AAE3B,cAAM,YACJ,OAAO,eAAe,cACjB,WACE,kBACH;AACN,YAAI;AACJ,YAAI;AACJ,YAAI,KAAK,cAAc,oBAAoB,OAAO,cAAc,YAAY;AAC1E,uBAAa,IAAI,UAAU;AAC3B,sBAAY,WAAW,MAAM,YAAY,MAAM,GAAG,KAAK,cAAc,gBAAgB;AAAA,QACvF;AACA,YAAI;AACJ,YAAI;AACF,qBAAW,MAAM,KAAK,UAAU,KAAK;AAAA,YACnC,QAAQ;AAAA,YACR;AAAA,YACA;AAAA,YACA,QAAQ,YAAY;AAAA,UACtB,CAAC;AAAA,QACH,UAAE;AACA,cAAI,UAAW,cAAa,SAAS;AAAA,QACvC;AACA,cAAM,WAAW,KAAK,IAAI,IAAI;AAG9B,cAAM,QAAQ;AAAA,UACZ,KAAK,QAAQ,IAAI,YAAU,OAAO,gBAAgB,UAAU,EAAE,SAAS,CAAC,CAAC;AAAA,QAC3E;AAEA,YAAI,CAAC,SAAS,IAAI;AAChB,gBAAM,IAAI,MAAM,6BAA6B,SAAS,UAAU,EAAE;AAAA,QACpE;AAEA,cAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,cAAMA,UAAuC,CAAC;AAE9C,0BAAkB,QAAQ,UAAQ;AAChC,gBAAM,YAAY,KAAK,SAAS,IAAI,GAAG;AACvC,UAAAA,QAAO,IAAI,IAAI,KAAK;AAAA,YAClB;AAAA,YACA,KAAK,mBAAmB,IAAuB;AAAA,UACjD;AAAA,QACF,CAAC;AAED,eAAOA;AAAA,MACT;AAEA,YAAM,SAAS,KAAK,cAAc,MAAM,UACpC,MAAM;AAAA,QACJ;AAAA,QACA,KAAK,cAAc,MAAM;AAAA,QACzB,KAAK,cAAc,MAAM;AAAA,QACzB,CAAC,SAAS,OAAO,cAAc;AAE7B,kBAAQ;AAAA,YACN,KAAK,QAAQ,IAAI,YAAU,OAAO,iBAAiB,SAAS,OAAO,SAAS,CAAC;AAAA,UAC/E,EAAE,MAAM,QAAQ,KAAK;AAAA,QACvB;AAAA,MACF,IACA,MAAM,cAAc;AAGxB,YAAM,QAAQ;AAAA,QACZ,KAAK,QAAQ,IAAI,YAAU,OAAO,mBAAmB,QAAQ,aAAa,CAAC;AAAA,MAC7E;AAGA,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,YAAM,QAAQ,IAAI,KAAK,QAAQ,IAAI,YAAU,OAAO,UAAU,OAAgB,aAAa,CAAC,CAAC;AAG7F,YAAM,iBAA+C,CAAC;AAEtD,wBAAkB,QAAQ,iBAAe;AACvC,uBAAe,WAAW,IAAI,KAAK,mBAAmB,WAA8B;AAGpF,gBAAQ;AAAA,UACN,KAAK,QAAQ;AAAA,YAAI,YACf,OAAO;AAAA,cACL;AAAA,cACA,KAAK,mBAAmB,WAA8B;AAAA,cACtD;AAAA,YACF;AAAA,UACF;AAAA,QACF,EAAE,MAAM,QAAQ,KAAK;AAAA,MACvB,CAAC;AAED,aAAO;AAAA,IACT;AAAA,EACF;AACF;","names":["result"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/plugins/toolbar-plugin.ts","../src/constants.ts","../src/client.ts"],"sourcesContent":["export type * from './types'\nexport { SupaClient } from './client'\nexport type { SupaPlugin, SupaPluginConfig } from './plugins/types'\nexport { SupaToolbarPlugin as ToolbarPlugin } from './plugins/toolbar-plugin'\nexport type {\n SupaToolbarPluginConfig,\n SupaToolbarPosition,\n SupaToolbarOverrideChange,\n SupaToolbarOverrideChangeCallback,\n} from './plugins/toolbar-plugin'\n// export { LoggingPlugin } from \"./plugins/logging-plugin\";\n// export { CachingPlugin } from \"./plugins/caching-plugin\";\n// export { AnalyticsPlugin } from \"./plugins/analytics-plugin\";\n// export { LocalDevPlugin } from \"./plugins/local-dev-plugin\";\n","export function sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nexport async function retry<T>(\n fn: () => Promise<T>,\n maxAttempts: number = 3,\n backoff: number = 1000,\n onRetry?: (attempt: number, error: Error, willRetry: boolean) => void\n): Promise<T> {\n let lastError: Error\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn()\n } catch (error) {\n lastError = error as Error\n const willRetry = attempt < maxAttempts\n\n if (onRetry) {\n onRetry(attempt, lastError, willRetry)\n }\n\n if (!willRetry) break\n await sleep(backoff * Math.pow(2, attempt - 1))\n }\n }\n\n throw lastError!\n}\n","import { SupaPlugin, SupaPluginConfig } from './types'\nimport { FeatureContext, FeatureValue } from '../types'\n\nexport interface SupaToolbarPosition {\n placement?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'\n offset?: { x: string; y: string }\n}\n\nexport type SupaToolbarOverrideChange = {\n feature: string\n value: FeatureValue\n}\n\nexport type SupaToolbarOverrideChangeCallback = (\n featureOverride: SupaToolbarOverrideChange,\n allOverrides: Record<string, FeatureValue>\n) => void\n\nexport interface SupaToolbarPluginConfig extends Omit<SupaPluginConfig, 'enabled'> {\n enabled?: boolean | 'auto' // auto means show only on localhost\n position?: SupaToolbarPosition\n onOverrideChange?: SupaToolbarOverrideChangeCallback\n}\n\ninterface SupaToolbarState {\n overrides: Record<string, FeatureValue>\n features: Set<string>\n featureValues: Record<string, FeatureValue>\n context?: FeatureContext\n searchQuery: string\n useLocalOverrides: boolean\n}\n\nconst DEFAULT_STORAGE_KEY = 'supaship-feature-overrides'\n\nconst NO_FEATURES_MESSAGE = `No feature flags configured in the client.`\n\n/**\n * Toolbar plugin for local feature flag testing\n * Provides a visual interface to override feature flags during development\n */\nexport class SupaToolbarPlugin implements SupaPlugin {\n name = 'toolbar-plugin'\n private config: {\n enabled: boolean | 'auto'\n position: Required<SupaToolbarPosition>\n onOverrideChange?: SupaToolbarOverrideChangeCallback\n }\n private state: SupaToolbarState\n private clientId?: string\n private storageKey: string = DEFAULT_STORAGE_KEY\n\n constructor(config: SupaToolbarPluginConfig = {}) {\n this.config = {\n enabled: config.enabled ?? 'auto',\n position: {\n placement: config.position?.placement ?? 'bottom-right',\n offset: config.position?.offset ?? { x: '1rem', y: '1rem' },\n },\n onOverrideChange: config.onOverrideChange,\n }\n\n this.state = {\n overrides: {},\n features: new Set(),\n featureValues: {},\n searchQuery: '',\n useLocalOverrides: true,\n }\n }\n\n cleanup(): void {\n this.removeToolbar()\n }\n\n private shouldShowToolbar(): boolean {\n if (this.config.enabled === true) return true\n if (this.config.enabled === false) return false\n\n // Auto mode: show only on localhost\n if (typeof window !== 'undefined') {\n return (\n window.location.hostname === 'localhost' ||\n window.location.hostname === '127.0.0.1' ||\n window.location.hostname === '' ||\n window.location.hostname.endsWith('.local') ||\n window.location.hostname.endsWith('.localhost')\n )\n }\n return false\n }\n\n onInit(params: {\n availableFeatures: Record<string, FeatureValue>\n context?: FeatureContext\n clientId: string\n }): void {\n const { availableFeatures, context, clientId } = params\n\n // Set client ID for DOM element IDs\n this.clientId = clientId\n\n // Use shared storage key (not client-specific) to persist across refreshes\n this.storageKey = DEFAULT_STORAGE_KEY\n\n // Load overrides from shared storage\n this.state.overrides = this.loadOverrides()\n\n // Initialize with all available features and their fallback values from config\n this.state.features = new Set(Object.keys(availableFeatures))\n this.state.featureValues = { ...availableFeatures }\n this.state.context = context\n\n // Inject toolbar if conditions are met\n if (this.shouldShowToolbar()) {\n this.injectToolbar()\n }\n\n // Update toolbar UI if it exists\n this.updateToolbarUI()\n }\n\n async beforeGetFeatures(_featureNames: string[], context?: FeatureContext): Promise<void> {\n // Update context if it changed\n this.state.context = context\n\n // Load overrides from shared storage\n this.state.overrides = this.loadOverrides()\n\n // Update toolbar UI if it exists\n this.updateToolbarUI()\n }\n\n async afterGetFeatures(\n results: Record<string, FeatureValue>,\n context?: FeatureContext\n ): Promise<void> {\n // Update feature values with fetched results (this replaces config fallback values)\n Object.keys(results).forEach(name => {\n this.state.featureValues[name] = results[name]\n })\n\n // Apply overrides to results only if local overrides are enabled\n if (this.state.useLocalOverrides) {\n Object.keys(this.state.overrides).forEach(featureName => {\n if (featureName in results) {\n results[featureName] = this.state.overrides[featureName]\n }\n })\n }\n\n // Track features and update UI\n Object.keys(results).forEach(name => this.state.features.add(name))\n this.state.context = context\n this.updateToolbarUI()\n }\n\n private loadOverrides(): Record<string, FeatureValue> {\n if (typeof window === 'undefined' || !window.localStorage) {\n return {}\n }\n\n try {\n const stored = window.localStorage.getItem(this.storageKey)\n return stored ? JSON.parse(stored) : {}\n } catch {\n return {}\n }\n }\n\n private saveOverrides(\n feature?: string,\n value?: FeatureValue,\n allOverrides?: Record<string, FeatureValue>\n ): void {\n if (typeof window === 'undefined' || !window.localStorage) {\n return\n }\n\n try {\n window.localStorage.setItem(this.storageKey, JSON.stringify(allOverrides))\n this.config.onOverrideChange?.(\n { feature: feature ?? '', value: value ?? null },\n allOverrides ?? {}\n )\n } catch (error) {\n console.error('Supaship: Failed to save feature overrides:', error)\n }\n }\n\n public setOverride(featureName: string, value: FeatureValue): void {\n this.state.overrides[featureName] = value\n this.saveOverrides(featureName, value, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public removeOverride(featureName: string): void {\n delete this.state.overrides[featureName]\n this.saveOverrides(featureName, null, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public clearAllOverrides(): void {\n this.state.overrides = {}\n this.saveOverrides('', null, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public getOverrides(): Record<string, FeatureValue> {\n return { ...this.state.overrides }\n }\n\n private injectToolbar(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return\n }\n\n // Check if toolbar with this client ID already exists\n const toolbarId = `supaship-toolbar-${this.clientId}`\n if (document.getElementById(toolbarId)) {\n return\n }\n\n // Create toolbar container\n const toolbar = document.createElement('div')\n toolbar.id = toolbarId\n toolbar.setAttribute('data-supaship-client', this.clientId || '')\n toolbar.innerHTML = this.getToolbarHTML()\n\n // Add styles\n this.injectStyles()\n\n // Add to DOM\n document.body.appendChild(toolbar)\n\n // Add event listeners\n this.attachEventListeners()\n }\n\n private removeToolbar(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const toolbar = document.getElementById(`supaship-toolbar-${this.clientId}`)\n if (toolbar) {\n toolbar.remove()\n }\n\n const styles = document.getElementById('supaship-toolbar-styles')\n if (styles) {\n styles.remove()\n }\n }\n\n private getToolbarHTML(): string {\n const { placement, offset } = this.config.position\n const positionClass = `supaship-toolbar-${placement}`\n const offsetX = offset?.x ?? '1rem'\n const offsetY = offset?.y ?? '1rem'\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n const panelId = `supaship-toolbar-panel-${this.clientId}`\n const searchId = `supaship-search-input-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const closeId = `supaship-close-toolbar-${this.clientId}`\n const contentId = `supaship-toolbar-content-${this.clientId}`\n const badgeId = `supaship-toolbar-badge-${this.clientId}`\n const headerOverrideCountId = `supaship-header-override-count-${this.clientId}`\n\n return `\n <div class=\"supaship-toolbar-container ${positionClass}\" style=\"--offset-x: ${offsetX}; --offset-y: ${offsetY};\">\n <button class=\"supaship-toolbar-toggle\" id=\"${toggleId}\" aria-label=\"Supaship Toolbar\" title=\"Supaship Toolbar\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 256 256\"\n width=\"32\"\n style=\"vertical-align: middle;\">\n <rect width='256' height='256' rx='128' fill='#e4f222'></rect>\n <line\n x1='66'\n y1='118'\n x2='118'\n y2='66'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n <line\n x1='186'\n y1='70'\n x2='70'\n y2='186'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n <line\n x1='190'\n y1='138'\n x2='138'\n y2='190'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n </svg>\n <span class=\"supaship-toolbar-badge\" id=\"${badgeId}\"></span>\n </button>\n <div class=\"supaship-toolbar-panel\" id=\"${panelId}\">\n <div class=\"supaship-toolbar-header\">\n <input\n type=\"text\"\n class=\"supaship-search-input\"\n id=\"${searchId}\"\n placeholder=\"Search features\"\n />\n <span class=\"supaship-toolbar-overrides-label\" id=\"${headerOverrideCountId}\">0 overrides</span>\n <button\n class=\"supaship-header-btn\"\n id=\"${clearId}\"\n aria-label=\"Reset all overrides\"\n title=\"Reset all overrides to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/>\n <path d=\"M3 3v5h5\"/>\n <path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/>\n <path d=\"M16 16h5v5\"/>\n </svg>\n </button>\n <button\n class=\"supaship-header-btn\"\n id=\"${closeId}\"\n aria-label=\"Close toolbar\"\n title=\"Close toolbar\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6 6 18\"/>\n <path d=\"m6 6 12 12\"/>\n </svg>\n </button>\n </div>\n <div class=\"supaship-toolbar-content\" id=\"${contentId}\">\n <div class=\"supaship-toolbar-empty\">${NO_FEATURES_MESSAGE}</div>\n </div>\n </div>\n </div>\n `\n }\n\n private injectStyles(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n if (document.getElementById('supaship-toolbar-styles')) {\n return\n }\n\n const styles = document.createElement('style')\n styles.id = 'supaship-toolbar-styles'\n styles.textContent = `\n .supaship-toolbar-container {\n position: fixed;\n z-index: 999999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n font-size: 14px;\n }\n\n .supaship-toolbar-bottom-right {\n bottom: var(--offset-y);\n right: var(--offset-x);\n }\n\n .supaship-toolbar-bottom-left {\n bottom: var(--offset-y);\n left: var(--offset-x);\n }\n\n .supaship-toolbar-top-right {\n top: var(--offset-y);\n right: var(--offset-x);\n }\n\n .supaship-toolbar-top-left {\n top: var(--offset-y);\n left: var(--offset-x);\n }\n\n .supaship-toolbar-toggle {\n position: relative;\n width: 36px;\n height: 36px;\n border-radius: 100%;\n border: none;\n color: white;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n transition: transform 0.2s, box-shadow 0.2s;\n }\n\n .supaship-toolbar-toggle:hover {\n transform: scale(1.05);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);\n }\n\n .supaship-toolbar-badge {\n position: absolute;\n top: -4px;\n right: -4px;\n background: #ef4444;\n color: white;\n font-size: 10px;\n font-weight: 600;\n min-width: 18px;\n height: 18px;\n border-radius: 9px;\n display: none;\n align-items: center;\n justify-content: center;\n padding: 0 5px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n pointer-events: none;\n }\n\n .supaship-toolbar-badge.show {\n display: flex;\n }\n\n .supaship-toolbar-panel {\n position: absolute;\n bottom: 48px;\n right: 0;\n width: 300px;\n max-height: 600px;\n background: #1a1a1a;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);\n display: none;\n flex-direction: column;\n overflow: hidden;\n border: 1px solid #333;\n }\n\n .supaship-toolbar-bottom-left .supaship-toolbar-panel,\n .supaship-toolbar-top-left .supaship-toolbar-panel {\n right: auto;\n left: 0;\n }\n\n .supaship-toolbar-top-right .supaship-toolbar-panel,\n .supaship-toolbar-top-left .supaship-toolbar-panel {\n bottom: auto;\n top: 60px;\n }\n\n .supaship-toolbar-panel.open {\n display: flex;\n }\n\n .supaship-toolbar-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n border-bottom: 1px solid #333;\n background: #0f0f0f;\n min-width: 0;\n overflow: hidden;\n }\n\n .supaship-search-input {\n flex: 1;\n min-width: 0;\n background: transparent;\n border: none;\n color: #e5e5e5;\n padding: 0;\n font-size: 13px;\n outline: none;\n }\n\n .supaship-search-input::placeholder {\n color: #888;\n }\n\n .supaship-header-btn {\n background: transparent;\n border: none;\n color: #e5e5e5;\n width: 24px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.2s;\n padding: 0;\n }\n\n .supaship-header-btn:hover {\n color: #ef4444;\n }\n\n .supaship-toolbar-content {\n flex: 1;\n overflow-y: auto;\n padding: 8px;\n min-height: 300px;\n max-height: 300px;\n }\n\n .supaship-toolbar-empty {\n padding: 32px 16px;\n text-align: center;\n color: #888;\n }\n\n .supaship-feature-item {\n padding: 0 6px;\n }\n\n .supaship-feature-item.disabled {\n opacity: 0.5;\n pointer-events: none;\n }\n\n .supaship-feature-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n min-height: 32px;\n }\n\n .supaship-feature-name {\n font-weight: 500;\n color: #e5e5e5;\n font-size: 13px;\n flex: 1;\n min-width: 0;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n }\n\n .supaship-feature-override-indicator {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #ef4444;\n flex-shrink: 0;\n }\n\n .supaship-toolbar-overrides-label {\n font-size: 12px;\n color: #888;\n white-space: nowrap;\n flex-shrink: 0;\n transition: all 0.2s;\n }\n\n .supaship-toolbar-overrides-label-count.has-overrides {\n background: #ef4444;\n color: white;\n font-size: 10px;\n font-weight: 600;\n min-width: 18px;\n height: 18px;\n border-radius: 9px;\n padding: 2px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n .supaship-feature-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n min-height: 20px;\n }\n\n .supaship-feature-content {\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n .supaship-feature-input {\n flex: 1;\n padding: 6px 8px;\n background: #1a1a1a;\n border: 1px solid #555;\n color: #e5e5e5;\n border-radius: 4px;\n font-size: 13px;\n font-family: 'Monaco', 'Courier New', monospace;\n outline: none;\n resize: vertical;\n min-height: 60px;\n margin-bottom: 8px;\n }\n\n .supaship-feature-input:focus {\n border-color: #667eea;\n }\n\n .supaship-btn {\n padding: 4px 12px;\n border: none;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n }\n\n .supaship-btn-primary {\n background: #444;\n color: white;\n }\n\n .supaship-btn-primary:hover {\n background: #555;\n }\n\n .supaship-btn-secondary {\n background: #444;\n color: #e5e5e5;\n }\n\n .supaship-btn-secondary:hover {\n background: #555;\n }\n\n .supaship-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .supaship-btn:disabled:hover {\n background: #444;\n }\n\n .supaship-header-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n }\n\n .supaship-header-btn:disabled:hover {\n color: #e5e5e5;\n }\n\n .supaship-toggle {\n position: relative;\n display: inline-block;\n width: 32px;\n height: 18px;\n flex-shrink: 0;\n }\n\n .supaship-toggle input {\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .supaship-toggle-slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: #333;\n border: 1px solid #555;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 20px;\n }\n\n .supaship-toggle-slider:before {\n position: absolute;\n content: \"\";\n height: 14px;\n width: 14px;\n left: 2px;\n bottom: 1px;\n background-color: #666;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 50%;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n }\n\n .supaship-toggle input:checked + .supaship-toggle-slider {\n background-color: #fff;\n border-color: #fff;\n }\n\n .supaship-toggle input:checked + .supaship-toggle-slider:before {\n transform: translateX(13px);\n background-color: #000;\n }\n\n .supaship-toggle input:disabled + .supaship-toggle-slider {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .supaship-toggle:hover .supaship-toggle-slider:before {\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);\n }\n\n .supaship-btn-icon {\n background: transparent;\n border: none;\n color: #e5e5e5;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n cursor: pointer;\n padding: 0;\n transition: all 0.2s;\n flex-shrink: 0;\n }\n\n .supaship-btn-icon:hover {\n background: #444;\n color: #ef4444;\n }\n `\n document.head.appendChild(styles)\n }\n\n private attachEventListeners(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n const panelId = `supaship-toolbar-panel-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const closeId = `supaship-close-toolbar-${this.clientId}`\n const searchId = `supaship-search-input-${this.clientId}`\n const contentId = `supaship-toolbar-content-${this.clientId}`\n\n const toggle = document.getElementById(toggleId)\n const panel = document.getElementById(panelId)\n const clearAll = document.getElementById(clearId)\n const close = document.getElementById(closeId)\n const searchInput = document.getElementById(searchId) as HTMLInputElement\n const content = document.getElementById(contentId)\n\n toggle?.addEventListener('click', () => {\n panel?.classList.toggle('open')\n })\n\n close?.addEventListener('click', () => {\n panel?.classList.remove('open')\n })\n\n clearAll?.addEventListener('click', () => {\n this.clearAllOverrides()\n })\n\n searchInput?.addEventListener('input', e => {\n this.state.searchQuery = (e.target as HTMLInputElement).value.toLowerCase()\n this.updateToolbarUI()\n })\n\n // Use event delegation on content element - survives innerHTML updates\n if (content) {\n // Handle button clicks (remove and set actions)\n content.addEventListener('click', (e: Event) => {\n const target = e.target as HTMLElement\n const buttonElement = target.closest('button[data-action]') as HTMLButtonElement\n if (!buttonElement) return\n\n e.preventDefault()\n e.stopPropagation()\n\n const featureName = buttonElement.dataset.feature!\n const action = buttonElement.dataset.action\n\n if (action === 'remove') {\n this.removeOverride(featureName)\n } else if (action === 'set') {\n const textarea = content.querySelector(\n `textarea[data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLTextAreaElement\n if (textarea && textarea.value.trim()) {\n try {\n const value = JSON.parse(textarea.value)\n this.setOverride(featureName, value)\n } catch {\n // If not valid JSON, wrap string in object\n this.setOverride(featureName, { value: textarea.value })\n }\n }\n }\n })\n\n // Handle checkbox changes for boolean toggles\n content.addEventListener('change', (e: Event) => {\n const target = e.target as HTMLInputElement\n if (target.type === 'checkbox' && target.dataset.type === 'boolean') {\n const featureName = target.dataset.feature!\n const newValue = target.checked\n this.setOverride(featureName, newValue)\n }\n })\n\n // Handle textarea input to update button states\n content.addEventListener('input', (e: Event) => {\n const target = e.target as HTMLTextAreaElement\n if (target.tagName === 'TEXTAREA' && target.dataset.feature) {\n const featureName = target.dataset.feature!\n const originalValue = target.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = target.value !== originalValue\n const hasContent = target.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n }\n })\n\n // Handle textarea paste events\n content.addEventListener('paste', (e: Event) => {\n const target = e.target as HTMLTextAreaElement\n if (target.tagName === 'TEXTAREA' && target.dataset.feature) {\n setTimeout(() => {\n const featureName = target.dataset.feature!\n const originalValue = target.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${featureName}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = target.value !== originalValue\n const hasContent = target.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n }, 0)\n }\n })\n\n // Handle Ctrl/Cmd+Enter to set override\n content.addEventListener('keydown', (e: KeyboardEvent) => {\n const target = e.target as HTMLTextAreaElement\n if (\n target.tagName === 'TEXTAREA' &&\n target.dataset.feature &&\n (e.ctrlKey || e.metaKey) &&\n e.key === 'Enter'\n ) {\n e.preventDefault()\n const featureName = target.dataset.feature!\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn && !overrideBtn.disabled) {\n overrideBtn.click()\n }\n }\n })\n }\n }\n\n private updateToolbarUI(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const contentId = `supaship-toolbar-content-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const badgeId = `supaship-toolbar-badge-${this.clientId}`\n const headerOverrideCountId = `supaship-header-override-count-${this.clientId}`\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n\n const content = document.getElementById(contentId)\n const clearAllBtn = document.getElementById(clearId) as HTMLButtonElement\n const badge = document.getElementById(badgeId)\n const headerOverrideCount = document.getElementById(headerOverrideCountId)\n const toggleBtn = document.getElementById(toggleId) as HTMLButtonElement\n\n if (!content) {\n console.warn('[Toolbar] Content element not found:', contentId)\n return\n }\n\n // Update clear all button state and badge\n const overrideCount = Object.keys(this.state.overrides).length\n const hasOverrides = overrideCount > 0\n if (clearAllBtn) {\n clearAllBtn.disabled = !hasOverrides\n }\n\n // Update badge on toggle button\n if (badge) {\n if (hasOverrides) {\n badge.textContent = overrideCount > 99 ? '99+' : String(overrideCount)\n badge.classList.add('show')\n } else {\n badge.classList.remove('show')\n }\n }\n\n // Update tooltip on toggle button\n if (toggleBtn) {\n if (hasOverrides) {\n toggleBtn.setAttribute(\n 'title',\n `Supaship Toolbar, ${overrideCount} override${overrideCount === 1 ? '' : 's'}`\n )\n toggleBtn.setAttribute(\n 'aria-label',\n `Supaship Toolbar, ${overrideCount} override${overrideCount === 1 ? '' : 's'}`\n )\n } else {\n toggleBtn.setAttribute('title', 'Supaship Toolbar')\n toggleBtn.setAttribute('aria-label', 'Supaship Toolbar')\n }\n }\n\n // Update override count in header\n if (headerOverrideCount) {\n // Escape overrideCount to prevent any potential XSS (defense in depth)\n const escapedCount = this.escapeHtml(String(overrideCount))\n headerOverrideCount.innerHTML = `<span class=\"supaship-toolbar-overrides-label-count ${hasOverrides ? 'has-overrides' : ''}\">${escapedCount}</span> override${overrideCount === 1 ? '' : 's'}`\n }\n\n const features = Array.from(this.state.features).sort()\n\n // Filter features based on search query\n const filteredFeatures = features.filter(name =>\n name.toLowerCase().includes(this.state.searchQuery)\n )\n\n if (filteredFeatures.length === 0) {\n content.innerHTML = this.state.searchQuery\n ? '<div class=\"supaship-toolbar-empty\">No matching features found</div>'\n : `<div class=\"supaship-toolbar-empty\">${NO_FEATURES_MESSAGE}</div>`\n return\n }\n\n const htmlContent = filteredFeatures\n .map(featureName => {\n const hasOverride = featureName in this.state.overrides\n const currentValue = this.state.featureValues[featureName]\n const overrideValue = hasOverride ? this.state.overrides[featureName] : currentValue\n const isDisabled = !this.state.useLocalOverrides\n const itemClass = `supaship-feature-item ${isDisabled ? 'disabled' : ''}`\n\n // Check if the feature is boolean\n const isBoolean =\n typeof currentValue === 'boolean' || (hasOverride && typeof overrideValue === 'boolean')\n\n if (isBoolean) {\n // Render toggle switch for boolean values (single row layout)\n const isChecked = hasOverride ? overrideValue === true : currentValue === true\n return `\n <div class=\"${itemClass}\">\n <div class=\"supaship-feature-row\">\n <span class=\"supaship-feature-name\">\n ${this.escapeHtml(featureName)}\n ${hasOverride ? '<span class=\"supaship-feature-override-indicator\" title=\"Serving local override\"></span>' : ''}\n </span>\n <div class=\"supaship-feature-actions\">\n ${\n hasOverride\n ? `\n <button\n class=\"supaship-btn-icon\"\n data-feature=\"${this.escapeHtml(featureName)}\"\n data-action=\"remove\"\n title=\"Reset to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/>\n <path d=\"M3 3v5h5\"/>\n <path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/>\n <path d=\"M16 16h5v5\"/>\n </svg>\n </button>\n `\n : ''\n }\n <label class=\"supaship-toggle\">\n <input\n type=\"checkbox\"\n ${isChecked ? 'checked' : ''}\n data-feature=\"${this.escapeHtml(featureName)}\"\n data-type=\"boolean\"\n />\n <span class=\"supaship-toggle-slider\"></span>\n </label>\n </div>\n </div>\n </div>\n `\n } else {\n // Render textarea for non-boolean values\n const currentDisplayValue = hasOverride\n ? JSON.stringify(overrideValue)\n : currentValue !== undefined\n ? JSON.stringify(currentValue)\n : ''\n const escapedFeatureName = this.escapeHtml(featureName)\n const escapedCurrentDisplayValue = this.escapeHtml(currentDisplayValue)\n const escapedTextareaContent = hasOverride\n ? this.escapeHtml(JSON.stringify(overrideValue))\n : escapedCurrentDisplayValue\n\n return `\n <div class=\"${itemClass}\">\n <div class=\"supaship-feature-row\">\n <span class=\"supaship-feature-name\">\n ${escapedFeatureName}\n ${hasOverride ? '<span class=\"supaship-feature-override-indicator\" title=\"Serving local override\"></span>' : ''}\n </span>\n <div class=\"supaship-feature-actions\">\n ${\n hasOverride\n ? `\n <button\n class=\"supaship-btn-icon\"\n data-feature=\"${escapedFeatureName}\"\n data-action=\"remove\"\n title=\"Reset to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/>\n <path d=\"M3 3v5h5\"/>\n <path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/>\n <path d=\"M16 16h5v5\"/>\n </svg>\n </button>\n `\n : ''\n }\n <button\n class=\"supaship-btn supaship-btn-primary\"\n data-feature=\"${escapedFeatureName}\"\n data-action=\"set\"\n disabled>\n Override\n </button>\n </div>\n </div>\n <div class=\"supaship-feature-content\">\n <textarea\n class=\"supaship-feature-input\"\n placeholder=\"Override JSON value\"\n data-feature=\"${escapedFeatureName}\"\n data-original=\"${escapedCurrentDisplayValue}\"\n >${escapedTextareaContent}</textarea>\n </div>\n </div>\n `\n }\n })\n .join('')\n\n requestAnimationFrame(() => {\n // Set innerHTML - event listeners are handled via delegation in attachEventListeners()\n content.innerHTML = htmlContent\n\n // Update button states for textareas that already have values\n content.querySelectorAll('textarea[data-feature]').forEach(textarea => {\n const textareaElement = textarea as HTMLTextAreaElement\n const featureName = textareaElement.dataset.feature!\n const originalValue = textareaElement.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = textareaElement.value !== originalValue\n const hasContent = textareaElement.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n })\n })\n }\n\n private escapeHtml(text: string): string {\n const div = typeof document !== 'undefined' ? document.createElement('div') : null\n if (div) {\n div.textContent = text\n return div.innerHTML\n }\n return text.replace(/[&<>\"']/g, char => {\n const escapeMap: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n }\n return escapeMap[char]\n })\n }\n\n /**\n * Escapes special characters in CSS attribute selectors to prevent CSS injection\n * @param value The value to escape for use in CSS attribute selectors\n */\n private escapeCssSelector(value: string): string {\n // Escape special CSS selector characters: \", ', ], \\\n return value.replace(/[\"'\\\\\\]]/g, '\\\\$&')\n }\n}\n","export const DEFAULT_FEATURES_URL = 'https://edge.supaship.com/v1/features'\nexport const DEFAULT_EVENTS_URL = 'https://edge.supaship.com/v1/events'\n","import {\n SupaClientConfig,\n FeatureContext,\n FeatureValue,\n NetworkConfig,\n Features,\n FeaturesWithFallbacks,\n} from './types'\nimport { retry } from './utils'\nimport { SupaPlugin } from './plugins/types'\nimport { SupaToolbarPlugin } from './plugins/toolbar-plugin'\nimport { DEFAULT_FEATURES_URL, DEFAULT_EVENTS_URL } from './constants'\n\ntype RequiredRetryConfig = Required<NonNullable<NetworkConfig['retry']>>\ntype ResolvedNetworkConfig = {\n featuresAPIUrl: string\n eventsAPIUrl: string\n retry: RequiredRetryConfig\n requestTimeoutMs: number\n}\n\nexport class SupaClient<TFeatures extends FeaturesWithFallbacks> {\n private apiKey: string\n private environment: string\n private defaultContext?: FeatureContext\n private plugins: SupaPlugin[]\n private featureDefinitions: Features<TFeatures>\n private clientId: string\n\n private fetchImpl: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>\n private networkConfig: ResolvedNetworkConfig\n\n constructor(config: SupaClientConfig & { features: TFeatures }) {\n this.apiKey = config.apiKey\n this.environment = config.environment\n this.defaultContext = config.context\n this.featureDefinitions = config.features as Features<TFeatures>\n\n // Generate unique client ID\n this.clientId = this.generateClientId()\n\n this.networkConfig = {\n featuresAPIUrl: config.networkConfig?.featuresAPIUrl || DEFAULT_FEATURES_URL,\n eventsAPIUrl: config.networkConfig?.eventsAPIUrl || DEFAULT_EVENTS_URL,\n retry: {\n enabled: config.networkConfig?.retry?.enabled ?? true,\n maxAttempts: config.networkConfig?.retry?.maxAttempts ?? 3,\n backoff: config.networkConfig?.retry?.backoff ?? 1000,\n },\n requestTimeoutMs: config.networkConfig?.requestTimeoutMs ?? 10000,\n }\n\n // Prefer injected fetch, then global fetch if available\n const globalFetch: typeof fetch | undefined =\n typeof globalThis !== 'undefined'\n ? (globalThis as unknown as { fetch?: typeof fetch }).fetch\n : undefined\n if (config.networkConfig?.fetchFn) {\n this.fetchImpl = config.networkConfig.fetchFn\n } else if (typeof globalFetch === 'function') {\n this.fetchImpl = globalFetch.bind(globalThis)\n } else {\n throw new Error(\n 'No fetch implementation available. Provide fetchFn in config or use a runtime with global fetch (e.g., Node 18+, browsers).'\n )\n }\n\n // Initialize plugins with automatic toolbar plugin in browser\n this.plugins = this.initializePlugins(config)\n\n // Initialize plugins with available features and their fallback values\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onInit?.({\n clientId: this.clientId,\n availableFeatures: this.featureDefinitions,\n context: this.defaultContext,\n })\n )\n ).catch(console.error)\n }\n\n /**\n * Generate a unique client ID\n */\n private generateClientId(): string {\n return `supaship-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`\n }\n\n /**\n * Initialize plugins with automatic toolbar plugin in browser environments\n */\n private initializePlugins(config: SupaClientConfig & { features: TFeatures }): SupaPlugin[] {\n const plugins = config.plugins || []\n\n // Check if we're in a browser environment\n const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined'\n\n // If toolbar is explicitly disabled, don't add it\n if (config.toolbar === false) {\n return plugins\n }\n\n // If in browser and toolbar not disabled, add it automatically\n if (isBrowser) {\n // Check if user already added toolbar plugin manually\n const hasToolbarPlugin = plugins.some(p => p.name === 'toolbar-plugin')\n\n if (!hasToolbarPlugin) {\n // Add toolbar with user config or defaults\n const toolbarConfig = config.toolbar || { enabled: 'auto' }\n const toolbarPlugin = new SupaToolbarPlugin(toolbarConfig)\n return [toolbarPlugin, ...plugins]\n }\n }\n\n return plugins\n }\n\n /**\n * Updates the default context for the client\n * @param context - New context to merge with or replace the existing context\n * @param mergeWithExisting - Whether to merge with existing context (default: true)\n */\n updateContext(context: FeatureContext, mergeWithExisting: boolean = true): void {\n const oldContext = this.defaultContext\n\n if (mergeWithExisting && this.defaultContext) {\n this.defaultContext = { ...this.defaultContext, ...context }\n } else {\n this.defaultContext = context\n }\n\n // Notify plugins of context change\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onContextUpdate?.(oldContext, this.defaultContext!, 'updateContext')\n )\n ).catch(console.error)\n }\n\n /**\n * Gets the current default context\n */\n getContext(): FeatureContext | undefined {\n return this.defaultContext\n }\n\n /**\n * Gets the fallback value for a feature from its definition\n */\n getFeatureFallback<TKey extends keyof TFeatures>(featureName: TKey): Features<TFeatures>[TKey] {\n return this.featureDefinitions[featureName]\n }\n\n private getVariationValue(variation: FeatureValue, fallback: FeatureValue): FeatureValue {\n if (variation !== undefined && variation !== null) {\n return variation\n }\n\n return fallback ?? null\n }\n\n async getFeature<TKey extends keyof TFeatures>(\n featureName: TKey,\n options?: { context?: FeatureContext }\n ): Promise<Features<TFeatures>[TKey]> {\n const { context } = options ?? {}\n\n // Only merge context if it's defined and not null\n const mergedContext: FeatureContext | undefined =\n typeof context === 'object' && context !== null\n ? { ...(this.defaultContext ?? {}), ...context }\n : this.defaultContext\n\n try {\n const response = await this.getFeatures([featureName as string], {\n context: mergedContext,\n })\n\n // Get the specific feature value\n const value = response[featureName as string]\n return value as Features<TFeatures>[TKey]\n } catch (error) {\n // Run onError hooks\n await Promise.all(this.plugins.map(plugin => plugin.onError?.(error as Error, mergedContext)))\n\n // Use fallback feature value when API fails\n const fallbackValue = this.featureDefinitions[featureName]\n\n // Notify plugins that fallback was used\n await Promise.all(\n this.plugins.map(plugin =>\n plugin.onFallbackUsed?.(\n featureName as string,\n fallbackValue as FeatureValue,\n error as Error\n )\n )\n )\n return fallbackValue as Features<TFeatures>[TKey]\n }\n }\n\n async getFeatures<TKeys extends readonly (keyof TFeatures)[]>(\n featureNames: TKeys,\n options?: { context?: FeatureContext }\n ): Promise<{ [K in TKeys[number]]: Features<TFeatures>[K] }> {\n const { context: contextOverride } = options ?? {}\n\n // Only merge context if it's defined and not null\n const mergedContext: FeatureContext | undefined =\n typeof contextOverride === 'object' && contextOverride !== null\n ? { ...(this.defaultContext ?? {}), ...contextOverride }\n : this.defaultContext\n\n // Notify plugins of context update for this request\n if (contextOverride) {\n await Promise.all(\n this.plugins.map(plugin =>\n plugin.onContextUpdate?.(this.defaultContext, mergedContext!, 'request')\n )\n )\n }\n\n // Convert feature names to strings for API call\n const featureNamesArray = featureNames.map(name => name as string)\n\n try {\n // Run beforeGetFeatures hooks\n await Promise.all(\n this.plugins.map(plugin => plugin.beforeGetFeatures?.(featureNamesArray, mergedContext))\n )\n\n type FeaturesResponse = { features: Record<string, { variation: FeatureValue }> }\n const fetchFeatures = async (): Promise<Record<string, FeatureValue>> => {\n const url = this.networkConfig.featuresAPIUrl\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n }\n const body = JSON.stringify({\n environment: this.environment,\n features: featureNamesArray,\n context: mergedContext,\n })\n\n // Notify plugins before request\n await Promise.all(this.plugins.map(plugin => plugin.beforeRequest?.(url, body, headers)))\n\n const startTime = Date.now()\n // Support timeout via AbortController when available\n const AbortCtrl: typeof AbortController | undefined =\n typeof globalThis !== 'undefined'\n ? (globalThis as unknown as { AbortController?: typeof AbortController })\n .AbortController\n : undefined\n let controller: AbortController | undefined\n let timeoutId: ReturnType<typeof setTimeout> | undefined\n if (this.networkConfig.requestTimeoutMs && typeof AbortCtrl === 'function') {\n controller = new AbortCtrl()\n timeoutId = setTimeout(() => controller?.abort(), this.networkConfig.requestTimeoutMs)\n }\n let response: Response\n try {\n response = await this.fetchImpl(url, {\n method: 'POST',\n headers,\n body,\n signal: controller?.signal,\n })\n } finally {\n if (timeoutId) clearTimeout(timeoutId)\n }\n const duration = Date.now() - startTime\n\n // Notify plugins after response\n await Promise.all(\n this.plugins.map(plugin => plugin.afterResponse?.(response, { duration }))\n )\n\n if (!response.ok) {\n throw new Error(`Failed to fetch features: ${response.statusText}`)\n }\n\n const data = (await response.json()) as FeaturesResponse\n const result: Record<string, FeatureValue> = {}\n\n featureNamesArray.forEach(name => {\n const variation = data.features[name]?.variation\n result[name] = this.getVariationValue(\n variation,\n this.featureDefinitions[name as keyof TFeatures]\n )\n })\n\n return result\n }\n\n const result = this.networkConfig.retry.enabled\n ? await retry(\n fetchFeatures,\n this.networkConfig.retry.maxAttempts,\n this.networkConfig.retry.backoff,\n (attempt, error, willRetry) => {\n // Notify plugins of retry attempts\n Promise.all(\n this.plugins.map(plugin => plugin.onRetryAttempt?.(attempt, error, willRetry))\n ).catch(console.error)\n }\n )\n : await fetchFeatures()\n\n // Run afterGetFeatures hooks\n await Promise.all(\n this.plugins.map(plugin => plugin.afterGetFeatures?.(result, mergedContext))\n )\n\n // Return the fetched features\n return result as { [K in TKeys[number]]: Features<TFeatures>[K] }\n } catch (error) {\n // Run onError hooks\n await Promise.all(this.plugins.map(plugin => plugin.onError?.(error as Error, mergedContext)))\n\n // Create fallback result with requested feature names\n const fallbackResult: Record<string, FeatureValue> = {}\n\n featureNamesArray.forEach(featureName => {\n fallbackResult[featureName] = this.featureDefinitions[featureName as keyof TFeatures]\n\n // Notify plugins that fallback was used for each feature\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onFallbackUsed?.(\n featureName,\n this.featureDefinitions[featureName as keyof TFeatures],\n error as Error\n )\n )\n ).catch(console.error)\n })\n\n return fallbackResult as { [K in TKeys[number]]: Features<TFeatures>[K] }\n }\n }\n}\n"],"mappings":"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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="24"
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 256 256" width="18" height="18">
232
- <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"/>
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>
@@ -282,7 +297,6 @@ var SupaToolbarPlugin = class {
282
297
  width: 36px;
283
298
  height: 36px;
284
299
  border-radius: 100%;
285
- background: #000;
286
300
  border: none;
287
301
  color: white;
288
302
  cursor: pointer;
@@ -636,16 +650,21 @@ var SupaToolbarPlugin = class {
636
650
  const toggleId = `supaship-toolbar-toggle-${this.clientId}`;
637
651
  const panelId = `supaship-toolbar-panel-${this.clientId}`;
638
652
  const clearId = `supaship-clear-all-${this.clientId}`;
653
+ const closeId = `supaship-close-toolbar-${this.clientId}`;
639
654
  const searchId = `supaship-search-input-${this.clientId}`;
640
655
  const contentId = `supaship-toolbar-content-${this.clientId}`;
641
656
  const toggle = document.getElementById(toggleId);
642
657
  const panel = document.getElementById(panelId);
643
658
  const clearAll = document.getElementById(clearId);
659
+ const close = document.getElementById(closeId);
644
660
  const searchInput = document.getElementById(searchId);
645
661
  const content = document.getElementById(contentId);
646
662
  toggle?.addEventListener("click", () => {
647
663
  panel?.classList.toggle("open");
648
664
  });
665
+ close?.addEventListener("click", () => {
666
+ panel?.classList.remove("open");
667
+ });
649
668
  clearAll?.addEventListener("click", () => {
650
669
  this.clearAllOverrides();
651
670
  });
@@ -815,8 +834,11 @@ var SupaToolbarPlugin = class {
815
834
  data-action="remove"
816
835
  title="Reset to default"
817
836
  >
818
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="14" height="14">
819
- <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"/>
837
+ <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">
838
+ <path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
839
+ <path d="M3 3v5h5"/>
840
+ <path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/>
841
+ <path d="M16 16h5v5"/>
820
842
  </svg>
821
843
  </button>
822
844
  ` : ""}
@@ -853,8 +875,11 @@ var SupaToolbarPlugin = class {
853
875
  data-action="remove"
854
876
  title="Reset to default"
855
877
  >
856
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="14" height="14">
857
- <path d="M240,56v48a8,8,0,0,1-8,8H184a8,8,0,0,1,0-16H211.4L184.81,71.64l-.25-.24a80,80,0,1,0-1.67,114.78,8,8,0,0,1,11,11.63A95.44,95.44,0,0,1,128,224h-1.32A96,96,0,1,1,195.75,60L224,85.8V56a8,8,0,1,1,16,0Z" fill="currentColor"/>
878
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
879
+ <path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
880
+ <path d="M3 3v5h5"/>
881
+ <path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/>
882
+ <path d="M16 16h5v5"/>
858
883
  </svg>
859
884
  </button>
860
885
  ` : ""}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils.ts","../src/plugins/toolbar-plugin.ts","../src/constants.ts","../src/client.ts"],"sourcesContent":["export function sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nexport async function retry<T>(\n fn: () => Promise<T>,\n maxAttempts: number = 3,\n backoff: number = 1000,\n onRetry?: (attempt: number, error: Error, willRetry: boolean) => void\n): Promise<T> {\n let lastError: Error\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn()\n } catch (error) {\n lastError = error as Error\n const willRetry = attempt < maxAttempts\n\n if (onRetry) {\n onRetry(attempt, lastError, willRetry)\n }\n\n if (!willRetry) break\n await sleep(backoff * Math.pow(2, attempt - 1))\n }\n }\n\n throw lastError!\n}\n","import { SupaPlugin, SupaPluginConfig } from './types'\nimport { FeatureContext, FeatureValue } from '../types'\n\nexport interface SupaToolbarPosition {\n placement?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'\n offset?: { x: string; y: string }\n}\n\nexport type SupaToolbarOverrideChange = {\n feature: string\n value: FeatureValue\n}\n\nexport type SupaToolbarOverrideChangeCallback = (\n featureOverride: SupaToolbarOverrideChange,\n allOverrides: Record<string, FeatureValue>\n) => void\n\nexport interface SupaToolbarPluginConfig extends Omit<SupaPluginConfig, 'enabled'> {\n enabled?: boolean | 'auto' // auto means show only on localhost\n position?: SupaToolbarPosition\n onOverrideChange?: SupaToolbarOverrideChangeCallback\n}\n\ninterface SupaToolbarState {\n overrides: Record<string, FeatureValue>\n features: Set<string>\n featureValues: Record<string, FeatureValue>\n context?: FeatureContext\n searchQuery: string\n useLocalOverrides: boolean\n}\n\nconst DEFAULT_STORAGE_KEY = 'supaship-feature-overrides'\n\nconst NO_FEATURES_MESSAGE = `No feature flags configured in the client.`\n\n/**\n * Toolbar plugin for local feature flag testing\n * Provides a visual interface to override feature flags during development\n */\nexport class SupaToolbarPlugin implements SupaPlugin {\n name = 'toolbar-plugin'\n private config: {\n enabled: boolean | 'auto'\n position: Required<SupaToolbarPosition>\n onOverrideChange?: SupaToolbarOverrideChangeCallback\n }\n private state: SupaToolbarState\n private clientId?: string\n private storageKey: string = DEFAULT_STORAGE_KEY\n\n constructor(config: SupaToolbarPluginConfig = {}) {\n this.config = {\n enabled: config.enabled ?? 'auto',\n position: {\n placement: config.position?.placement ?? 'bottom-right',\n offset: config.position?.offset ?? { x: '1rem', y: '1rem' },\n },\n onOverrideChange: config.onOverrideChange,\n }\n\n this.state = {\n overrides: {},\n features: new Set(),\n featureValues: {},\n searchQuery: '',\n useLocalOverrides: true,\n }\n }\n\n cleanup(): void {\n this.removeToolbar()\n }\n\n private shouldShowToolbar(): boolean {\n if (this.config.enabled === true) return true\n if (this.config.enabled === false) return false\n\n // Auto mode: show only on localhost\n if (typeof window !== 'undefined') {\n return (\n window.location.hostname === 'localhost' ||\n window.location.hostname === '127.0.0.1' ||\n window.location.hostname === '' ||\n window.location.hostname.endsWith('.local') ||\n window.location.hostname.endsWith('.localhost')\n )\n }\n return false\n }\n\n onInit(params: {\n availableFeatures: Record<string, FeatureValue>\n context?: FeatureContext\n clientId: string\n }): void {\n const { availableFeatures, context, clientId } = params\n\n // Set client ID for DOM element IDs\n this.clientId = clientId\n\n // Use shared storage key (not client-specific) to persist across refreshes\n this.storageKey = DEFAULT_STORAGE_KEY\n\n // Load overrides from shared storage\n this.state.overrides = this.loadOverrides()\n\n // Initialize with all available features and their fallback values from config\n this.state.features = new Set(Object.keys(availableFeatures))\n this.state.featureValues = { ...availableFeatures }\n this.state.context = context\n\n // Inject toolbar if conditions are met\n if (this.shouldShowToolbar()) {\n this.injectToolbar()\n }\n\n // Update toolbar UI if it exists\n this.updateToolbarUI()\n }\n\n async beforeGetFeatures(_featureNames: string[], context?: FeatureContext): Promise<void> {\n // Update context if it changed\n this.state.context = context\n\n // Load overrides from shared storage\n this.state.overrides = this.loadOverrides()\n\n // Update toolbar UI if it exists\n this.updateToolbarUI()\n }\n\n async afterGetFeatures(\n results: Record<string, FeatureValue>,\n context?: FeatureContext\n ): Promise<void> {\n // Update feature values with fetched results (this replaces config fallback values)\n Object.keys(results).forEach(name => {\n this.state.featureValues[name] = results[name]\n })\n\n // Apply overrides to results only if local overrides are enabled\n if (this.state.useLocalOverrides) {\n Object.keys(this.state.overrides).forEach(featureName => {\n if (featureName in results) {\n results[featureName] = this.state.overrides[featureName]\n }\n })\n }\n\n // Track features and update UI\n Object.keys(results).forEach(name => this.state.features.add(name))\n this.state.context = context\n this.updateToolbarUI()\n }\n\n private loadOverrides(): Record<string, FeatureValue> {\n if (typeof window === 'undefined' || !window.localStorage) {\n return {}\n }\n\n try {\n const stored = window.localStorage.getItem(this.storageKey)\n return stored ? JSON.parse(stored) : {}\n } catch {\n return {}\n }\n }\n\n private saveOverrides(\n feature?: string,\n value?: FeatureValue,\n allOverrides?: Record<string, FeatureValue>\n ): void {\n if (typeof window === 'undefined' || !window.localStorage) {\n return\n }\n\n try {\n window.localStorage.setItem(this.storageKey, JSON.stringify(allOverrides))\n this.config.onOverrideChange?.(\n { feature: feature ?? '', value: value ?? null },\n allOverrides ?? {}\n )\n } catch (error) {\n console.error('Supaship: Failed to save feature overrides:', error)\n }\n }\n\n public setOverride(featureName: string, value: FeatureValue): void {\n this.state.overrides[featureName] = value\n this.saveOverrides(featureName, value, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public removeOverride(featureName: string): void {\n delete this.state.overrides[featureName]\n this.saveOverrides(featureName, null, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public clearAllOverrides(): void {\n this.state.overrides = {}\n this.saveOverrides('', null, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public getOverrides(): Record<string, FeatureValue> {\n return { ...this.state.overrides }\n }\n\n private injectToolbar(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return\n }\n\n // Check if toolbar with this client ID already exists\n const toolbarId = `supaship-toolbar-${this.clientId}`\n if (document.getElementById(toolbarId)) {\n return\n }\n\n // Create toolbar container\n const toolbar = document.createElement('div')\n toolbar.id = toolbarId\n toolbar.setAttribute('data-supaship-client', this.clientId || '')\n toolbar.innerHTML = this.getToolbarHTML()\n\n // Add styles\n this.injectStyles()\n\n // Add to DOM\n document.body.appendChild(toolbar)\n\n // Add event listeners\n this.attachEventListeners()\n }\n\n private removeToolbar(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const toolbar = document.getElementById(`supaship-toolbar-${this.clientId}`)\n if (toolbar) {\n toolbar.remove()\n }\n\n const styles = document.getElementById('supaship-toolbar-styles')\n if (styles) {\n styles.remove()\n }\n }\n\n private getToolbarHTML(): string {\n const { placement, offset } = this.config.position\n const positionClass = `supaship-toolbar-${placement}`\n const offsetX = offset?.x ?? '1rem'\n const offsetY = offset?.y ?? '1rem'\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n const panelId = `supaship-toolbar-panel-${this.clientId}`\n const searchId = `supaship-search-input-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const 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 '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n }\n return escapeMap[char]\n })\n }\n\n /**\n * Escapes special characters in CSS attribute selectors to prevent CSS injection\n * @param value The value to escape for use in CSS attribute selectors\n */\n private escapeCssSelector(value: string): string {\n // Escape special CSS selector characters: \", ', ], \\\n return value.replace(/[\"'\\\\\\]]/g, '\\\\$&')\n }\n}\n","export const DEFAULT_FEATURES_URL = 'https://edge.supaship.com/v1/features'\nexport const DEFAULT_EVENTS_URL = 'https://edge.supaship.com/v1/events'\n","import {\n SupaClientConfig,\n FeatureContext,\n FeatureValue,\n NetworkConfig,\n Features,\n FeaturesWithFallbacks,\n} from './types'\nimport { retry } from './utils'\nimport { SupaPlugin } from './plugins/types'\nimport { SupaToolbarPlugin } from './plugins/toolbar-plugin'\nimport { DEFAULT_FEATURES_URL, DEFAULT_EVENTS_URL } from './constants'\n\ntype RequiredRetryConfig = Required<NonNullable<NetworkConfig['retry']>>\ntype ResolvedNetworkConfig = {\n featuresAPIUrl: string\n eventsAPIUrl: string\n retry: RequiredRetryConfig\n requestTimeoutMs: number\n}\n\nexport class SupaClient<TFeatures extends FeaturesWithFallbacks> {\n private apiKey: string\n private environment: string\n private defaultContext?: FeatureContext\n private plugins: SupaPlugin[]\n private featureDefinitions: Features<TFeatures>\n private clientId: string\n\n private fetchImpl: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>\n private networkConfig: ResolvedNetworkConfig\n\n constructor(config: SupaClientConfig & { features: TFeatures }) {\n this.apiKey = config.apiKey\n this.environment = config.environment\n this.defaultContext = config.context\n this.featureDefinitions = config.features as Features<TFeatures>\n\n // Generate unique client ID\n this.clientId = this.generateClientId()\n\n this.networkConfig = {\n featuresAPIUrl: config.networkConfig?.featuresAPIUrl || DEFAULT_FEATURES_URL,\n eventsAPIUrl: config.networkConfig?.eventsAPIUrl || DEFAULT_EVENTS_URL,\n retry: {\n enabled: config.networkConfig?.retry?.enabled ?? true,\n maxAttempts: config.networkConfig?.retry?.maxAttempts ?? 3,\n backoff: config.networkConfig?.retry?.backoff ?? 1000,\n },\n requestTimeoutMs: config.networkConfig?.requestTimeoutMs ?? 10000,\n }\n\n // Prefer injected fetch, then global fetch if available\n const globalFetch: typeof fetch | undefined =\n typeof globalThis !== 'undefined'\n ? (globalThis as unknown as { fetch?: typeof fetch }).fetch\n : undefined\n if (config.networkConfig?.fetchFn) {\n this.fetchImpl = config.networkConfig.fetchFn\n } else if (typeof globalFetch === 'function') {\n this.fetchImpl = globalFetch.bind(globalThis)\n } else {\n throw new Error(\n 'No fetch implementation available. Provide fetchFn in config or use a runtime with global fetch (e.g., Node 18+, browsers).'\n )\n }\n\n // Initialize plugins with automatic toolbar plugin in browser\n this.plugins = this.initializePlugins(config)\n\n // Initialize plugins with available features and their fallback values\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onInit?.({\n clientId: this.clientId,\n availableFeatures: this.featureDefinitions,\n context: this.defaultContext,\n })\n )\n ).catch(console.error)\n }\n\n /**\n * Generate a unique client ID\n */\n private generateClientId(): string {\n return `supaship-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`\n }\n\n /**\n * Initialize plugins with automatic toolbar plugin in browser environments\n */\n private initializePlugins(config: SupaClientConfig & { features: TFeatures }): SupaPlugin[] {\n const plugins = config.plugins || []\n\n // Check if we're in a browser environment\n const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined'\n\n // If toolbar is explicitly disabled, don't add it\n if (config.toolbar === false) {\n return plugins\n }\n\n // If in browser and toolbar not disabled, add it automatically\n if (isBrowser) {\n // Check if user already added toolbar plugin manually\n const hasToolbarPlugin = plugins.some(p => p.name === 'toolbar-plugin')\n\n if (!hasToolbarPlugin) {\n // Add toolbar with user config or defaults\n const toolbarConfig = config.toolbar || { enabled: 'auto' }\n const toolbarPlugin = new SupaToolbarPlugin(toolbarConfig)\n return [toolbarPlugin, ...plugins]\n }\n }\n\n return plugins\n }\n\n /**\n * Updates the default context for the client\n * @param context - New context to merge with or replace the existing context\n * @param mergeWithExisting - Whether to merge with existing context (default: true)\n */\n updateContext(context: FeatureContext, mergeWithExisting: boolean = true): void {\n const oldContext = this.defaultContext\n\n if (mergeWithExisting && this.defaultContext) {\n this.defaultContext = { ...this.defaultContext, ...context }\n } else {\n this.defaultContext = context\n }\n\n // Notify plugins of context change\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onContextUpdate?.(oldContext, this.defaultContext!, 'updateContext')\n )\n ).catch(console.error)\n }\n\n /**\n * Gets the current default context\n */\n getContext(): FeatureContext | undefined {\n return this.defaultContext\n }\n\n /**\n * Gets the fallback value for a feature from its definition\n */\n getFeatureFallback<TKey extends keyof TFeatures>(featureName: TKey): Features<TFeatures>[TKey] {\n return this.featureDefinitions[featureName]\n }\n\n private getVariationValue(variation: FeatureValue, fallback: FeatureValue): FeatureValue {\n if (variation !== undefined && variation !== null) {\n return variation\n }\n\n return fallback ?? null\n }\n\n async getFeature<TKey extends keyof TFeatures>(\n featureName: TKey,\n options?: { context?: FeatureContext }\n ): Promise<Features<TFeatures>[TKey]> {\n const { context } = options ?? {}\n\n // Only merge context if it's defined and not null\n const mergedContext: FeatureContext | undefined =\n typeof context === 'object' && context !== null\n ? { ...(this.defaultContext ?? {}), ...context }\n : this.defaultContext\n\n try {\n const response = await this.getFeatures([featureName as string], {\n context: mergedContext,\n })\n\n // Get the specific feature value\n const value = response[featureName as string]\n return value as Features<TFeatures>[TKey]\n } catch (error) {\n // Run onError hooks\n await Promise.all(this.plugins.map(plugin => plugin.onError?.(error as Error, mergedContext)))\n\n // Use fallback feature value when API fails\n const fallbackValue = this.featureDefinitions[featureName]\n\n // Notify plugins that fallback was used\n await Promise.all(\n this.plugins.map(plugin =>\n plugin.onFallbackUsed?.(\n featureName as string,\n fallbackValue as FeatureValue,\n error as Error\n )\n )\n )\n return fallbackValue as Features<TFeatures>[TKey]\n }\n }\n\n async getFeatures<TKeys extends readonly (keyof TFeatures)[]>(\n featureNames: TKeys,\n options?: { context?: FeatureContext }\n ): Promise<{ [K in TKeys[number]]: Features<TFeatures>[K] }> {\n const { context: contextOverride } = options ?? {}\n\n // Only merge context if it's defined and not null\n const mergedContext: FeatureContext | undefined =\n typeof contextOverride === 'object' && contextOverride !== null\n ? { ...(this.defaultContext ?? {}), ...contextOverride }\n : this.defaultContext\n\n // Notify plugins of context update for this request\n if (contextOverride) {\n await Promise.all(\n this.plugins.map(plugin =>\n plugin.onContextUpdate?.(this.defaultContext, mergedContext!, 'request')\n )\n )\n }\n\n // Convert feature names to strings for API call\n const featureNamesArray = featureNames.map(name => name as string)\n\n try {\n // Run beforeGetFeatures hooks\n await Promise.all(\n this.plugins.map(plugin => plugin.beforeGetFeatures?.(featureNamesArray, mergedContext))\n )\n\n type FeaturesResponse = { features: Record<string, { variation: FeatureValue }> }\n const fetchFeatures = async (): Promise<Record<string, FeatureValue>> => {\n const url = this.networkConfig.featuresAPIUrl\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n }\n const body = JSON.stringify({\n environment: this.environment,\n features: featureNamesArray,\n context: mergedContext,\n })\n\n // Notify plugins before request\n await Promise.all(this.plugins.map(plugin => plugin.beforeRequest?.(url, body, headers)))\n\n const startTime = Date.now()\n // Support timeout via AbortController when available\n const AbortCtrl: typeof AbortController | undefined =\n typeof globalThis !== 'undefined'\n ? (globalThis as unknown as { AbortController?: typeof AbortController })\n .AbortController\n : undefined\n let controller: AbortController | undefined\n let timeoutId: ReturnType<typeof setTimeout> | undefined\n if (this.networkConfig.requestTimeoutMs && typeof AbortCtrl === 'function') {\n controller = new AbortCtrl()\n timeoutId = setTimeout(() => controller?.abort(), this.networkConfig.requestTimeoutMs)\n }\n let response: Response\n try {\n response = await this.fetchImpl(url, {\n method: 'POST',\n headers,\n body,\n signal: controller?.signal,\n })\n } finally {\n if (timeoutId) clearTimeout(timeoutId)\n }\n const duration = Date.now() - startTime\n\n // Notify plugins after response\n await Promise.all(\n this.plugins.map(plugin => plugin.afterResponse?.(response, { duration }))\n )\n\n if (!response.ok) {\n throw new Error(`Failed to fetch features: ${response.statusText}`)\n }\n\n const data = (await response.json()) as FeaturesResponse\n const result: Record<string, FeatureValue> = {}\n\n featureNamesArray.forEach(name => {\n const variation = data.features[name]?.variation\n result[name] = this.getVariationValue(\n variation,\n this.featureDefinitions[name as keyof TFeatures]\n )\n })\n\n return result\n }\n\n const result = this.networkConfig.retry.enabled\n ? await retry(\n fetchFeatures,\n this.networkConfig.retry.maxAttempts,\n this.networkConfig.retry.backoff,\n (attempt, error, willRetry) => {\n // Notify plugins of retry attempts\n Promise.all(\n this.plugins.map(plugin => plugin.onRetryAttempt?.(attempt, error, willRetry))\n ).catch(console.error)\n }\n )\n : await fetchFeatures()\n\n // Run afterGetFeatures hooks\n await Promise.all(\n this.plugins.map(plugin => plugin.afterGetFeatures?.(result, mergedContext))\n )\n\n // Return the fetched features\n return result as { [K in TKeys[number]]: Features<TFeatures>[K] }\n } catch (error) {\n // Run onError hooks\n await Promise.all(this.plugins.map(plugin => plugin.onError?.(error as Error, mergedContext)))\n\n // Create fallback result with requested feature names\n const fallbackResult: Record<string, FeatureValue> = {}\n\n featureNamesArray.forEach(featureName => {\n fallbackResult[featureName] = this.featureDefinitions[featureName as keyof TFeatures]\n\n // Notify plugins that fallback was used for each feature\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onFallbackUsed?.(\n featureName,\n this.featureDefinitions[featureName as keyof TFeatures],\n error as Error\n )\n )\n ).catch(console.error)\n })\n\n return fallbackResult as { [K in TKeys[number]]: Features<TFeatures>[K] }\n }\n }\n}\n"],"mappings":";AAAO,SAAS,MAAM,IAA2B;AAC/C,SAAO,IAAI,QAAQ,aAAW,WAAW,SAAS,EAAE,CAAC;AACvD;AAEA,eAAsB,MACpB,IACA,cAAsB,GACtB,UAAkB,KAClB,SACY;AACZ,MAAI;AAEJ,WAAS,UAAU,GAAG,WAAW,aAAa,WAAW;AACvD,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,SAAS,OAAO;AACd,kBAAY;AACZ,YAAM,YAAY,UAAU;AAE5B,UAAI,SAAS;AACX,gBAAQ,SAAS,WAAW,SAAS;AAAA,MACvC;AAEA,UAAI,CAAC,UAAW;AAChB,YAAM,MAAM,UAAU,KAAK,IAAI,GAAG,UAAU,CAAC,CAAC;AAAA,IAChD;AAAA,EACF;AAEA,QAAM;AACR;;;ACIA,IAAM,sBAAsB;AAE5B,IAAM,sBAAsB;AAMrB,IAAM,oBAAN,MAA8C;AAAA,EAWnD,YAAY,SAAkC,CAAC,GAAG;AAVlD,gBAAO;AAQP,SAAQ,aAAqB;AAG3B,SAAK,SAAS;AAAA,MACZ,SAAS,OAAO,WAAW;AAAA,MAC3B,UAAU;AAAA,QACR,WAAW,OAAO,UAAU,aAAa;AAAA,QACzC,QAAQ,OAAO,UAAU,UAAU,EAAE,GAAG,QAAQ,GAAG,OAAO;AAAA,MAC5D;AAAA,MACA,kBAAkB,OAAO;AAAA,IAC3B;AAEA,SAAK,QAAQ;AAAA,MACX,WAAW,CAAC;AAAA,MACZ,UAAU,oBAAI,IAAI;AAAA,MAClB,eAAe,CAAC;AAAA,MAChB,aAAa;AAAA,MACb,mBAAmB;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,UAAgB;AACd,SAAK,cAAc;AAAA,EACrB;AAAA,EAEQ,oBAA6B;AACnC,QAAI,KAAK,OAAO,YAAY,KAAM,QAAO;AACzC,QAAI,KAAK,OAAO,YAAY,MAAO,QAAO;AAG1C,QAAI,OAAO,WAAW,aAAa;AACjC,aACE,OAAO,SAAS,aAAa,eAC7B,OAAO,SAAS,aAAa,eAC7B,OAAO,SAAS,aAAa,MAC7B,OAAO,SAAS,SAAS,SAAS,QAAQ,KAC1C,OAAO,SAAS,SAAS,SAAS,YAAY;AAAA,IAElD;AACA,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,QAIE;AACP,UAAM,EAAE,mBAAmB,SAAS,SAAS,IAAI;AAGjD,SAAK,WAAW;AAGhB,SAAK,aAAa;AAGlB,SAAK,MAAM,YAAY,KAAK,cAAc;AAG1C,SAAK,MAAM,WAAW,IAAI,IAAI,OAAO,KAAK,iBAAiB,CAAC;AAC5D,SAAK,MAAM,gBAAgB,EAAE,GAAG,kBAAkB;AAClD,SAAK,MAAM,UAAU;AAGrB,QAAI,KAAK,kBAAkB,GAAG;AAC5B,WAAK,cAAc;AAAA,IACrB;AAGA,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,MAAM,kBAAkB,eAAyB,SAAyC;AAExF,SAAK,MAAM,UAAU;AAGrB,SAAK,MAAM,YAAY,KAAK,cAAc;AAG1C,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,MAAM,iBACJ,SACA,SACe;AAEf,WAAO,KAAK,OAAO,EAAE,QAAQ,UAAQ;AACnC,WAAK,MAAM,cAAc,IAAI,IAAI,QAAQ,IAAI;AAAA,IAC/C,CAAC;AAGD,QAAI,KAAK,MAAM,mBAAmB;AAChC,aAAO,KAAK,KAAK,MAAM,SAAS,EAAE,QAAQ,iBAAe;AACvD,YAAI,eAAe,SAAS;AAC1B,kBAAQ,WAAW,IAAI,KAAK,MAAM,UAAU,WAAW;AAAA,QACzD;AAAA,MACF,CAAC;AAAA,IACH;AAGA,WAAO,KAAK,OAAO,EAAE,QAAQ,UAAQ,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC;AAClE,SAAK,MAAM,UAAU;AACrB,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEQ,gBAA8C;AACpD,QAAI,OAAO,WAAW,eAAe,CAAC,OAAO,cAAc;AACzD,aAAO,CAAC;AAAA,IACV;AAEA,QAAI;AACF,YAAM,SAAS,OAAO,aAAa,QAAQ,KAAK,UAAU;AAC1D,aAAO,SAAS,KAAK,MAAM,MAAM,IAAI,CAAC;AAAA,IACxC,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EAEQ,cACN,SACA,OACA,cACM;AACN,QAAI,OAAO,WAAW,eAAe,CAAC,OAAO,cAAc;AACzD;AAAA,IACF;AAEA,QAAI;AACF,aAAO,aAAa,QAAQ,KAAK,YAAY,KAAK,UAAU,YAAY,CAAC;AACzE,WAAK,OAAO;AAAA,QACV,EAAE,SAAS,WAAW,IAAI,OAAO,SAAS,KAAK;AAAA,QAC/C,gBAAgB,CAAC;AAAA,MACnB;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,+CAA+C,KAAK;AAAA,IACpE;AAAA,EACF;AAAA,EAEO,YAAY,aAAqB,OAA2B;AACjE,SAAK,MAAM,UAAU,WAAW,IAAI;AACpC,SAAK,cAAc,aAAa,OAAO,KAAK,MAAM,SAAS;AAC3D,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEO,eAAe,aAA2B;AAC/C,WAAO,KAAK,MAAM,UAAU,WAAW;AACvC,SAAK,cAAc,aAAa,MAAM,KAAK,MAAM,SAAS;AAC1D,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEO,oBAA0B;AAC/B,SAAK,MAAM,YAAY,CAAC;AACxB,SAAK,cAAc,IAAI,MAAM,KAAK,MAAM,SAAS;AACjD,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEO,eAA6C;AAClD,WAAO,EAAE,GAAG,KAAK,MAAM,UAAU;AAAA,EACnC;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,OAAO,WAAW,eAAe,OAAO,aAAa,aAAa;AACpE;AAAA,IACF;AAGA,UAAM,YAAY,oBAAoB,KAAK,QAAQ;AACnD,QAAI,SAAS,eAAe,SAAS,GAAG;AACtC;AAAA,IACF;AAGA,UAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,YAAQ,KAAK;AACb,YAAQ,aAAa,wBAAwB,KAAK,YAAY,EAAE;AAChE,YAAQ,YAAY,KAAK,eAAe;AAGxC,SAAK,aAAa;AAGlB,aAAS,KAAK,YAAY,OAAO;AAGjC,SAAK,qBAAqB;AAAA,EAC5B;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,OAAO,aAAa,aAAa;AACnC;AAAA,IACF;AAEA,UAAM,UAAU,SAAS,eAAe,oBAAoB,KAAK,QAAQ,EAAE;AAC3E,QAAI,SAAS;AACX,cAAQ,OAAO;AAAA,IACjB;AAEA,UAAM,SAAS,SAAS,eAAe,yBAAyB;AAChE,QAAI,QAAQ;AACV,aAAO,OAAO;AAAA,IAChB;AAAA,EACF;AAAA,EAEQ,iBAAyB;AAC/B,UAAM,EAAE,WAAW,OAAO,IAAI,KAAK,OAAO;AAC1C,UAAM,gBAAgB,oBAAoB,SAAS;AACnD,UAAM,UAAU,QAAQ,KAAK;AAC7B,UAAM,UAAU,QAAQ,KAAK;AAC7B,UAAM,WAAW,2BAA2B,KAAK,QAAQ;AACzD,UAAM,UAAU,0BAA0B,KAAK,QAAQ;AACvD,UAAM,WAAW,yBAAyB,KAAK,QAAQ;AACvD,UAAM,UAAU,sBAAsB,KAAK,QAAQ;AACnD,UAAM,YAAY,4BAA4B,KAAK,QAAQ;AAC3D,UAAM,UAAU,0BAA0B,KAAK,QAAQ;AACvD,UAAM,wBAAwB,kCAAkC,KAAK,QAAQ;AAE7E,WAAO;AAAA,+CACoC,aAAa,wBAAwB,OAAO,iBAAiB,OAAO;AAAA,sDAC7D,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qDAsCT,OAAO;AAAA;AAAA,kDAEV,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,oBAKrC,QAAQ;AAAA;AAAA;AAAA,iEAGqC,qBAAqB;AAAA;AAAA;AAAA,oBAGlE,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sDAS2B,SAAS;AAAA,kDACb,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKnE;AAAA,EAEQ,eAAqB;AAC3B,QAAI,OAAO,aAAa,aAAa;AACnC;AAAA,IACF;AAEA,QAAI,SAAS,eAAe,yBAAyB,GAAG;AACtD;AAAA,IACF;AAEA,UAAM,SAAS,SAAS,cAAc,OAAO;AAC7C,WAAO,KAAK;AACZ,WAAO,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0XrB,aAAS,KAAK,YAAY,MAAM;AAAA,EAClC;AAAA,EAEQ,uBAA6B;AACnC,QAAI,OAAO,aAAa,aAAa;AACnC;AAAA,IACF;AAEA,UAAM,WAAW,2BAA2B,KAAK,QAAQ;AACzD,UAAM,UAAU,0BAA0B,KAAK,QAAQ;AACvD,UAAM,UAAU,sBAAsB,KAAK,QAAQ;AACnD,UAAM,WAAW,yBAAyB,KAAK,QAAQ;AACvD,UAAM,YAAY,4BAA4B,KAAK,QAAQ;AAE3D,UAAM,SAAS,SAAS,eAAe,QAAQ;AAC/C,UAAM,QAAQ,SAAS,eAAe,OAAO;AAC7C,UAAM,WAAW,SAAS,eAAe,OAAO;AAChD,UAAM,cAAc,SAAS,eAAe,QAAQ;AACpD,UAAM,UAAU,SAAS,eAAe,SAAS;AAEjD,YAAQ,iBAAiB,SAAS,MAAM;AACtC,aAAO,UAAU,OAAO,MAAM;AAAA,IAChC,CAAC;AAED,cAAU,iBAAiB,SAAS,MAAM;AACxC,WAAK,kBAAkB;AAAA,IACzB,CAAC;AAED,iBAAa,iBAAiB,SAAS,OAAK;AAC1C,WAAK,MAAM,cAAe,EAAE,OAA4B,MAAM,YAAY;AAC1E,WAAK,gBAAgB;AAAA,IACvB,CAAC;AAGD,QAAI,SAAS;AAEX,cAAQ,iBAAiB,SAAS,CAAC,MAAa;AAC9C,cAAM,SAAS,EAAE;AACjB,cAAM,gBAAgB,OAAO,QAAQ,qBAAqB;AAC1D,YAAI,CAAC,cAAe;AAEpB,UAAE,eAAe;AACjB,UAAE,gBAAgB;AAElB,cAAM,cAAc,cAAc,QAAQ;AAC1C,cAAM,SAAS,cAAc,QAAQ;AAErC,YAAI,WAAW,UAAU;AACvB,eAAK,eAAe,WAAW;AAAA,QACjC,WAAW,WAAW,OAAO;AAC3B,gBAAM,WAAW,QAAQ;AAAA,YACvB,0BAA0B,KAAK,kBAAkB,WAAW,CAAC;AAAA,UAC/D;AACA,cAAI,YAAY,SAAS,MAAM,KAAK,GAAG;AACrC,gBAAI;AACF,oBAAM,QAAQ,KAAK,MAAM,SAAS,KAAK;AACvC,mBAAK,YAAY,aAAa,KAAK;AAAA,YACrC,QAAQ;AAEN,mBAAK,YAAY,aAAa,EAAE,OAAO,SAAS,MAAM,CAAC;AAAA,YACzD;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAGD,cAAQ,iBAAiB,UAAU,CAAC,MAAa;AAC/C,cAAM,SAAS,EAAE;AACjB,YAAI,OAAO,SAAS,cAAc,OAAO,QAAQ,SAAS,WAAW;AACnE,gBAAM,cAAc,OAAO,QAAQ;AACnC,gBAAM,WAAW,OAAO;AACxB,eAAK,YAAY,aAAa,QAAQ;AAAA,QACxC;AAAA,MACF,CAAC;AAGD,cAAQ,iBAAiB,SAAS,CAAC,MAAa;AAC9C,cAAM,SAAS,EAAE;AACjB,YAAI,OAAO,YAAY,cAAc,OAAO,QAAQ,SAAS;AAC3D,gBAAM,cAAc,OAAO,QAAQ;AACnC,gBAAM,gBAAgB,OAAO,QAAQ,YAAY;AACjD,gBAAM,cAAc,QAAQ;AAAA,YAC1B,2CAA2C,KAAK,kBAAkB,WAAW,CAAC;AAAA,UAChF;AAEA,cAAI,aAAa;AACf,kBAAM,aAAa,OAAO,UAAU;AACpC,kBAAM,aAAa,OAAO,MAAM,KAAK,EAAE,SAAS;AAChD,wBAAY,WAAW,CAAC,cAAc,CAAC;AAAA,UACzC;AAAA,QACF;AAAA,MACF,CAAC;AAGD,cAAQ,iBAAiB,SAAS,CAAC,MAAa;AAC9C,cAAM,SAAS,EAAE;AACjB,YAAI,OAAO,YAAY,cAAc,OAAO,QAAQ,SAAS;AAC3D,qBAAW,MAAM;AACf,kBAAM,cAAc,OAAO,QAAQ;AACnC,kBAAM,gBAAgB,OAAO,QAAQ,YAAY;AACjD,kBAAM,cAAc,QAAQ;AAAA,cAC1B,2CAA2C,WAAW;AAAA,YACxD;AAEA,gBAAI,aAAa;AACf,oBAAM,aAAa,OAAO,UAAU;AACpC,oBAAM,aAAa,OAAO,MAAM,KAAK,EAAE,SAAS;AAChD,0BAAY,WAAW,CAAC,cAAc,CAAC;AAAA,YACzC;AAAA,UACF,GAAG,CAAC;AAAA,QACN;AAAA,MACF,CAAC;AAGD,cAAQ,iBAAiB,WAAW,CAAC,MAAqB;AACxD,cAAM,SAAS,EAAE;AACjB,YACE,OAAO,YAAY,cACnB,OAAO,QAAQ,YACd,EAAE,WAAW,EAAE,YAChB,EAAE,QAAQ,SACV;AACA,YAAE,eAAe;AACjB,gBAAM,cAAc,OAAO,QAAQ;AACnC,gBAAM,cAAc,QAAQ;AAAA,YAC1B,2CAA2C,KAAK,kBAAkB,WAAW,CAAC;AAAA,UAChF;AAEA,cAAI,eAAe,CAAC,YAAY,UAAU;AACxC,wBAAY,MAAM;AAAA,UACpB;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEQ,kBAAwB;AAC9B,QAAI,OAAO,aAAa,aAAa;AACnC;AAAA,IACF;AAEA,UAAM,YAAY,4BAA4B,KAAK,QAAQ;AAC3D,UAAM,UAAU,sBAAsB,KAAK,QAAQ;AACnD,UAAM,UAAU,0BAA0B,KAAK,QAAQ;AACvD,UAAM,wBAAwB,kCAAkC,KAAK,QAAQ;AAC7E,UAAM,WAAW,2BAA2B,KAAK,QAAQ;AAEzD,UAAM,UAAU,SAAS,eAAe,SAAS;AACjD,UAAM,cAAc,SAAS,eAAe,OAAO;AACnD,UAAM,QAAQ,SAAS,eAAe,OAAO;AAC7C,UAAM,sBAAsB,SAAS,eAAe,qBAAqB;AACzE,UAAM,YAAY,SAAS,eAAe,QAAQ;AAElD,QAAI,CAAC,SAAS;AACZ,cAAQ,KAAK,wCAAwC,SAAS;AAC9D;AAAA,IACF;AAGA,UAAM,gBAAgB,OAAO,KAAK,KAAK,MAAM,SAAS,EAAE;AACxD,UAAM,eAAe,gBAAgB;AACrC,QAAI,aAAa;AACf,kBAAY,WAAW,CAAC;AAAA,IAC1B;AAGA,QAAI,OAAO;AACT,UAAI,cAAc;AAChB,cAAM,cAAc,gBAAgB,KAAK,QAAQ,OAAO,aAAa;AACrE,cAAM,UAAU,IAAI,MAAM;AAAA,MAC5B,OAAO;AACL,cAAM,UAAU,OAAO,MAAM;AAAA,MAC/B;AAAA,IACF;AAGA,QAAI,WAAW;AACb,UAAI,cAAc;AAChB,kBAAU;AAAA,UACR;AAAA,UACA,qBAAqB,aAAa,YAAY,kBAAkB,IAAI,KAAK,GAAG;AAAA,QAC9E;AACA,kBAAU;AAAA,UACR;AAAA,UACA,qBAAqB,aAAa,YAAY,kBAAkB,IAAI,KAAK,GAAG;AAAA,QAC9E;AAAA,MACF,OAAO;AACL,kBAAU,aAAa,SAAS,kBAAkB;AAClD,kBAAU,aAAa,cAAc,kBAAkB;AAAA,MACzD;AAAA,IACF;AAGA,QAAI,qBAAqB;AAEvB,YAAM,eAAe,KAAK,WAAW,OAAO,aAAa,CAAC;AAC1D,0BAAoB,YAAY,uDAAuD,eAAe,kBAAkB,EAAE,KAAK,YAAY,mBAAmB,kBAAkB,IAAI,KAAK,GAAG;AAAA,IAC9L;AAEA,UAAM,WAAW,MAAM,KAAK,KAAK,MAAM,QAAQ,EAAE,KAAK;AAGtD,UAAM,mBAAmB,SAAS;AAAA,MAAO,UACvC,KAAK,YAAY,EAAE,SAAS,KAAK,MAAM,WAAW;AAAA,IACpD;AAEA,QAAI,iBAAiB,WAAW,GAAG;AACjC,cAAQ,YAAY,KAAK,MAAM,cAC3B,yEACA,uCAAuC,mBAAmB;AAC9D;AAAA,IACF;AAEA,UAAM,cAAc,iBACjB,IAAI,iBAAe;AAClB,YAAM,cAAc,eAAe,KAAK,MAAM;AAC9C,YAAM,eAAe,KAAK,MAAM,cAAc,WAAW;AACzD,YAAM,gBAAgB,cAAc,KAAK,MAAM,UAAU,WAAW,IAAI;AACxE,YAAM,aAAa,CAAC,KAAK,MAAM;AAC/B,YAAM,YAAY,yBAAyB,aAAa,aAAa,EAAE;AAGvE,YAAM,YACJ,OAAO,iBAAiB,aAAc,eAAe,OAAO,kBAAkB;AAEhF,UAAI,WAAW;AAEb,cAAM,YAAY,cAAc,kBAAkB,OAAO,iBAAiB;AAC1E,eAAO;AAAA,wBACO,SAAS;AAAA;AAAA;AAAA,kBAGf,KAAK,WAAW,WAAW,CAAC;AAAA,kBAC5B,cAAc,6FAA6F,EAAE;AAAA;AAAA;AAAA,kBAI7G,cACI;AAAA;AAAA;AAAA,oCAGc,KAAK,WAAW,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAS1C,EACN;AAAA;AAAA;AAAA;AAAA,sBAIM,YAAY,YAAY,EAAE;AAAA,oCACZ,KAAK,WAAW,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MASxD,OAAO;AAEL,cAAM,sBAAsB,cACxB,KAAK,UAAU,aAAa,IAC5B,iBAAiB,SACf,KAAK,UAAU,YAAY,IAC3B;AACN,cAAM,qBAAqB,KAAK,WAAW,WAAW;AACtD,cAAM,6BAA6B,KAAK,WAAW,mBAAmB;AACtE,cAAM,yBAAyB,cAC3B,KAAK,WAAW,KAAK,UAAU,aAAa,CAAC,IAC7C;AAEJ,eAAO;AAAA,wBACO,SAAS;AAAA;AAAA;AAAA,kBAGf,kBAAkB;AAAA,kBAClB,cAAc,6FAA6F,EAAE;AAAA;AAAA;AAAA,kBAI7G,cACI;AAAA;AAAA;AAAA,sCAGgB,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBASlC,EACN;AAAA;AAAA;AAAA,kCAGkB,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gCAWpB,kBAAkB;AAAA,iCACjB,0BAA0B;AAAA,iBAC1C,sBAAsB;AAAA;AAAA;AAAA;AAAA,MAI/B;AAAA,IACF,CAAC,EACA,KAAK,EAAE;AAEV,0BAAsB,MAAM;AAE1B,cAAQ,YAAY;AAGpB,cAAQ,iBAAiB,wBAAwB,EAAE,QAAQ,cAAY;AACrE,cAAM,kBAAkB;AACxB,cAAM,cAAc,gBAAgB,QAAQ;AAC5C,cAAM,gBAAgB,gBAAgB,QAAQ,YAAY;AAC1D,cAAM,cAAc,QAAQ;AAAA,UAC1B,2CAA2C,KAAK,kBAAkB,WAAW,CAAC;AAAA,QAChF;AAEA,YAAI,aAAa;AACf,gBAAM,aAAa,gBAAgB,UAAU;AAC7C,gBAAM,aAAa,gBAAgB,MAAM,KAAK,EAAE,SAAS;AACzD,sBAAY,WAAW,CAAC,cAAc,CAAC;AAAA,QACzC;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEQ,WAAW,MAAsB;AACvC,UAAM,MAAM,OAAO,aAAa,cAAc,SAAS,cAAc,KAAK,IAAI;AAC9E,QAAI,KAAK;AACP,UAAI,cAAc;AAClB,aAAO,IAAI;AAAA,IACb;AACA,WAAO,KAAK,QAAQ,YAAY,UAAQ;AACtC,YAAM,YAAoC;AAAA,QACxC,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,MACP;AACA,aAAO,UAAU,IAAI;AAAA,IACvB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,kBAAkB,OAAuB;AAE/C,WAAO,MAAM,QAAQ,aAAa,MAAM;AAAA,EAC1C;AACF;;;AC5kCO,IAAM,uBAAuB;AAC7B,IAAM,qBAAqB;;;ACoB3B,IAAM,aAAN,MAA0D;AAAA,EAW/D,YAAY,QAAoD;AAC9D,SAAK,SAAS,OAAO;AACrB,SAAK,cAAc,OAAO;AAC1B,SAAK,iBAAiB,OAAO;AAC7B,SAAK,qBAAqB,OAAO;AAGjC,SAAK,WAAW,KAAK,iBAAiB;AAEtC,SAAK,gBAAgB;AAAA,MACnB,gBAAgB,OAAO,eAAe,kBAAkB;AAAA,MACxD,cAAc,OAAO,eAAe,gBAAgB;AAAA,MACpD,OAAO;AAAA,QACL,SAAS,OAAO,eAAe,OAAO,WAAW;AAAA,QACjD,aAAa,OAAO,eAAe,OAAO,eAAe;AAAA,QACzD,SAAS,OAAO,eAAe,OAAO,WAAW;AAAA,MACnD;AAAA,MACA,kBAAkB,OAAO,eAAe,oBAAoB;AAAA,IAC9D;AAGA,UAAM,cACJ,OAAO,eAAe,cACjB,WAAmD,QACpD;AACN,QAAI,OAAO,eAAe,SAAS;AACjC,WAAK,YAAY,OAAO,cAAc;AAAA,IACxC,WAAW,OAAO,gBAAgB,YAAY;AAC5C,WAAK,YAAY,YAAY,KAAK,UAAU;AAAA,IAC9C,OAAO;AACL,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAGA,SAAK,UAAU,KAAK,kBAAkB,MAAM;AAG5C,YAAQ;AAAA,MACN,KAAK,QAAQ;AAAA,QAAI,YACf,OAAO,SAAS;AAAA,UACd,UAAU,KAAK;AAAA,UACf,mBAAmB,KAAK;AAAA,UACxB,SAAS,KAAK;AAAA,QAChB,CAAC;AAAA,MACH;AAAA,IACF,EAAE,MAAM,QAAQ,KAAK;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAA2B;AACjC,WAAO,YAAY,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,EAC7E;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,QAAkE;AAC1F,UAAM,UAAU,OAAO,WAAW,CAAC;AAGnC,UAAM,YAAY,OAAO,WAAW,eAAe,OAAO,aAAa;AAGvE,QAAI,OAAO,YAAY,OAAO;AAC5B,aAAO;AAAA,IACT;AAGA,QAAI,WAAW;AAEb,YAAM,mBAAmB,QAAQ,KAAK,OAAK,EAAE,SAAS,gBAAgB;AAEtE,UAAI,CAAC,kBAAkB;AAErB,cAAM,gBAAgB,OAAO,WAAW,EAAE,SAAS,OAAO;AAC1D,cAAM,gBAAgB,IAAI,kBAAkB,aAAa;AACzD,eAAO,CAAC,eAAe,GAAG,OAAO;AAAA,MACnC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,cAAc,SAAyB,oBAA6B,MAAY;AAC9E,UAAM,aAAa,KAAK;AAExB,QAAI,qBAAqB,KAAK,gBAAgB;AAC5C,WAAK,iBAAiB,EAAE,GAAG,KAAK,gBAAgB,GAAG,QAAQ;AAAA,IAC7D,OAAO;AACL,WAAK,iBAAiB;AAAA,IACxB;AAGA,YAAQ;AAAA,MACN,KAAK,QAAQ;AAAA,QAAI,YACf,OAAO,kBAAkB,YAAY,KAAK,gBAAiB,eAAe;AAAA,MAC5E;AAAA,IACF,EAAE,MAAM,QAAQ,KAAK;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,aAAyC;AACvC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAiD,aAA8C;AAC7F,WAAO,KAAK,mBAAmB,WAAW;AAAA,EAC5C;AAAA,EAEQ,kBAAkB,WAAyB,UAAsC;AACvF,QAAI,cAAc,UAAa,cAAc,MAAM;AACjD,aAAO;AAAA,IACT;AAEA,WAAO,YAAY;AAAA,EACrB;AAAA,EAEA,MAAM,WACJ,aACA,SACoC;AACpC,UAAM,EAAE,QAAQ,IAAI,WAAW,CAAC;AAGhC,UAAM,gBACJ,OAAO,YAAY,YAAY,YAAY,OACvC,EAAE,GAAI,KAAK,kBAAkB,CAAC,GAAI,GAAG,QAAQ,IAC7C,KAAK;AAEX,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,YAAY,CAAC,WAAqB,GAAG;AAAA,QAC/D,SAAS;AAAA,MACX,CAAC;AAGD,YAAM,QAAQ,SAAS,WAAqB;AAC5C,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,YAAM,QAAQ,IAAI,KAAK,QAAQ,IAAI,YAAU,OAAO,UAAU,OAAgB,aAAa,CAAC,CAAC;AAG7F,YAAM,gBAAgB,KAAK,mBAAmB,WAAW;AAGzD,YAAM,QAAQ;AAAA,QACZ,KAAK,QAAQ;AAAA,UAAI,YACf,OAAO;AAAA,YACL;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,YACJ,cACA,SAC2D;AAC3D,UAAM,EAAE,SAAS,gBAAgB,IAAI,WAAW,CAAC;AAGjD,UAAM,gBACJ,OAAO,oBAAoB,YAAY,oBAAoB,OACvD,EAAE,GAAI,KAAK,kBAAkB,CAAC,GAAI,GAAG,gBAAgB,IACrD,KAAK;AAGX,QAAI,iBAAiB;AACnB,YAAM,QAAQ;AAAA,QACZ,KAAK,QAAQ;AAAA,UAAI,YACf,OAAO,kBAAkB,KAAK,gBAAgB,eAAgB,SAAS;AAAA,QACzE;AAAA,MACF;AAAA,IACF;AAGA,UAAM,oBAAoB,aAAa,IAAI,UAAQ,IAAc;AAEjE,QAAI;AAEF,YAAM,QAAQ;AAAA,QACZ,KAAK,QAAQ,IAAI,YAAU,OAAO,oBAAoB,mBAAmB,aAAa,CAAC;AAAA,MACzF;AAGA,YAAM,gBAAgB,YAAmD;AACvE,cAAM,MAAM,KAAK,cAAc;AAC/B,cAAM,UAAkC;AAAA,UACtC,gBAAgB;AAAA,UAChB,eAAe,UAAU,KAAK,MAAM;AAAA,QACtC;AACA,cAAM,OAAO,KAAK,UAAU;AAAA,UAC1B,aAAa,KAAK;AAAA,UAClB,UAAU;AAAA,UACV,SAAS;AAAA,QACX,CAAC;AAGD,cAAM,QAAQ,IAAI,KAAK,QAAQ,IAAI,YAAU,OAAO,gBAAgB,KAAK,MAAM,OAAO,CAAC,CAAC;AAExF,cAAM,YAAY,KAAK,IAAI;AAE3B,cAAM,YACJ,OAAO,eAAe,cACjB,WACE,kBACH;AACN,YAAI;AACJ,YAAI;AACJ,YAAI,KAAK,cAAc,oBAAoB,OAAO,cAAc,YAAY;AAC1E,uBAAa,IAAI,UAAU;AAC3B,sBAAY,WAAW,MAAM,YAAY,MAAM,GAAG,KAAK,cAAc,gBAAgB;AAAA,QACvF;AACA,YAAI;AACJ,YAAI;AACF,qBAAW,MAAM,KAAK,UAAU,KAAK;AAAA,YACnC,QAAQ;AAAA,YACR;AAAA,YACA;AAAA,YACA,QAAQ,YAAY;AAAA,UACtB,CAAC;AAAA,QACH,UAAE;AACA,cAAI,UAAW,cAAa,SAAS;AAAA,QACvC;AACA,cAAM,WAAW,KAAK,IAAI,IAAI;AAG9B,cAAM,QAAQ;AAAA,UACZ,KAAK,QAAQ,IAAI,YAAU,OAAO,gBAAgB,UAAU,EAAE,SAAS,CAAC,CAAC;AAAA,QAC3E;AAEA,YAAI,CAAC,SAAS,IAAI;AAChB,gBAAM,IAAI,MAAM,6BAA6B,SAAS,UAAU,EAAE;AAAA,QACpE;AAEA,cAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,cAAMA,UAAuC,CAAC;AAE9C,0BAAkB,QAAQ,UAAQ;AAChC,gBAAM,YAAY,KAAK,SAAS,IAAI,GAAG;AACvC,UAAAA,QAAO,IAAI,IAAI,KAAK;AAAA,YAClB;AAAA,YACA,KAAK,mBAAmB,IAAuB;AAAA,UACjD;AAAA,QACF,CAAC;AAED,eAAOA;AAAA,MACT;AAEA,YAAM,SAAS,KAAK,cAAc,MAAM,UACpC,MAAM;AAAA,QACJ;AAAA,QACA,KAAK,cAAc,MAAM;AAAA,QACzB,KAAK,cAAc,MAAM;AAAA,QACzB,CAAC,SAAS,OAAO,cAAc;AAE7B,kBAAQ;AAAA,YACN,KAAK,QAAQ,IAAI,YAAU,OAAO,iBAAiB,SAAS,OAAO,SAAS,CAAC;AAAA,UAC/E,EAAE,MAAM,QAAQ,KAAK;AAAA,QACvB;AAAA,MACF,IACA,MAAM,cAAc;AAGxB,YAAM,QAAQ;AAAA,QACZ,KAAK,QAAQ,IAAI,YAAU,OAAO,mBAAmB,QAAQ,aAAa,CAAC;AAAA,MAC7E;AAGA,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,YAAM,QAAQ,IAAI,KAAK,QAAQ,IAAI,YAAU,OAAO,UAAU,OAAgB,aAAa,CAAC,CAAC;AAG7F,YAAM,iBAA+C,CAAC;AAEtD,wBAAkB,QAAQ,iBAAe;AACvC,uBAAe,WAAW,IAAI,KAAK,mBAAmB,WAA8B;AAGpF,gBAAQ;AAAA,UACN,KAAK,QAAQ;AAAA,YAAI,YACf,OAAO;AAAA,cACL;AAAA,cACA,KAAK,mBAAmB,WAA8B;AAAA,cACtD;AAAA,YACF;AAAA,UACF;AAAA,QACF,EAAE,MAAM,QAAQ,KAAK;AAAA,MACvB,CAAC;AAED,aAAO;AAAA,IACT;AAAA,EACF;AACF;","names":["result"]}
1
+ {"version":3,"sources":["../src/utils.ts","../src/plugins/toolbar-plugin.ts","../src/constants.ts","../src/client.ts"],"sourcesContent":["export function sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nexport async function retry<T>(\n fn: () => Promise<T>,\n maxAttempts: number = 3,\n backoff: number = 1000,\n onRetry?: (attempt: number, error: Error, willRetry: boolean) => void\n): Promise<T> {\n let lastError: Error\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn()\n } catch (error) {\n lastError = error as Error\n const willRetry = attempt < maxAttempts\n\n if (onRetry) {\n onRetry(attempt, lastError, willRetry)\n }\n\n if (!willRetry) break\n await sleep(backoff * Math.pow(2, attempt - 1))\n }\n }\n\n throw lastError!\n}\n","import { SupaPlugin, SupaPluginConfig } from './types'\nimport { FeatureContext, FeatureValue } from '../types'\n\nexport interface SupaToolbarPosition {\n placement?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'\n offset?: { x: string; y: string }\n}\n\nexport type SupaToolbarOverrideChange = {\n feature: string\n value: FeatureValue\n}\n\nexport type SupaToolbarOverrideChangeCallback = (\n featureOverride: SupaToolbarOverrideChange,\n allOverrides: Record<string, FeatureValue>\n) => void\n\nexport interface SupaToolbarPluginConfig extends Omit<SupaPluginConfig, 'enabled'> {\n enabled?: boolean | 'auto' // auto means show only on localhost\n position?: SupaToolbarPosition\n onOverrideChange?: SupaToolbarOverrideChangeCallback\n}\n\ninterface SupaToolbarState {\n overrides: Record<string, FeatureValue>\n features: Set<string>\n featureValues: Record<string, FeatureValue>\n context?: FeatureContext\n searchQuery: string\n useLocalOverrides: boolean\n}\n\nconst DEFAULT_STORAGE_KEY = 'supaship-feature-overrides'\n\nconst NO_FEATURES_MESSAGE = `No feature flags configured in the client.`\n\n/**\n * Toolbar plugin for local feature flag testing\n * Provides a visual interface to override feature flags during development\n */\nexport class SupaToolbarPlugin implements SupaPlugin {\n name = 'toolbar-plugin'\n private config: {\n enabled: boolean | 'auto'\n position: Required<SupaToolbarPosition>\n onOverrideChange?: SupaToolbarOverrideChangeCallback\n }\n private state: SupaToolbarState\n private clientId?: string\n private storageKey: string = DEFAULT_STORAGE_KEY\n\n constructor(config: SupaToolbarPluginConfig = {}) {\n this.config = {\n enabled: config.enabled ?? 'auto',\n position: {\n placement: config.position?.placement ?? 'bottom-right',\n offset: config.position?.offset ?? { x: '1rem', y: '1rem' },\n },\n onOverrideChange: config.onOverrideChange,\n }\n\n this.state = {\n overrides: {},\n features: new Set(),\n featureValues: {},\n searchQuery: '',\n useLocalOverrides: true,\n }\n }\n\n cleanup(): void {\n this.removeToolbar()\n }\n\n private shouldShowToolbar(): boolean {\n if (this.config.enabled === true) return true\n if (this.config.enabled === false) return false\n\n // Auto mode: show only on localhost\n if (typeof window !== 'undefined') {\n return (\n window.location.hostname === 'localhost' ||\n window.location.hostname === '127.0.0.1' ||\n window.location.hostname === '' ||\n window.location.hostname.endsWith('.local') ||\n window.location.hostname.endsWith('.localhost')\n )\n }\n return false\n }\n\n onInit(params: {\n availableFeatures: Record<string, FeatureValue>\n context?: FeatureContext\n clientId: string\n }): void {\n const { availableFeatures, context, clientId } = params\n\n // Set client ID for DOM element IDs\n this.clientId = clientId\n\n // Use shared storage key (not client-specific) to persist across refreshes\n this.storageKey = DEFAULT_STORAGE_KEY\n\n // Load overrides from shared storage\n this.state.overrides = this.loadOverrides()\n\n // Initialize with all available features and their fallback values from config\n this.state.features = new Set(Object.keys(availableFeatures))\n this.state.featureValues = { ...availableFeatures }\n this.state.context = context\n\n // Inject toolbar if conditions are met\n if (this.shouldShowToolbar()) {\n this.injectToolbar()\n }\n\n // Update toolbar UI if it exists\n this.updateToolbarUI()\n }\n\n async beforeGetFeatures(_featureNames: string[], context?: FeatureContext): Promise<void> {\n // Update context if it changed\n this.state.context = context\n\n // Load overrides from shared storage\n this.state.overrides = this.loadOverrides()\n\n // Update toolbar UI if it exists\n this.updateToolbarUI()\n }\n\n async afterGetFeatures(\n results: Record<string, FeatureValue>,\n context?: FeatureContext\n ): Promise<void> {\n // Update feature values with fetched results (this replaces config fallback values)\n Object.keys(results).forEach(name => {\n this.state.featureValues[name] = results[name]\n })\n\n // Apply overrides to results only if local overrides are enabled\n if (this.state.useLocalOverrides) {\n Object.keys(this.state.overrides).forEach(featureName => {\n if (featureName in results) {\n results[featureName] = this.state.overrides[featureName]\n }\n })\n }\n\n // Track features and update UI\n Object.keys(results).forEach(name => this.state.features.add(name))\n this.state.context = context\n this.updateToolbarUI()\n }\n\n private loadOverrides(): Record<string, FeatureValue> {\n if (typeof window === 'undefined' || !window.localStorage) {\n return {}\n }\n\n try {\n const stored = window.localStorage.getItem(this.storageKey)\n return stored ? JSON.parse(stored) : {}\n } catch {\n return {}\n }\n }\n\n private saveOverrides(\n feature?: string,\n value?: FeatureValue,\n allOverrides?: Record<string, FeatureValue>\n ): void {\n if (typeof window === 'undefined' || !window.localStorage) {\n return\n }\n\n try {\n window.localStorage.setItem(this.storageKey, JSON.stringify(allOverrides))\n this.config.onOverrideChange?.(\n { feature: feature ?? '', value: value ?? null },\n allOverrides ?? {}\n )\n } catch (error) {\n console.error('Supaship: Failed to save feature overrides:', error)\n }\n }\n\n public setOverride(featureName: string, value: FeatureValue): void {\n this.state.overrides[featureName] = value\n this.saveOverrides(featureName, value, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public removeOverride(featureName: string): void {\n delete this.state.overrides[featureName]\n this.saveOverrides(featureName, null, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public clearAllOverrides(): void {\n this.state.overrides = {}\n this.saveOverrides('', null, this.state.overrides)\n this.updateToolbarUI()\n }\n\n public getOverrides(): Record<string, FeatureValue> {\n return { ...this.state.overrides }\n }\n\n private injectToolbar(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return\n }\n\n // Check if toolbar with this client ID already exists\n const toolbarId = `supaship-toolbar-${this.clientId}`\n if (document.getElementById(toolbarId)) {\n return\n }\n\n // Create toolbar container\n const toolbar = document.createElement('div')\n toolbar.id = toolbarId\n toolbar.setAttribute('data-supaship-client', this.clientId || '')\n toolbar.innerHTML = this.getToolbarHTML()\n\n // Add styles\n this.injectStyles()\n\n // Add to DOM\n document.body.appendChild(toolbar)\n\n // Add event listeners\n this.attachEventListeners()\n }\n\n private removeToolbar(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const toolbar = document.getElementById(`supaship-toolbar-${this.clientId}`)\n if (toolbar) {\n toolbar.remove()\n }\n\n const styles = document.getElementById('supaship-toolbar-styles')\n if (styles) {\n styles.remove()\n }\n }\n\n private getToolbarHTML(): string {\n const { placement, offset } = this.config.position\n const positionClass = `supaship-toolbar-${placement}`\n const offsetX = offset?.x ?? '1rem'\n const offsetY = offset?.y ?? '1rem'\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n const panelId = `supaship-toolbar-panel-${this.clientId}`\n const searchId = `supaship-search-input-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const closeId = `supaship-close-toolbar-${this.clientId}`\n const contentId = `supaship-toolbar-content-${this.clientId}`\n const badgeId = `supaship-toolbar-badge-${this.clientId}`\n const headerOverrideCountId = `supaship-header-override-count-${this.clientId}`\n\n return `\n <div class=\"supaship-toolbar-container ${positionClass}\" style=\"--offset-x: ${offsetX}; --offset-y: ${offsetY};\">\n <button class=\"supaship-toolbar-toggle\" id=\"${toggleId}\" aria-label=\"Supaship Toolbar\" title=\"Supaship Toolbar\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 256 256\"\n width=\"32\"\n style=\"vertical-align: middle;\">\n <rect width='256' height='256' rx='128' fill='#e4f222'></rect>\n <line\n x1='66'\n y1='118'\n x2='118'\n y2='66'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n <line\n x1='186'\n y1='70'\n x2='70'\n y2='186'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n <line\n x1='190'\n y1='138'\n x2='138'\n y2='190'\n fill='none'\n stroke='#000'\n stroke-linecap='round'\n stroke-linejoin='round'\n stroke-width='24'></line>\n </svg>\n <span class=\"supaship-toolbar-badge\" id=\"${badgeId}\"></span>\n </button>\n <div class=\"supaship-toolbar-panel\" id=\"${panelId}\">\n <div class=\"supaship-toolbar-header\">\n <input\n type=\"text\"\n class=\"supaship-search-input\"\n id=\"${searchId}\"\n placeholder=\"Search features\"\n />\n <span class=\"supaship-toolbar-overrides-label\" id=\"${headerOverrideCountId}\">0 overrides</span>\n <button\n class=\"supaship-header-btn\"\n id=\"${clearId}\"\n aria-label=\"Reset all overrides\"\n title=\"Reset all overrides to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/>\n <path d=\"M3 3v5h5\"/>\n <path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/>\n <path d=\"M16 16h5v5\"/>\n </svg>\n </button>\n <button\n class=\"supaship-header-btn\"\n id=\"${closeId}\"\n aria-label=\"Close toolbar\"\n title=\"Close toolbar\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6 6 18\"/>\n <path d=\"m6 6 12 12\"/>\n </svg>\n </button>\n </div>\n <div class=\"supaship-toolbar-content\" id=\"${contentId}\">\n <div class=\"supaship-toolbar-empty\">${NO_FEATURES_MESSAGE}</div>\n </div>\n </div>\n </div>\n `\n }\n\n private injectStyles(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n if (document.getElementById('supaship-toolbar-styles')) {\n return\n }\n\n const styles = document.createElement('style')\n styles.id = 'supaship-toolbar-styles'\n styles.textContent = `\n .supaship-toolbar-container {\n position: fixed;\n z-index: 999999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n font-size: 14px;\n }\n\n .supaship-toolbar-bottom-right {\n bottom: var(--offset-y);\n right: var(--offset-x);\n }\n\n .supaship-toolbar-bottom-left {\n bottom: var(--offset-y);\n left: var(--offset-x);\n }\n\n .supaship-toolbar-top-right {\n top: var(--offset-y);\n right: var(--offset-x);\n }\n\n .supaship-toolbar-top-left {\n top: var(--offset-y);\n left: var(--offset-x);\n }\n\n .supaship-toolbar-toggle {\n position: relative;\n width: 36px;\n height: 36px;\n border-radius: 100%;\n border: none;\n color: white;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n transition: transform 0.2s, box-shadow 0.2s;\n }\n\n .supaship-toolbar-toggle:hover {\n transform: scale(1.05);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);\n }\n\n .supaship-toolbar-badge {\n position: absolute;\n top: -4px;\n right: -4px;\n background: #ef4444;\n color: white;\n font-size: 10px;\n font-weight: 600;\n min-width: 18px;\n height: 18px;\n border-radius: 9px;\n display: none;\n align-items: center;\n justify-content: center;\n padding: 0 5px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n pointer-events: none;\n }\n\n .supaship-toolbar-badge.show {\n display: flex;\n }\n\n .supaship-toolbar-panel {\n position: absolute;\n bottom: 48px;\n right: 0;\n width: 300px;\n max-height: 600px;\n background: #1a1a1a;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);\n display: none;\n flex-direction: column;\n overflow: hidden;\n border: 1px solid #333;\n }\n\n .supaship-toolbar-bottom-left .supaship-toolbar-panel,\n .supaship-toolbar-top-left .supaship-toolbar-panel {\n right: auto;\n left: 0;\n }\n\n .supaship-toolbar-top-right .supaship-toolbar-panel,\n .supaship-toolbar-top-left .supaship-toolbar-panel {\n bottom: auto;\n top: 60px;\n }\n\n .supaship-toolbar-panel.open {\n display: flex;\n }\n\n .supaship-toolbar-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n border-bottom: 1px solid #333;\n background: #0f0f0f;\n min-width: 0;\n overflow: hidden;\n }\n\n .supaship-search-input {\n flex: 1;\n min-width: 0;\n background: transparent;\n border: none;\n color: #e5e5e5;\n padding: 0;\n font-size: 13px;\n outline: none;\n }\n\n .supaship-search-input::placeholder {\n color: #888;\n }\n\n .supaship-header-btn {\n background: transparent;\n border: none;\n color: #e5e5e5;\n width: 24px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.2s;\n padding: 0;\n }\n\n .supaship-header-btn:hover {\n color: #ef4444;\n }\n\n .supaship-toolbar-content {\n flex: 1;\n overflow-y: auto;\n padding: 8px;\n min-height: 300px;\n max-height: 300px;\n }\n\n .supaship-toolbar-empty {\n padding: 32px 16px;\n text-align: center;\n color: #888;\n }\n\n .supaship-feature-item {\n padding: 0 6px;\n }\n\n .supaship-feature-item.disabled {\n opacity: 0.5;\n pointer-events: none;\n }\n\n .supaship-feature-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n min-height: 32px;\n }\n\n .supaship-feature-name {\n font-weight: 500;\n color: #e5e5e5;\n font-size: 13px;\n flex: 1;\n min-width: 0;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n }\n\n .supaship-feature-override-indicator {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #ef4444;\n flex-shrink: 0;\n }\n\n .supaship-toolbar-overrides-label {\n font-size: 12px;\n color: #888;\n white-space: nowrap;\n flex-shrink: 0;\n transition: all 0.2s;\n }\n\n .supaship-toolbar-overrides-label-count.has-overrides {\n background: #ef4444;\n color: white;\n font-size: 10px;\n font-weight: 600;\n min-width: 18px;\n height: 18px;\n border-radius: 9px;\n padding: 2px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n .supaship-feature-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n min-height: 20px;\n }\n\n .supaship-feature-content {\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n .supaship-feature-input {\n flex: 1;\n padding: 6px 8px;\n background: #1a1a1a;\n border: 1px solid #555;\n color: #e5e5e5;\n border-radius: 4px;\n font-size: 13px;\n font-family: 'Monaco', 'Courier New', monospace;\n outline: none;\n resize: vertical;\n min-height: 60px;\n margin-bottom: 8px;\n }\n\n .supaship-feature-input:focus {\n border-color: #667eea;\n }\n\n .supaship-btn {\n padding: 4px 12px;\n border: none;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n }\n\n .supaship-btn-primary {\n background: #444;\n color: white;\n }\n\n .supaship-btn-primary:hover {\n background: #555;\n }\n\n .supaship-btn-secondary {\n background: #444;\n color: #e5e5e5;\n }\n\n .supaship-btn-secondary:hover {\n background: #555;\n }\n\n .supaship-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .supaship-btn:disabled:hover {\n background: #444;\n }\n\n .supaship-header-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n }\n\n .supaship-header-btn:disabled:hover {\n color: #e5e5e5;\n }\n\n .supaship-toggle {\n position: relative;\n display: inline-block;\n width: 32px;\n height: 18px;\n flex-shrink: 0;\n }\n\n .supaship-toggle input {\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .supaship-toggle-slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: #333;\n border: 1px solid #555;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 20px;\n }\n\n .supaship-toggle-slider:before {\n position: absolute;\n content: \"\";\n height: 14px;\n width: 14px;\n left: 2px;\n bottom: 1px;\n background-color: #666;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n border-radius: 50%;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n }\n\n .supaship-toggle input:checked + .supaship-toggle-slider {\n background-color: #fff;\n border-color: #fff;\n }\n\n .supaship-toggle input:checked + .supaship-toggle-slider:before {\n transform: translateX(13px);\n background-color: #000;\n }\n\n .supaship-toggle input:disabled + .supaship-toggle-slider {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .supaship-toggle:hover .supaship-toggle-slider:before {\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);\n }\n\n .supaship-btn-icon {\n background: transparent;\n border: none;\n color: #e5e5e5;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n cursor: pointer;\n padding: 0;\n transition: all 0.2s;\n flex-shrink: 0;\n }\n\n .supaship-btn-icon:hover {\n background: #444;\n color: #ef4444;\n }\n `\n document.head.appendChild(styles)\n }\n\n private attachEventListeners(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n const panelId = `supaship-toolbar-panel-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const closeId = `supaship-close-toolbar-${this.clientId}`\n const searchId = `supaship-search-input-${this.clientId}`\n const contentId = `supaship-toolbar-content-${this.clientId}`\n\n const toggle = document.getElementById(toggleId)\n const panel = document.getElementById(panelId)\n const clearAll = document.getElementById(clearId)\n const close = document.getElementById(closeId)\n const searchInput = document.getElementById(searchId) as HTMLInputElement\n const content = document.getElementById(contentId)\n\n toggle?.addEventListener('click', () => {\n panel?.classList.toggle('open')\n })\n\n close?.addEventListener('click', () => {\n panel?.classList.remove('open')\n })\n\n clearAll?.addEventListener('click', () => {\n this.clearAllOverrides()\n })\n\n searchInput?.addEventListener('input', e => {\n this.state.searchQuery = (e.target as HTMLInputElement).value.toLowerCase()\n this.updateToolbarUI()\n })\n\n // Use event delegation on content element - survives innerHTML updates\n if (content) {\n // Handle button clicks (remove and set actions)\n content.addEventListener('click', (e: Event) => {\n const target = e.target as HTMLElement\n const buttonElement = target.closest('button[data-action]') as HTMLButtonElement\n if (!buttonElement) return\n\n e.preventDefault()\n e.stopPropagation()\n\n const featureName = buttonElement.dataset.feature!\n const action = buttonElement.dataset.action\n\n if (action === 'remove') {\n this.removeOverride(featureName)\n } else if (action === 'set') {\n const textarea = content.querySelector(\n `textarea[data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLTextAreaElement\n if (textarea && textarea.value.trim()) {\n try {\n const value = JSON.parse(textarea.value)\n this.setOverride(featureName, value)\n } catch {\n // If not valid JSON, wrap string in object\n this.setOverride(featureName, { value: textarea.value })\n }\n }\n }\n })\n\n // Handle checkbox changes for boolean toggles\n content.addEventListener('change', (e: Event) => {\n const target = e.target as HTMLInputElement\n if (target.type === 'checkbox' && target.dataset.type === 'boolean') {\n const featureName = target.dataset.feature!\n const newValue = target.checked\n this.setOverride(featureName, newValue)\n }\n })\n\n // Handle textarea input to update button states\n content.addEventListener('input', (e: Event) => {\n const target = e.target as HTMLTextAreaElement\n if (target.tagName === 'TEXTAREA' && target.dataset.feature) {\n const featureName = target.dataset.feature!\n const originalValue = target.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = target.value !== originalValue\n const hasContent = target.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n }\n })\n\n // Handle textarea paste events\n content.addEventListener('paste', (e: Event) => {\n const target = e.target as HTMLTextAreaElement\n if (target.tagName === 'TEXTAREA' && target.dataset.feature) {\n setTimeout(() => {\n const featureName = target.dataset.feature!\n const originalValue = target.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${featureName}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = target.value !== originalValue\n const hasContent = target.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n }, 0)\n }\n })\n\n // Handle Ctrl/Cmd+Enter to set override\n content.addEventListener('keydown', (e: KeyboardEvent) => {\n const target = e.target as HTMLTextAreaElement\n if (\n target.tagName === 'TEXTAREA' &&\n target.dataset.feature &&\n (e.ctrlKey || e.metaKey) &&\n e.key === 'Enter'\n ) {\n e.preventDefault()\n const featureName = target.dataset.feature!\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn && !overrideBtn.disabled) {\n overrideBtn.click()\n }\n }\n })\n }\n }\n\n private updateToolbarUI(): void {\n if (typeof document === 'undefined') {\n return\n }\n\n const contentId = `supaship-toolbar-content-${this.clientId}`\n const clearId = `supaship-clear-all-${this.clientId}`\n const badgeId = `supaship-toolbar-badge-${this.clientId}`\n const headerOverrideCountId = `supaship-header-override-count-${this.clientId}`\n const toggleId = `supaship-toolbar-toggle-${this.clientId}`\n\n const content = document.getElementById(contentId)\n const clearAllBtn = document.getElementById(clearId) as HTMLButtonElement\n const badge = document.getElementById(badgeId)\n const headerOverrideCount = document.getElementById(headerOverrideCountId)\n const toggleBtn = document.getElementById(toggleId) as HTMLButtonElement\n\n if (!content) {\n console.warn('[Toolbar] Content element not found:', contentId)\n return\n }\n\n // Update clear all button state and badge\n const overrideCount = Object.keys(this.state.overrides).length\n const hasOverrides = overrideCount > 0\n if (clearAllBtn) {\n clearAllBtn.disabled = !hasOverrides\n }\n\n // Update badge on toggle button\n if (badge) {\n if (hasOverrides) {\n badge.textContent = overrideCount > 99 ? '99+' : String(overrideCount)\n badge.classList.add('show')\n } else {\n badge.classList.remove('show')\n }\n }\n\n // Update tooltip on toggle button\n if (toggleBtn) {\n if (hasOverrides) {\n toggleBtn.setAttribute(\n 'title',\n `Supaship Toolbar, ${overrideCount} override${overrideCount === 1 ? '' : 's'}`\n )\n toggleBtn.setAttribute(\n 'aria-label',\n `Supaship Toolbar, ${overrideCount} override${overrideCount === 1 ? '' : 's'}`\n )\n } else {\n toggleBtn.setAttribute('title', 'Supaship Toolbar')\n toggleBtn.setAttribute('aria-label', 'Supaship Toolbar')\n }\n }\n\n // Update override count in header\n if (headerOverrideCount) {\n // Escape overrideCount to prevent any potential XSS (defense in depth)\n const escapedCount = this.escapeHtml(String(overrideCount))\n headerOverrideCount.innerHTML = `<span class=\"supaship-toolbar-overrides-label-count ${hasOverrides ? 'has-overrides' : ''}\">${escapedCount}</span> override${overrideCount === 1 ? '' : 's'}`\n }\n\n const features = Array.from(this.state.features).sort()\n\n // Filter features based on search query\n const filteredFeatures = features.filter(name =>\n name.toLowerCase().includes(this.state.searchQuery)\n )\n\n if (filteredFeatures.length === 0) {\n content.innerHTML = this.state.searchQuery\n ? '<div class=\"supaship-toolbar-empty\">No matching features found</div>'\n : `<div class=\"supaship-toolbar-empty\">${NO_FEATURES_MESSAGE}</div>`\n return\n }\n\n const htmlContent = filteredFeatures\n .map(featureName => {\n const hasOverride = featureName in this.state.overrides\n const currentValue = this.state.featureValues[featureName]\n const overrideValue = hasOverride ? this.state.overrides[featureName] : currentValue\n const isDisabled = !this.state.useLocalOverrides\n const itemClass = `supaship-feature-item ${isDisabled ? 'disabled' : ''}`\n\n // Check if the feature is boolean\n const isBoolean =\n typeof currentValue === 'boolean' || (hasOverride && typeof overrideValue === 'boolean')\n\n if (isBoolean) {\n // Render toggle switch for boolean values (single row layout)\n const isChecked = hasOverride ? overrideValue === true : currentValue === true\n return `\n <div class=\"${itemClass}\">\n <div class=\"supaship-feature-row\">\n <span class=\"supaship-feature-name\">\n ${this.escapeHtml(featureName)}\n ${hasOverride ? '<span class=\"supaship-feature-override-indicator\" title=\"Serving local override\"></span>' : ''}\n </span>\n <div class=\"supaship-feature-actions\">\n ${\n hasOverride\n ? `\n <button\n class=\"supaship-btn-icon\"\n data-feature=\"${this.escapeHtml(featureName)}\"\n data-action=\"remove\"\n title=\"Reset to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/>\n <path d=\"M3 3v5h5\"/>\n <path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/>\n <path d=\"M16 16h5v5\"/>\n </svg>\n </button>\n `\n : ''\n }\n <label class=\"supaship-toggle\">\n <input\n type=\"checkbox\"\n ${isChecked ? 'checked' : ''}\n data-feature=\"${this.escapeHtml(featureName)}\"\n data-type=\"boolean\"\n />\n <span class=\"supaship-toggle-slider\"></span>\n </label>\n </div>\n </div>\n </div>\n `\n } else {\n // Render textarea for non-boolean values\n const currentDisplayValue = hasOverride\n ? JSON.stringify(overrideValue)\n : currentValue !== undefined\n ? JSON.stringify(currentValue)\n : ''\n const escapedFeatureName = this.escapeHtml(featureName)\n const escapedCurrentDisplayValue = this.escapeHtml(currentDisplayValue)\n const escapedTextareaContent = hasOverride\n ? this.escapeHtml(JSON.stringify(overrideValue))\n : escapedCurrentDisplayValue\n\n return `\n <div class=\"${itemClass}\">\n <div class=\"supaship-feature-row\">\n <span class=\"supaship-feature-name\">\n ${escapedFeatureName}\n ${hasOverride ? '<span class=\"supaship-feature-override-indicator\" title=\"Serving local override\"></span>' : ''}\n </span>\n <div class=\"supaship-feature-actions\">\n ${\n hasOverride\n ? `\n <button\n class=\"supaship-btn-icon\"\n data-feature=\"${escapedFeatureName}\"\n data-action=\"remove\"\n title=\"Reset to default\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/>\n <path d=\"M3 3v5h5\"/>\n <path d=\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\"/>\n <path d=\"M16 16h5v5\"/>\n </svg>\n </button>\n `\n : ''\n }\n <button\n class=\"supaship-btn supaship-btn-primary\"\n data-feature=\"${escapedFeatureName}\"\n data-action=\"set\"\n disabled>\n Override\n </button>\n </div>\n </div>\n <div class=\"supaship-feature-content\">\n <textarea\n class=\"supaship-feature-input\"\n placeholder=\"Override JSON value\"\n data-feature=\"${escapedFeatureName}\"\n data-original=\"${escapedCurrentDisplayValue}\"\n >${escapedTextareaContent}</textarea>\n </div>\n </div>\n `\n }\n })\n .join('')\n\n requestAnimationFrame(() => {\n // Set innerHTML - event listeners are handled via delegation in attachEventListeners()\n content.innerHTML = htmlContent\n\n // Update button states for textareas that already have values\n content.querySelectorAll('textarea[data-feature]').forEach(textarea => {\n const textareaElement = textarea as HTMLTextAreaElement\n const featureName = textareaElement.dataset.feature!\n const originalValue = textareaElement.dataset.original || ''\n const overrideBtn = content.querySelector(\n `button[data-action=\"set\"][data-feature=\"${this.escapeCssSelector(featureName)}\"]`\n ) as HTMLButtonElement\n\n if (overrideBtn) {\n const hasChanged = textareaElement.value !== originalValue\n const hasContent = textareaElement.value.trim().length > 0\n overrideBtn.disabled = !hasChanged || !hasContent\n }\n })\n })\n }\n\n private escapeHtml(text: string): string {\n const div = typeof document !== 'undefined' ? document.createElement('div') : null\n if (div) {\n div.textContent = text\n return div.innerHTML\n }\n return text.replace(/[&<>\"']/g, char => {\n const escapeMap: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n }\n return escapeMap[char]\n })\n }\n\n /**\n * Escapes special characters in CSS attribute selectors to prevent CSS injection\n * @param value The value to escape for use in CSS attribute selectors\n */\n private escapeCssSelector(value: string): string {\n // Escape special CSS selector characters: \", ', ], \\\n return value.replace(/[\"'\\\\\\]]/g, '\\\\$&')\n }\n}\n","export const DEFAULT_FEATURES_URL = 'https://edge.supaship.com/v1/features'\nexport const DEFAULT_EVENTS_URL = 'https://edge.supaship.com/v1/events'\n","import {\n SupaClientConfig,\n FeatureContext,\n FeatureValue,\n NetworkConfig,\n Features,\n FeaturesWithFallbacks,\n} from './types'\nimport { retry } from './utils'\nimport { SupaPlugin } from './plugins/types'\nimport { SupaToolbarPlugin } from './plugins/toolbar-plugin'\nimport { DEFAULT_FEATURES_URL, DEFAULT_EVENTS_URL } from './constants'\n\ntype RequiredRetryConfig = Required<NonNullable<NetworkConfig['retry']>>\ntype ResolvedNetworkConfig = {\n featuresAPIUrl: string\n eventsAPIUrl: string\n retry: RequiredRetryConfig\n requestTimeoutMs: number\n}\n\nexport class SupaClient<TFeatures extends FeaturesWithFallbacks> {\n private apiKey: string\n private environment: string\n private defaultContext?: FeatureContext\n private plugins: SupaPlugin[]\n private featureDefinitions: Features<TFeatures>\n private clientId: string\n\n private fetchImpl: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>\n private networkConfig: ResolvedNetworkConfig\n\n constructor(config: SupaClientConfig & { features: TFeatures }) {\n this.apiKey = config.apiKey\n this.environment = config.environment\n this.defaultContext = config.context\n this.featureDefinitions = config.features as Features<TFeatures>\n\n // Generate unique client ID\n this.clientId = this.generateClientId()\n\n this.networkConfig = {\n featuresAPIUrl: config.networkConfig?.featuresAPIUrl || DEFAULT_FEATURES_URL,\n eventsAPIUrl: config.networkConfig?.eventsAPIUrl || DEFAULT_EVENTS_URL,\n retry: {\n enabled: config.networkConfig?.retry?.enabled ?? true,\n maxAttempts: config.networkConfig?.retry?.maxAttempts ?? 3,\n backoff: config.networkConfig?.retry?.backoff ?? 1000,\n },\n requestTimeoutMs: config.networkConfig?.requestTimeoutMs ?? 10000,\n }\n\n // Prefer injected fetch, then global fetch if available\n const globalFetch: typeof fetch | undefined =\n typeof globalThis !== 'undefined'\n ? (globalThis as unknown as { fetch?: typeof fetch }).fetch\n : undefined\n if (config.networkConfig?.fetchFn) {\n this.fetchImpl = config.networkConfig.fetchFn\n } else if (typeof globalFetch === 'function') {\n this.fetchImpl = globalFetch.bind(globalThis)\n } else {\n throw new Error(\n 'No fetch implementation available. Provide fetchFn in config or use a runtime with global fetch (e.g., Node 18+, browsers).'\n )\n }\n\n // Initialize plugins with automatic toolbar plugin in browser\n this.plugins = this.initializePlugins(config)\n\n // Initialize plugins with available features and their fallback values\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onInit?.({\n clientId: this.clientId,\n availableFeatures: this.featureDefinitions,\n context: this.defaultContext,\n })\n )\n ).catch(console.error)\n }\n\n /**\n * Generate a unique client ID\n */\n private generateClientId(): string {\n return `supaship-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`\n }\n\n /**\n * Initialize plugins with automatic toolbar plugin in browser environments\n */\n private initializePlugins(config: SupaClientConfig & { features: TFeatures }): SupaPlugin[] {\n const plugins = config.plugins || []\n\n // Check if we're in a browser environment\n const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined'\n\n // If toolbar is explicitly disabled, don't add it\n if (config.toolbar === false) {\n return plugins\n }\n\n // If in browser and toolbar not disabled, add it automatically\n if (isBrowser) {\n // Check if user already added toolbar plugin manually\n const hasToolbarPlugin = plugins.some(p => p.name === 'toolbar-plugin')\n\n if (!hasToolbarPlugin) {\n // Add toolbar with user config or defaults\n const toolbarConfig = config.toolbar || { enabled: 'auto' }\n const toolbarPlugin = new SupaToolbarPlugin(toolbarConfig)\n return [toolbarPlugin, ...plugins]\n }\n }\n\n return plugins\n }\n\n /**\n * Updates the default context for the client\n * @param context - New context to merge with or replace the existing context\n * @param mergeWithExisting - Whether to merge with existing context (default: true)\n */\n updateContext(context: FeatureContext, mergeWithExisting: boolean = true): void {\n const oldContext = this.defaultContext\n\n if (mergeWithExisting && this.defaultContext) {\n this.defaultContext = { ...this.defaultContext, ...context }\n } else {\n this.defaultContext = context\n }\n\n // Notify plugins of context change\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onContextUpdate?.(oldContext, this.defaultContext!, 'updateContext')\n )\n ).catch(console.error)\n }\n\n /**\n * Gets the current default context\n */\n getContext(): FeatureContext | undefined {\n return this.defaultContext\n }\n\n /**\n * Gets the fallback value for a feature from its definition\n */\n getFeatureFallback<TKey extends keyof TFeatures>(featureName: TKey): Features<TFeatures>[TKey] {\n return this.featureDefinitions[featureName]\n }\n\n private getVariationValue(variation: FeatureValue, fallback: FeatureValue): FeatureValue {\n if (variation !== undefined && variation !== null) {\n return variation\n }\n\n return fallback ?? null\n }\n\n async getFeature<TKey extends keyof TFeatures>(\n featureName: TKey,\n options?: { context?: FeatureContext }\n ): Promise<Features<TFeatures>[TKey]> {\n const { context } = options ?? {}\n\n // Only merge context if it's defined and not null\n const mergedContext: FeatureContext | undefined =\n typeof context === 'object' && context !== null\n ? { ...(this.defaultContext ?? {}), ...context }\n : this.defaultContext\n\n try {\n const response = await this.getFeatures([featureName as string], {\n context: mergedContext,\n })\n\n // Get the specific feature value\n const value = response[featureName as string]\n return value as Features<TFeatures>[TKey]\n } catch (error) {\n // Run onError hooks\n await Promise.all(this.plugins.map(plugin => plugin.onError?.(error as Error, mergedContext)))\n\n // Use fallback feature value when API fails\n const fallbackValue = this.featureDefinitions[featureName]\n\n // Notify plugins that fallback was used\n await Promise.all(\n this.plugins.map(plugin =>\n plugin.onFallbackUsed?.(\n featureName as string,\n fallbackValue as FeatureValue,\n error as Error\n )\n )\n )\n return fallbackValue as Features<TFeatures>[TKey]\n }\n }\n\n async getFeatures<TKeys extends readonly (keyof TFeatures)[]>(\n featureNames: TKeys,\n options?: { context?: FeatureContext }\n ): Promise<{ [K in TKeys[number]]: Features<TFeatures>[K] }> {\n const { context: contextOverride } = options ?? {}\n\n // Only merge context if it's defined and not null\n const mergedContext: FeatureContext | undefined =\n typeof contextOverride === 'object' && contextOverride !== null\n ? { ...(this.defaultContext ?? {}), ...contextOverride }\n : this.defaultContext\n\n // Notify plugins of context update for this request\n if (contextOverride) {\n await Promise.all(\n this.plugins.map(plugin =>\n plugin.onContextUpdate?.(this.defaultContext, mergedContext!, 'request')\n )\n )\n }\n\n // Convert feature names to strings for API call\n const featureNamesArray = featureNames.map(name => name as string)\n\n try {\n // Run beforeGetFeatures hooks\n await Promise.all(\n this.plugins.map(plugin => plugin.beforeGetFeatures?.(featureNamesArray, mergedContext))\n )\n\n type FeaturesResponse = { features: Record<string, { variation: FeatureValue }> }\n const fetchFeatures = async (): Promise<Record<string, FeatureValue>> => {\n const url = this.networkConfig.featuresAPIUrl\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n }\n const body = JSON.stringify({\n environment: this.environment,\n features: featureNamesArray,\n context: mergedContext,\n })\n\n // Notify plugins before request\n await Promise.all(this.plugins.map(plugin => plugin.beforeRequest?.(url, body, headers)))\n\n const startTime = Date.now()\n // Support timeout via AbortController when available\n const AbortCtrl: typeof AbortController | undefined =\n typeof globalThis !== 'undefined'\n ? (globalThis as unknown as { AbortController?: typeof AbortController })\n .AbortController\n : undefined\n let controller: AbortController | undefined\n let timeoutId: ReturnType<typeof setTimeout> | undefined\n if (this.networkConfig.requestTimeoutMs && typeof AbortCtrl === 'function') {\n controller = new AbortCtrl()\n timeoutId = setTimeout(() => controller?.abort(), this.networkConfig.requestTimeoutMs)\n }\n let response: Response\n try {\n response = await this.fetchImpl(url, {\n method: 'POST',\n headers,\n body,\n signal: controller?.signal,\n })\n } finally {\n if (timeoutId) clearTimeout(timeoutId)\n }\n const duration = Date.now() - startTime\n\n // Notify plugins after response\n await Promise.all(\n this.plugins.map(plugin => plugin.afterResponse?.(response, { duration }))\n )\n\n if (!response.ok) {\n throw new Error(`Failed to fetch features: ${response.statusText}`)\n }\n\n const data = (await response.json()) as FeaturesResponse\n const result: Record<string, FeatureValue> = {}\n\n featureNamesArray.forEach(name => {\n const variation = data.features[name]?.variation\n result[name] = this.getVariationValue(\n variation,\n this.featureDefinitions[name as keyof TFeatures]\n )\n })\n\n return result\n }\n\n const result = this.networkConfig.retry.enabled\n ? await retry(\n fetchFeatures,\n this.networkConfig.retry.maxAttempts,\n this.networkConfig.retry.backoff,\n (attempt, error, willRetry) => {\n // Notify plugins of retry attempts\n Promise.all(\n this.plugins.map(plugin => plugin.onRetryAttempt?.(attempt, error, willRetry))\n ).catch(console.error)\n }\n )\n : await fetchFeatures()\n\n // Run afterGetFeatures hooks\n await Promise.all(\n this.plugins.map(plugin => plugin.afterGetFeatures?.(result, mergedContext))\n )\n\n // Return the fetched features\n return result as { [K in TKeys[number]]: Features<TFeatures>[K] }\n } catch (error) {\n // Run onError hooks\n await Promise.all(this.plugins.map(plugin => plugin.onError?.(error as Error, mergedContext)))\n\n // Create fallback result with requested feature names\n const fallbackResult: Record<string, FeatureValue> = {}\n\n featureNamesArray.forEach(featureName => {\n fallbackResult[featureName] = this.featureDefinitions[featureName as keyof TFeatures]\n\n // Notify plugins that fallback was used for each feature\n Promise.all(\n this.plugins.map(plugin =>\n plugin.onFallbackUsed?.(\n featureName,\n this.featureDefinitions[featureName as keyof TFeatures],\n error as Error\n )\n )\n ).catch(console.error)\n })\n\n return fallbackResult as { [K in TKeys[number]]: Features<TFeatures>[K] }\n }\n }\n}\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyXrB,aAAS,KAAK,YAAY,MAAM;AAAA,EAClC;AAAA,EAEQ,uBAA6B;AACnC,QAAI,OAAO,aAAa,aAAa;AACnC;AAAA,IACF;AAEA,UAAM,WAAW,2BAA2B,KAAK,QAAQ;AACzD,UAAM,UAAU,0BAA0B,KAAK,QAAQ;AACvD,UAAM,UAAU,sBAAsB,KAAK,QAAQ;AACnD,UAAM,UAAU,0BAA0B,KAAK,QAAQ;AACvD,UAAM,WAAW,yBAAyB,KAAK,QAAQ;AACvD,UAAM,YAAY,4BAA4B,KAAK,QAAQ;AAE3D,UAAM,SAAS,SAAS,eAAe,QAAQ;AAC/C,UAAM,QAAQ,SAAS,eAAe,OAAO;AAC7C,UAAM,WAAW,SAAS,eAAe,OAAO;AAChD,UAAM,QAAQ,SAAS,eAAe,OAAO;AAC7C,UAAM,cAAc,SAAS,eAAe,QAAQ;AACpD,UAAM,UAAU,SAAS,eAAe,SAAS;AAEjD,YAAQ,iBAAiB,SAAS,MAAM;AACtC,aAAO,UAAU,OAAO,MAAM;AAAA,IAChC,CAAC;AAED,WAAO,iBAAiB,SAAS,MAAM;AACrC,aAAO,UAAU,OAAO,MAAM;AAAA,IAChC,CAAC;AAED,cAAU,iBAAiB,SAAS,MAAM;AACxC,WAAK,kBAAkB;AAAA,IACzB,CAAC;AAED,iBAAa,iBAAiB,SAAS,OAAK;AAC1C,WAAK,MAAM,cAAe,EAAE,OAA4B,MAAM,YAAY;AAC1E,WAAK,gBAAgB;AAAA,IACvB,CAAC;AAGD,QAAI,SAAS;AAEX,cAAQ,iBAAiB,SAAS,CAAC,MAAa;AAC9C,cAAM,SAAS,EAAE;AACjB,cAAM,gBAAgB,OAAO,QAAQ,qBAAqB;AAC1D,YAAI,CAAC,cAAe;AAEpB,UAAE,eAAe;AACjB,UAAE,gBAAgB;AAElB,cAAM,cAAc,cAAc,QAAQ;AAC1C,cAAM,SAAS,cAAc,QAAQ;AAErC,YAAI,WAAW,UAAU;AACvB,eAAK,eAAe,WAAW;AAAA,QACjC,WAAW,WAAW,OAAO;AAC3B,gBAAM,WAAW,QAAQ;AAAA,YACvB,0BAA0B,KAAK,kBAAkB,WAAW,CAAC;AAAA,UAC/D;AACA,cAAI,YAAY,SAAS,MAAM,KAAK,GAAG;AACrC,gBAAI;AACF,oBAAM,QAAQ,KAAK,MAAM,SAAS,KAAK;AACvC,mBAAK,YAAY,aAAa,KAAK;AAAA,YACrC,QAAQ;AAEN,mBAAK,YAAY,aAAa,EAAE,OAAO,SAAS,MAAM,CAAC;AAAA,YACzD;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAGD,cAAQ,iBAAiB,UAAU,CAAC,MAAa;AAC/C,cAAM,SAAS,EAAE;AACjB,YAAI,OAAO,SAAS,cAAc,OAAO,QAAQ,SAAS,WAAW;AACnE,gBAAM,cAAc,OAAO,QAAQ;AACnC,gBAAM,WAAW,OAAO;AACxB,eAAK,YAAY,aAAa,QAAQ;AAAA,QACxC;AAAA,MACF,CAAC;AAGD,cAAQ,iBAAiB,SAAS,CAAC,MAAa;AAC9C,cAAM,SAAS,EAAE;AACjB,YAAI,OAAO,YAAY,cAAc,OAAO,QAAQ,SAAS;AAC3D,gBAAM,cAAc,OAAO,QAAQ;AACnC,gBAAM,gBAAgB,OAAO,QAAQ,YAAY;AACjD,gBAAM,cAAc,QAAQ;AAAA,YAC1B,2CAA2C,KAAK,kBAAkB,WAAW,CAAC;AAAA,UAChF;AAEA,cAAI,aAAa;AACf,kBAAM,aAAa,OAAO,UAAU;AACpC,kBAAM,aAAa,OAAO,MAAM,KAAK,EAAE,SAAS;AAChD,wBAAY,WAAW,CAAC,cAAc,CAAC;AAAA,UACzC;AAAA,QACF;AAAA,MACF,CAAC;AAGD,cAAQ,iBAAiB,SAAS,CAAC,MAAa;AAC9C,cAAM,SAAS,EAAE;AACjB,YAAI,OAAO,YAAY,cAAc,OAAO,QAAQ,SAAS;AAC3D,qBAAW,MAAM;AACf,kBAAM,cAAc,OAAO,QAAQ;AACnC,kBAAM,gBAAgB,OAAO,QAAQ,YAAY;AACjD,kBAAM,cAAc,QAAQ;AAAA,cAC1B,2CAA2C,WAAW;AAAA,YACxD;AAEA,gBAAI,aAAa;AACf,oBAAM,aAAa,OAAO,UAAU;AACpC,oBAAM,aAAa,OAAO,MAAM,KAAK,EAAE,SAAS;AAChD,0BAAY,WAAW,CAAC,cAAc,CAAC;AAAA,YACzC;AAAA,UACF,GAAG,CAAC;AAAA,QACN;AAAA,MACF,CAAC;AAGD,cAAQ,iBAAiB,WAAW,CAAC,MAAqB;AACxD,cAAM,SAAS,EAAE;AACjB,YACE,OAAO,YAAY,cACnB,OAAO,QAAQ,YACd,EAAE,WAAW,EAAE,YAChB,EAAE,QAAQ,SACV;AACA,YAAE,eAAe;AACjB,gBAAM,cAAc,OAAO,QAAQ;AACnC,gBAAM,cAAc,QAAQ;AAAA,YAC1B,2CAA2C,KAAK,kBAAkB,WAAW,CAAC;AAAA,UAChF;AAEA,cAAI,eAAe,CAAC,YAAY,UAAU;AACxC,wBAAY,MAAM;AAAA,UACpB;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEQ,kBAAwB;AAC9B,QAAI,OAAO,aAAa,aAAa;AACnC;AAAA,IACF;AAEA,UAAM,YAAY,4BAA4B,KAAK,QAAQ;AAC3D,UAAM,UAAU,sBAAsB,KAAK,QAAQ;AACnD,UAAM,UAAU,0BAA0B,KAAK,QAAQ;AACvD,UAAM,wBAAwB,kCAAkC,KAAK,QAAQ;AAC7E,UAAM,WAAW,2BAA2B,KAAK,QAAQ;AAEzD,UAAM,UAAU,SAAS,eAAe,SAAS;AACjD,UAAM,cAAc,SAAS,eAAe,OAAO;AACnD,UAAM,QAAQ,SAAS,eAAe,OAAO;AAC7C,UAAM,sBAAsB,SAAS,eAAe,qBAAqB;AACzE,UAAM,YAAY,SAAS,eAAe,QAAQ;AAElD,QAAI,CAAC,SAAS;AACZ,cAAQ,KAAK,wCAAwC,SAAS;AAC9D;AAAA,IACF;AAGA,UAAM,gBAAgB,OAAO,KAAK,KAAK,MAAM,SAAS,EAAE;AACxD,UAAM,eAAe,gBAAgB;AACrC,QAAI,aAAa;AACf,kBAAY,WAAW,CAAC;AAAA,IAC1B;AAGA,QAAI,OAAO;AACT,UAAI,cAAc;AAChB,cAAM,cAAc,gBAAgB,KAAK,QAAQ,OAAO,aAAa;AACrE,cAAM,UAAU,IAAI,MAAM;AAAA,MAC5B,OAAO;AACL,cAAM,UAAU,OAAO,MAAM;AAAA,MAC/B;AAAA,IACF;AAGA,QAAI,WAAW;AACb,UAAI,cAAc;AAChB,kBAAU;AAAA,UACR;AAAA,UACA,qBAAqB,aAAa,YAAY,kBAAkB,IAAI,KAAK,GAAG;AAAA,QAC9E;AACA,kBAAU;AAAA,UACR;AAAA,UACA,qBAAqB,aAAa,YAAY,kBAAkB,IAAI,KAAK,GAAG;AAAA,QAC9E;AAAA,MACF,OAAO;AACL,kBAAU,aAAa,SAAS,kBAAkB;AAClD,kBAAU,aAAa,cAAc,kBAAkB;AAAA,MACzD;AAAA,IACF;AAGA,QAAI,qBAAqB;AAEvB,YAAM,eAAe,KAAK,WAAW,OAAO,aAAa,CAAC;AAC1D,0BAAoB,YAAY,uDAAuD,eAAe,kBAAkB,EAAE,KAAK,YAAY,mBAAmB,kBAAkB,IAAI,KAAK,GAAG;AAAA,IAC9L;AAEA,UAAM,WAAW,MAAM,KAAK,KAAK,MAAM,QAAQ,EAAE,KAAK;AAGtD,UAAM,mBAAmB,SAAS;AAAA,MAAO,UACvC,KAAK,YAAY,EAAE,SAAS,KAAK,MAAM,WAAW;AAAA,IACpD;AAEA,QAAI,iBAAiB,WAAW,GAAG;AACjC,cAAQ,YAAY,KAAK,MAAM,cAC3B,yEACA,uCAAuC,mBAAmB;AAC9D;AAAA,IACF;AAEA,UAAM,cAAc,iBACjB,IAAI,iBAAe;AAClB,YAAM,cAAc,eAAe,KAAK,MAAM;AAC9C,YAAM,eAAe,KAAK,MAAM,cAAc,WAAW;AACzD,YAAM,gBAAgB,cAAc,KAAK,MAAM,UAAU,WAAW,IAAI;AACxE,YAAM,aAAa,CAAC,KAAK,MAAM;AAC/B,YAAM,YAAY,yBAAyB,aAAa,aAAa,EAAE;AAGvE,YAAM,YACJ,OAAO,iBAAiB,aAAc,eAAe,OAAO,kBAAkB;AAEhF,UAAI,WAAW;AAEb,cAAM,YAAY,cAAc,kBAAkB,OAAO,iBAAiB;AAC1E,eAAO;AAAA,wBACO,SAAS;AAAA;AAAA;AAAA,kBAGf,KAAK,WAAW,WAAW,CAAC;AAAA,kBAC5B,cAAc,6FAA6F,EAAE;AAAA;AAAA;AAAA,kBAI7G,cACI;AAAA;AAAA;AAAA,oCAGc,KAAK,WAAW,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAY1C,EACN;AAAA;AAAA;AAAA;AAAA,sBAIM,YAAY,YAAY,EAAE;AAAA,oCACZ,KAAK,WAAW,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MASxD,OAAO;AAEL,cAAM,sBAAsB,cACxB,KAAK,UAAU,aAAa,IAC5B,iBAAiB,SACf,KAAK,UAAU,YAAY,IAC3B;AACN,cAAM,qBAAqB,KAAK,WAAW,WAAW;AACtD,cAAM,6BAA6B,KAAK,WAAW,mBAAmB;AACtE,cAAM,yBAAyB,cAC3B,KAAK,WAAW,KAAK,UAAU,aAAa,CAAC,IAC7C;AAEJ,eAAO;AAAA,wBACO,SAAS;AAAA;AAAA;AAAA,kBAGf,kBAAkB;AAAA,kBAClB,cAAc,6FAA6F,EAAE;AAAA;AAAA;AAAA,kBAI7G,cACI;AAAA;AAAA;AAAA,sCAGgB,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAYlC,EACN;AAAA;AAAA;AAAA,kCAGkB,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gCAWpB,kBAAkB;AAAA,iCACjB,0BAA0B;AAAA,iBAC1C,sBAAsB;AAAA;AAAA;AAAA;AAAA,MAI/B;AAAA,IACF,CAAC,EACA,KAAK,EAAE;AAEV,0BAAsB,MAAM;AAE1B,cAAQ,YAAY;AAGpB,cAAQ,iBAAiB,wBAAwB,EAAE,QAAQ,cAAY;AACrE,cAAM,kBAAkB;AACxB,cAAM,cAAc,gBAAgB,QAAQ;AAC5C,cAAM,gBAAgB,gBAAgB,QAAQ,YAAY;AAC1D,cAAM,cAAc,QAAQ;AAAA,UAC1B,2CAA2C,KAAK,kBAAkB,WAAW,CAAC;AAAA,QAChF;AAEA,YAAI,aAAa;AACf,gBAAM,aAAa,gBAAgB,UAAU;AAC7C,gBAAM,aAAa,gBAAgB,MAAM,KAAK,EAAE,SAAS;AACzD,sBAAY,WAAW,CAAC,cAAc,CAAC;AAAA,QACzC;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEQ,WAAW,MAAsB;AACvC,UAAM,MAAM,OAAO,aAAa,cAAc,SAAS,cAAc,KAAK,IAAI;AAC9E,QAAI,KAAK;AACP,UAAI,cAAc;AAClB,aAAO,IAAI;AAAA,IACb;AACA,WAAO,KAAK,QAAQ,YAAY,UAAQ;AACtC,YAAM,YAAoC;AAAA,QACxC,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,MACP;AACA,aAAO,UAAU,IAAI;AAAA,IACvB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,kBAAkB,OAAuB;AAE/C,WAAO,MAAM,QAAQ,aAAa,MAAM;AAAA,EAC1C;AACF;;;ACtmCO,IAAM,uBAAuB;AAC7B,IAAM,qBAAqB;;;ACoB3B,IAAM,aAAN,MAA0D;AAAA,EAW/D,YAAY,QAAoD;AAC9D,SAAK,SAAS,OAAO;AACrB,SAAK,cAAc,OAAO;AAC1B,SAAK,iBAAiB,OAAO;AAC7B,SAAK,qBAAqB,OAAO;AAGjC,SAAK,WAAW,KAAK,iBAAiB;AAEtC,SAAK,gBAAgB;AAAA,MACnB,gBAAgB,OAAO,eAAe,kBAAkB;AAAA,MACxD,cAAc,OAAO,eAAe,gBAAgB;AAAA,MACpD,OAAO;AAAA,QACL,SAAS,OAAO,eAAe,OAAO,WAAW;AAAA,QACjD,aAAa,OAAO,eAAe,OAAO,eAAe;AAAA,QACzD,SAAS,OAAO,eAAe,OAAO,WAAW;AAAA,MACnD;AAAA,MACA,kBAAkB,OAAO,eAAe,oBAAoB;AAAA,IAC9D;AAGA,UAAM,cACJ,OAAO,eAAe,cACjB,WAAmD,QACpD;AACN,QAAI,OAAO,eAAe,SAAS;AACjC,WAAK,YAAY,OAAO,cAAc;AAAA,IACxC,WAAW,OAAO,gBAAgB,YAAY;AAC5C,WAAK,YAAY,YAAY,KAAK,UAAU;AAAA,IAC9C,OAAO;AACL,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAGA,SAAK,UAAU,KAAK,kBAAkB,MAAM;AAG5C,YAAQ;AAAA,MACN,KAAK,QAAQ;AAAA,QAAI,YACf,OAAO,SAAS;AAAA,UACd,UAAU,KAAK;AAAA,UACf,mBAAmB,KAAK;AAAA,UACxB,SAAS,KAAK;AAAA,QAChB,CAAC;AAAA,MACH;AAAA,IACF,EAAE,MAAM,QAAQ,KAAK;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAA2B;AACjC,WAAO,YAAY,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,EAC7E;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,QAAkE;AAC1F,UAAM,UAAU,OAAO,WAAW,CAAC;AAGnC,UAAM,YAAY,OAAO,WAAW,eAAe,OAAO,aAAa;AAGvE,QAAI,OAAO,YAAY,OAAO;AAC5B,aAAO;AAAA,IACT;AAGA,QAAI,WAAW;AAEb,YAAM,mBAAmB,QAAQ,KAAK,OAAK,EAAE,SAAS,gBAAgB;AAEtE,UAAI,CAAC,kBAAkB;AAErB,cAAM,gBAAgB,OAAO,WAAW,EAAE,SAAS,OAAO;AAC1D,cAAM,gBAAgB,IAAI,kBAAkB,aAAa;AACzD,eAAO,CAAC,eAAe,GAAG,OAAO;AAAA,MACnC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,cAAc,SAAyB,oBAA6B,MAAY;AAC9E,UAAM,aAAa,KAAK;AAExB,QAAI,qBAAqB,KAAK,gBAAgB;AAC5C,WAAK,iBAAiB,EAAE,GAAG,KAAK,gBAAgB,GAAG,QAAQ;AAAA,IAC7D,OAAO;AACL,WAAK,iBAAiB;AAAA,IACxB;AAGA,YAAQ;AAAA,MACN,KAAK,QAAQ;AAAA,QAAI,YACf,OAAO,kBAAkB,YAAY,KAAK,gBAAiB,eAAe;AAAA,MAC5E;AAAA,IACF,EAAE,MAAM,QAAQ,KAAK;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,aAAyC;AACvC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAiD,aAA8C;AAC7F,WAAO,KAAK,mBAAmB,WAAW;AAAA,EAC5C;AAAA,EAEQ,kBAAkB,WAAyB,UAAsC;AACvF,QAAI,cAAc,UAAa,cAAc,MAAM;AACjD,aAAO;AAAA,IACT;AAEA,WAAO,YAAY;AAAA,EACrB;AAAA,EAEA,MAAM,WACJ,aACA,SACoC;AACpC,UAAM,EAAE,QAAQ,IAAI,WAAW,CAAC;AAGhC,UAAM,gBACJ,OAAO,YAAY,YAAY,YAAY,OACvC,EAAE,GAAI,KAAK,kBAAkB,CAAC,GAAI,GAAG,QAAQ,IAC7C,KAAK;AAEX,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,YAAY,CAAC,WAAqB,GAAG;AAAA,QAC/D,SAAS;AAAA,MACX,CAAC;AAGD,YAAM,QAAQ,SAAS,WAAqB;AAC5C,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,YAAM,QAAQ,IAAI,KAAK,QAAQ,IAAI,YAAU,OAAO,UAAU,OAAgB,aAAa,CAAC,CAAC;AAG7F,YAAM,gBAAgB,KAAK,mBAAmB,WAAW;AAGzD,YAAM,QAAQ;AAAA,QACZ,KAAK,QAAQ;AAAA,UAAI,YACf,OAAO;AAAA,YACL;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,YACJ,cACA,SAC2D;AAC3D,UAAM,EAAE,SAAS,gBAAgB,IAAI,WAAW,CAAC;AAGjD,UAAM,gBACJ,OAAO,oBAAoB,YAAY,oBAAoB,OACvD,EAAE,GAAI,KAAK,kBAAkB,CAAC,GAAI,GAAG,gBAAgB,IACrD,KAAK;AAGX,QAAI,iBAAiB;AACnB,YAAM,QAAQ;AAAA,QACZ,KAAK,QAAQ;AAAA,UAAI,YACf,OAAO,kBAAkB,KAAK,gBAAgB,eAAgB,SAAS;AAAA,QACzE;AAAA,MACF;AAAA,IACF;AAGA,UAAM,oBAAoB,aAAa,IAAI,UAAQ,IAAc;AAEjE,QAAI;AAEF,YAAM,QAAQ;AAAA,QACZ,KAAK,QAAQ,IAAI,YAAU,OAAO,oBAAoB,mBAAmB,aAAa,CAAC;AAAA,MACzF;AAGA,YAAM,gBAAgB,YAAmD;AACvE,cAAM,MAAM,KAAK,cAAc;AAC/B,cAAM,UAAkC;AAAA,UACtC,gBAAgB;AAAA,UAChB,eAAe,UAAU,KAAK,MAAM;AAAA,QACtC;AACA,cAAM,OAAO,KAAK,UAAU;AAAA,UAC1B,aAAa,KAAK;AAAA,UAClB,UAAU;AAAA,UACV,SAAS;AAAA,QACX,CAAC;AAGD,cAAM,QAAQ,IAAI,KAAK,QAAQ,IAAI,YAAU,OAAO,gBAAgB,KAAK,MAAM,OAAO,CAAC,CAAC;AAExF,cAAM,YAAY,KAAK,IAAI;AAE3B,cAAM,YACJ,OAAO,eAAe,cACjB,WACE,kBACH;AACN,YAAI;AACJ,YAAI;AACJ,YAAI,KAAK,cAAc,oBAAoB,OAAO,cAAc,YAAY;AAC1E,uBAAa,IAAI,UAAU;AAC3B,sBAAY,WAAW,MAAM,YAAY,MAAM,GAAG,KAAK,cAAc,gBAAgB;AAAA,QACvF;AACA,YAAI;AACJ,YAAI;AACF,qBAAW,MAAM,KAAK,UAAU,KAAK;AAAA,YACnC,QAAQ;AAAA,YACR;AAAA,YACA;AAAA,YACA,QAAQ,YAAY;AAAA,UACtB,CAAC;AAAA,QACH,UAAE;AACA,cAAI,UAAW,cAAa,SAAS;AAAA,QACvC;AACA,cAAM,WAAW,KAAK,IAAI,IAAI;AAG9B,cAAM,QAAQ;AAAA,UACZ,KAAK,QAAQ,IAAI,YAAU,OAAO,gBAAgB,UAAU,EAAE,SAAS,CAAC,CAAC;AAAA,QAC3E;AAEA,YAAI,CAAC,SAAS,IAAI;AAChB,gBAAM,IAAI,MAAM,6BAA6B,SAAS,UAAU,EAAE;AAAA,QACpE;AAEA,cAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,cAAMA,UAAuC,CAAC;AAE9C,0BAAkB,QAAQ,UAAQ;AAChC,gBAAM,YAAY,KAAK,SAAS,IAAI,GAAG;AACvC,UAAAA,QAAO,IAAI,IAAI,KAAK;AAAA,YAClB;AAAA,YACA,KAAK,mBAAmB,IAAuB;AAAA,UACjD;AAAA,QACF,CAAC;AAED,eAAOA;AAAA,MACT;AAEA,YAAM,SAAS,KAAK,cAAc,MAAM,UACpC,MAAM;AAAA,QACJ;AAAA,QACA,KAAK,cAAc,MAAM;AAAA,QACzB,KAAK,cAAc,MAAM;AAAA,QACzB,CAAC,SAAS,OAAO,cAAc;AAE7B,kBAAQ;AAAA,YACN,KAAK,QAAQ,IAAI,YAAU,OAAO,iBAAiB,SAAS,OAAO,SAAS,CAAC;AAAA,UAC/E,EAAE,MAAM,QAAQ,KAAK;AAAA,QACvB;AAAA,MACF,IACA,MAAM,cAAc;AAGxB,YAAM,QAAQ;AAAA,QACZ,KAAK,QAAQ,IAAI,YAAU,OAAO,mBAAmB,QAAQ,aAAa,CAAC;AAAA,MAC7E;AAGA,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,YAAM,QAAQ,IAAI,KAAK,QAAQ,IAAI,YAAU,OAAO,UAAU,OAAgB,aAAa,CAAC,CAAC;AAG7F,YAAM,iBAA+C,CAAC;AAEtD,wBAAkB,QAAQ,iBAAe;AACvC,uBAAe,WAAW,IAAI,KAAK,mBAAmB,WAA8B;AAGpF,gBAAQ;AAAA,UACN,KAAK,QAAQ;AAAA,YAAI,YACf,OAAO;AAAA,cACL;AAAA,cACA,KAAK,mBAAmB,WAA8B;AAAA,cACtD;AAAA,YACF;AAAA,UACF;AAAA,QACF,EAAE,MAAM,QAAQ,KAAK;AAAA,MACvB,CAAC;AAED,aAAO;AAAA,IACT;AAAA,EACF;AACF;","names":["result"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supashiphq/javascript-sdk",
3
- "version": "0.7.15",
3
+ "version": "0.7.17",
4
4
  "description": "JavaScript SDK for Supaship",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",