@ytspar/devbar 0.0.1 → 1.0.0-canary.19cb05c
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/LICENSE +21 -0
- package/README.md +173 -28
- package/dist/GlobalDevBar.d.ts +318 -0
- package/dist/GlobalDevBar.js +3281 -0
- package/dist/accessibility.d.ts +84 -0
- package/dist/accessibility.js +155 -0
- package/dist/constants.d.ts +301 -0
- package/dist/constants.js +641 -0
- package/dist/debug.d.ts +39 -0
- package/dist/debug.js +92 -0
- package/dist/earlyConsoleCapture.d.ts +34 -0
- package/dist/earlyConsoleCapture.js +77 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +25 -0
- package/dist/lazy/index.d.ts +6 -0
- package/dist/lazy/index.js +6 -0
- package/dist/lazy/lazyHtml2Canvas.d.ts +29 -0
- package/dist/lazy/lazyHtml2Canvas.js +37 -0
- package/dist/network.d.ts +92 -0
- package/dist/network.js +176 -0
- package/dist/outline.d.ts +14 -0
- package/dist/outline.js +248 -0
- package/dist/presets.d.ts +57 -0
- package/dist/presets.js +133 -0
- package/dist/schema.d.ts +14 -0
- package/dist/schema.js +114 -0
- package/dist/settings.d.ts +150 -0
- package/dist/settings.js +292 -0
- package/dist/storage.d.ts +83 -0
- package/dist/storage.js +182 -0
- package/dist/types.d.ts +75 -0
- package/dist/types.js +8 -0
- package/dist/ui/buttons.d.ts +21 -0
- package/dist/ui/buttons.js +55 -0
- package/dist/ui/icons.d.ts +13 -0
- package/dist/ui/icons.js +25 -0
- package/dist/ui/index.d.ts +8 -0
- package/dist/ui/index.js +8 -0
- package/dist/ui/modals.d.ts +44 -0
- package/dist/ui/modals.js +190 -0
- package/dist/utils.d.ts +11 -0
- package/dist/utils.js +13 -0
- package/package.json +58 -6
|
@@ -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;
|
package/dist/settings.js
ADDED
|
@@ -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;
|
package/dist/storage.js
ADDED
|
@@ -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
|
+
}
|