@launchdarkly/toolbar 2.7.0-beta.1 → 2.8.0-beta.1

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.
Files changed (25) hide show
  1. package/cdn/toolbar.min.js +81 -81
  2. package/dist/core/services/DevServerClient.d.ts +2 -0
  3. package/dist/core/ui/Toolbar/components/FilterOptions/FilterOptions.css.d.ts +1 -0
  4. package/dist/core/ui/Toolbar/components/FilterOptions/FilterOptions.d.ts +1 -0
  5. package/dist/core/ui/Toolbar/components/FilterOptions/ShareUrlButton.css.d.ts +2 -0
  6. package/dist/core/ui/Toolbar/components/FilterOptions/ShareUrlButton.d.ts +7 -0
  7. package/dist/core/ui/Toolbar/components/ShareStatePopover/ShareStatePopover.css.d.ts +10 -0
  8. package/dist/core/ui/Toolbar/components/ShareStatePopover/ShareStatePopover.d.ts +15 -0
  9. package/dist/core/ui/Toolbar/components/ShareStatePopover/index.d.ts +2 -0
  10. package/dist/core/ui/Toolbar/components/icons/ShareIcon.d.ts +5 -0
  11. package/dist/core/ui/Toolbar/components/icons/index.d.ts +1 -0
  12. package/dist/core/ui/Toolbar/components/new/Settings/ShareButton.d.ts +5 -0
  13. package/dist/core/ui/Toolbar/components/new/Settings/ShareButton.module.css.d.ts +1 -0
  14. package/dist/core/ui/Toolbar/context/DevServerProvider.d.ts +2 -0
  15. package/dist/core/ui/Toolbar/hooks/index.d.ts +1 -0
  16. package/dist/core/ui/Toolbar/hooks/useLocalStorage.d.ts +10 -0
  17. package/dist/core/ui/Toolbar/utils/context.d.ts +11 -12
  18. package/dist/core/ui/Toolbar/utils/localStorage.d.ts +2 -0
  19. package/dist/core/utils/analytics.d.ts +9 -0
  20. package/dist/core/utils/urlOverrides.d.ts +105 -0
  21. package/dist/index.cjs +1 -1
  22. package/dist/js/index.js +1 -1
  23. package/dist/js/react.js +1 -1
  24. package/dist/react.cjs +1 -1
  25. package/package.json +20 -20
@@ -5,6 +5,7 @@ export interface DevServerProjectResponse {
5
5
  flagsState: Record<string, FlagState>;
6
6
  overrides: Record<string, Override>;
7
7
  sourceEnvironmentKey: string;
8
+ context?: any;
8
9
  }
9
10
  export interface FlagState {
10
11
  value: any;
@@ -24,4 +25,5 @@ export declare class DevServerClient {
24
25
  value: any;
25
26
  }>;
26
27
  clearOverride(flagKey: string): Promise<void>;
28
+ updateProjectContext(context: any): Promise<DevServerProjectResponse>;
27
29
  }
@@ -2,3 +2,4 @@ export declare const container: string;
2
2
  export declare const filterButton: string;
3
3
  export declare const bottomRow: string;
4
4
  export declare const statusText: string;
5
+ export declare const buttonGroup: string;
@@ -5,6 +5,7 @@ export interface FilterOptionsProps {
5
5
  starredCount: number;
6
6
  onClearOverrides?: () => void;
7
7
  onClearStarred?: () => void;
8
+ onShareUrl?: () => void;
8
9
  isLoading?: boolean;
9
10
  }
