-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsw.js
More file actions
160 lines (147 loc) · 5.17 KB
/
sw.js
File metadata and controls
160 lines (147 loc) · 5.17 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
const CACHE_NAME = 'objectstore-v3.2';
const API_CACHE = 'objectstore-api-v3.2';
const ASSET_CACHE = 'objectstore-assets-v3.2';
const PRECACHE_URLS = [
'./',
'./js/nav.js',
'./css/tier-system.css',
'./favicon.svg'
];
const API_ENDPOINTS = [
// Core game data
'./api/v1/weapons.json',
'./api/v1/armor.json',
'./api/v1/materials.json',
'./api/v1/consumables.json',
'./api/v1/skills.json',
'./api/v1/weaponSkills.json',
'./api/v1/enemies.json',
'./api/v1/bosses.json',
'./api/v1/classes.json',
'./api/v1/races.json',
'./api/v1/factions.json',
'./api/v1/attributes.json',
'./api/v1/professions.json',
'./api/v1/sprites.json',
'./api/v1/sprites2d.json',
'./api/v1/effectSprites.json',
'./api/v1/abilityEffects.json',
'./api/v1/factionUnits.json',
// v3.0.0 — Game data extraction
'./api/v1/quests.json',
'./api/v1/missions.json',
'./api/v1/skillTrees.json',
'./api/v1/equipment.json',
'./api/v1/enemyTemplates.json',
'./api/v1/worldMap.json',
'./api/v1/dialogue.json',
'./api/v1/cutscenes.json',
'./api/v1/regions.json',
'./api/v1/battleFormations.json',
'./api/v1/randomEvents.json',
'./api/v1/lore.json',
// v3.0.0 — Asset registries
'./api/v1/audio.json',
'./api/v1/video.json',
'./api/v1/heroes.json',
'./api/v1/models3d.json',
// v3.0.0 — Additional systems
'./api/v1/ai.json',
'./api/v1/animations.json',
'./api/v1/asset-registry.json',
'./api/v1/controllers.json',
'./api/v1/ecs.json',
'./api/v1/nodeUpgrades.json',
'./api/v1/rendering.json',
'./api/v1/rtsModels.json',
'./api/v1/spriteMaps.json',
'./api/v1/terrain.json',
'./api/v1/tileMaps.json'
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME).then(cache => cache.addAll(PRECACHE_URLS))
.then(() => caches.open(API_CACHE).then(cache => {
// Best-effort precache API data
return Promise.allSettled(API_ENDPOINTS.map(url => cache.add(url)));
}))
.then(() => self.skipWaiting())
);
});
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(keys =>
Promise.all(keys.filter(k => k !== CACHE_NAME && k !== API_CACHE && k !== ASSET_CACHE).map(k => caches.delete(k)))
).then(() => self.clients.claim())
);
});
self.addEventListener('fetch', event => {
// Only handle GET requests; let the browser deal with everything else.
if (event.request.method !== 'GET') return;
const url = new URL(event.request.url);
// Ignore non-http(s) requests (chrome-extension://, blob:, data:, etc.)
if (!event.request.url.startsWith('http')) return;
// CRITICAL: never intercept cross-origin requests. The Cache API rejects
// opaque cross-origin responses with a TypeError on cache.put(), which would
// surface as "Failed to fetch" for every R2/CDN image. Let the browser
// handle off-origin resources (e.g. assets.grudge-studio.com,
// objectstore.grudge-studio.com, fonts.gstatic.com) directly.
if (url.origin !== self.location.origin) return;
// API JSON: stale-while-revalidate
if (url.pathname.includes('/api/v1/') && url.pathname.endsWith('.json')) {
event.respondWith(
caches.open(API_CACHE).then(cache =>
cache.match(event.request).then(cached => {
const fetchPromise = fetch(event.request).then(response => {
if (response.ok) cache.put(event.request, response.clone()).catch(() => {});
return response;
}).catch(() => cached);
return cached || fetchPromise;
})
)
);
return;
}
// Sprite/icon assets: cache-first (same-origin only).
// Validates Content-Type so a Vercel/Pages SPA-fallback HTML 200 can never
// be cached as an image and silently break the page.
if (/\.(png|jpg|jpeg|gif|webp|svg)$/i.test(url.pathname)) {
event.respondWith(
caches.open(ASSET_CACHE).then(cache =>
cache.match(event.request).then(cached => {
if (cached) return cached;
return fetch(event.request).then(response => {
const ct = response.headers.get('content-type') || '';
const looksImage = ct.startsWith('image/') || ct === '' || /\.svg$/i.test(url.pathname);
if (response.ok && looksImage) {
cache.put(event.request, response.clone()).catch(() => {});
}
return response;
}).catch(() => cached || Response.error());
})
)
);
return;
}
// HTML pages: network-first
if (url.pathname.endsWith('.html') || url.pathname.endsWith('/')) {
event.respondWith(
fetch(event.request).then(response => {
const clone = response.clone();
caches.open(CACHE_NAME).then(cache => cache.put(event.request, clone).catch(() => {}));
return response;
}).catch(() => caches.match(event.request))
);
return;
}
// Default: cache-first, then network
event.respondWith(
caches.match(event.request).then(cached => cached || fetch(event.request).catch(() => Response.error()))
);
});
// Allow the page to instruct the SW to skip waiting (used after deploys).
self.addEventListener('message', event => {
if (event.data === 'SKIP_WAITING' || event.data?.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});