@dynlabs/react-native-immutable-file-cache 1.0.0-alpha.2 → 1.0.0-alpha.4

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 (74) hide show
  1. package/lib/commonjs/adapters/memoryAdapter.js +1 -0
  2. package/lib/commonjs/adapters/memoryAdapter.js.map +1 -1
  3. package/lib/commonjs/adapters/rnfsAdapter.js +47 -27
  4. package/lib/commonjs/adapters/rnfsAdapter.js.map +1 -1
  5. package/lib/commonjs/adapters/webAdapter.js +1 -0
  6. package/lib/commonjs/adapters/webAdapter.js.map +1 -1
  7. package/lib/commonjs/core/adapter.js +54 -0
  8. package/lib/commonjs/core/adapter.js.map +1 -1
  9. package/lib/commonjs/core/cacheEngine.js +452 -59
  10. package/lib/commonjs/core/cacheEngine.js.map +1 -1
  11. package/lib/commonjs/core/errors.js +9 -6
  12. package/lib/commonjs/core/errors.js.map +1 -1
  13. package/lib/commonjs/core/hash.js +3 -3
  14. package/lib/commonjs/core/hash.js.map +1 -1
  15. package/lib/commonjs/core/indexStore.js +85 -8
  16. package/lib/commonjs/core/indexStore.js.map +1 -1
  17. package/lib/commonjs/core/prune.js +42 -11
  18. package/lib/commonjs/core/prune.js.map +1 -1
  19. package/lib/commonjs/core/types.js +132 -0
  20. package/lib/commonjs/core/types.js.map +1 -1
  21. package/lib/commonjs/index.js +33 -0
  22. package/lib/commonjs/index.js.map +1 -1
  23. package/lib/module/adapters/memoryAdapter.js +1 -0
  24. package/lib/module/adapters/memoryAdapter.js.map +1 -1
  25. package/lib/module/adapters/rnfsAdapter.js +27 -6
  26. package/lib/module/adapters/rnfsAdapter.js.map +1 -1
  27. package/lib/module/adapters/webAdapter.js +1 -0
  28. package/lib/module/adapters/webAdapter.js.map +1 -1
  29. package/lib/module/core/adapter.js +48 -0
  30. package/lib/module/core/adapter.js.map +1 -1
  31. package/lib/module/core/cacheEngine.js +453 -60
  32. package/lib/module/core/cacheEngine.js.map +1 -1
  33. package/lib/module/core/errors.js +9 -6
  34. package/lib/module/core/errors.js.map +1 -1
  35. package/lib/module/core/hash.js +3 -3
  36. package/lib/module/core/hash.js.map +1 -1
  37. package/lib/module/core/indexStore.js +86 -8
  38. package/lib/module/core/indexStore.js.map +1 -1
  39. package/lib/module/core/prune.js +40 -11
  40. package/lib/module/core/prune.js.map +1 -1
  41. package/lib/module/core/types.js +130 -1
  42. package/lib/module/core/types.js.map +1 -1
  43. package/lib/module/index.js +4 -0
  44. package/lib/module/index.js.map +1 -1
  45. package/lib/typescript/src/adapters/memoryAdapter.d.ts.map +1 -1
  46. package/lib/typescript/src/adapters/rnfsAdapter.d.ts +70 -1
  47. package/lib/typescript/src/adapters/rnfsAdapter.d.ts.map +1 -1
  48. package/lib/typescript/src/adapters/webAdapter.d.ts.map +1 -1
  49. package/lib/typescript/src/core/adapter.d.ts +16 -0
  50. package/lib/typescript/src/core/adapter.d.ts.map +1 -1
  51. package/lib/typescript/src/core/cacheEngine.d.ts +120 -1
  52. package/lib/typescript/src/core/cacheEngine.d.ts.map +1 -1
  53. package/lib/typescript/src/core/errors.d.ts +6 -5
  54. package/lib/typescript/src/core/errors.d.ts.map +1 -1
  55. package/lib/typescript/src/core/indexStore.d.ts +7 -0
  56. package/lib/typescript/src/core/indexStore.d.ts.map +1 -1
  57. package/lib/typescript/src/core/prune.d.ts +22 -8
  58. package/lib/typescript/src/core/prune.d.ts.map +1 -1
  59. package/lib/typescript/src/core/types.d.ts +153 -0
  60. package/lib/typescript/src/core/types.d.ts.map +1 -1
  61. package/lib/typescript/src/index.d.ts +6 -3
  62. package/lib/typescript/src/index.d.ts.map +1 -1
  63. package/package.json +14 -2
  64. package/src/adapters/memoryAdapter.ts +3 -0
  65. package/src/adapters/rnfsAdapter.ts +85 -6
  66. package/src/adapters/webAdapter.ts +1 -0
  67. package/src/core/adapter.ts +28 -0
  68. package/src/core/cacheEngine.ts +476 -62
  69. package/src/core/errors.ts +8 -6
  70. package/src/core/hash.ts +3 -3
  71. package/src/core/indexStore.ts +99 -11
  72. package/src/core/prune.ts +44 -14
  73. package/src/core/types.ts +194 -0
  74. package/src/index.ts +23 -1
