@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/src/index.cjs.js CHANGED
@@ -1,88 +1,283 @@
1
1
  /**
2
- * SmartOffline SDK (CommonJS version)
2
+ * SmartOffline SDK (CommonJS version) - v2.0
3
+ *
4
+ * Complete, reliable offline-first caching SDK for web applications.
5
+ *
6
+ * Usage:
7
+ * ```javascript
8
+ * const { SmartOffline, setupSmartOffline } = require('@soham20/smart-offline-sdk')
9
+ *
10
+ * // Initialize early in your app
11
+ * setupSmartOffline({
12
+ * pages: ['/dashboard/*', '/products/*'],
13
+ * apis: ['/api/v1/*'],
14
+ * debug: true
15
+ * }).then(result => {
16
+ * if (result.success) {
17
+ * console.log('SmartOffline ready!')
18
+ * }
19
+ * })
20
+ * ```
3
21
  */
22
+
23
+ // ============================================================================
24
+ // DEFAULT CONFIGURATION
25
+ // ============================================================================
26
+
27
+ const DEFAULT_CONFIG = {
28
+ pages: [],
29
+ apis: [],
30
+ debug:
31
+ typeof process !== "undefined" &&
32
+ process.env &&
33
+ process.env.NODE_ENV !== "production",
34
+ frequencyThreshold: 3,
35
+ recencyThreshold: 24 * 60 * 60 * 1000, // 24 hours
36
+ maxResourceSize: 10 * 1024 * 1024, // 10MB
37
+ networkQuality: "auto",
38
+ significance: {},
39
+ weights: { frequency: 1, recency: 1, size: 1 },
40
+ customPriorityFn: null,
41
+ enableDetailedLogs: false,
42
+ serviceWorkerPath: "/smart-offline-sw.js",
43
+ serviceWorkerScope: "/",
44
+ };
45
+
46
+ // ============================================================================
47
+ // STATE
48
+ // ============================================================================
49
+
50
+ let isInitialized = false;
51
+ let serviceWorkerRegistration = null;
52
+ let currentConfig = { ...DEFAULT_CONFIG };
53
+
54
+ // Event listener registry
4
55
  const eventListeners = {
5
56
  cache: [],
6
57
  skip: [],
7
58
  serve: [],
8
59
  clear: [],
9
- error: []
60
+ error: [],
10
61
  };
11
62
 
12
- function init(config = {}) {
63
+ // ============================================================================
64
+ // MAIN SETUP FUNCTION
65
+ // ============================================================================
66
+
67
+ /**
68
+ * Initialize the SmartOffline SDK with complete setup
69
+ */
70
+ async function setupSmartOffline(config = {}) {
71
+ // Merge with defaults
72
+ currentConfig = { ...DEFAULT_CONFIG, ...config };
73
+
74
+ // Check if already initialized
75
+ if (isInitialized) {
76
+ if (currentConfig.debug) {
77
+ console.warn("[SmartOffline] Already initialized, updating config...");
78
+ }
79
+ await sendConfigToServiceWorker();
80
+ return { success: true, registration: serviceWorkerRegistration };
81
+ }
82
+
83
+ // Check browser support
13
84
  if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) {
14
- console.warn("Service Workers not supported");
15
- return;
16
- }
17
-
18
- const sdkConfig = {
19
- pages: config.pages || [],
20
- apis: config.apis || [],
21
- debug: config.debug || false,
22
- frequencyThreshold: config.frequencyThreshold ?? 3,
23
- recencyThreshold: config.recencyThreshold ?? 24 * 60 * 60 * 1000,
24
- maxResourceSize: config.maxResourceSize ?? Infinity,
25
- networkQuality: config.networkQuality ?? "auto",
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
34
- };
85
+ console.error(
86
+ "[SmartOffline] Service Workers not supported in this browser",
87
+ );
88
+ return { success: false, error: "Service Workers not supported" };
89
+ }
35
90
 
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);
91
+ if (typeof window === "undefined" || !("caches" in window)) {
92
+ console.error("[SmartOffline] Cache API not supported in this browser");
93
+ return { success: false, error: "Cache API not supported" };
42
94
  }
