@soham20/smart-offline-sdk 1.0.0 ā 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/smart-offline-sw.js +181 -41
- package/src/index.js +229 -26
package/package.json
CHANGED
package/smart-offline-sw.js
CHANGED
|
@@ -20,6 +20,141 @@ let SDK_CONFIG = {
|
|
|
20
20
|
enableDetailedLogs: false
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* -------- CONSOLE LOGGING UTILITIES --------
|
|
25
|
+
* Rich, colorful debug logs for caching activity
|
|
26
|
+
*/
|
|
27
|
+
const LOG_STYLES = {
|
|
28
|
+
banner: 'background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); color: white; font-size: 14px; padding: 8px 16px; border-radius: 4px; font-weight: bold;',
|
|
29
|
+
cached: 'background: #10B981; color: white; padding: 4px 8px; border-radius: 4px; font-weight: bold;',
|
|
30
|
+
skipped: 'background: #F59E0B; color: white; padding: 4px 8px; border-radius: 4px; font-weight: bold;',
|
|
31
|
+
served: 'background: #3B82F6; color: white; padding: 4px 8px; border-radius: 4px; font-weight: bold;',
|
|
32
|
+
error: 'background: #EF4444; color: white; padding: 4px 8px; border-radius: 4px; font-weight: bold;',
|
|
33
|
+
intercept: 'background: #8B5CF6; color: white; padding: 4px 8px; border-radius: 4px; font-weight: bold;',
|
|
34
|
+
url: 'color: #6366F1; font-weight: bold;',
|
|
35
|
+
reason: 'color: #059669; font-style: italic;',
|
|
36
|
+
metadata: 'color: #6B7280;',
|
|
37
|
+
priority_high: 'background: #DC2626; color: white; padding: 2px 6px; border-radius: 3px; font-size: 11px;',
|
|
38
|
+
priority_normal: 'background: #9CA3AF; color: white; padding: 2px 6px; border-radius: 3px; font-size: 11px;',
|
|
39
|
+
type_api: 'background: #7C3AED; color: white; padding: 2px 6px; border-radius: 3px; font-size: 11px;',
|
|
40
|
+
type_page: 'background: #2563EB; color: white; padding: 2px 6px; border-radius: 3px; font-size: 11px;'
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
function formatBytes(bytes) {
|
|
44
|
+
if (bytes === 0 || !bytes) return '0 B';
|
|
45
|
+
const k = 1024;
|
|
46
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
47
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
48
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function formatTimestamp(ts) {
|
|
52
|
+
return new Date(ts).toLocaleTimeString();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Print rich cache log to console
|
|
57
|
+
*/
|
|
58
|
+
function printCacheLog(action, url, reason, details = {}) {
|
|
59
|
+
if (!SDK_CONFIG.debug) return;
|
|
60
|
+
|
|
61
|
+
const time = formatTimestamp(Date.now());
|
|
62
|
+
const shortUrl = url.length > 60 ? url.substring(0, 57) + '...' : url;
|
|
63
|
+
|
|
64
|
+
switch (action) {
|
|
65
|
+
case 'CACHED':
|
|
66
|
+
console.groupCollapsed(
|
|
67
|
+
`%cš¾ CACHED %c${shortUrl}`,
|
|
68
|
+
LOG_STYLES.cached,
|
|
69
|
+
LOG_STYLES.url
|
|
70
|
+
);
|
|
71
|
+
console.log(`%cā° Time: %c${time}`, 'font-weight: bold;', 'color: #6B7280;');
|
|
72
|
+
console.log(`%cš URL: %c${url}`, 'font-weight: bold;', LOG_STYLES.url);
|
|
73
|
+
console.log(`%cš Reason: %c${reason}`, 'font-weight: bold;', LOG_STYLES.reason);
|
|
74
|
+
if (details.type) {
|
|
75
|
+
console.log(`%cš¦ Type: %c${details.type}`, 'font-weight: bold;', details.type === 'API' ? LOG_STYLES.type_api : LOG_STYLES.type_page);
|
|
76
|
+
}
|
|
77
|
+
if (details.priority) {
|
|
78
|
+
console.log(`%cā” Priority: %c${details.priority.toUpperCase()}`, 'font-weight: bold;', details.priority === 'high' ? LOG_STYLES.priority_high : LOG_STYLES.priority_normal);
|
|
79
|
+
}
|
|
80
|
+
if (details.size) {
|
|
81
|
+
console.log(`%cš Size: %c${formatBytes(details.size)}`, 'font-weight: bold;', 'color: #6B7280;');
|
|
82
|
+
}
|
|
83
|
+
if (details.usage) {
|
|
84
|
+
console.log(`%cš Usage Stats:`, 'font-weight: bold;', details.usage);
|
|
85
|
+
}
|
|
86
|
+
console.log('%cā
Object cached successfully', 'color: #10B981; font-weight: bold;');
|
|
87
|
+
console.groupEnd();
|
|
88
|
+
break;
|
|
89
|
+
|
|
90
|
+
case 'SKIPPED':
|
|
91
|
+
console.groupCollapsed(
|
|
92
|
+
`%cāļø SKIPPED %c${shortUrl}`,
|
|
93
|
+
LOG_STYLES.skipped,
|
|
94
|
+
LOG_STYLES.url
|
|
95
|
+
);
|
|
96
|
+
console.log(`%cā° Time: %c${time}`, 'font-weight: bold;', 'color: #6B7280;');
|
|
97
|
+
console.log(`%cš URL: %c${url}`, 'font-weight: bold;', LOG_STYLES.url);
|
|
98
|
+
console.log(`%cš« Reason: %c${reason}`, 'font-weight: bold;', 'color: #F59E0B; font-weight: bold;');
|
|
99
|
+
if (details.size && details.limit) {
|
|
100
|
+
console.log(`%cš Size: %c${formatBytes(details.size)} (limit: ${formatBytes(details.limit)})`, 'font-weight: bold;', 'color: #EF4444;');
|
|
101
|
+
}
|
|
102
|
+
if (details.networkQuality) {
|
|
103
|
+
console.log(`%cš Network: %c${details.networkQuality}`, 'font-weight: bold;', 'color: #6B7280;');
|
|
104
|
+
}
|
|
105
|
+
if (details.priority) {
|
|
106
|
+
console.log(`%cā” Priority: %c${details.priority.toUpperCase()}`, 'font-weight: bold;', details.priority === 'high' ? LOG_STYLES.priority_high : LOG_STYLES.priority_normal);
|
|
107
|
+
}
|
|
108
|
+
console.log('%cā ļø Object NOT cached', 'color: #F59E0B; font-weight: bold;');
|
|
109
|
+
console.groupEnd();
|
|
110
|
+
break;
|
|
111
|
+
|
|
112
|
+
case 'SERVED':
|
|
113
|
+
console.groupCollapsed(
|
|
114
|
+
`%cš¤ SERVED FROM CACHE %c${shortUrl}`,
|
|
115
|
+
LOG_STYLES.served,
|
|
116
|
+
LOG_STYLES.url
|
|
117
|
+
);
|
|
118
|
+
console.log(`%cā° Time: %c${time}`, 'font-weight: bold;', 'color: #6B7280;');
|
|
119
|
+
console.log(`%cš URL: %c${url}`, 'font-weight: bold;', LOG_STYLES.url);
|
|
120
|
+
console.log(`%cš Reason: %c${reason}`, 'font-weight: bold;', LOG_STYLES.reason);
|
|
121
|
+
if (details.priority) {
|
|
122
|
+
console.log(`%cā” Priority: %c${details.priority.toUpperCase()}`, 'font-weight: bold;', details.priority === 'high' ? LOG_STYLES.priority_high : LOG_STYLES.priority_normal);
|
|
123
|
+
}
|
|
124
|
+
if (details.usage) {
|
|
125
|
+
console.log(`%cš Usage Stats:`, 'font-weight: bold;', details.usage);
|
|
126
|
+
}
|
|
127
|
+
console.log('%cš Served from offline cache', 'color: #3B82F6; font-weight: bold;');
|
|
128
|
+
console.groupEnd();
|
|
129
|
+
break;
|
|
130
|
+
|
|
131
|
+
case 'ERROR':
|
|
132
|
+
console.groupCollapsed(
|
|
133
|
+
`%cā CACHE MISS %c${shortUrl}`,
|
|
134
|
+
LOG_STYLES.error,
|
|
135
|
+
LOG_STYLES.url
|
|
136
|
+
);
|
|
137
|
+
console.log(`%cā° Time: %c${time}`, 'font-weight: bold;', 'color: #6B7280;');
|
|
138
|
+
console.log(`%cš URL: %c${url}`, 'font-weight: bold;', LOG_STYLES.url);
|
|
139
|
+
console.log(`%cš« Reason: %c${reason}`, 'font-weight: bold;', 'color: #EF4444; font-weight: bold;');
|
|
140
|
+
if (details.priority) {
|
|
141
|
+
console.log(`%cā” Priority: %c${details.priority.toUpperCase()}`, 'font-weight: bold;', details.priority === 'high' ? LOG_STYLES.priority_high : LOG_STYLES.priority_normal);
|
|
142
|
+
}
|
|
143
|
+
console.log('%cā Resource not available offline', 'color: #EF4444; font-weight: bold;');
|
|
144
|
+
console.groupEnd();
|
|
145
|
+
break;
|
|
146
|
+
|
|
147
|
+
case 'INTERCEPT':
|
|
148
|
+
console.log(
|
|
149
|
+
`%cš INTERCEPT %c${shortUrl} %c[${details.isAPI ? 'API' : 'PAGE'}]`,
|
|
150
|
+
LOG_STYLES.intercept,
|
|
151
|
+
LOG_STYLES.url,
|
|
152
|
+
details.isAPI ? LOG_STYLES.type_api : LOG_STYLES.type_page
|
|
153
|
+
);
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
23
158
|
/**
|
|
24
159
|
* Receive config from SDK
|
|
25
160
|
*/
|
|
@@ -44,9 +179,27 @@ self.addEventListener("message", (event) => {
|
|
|
44
179
|
});
|
|
45
180
|
|
|
46
181
|
/**
|
|
47
|
-
* Log cache events to IndexedDB
|
|
182
|
+
* Log cache events to IndexedDB and send to main thread
|
|
48
183
|
*/
|
|
49
184
|
function logEvent(type, url, reason, metadata = {}) {
|
|
185
|
+
const eventData = {
|
|
186
|
+
type: `CACHE_${type.toUpperCase()}`,
|
|
187
|
+
url,
|
|
188
|
+
reason,
|
|
189
|
+
metadata,
|
|
190
|
+
timestamp: Date.now()
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// Always send to main thread when debug or enableDetailedLogs is on
|
|
194
|
+
if (SDK_CONFIG.debug || SDK_CONFIG.enableDetailedLogs) {
|
|
195
|
+
self.clients.matchAll().then(clients => {
|
|
196
|
+
clients.forEach(client => {
|
|
197
|
+
client.postMessage(eventData);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Store in IndexedDB only if enableDetailedLogs is on
|
|
50
203
|
if (!SDK_CONFIG.enableDetailedLogs) return;
|
|
51
204
|
|
|
52
205
|
const request = indexedDB.open("smart-offline-logs", 1);
|
|
@@ -72,19 +225,6 @@ function logEvent(type, url, reason, metadata = {}) {
|
|
|
72
225
|
date: new Date().toISOString()
|
|
73
226
|
});
|
|
74
227
|
};
|
|
75
|
-
|
|
76
|
-
// Also send to main thread
|
|
77
|
-
self.clients.matchAll().then(clients => {
|
|
78
|
-
clients.forEach(client => {
|
|
79
|
-
client.postMessage({
|
|
80
|
-
type: `CACHE_${type.toUpperCase()}`,
|
|
81
|
-
url,
|
|
82
|
-
reason,
|
|
83
|
-
metadata,
|
|
84
|
-
timestamp: Date.now()
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
228
|
}
|
|
89
229
|
|
|
90
230
|
/**
|
|
@@ -250,9 +390,8 @@ self.addEventListener("fetch", (event) => {
|
|
|
250
390
|
|
|
251
391
|
if (!isPage && !isAPI) return;
|
|
252
392
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
393
|
+
// Log interception event
|
|
394
|
+
printCacheLog('INTERCEPT', request.url, 'URL pattern matched', { isPage, isAPI });
|
|
256
395
|
|
|
257
396
|
event.respondWith(
|
|
258
397
|
fetch(request)
|
|
@@ -269,12 +408,11 @@ self.addEventListener("fetch", (event) => {
|
|
|
269
408
|
limit: SDK_CONFIG.maxResourceSize
|
|
270
409
|
});
|
|
271
410
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
411
|
+
printCacheLog('SKIPPED', request.url, 'Resource too large (exceeds maxResourceSize)', {
|
|
412
|
+
size,
|
|
413
|
+
limit: SDK_CONFIG.maxResourceSize
|
|
414
|
+
});
|
|
415
|
+
|
|
278
416
|
return response;
|
|
279
417
|
}
|
|
280
418
|
|
|
@@ -289,12 +427,11 @@ self.addEventListener("fetch", (event) => {
|
|
|
289
427
|
priority: 'low'
|
|
290
428
|
});
|
|
291
429
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}
|
|
430
|
+
printCacheLog('SKIPPED', request.url, 'Slow network detected - skipping low priority resource', {
|
|
431
|
+
networkQuality: netQuality,
|
|
432
|
+
priority: 'low'
|
|
433
|
+
});
|
|
434
|
+
|
|
298
435
|
return response;
|
|
299
436
|
}
|
|
300
437
|
|
|
@@ -308,12 +445,13 @@ self.addEventListener("fetch", (event) => {
|
|
|
308
445
|
});
|
|
309
446
|
});
|
|
310
447
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
448
|
+
// Rich console log for successful cache
|
|
449
|
+
printCacheLog('CACHED', request.url, 'Successfully fetched from network and cached', {
|
|
450
|
+
type: isAPI ? 'API' : 'PAGE',
|
|
451
|
+
size,
|
|
452
|
+
priority: highPriority ? 'high' : 'normal',
|
|
453
|
+
usage: usage ? { accessCount: usage.count, lastAccessed: new Date(usage.lastAccessed).toLocaleString() } : { accessCount: 1, lastAccessed: 'Now (first access)' }
|
|
454
|
+
});
|
|
317
455
|
|
|
318
456
|
return response;
|
|
319
457
|
})
|
|
@@ -331,16 +469,18 @@ self.addEventListener("fetch", (event) => {
|
|
|
331
469
|
usage: usage ? { count: usage.count, lastAccessed: usage.lastAccessed } : null
|
|
332
470
|
});
|
|
333
471
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
);
|
|
339
|
-
}
|
|
472
|
+
printCacheLog('SERVED', request.url, 'Network unavailable - serving from cache', {
|
|
473
|
+
priority: highPriority ? 'high' : 'normal',
|
|
474
|
+
usage: usage ? { accessCount: usage.count, lastAccessed: new Date(usage.lastAccessed).toLocaleString() } : null
|
|
475
|
+
});
|
|
340
476
|
} else {
|
|
341
477
|
logEvent('error', request.url, 'cache_miss_offline', {
|
|
342
478
|
priority: highPriority ? 'high' : 'normal'
|
|
343
479
|
});
|
|
480
|
+
|
|
481
|
+
printCacheLog('ERROR', request.url, 'Network unavailable and resource not in cache', {
|
|
482
|
+
priority: highPriority ? 'high' : 'normal'
|
|
483
|
+
});
|
|
344
484
|
}
|
|
345
485
|
|
|
346
486
|
return cached;
|
package/src/index.js
CHANGED
|
@@ -92,7 +92,8 @@ export async function setupSmartOffline(config = {}) {
|
|
|
92
92
|
try {
|
|
93
93
|
// Register service worker
|
|
94
94
|
if (currentConfig.debug) {
|
|
95
|
-
|
|
95
|
+
printStartupBanner();
|
|
96
|
+
console.log("%cš§ Registering service worker...", "color: #94a3b8;");
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
serviceWorkerRegistration = await navigator.serviceWorker.register(
|
|
@@ -102,8 +103,8 @@ export async function setupSmartOffline(config = {}) {
|
|
|
102
103
|
|
|
103
104
|
if (currentConfig.debug) {
|
|
104
105
|
console.log(
|
|
105
|
-
"
|
|
106
|
-
serviceWorkerRegistration.scope
|
|
106
|
+
"%cā Service worker registered", "color: #22c55e;",
|
|
107
|
+
`(scope: ${serviceWorkerRegistration.scope})`
|
|
107
108
|
);
|
|
108
109
|
}
|
|
109
110
|
|
|
@@ -111,7 +112,7 @@ export async function setupSmartOffline(config = {}) {
|
|
|
111
112
|
await navigator.serviceWorker.ready;
|
|
112
113
|
|
|
113
114
|
if (currentConfig.debug) {
|
|
114
|
-
console.log("
|
|
115
|
+
console.log("%cā Service worker ready", "color: #22c55e;");
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
// Send configuration to service worker
|
|
@@ -123,12 +124,7 @@ export async function setupSmartOffline(config = {}) {
|
|
|
123
124
|
isInitialized = true;
|
|
124
125
|
|
|
125
126
|
if (currentConfig.debug) {
|
|
126
|
-
|
|
127
|
-
pages: currentConfig.pages,
|
|
128
|
-
apis: currentConfig.apis,
|
|
129
|
-
frequencyThreshold: currentConfig.frequencyThreshold,
|
|
130
|
-
recencyThreshold: `${currentConfig.recencyThreshold / (60 * 60 * 1000)}h`,
|
|
131
|
-
});
|
|
127
|
+
printConfigSummary();
|
|
132
128
|
}
|
|
133
129
|
|
|
134
130
|
return { success: true, registration: serviceWorkerRegistration };
|
|
@@ -145,6 +141,108 @@ export async function setupSmartOffline(config = {}) {
|
|
|
145
141
|
// HELPER FUNCTIONS
|
|
146
142
|
// ============================================================================
|
|
147
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Print startup banner
|
|
146
|
+
*/
|
|
147
|
+
function printStartupBanner() {
|
|
148
|
+
console.log(
|
|
149
|
+
`%c
|
|
150
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
151
|
+
ā ā
|
|
152
|
+
ā š SmartOffline SDK v1.0.0 ā
|
|
153
|
+
ā Intelligent offline-first caching ā
|
|
154
|
+
ā ā
|
|
155
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
156
|
+
`,
|
|
157
|
+
"color: #3b82f6; font-weight: bold;"
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Print configuration summary
|
|
163
|
+
*/
|
|
164
|
+
function printConfigSummary() {
|
|
165
|
+
console.log(
|
|
166
|
+
`%cāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā`,
|
|
167
|
+
"color: #475569;"
|
|
168
|
+
);
|
|
169
|
+
console.log(
|
|
170
|
+
`%cš Configuration Summary`,
|
|
171
|
+
"color: #22c55e; font-weight: bold; font-size: 14px;"
|
|
172
|
+
);
|
|
173
|
+
console.log(
|
|
174
|
+
`%cāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā`,
|
|
175
|
+
"color: #475569;"
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
// Pages
|
|
179
|
+
console.log("%cš Pages to cache:", "color: #60a5fa; font-weight: bold;");
|
|
180
|
+
if (currentConfig.pages.length > 0) {
|
|
181
|
+
currentConfig.pages.forEach((p) =>
|
|
182
|
+
console.log(` %c⢠${p}`, "color: #94a3b8;")
|
|
183
|
+
);
|
|
184
|
+
} else {
|
|
185
|
+
console.log(" %c(none)", "color: #64748b; font-style: italic;");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// APIs
|
|
189
|
+
console.log("%cš APIs to cache:", "color: #60a5fa; font-weight: bold;");
|
|
190
|
+
if (currentConfig.apis.length > 0) {
|
|
191
|
+
currentConfig.apis.forEach((a) =>
|
|
192
|
+
console.log(` %c⢠${a}`, "color: #94a3b8;")
|
|
193
|
+
);
|
|
194
|
+
} else {
|
|
195
|
+
console.log(" %c(none)", "color: #64748b; font-style: italic;");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Priority settings
|
|
199
|
+
console.log("%cā” Priority settings:", "color: #60a5fa; font-weight: bold;");
|
|
200
|
+
console.log(
|
|
201
|
+
` %cFrequency threshold: ${currentConfig.frequencyThreshold} accesses`,
|
|
202
|
+
"color: #94a3b8;"
|
|
203
|
+
);
|
|
204
|
+
console.log(
|
|
205
|
+
` %cRecency threshold: ${currentConfig.recencyThreshold / (60 * 60 * 1000)}h`,
|
|
206
|
+
"color: #94a3b8;"
|
|
207
|
+
);
|
|
208
|
+
console.log(
|
|
209
|
+
` %cMax resource size: ${formatBytes(currentConfig.maxResourceSize)}`,
|
|
210
|
+
"color: #94a3b8;"
|
|
211
|
+
);
|
|
212
|
+
console.log(
|
|
213
|
+
` %cNetwork quality: ${currentConfig.networkQuality}`,
|
|
214
|
+
"color: #94a3b8;"
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
// Significance overrides
|
|
218
|
+
const sigKeys = Object.keys(currentConfig.significance || {});
|
|
219
|
+
if (sigKeys.length > 0) {
|
|
220
|
+
console.log(
|
|
221
|
+
"%cšÆ Significance overrides:",
|
|
222
|
+
"color: #60a5fa; font-weight: bold;"
|
|
223
|
+
);
|
|
224
|
+
sigKeys.forEach((key) => {
|
|
225
|
+
const val = currentConfig.significance[key];
|
|
226
|
+
const icon = val === "high" ? "š“" : val === "low" ? "šµ" : "āŖ";
|
|
227
|
+
console.log(` %c${icon} ${key} ā ${val}`, "color: #94a3b8;");
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
console.log(
|
|
232
|
+
`%cāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā`,
|
|
233
|
+
"color: #475569;"
|
|
234
|
+
);
|
|
235
|
+
console.log(
|
|
236
|
+
"%cā
SmartOffline is now active! Cache events will appear below.",
|
|
237
|
+
"color: #22c55e; font-weight: bold;"
|
|
238
|
+
);
|
|
239
|
+
console.log(
|
|
240
|
+
`%cāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā`,
|
|
241
|
+
"color: #475569;"
|
|
242
|
+
);
|
|
243
|
+
console.log(""); // Empty line for spacing
|
|
244
|
+
}
|
|
245
|
+
|
|
148
246
|
async function sendConfigToServiceWorker() {
|
|
149
247
|
const controller = navigator.serviceWorker.controller;
|
|
150
248
|
|
|
@@ -202,29 +300,134 @@ function setupEventListenersInternal() {
|
|
|
202
300
|
const listeners = eventListeners[eventType] || [];
|
|
203
301
|
listeners.forEach((fn) => fn(cacheEvent));
|
|
204
302
|
|
|
205
|
-
//
|
|
303
|
+
// Rich debug logging with colors
|
|
206
304
|
if (currentConfig.debug) {
|
|
207
|
-
|
|
208
|
-
CACHE_CACHE: "š¾",
|
|
209
|
-
CACHE_SKIP: "āļø",
|
|
210
|
-
CACHE_SERVE: "š¤",
|
|
211
|
-
CACHE_ERROR: "ā",
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
console.log(
|
|
215
|
-
`[SmartOffline] ${icon[cacheEvent.type] || "š"} ${cacheEvent.type.replace("CACHE_", "")}:`,
|
|
216
|
-
cacheEvent.url.replace(
|
|
217
|
-
typeof window !== "undefined" ? window.location.origin : "",
|
|
218
|
-
"",
|
|
219
|
-
),
|
|
220
|
-
`(${cacheEvent.reason})`,
|
|
221
|
-
);
|
|
305
|
+
logCacheEventToConsole(cacheEvent);
|
|
222
306
|
}
|
|
223
307
|
}
|
|
224
308
|
}
|
|
225
309
|
});
|
|
226
310
|
}
|
|
227
311
|
|
|
312
|
+
/**
|
|
313
|
+
* Rich console logging for cache events with colors and formatting
|
|
314
|
+
*/
|
|
315
|
+
function logCacheEventToConsole(cacheEvent) {
|
|
316
|
+
const origin = typeof window !== "undefined" ? window.location.origin : "";
|
|
317
|
+
const shortUrl = cacheEvent.url.replace(origin, "") || cacheEvent.url;
|
|
318
|
+
const time = new Date(cacheEvent.timestamp).toLocaleTimeString();
|
|
319
|
+
|
|
320
|
+
// Style configurations for each event type
|
|
321
|
+
const styles = {
|
|
322
|
+
CACHE_INTERCEPT: {
|
|
323
|
+
icon: "š",
|
|
324
|
+
label: "INTERCEPT",
|
|
325
|
+
bgColor: "#6366f1",
|
|
326
|
+
textColor: "#fff",
|
|
327
|
+
},
|
|
328
|
+
CACHE_CACHE: {
|
|
329
|
+
icon: "š¾",
|
|
330
|
+
label: "CACHED",
|
|
331
|
+
bgColor: "#22c55e",
|
|
332
|
+
textColor: "#fff",
|
|
333
|
+
},
|
|
334
|
+
CACHE_SKIP: {
|
|
335
|
+
icon: "āļø",
|
|
336
|
+
label: "SKIPPED",
|
|
337
|
+
bgColor: "#f59e0b",
|
|
338
|
+
textColor: "#000",
|
|
339
|
+
},
|
|
340
|
+
CACHE_SERVE: {
|
|
341
|
+
icon: "š¤",
|
|
342
|
+
label: "SERVED",
|
|
343
|
+
bgColor: "#3b82f6",
|
|
344
|
+
textColor: "#fff",
|
|
345
|
+
},
|
|
346
|
+
CACHE_ERROR: {
|
|
347
|
+
icon: "ā",
|
|
348
|
+
label: "MISS",
|
|
349
|
+
bgColor: "#ef4444",
|
|
350
|
+
textColor: "#fff",
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const style = styles[cacheEvent.type] || {
|
|
355
|
+
icon: "š",
|
|
356
|
+
label: "EVENT",
|
|
357
|
+
bgColor: "#64748b",
|
|
358
|
+
textColor: "#fff",
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// Build metadata string
|
|
362
|
+
let metaInfo = "";
|
|
363
|
+
if (cacheEvent.metadata) {
|
|
364
|
+
const meta = cacheEvent.metadata;
|
|
365
|
+
const parts = [];
|
|
366
|
+
if (meta.type) parts.push(`type: ${meta.type}`);
|
|
367
|
+
if (meta.priority) parts.push(`priority: ${meta.priority}`);
|
|
368
|
+
if (meta.size) parts.push(`size: ${formatBytes(meta.size)}`);
|
|
369
|
+
if (meta.networkQuality) parts.push(`network: ${meta.networkQuality}`);
|
|
370
|
+
if (meta.isPage !== undefined) parts.push(meta.isPage ? "PAGE" : "API");
|
|
371
|
+
if (parts.length > 0) metaInfo = ` [${parts.join(", ")}]`;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Reason badge
|
|
375
|
+
const reasonText = cacheEvent.reason ? ` ā ${formatReason(cacheEvent.reason)}` : "";
|
|
376
|
+
|
|
377
|
+
// Log with styling
|
|
378
|
+
console.log(
|
|
379
|
+
`%c ${style.icon} SmartOffline %c ${style.label} %c ${time} %c ${shortUrl}${reasonText}${metaInfo}`,
|
|
380
|
+
`background: #1e293b; color: #fff; padding: 2px 6px; border-radius: 3px 0 0 3px; font-weight: bold;`,
|
|
381
|
+
`background: ${style.bgColor}; color: ${style.textColor}; padding: 2px 8px; font-weight: bold;`,
|
|
382
|
+
`background: #334155; color: #94a3b8; padding: 2px 6px;`,
|
|
383
|
+
`background: transparent; color: #e2e8f0; padding: 2px 6px;`
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
// For cached items, show additional details in a group
|
|
387
|
+
if (cacheEvent.type === "CACHE_CACHE" && cacheEvent.metadata) {
|
|
388
|
+
console.groupCollapsed(` ā³ Details for ${shortUrl}`);
|
|
389
|
+
console.table({
|
|
390
|
+
URL: shortUrl,
|
|
391
|
+
Type: cacheEvent.metadata.type || "unknown",
|
|
392
|
+
Priority: cacheEvent.metadata.priority || "normal",
|
|
393
|
+
Size: cacheEvent.metadata.size ? formatBytes(cacheEvent.metadata.size) : "unknown",
|
|
394
|
+
Timestamp: new Date(cacheEvent.timestamp).toISOString(),
|
|
395
|
+
});
|
|
396
|
+
console.groupEnd();
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// For serve events, show usage data if available
|
|
400
|
+
if (cacheEvent.type === "CACHE_SERVE" && cacheEvent.metadata?.usage) {
|
|
401
|
+
console.groupCollapsed(` ā³ Usage data for ${shortUrl}`);
|
|
402
|
+
console.table({
|
|
403
|
+
"Access Count": cacheEvent.metadata.usage.count,
|
|
404
|
+
"Last Accessed": new Date(cacheEvent.metadata.usage.lastAccessed).toISOString(),
|
|
405
|
+
Priority: cacheEvent.metadata.priority || "normal",
|
|
406
|
+
});
|
|
407
|
+
console.groupEnd();
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Format bytes to human readable string
|
|
413
|
+
*/
|
|
414
|
+
function formatBytes(bytes) {
|
|
415
|
+
if (bytes === 0) return "0 B";
|
|
416
|
+
const k = 1024;
|
|
417
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
418
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
419
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Format reason string to be more readable
|
|
424
|
+
*/
|
|
425
|
+
function formatReason(reason) {
|
|
426
|
+
return reason
|
|
427
|
+
.replace(/_/g, " ")
|
|
428
|
+
.replace(/\b\w/g, (l) => l.toUpperCase());
|
|
429
|
+
}
|
|
430
|
+
|
|
228
431
|
function openDB(name, version) {
|
|
229
432
|
return new Promise((resolve, reject) => {
|
|
230
433
|
const request = indexedDB.open(name, version);
|