cachel 1.0.0 → 1.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/README.md +43 -0
- package/cachel.js +26 -1
- package/lib/idb.js +1 -1
- package/package.json +17 -4
- package/utils/chunkify.js +10 -0
- package/utils/{blob.js → convertToBlob.js} +1 -1
- package/utils/index.js +3 -0
package/README.md
CHANGED
|
@@ -4,6 +4,21 @@ Offline-first asset caching for the browser, powered by IndexedDB.
|
|
|
4
4
|
|
|
5
5
|
Fetch remote assets once, serve them forever from local cache. Works with any framework or none at all.
|
|
6
6
|
|
|
7
|
+

|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- Fetch and cache remote assets in IndexedDB with one call
|
|
15
|
+
- Batch cache multiple assets with controlled concurrency via `loadMany`
|
|
16
|
+
- Serve cached assets as object URLs, works fully offline
|
|
17
|
+
- Skips network requests for already cached assets
|
|
18
|
+
- Supports images, videos, audio and fonts
|
|
19
|
+
- No service worker required
|
|
20
|
+
- Zero dependencies
|
|
21
|
+
|
|
7
22
|
---
|
|
8
23
|
|
|
9
24
|
## Install
|
|
@@ -59,6 +74,34 @@ Throws if the fetch fails or the content type is not supported.
|
|
|
59
74
|
|
|
60
75
|
---
|
|
61
76
|
|
|
77
|
+
### `cache.loadMany(urls, chunkSize?)`
|
|
78
|
+
|
|
79
|
+
Fetches and caches multiple assets in controlled parallel chunks. Returns a status object with results, success count, failed count, and time elapsed in milliseconds.
|
|
80
|
+
|
|
81
|
+
```javascript
|
|
82
|
+
const status = await cache.loadMany([
|
|
83
|
+
'https://example.com/logo.png',
|
|
84
|
+
'https://example.com/hero.jpg',
|
|
85
|
+
'https://example.com/font.woff2'
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
console.log(status);
|
|
89
|
+
// {
|
|
90
|
+
// results: [...], // raw Promise.allSettled results
|
|
91
|
+
// success: 2,
|
|
92
|
+
// failed: 1,
|
|
93
|
+
// timeElapsed: 1240 // in milliseconds
|
|
94
|
+
// }
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`chunkSize` controls how many assets are fetched in parallel per round. Defaults to `8`, max is `8`. Assets already cached are skipped automatically.
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
await cache.loadMany(urls, 4); // 4 parallel fetches per round
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
62
105
|
### `cache.get(url)`
|
|
63
106
|
|
|
64
107
|
Retrieves a cached asset and returns it as an object URL. Returns `null` if not found.
|
package/cachel.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import Idb from "./lib/idb.js";
|
|
2
|
-
import convertToBlob from './utils/
|
|
2
|
+
import { convertToBlob, chunkify } from './utils/index.js';
|
|
3
3
|
|
|
4
4
|
class Cachel {
|
|
5
5
|
|
|
6
6
|
#idb;
|
|
7
|
+
#defaultChunkSize = 8;
|
|
7
8
|
|
|
8
9
|
constructor(name = 'idb'){
|
|
9
10
|
this.#idb = new Idb(name);
|
|
@@ -56,6 +57,30 @@ class Cachel {
|
|
|
56
57
|
return this.#idb.clear();
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
async loadMany(urls, chunkSize = this.#defaultChunkSize){
|
|
61
|
+
if(!(Array.isArray(urls) && urls.length)) return;
|
|
62
|
+
if(chunkSize > this.#defaultChunkSize){
|
|
63
|
+
console.warn(`cachel: supplied chunkSize is greater than max permissible chunk size; clamping the size to ${this.#defaultChunkSize}`);
|
|
64
|
+
}
|
|
65
|
+
chunkSize = Math.min(Math.max(1, chunkSize), this.#defaultChunkSize);
|
|
66
|
+
const chunks = chunkify(urls, chunkSize);
|
|
67
|
+
const results = [];
|
|
68
|
+
const startTime = performance.now();
|
|
69
|
+
for(const chunk of chunks){
|
|
70
|
+
const chunkResults = await Promise.allSettled(chunk.map(url => this.load(url)));
|
|
71
|
+
results.push(...chunkResults);
|
|
72
|
+
}
|
|
73
|
+
const timeElapsed = (performance.now() - startTime);
|
|
74
|
+
const success = results.filter(result => result.status === 'fulfilled').length;
|
|
75
|
+
const status = {
|
|
76
|
+
results,
|
|
77
|
+
success,
|
|
78
|
+
failed: results.length - success,
|
|
79
|
+
timeElapsed
|
|
80
|
+
}
|
|
81
|
+
return status;
|
|
82
|
+
}
|
|
83
|
+
|
|
59
84
|
}
|
|
60
85
|
|
|
61
86
|
export default Cachel;
|
package/lib/idb.js
CHANGED
package/package.json
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cachel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"main": "cachel.js",
|
|
5
|
-
"files": [
|
|
5
|
+
"files": [
|
|
6
|
+
"cachel.js",
|
|
7
|
+
"lib/",
|
|
8
|
+
"utils/",
|
|
9
|
+
"README.md",
|
|
10
|
+
"LICENSE"
|
|
11
|
+
],
|
|
6
12
|
"scripts": {
|
|
7
13
|
"dev": "live-server"
|
|
8
14
|
},
|
|
9
|
-
"keywords": [
|
|
15
|
+
"keywords": [
|
|
16
|
+
"cache",
|
|
17
|
+
"indexeddb",
|
|
18
|
+
"offline",
|
|
19
|
+
"pwa",
|
|
20
|
+
"blob",
|
|
21
|
+
"assets"
|
|
22
|
+
],
|
|
10
23
|
"author": {
|
|
11
24
|
"name": "Geet Trivedi",
|
|
12
25
|
"url": "https://www.geettrivedi.com"
|
|
@@ -17,4 +30,4 @@
|
|
|
17
30
|
"devDependencies": {
|
|
18
31
|
"live-server": "^1.2.2"
|
|
19
32
|
}
|
|
20
|
-
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const chunkify = (arr, chunkSize) => arr.reduce((chunksArr, item, index) => {
|
|
2
|
+
const chunkIndex = Math.floor(index/chunkSize);
|
|
3
|
+
if(!chunksArr[chunkIndex]){
|
|
4
|
+
chunksArr[chunkIndex] = [];
|
|
5
|
+
}
|
|
6
|
+
chunksArr[chunkIndex].push(item);
|
|
7
|
+
return chunksArr;
|
|
8
|
+
}, []);
|
|
9
|
+
|
|
10
|
+
export default chunkify;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const allowedContentTypes = ['image/', 'video/', 'audio/', 'font/'];
|
|
1
2
|
const convertToBlob = async url => {
|
|
2
3
|
try{
|
|
3
4
|
const response = await fetch(url);
|
|
@@ -7,7 +8,6 @@ const convertToBlob = async url => {
|
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
const contentType = response.headers.get('content-type');
|
|
10
|
-
const allowedContentTypes = ['image/', 'video/', 'audio/', 'font/'];
|
|
11
11
|
const isContentTypeAllowed = allowedContentTypes.some(type => contentType.startsWith(type));
|
|
12
12
|
|
|
13
13
|
if(!isContentTypeAllowed){
|
package/utils/index.js
ADDED