webtun 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "WebTun",
3
+ "short_name": "WebTun",
4
+ "description": "Self-hosted web terminal with file explorer and Cloudflare Tunnel",
5
+ "start_url": "/",
6
+ "scope": "/",
7
+ "lang": "en",
8
+ "display": "standalone",
9
+ "display_override": ["window-controls-overlay", "standalone"],
10
+ "orientation": "any",
11
+ "background_color": "#1a1b26",
12
+ "theme_color": "#1a1b26",
13
+ "categories": ["developer", "utilities"],
14
+ "screenshots": [],
15
+ "prefer_related_applications": false,
16
+ "icons": [
17
+ { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" },
18
+ { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }
19
+ ],
20
+ "shortcuts": [
21
+ { "name": "New Terminal", "url": "/", "description": "Open a new terminal session" }
22
+ ]
23
+ }
package/public/sw.js ADDED
@@ -0,0 +1,70 @@
1
+ const CACHE = 'webtun-v3';
2
+ const PRECACHE = [
3
+ '/',
4
+ '/index.html',
5
+ '/manifest.json',
6
+ '/icon-192.png',
7
+ '/icon-512.png',
8
+ ];
9
+
10
+ self.addEventListener('install', e => {
11
+ e.waitUntil(
12
+ caches.open(CACHE).then(c => c.addAll(PRECACHE)).then(() => self.skipWaiting())
13
+ );
14
+ });
15
+
16
+ self.addEventListener('activate', e => {
17
+ e.waitUntil(
18
+ caches.keys().then(keys =>
19
+ Promise.all(keys.filter(k => k !== CACHE).map(k => caches.delete(k)))
20
+ ).then(() => self.clients.claim())
21
+ );
22
+ });
23
+
24
+ self.addEventListener('message', e => {
25
+ if (e.data === 'skipWaiting') {
26
+ self.skipWaiting();
27
+ }
28
+ });
29
+
30
+ self.addEventListener('fetch', e => {
31
+ if (e.request.method !== 'GET') return;
32
+ if (!e.request.url.startsWith('http://') && !e.request.url.startsWith('https://')) return;
33
+
34
+ const url = new URL(e.request.url);
35
+ if (url.pathname.startsWith('/api')) return;
36
+
37
+ // Network-First for HTML/navigation
38
+ const isNavigation = e.request.mode === 'navigate' || (url.pathname === '/') || (url.pathname.endsWith('.html') && e.request.mode === 'same-origin');
39
+ if (isNavigation) {
40
+ e.respondWith(
41
+ fetch(e.request).then(res => {
42
+ if (res && res.status === 200 && res.type !== 'opaque') {
43
+ const clone = res.clone();
44
+ caches.open(CACHE).then(c => { try { c.put(e.request, clone); } catch {} });
45
+ }
46
+ return res;
47
+ }).catch(() =>
48
+ caches.match(e.request).then(cached =>
49
+ cached || caches.match('/index.html')
50
+ )
51
+ )
52
+ );
53
+ return;
54
+ }
55
+
56
+ // Cache-First for static assets
57
+ // CDN resources are fetched from network (not precached) to avoid opaque response failures
58
+ e.respondWith(
59
+ caches.match(e.request).then(cached => {
60
+ if (cached) return cached;
61
+ return fetch(e.request).then(res => {
62
+ if (res && res.status === 200 && res.type !== 'opaque') {
63
+ const clone = res.clone();
64
+ caches.open(CACHE).then(c => { try { c.put(e.request, clone); } catch {} });
65
+ }
66
+ return res;
67
+ }).catch(() => cached || new Response('Offline', { status: 503 }));
68
+ })
69
+ );
70
+ });