wasm: remove unnecessary manifest parsing

Kim committed Nov 17, 2025 at 11:02 UTC a9c5f3b5fac5427c0d89428ee9df7f627a7578a5
2 files changed +57 -88
cmd/relay-server/frontend.go
+2 -69
@@ -3,7 +3,6 @@ package main
3 import (
4 "embed"
5 "encoding/json"
6 - "fmt"
6 "net/http"
7 pathpkg "path"
8 "strconv"
@@ -515,69 +514,6 @@ func serveDynamicServiceWorker(w http.ResponseWriter, r *http.Request) {
514 return
515 }
516
518 - // Find the content-addressed WASM file
519 - wasmCacheMu.RLock()
520 - var wasmHash string
521 - var wasmFile string
522 - for filename, entry := range wasmCache {
523 - wasmHash = entry.hash
524 - wasmFile = filename
525 - break
526 - }
527 - wasmCacheMu.RUnlock()
528 -
529 - // Fallback: scan embedded WASM directory if cache is empty
530 - if wasmHash == "" {
531 - entries, err := wasmFS.ReadDir("dist")
532 - if err == nil {
533 - for _, entry := range entries {
534 - if entry.IsDir() {
535 - continue
536 - }
537 - name := entry.Name()
538 - if strings.HasSuffix(name, ".wasm.br") && len(name) == 72 {
539 - hash := strings.TrimSuffix(name, ".wasm.br")
540 - if isHexString(hash) && len(hash) == 64 {
541 - wasmHash = hash
542 - wasmFile = hash + ".wasm"
543 - break
544 - }
545 - }
546 - }
547 - }
548 - }
549 -
550 - // Generate WASM URL
551 - wasmURL := portalUIURL + "/frontend/" + wasmFile
552 -
553 - // Create manifest object
554 - manifestData := map[string]string{
555 - "wasmFile": wasmFile,
556 - "wasmUrl": wasmURL,
557 - "hash": wasmHash,
558 - "bootstraps": bootstrapURIs,
559 - }
560 -
561 - // Convert manifest to JSON string
562 - manifestJSON, err := json.Marshal(manifestData)
563 - if err != nil {
564 - log.Error().Err(err).Msg("Failed to marshal manifest for service worker")
565 - http.Error(w, "Internal server error", http.StatusInternalServerError)
566 - return
567 - }
568 -
569 - // Replace placeholders
570 - result := string(content)
571 - result = strings.ReplaceAll(result, "<PORTAL_UI_URL>", portalUIURL)
572 - result = strings.ReplaceAll(result, "\"<WASM_MANIFEST>\"", string(manifestJSON))
573 -
574 - // Inject __BOOTSTRAP_SERVERS__ as a global variable in service worker
575 - bootstrapServersLine := fmt.Sprintf("self.__BOOTSTRAP_SERVERS__ = %q;\n", bootstrapURIs)
576 -
577 - // Insert after the wasmManifest line (after line that sets wasmManifest)
578 - manifestLine := "let wasmManifest = JSON.parse(wasmManifestString);"
579 - result = strings.Replace(result, manifestLine, manifestLine+"\n"+bootstrapServersLine, 1)
580 -
517 // Set headers for no caching
518 w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
519 w.Header().Set("Pragma", "no-cache")
@@ -586,10 +522,7 @@ func serveDynamicServiceWorker(w http.ResponseWriter, r *http.Request) {
522
523 // Send response
524 w.WriteHeader(http.StatusOK)
589 - w.Write([]byte(result))
525 + w.Write(content)
526
591 - log.Debug().
592 - Str("portalUIURL", portalUIURL).
593 - Str("wasmHash", wasmHash).
594 - Msg("Served dynamic service-worker.js")
527 + log.Debug().Msg("Served service-worker.js")
528 }
cmd/webclient/service-worker.js
+55 -19
@@ -1,7 +1,7 @@
1 //const wasm_exec_URL = "https://cdn.jsdelivr.net/gh/golang/go@go1.25.3/lib/wasm/wasm_exec.js";
2 -let BASE_PATH = "<PORTAL_UI_URL>";
3 -let wasmManifestString = '"<WASM_MANIFEST>"';
4 -let wasmManifest;
2 +const BASE_PATH = self.location.origin || "";
3 +let wasmManifest = null;
4 +let wasmManifestPromise = null;
5
6 // Debug mode detection (disable verbose logging in production)
7 const DEBUG_MODE = self.location.hostname === 'localhost' ||
@@ -14,19 +14,52 @@ function debugLog(...args) {
14 }
15 }
16
17 -// Parse manifest with error handling
18 -try {
19 - wasmManifest = JSON.parse(wasmManifestString);
20 - debugLog("[SW] Manifest parsed successfully:", wasmManifest);
21 -} catch (error) {
22 - console.error("[SW] Failed to parse WASM manifest:", error);
23 - console.error("[SW] Manifest string:", wasmManifestString);
24 - // Use fallback manifest
25 - wasmManifest = {
26 - wasmFile: "main.wasm",
27 - wasmUrl: null
28 - };
29 - console.warn("[SW] Using fallback manifest:", wasmManifest);
17 +// Load manifest from backend (decouples SW from Go template)
18 +async function loadManifest() {
19 + if (wasmManifest) {
20 + return wasmManifest;
21 + }
22 +
23 + if (wasmManifestPromise) {
24 + return wasmManifestPromise;
25 + }
26 +
27 + wasmManifestPromise = (async () => {
28 + try {
29 + debugLog("[SW] Fetching WASM manifest...");
30 + const response = await fetch("/frontend/manifest.json", { cache: "no-cache" });
31 +
32 + if (!response.ok) {
33 + throw new Error(`HTTP ${response.status}: ${response.statusText}`);
34 + }
35 +
36 + const manifest = await response.json();
37 + wasmManifest = manifest;
38 +
39 + // Expose bootstrap servers to WASM runtime (service worker global)
40 + if (manifest.bootstraps) {
41 + self.__BOOTSTRAP_SERVERS__ = manifest.bootstraps;
42 + debugLog("[SW] Bootstraps loaded from manifest:", manifest.bootstraps);
43 + }
44 +
45 + debugLog("[SW] Manifest loaded successfully:", manifest);
46 + return manifest;
47 + } catch (error) {
48 + console.error("[SW] Failed to load WASM manifest:", error);
49 +
50 + // Fallback manifest
51 + wasmManifest = {
52 + wasmFile: "main.wasm",
53 + wasmUrl: null
54 + };
55 + console.warn("[SW] Using fallback manifest:", wasmManifest);
56 + return wasmManifest;
57 + } finally {
58 + wasmManifestPromise = null;
59 + }
60 + })();
61 +
62 + return wasmManifestPromise;
63 }
64
65 let wasm_exec_URL = BASE_PATH + "/frontend/wasm_exec.js";
@@ -388,12 +421,15 @@ async function runWASM() {
421 }
422
423 try {
424 + // Ensure manifest is loaded
425 + const manifest = await loadManifest();
426 +
427 // Determine WASM URL from manifest
428 let wasm_URL;
393 - if (wasmManifest.wasmUrl && new URL(wasmManifest.wasmUrl).protocol !== "http:") {
394 - wasm_URL = wasmManifest.wasmUrl;
429 + if (manifest.wasmUrl && new URL(manifest.wasmUrl).protocol !== "http:") {
430 + wasm_URL = manifest.wasmUrl;
431 } else {
396 - wasm_URL = `/frontend/${wasmManifest.wasmFile}`;
432 + wasm_URL = `/frontend/${manifest.wasmFile}`;
433 }
434 debugLog("[SW] WASM URL:", wasm_URL);
435