10
11
  export declare function FilterOptions(props: FilterOptionsProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,2 @@
1
+ export declare const shareButton: string;
2
+ export declare const smallIcon: string;
@@ -0,0 +1,7 @@
1
+ interface ShareUrlButtonProps {
2
+ onClick: () => void;
3
+ isLoading?: boolean;
4
+ count: number;
5
+ }
6
+ export declare function ShareUrlButton({ onClick, isLoading, count }: ShareUrlButtonProps): import("react/jsx-runtime").JSX.Element | null;
7
+ export {};
@@ -0,0 +1,10 @@
1
+ export declare const popover: string;
2
+ export declare const header: string;
3
+ export declare const title: string;
4
+ export declare const content: string;
5
+ export declare const description: string;
6
+ export declare const options: string;
7
+ export declare const option: string;
8
+ export declare const optionLabel: string;
9
+ export declare const count: string;
10
+ export declare const actions: string;
@@ -0,0 +1,15 @@
1
+ export interface ShareStateOptions {
2
+ includeFlagOverrides: boolean;
3
+ includeContexts: boolean;
4
+ includeSettings: boolean;
5
+ }
6
+ interface ShareStatePopoverProps {
7
+ isOpen: boolean;
8
+ onClose: () => void;
9
+ onShare: (options: ShareStateOptions) => void;
10
+ overrideCount: number;
11
+ contextCount: number;
12
+ anchorRef?: React.RefObject<HTMLElement>;
13
+ }
14
+ export declare function ShareStatePopover(props: ShareStatePopoverProps): import("react/jsx-runtime").JSX.Element;
15
+ export {};
@@ -0,0 +1,2 @@
1
+ export { ShareStatePopover } from './ShareStatePopover';
2
+ export type { ShareStateOptions } from './ShareStatePopover';
@@ -0,0 +1,5 @@
1
+ interface IconProps {
2
+ className?: string;
3
+ }
4
+ export declare function ShareIcon({ className }: IconProps): import("react/jsx-runtime").JSX.Element;
5
+ export {};
@@ -24,3 +24,4 @@ export { ExternalLinkIcon } from './ExternalLinkIcon';
24
24
  export { CursorIcon } from './CursorIcon';
25
25
  export { InfoIcon } from './InfoIcon';
26
26
  export { AddIcon } from './AddIcon';
27
+ export { ShareIcon } from './ShareIcon';
@@ -0,0 +1,5 @@
1
+ interface ShareButtonProps {
2
+ onClick: (e: React.MouseEvent) => void;
3
+ }
4
+ export declare function ShareButton({ onClick }: ShareButtonProps): import("react/jsx-runtime").JSX.Element;
5
+ export {};
@@ -0,0 +1 @@
1
+ export declare const button: string;
@@ -1,4 +1,5 @@
1
1
  import type { FC, ReactNode } from 'react';
2
+ import { DevServerClient } from '../../../services/DevServerClient';
2
3
  import { LdToolbarConfig, ToolbarState } from '../../../types/devServer';
3
4
  interface DevServerContextValue {
4
5
  state: ToolbarState;
@@ -6,6 +7,7 @@ interface DevServerContextValue {
6
7
  clearOverride: (flagKey: string) => Promise<void>;
7
8
  clearAllOverrides: () => Promise<void>;
8
9
  refresh: () => Promise<void>;
10
+ devServerClient: DevServerClient | null;
9
11
  }
10
12
  export declare const useDevServerContext: () => DevServerContextValue;
11
13
  export interface DevServerProviderProps {
@@ -3,3 +3,4 @@ export { useToolbarVisibility } from './useToolbarVisibility';
3
3
  export { useToolbarDrag } from './useToolbarDrag';
4
4
  export { useEvents } from './useEvents';
5
5
  export { useCurrentDate } from './useCurrentDate';
6
+ export { useLocalStorage } from './useLocalStorage';
@@ -0,0 +1,10 @@
1
+ interface UseLocalStorageOptions<T> {
2
+ /** Sync across browser tabs (default: false) */
3
+ syncTabs?: boolean;
4
+ /** Custom serializer (default: JSON.stringify) */
5
+ serialize?: (value: T) => string;
6
+ /** Custom deserializer (default: JSON.parse) */
7
+ deserialize?: (value: string) => T;
8
+ }
9
+ export declare function useLocalStorage<T>(key: string, defaultValue: T, options?: UseLocalStorageOptions<T>): [T, (value: T | ((prev: T) => T)) => void, () => void];
10
+ export {};
@@ -1,17 +1,7 @@
1
1
  import type { LDContext } from 'launchdarkly-js-client-sdk';
2
2
  /**
3
- * Generates a deterministic ID for a context based on its properties.
4
- * The same context object will always produce the same ID.
5
- * This is used to compare contexts for equality.
6
- *
7
- * For multi-kind contexts, the deprecated 'user' kind is excluded from
8
- * hash generation to prevent false duplicate detection.
9
- *
10
- * Results are cached to minimize computation during React renders.
11
- */
12
- export declare function generateContextId(context?: LDContext | null): string;
13
- /**
14
- * Check if two contexts are the same using their generated IDs
3
+ * Check if two contexts are the same using their stable IDs (kind+key)
4
+ * @deprecated Prefer comparing getStableContextId(a) === getStableContextId(b) directly
15
5
  */
16
6
  export declare function areContextsEqual(a: LDContext | null | undefined, b: LDContext | null | undefined): boolean;
17
7
  /**
@@ -32,3 +22,12 @@ export declare function getContextDisplayName(context: LDContext | null | undefi
32
22
  * Check if a context is anonymous
33
23
  */
34
24
  export declare function isContextAnonymous(context: LDContext | null | undefined): boolean;
25
+ /**
26
+ * Generates a stable identifier for a context based on kind+key.
27
+ * This identifier remains the same even if other properties change,
28
+ * allowing contexts to be updated rather than duplicated.
29
+ *
30
+ * For single-kind contexts: "kind:key"
31
+ * For multi-kind contexts: "multi:kind1:key1:kind2:key2" (sorted by kind name)
32
+ */
33
+ export declare function getStableContextId(context: LDContext | null | undefined): string;
@@ -45,3 +45,5 @@ export declare function loadIsOptedInToSessionReplay(): boolean;
45
45
  export declare function loadIsOptedInToEnhancedAnalytics(): boolean;
46
46
  export declare function saveAnalyticsConsentShown(shown: boolean): void;
47
47
  export declare function loadAnalyticsConsentShown(): boolean;
48
+ export declare function loadAllSettings(): Partial<ToolbarSettings>;
49
+ export declare function saveAllSettings(settings: Partial<ToolbarSettings>): void;
@@ -135,4 +135,13 @@ export declare class ToolbarAnalytics {
135
135
  * Track context key copy
136
136
  */
137
137
  trackContextKeyCopy(contextKey: string): void;
138
+ /**
139
+ * Track state sharing via URL
140
+ */
141
+ trackShareState(options: {
142
+ includeSettings: boolean;
143
+ overrideCount: number;
144
+ contextCount: number;
145
+ starredFlagCount: number;
146
+ }): void;
138
147
  }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Utilities for sharing and loading toolbar state via URL query parameters.
3
+ *
4
+ * State is encoded as a single base64-encoded JSON object in the ldToolbarState parameter.
5
+ * The state includes: flag overrides, contexts, settings, starred flags, etc.
6
+ */
7
+ import { LDContext } from 'launchdarkly-js-client-sdk';
8
+ import { ToolbarSettings } from '../ui/Toolbar/utils/localStorage';
9
+ /** Current version of the shared state format */
10
+ export declare const SHARED_STATE_VERSION = 1;
11
+ /** Default query parameter name for shared toolbar state */
12
+ export declare const DEFAULT_STATE_PARAM = "ldToolbarState";
13
+ /** Maximum size of encoded state in characters (warning threshold) */
14
+ export declare const MAX_STATE_SIZE_WARNING = 2000;
15
+ /** Maximum size of encoded state in characters (hard limit) */
16
+ export declare const MAX_STATE_SIZE_LIMIT = 8192;
17
+ /**
18
+ * Structure of the shared toolbar state
19
+ */
20
+ export interface SharedToolbarState {
21
+ /** Version of the state format */
22
+ version: number;
23
+ /** Flag overrides (flag key -> value) */
24
+ overrides: Record<string, any>;
25
+ /** Saved contexts */
26
+ contexts: LDContext[];
27
+ /** Currently active context */
28
+ activeContext: LDContext | null;
29
+ /** Toolbar settings */
30
+ settings: Partial<ToolbarSettings>;
31
+ /** Starred flag keys */
32
+ starredFlags: string[];
33
+ }
34
+ /**
35
+ * Result of serializing shared state
36
+ */
37
+ export interface SerializeStateResult {
38
+ /** The full URL with the state parameter */
39
+ url: string;
40
+ /** Size of the encoded state in characters */
41
+ size: number;
42
+ /** Whether the size exceeds the warning threshold */
43
+ exceedsWarning: boolean;
44
+ /** Whether the size exceeds the hard limit */
45
+ exceedsLimit: boolean;
46
+ }
47
+ /**
48
+ * Result of parsing shared state from URL
49
+ */
50
+ export interface ParseStateResult {
51
+ /** Whether a state parameter was found in the URL */
52
+ found: boolean;
53
+ /** The parsed state (if found and valid) */
54
+ state: SharedToolbarState | null;
55
+ /** Error message if parsing failed */
56
+ error: string | null;
57
+ }
58
+ /**
59
+ * Serializes toolbar state into a URL with a query parameter
60
+ *
61
+ * @param state - The state to serialize
62
+ * @param baseUrl - The base URL to append the parameter to (defaults to current URL without params)
63
+ * @param paramName - The query parameter name (defaults to 'ldToolbarState')
64
+ * @returns Result containing the URL and size information
65
+ *
66
+ */
67
+ export declare function serializeToolbarState(state: SharedToolbarState, baseUrl?: string, paramName?: string): SerializeStateResult;
68
+ /**
69
+ * Parses toolbar state from a URL query parameter
70
+ *
71
+ * @param url - The URL to parse (defaults to window.location.href)
72
+ * @param paramName - The query parameter name (defaults to 'ldToolbarState')
73
+ * @returns Result containing whether state was found, the parsed state, and any error
74
+ *
75
+ */
76
+ export declare function parseToolbarState(url?: string, paramName?: string): ParseStateResult;
77
+ /**
78
+ * Checks if the current URL contains a toolbar state parameter
79
+ *
80
+ * @param url - The URL to check (defaults to window.location.href)
81
+ * @param paramName - The query parameter name (defaults to 'ldToolbarState')
82
+ * @returns True if the URL contains a state parameter
83
+ */
84
+ export declare function hasToolbarState(url?: string, paramName?: string): boolean;
85
+ /**
86
+ * Removes the toolbar state parameter from the current URL
87
+ * Uses history.replaceState to avoid affecting browser history
88
+ *
89
+ * @param paramName - The query parameter name (defaults to 'ldToolbarState')
90
+ */
91
+ export declare function clearToolbarStateFromUrl(paramName?: string): void;
92
+ /**
93
+ * Loads shared toolbar state from URL and applies it to localStorage
94
+ * Should be called during toolbar initialization, before any components render
95
+ *
96
+ * @param paramName - The query parameter name (defaults to 'ldToolbarState')
97
+ * @param flagOverrideStorageNamespace - The storage namespace for flag overrides (defaults to 'ld-flag-override')
98
+ * @param flagOverridePlugin - Optional flag override plugin to apply overrides to directly
99
+ * @returns Object indicating whether state was loaded and if there were any errors
100
+ *
101
+ */
102
+ export declare function loadSharedStateFromUrl(paramName?: string, flagOverrideStorageNamespace?: string, flagOverridePlugin?: any): {
103
+ loaded: boolean;
104
+ error: string | null;
105
+ };
package/dist/index.cjs CHANGED
@@ -552,7 +552,7 @@ async function lazyLoad(signal, url) {
552
552
  throw new Error(`Could not load LaunchDarkly developer toolbar bundle from ${url}`);
553
553
  }
554
554
  }
555
- var package_namespaceObject = JSON.parse('{"rE":"2.7.0-beta.1"}');
555
+ var package_namespaceObject = JSON.parse('{"rE":"2.8.0-beta.1"}');
556
556
  function useLaunchDarklyToolbar(args) {
557
557
  const { toolbarBundleUrl, enabled, ...initConfig } = args;
558
558
  const configRef = (0, external_react_namespaceObject.useRef)(null);
package/dist/js/index.js CHANGED
@@ -518,7 +518,7 @@ async function lazyLoad(signal, url) {
518
518
  throw new Error(`Could not load LaunchDarkly developer toolbar bundle from ${url}`);
519
519
  }
520
520
  }
521
- var package_namespaceObject = JSON.parse('{"rE":"2.7.0-beta.1"}');
521
+ var package_namespaceObject = JSON.parse('{"rE":"2.8.0-beta.1"}');
522
522
  function useLaunchDarklyToolbar(args) {
523
523
  const { toolbarBundleUrl, enabled, ...initConfig } = args;
524
524
  const configRef = useRef(null);
package/dist/js/react.js CHANGED
@@ -49,7 +49,7 @@ async function lazyLoad(signal, url) {
49
49
  throw new Error(`Could not load LaunchDarkly developer toolbar bundle from ${url}`);
50
50
  }
51
51
  }
52
- var package_namespaceObject = JSON.parse('{"rE":"2.7.0-beta.1"}');
52
+ var package_namespaceObject = JSON.parse('{"rE":"2.8.0-beta.1"}');
53
53
  function useLaunchDarklyToolbar(args) {
54
54
  const { toolbarBundleUrl, enabled, ...initConfig } = args;
55
55
  const configRef = useRef(null);
package/dist/react.cjs CHANGED
@@ -78,7 +78,7 @@ async function lazyLoad(signal, url) {
78
78
  throw new Error(`Could not load LaunchDarkly developer toolbar bundle from ${url}`);
79
79
  }
80
80
  }
81
- var package_namespaceObject = JSON.parse('{"rE":"2.7.0-beta.1"}');
81
+ var package_namespaceObject = JSON.parse('{"rE":"2.8.0-beta.1"}');
82
82
  function useLaunchDarklyToolbar(args) {
83
83
  const { toolbarBundleUrl, enabled, ...initConfig } = args;
84
84
  const configRef = (0, external_react_namespaceObject.useRef)(null);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@launchdarkly/toolbar",
4
- "version": "2.7.0-beta.1",
4
+ "version": "2.8.0-beta.1",
5
5
  "description": "A framework-agnostic developer toolbar for interacting with LaunchDarkly during development",
6
6
  "keywords": [
7
7
  "launchdarkly",
@@ -93,51 +93,51 @@
93
93
  "devDependencies": {
94
94
  "@codemirror/commands": "^6.10.1",
95
95
  "@codemirror/lang-json": "^6.0.2",
96
- "@codemirror/language": "^6.11.3",
96
+ "@codemirror/language": "^6.12.1",
97
97
  "@codemirror/lint": "^6.9.2",
98
- "@codemirror/state": "^6.5.2",
99
- "@codemirror/view": "^6.38.8",
100
- "@launchdarkly/observability": "^0.4.9",
101
- "@launchdarkly/session-replay": "^0.4.9",
102
- "@launchpad-ui/components": "^0.17.7",
98
+ "@codemirror/state": "^6.5.3",
99
+ "@codemirror/view": "^6.39.7",
100
+ "@launchdarkly/observability": "^0.5.0",
101
+ "@launchdarkly/session-replay": "^0.5.0",
102
+ "@launchpad-ui/components": "^0.17.8",
103
103
  "@launchpad-ui/tokens": "^0.15.1",
104
104
  "@lezer/highlight": "^1.2.3",
105
105
  "@react-aria/focus": "^3.21.2",
106
106
  "@react-stately/flags": "^3.1.2",
107
- "@rsbuild/core": "^1.6.14",
107
+ "@rsbuild/core": "^1.7.1",
108
108
  "@rsbuild/plugin-react": "^1.4.2",
109
109
  "@rslib/core": "^0.18.4",
110
- "@storybook/addon-docs": "^10.1.5",
110
+ "@storybook/addon-docs": "^10.1.10",
111
111
  "@storybook/addon-essentials": "^9.0.0-alpha.12",
112
112
  "@storybook/addon-interactions": "^9.0.0-alpha.10",
113
- "@storybook/addon-links": "^10.1.7",
114
- "@storybook/addon-onboarding": "^10.1.7",
113
+ "@storybook/addon-links": "^10.1.10",
114
+ "@storybook/addon-onboarding": "^10.1.10",
115
115
  "@storybook/blocks": "^9.0.0-alpha.17",
116
- "@storybook/react": "^10.0.8",
116
+ "@storybook/react": "^10.1.10",
117
117
  "@storybook/react-vite": "^10.1.10",
118
118
  "@storybook/test": "^9.0.0-alpha.2",
119
119
  "@tanstack/react-virtual": "^3.13.13",
120
120
  "@testing-library/jest-dom": "^6.9.1",
121
121
  "@testing-library/react": "^16.3.0",
122
- "@types/node": "^25.0.1",
122
+ "@types/node": "^25.0.3",
123
123
  "@types/react": "19.2.7",
124
124
  "@types/react-dom": "19.2.3",
125
- "@vanilla-extract/css": "^1.17.5",
126
- "@vanilla-extract/vite-plugin": "^5.1.3",
127
- "@vanilla-extract/webpack-plugin": "^2.3.24",
128
- "@vitest/coverage-v8": "4.0.15",
125
+ "@vanilla-extract/css": "^1.18.0",
126
+ "@vanilla-extract/vite-plugin": "^5.1.4",
127
+ "@vanilla-extract/webpack-plugin": "^2.3.25",
128
+ "@vitest/coverage-v8": "4.0.16",
129
129
  "css-loader": "^7.1.2",
130
130
  "jsdom": "^27.3.0",
131
131
  "launchdarkly-js-client-sdk": "^3.9.0",
132
132
  "motion": "^12.23.26",
133
- "oxlint": "^1.32.0",
133
+ "oxlint": "^1.35.0",
134
134
  "react": "^19.2.3",
135
135
  "react-dom": "^19.2.3",
136
- "storybook": "^10.1.4",
136
+ "storybook": "^10.1.10",
137
137
  "storybook-addon-rslib": "^3.2.0",
138
138
  "storybook-react-rsbuild": "^3.1.0",
139
139
  "typescript": "^5.9.3",
140
- "vitest": "^4.0.15"
140
+ "vitest": "^4.0.16"
141
141
  },
142
142
  "peerDependencies": {
143
143
  "launchdarkly-js-client-sdk": ">=3.9.0 <4.0.0",