@dynlabs/react-native-immutable-file-cache 1.0.0-alpha.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/LICENSE +21 -0
- package/README.md +415 -0
- package/lib/commonjs/adapters/memoryAdapter.js +266 -0
- package/lib/commonjs/adapters/memoryAdapter.js.map +1 -0
- package/lib/commonjs/adapters/rnfsAdapter.js +259 -0
- package/lib/commonjs/adapters/rnfsAdapter.js.map +1 -0
- package/lib/commonjs/adapters/webAdapter.js +432 -0
- package/lib/commonjs/adapters/webAdapter.js.map +1 -0
- package/lib/commonjs/core/adapter.js +2 -0
- package/lib/commonjs/core/adapter.js.map +1 -0
- package/lib/commonjs/core/cacheEngine.js +578 -0
- package/lib/commonjs/core/cacheEngine.js.map +1 -0
- package/lib/commonjs/core/errors.js +83 -0
- package/lib/commonjs/core/errors.js.map +1 -0
- package/lib/commonjs/core/hash.js +83 -0
- package/lib/commonjs/core/hash.js.map +1 -0
- package/lib/commonjs/core/indexStore.js +175 -0
- package/lib/commonjs/core/indexStore.js.map +1 -0
- package/lib/commonjs/core/mutex.js +143 -0
- package/lib/commonjs/core/mutex.js.map +1 -0
- package/lib/commonjs/core/prune.js +127 -0
- package/lib/commonjs/core/prune.js.map +1 -0
- package/lib/commonjs/core/types.js +6 -0
- package/lib/commonjs/core/types.js.map +1 -0
- package/lib/commonjs/factory.js +56 -0
- package/lib/commonjs/factory.js.map +1 -0
- package/lib/commonjs/index.js +110 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/index.native.js +74 -0
- package/lib/commonjs/index.native.js.map +1 -0
- package/lib/commonjs/index.web.js +75 -0
- package/lib/commonjs/index.web.js.map +1 -0
- package/lib/commonjs/types/react-native-fs.d.js +2 -0
- package/lib/commonjs/types/react-native-fs.d.js.map +1 -0
- package/lib/module/adapters/memoryAdapter.js +261 -0
- package/lib/module/adapters/memoryAdapter.js.map +1 -0
- package/lib/module/adapters/rnfsAdapter.js +251 -0
- package/lib/module/adapters/rnfsAdapter.js.map +1 -0
- package/lib/module/adapters/webAdapter.js +426 -0
- package/lib/module/adapters/webAdapter.js.map +1 -0
- package/lib/module/core/adapter.js +2 -0
- package/lib/module/core/adapter.js.map +1 -0
- package/lib/module/core/cacheEngine.js +571 -0
- package/lib/module/core/cacheEngine.js.map +1 -0
- package/lib/module/core/errors.js +71 -0
- package/lib/module/core/errors.js.map +1 -0
- package/lib/module/core/hash.js +76 -0
- package/lib/module/core/hash.js.map +1 -0
- package/lib/module/core/indexStore.js +168 -0
- package/lib/module/core/indexStore.js.map +1 -0
- package/lib/module/core/mutex.js +135 -0
- package/lib/module/core/mutex.js.map +1 -0
- package/lib/module/core/prune.js +116 -0
- package/lib/module/core/prune.js.map +1 -0
- package/lib/module/core/types.js +2 -0
- package/lib/module/core/types.js.map +1 -0
- package/lib/module/factory.js +49 -0
- package/lib/module/factory.js.map +1 -0
- package/lib/module/index.js +41 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/index.native.js +54 -0
- package/lib/module/index.native.js.map +1 -0
- package/lib/module/index.web.js +55 -0
- package/lib/module/index.web.js.map +1 -0
- package/lib/module/types/react-native-fs.d.js +2 -0
- package/lib/module/types/react-native-fs.d.js.map +1 -0
- package/lib/typescript/src/adapters/memoryAdapter.d.ts +23 -0
- package/lib/typescript/src/adapters/memoryAdapter.d.ts.map +1 -0
- package/lib/typescript/src/adapters/rnfsAdapter.d.ts +18 -0
- package/lib/typescript/src/adapters/rnfsAdapter.d.ts.map +1 -0
- package/lib/typescript/src/adapters/webAdapter.d.ts +30 -0
- package/lib/typescript/src/adapters/webAdapter.d.ts.map +1 -0
- package/lib/typescript/src/core/adapter.d.ts +105 -0
- package/lib/typescript/src/core/adapter.d.ts.map +1 -0
- package/lib/typescript/src/core/cacheEngine.d.ts +99 -0
- package/lib/typescript/src/core/cacheEngine.d.ts.map +1 -0
- package/lib/typescript/src/core/errors.d.ts +54 -0
- package/lib/typescript/src/core/errors.d.ts.map +1 -0
- package/lib/typescript/src/core/hash.d.ts +20 -0
- package/lib/typescript/src/core/hash.d.ts.map +1 -0
- package/lib/typescript/src/core/indexStore.d.ts +34 -0
- package/lib/typescript/src/core/indexStore.d.ts.map +1 -0
- package/lib/typescript/src/core/mutex.d.ts +49 -0
- package/lib/typescript/src/core/mutex.d.ts.map +1 -0
- package/lib/typescript/src/core/prune.d.ts +39 -0
- package/lib/typescript/src/core/prune.d.ts.map +1 -0
- package/lib/typescript/src/core/types.d.ts +109 -0
- package/lib/typescript/src/core/types.d.ts.map +1 -0
- package/lib/typescript/src/factory.d.ts +46 -0
- package/lib/typescript/src/factory.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +20 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/index.native.d.ts +37 -0
- package/lib/typescript/src/index.native.d.ts.map +1 -0
- package/lib/typescript/src/index.web.d.ts +38 -0
- package/lib/typescript/src/index.web.d.ts.map +1 -0
- package/package.json +125 -0
- package/src/adapters/memoryAdapter.ts +307 -0
- package/src/adapters/rnfsAdapter.ts +283 -0
- package/src/adapters/webAdapter.ts +480 -0
- package/src/core/adapter.ts +128 -0
- package/src/core/cacheEngine.ts +634 -0
- package/src/core/errors.ts +82 -0
- package/src/core/hash.ts +78 -0
- package/src/core/indexStore.ts +184 -0
- package/src/core/mutex.ts +134 -0
- package/src/core/prune.ts +145 -0
- package/src/core/types.ts +165 -0
- package/src/factory.ts +60 -0
- package/src/index.native.ts +58 -0
- package/src/index.ts +82 -0
- package/src/index.web.ts +59 -0
- package/src/types/react-native-fs.d.ts +75 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["CacheError","Error","constructor","message","name","Object","setPrototypeOf","new","target","prototype","exports","UnsupportedSourceError","code","sourceType","adapterKind","AdapterIOError","operation","path","cause","CorruptIndexError","reason","ImmutableConflictError","key","EntryNotFoundError"],"sourceRoot":"../../../src","sources":["core/errors.ts"],"mappings":";;;;;;AAEA;AACA;AACA;AACO,MAAeA,UAAU,SAASC,KAAK,CAAC;EAG7CC,WAAWA,CAACC,OAAe,EAAE;IAC3B,KAAK,CAACA,OAAO,CAAC;IACd,IAAI,CAACC,IAAI,GAAG,IAAI,CAACF,WAAW,CAACE,IAAI;IACjCC,MAAM,CAACC,cAAc,CAAC,IAAI,EAAEC,GAAG,CAACC,MAAM,CAACC,SAAS,CAAC;EACnD;AACF;;AAEA;AACA;AACA;AAFAC,OAAA,CAAAV,UAAA,GAAAA,UAAA;AAGO,MAAMW,sBAAsB,SAASX,UAAU,CAAC;EAC5CY,IAAI,GAAG,oBAAoB;EAEpCV,WAAWA,CACOW,UAAiC,EACjCC,WAAmB,EACnC;IACA,KAAK,CAAC,YAAYA,WAAW,mCAAmCD,UAAU,GAAG,CAAC;IAAC,KAH/DA,UAAiC,GAAjCA,UAAiC;IAAA,KACjCC,WAAmB,GAAnBA,WAAmB;EAGrC;AACF;;AAEA;AACA;AACA;AAFAJ,OAAA,CAAAC,sBAAA,GAAAA,sBAAA;AAGO,MAAMI,cAAc,SAASf,UAAU,CAAC;EACpCY,IAAI,GAAG,kBAAkB;EAElCV,WAAWA,CACOc,SAAiB,EACjBC,IAAY,EACZC,KAAa,EAC7B;IACA,KAAK,CACH,6BAA6BF,SAAS,cAAcC,IAAI,MAAMC,KAAK,EAAEf,OAAO,IAAI,SAAS,EAC3F,CAAC;IAAC,KANca,SAAiB,GAAjBA,SAAiB;IAAA,KACjBC,IAAY,GAAZA,IAAY;IAAA,KACZC,KAAa,GAAbA,KAAa;EAK/B;AACF;;AAEA;AACA;AACA;AAFAR,OAAA,CAAAK,cAAA,GAAAA,cAAA;AAGO,MAAMI,iBAAiB,SAASnB,UAAU,CAAC;EACvCY,IAAI,GAAG,eAAe;EAE/BV,WAAWA,CACOkB,MAAc,EACdF,KAAa,EAC7B;IACA,KAAK,CAAC,2BAA2BE,MAAM,EAAE,CAAC;IAAC,KAH3BA,MAAc,GAAdA,MAAc;IAAA,KACdF,KAAa,GAAbA,KAAa;EAG/B;AACF;;AAEA;AACA;AACA;AACA;AAHAR,OAAA,CAAAS,iBAAA,GAAAA,iBAAA;AAIO,MAAME,sBAAsB,SAASrB,UAAU,CAAC;EAC5CY,IAAI,GAAG,oBAAoB;EAEpCV,WAAWA,CAAiBoB,GAAW,EAAE;IACvC,KAAK,CAAC,8CAA8CA,GAAG,GAAG,CAAC;IAAC,KADlCA,GAAW,GAAXA,GAAW;EAEvC;AACF;;AAEA;AACA;AACA;AAFAZ,OAAA,CAAAW,sBAAA,GAAAA,sBAAA;AAGO,MAAME,kBAAkB,SAASvB,UAAU,CAAC;EACxCY,IAAI,GAAG,iBAAiB;EAEjCV,WAAWA,CAAiBoB,GAAW,EAAE;IACvC,KAAK,CAAC,kCAAkCA,GAAG,GAAG,CAAC;IAAC,KADtBA,GAAW,GAAXA,GAAW;EAEvC;AACF;AAACZ,OAAA,CAAAa,kBAAA,GAAAA,kBAAA","ignoreList":[]}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.hash = hash;
|
|
7
|
+
exports.hashSync = hashSync;
|
|
8
|
+
/**
|
|
9
|
+
* Cross-platform SHA-256 hashing utility.
|
|
10
|
+
* Uses SubtleCrypto on web and native platforms that support it.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Converts an ArrayBuffer to a lowercase hex string.
|
|
15
|
+
*/
|
|
16
|
+
function arrayBufferToHex(buffer) {
|
|
17
|
+
const bytes = new Uint8Array(buffer);
|
|
18
|
+
let hex = "";
|
|
19
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
20
|
+
hex += bytes[i].toString(16).padStart(2, "0");
|
|
21
|
+
}
|
|
22
|
+
return hex;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Converts a string to a Uint8Array using UTF-8 encoding.
|
|
27
|
+
*/
|
|
28
|
+
function stringToUint8Array(str) {
|
|
29
|
+
const encoder = new TextEncoder();
|
|
30
|
+
return encoder.encode(str);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Computes SHA-256 hash of the input string.
|
|
35
|
+
* Returns lowercase hex string.
|
|
36
|
+
*
|
|
37
|
+
* Uses SubtleCrypto API which is available in:
|
|
38
|
+
* - Modern browsers
|
|
39
|
+
* - React Native (via Hermes or polyfill)
|
|
40
|
+
* - Node.js 15+
|
|
41
|
+
*/
|
|
42
|
+
async function hash(input) {
|
|
43
|
+
// Use SubtleCrypto (available in browsers and modern RN)
|
|
44
|
+
if (typeof globalThis.crypto?.subtle?.digest === "function") {
|
|
45
|
+
const data = stringToUint8Array(input);
|
|
46
|
+
const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", data.buffer);
|
|
47
|
+
return arrayBufferToHex(hashBuffer);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Fallback: Simple hash for environments without crypto
|
|
51
|
+
// This is a basic djb2-based hash - NOT cryptographically secure
|
|
52
|
+
// Should only be used as last resort fallback
|
|
53
|
+
return fallbackHash(input);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Simple non-cryptographic hash fallback.
|
|
58
|
+
* Used only when SubtleCrypto is unavailable.
|
|
59
|
+
*/
|
|
60
|
+
function fallbackHash(input) {
|
|
61
|
+
let h1 = 0xdeadbeef;
|
|
62
|
+
let h2 = 0x41c6ce57;
|
|
63
|
+
for (let i = 0; i < input.length; i++) {
|
|
64
|
+
const ch = input.charCodeAt(i);
|
|
65
|
+
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
66
|
+
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
67
|
+
}
|
|
68
|
+
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507);
|
|
69
|
+
h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909);
|
|
70
|
+
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507);
|
|
71
|
+
h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909);
|
|
72
|
+
const result = 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
|
73
|
+
return result.toString(16).padStart(16, "0");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Synchronous hash for cases where async is not possible.
|
|
78
|
+
* Uses fallback algorithm - prefer async hash() when possible.
|
|
79
|
+
*/
|
|
80
|
+
function hashSync(input) {
|
|
81
|
+
return fallbackHash(input);
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["arrayBufferToHex","buffer","bytes","Uint8Array","hex","i","length","toString","padStart","stringToUint8Array","str","encoder","TextEncoder","encode","hash","input","globalThis","crypto","subtle","digest","data","hashBuffer","fallbackHash","h1","h2","ch","charCodeAt","Math","imul","result","hashSync"],"sourceRoot":"../../../src","sources":["core/hash.ts"],"mappings":";;;;;;;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,SAASA,gBAAgBA,CAACC,MAAmB,EAAU;EACrD,MAAMC,KAAK,GAAG,IAAIC,UAAU,CAACF,MAAM,CAAC;EACpC,IAAIG,GAAG,GAAG,EAAE;EACZ,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGH,KAAK,CAACI,MAAM,EAAED,CAAC,EAAE,EAAE;IACrCD,GAAG,IAAIF,KAAK,CAACG,CAAC,CAAC,CAACE,QAAQ,CAAC,EAAE,CAAC,CAACC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;EAC/C;EACA,OAAOJ,GAAG;AACZ;;AAEA;AACA;AACA;AACA,SAASK,kBAAkBA,CAACC,GAAW,EAAc;EACnD,MAAMC,OAAO,GAAG,IAAIC,WAAW,CAAC,CAAC;EACjC,OAAOD,OAAO,CAACE,MAAM,CAACH,GAAG,CAAC;AAC5B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,eAAeI,IAAIA,CAACC,KAAa,EAAmB;EACzD;EACA,IAAI,OAAOC,UAAU,CAACC,MAAM,EAAEC,MAAM,EAAEC,MAAM,KAAK,UAAU,EAAE;IAC3D,MAAMC,IAAI,GAAGX,kBAAkB,CAACM,KAAK,CAAC;IACtC,MAAMM,UAAU,GAAG,MAAML,UAAU,CAACC,MAAM,CAACC,MAAM,CAACC,MAAM,CAAC,SAAS,EAAEC,IAAI,CAACnB,MAAqB,CAAC;IAC/F,OAAOD,gBAAgB,CAACqB,UAAU,CAAC;EACrC;;EAEA;EACA;EACA;EACA,OAAOC,YAAY,CAACP,KAAK,CAAC;AAC5B;;AAEA;AACA;AACA;AACA;AACA,SAASO,YAAYA,CAACP,KAAa,EAAU;EAC3C,IAAIQ,EAAE,GAAG,UAAU;EACnB,IAAIC,EAAE,GAAG,UAAU;EAEnB,KAAK,IAAInB,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGU,KAAK,CAACT,MAAM,EAAED,CAAC,EAAE,EAAE;IACrC,MAAMoB,EAAE,GAAGV,KAAK,CAACW,UAAU,CAACrB,CAAC,CAAC;IAC9BkB,EAAE,GAAGI,IAAI,CAACC,IAAI,CAACL,EAAE,GAAGE,EAAE,EAAE,UAAU,CAAC;IACnCD,EAAE,GAAGG,IAAI,CAACC,IAAI,CAACJ,EAAE,GAAGC,EAAE,EAAE,UAAU,CAAC;EACrC;EAEAF,EAAE,GAAGI,IAAI,CAACC,IAAI,CAACL,EAAE,GAAIA,EAAE,KAAK,EAAG,EAAE,UAAU,CAAC;EAC5CA,EAAE,IAAII,IAAI,CAACC,IAAI,CAACJ,EAAE,GAAIA,EAAE,KAAK,EAAG,EAAE,UAAU,CAAC;EAC7CA,EAAE,GAAGG,IAAI,CAACC,IAAI,CAACJ,EAAE,GAAIA,EAAE,KAAK,EAAG,EAAE,UAAU,CAAC;EAC5CA,EAAE,IAAIG,IAAI,CAACC,IAAI,CAACL,EAAE,GAAIA,EAAE,KAAK,EAAG,EAAE,UAAU,CAAC;EAE7C,MAAMM,MAAM,GAAG,UAAU,IAAI,OAAO,GAAGL,EAAE,CAAC,IAAID,EAAE,KAAK,CAAC,CAAC;EACvD,OAAOM,MAAM,CAACtB,QAAQ,CAAC,EAAE,CAAC,CAACC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC;AAC9C;;AAEA;AACA;AACA;AACA;AACO,SAASsB,QAAQA,CAACf,KAAa,EAAU;EAC9C,OAAOO,YAAY,CAACP,KAAK,CAAC;AAC5B","ignoreList":[]}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.IndexStore = void 0;
|
|
7
|
+
var _errors = require("./errors");
|
|
8
|
+
var _prune = require("./prune");
|
|
9
|
+
const INDEX_VERSION = 1;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Manages persistence of the cache index via the storage adapter.
|
|
13
|
+
*/
|
|
14
|
+
class IndexStore {
|
|
15
|
+
constructor(adapter, indexPath) {
|
|
16
|
+
this._adapter = adapter;
|
|
17
|
+
this._indexPath = indexPath;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Load index from storage.
|
|
22
|
+
* Returns empty index if file doesn't exist.
|
|
23
|
+
* Throws CorruptIndexError if index is malformed.
|
|
24
|
+
*/
|
|
25
|
+
async load() {
|
|
26
|
+
try {
|
|
27
|
+
const exists = await this._adapter.exists(this._indexPath);
|
|
28
|
+
if (!exists) {
|
|
29
|
+
return (0, _prune.createEmptyIndex)();
|
|
30
|
+
}
|
|
31
|
+
const content = await this._adapter.readText(this._indexPath, "utf8");
|
|
32
|
+
const parsed = JSON.parse(content);
|
|
33
|
+
return this._validateIndex(parsed);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
if (error instanceof _errors.CorruptIndexError) {
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
if (error instanceof SyntaxError) {
|
|
39
|
+
throw new _errors.CorruptIndexError("Invalid JSON", error);
|
|
40
|
+
}
|
|
41
|
+
// File doesn't exist or other read error - return empty
|
|
42
|
+
if (error instanceof _errors.AdapterIOError) {
|
|
43
|
+
return (0, _prune.createEmptyIndex)();
|
|
44
|
+
}
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Save index atomically via the adapter.
|
|
51
|
+
*/
|
|
52
|
+
async save(index) {
|
|
53
|
+
const content = JSON.stringify(index, null, 2);
|
|
54
|
+
await this._adapter.writeTextAtomic(this._indexPath, content, "utf8");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Validate and rebuild index from filesystem if corrupt.
|
|
59
|
+
* Scans the entries directory and reconstructs metadata.
|
|
60
|
+
*/
|
|
61
|
+
async rebuild(entriesDir) {
|
|
62
|
+
const entries = {};
|
|
63
|
+
let totalSizeBytes = 0;
|
|
64
|
+
try {
|
|
65
|
+
const files = await this._adapter.listDir(entriesDir);
|
|
66
|
+
for (const file of files) {
|
|
67
|
+
try {
|
|
68
|
+
const filePath = `${entriesDir}/${file}`;
|
|
69
|
+
const stat = await this._adapter.stat(filePath);
|
|
70
|
+
|
|
71
|
+
// Extract hash and extension from filename
|
|
72
|
+
const lastDot = file.lastIndexOf(".");
|
|
73
|
+
const hash = lastDot > 0 ? file.substring(0, lastDot) : file;
|
|
74
|
+
const ext = lastDot > 0 ? file.substring(lastDot) : "";
|
|
75
|
+
|
|
76
|
+
// Create minimal entry metadata
|
|
77
|
+
// Note: We lose the original key during rebuild
|
|
78
|
+
const entry = {
|
|
79
|
+
key: hash,
|
|
80
|
+
// Use hash as key since original is lost
|
|
81
|
+
hash,
|
|
82
|
+
ext,
|
|
83
|
+
sizeBytes: stat.sizeBytes,
|
|
84
|
+
createdAt: stat.mtimeMs,
|
|
85
|
+
lastAccessedAt: stat.mtimeMs
|
|
86
|
+
};
|
|
87
|
+
entries[hash] = entry;
|
|
88
|
+
totalSizeBytes += stat.sizeBytes;
|
|
89
|
+
} catch {
|
|
90
|
+
// Skip files that can't be stat'd
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
} catch {
|
|
95
|
+
// If we can't list the directory, return empty index
|
|
96
|
+
return (0, _prune.createEmptyIndex)();
|
|
97
|
+
}
|
|
98
|
+
const rebuiltIndex = {
|
|
99
|
+
version: INDEX_VERSION,
|
|
100
|
+
entries,
|
|
101
|
+
totalSizeBytes,
|
|
102
|
+
lastModifiedAt: Date.now()
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// Save the rebuilt index
|
|
106
|
+
await this.save(rebuiltIndex);
|
|
107
|
+
return rebuiltIndex;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Validates that the parsed object is a valid ICacheIndex.
|
|
112
|
+
*/
|
|
113
|
+
_validateIndex(parsed) {
|
|
114
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
115
|
+
throw new _errors.CorruptIndexError("Index is not an object");
|
|
116
|
+
}
|
|
117
|
+
const obj = parsed;
|
|
118
|
+
|
|
119
|
+
// Check version
|
|
120
|
+
if (obj.version !== INDEX_VERSION) {
|
|
121
|
+
throw new _errors.CorruptIndexError(`Unsupported version: ${String(obj.version)}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Check entries
|
|
125
|
+
if (typeof obj.entries !== "object" || obj.entries === null) {
|
|
126
|
+
throw new _errors.CorruptIndexError("Entries is not an object");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Validate each entry has required fields
|
|
130
|
+
const entries = obj.entries;
|
|
131
|
+
for (const [key, entry] of Object.entries(entries)) {
|
|
132
|
+
this._validateEntry(key, entry);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Check totalSizeBytes
|
|
136
|
+
if (typeof obj.totalSizeBytes !== "number" || obj.totalSizeBytes < 0) {
|
|
137
|
+
throw new _errors.CorruptIndexError("Invalid totalSizeBytes");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Check lastModifiedAt
|
|
141
|
+
if (typeof obj.lastModifiedAt !== "number") {
|
|
142
|
+
throw new _errors.CorruptIndexError("Invalid lastModifiedAt");
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
version: INDEX_VERSION,
|
|
146
|
+
entries: entries,
|
|
147
|
+
totalSizeBytes: obj.totalSizeBytes,
|
|
148
|
+
lastModifiedAt: obj.lastModifiedAt
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Validates that an entry has all required fields.
|
|
154
|
+
*/
|
|
155
|
+
_validateEntry(key, entry) {
|
|
156
|
+
if (typeof entry !== "object" || entry === null) {
|
|
157
|
+
throw new _errors.CorruptIndexError(`Entry "${key}" is not an object`);
|
|
158
|
+
}
|
|
159
|
+
const obj = entry;
|
|
160
|
+
const requiredStrings = ["key", "hash", "ext"];
|
|
161
|
+
const requiredNumbers = ["sizeBytes", "createdAt", "lastAccessedAt"];
|
|
162
|
+
for (const field of requiredStrings) {
|
|
163
|
+
if (typeof obj[field] !== "string") {
|
|
164
|
+
throw new _errors.CorruptIndexError(`Entry "${key}" missing string field "${field}"`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
for (const field of requiredNumbers) {
|
|
168
|
+
if (typeof obj[field] !== "number") {
|
|
169
|
+
throw new _errors.CorruptIndexError(`Entry "${key}" missing number field "${field}"`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
exports.IndexStore = IndexStore;
|
|
175
|
+
//# sourceMappingURL=indexStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_errors","require","_prune","INDEX_VERSION","IndexStore","constructor","adapter","indexPath","_adapter","_indexPath","load","exists","createEmptyIndex","content","readText","parsed","JSON","parse","_validateIndex","error","CorruptIndexError","SyntaxError","AdapterIOError","save","index","stringify","writeTextAtomic","rebuild","entriesDir","entries","totalSizeBytes","files","listDir","file","filePath","stat","lastDot","lastIndexOf","hash","substring","ext","entry","key","sizeBytes","createdAt","mtimeMs","lastAccessedAt","rebuiltIndex","version","lastModifiedAt","Date","now","obj","String","Object","_validateEntry","requiredStrings","requiredNumbers","field","exports"],"sourceRoot":"../../../src","sources":["core/indexStore.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.Mutex = exports.KeyedMutex = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Async mutex for serializing critical sections.
|
|
9
|
+
* Ensures only one operation runs at a time within a critical section.
|
|
10
|
+
*/
|
|
11
|
+
class Mutex {
|
|
12
|
+
_queue = [];
|
|
13
|
+
_locked = false;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Acquires the mutex lock.
|
|
17
|
+
* Returns a release function that must be called when done.
|
|
18
|
+
*/
|
|
19
|
+
async acquire() {
|
|
20
|
+
return new Promise(resolve => {
|
|
21
|
+
const tryAcquire = () => {
|
|
22
|
+
if (!this._locked) {
|
|
23
|
+
this._locked = true;
|
|
24
|
+
resolve(this._createRelease());
|
|
25
|
+
} else {
|
|
26
|
+
this._queue.push(tryAcquire);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
tryAcquire();
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Creates a release function for the current lock holder.
|
|
35
|
+
*/
|
|
36
|
+
_createRelease() {
|
|
37
|
+
let released = false;
|
|
38
|
+
return () => {
|
|
39
|
+
if (released) {
|
|
40
|
+
return; // Prevent double-release
|
|
41
|
+
}
|
|
42
|
+
released = true;
|
|
43
|
+
this._locked = false;
|
|
44
|
+
const next = this._queue.shift();
|
|
45
|
+
if (next) {
|
|
46
|
+
next();
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Returns whether the mutex is currently locked.
|
|
53
|
+
*/
|
|
54
|
+
get isLocked() {
|
|
55
|
+
return this._locked;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Executes a function with the mutex held.
|
|
60
|
+
* Automatically releases the mutex when done.
|
|
61
|
+
*/
|
|
62
|
+
async runExclusive(fn) {
|
|
63
|
+
const release = await this.acquire();
|
|
64
|
+
try {
|
|
65
|
+
return await fn();
|
|
66
|
+
} finally {
|
|
67
|
+
release();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Keyed mutex for per-key locking.
|
|
74
|
+
* Allows concurrent operations on different keys while serializing same-key operations.
|
|
75
|
+
*/
|
|
76
|
+
exports.Mutex = Mutex;
|
|
77
|
+
class KeyedMutex {
|
|
78
|
+
_mutexes = new Map();
|
|
79
|
+
_refCounts = new Map();
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Acquires lock for a specific key.
|
|
83
|
+
* Returns a release function that must be called when done.
|
|
84
|
+
*/
|
|
85
|
+
async acquire(key) {
|
|
86
|
+
// Get or create mutex for this key
|
|
87
|
+
let mutex = this._mutexes.get(key);
|
|
88
|
+
if (!mutex) {
|
|
89
|
+
mutex = new Mutex();
|
|
90
|
+
this._mutexes.set(key, mutex);
|
|
91
|
+
this._refCounts.set(key, 0);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Increment ref count
|
|
95
|
+
this._refCounts.set(key, (this._refCounts.get(key) ?? 0) + 1);
|
|
96
|
+
|
|
97
|
+
// Acquire the mutex
|
|
98
|
+
const innerRelease = await mutex.acquire();
|
|
99
|
+
|
|
100
|
+
// Return wrapped release that cleans up when ref count hits 0
|
|
101
|
+
let released = false;
|
|
102
|
+
return () => {
|
|
103
|
+
if (released) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
released = true;
|
|
107
|
+
|
|
108
|
+
// Release the inner mutex
|
|
109
|
+
innerRelease();
|
|
110
|
+
|
|
111
|
+
// Decrement ref count and clean up if zero
|
|
112
|
+
const count = (this._refCounts.get(key) ?? 1) - 1;
|
|
113
|
+
if (count <= 0) {
|
|
114
|
+
this._mutexes.delete(key);
|
|
115
|
+
this._refCounts.delete(key);
|
|
116
|
+
} else {
|
|
117
|
+
this._refCounts.set(key, count);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Executes a function with the keyed mutex held.
|
|
124
|
+
* Automatically releases the mutex when done.
|
|
125
|
+
*/
|
|
126
|
+
async runExclusive(key, fn) {
|
|
127
|
+
const release = await this.acquire(key);
|
|
128
|
+
try {
|
|
129
|
+
return await fn();
|
|
130
|
+
} finally {
|
|
131
|
+
release();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Returns the number of active keys with pending operations.
|
|
137
|
+
*/
|
|
138
|
+
get activeKeyCount() {
|
|
139
|
+
return this._mutexes.size;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
exports.KeyedMutex = KeyedMutex;
|
|
143
|
+
//# sourceMappingURL=mutex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["Mutex","_queue","_locked","acquire","Promise","resolve","tryAcquire","_createRelease","push","released","next","shift","isLocked","runExclusive","fn","release","exports","KeyedMutex","_mutexes","Map","_refCounts","key","mutex","get","set","innerRelease","count","delete","activeKeyCount","size"],"sourceRoot":"../../../src","sources":["core/mutex.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.addEntryToIndex = addEntryToIndex;
|
|
7
|
+
exports.createEmptyIndex = createEmptyIndex;
|
|
8
|
+
exports.getExpiredEntries = getExpiredEntries;
|
|
9
|
+
exports.getLruPruneTargets = getLruPruneTargets;
|
|
10
|
+
exports.removeEntriesFromIndex = removeEntriesFromIndex;
|
|
11
|
+
exports.touchEntry = touchEntry;
|
|
12
|
+
/**
|
|
13
|
+
* Options for pruning operations.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Identifies entries to remove based on TTL expiration.
|
|
18
|
+
* Returns entries that have expired (expiresAt < now).
|
|
19
|
+
*/
|
|
20
|
+
function getExpiredEntries(index, now = Date.now()) {
|
|
21
|
+
const expired = [];
|
|
22
|
+
for (const entry of Object.values(index.entries)) {
|
|
23
|
+
if (entry.expiresAt !== undefined && entry.expiresAt < now) {
|
|
24
|
+
expired.push(entry);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return expired;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Identifies entries to remove based on LRU policy to meet size limit.
|
|
32
|
+
* Sorts entries by lastAccessedAt (oldest first) and returns entries
|
|
33
|
+
* that need to be removed to get under maxSizeBytes.
|
|
34
|
+
*/
|
|
35
|
+
function getLruPruneTargets(index, maxSizeBytes) {
|
|
36
|
+
if (index.totalSizeBytes <= maxSizeBytes) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Sort entries by lastAccessedAt ascending (oldest first = LRU candidates)
|
|
41
|
+
const sortedEntries = Object.values(index.entries).sort((a, b) => a.lastAccessedAt - b.lastAccessedAt);
|
|
42
|
+
const toRemove = [];
|
|
43
|
+
let currentSize = index.totalSizeBytes;
|
|
44
|
+
for (const entry of sortedEntries) {
|
|
45
|
+
if (currentSize <= maxSizeBytes) {
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
toRemove.push(entry);
|
|
49
|
+
currentSize -= entry.sizeBytes;
|
|
50
|
+
}
|
|
51
|
+
return toRemove;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Creates a new index with the specified entries removed.
|
|
56
|
+
* Returns a new ICacheIndex without mutating the original.
|
|
57
|
+
*/
|
|
58
|
+
function removeEntriesFromIndex(index, keysToRemove) {
|
|
59
|
+
const keySet = new Set(keysToRemove);
|
|
60
|
+
const newEntries = {};
|
|
61
|
+
let newTotalSize = 0;
|
|
62
|
+
for (const [key, entry] of Object.entries(index.entries)) {
|
|
63
|
+
if (!keySet.has(key)) {
|
|
64
|
+
newEntries[key] = entry;
|
|
65
|
+
newTotalSize += entry.sizeBytes;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
version: 1,
|
|
70
|
+
entries: newEntries,
|
|
71
|
+
totalSizeBytes: newTotalSize,
|
|
72
|
+
lastModifiedAt: Date.now()
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Adds or updates an entry in the index.
|
|
78
|
+
* Returns a new ICacheIndex without mutating the original.
|
|
79
|
+
*/
|
|
80
|
+
function addEntryToIndex(index, entry) {
|
|
81
|
+
const existingEntry = index.entries[entry.key];
|
|
82
|
+
const sizeDelta = entry.sizeBytes - (existingEntry?.sizeBytes ?? 0);
|
|
83
|
+
return {
|
|
84
|
+
version: 1,
|
|
85
|
+
entries: {
|
|
86
|
+
...index.entries,
|
|
87
|
+
[entry.key]: entry
|
|
88
|
+
},
|
|
89
|
+
totalSizeBytes: index.totalSizeBytes + sizeDelta,
|
|
90
|
+
lastModifiedAt: Date.now()
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Updates the lastAccessedAt timestamp for an entry.
|
|
96
|
+
* Returns a new ICacheIndex without mutating the original.
|
|
97
|
+
*/
|
|
98
|
+
function touchEntry(index, key, accessedAt = Date.now()) {
|
|
99
|
+
const entry = index.entries[key];
|
|
100
|
+
if (!entry) {
|
|
101
|
+
return index;
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
...index,
|
|
105
|
+
entries: {
|
|
106
|
+
...index.entries,
|
|
107
|
+
[key]: {
|
|
108
|
+
...entry,
|
|
109
|
+
lastAccessedAt: accessedAt
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
lastModifiedAt: accessedAt
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Creates an empty cache index.
|
|
118
|
+
*/
|
|
119
|
+
function createEmptyIndex() {
|
|
120
|
+
return {
|
|
121
|
+
version: 1,
|
|
122
|
+
entries: {},
|
|
123
|
+
totalSizeBytes: 0,
|
|
124
|
+
lastModifiedAt: Date.now()
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=prune.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["getExpiredEntries","index","now","Date","expired","entry","Object","values","entries","expiresAt","undefined","push","getLruPruneTargets","maxSizeBytes","totalSizeBytes","sortedEntries","sort","a","b","lastAccessedAt","toRemove","currentSize","sizeBytes","removeEntriesFromIndex","keysToRemove","keySet","Set","newEntries","newTotalSize","key","has","version","lastModifiedAt","addEntryToIndex","existingEntry","sizeDelta","touchEntry","accessedAt","createEmptyIndex"],"sourceRoot":"../../../src","sources":["core/prune.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../../src","sources":["core/types.ts"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.createCacheEngineSync = createCacheEngineSync;
|
|
7
|
+
exports.createImmutableFileCache = createImmutableFileCache;
|
|
8
|
+
var _cacheEngine = require("./core/cacheEngine");
|
|
9
|
+
/**
|
|
10
|
+
* Creates an immutable file cache instance.
|
|
11
|
+
*
|
|
12
|
+
* This is the base factory function. Platform-specific entrypoints
|
|
13
|
+
* (index.native.ts, index.web.ts) wrap this to auto-select the appropriate adapter.
|
|
14
|
+
*
|
|
15
|
+
* @param options - Cache configuration options
|
|
16
|
+
* @param adapter - Storage adapter (required in base factory)
|
|
17
|
+
* @returns Initialized CacheEngine instance
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { createImmutableFileCache } from "react-native-immutable-file-cache";
|
|
22
|
+
*
|
|
23
|
+
* const cache = await createImmutableFileCache({
|
|
24
|
+
* namespace: "images",
|
|
25
|
+
* defaultTtlMs: 7 * 24 * 60 * 60 * 1000, // 7 days
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
async function createImmutableFileCache(options) {
|
|
30
|
+
const {
|
|
31
|
+
autoInit = true,
|
|
32
|
+
adapter,
|
|
33
|
+
...config
|
|
34
|
+
} = options;
|
|
35
|
+
const engine = new _cacheEngine.CacheEngine(config, adapter);
|
|
36
|
+
if (autoInit) {
|
|
37
|
+
await engine.init();
|
|
38
|
+
}
|
|
39
|
+
return engine;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Creates a cache engine without auto-initialization.
|
|
44
|
+
* Useful when you need to control initialization timing.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const engine = createCacheEngineSync({ namespace: "images" }, adapter);
|
|
49
|
+
* // Later...
|
|
50
|
+
* await engine.init();
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
function createCacheEngineSync(config, adapter) {
|
|
54
|
+
return new _cacheEngine.CacheEngine(config, adapter);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_cacheEngine","require","createImmutableFileCache","options","autoInit","adapter","config","engine","CacheEngine","init","createCacheEngineSync"],"sourceRoot":"../../src","sources":["factory.ts"],"mappings":";;;;;;;AAEA,IAAAA,YAAA,GAAAC,OAAA;AAUA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,eAAeC,wBAAwBA,CAC5CC,OAA2D,EACrC;EACtB,MAAM;IAAEC,QAAQ,GAAG,IAAI;IAAEC,OAAO;IAAE,GAAGC;EAAO,CAAC,GAAGH,OAAO;EAEvD,MAAMI,MAAM,GAAG,IAAIC,wBAAW,CAACF,MAAM,EAAED,OAAO,CAAC;EAE/C,IAAID,QAAQ,EAAE;IACZ,MAAMG,MAAM,CAACE,IAAI,CAAC,CAAC;EACrB;EAEA,OAAOF,MAAM;AACf;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASG,qBAAqBA,CAACJ,MAAoB,EAAED,OAAwB,EAAe;EACjG,OAAO,IAAIG,wBAAW,CAACF,MAAM,EAAED,OAAO,CAAC;AACzC","ignoreList":[]}
|