@casfa/storage-fs 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/README.md ADDED
@@ -0,0 +1,127 @@
1
+ # @casfa/storage-fs
2
+
3
+ File system storage provider for CAS.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @casfa/storage-fs
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ A file system-backed storage provider for CAS (Content-Addressable Storage). Stores content in a sharded directory structure for efficient file system performance.
14
+
15
+ ### Storage Structure
16
+
17
+ ```
18
+ {basePath}/
19
+ ├── ab/
20
+ │ └── cd/
21
+ │ └── abcd1234... (full hash)
22
+ ├── ef/
23
+ │ └── 01/
24
+ │ └── ef012345...
25
+ └── ...
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ ### Basic Usage
31
+
32
+ ```typescript
33
+ import { createFsStorage } from '@casfa/storage-fs';
34
+
35
+ const storage = createFsStorage({
36
+ basePath: '/var/cas/data',
37
+ });
38
+
39
+ // Store data
40
+ await storage.put('node:abcd1234...', data);
41
+
42
+ // Retrieve data
43
+ const data = await storage.get('node:abcd1234...');
44
+
45
+ // Check existence
46
+ const exists = await storage.has('node:abcd1234...');
47
+
48
+ // Delete
49
+ const deleted = await storage.delete('node:abcd1234...');
50
+ ```
51
+
52
+ ### With LRU Cache
53
+
54
+ ```typescript
55
+ import { createFsStorageWithCache } from '@casfa/storage-fs';
56
+
57
+ const storage = createFsStorageWithCache({
58
+ basePath: '/var/cas/data',
59
+ cacheSize: 1000, // Max items in cache
60
+ cacheMaxAge: 60000, // TTL in milliseconds
61
+ });
62
+
63
+ // Cache is automatically managed
64
+ const data = await storage.get('node:abcd1234...'); // May hit cache
65
+ ```
66
+
67
+ ## Configuration
68
+
69
+ ```typescript
70
+ interface FsStorageConfig {
71
+ // Required: Base directory for storage
72
+ basePath: string;
73
+
74
+ // Optional: File permissions (default: 0o644)
75
+ fileMode?: number;
76
+
77
+ // Optional: Directory permissions (default: 0o755)
78
+ dirMode?: number;
79
+ }
80
+ ```
81
+
82
+ ### Cache Options
83
+
84
+ ```typescript
85
+ interface FsStorageWithCacheConfig extends FsStorageConfig {
86
+ // Max items in LRU cache (default: 1000)
87
+ cacheSize?: number;
88
+
89
+ // Cache TTL in milliseconds (default: 60000)
90
+ cacheMaxAge?: number;
91
+ }
92
+ ```
93
+
94
+ ## API Reference
95
+
96
+ ### Functions
97
+
98
+ - `createFsStorage(config)` - Create storage without cache
99
+ - `createFsStorageWithCache(config)` - Create storage with LRU cache
100
+
101
+ ### StorageProvider Interface
102
+
103
+ ```typescript
104
+ interface StorageProvider {
105
+ get(key: string): Promise<Uint8Array | null>;
106
+ put(key: string, data: Uint8Array): Promise<void>;
107
+ has(key: string): Promise<boolean>;
108
+ delete(key: string): Promise<boolean>;
109
+ }
110
+ ```
111
+
112
+ ## Performance Tips
113
+
114
+ 1. **Use caching** for read-heavy workloads
115
+ 2. **Use SSD storage** for better random access performance
116
+ 3. **Monitor disk space** as CAS data is append-only by nature
117
+ 4. Consider **separate partitions** for CAS data to prevent filling system disk
118
+
119
+ ## Related Packages
120
+
121
+ - `@casfa/storage-core` - Core types and utilities
122
+ - `@casfa/storage-memory` - In-memory storage (for testing)
123
+ - `@casfa/storage-s3` - S3 storage (for cloud deployment)
124
+
125
+ ## License
126
+
127
+ MIT
@@ -0,0 +1,127 @@
1
+ # @casfa/storage-fs
2
+
3
+ 基于文件系统的 CAS 存储提供者。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ bun add @casfa/storage-fs
9
+ ```
10
+
11
+ ## 概述
12
+
13
+ 基于文件系统的 CAS(内容寻址存储)存储提供者。采用分片目录结构存储内容,以获得更好的文件系统性能。
14
+
15
+ ### 存储结构
16
+
17
+ ```
18
+ {basePath}/
19
+ ├── ab/
20
+ │ └── cd/
21
+ │ └── abcd1234... (full hash)
22
+ ├── ef/
23
+ │ └── 01/
24
+ │ └── ef012345...
25
+ └── ...
26
+ ```
27
+
28
+ ## 使用方法
29
+
30
+ ### 基本用法
31
+
32
+ ```typescript
33
+ import { createFsStorage } from '@casfa/storage-fs';
34
+
35
+ const storage = createFsStorage({
36
+ basePath: '/var/cas/data',
37
+ });
38
+
39
+ // 存储数据
40
+ await storage.put('node:abcd1234...', data);
41
+
42
+ // 检索数据
43
+ const data = await storage.get('node:abcd1234...');
44
+
45
+ // 检查是否存在
46
+ const exists = await storage.has('node:abcd1234...');
47
+
48
+ // 删除
49
+ const deleted = await storage.delete('node:abcd1234...');
50
+ ```
51
+
52
+ ### 带 LRU 缓存
53
+
54
+ ```typescript
55
+ import { createFsStorageWithCache } from '@casfa/storage-fs';
56
+
57
+ const storage = createFsStorageWithCache({
58
+ basePath: '/var/cas/data',
59
+ cacheSize: 1000, // 缓存最大条目数
60
+ cacheMaxAge: 60000, // TTL(毫秒)
61
+ });
62
+
63
+ // 缓存自动管理
64
+ const data = await storage.get('node:abcd1234...'); // 可能命中缓存
65
+ ```
66
+
67
+ ## 配置
68
+
69
+ ```typescript
70
+ interface FsStorageConfig {
71
+ // 必需:存储基础目录
72
+ basePath: string;
73
+
74
+ // 可选:文件权限(默认: 0o644)
75
+ fileMode?: number;
76
+
77
+ // 可选:目录权限(默认: 0o755)
78
+ dirMode?: number;
79
+ }
80
+ ```
81
+
82
+ ### 缓存选项
83
+
84
+ ```typescript
85
+ interface FsStorageWithCacheConfig extends FsStorageConfig {
86
+ // LRU 缓存最大条目数(默认: 1000)
87
+ cacheSize?: number;
88
+
89
+ // 缓存 TTL,单位毫秒(默认: 60000)
90
+ cacheMaxAge?: number;
91
+ }
92
+ ```
93
+
94
+ ## API 参考
95
+
96
+ ### 函数
97
+
98
+ - `createFsStorage(config)` - 创建不带缓存的存储
99
+ - `createFsStorageWithCache(config)` - 创建带 LRU 缓存的存储
100
+
101
+ ### StorageProvider 接口
102
+
103
+ ```typescript
104
+ interface StorageProvider {
105
+ get(key: string): Promise<Uint8Array | null>;
106
+ put(key: string, data: Uint8Array): Promise<void>;
107
+ has(key: string): Promise<boolean>;
108
+ delete(key: string): Promise<boolean>;
109
+ }
110
+ ```
111
+
112
+ ## 性能建议
113
+
114
+ 1. **使用缓存** 以应对读密集型工作负载
115
+ 2. **使用 SSD 存储** 以获得更好的随机访问性能
116
+ 3. **监控磁盘空间**,因为 CAS 数据本质上是只追加的
117
+ 4. 考虑为 CAS 数据使用**独立分区**,防止占满系统磁盘
118
+
119
+ ## 相关包
120
+
121
+ - `@casfa/storage-core` - 核心类型与工具
122
+ - `@casfa/storage-memory` - 内存存储(用于测试)
123
+ - `@casfa/storage-s3` - S3 存储(用于云端部署)
124
+
125
+ ## 许可证
126
+
127
+ MIT
@@ -0,0 +1,40 @@
1
+ import { StorageProvider } from '@casfa/storage-core';
2
+
3
+ /**
4
+ * File System Storage Provider for CAS
5
+ *
6
+ * Implements StorageProvider with:
7
+ * - LRU cache for key existence checks
8
+ * - Local file system backend storage
9
+ * - Automatic directory creation
10
+ */
11
+
12
+ /**
13
+ * File System Storage configuration
14
+ */
15
+ type FsStorageConfig = {
16
+ /** Base directory for storage */
17
+ basePath: string;
18
+ /** LRU cache size for key existence (default: 10000) */
19
+ cacheSize?: number;
20
+ /** Key prefix in storage (default: "cas/sha256/") */
21
+ prefix?: string;
22
+ };
23
+ /**
24
+ * Create a file system-backed storage provider
25
+ */
26
+ declare const createFsStorage: (config: FsStorageConfig) => StorageProvider;
27
+ /**
28
+ * Create file system storage with cache control methods (for testing)
29
+ */
30
+ declare const createFsStorageWithCache: (config: FsStorageConfig) => {
31
+ clearCache: () => void;
32
+ getCacheStats: () => {
33
+ size: number;
34
+ };
35
+ has: (key: string) => Promise<boolean>;
36
+ get: (key: string) => Promise<Uint8Array | null>;
37
+ put: (key: string, value: Uint8Array) => Promise<void>;
38
+ };
39
+
40
+ export { type FsStorageConfig, createFsStorage, createFsStorageWithCache };
package/dist/index.js ADDED
@@ -0,0 +1,77 @@
1
+ // src/fs-storage.ts
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
3
+ import { dirname, join } from "path";
4
+ import {
5
+ createLRUCache,
6
+ DEFAULT_CACHE_SIZE,
7
+ toStoragePath
8
+ } from "@casfa/storage-core";
9
+ var createFsStorage = (config) => {
10
+ const basePath = config.basePath;
11
+ const prefix = config.prefix ?? "cas/sha256/";
12
+ const existsCache = createLRUCache(config.cacheSize ?? DEFAULT_CACHE_SIZE);
13
+ if (!existsSync(basePath)) {
14
+ mkdirSync(basePath, { recursive: true });
15
+ }
16
+ const toFilePath = (casKey) => {
17
+ const storagePath = toStoragePath(casKey, prefix);
18
+ return join(basePath, storagePath);
19
+ };
20
+ const has = async (key) => {
21
+ const cached = existsCache.get(key);
22
+ if (cached !== void 0) {
23
+ return cached;
24
+ }
25
+ const filePath = toFilePath(key);
26
+ const exists = existsSync(filePath);
27
+ if (exists) {
28
+ existsCache.set(key, true);
29
+ }
30
+ return exists;
31
+ };
32
+ const get = async (key) => {
33
+ const filePath = toFilePath(key);
34
+ try {
35
+ const buffer = readFileSync(filePath);
36
+ existsCache.set(key, true);
37
+ return new Uint8Array(buffer);
38
+ } catch (error) {
39
+ const err = error;
40
+ if (err.code === "ENOENT") {
41
+ return null;
42
+ }
43
+ throw error;
44
+ }
45
+ };
46
+ const put = async (key, value) => {
47
+ if (existsCache.get(key)) {
48
+ return;
49
+ }
50
+ const exists = await has(key);
51
+ if (exists) {
52
+ return;
53
+ }
54
+ const filePath = toFilePath(key);
55
+ const dir = dirname(filePath);
56
+ if (!existsSync(dir)) {
57
+ mkdirSync(dir, { recursive: true });
58
+ }
59
+ writeFileSync(filePath, value);
60
+ existsCache.set(key, true);
61
+ };
62
+ return { has, get, put };
63
+ };
64
+ var createFsStorageWithCache = (config) => {
65
+ const existsCache = createLRUCache(config.cacheSize ?? DEFAULT_CACHE_SIZE);
66
+ const storage = createFsStorage(config);
67
+ return {
68
+ ...storage,
69
+ clearCache: () => existsCache.clear(),
70
+ getCacheStats: () => ({ size: existsCache.size() })
71
+ };
72
+ };
73
+ export {
74
+ createFsStorage,
75
+ createFsStorageWithCache
76
+ };
77
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/fs-storage.ts"],"sourcesContent":["/**\n * File System Storage Provider for CAS\n *\n * Implements StorageProvider with:\n * - LRU cache for key existence checks\n * - Local file system backend storage\n * - Automatic directory creation\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport {\n createLRUCache,\n DEFAULT_CACHE_SIZE,\n type StorageProvider,\n toStoragePath,\n} from \"@casfa/storage-core\";\n\n/**\n * File System Storage configuration\n */\nexport type FsStorageConfig = {\n /** Base directory for storage */\n basePath: string;\n /** LRU cache size for key existence (default: 10000) */\n cacheSize?: number;\n /** Key prefix in storage (default: \"cas/sha256/\") */\n prefix?: string;\n};\n\n/**\n * Create a file system-backed storage provider\n */\nexport const createFsStorage = (config: FsStorageConfig): StorageProvider => {\n const basePath = config.basePath;\n const prefix = config.prefix ?? \"cas/sha256/\";\n const existsCache = createLRUCache<string, boolean>(config.cacheSize ?? DEFAULT_CACHE_SIZE);\n\n // Ensure base directory exists\n if (!existsSync(basePath)) {\n mkdirSync(basePath, { recursive: true });\n }\n\n const toFilePath = (casKey: string): string => {\n const storagePath = toStoragePath(casKey, prefix);\n return join(basePath, storagePath);\n };\n\n const has = async (key: string): Promise<boolean> => {\n // Check cache first\n const cached = existsCache.get(key);\n if (cached !== undefined) {\n return cached;\n }\n\n // Check file system\n const filePath = toFilePath(key);\n const exists = existsSync(filePath);\n\n if (exists) {\n existsCache.set(key, true);\n }\n // Don't cache non-existence (it might be written later)\n\n return exists;\n };\n\n const get = async (key: string): Promise<Uint8Array | null> => {\n const filePath = toFilePath(key);\n\n try {\n const buffer = readFileSync(filePath);\n // Mark as existing in cache\n existsCache.set(key, true);\n return new Uint8Array(buffer);\n } catch (error: unknown) {\n const err = error as { code?: string };\n if (err.code === \"ENOENT\") {\n return null;\n }\n throw error;\n }\n };\n\n const put = async (key: string, value: Uint8Array): Promise<void> => {\n // Check cache first (avoid redundant writes)\n if (existsCache.get(key)) {\n return;\n }\n\n // Check if already exists\n const exists = await has(key);\n if (exists) {\n return;\n }\n\n const filePath = toFilePath(key);\n const dir = dirname(filePath);\n\n // Ensure directory exists\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // Write file\n writeFileSync(filePath, value);\n\n // Mark as existing\n existsCache.set(key, true);\n };\n\n return { has, get, put };\n};\n\n/**\n * Create file system storage with cache control methods (for testing)\n */\nexport const createFsStorageWithCache = (config: FsStorageConfig) => {\n const existsCache = createLRUCache<string, boolean>(config.cacheSize ?? DEFAULT_CACHE_SIZE);\n\n const storage = createFsStorage(config);\n\n return {\n ...storage,\n clearCache: () => existsCache.clear(),\n getCacheStats: () => ({ size: existsCache.size() }),\n };\n};\n"],"mappings":";AASA,SAAS,YAAY,WAAW,cAAc,qBAAqB;AACnE,SAAS,SAAS,YAAY;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,OACK;AAiBA,IAAM,kBAAkB,CAAC,WAA6C;AAC3E,QAAM,WAAW,OAAO;AACxB,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,cAAc,eAAgC,OAAO,aAAa,kBAAkB;AAG1F,MAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,cAAU,UAAU,EAAE,WAAW,KAAK,CAAC;AAAA,EACzC;AAEA,QAAM,aAAa,CAAC,WAA2B;AAC7C,UAAM,cAAc,cAAc,QAAQ,MAAM;AAChD,WAAO,KAAK,UAAU,WAAW;AAAA,EACnC;AAEA,QAAM,MAAM,OAAO,QAAkC;AAEnD,UAAM,SAAS,YAAY,IAAI,GAAG;AAClC,QAAI,WAAW,QAAW;AACxB,aAAO;AAAA,IACT;AAGA,UAAM,WAAW,WAAW,GAAG;AAC/B,UAAM,SAAS,WAAW,QAAQ;AAElC,QAAI,QAAQ;AACV,kBAAY,IAAI,KAAK,IAAI;AAAA,IAC3B;AAGA,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,OAAO,QAA4C;AAC7D,UAAM,WAAW,WAAW,GAAG;AAE/B,QAAI;AACF,YAAM,SAAS,aAAa,QAAQ;AAEpC,kBAAY,IAAI,KAAK,IAAI;AACzB,aAAO,IAAI,WAAW,MAAM;AAAA,IAC9B,SAAS,OAAgB;AACvB,YAAM,MAAM;AACZ,UAAI,IAAI,SAAS,UAAU;AACzB,eAAO;AAAA,MACT;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM,MAAM,OAAO,KAAa,UAAqC;AAEnE,QAAI,YAAY,IAAI,GAAG,GAAG;AACxB;AAAA,IACF;AAGA,UAAM,SAAS,MAAM,IAAI,GAAG;AAC5B,QAAI,QAAQ;AACV;AAAA,IACF;AAEA,UAAM,WAAW,WAAW,GAAG;AAC/B,UAAM,MAAM,QAAQ,QAAQ;AAG5B,QAAI,CAAC,WAAW,GAAG,GAAG;AACpB,gBAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,IACpC;AAGA,kBAAc,UAAU,KAAK;AAG7B,gBAAY,IAAI,KAAK,IAAI;AAAA,EAC3B;AAEA,SAAO,EAAE,KAAK,KAAK,IAAI;AACzB;AAKO,IAAM,2BAA2B,CAAC,WAA4B;AACnE,QAAM,cAAc,eAAgC,OAAO,aAAa,kBAAkB;AAE1F,QAAM,UAAU,gBAAgB,MAAM;AAEtC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,YAAY,MAAM,YAAY,MAAM;AAAA,IACpC,eAAe,OAAO,EAAE,MAAM,YAAY,KAAK,EAAE;AAAA,EACnD;AACF;","names":[]}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@casfa/storage-fs",
3
+ "version": "0.1.0",
4
+ "description": "File system storage provider for CAS",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "tsup",
16
+ "dev": "tsup --watch",
17
+ "typecheck": "tsc --noEmit",
18
+ "lint": "biome check .",
19
+ "lint:fix": "biome check --write .",
20
+ "check": "tsc --noEmit && biome check .",
21
+ "check:fix": "tsc --noEmit && biome check --write .",
22
+ "prepublishOnly": "bun run build"
23
+ },
24
+ "dependencies": {
25
+ "@casfa/storage-core": "workspace:*"
26
+ },
27
+ "devDependencies": {
28
+ "typescript": "^5.7.2"
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "README.md"
33
+ ],
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "license": "MIT"
38
+ }