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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bloby-bot",
3
- "version": "0.53.6",
3
+ "version": "0.53.7",
4
4
  "releaseNotes": [
5
5
  "1. New Morphy animation system: config-driven sprites loaded from /morphy/*.json",
6
6
  "2. Swapped teleporting (splash) and headphones (bubble + chat) to the new format",
@@ -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) → stale-while-revalidate (precached on install)
116
- // Static assets (img/video/fonts) stale-while-revalidate
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-v16';
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 c.match('/').then(function(hit) {
187
- console.log('[SW] cache hit for /:', !!hit);
188
- var net = fetch(request)
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 → stale-while-revalidate
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 c.match(request).then(function(hit) {
208
- var net = fetch(request)
209
- .then(function(r) { if (r.ok) c.put(request, r.clone()); return r; })
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
- function sync() {
86
- var ov = document.querySelector('vite-error-overlay');
87
- if (ov) {
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
- // Error cleared (agent fixed it) — drop our overlay and re-arm for the next episode.
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>; observing body's childList is enough
99
- // and far lighter than a full-document subtree observer.
100
- var obs = new MutationObserver(sync);
101
- obs.observe(document.body, { childList: true });
102
- sync();
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) → stale-while-revalidate (precached on install)
5
- // Static assets (img/video/fonts) stale-while-revalidate
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-v7';
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
- c.match('/').then(hit => {
75
- console.log('[SW] cache hit for /:', !!hit);
76
- const net = fetch(request)
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 → stale-while-revalidate ────────────────────
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
- c.match(request).then(hit => {
98
- const net = fetch(request)
99
- .then(r => { if (r.ok) c.put(request, r.clone()); return r; })
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