43
95
 
44
- navigator.serviceWorker.register("/smart-offline-sw.js").then(() => {
45
- console.log("Smart Offline Service Worker registered");
96
+ try {
97
+ // Register service worker
98
+ if (currentConfig.debug) {
99
+ console.log("[SmartOffline] Registering service worker...");
100
+ }
46
101
 
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
- });
102
+ serviceWorkerRegistration = await navigator.serviceWorker.register(
103
+ currentConfig.serviceWorkerPath,
104
+ { scope: currentConfig.serviceWorkerScope },
105
+ );
54
106
 
55
- navigator.serviceWorker.ready.then(() => {
56
- if (navigator.serviceWorker.controller) {
57
- navigator.serviceWorker.controller.postMessage({
58
- type: "INIT_CONFIG",
59
- payload: sdkConfig,
60
- });
107
+ if (currentConfig.debug) {
108
+ console.log(
109
+ "[SmartOffline] Service worker registered:",
110
+ serviceWorkerRegistration.scope,
111
+ );
112
+ }
113
+
114
+ // Wait for the service worker to be ready
115
+ await navigator.serviceWorker.ready;
116
+
117
+ if (currentConfig.debug) {
118
+ console.log("[SmartOffline] Service worker ready");
119
+ }
120
+
121
+ // Send configuration to service worker
122
+ await sendConfigToServiceWorker();
123
+
124
+ // Set up event listeners
125
+ setupEventListeners();
61
126
 
62
- if (sdkConfig.debug) {
63
- console.log("[SmartOffline] Config sent to SW:", sdkConfig);
127
+ isInitialized = true;
128
+
129
+ if (currentConfig.debug) {
130
+ console.log("[SmartOffline] Setup complete! Configuration:", {
131
+ pages: currentConfig.pages,
132
+ apis: currentConfig.apis,
133
+ frequencyThreshold: currentConfig.frequencyThreshold,
134
+ recencyThreshold:
135
+ currentConfig.recencyThreshold / (60 * 60 * 1000) + "h",
136
+ });
137
+ }
138
+
139
+ return { success: true, registration: serviceWorkerRegistration };
140
+ } catch (error) {
141
+ console.error("[SmartOffline] Setup failed:", error);
142
+ return {
143
+ success: false,
144
+ error: error instanceof Error ? error.message : String(error),
145
+ };
146
+ }
147
+ }
148
+
149
+ // ============================================================================
150
+ // HELPER FUNCTIONS
151
+ // ============================================================================
152
+
153
+ async function sendConfigToServiceWorker() {
154
+ const controller = navigator.serviceWorker.controller;
155
+
156
+ if (!controller) {
157
+ await new Promise((resolve) => setTimeout(resolve, 100));
158
+ const reg = await navigator.serviceWorker.ready;
159
+ const worker = reg.active;
160
+
161
+ if (worker) {
162
+ sendConfigMessage(worker);
163
+ }
164
+ } else {
165
+ sendConfigMessage(controller);
166
+ }
167
+ }
168
+
169
+ function sendConfigMessage(worker) {
170
+ const transferableConfig = {
171
+ ...currentConfig,
172
+ customPriorityFn: currentConfig.customPriorityFn
173
+ ? currentConfig.customPriorityFn.toString()
174
+ : null,
175
+ onCacheEvent: undefined,
176
+ };
177
+
178
+ worker.postMessage({
179
+ type: "INIT_CONFIG",
180
+ payload: transferableConfig,
181
+ });
182
+
183
+ if (currentConfig.debug) {
184
+ console.log("[SmartOffline] Config sent to service worker");
185
+ }
186
+ }
187
+
188
+ function setupEventListeners() {
189
+ navigator.serviceWorker.addEventListener("message", (event) => {
190
+ if (event.data && typeof event.data.type === "string") {
191
+ if (event.data.type.startsWith("CACHE_")) {
192
+ const cacheEvent = {
193
+ type: event.data.type,
194
+ url: event.data.url,
195
+ reason: event.data.reason,
196
+ metadata: event.data.metadata || {},
197
+ timestamp: event.data.timestamp || Date.now(),
198
+ };
199
+
200
+ // Call user callback if provided
201
+ if (currentConfig.onCacheEvent) {
202
+ currentConfig.onCacheEvent(cacheEvent);
64
203
  }
65
- }
66
- });
67
204
 
68
- navigator.serviceWorker.addEventListener("controllerchange", () => {
69
- if (navigator.serviceWorker.controller) {
70
- navigator.serviceWorker.controller.postMessage({
71
- type: "INIT_CONFIG",
72
- payload: sdkConfig,
73
- });
205
+ // Call registered event listeners
206
+ const eventType = cacheEvent.type.replace("CACHE_", "").toLowerCase();
207
+ const listeners = eventListeners[eventType] || [];
208
+ listeners.forEach((fn) => fn(cacheEvent));
209
+
210
+ // Debug logging
211
+ if (currentConfig.debug) {
212
+ const icon = {
213
+ CACHE_CACHE: "💾",
214
+ CACHE_SKIP: "⏭️",
215
+ CACHE_SERVE: "📤",
216
+ CACHE_ERROR: "❌",
217
+ };
74
218
 
75
- if (sdkConfig.debug) {
76
219
  console.log(
77
- "[SmartOffline] Config sent after controllerchange:",
78
- sdkConfig,
220
+ "[SmartOffline] " +
221
+ (icon[cacheEvent.type] || "📝") +
222
+ " " +
223
+ cacheEvent.type.replace("CACHE_", "") +
224
+ ":",
225
+ cacheEvent.url.replace(
226
+ typeof window !== "undefined" ? window.location.origin : "",
227
+ "",
228
+ ),
229
+ "(" + cacheEvent.reason + ")",
79
230
  );
80
231
  }
81
232
  }
82
- });
233
+ }
83
234
  });
84
235
  }
85
236
 
237
+ function openDB(name, version) {
238
+ return new Promise((resolve, reject) => {
239
+ const request = indexedDB.open(name, version);
240
+ request.onupgradeneeded = () => {
241
+ const db = request.result;
242
+ if (
243
+ name === "smart-offline-logs-v2" &&
244
+ !db.objectStoreNames.contains("logs")
245
+ ) {
246
+ db.createObjectStore("logs", { autoIncrement: true });
247
+ }
248
+ if (
249
+ name === "smart-offline-usage-v2" &&
250
+ !db.objectStoreNames.contains("usage")
251
+ ) {
252
+ db.createObjectStore("usage", { keyPath: "url" });
253
+ }
254
+ };
255
+ request.onsuccess = () => resolve(request.result);
256
+ request.onerror = () => reject(request.error);
257
+ });
258
+ }
259
+
260
+ // ============================================================================
261
+ // PUBLIC API
262
+ // ============================================================================
263
+
264
+ async function updateConfig(newConfig) {
265
+ currentConfig = { ...currentConfig, ...newConfig };
266
+ await sendConfigToServiceWorker();
267
+ }
268
+
269
+ function getConfig() {
270
+ return { ...currentConfig };
271
+ }
272
+
273
+ function isSmartOfflineReady() {
274
+ return isInitialized;
275
+ }
276
+
277
+ function getServiceWorkerRegistration() {
278
+ return serviceWorkerRegistration;
279
+ }
280
+
86
281
  function on(eventType, callback) {
87
282
  if (eventListeners[eventType]) {
88
283
  eventListeners[eventType].push(callback);
@@ -99,55 +294,326 @@ function off(eventType, callback) {
99
294
  }
100
295
 
101
296
  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
- });
297
+ try {
298
+ const db = await openDB("smart-offline-logs-v2", 1);
299
+ const tx = db.transaction("logs", "readonly");
300
+ const store = tx.objectStore("logs");
301
+ return new Promise((resolve) => {
302
+ const request = store.getAll();
303
+ request.onsuccess = () => resolve(request.result || []);
304
+ request.onerror = () => resolve([]);
305
+ });
306
+ } catch {
307
+ return [];
308
+ }
110
309
  }
111
310
 
112
311
  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();
312
+ try {
313
+ const db = await openDB("smart-offline-logs-v2", 1);
314
+ const tx = db.transaction("logs", "readwrite");
315
+ const store = tx.objectStore("logs");
316
+ store.clear();
317
+ } catch {
318
+ // Ignore errors
319
+ }
117
320
  }
