-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsw.js
More file actions
223 lines (213 loc) · 6.89 KB
/
Copy pathsw.js
File metadata and controls
223 lines (213 loc) · 6.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
// roughlogic service worker.
// Cache-first for the application shell; cache-on-first-fetch for data shards.
// The shell is an ATOMIC, version-consistent snapshot: every asset is
// precached together on install under a build-hash-keyed cache, and reads
// only ever come from that one cache. A new deploy bumps the build hash,
// which precaches a fresh snapshot and (via skipWaiting/clients.claim)
// serves it on the next reload. Old caches are deleted on activation.
//
// Do NOT switch the shell to stale-while-revalidate: SWR revalidates each
// asset with an independent background fetch into the shared cache, so the
// completions race and a reload can pair a fresh index.html with a stale
// app.js - silently breaking the home search/picker. Atomicity matters more
// than shaving one reload off an unchanged-hash refresh.
const BUILD_HASH = "dev-0004";
const SHELL_CACHE = "roughlogic-shell-" + BUILD_HASH;
const DATA_CACHE = "roughlogic-data-" + BUILD_HASH;
const SHELL_ASSETS = [
"./",
"./index.html",
"./styles.css",
"./app.js",
"./tools-data.js",
"./pure-math.js",
"./routing.js",
"./text-lead.js",
"./hash-state.js",
"./data-stamp.js",
"./clipboard.js",
"./ui-fields.js",
"./ui-validity.js",
"./integrity.js",
"./calc-electrical.js",
"./calc-motor.js",
"./calc-solar.js",
"./calc-powerquality.js",
"./calc-feeder.js",
"./calc-lowvoltage.js",
"./calc-metalair.js",
"./calc-gas.js",
"./calc-pipefit.js",
"./calc-plumbing.js",
"./calc-plumbingtakeoff.js",
"./calc-septic.js",
"./calc-service.js",
"./calc-drainage.js",
"./calc-hvac.js",
"./calc-refrigerant.js",
"./calc-hvacsystems.js",
"./calc-velocity.js",
"./calc-restoration.js",
"./calc-demo.js",
"./calc-construction.js",
"./calc-finish.js",
"./calc-elecdesign.js",
"./calc-hvacservice.js",
"./calc-disinfect.js",
"./calc-civil.js",
"./calc-steel.js",
"./calc-concrete.js",
"./calc-geotech.js",
"./calc-masonry.js",
"./calc-lateral.js",
"./calc-earthwork.js",
"./calc-fire.js",
"./calc-rescue.js",
"./calc-firesprinkler.js",
"./calc-cross.js",
"./calc-fab.js",
"./calc-layout.js",
"./calc-shop.js",
"./calc-references.js",
"./calc-trucking.js",
"./calc-mechanic.js",
"./calc-machining.js",
"./calc-agriculture.js",
"./calc-arborist.js",
"./calc-water.js",
"./calc-treatment.js",
"./calc-stage.js",
"./calc-kitchen.js",
"./calc-field.js",
"./calc-survey.js",
"./calc-historical.js",
"./calc-lab.js",
"./calc-accounting.js",
"./calc-realestate.js",
"./calc-edu.js",
"./calc-rigging.js",
"./v5-platform.js",
"./citations.js",
"./cost-output.js",
"./context-band.js",
"./standard-sizes.js",
"./limitation-banner.js",
"./tile-meta.js",
"./search-discovery.js",
"./manual-j-worker.js",
"./theme.js",
"./favicon.svg",
"./site.webmanifest",
];
const DATA_MANIFESTS = [
"./data/integrity.json",
"./data/electrical/manifest.json",
"./data/plumbing/manifest.json",
"./data/hvac/manifest.json",
"./data/restoration/manifest.json",
"./data/construction/manifest.json",
"./data/fire/manifest.json",
"./data/physical-constants/manifest.json",
"./data/crosswalks/manifest.json",
"./data/summaries/manifest.json",
"./data/trucking/manifest.json",
"./data/historical/manifest.json",
"./data/accounting/manifest.json",
"./data/legal/manifest.json",
"./data/lab/manifest.json",
"./data/cross/manifest.json",
"./data/field/manifest.json",
"./data/realestate/manifest.json",
"./data/search/manifest.json",
// Per-group alias shards (spec-v590 split remediation; generated by
// scripts/build-alias-shards.mjs -- the aliases.json master is the
// authoring source and is intentionally NOT precached).
"./data/search/aliases-a.json",
"./data/search/aliases-b.json",
"./data/search/aliases-c.json",
"./data/search/aliases-d.json",
"./data/search/aliases-e.json",
"./data/search/aliases-f.json",
"./data/search/aliases-g.json",
"./data/search/aliases-h.json",
"./data/search/aliases-j.json",
"./data/search/aliases-k.json",
"./data/search/aliases-l.json",
"./data/search/aliases-m.json",
"./data/search/aliases-n.json",
"./data/search/aliases-o.json",
"./data/search/aliases-p.json",
"./data/search/aliases-q.json",
"./data/search/aliases-r.json",
"./data/search/aliases-t.json",
"./data/search/aliases-x.json",
"./data/search/aliases-y.json",
"./data/search/aliases-z.json",
"./data/search/slots.json",
"./data/search/preview-map.json",
];
self.addEventListener("install", (event) => {
event.waitUntil((async () => {
const shell = await caches.open(SHELL_CACHE);
// Precache with cache: "reload" so each fetch bypasses any HTTP-cache
// entry and revalidates to the network. Defense in depth: it guarantees a
// new-hash install builds its snapshot from current bytes rather than a
// stale app.js still sitting in the browser cache, so the snapshot is
// always version-consistent (a fresh index.html never pairs with a stale
// app.js, which would silently break the home search/picker).
const reload = (url) => new Request(url, { cache: "reload" });
// Tolerate missing optional assets so installation does not fail in dev.
await Promise.all(
SHELL_ASSETS.map((url) => shell.add(reload(url)).catch(() => undefined))
);
const data = await caches.open(DATA_CACHE);
await Promise.all(
DATA_MANIFESTS.map((url) => data.add(reload(url)).catch(() => undefined))
);
await self.skipWaiting();
})());
});
self.addEventListener("activate", (event) => {
event.waitUntil((async () => {
const keys = await caches.keys();
await Promise.all(
keys
.filter((k) => k !== SHELL_CACHE && k !== DATA_CACHE)
.map((k) => caches.delete(k))
);
await self.clients.claim();
})());
});
self.addEventListener("fetch", (event) => {
const req = event.request;
if (req.method !== "GET") return;
const url = new URL(req.url);
// Same origin only; the CSP forbids cross-origin connections at runtime.
if (url.origin !== self.location.origin) return;
if (url.pathname.includes("/data/")) {
event.respondWith(cacheFirst(DATA_CACHE, req));
return;
}
// Shell: cache-first, network fallback, with offline tolerance.
event.respondWith(cacheFirst(SHELL_CACHE, req));
});
async function cacheFirst(cacheName, request) {
const cache = await caches.open(cacheName);
const cached = await cache.match(request);
if (cached) return cached;
try {
const response = await fetch(request);
if (response && response.ok) {
cache.put(request, response.clone());
}
return response;
} catch (e) {
// If the request is a navigation, serve the shell.
if (request.mode === "navigate") {
const shell = await caches.open(SHELL_CACHE);
const fallback = await shell.match("./index.html");
if (fallback) return fallback;
}
return new Response("", { status: 504, statusText: "offline" });
}
}