@gxp-dev/tools 2.0.6 → 2.0.8

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 (100) hide show
  1. package/bin/lib/commands/build.js +18 -12
  2. package/browser-extensions/README.md +1 -0
  3. package/browser-extensions/chrome/background.js +857 -0
  4. package/browser-extensions/chrome/content.js +51 -0
  5. package/browser-extensions/chrome/devtools.html +9 -0
  6. package/browser-extensions/chrome/devtools.js +23 -0
  7. package/browser-extensions/chrome/icons/gx_off_128.png +0 -0
  8. package/browser-extensions/chrome/icons/gx_off_16.png +0 -0
  9. package/browser-extensions/chrome/icons/gx_off_32.png +0 -0
  10. package/browser-extensions/chrome/icons/gx_off_64.png +0 -0
  11. package/browser-extensions/chrome/icons/gx_on_128.png +0 -0
  12. package/browser-extensions/chrome/icons/gx_on_16.png +0 -0
  13. package/browser-extensions/chrome/icons/gx_on_32.png +0 -0
  14. package/browser-extensions/chrome/icons/gx_on_64.png +0 -0
  15. package/browser-extensions/chrome/inspector.js +1087 -0
  16. package/browser-extensions/chrome/manifest.json +70 -0
  17. package/browser-extensions/chrome/panel.html +638 -0
  18. package/browser-extensions/chrome/panel.js +862 -0
  19. package/browser-extensions/chrome/popup.html +399 -0
  20. package/browser-extensions/chrome/popup.js +515 -0
  21. package/browser-extensions/chrome/rules.json +1 -0
  22. package/browser-extensions/chrome/test-chrome.html +145 -0
  23. package/browser-extensions/chrome/test-mixed-content.html +190 -0
  24. package/browser-extensions/chrome/test-uri-pattern.html +199 -0
  25. package/browser-extensions/firefox/README.md +134 -0
  26. package/browser-extensions/firefox/background.js +804 -0
  27. package/browser-extensions/firefox/content.js +120 -0
  28. package/browser-extensions/firefox/debug-errors.html +229 -0
  29. package/browser-extensions/firefox/debug-https.html +113 -0
  30. package/browser-extensions/firefox/devtools.html +9 -0
  31. package/browser-extensions/firefox/devtools.js +24 -0
  32. package/browser-extensions/firefox/icons/gx_off_128.png +0 -0
  33. package/browser-extensions/firefox/icons/gx_off_16.png +0 -0
  34. package/browser-extensions/firefox/icons/gx_off_32.png +0 -0
  35. package/browser-extensions/firefox/icons/gx_off_64.png +0 -0
  36. package/browser-extensions/firefox/icons/gx_on_128.png +0 -0
  37. package/browser-extensions/firefox/icons/gx_on_16.png +0 -0
  38. package/browser-extensions/firefox/icons/gx_on_32.png +0 -0
  39. package/browser-extensions/firefox/icons/gx_on_64.png +0 -0
  40. package/browser-extensions/firefox/inspector.js +1087 -0
  41. package/browser-extensions/firefox/manifest.json +67 -0
  42. package/browser-extensions/firefox/panel.html +638 -0
  43. package/browser-extensions/firefox/panel.js +862 -0
  44. package/browser-extensions/firefox/popup.html +525 -0
  45. package/browser-extensions/firefox/popup.js +536 -0
  46. package/browser-extensions/firefox/test-gramercy.html +126 -0
  47. package/browser-extensions/firefox/test-imports.html +58 -0
  48. package/browser-extensions/firefox/test-masking.html +147 -0
  49. package/browser-extensions/firefox/test-uri-pattern.html +199 -0
  50. package/package.json +7 -2
  51. package/runtime/PortalContainer.vue +326 -0
  52. package/runtime/dev-tools/DevToolsModal.vue +217 -0
  53. package/runtime/dev-tools/LayoutSwitcher.vue +221 -0
  54. package/runtime/dev-tools/MockDataEditor.vue +621 -0
  55. package/runtime/dev-tools/SocketSimulator.vue +562 -0
  56. package/runtime/dev-tools/StoreInspector.vue +644 -0
  57. package/runtime/dev-tools/index.js +6 -0
  58. package/runtime/gxpStringsPlugin.js +428 -0
  59. package/runtime/index.html +22 -0
  60. package/runtime/main.js +32 -0
  61. package/runtime/mock-api/auth-middleware.js +97 -0
  62. package/runtime/mock-api/image-generator.js +221 -0
  63. package/runtime/mock-api/index.js +197 -0
  64. package/runtime/mock-api/response-generator.js +394 -0
  65. package/runtime/mock-api/route-generator.js +323 -0
  66. package/runtime/mock-api/socket-triggers.js +371 -0
  67. package/runtime/mock-api/spec-loader.js +300 -0
  68. package/runtime/server.js +180 -0
  69. package/runtime/stores/gxpPortalConfigStore.js +554 -0
  70. package/runtime/stores/index.js +6 -0
  71. package/runtime/vite-inspector-plugin.js +749 -0
  72. package/runtime/vite-source-tracker-plugin.js +232 -0
  73. package/runtime/vite.config.js +402 -0
  74. package/scripts/launch-chrome.js +90 -0
  75. package/scripts/pack-chrome.js +91 -0
  76. package/socket-events/AiSessionMessageCreated.json +18 -0
  77. package/socket-events/SocialStreamPostCreated.json +24 -0
  78. package/socket-events/SocialStreamPostVariantCompleted.json +23 -0
  79. package/template/README.md +332 -0
  80. package/template/app-manifest.json +32 -0
  81. package/template/dev-assets/images/avatar-placeholder.png +0 -0
  82. package/template/dev-assets/images/background-placeholder.jpg +0 -0
  83. package/template/dev-assets/images/banner-placeholder.jpg +0 -0
  84. package/template/dev-assets/images/icon-placeholder.png +0 -0
  85. package/template/dev-assets/images/logo-placeholder.png +0 -0
  86. package/template/dev-assets/images/product-placeholder.jpg +0 -0
  87. package/template/dev-assets/images/thumbnail-placeholder.jpg +0 -0
  88. package/template/env.example +51 -0
  89. package/template/gitignore +53 -0
  90. package/template/index.html +22 -0
  91. package/template/main.js +28 -0
  92. package/template/src/DemoPage.vue +459 -0
  93. package/template/src/Plugin.vue +38 -0
  94. package/template/src/stores/index.js +9 -0
  95. package/template/src/stores/test-data.json +173 -0
  96. package/template/theme-layouts/AdditionalStyling.css +0 -0
  97. package/template/theme-layouts/PrivateLayout.vue +39 -0
  98. package/template/theme-layouts/PublicLayout.vue +39 -0
  99. package/template/theme-layouts/SystemLayout.vue +39 -0
  100. package/template/vite.config.js +333 -0