118
321
 
119
- async function clearCache() {
322
+ async function clearAllCache() {
120
323
  const cacheNames = await caches.keys();
121
- const smartOfflineCaches = cacheNames.filter(name => name.includes('smart-offline'));
122
- await Promise.all(smartOfflineCaches.map(name => caches.delete(name)));
123
-
124
- // Also clear usage tracking database
125
- const db = await openDB('smart-offline', 1);
126
- const tx = db.transaction('usage', 'readwrite');
127
- const store = tx.objectStore('usage');
128
- store.clear();
129
-
130
- console.log('🗑️ All SmartOffline caches and usage data cleared');
324
+ for (const name of cacheNames) {
325
+ if (name.startsWith("smart-offline")) {
326
+ await caches.delete(name);
327
+ }
328
+ }
329
+
330
+ const dbNames = ["smart-offline-usage-v2", "smart-offline-logs-v2"];
331
+ for (const dbName of dbNames) {
332
+ try {
333
+ indexedDB.deleteDatabase(dbName);
334
+ } catch {
335
+ // Ignore errors
336
+ }
337
+ }
338
+
339
+ if (currentConfig.debug) {
340
+ console.log("[SmartOffline] All cache data cleared");
341
+ }
131
342
  }
132
343
 
133
- function openDB(name, version) {
134
- return new Promise((resolve, reject) => {
135
- const request = indexedDB.open(name, version);
136
- request.onupgradeneeded = () => {
137
- const db = request.result;
138
- if (name === 'smart-offline-logs' && !db.objectStoreNames.contains('logs')) {
139
- db.createObjectStore('logs', { autoIncrement: true });
140
- }
141
- if (name === 'smart-offline' && !db.objectStoreNames.contains('usage')) {
142
- db.createObjectStore('usage', { keyPath: 'url' });
344
+ async function getCacheStats() {
345
+ let cachedItems = 0;
346
+ let cacheSize = 0;
347
+ let trackedUrls = 0;
348
+
349
+ try {
350
+ const cache = await caches.open("smart-offline-cache-v2");
351
+ const keys = await cache.keys();
352
+ cachedItems = keys.length;
353
+
354
+ for (const request of keys) {
355
+ const response = await cache.match(request);
356
+ if (response) {
357
+ const size = parseInt(
358
+ response.headers.get("content-length") || "0",
359
+ 10,
360
+ );
361
+ cacheSize += size;
143
362
  }
144
- };
145
- request.onsuccess = () => resolve(request.result);
146
- request.onerror = () => reject(request.error);
363
+ }
364
+ } catch {
365
+ // Ignore errors
366
+ }
367
+
368
+ try {
369
+ const count = await new Promise((resolve) => {
370
+ const request = indexedDB.open("smart-offline-usage-v2", 1);
371
+ request.onsuccess = () => {
372
+ const db = request.result;
373
+ try {
374
+ const tx = db.transaction("usage", "readonly");
375
+ const store = tx.objectStore("usage");
376
+ const countReq = store.count();
377
+ countReq.onsuccess = () => resolve(countReq.result);
378
+ countReq.onerror = () => resolve(0);
379
+ } catch {
380
+ resolve(0);
381
+ }
382
+ };
383
+ request.onerror = () => resolve(0);
384
+ });
385
+ trackedUrls = count;
386
+ } catch {
387
+ // Ignore errors
388
+ }
389
+
390
+ return { cachedItems, trackedUrls, cacheSize };
391
+ }
392
+
393
+ async function forceUpdate() {
394
+ if (serviceWorkerRegistration) {
395
+ await serviceWorkerRegistration.update();
396
+ if (currentConfig.debug) {
397
+ console.log("[SmartOffline] Service worker update triggered");
398
+ }
399
+ }
400
+ }
401
+
402
+ async function uninstall() {
403
+ if (serviceWorkerRegistration) {
404
+ await serviceWorkerRegistration.unregister();
405
+ }
406
+ await clearAllCache();
407
+ isInitialized = false;
408
+ serviceWorkerRegistration = null;
409
+
410
+ if (currentConfig.debug) {
411
+ console.log("[SmartOffline] Uninstalled and cleaned up");
412
+ }
413
+ }
414
+
415
+ function init(config = {}) {
416
+ setupSmartOffline(config);
417
+ }
418
+
419
+ // ============================================================================
420
+ // TEST UTILITIES
421
+ // ============================================================================
422
+
423
+ function matchesPattern(url, pattern) {
424
+ if (!pattern.includes("*")) {
425
+ return url.includes(pattern);
426
+ }
427
+ const regexPattern = pattern
428
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&")
429
+ .replace(/\*/g, ".*");
430
+ return new RegExp(regexPattern).test(url);
431
+ }
432
+
433
+ function isHighPriority(usage, url, config = {}) {
434
+ const finalConfig = { ...DEFAULT_CONFIG, ...config };
435
+
436
+ if (
437
+ finalConfig.customPriorityFn &&
438
+ typeof finalConfig.customPriorityFn === "function"
439
+ ) {
440
+ try {
441
+ return finalConfig.customPriorityFn(usage, url, finalConfig) > 50;
442
+ } catch (e) {
443
+ console.error("Custom priority function error:", e);
444
+ }
445
+ }
446
+
447
+ for (const pattern in finalConfig.significance) {
448
+ if (url.includes(pattern)) {
449
+ if (finalConfig.significance[pattern] === "high") return true;
450
+ if (finalConfig.significance[pattern] === "low") return false;
451
+ }
452
+ }
453
+
454
+ if (!usage) return false;
455
+
456
+ const weights = finalConfig.weights || { frequency: 1, recency: 1, size: 1 };
457
+ const frequencyScore = Math.min(
458
+ 100,
459
+ (usage.count / finalConfig.frequencyThreshold) * 100,
460
+ );
461
+ const timeSinceAccess = Date.now() - usage.lastAccessed;
462
+ const recencyScore = Math.max(
463
+ 0,
464
+ 100 - (timeSinceAccess / finalConfig.recencyThreshold) * 100,
465
+ );
466
+ const totalWeight = weights.frequency + weights.recency;
467
+ const weightedScore =
468
+ (frequencyScore * weights.frequency + recencyScore * weights.recency) /
469
+ totalWeight;
470
+
471
+ return weightedScore > 50;
472
+ }
473
+
474
+ function shouldCacheUrl(url, config = {}) {
475
+ const pages = config.pages || [];
476
+ const apis = config.apis || [];
477
+ const isPage = pages.some((p) => matchesPattern(url, p));
478
+ const isAPI = apis.some((a) => matchesPattern(url, a));
479
+ return { isPage, isAPI };
480
+ }
481
+
482
+ // SmartOfflineTestSuite class
483
+ function SmartOfflineTestSuite(config = {}) {
484
+ this.config = { ...DEFAULT_CONFIG, ...config };
485
+ this.results = [];
486
+ }
487
+
488
+ SmartOfflineTestSuite.prototype.runAll = async function () {
489
+ this.results = [];
490
+ // Add basic algorithm tests
491
+ this.testPatternMatching();
492
+ this.testFrequencyPriority();
493
+ return this.results;
494
+ };
495
+
496
+ SmartOfflineTestSuite.prototype.testPatternMatching = function () {
497
+ const tests = [
498
+ { url: "/admin/charts", pattern: "/admin/charts", expected: true },
499
+ { url: "/admin/charts/123", pattern: "/admin/charts/*", expected: true },
500
+ ];
501
+ let passed = true;
502
+ for (const test of tests) {
503
+ if (matchesPattern(test.url, test.pattern) !== test.expected) {
504
+ passed = false;
505
+ break;
506
+ }
507
+ }
508
+ this.results.push({
509
+ name: "Pattern Matching",
510
+ passed,
511
+ message: passed ? "All patterns matched" : "Pattern matching failed",
147
512
  });
513
+ };
514
+
515
+ SmartOfflineTestSuite.prototype.testFrequencyPriority = function () {
516
+ const highUsage = { url: "/test", count: 5, lastAccessed: Date.now() };
517
+ const result = isHighPriority(highUsage, "/test", this.config);
518
+ this.results.push({
519
+ name: "Frequency Priority",
520
+ passed: result,
521
+ message: result ? "High frequency works" : "Frequency check failed",
522
+ });
523
+ };
524
+
525
+ SmartOfflineTestSuite.prototype.printResults = function () {
526
+ console.info("\n========================================");
527
+ console.info(" SmartOffline SDK Test Results");
528
+ console.info("========================================\n");
529
+ for (const result of this.results) {
530
+ console.info(
531
+ (result.passed ? "✅" : "❌") + " " + result.name + ": " + result.message,
532
+ );
533
+ }
534
+ };
535
+
536
+ // CacheInspector class
537
+ function CacheInspector() {}
538
+
539
+ CacheInspector.prototype.getCachedItems = async function () {
540
+ try {
541
+ const cache = await caches.open("smart-offline-cache-v2");
542
+ const keys = await cache.keys();
543
+ const items = [];
544
+ for (const request of keys) {
545
+ const response = await cache.match(request);
546
+ if (response) {
547
+ items.push({
548
+ url: request.url,
549
+ size: parseInt(response.headers.get("content-length") || "0", 10),
550
+ contentType: response.headers.get("content-type") || "unknown",
551
+ });
552
+ }
553
+ }
554
+ return items;
555
+ } catch {
556
+ return [];
557
+ }
558
+ };
559
+
560
+ CacheInspector.prototype.showAll = async function () {
561
+ const items = await this.getCachedItems();
562
+ console.info("\n🔍 SmartOffline Cache Inspector");
563
+ console.info("📦 Cached Items (" + items.length + "):");
564
+ console.table(items);
565
+ };
566
+
567
+ async function runSmartOfflineTests(config) {
568
+ const suite = new SmartOfflineTestSuite(config);
569
+ const results = await suite.runAll();
570
+ suite.printResults();
571
+ return results;
148
572
  }
149
573
 
150
- const SmartOffline = { init, on, off, getCacheLogs, clearCacheLogs, clearCache };
574
+ // ============================================================================
575
+ // EXPORTS
576
+ // ============================================================================
577
+
578
+ const SmartOffline = {
579
+ setup: setupSmartOffline,
580
+ init,
581
+ updateConfig,
582
+ getConfig,
583
+ isReady: isSmartOfflineReady,
584
+ getRegistration: getServiceWorkerRegistration,
585
+ on,
586
+ off,
587
+ clearCache: clearAllCache,
588
+ getStats: getCacheStats,
589
+ getCacheLogs,
590
+ clearCacheLogs,
591
+ forceUpdate,
592
+ uninstall,
593
+ };
594
+
595
+ module.exports = {
596
+ SmartOffline,
597
+ setupSmartOffline,
598
+ updateConfig,
599
+ getConfig,
600
+ isSmartOfflineReady,
601
+ getServiceWorkerRegistration,
602
+ on,
603
+ off,
604
+ clearAllCache,
605
+ getCacheStats,
606
+ getCacheLogs,
607
+ clearCacheLogs,
608
+ forceUpdate,
609
+ uninstall,
610
+ init,
611
+ matchesPattern,
612
+ isHighPriority,
613
+ shouldCacheUrl,
614
+ SmartOfflineTestSuite,
615
+ CacheInspector,
616
+ runSmartOfflineTests,
617
+ };
151
618
 
152
- module.exports = { SmartOffline };
153
619
  module.exports.default = SmartOffline;