Follow-up fix for deployed PWA service-worker path and type-fetch 404 noise (#1583)
* Fix ManifoldCAD PWA paths and manifest metadata * fix(editor): handle service worker registration failures and stabilize PWA path resolution * fix(editor): handle service worker registration failures and stabilize PWA path resolution * style(editor): align editor.js with clang-format * fix(editor): clear type fetch indicator after resolution * fix(editor): improve type indicator visibility and harden service worker - Declare autoTypings with let before AutoTypings.create() to avoid TDZ issues when callbacks fire during creation - Enforce a minimum 350ms display window for the type-fetch indicator so it is visible even when resolution completes very quickly - Track resolveEventUntil timestamp so a ResolveNewImports event alone is enough to keep the indicator on even if isResolving clears early - Use console.log (not console.debug) for indicator ON/OFF transitions so they appear without enabling Verbose in DevTools service-worker.js: - Guard fetch handler to only intercept GET requests so POST/PUT etc. pass through without error - Wrap network fetch in try/catch; fall back to cached response on network failure instead of propagating an uncaught rejection - Add skipWaiting() on install and clients.claim() on activate for immediate worker takeover on update * style(editor): apply clang-format to editor.js and service-worker.js * refactor(editor): simplify type indicator for review split
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
import esbuildWasmUrl from 'esbuild-wasm/esbuild.wasm?url';
|
||||
import ManifoldWorker from 'manifold-3d/lib/worker.bundled.js?worker';
|
||||
import manifoldWasmUrl from 'manifold-3d/manifold.wasm?url';
|
||||
import {AutoTypings, LocalStorageCache} from 'monaco-editor-auto-typings';
|
||||
import {AutoTypings, JsDelivrSourceResolver, LocalStorageCache} from 'monaco-editor-auto-typings';
|
||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.main';
|
||||
// '?worker' is vite convention to load a module as a web worker.
|
||||
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
|
||||
@@ -28,9 +28,13 @@ const CODE_START = '<code>';
|
||||
const exampleFunctions = self.examples;
|
||||
|
||||
if (navigator.serviceWorker) {
|
||||
// Resolve against the current module URL so registration works on subpaths.
|
||||
const serviceWorkerUrl = new URL('./service-worker.js', import.meta.url);
|
||||
navigator.serviceWorker.register(serviceWorkerUrl, {scope: './'});
|
||||
// Resolve against the current page URL so production asset paths don't
|
||||
// redirect registration into /assets.
|
||||
const serviceWorkerUrl = new URL('./service-worker.js', window.location.href);
|
||||
navigator.serviceWorker.register(serviceWorkerUrl, {scope: './'})
|
||||
.catch(error => {
|
||||
console.error('Service worker registration failed:', error);
|
||||
});
|
||||
}
|
||||
|
||||
let editor = undefined;
|
||||
@@ -318,15 +322,20 @@ async function createEditor() {
|
||||
});
|
||||
|
||||
// Make sure `manifold-3d/manifoldCAD` types are available for import.
|
||||
const manifoldCADTypesUrl =
|
||||
new URL('./manifoldCAD.d.ts', window.location.href);
|
||||
const manifoldCADGlobalsTypesUrl =
|
||||
new URL('./manifoldCADGlobals.d.ts', window.location.href);
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
await (await fetch('/manifoldCAD.d.ts')).text(),
|
||||
await (await fetch(manifoldCADTypesUrl)).text(),
|
||||
'inmemory://model/node_modules/manifold-3d/manifoldCAD.d.ts');
|
||||
|
||||
// Types in the global namespace for top-level scripts.
|
||||
// This could be improved in the future. API-Extractor intentionally doesn't
|
||||
// global variables, so another tool may be a better fit.
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
(await (await fetch('/manifoldCADGlobals.d.ts')).text())
|
||||
(await (await fetch(manifoldCADGlobalsTypesUrl)).text())
|
||||
.replace(/^export /gm, ''));
|
||||
|
||||
// Load up all scripts so that monaco can check types of multi-file models.
|
||||
@@ -338,13 +347,28 @@ async function createEditor() {
|
||||
const typeIndicator = document.getElementById('type-indicator');
|
||||
self.window.typecache = new LocalStorageCache();
|
||||
|
||||
// We inject manifold-3d typings locally above, so avoid extra CDN probes for
|
||||
// that package path. This prevents noisy 404s in production logs.
|
||||
const jsDelivrResolver = new JsDelivrSourceResolver();
|
||||
const sourceResolver = {
|
||||
resolvePackageJson: async (packageName, version, subPath) => {
|
||||
if (packageName === 'manifold-3d') return '';
|
||||
return jsDelivrResolver.resolvePackageJson(packageName, version, subPath);
|
||||
},
|
||||
resolveSourceFile: async (packageName, version, path) => {
|
||||
if (packageName === 'manifold-3d') return '';
|
||||
return jsDelivrResolver.resolveSourceFile(packageName, version, path);
|
||||
}
|
||||
};
|
||||
|
||||
const autoTypings = await AutoTypings.create(editor, {
|
||||
sourceResolver,
|
||||
sourceCache: self.window.typecache,
|
||||
onError: e => {
|
||||
console.error(e);
|
||||
if (typeIndicator) typeIndicator.textContent = '';
|
||||
},
|
||||
onUpdate: (update, text) => {
|
||||
onUpdate: (_, text) => {
|
||||
if (typeIndicator) typeIndicator.textContent = 'Fetching types...';
|
||||
console.debug(text);
|
||||
},
|
||||
|
||||
@@ -15,27 +15,49 @@
|
||||
// Increment version when updating CDN URLs to clean up cache.
|
||||
const cacheName = 'manifoldCAD-cache-v1';
|
||||
|
||||
self.addEventListener('install', e => {
|
||||
e.waitUntil(self.skipWaiting());
|
||||
});
|
||||
|
||||
self.addEventListener('activate', e => {
|
||||
e.waitUntil(caches.keys().then((keyList) => {
|
||||
return Promise.all(keyList.map((key) => {
|
||||
if (key !== cacheName) {
|
||||
return caches.delete(key);
|
||||
}
|
||||
}));
|
||||
}));
|
||||
e.waitUntil(caches.keys()
|
||||
.then((keyList) => {
|
||||
return Promise.all(keyList.map((key) => {
|
||||
if (key !== cacheName) {
|
||||
return caches.delete(key);
|
||||
}
|
||||
}));
|
||||
})
|
||||
.then(() => self.clients.claim()));
|
||||
});
|
||||
|
||||
// Serves from cache, then updates the cache in the background from the network,
|
||||
// if available. Update available on refresh.
|
||||
self.addEventListener(
|
||||
'fetch',
|
||||
e => {e.respondWith(caches.match(e.request).then(cachedResponse => {
|
||||
const networkFetch = fetch(e.request).then(response => {
|
||||
const clone = response.clone();
|
||||
caches.open(cacheName).then(cache => {
|
||||
cache.put(e.request, clone);
|
||||
});
|
||||
return response;
|
||||
});
|
||||
return cachedResponse || networkFetch;
|
||||
}))});
|
||||
self.addEventListener('fetch', e => {
|
||||
if (e.request.method !== 'GET') {
|
||||
return;
|
||||
}
|
||||
|
||||
e.respondWith((async () => {
|
||||
const cachedResponse = await caches.match(e.request);
|
||||
|
||||
try {
|
||||
const response = await fetch(e.request);
|
||||
|
||||
// Only cache successful basic and opaque responses.
|
||||
if (response.ok || response.type === 'opaque') {
|
||||
const cache = await caches.open(cacheName);
|
||||
await cache.put(e.request, response.clone());
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
console.debug('Service worker fetch failed:', e.request.url, error);
|
||||
return Response.error();
|
||||
}
|
||||
})());
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user