@@ -0,0 +1,804 @@
1
+ // Extension state
2
+ let config = {
3
+ enabled: false,
4
+ // Legacy fields for backward compatibility
5
+ redirectUrl: "https://localhost:3060/src/Plugin.vue",
6
+ urlPattern: "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
7
+ useCustomPattern: false,
8
+ // New rules-based configuration
9
+ rules: {
10
+ js: {
11
+ enabled: true,
12
+ pattern: "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
13
+ redirectUrl: "https://localhost:3060/src/Plugin.vue",
14
+ useCustomPattern: false,
15
+ },
16
+ css: {
17
+ enabled: false,
18
+ pattern:
19
+ "uploads\\/plugin-version\\/\\d+\\/style_file_name\\/.*\\.css(\\?.*)?",
20
+ redirectUrl: "",
21
+ returnBlank: false,
22
+ useCustomPattern: false,
23
+ },
24
+ },
25
+ maskingMode: false,
26
+ clearCacheOnEnable: true,
27
+ disableCacheForRedirects: true,
28
+ };
29
+
30
+ let notificationTimeouts = new Map();
31
+ let cacheBlacklist = new Set();
32
+
33
+ // Initialize extension
34
+ browser.runtime.onStartup.addListener(initializeExtension);
35
+ browser.runtime.onInstalled.addListener(initializeExtension);
36
+
37
+ async function initializeExtension() {
38
+ console.log("[JavaScript Proxy] Initializing extension...");
39
+ try {
40
+ // Load saved configuration
41
+ const defaultConfig = {
42
+ enabled: false,
43
+ // Legacy fields for backward compatibility
44
+ redirectUrl: "https://localhost:3060/src/Plugin.vue",
45
+ urlPattern:
46
+ "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
47
+ useCustomPattern: false,
48
+ // New rules-based configuration
49
+ rules: {
50
+ js: {
51
+ enabled: true,
52
+ pattern:
53
+ "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
54
+ redirectUrl: "https://localhost:3060/src/Plugin.vue",
55
+ useCustomPattern: false,
56
+ },
57
+ css: {
58
+ enabled: false,
59
+ pattern:
60
+ "uploads\\/plugin-version\\/\\d+\\/style_file_name\\/.*\\.css(\\?.*)?",
61
+ redirectUrl: "",
62
+ returnBlank: false,
63
+ useCustomPattern: false,
64
+ },
65
+ },
66
+ maskingMode: false,
67
+ clearCacheOnEnable: true,
68
+ disableCacheForRedirects: true,
69
+ };
70
+
71
+ const result = await browser.storage.sync.get(defaultConfig);
72
+ config = result;
73
+
74
+ // Migrate legacy configuration to new rules format
75
+ config = migrateConfig(config);
76
+
77
+ console.log("[JavaScript Proxy] Loaded configuration:", config);
78
+
79
+ updateIcon();
80
+ updateRequestListener();
81
+
82
+ console.log("[JavaScript Proxy] Extension initialized successfully");
83
+ } catch (error) {
84
+ console.error("[JavaScript Proxy] Failed to initialize:", error);
85
+ }
86
+ }
87
+
88
+ // Migrate legacy configuration to new rules format
89
+ function migrateConfig(config) {
90
+ // If rules don't exist, create them from legacy config
91
+ if (!config.rules) {
92
+ config.rules = {
93
+ js: {
94
+ enabled: true,
95
+ pattern:
96
+ config.urlPattern ||
97
+ "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
98
+ redirectUrl:
99
+ config.redirectUrl || "https://localhost:3060/src/Plugin.vue",
100
+ useCustomPattern: config.useCustomPattern || false,
101
+ },
102
+ css: {
103
+ enabled: true,
104
+ pattern:
105
+ "uploads\\/plugin-version\\/\\d+\\/style_file_name\\/.*\\.css(\\?.*)?",
106
+ redirectUrl: "",
107
+ returnBlank: true,
108
+ useCustomPattern: false,
109
+ },
110
+ };
111
+ } else {
112
+ // Ensure all required fields exist
113
+ if (!config.rules.js) {
114
+ config.rules.js = {
115
+ enabled: true,
116
+ pattern:
117
+ config.urlPattern ||
118
+ "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
119
+ redirectUrl:
120
+ config.redirectUrl || "https://localhost:3060/src/Plugin.vue",
121
+ useCustomPattern: config.useCustomPattern || false,
122
+ };
123
+ }
124
+ if (!config.rules.css) {
125
+ config.rules.css = {
126
+ enabled: true,
127
+ pattern:
128
+ "uploads\\/plugin-version\\/\\d+\\/style_file_name\\/.*\\.css(\\?.*)?",
129
+ redirectUrl: "",
130
+ returnBlank: true,
131
+ useCustomPattern: false,
132
+ };
133
+ }
134
+ }
135
+
136
+ return config;
137
+ }
138
+
139
+ // Update toolbar icon based on state
140
+ function updateIcon() {
141
+ const iconPath = config.enabled ? "icons/gx_on" : "icons/gx_off";
142
+ browser.browserAction.setIcon({
143
+ path: {
144
+ 16: `${iconPath}_16.png`,
145
+ 32: `${iconPath}_32.png`,
146
+ 48: `${iconPath}_48.png`,
147
+ 128: `${iconPath}_128.png`,
148
+ },
149
+ });
150
+
151
+ const title = config.enabled
152
+ ? "JavaScript Proxy (ON)"
153
+ : "JavaScript Proxy (OFF)";
154
+ browser.browserAction.setTitle({ title });
155
+ }
156
+
157
+ // Cache management functions
158
+ async function clearCacheForPattern(urlPattern = null) {
159
+ try {
160
+ // Collect all patterns that need cache clearing
161
+ const patterns = [];
162
+
163
+ if (urlPattern) {
164
+ patterns.push(urlPattern);
165
+ } else {
166
+ // Clear cache for all enabled rules
167
+ for (const [ruleType, rule] of Object.entries(config.rules || {})) {
168
+ if (rule.enabled && rule.pattern) {
169
+ patterns.push(rule.pattern);
170
+ }
171
+ }
172
+
173
+ // Include legacy pattern if exists
174
+ if (config.urlPattern) {
175
+ patterns.push(config.urlPattern);
176
+ }
177
+ }
178
+
179
+ console.log(`[JavaScript Proxy] Clearing cache for patterns:`, patterns);
180
+
181
+ // Clear browser cache
182
+ await browser.browsingData.removeCache({
183
+ origins: [], // Empty array means all origins
184
+ since: 0, // Clear all cache entries
185
+ });
186
+
187
+ // Clear service worker caches for each pattern
188
+ for (const pattern of patterns) {
189
+ await clearServiceWorkerCaches(pattern);
190
+ }
191
+
192
+ console.log("[JavaScript Proxy] Cache cleared successfully");
193
+ } catch (error) {
194
+ console.error("[JavaScript Proxy] Error clearing cache:", error);
195
+ }
196
+ }
197
+
198
+ async function clearServiceWorkerCaches(urlPattern) {
199
+ try {
200
+ // Get all active tabs
201
+ const tabs = await browser.tabs.query({});
202
+
203
+ // Script to clear service worker caches for matching URLs
204
+ const clearCacheScript = `
205
+ (async function() {
206
+ try {
207
+ if ('caches' in window) {
208
+ const cacheNames = await caches.keys();
209
+ const pattern = new RegExp("${urlPattern.replace(/\\/g, "\\\\")}", "i");
210
+
211
+ for (const cacheName of cacheNames) {
212
+ const cache = await caches.open(cacheName);
213
+ const requests = await cache.keys();
214
+
215
+ for (const request of requests) {
216
+ if (pattern.test(request.url)) {
217
+ await cache.delete(request);
218
+ console.log('[JavaScript Proxy] Deleted from cache:', request.url);
219
+ }
220
+ }
221
+ }
222
+
223
+ // Also try to update service worker registration
224
+ if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
225
+ navigator.serviceWorker.controller.postMessage({
226
+ type: 'CLEAR_CACHE_FOR_PATTERN',
227
+ pattern: "${urlPattern.replace(/\\/g, "\\\\")}"
228
+ });
229
+ }
230
+ }
231
+ } catch (error) {
232
+ console.error('[JavaScript Proxy] Error clearing service worker cache:', error);
233
+ }
234
+ })();
235
+ `;
236
+
237
+ // Inject the script into each tab
238
+ for (const tab of tabs) {
239
+ try {
240
+ if (
241
+ tab.url &&
242
+ !tab.url.startsWith("moz-extension://") &&
243
+ !tab.url.startsWith("about:")
244
+ ) {
245
+ await browser.tabs.executeScript(tab.id, {
246
+ code: clearCacheScript,
247
+ });
248
+ }
249
+ } catch (error) {
250
+ // Tab might not allow script injection, continue with others
251
+ console.debug(
252
+ `[JavaScript Proxy] Could not inject cache clear script into tab ${tab.id}:`,
253
+ error
254
+ );
255
+ }
256
+ }
257
+
258
+ console.log("[JavaScript Proxy] Service worker cache clearing attempted");
259
+ } catch (error) {
260
+ console.error(
261
+ "[JavaScript Proxy] Error clearing service worker caches:",
262
+ error
263
+ );
264
+ }
265
+ }
266
+
267
+ async function addToCacheBlacklist(url) {
268
+ try {
269
+ const urlObj = new URL(url);
270
+ const origin = urlObj.origin;
271
+ cacheBlacklist.add(origin);
272
+ console.log(`[JavaScript Proxy] Added ${origin} to cache blacklist`);
273
+ } catch (error) {
274
+ console.error("[JavaScript Proxy] Error adding to cache blacklist:", error);
275
+ }
276
+ }
277
+
278
+ function shouldDisableCache(url) {
279
+ if (!config.disableCacheForRedirects) return false;
280
+
281
+ try {
282
+ // Check against all enabled rules
283
+ for (const [ruleType, rule] of Object.entries(config.rules || {})) {
284
+ if (rule.enabled) {
285
+ const pattern =
286
+ rule.useCustomPattern && rule.pattern ? rule.pattern : rule.pattern;
287
+ const regex = new RegExp(pattern, "i");
288
+ if (regex.test(url)) {
289
+ return true;
290
+ }
291
+ }
292
+ }
293
+
294
+ // Fallback to legacy pattern for backward compatibility
295
+ if (config.urlPattern) {
296
+ const regex = new RegExp(config.urlPattern, "i");
297
+ return regex.test(url);
298
+ }
299
+
300
+ return false;
301
+ } catch (error) {
302
+ console.error(
303
+ "[JavaScript Proxy] Error checking cache disable pattern:",
304
+ error
305
+ );
306
+ return false;
307
+ }
308
+ }
309
+
310
+ // Handle web requests
311
+ function handleRequest(details) {
312
+ if (!config.enabled) return {};
313
+
314
+ // Skip certain request types that commonly cause issues
315
+ const skipTypes = ["ping", "csp_report", "beacon"];
316
+ if (skipTypes.includes(details.type)) {
317
+ return {};
318
+ }
319
+
320
+ // Skip data URLs and blob URLs
321
+ if (
322
+ details.url.startsWith("data:") ||
323
+ details.url.startsWith("blob:") ||
324
+ details.url.startsWith("chrome:") ||
325
+ details.url.startsWith("moz-extension:")
326
+ ) {
327
+ return {};
328
+ }
329
+
330
+ try {
331
+ // Check each rule type
332
+ for (const [ruleType, rule] of Object.entries(config.rules || {})) {
333
+ if (!rule.enabled || !rule.pattern) continue;
334
+
335
+ // Check if this rule type matches the resource type
336
+ if (ruleType === "js" && details.type !== "script") continue;
337
+ if (ruleType === "css" && details.type !== "stylesheet") continue;
338
+
339
+ const regex = new RegExp(rule.pattern, "i");
340
+
341
+ // Test the full URL against the pattern
342
+ if (regex.test(details.url)) {
343
+ // Handle blank return for CSS
344
+ if (rule.returnBlank) {
345
+ console.log(
346
+ `[JavaScript Proxy] Returning blank for ${ruleType}: ${details.url}`
347
+ );
348
+ // Return a data URL with empty content
349
+ const blankUrl =
350
+ ruleType === "css"
351
+ ? "data:text/css;charset=utf-8,"
352
+ : "data:text/javascript;charset=utf-8,";
353
+ return { redirectUrl: blankUrl };
354
+ }
355
+
356
+ if (rule.redirectUrl) {
357
+ let newUrl = rule.redirectUrl;
358
+
359
+ // Ensure URL has protocol
360
+ if (!newUrl.includes("://")) {
361
+ newUrl = "https://" + newUrl;
362
+ }
363
+
364
+ const requestType = getRequestTypeDescription(details.type);
365
+
366
+ // Check if this would create a redirect loop
367
+ if (details.url === newUrl || details.url.includes("localhost")) {
368
+ console.log(
369
+ `[JavaScript Proxy] Skipping redirect loop: ${details.url}`
370
+ );
371
+ return {};
372
+ }
373
+
374
+ // Check if this rule should use URL masking
375
+ if (config.maskingMode) {
376
+ // Store original URL for header modification
377
+ storeOriginalUrl(details.requestId, details.url, newUrl);
378
+
379
+ console.log(
380
+ `[JavaScript Proxy] Masking ${ruleType}: ${details.url} (routing to ${newUrl})`
381
+ );
382
+
383
+ // Don't redirect - we'll modify headers instead
384
+ return {};
385
+ } else {
386
+ // Clear cache if enabled (for traditional redirect)
387
+ if (config.clearCacheOnEnable) {
388
+ clearCacheForPattern(rule.pattern);
389
+ }
390
+
391
+ // Traditional redirect approach
392
+ showProxyNotification(details.url, newUrl, requestType);
393
+
394
+ console.log(
395
+ `[JavaScript Proxy] Redirecting ${ruleType}: ${details.url} → ${newUrl}`
396
+ );
397
+
398
+ return { redirectUrl: newUrl };
399
+ }
400
+ }
401
+ }
402
+ }
403
+
404
+ // Fallback to legacy pattern for backward compatibility
405
+ if (config.urlPattern && details.type === "script") {
406
+ const regex = new RegExp(config.urlPattern, "i");
407
+
408
+ if (regex.test(details.url)) {
409
+ let newUrl = config.redirectUrl;
410
+
411
+ if (!newUrl.includes("://")) {
412
+ newUrl = "https://" + newUrl;
413
+ }
414
+
415
+ const requestType = getRequestTypeDescription(details.type);
416
+
417
+ if (details.url === newUrl || details.url.includes("localhost")) {
418
+ console.log(
419
+ `[JavaScript Proxy] Skipping redirect loop: ${details.url}`
420
+ );
421
+ return {};
422
+ }
423
+
424
+ if (config.maskingMode) {
425
+ storeOriginalUrl(details.requestId, details.url, newUrl);
426
+ console.log(
427
+ `[JavaScript Proxy] Legacy masking: ${details.url} (routing to ${newUrl})`
428
+ );
429
+ return {};
430
+ } else {
431
+ if (config.clearCacheOnEnable) {
432
+ clearCacheForPattern(config.urlPattern);
433
+ }
434
+
435
+ showProxyNotification(details.url, newUrl, requestType);
436
+ console.log(
437
+ `[JavaScript Proxy] Legacy redirecting: ${details.url} → ${newUrl}`
438
+ );
439
+ return { redirectUrl: newUrl };
440
+ }
441
+ }
442
+ }
443
+ } catch (error) {
444
+ console.error("[JavaScript Proxy] Error processing request:", error);
445
+ }
446
+
447
+ return {};
448
+ }
449
+
450
+ // Store mapping of request IDs to proxy info for header modification
451
+ const pendingProxyRequests = new Map();
452
+
453
+ function storeOriginalUrl(requestId, originalUrl, redirectUrl) {
454
+ pendingProxyRequests.set(requestId, {
455
+ originalUrl,
456
+ redirectUrl,
457
+ timestamp: Date.now(),
458
+ });
459
+
460
+ // Clean up old entries after 30 seconds
461
+ setTimeout(() => {
462
+ pendingProxyRequests.delete(requestId);
463
+ }, 30000);
464
+ }
465
+
466
+ // Get human-readable description of request type
467
+ function getRequestTypeDescription(type) {
468
+ const typeMap = {
469
+ script: "JavaScript",
470
+ main_frame: "Page",
471
+ sub_frame: "Frame",
472
+ stylesheet: "CSS",
473
+ image: "Image",
474
+ font: "Font",
475
+ object: "Plugin",
476
+ xmlhttprequest: "XHR",
477
+ ping: "Ping",
478
+ csp_report: "CSP Report",
479
+ media: "Media",
480
+ websocket: "WebSocket",
481
+ other: "Other",
482
+ };
483
+ return typeMap[type] || type;
484
+ }
485
+
486
+ // Show desktop notification for proxy activity
487
+ function showProxyNotification(from, to, requestType = "") {
488
+ const shortFrom = from.length > 50 ? from.substring(0, 47) + "..." : from;
489
+ const shortTo = to.length > 30 ? to.substring(0, 27) + "..." : to;
490
+
491
+ const notificationId = `proxy-${Date.now()}`;
492
+
493
+ browser.notifications.create(notificationId, {
494
+ type: "basic",
495
+ iconUrl: "icons/gx_on_48.png",
496
+ title: "JavaScript Proxy Active",
497
+ message: `${requestType}\n${shortFrom}\n→ ${shortTo}`,
498
+ });
499
+
500
+ // Clear existing timeout for this type
501
+ if (notificationTimeouts.has("proxy")) {
502
+ clearTimeout(notificationTimeouts.get("proxy"));
503
+ }
504
+
505
+ // Auto-dismiss after 3 seconds
506
+ const timeoutId = setTimeout(() => {
507
+ browser.notifications.clear(notificationId);
508
+ notificationTimeouts.delete("proxy");
509
+ }, 3000);
510
+
511
+ notificationTimeouts.set("proxy", timeoutId);
512
+ }
513
+
514
+ // Handle request headers for masking mode and cache control
515
+ function handleRequestHeaders(details) {
516
+ if (!config.enabled) return {};
517
+
518
+ // Initialize modifications
519
+ const modifications = { requestHeaders: details.requestHeaders };
520
+ let hasModifications = false;
521
+
522
+ // Handle masking mode
523
+ if (config.maskingMode) {
524
+ const proxyInfo = pendingProxyRequests.get(details.requestId);
525
+ if (proxyInfo) {
526
+ // Add/modify headers to properly route to localhost
527
+ const hostHeader = modifications.requestHeaders.find(
528
+ (h) => h.name.toLowerCase() === "host"
529
+ );
530
+ if (hostHeader) {
531
+ try {
532
+ const proxyUrl = new URL(proxyInfo.redirectUrl);
533
+ hostHeader.value = proxyUrl.host;
534
+ hasModifications = true;
535
+ } catch (error) {
536
+ console.error("[JavaScript Proxy] Error parsing proxy URL:", error);
537
+ }
538
+ }
539
+ }
540
+ }
541
+
542
+ // Handle cache control for URLs matching our pattern
543
+ if (config.disableCacheForRedirects && shouldDisableCache(details.url)) {
544
+ // Add cache-busting headers
545
+ const cacheHeaders = [
546
+ { name: "Cache-Control", value: "no-cache, no-store, must-revalidate" },
547
+ { name: "Pragma", value: "no-cache" },
548
+ { name: "Expires", value: "0" },
549
+ ];
550
+
551
+ cacheHeaders.forEach((cacheHeader) => {
552
+ const existingHeader = modifications.requestHeaders.find(
553
+ (h) => h.name.toLowerCase() === cacheHeader.name.toLowerCase()
554
+ );
555
+ if (existingHeader) {
556
+ existingHeader.value = cacheHeader.value;
557
+ } else {
558
+ modifications.requestHeaders.push(cacheHeader);
559
+ }
560
+ hasModifications = true;
561
+ });
562
+
563
+ console.log(
564
+ `[JavaScript Proxy] Added cache-busting headers for: ${details.url}`
565
+ );
566
+ }
567
+
568
+ return hasModifications ? modifications : {};
569
+ }
570
+
571
+ // Handle response headers for masking mode and cache control
572
+ function handleResponseHeaders(details) {
573
+ if (!config.enabled) return {};
574
+
575
+ // Initialize modifications
576
+ const modifications = { responseHeaders: details.responseHeaders };
577
+ let hasModifications = false;
578
+
579
+ // Handle masking mode
580
+ if (config.maskingMode) {
581
+ const proxyInfo = pendingProxyRequests.get(details.requestId);
582
+ if (proxyInfo) {
583
+ // Add CORS headers if needed
584
+ const corsHeaders = [
585
+ { name: "Access-Control-Allow-Origin", value: "*" },
586
+ {
587
+ name: "Access-Control-Allow-Methods",
588
+ value: "GET, POST, PUT, DELETE, OPTIONS",
589
+ },
590
+ {
591
+ name: "Access-Control-Allow-Headers",
592
+ value: "Content-Type, Authorization",
593
+ },
594
+ ];
595
+
596
+ corsHeaders.forEach((corsHeader) => {
597
+ const existingHeader = modifications.responseHeaders.find(
598
+ (h) => h.name.toLowerCase() === corsHeader.name.toLowerCase()
599
+ );
600
+ if (existingHeader) {
601
+ existingHeader.value = corsHeader.value;
602
+ } else {
603
+ modifications.responseHeaders.push(corsHeader);
604
+ }
605
+ hasModifications = true;
606
+ });
607
+ }
608
+ }
609
+
610
+ // Handle cache control for URLs matching our pattern
611
+ if (config.disableCacheForRedirects && shouldDisableCache(details.url)) {
612
+ // Add response cache-busting headers
613
+ const cacheHeaders = [
614
+ { name: "Cache-Control", value: "no-cache, no-store, must-revalidate" },
615
+ { name: "Pragma", value: "no-cache" },
616
+ { name: "Expires", value: "0" },
617
+ { name: "Last-Modified", value: new Date().toUTCString() },
618
+ { name: "ETag", value: `"${Date.now()}"` },
619
+ ];
620
+
621
+ cacheHeaders.forEach((cacheHeader) => {
622
+ const existingHeader = modifications.responseHeaders.find(
623
+ (h) => h.name.toLowerCase() === cacheHeader.name.toLowerCase()
624
+ );
625
+ if (existingHeader) {
626
+ existingHeader.value = cacheHeader.value;
627
+ } else {
628
+ modifications.responseHeaders.push(cacheHeader);
629
+ }
630
+ hasModifications = true;
631
+ });
632
+
633
+ console.log(
634
+ `[JavaScript Proxy] Added response cache-busting headers for: ${details.url}`
635
+ );
636
+ }
637
+
638
+ if (hasModifications) {
639
+ console.log(
640
+ `[JavaScript Proxy] Modified response headers for: ${details.url}`
641
+ );
642
+ }
643
+
644
+ return hasModifications ? modifications : {};
645
+ }
646
+
647
+ // Update request listener based on current state
648
+ function updateRequestListener() {
649
+ // Remove existing listeners
650
+ if (browser.webRequest.onBeforeRequest.hasListener(handleRequest)) {
651
+ browser.webRequest.onBeforeRequest.removeListener(handleRequest);
652
+ }
653
+ if (
654
+ browser.webRequest.onBeforeSendHeaders.hasListener(handleRequestHeaders)
655
+ ) {
656
+ browser.webRequest.onBeforeSendHeaders.removeListener(handleRequestHeaders);
657
+ }
658
+ if (browser.webRequest.onHeadersReceived.hasListener(handleResponseHeaders)) {
659
+ browser.webRequest.onHeadersReceived.removeListener(handleResponseHeaders);
660
+ }
661
+
662
+ if (config.enabled) {
663
+ // Add request interceptor
664
+ browser.webRequest.onBeforeRequest.addListener(
665
+ handleRequest,
666
+ { urls: ["<all_urls>"] },
667
+ ["blocking"]
668
+ );
669
+
670
+ // Add header modification listeners for masking mode OR cache control
671
+ if (config.maskingMode || config.disableCacheForRedirects) {
672
+ browser.webRequest.onBeforeSendHeaders.addListener(
673
+ handleRequestHeaders,
674
+ { urls: ["<all_urls>"] },
675
+ ["blocking", "requestHeaders"]
676
+ );
677
+
678
+ browser.webRequest.onHeadersReceived.addListener(
679
+ handleResponseHeaders,
680
+ { urls: ["<all_urls>"] },
681
+ ["blocking", "responseHeaders"]
682
+ );
683
+ }
684
+
685
+ console.log("[JavaScript Proxy] Request listener enabled");
686
+ } else {
687
+ console.log("[JavaScript Proxy] Request listener disabled");
688
+ }
689
+ }
690
+
691
+ // ============================================================
692
+ // DevTools Panel Communication
693
+ // ============================================================
694
+
695
+ const devtoolsConnections = new Map();
696
+
697
+ browser.runtime.onConnect.addListener((port) => {
698
+ if (port.name === 'gxp-devtools-panel') {
699
+ const extensionListener = (message, port) => {
700
+ if (message.name === 'init') {
701
+ devtoolsConnections.set(message.tabId, port);
702
+ console.log('[GxP DevTools] Panel connected for tab:', message.tabId);
703
+ }
704
+
705
+ // Forward messages to content script
706
+ if (message.tabId && message.action) {
707
+ browser.tabs.sendMessage(message.tabId, message).then((response) => {
708
+ port.postMessage(response);
709
+ }).catch(console.error);
710
+ }
711
+ };
712
+
713
+ port.onMessage.addListener(extensionListener);
714
+
715
+ port.onDisconnect.addListener(() => {
716
+ // Remove the connection when panel is closed
717
+ for (const [tabId, connectedPort] of devtoolsConnections.entries()) {
718
+ if (connectedPort === port) {
719
+ devtoolsConnections.delete(tabId);
720
+ console.log('[GxP DevTools] Panel disconnected for tab:', tabId);
721
+ break;
722
+ }
723
+ }
724
+ });
725
+ }
726
+ });
727
+
728
+ // Forward messages from content script to DevTools panel
729
+ function forwardToDevTools(tabId, message) {
730
+ const port = devtoolsConnections.get(tabId);
731
+ if (port) {
732
+ try {
733
+ port.postMessage(message);
734
+ } catch (error) {
735
+ console.error('[GxP DevTools] Error forwarding to panel:', error);
736
+ }
737
+ }
738
+ }
739
+
740
+ // Handle messages from popup
741
+ browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
742
+ console.log("[JavaScript Proxy] Received message:", request);
743
+
744
+ // Handle messages from content script about element selection
745
+ if (request.type === 'elementSelected' && sender.tab) {
746
+ forwardToDevTools(sender.tab.id, request);
747
+ return;
748
+ }
749
+
750
+ switch (request.action) {
751
+ case "toggleProxy":
752
+ config.enabled = request.enabled;
753
+ browser.storage.sync.set({ enabled: config.enabled });
754
+ updateIcon();
755
+ updateRequestListener();
756
+ // Clear cache when enabling the proxy
757
+ if (config.enabled && config.clearCacheOnEnable) {
758
+ clearCacheForPattern(); // Clear cache for all enabled patterns
759
+ }
760
+ sendResponse({ success: true, enabled: config.enabled });
761
+ break;
762
+
763
+ case "updateConfig":
764
+ config = { ...config, ...request.config };
765
+ browser.storage.sync.set(config);
766
+ updateIcon();
767
+ updateRequestListener();
768
+ sendResponse({ success: true });
769
+ break;
770
+
771
+ case "getConfig":
772
+ sendResponse(config);
773
+ break;
774
+
775
+ case "clearCache":
776
+ clearCacheForPattern() // Clear cache for all enabled patterns
777
+ .then(() => {
778
+ sendResponse({ success: true });
779
+ })
780
+ .catch((error) => {
781
+ sendResponse({ success: false, error: error.message });
782
+ });
783
+ break;
784
+
785
+ default:
786
+ console.warn("[JavaScript Proxy] Unknown action:", request.action);
787
+ sendResponse({ success: false, error: "Unknown action" });
788
+ }
789
+
790
+ return true; // Keep message channel open for async response
791
+ });
792
+
793
+ // Clear expired proxy requests periodically
794
+ setInterval(() => {
795
+ const now = Date.now();
796
+ for (const [requestId, info] of pendingProxyRequests.entries()) {
797
+ if (now - info.timestamp > 30000) {
798
+ pendingProxyRequests.delete(requestId);
799
+ }
800
+ }
801
+ }, 10000);
802
+
803
+ // Initialize on startup
804
+ initializeExtension();