{"_id":"@baaqar/artifact-to-pwa","_rev":"8-48ab0a6ac9b95dfe12dfa6b840ef9f17","name":"@baaqar/artifact-to-pwa","dist-tags":{"latest":"3.0.2"},"versions":{"1.0.0":{"name":"@baaqar/artifact-to-pwa","version":"1.0.0","keywords":["pwa","progressive-web-app","claude","artifact","installable","offline","cli","converter"],"author":"","license":"MIT","_id":"@baaqar/artifact-to-pwa@1.0.0","maintainers":[{"name":"baaqar","email":"baaqarnaqi@gmail.com"}],"bin":{"artifact-to-pwa":"bin/cli.js"},"dist":{"shasum":"7187250bb682454e492baa89a3e337f004d0e0f9","tarball":"https://registry.npmjs.org/@baaqar/artifact-to-pwa/-/artifact-to-pwa-1.0.0.tgz","fileCount":7,"integrity":"sha512-wUN5BquMtYxG4KsKlpbJgNr5EYYs9MKjHMC7c5U9xxcdUaneoUNsfBE36nlQ/2TimcrWlZgEucahs7E+IJpiNg==","signatures":[{"sig":"MEUCIQD23rgMprEo67OtMFW5Ss8GvWC870NjF4kSlezC7/JqsAIgYevFJjne9bfYjuPTtR0cdTlQeOEYT/FTCF9t5qrCcB0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":22431},"type":"module","engines":{"node":">=18"},"gitHead":"396928c408d80f461660964c0fae2066d276e9ae","_npmUser":{"name":"baaqar","email":"baaqarnaqi@gmail.com"},"_npmVersion":"10.9.2","description":"Convert any Claude artifact (HTML/React/JSX) or public URL into an installable PWA — no build step required","directories":{},"_nodeVersion":"22.15.0","dependencies":{"chalk":"^5.3.0","commander":"^12.1.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/artifact-to-pwa_1.0.0_1781460708367_0.9483204707023782","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@baaqar/artifact-to-pwa","version":"1.1.0","keywords":["pwa","progressive-web-app","claude","artifact","installable","offline","cli","converter","indexeddb","localStorage"],"author":"","license":"MIT","_id":"@baaqar/artifact-to-pwa@1.1.0","maintainers":[{"name":"baaqar","email":"baaqarnaqi@gmail.com"}],"bin":{"artifact-to-pwa":"bin/cli.js"},"dist":{"shasum":"3fe85c810f46b1065d8d7e8e3a11b7a9da6ac1a7","tarball":"https://registry.npmjs.org/@baaqar/artifact-to-pwa/-/artifact-to-pwa-1.1.0.tgz","fileCount":8,"integrity":"sha512-YVcdB8QeiZgqKhwIvmRrbj9lCrKLbISYBxPVE1UceBGRMeAEtjhyCJBQvxt2D7CqRFU8nfsOMYOQUh/jO0lv3g==","signatures":[{"sig":"MEQCIAsOlG78n/nVBNLpeKdDPAKuAvI9seZ/+W7/PPTySjCDAiANuSQK1r1tU+Kt9EWin0D7UdTBcZjrNwFDo7j3fy/CmA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":28918},"type":"module","engines":{"node":">=18"},"gitHead":"81dd1bf9d7a67a04096b53d4b6209063b343027e","_npmUser":{"name":"baaqar","email":"baaqarnaqi@gmail.com"},"_npmVersion":"10.9.2","description":"Convert any Claude artifact (HTML/React/JSX) or public URL into an installable PWA — no build step required","directories":{},"_nodeVersion":"22.15.0","dependencies":{"chalk":"^5.3.0","commander":"^12.1.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/artifact-to-pwa_1.1.0_1781610666051_0.4136725921940325","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@baaqar/artifact-to-pwa","version":"2.0.0","keywords":["pwa","claude","artifact","cli","electron","windows","exe","desktop","react","jsx","standalone","converter"],"author":"","license":"MIT","_id":"@baaqar/artifact-to-pwa@2.0.0","maintainers":[{"name":"baaqar","email":"baaqarnaqi@gmail.com"}],"homepage":"https://github.com/Baaqar-007/artifact-to-pwa#readme","bugs":{"url":"https://github.com/Baaqar-007/artifact-to-pwa/issues"},"bin":{"artifact-to-pwa":"bin/cli.js"},"dist":{"shasum":"a6868a772e8c034bc076273373f8ba06994df6df","tarball":"https://registry.npmjs.org/@baaqar/artifact-to-pwa/-/artifact-to-pwa-2.0.0.tgz","fileCount":10,"integrity":"sha512-8i4S99BcYA7pFL5iOXVgzij2Tmf5ggb5bBvwWgB8PNS9fmWiJTRQ5KMT1HHuN2BTgRUArR5RhcmQWbV+Cbik0Q==","signatures":[{"sig":"MEUCIQCzb3lhO2Y+oXYtF0NAFninVTwA0/5A8Kd4PLs4+jLJaAIgPf/Ok31cpULstcAvJFSSIIvtOPOMOIi2skL+H+CXxQo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@baaqar%2fartifact-to-pwa@2.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":29335},"type":"module","engines":{"node":">=18"},"gitHead":"8167239d98a4ad46e1907f83cbb1a4b3c6c94587","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f7830986-7e49-4979-a361-886daae3372b"}},"repository":{"url":"git+https://github.com/Baaqar-007/artifact-to-pwa.git","type":"git"},"_npmVersion":"11.16.0","description":"Convert any Claude artifact (HTML/React/JSX) or public URL into a native Windows .exe; no compiler, no App Store","directories":{},"_nodeVersion":"24.18.0","dependencies":{"chalk":"^5.3.0","rcedit":"^4.0.0","esbuild":"^0.21.0","commander":"^12.1.0","png-to-ico":"^2.1.8","extract-zip":"^2.0.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/artifact-to-pwa_2.0.0_1785330496748_0.0951593835917135","host":"s3://npm-registry-packages-npm-production"}},"2.0.1":{"name":"@baaqar/artifact-to-pwa","version":"2.0.1","keywords":["pwa","claude","artifact","cli","electron","windows","exe","desktop","react","jsx","standalone","converter"],"author":"","license":"MIT","_id":"@baaqar/artifact-to-pwa@2.0.1","maintainers":[{"name":"baaqar","email":"baaqarnaqi@gmail.com"}],"homepage":"https://github.com/Baaqar-007/artifact-to-pwa#readme","bugs":{"url":"https://github.com/Baaqar-007/artifact-to-pwa/issues"},"bin":{"artifact-to-pwa":"bin/cli.js"},"dist":{"shasum":"aa5753a1b408146ec430e032ebad8e7545e62e82","tarball":"https://registry.npmjs.org/@baaqar/artifact-to-pwa/-/artifact-to-pwa-2.0.1.tgz","fileCount":10,"integrity":"sha512-izlyPxYVSnEf4B0ku+Fg1f4p+FSZYd3weRqPfDTujEXL2uV/Vmc7XNQi+8X+w7H6cuhEWXWgzvYEl/FKSlDjBA==","signatures":[{"sig":"MEYCIQC0Q+ReL6uzpDcg8emTUn2ngjXBQMGCmQRYu5F8viLF8QIhAMBE9LShmakO6+hsfib3+eEoZ3ET5x9RzcnlLYY29vQS","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@baaqar%2fartifact-to-pwa@2.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":29335},"type":"module","engines":{"node":">=18"},"gitHead":"5ecc1a6d43b774138ba9723bd52d072d12b5c436","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f7830986-7e49-4979-a361-886daae3372b"}},"repository":{"url":"git+https://github.com/Baaqar-007/artifact-to-pwa.git","type":"git"},"_npmVersion":"11.16.0","description":"Convert any Claude artifact (HTML/React/JSX) or public URL into a native Windows .exe; no compiler, no App Store","directories":{},"_nodeVersion":"24.18.0","dependencies":{"chalk":"^5.3.0","rcedit":"^4.0.0","esbuild":"^0.25.0","commander":"^12.1.0","png-to-ico":"^2.1.8","extract-zip":"^2.0.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/artifact-to-pwa_2.0.1_1785347316447_0.2219849577141635","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"@baaqar/artifact-to-pwa","version":"2.1.0","keywords":["pwa","claude","artifact","cli","electron","windows","exe","desktop","react","jsx","standalone","converter"],"author":"","license":"MIT","_id":"@baaqar/artifact-to-pwa@2.1.0","maintainers":[{"name":"baaqar","email":"baaqarnaqi@gmail.com"}],"homepage":"https://github.com/Baaqar-007/artifact-to-pwa#readme","bugs":{"url":"https://github.com/Baaqar-007/artifact-to-pwa/issues"},"bin":{"artifact-to-pwa":"bin/cli.js"},"dist":{"shasum":"97d410d3a44d9506e61b174a7588e5771d2360ce","tarball":"https://registry.npmjs.org/@baaqar/artifact-to-pwa/-/artifact-to-pwa-2.1.0.tgz","fileCount":10,"integrity":"sha512-FckRj3MFl196gWkLUv+Vj5YGjhbxKoMfxIyQXnhs0femObUJrE9Q810lojJyJaGnGdP0l30GGhnVF+1o0qmg3Q==","signatures":[{"sig":"MEUCIBujABpo8hwmYSRP+fkM1O3SA4TCeVK5DMAzDxYJKgDfAiEAqq+pkfdbz8pj2diWgf1MQIe7SU6lt14Gah2RpYC5RyQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@baaqar%2fartifact-to-pwa@2.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":28492},"type":"module","engines":{"node":">=18"},"gitHead":"da3f88f882d06398a60d567ffaa5e09245013438","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f7830986-7e49-4979-a361-886daae3372b"}},"repository":{"url":"git+https://github.com/Baaqar-007/artifact-to-pwa.git","type":"git"},"_npmVersion":"11.16.0","description":"Convert any Claude artifact (HTML/React/JSX) or public URL into a native Windows .exe; no compiler, no App Store","directories":{},"_nodeVersion":"24.18.0","dependencies":{"chalk":"^5.3.0","rcedit":"^4.0.0","esbuild":"^0.25.0","commander":"^12.1.0","png-to-ico":"^2.1.8","extract-zip":"^2.0.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/artifact-to-pwa_2.1.0_1785414877799_0.8776035068579144","host":"s3://npm-registry-packages-npm-production"}},"3.0.0":{"name":"@baaqar/artifact-to-pwa","version":"3.0.0","keywords":["pwa","claude","artifact","cli","neutralino","webview2","windows","exe","desktop","react","jsx","standalone","converter"],"author":"","license":"MIT","_id":"@baaqar/artifact-to-pwa@3.0.0","maintainers":[{"name":"baaqar","email":"baaqarnaqi@gmail.com"}],"homepage":"https://github.com/Baaqar-007/artifact-to-pwa#readme","bugs":{"url":"https://github.com/Baaqar-007/artifact-to-pwa/issues"},"bin":{"artifact-to-pwa":"bin/cli.js"},"dist":{"shasum":"e7910100609da535f838a0434d8de666422336f3","tarball":"https://registry.npmjs.org/@baaqar/artifact-to-pwa/-/artifact-to-pwa-3.0.0.tgz","fileCount":11,"integrity":"sha512-T4cKuhV6/kMlkxcFs5a4qLXJTk9FiJh+ptqvBU+oM0ySC50t5G5RbcpSqmQNZW2vDq7S6kk8degmuEBxgHDMOw==","signatures":[{"sig":"MEYCIQCa/D4No03Iqc1PnUuGJ5kWj+B7XCibDHCWIHweZE0irQIhANahxwRzy+X46/+wTBidnMpZGOZwHVRpTjKVuL36T7Lr","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@baaqar%2fartifact-to-pwa@3.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":91150},"type":"module","engines":{"node":">=18"},"gitHead":"ce2dfddf4f4282c05af30941eb77e89c84fde11c","scripts":{"test":"node --test test/compat.test.js","test:skip-bundle":"SKIP_BUNDLE_TESTS=1 node --test test/compat.test.js"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f7830986-7e49-4979-a361-886daae3372b"}},"repository":{"url":"git+https://github.com/Baaqar-007/artifact-to-pwa.git","type":"git"},"_npmVersion":"11.16.0","description":"Convert any Claude artifact (HTML/React/JSX) into a native Windows .exe — ~5 MB output via Neutralino + WebView2","directories":{},"_nodeVersion":"24.18.0","dependencies":{"ora":"^8.1.1","chalk":"^5.3.0","rcedit":"^4.0.0","esbuild":"^0.25.0","commander":"^12.1.0","png-to-ico":"^2.1.8","extract-zip":"^2.0.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/artifact-to-pwa_3.0.0_1785658465888_0.3874809516802378","host":"s3://npm-registry-packages-npm-production"}},"3.0.1":{"name":"@baaqar/artifact-to-pwa","version":"3.0.1","keywords":["pwa","claude","artifact","cli","neutralino","webview2","windows","exe","desktop","react","jsx","standalone","converter"],"author":"","license":"MIT","_id":"@baaqar/artifact-to-pwa@3.0.1","maintainers":[{"name":"baaqar","email":"baaqarnaqi@gmail.com"}],"homepage":"https://github.com/Baaqar-007/artifact-to-pwa#readme","bugs":{"url":"https://github.com/Baaqar-007/artifact-to-pwa/issues"},"bin":{"artifact-to-pwa":"bin/cli.js"},"dist":{"shasum":"145eca9124580fee692354708dab4f6e68beee85","tarball":"https://registry.npmjs.org/@baaqar/artifact-to-pwa/-/artifact-to-pwa-3.0.1.tgz","fileCount":11,"integrity":"sha512-FO+LVTfP5CN7eAOL3pip7sPVIVNFT0wJbGH2vaBGoxCazFyv/FYq6eGEAMr5+8Z5Q+fu0wVYt5QcZlWlHr9uaw==","signatures":[{"sig":"MEQCIE89fU8Uk8OyZhOamA1nltjIQsYVZy2GPpt9Zyg9liPqAiAUIhbasUX7hNS+qEiyKAZtYurvFS1f67Exo8wEI1O1uQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@baaqar%2fartifact-to-pwa@3.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":92962},"type":"module","engines":{"node":">=18"},"gitHead":"81a3e75cc323afadae7e7d4672a21c5c5fe948e9","scripts":{"test":"node --test test/compat.test.js","test:skip-bundle":"SKIP_BUNDLE_TESTS=1 node --test test/compat.test.js"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f7830986-7e49-4979-a361-886daae3372b"}},"repository":{"url":"git+https://github.com/Baaqar-007/artifact-to-pwa.git","type":"git"},"_npmVersion":"11.16.0","description":"Convert any Claude artifact (HTML/React/JSX) into a native .exe — ~5 MB output via Neutralino + WebView2","directories":{},"_nodeVersion":"24.18.0","dependencies":{"ora":"^8.1.1","chalk":"^5.3.0","rcedit":"^4.0.0","esbuild":"^0.25.0","commander":"^12.1.0","png-to-ico":"^2.1.8","extract-zip":"^2.0.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/artifact-to-pwa_3.0.1_1786630039291_0.9528634986422724","host":"s3://npm-registry-packages-npm-production"}},"3.0.2":{"name":"@baaqar/artifact-to-pwa","version":"3.0.2","description":"Convert any Claude artifact (HTML/React/JSX) into a native .exe — ~5 MB output via Neutralino + WebView2","type":"module","publishConfig":{"access":"public"},"bin":{"artifact-to-pwa":"bin/cli.js"},"scripts":{"test":"node --test test/compat.test.js","test:skip-bundle":"SKIP_BUNDLE_TESTS=1 node --test test/compat.test.js"},"keywords":["pwa","claude","artifact","cli","neutralino","webview2","windows","exe","desktop","react","jsx","standalone","converter"],"repository":{"type":"git","url":"git+https://github.com/Baaqar-007/artifact-to-pwa.git"},"author":"","license":"MIT","engines":{"node":">=18"},"dependencies":{"chalk":"^5.3.0","commander":"^12.1.0","esbuild":"^0.25.0","unzipper":"^0.12.3","ora":"^8.1.1","png-to-ico":"^2.1.8","rcedit":"^4.0.0"},"gitHead":"e95b09a85f67ab1135a6fdf8676e242c84b1717e","_id":"@baaqar/artifact-to-pwa@3.0.2","bugs":{"url":"https://github.com/Baaqar-007/artifact-to-pwa/issues"},"homepage":"https://github.com/Baaqar-007/artifact-to-pwa#readme","_nodeVersion":"24.19.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-xYmvM+xth6kpyzIihe6ebiD2aaGOsdlVRrzsV/4pKxxuVF93hbdbXPpbCGFi1zdY0f9E3+zs3uC010DD4VTP+g==","shasum":"33f213d6a70b1118cadb29400e6e3dece7c31aa5","tarball":"https://registry.npmjs.org/@baaqar/artifact-to-pwa/-/artifact-to-pwa-3.0.2.tgz","fileCount":11,"unpackedSize":93374,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@baaqar%2fartifact-to-pwa@3.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIF/ZF6r1kOybmhs92az/tknstMAt0kl4A3lhSK/MS5QlAiEA4T6EDhzCQ+Xb6bbjr2l3QLTXMKJ0i/DCI5Fe5n/9+bc="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f7830986-7e49-4979-a361-886daae3372b"}},"directories":{},"maintainers":[{"name":"baaqar","email":"baaqarnaqi@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/artifact-to-pwa_3.0.2_1786736469312_0.887199356512405"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-14T18:11:48.210Z","modified":"2026-08-14T19:41:09.873Z","1.0.0":"2026-06-14T18:11:48.527Z","1.1.0":"2026-06-16T11:51:06.195Z","2.0.0":"2026-07-29T13:08:16.925Z","2.0.1":"2026-07-29T17:48:36.648Z","2.1.0":"2026-07-30T12:34:37.940Z","3.0.0":"2026-08-02T08:14:26.029Z","3.0.1":"2026-08-13T14:07:19.511Z","3.0.2":"2026-08-14T19:41:09.536Z"},"bugs":{"url":"https://github.com/Baaqar-007/artifact-to-pwa/issues"},"license":"MIT","homepage":"https://github.com/Baaqar-007/artifact-to-pwa#readme","keywords":["pwa","claude","artifact","cli","neutralino","webview2","windows","exe","desktop","react","jsx","standalone","converter"],"repository":{"type":"git","url":"git+https://github.com/Baaqar-007/artifact-to-pwa.git"},"description":"Convert any Claude artifact (HTML/React/JSX) into a native .exe — ~5 MB output via Neutralino + WebView2","maintainers":[{"name":"baaqar","email":"baaqarnaqi@gmail.com"}],"readme":"# Native Artifact\n\n[![npm version](https://img.shields.io/npm/v/@baaqar/artifact-to-pwa)](https://www.npmjs.com/package/@baaqar/artifact-to-pwa)\n[![npm downloads](https://img.shields.io/npm/dw/@baaqar/artifact-to-pwa)](https://www.npmjs.com/package/@baaqar/artifact-to-pwa)\n[![license](https://img.shields.io/npm/l/@baaqar/artifact-to-pwa)](./LICENSE)\n[![node](https://img.shields.io/node/v/@baaqar/artifact-to-pwa)](https://nodejs.org)\n\n> Convert any Claude artifact (.jsx / .html) into a native desktop application.\n> ~5 MB output. No compiler. No App Store. No manual setup.\n\n```bash\nnpx artifact-to-pwa ./my-app.jsx --name \"My Tool\"\n```\n\n---\n\n## Table of Contents\n\n- [Quick Start](#quick-start)\n- [How to Get Your Artifact File](#how-to-get-your-artifact-file)\n- [Options](#options)\n- [Output Structure](#output-structure)\n- [How It Works](#how-it-works)\n- [Claude Runtime Compatibility](#claude-runtime-compatibility)\n- [Persistent Storage](#persistent-storage)\n- [Supported and Unsupported Features](#supported-and-unsupported-features)\n- [Build History](#build-history)\n- [Architecture Reference](#architecture-reference)\n- [Changelog](#changelog)\n\n---\n\n## Quick Start\n\n```bash\n# Convert a local artifact file\nnpx artifact-to-pwa ./my-app.jsx\n\n# With a custom name and icon\nnpx artifact-to-pwa ./app.jsx --name \"My Tool\" --icon ./icon.png\n```\n\nOn first run the tool downloads the Neutralino runtime (~4 MB) and caches it\npermanently. Subsequent builds finish in under a second.\n\n**Requirements:**\n- Node.js 18 or later\n- Windows 10 (updated after January 2023) or Windows 11\n- Linux: Ubuntu 20.04+, Fedora 36+, or Arch with WebKitGTK installed\n- Internet connection on first build only\n\n---\n\n## How to Get Your Artifact File\n\nClaude does not allow direct URL scraping (Anthropic enforces `X-Frame-Options`\nheaders on all artifact URLs). The correct workflow is:\n\n1. Open your artifact in Claude\n2. Click the **`<>`** source code button in the artifact toolbar\n3. Copy the code and save it as `my-app.jsx` or `my-app.html`\n4. Run `npx artifact-to-pwa ./my-app.jsx`\n\n---\n\n## Options\n\n| Flag | Description | Default |\n|------|-------------|---------|\n| `-n, --name <name>` | App name shown in the title bar and taskbar | Derived from filename |\n| `-c, --color <hex>` | Theme color written into the Neutralino config | `#6366f1` |\n| `-i, --icon <path>` | Path to a `.png` icon (converted to `.ico` and patched into the exe) | Electron default |\n| `-o, --out <dir>` | Output directory | `./<slug>-windows` |\n\n---\n\n## Output Structure\n\n```\nmy-tool-windows/          (~5 MB total)\n├── My Tool.exe           ← double-click to launch\n├── neutralino.config.json\n├── resources/\n│   ├── index.html        ← your bundled artifact\n│   └── js/\n│       └── neutralino.js ← Neutralino client bridge\n└── README-Launch.txt     ← SmartScreen bypass instructions\n```\n```\nmy-tool-linux/\n├── My Tool          ← run this (or start.sh)\n├── start.sh         ← convenience launcher\n├── neutralino.config.json\n├── resources/\n│   ├── index.html\n│   └── js/neutralino.js\n└── README-Launch.txt\n```\n\nTo share the app: **zip the entire folder** and send it.\nThe recipient needs WebView2 installed (see below).\n\n**Launching on Windows:**\nThe `.exe` is unsigned. On first launch Windows SmartScreen shows a warning.\nClick **More info → Run anyway**. This appears once per machine.\n\n---\n\n## How It Works\n\nThe tool has three pipeline stages that run in sequence.\n\n### Stage 1 — Bundle\n\nThe artifact source file is read and classified into one of three types:\n\n- **Full HTML** (`<!DOCTYPE html>` or `<html>` present): passed through with\n  the Claude compatibility shims and Neutralino bridge injected into `<head>`.\n- **HTML fragment** (partial markup): wrapped in a complete shell document,\n  then same injection as above.\n- **React / JSX** (detected by import patterns and JSX syntax): processed\n  through esbuild. React imports are stripped first (they are re-provided by\n  the entry wrapper), third-party dependencies are installed into a\n  hash-keyed local cache, and the result is a single self-contained HTML file\n  with the bundled JavaScript inlined.\n\nThe entry file for esbuild is written **beside the artifact** (same directory),\nnot in `os.tmpdir()`. This is critical: esbuild resolves relative imports like\n`./styles.css` and `./icon.png` relative to the entry file's location.\nWriting the entry to a temp directory breaks all relative imports silently.\n\n### Stage 2 — Runtime\n\nThe Neutralino binary and client library are fetched from the official GitHub\nReleases API if not already cached. Both files are downloaded in parallel.\nSHA-256 integrity is verified using the `digest` field from the GitHub releases\nAPI response. The cache self-heals: if a cached file fails verification it is\ndeleted and re-downloaded automatically.\n\nDetails of the download pipeline:\n\n- Asset URLs are resolved dynamically via the GitHub releases API `assets` array\n  rather than hardcoded paths. This means the tool keeps working if Neutralino\n  changes its asset naming between releases.\n- `browser_download_url` is always used, never the `url` field. The `url` field\n  is a GitHub API endpoint that returns JSON metadata unless an exact `Accept`\n  header is sent. Using `browser_download_url` avoids this class of error.\n- After downloading, a `Content-Type` guard rejects any response that contains\n  `text/html` or `application/json` before the bytes reach disk.\n- The progress bar clamps downloaded bytes against `Content-Length` before\n  computing bar fill. Some servers report incorrect `Content-Length` values,\n  which previously caused a negative `repeat()` call and a crash.\n\n### Stage 3 — Inject\n\nThe bundled HTML and Neutralino runtime are assembled into the output directory:\n\n1. The Neutralino binary is copied and renamed to `<AppName>.exe`.\n2. `WebView2Loader.dll` is copied alongside the exe if present in the runtime ZIP.\n3. `neutralino.config.json` is generated with a deterministic localhost port\n   derived from the app slug (range 49152–65534). Each app gets its own port\n   so their localStorage origins are isolated from each other.\n4. The client library is copied to `resources/js/neutralino.js`.\n5. The bundled `index.html` is written to `resources/`.\n6. If `--icon` was provided, the PNG is converted to ICO and patched into the\n   exe using `rcedit`.\n7. `README-Launch.txt` is written with WebView2 download link and SmartScreen\n   bypass instructions.\n\n---\n\n## Claude Runtime Compatibility\n\nClaude exposes APIs on `window` that only exist inside its iframe environment.\nWhen an artifact runs as a standalone app these APIs are absent, and any\nartifact that calls them fails silently.\n\nThe tool automatically injects a compatibility layer (`src/runtime.js`) as the\nfirst script on every generated page, before the Neutralino bridge and before\nany app code. Each shim is guarded so it never overwrites Claude's native\nimplementation — the same HTML file works inside Claude (uses native APIs)\nand as a standalone app (uses shims) without any modification.\n\n### `window.storage`\n\nThe most common Claude-native API. Used by artifacts to persist data across\nsessions inside Claude.\n\n**Claude's contract:**\n\n```javascript\nawait window.storage.set('key', 'value')     // → void\nawait window.storage.get('key')              // → { value: string } | null\nawait window.storage.remove('key')           // → void\nawait window.storage.clear()                 // → void\n```\n\nNote that `get()` returns `{ value: string }` or `null`, not a raw string.\nArtifacts typically do:\n\n```javascript\nconst result = await window.storage.get('key');\nif (result) use(result.value);\n```\n\n**Shim implementation:**\nBacked by `localStorage`, namespaced under the prefix `__cs__` to avoid\ncolliding with any direct `localStorage` usage in the same artifact.\nAll methods return resolved Promises so existing artifact code works unchanged.\n\n### Adding future shims\n\nWhen Claude introduces new runtime APIs, add them to `src/runtime.js`:\n\n```javascript\nconst SHIM_WHATEVER = `\n(function () {\n  if (window.whatever) return;\n  window.whatever = { ... };\n}());`.trim();\n\nconst SHIMS = [\n  SHIM_STORAGE,\n  SHIM_WHATEVER,  // ← add here\n];\n```\n\nThe aggregate `CLAUDE_RUNTIME_SHIM` export is then automatically picked up\nby `src/bundle.js` and injected everywhere.\n\n---\n\n## Persistent Storage\n\n`localStorage` persists natively via WebView2's built-in profile storage.\n\nWebView2 stores its data at:\n```\n%LocalAppData%\\com.artifact-to-pwa.<slug>\\EBWebView\\Default\\Local Storage\\\n```\n\nThis directory is created by WebView2 on first launch and survives app updates,\nreinstalls, and reboots. No shim or IPC bridge is needed — `localStorage.setItem()`\nin your artifact just works exactly as it does in a browser.\n\nThe port assigned to each app (derived deterministically from its slug) ensures\nthat `http://localhost:<port>` is a stable, consistent origin across every\nlaunch. This is what WebView2 uses as the key for localStorage. Two apps with\ndifferent names get different ports and therefore different, isolated storage.\n\n**WebView2 requirement:**\nWebView2 is pre-installed on Windows 11 and on Windows 10 updated after\nJanuary 2023 (delivered via the Edge update channel). If it is missing, the\napp will fail to open. The `README-Launch.txt` included in every generated\napp links to the free 1.5 MB WebView2 Bootstrapper installer.\n\n---\n\n## Supported and Unsupported Features\n\n### Works\n\n- React hooks (`useState`, `useEffect`, `useContext`, `useRef`, etc.)\n- `React.lazy` and `Suspense` with static import paths\n- Tailwind CSS classes\n- Third-party npm packages (`recharts`, `lodash`, `date-fns`, `zod`, etc.)\n- SVG, PNG, JPG, GIF, WebP imports (converted to data URLs at build time)\n- CSS imports (inlined as strings)\n- Web fonts (`.woff`, `.woff2` converted to data URLs)\n- `localStorage` and `sessionStorage`\n- `window.storage` (Claude API — shimmed)\n- `fetch()` to external APIs\n- Canvas and WebGL\n- `<input type=\"file\">` file uploads\n- IndexedDB\n- WebSockets to external servers\n- TypeScript and TSX\n- Dynamic imports with static paths\n\n### Does Not Work\n\n**Node.js built-in modules** — `fs`, `path`, `os`, `crypto`, `http`, `https`,\n`child_process`, and all other Node core modules cannot run in a WebView.\nThe build will fail with a human-readable error and a suggestion to use the\nequivalent Neutralino API.\n\n**Server frameworks** — Express, Koa, Fastify, and similar packages require\na Node.js process and have no browser equivalent.\n\n**Meta-frameworks** — Next.js, Remix, Gatsby, and SvelteKit require a Node.js\nserver for SSR, ISR, or their data-fetching layers.\n\n**Build tools as runtime dependencies** — Vite, Webpack, Rollup, and Parcel\nare build tools. If your artifact imports them, that import should be removed.\n\n**Electron APIs** — `ipcRenderer`, `contextBridge`, and other Electron-specific\nAPIs do not exist in a Neutralino app. Use `Neutralino.*` APIs instead.\n\n**Dynamic imports with variable paths** — `import(\\`./plugins/${name}\\`)` cannot\nbe statically analysed by esbuild. Replace with a static map of lazy imports.\n\nSee `docs/UNSUPPORTED.md` in the repository for the full reference including\nNeutralino API alternatives for each unsupported pattern.\n\n---\n\n## Build History\n\nThe tool went through several complete architectural rewrites. Each one was\ndriven by real problems found during testing or reported by users.\nThis section explains every decision in the order it was made.\n\n---\n\n### v1.0.0 — PWA Folder Output\n\n**The idea:** the simplest way to make a Claude artifact installable outside\nthe chat is to wrap it as a Progressive Web App. A PWA is just a website with\nthree extra files: a `manifest.json` (tells the browser it is an app), a\nservice worker (`sw.js` for offline caching), and an icon. The browser then\noffers an \"Add to Home Screen\" prompt.\n\n**What it generated:**\n\n```\nmy-app-pwa/\n├── index.html\n├── manifest.json\n├── sw.js\n├── icon-192.png\n├── icon-512.png\n└── README.md\n```\n\n**URL mode:** the tool also accepted a Claude artifact URL and embedded it in\na fullscreen `<iframe>`. This avoided the need to paste source code.\n\n**How the React wrapping worked:** because Claude artifacts are often raw JSX\nrather than complete HTML files, the tool detected the source type using regex\npatterns and wrapped React/JSX files by injecting Babel standalone and CDN\nReact script tags. No local build step.\n\n**What went wrong:**\n\n- The iframe URL mode silently failed for all Claude artifact URLs because\n  Anthropic enforces `X-Frame-Options: DENY` on artifact pages. The iframe\n  would load a blank page with no error.\n- The generated `index.html` was sometimes empty. The cause was an ESM import\n  chain failure: `templates.js` imported `wrapCode` from `detect.js`, which\n  had just been updated to import from `storage.js`. If any link in that chain\n  resolved incorrectly the function returned `undefined`, and `writeFileSync`\n  wrote that silently.\n- PWA install requires HTTPS. Running the output folder locally with `npx serve`\n  worked, but dragging it to Netlify Drop worked better. The UX friction of\n  \"serve a folder over HTTP just to see if your app works\" was higher than expected.\n\n---\n\n### v1.1.0 — localStorage → IndexedDB Shim\n\n**The problem reported:** a developer on dev.to pointed out that when an\nartifact is converted and served from a new URL (e.g. deployed to Netlify),\nits `localStorage` is empty. `localStorage` is keyed by origin (the page's\nURL). Moving the file to a new URL means a new origin, which means a new,\nempty `localStorage`. All saved data is lost.\n\n**The fix:** inject a proxy object that intercepts every `localStorage` call\nand routes it through IndexedDB instead. IndexedDB is also origin-scoped, but\nwe can control the key name and use a stable identifier.\n\n**How the shim worked:**\n- Replaced `window.localStorage` with a proxy object at page load.\n- The proxy maintained an in-memory mirror for synchronous reads (IndexedDB is\n  async; `localStorage.getItem()` is synchronous, so a true drop-in requires\n  a pre-populated in-memory cache).\n- On page load the shim read all IndexedDB data into the mirror, briefly hiding\n  the page to prevent a flash of empty state, then revealed it once data was ready.\n- On any `setItem` call, the mirror was updated synchronously and an async\n  IndexedDB write was dispatched.\n\n**The durability gap:** the shim was correct in normal operation but had a\ntheoretical data loss window. Between a `setItem()` call returning and the\nIndexedDB write being flushed to disk, a hard crash could lose the last write.\nFor streak trackers and todo lists this was acceptable. For financial data it\nwould not be. This was documented and communicated to users.\n\n**Result:** downloads jumped from ~80 to ~260 in a few days after the storage\naddition. The shim addressed the most common real-world pain point.\n\n---\n\n### v2.0.0 — Native Windows .exe via Electron Binary Injection\n\n**The problem with PWAs:** a PWA is still a website. Installing it requires\nHTTPS hosting, a browser with PWA support, and the user understanding the\n\"Add to Home Screen\" flow. Many users just wanted a file they could\ndouble-click.\n\n**The original architecture proposal:** use Tauri (Rust-based) to compile a\nnative binary. Tauri produces ~5 MB binaries and uses the OS webview, which\nwould have been ideal.\n\n**Why Tauri was rejected for v2:** Tauri requires the Rust toolchain and\nplatform build tools on the user's machine. On Windows this means Visual Studio\nBuild Tools (~4 GB). First builds take 3–5 minutes. This is a fatal UX problem\nfor a tool that should produce an app in under a second.\n\n**The chosen approach — binary injection:** instead of compiling anything\nlocally, download a pre-built Electron binary (~85 MB, cached permanently)\nand inject the artifact into it. Electron ships as a directory of files.\nThe entry point is whatever is in `resources/app/`. By writing our own\n`main.js`, `preload.js`, and `index.html` into that directory and renaming\n`electron.exe` to `AppName.exe`, we get a native `.exe` with no local\ncompilation at all. Build time drops from minutes to milliseconds.\n\n**The storage architecture:** Electron's renderer process (`index.html`) cannot\naccess the filesystem directly. Instead:\n- A `preload.js` script exposes three IPC channels (`storage:set`,\n  `storage:remove`, `storage:clear`) to the renderer via Electron's\n  `contextBridge`.\n- A `window.localStorage` proxy in the HTML intercepted all storage calls and\n  routed writes through those IPC channels to the main process.\n- The main process wrote data atomically to `%AppData%\\<AppName>\\storage.json`\n  using a write-to-temp-then-rename pattern to prevent corruption.\n\n**What actually shipped:** a ~350 MB output directory, which was the main\ncomplaint. This was the known cost of bundling Chromium.\n\n---\n\n### v2.0.1 — esbuild CORS Patch\n\nA CORS vulnerability in esbuild versions below `0.25.0` was forcing npm to\ndowngrade installations. The only change in this release was bumping the esbuild\ndependency to `^0.25.0`.\n\n---\n\n### v2.1.0 — Five Targeted Fixes\n\nFive issues were found and fixed simultaneously:\n\n**Fix 1 — URL support deprecated.**\nThe URL fetch mode was formally removed. Anthropic's `X-Frame-Options` headers\nblock both iframe embedding and direct HTML scraping for all artifact URLs.\nThe headless browser alternative (Puppeteer) would have added ~150 MB of\nbrowser binaries and still failed whenever Anthropic changed their auth flow.\nThe cost-to-benefit ratio was wrong. `fetchURL()` was replaced with a function\nthat immediately throws with exact instructions for downloading the file manually.\n\n**Fix 2 — Clean output structure.**\nThe Electron output dumped 15+ DLL files alongside the exe at the root level,\nwhich felt unprofessional. All Electron runtime files were moved into an\n`_internal/` subdirectory. A batch launcher (`Start MyApp.bat`) at the root\nlevel changed directory to `_internal/` before calling the exe.\nThe reason `_internal/` was necessary: the Electron binary must be in the same\ndirectory as its DLLs (Windows PE loader requirement). Moving only the exe to\nthe root would make it fail to start. The batch launcher works around this.\n\n**Fix 3 — Storage persistence bug.**\nData was not persisting across sessions. Two stacked bugs:\n- The IPC handlers (`ipcMain.on('storage:set', ...)`) were registered *after*\n  `createWindow()`. Any write from the renderer during first render arrived\n  before the handler existed and was silently dropped. Fix: register handlers\n  before creating the window.\n- The temp file approach for HTML injection used the system temp directory.\n  On some machines `%TEMP%` has restricted write access. Fix: use `userData`\n  instead.\n\n**Fix 4 — React import crash.**\nesbuild was crashing with \"Identifier already declared\" on many Claude artifacts.\nThe entry wrapper added `import React from 'react'` but the artifact source\nalready contained React imports. The original regex only caught two forms.\nClaude artifacts produce many more:\n`import React, { useState } from 'react'`,\n`import * as React from 'react'`,\n`import ReactDOM from 'react-dom'`,\n`import { createRoot } from 'react-dom/client'`, etc.\nFix: comprehensive stripping of all seven React/react-dom import forms before\npassing code to esbuild.\n\n**Fix 5 — Error handling.**\nRaw Node.js stack traces were printed on failure, and some error paths exited\nwith code 0 (success). Fix: `ora` spinners for long operations, a global\n`try/catch` wrapper in the build orchestrator, human-readable messages for\ncommon esbuild and npm failures, and `process.exit(1)` on every error path.\n\n---\n\n### v3.0.0 — Neutralino Migration & Production Hardening\n\n**The core problem with Electron:** a 350 MB output for a 5-page artifact with\na heatmap is objectively wrong. The artifact logic is ~50 KB. The Electron\nruntime is ~300 MB because it bundles its own copy of Chromium and Node.js.\nThere is no configuration that meaningfully reduces this.\n\n**The solution — Neutralino:** Neutralino is a lightweight framework that uses\nthe OS's built-in WebView instead of bundling one.\n\n| | Electron | Neutralino |\n|--|----------|------------|\n| Output size | ~350 MB | ~5 MB |\n| Runtime download | ~85 MB | ~4 MB |\n| Storage shim | Required (IPC bridge) | Not needed |\n| localStorage | Complex proxy via IPC | Native WebView2 persistence |\n\nOn Windows 10/11, Neutralino uses WebView2, which is part of Microsoft Edge\nand pre-installed on all modern Windows machines. WebView2 stores localStorage\nin a persistent profile directory tied to the `applicationId` in\n`neutralino.config.json`. No shim, no IPC, no proxy — `localStorage.setItem()`\njust works and persists forever.\n\n**Why dynamic binary fetching:** the Neutralino binary is ~4 MB. Bundling it\ninside the npm package would make `npm install artifact-to-pwa` download it for\nevery user on every platform, even users who never build for Windows. Instead\nthe binary is fetched on the first build and cached in `~/.artifact-to-pwa/`.\n\n**The storage design change:** the entire Electron `main.js` / `preload.js` /\nIPC architecture was deleted. `template/main.js` and `template/preload.js`\nwere removed from the repository. Storage now works without any intervention.\n\n**The port design:** Neutralino serves the app at `http://localhost:<port>/`.\nThe port is derived deterministically from the app's slug using a hash function,\nproducing a number in the 49152–65534 range. This gives each app a stable,\nconsistent origin so localStorage is isolated between apps and persists\ncorrectly across restarts.\n\n**extract-zip removal and re-addition:** extract-zip was removed in v2.2.0\nbecause the initial plan assumed Neutralino binaries were single files. When\nthe actual download was found to be a ZIP (in v3.0.0), extract-zip was added\nback.\n\nThe architecture was correct but had several production issues discovered\nduring real-world testing.\n\n**Bug 1 — HTTP 404 on binary download.**\nThe shell downloader tried to fetch `neutralino-win_x64.exe` directly from\nthe release URL. This file does not exist as a standalone asset. Neutralino\ndistributes all platform binaries inside a single `neutralino.zip` archive.\nAdditionally, the client library (`neutralino.js`) comes from a completely\nseparate repository (`neutralinojs/neutralino.js`), not from the main\n`neutralinojs/neutralinojs` repo.\n\nFix: use the GitHub releases API `assets` array to locate correct URLs\ndynamically rather than guessing filenames. Download `neutralino.zip`, extract\nit, and copy the Windows-specific files. Download `neutralino.js` from the\ncorrect second repo.\n\n**Bug 2 — SHA-256 mismatch on every run after the first.**\nAfter downloading and extracting, the code stored the ZIP file's GitHub API\ndigest as the expected hash for `neutralino-win_x64.exe`. On the next run it\ncomputed the SHA-256 of the extracted exe and compared it against the ZIP's\nhash. These are different files with different hashes. This comparison always\nfailed.\n\nFix: after extraction, compute and store the SHA-256 of the *extracted* exe\nand client library. These are the files that will be verified on subsequent runs.\n\n**Bug 3 — Wrong download URL.**\nThe code used `zipAsset.url || zipAsset.browser_download_url`. The `url` field\nin a GitHub releases API response is an API endpoint\n(`https://api.github.com/repos/.../releases/assets/<id>`) that returns JSON\nmetadata unless you send `Accept: application/octet-stream`. Without that\nheader it returns a 2 KB JSON object. The SHA-256 of that JSON object obviously\ndoes not match the expected hash of the binary. The reported download size\n(2 KB) was the diagnostic — a 4 MB binary cannot be 2 KB.\n\nFix: always use `browser_download_url` (direct CDN link, no headers needed).\nAdd a `Content-Type` guard that rejects `text/html` and `application/json`\nresponses before they reach disk.\n\n**Bug 4 — Cache never self-healed.**\nWhen integrity verification failed, the code threw an error telling the user\nto manually delete `~/.artifact-to-pwa/neutralino/`. This is a bad user\nexperience for what is fundamentally a recoverable situation.\n\nFix: if verification fails, call `rmSync(cacheDir)` and fall through to the\ndownload code. Only surface an error to the user if a freshly downloaded\nruntime also fails verification (which would indicate a problem with the\nupstream release itself).\n\n**Bug 5 — CSS and image imports failing.**\nWhen bundling a React artifact that imported `./styles.css` or `./icon.png`,\nesbuild could not find those files. The reason: the temporary entry file was\nwritten to `os.tmpdir()`. esbuild resolves relative imports relative to the\nentry file's location. `os.tmpdir()` is a completely different directory from\nthe artifact. The files simply did not exist there.\n\nFix: write the entry file to `dirname(filePath)` — the same directory as the\nartifact. Every relative import resolves correctly.\n\n**Bug 6 — `window.storage` undefined.**\nSome Claude artifacts use `window.storage`, a runtime API that Claude injects\ninto its iframe environment. This API does not exist anywhere else. When the\nconverted app called `window.storage.get(...)`, it got `undefined.get()` and\nthrew silently (caught internally by the artifact). Persistence appeared broken\nwhen it was actually a missing API.\n\nFix: create `src/runtime.js` as a dedicated compatibility layer. It exports a\n`CLAUDE_RUNTIME_SHIM` — a `<script>` block that is injected as the first\nscript on every generated page. The shim defines `window.storage` backed by\nlocalStorage, namespaced under `__cs__` to avoid key collisions. It is guarded\nso it never overwrites Claude's native implementation.\n\n**Hardening — Version pinning.**\nAll `latest` version specifiers were replaced with pinned versions:\nNeutralino `6.7.0` and React `18.3.1`. This prevents a Neutralino or React\nbreaking change from silently changing the behaviour of a future build.\n\n**Hardening — detect.js module.**\nReact detection, import extraction, import stripping, and export normalisation\nwere extracted from `bundle.js` into their own `src/detect.js` module.\n`extractBareImports()` was extended to recognise Node.js built-in modules and\nknown unsupported packages (Express, Electron, Next.js, etc.) and throw\nhuman-readable errors with Neutralino API alternatives before esbuild runs.\n\n**Hardening — Hash-based npm cache.**\nThe single global `npm-cache` directory was replaced with per-dependency-set\ndirectories keyed by SHA-256 of the full dependency spec list. Different\nartifacts with different third-party dependencies get isolated caches and\nnever interfere with each other.\n\n**Compatibility test suite.**\n`test/compat.test.js` uses Node's built-in `node:test` runner (no extra\nframework, available since Node 18). It covers detection logic, unsupported\nimport error messages, and end-to-end bundling of representative artifact\npatterns: simple React, Tailwind, localStorage usage, full HTML passthrough,\nCSS imports, and image imports. Run with `npm test`.\n\n### v3.0.1 - Linux compatibility\n\n- Linux support: extracts neutralino-linux_x64, marks executable, writes start.sh launcher\n- README-Launch.txt includes distro-specific WebKitGTK install instructions\n\n### v3.0.2 - High vulnerability\n- Replaced extract-zip with unzipper (resolves high severity symlink path traversal advisory GHSA-jmr9-qjv8-65gv)\n---\n\n## Architecture Reference\n\n```\nnpx artifact-to-pwa ./app.jsx --name \"My App\"\n        │\n        ▼\nbin/cli.js              ← Commander parses flags, calls build()\n        │\n        ▼\nsrc/build.js            ← Orchestrates the three stages\n        │\n        ├── Stage 1: Bundle ──────────────────────────────────────────────────\n        │       │\n        │       ├── src/detect.js       detectArtifactType()\n        │       │                       extractBareImports()   (throws on Node builtins)\n        │       │                       stripReactImports()\n        │       │                       normaliseDefaultExport()\n        │       │\n        │       ├── src/bundle.js       installDeps()          (hash-keyed npm cache)\n        │       │                       esbuild.build()        (entry beside artifact)\n        │       │                       injectNeutralinoScript()\n        │       │\n        │       └── src/runtime.js      CLAUDE_RUNTIME_SHIM    (window.storage + future)\n        │\n        ├── Stage 2: Runtime ─────────────────────────────────────────────────\n        │       │\n        │       └── src/shell.js        fetchReleaseAssets()   (GitHub assets API)\n        │                               downloadToFile()       (parallel, progress bar)\n        │                               verifyDigest()         (SHA-256 via GitHub digest)\n        │                               extractZip()\n        │                               self-healing cache     (rmSync + re-download)\n        │\n        └── Stage 3: Inject ──────────────────────────────────────────────────\n                │\n                └── src/inject.js       portFromSlug()         (deterministic port)\n                                        buildConfig()          (neutralino.config.json)\n                                        rcedit()               (exe metadata + icon)\n                                        launchReadme()\n```\n\n**File responsibilities:**\n\n| File | Responsibility |\n|------|---------------|\n| `bin/cli.js` | CLI entry, flag parsing |\n| `src/build.js` | Pipeline orchestration, error handling, output messages |\n| `src/detect.js` | Artifact type detection, import analysis, React stripping |\n| `src/bundle.js` | esbuild integration, npm dep management, HTML construction |\n| `src/runtime.js` | Claude API compatibility shims |\n| `src/shell.js` | Neutralino binary download, cache management, integrity |\n| `src/inject.js` | Output directory assembly, config generation, exe patching |\n| `src/fetch.js` | URL deprecation notice |\n| `test/compat.test.js` | Compatibility test suite |\n| `docs/UNSUPPORTED.md` | Unsupported feature reference |\n\n---\n\n## Changelog\n\n### v3.0.2\n- Replaced extract-zip with unzipper (resolves high severity symlink path traversal advisory GHSA-jmr9-qjv8-65gv)\n\n### v3.0.1\n- Linux support: extracts neutralino-linux_x64, marks executable, writes start.sh launcher\n- README-Launch.txt includes distro-specific WebKitGTK install instructions\n\n### v3.0.0\n\n- Switch from Electron to Neutralino — output drops from ~350 MB to ~5 MB\n- localStorage persists natively via WebView2 profile (no shim needed)\n- Dynamic binary fetching — Neutralino binary not bundled in npm package\n- Removed `extract-zip`, `template/main.js`, `template/preload.js`\n- Deterministic port-per-app for stable localStorage origin\n- Fix: GitHub assets API used for download URL discovery (no more hardcoded filenames)\n- Fix: binaries extracted from `neutralino.zip`, not downloaded as raw EXEs\n- Fix: `browser_download_url` always used — `url` field returns JSON metadata\n- Fix: SHA-256 stored for extracted files, not the ZIP (fixes perpetual cache mismatch)\n- Fix: self-healing cache — corruption triggers automatic delete and re-download\n- Fix: esbuild entry file written beside artifact (fixes CSS and image imports)\n- Fix: `window.storage` Claude API compatibility shim injected on every page\n- Feat: `src/runtime.js` — dedicated module for Claude API compatibility\n- Feat: `src/detect.js` — separate module for React detection and import analysis\n- Feat: human-readable errors for Node.js built-in and unsupported package imports\n- Feat: hash-keyed npm dependency cache (isolated per dependency set)\n- Harden: Neutralino pinned to v6.7.0, React pinned to 18.3.1\n- Harden: `Content-Type` guard rejects HTML/JSON responses before they reach disk\n- Harden: progress bar clamped to prevent crash on incorrect `Content-Length`\n- Test: `test/compat.test.js` compatibility suite (Node built-in test runner)\n- Docs: `docs/UNSUPPORTED.md` with Neutralino API alternatives\n\n\n\n### v2.1.0\n- URL input deprecated (Anthropic X-Frame-Options blocks all scraping)\n- Output restructured: Electron internals in `_internal/`, batch launcher at root\n- Storage persistence fixed: IPC handlers registered before window creation\n- React import crash fixed: all seven React/react-dom import forms stripped\n- Error handling: ora spinners, global try/catch, proper exit codes\n\n### v2.0.1\n- esbuild bumped to `^0.25.0` (CORS vulnerability patch)\n\n### v2.0.0\n- Native Windows `.exe` via prebuilt Electron binary injection\n- Electron IPC bridge for localStorage persistence\n- esbuild bundler replaces Babel CDN (offline at runtime)\n- Ephemeral npm install for third-party artifact dependencies\n- Icon support via `--icon` (PNG → ICO + rcedit)\n\n### v1.1.0\n- localStorage → IndexedDB shim (data persists when app is moved to new URL)\n- ~260 downloads within a week of release\n\n### v1.0.0\n- Initial release: PWA folder output (manifest, service worker, icons)\n- Auto-detection of HTML, HTML fragment, and React/JSX source types\n- URL mode (later deprecated — blocked by X-Frame-Options)\n\n---\n\nMade to keep Claude artifacts alive outside the chat.\nhttps://github.com/Baaqar-007/artifact-to-pwa","readmeFilename":"README.md"}