@soham20/smart-offline-sdk 0.2.1 โ 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/README.md +201 -35
- package/package.json +4 -3
- package/smart-offline-sw.js +181 -41
- package/src/SmartOfflineSetup.ts +658 -0
- package/src/SmartOfflineTestUtils.ts +578 -0
- package/src/index.cjs +576 -0
- package/src/index.cjs.js +563 -97
- package/src/index.d.ts +320 -52
- package/src/index.js +985 -125
package/README.md
CHANGED
|
@@ -1,63 +1,229 @@
|
|
|
1
|
-
#
|
|
1
|
+
# SmartOffline SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A complete, reliable offline-first caching SDK for web applications. Provides intelligent priority-based caching with configurable algorithms.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
- ๐ **Easy Setup** - Single function call to initialize
|
|
8
|
+
- ๐ฏ **Priority-Based Caching** - Intelligent frequency and recency scoring
|
|
9
|
+
- ๐ **Network Aware** - Adapts caching strategy based on connection quality
|
|
10
|
+
- ๐ **Usage Tracking** - Tracks access patterns to optimize caching
|
|
11
|
+
- ๐ง **Highly Configurable** - Customize every aspect of the caching algorithm
|
|
12
|
+
- ๐ **TypeScript Support** - Full type definitions included
|
|
13
|
+
- ๐งช **Test Utilities** - Built-in testing and inspection tools
|
|
9
14
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
});
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @soham20/smart-offline-sdk
|
|
15
19
|
```
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
Or directly from GitHub:
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
```bash
|
|
24
|
+
npm install git+https://github.com/OwaisShaikh1/Hackvision2026.git
|
|
25
|
+
```
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
import { setupSmartOffline } from "@soham20/smart-offline-sdk";
|
|
22
31
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
// Initialize early in your app
|
|
33
|
+
await setupSmartOffline({
|
|
34
|
+
pages: ["/dashboard/*", "/products/*"],
|
|
35
|
+
apis: ["/api/v1/*"],
|
|
36
|
+
debug: true,
|
|
37
|
+
});
|
|
38
|
+
```
|
|
30
39
|
|
|
31
|
-
|
|
40
|
+
## Configuration Options
|
|
41
|
+
|
|
42
|
+
| Option | Type | Default | Description |
|
|
43
|
+
| -------------------- | --------------------------------- | ------------------------ | ------------------------------------------------------- |
|
|
44
|
+
| `pages` | `string[]` | `[]` | URL patterns for pages to cache (supports `*` wildcard) |
|
|
45
|
+
| `apis` | `string[]` | `[]` | URL patterns for API endpoints to cache |
|
|
46
|
+
| `debug` | `boolean` | `false` | Enable debug logging in console |
|
|
47
|
+
| `frequencyThreshold` | `number` | `3` | Number of accesses before URL is high priority |
|
|
48
|
+
| `recencyThreshold` | `number` | `86400000` | Time in ms for "recent" access (default: 24h) |
|
|
49
|
+
| `maxResourceSize` | `number` | `10485760` | Max bytes to cache per resource (default: 10MB) |
|
|
50
|
+
| `networkQuality` | `'auto' \| 'slow' \| 'fast'` | `'auto'` | Network quality detection mode |
|
|
51
|
+
| `significance` | `Record<string, 'high' \| 'low'>` | `{}` | Priority overrides for URL patterns |
|
|
52
|
+
| `weights` | `{ frequency, recency, size }` | `{ 1, 1, 1 }` | Weights for priority calculation |
|
|
53
|
+
| `customPriorityFn` | `Function` | `null` | Custom priority function (0-100) |
|
|
54
|
+
| `enableDetailedLogs` | `boolean` | `false` | Enable detailed event logging |
|
|
55
|
+
| `onCacheEvent` | `Function` | `undefined` | Callback for cache events |
|
|
56
|
+
| `serviceWorkerPath` | `string` | `'/smart-offline-sw.js'` | Path to service worker file |
|
|
57
|
+
| `serviceWorkerScope` | `string` | `'/'` | Service worker scope |
|
|
58
|
+
|
|
59
|
+
## Complete Example
|
|
32
60
|
|
|
33
61
|
```javascript
|
|
34
|
-
SmartOffline
|
|
35
|
-
|
|
36
|
-
|
|
62
|
+
import { setupSmartOffline, SmartOffline } from "@soham20/smart-offline-sdk";
|
|
63
|
+
|
|
64
|
+
// Full configuration example
|
|
65
|
+
const result = await setupSmartOffline({
|
|
66
|
+
// URL patterns to cache
|
|
67
|
+
pages: ["/admin/*", "/dashboard", "/products/*"],
|
|
68
|
+
apis: ["/api/v1/*", "/graphql"],
|
|
69
|
+
|
|
70
|
+
// Enable debug logging
|
|
37
71
|
debug: true,
|
|
38
72
|
|
|
39
73
|
// Priority tuning
|
|
40
|
-
frequencyThreshold: 5,
|
|
74
|
+
frequencyThreshold: 5, // 5 accesses = high priority
|
|
41
75
|
recencyThreshold: 12 * 60 * 60 * 1000, // 12 hours
|
|
42
|
-
maxResourceSize:
|
|
43
|
-
|
|
76
|
+
maxResourceSize: 5 * 1024 * 1024, // 5MB max
|
|
77
|
+
|
|
78
|
+
// Network detection
|
|
79
|
+
networkQuality: "auto",
|
|
80
|
+
|
|
81
|
+
// Manual priority overrides
|
|
44
82
|
significance: {
|
|
45
|
-
"/api/critical": "high",
|
|
46
|
-
"/api/
|
|
83
|
+
"/api/critical/*": "high", // Always cache
|
|
84
|
+
"/api/logs/*": "low", // Never cache
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
// Weight configuration
|
|
88
|
+
weights: {
|
|
89
|
+
frequency: 2, // Frequency is 2x more important
|
|
90
|
+
recency: 1,
|
|
91
|
+
size: 1,
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
// Custom priority function
|
|
95
|
+
customPriorityFn: (usage, url, config) => {
|
|
96
|
+
if (url.includes("vip")) return 100;
|
|
97
|
+
if (!usage) return 0;
|
|
98
|
+
return usage.count >= 5 ? 100 : 25;
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
// Event callback
|
|
102
|
+
onCacheEvent: (event) => {
|
|
103
|
+
console.log(`Cache event: ${event.type}`, event.url);
|
|
47
104
|
},
|
|
48
105
|
});
|
|
106
|
+
|
|
107
|
+
if (result.success) {
|
|
108
|
+
console.log("SmartOffline ready!");
|
|
109
|
+
}
|
|
49
110
|
```
|
|
50
111
|
|
|
51
|
-
##
|
|
112
|
+
## API Reference
|
|
52
113
|
|
|
53
|
-
###
|
|
114
|
+
### Main Functions
|
|
54
115
|
|
|
55
|
-
```
|
|
56
|
-
|
|
116
|
+
```javascript
|
|
117
|
+
import {
|
|
118
|
+
setupSmartOffline, // Initialize SDK (async)
|
|
119
|
+
updateConfig, // Update config at runtime
|
|
120
|
+
getConfig, // Get current configuration
|
|
121
|
+
isSmartOfflineReady, // Check if initialized
|
|
122
|
+
clearAllCache, // Clear all cached data
|
|
123
|
+
getCacheStats, // Get cache statistics
|
|
124
|
+
forceUpdate, // Force SW update
|
|
125
|
+
uninstall, // Uninstall SDK
|
|
126
|
+
} from "@soham20/smart-offline-sdk";
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Event Handling
|
|
130
|
+
|
|
131
|
+
```javascript
|
|
132
|
+
import { on, off } from "@soham20/smart-offline-sdk";
|
|
133
|
+
|
|
134
|
+
// Listen for cache events
|
|
135
|
+
on("cache", (event) => console.log("Cached:", event.url));
|
|
136
|
+
on("skip", (event) => console.log("Skipped:", event.url));
|
|
137
|
+
on("serve", (event) => console.log("Served from cache:", event.url));
|
|
138
|
+
on("error", (event) => console.log("Error:", event.url));
|
|
139
|
+
|
|
140
|
+
// Remove listener
|
|
141
|
+
off("cache", myCallback);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### SmartOffline Object
|
|
145
|
+
|
|
146
|
+
```javascript
|
|
147
|
+
import { SmartOffline } from "@soham20/smart-offline-sdk";
|
|
148
|
+
|
|
149
|
+
// All methods available on SmartOffline object
|
|
150
|
+
SmartOffline.setup(config); // setupSmartOffline
|
|
151
|
+
SmartOffline.updateConfig(cfg); // Update configuration
|
|
152
|
+
SmartOffline.getConfig(); // Get current config
|
|
153
|
+
SmartOffline.isReady(); // Check if ready
|
|
154
|
+
SmartOffline.clearCache(); // Clear all cache
|
|
155
|
+
SmartOffline.getStats(); // Get cache stats
|
|
156
|
+
SmartOffline.forceUpdate(); // Force SW update
|
|
157
|
+
SmartOffline.uninstall(); // Clean uninstall
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Test Utilities
|
|
161
|
+
|
|
162
|
+
```javascript
|
|
163
|
+
import {
|
|
164
|
+
SmartOfflineTestSuite,
|
|
165
|
+
runSmartOfflineTests,
|
|
166
|
+
CacheInspector,
|
|
167
|
+
} from "@soham20/smart-offline-sdk";
|
|
168
|
+
|
|
169
|
+
// Run all tests
|
|
170
|
+
const results = await runSmartOfflineTests();
|
|
171
|
+
|
|
172
|
+
// Or use the test suite directly
|
|
173
|
+
const suite = new SmartOfflineTestSuite({ pages: ["/test/*"] });
|
|
174
|
+
await suite.runAll();
|
|
175
|
+
suite.printResults();
|
|
176
|
+
|
|
177
|
+
// Inspect cache
|
|
178
|
+
const inspector = new CacheInspector();
|
|
179
|
+
await inspector.showAll();
|
|
180
|
+
const data = await inspector.getAllData();
|
|
57
181
|
```
|
|
58
182
|
|
|
59
|
-
|
|
183
|
+
## Browser Console Testing
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
// After SDK is loaded, these are available globally:
|
|
187
|
+
await runSmartOfflineTests();
|
|
188
|
+
await SmartOffline.getStats();
|
|
189
|
+
new CacheInspector().showAll();
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## How the Algorithm Works
|
|
193
|
+
|
|
194
|
+
1. **URL Pattern Matching**: Caches pages/APIs matching configured patterns
|
|
195
|
+
2. **Frequency Scoring**: URLs accessed >= `frequencyThreshold` times get higher score
|
|
196
|
+
3. **Recency Scoring**: URLs accessed within `recencyThreshold` get higher score
|
|
197
|
+
4. **Weighted Calculation**:
|
|
198
|
+
- `frequencyScore = min(100, (accessCount / threshold) * 100)`
|
|
199
|
+
- `recencyScore = max(0, 100 - (timeSinceAccess / threshold) * 100)`
|
|
200
|
+
- `weightedScore = (freqScore * freqWeight + recencyScore * recencyWeight) / totalWeight`
|
|
201
|
+
- HIGH priority if `weightedScore > 50`
|
|
202
|
+
5. **Network Awareness**: On slow networks, only HIGH priority resources are cached
|
|
203
|
+
6. **Size Limits**: Resources exceeding `maxResourceSize` are skipped
|
|
204
|
+
|
|
205
|
+
## Service Worker Setup
|
|
206
|
+
|
|
207
|
+
Copy `smart-offline-sw.js` to your public directory:
|
|
60
208
|
|
|
61
209
|
```bash
|
|
62
|
-
|
|
210
|
+
cp node_modules/@soham20/smart-offline-sdk/smart-offline-sw.js public/
|
|
63
211
|
```
|
|
212
|
+
|
|
213
|
+
## TypeScript
|
|
214
|
+
|
|
215
|
+
Full TypeScript support with exported types:
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
import type {
|
|
219
|
+
SmartOfflineConfig,
|
|
220
|
+
CacheEvent,
|
|
221
|
+
UsageData,
|
|
222
|
+
SetupResult,
|
|
223
|
+
CacheStats,
|
|
224
|
+
} from "@soham20/smart-offline-sdk";
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soham20/smart-offline-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Smart offline-first JavaScript SDK with intelligent caching for web applications",
|
|
5
|
-
"main": "./src/index.cjs
|
|
5
|
+
"main": "./src/index.cjs",
|
|
6
6
|
"module": "./src/index.js",
|
|
7
7
|
"browser": "./src/index.js",
|
|
8
8
|
"types": "./src/index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
11
|
"import": "./src/index.js",
|
|
12
|
-
"require": "./src/index.cjs
|
|
12
|
+
"require": "./src/index.cjs",
|
|
13
13
|
"browser": "./src/index.js",
|
|
14
|
+
"types": "./src/index.d.ts",
|
|
14
15
|
"default": "./src/index.js"
|
|
15
16
|
},
|
|
16
17
|
"./client": {
|
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;
|