main.go raw

   1  package main
   2  
   3  import (
   4  	"common/jsbridge/idb"
   5  	"common/jsbridge/registry"
   6  	"common/jsbridge/sw"
   7  )
   8  
   9  // App Shell domain — Service Worker lifecycle, static asset caching, SSE version monitoring.
  10  
  11  const cacheName = "smesh"
  12  
  13  var appFiles = []string{
  14  	// Main app.
  15  	"/",
  16  	"/index.html",
  17  	"/$entry.mjs",
  18  	"/smesh3.mjs",
  19  	"/common_crypto_secp256k1.mjs",
  20  	"/common_crypto_sha256.mjs",
  21  	"/common_helpers.mjs",
  22  	"/common_jsbridge_dom.mjs",
  23  	"/common_jsbridge_localstorage.mjs",
  24  	"/common_jsbridge_signer.mjs",
  25  	"/common_nostr.mjs",
  26  	"/$runtime/index.mjs",
  27  	"/$runtime/runtime.mjs",
  28  	"/$runtime/goroutine.mjs",
  29  	"/$runtime/channel.mjs",
  30  	"/$runtime/builtin.mjs",
  31  	"/$runtime/types.mjs",
  32  	"/$runtime/sync.mjs",
  33  	"/$runtime/dom.mjs",
  34  	"/$runtime/localstorage.mjs",
  35  	"/$runtime/signer.mjs",
  36  	"/$wasm/secp256k1.mjs",
  37  	"/$wasm/secp256k1.wasm",
  38  	"/smesh-loader.svg",
  39  	// Service worker — core.
  40  	"/$sw/$entry.mjs",
  41  	"/$sw/sw.mjs",
  42  	"/$sw/common_jsbridge_sw.mjs",
  43  	"/$sw/common_jsbridge_idb.mjs",
  44  	"/$sw/common_jsbridge_registry.mjs",
  45  	"/$sw/common_crypto_secp256k1.mjs",
  46  	"/$sw/common_crypto_sha256.mjs",
  47  	"/$sw/common_helpers.mjs",
  48  	"/$sw/common_nostr.mjs",
  49  	"/$sw/common_relay.mjs",
  50  	"/$sw/$runtime/index.mjs",
  51  	"/$sw/$runtime/runtime.mjs",
  52  	"/$sw/$runtime/goroutine.mjs",
  53  	"/$sw/$runtime/channel.mjs",
  54  	"/$sw/$runtime/builtin.mjs",
  55  	"/$sw/$runtime/types.mjs",
  56  	"/$sw/$runtime/sync.mjs",
  57  	"/$sw/$runtime/sw.mjs",
  58  	"/$sw/$runtime/idb.mjs",
  59  	"/$sw/$runtime/subtle.mjs",
  60  	"/$sw/$runtime/crypto.mjs",
  61  	"/$sw/$runtime/registry.mjs",
  62  	// Service worker — deferred extensions (cached but loaded on demand).
  63  	"/$sw/ext/swcrypto/$entry.mjs",
  64  	"/$sw/ext/swcrypto/swcrypto.mjs",
  65  	"/$sw/ext/swcrypto/common_crypto_secp256k1.mjs",
  66  	"/$sw/ext/swcrypto/common_crypto_sha256.mjs",
  67  	"/$sw/ext/swcrypto/common_crypto_chacha20.mjs",
  68  	"/$sw/ext/swcrypto/common_crypto_hmac.mjs",
  69  	"/$sw/ext/swcrypto/common_crypto_hkdf.mjs",
  70  	"/$sw/ext/swcrypto/common_crypto_nip44.mjs",
  71  	"/$sw/ext/swcrypto/common_crypto_nip04.mjs",
  72  	"/$sw/ext/swcrypto/common_helpers.mjs",
  73  	"/$sw/ext/swcrypto/common_nostr.mjs",
  74  	"/$sw/ext/swcrypto/common_jsbridge_subtle.mjs",
  75  	"/$sw/ext/swcrypto/common_jsbridge_sw.mjs",
  76  	"/$sw/ext/swcrypto/common_jsbridge_registry.mjs",
  77  	"/$sw/ext/swcrypto/$runtime/index.mjs",
  78  	"/$sw/ext/swcrypto/$runtime/runtime.mjs",
  79  	"/$sw/ext/swcrypto/$runtime/goroutine.mjs",
  80  	"/$sw/ext/swcrypto/$runtime/channel.mjs",
  81  	"/$sw/ext/swcrypto/$runtime/builtin.mjs",
  82  	"/$sw/ext/swcrypto/$runtime/types.mjs",
  83  	"/$sw/ext/swcrypto/$runtime/sync.mjs",
  84  	"/$sw/ext/swcrypto/$runtime/sw.mjs",
  85  	"/$sw/ext/swcrypto/$runtime/subtle.mjs",
  86  	"/$sw/ext/swcrypto/$runtime/crypto.mjs",
  87  	"/$sw/ext/swcrypto/$runtime/registry.mjs",
  88  	"/$sw/ext/swmarmot/$entry.mjs",
  89  	"/$sw/ext/swmarmot/swmarmot.mjs",
  90  	"/$sw/ext/swmarmot/common_crypto_secp256k1.mjs",
  91  	"/$sw/ext/swmarmot/common_crypto_sha256.mjs",
  92  	"/$sw/ext/swmarmot/common_helpers.mjs",
  93  	"/$sw/ext/swmarmot/common_jsbridge_subtle.mjs",
  94  	"/$sw/ext/swmarmot/common_jsbridge_sw.mjs",
  95  	"/$sw/ext/swmarmot/common_jsbridge_ws.mjs",
  96  	"/$sw/ext/swmarmot/common_jsbridge_registry.mjs",
  97  	"/$sw/ext/swmarmot/$runtime/index.mjs",
  98  	"/$sw/ext/swmarmot/$runtime/runtime.mjs",
  99  	"/$sw/ext/swmarmot/$runtime/goroutine.mjs",
 100  	"/$sw/ext/swmarmot/$runtime/channel.mjs",
 101  	"/$sw/ext/swmarmot/$runtime/builtin.mjs",
 102  	"/$sw/ext/swmarmot/$runtime/types.mjs",
 103  	"/$sw/ext/swmarmot/$runtime/sync.mjs",
 104  	"/$sw/ext/swmarmot/$runtime/sw.mjs",
 105  	"/$sw/ext/swmarmot/$runtime/subtle.mjs",
 106  	"/$sw/ext/swmarmot/$runtime/ws.mjs",
 107  	"/$sw/ext/swmarmot/$runtime/crypto.mjs",
 108  	"/$sw/ext/swmarmot/$runtime/registry.mjs",
 109  }
 110  
 111  var currentVersion string
 112  
 113  func main() {
 114  	registerCoreHooks()
 115  	initRouter()
 116  	initRelayProxy()
 117  	initSharedState()
 118  	idb.Open(func() {})
 119  	sw.OnInstall(onInstall)
 120  	sw.OnActivate(onActivate)
 121  	sw.OnFetch(onFetch)
 122  	sw.OnMessage(onMessage)
 123  }
 124  
 125  func registerCoreHooks() {
 126  	registry.OnSaveDMRecord(routerSaveDMRecordJSON)
 127  	registry.OnBroadcastToClients(broadcastToClients)
 128  	registry.OnSendToClient(sendToClient)
 129  }
 130  
 131  func onInstall(event sw.Event) {
 132  	sw.WaitUntil(event, func(done func()) {
 133  		sw.Fetch("/__version", func(resp sw.Response, ok bool) {
 134  			if ok {
 135  				// TODO: read response body
 136  			}
 137  			sw.CacheOpen(cacheName, func(cache sw.Cache) {
 138  				sw.CacheAddAll(cache, appFiles, func() {
 139  					sw.SkipWaiting()
 140  					done()
 141  				})
 142  			})
 143  		})
 144  	})
 145  }
 146  
 147  func onActivate(event sw.Event) {
 148  	sw.WaitUntil(event, func(done func()) {
 149  		sw.ClaimClients(func() {
 150  			connectSSE()
 151  			done()
 152  		})
 153  	})
 154  }
 155  
 156  func onFetch(event sw.Event) {
 157  	path := sw.GetRequestPath(event)
 158  	if path == "/__sse" || path == "/__version" {
 159  		return
 160  	}
 161  	if len(path) > 4 && path[:5] == "/$sw/" {
 162  		return
 163  	}
 164  	sw.RespondWithCacheFirst(event)
 165  }
 166  
 167  func onMessage(event sw.Event) {
 168  	data := sw.GetMessageData(event)
 169  	clientID := sw.GetMessageClientID(event)
 170  
 171  	if data == "activate-update" {
 172  		refreshAndReload()
 173  		return
 174  	}
 175  
 176  	w := newMW(data)
 177  	msgType := w.str()
 178  
 179  	switch msgType {
 180  	case "SKIP_WAITING":
 181  		sw.SkipWaiting()
 182  	default:
 183  		routeMessage(clientID, &w, msgType)
 184  	}
 185  }
 186  
 187  func connectSSE() {
 188  	sw.SSEConnect("/__sse", func(data string) {
 189  		v := data
 190  		if currentVersion == "" {
 191  			currentVersion = v
 192  			return
 193  		}
 194  		if v != currentVersion {
 195  			currentVersion = v
 196  			notifyUpdate()
 197  		}
 198  	})
 199  }
 200  
 201  func notifyUpdate() {
 202  	sw.MatchClients(func(client sw.Client) {
 203  		sw.PostMessage(client, "update-available")
 204  	})
 205  }
 206  
 207  func refreshAndReload() {
 208  	sw.CacheOpen(cacheName, func(cache sw.Cache) {
 209  		refreshFiles(cache, 0, func() {
 210  			sw.MatchClients(func(client sw.Client) {
 211  				sw.Navigate(client, "")
 212  			})
 213  		})
 214  	})
 215  }
 216  
 217  func refreshFiles(cache sw.Cache, idx int, done func()) {
 218  	if idx >= len(appFiles) {
 219  		done()
 220  		return
 221  	}
 222  	file := appFiles[idx]
 223  	bust := file + "?v=" + currentVersion
 224  	sw.Fetch(bust, func(resp sw.Response, ok bool) {
 225  		if ok && sw.ResponseOK(resp) {
 226  			sw.CachePut(cache, file, resp, func() {
 227  				refreshFiles(cache, idx+1, done)
 228  			})
 229  		} else {
 230  			refreshFiles(cache, idx+1, done)
 231  		}
 232  	})
 233  }
 234