@casfa/storage-fs 0.2.0 → 0.3.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 +91 -0
- package/README.zh-CN.md +91 -0
- package/dist/fs-storage.d.ts +23 -0
- package/dist/fs-storage.d.ts.map +1 -0
- package/dist/index.d.ts +4 -37
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -49
- package/dist/index.js.map +10 -1
- package/package.json +3 -3
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
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
|
+
|
|
46
|
+
## Configuration
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
interface FsStorageConfig {
|
|
50
|
+
// Required: Base directory for storage
|
|
51
|
+
basePath: string;
|
|
52
|
+
|
|
53
|
+
// Optional: File permissions (default: 0o644)
|
|
54
|
+
fileMode?: number;
|
|
55
|
+
|
|
56
|
+
// Optional: Directory permissions (default: 0o755)
|
|
57
|
+
dirMode?: number;
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## API Reference
|
|
62
|
+
|
|
63
|
+
### Functions
|
|
64
|
+
|
|
65
|
+
- `createFsStorage(config)` - Create file-system storage
|
|
66
|
+
|
|
67
|
+
### StorageProvider Interface
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
interface StorageProvider {
|
|
71
|
+
get(key: string): Promise<Uint8Array | null>;
|
|
72
|
+
put(key: string, data: Uint8Array): Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Performance Tips
|
|
77
|
+
|
|
78
|
+
1. **Use caching** for read-heavy workloads
|
|
79
|
+
2. **Use SSD storage** for better random access performance
|
|
80
|
+
3. **Monitor disk space** as CAS data is append-only by nature
|
|
81
|
+
4. Consider **separate partitions** for CAS data to prevent filling system disk
|
|
82
|
+
|
|
83
|
+
## Related Packages
|
|
84
|
+
|
|
85
|
+
- `@casfa/storage-core` - Core types and utilities
|
|
86
|
+
- `@casfa/storage-memory` - In-memory storage (for testing)
|
|
87
|
+
- `@casfa/storage-s3` - S3 storage (for cloud deployment)
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
MIT
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
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
|
+
## 配置
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
interface FsStorageConfig {
|
|
50
|
+
// 必需:存储基础目录
|
|
51
|
+
basePath: string;
|
|
52
|
+
|
|
53
|
+
// 可选:文件权限(默认: 0o644)
|
|
54
|
+
fileMode?: number;
|
|
55
|
+
|
|
56
|
+
// 可选:目录权限(默认: 0o755)
|
|
57
|
+
dirMode?: number;
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## API 参考
|
|
62
|
+
|
|
63
|
+
### 函数
|
|
64
|
+
|
|
65
|
+
- `createFsStorage(config)` - 创建文件系统存储
|
|
66
|
+
|
|
67
|
+
### StorageProvider 接口
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
interface StorageProvider {
|
|
71
|
+
get(key: string): Promise<Uint8Array | null>;
|
|
72
|
+
put(key: string, data: Uint8Array): Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 性能建议
|
|
77
|
+
|
|
78
|
+
1. **使用缓存** 以应对读密集型工作负载
|
|
79
|
+
2. **使用 SSD 存储** 以获得更好的随机访问性能
|
|
80
|
+
3. **监控磁盘空间**,因为 CAS 数据本质上是只追加的
|
|
81
|
+
4. 考虑为 CAS 数据使用**独立分区**,防止占满系统磁盘
|
|
82
|
+
|
|
83
|
+
## 相关包
|
|
84
|
+
|
|
85
|
+
- `@casfa/storage-core` - 核心类型与工具
|
|
86
|
+
- `@casfa/storage-memory` - 内存存储(用于测试)
|
|
87
|
+
- `@casfa/storage-s3` - S3 存储(用于云端部署)
|
|
88
|
+
|
|
89
|
+
## 许可证
|
|
90
|
+
|
|
91
|
+
MIT
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File System Storage Provider for CAS
|
|
3
|
+
*
|
|
4
|
+
* Implements StorageProvider with:
|
|
5
|
+
* - Local file system backend storage
|
|
6
|
+
* - Automatic directory creation
|
|
7
|
+
* - Internal existence check in put() to avoid redundant writes
|
|
8
|
+
*/
|
|
9
|
+
import type { StorageProvider } from "@casfa/storage-core";
|
|
10
|
+
/**
|
|
11
|
+
* File System Storage configuration
|
|
12
|
+
*/
|
|
13
|
+
export type FsStorageConfig = {
|
|
14
|
+
/** Base directory for storage */
|
|
15
|
+
basePath: string;
|
|
16
|
+
/** Key prefix in storage (default: "cas/v1/") */
|
|
17
|
+
prefix?: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Create a file system-backed storage provider
|
|
21
|
+
*/
|
|
22
|
+
export declare const createFsStorage: (config: FsStorageConfig) => StorageProvider;
|
|
23
|
+
//# sourceMappingURL=fs-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs-storage.d.ts","sourceRoot":"","sources":["../src/fs-storage.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAa3D;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,eAAe,KAAG,eA6CzD,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,40 +1,7 @@
|
|
|
1
|
-
import { StorageProvider } from '@casfa/storage-core';
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
* File System Storage
|
|
2
|
+
* CAS File System Storage
|
|
5
3
|
*
|
|
6
|
-
*
|
|
7
|
-
* - LRU cache for key existence checks
|
|
8
|
-
* - Local file system backend storage
|
|
9
|
-
* - Automatic directory creation
|
|
4
|
+
* File system-backed storage provider for CAS.
|
|
10
5
|
*/
|
|
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 };
|
|
6
|
+
export { createFsStorage, type FsStorageConfig } from "./fs-storage.ts";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,39 +1,22 @@
|
|
|
1
1
|
// src/fs-storage.ts
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
2
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { stat } from "node:fs/promises";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
var toStoragePath = (key, prefix) => {
|
|
6
|
+
const subdir = key.slice(0, 2);
|
|
7
|
+
return `${prefix}${subdir}/${key}`;
|
|
8
|
+
};
|
|
9
9
|
var createFsStorage = (config) => {
|
|
10
10
|
const basePath = config.basePath;
|
|
11
|
-
const prefix = config.prefix ?? "cas/
|
|
12
|
-
const existsCache = createLRUCache(config.cacheSize ?? DEFAULT_CACHE_SIZE);
|
|
13
|
-
if (!existsSync(basePath)) {
|
|
14
|
-
mkdirSync(basePath, { recursive: true });
|
|
15
|
-
}
|
|
11
|
+
const prefix = config.prefix ?? "cas/v1/";
|
|
16
12
|
const toFilePath = (casKey) => {
|
|
17
13
|
const storagePath = toStoragePath(casKey, prefix);
|
|
18
14
|
return join(basePath, storagePath);
|
|
19
15
|
};
|
|
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
16
|
const get = async (key) => {
|
|
33
17
|
const filePath = toFilePath(key);
|
|
34
18
|
try {
|
|
35
19
|
const buffer = readFileSync(filePath);
|
|
36
|
-
existsCache.set(key, true);
|
|
37
20
|
return new Uint8Array(buffer);
|
|
38
21
|
} catch (error) {
|
|
39
22
|
const err = error;
|
|
@@ -44,34 +27,19 @@ var createFsStorage = (config) => {
|
|
|
44
27
|
}
|
|
45
28
|
};
|
|
46
29
|
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
30
|
const filePath = toFilePath(key);
|
|
31
|
+
try {
|
|
32
|
+
await stat(filePath);
|
|
33
|
+
return;
|
|
34
|
+
} catch {}
|
|
55
35
|
const dir = dirname(filePath);
|
|
56
|
-
|
|
57
|
-
mkdirSync(dir, { recursive: true });
|
|
58
|
-
}
|
|
36
|
+
mkdirSync(dir, { recursive: true });
|
|
59
37
|
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
38
|
};
|
|
39
|
+
return { get, put };
|
|
72
40
|
};
|
|
73
41
|
export {
|
|
74
|
-
createFsStorage
|
|
75
|
-
createFsStorageWithCache
|
|
42
|
+
createFsStorage
|
|
76
43
|
};
|
|
77
|
-
|
|
44
|
+
|
|
45
|
+
//# debugId=21AA1842112D604764756E2164756E21
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1,10 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/fs-storage.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * File System Storage Provider for CAS\n *\n * Implements StorageProvider with:\n * - Local file system backend storage\n * - Automatic directory creation\n * - Internal existence check in put() to avoid redundant writes\n */\n\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { stat } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport type { StorageProvider } from \"@casfa/storage-core\";\n\n/**\n * Create storage path from a CB32 storage key.\n * Uses first 2 chars as subdirectory for better distribution.\n *\n * Example: 240B5PHBGEC2A705WTKKMVRS30 -> cas/v1/24/240B5PHBGEC2A705WTKKMVRS30\n */\nconst toStoragePath = (key: string, prefix: string): string => {\n const subdir = key.slice(0, 2);\n return `${prefix}${subdir}/${key}`;\n};\n\n/**\n * File System Storage configuration\n */\nexport type FsStorageConfig = {\n /** Base directory for storage */\n basePath: string;\n /** Key prefix in storage (default: \"cas/v1/\") */\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/v1/\";\n\n const toFilePath = (casKey: string): string => {\n const storagePath = toStoragePath(casKey, prefix);\n return join(basePath, storagePath);\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 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 const filePath = toFilePath(key);\n\n // Internal optimization: stat is cheaper than write for existing files\n try {\n await stat(filePath);\n return; // already exists\n } catch {\n // doesn't exist, proceed to write\n }\n\n const dir = dirname(filePath);\n\n // Ensure directory exists\n mkdirSync(dir, { recursive: true });\n\n // Write file\n writeFileSync(filePath, value);\n };\n\n return { get, put };\n};\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";AASA;AACA;AACA;AASA,IAAM,gBAAgB,CAAC,KAAa,WAA2B;AAAA,EAC7D,MAAM,SAAS,IAAI,MAAM,GAAG,CAAC;AAAA,EAC7B,OAAO,GAAG,SAAS,UAAU;AAAA;AAgBxB,IAAM,kBAAkB,CAAC,WAA6C;AAAA,EAC3E,MAAM,WAAW,OAAO;AAAA,EACxB,MAAM,SAAS,OAAO,UAAU;AAAA,EAEhC,MAAM,aAAa,CAAC,WAA2B;AAAA,IAC7C,MAAM,cAAc,cAAc,QAAQ,MAAM;AAAA,IAChD,OAAO,KAAK,UAAU,WAAW;AAAA;AAAA,EAGnC,MAAM,MAAM,OAAO,QAA4C;AAAA,IAC7D,MAAM,WAAW,WAAW,GAAG;AAAA,IAE/B,IAAI;AAAA,MACF,MAAM,SAAS,aAAa,QAAQ;AAAA,MACpC,OAAO,IAAI,WAAW,MAAM;AAAA,MAC5B,OAAO,OAAgB;AAAA,MACvB,MAAM,MAAM;AAAA,MACZ,IAAI,IAAI,SAAS,UAAU;AAAA,QACzB,OAAO;AAAA,MACT;AAAA,MACA,MAAM;AAAA;AAAA;AAAA,EAIV,MAAM,MAAM,OAAO,KAAa,UAAqC;AAAA,IACnE,MAAM,WAAW,WAAW,GAAG;AAAA,IAG/B,IAAI;AAAA,MACF,MAAM,KAAK,QAAQ;AAAA,MACnB;AAAA,MACA,MAAM;AAAA,IAIR,MAAM,MAAM,QAAQ,QAAQ;AAAA,IAG5B,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,IAGlC,cAAc,UAAU,KAAK;AAAA;AAAA,EAG/B,OAAO,EAAE,KAAK,IAAI;AAAA;",
|
|
8
|
+
"debugId": "21AA1842112D604764756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@casfa/storage-fs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "File system storage provider for CAS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
+
"bun": "./src/index.ts",
|
|
10
11
|
"types": "./dist/index.d.ts",
|
|
11
12
|
"import": "./dist/index.js"
|
|
12
13
|
}
|
|
13
14
|
},
|
|
14
15
|
"scripts": {
|
|
15
|
-
"build": "
|
|
16
|
-
"dev": "tsup --watch",
|
|
16
|
+
"build": "bun ../../scripts/build-pkg.ts",
|
|
17
17
|
"typecheck": "tsc --noEmit",
|
|
18
18
|
"lint": "biome check .",
|
|
19
19
|
"lint:fix": "biome check --write .",
|