From 3d81adc6a1896a8d3d9119d4f08a7211493e4ac2 Mon Sep 17 00:00:00 2001
From: Rajiv <51883613+thoughtlessmind@users.noreply.github.com>
Date: Sat, 26 Jul 2025 01:42:36 +0530
Subject: [PATCH 1/2] refactor: added the build and zip process and done
refactoring
---
.gitignore | 29 ++
README.md | 33 +-
addOutline.js | 4 +-
background.js | 37 +-
focus-mode.css | 11 -
manifest.json | 4 +-
package-lock.json | 1065 +++++++++++++++++++++++++++++++++++++++++++++
package.json | 32 ++
removeOutline.js | 13 +-
scripts/build.js | 249 +++++++++++
scripts/zip.js | 146 +++++++
11 files changed, 1589 insertions(+), 34 deletions(-)
delete mode 100644 focus-mode.css
create mode 100644 package-lock.json
create mode 100644 package.json
create mode 100644 scripts/build.js
create mode 100644 scripts/zip.js
diff --git a/.gitignore b/.gitignore
index e69de29..0a3a29d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1,29 @@
+# Dependencies
+node_modules/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Build output
+dist/
+*.zip
+
+# IDE files
+.vscode/
+.idea/
+*.swp
+*.swo
+
+# OS files
+.DS_Store
+Thumbs.db
+
+# Logs
+*.log
+
+# Environment files
+.env
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
\ No newline at end of file
diff --git a/README.md b/README.md
index adf2009..87935e3 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,36 @@
# Diver
-> Dive deep into elements of your webpage. Highlights all the elements of the page
+> A developer tool that adds colorful outlines to all DOM elements on a webpage, making it easy to visualize page structure and debug layout issues.
-Diver is a chrome extension designed to enhance your web browsing experience by providing a unique way to interact with web elements. With a simple click, Diver adds outlines to every element on a webpage, allowing you to visualize the structure and hierarchy of the page's content.
+Diver is a Chrome extension that highlights all DOM elements on any webpage with colorful outlines. Perfect for web developers and designers to debug layout issues and understand page structure.
-Key Features:
+## š Features
-- Click to add outlines: With a single click, Diver adds outlines to every element on a webpage, highlighting their boundaries.
+- **One-Click Toggle**: Instantly add/remove colorful outlines from all page elements
+- **Visual Feedback**: Clear badge indicators (ON/OFF)
+- **Cross-Frame Support**: Works across all frames including iframes
+- **Lightweight**: Minimal performance impact
-- Toggle outlines on/off: Click again to remove the outlines, providing a clear view of the webpage while retaining the flexibility to quickly inspect elements again.
+## š ļø Installation
-The diver is user-friendly and lightweight, ensuring it seamlessly integrates into your browsing experience without slowing you down. It's a must-have tool for web developers and designers, enabling them to streamline their workflow and gain valuable insights into the structure of any webpage.
+### Chrome Web Store (Recommended)
+[](https://chromewebstore.google.com/detail/diver/onfnfdmnccoaknhjinebmlncejnofhoc)
-Example Preview
+### From Source
+1. Clone: `git clone https://github.com/thoughtlessmind/diver.git`
+2. Open `chrome://extensions/`
+3. Enable "Developer mode"
+4. Click "Load unpacked" ā Select project directory
+
+## šø Preview
+
+## š Links
+
+- **[Chrome Web Store](https://chromewebstore.google.com/detail/diver/onfnfdmnccoaknhjinebmlncejnofhoc)** - Install directly
+- **[GitHub Repository](https://github.com/thoughtlessmind/diver)** - Source code
+
+## š License
+
+MIT License - see [LICENSE](LICENSE) file for details.
diff --git a/addOutline.js b/addOutline.js
index 5fce096..4624210 100644
--- a/addOutline.js
+++ b/addOutline.js
@@ -1,4 +1,4 @@
-[].forEach.call(document.querySelectorAll("*"), function (a) {
- a.style.outline =
+document.querySelectorAll("*").forEach(function (element) {
+ element.style.outline =
"1px solid #" + (~~(Math.random() * (1 << 24))).toString(16);
});
diff --git a/background.js b/background.js
index 125cc55..929d093 100644
--- a/background.js
+++ b/background.js
@@ -2,21 +2,26 @@ chrome.runtime.onInstalled.addListener(() => {
chrome.action.setBadgeText({
text: "",
});
+ // Initialize extension state for all tabs
+ chrome.storage.local.clear();
});
-const extensions = "https://developer.chrome.com/docs/extensions";
-const webstore = "https://developer.chrome.com/docs/webstore";
-
chrome.action.onClicked.addListener(async (tab) => {
- // Retrieve the action badge to check if the extension is 'ON' or 'OFF'
- const prevState = await chrome.action.getBadgeText({ tabId: tab.id });
- // Next state will always be the opposite
- const nextState = prevState === "ON" ? "OFF" : "ON";
+ // Get the current state from storage (more reliable than badge text)
+ const tabStateKey = `tab_${tab.id}_state`;
+ const result = await chrome.storage.local.get([tabStateKey]);
+ const currentState = result[tabStateKey] || "OFF";
+
+ // Toggle state
+ const nextState = currentState === "ON" ? "OFF" : "ON";
+
+ // Save new state to storage
+ await chrome.storage.local.set({ [tabStateKey]: nextState });
- // Set the action badge to the next state
+ // Set the action badge to show current state
await chrome.action.setBadgeText({
tabId: tab.id,
- text: nextState === "ON" ? "ON" : "",
+ text: nextState,
});
if (nextState === "ON") {
@@ -29,5 +34,19 @@ chrome.action.onClicked.addListener(async (tab) => {
target: { tabId: tab.id, allFrames: true },
files: ["removeOutline.js"],
});
+
+ // Clear the "OFF" badge after 3 seconds (but keep state in storage)
+ setTimeout(async () => {
+ await chrome.action.setBadgeText({
+ tabId: tab.id,
+ text: "",
+ });
+ }, 3000);
}
});
+
+// Clean up storage when tab is closed to prevent memory bloat
+chrome.tabs.onRemoved.addListener((tabId) => {
+ const tabStateKey = `tab_${tabId}_state`;
+ chrome.storage.local.remove([tabStateKey]);
+});
diff --git a/focus-mode.css b/focus-mode.css
deleted file mode 100644
index 7afac17..0000000
--- a/focus-mode.css
+++ /dev/null
@@ -1,11 +0,0 @@
-body > .scaffold > :is(top-nav, navigation-rail, side-nav, footer),
-main > :not(:last-child),
-main > :last-child > navigation-tree,
-main .toc-container {
- display: none;
-}
-
-main > :last-child {
- margin-top: min(10vmax, 10rem);
- margin-bottom: min(10vmax, 10rem);
-}
diff --git a/manifest.json b/manifest.json
index 0a1f106..bd08b46 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,9 +1,9 @@
{
"name": "Diver",
"manifest_version": 3,
- "description": "Diver deep into elements of your webpage. Highlights all the elements of the page",
+ "description": "Adds colorful outlines to all DOM elements for easy page structure visualization and debugging.",
"version": "0.0.0.1",
- "permissions": ["activeTab", "scripting"],
+ "permissions": ["activeTab", "scripting", "storage", "tabs"],
"homepage_url": "https://github.com/thoughtlessmind/diver",
"icons": {
"16": "images/icon-16.png",
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..d6e9e7d
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1065 @@
+{
+ "name": "diver-chrome-extension",
+ "version": "0.0.1",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "diver-chrome-extension",
+ "version": "0.0.1",
+ "license": "MIT",
+ "devDependencies": {
+ "archiver": "^6.0.1",
+ "rimraf": "^5.0.5",
+ "terser": "^5.24.0"
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.12",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz",
+ "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.10",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.10.tgz",
+ "integrity": "sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
+ "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.29",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz",
+ "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/archiver": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/archiver/-/archiver-6.0.2.tgz",
+ "integrity": "sha512-UQ/2nW7NMl1G+1UnrLypQw1VdT9XZg/ECcKPq7l+STzStrSivFIXIp34D8M5zeNGW5NoOupdYCHv6VySCPNNlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "archiver-utils": "^4.0.1",
+ "async": "^3.2.4",
+ "buffer-crc32": "^0.2.1",
+ "readable-stream": "^3.6.0",
+ "readdir-glob": "^1.1.2",
+ "tar-stream": "^3.0.0",
+ "zip-stream": "^5.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/archiver-utils": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-4.0.1.tgz",
+ "integrity": "sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "glob": "^8.0.0",
+ "graceful-fs": "^4.2.0",
+ "lazystream": "^1.0.0",
+ "lodash": "^4.17.15",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/b4a": {
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
+ "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/bare-events": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.6.0.tgz",
+ "integrity": "sha512-EKZ5BTXYExaNqi3I3f9RtEsaI/xBSGjE0XZCZilPzFAV/goswFHuPd9jEZlPIZ/iNZJwDSao9qRiScySz7MbQg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/compress-commons": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.3.tgz",
+ "integrity": "sha512-/UIcLWvwAQyVibgpQDPtfNM3SvqN7G9elAPAV7GM0L53EbNWwWiCsWtK8Fwed/APEbptPHXs5PuW+y8Bq8lFTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "crc-32": "^1.2.0",
+ "crc32-stream": "^5.0.0",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/crc-32": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+ "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "crc32": "bin/crc32.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/crc32-stream": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.1.tgz",
+ "integrity": "sha512-lO1dFui+CEUh/ztYIpgpKItKW9Bb4NWakCRJrnqAbFIYD+OZAwb2VfD5T5eXMw2FNcsDHkQcNl/Wh3iVXYwU6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "crc-32": "^1.2.0",
+ "readable-stream": "^3.4.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-fifo": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
+ "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/glob": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/lazystream": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
+ "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readable-stream": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.6.3"
+ }
+ },
+ "node_modules/lazystream/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/lazystream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lazystream/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/readdir-glob": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz",
+ "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "minimatch": "^5.1.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "5.0.10",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
+ "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^10.3.7"
+ },
+ "bin": {
+ "rimraf": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf/node_modules/glob": {
+ "version": "10.4.5",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/streamx": {
+ "version": "2.22.1",
+ "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz",
+ "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-fifo": "^1.3.2",
+ "text-decoder": "^1.1.0"
+ },
+ "optionalDependencies": {
+ "bare-events": "^2.2.0"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/string-width-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tar-stream": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
+ "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "b4a": "^1.6.4",
+ "fast-fifo": "^1.2.0",
+ "streamx": "^2.15.0"
+ }
+ },
+ "node_modules/terser": {
+ "version": "5.43.1",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz",
+ "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.14.0",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/text-decoder": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz",
+ "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "b4a": "^1.6.4"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/zip-stream": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-5.0.2.tgz",
+ "integrity": "sha512-LfOdrUvPB8ZoXtvOBz6DlNClfvi//b5d56mSWyJi7XbH/HfhOHfUhOqxhT/rUiR7yiktlunqRo+jY6y/cWC/5g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "archiver-utils": "^4.0.1",
+ "compress-commons": "^5.0.1",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..992312c
--- /dev/null
+++ b/package.json
@@ -0,0 +1,32 @@
+{
+ "name": "diver-chrome-extension",
+ "version": "0.0.1",
+ "description": "Dive deep into elements of your webpage. Highlights all the elements of the page",
+ "main": "background.js",
+ "scripts": {
+ "build": "node scripts/build.js",
+ "build:zip": "npm run build && npm run zip",
+ "zip": "node scripts/zip.js",
+ "clean": "rimraf dist",
+ "version:patch": "npm version patch && npm run build:zip",
+ "version:minor": "npm version minor && npm run build:zip",
+ "version:major": "npm version major && npm run build:zip"
+ },
+ "keywords": [
+ "chrome-extension",
+ "web-development",
+ "debugging",
+ "dom-inspector"
+ ],
+ "author": "thoughtlessmind",
+ "license": "MIT",
+ "homepage": "https://github.com/thoughtlessmind/diver",
+ "devDependencies": {
+ "archiver": "^6.0.1",
+ "rimraf": "^5.0.5",
+ "terser": "^5.24.0"
+ },
+ "files": [
+ "dist/"
+ ]
+}
\ No newline at end of file
diff --git a/removeOutline.js b/removeOutline.js
index 9dbc436..4ab026b 100644
--- a/removeOutline.js
+++ b/removeOutline.js
@@ -1,3 +1,10 @@
-[].forEach.call(document.querySelectorAll("*"), function (a) {
- a.style.outline = "none";
-});
+/**
+ * Removes the outline from all elements on the page.
+ */
+function removeOutline() {
+ document.querySelectorAll("*").forEach(function (element) {
+ element.style.removeProperty("outline");
+ });
+}
+
+removeOutline();
diff --git a/scripts/build.js b/scripts/build.js
new file mode 100644
index 0000000..a1796c8
--- /dev/null
+++ b/scripts/build.js
@@ -0,0 +1,249 @@
+#!/usr/bin/env node
+
+const fs = require("fs");
+const path = require("path");
+const { minify } = require("terser");
+
+/** The source directory */
+const SRC_DIR = path.resolve(__dirname, "..");
+
+/** The destination directory */
+const DIST_DIR = path.resolve(__dirname, "..", "dist");
+
+/** Directories to copy recursively */
+const DIRS_TO_COPY = ["images"];
+
+/** JavaScript files to minify */
+const JS_FILES = ["background.js", "addOutline.js", "removeOutline.js"];
+
+/** Files that don't need processing (copy as-is) */
+const STATIC_FILES = ["manifest.json", "LICENSE", "README.md"];
+
+/**
+ * Ensures that a directory exists.
+ * @param {string} dir - The directory to ensure.
+ */
+function ensureDir(dir) {
+ if (!fs.existsSync(dir)) {
+ fs.mkdirSync(dir, { recursive: true });
+ console.log(`Created directory: ${dir}`);
+ }
+}
+
+/**
+ * Copies a file from the source directory to the destination directory.
+ * @param {string} src - The source file path.
+ * @param {string} dest - The destination file path.
+ */
+function copyFile(src, dest) {
+ const destDir = path.dirname(dest);
+ ensureDir(destDir);
+
+ fs.copyFileSync(src, dest);
+ console.log(
+ `Copied: ${path.relative(SRC_DIR, src)} ā ${path.relative(SRC_DIR, dest)}`
+ );
+}
+
+/**
+ * Minifies a JavaScript file and copies it to the destination directory.
+ * @param {string} src - The source file path.
+ * @param {string} dest - The destination file path.
+ */
+async function minifyAndCopyJS(src, dest) {
+ const destDir = path.dirname(dest);
+ ensureDir(destDir);
+
+ try {
+ const code = fs.readFileSync(src, "utf8");
+ const result = await minify(code, {
+ compress: {
+ drop_console: false, // Keep console logs for debugging
+ drop_debugger: true,
+ pure_funcs: ["console.debug"],
+ },
+ mangle: {
+ reserved: ["chrome"], // Don't mangle Chrome API names
+ },
+ format: {
+ comments: false, // Remove comments
+ },
+ });
+
+ if (result.error) {
+ console.warn(`ā ļø Minification failed for ${src}, copying original`);
+ copyFile(src, dest);
+ return;
+ }
+
+ fs.writeFileSync(dest, result.code);
+ const originalSize = Buffer.byteLength(code, "utf8");
+ const minifiedSize = Buffer.byteLength(result.code, "utf8");
+ const savings = (
+ ((originalSize - minifiedSize) / originalSize) *
+ 100
+ ).toFixed(1);
+
+ console.log(
+ `Minified: ${path.relative(SRC_DIR, src)} ā ${path.relative(
+ SRC_DIR,
+ dest
+ )} (${savings}% smaller)`
+ );
+ } catch (error) {
+ console.warn(`ā ļø Error minifying ${src}:`, error.message);
+ console.log(`Copying original file instead...`);
+ copyFile(src, dest);
+ }
+}
+
+/**
+ * Copies a directory from the source directory to the destination directory.
+ * @param {string} src - The source directory path.
+ * @param {string} dest - The destination directory path.
+ */
+function copyDirectory(src, dest) {
+ ensureDir(dest);
+
+ const items = fs.readdirSync(src);
+
+ for (const item of items) {
+ const srcPath = path.join(src, item);
+ const destPath = path.join(dest, item);
+
+ const stat = fs.statSync(srcPath);
+
+ if (stat.isDirectory()) {
+ copyDirectory(srcPath, destPath);
+ } else {
+ copyFile(srcPath, destPath);
+ }
+ }
+}
+
+/**
+ * Updates the manifest version to match the package.json version.
+ */
+function updateManifestVersion() {
+ const packageJsonPath = path.join(SRC_DIR, "package.json");
+ const manifestPath = path.join(DIST_DIR, "manifest.json");
+
+ if (fs.existsSync(packageJsonPath)) {
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
+
+ // Update manifest version to match package.json
+ manifest.version = packageJson.version;
+
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
+ console.log(`Updated manifest.json version to ${packageJson.version}`);
+ }
+}
+
+/**
+ * Cleans the dist directory.
+ */
+function cleanDist() {
+ if (fs.existsSync(DIST_DIR)) {
+ fs.rmSync(DIST_DIR, { recursive: true, force: true });
+ console.log("Cleaned dist directory");
+ }
+}
+
+/**
+ * Builds the Chrome extension.
+ */
+async function build() {
+ console.log("š Building Diver Chrome Extension...\n");
+
+ // Clean previous build
+ cleanDist();
+
+ // Create dist directory
+ ensureDir(DIST_DIR);
+
+ // Copy static files
+ console.log("š Copying static files...");
+ for (const file of STATIC_FILES) {
+ const src = path.join(SRC_DIR, file);
+ const dest = path.join(DIST_DIR, file);
+
+ if (fs.existsSync(src)) {
+ copyFile(src, dest);
+ } else {
+ console.warn(`ā ļø File not found: ${file}`);
+ }
+ }
+
+ // Minify JavaScript files
+ console.log("\nā” Minifying JavaScript files...");
+ for (const file of JS_FILES) {
+ const src = path.join(SRC_DIR, file);
+ const dest = path.join(DIST_DIR, file);
+
+ if (fs.existsSync(src)) {
+ await minifyAndCopyJS(src, dest);
+ } else {
+ console.warn(`ā ļø JavaScript file not found: ${file}`);
+ }
+ }
+
+ // Copy directories
+ console.log("\nš Copying directories...");
+ for (const dir of DIRS_TO_COPY) {
+ const src = path.join(SRC_DIR, dir);
+ const dest = path.join(DIST_DIR, dir);
+
+ if (fs.existsSync(src)) {
+ copyDirectory(src, dest);
+ } else {
+ console.warn(`ā ļø Directory not found: ${dir}`);
+ }
+ }
+
+ // Update manifest version
+ console.log("\nš Updating manifest version...");
+ updateManifestVersion();
+
+ // Build summary
+ const distFiles = fs.readdirSync(DIST_DIR, { recursive: true });
+ const totalSize = calculateDirectorySize(DIST_DIR);
+ console.log(
+ `\nā
Build complete! Generated ${distFiles.length} files in dist/`
+ );
+ console.log(`š Total build size: ${(totalSize / 1024).toFixed(1)} KB`);
+ console.log(`š¦ Minified and ready for Chrome Web Store upload: dist/`);
+}
+
+/**
+ * Calculates the size of a directory.
+ * @param {string} dir - The directory to calculate the size of.
+ * @returns {number} The size of the directory in bytes.
+ */
+function calculateDirectorySize(dir) {
+ let totalSize = 0;
+ const files = fs.readdirSync(dir, { recursive: true });
+
+ for (const file of files) {
+ const filePath = path.join(dir, file);
+ const stats = fs.statSync(filePath);
+
+ if (stats.isFile()) {
+ totalSize += stats.size;
+ }
+ }
+
+ return totalSize;
+}
+
+/**
+ * Runs the build process.
+ */
+if (require.main === module) {
+ build().catch((error) => {
+ console.error("ā Build failed:", error.message);
+ process.exit(1);
+ });
+}
+
+module.exports = { build };
diff --git a/scripts/zip.js b/scripts/zip.js
new file mode 100644
index 0000000..2b54cc6
--- /dev/null
+++ b/scripts/zip.js
@@ -0,0 +1,146 @@
+#!/usr/bin/env node
+
+const fs = require("fs");
+const path = require("path");
+const archiver = require("archiver");
+
+/** The root directory */
+const ROOT_DIR = path.resolve(__dirname, "..");
+
+/** The destination directory */
+const DIST_DIR = path.resolve(ROOT_DIR, "dist");
+
+/**
+ * Gets the version from the package.json file.
+ * @returns {string} The version from the package.json file.
+ */
+function getVersionFromPackageJson() {
+ const packageJsonPath = path.join(ROOT_DIR, "package.json");
+
+ if (fs.existsSync(packageJsonPath)) {
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
+ return packageJson.version;
+ }
+
+ return "1.0.0";
+}
+
+/**
+ * Creates a ZIP file of the dist directory.
+ * @returns {Promise} A promise that resolves to the path of the ZIP file.
+ */
+function createZip() {
+ return new Promise((resolve, reject) => {
+ // Check if dist directory exists
+ if (!fs.existsSync(DIST_DIR)) {
+ console.error('ā dist/ directory not found. Run "npm run build" first.');
+ process.exit(1);
+ }
+
+ const version = getVersionFromPackageJson();
+ const zipName = `diver-v${version}.zip`;
+ const zipPath = path.join(ROOT_DIR, zipName);
+
+ console.log(`š¦ Creating ZIP package: ${zipName}`);
+
+ // Create a file to stream archive data to
+ const output = fs.createWriteStream(zipPath);
+ const archive = archiver("zip", {
+ zlib: { level: 9 }, // Maximum compression
+ });
+
+ // Listen for all archive data to be written
+ output.on("close", () => {
+ const sizeInMB = (archive.pointer() / 1024 / 1024).toFixed(2);
+ console.log(`ā
ZIP created successfully!`);
+ console.log(
+ `š Archive size: ${sizeInMB} MB (${archive.pointer()} bytes)`
+ );
+ console.log(`š Location: ${zipPath}`);
+ console.log(`š Ready for Chrome Web Store upload!`);
+ resolve(zipPath);
+ });
+
+ // Handle warnings (like stat failures and other non-blocking errors)
+ archive.on("warning", (err) => {
+ if (err.code === "ENOENT") {
+ console.warn("ā ļø Warning:", err.message);
+ } else {
+ reject(err);
+ }
+ });
+
+ // Handle errors
+ archive.on("error", (err) => {
+ console.error("ā Archive error:", err.message);
+ reject(err);
+ });
+
+ // Pipe archive data to the file
+ archive.pipe(output);
+
+ // Add the entire dist directory to the archive
+ archive.directory(DIST_DIR, false);
+
+ // Finalize the archive
+ archive.finalize();
+ });
+}
+
+/**
+ * Validates the ZIP file.
+ * @returns {boolean} True if the ZIP file is valid, false otherwise.
+ */
+function validateZip() {
+ const version = getVersionFromPackageJson();
+ const zipPath = path.join(ROOT_DIR, `diver-v${version}.zip`);
+
+ if (fs.existsSync(zipPath)) {
+ const stats = fs.statSync(zipPath);
+ const sizeInMB = (stats.size / 1024 / 1024).toFixed(2);
+
+ console.log(`\nš ZIP Package Summary:`);
+ console.log(` File: diver-v${version}.zip`);
+ console.log(` Size: ${sizeInMB} MB`);
+ console.log(` Path: ${zipPath}`);
+
+ // Check if size is reasonable for Chrome Web Store (max 128MB)
+ if (stats.size > 128 * 1024 * 1024) {
+ console.warn(
+ `ā ļø Warning: ZIP size exceeds Chrome Web Store limit (128MB)`
+ );
+ } else {
+ console.log(`ā
Size is within Chrome Web Store limits`);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+async function main() {
+ try {
+ console.log("š¦ Creating Chrome Web Store package...\n");
+
+ await createZip();
+ validateZip();
+
+ console.log("\nš Package ready for Chrome Web Store submission!");
+ console.log("š Next steps:");
+ console.log(" 1. Go to https://chrome.google.com/webstore/devconsole");
+ console.log(" 2. Upload the generated ZIP file");
+ console.log(" 3. Fill in store listing details");
+ console.log(" 4. Submit for review");
+ } catch (error) {
+ console.error("ā Failed to create ZIP:", error.message);
+ process.exit(1);
+ }
+}
+
+// Run if called directly
+if (require.main === module) {
+ main();
+}
+
+module.exports = { createZip, validateZip };
From 3c1e01bda6b8b9df8829878b49607516ef72e61e Mon Sep 17 00:00:00 2001
From: Rajiv <51883613+thoughtlessmind@users.noreply.github.com>
Date: Sat, 26 Jul 2025 02:11:42 +0530
Subject: [PATCH 2/2] refactor: removed delayed badge update and reduce
overhead code
---
background.js | 66 ++++++++++++++++++---------------------------------
manifest.json | 2 +-
2 files changed, 24 insertions(+), 44 deletions(-)
diff --git a/background.js b/background.js
index 929d093..37eed25 100644
--- a/background.js
+++ b/background.js
@@ -1,52 +1,32 @@
-chrome.runtime.onInstalled.addListener(() => {
- chrome.action.setBadgeText({
- text: "",
- });
- // Initialize extension state for all tabs
- chrome.storage.local.clear();
-});
+// Simple and performant: use badge text as the source of truth
+// Empty badge = OFF, "ON" badge = ON
chrome.action.onClicked.addListener(async (tab) => {
- // Get the current state from storage (more reliable than badge text)
- const tabStateKey = `tab_${tab.id}_state`;
- const result = await chrome.storage.local.get([tabStateKey]);
- const currentState = result[tabStateKey] || "OFF";
-
- // Toggle state
- const nextState = currentState === "ON" ? "OFF" : "ON";
-
- // Save new state to storage
- await chrome.storage.local.set({ [tabStateKey]: nextState });
-
- // Set the action badge to show current state
- await chrome.action.setBadgeText({
- tabId: tab.id,
- text: nextState,
- });
-
- if (nextState === "ON") {
+ // Get current state from badge (reliable and fast)
+ const currentBadge = await chrome.action.getBadgeText({ tabId: tab.id });
+ const isCurrentlyOn = currentBadge === "ON";
+
+ if (isCurrentlyOn) {
+ // Turn OFF: Remove outlines and clear badge
await chrome.scripting.executeScript({
target: { tabId: tab.id, allFrames: true },
- files: ["addOutline.js"],
+ files: ["removeOutline.js"],
});
- } else if (nextState === "OFF") {
+
+ await chrome.action.setBadgeText({
+ tabId: tab.id,
+ text: "", // Empty = OFF state
+ });
+ } else {
+ // Turn ON: Add outlines and show badge
await chrome.scripting.executeScript({
target: { tabId: tab.id, allFrames: true },
- files: ["removeOutline.js"],
+ files: ["addOutline.js"],
+ });
+
+ await chrome.action.setBadgeText({
+ tabId: tab.id,
+ text: "ON",
});
-
- // Clear the "OFF" badge after 3 seconds (but keep state in storage)
- setTimeout(async () => {
- await chrome.action.setBadgeText({
- tabId: tab.id,
- text: "",
- });
- }, 3000);
}
-});
-
-// Clean up storage when tab is closed to prevent memory bloat
-chrome.tabs.onRemoved.addListener((tabId) => {
- const tabStateKey = `tab_${tabId}_state`;
- chrome.storage.local.remove([tabStateKey]);
-});
+});
\ No newline at end of file
diff --git a/manifest.json b/manifest.json
index bd08b46..fec0047 100644
--- a/manifest.json
+++ b/manifest.json
@@ -3,7 +3,7 @@
"manifest_version": 3,
"description": "Adds colorful outlines to all DOM elements for easy page structure visualization and debugging.",
"version": "0.0.0.1",
- "permissions": ["activeTab", "scripting", "storage", "tabs"],
+ "permissions": ["activeTab", "scripting"],
"homepage_url": "https://github.com/thoughtlessmind/diver",
"icons": {
"16": "images/icon-16.png",