@@ -3,18 +3,21 @@
3
3
  *
4
4
  * Cross-platform immutable file cache with pluggable storage adapters.
5
5
  */
6
- export type { IStorageAdapter, TAdapterPath, TBinarySource, IBinaryWriteResult, IBinaryWriteOptions, IFileStat, TProgressCallback, } from "./core/adapter";
7
- export type { ICacheConfig, ICacheEntry, ICacheEntryMeta, ICacheIndex, IPutOptions, IPutResult, IGetResult, IListOptions, IPruneResult, ICacheStats, TPutStatus, TSortField, TSortOrder, } from "./core/types";
6
+ export type { IStorageAdapter, TAdapterPath, TBinarySource, TBinarySourceType, IBinaryWriteResult, IBinaryWriteOptions, IFileStat, TProgressCallback, } from "./core/adapter";
7
+ export { adapterSupportsSource } from "./core/adapter";
8
+ export type { ICacheConfig, ICacheEntry, ICacheEntryMeta, ICacheIndex, IPutOptions, IPutResult, IGetResult, IGetOrPutResult, TFetcher, IFetcherResult, IListOptions, IPruneResult, ICacheStats, TPutStatus, TSortField, TSortOrder, TResult, IOk, IErr, TCacheEvent, TCacheEventHandler, ICacheHitEvent, ICacheMissEvent, ICacheWriteEvent, ICacheRemoveEvent, ICachePruneEvent, ICacheErrorEvent, } from "./core/types";
9
+ export { ok, err } from "./core/types";
8
10
  export { CacheError, UnsupportedSourceError, AdapterIOError, CorruptIndexError, ImmutableConflictError, EntryNotFoundError, } from "./core/errors";
9
11
  export { CacheEngine } from "./core/cacheEngine";
10
12
  export { createImmutableFileCache, createCacheEngineSync } from "./factory";
11
13
  export type { ICreateCacheOptions } from "./factory";
12
14
  export { createRnfsAdapter } from "./adapters/rnfsAdapter";
13
- export type { IRnfsAdapterOptions } from "./adapters/rnfsAdapter";
15
+ export type { IRnfsAdapterOptions, IRnfsLike } from "./adapters/rnfsAdapter";
14
16
  export { createWebAdapter } from "./adapters/webAdapter";
15
17
  export type { IWebAdapterOptions, IWebAdapterWithCleanup } from "./adapters/webAdapter";
16
18
  export { createMemoryAdapter } from "./adapters/memoryAdapter";
17
19
  export type { IMemoryAdapter } from "./adapters/memoryAdapter";
18
20
  export { hash, hashSync } from "./core/hash";
19
21
  export { Mutex, KeyedMutex } from "./core/mutex";
