@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.
Files changed (113) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +415 -0
  3. package/lib/commonjs/adapters/memoryAdapter.js +266 -0
  4. package/lib/commonjs/adapters/memoryAdapter.js.map +1 -0
  5. package/lib/commonjs/adapters/rnfsAdapter.js +259 -0
  6. package/lib/commonjs/adapters/rnfsAdapter.js.map +1 -0
  7. package/lib/commonjs/adapters/webAdapter.js +432 -0
  8. package/lib/commonjs/adapters/webAdapter.js.map +1 -0
  9. package/lib/commonjs/core/adapter.js +2 -0
  10. package/lib/commonjs/core/adapter.js.map +1 -0
  11. package/lib/commonjs/core/cacheEngine.js +578 -0
  12. package/lib/commonjs/core/cacheEngine.js.map +1 -0
  13. package/lib/commonjs/core/errors.js +83 -0
  14. package/lib/commonjs/core/errors.js.map +1 -0
  15. package/lib/commonjs/core/hash.js +83 -0
  16. package/lib/commonjs/core/hash.js.map +1 -0
  17. package/lib/commonjs/core/indexStore.js +175 -0
  18. package/lib/commonjs/core/indexStore.js.map +1 -0
  19. package/lib/commonjs/core/mutex.js +143 -0
  20. package/lib/commonjs/core/mutex.js.map +1 -0
  21. package/lib/commonjs/core/prune.js +127 -0
  22. package/lib/commonjs/core/prune.js.map +1 -0
  23. package/lib/commonjs/core/types.js +6 -0
  24. package/lib/commonjs/core/types.js.map +1 -0
  25. package/lib/commonjs/factory.js +56 -0
  26. package/lib/commonjs/factory.js.map +1 -0
  27. package/lib/commonjs/index.js +110 -0
  28. package/lib/commonjs/index.js.map +1 -0
  29. package/lib/commonjs/index.native.js +74 -0
  30. package/lib/commonjs/index.native.js.map +1 -0
  31. package/lib/commonjs/index.web.js +75 -0
  32. package/lib/commonjs/index.web.js.map +1 -0
  33. package/lib/commonjs/types/react-native-fs.d.js +2 -0
  34. package/lib/commonjs/types/react-native-fs.d.js.map +1 -0
  35. package/lib/module/adapters/memoryAdapter.js +261 -0
  36. package/lib/module/adapters/memoryAdapter.js.map +1 -0
  37. package/lib/module/adapters/rnfsAdapter.js +251 -0
  38. package/lib/module/adapters/rnfsAdapter.js.map +1 -0
  39. package/lib/module/adapters/webAdapter.js +426 -0
  40. package/lib/module/adapters/webAdapter.js.map +1 -0
  41. package/lib/module/core/adapter.js +2 -0
  42. package/lib/module/core/adapter.js.map +1 -0
  43. package/lib/module/core/cacheEngine.js +571 -0
  44. package/lib/module/core/cacheEngine.js.map +1 -0
  45. package/lib/module/core/errors.js +71 -0
  46. package/lib/module/core/errors.js.map +1 -0
  47. package/lib/module/core/hash.js +76 -0
  48. package/lib/module/core/hash.js.map +1 -0
  49. package/lib/module/core/indexStore.js +168 -0
  50. package/lib/module/core/indexStore.js.map +1 -0
  51. package/lib/module/core/mutex.js +135 -0
  52. package/lib/module/core/mutex.js.map +1 -0
  53. package/lib/module/core/prune.js +116 -0
  54. package/lib/module/core/prune.js.map +1 -0
  55. package/lib/module/core/types.js +2 -0
  56. package/lib/module/core/types.js.map +1 -0
  57. package/lib/module/factory.js +49 -0
  58. package/lib/module/factory.js.map +1 -0
  59. package/lib/module/index.js +41 -0
  60. package/lib/module/index.js.map +1 -0
  61. package/lib/module/index.native.js +54 -0
  62. package/lib/module/index.native.js.map +1 -0
  63. package/lib/module/index.web.js +55 -0
  64. package/lib/module/index.web.js.map +1 -0
  65. package/lib/module/types/react-native-fs.d.js +2 -0
  66. package/lib/module/types/react-native-fs.d.js.map +1 -0
  67. package/lib/typescript/src/adapters/memoryAdapter.d.ts +23 -0
  68. package/lib/typescript/src/adapters/memoryAdapter.d.ts.map +1 -0
  69. package/lib/typescript/src/adapters/rnfsAdapter.d.ts +18 -0
  70. package/lib/typescript/src/adapters/rnfsAdapter.d.ts.map +1 -0
  71. package/lib/typescript/src/adapters/webAdapter.d.ts +30 -0
  72. package/lib/typescript/src/adapters/webAdapter.d.ts.map +1 -0
  73. package/lib/typescript/src/core/adapter.d.ts +105 -0
  74. package/lib/typescript/src/core/adapter.d.ts.map +1 -0
  75. package/lib/typescript/src/core/cacheEngine.d.ts +99 -0
  76. package/lib/typescript/src/core/cacheEngine.d.ts.map +1 -0
  77. package/lib/typescript/src/core/errors.d.ts +54 -0
  78. package/lib/typescript/src/core/errors.d.ts.map +1 -0
  79. package/lib/typescript/src/core/hash.d.ts +20 -0
  80. package/lib/typescript/src/core/hash.d.ts.map +1 -0
  81. package/lib/typescript/src/core/indexStore.d.ts +34 -0
  82. package/lib/typescript/src/core/indexStore.d.ts.map +1 -0
  83. package/lib/typescript/src/core/mutex.d.ts +49 -0
  84. package/lib/typescript/src/core/mutex.d.ts.map +1 -0
  85. package/lib/typescript/src/core/prune.d.ts +39 -0
  86. package/lib/typescript/src/core/prune.d.ts.map +1 -0
  87. package/lib/typescript/src/core/types.d.ts +109 -0
  88. package/lib/typescript/src/core/types.d.ts.map +1 -0
  89. package/lib/typescript/src/factory.d.ts +46 -0
  90. package/lib/typescript/src/factory.d.ts.map +1 -0
  91. package/lib/typescript/src/index.d.ts +20 -0
  92. package/lib/typescript/src/index.d.ts.map +1 -0
  93. package/lib/typescript/src/index.native.d.ts +37 -0
  94. package/lib/typescript/src/index.native.d.ts.map +1 -0
  95. package/lib/typescript/src/index.web.d.ts +38 -0
  96. package/lib/typescript/src/index.web.d.ts.map +1 -0
  97. package/package.json +125 -0
  98. package/src/adapters/memoryAdapter.ts +307 -0
  99. package/src/adapters/rnfsAdapter.ts +283 -0
  100. package/src/adapters/webAdapter.ts +480 -0
  101. package/src/core/adapter.ts +128 -0
  102. package/src/core/cacheEngine.ts +634 -0
  103. package/src/core/errors.ts +82 -0
  104. package/src/core/hash.ts +78 -0
  105. package/src/core/indexStore.ts +184 -0
  106. package/src/core/mutex.ts +134 -0
  107. package/src/core/prune.ts +145 -0
  108. package/src/core/types.ts +165 -0
  109. package/src/factory.ts +60 -0
  110. package/src/index.native.ts +58 -0
  111. package/src/index.ts +82 -0
  112. package/src/index.web.ts +59 -0
  113. package/src/types/react-native-fs.d.ts +75 -0
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Base error class for cache operations.
3
+ */
4
+ export class CacheError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = this.constructor.name;
8
+ Object.setPrototypeOf(this, new.target.prototype);
9
+ }
10
+ }
11
+
12
+ /**
13
+ * Thrown when an adapter receives a TBinarySource type it cannot handle.
14
+ */
15
+ export class UnsupportedSourceError extends CacheError {
16
+ code = "UNSUPPORTED_SOURCE";
17
+ constructor(sourceType, adapterKind) {
18
+ super(`Adapter "${adapterKind}" does not support source type "${sourceType}"`);
19
+ this.sourceType = sourceType;
20
+ this.adapterKind = adapterKind;
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Thrown when an adapter I/O operation fails.
26
+ */
27
+ export class AdapterIOError extends CacheError {
28
+ code = "ADAPTER_IO_ERROR";
29
+ constructor(operation, path, cause) {
30
+ super(`Adapter I/O error during "${operation}" at path "${path}": ${cause?.message ?? "unknown"}`);
31
+ this.operation = operation;
32
+ this.path = path;
33
+ this.cause = cause;
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Thrown when the cache index is corrupt and cannot be parsed.
39
+ */
40
+ export class CorruptIndexError extends CacheError {
41
+ code = "CORRUPT_INDEX";
42
+ constructor(reason, cause) {
43
+ super(`Cache index is corrupt: ${reason}`);
44
+ this.reason = reason;
45
+ this.cause = cause;
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Internal error when attempting to overwrite an immutable entry.
51
+ * Public API converts this to {status: "exists"} response.
52
+ */
53
+ export class ImmutableConflictError extends CacheError {
54
+ code = "IMMUTABLE_CONFLICT";
55
+ constructor(key) {
56
+ super(`Cannot overwrite immutable entry with key "${key}"`);
57
+ this.key = key;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Thrown when a required entry is not found.
63
+ */
64
+ export class EntryNotFoundError extends CacheError {
65
+ code = "ENTRY_NOT_FOUND";
66
+ constructor(key) {
67
+ super(`Cache entry not found for key "${key}"`);
68
+ this.key = key;
69
+ }
70
+ }
71
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["CacheError","Error","constructor","message","name","Object","setPrototypeOf","new","target","prototype","UnsupportedSourceError","code","sourceType","adapterKind","AdapterIOError","operation","path","cause","CorruptIndexError","reason","ImmutableConflictError","key","EntryNotFoundError"],"sourceRoot":"../../../src","sources":["core/errors.ts"],"mappings":"AAEA;AACA;AACA;AACA,OAAO,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;AACA,OAAO,MAAMC,sBAAsB,SAASV,UAAU,CAAC;EAC5CW,IAAI,GAAG,oBAAoB;EAEpCT,WAAWA,CACOU,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;AACA,OAAO,MAAMC,cAAc,SAASd,UAAU,CAAC;EACpCW,IAAI,GAAG,kBAAkB;EAElCT,WAAWA,CACOa,SAAiB,EACjBC,IAAY,EACZC,KAAa,EAC7B;IACA,KAAK,CACH,6BAA6BF,SAAS,cAAcC,IAAI,MAAMC,KAAK,EAAEd,OAAO,IAAI,SAAS,EAC3F,CAAC;IAAC,KANcY,SAAiB,GAAjBA,SAAiB;IAAA,KACjBC,IAAY,GAAZA,IAAY;IAAA,KACZC,KAAa,GAAbA,KAAa;EAK/B;AACF;;AAEA;AACA;AACA;AACA,OAAO,MAAMC,iBAAiB,SAASlB,UAAU,CAAC;EACvCW,IAAI,GAAG,eAAe;EAE/BT,WAAWA,CACOiB,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;AACA,OAAO,MAAMG,sBAAsB,SAASpB,UAAU,CAAC;EAC5CW,IAAI,GAAG,oBAAoB;EAEpCT,WAAWA,CAAiBmB,GAAW,EAAE;IACvC,KAAK,CAAC,8CAA8CA,GAAG,GAAG,CAAC;IAAC,KADlCA,GAAW,GAAXA,GAAW;EAEvC;AACF;;AAEA;AACA;AACA;AACA,OAAO,MAAMC,kBAAkB,SAAStB,UAAU,CAAC;EACxCW,IAAI,GAAG,iBAAiB;EAEjCT,WAAWA,CAAiBmB,GAAW,EAAE;IACvC,KAAK,CAAC,kCAAkCA,GAAG,GAAG,CAAC;IAAC,KADtBA,GAAW,GAAXA,GAAW;EAEvC;AACF","ignoreList":[]}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Cross-platform SHA-256 hashing utility.
3
+ * Uses SubtleCrypto on web and native platforms that support it.
4
+ */
5
+
6
+ /**
7
+ * Converts an ArrayBuffer to a lowercase hex string.
8
+ */
9
+ function arrayBufferToHex(buffer) {
10
+ const bytes = new Uint8Array(buffer);
11
+ let hex = "";
12
+ for (let i = 0; i < bytes.length; i++) {
13
+ hex += bytes[i].toString(16).padStart(2, "0");
14
+ }
15
+ return hex;
16
+ }
17
+
18
+ /**
19
+ * Converts a string to a Uint8Array using UTF-8 encoding.
20
+ */
21
+ function stringToUint8Array(str) {
22
+ const encoder = new TextEncoder();
23
+ return encoder.encode(str);
24
+ }
25
+
26
+ /**
27
+ * Computes SHA-256 hash of the input string.
28
+ * Returns lowercase hex string.
29
+ *
30
+ * Uses SubtleCrypto API which is available in:
31
+ * - Modern browsers
32
+ * - React Native (via Hermes or polyfill)
33
+ * - Node.js 15+
34
+ */
35
+ export async function hash(input) {
36
+ // Use SubtleCrypto (available in browsers and modern RN)
37
+ if (typeof globalThis.crypto?.subtle?.digest === "function") {
38
+ const data = stringToUint8Array(input);
39
+ const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", data.buffer);
40
+ return arrayBufferToHex(hashBuffer);
41
+ }
42
+
43
+ // Fallback: Simple hash for environments without crypto
44
+ // This is a basic djb2-based hash - NOT cryptographically secure
45
+ // Should only be used as last resort fallback
46
+ return fallbackHash(input);
47
+ }
48
+
49
+ /**
50
+ * Simple non-cryptographic hash fallback.
51
+ * Used only when SubtleCrypto is unavailable.
52
+ */
53
+ function fallbackHash(input) {
54
+ let h1 = 0xdeadbeef;
55
+ let h2 = 0x41c6ce57;
56
+ for (let i = 0; i < input.length; i++) {
57
+ const ch = input.charCodeAt(i);
58
+ h1 = Math.imul(h1 ^ ch, 2654435761);
59
+ h2 = Math.imul(h2 ^ ch, 1597334677);
60
+ }
61
+ h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507);
62
+ h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909);
63
+ h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507);
64
+ h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909);
65
+ const result = 4294967296 * (2097151 & h2) + (h1 >>> 0);
66
+ return result.toString(16).padStart(16, "0");
67
+ }
68
+
69
+ /**
70
+ * Synchronous hash for cases where async is not possible.
71
+ * Uses fallback algorithm - prefer async hash() when possible.
72
+ */
73
+ export function hashSync(input) {
74
+ return fallbackHash(input);
75
+ }
76
+ //# 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;AACA,OAAO,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;AACA,OAAO,SAASsB,QAAQA,CAACf,KAAa,EAAU;EAC9C,OAAOO,YAAY,CAACP,KAAK,CAAC;AAC5B","ignoreList":[]}
@@ -0,0 +1,168 @@
1
+ import { CorruptIndexError, AdapterIOError } from "./errors";
2
+ import { createEmptyIndex } from "./prune";
3
+ const INDEX_VERSION = 1;
4
+
5
+ /**
6
+ * Manages persistence of the cache index via the storage adapter.
7
+ */
8
+ export class IndexStore {
9
+ constructor(adapter, indexPath) {
10
+ this._adapter = adapter;
11
+ this._indexPath = indexPath;
12
+ }
13
+
14
+ /**
15
+ * Load index from storage.
16
+ * Returns empty index if file doesn't exist.
17
+ * Throws CorruptIndexError if index is malformed.
18
+ */
19
+ async load() {
20
+ try {
21
+ const exists = await this._adapter.exists(this._indexPath);
22
+ if (!exists) {
23
+ return createEmptyIndex();
24
+ }
25
+ const content = await this._adapter.readText(this._indexPath, "utf8");
26
+ const parsed = JSON.parse(content);
27
+ return this._validateIndex(parsed);
28
+ } catch (error) {
29
+ if (error instanceof CorruptIndexError) {
30
+ throw error;
31
+ }
32
+ if (error instanceof SyntaxError) {
33
+ throw new CorruptIndexError("Invalid JSON", error);
34
+ }
35
+ // File doesn't exist or other read error - return empty
36
+ if (error instanceof AdapterIOError) {
37
+ return createEmptyIndex();
38
+ }
39
+ throw error;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Save index atomically via the adapter.
45
+ */
46
+ async save(index) {
47
+ const content = JSON.stringify(index, null, 2);
48
+ await this._adapter.writeTextAtomic(this._indexPath, content, "utf8");
49
+ }
50
+
51
+ /**
52
+ * Validate and rebuild index from filesystem if corrupt.
53
+ * Scans the entries directory and reconstructs metadata.
54
+ */
55
+ async rebuild(entriesDir) {
56
+ const entries = {};
57
+ let totalSizeBytes = 0;
58
+ try {
59
+ const files = await this._adapter.listDir(entriesDir);
60
+ for (const file of files) {
61
+ try {
62
+ const filePath = `${entriesDir}/${file}`;
63
+ const stat = await this._adapter.stat(filePath);
64
+
65
+ // Extract hash and extension from filename
66
+ const lastDot = file.lastIndexOf(".");
67
+ const hash = lastDot > 0 ? file.substring(0, lastDot) : file;
68
+ const ext = lastDot > 0 ? file.substring(lastDot) : "";
69
+
70
+ // Create minimal entry metadata
71
+ // Note: We lose the original key during rebuild
72
+ const entry = {
73
+ key: hash,
74
+ // Use hash as key since original is lost
75
+ hash,
76
+ ext,
77
+ sizeBytes: stat.sizeBytes,
78
+ createdAt: stat.mtimeMs,
79
+ lastAccessedAt: stat.mtimeMs
80
+ };
81
+ entries[hash] = entry;
82
+ totalSizeBytes += stat.sizeBytes;
83
+ } catch {
84
+ // Skip files that can't be stat'd
85
+ continue;
86
+ }
87
+ }
88
+ } catch {
89
+ // If we can't list the directory, return empty index
90
+ return createEmptyIndex();
91
+ }
92
+ const rebuiltIndex = {
93
+ version: INDEX_VERSION,
94
+ entries,
95
+ totalSizeBytes,
96
+ lastModifiedAt: Date.now()
97
+ };
98
+
99
+ // Save the rebuilt index
100
+ await this.save(rebuiltIndex);
101
+ return rebuiltIndex;
102
+ }
103
+
104
+ /**
105
+ * Validates that the parsed object is a valid ICacheIndex.
106
+ */
107
+ _validateIndex(parsed) {
108
+ if (typeof parsed !== "object" || parsed === null) {
109
+ throw new CorruptIndexError("Index is not an object");
110
+ }
111
+ const obj = parsed;
112
+
113
+ // Check version
114
+ if (obj.version !== INDEX_VERSION) {
115
+ throw new CorruptIndexError(`Unsupported version: ${String(obj.version)}`);
116
+ }
117
+
118
+ // Check entries
119
+ if (typeof obj.entries !== "object" || obj.entries === null) {
120
+ throw new CorruptIndexError("Entries is not an object");
121
+ }
122
+
123
+ // Validate each entry has required fields
124
+ const entries = obj.entries;
125
+ for (const [key, entry] of Object.entries(entries)) {
126
+ this._validateEntry(key, entry);
127
+ }
128
+
129
+ // Check totalSizeBytes
130
+ if (typeof obj.totalSizeBytes !== "number" || obj.totalSizeBytes < 0) {
131
+ throw new CorruptIndexError("Invalid totalSizeBytes");
132
+ }
133
+
134
+ // Check lastModifiedAt
135
+ if (typeof obj.lastModifiedAt !== "number") {
136
+ throw new CorruptIndexError("Invalid lastModifiedAt");
137
+ }
138
+ return {
139
+ version: INDEX_VERSION,
140
+ entries: entries,
141
+ totalSizeBytes: obj.totalSizeBytes,
142
+ lastModifiedAt: obj.lastModifiedAt
143
+ };
144
+ }
145
+
146
+ /**
147
+ * Validates that an entry has all required fields.
148
+ */
149
+ _validateEntry(key, entry) {
150
+ if (typeof entry !== "object" || entry === null) {
151
+ throw new CorruptIndexError(`Entry "${key}" is not an object`);
152
+ }
153
+ const obj = entry;
154
+ const requiredStrings = ["key", "hash", "ext"];
155
+ const requiredNumbers = ["sizeBytes", "createdAt", "lastAccessedAt"];
156
+ for (const field of requiredStrings) {
157
+ if (typeof obj[field] !== "string") {
158
+ throw new CorruptIndexError(`Entry "${key}" missing string field "${field}"`);
159
+ }
160
+ }
161
+ for (const field of requiredNumbers) {
162
+ if (typeof obj[field] !== "number") {
163
+ throw new CorruptIndexError(`Entry "${key}" missing number field "${field}"`);
164
+ }
165
+ }
166
+ }
167
+ }
168
+ //# sourceMappingURL=indexStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["CorruptIndexError","AdapterIOError","createEmptyIndex","INDEX_VERSION","IndexStore","constructor","adapter","indexPath","_adapter","_indexPath","load","exists","content","readText","parsed","JSON","parse","_validateIndex","error","SyntaxError","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"],"sourceRoot":"../../../src","sources":["core/indexStore.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Async mutex for serializing critical sections.
3
+ * Ensures only one operation runs at a time within a critical section.
4
+ */
5
+ export class Mutex {
6
+ _queue = [];
7
+ _locked = false;
8
+
9
+ /**
10
+ * Acquires the mutex lock.
11
+ * Returns a release function that must be called when done.
12
+ */
13
+ async acquire() {
14
+ return new Promise(resolve => {
15
+ const tryAcquire = () => {
16
+ if (!this._locked) {
17
+ this._locked = true;
18
+ resolve(this._createRelease());
19
+ } else {
20
+ this._queue.push(tryAcquire);
21
+ }
22
+ };
23
+ tryAcquire();
24
+ });
25
+ }
26
+
27
+ /**
28
+ * Creates a release function for the current lock holder.
29
+ */
30
+ _createRelease() {
31
+ let released = false;
32
+ return () => {
33
+ if (released) {
34
+ return; // Prevent double-release
35
+ }
36
+ released = true;
37
+ this._locked = false;
38
+ const next = this._queue.shift();
39
+ if (next) {
40
+ next();
41
+ }
42
+ };
43
+ }
44
+
45
+ /**
46
+ * Returns whether the mutex is currently locked.
47
+ */
48
+ get isLocked() {
49
+ return this._locked;
50
+ }
51
+
52
+ /**
53
+ * Executes a function with the mutex held.
54
+ * Automatically releases the mutex when done.
55
+ */
56
+ async runExclusive(fn) {
57
+ const release = await this.acquire();
58
+ try {
59
+ return await fn();
60
+ } finally {
61
+ release();
62
+ }
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Keyed mutex for per-key locking.
68
+ * Allows concurrent operations on different keys while serializing same-key operations.
69
+ */
70
+ export class KeyedMutex {
71
+ _mutexes = new Map();
72
+ _refCounts = new Map();
73
+
74
+ /**
75
+ * Acquires lock for a specific key.
76
+ * Returns a release function that must be called when done.
77
+ */
78
+ async acquire(key) {
79
+ // Get or create mutex for this key
80
+ let mutex = this._mutexes.get(key);
81
+ if (!mutex) {
82
+ mutex = new Mutex();
83
+ this._mutexes.set(key, mutex);
84
+ this._refCounts.set(key, 0);
85
+ }
86
+
87
+ // Increment ref count
88
+ this._refCounts.set(key, (this._refCounts.get(key) ?? 0) + 1);
89
+
90
+ // Acquire the mutex
91
+ const innerRelease = await mutex.acquire();
92
+
93
+ // Return wrapped release that cleans up when ref count hits 0
94
+ let released = false;
95
+ return () => {
96
+ if (released) {
97
+ return;
98
+ }
99
+ released = true;
100
+
101
+ // Release the inner mutex
102
+ innerRelease();
103
+
104
+ // Decrement ref count and clean up if zero
105
+ const count = (this._refCounts.get(key) ?? 1) - 1;
106
+ if (count <= 0) {
107
+ this._mutexes.delete(key);
108
+ this._refCounts.delete(key);
109
+ } else {
110
+ this._refCounts.set(key, count);
111
+ }
112
+ };
113
+ }
114
+
115
+ /**
116
+ * Executes a function with the keyed mutex held.
117
+ * Automatically releases the mutex when done.
118
+ */
119
+ async runExclusive(key, fn) {
120
+ const release = await this.acquire(key);
121
+ try {
122
+ return await fn();
123
+ } finally {
124
+ release();
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Returns the number of active keys with pending operations.
130
+ */
131
+ get activeKeyCount() {
132
+ return this._mutexes.size;
133
+ }
134
+ }
135
+ //# 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","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,116 @@
1
+ /**
2
+ * Options for pruning operations.
3
+ */
4
+
5
+ /**
6
+ * Identifies entries to remove based on TTL expiration.
7
+ * Returns entries that have expired (expiresAt < now).
8
+ */
9
+ export function getExpiredEntries(index, now = Date.now()) {
10
+ const expired = [];
11
+ for (const entry of Object.values(index.entries)) {
12
+ if (entry.expiresAt !== undefined && entry.expiresAt < now) {
13
+ expired.push(entry);
14
+ }
15
+ }
16
+ return expired;
17
+ }
18
+
19
+ /**
20
+ * Identifies entries to remove based on LRU policy to meet size limit.
21
+ * Sorts entries by lastAccessedAt (oldest first) and returns entries
22
+ * that need to be removed to get under maxSizeBytes.
23
+ */
24
+ export function getLruPruneTargets(index, maxSizeBytes) {
25
+ if (index.totalSizeBytes <= maxSizeBytes) {
26
+ return [];
27
+ }
28
+
29
+ // Sort entries by lastAccessedAt ascending (oldest first = LRU candidates)
30
+ const sortedEntries = Object.values(index.entries).sort((a, b) => a.lastAccessedAt - b.lastAccessedAt);
31
+ const toRemove = [];
32
+ let currentSize = index.totalSizeBytes;
33
+ for (const entry of sortedEntries) {
34
+ if (currentSize <= maxSizeBytes) {
35
+ break;
36
+ }
37
+ toRemove.push(entry);
38
+ currentSize -= entry.sizeBytes;
39
+ }
40
+ return toRemove;
41
+ }
42
+
43
+ /**
44
+ * Creates a new index with the specified entries removed.
45
+ * Returns a new ICacheIndex without mutating the original.
46
+ */
47
+ export function removeEntriesFromIndex(index, keysToRemove) {
48
+ const keySet = new Set(keysToRemove);
49
+ const newEntries = {};
50
+ let newTotalSize = 0;
51
+ for (const [key, entry] of Object.entries(index.entries)) {
52
+ if (!keySet.has(key)) {
53
+ newEntries[key] = entry;
54
+ newTotalSize += entry.sizeBytes;
55
+ }
56
+ }
57
+ return {
58
+ version: 1,
59
+ entries: newEntries,
60
+ totalSizeBytes: newTotalSize,
61
+ lastModifiedAt: Date.now()
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Adds or updates an entry in the index.
67
+ * Returns a new ICacheIndex without mutating the original.
68
+ */
69
+ export function addEntryToIndex(index, entry) {
70
+ const existingEntry = index.entries[entry.key];
71
+ const sizeDelta = entry.sizeBytes - (existingEntry?.sizeBytes ?? 0);
72
+ return {
73
+ version: 1,
74
+ entries: {
75
+ ...index.entries,
76
+ [entry.key]: entry
77
+ },
78
+ totalSizeBytes: index.totalSizeBytes + sizeDelta,
79
+ lastModifiedAt: Date.now()
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Updates the lastAccessedAt timestamp for an entry.
85
+ * Returns a new ICacheIndex without mutating the original.
86
+ */
87
+ export function touchEntry(index, key, accessedAt = Date.now()) {
88
+ const entry = index.entries[key];
89
+ if (!entry) {
90
+ return index;
91
+ }
92
+ return {
93
+ ...index,
94
+ entries: {
95
+ ...index.entries,
96
+ [key]: {
97
+ ...entry,
98
+ lastAccessedAt: accessedAt
99
+ }
100
+ },
101
+ lastModifiedAt: accessedAt
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Creates an empty cache index.
107
+ */
108
+ export function createEmptyIndex() {
109
+ return {
110
+ version: 1,
111
+ entries: {},
112
+ totalSizeBytes: 0,
113
+ lastModifiedAt: Date.now()
114
+ };
115
+ }
116
+ //# 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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"../../../src","sources":["core/types.ts"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,49 @@
1
+ import { CacheEngine } from "./core/cacheEngine";
2
+ /**
3
+ * Creates an immutable file cache instance.
4
+ *
5
+ * This is the base factory function. Platform-specific entrypoints
6
+ * (index.native.ts, index.web.ts) wrap this to auto-select the appropriate adapter.
7
+ *
8
+ * @param options - Cache configuration options
9
+ * @param adapter - Storage adapter (required in base factory)
10
+ * @returns Initialized CacheEngine instance
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { createImmutableFileCache } from "react-native-immutable-file-cache";
15
+ *
16
+ * const cache = await createImmutableFileCache({
17
+ * namespace: "images",
18
+ * defaultTtlMs: 7 * 24 * 60 * 60 * 1000, // 7 days
19
+ * });
20
+ * ```
21
+ */
22
+ export async function createImmutableFileCache(options) {
23
+ const {
24
+ autoInit = true,
25
+ adapter,
26
+ ...config
27
+ } = options;
28
+ const engine = new CacheEngine(config, adapter);
29
+ if (autoInit) {
30
+ await engine.init();
31
+ }
32
+ return engine;
33
+ }
34
+
35
+ /**
36
+ * Creates a cache engine without auto-initialization.
37
+ * Useful when you need to control initialization timing.
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * const engine = createCacheEngineSync({ namespace: "images" }, adapter);
42
+ * // Later...
43
+ * await engine.init();
44
+ * ```
45
+ */
46
+ export function createCacheEngineSync(config, adapter) {
47
+ return new CacheEngine(config, adapter);
48
+ }
49
+ //# sourceMappingURL=factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["CacheEngine","createImmutableFileCache","options","autoInit","adapter","config","engine","init","createCacheEngineSync"],"sourceRoot":"../../src","sources":["factory.ts"],"mappings":"AAEA,SAASA,WAAW,QAAQ,oBAAoB;AAUhD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeC,wBAAwBA,CAC5CC,OAA2D,EACrC;EACtB,MAAM;IAAEC,QAAQ,GAAG,IAAI;IAAEC,OAAO;IAAE,GAAGC;EAAO,CAAC,GAAGH,OAAO;EAEvD,MAAMI,MAAM,GAAG,IAAIN,WAAW,CAACK,MAAM,EAAED,OAAO,CAAC;EAE/C,IAAID,QAAQ,EAAE;IACZ,MAAMG,MAAM,CAACC,IAAI,CAAC,CAAC;EACrB;EAEA,OAAOD,MAAM;AACf;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASE,qBAAqBA,CAACH,MAAoB,EAAED,OAAwB,EAAe;EACjG,OAAO,IAAIJ,WAAW,CAACK,MAAM,EAAED,OAAO,CAAC;AACzC","ignoreList":[]}