@soham20/smart-offline-sdk 0.1.4 → 0.2.0
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 +123 -16
- package/src/index.cjs.js +79 -1
- package/src/index.d.ts +53 -2
- package/src/index.js +98 -2
package/package.json
CHANGED
package/smart-offline-sw.js
CHANGED
|
@@ -15,6 +15,9 @@ let SDK_CONFIG = {
|
|
|
15
15
|
maxResourceSize: Infinity,
|
|
16
16
|
networkQuality: "auto", // 'auto' | 'fast' | 'slow'
|
|
17
17
|
significance: {}, // { urlPattern: 'high' | 'normal' | 'low' }
|
|
18
|
+
weights: { frequency: 1, recency: 1, size: 1 },
|
|
19
|
+
customPriorityFn: null,
|
|
20
|
+
enableDetailedLogs: false
|
|
18
21
|
};
|
|
19
22
|
|
|
20
23
|
/**
|
|
@@ -24,12 +27,66 @@ self.addEventListener("message", (event) => {
|
|
|
24
27
|
if (event.data && event.data.type === "INIT_CONFIG") {
|
|
25
28
|
SDK_CONFIG = event.data.payload;
|
|
26
29
|
|
|
30
|
+
// Deserialize custom priority function if provided
|
|
31
|
+
if (SDK_CONFIG.customPriorityFn && typeof SDK_CONFIG.customPriorityFn === 'string') {
|
|
32
|
+
try {
|
|
33
|
+
SDK_CONFIG.customPriorityFn = eval(`(${SDK_CONFIG.customPriorityFn})`);
|
|
34
|
+
} catch (e) {
|
|
35
|
+
console.error('[SmartOffline] Failed to parse customPriorityFn:', e);
|
|
36
|
+
SDK_CONFIG.customPriorityFn = null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
27
40
|
if (SDK_CONFIG.debug) {
|
|
28
41
|
console.log("[SmartOffline] Config received:", SDK_CONFIG);
|
|
29
42
|
}
|
|
30
43
|
}
|
|
31
44
|
});
|
|
32
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Log cache events to IndexedDB
|
|
48
|
+
*/
|
|
49
|
+
function logEvent(type, url, reason, metadata = {}) {
|
|
50
|
+
if (!SDK_CONFIG.enableDetailedLogs) return;
|
|
51
|
+
|
|
52
|
+
const request = indexedDB.open("smart-offline-logs", 1);
|
|
53
|
+
|
|
54
|
+
request.onupgradeneeded = () => {
|
|
55
|
+
const db = request.result;
|
|
56
|
+
if (!db.objectStoreNames.contains("logs")) {
|
|
57
|
+
db.createObjectStore("logs", { autoIncrement: true });
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
request.onsuccess = () => {
|
|
62
|
+
const db = request.result;
|
|
63
|
+
const tx = db.transaction("logs", "readwrite");
|
|
64
|
+
const store = tx.objectStore("logs");
|
|
65
|
+
|
|
66
|
+
store.add({
|
|
67
|
+
type,
|
|
68
|
+
url,
|
|
69
|
+
reason,
|
|
70
|
+
metadata,
|
|
71
|
+
timestamp: Date.now(),
|
|
72
|
+
date: new Date().toISOString()
|
|
73
|
+
});
|
|
74
|
+
};
|
|
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
|
+
}
|
|
89
|
+
|
|
33
90
|
/**
|
|
34
91
|
* -------- Usage Tracking (IndexedDB) --------
|
|
35
92
|
* Tracks frequency + recency per URL
|
|
@@ -86,9 +143,19 @@ function getUsage(url) {
|
|
|
86
143
|
}
|
|
87
144
|
|
|
88
145
|
/**
|
|
89
|
-
* Decide priority based on real usage and developer-tuned config
|
|
146
|
+
* Decide priority based on real usage and developer-tuned config with weights
|
|
90
147
|
*/
|
|
91
148
|
function isHighPriority(usage, url) {
|
|
149
|
+
// Custom priority function override
|
|
150
|
+
if (SDK_CONFIG.customPriorityFn && typeof SDK_CONFIG.customPriorityFn === 'function') {
|
|
151
|
+
try {
|
|
152
|
+
const score = SDK_CONFIG.customPriorityFn(usage, url, SDK_CONFIG);
|
|
153
|
+
return score > 50; // Scores above 50 are high priority
|
|
154
|
+
} catch (e) {
|
|
155
|
+
console.error('[SmartOffline] Custom priority function error:', e);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
92
159
|
// Manual significance override
|
|
93
160
|
for (const pattern in SDK_CONFIG.significance) {
|
|
94
161
|
if (url.includes(pattern)) {
|
|
@@ -101,10 +168,25 @@ function isHighPriority(usage, url) {
|
|
|
101
168
|
|
|
102
169
|
if (!usage) return false;
|
|
103
170
|
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
171
|
+
// Weighted priority calculation
|
|
172
|
+
const weights = SDK_CONFIG.weights || { frequency: 1, recency: 1, size: 1 };
|
|
173
|
+
|
|
174
|
+
// Frequency score (0-100)
|
|
175
|
+
const frequencyScore = Math.min(100, (usage.count / SDK_CONFIG.frequencyThreshold) * 100);
|
|
176
|
+
|
|
177
|
+
// Recency score (0-100)
|
|
178
|
+
const timeSinceAccess = Date.now() - usage.lastAccessed;
|
|
179
|
+
const recencyScore = Math.max(0, 100 - (timeSinceAccess / SDK_CONFIG.recencyThreshold) * 100);
|
|
180
|
+
|
|
181
|
+
// Weighted total
|
|
182
|
+
const totalWeight = weights.frequency + weights.recency;
|
|
183
|
+
const weightedScore = (
|
|
184
|
+
(frequencyScore * weights.frequency) +
|
|
185
|
+
(recencyScore * weights.recency)
|
|
186
|
+
) / totalWeight;
|
|
187
|
+
|
|
188
|
+
// High priority if weighted score > 50
|
|
189
|
+
return weightedScore > 50;
|
|
108
190
|
}
|
|
109
191
|
|
|
110
192
|
/**
|
|
@@ -182,6 +264,11 @@ self.addEventListener("fetch", (event) => {
|
|
|
182
264
|
const contentLength = response.headers.get("content-length");
|
|
183
265
|
const size = contentLength ? parseInt(contentLength, 10) : 0;
|
|
184
266
|
if (size > SDK_CONFIG.maxResourceSize) {
|
|
267
|
+
logEvent('skip', request.url, 'size_limit_exceeded', {
|
|
268
|
+
size,
|
|
269
|
+
limit: SDK_CONFIG.maxResourceSize
|
|
270
|
+
});
|
|
271
|
+
|
|
185
272
|
if (SDK_CONFIG.debug) {
|
|
186
273
|
console.log(
|
|
187
274
|
`[SmartOffline] Skipped caching (size ${size} > ${SDK_CONFIG.maxResourceSize}):`,
|
|
@@ -193,8 +280,15 @@ self.addEventListener("fetch", (event) => {
|
|
|
193
280
|
|
|
194
281
|
// Network quality aware caching
|
|
195
282
|
const netQuality = getEffectiveNetworkQuality();
|
|
196
|
-
|
|
197
|
-
|
|
283
|
+
const usage = await getUsage(request.url);
|
|
284
|
+
const highPriority = isHighPriority(usage, request.url);
|
|
285
|
+
|
|
286
|
+
if (netQuality === "slow" && !highPriority) {
|
|
287
|
+
logEvent('skip', request.url, 'slow_network_low_priority', {
|
|
288
|
+
networkQuality: netQuality,
|
|
289
|
+
priority: 'low'
|
|
290
|
+
});
|
|
291
|
+
|
|
198
292
|
if (SDK_CONFIG.debug) {
|
|
199
293
|
console.log(
|
|
200
294
|
`[SmartOffline] Skipped caching (slow network, not high priority):`,
|
|
@@ -207,6 +301,11 @@ self.addEventListener("fetch", (event) => {
|
|
|
207
301
|
const clone = response.clone();
|
|
208
302
|
caches.open(CACHE_NAME).then((cache) => {
|
|
209
303
|
cache.put(request.url, clone);
|
|
304
|
+
logEvent('cache', request.url, 'network_fetch_success', {
|
|
305
|
+
type: isAPI ? 'API' : 'PAGE',
|
|
306
|
+
size,
|
|
307
|
+
priority: highPriority ? 'high' : 'normal'
|
|
308
|
+
});
|
|
210
309
|
});
|
|
211
310
|
|
|
212
311
|
if (SDK_CONFIG.debug) {
|
|
@@ -218,25 +317,33 @@ self.addEventListener("fetch", (event) => {
|
|
|
218
317
|
|
|
219
318
|
return response;
|
|
220
319
|
})
|
|
221
|
-
.catch(() => {
|
|
320
|
+
.catch(async () => {
|
|
222
321
|
// Offline / network failure
|
|
223
322
|
trackUsage(request.url);
|
|
224
323
|
|
|
225
|
-
|
|
226
|
-
|
|
324
|
+
const usage = await getUsage(request.url);
|
|
325
|
+
const highPriority = isHighPriority(usage, request.url);
|
|
326
|
+
const cached = await caches.match(request.url);
|
|
327
|
+
|
|
328
|
+
if (cached) {
|
|
329
|
+
logEvent('serve', request.url, 'offline_cache_hit', {
|
|
330
|
+
priority: highPriority ? 'high' : 'normal',
|
|
331
|
+
usage: usage ? { count: usage.count, lastAccessed: usage.lastAccessed } : null
|
|
332
|
+
});
|
|
227
333
|
|
|
228
334
|
if (SDK_CONFIG.debug) {
|
|
229
335
|
console.log(
|
|
230
|
-
`[SmartOffline] ${
|
|
231
|
-
highPriority ? "HIGH" : "NORMAL"
|
|
232
|
-
} priority:`,
|
|
336
|
+
`[SmartOffline] Served from cache (${highPriority ? "HIGH" : "NORMAL"} priority):`,
|
|
233
337
|
request.url
|
|
234
338
|
);
|
|
235
339
|
}
|
|
340
|
+
} else {
|
|
341
|
+
logEvent('error', request.url, 'cache_miss_offline', {
|
|
342
|
+
priority: highPriority ? 'high' : 'normal'
|
|
343
|
+
});
|
|
344
|
+
}
|
|
236
345
|
|
|
237
|
-
|
|
238
|
-
return caches.match(request.url);
|
|
239
|
-
});
|
|
346
|
+
return cached;
|
|
240
347
|
})
|
|
241
348
|
);
|
|
242
349
|
});
|
package/src/index.cjs.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SmartOffline SDK (CommonJS version)
|
|
3
3
|
*/
|
|
4
|
+
const eventListeners = {
|
|
5
|
+
cache: [],
|
|
6
|
+
skip: [],
|
|
7
|
+
serve: [],
|
|
8
|
+
clear: [],
|
|
9
|
+
error: []
|
|
10
|
+
};
|
|
11
|
+
|
|
4
12
|
function init(config = {}) {
|
|
5
13
|
if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) {
|
|
6
14
|
console.warn("Service Workers not supported");
|
|
@@ -16,11 +24,34 @@ function init(config = {}) {
|
|
|
16
24
|
maxResourceSize: config.maxResourceSize ?? Infinity,
|
|
17
25
|
networkQuality: config.networkQuality ?? "auto",
|
|
18
26
|
significance: config.significance ?? {},
|
|
27
|
+
weights: {
|
|
28
|
+
frequency: config.weights?.frequency ?? 1,
|
|
29
|
+
recency: config.weights?.recency ?? 1,
|
|
30
|
+
size: config.weights?.size ?? 1
|
|
31
|
+
},
|
|
32
|
+
customPriorityFn: config.customPriorityFn ? config.customPriorityFn.toString() : null,
|
|
33
|
+
enableDetailedLogs: config.enableDetailedLogs ?? false
|
|
19
34
|
};
|
|
20
35
|
|
|
36
|
+
if (config.onCacheEvent) {
|
|
37
|
+
eventListeners.cache.push(config.onCacheEvent);
|
|
38
|
+
eventListeners.skip.push(config.onCacheEvent);
|
|
39
|
+
eventListeners.serve.push(config.onCacheEvent);
|
|
40
|
+
eventListeners.clear.push(config.onCacheEvent);
|
|
41
|
+
eventListeners.error.push(config.onCacheEvent);
|
|
42
|
+
}
|
|
43
|
+
|
|
21
44
|
navigator.serviceWorker.register("/smart-offline-sw.js").then(() => {
|
|
22
45
|
console.log("Smart Offline Service Worker registered");
|
|
23
46
|
|
|
47
|
+
navigator.serviceWorker.addEventListener('message', (event) => {
|
|
48
|
+
if (event.data && event.data.type) {
|
|
49
|
+
const eventType = event.data.type.replace('CACHE_', '').toLowerCase();
|
|
50
|
+
const listeners = eventListeners[eventType] || [];
|
|
51
|
+
listeners.forEach(fn => fn(event.data));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
24
55
|
navigator.serviceWorker.ready.then(() => {
|
|
25
56
|
if (navigator.serviceWorker.controller) {
|
|
26
57
|
navigator.serviceWorker.controller.postMessage({
|
|
@@ -52,7 +83,54 @@ function init(config = {}) {
|
|
|
52
83
|
});
|
|
53
84
|
}
|
|
54
85
|
|
|
55
|
-
|
|
86
|
+
function on(eventType, callback) {
|
|
87
|
+
if (eventListeners[eventType]) {
|
|
88
|
+
eventListeners[eventType].push(callback);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function off(eventType, callback) {
|
|
93
|
+
if (eventListeners[eventType]) {
|
|
94
|
+
const index = eventListeners[eventType].indexOf(callback);
|
|
95
|
+
if (index > -1) {
|
|
96
|
+
eventListeners[eventType].splice(index, 1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function getCacheLogs() {
|
|
102
|
+
const db = await openDB('smart-offline-logs', 1);
|
|
103
|
+
const tx = db.transaction('logs', 'readonly');
|
|
104
|
+
const store = tx.objectStore('logs');
|
|
105
|
+
return new Promise((resolve) => {
|
|
106
|
+
const request = store.getAll();
|
|
107
|
+
request.onsuccess = () => resolve(request.result || []);
|
|
108
|
+
request.onerror = () => resolve([]);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function clearCacheLogs() {
|
|
113
|
+
const db = await openDB('smart-offline-logs', 1);
|
|
114
|
+
const tx = db.transaction('logs', 'readwrite');
|
|
115
|
+
const store = tx.objectStore('logs');
|
|
116
|
+
store.clear();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function openDB(name, version) {
|
|
120
|
+
return new Promise((resolve, reject) => {
|
|
121
|
+
const request = indexedDB.open(name, version);
|
|
122
|
+
request.onupgradeneeded = () => {
|
|
123
|
+
const db = request.result;
|
|
124
|
+
if (!db.objectStoreNames.contains('logs')) {
|
|
125
|
+
db.createObjectStore('logs', { autoIncrement: true });
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
request.onsuccess = () => resolve(request.result);
|
|
129
|
+
request.onerror = () => reject(request.error);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const SmartOffline = { init, on, off, getCacheLogs, clearCacheLogs };
|
|
56
134
|
|
|
57
135
|
module.exports = { SmartOffline };
|
|
58
136
|
module.exports.default = SmartOffline;
|
package/src/index.d.ts
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
export interface UsageData {
|
|
2
|
+
url: string;
|
|
3
|
+
count: number;
|
|
4
|
+
lastAccessed: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface CacheWeights {
|
|
8
|
+
/** Weight for frequency score (default: 1) */
|
|
9
|
+
frequency?: number;
|
|
10
|
+
/** Weight for recency score (default: 1) */
|
|
11
|
+
recency?: number;
|
|
12
|
+
/** Weight for size consideration (default: 1) */
|
|
13
|
+
size?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface CacheEvent {
|
|
17
|
+
type: 'CACHE_CACHE' | 'CACHE_SKIP' | 'CACHE_SERVE' | 'CACHE_CLEAR' | 'CACHE_ERROR';
|
|
18
|
+
url: string;
|
|
19
|
+
reason: string;
|
|
20
|
+
metadata?: any;
|
|
21
|
+
timestamp: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type CustomPriorityFunction = (
|
|
25
|
+
usage: UsageData | null,
|
|
26
|
+
url: string,
|
|
27
|
+
config: SmartOfflineConfig
|
|
28
|
+
) => number; // Return 0-100, >50 is high priority
|
|
29
|
+
|
|
1
30
|
export interface SmartOfflineConfig {
|
|
2
31
|
/** Array of page URL patterns to cache */
|
|
3
32
|
pages?: string[];
|
|
@@ -12,14 +41,36 @@ export interface SmartOfflineConfig {
|
|
|
12
41
|
/** Max bytes to cache per resource; larger resources skipped (default: Infinity) */
|
|
13
42
|
maxResourceSize?: number;
|
|
14
43
|
/** Network quality setting: 'auto' | 'fast' | 'slow' (default: 'auto') */
|
|
15
|
-
networkQuality?:
|
|
44
|
+
networkQuality?: "auto" | "fast" | "slow";
|
|
16
45
|
/** Manual priority overrides by URL pattern */
|
|
17
|
-
significance?: Record<string,
|
|
46
|
+
significance?: Record<string, "high" | "normal" | "low">;
|
|
47
|
+
/** Weights for priority calculation (default: all 1) */
|
|
48
|
+
weights?: CacheWeights;
|
|
49
|
+
/** Custom priority function for complete control */
|
|
50
|
+
customPriorityFn?: CustomPriorityFunction;
|
|
51
|
+
/** Enable detailed event logging to IndexedDB */
|
|
52
|
+
enableDetailedLogs?: boolean;
|
|
53
|
+
/** Callback for all cache events */
|
|
54
|
+
onCacheEvent?: (event: CacheEvent) => void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface CacheLog {
|
|
58
|
+
type: string;
|
|
59
|
+
url: string;
|
|
60
|
+
reason: string;
|
|
61
|
+
metadata?: any;
|
|
62
|
+
timestamp: number;
|
|
63
|
+
date: string;
|
|
18
64
|
}
|
|
19
65
|
|
|
20
66
|
export interface SmartOfflineSDK {
|
|
21
67
|
init(config?: SmartOfflineConfig): void;
|
|
68
|
+
on(eventType: 'cache' | 'skip' | 'serve' | 'clear' | 'error', callback: (event: CacheEvent) => void): void;
|
|
69
|
+
off(eventType: 'cache' | 'skip' | 'serve' | 'clear' | 'error', callback: (event: CacheEvent) => void): void;
|
|
70
|
+
getCacheLogs(): Promise<CacheLog[]>;
|
|
71
|
+
clearCacheLogs(): Promise<void>;
|
|
22
72
|
}
|
|
23
73
|
|
|
24
74
|
export declare const SmartOffline: SmartOfflineSDK;
|
|
25
75
|
export default SmartOffline;
|
|
76
|
+
|
package/src/index.js
CHANGED
|
@@ -7,7 +7,20 @@
|
|
|
7
7
|
* - maxResourceSize: max bytes to cache per resource; larger resources skipped (default Infinity)
|
|
8
8
|
* - networkQuality: 'auto' | 'fast' | 'slow' — affects caching aggressiveness (default 'auto')
|
|
9
9
|
* - significance: { [urlPattern]: 'high' | 'normal' | 'low' } — manual priority overrides
|
|
10
|
+
* - weights: { frequency: number, recency: number, size: number } — priority weights (default all 1)
|
|
11
|
+
* - customPriorityFn: (usage, url, config) => number — custom priority function (0-100)
|
|
12
|
+
* - onCacheEvent: (event) => void — callback for cache events (cache, skip, serve, clear)
|
|
10
13
|
*/
|
|
14
|
+
|
|
15
|
+
// Event listener registry
|
|
16
|
+
const eventListeners = {
|
|
17
|
+
cache: [],
|
|
18
|
+
skip: [],
|
|
19
|
+
serve: [],
|
|
20
|
+
clear: [],
|
|
21
|
+
error: []
|
|
22
|
+
};
|
|
23
|
+
|
|
11
24
|
function init(config = {}) {
|
|
12
25
|
if (!("serviceWorker" in navigator)) {
|
|
13
26
|
console.warn("Service Workers not supported");
|
|
@@ -23,11 +36,38 @@ function init(config = {}) {
|
|
|
23
36
|
maxResourceSize: config.maxResourceSize ?? Infinity,
|
|
24
37
|
networkQuality: config.networkQuality ?? "auto",
|
|
25
38
|
significance: config.significance ?? {},
|
|
39
|
+
|
|
40
|
+
// New features
|
|
41
|
+
weights: {
|
|
42
|
+
frequency: config.weights?.frequency ?? 1,
|
|
43
|
+
recency: config.weights?.recency ?? 1,
|
|
44
|
+
size: config.weights?.size ?? 1
|
|
45
|
+
},
|
|
46
|
+
customPriorityFn: config.customPriorityFn ? config.customPriorityFn.toString() : null,
|
|
47
|
+
enableDetailedLogs: config.enableDetailedLogs ?? false
|
|
26
48
|
};
|
|
27
49
|
|
|
28
|
-
|
|
50
|
+
// Register event listener if provided
|
|
51
|
+
if (config.onCacheEvent) {
|
|
52
|
+
eventListeners.cache.push(config.onCacheEvent);
|
|
53
|
+
eventListeners.skip.push(config.onCacheEvent);
|
|
54
|
+
eventListeners.serve.push(config.onCacheEvent);
|
|
55
|
+
eventListeners.clear.push(config.onCacheEvent);
|
|
56
|
+
eventListeners.error.push(config.onCacheEvent);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
navigator.serviceWorker.register("/smart-offline-sw.js").then((registration) => {
|
|
29
60
|
console.log("Smart Offline Service Worker registered");
|
|
30
61
|
|
|
62
|
+
// Listen for messages from SW
|
|
63
|
+
navigator.serviceWorker.addEventListener('message', (event) => {
|
|
64
|
+
if (event.data && event.data.type) {
|
|
65
|
+
const eventType = event.data.type.replace('CACHE_', '').toLowerCase();
|
|
66
|
+
const listeners = eventListeners[eventType] || [];
|
|
67
|
+
listeners.forEach(fn => fn(event.data));
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
31
71
|
navigator.serviceWorker.ready.then(() => {
|
|
32
72
|
if (navigator.serviceWorker.controller) {
|
|
33
73
|
navigator.serviceWorker.controller.postMessage({
|
|
@@ -59,7 +99,63 @@ function init(config = {}) {
|
|
|
59
99
|
});
|
|
60
100
|
}
|
|
61
101
|
|
|
62
|
-
|
|
102
|
+
// API for managing event listeners
|
|
103
|
+
function on(eventType, callback) {
|
|
104
|
+
if (eventListeners[eventType]) {
|
|
105
|
+
eventListeners[eventType].push(callback);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function off(eventType, callback) {
|
|
110
|
+
if (eventListeners[eventType]) {
|
|
111
|
+
const index = eventListeners[eventType].indexOf(callback);
|
|
112
|
+
if (index > -1) {
|
|
113
|
+
eventListeners[eventType].splice(index, 1);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// API to get cache logs
|
|
119
|
+
async function getCacheLogs() {
|
|
120
|
+
const db = await openDB('smart-offline-logs', 1);
|
|
121
|
+
const tx = db.transaction('logs', 'readonly');
|
|
122
|
+
const store = tx.objectStore('logs');
|
|
123
|
+
return new Promise((resolve) => {
|
|
124
|
+
const request = store.getAll();
|
|
125
|
+
request.onsuccess = () => resolve(request.result || []);
|
|
126
|
+
request.onerror = () => resolve([]);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// API to clear cache logs
|
|
131
|
+
async function clearCacheLogs() {
|
|
132
|
+
const db = await openDB('smart-offline-logs', 1);
|
|
133
|
+
const tx = db.transaction('logs', 'readwrite');
|
|
134
|
+
const store = tx.objectStore('logs');
|
|
135
|
+
store.clear();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function openDB(name, version) {
|
|
139
|
+
return new Promise((resolve, reject) => {
|
|
140
|
+
const request = indexedDB.open(name, version);
|
|
141
|
+
request.onupgradeneeded = () => {
|
|
142
|
+
const db = request.result;
|
|
143
|
+
if (!db.objectStoreNames.contains('logs')) {
|
|
144
|
+
db.createObjectStore('logs', { autoIncrement: true });
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
request.onsuccess = () => resolve(request.result);
|
|
148
|
+
request.onerror = () => reject(request.error);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const SmartOffline = {
|
|
153
|
+
init,
|
|
154
|
+
on,
|
|
155
|
+
off,
|
|
156
|
+
getCacheLogs,
|
|
157
|
+
clearCacheLogs
|
|
158
|
+
};
|
|
63
159
|
|
|
64
160
|
export { SmartOffline }; // ✅ named export
|
|
65
161
|
export default SmartOffline; // ✅ default export
|