22
+ export { isEntryExpired, isEntryValid } from "./core/prune";
20
23
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,YAAY,EACV,eAAe,EACf,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,SAAS,EACT,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AAExB,YAAY,EACV,YAAY,EACZ,WAAW,EACX,eAAe,EACf,WAAW,EACX,WAAW,EACX,UAAU,EACV,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,UAAU,EACV,UAAU,GACX,MAAM,cAAc,CAAC;AAMtB,OAAO,EACL,UAAU,EACV,sBAAsB,EACtB,cAAc,EACd,iBAAiB,EACjB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AAMvB,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAMjD,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAE5E,YAAY,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAMrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAElE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAExF,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAM/D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,YAAY,EACV,eAAe,EACf,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,SAAS,EACT,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,YAAY,EACV,YAAY,EACZ,WAAW,EACX,eAAe,EACf,WAAW,EACX,WAAW,EACX,UAAU,EACV,UAAU,EACV,eAAe,EACf,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,UAAU,EACV,UAAU,EAEV,OAAO,EACP,GAAG,EACH,IAAI,EAEJ,WAAW,EACX,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAMvC,OAAO,EACL,UAAU,EACV,sBAAsB,EACtB,cAAc,EACd,iBAAiB,EACjB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AAMvB,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAMjD,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAE5E,YAAY,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAMrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAE7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAExF,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAM/D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynlabs/react-native-immutable-file-cache",
3
- "version": "1.0.0-alpha.2",
3
+ "version": "1.0.0-alpha.4",
4
4
  "description": "Cross-platform immutable file cache with pluggable storage adapters for React Native and Web",
5
5
  "author": "dienp",
6
6
  "license": "MIT",
@@ -63,7 +63,8 @@
63
63
  "prepublishOnly": "npm run build && npm run lint && npm run typecheck && npm test",
64
64
  "release:patch": "npm version patch",
65
65
  "release:minor": "npm version minor",
66
- "release:major": "npm version major"
66
+ "release:major": "npm version major",
67
+ "prepare": "husky"
67
68
  },
68
69
  "keywords": [
69
70
  "react-native",
@@ -98,7 +99,9 @@
98
99
  "eslint": "^8.57.0",
99
100
  "eslint-config-prettier": "^9.1.0",
100
101
  "eslint-plugin-prettier": "^5.1.3",
102
+ "husky": "^9.1.7",
101
103
  "jest": "^29.7.0",
104
+ "lint-staged": "^16.2.7",
102
105
  "prettier": "^3.2.5",
103
106
  "react-native-builder-bob": "^0.23.2",
104
107
  "rimraf": "^5.0.5",
@@ -121,5 +124,14 @@
121
124
  },
122
125
  "engines": {
123
126
  "node": ">=18"
127
+ },
128
+ "lint-staged": {
129
+ "src/**/*.{ts,tsx}": [
130
+ "eslint --fix",
131
+ "prettier --write"
132
+ ],
133
+ "__tests__/**/*.ts": [
134
+ "prettier --write"
135
+ ]
124
136
  }
125
137
  }
