bloby-bot 0.53.6 → 0.53.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/supervisor/index.ts +15 -22
- package/supervisor/workspace-guard.js +29 -9
- package/workspace/client/public/sw.js +14 -23
package/package.json
CHANGED
package/supervisor/index.ts
CHANGED
|
@@ -112,12 +112,15 @@ const MIME_TYPES: Record<string, string> = {
|
|
|
112
112
|
const SW_JS = `// Service worker — app-shell caching + push notifications
|
|
113
113
|
// Caching strategy:
|
|
114
114
|
// Hashed assets (/assets/*-AbCd12.js) → cache-first (immutable)
|
|
115
|
-
// Navigation (HTML) →
|
|
116
|
-
// Static assets
|
|
117
|
-
// JS/CSS modules → stale-while-revalidate
|
|
115
|
+
// Navigation (HTML) → network-first (cache = offline fallback only)
|
|
116
|
+
// Static assets / JS / CSS modules → network-first (cache = offline fallback only)
|
|
118
117
|
// API, WebSocket, Vite internals → network-only (no cache)
|
|
118
|
+
// Network-first (NOT stale-while-revalidate) on navigations + modules: Bloby is a LIVE-EDIT
|
|
119
|
+
// tool, so the browser must always see the current app and its build errors — never a stale
|
|
120
|
+
// cached shell that masks a broken (or just-fixed) frontend and produces the confusing
|
|
121
|
+
// "normal refresh is broken but hard refresh works" split. Cache is a pure offline fallback.
|
|
119
122
|
|
|
120
|
-
var CACHE = 'bloby-
|
|
123
|
+
var CACHE = 'bloby-v17';
|
|
121
124
|
var HASHED_RE = new RegExp('/assets/.+-[a-zA-Z0-9]{6,}[.](js|css)$');
|
|
122
125
|
|
|
123
126
|
// Precache the HTML shell on install so the cache is never empty.
|
|
@@ -182,18 +185,11 @@ self.addEventListener('fetch', function(event) {
|
|
|
182
185
|
if (request.mode === 'navigate') {
|
|
183
186
|
console.log('[SW] navigate →', url.pathname);
|
|
184
187
|
if (url.pathname === '/' || url.pathname === '/index.html') {
|
|
188
|
+
// Network-first: always fetch the live dashboard; only fall back to cache when offline.
|
|
185
189
|
event.respondWith(caches.open(CACHE).then(function(c) {
|
|
186
|
-
return
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
.then(function(r) {
|
|
190
|
-
console.log('[SW] network response for /:', r.status);
|
|
191
|
-
if (r.ok) c.put('/', r.clone());
|
|
192
|
-
return r;
|
|
193
|
-
})
|
|
194
|
-
.catch(function(err) { console.warn('[SW] network failed, using cache:', err.message); return hit; });
|
|
195
|
-
return hit || net;
|
|
196
|
-
});
|
|
190
|
+
return fetch(request)
|
|
191
|
+
.then(function(r) { if (r.ok) c.put('/', r.clone()); return r; })
|
|
192
|
+
.catch(function(err) { console.warn('[SW] / network failed, using cache:', err.message); return c.match('/'); });
|
|
197
193
|
}));
|
|
198
194
|
return;
|
|
199
195
|
}
|
|
@@ -202,14 +198,11 @@ self.addEventListener('fetch', function(event) {
|
|
|
202
198
|
return;
|
|
203
199
|
}
|
|
204
200
|
|
|
205
|
-
// Everything else →
|
|
201
|
+
// Everything else (JS/CSS modules, static assets) → network-first, cache as offline fallback.
|
|
206
202
|
event.respondWith(caches.open(CACHE).then(function(c) {
|
|
207
|
-
return
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
.catch(function() { return hit; });
|
|
211
|
-
return hit || net;
|
|
212
|
-
});
|
|
203
|
+
return fetch(request)
|
|
204
|
+
.then(function(r) { if (r.ok) c.put(request, r.clone()); return r; })
|
|
205
|
+
.catch(function() { return c.match(request); });
|
|
213
206
|
}));
|
|
214
207
|
});
|
|
215
208
|
|
|
@@ -82,22 +82,42 @@
|
|
|
82
82
|
return d;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
var start = Date.now();
|
|
86
|
+
var sawError = false;
|
|
87
|
+
|
|
88
|
+
function appLooksBroken() {
|
|
89
|
+
// 1. Compile/import error → Vite renders <vite-error-overlay>. Immediate, reliable.
|
|
90
|
+
if (document.querySelector('vite-error-overlay')) return true;
|
|
91
|
+
var root = document.getElementById('root');
|
|
92
|
+
if (!root) return false; // not the dashboard shell — don't interfere
|
|
93
|
+
// Healthy = React rendered real content into #root (and it isn't the workspace template's
|
|
94
|
+
// own "Your app crashed" fallback, which we supersede with the friendly screen).
|
|
95
|
+
var mounted = root.children.length > 0 && (root.textContent || '').indexOf('Your app crashed') === -1;
|
|
96
|
+
if (mounted) return false;
|
|
97
|
+
// Not mounted: if we already saw a hard error, judge immediately; otherwise give a cold
|
|
98
|
+
// load a generous grace window before declaring a black screen (avoids false positives).
|
|
99
|
+
if (sawError) return true;
|
|
100
|
+
return (Date.now() - start) > 8000;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function evaluate() {
|
|
104
|
+
if (appLooksBroken()) {
|
|
88
105
|
var err = readViteError();
|
|
89
106
|
if (err) lastErr = err;
|
|
90
107
|
if (!overlay && !dismissed) overlay = buildOverlay();
|
|
91
108
|
} else {
|
|
92
|
-
//
|
|
109
|
+
// App healthy (or recovered) — drop our overlay and re-arm for the next episode.
|
|
93
110
|
dismissed = false;
|
|
94
111
|
if (overlay) { overlay.remove(); overlay = null; }
|
|
95
112
|
}
|
|
96
113
|
}
|
|
97
114
|
|
|
98
|
-
// Vite appends <vite-error-overlay> directly under <body
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
115
|
+
// Triggers: Vite appends <vite-error-overlay> directly under <body> (childList catches it
|
|
116
|
+
// instantly + cheaply); a steady tick handles the timeout-based black-screen detection and
|
|
117
|
+
// recovery; window errors flip sawError so real load failures surface fast.
|
|
118
|
+
new MutationObserver(evaluate).observe(document.body, { childList: true });
|
|
119
|
+
setInterval(evaluate, 1500);
|
|
120
|
+
window.addEventListener('error', function () { sawError = true; evaluate(); });
|
|
121
|
+
window.addEventListener('unhandledrejection', function () { sawError = true; evaluate(); });
|
|
122
|
+
evaluate();
|
|
103
123
|
})();
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
// Service worker — app-shell caching + push notifications
|
|
2
2
|
// Caching strategy:
|
|
3
3
|
// Hashed assets (/assets/*-AbCd12.js) → cache-first (immutable)
|
|
4
|
-
// Navigation (HTML) →
|
|
5
|
-
// Static assets
|
|
6
|
-
// JS/CSS modules → stale-while-revalidate
|
|
4
|
+
// Navigation (HTML) → network-first (cache = offline fallback only)
|
|
5
|
+
// Static assets / JS / CSS modules → network-first (cache = offline fallback only)
|
|
7
6
|
// API, WebSocket, Vite internals → network-only (no cache)
|
|
7
|
+
// Network-first on navigations + modules so a live-edit always shows the current app and its
|
|
8
|
+
// build errors, never a stale cached shell. Cache is a pure offline fallback. (Mirror of the
|
|
9
|
+
// supervisor's SW_JS in supervisor/index.ts — keep in sync.)
|
|
8
10
|
|
|
9
|
-
const CACHE = 'bloby-
|
|
11
|
+
const CACHE = 'bloby-v17';
|
|
10
12
|
|
|
11
13
|
// Precache the HTML shell on install so the cache is never empty.
|
|
12
14
|
// Without this, the first navigation isn't intercepted (SW wasn't
|
|
@@ -70,18 +72,11 @@ self.addEventListener('fetch', (event) => {
|
|
|
70
72
|
if (request.mode === 'navigate') {
|
|
71
73
|
console.log('[SW] navigate →', url.pathname);
|
|
72
74
|
if (url.pathname === '/' || url.pathname === '/index.html') {
|
|
75
|
+
// Network-first: always fetch the live dashboard; cache only as offline fallback.
|
|
73
76
|
event.respondWith(caches.open(CACHE).then(c =>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
.then(r => {
|
|
78
|
-
console.log('[SW] network response for /:', r.status);
|
|
79
|
-
if (r.ok) c.put('/', r.clone());
|
|
80
|
-
return r;
|
|
81
|
-
})
|
|
82
|
-
.catch(err => { console.warn('[SW] network failed, using cache:', err.message); return hit; });
|
|
83
|
-
return hit || net;
|
|
84
|
-
})
|
|
77
|
+
fetch(request)
|
|
78
|
+
.then(r => { if (r.ok) c.put('/', r.clone()); return r; })
|
|
79
|
+
.catch(err => { console.warn('[SW] / network failed, using cache:', err.message); return c.match('/'); })
|
|
85
80
|
));
|
|
86
81
|
return;
|
|
87
82
|
}
|
|
@@ -90,16 +85,12 @@ self.addEventListener('fetch', (event) => {
|
|
|
90
85
|
return;
|
|
91
86
|
}
|
|
92
87
|
|
|
93
|
-
// ── Everything else →
|
|
94
|
-
// Serves cached version instantly, refreshes cache in background.
|
|
88
|
+
// ── Everything else → network-first, cache as offline fallback ───
|
|
95
89
|
// Covers: JS/CSS modules, images, video, fonts, manifest, etc.
|
|
96
90
|
event.respondWith(caches.open(CACHE).then(c =>
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
.catch(() => hit);
|
|
101
|
-
return hit || net;
|
|
102
|
-
})
|
|
91
|
+
fetch(request)
|
|
92
|
+
.then(r => { if (r.ok) c.put(request, r.clone()); return r; })
|
|
93
|
+
.catch(() => c.match(request))
|
|
103
94
|
));
|
|
104
95
|
});
|
|
105
96
|
|