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