@@ -67,6 +67,9 @@ export function createMemoryAdapter(namespace?: string): IMemoryAdapter {
67
67
  const adapter: IMemoryAdapter = {
68
68
  kind: "memory",
69
69
  rootId,
70
+ supportedSources: new Set(["url", "file", "blob", "bytes"]) as ReadonlySet<
71
+ TBinarySource["type"]
72
+ >,
70
73
 
71
74
  // ─────────────────────────────────────────────────────────────────
72
75
  // Directory Management
@@ -1,8 +1,11 @@
1
1
  /**
2
2
  * React Native FS Adapter
3
3
  *
4
- * This is the ONLY file that imports react-native-fs.
4
+ * This is the ONLY file that imports react-native-fs by default.
5
5
  * Implements IStorageAdapter using RNFS APIs.
6
+ *
7
+ * Users can provide their own RNFS-compatible instance (e.g., a fork)
8
+ * via the `rnfs` option.
6
9
  */
7
10
 
8
11
  /* eslint-disable @typescript-eslint/no-unsafe-assignment */
@@ -13,7 +16,7 @@
13
16
  /* eslint-disable @typescript-eslint/require-await */
14
17
 
15
18
  // RNFS types are not fully typed, disable unsafe rules for this adapter
16
- import RNFS, { type DownloadResult } from "react-native-fs";
19
+ import DefaultRNFS, { type DownloadResult } from "react-native-fs";
17
20
  import type {
18
21
  IStorageAdapter,
19
22
  TAdapterPath,
@@ -24,17 +27,86 @@ import type {
24
27
  } from "../core/adapter";
25
28
  import { UnsupportedSourceError, AdapterIOError } from "../core/errors";
26
29
 
30
+ /**
31
+ * Minimal interface for RNFS-compatible libraries.
32
+ * Allows users to provide alternative implementations or forks.
33
+ */
34
+ export interface IRnfsLike {
35
+ /** Path to the caches directory. */
36
+ readonly CachesDirectoryPath: string;
37
+
38
+ /** Check if a file/directory exists. */
39
+ exists(path: string): Promise<boolean>;
40
+
41
+ /** Create a directory (and parents). */
42
+ mkdir(path: string): Promise<void>;
43
+
44
+ /** Delete a file or directory. */
45
+ unlink(path: string): Promise<void>;
46
+
47
+ /** Read directory contents. */
48
+ readDir(path: string): Promise<ReadonlyArray<{ name: string }>>;
49
+
50
+ /** Read file as text. */
51
+ readFile(path: string, encoding: "utf8" | "base64"): Promise<string>;
52
+
53
+ /** Write file. */
54
+ writeFile(path: string, content: string, encoding: "utf8" | "base64"): Promise<void>;
55
+
56
+ /** Move/rename a file. */
57
+ moveFile(from: string, to: string): Promise<void>;
58
+
59
+ /** Copy a file. */
60
+ copyFile(from: string, to: string): Promise<void>;
61
+
62
+ /** Get file stats. */
63
+ stat(path: string): Promise<{ size: number; mtime: string | number | Date }>;
64
+
65
+ /** Download a file from URL. */
66
+ downloadFile(options: {
67
+ fromUrl: string;
68
+ toFile: string;
69
+ headers?: Record<string, string>;
70
+ progress?: (res: { contentLength: number; bytesWritten: number }) => void;
71
+ progressDivider?: number;
72
+ }): { promise: Promise<DownloadResult> };
73
+ }
74
+
27
75
  export interface IRnfsAdapterOptions {
28
76
  /** Base directory. @default RNFS.CachesDirectoryPath */
29
77
  readonly baseDir?: string;
30
78
  /** Namespace subdirectory. */
31
79
  readonly namespace?: string;
80
+ /**
81
+ * Custom RNFS-compatible instance.
82
+ * Use this to provide a fork or alternative implementation of react-native-fs.
83
+ * @default The default react-native-fs import
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * import CustomRNFS from 'react-native-fs-turbo';
88
+ *
89
+ * const adapter = createRnfsAdapter({
90
+ * rnfs: CustomRNFS,
91
+ * namespace: 'my-cache',
92
+ * });
93
+ * ```
94
+ */
95
+ readonly rnfs?: IRnfsLike;
32
96
  }
33
97
 
34
98
  /**
35
99
  * Creates a storage adapter backed by react-native-fs.
100
+ *
101
+ * @param options - Configuration options
102
+ * @param options.baseDir - Base directory for cache storage
103
+ * @param options.namespace - Namespace subdirectory for isolation
104
+ * @param options.rnfs - Custom RNFS-compatible instance (for forks)
36
105
  */
37
106
  export function createRnfsAdapter(options?: IRnfsAdapterOptions): IStorageAdapter {
107
+ // Use provided RNFS instance or fall back to default import
108
+ const RNFS: IRnfsLike = options?.rnfs ?? DefaultRNFS;
109
+
38
110
  const baseDir = options?.baseDir ?? RNFS.CachesDirectoryPath;
39
111
  const namespace = options?.namespace ?? "default";
40
112
  const rootPath = `${baseDir}/immutable-cache/${namespace}`;
@@ -56,6 +128,7 @@ export function createRnfsAdapter(options?: IRnfsAdapterOptions): IStorageAdapte
56
128
  const adapter: IStorageAdapter = {
57
129
  kind: "native-fs",
58
130
  rootId: rootPath,
131
+ supportedSources: new Set(["url", "file", "bytes"]) as ReadonlySet<TBinarySource["type"]>,
59
132
 
60
133
  // ─────────────────────────────────────────────────────────────────
61
134
  // Directory Management
@@ -273,11 +346,17 @@ export function createRnfsAdapter(options?: IRnfsAdapterOptions): IStorageAdapte
273
346
 
274
347
  /**
275
348
  * Converts Uint8Array to base64 string.
349
+ * Uses chunked approach for better performance with large arrays.
276
350
  */
277
351
  function uint8ArrayToBase64(bytes: Uint8Array): string {
278
- let binary = "";
279
- for (let i = 0; i < bytes.length; i++) {
280
- binary += String.fromCharCode(bytes[i]);
352
+ // Process in chunks to avoid call stack issues with large arrays
353
+ const CHUNK_SIZE = 0x8000; // 32KB chunks
354
+ const chunks: string[] = [];
355
+
356
+ for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
357
+ const chunk = bytes.subarray(i, i + CHUNK_SIZE);
358
+ chunks.push(String.fromCharCode.apply(null, chunk as unknown as number[]));
281
359
  }
282
- return btoa(binary);
360
+
361
+ return btoa(chunks.join(""));
283
362
  }
@@ -180,6 +180,7 @@ export function createWebAdapter(options?: IWebAdapterOptions): IStorageAdapter
180
180
  const adapter: IStorageAdapter = {
181
181
  kind: "web",
182
182
  rootId,
183
+ supportedSources: new Set(["url", "blob", "bytes"]) as ReadonlySet<TBinarySource["type"]>,
183
184
 
184
185
  // ─────────────────────────────────────────────────────────────────
185
186
  // Directory Management
@@ -4,6 +4,11 @@
4
4
  */
5
5
  export type TAdapterPath = string;
6
6
 
7
+ /**
8
+ * Binary source type identifier.
9
+ */
10
+ export type TBinarySourceType = TBinarySource["type"];
11
+
7
12
  /**
8
13
  * Discriminated union for binary content sources.
9
14
  * Adapters support the subset they can handle; throw UnsupportedSourceError otherwise.
@@ -59,6 +64,14 @@ export interface IStorageAdapter {
59
64
  /** A stable root identifier (e.g., absolute cache root on native, cache name on web). */
60
65
  readonly rootId: string;
61
66
 
67
+ /**
68
+ * Set of binary source types this adapter supports.
69
+ * Used for capability checking before attempting operations.
70
+ * If not provided, all source types are assumed to be potentially supported
71
+ * (with UnsupportedSourceError thrown at runtime for unsupported types).
72
+ */
73
+ readonly supportedSources?: ReadonlySet<TBinarySourceType>;
74
+
62
75
  // ─────────────────────────────────────────────────────────────────
63
76
  // Directory Management
64
77
  // ─────────────────────────────────────────────────────────────────
@@ -126,3 +139,18 @@ export interface IStorageAdapter {
126
139
  */
127
140
  getPublicUri(path: TAdapterPath): Promise<string>;
128
141
  }
142
+
143
+ /**
144
+ * Checks if an adapter supports a given binary source type.
145
+ * Returns true if the adapter declares support or if no capability declaration exists.
146
+ */
147
+ export function adapterSupportsSource(
148
+ adapter: IStorageAdapter,
149
+ sourceType: TBinarySourceType
150
+ ): boolean {
151
+ if (!adapter.supportedSources) {
152
+ // No capability declaration - assume all types potentially supported
153
+ return true;
154
+ }
155
+ return adapter.supportedSources.has(sourceType);
156
+ }