fs-object-storage 1.0.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.
@@ -0,0 +1,66 @@
1
+ // MinIO接続テスト用サンプル
2
+ import * as Minio from 'minio';
3
+
4
+ console.log('=== MinIO接続テスト ===');
5
+
6
+ const minioClient = new Minio.Client({
7
+ endPoint: 'localhost',
8
+ port: 9000,
9
+ useSSL: false,
10
+ accessKey: 'minioadmin',
11
+ secretKey: 'minioadmin123'
12
+ });
13
+
14
+ async function testMinIOConnection() {
15
+ try {
16
+ console.log('\n1. MinIOサーバーへの接続テスト');
17
+
18
+ // バケット一覧の取得テスト
19
+ const buckets = await minioClient.listBuckets();
20
+ console.log('✅ 接続成功!現在のバケット数:', buckets.length);
21
+
22
+ // テスト用バケットの作成
23
+ const testBucket = 'fs-storage-test';
24
+ const bucketExists = await minioClient.bucketExists(testBucket);
25
+
26
+ if (!bucketExists) {
27
+ await minioClient.makeBucket(testBucket);
28
+ console.log(`✅ テストバケット '${testBucket}' を作成したのだ`);
29
+ } else {
30
+ console.log(`✅ テストバケット '${testBucket}' は既に存在するのだ`);
31
+ }
32
+
33
+ // テストファイルの書き込み
34
+ const testData = 'Hello MinIO from Node.js!';
35
+ const testFileName = 'test-file.txt';
36
+
37
+ await minioClient.putObject(testBucket, testFileName, testData);
38
+ console.log(`✅ テストファイル '${testFileName}' を書き込んだのだ`);
39
+
40
+ // テストファイルの読み込み
41
+ const dataStream = await minioClient.getObject(testBucket, testFileName);
42
+ let result = '';
43
+
44
+ dataStream.on('data', (chunk) => {
45
+ result += chunk;
46
+ });
47
+
48
+ dataStream.on('end', () => {
49
+ console.log(`✅ テストファイル読み込み結果: ${result}`);
50
+ console.log('\n🎉 MinIO環境は正常に動作しているのだ!');
51
+ });
52
+
53
+ dataStream.on('error', (err) => {
54
+ console.error('❌ ファイル読み込みエラー:', err);
55
+ });
56
+
57
+ } catch (error) {
58
+ console.error('❌ MinIO接続エラー:', error.message);
59
+ console.log('\n💡 MinIOサーバーが起動しているか確認するのだ:');
60
+ console.log(' - docker compose ps');
61
+ console.log(' - http://localhost:9001 でWebコンソールにアクセス');
62
+ }
63
+ }
64
+
65
+ // 接続テスト実行
66
+ testMinIOConnection();
@@ -0,0 +1,64 @@
1
+ // MinIOライブラリのAPI調査用サンプル(接続なし確認)
2
+ import * as Minio from 'minio';
3
+
4
+ console.log('=== MinIO API調査 ===');
5
+
6
+ // 1. MinIOクライアントの基本的なAPI確認
7
+ console.log('\n1. MinIOクライアントの利用可能なメソッド:');
8
+ const client = new Minio.Client({
9
+ endPoint: 'localhost',
10
+ port: 9000,
11
+ useSSL: false,
12
+ accessKey: 'dummy',
13
+ secretKey: 'dummy'
14
+ });
15
+
16
+ const clientMethods = Object.getOwnPropertyNames(Object.getPrototypeOf(client))
17
+ .filter(name => typeof client[name] === 'function' && !name.startsWith('_'));
18
+ console.log('主要メソッド:', clientMethods);
19
+
20
+ // 2. 重要なメソッドの詳細確認
21
+ console.log('\n2. 重要なメソッドの詳細:');
22
+ const importantMethods = [
23
+ 'getObject',
24
+ 'putObject',
25
+ 'removeObject',
26
+ 'statObject',
27
+ 'listObjects',
28
+ 'bucketExists',
29
+ 'makeBucket'
30
+ ];
31
+
32
+ importantMethods.forEach(method => {
33
+ if (typeof client[method] === 'function') {
34
+ console.log(`✓ ${method}: 利用可能`);
35
+ } else {
36
+ console.log(`✗ ${method}: 利用不可`);
37
+ }
38
+ });
39
+
40
+ // 3. fs互換APIとの対応関係の分析
41
+ console.log('\n3. fs互換APIとMinIO APIの対応関係:');
42
+ const fsToMinioMapping = {
43
+ 'fs.readFile': 'client.getObject',
44
+ 'fs.writeFile': 'client.putObject',
45
+ 'fs.exists': 'client.statObject (catchでfalse)',
46
+ 'fs.stat': 'client.statObject',
47
+ 'fs.unlink': 'client.removeObject',
48
+ 'fs.readdir': 'client.listObjects'
49
+ };
50
+
51
+ Object.entries(fsToMinioMapping).forEach(([fsMethod, minioMethod]) => {
52
+ console.log(`${fsMethod} → ${minioMethod}`);
53
+ });
54
+
55
+ // 4. 設定可能なオプションの確認
56
+ console.log('\n4. 主要設定項目:');
57
+ console.log('- endPoint: MinIOサーバーのホスト');
58
+ console.log('- port: ポート番号');
59
+ console.log('- useSSL: SSL使用有無');
60
+ console.log('- accessKey: アクセスキー');
61
+ console.log('- secretKey: シークレットキー');
62
+ console.log('- region: リージョン(オプション)');
63
+
64
+ console.log('\n=== MinIO調査完了 ===');
package/src/index.d.ts ADDED
@@ -0,0 +1,98 @@
1
+ import { Readable, Writable } from 'stream';
2
+
3
+ export interface MinioConfig {
4
+ endPoint: string;
5
+ port?: number;
6
+ useSSL?: boolean;
7
+ accessKey: string;
8
+ secretKey: string;
9
+ region?: string;
10
+ sessionToken?: string;
11
+ partSize?: number;
12
+ }
13
+
14
+ export interface StatResult {
15
+ size: number;
16
+ mtime: Date;
17
+ isFile(): boolean;
18
+ isDirectory(): boolean;
19
+ isBlockDevice(): boolean;
20
+ isCharacterDevice(): boolean;
21
+ isSymbolicLink(): boolean;
22
+ isFIFO(): boolean;
23
+ isSocket(): boolean;
24
+ }
25
+
26
+ export interface WriteFileOptions {
27
+ encoding?: BufferEncoding;
28
+ mode?: number;
29
+ flag?: string;
30
+ }
31
+
32
+ export interface MkdirOptions {
33
+ recursive?: boolean;
34
+ mode?: number;
35
+ }
36
+
37
+ export interface FileSystemError extends Error {
38
+ code: string;
39
+ errno: number;
40
+ path?: string;
41
+ syscall?: string;
42
+ }
43
+
44
+ export default class FsMinioClient {
45
+ constructor(config: MinioConfig);
46
+
47
+ // File operations
48
+ readFile(path: string): Promise<Buffer>;
49
+ readFile(path: string, encoding: BufferEncoding): Promise<string>;
50
+ readFile(path: string, encoding?: BufferEncoding): Promise<Buffer | string>;
51
+
52
+ writeFile(path: string, data: string | Buffer | Uint8Array, options?: WriteFileOptions): Promise<void>;
53
+
54
+ exists(path: string): Promise<boolean>;
55
+
56
+ stat(path: string): Promise<StatResult>;
57
+
58
+ unlink(path: string): Promise<void>;
59
+
60
+ copyFile(src: string, dest: string): Promise<void>;
61
+
62
+ // Directory operations
63
+ readdir(path: string): Promise<string[]>;
64
+
65
+ mkdir(path: string, options?: MkdirOptions): Promise<void>;
66
+
67
+ rmdir(path: string): Promise<void>;
68
+
69
+ // Stream operations
70
+ createReadStream(path: string): Promise<Readable>;
71
+
72
+ createWriteStream(path: string): Promise<Writable>;
73
+ }
74
+
75
+ export class ErrorHandler {
76
+ static convertMinioError(error: Error, path?: string): FileSystemError;
77
+ static createFileSystemError(code: string, path?: string, syscall?: string): FileSystemError;
78
+ }
79
+
80
+ export class PathConverter {
81
+ static splitPath(path: string): { bucket: string; key: string };
82
+ static joinPath(bucket: string, key: string): string;
83
+ static normalizePath(path: string): string;
84
+ static isDirectory(path: string): boolean;
85
+ static getParentPath(path: string): string;
86
+ static getBasename(path: string): string;
87
+ }
88
+
89
+ export class StreamConverter {
90
+ static bufferToString(buffer: Buffer, encoding?: BufferEncoding): string;
91
+ static stringToBuffer(str: string, encoding?: BufferEncoding): Buffer;
92
+ static streamToBuffer(stream: Readable): Promise<Buffer>;
93
+ static bufferToStream(buffer: Buffer): Readable;
94
+ static createPassThroughStream(): { stream: Writable; promise: Promise<Buffer> };
95
+ static normalizeData(data: any): Buffer;
96
+ }
97
+
98
+ export default FsMinioClient;
package/src/index.js ADDED
@@ -0,0 +1,17 @@
1
+ // fs-minio - fs-compatible API for MinIO/S3 object storage
2
+ // Main entry point
3
+
4
+ import FsMinioClient from './lib/FsMinioClient.js';
5
+ import PathConverter from './lib/PathConverter.js';
6
+ import StreamConverter from './lib/StreamConverter.js';
7
+ import ErrorHandler from './lib/ErrorHandler.js';
8
+
9
+ export {
10
+ FsMinioClient,
11
+ PathConverter,
12
+ StreamConverter,
13
+ ErrorHandler
14
+ };
15
+
16
+ // Default export for convenience
17
+ export default FsMinioClient;
@@ -0,0 +1,149 @@
1
+ // ErrorHandler.js - MinIO errors to fs-compatible errors conversion
2
+
3
+ class ErrorHandler {
4
+ static errorMapping = {
5
+ // MinIO/S3 specific errors
6
+ 'NoSuchKey': { code: 'ENOENT', errno: -2, message: 'no such file or directory' },
7
+ 'NoSuchBucket': { code: 'ENOENT', errno: -2, message: 'no such file or directory' },
8
+ 'BucketNotFound': { code: 'ENOENT', errno: -2, message: 'no such file or directory' },
9
+ 'AccessDenied': { code: 'EACCES', errno: -13, message: 'permission denied' },
10
+ 'InvalidBucketName': { code: 'EINVAL', errno: -22, message: 'invalid argument' },
11
+ 'BucketAlreadyExists': { code: 'EEXIST', errno: -17, message: 'file already exists' },
12
+ 'KeyTooLong': { code: 'ENAMETOOLONG', errno: -36, message: 'file name too long' },
13
+
14
+ // Network/Connection errors
15
+ 'ENOTFOUND': { code: 'ENOTFOUND', errno: -3008, message: 'getaddrinfo ENOTFOUND' },
16
+ 'ECONNREFUSED': { code: 'ECONNREFUSED', errno: -61, message: 'connect ECONNREFUSED' },
17
+ 'ETIMEDOUT': { code: 'ETIMEDOUT', errno: -60, message: 'operation timed out' },
18
+
19
+ // Default fallback
20
+ 'Unknown': { code: 'EIO', errno: -5, message: 'input/output error' }
21
+ };
22
+
23
+ /**
24
+ * Convert MinIO error to fs-compatible error
25
+ * @param {Error} minioError - Original MinIO error
26
+ * @param {string} path - File path for context
27
+ * @param {string} operation - Operation that failed (e.g., 'open', 'read', 'write')
28
+ * @returns {Error} fs-compatible error
29
+ */
30
+ static convertError(minioError, path = null, operation = null) {
31
+ // If it's already an fs-style error, return as-is
32
+ if (minioError.code && minioError.errno) {
33
+ return minioError;
34
+ }
35
+
36
+ let errorInfo;
37
+
38
+ // Try to identify error by MinIO error code
39
+ if (minioError.code && this.errorMapping[minioError.code]) {
40
+ errorInfo = this.errorMapping[minioError.code];
41
+ } // Try to identify by error message patterns
42
+ else if (minioError.message) {
43
+ if (minioError.message.includes('key does not exist') ||
44
+ minioError.message.includes('NoSuchKey') ||
45
+ minioError.message.includes('Not Found')) {
46
+ errorInfo = this.errorMapping['NoSuchKey'];
47
+ } else if (minioError.message.includes('bucket does not exist') ||
48
+ minioError.message.includes('NoSuchBucket')) {
49
+ errorInfo = this.errorMapping['NoSuchBucket'];
50
+ } else if (minioError.message.includes('access denied') ||
51
+ minioError.message.includes('AccessDenied')) {
52
+ errorInfo = this.errorMapping['AccessDenied'];
53
+ } else if (minioError.message.includes('ENOTFOUND')) {
54
+ errorInfo = this.errorMapping['ENOTFOUND'];
55
+ } else if (minioError.message.includes('ECONNREFUSED')) {
56
+ errorInfo = this.errorMapping['ECONNREFUSED'];
57
+ } else if (minioError.message.includes('timeout') ||
58
+ minioError.message.includes('ETIMEDOUT')) {
59
+ errorInfo = this.errorMapping['ETIMEDOUT'];
60
+ } else {
61
+ errorInfo = this.errorMapping['Unknown'];
62
+ }
63
+ } else {
64
+ errorInfo = this.errorMapping['Unknown'];
65
+ }
66
+
67
+ // Create fs-style error
68
+ const error = new Error();
69
+ error.code = errorInfo.code;
70
+ error.errno = errorInfo.errno;
71
+ error.syscall = operation || 'open';
72
+ error.path = path;
73
+
74
+ // Create message in fs style: "ENOENT: no such file or directory, open '/path/to/file'"
75
+ if (path) {
76
+ error.message = `${errorInfo.code}: ${errorInfo.message}, ${error.syscall} '${path}'`;
77
+ } else {
78
+ error.message = `${errorInfo.code}: ${errorInfo.message}`;
79
+ }
80
+
81
+ // Preserve original error information
82
+ error.originalError = minioError;
83
+
84
+ return error;
85
+ }
86
+
87
+ /**
88
+ * Create custom fs-style error
89
+ * @param {string} code - Error code (e.g., 'ENOENT')
90
+ * @param {string} path - File path
91
+ * @param {string} operation - Operation name
92
+ * @returns {Error} fs-compatible error
93
+ */
94
+ static createError(code, path = null, operation = 'open') {
95
+ // ENOENT, EACCES, EEXIST, EINVAL, ENAMETOOLONG など標準エラーコードにも対応
96
+ let errorInfo = this.errorMapping[code];
97
+ if (!errorInfo) {
98
+ // 標準的なfsエラーコードをサポート
99
+ switch (code) {
100
+ case 'ENOENT': errorInfo = { code: 'ENOENT', errno: -2, message: 'no such file or directory' }; break;
101
+ case 'EACCES': errorInfo = { code: 'EACCES', errno: -13, message: 'permission denied' }; break;
102
+ case 'EEXIST': errorInfo = { code: 'EEXIST', errno: -17, message: 'file already exists' }; break;
103
+ case 'EINVAL': errorInfo = { code: 'EINVAL', errno: -22, message: 'invalid argument' }; break;
104
+ case 'ENAMETOOLONG': errorInfo = { code: 'ENAMETOOLONG', errno: -36, message: 'file name too long' }; break;
105
+ default: errorInfo = this.errorMapping['Unknown'];
106
+ }
107
+ }
108
+ const error = new Error();
109
+ error.code = code;
110
+ error.errno = errorInfo.errno;
111
+ error.syscall = operation;
112
+ error.path = path;
113
+ if (path) {
114
+ error.message = `${code}: ${errorInfo.message}, ${operation} '${path}'`;
115
+ } else {
116
+ error.message = `${code}: ${errorInfo.message}`;
117
+ }
118
+ return error;
119
+ }
120
+
121
+ /**
122
+ * Check if error indicates file not found
123
+ * @param {Error} error - Error to check
124
+ * @returns {boolean} True if file not found error
125
+ */
126
+ static isNotFoundError(error) {
127
+ return error && error.code === 'ENOENT';
128
+ }
129
+
130
+ /**
131
+ * Check if error indicates access denied
132
+ * @param {Error} error - Error to check
133
+ * @returns {boolean} True if access denied error
134
+ */
135
+ static isAccessDeniedError(error) {
136
+ return error && error.code === 'EACCES';
137
+ }
138
+
139
+ /**
140
+ * Check if error indicates file already exists
141
+ * @param {Error} error - Error to check
142
+ * @returns {boolean} True if file exists error
143
+ */
144
+ static isExistsError(error) {
145
+ return error && error.code === 'EEXIST';
146
+ }
147
+ }
148
+
149
+ export default ErrorHandler;