@xiboplayer/cache 0.1.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/docs/CACHE_PROXY_ARCHITECTURE.md +439 -0
- package/docs/README.md +118 -0
- package/package.json +41 -0
- package/src/cache-proxy.js +493 -0
- package/src/cache-proxy.test.js +391 -0
- package/src/cache.js +739 -0
- package/src/cache.test.js +760 -0
- package/src/download-manager.js +434 -0
- package/src/download-manager.test.js +726 -0
- package/src/index.js +4 -0
- package/src/test-utils.js +133 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
# CacheProxy Architecture - Unified Cache Interface
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The CacheProxy provides a unified interface for file caching and downloading that works seamlessly with both Service Worker and direct cache implementations. This abstraction enables platform-independent code and automatic backend selection.
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
┌─────────────────────────────────────────────────────┐
|
|
11
|
+
│ Client Code (PWA, XLR, Mobile, etc.) │
|
|
12
|
+
│ - Uses CacheProxy for all file operations │
|
|
13
|
+
│ - No knowledge of backend implementation │
|
|
14
|
+
│ - Minimal integration code │
|
|
15
|
+
└─────────────────────────────────────────────────────┘
|
|
16
|
+
↓
|
|
17
|
+
┌─────────────────────────────────────────────────────┐
|
|
18
|
+
│ CacheProxy Module (Shared Interface) │
|
|
19
|
+
│ - Detects environment (Service Worker vs Direct) │
|
|
20
|
+
│ - Provides unified API: get/download/cache files │
|
|
21
|
+
│ - No client code changes needed │
|
|
22
|
+
└─────────────────────────────────────────────────────┘
|
|
23
|
+
↓
|
|
24
|
+
┌───────────────┴───────────────┐
|
|
25
|
+
↓ ↓
|
|
26
|
+
┌─────────────────┐ ┌─────────────────┐
|
|
27
|
+
│ Service Worker │ │ Direct Cache │
|
|
28
|
+
│ Backend │ │ Backend │
|
|
29
|
+
│ │ │ │
|
|
30
|
+
│ - Downloads in │ │ - cache.js │
|
|
31
|
+
│ background │ │ - IndexedDB │
|
|
32
|
+
│ - Caches files │ │ - Blocking │
|
|
33
|
+
│ - Serves via │ │ downloads │
|
|
34
|
+
│ fetch │ │ │
|
|
35
|
+
└─────────────────┘ └─────────────────┘
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Components
|
|
39
|
+
|
|
40
|
+
### 1. CacheProxy (Main Interface)
|
|
41
|
+
|
|
42
|
+
**Location**: `packages/core/src/cache-proxy.js`
|
|
43
|
+
|
|
44
|
+
**Purpose**: Auto-detects backend and provides unified API
|
|
45
|
+
|
|
46
|
+
**API**:
|
|
47
|
+
```javascript
|
|
48
|
+
class CacheProxy {
|
|
49
|
+
async init()
|
|
50
|
+
async getFile(type: string, id: string): Promise<Blob|null>
|
|
51
|
+
async requestDownload(files: FileInfo[]): Promise<void>
|
|
52
|
+
async isCached(type: string, id: string): Promise<boolean>
|
|
53
|
+
getBackendType(): string // 'service-worker' | 'direct'
|
|
54
|
+
isUsingServiceWorker(): boolean
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Usage**:
|
|
59
|
+
```javascript
|
|
60
|
+
import { CacheProxy } from '@core/cache-proxy.js';
|
|
61
|
+
|
|
62
|
+
// Initialize
|
|
63
|
+
const proxy = new CacheProxy(cacheManager);
|
|
64
|
+
await proxy.init();
|
|
65
|
+
|
|
66
|
+
// Get file (works with both backends)
|
|
67
|
+
const blob = await proxy.getFile('media', '123');
|
|
68
|
+
|
|
69
|
+
// Request downloads
|
|
70
|
+
await proxy.requestDownload([
|
|
71
|
+
{ id: '1', type: 'media', path: 'https://...', md5: '...' }
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
// Check cache
|
|
75
|
+
const cached = await proxy.isCached('layout', '456');
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 2. ServiceWorkerBackend
|
|
79
|
+
|
|
80
|
+
**Purpose**: Routes requests to Service Worker
|
|
81
|
+
|
|
82
|
+
**Responsibilities**:
|
|
83
|
+
- Detects Service Worker availability
|
|
84
|
+
- Uses postMessage for downloads
|
|
85
|
+
- Uses fetch for file retrieval
|
|
86
|
+
- Non-blocking downloads (background)
|
|
87
|
+
|
|
88
|
+
**Implementation Details**:
|
|
89
|
+
```javascript
|
|
90
|
+
class ServiceWorkerBackend {
|
|
91
|
+
async getFile(type, id) {
|
|
92
|
+
// Fetch from /player/cache/{type}/{id}
|
|
93
|
+
// Service Worker intercepts and serves from cache
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async requestDownload(files) {
|
|
97
|
+
// postMessage to Service Worker
|
|
98
|
+
// Downloads happen in background
|
|
99
|
+
// Returns immediately
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 3. DirectCacheBackend
|
|
105
|
+
|
|
106
|
+
**Purpose**: Fallback when Service Worker unavailable
|
|
107
|
+
|
|
108
|
+
**Responsibilities**:
|
|
109
|
+
- Uses cache.js directly
|
|
110
|
+
- Blocking downloads
|
|
111
|
+
- IndexedDB metadata storage
|
|
112
|
+
- Browser Cache API for files
|
|
113
|
+
|
|
114
|
+
**Implementation Details**:
|
|
115
|
+
```javascript
|
|
116
|
+
class DirectCacheBackend {
|
|
117
|
+
async getFile(type, id) {
|
|
118
|
+
// Call cacheManager.getCachedFile()
|
|
119
|
+
// Direct Cache API access
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async requestDownload(files) {
|
|
123
|
+
// Sequential downloads
|
|
124
|
+
// Blocks until complete
|
|
125
|
+
// Fallback for non-Service Worker environments
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Backend Selection
|
|
131
|
+
|
|
132
|
+
CacheProxy automatically selects the best backend:
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
async init() {
|
|
136
|
+
// Try Service Worker first
|
|
137
|
+
if (navigator.serviceWorker?.controller) {
|
|
138
|
+
this.backend = new ServiceWorkerBackend();
|
|
139
|
+
this.backendType = 'service-worker';
|
|
140
|
+
} else {
|
|
141
|
+
// Fallback to direct cache
|
|
142
|
+
this.backend = new DirectCacheBackend(cacheManager);
|
|
143
|
+
this.backendType = 'direct';
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Selection Criteria
|
|
149
|
+
|
|
150
|
+
| Condition | Backend | Reason |
|
|
151
|
+
|-----------|---------|--------|
|
|
152
|
+
| Service Worker active | ServiceWorkerBackend | Better performance, non-blocking |
|
|
153
|
+
| Service Worker not active | DirectCacheBackend | Compatibility, works everywhere |
|
|
154
|
+
| Service Worker failed | DirectCacheBackend | Graceful degradation |
|
|
155
|
+
|
|
156
|
+
## Integration Example
|
|
157
|
+
|
|
158
|
+
### Before (Without CacheProxy)
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
// Complex logic with manual fallback
|
|
162
|
+
const serviceWorkerActive = navigator.serviceWorker?.controller;
|
|
163
|
+
|
|
164
|
+
if (serviceWorkerActive) {
|
|
165
|
+
try {
|
|
166
|
+
await sendFilesToServiceWorker(files);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
for (const file of files) {
|
|
169
|
+
await cacheManager.downloadFile(file);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
for (const file of files) {
|
|
174
|
+
await cacheManager.downloadFile(file);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Separate media retrieval
|
|
179
|
+
const response = await cacheManager.getCachedResponse('media', fileId);
|
|
180
|
+
const blob = await response.blob();
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### After (With CacheProxy)
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
// Simple, unified interface
|
|
187
|
+
await cacheProxy.requestDownload(files);
|
|
188
|
+
|
|
189
|
+
// Consistent file retrieval
|
|
190
|
+
const blob = await cacheProxy.getFile('media', fileId);
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Benefits**:
|
|
194
|
+
- 75% less code
|
|
195
|
+
- Automatic backend selection
|
|
196
|
+
- No error handling duplication
|
|
197
|
+
- Platform-independent
|
|
198
|
+
|
|
199
|
+
## Performance Characteristics
|
|
200
|
+
|
|
201
|
+
### Service Worker Backend
|
|
202
|
+
|
|
203
|
+
| Metric | Value | Notes |
|
|
204
|
+
|--------|-------|-------|
|
|
205
|
+
| Download latency | ~100ms | postMessage + enqueue |
|
|
206
|
+
| File retrieval | ~10ms | Fetch intercept |
|
|
207
|
+
| Blocking | No | Downloads in background |
|
|
208
|
+
| Concurrency | 4 files | Configurable |
|
|
209
|
+
|
|
210
|
+
### Direct Cache Backend
|
|
211
|
+
|
|
212
|
+
| Metric | Value | Notes |
|
|
213
|
+
|--------|-------|-------|
|
|
214
|
+
| Download latency | Varies | Depends on file size |
|
|
215
|
+
| File retrieval | ~5ms | Direct cache access |
|
|
216
|
+
| Blocking | Yes | Sequential downloads |
|
|
217
|
+
| Concurrency | 1 file | No parallelism |
|
|
218
|
+
|
|
219
|
+
## Service Worker Download Flow
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
Client CacheProxy Service Worker
|
|
223
|
+
│ │ │
|
|
224
|
+
│ requestDownload() │ │
|
|
225
|
+
│─────────────────────> │ │
|
|
226
|
+
│ │ postMessage │
|
|
227
|
+
│ │ (DOWNLOAD_FILES) │
|
|
228
|
+
│ │─────────────────────> │
|
|
229
|
+
│ │ │
|
|
230
|
+
│ │ │ enqueue files
|
|
231
|
+
│ │ │ start downloads
|
|
232
|
+
│ │ │
|
|
233
|
+
│ │ acknowledge │
|
|
234
|
+
│ │ <─────────────────────│
|
|
235
|
+
│ return (immediate) │ │
|
|
236
|
+
│ <─────────────────────│ │
|
|
237
|
+
│ │ │
|
|
238
|
+
│ ... continue work ... │ │ ... downloading ...
|
|
239
|
+
│ │ │
|
|
240
|
+
│ getFile('media', '1') │ │
|
|
241
|
+
│─────────────────────> │ │
|
|
242
|
+
│ │ fetch /cache/media/1 │
|
|
243
|
+
│ │─────────────────────> │
|
|
244
|
+
│ │ │
|
|
245
|
+
│ │ │ if cached: serve
|
|
246
|
+
│ │ │ if downloading: wait
|
|
247
|
+
│ │ │ if not found: 404
|
|
248
|
+
│ │ │
|
|
249
|
+
│ │ <─────────────────────│
|
|
250
|
+
│ <─────────────────────│ │
|
|
251
|
+
│ blob │ │
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Direct Cache Download Flow
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
Client CacheProxy Cache.js
|
|
258
|
+
│ │ │
|
|
259
|
+
│ requestDownload() │ │
|
|
260
|
+
│─────────────────────> │ │
|
|
261
|
+
│ │ downloadFile() x N │
|
|
262
|
+
│ │─────────────────────> │
|
|
263
|
+
│ │ │ fetch file
|
|
264
|
+
│ │ │ verify MD5
|
|
265
|
+
│ │ │ cache in Cache API
|
|
266
|
+
│ │ │ save metadata to IDB
|
|
267
|
+
│ │ │
|
|
268
|
+
│ │ <─────────────────────│
|
|
269
|
+
│ return (after all │ │
|
|
270
|
+
│ downloads complete) │ │
|
|
271
|
+
│ <─────────────────────│ │
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Error Handling
|
|
275
|
+
|
|
276
|
+
CacheProxy handles errors gracefully:
|
|
277
|
+
|
|
278
|
+
```javascript
|
|
279
|
+
try {
|
|
280
|
+
await cacheProxy.requestDownload(files);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
// Backend-specific error
|
|
283
|
+
// Already logged by backend
|
|
284
|
+
// Fallback handled automatically
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Error Scenarios
|
|
289
|
+
|
|
290
|
+
| Scenario | Service Worker | Direct Cache |
|
|
291
|
+
|----------|----------------|--------------|
|
|
292
|
+
| Network failure | Retry in SW | Throw error |
|
|
293
|
+
| File not found | 404 on fetch | null on get |
|
|
294
|
+
| MD5 mismatch | Log warning, continue | Log warning, continue |
|
|
295
|
+
| SW not available | Auto-switch to Direct | N/A |
|
|
296
|
+
|
|
297
|
+
## Testing
|
|
298
|
+
|
|
299
|
+
### Unit Tests
|
|
300
|
+
|
|
301
|
+
```javascript
|
|
302
|
+
// Test backend detection
|
|
303
|
+
it('should use Service Worker when available', async () => {
|
|
304
|
+
const proxy = new CacheProxy(cacheManager);
|
|
305
|
+
await proxy.init();
|
|
306
|
+
expect(proxy.getBackendType()).toBe('service-worker');
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Test fallback
|
|
310
|
+
it('should fallback to direct cache', async () => {
|
|
311
|
+
// Mock SW not available
|
|
312
|
+
const proxy = new CacheProxy(cacheManager);
|
|
313
|
+
await proxy.init();
|
|
314
|
+
expect(proxy.getBackendType()).toBe('direct');
|
|
315
|
+
});
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Integration Tests
|
|
319
|
+
|
|
320
|
+
```javascript
|
|
321
|
+
// Test file download
|
|
322
|
+
it('should download and cache files', async () => {
|
|
323
|
+
const files = [{ id: '1', type: 'media', path: 'https://...' }];
|
|
324
|
+
await proxy.requestDownload(files);
|
|
325
|
+
|
|
326
|
+
const blob = await proxy.getFile('media', '1');
|
|
327
|
+
expect(blob).toBeTruthy();
|
|
328
|
+
expect(blob.size).toBeGreaterThan(0);
|
|
329
|
+
});
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Migration Guide
|
|
333
|
+
|
|
334
|
+
### For Existing Platforms
|
|
335
|
+
|
|
336
|
+
1. **Import CacheProxy**:
|
|
337
|
+
```javascript
|
|
338
|
+
import { CacheProxy } from '@core/cache-proxy.js';
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
2. **Initialize**:
|
|
342
|
+
```javascript
|
|
343
|
+
const cacheProxy = new CacheProxy(cacheManager);
|
|
344
|
+
await cacheProxy.init();
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
3. **Replace download code**:
|
|
348
|
+
```javascript
|
|
349
|
+
// Old:
|
|
350
|
+
await cacheManager.downloadFile(file);
|
|
351
|
+
|
|
352
|
+
// New:
|
|
353
|
+
await cacheProxy.requestDownload([file]);
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
4. **Replace file retrieval**:
|
|
357
|
+
```javascript
|
|
358
|
+
// Old:
|
|
359
|
+
const response = await cacheManager.getCachedResponse('media', id);
|
|
360
|
+
const blob = await response.blob();
|
|
361
|
+
|
|
362
|
+
// New:
|
|
363
|
+
const blob = await cacheProxy.getFile('media', id);
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### For New Platforms
|
|
367
|
+
|
|
368
|
+
Simply use CacheProxy from the start:
|
|
369
|
+
|
|
370
|
+
```javascript
|
|
371
|
+
class MyPlayer {
|
|
372
|
+
async init() {
|
|
373
|
+
// Initialize CacheProxy
|
|
374
|
+
this.cache = new CacheProxy(cacheManager);
|
|
375
|
+
await this.cache.init();
|
|
376
|
+
|
|
377
|
+
// Use unified API
|
|
378
|
+
const files = await this.xmds.requiredFiles();
|
|
379
|
+
await this.cache.requestDownload(files);
|
|
380
|
+
|
|
381
|
+
const blob = await this.cache.getFile('media', '123');
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
## Future Enhancements
|
|
387
|
+
|
|
388
|
+
### Planned Features
|
|
389
|
+
|
|
390
|
+
1. **Smart Backend Switching**:
|
|
391
|
+
- Monitor Service Worker health
|
|
392
|
+
- Auto-switch if SW becomes unresponsive
|
|
393
|
+
- Fallback to direct cache on errors
|
|
394
|
+
|
|
395
|
+
2. **Progress Tracking**:
|
|
396
|
+
```javascript
|
|
397
|
+
proxy.on('download-progress', (progress) => {
|
|
398
|
+
console.log(`Downloaded ${progress.loaded}/${progress.total}`);
|
|
399
|
+
});
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
3. **Cache Invalidation**:
|
|
403
|
+
```javascript
|
|
404
|
+
await proxy.invalidate('media', '123');
|
|
405
|
+
await proxy.invalidateAll();
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
4. **Prefetching**:
|
|
409
|
+
```javascript
|
|
410
|
+
await proxy.prefetch(['media/1', 'layout/2']);
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
## Benefits
|
|
414
|
+
|
|
415
|
+
### For Developers
|
|
416
|
+
|
|
417
|
+
1. **Simpler Code**: 75% reduction in cache-related code
|
|
418
|
+
2. **Automatic Optimization**: Best backend selected automatically
|
|
419
|
+
3. **Platform Independence**: Same code works everywhere
|
|
420
|
+
4. **Better Testing**: Mock backends for unit tests
|
|
421
|
+
|
|
422
|
+
### For Users
|
|
423
|
+
|
|
424
|
+
1. **Better Performance**: Service Worker when available
|
|
425
|
+
2. **Better Compatibility**: Fallback when SW unavailable
|
|
426
|
+
3. **Transparent**: No difference in functionality
|
|
427
|
+
4. **Reliable**: Graceful degradation on errors
|
|
428
|
+
|
|
429
|
+
## Summary
|
|
430
|
+
|
|
431
|
+
CacheProxy provides a clean abstraction layer that:
|
|
432
|
+
- ✅ Automatically selects best backend
|
|
433
|
+
- ✅ Provides unified API across platforms
|
|
434
|
+
- ✅ Enables non-blocking downloads with Service Worker
|
|
435
|
+
- ✅ Gracefully falls back to direct cache
|
|
436
|
+
- ✅ Simplifies platform-specific code
|
|
437
|
+
- ✅ Makes testing easier
|
|
438
|
+
|
|
439
|
+
**Result**: Platform-independent cache code that just works.
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# @xiboplayer/cache Documentation
|
|
2
|
+
|
|
3
|
+
**Offline caching and download management with parallel chunk downloads.**
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `@xiboplayer/cache` package provides:
|
|
8
|
+
|
|
9
|
+
- **CacheManager** - IndexedDB-based media storage
|
|
10
|
+
- **CacheProxy** - Service Worker integration
|
|
11
|
+
- **DownloadManager** - Parallel chunk downloads (4x faster)
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @xiboplayer/cache
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
import { CacheManager, DownloadManager } from '@xiboplayer/cache';
|
|
23
|
+
|
|
24
|
+
// Initialize cache
|
|
25
|
+
const cache = new CacheManager();
|
|
26
|
+
await cache.initialize();
|
|
27
|
+
|
|
28
|
+
// Download with parallel chunks
|
|
29
|
+
const downloader = new DownloadManager(cache);
|
|
30
|
+
await downloader.downloadFile(url, { chunkSize: 1024 * 1024 });
|
|
31
|
+
|
|
32
|
+
// Retrieve from cache
|
|
33
|
+
const blob = await cache.get(url);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
### Parallel Chunk Downloads
|
|
39
|
+
|
|
40
|
+
Downloads large files in 4 concurrent chunks (configurable), achieving 2-4x speed improvement over sequential downloads.
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
const CONCURRENT_CHUNKS = 4; // Adjust 2-6 based on network
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Cache Validation
|
|
47
|
+
|
|
48
|
+
Automatically validates cached entries:
|
|
49
|
+
- Content-Type verification
|
|
50
|
+
- Size validation (> 100 bytes)
|
|
51
|
+
- Corrupted entry detection
|
|
52
|
+
|
|
53
|
+
### Blob URL Lifecycle
|
|
54
|
+
|
|
55
|
+
Proper blob URL management prevents memory leaks:
|
|
56
|
+
- Layout-scoped tracking
|
|
57
|
+
- Automatic revocation on layout switch
|
|
58
|
+
- Media URL cleanup
|
|
59
|
+
|
|
60
|
+
## API Reference
|
|
61
|
+
|
|
62
|
+
### CacheManager
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
class CacheManager {
|
|
66
|
+
async initialize()
|
|
67
|
+
async get(key)
|
|
68
|
+
async set(key, blob)
|
|
69
|
+
async has(key)
|
|
70
|
+
async delete(key)
|
|
71
|
+
async clear()
|
|
72
|
+
async getSize()
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### DownloadManager
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
class DownloadManager {
|
|
80
|
+
constructor(cacheManager, options)
|
|
81
|
+
async downloadFile(url, options)
|
|
82
|
+
async downloadBatch(urls)
|
|
83
|
+
getProgress(url)
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### CacheProxy
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
class CacheProxy {
|
|
91
|
+
constructor(cacheManager)
|
|
92
|
+
register(serviceWorkerUrl)
|
|
93
|
+
unregister()
|
|
94
|
+
update()
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Performance
|
|
99
|
+
|
|
100
|
+
| Operation | Time |
|
|
101
|
+
|-----------|------|
|
|
102
|
+
| 1GB file download | 1-2 min (vs 5 min sequential) |
|
|
103
|
+
| Cache lookup | <10ms |
|
|
104
|
+
| Cache write | <50ms |
|
|
105
|
+
|
|
106
|
+
## Dependencies
|
|
107
|
+
|
|
108
|
+
- `@xiboplayer/utils` - Logger, EventEmitter
|
|
109
|
+
|
|
110
|
+
## Related Packages
|
|
111
|
+
|
|
112
|
+
- [@xiboplayer/core](../../core/docs/) - Player core
|
|
113
|
+
- [@xiboplayer/sw](../../sw/docs/) - Service Worker toolkit
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
**Package Version**: 1.0.0
|
|
118
|
+
**Last Updated**: 2026-02-10
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xiboplayer/cache",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Offline caching and download management with parallel chunk downloads",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js",
|
|
9
|
+
"./cache": "./src/cache.js",
|
|
10
|
+
"./cache-proxy": "./src/cache-proxy.js",
|
|
11
|
+
"./download-manager": "./src/download-manager.js"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"spark-md5": "^3.0.2",
|
|
15
|
+
"@xiboplayer/utils": "0.1.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"vitest": "^2.0.0",
|
|
19
|
+
"jsdom": "^25.0.0"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"xibo",
|
|
23
|
+
"digital-signage",
|
|
24
|
+
"cache",
|
|
25
|
+
"offline",
|
|
26
|
+
"download",
|
|
27
|
+
"indexeddb"
|
|
28
|
+
],
|
|
29
|
+
"author": "Pau Aliagas <linuxnow@gmail.com>",
|
|
30
|
+
"license": "AGPL-3.0-or-later",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/xibo-players/xiboplayer.git",
|
|
34
|
+
"directory": "packages/cache"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"test:watch": "vitest",
|
|
39
|
+
"test:coverage": "vitest run --coverage"
|
|
40
|
+
}
|
|
41
|
+
}
|