@ytspar/devbar 0.0.1 → 1.0.0-canary.2b99e1e

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.
@@ -0,0 +1,150 @@
1
+ /**
2
+ * DevBar Settings Persistence
3
+ *
4
+ * Handles saving and loading DevBar settings with Sweetlink server persistence
5
+ * and localStorage fallback.
6
+ */
7
+ import type { ThemeMode } from './types.js';
8
+ /**
9
+ * Position options for the DevBar
10
+ */
11
+ export type DevBarPosition = 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right' | 'bottom-center';
12
+ /**
13
+ * Metrics visibility configuration
14
+ */
15
+ export interface MetricsVisibility {
16
+ breakpoint: boolean;
17
+ fcp: boolean;
18
+ lcp: boolean;
19
+ cls: boolean;
20
+ inp: boolean;
21
+ pageSize: boolean;
22
+ }
23
+ /**
24
+ * Complete DevBar settings schema
25
+ */
26
+ export interface DevBarSettings {
27
+ /** Schema version for future migrations */
28
+ version: 1;
29
+ position: DevBarPosition;
30
+ themeMode: ThemeMode;
31
+ compactMode: boolean;
32
+ accentColor: string;
33
+ showScreenshot: boolean;
34
+ showConsoleBadges: boolean;
35
+ showTooltips: boolean;
36
+ showMetrics: MetricsVisibility;
37
+ debug: boolean;
38
+ }
39
+ /** Default accent color (emerald) */
40
+ export declare const DEFAULT_ACCENT_COLOR = "#10b981";
41
+ /** Preset accent colors for the color picker */
42
+ export declare const ACCENT_COLOR_PRESETS: readonly [{
43
+ readonly name: "Emerald";
44
+ readonly value: "#10b981";
45
+ }, {
46
+ readonly name: "Blue";
47
+ readonly value: "#3b82f6";
48
+ }, {
49
+ readonly name: "Purple";
50
+ readonly value: "#a855f7";
51
+ }, {
52
+ readonly name: "Pink";
53
+ readonly value: "#ec4899";
54
+ }, {
55
+ readonly name: "Amber";
56
+ readonly value: "#f59e0b";
57
+ }, {
58
+ readonly name: "Cyan";
59
+ readonly value: "#06b6d4";
60
+ }];
61
+ /**
62
+ * Default settings used when no saved settings exist
63
+ */
64
+ export declare const DEFAULT_SETTINGS: DevBarSettings;
65
+ /** LocalStorage key for DevBar settings fallback */
66
+ export declare const SETTINGS_STORAGE_KEY = "devbar-settings";
67
+ /**
68
+ * Callback type for settings change events
69
+ */
70
+ export type SettingsChangeCallback = (settings: DevBarSettings) => void;
71
+ /**
72
+ * SettingsManager handles loading and saving DevBar settings.
73
+ *
74
+ * Storage priority:
75
+ * 1. Sweetlink server (.devbar/settings.json) when connected
76
+ * 2. localStorage fallback when disconnected
77
+ *
78
+ * Settings are always saved to localStorage as a backup, ensuring
79
+ * settings persist even when Sweetlink is unavailable.
80
+ */
81
+ export declare class SettingsManager {
82
+ private settings;
83
+ private ws;
84
+ private sweetlinkConnected;
85
+ private changeCallbacks;
86
+ private saveTimeout;
87
+ private pendingLoadResolvers;
88
+ /** Debounce delay for saving settings (ms) */
89
+ private static readonly SAVE_DEBOUNCE_MS;
90
+ constructor();
91
+ /**
92
+ * Set the WebSocket connection for Sweetlink communication
93
+ */
94
+ setWebSocket(ws: WebSocket | null): void;
95
+ /**
96
+ * Update connection status (called when WebSocket connects/disconnects)
97
+ */
98
+ setConnected(connected: boolean): void;
99
+ /**
100
+ * Get current settings (synchronous)
101
+ */
102
+ getSettings(): DevBarSettings;
103
+ /**
104
+ * Get a specific setting value
105
+ */
106
+ get<K extends keyof DevBarSettings>(key: K): DevBarSettings[K];
107
+ /**
108
+ * Load settings from storage
109
+ *
110
+ * When Sweetlink is connected, requests settings from the server.
111
+ * Otherwise, loads from localStorage.
112
+ */
113
+ loadSettings(): Promise<DevBarSettings>;
114
+ /**
115
+ * Handle settings loaded from server (called by WebSocket message handler)
116
+ */
117
+ handleSettingsLoaded(settings: DevBarSettings | null): void;
118
+ /**
119
+ * Save settings (debounced)
120
+ *
121
+ * Saves to both Sweetlink server (if connected) and localStorage.
122
+ */
123
+ saveSettings(partial: Partial<DevBarSettings>): void;
124
+ /**
125
+ * Save settings immediately without debouncing
126
+ */
127
+ saveSettingsNow(partial: Partial<DevBarSettings>): void;
128
+ /**
129
+ * Reset settings to defaults
130
+ */
131
+ resetToDefaults(): void;
132
+ /**
133
+ * Subscribe to settings changes
134
+ */
135
+ onChange(callback: SettingsChangeCallback): () => void;
136
+ private performSave;
137
+ private notifyChange;
138
+ private loadFromLocalStorage;
139
+ private saveToLocalStorage;
140
+ private loadFromServer;
141
+ private saveToServer;
142
+ /**
143
+ * Migrate settings from older versions and fill in missing defaults
144
+ */
145
+ private migrateSettings;
146
+ }
147
+ /**
148
+ * Get the singleton SettingsManager instance
149
+ */
150
+ export declare function getSettingsManager(): SettingsManager;
@@ -0,0 +1,292 @@
1
+ /**
2
+ * DevBar Settings Persistence
3
+ *
4
+ * Handles saving and loading DevBar settings with Sweetlink server persistence
5
+ * and localStorage fallback.
6
+ */
7
+ // ============================================================================
8
+ // Default Settings
9
+ // ============================================================================
10
+ /** Default accent color (emerald) */
11
+ export const DEFAULT_ACCENT_COLOR = '#10b981';
12
+ /** Preset accent colors for the color picker */
13
+ export const ACCENT_COLOR_PRESETS = [
14
+ { name: 'Emerald', value: '#10b981' },
15
+ { name: 'Blue', value: '#3b82f6' },
16
+ { name: 'Purple', value: '#a855f7' },
17
+ { name: 'Pink', value: '#ec4899' },
18
+ { name: 'Amber', value: '#f59e0b' },
19
+ { name: 'Cyan', value: '#06b6d4' },
20
+ ];
21
+ /**
22
+ * Default settings used when no saved settings exist
23
+ */
24
+ export const DEFAULT_SETTINGS = {
25
+ version: 1,
26
+ // Display
27
+ position: 'bottom-left',
28
+ themeMode: 'system',
29
+ compactMode: false,
30
+ accentColor: DEFAULT_ACCENT_COLOR,
31
+ // Features
32
+ showScreenshot: true,
33
+ showConsoleBadges: true,
34
+ showTooltips: true,
35
+ // Metrics visibility
36
+ showMetrics: {
37
+ breakpoint: true,
38
+ fcp: true,
39
+ lcp: true,
40
+ cls: true,
41
+ inp: true,
42
+ pageSize: true,
43
+ },
44
+ // Debug
45
+ debug: false,
46
+ };
47
+ // ============================================================================
48
+ // Storage Keys
49
+ // ============================================================================
50
+ /** LocalStorage key for DevBar settings fallback */
51
+ export const SETTINGS_STORAGE_KEY = 'devbar-settings';
52
+ /**
53
+ * SettingsManager handles loading and saving DevBar settings.
54
+ *
55
+ * Storage priority:
56
+ * 1. Sweetlink server (.devbar/settings.json) when connected
57
+ * 2. localStorage fallback when disconnected
58
+ *
59
+ * Settings are always saved to localStorage as a backup, ensuring
60
+ * settings persist even when Sweetlink is unavailable.
61
+ */
62
+ export class SettingsManager {
63
+ constructor() {
64
+ this.ws = null;
65
+ this.sweetlinkConnected = false;
66
+ this.changeCallbacks = [];
67
+ this.saveTimeout = null;
68
+ this.pendingLoadResolvers = [];
69
+ this.settings = { ...DEFAULT_SETTINGS };
70
+ }
71
+ /**
72
+ * Set the WebSocket connection for Sweetlink communication
73
+ */
74
+ setWebSocket(ws) {
75
+ this.ws = ws;
76
+ this.sweetlinkConnected = ws !== null && ws.readyState === WebSocket.OPEN;
77
+ }
78
+ /**
79
+ * Update connection status (called when WebSocket connects/disconnects)
80
+ */
81
+ setConnected(connected) {
82
+ this.sweetlinkConnected = connected;
83
+ }
84
+ /**
85
+ * Get current settings (synchronous)
86
+ */
87
+ getSettings() {
88
+ return { ...this.settings };
89
+ }
90
+ /**
91
+ * Get a specific setting value
92
+ */
93
+ get(key) {
94
+ return this.settings[key];
95
+ }
96
+ /**
97
+ * Load settings from storage
98
+ *
99
+ * When Sweetlink is connected, requests settings from the server.
100
+ * Otherwise, loads from localStorage.
101
+ */
102
+ async loadSettings() {
103
+ // Always start with localStorage to have immediate values
104
+ const localSettings = this.loadFromLocalStorage();
105
+ this.settings = localSettings;
106
+ // If Sweetlink is connected, request server settings
107
+ if (this.sweetlinkConnected && this.ws && this.ws.readyState === WebSocket.OPEN) {
108
+ try {
109
+ const serverSettings = await this.loadFromServer();
110
+ if (serverSettings) {
111
+ this.settings = serverSettings;
112
+ // Sync to localStorage as backup
113
+ this.saveToLocalStorage(this.settings);
114
+ }
115
+ }
116
+ catch (error) {
117
+ console.warn('[DevBar] Failed to load settings from server, using localStorage:', error);
118
+ }
119
+ }
120
+ return this.settings;
121
+ }
122
+ /**
123
+ * Handle settings loaded from server (called by WebSocket message handler)
124
+ */
125
+ handleSettingsLoaded(settings) {
126
+ if (settings) {
127
+ this.settings = this.migrateSettings(settings);
128
+ this.saveToLocalStorage(this.settings);
129
+ this.notifyChange();
130
+ }
131
+ // Resolve any pending load promises
132
+ const resolvers = this.pendingLoadResolvers;
133
+ this.pendingLoadResolvers = [];
134
+ for (const resolve of resolvers) {
135
+ resolve(this.settings);
136
+ }
137
+ }
138
+ /**
139
+ * Save settings (debounced)
140
+ *
141
+ * Saves to both Sweetlink server (if connected) and localStorage.
142
+ */
143
+ saveSettings(partial) {
144
+ // Merge with current settings
145
+ this.settings = { ...this.settings, ...partial };
146
+ // Debounce saves to avoid excessive writes
147
+ if (this.saveTimeout) {
148
+ clearTimeout(this.saveTimeout);
149
+ }
150
+ this.saveTimeout = setTimeout(() => {
151
+ this.saveTimeout = null;
152
+ this.performSave();
153
+ }, SettingsManager.SAVE_DEBOUNCE_MS);
154
+ }
155
+ /**
156
+ * Save settings immediately without debouncing
157
+ */
158
+ saveSettingsNow(partial) {
159
+ if (this.saveTimeout) {
160
+ clearTimeout(this.saveTimeout);
161
+ this.saveTimeout = null;
162
+ }
163
+ this.settings = { ...this.settings, ...partial };
164
+ this.performSave();
165
+ }
166
+ /**
167
+ * Reset settings to defaults
168
+ */
169
+ resetToDefaults() {
170
+ this.settings = { ...DEFAULT_SETTINGS };
171
+ this.performSave();
172
+ this.notifyChange();
173
+ }
174
+ /**
175
+ * Subscribe to settings changes
176
+ */
177
+ onChange(callback) {
178
+ this.changeCallbacks.push(callback);
179
+ return () => {
180
+ this.changeCallbacks = this.changeCallbacks.filter((cb) => cb !== callback);
181
+ };
182
+ }
183
+ // ============================================================================
184
+ // Private Methods
185
+ // ============================================================================
186
+ performSave() {
187
+ // Always save to localStorage as backup
188
+ this.saveToLocalStorage(this.settings);
189
+ // Save to server if connected (saveToServer checks readyState internally)
190
+ if (this.sweetlinkConnected) {
191
+ this.saveToServer(this.settings);
192
+ }
193
+ this.notifyChange();
194
+ }
195
+ notifyChange() {
196
+ for (const callback of this.changeCallbacks) {
197
+ try {
198
+ callback(this.settings);
199
+ }
200
+ catch (error) {
201
+ console.error('[DevBar] Settings change callback error:', error);
202
+ }
203
+ }
204
+ }
205
+ loadFromLocalStorage() {
206
+ if (typeof localStorage === 'undefined') {
207
+ return { ...DEFAULT_SETTINGS };
208
+ }
209
+ try {
210
+ const stored = localStorage.getItem(SETTINGS_STORAGE_KEY);
211
+ if (!stored) {
212
+ return { ...DEFAULT_SETTINGS };
213
+ }
214
+ const parsed = JSON.parse(stored);
215
+ return this.migrateSettings(parsed);
216
+ }
217
+ catch (error) {
218
+ console.warn('[DevBar] Failed to parse localStorage settings:', error);
219
+ return { ...DEFAULT_SETTINGS };
220
+ }
221
+ }
222
+ saveToLocalStorage(settings) {
223
+ if (typeof localStorage === 'undefined')
224
+ return;
225
+ try {
226
+ localStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(settings));
227
+ }
228
+ catch (error) {
229
+ console.warn('[DevBar] Failed to save settings to localStorage:', error);
230
+ }
231
+ }
232
+ async loadFromServer() {
233
+ return new Promise((resolve) => {
234
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
235
+ resolve(null);
236
+ return;
237
+ }
238
+ // Store resolver to be called when settings-loaded message arrives
239
+ this.pendingLoadResolvers.push(resolve);
240
+ // Request settings from server
241
+ this.ws.send(JSON.stringify({ type: 'load-settings' }));
242
+ // Timeout after 5 seconds
243
+ setTimeout(() => {
244
+ const index = this.pendingLoadResolvers.indexOf(resolve);
245
+ if (index !== -1) {
246
+ this.pendingLoadResolvers.splice(index, 1);
247
+ resolve(null);
248
+ }
249
+ }, 5000);
250
+ });
251
+ }
252
+ saveToServer(settings) {
253
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
254
+ return;
255
+ this.ws.send(JSON.stringify({
256
+ type: 'save-settings',
257
+ data: { settings },
258
+ }));
259
+ }
260
+ /**
261
+ * Migrate settings from older versions and fill in missing defaults
262
+ */
263
+ migrateSettings(partial) {
264
+ // Merge partial settings over defaults, then handle nested objects specially
265
+ return {
266
+ ...DEFAULT_SETTINGS,
267
+ ...partial,
268
+ // Always use current schema version
269
+ version: 1,
270
+ // Deep merge showMetrics to preserve unset defaults
271
+ showMetrics: {
272
+ ...DEFAULT_SETTINGS.showMetrics,
273
+ ...partial.showMetrics,
274
+ },
275
+ };
276
+ }
277
+ }
278
+ /** Debounce delay for saving settings (ms) */
279
+ SettingsManager.SAVE_DEBOUNCE_MS = 300;
280
+ /**
281
+ * Singleton settings manager instance
282
+ */
283
+ let settingsManagerInstance = null;
284
+ /**
285
+ * Get the singleton SettingsManager instance
286
+ */
287
+ export function getSettingsManager() {
288
+ if (!settingsManagerInstance) {
289
+ settingsManagerInstance = new SettingsManager();
290
+ }
291
+ return settingsManagerInstance;
292
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Storage Inspector Utilities
3
+ *
4
+ * Utilities for inspecting and managing browser storage (localStorage, sessionStorage, cookies).
5
+ */
6
+ /**
7
+ * Storage item with parsed value
8
+ */
9
+ export interface StorageItem {
10
+ key: string;
11
+ value: string;
12
+ parsedValue?: unknown;
13
+ isParseable: boolean;
14
+ size: number;
15
+ }
16
+ /**
17
+ * Cookie item
18
+ */
19
+ export interface CookieItem {
20
+ name: string;
21
+ value: string;
22
+ size: number;
23
+ }
24
+ /**
25
+ * Storage data from all sources
26
+ */
27
+ export interface StorageData {
28
+ localStorage: StorageItem[];
29
+ sessionStorage: StorageItem[];
30
+ cookies: CookieItem[];
31
+ }
32
+ /**
33
+ * Get all localStorage items
34
+ */
35
+ export declare function getLocalStorage(): StorageItem[];
36
+ /**
37
+ * Get all sessionStorage items
38
+ */
39
+ export declare function getSessionStorage(): StorageItem[];
40
+ /**
41
+ * Get all cookies
42
+ */
43
+ export declare function getCookies(): CookieItem[];
44
+ /**
45
+ * Get all storage data from all sources
46
+ */
47
+ export declare function getStorageData(): StorageData;
48
+ /**
49
+ * Set a localStorage item
50
+ */
51
+ export declare function setLocalStorageItem(key: string, value: string): void;
52
+ /**
53
+ * Delete a localStorage item
54
+ */
55
+ export declare function deleteLocalStorageItem(key: string): void;
56
+ /**
57
+ * Set a sessionStorage item
58
+ */
59
+ export declare function setSessionStorageItem(key: string, value: string): void;
60
+ /**
61
+ * Delete a sessionStorage item
62
+ */
63
+ export declare function deleteSessionStorageItem(key: string): void;
64
+ /**
65
+ * Delete a cookie by name
66
+ */
67
+ export declare function deleteCookie(name: string): void;
68
+ /**
69
+ * Clear all localStorage
70
+ */
71
+ export declare function clearLocalStorage(): void;
72
+ /**
73
+ * Clear all sessionStorage
74
+ */
75
+ export declare function clearSessionStorage(): void;
76
+ /**
77
+ * Format storage size summary
78
+ */
79
+ export declare function formatStorageSummary(data: StorageData): string;
80
+ /**
81
+ * Beautify JSON string for display
82
+ */
83
+ export declare function beautifyJson(value: string): string;
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Storage Inspector Utilities
3
+ *
4
+ * Utilities for inspecting and managing browser storage (localStorage, sessionStorage, cookies).
5
+ */
6
+ /**
7
+ * Try to parse a JSON string
8
+ */
9
+ function tryParseJson(value) {
10
+ try {
11
+ const parsed = JSON.parse(value);
12
+ return { parsed, success: true };
13
+ }
14
+ catch {
15
+ return { parsed: undefined, success: false };
16
+ }
17
+ }
18
+ /**
19
+ * Get all items from a Storage object (localStorage or sessionStorage)
20
+ */
21
+ function getStorageItems(storage) {
22
+ const items = [];
23
+ for (let i = 0; i < storage.length; i++) {
24
+ const key = storage.key(i);
25
+ if (key === null)
26
+ continue;
27
+ const value = storage.getItem(key) ?? '';
28
+ const { parsed, success } = tryParseJson(value);
29
+ items.push({
30
+ key,
31
+ value,
32
+ parsedValue: success ? parsed : undefined,
33
+ isParseable: success,
34
+ size: new Blob([value]).size,
35
+ });
36
+ }
37
+ // Sort by key
38
+ return items.sort((a, b) => a.key.localeCompare(b.key));
39
+ }
40
+ /**
41
+ * Get all localStorage items
42
+ */
43
+ export function getLocalStorage() {
44
+ if (typeof localStorage === 'undefined')
45
+ return [];
46
+ return getStorageItems(localStorage);
47
+ }
48
+ /**
49
+ * Get all sessionStorage items
50
+ */
51
+ export function getSessionStorage() {
52
+ if (typeof sessionStorage === 'undefined')
53
+ return [];
54
+ return getStorageItems(sessionStorage);
55
+ }
56
+ /**
57
+ * Get all cookies
58
+ */
59
+ export function getCookies() {
60
+ if (typeof document === 'undefined' || !document.cookie)
61
+ return [];
62
+ return document.cookie
63
+ .split(';')
64
+ .map((cookie) => {
65
+ const [name, ...valueParts] = cookie.trim().split('=');
66
+ const value = valueParts.join('=');
67
+ return {
68
+ name: name.trim(),
69
+ value: decodeURIComponent(value || ''),
70
+ size: new Blob([value || '']).size,
71
+ };
72
+ })
73
+ .filter((c) => c.name)
74
+ .sort((a, b) => a.name.localeCompare(b.name));
75
+ }
76
+ /**
77
+ * Get all storage data from all sources
78
+ */
79
+ export function getStorageData() {
80
+ return {
81
+ localStorage: getLocalStorage(),
82
+ sessionStorage: getSessionStorage(),
83
+ cookies: getCookies(),
84
+ };
85
+ }
86
+ /**
87
+ * Set a localStorage item
88
+ */
89
+ export function setLocalStorageItem(key, value) {
90
+ if (typeof localStorage === 'undefined')
91
+ return;
92
+ localStorage.setItem(key, value);
93
+ }
94
+ /**
95
+ * Delete a localStorage item
96
+ */
97
+ export function deleteLocalStorageItem(key) {
98
+ if (typeof localStorage === 'undefined')
99
+ return;
100
+ localStorage.removeItem(key);
101
+ }
102
+ /**
103
+ * Set a sessionStorage item
104
+ */
105
+ export function setSessionStorageItem(key, value) {
106
+ if (typeof sessionStorage === 'undefined')
107
+ return;
108
+ sessionStorage.setItem(key, value);
109
+ }
110
+ /**
111
+ * Delete a sessionStorage item
112
+ */
113
+ export function deleteSessionStorageItem(key) {
114
+ if (typeof sessionStorage === 'undefined')
115
+ return;
116
+ sessionStorage.removeItem(key);
117
+ }
118
+ /**
119
+ * Delete a cookie by name
120
+ */
121
+ export function deleteCookie(name) {
122
+ if (typeof document === 'undefined')
123
+ return;
124
+ document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
125
+ }
126
+ /**
127
+ * Clear all localStorage
128
+ */
129
+ export function clearLocalStorage() {
130
+ if (typeof localStorage === 'undefined')
131
+ return;
132
+ localStorage.clear();
133
+ }
134
+ /**
135
+ * Clear all sessionStorage
136
+ */
137
+ export function clearSessionStorage() {
138
+ if (typeof sessionStorage === 'undefined')
139
+ return;
140
+ sessionStorage.clear();
141
+ }
142
+ /**
143
+ * Format storage size summary
144
+ */
145
+ export function formatStorageSummary(data) {
146
+ const localSize = data.localStorage.reduce((sum, item) => sum + item.size, 0);
147
+ const sessionSize = data.sessionStorage.reduce((sum, item) => sum + item.size, 0);
148
+ const cookieSize = data.cookies.reduce((sum, item) => sum + item.size, 0);
149
+ const parts = [];
150
+ if (data.localStorage.length > 0) {
151
+ parts.push(`localStorage: ${data.localStorage.length} items (${formatBytes(localSize)})`);
152
+ }
153
+ if (data.sessionStorage.length > 0) {
154
+ parts.push(`sessionStorage: ${data.sessionStorage.length} items (${formatBytes(sessionSize)})`);
155
+ }
156
+ if (data.cookies.length > 0) {
157
+ parts.push(`cookies: ${data.cookies.length} (${formatBytes(cookieSize)})`);
158
+ }
159
+ return parts.join(' | ') || 'No storage data';
160
+ }
161
+ /**
162
+ * Format bytes to human-readable string
163
+ */
164
+ function formatBytes(bytes) {
165
+ if (bytes < 1024)
166
+ return `${bytes} B`;
167
+ if (bytes < 1024 * 1024)
168
+ return `${(bytes / 1024).toFixed(1)} KB`;
169
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
170
+ }
171
+ /**
172
+ * Beautify JSON string for display
173
+ */
174
+ export function beautifyJson(value) {
175
+ try {
176
+ const parsed = JSON.parse(value);
177
+ return JSON.stringify(parsed, null, 2);
178
+ }
179
+ catch {
180
+ return value;
181
+ }
182
+ }