@h-ai/storage 0.1.0-alpha.61 → 0.1.0-alpha.62

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/dist/node.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/providers/storage-operation-log.ts","../src/providers/storage-provider-local.ts","../src/providers/storage-provider-s3.ts","../src/storage-main.ts"],"names":["logger","getMimeType","stat","dir","resolve","core","toStorageError","Buffer","ok","err"],"mappings":"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","file":"node.js","sourcesContent":["import type { StorageOperationLogConfig } from '../storage-config.js'\n\ninterface StorageOperationLogger {\n info: (message: string, context?: Record<string, unknown>) => void\n debug: (message: string, context?: Record<string, unknown>) => void\n trace: (message: string, context?: Record<string, unknown>) => void\n}\n\ntype StorageOperationCategory = 'read' | 'write'\n\nfunction normalizePayloadValue(value: unknown): unknown {\n if (value instanceof Uint8Array) {\n return { byteLength: value.byteLength }\n }\n return value\n}\n\nfunction stringifyOperationPayload(payload: unknown, maxLength: number): string {\n if (maxLength <= 0) {\n return ''\n }\n\n try {\n const text = JSON.stringify(payload, (_key, value: unknown) => {\n if (typeof value === 'bigint') {\n return value.toString()\n }\n if (typeof value === 'function') {\n return '[Function]'\n }\n return normalizePayloadValue(value)\n })\n return text.length > maxLength ? `${text.slice(0, maxLength)}...` : text\n }\n catch {\n const text = String(payload)\n return text.length > maxLength ? `${text.slice(0, maxLength)}...` : text\n }\n}\n\nexport function logStorageOperation(\n logger: StorageOperationLogger,\n config: StorageOperationLogConfig | undefined,\n category: StorageOperationCategory,\n operation: string,\n payload: unknown,\n): void {\n if (!config?.[category]) {\n return\n }\n\n logger[config.level]('Storage operation executed', {\n category,\n operation,\n payload: stringifyOperationPayload(payload, config.maxLength),\n })\n}\n","/**\n * @h-ai/storage — Local Provider 实现\n *\n * 基于本地文件系统的存储 Provider。\n * @module storage-provider-local\n */\n\nimport type { HaiError, HaiResult } from '@h-ai/core'\nimport type { LocalConfig, StorageConfig } from '../storage-config.js'\nimport type { DirOperations, FileMetadata, FileOperations, ListResult, PresignOperations, StorageProvider } from '../storage-types.js'\nimport { Buffer } from 'node:buffer'\nimport * as crypto from 'node:crypto'\nimport * as fs from 'node:fs'\nimport * as fsp from 'node:fs/promises'\n\nimport * as path from 'node:path'\nimport { core, err, ok } from '@h-ai/core'\n\nimport { storageM } from '../storage-i18n.js'\nimport { MIME_TYPE_DEFAULT, MIME_TYPES } from '../storage-mime.js'\nimport {\n\n HaiStorageError,\n\n} from '../storage-types.js'\nimport { logStorageOperation } from './storage-operation-log.js'\n\nconst logger = core.logger.child({ module: 'storage', scope: 'provider-local' })\nconst PATH_SEGMENT_SPLIT_REGEX = /[\\\\/]+/\n\n// ─── 辅助函数 ───\n\n/**\n * 将 Node.js 文件系统错误转换为 StorageError\n *\n * 映射规则:\n * - ENOENT → NOT_FOUND\n * - EACCES / EPERM → PERMISSION_DENIED\n * - ENOSPC → QUOTA_EXCEEDED\n * - EISDIR → INVALID_PATH\n * - 其他 → IO_ERROR\n *\n * @param error - 原始错误对象\n * @param key - 相关文件键(可选,用于错误消息)\n * @returns 统一的 StorageError\n */\nfunction toStorageError(error: unknown, key?: string): HaiError {\n const e = error as NodeJS.ErrnoException\n\n if (e.code === 'ENOENT') {\n return {\n ...HaiStorageError.NOT_FOUND,\n message: storageM('storage_fileNotFound', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.code === 'EACCES' || e.code === 'EPERM') {\n return {\n ...HaiStorageError.PERMISSION_DENIED,\n message: storageM('storage_permissionDenied', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.code === 'ENOSPC') {\n return {\n ...HaiStorageError.QUOTA_EXCEEDED,\n message: storageM('storage_diskSpaceInsufficient'),\n cause: error,\n }\n }\n\n if (e.code === 'EISDIR') {\n return {\n ...HaiStorageError.INVALID_PATH,\n message: storageM('storage_pathIsDir', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.code === 'EINVAL') {\n return {\n ...HaiStorageError.INVALID_PATH,\n message: e.message || storageM('storage_pathTraversal'),\n cause: error,\n }\n }\n\n return {\n code: HaiStorageError.IO_ERROR.code,\n message: storageM('storage_ioError', { params: { error: e.message ?? '' } }),\n cause: error,\n }\n}\n\n/**\n * 根据文件扩展名获取 MIME 类型\n *\n * 使用共享 MIME_TYPES 映射表,未匹配时返回 'application/octet-stream'。\n *\n * @param filePath - 文件路径或文件名\n * @returns MIME 类型字符串\n */\nfunction getMimeType(filePath: string): string {\n const ext = path.extname(filePath).toLowerCase().replace('.', '')\n return MIME_TYPES[ext] || MIME_TYPE_DEFAULT\n}\n\n/**\n * 根据文件 stat 信息计算 ETag\n *\n * 使用 \"size-mtime\" 格式,与 head 操作保持一致,无需读取文件内容。\n *\n * @param stat - 文件的 fs.Stats 对象\n * @returns 带双引号的 ETag 字符串,如 '\"a-18f7ec3b4c0\"'\n */\nfunction calculateEtag(stat: fs.Stats): string {\n return `\"${stat.size.toString(16)}-${stat.mtimeMs.toString(16)}\"`\n}\n\n/**\n * 检查 key 是否指向内部元数据文件(.meta.json)\n *\n * 拦截外部对 .meta.json 文件的直接访问,防止元数据泄漏。\n */\nfunction isMetaFile(key: string): boolean {\n return key.endsWith('.meta.json')\n}\n\n/**\n * 创建非法路径错误(用于统一映射为 INVALID_PATH)\n */\nfunction createInvalidPathError(): NodeJS.ErrnoException {\n const error = new Error(storageM('storage_pathTraversal')) as NodeJS.ErrnoException\n error.code = 'EINVAL'\n return error\n}\n\n/**\n * 判断路径片段中是否包含路径穿越标记(..)\n */\nfunction hasPathTraversalSegment(key: string): boolean {\n const segments = key.split(PATH_SEGMENT_SPLIT_REGEX).filter(Boolean)\n return segments.includes('..')\n}\n\n/**\n * 安全的路径解析,防止路径穿越攻击\n *\n * 处理流程:\n * 1. 拒绝绝对路径、空字节、`..` 路径穿越片段\n * 2. resolve(root, key) 得到完整路径\n * 3. 校验完整路径必须位于 root 内部(或等于 root)\n *\n * @param root - 存储根目录绝对路径\n * @param key - 用户提供的文件键\n * @returns 安全的完整文件路径\n * @throws 当解析后路径超出 root 范围时抛出异常\n */\nfunction safePath(root: string, key: string): string {\n // 拒绝绝对路径输入,防止 Windows 盘符绕过(如 D:\\etc\\passwd)\n if (path.isAbsolute(key)) {\n throw createInvalidPathError()\n }\n\n // 拒绝空字节与路径穿越片段\n if (key.includes('\\0') || hasPathTraversalSegment(key)) {\n throw createInvalidPathError()\n }\n\n const normalized = path.normalize(key)\n const fullPath = path.resolve(root, normalized)\n\n // 确保路径在 root 目录下\n const realRoot = path.resolve(root)\n const inRoot = fullPath === realRoot || fullPath.startsWith(`${realRoot}${path.sep}`)\n\n if (!inRoot) {\n throw createInvalidPathError()\n }\n\n return fullPath\n}\n\n// ─── Local Provider 实现 ───\n\n/**\n * 创建本地存储 Provider\n *\n * 基于本地文件系统实现的 StorageProvider。\n * connect 时会自动创建根目录;文件操作会自动创建中间目录。\n *\n * 已知限制:\n * - list 不支持 continuationToken 分页(仅支持 maxKeys 截断)\n * - presign 返回虚拟 `local://` URL,应用层需自行处理\n * - publicUrl 始终返回 null\n *\n * @returns Local Provider 实例\n */\nexport function createLocalProvider(): StorageProvider {\n let config: LocalConfig | null = null\n let connected = false\n\n // -------------------------------------------------------------------------\n // 辅助方法\n // -------------------------------------------------------------------------\n\n /** 获取当前 Local 配置,未初始化时抛异常 */\n function getConfig(): LocalConfig {\n if (!config) {\n throw new Error(storageM('storage_localNotInitialized'))\n }\n return config\n }\n\n /** 将用户 key 转为安全的绝对路径(经 safePath 防穿越处理) */\n function fullPath(key: string): string {\n return safePath(getConfig().root, key)\n }\n\n function logOperation(category: 'read' | 'write', operation: string, payload: unknown): void {\n logStorageOperation(logger, config?.operationLog, category, operation, payload)\n }\n\n /**\n * 将输入数据统一转为 Buffer\n *\n * @param data - 字符串、Buffer 或 Uint8Array\n * @returns Buffer 实例\n */\n function toBuffer(data: Buffer | Uint8Array | string): Buffer {\n if (typeof data === 'string') {\n return Buffer.from(data)\n }\n if (data instanceof Buffer) {\n return data\n }\n return Buffer.from(data)\n }\n\n // -------------------------------------------------------------------------\n // 文件操作实现\n // -------------------------------------------------------------------------\n\n const file: FileOperations = {\n async put(key, data, options = {}): Promise<HaiResult<FileMetadata>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n const buffer = toBuffer(data)\n logOperation('write', 'file.put', { key, byteLength: buffer.length, options })\n logger.debug('Putting file', { key, size: buffer.length })\n\n // 确保目录存在\n const dir = path.dirname(filePath)\n await fsp.mkdir(dir, { recursive: true, mode: getConfig().directoryMode })\n\n // 写入文件\n await fsp.writeFile(filePath, buffer, { mode: getConfig().fileMode })\n\n // 获取元数据\n const stat = await fsp.stat(filePath)\n const metadata: FileMetadata = {\n key,\n size: stat.size,\n contentType: options.contentType || getMimeType(key),\n lastModified: stat.mtime,\n etag: calculateEtag(stat),\n metadata: options.metadata,\n }\n\n // 存储自定义元数据(使用扩展属性或元数据文件)\n if (options.metadata) {\n const metaPath = `${filePath}.meta.json`\n await fsp.writeFile(metaPath, JSON.stringify({\n contentType: options.contentType,\n metadata: options.metadata,\n }))\n }\n\n return ok(metadata)\n }\n catch (error) {\n logger.warn('Put file failed', { key, error })\n return err(toStorageError(error, key))\n }\n },\n\n async get(key, options = {}): Promise<HaiResult<Buffer>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n logOperation('read', 'file.get', { key, options })\n\n // 检查范围请求\n if (options.rangeStart !== undefined || options.rangeEnd !== undefined) {\n const stat = await fsp.stat(filePath)\n const start = options.rangeStart ?? 0\n const end = options.rangeEnd !== undefined ? options.rangeEnd : stat.size - 1\n\n // 使用流读取指定范围\n return new Promise((resolve) => {\n const chunks: Buffer[] = []\n const stream = fs.createReadStream(filePath, { start, end })\n\n stream.on('data', (chunk: Buffer) => chunks.push(chunk))\n stream.on('end', () => resolve(ok(Buffer.concat(chunks))))\n stream.on('error', error => resolve(err(toStorageError(error, key))))\n })\n }\n\n const data = await fsp.readFile(filePath)\n return ok(data)\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async head(key): Promise<HaiResult<FileMetadata>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n logOperation('read', 'file.head', { key })\n const stat = await fsp.stat(filePath)\n\n if (stat.isDirectory()) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_pathIsDir', { params: { key } }),\n )\n }\n\n // 尝试读取元数据文件\n let customMetadata: { contentType?: string, metadata?: Record<string, string> } = {}\n try {\n const metaPath = `${filePath}.meta.json`\n const metaContent = await fsp.readFile(metaPath, 'utf-8')\n customMetadata = JSON.parse(metaContent)\n }\n catch {\n // 元数据文件不存在,忽略\n }\n\n // 基于文件大小和修改时间生成 ETag(无需读取整个文件)\n const etag = `\"${stat.size.toString(16)}-${stat.mtimeMs.toString(16)}\"`\n\n return ok({\n key,\n size: stat.size,\n contentType: customMetadata.contentType || getMimeType(key),\n lastModified: stat.mtime,\n etag,\n metadata: customMetadata.metadata,\n })\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async exists(key): Promise<HaiResult<boolean>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n logOperation('read', 'file.exists', { key })\n await fsp.access(filePath, fs.constants.F_OK)\n return ok(true)\n }\n catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return ok(false)\n }\n return err(toStorageError(error, key))\n }\n },\n\n async delete(key): Promise<HaiResult<void>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n logOperation('write', 'file.delete', { key })\n logger.debug('Deleting file', { key })\n await fsp.unlink(filePath)\n\n // 尝试删除元数据文件\n try {\n await fsp.unlink(`${filePath}.meta.json`)\n }\n catch {\n // 忽略\n }\n\n return ok(undefined)\n }\n catch (error) {\n // 如果文件不存在,视为删除成功\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return ok(undefined)\n }\n logger.warn('Delete file failed', { key, error })\n return err(toStorageError(error, key))\n }\n },\n\n async deleteMany(keys): Promise<HaiResult<void>> {\n logOperation('write', 'file.deleteMany', { count: keys.length, keys })\n // 并行删除,避免 await-in-loop(N+1)\n const results = await Promise.allSettled(keys.map(key => file.delete(key)))\n\n const errors: HaiError[] = []\n for (const result of results) {\n if (result.status === 'fulfilled' && !result.value.success) {\n errors.push(result.value.error)\n }\n }\n\n if (errors.length > 0) {\n return err(\n HaiStorageError.OPERATION_FAILED,\n storageM('storage_deleteManyFailed', { params: { count: errors.length } }),\n errors,\n )\n }\n\n return ok(undefined)\n },\n\n async copy(sourceKey, destKey, options = {}): Promise<HaiResult<FileMetadata>> {\n if (isMetaFile(sourceKey) || isMetaFile(destKey)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key: sourceKey } }),\n sourceKey,\n )\n }\n try {\n const sourcePath = fullPath(sourceKey)\n const destPath = fullPath(destKey)\n logOperation('write', 'file.copy', { sourceKey, destKey, options })\n logger.debug('Copying file', { sourceKey, destKey })\n\n // 确保目标目录存在\n await fsp.mkdir(path.dirname(destPath), { recursive: true, mode: getConfig().directoryMode })\n\n // 复制文件\n await fsp.copyFile(sourcePath, destPath)\n\n // 获取新文件的元数据(无需读取文件内容,使用 stat 计算 ETag)\n const stat = await fsp.stat(destPath)\n\n const metadata: FileMetadata = {\n key: destKey,\n size: stat.size,\n contentType: options.contentType || getMimeType(destKey),\n lastModified: stat.mtime,\n etag: calculateEtag(stat),\n metadata: options.metadata,\n }\n\n // 存储自定义元数据\n if (options.contentType || options.metadata) {\n const metaPath = `${destPath}.meta.json`\n await fsp.writeFile(metaPath, JSON.stringify({\n contentType: options.contentType,\n metadata: options.metadata,\n }))\n }\n\n return ok(metadata)\n }\n catch (error) {\n return err(toStorageError(error, sourceKey))\n }\n },\n }\n\n // -------------------------------------------------------------------------\n // 目录操作实现\n // -------------------------------------------------------------------------\n\n const dir: DirOperations = {\n async list(options = {}): Promise<HaiResult<ListResult>> {\n try {\n const cfg = getConfig()\n const prefix = options.prefix || ''\n const delimiter = options.delimiter || ''\n const maxKeys = options.maxKeys || 1000\n logOperation('read', 'dir.list', { options })\n\n const basePath = safePath(cfg.root, prefix)\n const files: FileMetadata[] = []\n const commonPrefixes = new Set<string>()\n\n // 递归读取目录\n async function readDir(dirPath: string): Promise<void> {\n let entries: fs.Dirent[]\n try {\n entries = await fsp.readdir(dirPath, { withFileTypes: true })\n }\n catch {\n return // 目录不存在,返回空\n }\n\n for (const entry of entries) {\n if (files.length >= maxKeys)\n return\n\n const fullEntryPath = path.join(dirPath, entry.name)\n const key = path.relative(cfg.root, fullEntryPath).replace(/\\\\/g, '/')\n\n // 跳过元数据文件\n if (entry.name.endsWith('.meta.json'))\n continue\n\n if (entry.isDirectory()) {\n if (delimiter) {\n // 有分隔符时,目录作为公共前缀\n commonPrefixes.add(`${key}/`)\n }\n else {\n // 无分隔符时,递归遍历\n await readDir(fullEntryPath)\n }\n }\n else if (entry.isFile()) {\n const stat = await fsp.stat(fullEntryPath)\n files.push({\n key,\n size: stat.size,\n contentType: getMimeType(key),\n lastModified: stat.mtime,\n })\n }\n }\n }\n\n await readDir(basePath)\n\n return ok({\n files,\n commonPrefixes: Array.from(commonPrefixes),\n isTruncated: files.length >= maxKeys,\n })\n }\n catch (error) {\n return err(toStorageError(error))\n }\n },\n\n async delete(prefix): Promise<HaiResult<void>> {\n try {\n const dirPath = fullPath(prefix)\n logOperation('write', 'dir.delete', { prefix })\n\n // 递归删除目录\n await fsp.rm(dirPath, { recursive: true, force: true })\n\n return ok(undefined)\n }\n catch (error) {\n // 如果目录不存在,视为删除成功\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return ok(undefined)\n }\n return err(toStorageError(error, prefix))\n }\n },\n }\n\n // -------------------------------------------------------------------------\n // 签名 URL 操作实现\n // -------------------------------------------------------------------------\n\n const presign: PresignOperations = {\n async getUrl(key, options?): Promise<HaiResult<string>> {\n logOperation('read', 'presign.getUrl', { key, options })\n // 本地存储不支持真正的签名 URL\n // 返回一个带签名参数的虚拟 URL,应用层需要自行处理\n const expiresIn = options?.expiresIn || 3600\n const expires = Math.floor(Date.now() / 1000) + expiresIn\n const signature = crypto\n .createHash('sha256')\n .update(`${key}:${expires}`)\n .digest('hex')\n .slice(0, 16)\n\n return ok(`local://${key}?expires=${expires}&signature=${signature}`)\n },\n\n async putUrl(key, options?): Promise<HaiResult<string>> {\n // 同上,返回虚拟 URL\n logOperation('write', 'presign.putUrl', { key, options })\n const expiresIn = options?.expiresIn || 3600\n const expires = Math.floor(Date.now() / 1000) + expiresIn\n const signature = crypto\n .createHash('sha256')\n .update(`put:${key}:${expires}`)\n .digest('hex')\n .slice(0, 16)\n\n return ok(`local://${key}?action=put&expires=${expires}&signature=${signature}`)\n },\n\n publicUrl(key): string | null {\n logOperation('read', 'presign.publicUrl', { key })\n // 本地存储不支持公开 URL\n return null\n },\n }\n\n // -------------------------------------------------------------------------\n // Provider 接口实现\n // -------------------------------------------------------------------------\n\n return {\n name: 'local',\n\n file,\n dir,\n presign,\n\n async connect(cfg: StorageConfig): Promise<HaiResult<void>> {\n if (cfg.type !== 'local') {\n return err(\n HaiStorageError.CONFIG_ERROR,\n storageM('storage_localConfigTypeError'),\n )\n }\n\n try {\n config = cfg\n logger.info('Connecting local provider', { root: cfg.root })\n\n // 确保根目录存在\n await fsp.mkdir(cfg.root, { recursive: true, mode: cfg.directoryMode })\n\n connected = true\n logger.info('Local provider connected', { root: cfg.root })\n return ok(undefined)\n }\n catch (error) {\n logger.error('Local provider connect failed', { error })\n config = null\n return err(toStorageError(error))\n }\n },\n\n async close(): Promise<void> {\n logger.info('Local provider disconnected')\n config = null\n connected = false\n },\n\n isConnected(): boolean {\n return connected\n },\n }\n}\n","/**\n * @h-ai/storage — S3 Provider 实现\n *\n * 基于 AWS SDK v3 实现的 S3 协议存储 Provider。\n * 支持 AWS S3、MinIO、RustFS、阿里云 OSS 等兼容 S3 协议的存储服务。\n * @module storage-provider-s3\n */\n\nimport type { HaiError, HaiResult } from '@h-ai/core'\nimport type { S3Config, StorageConfig } from '../storage-config.js'\nimport type { DirOperations, FileMetadata, FileOperations, ListResult, PresignOperations, StorageProvider } from '../storage-types.js'\nimport { Buffer } from 'node:buffer'\nimport {\n CopyObjectCommand,\n DeleteObjectCommand,\n DeleteObjectsCommand,\n GetObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n S3Client,\n} from '@aws-sdk/client-s3'\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner'\n\nimport { core, err, ok } from '@h-ai/core'\n\nimport { storageM } from '../storage-i18n.js'\nimport {\n\n HaiStorageError,\n\n} from '../storage-types.js'\nimport { logStorageOperation } from './storage-operation-log.js'\n\nconst logger = core.logger.child({ module: 'storage', scope: 'provider-s3' })\nconst TRAILING_SLASHES_REGEX = /\\/+$/\nconst LEADING_SLASHES_REGEX = /^\\/+/\n\n/**\n * 脱敏 S3 endpoint,避免日志暴露认证信息\n */\nfunction sanitizeEndpointUrl(url: string): string {\n try {\n const parsed = new URL(url)\n if (parsed.username)\n parsed.username = '***'\n if (parsed.password)\n parsed.password = '***'\n return parsed.toString()\n }\n catch {\n return '(invalid url)'\n }\n}\n\n// ─── 辅助函数 ───\n\n/**\n * 将 S3 SDK 错误转换为 StorageError\n *\n * 映射规则:\n * - NoSuchKey / NotFound / 404 → NOT_FOUND\n * - AccessDenied → PERMISSION_DENIED\n * - NoSuchBucket → CONFIG_ERROR\n * - NetworkError / ECONNREFUSED → NETWORK_ERROR\n * - 其他 → OPERATION_FAILED\n *\n * @param error - AWS SDK 抛出的原始错误\n * @param key - 相关文件键(可选,用于错误消息)\n * @returns 统一的 StorageError\n */\nfunction toStorageError(error: unknown, key?: string): HaiError {\n const e = error as { name?: string, message?: string, Code?: string }\n\n // S3 特定错误处理\n if (e.name === 'NoSuchKey' || e.Code === 'NoSuchKey'\n || e.name === 'NotFound' || e.Code === 'NotFound'\n || e.name === '404' || (e as { $metadata?: { httpStatusCode?: number } }).$metadata?.httpStatusCode === 404) {\n return {\n ...HaiStorageError.NOT_FOUND,\n message: storageM('storage_fileNotFound', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.name === 'AccessDenied' || e.Code === 'AccessDenied') {\n return {\n ...HaiStorageError.PERMISSION_DENIED,\n message: storageM('storage_permissionDenied', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.name === 'NoSuchBucket' || e.Code === 'NoSuchBucket') {\n return {\n ...HaiStorageError.CONFIG_ERROR,\n message: storageM('storage_bucketNotExist'),\n cause: error,\n }\n }\n\n // 网络相关错误\n if (e.name === 'NetworkError' || e.name?.includes('ECONNREFUSED')) {\n return {\n ...HaiStorageError.NETWORK_ERROR,\n message: storageM('storage_networkError'),\n cause: error,\n }\n }\n\n return {\n ...HaiStorageError.OPERATION_FAILED,\n message: storageM('storage_operationFailed', { params: { error: e.message ?? '' } }),\n cause: error,\n }\n}\n\n/**\n * 为 key 添加配置中的路径前缀\n *\n * @param key - 用户提供的文件键\n * @param prefix - 配置中的前缀(可能为空)\n * @returns 拼接后的完整 S3 key\n */\nfunction withPrefix(key: string, prefix: string): string {\n if (!prefix)\n return key\n return `${prefix.replace(TRAILING_SLASHES_REGEX, '')}/${key.replace(LEADING_SLASHES_REGEX, '')}`\n}\n\n/**\n * 从 S3 key 中移除配置前缀,还原为用户视角的文件键\n *\n * @param key - 包含前缀的完整 S3 key\n * @param prefix - 配置中的前缀\n * @returns 去除前缀后的文件键\n */\nfunction withoutPrefix(key: string, prefix: string): string {\n if (!prefix)\n return key\n const normalizedPrefix = `${prefix.replace(TRAILING_SLASHES_REGEX, '')}/`\n if (key.startsWith(normalizedPrefix)) {\n return key.slice(normalizedPrefix.length)\n }\n return key\n}\n\n// ─── S3 Provider 实现 ───\n\n/**\n * 创建 S3 存储 Provider\n *\n * 基于 AWS SDK v3 实现的 StorageProvider。\n * connect 时会创建 S3Client 并通过 ListObjectsV2 验证连接。\n * 支持配置 prefix 重定向所有 key,对用户透明。\n *\n * 实现说明:\n * - copy 时若指定了 contentType 或 metadata,使用 MetadataDirective='REPLACE'\n * - dir.delete 通过先 list 再批量删除实现\n * - presign 使用 @aws-sdk/s3-request-presigner 签名\n *\n * @returns S3 Provider 实例\n */\nexport function createS3Provider(): StorageProvider {\n let client: S3Client | null = null\n let config: S3Config | null = null\n\n // -------------------------------------------------------------------------\n // 内部辅助\n // -------------------------------------------------------------------------\n\n /** 获取当前 S3Client,未初始化时抛异常 */\n function getClient(): S3Client {\n if (!client) {\n throw new Error(storageM('storage_s3ClientNotInitialized'))\n }\n return client\n }\n\n /** 获取当前 S3 配置,未初始化时抛异常 */\n function getConfig(): S3Config {\n if (!config) {\n throw new Error(storageM('storage_s3ConfigNotInitialized'))\n }\n return config\n }\n\n /** 将用户 key 拼接配置前缀,得到完整 S3 对象键 */\n function fullKey(key: string): string {\n return withPrefix(key, getConfig().prefix)\n }\n\n function logOperation(category: 'read' | 'write', operation: string, payload: unknown): void {\n logStorageOperation(logger, config?.operationLog, category, operation, payload)\n }\n\n // -------------------------------------------------------------------------\n // 文件操作实现\n // -------------------------------------------------------------------------\n\n const file: FileOperations = {\n async put(key, data, options = {}): Promise<HaiResult<FileMetadata>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n\n const body = typeof data === 'string' ? Buffer.from(data) : data\n logOperation('write', 'file.put', { key, fullPath, byteLength: body.length, options })\n\n await s3Client.send(new PutObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n Body: body,\n ContentType: options.contentType,\n Metadata: options.metadata,\n CacheControl: options.cacheControl,\n ContentDisposition: options.contentDisposition,\n }))\n\n // 获取上传后的元数据\n const headResult = await file.head(key)\n if (headResult.success) {\n return headResult\n }\n\n // 如果获取元数据失败,返回基本信息\n return ok({\n key,\n size: body.length,\n contentType: options.contentType || 'application/octet-stream',\n lastModified: new Date(),\n })\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async get(key, options = {}): Promise<HaiResult<Buffer>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n logOperation('read', 'file.get', { key, fullPath, options })\n\n let Range: string | undefined\n if (options.rangeStart !== undefined || options.rangeEnd !== undefined) {\n const start = options.rangeStart ?? 0\n const end = options.rangeEnd !== undefined ? options.rangeEnd : ''\n Range = `bytes=${start}-${end}`\n }\n\n const response = await s3Client.send(new GetObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n Range,\n }))\n\n if (!response.Body) {\n return err(\n HaiStorageError.OPERATION_FAILED,\n storageM('storage_responseBodyEmpty'),\n )\n }\n\n // 将流转换为 Buffer\n const chunks: Uint8Array[] = []\n const stream = response.Body as AsyncIterable<Uint8Array>\n for await (const chunk of stream) {\n chunks.push(chunk)\n }\n\n return ok(Buffer.concat(chunks))\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async head(key): Promise<HaiResult<FileMetadata>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n logOperation('read', 'file.head', { key, fullPath })\n\n const response = await s3Client.send(new HeadObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n }))\n\n return ok({\n key,\n size: response.ContentLength || 0,\n contentType: response.ContentType || 'application/octet-stream',\n lastModified: response.LastModified || new Date(),\n etag: response.ETag,\n metadata: response.Metadata,\n })\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async exists(key): Promise<HaiResult<boolean>> {\n logOperation('read', 'file.exists', { key })\n const result = await file.head(key)\n if (result.success) {\n return ok(true)\n }\n if (result.error.code === HaiStorageError.NOT_FOUND.code) {\n return ok(false)\n }\n return err(result.error)\n },\n\n async delete(key): Promise<HaiResult<void>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n logOperation('write', 'file.delete', { key, fullPath })\n\n await s3Client.send(new DeleteObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n }))\n\n return ok(undefined)\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async deleteMany(keys): Promise<HaiResult<void>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n\n if (keys.length === 0) {\n return ok(undefined)\n }\n\n logOperation('write', 'file.deleteMany', { count: keys.length, keys })\n\n await s3Client.send(new DeleteObjectsCommand({\n Bucket: s3Config.bucket,\n Delete: {\n Objects: keys.map(key => ({ Key: fullKey(key) })),\n },\n }))\n\n return ok(undefined)\n }\n catch (error) {\n return err(toStorageError(error))\n }\n },\n\n async copy(sourceKey, destKey, options = {}): Promise<HaiResult<FileMetadata>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const sourceFullPath = fullKey(sourceKey)\n const destFullPath = fullKey(destKey)\n logOperation('write', 'file.copy', { sourceKey, destKey, sourceFullPath, destFullPath, options })\n\n await s3Client.send(new CopyObjectCommand({\n Bucket: s3Config.bucket,\n CopySource: `${s3Config.bucket}/${sourceFullPath}`,\n Key: destFullPath,\n ContentType: options.contentType,\n Metadata: options.metadata,\n MetadataDirective: (options.metadata || options.contentType) ? 'REPLACE' : 'COPY',\n }))\n\n return file.head(destKey)\n }\n catch (error) {\n return err(toStorageError(error, sourceKey))\n }\n },\n }\n\n // -------------------------------------------------------------------------\n // 目录操作实现\n // -------------------------------------------------------------------------\n\n const dir: DirOperations = {\n async list(options = {}): Promise<HaiResult<ListResult>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n logOperation('read', 'dir.list', { options })\n\n // 构建完整前缀\n let prefix = s3Config.prefix || ''\n if (options.prefix) {\n prefix = withPrefix(options.prefix, s3Config.prefix)\n }\n\n const response = await s3Client.send(new ListObjectsV2Command({\n Bucket: s3Config.bucket,\n Prefix: prefix || undefined,\n Delimiter: options.delimiter,\n MaxKeys: options.maxKeys,\n ContinuationToken: options.continuationToken,\n }))\n\n const files: FileMetadata[] = (response.Contents || []).map(item => ({\n key: withoutPrefix(item.Key || '', s3Config.prefix),\n size: item.Size || 0,\n contentType: 'application/octet-stream', // ListObjects 不返回 ContentType\n lastModified: item.LastModified || new Date(),\n etag: item.ETag,\n }))\n\n const commonPrefixes = (response.CommonPrefixes || [])\n .map(p => withoutPrefix(p.Prefix || '', s3Config.prefix))\n .filter(p => p.length > 0)\n\n return ok({\n files,\n commonPrefixes,\n nextContinuationToken: response.NextContinuationToken,\n isTruncated: response.IsTruncated || false,\n })\n }\n catch (error) {\n return err(toStorageError(error))\n }\n },\n\n async delete(prefix): Promise<HaiResult<void>> {\n try {\n logOperation('write', 'dir.delete', { prefix })\n // 分页列出并逐批删除,每批最多 1000 个对象\n let continuationToken: string | undefined\n\n do {\n const listResult = await dir.list({\n prefix,\n continuationToken,\n maxKeys: 1000,\n })\n\n if (!listResult.success) {\n return err(listResult.error)\n }\n\n const keys = listResult.data.files.map(f => f.key)\n if (keys.length > 0) {\n const deleteResult = await file.deleteMany(keys)\n if (!deleteResult.success) {\n return deleteResult\n }\n }\n\n continuationToken = listResult.data.nextContinuationToken\n } while (continuationToken)\n\n return ok(undefined)\n }\n catch (error) {\n return err(toStorageError(error))\n }\n },\n }\n\n // -------------------------------------------------------------------------\n // 签名 URL 操作实现\n // -------------------------------------------------------------------------\n\n const presign: PresignOperations = {\n async getUrl(key, options?): Promise<HaiResult<string>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n logOperation('read', 'presign.getUrl', { key, fullPath, options })\n\n const command = new GetObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n ResponseContentType: options?.responseContentType,\n ResponseContentDisposition: options?.responseContentDisposition,\n })\n\n const url = await getSignedUrl(s3Client, command, {\n expiresIn: options?.expiresIn || 3600,\n })\n\n return ok(url)\n }\n catch (error) {\n return err(\n HaiStorageError.PRESIGN_FAILED,\n storageM('storage_presignUrlFailed'),\n error,\n )\n }\n },\n\n async putUrl(key, options?): Promise<HaiResult<string>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n logOperation('write', 'presign.putUrl', { key, fullPath, options })\n\n const command = new PutObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n ContentType: options?.contentType || 'application/octet-stream',\n })\n\n const url = await getSignedUrl(s3Client, command, {\n expiresIn: options?.expiresIn || 3600,\n })\n\n return ok(url)\n }\n catch (error) {\n return err(\n HaiStorageError.PRESIGN_FAILED,\n storageM('storage_presignUploadUrlFailed'),\n error,\n )\n }\n },\n\n publicUrl(key): string | null {\n const s3Config = getConfig()\n if (!s3Config.publicUrl) {\n return null\n }\n const fullPath = fullKey(key)\n logOperation('read', 'presign.publicUrl', { key, fullPath })\n return `${s3Config.publicUrl.replace(TRAILING_SLASHES_REGEX, '')}/${fullPath}`\n },\n }\n\n // -------------------------------------------------------------------------\n // Provider 接口实现\n // -------------------------------------------------------------------------\n\n return {\n name: 's3',\n\n file,\n dir,\n presign,\n\n async connect(cfg: StorageConfig): Promise<HaiResult<void>> {\n if (cfg.type !== 's3') {\n return err(\n HaiStorageError.CONFIG_ERROR,\n storageM('storage_s3ConfigTypeError'),\n )\n }\n\n try {\n config = cfg\n logger.info('Connecting S3 provider', {\n bucket: cfg.bucket,\n region: cfg.region,\n endpoint: cfg.endpoint ? sanitizeEndpointUrl(cfg.endpoint) : undefined,\n })\n\n client = new S3Client({\n region: cfg.region,\n endpoint: cfg.endpoint,\n forcePathStyle: cfg.forcePathStyle,\n credentials: {\n accessKeyId: cfg.accessKeyId,\n secretAccessKey: cfg.secretAccessKey,\n },\n })\n\n // 测试连接:尝试列出一个对象\n await client.send(new ListObjectsV2Command({\n Bucket: cfg.bucket,\n MaxKeys: 1,\n }))\n\n logger.info('S3 provider connected', { bucket: cfg.bucket })\n return ok(undefined)\n }\n catch (error) {\n logger.error('S3 provider connect failed', { error })\n config = null\n client = null\n return err(toStorageError(error))\n }\n },\n\n async close(): Promise<void> {\n if (client) {\n client.destroy()\n client = null\n config = null\n logger.info('S3 provider disconnected')\n }\n },\n\n isConnected(): boolean {\n return client !== null && config !== null\n },\n }\n}\n","/**\n * @h-ai/storage — 存储服务主入口\n *\n * 提供统一的 `storage` 对象,管理运行时状态与生命周期。\n * @module storage-main\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { StorageConfig, StorageConfigInput } from './storage-config.js'\nimport type { DirOperations, FileOperations, PresignOperations, StorageFunctions, StorageProvider } from './storage-types.js'\n\nimport { core, err, ok } from '@h-ai/core'\n\nimport { createLocalProvider } from './providers/storage-provider-local.js'\nimport { createS3Provider } from './providers/storage-provider-s3.js'\nimport { StorageConfigSchema } from './storage-config.js'\nimport { storageM } from './storage-i18n.js'\nimport {\n\n HaiStorageError,\n\n} from './storage-types.js'\n\nconst logger = core.logger.child({ module: 'storage', scope: 'main' })\n\n// ─── 内部状态 ───\n\n/** 当前使用的存储 Provider 实例(init 后非空,close 后置空) */\nlet currentProvider: StorageProvider | null = null\n\n/** 当前解析后的存储配置(init 后非空,close 后置空) */\nlet currentConfig: StorageConfig | null = null\n\n/** 并发初始化防护标志;防止多次 init() 同时执行导致资源泄漏 */\nlet initInProgress = false\n\n// ─── Provider 工厂 ───\n\n/**\n * 根据配置类型创建对应的存储 Provider\n *\n * @param config - 经 Schema 校验后的存储配置\n * @returns 对应类型的 Provider 实例(尚未连接)\n * @throws 理论上仅在出现未覆盖分支时抛出异常(正常情况下由 Schema 保证不会发生)\n */\nfunction createProvider(config: StorageConfig): StorageProvider {\n switch (config.type) {\n case 's3': return createS3Provider()\n case 'local': return createLocalProvider()\n default:\n throw new Error(storageM('storage_unsupportedType', { params: { type: (config as StorageConfig).type } }))\n }\n}\n\n// ─── 未初始化占位 ───\n\n/**\n * 创建未初始化状态的错误工具包\n *\n * 当 storage 未调用 init() 就直接使用 file/dir/presign 操作时,\n * 所有方法调用都会返回 NOT_INITIALIZED 错误。\n */\nconst notInitialized = core.module.createNotInitializedKit(\n HaiStorageError.NOT_INITIALIZED,\n () => storageM('storage_notInitialized'),\n)\n\n/** 未初始化时的文件操作占位代理 */\nconst notInitializedFile = notInitialized.proxy<FileOperations>()\n\n/** 未初始化时的目录操作占位代理 */\nconst notInitializedDir = notInitialized.proxy<DirOperations>()\n\n/**\n * 未初始化时的签名 URL 操作占位代理\n *\n * publicUrl 始终返回 null;其余方法返回 NOT_INITIALIZED 错误。\n */\nconst notInitializedPresignBase = notInitialized.proxy<PresignOperations>()\nconst notInitializedPresign = new Proxy(\n notInitializedPresignBase,\n {\n get: (target, prop, receiver) => prop === 'publicUrl' ? () => null : Reflect.get(target, prop, receiver),\n },\n)\n\n// ─── 存储服务对象 ───\n\n/**\n * 存储服务单例\n *\n * 使用前必须先调用 `storage.init()` 初始化,传入 S3 或本地存储配置。\n * 初始化后通过 `storage.file`、`storage.dir`、`storage.presign` 访问各操作接口。\n *\n * @example\n * ```ts\n * import { storage } from '@h-ai/storage'\n *\n * // 初始化\n * await storage.init({ type: 'local', root: '/data/uploads' })\n *\n * // 上传文件\n * await storage.file.put('hello.txt', 'Hello World')\n *\n * // 关闭连接\n * await storage.close()\n * ```\n */\nexport const storage: StorageFunctions = {\n /**\n * 初始化存储连接。\n *\n * 如果当前已有活跃连接,会先自动 close 再重新初始化。\n * 配置会通过 Zod Schema 校验;校验失败或连接异常会返回 `CONNECTION_FAILED`。\n *\n * @param config 存储配置(S3 或本地),支持省略带默认值的字段。\n * @returns 成功时返回 ok(undefined);失败时返回包含错误码和消息的 err。\n */\n async init(config: StorageConfigInput): Promise<HaiResult<void>> {\n // 并发初始化防护:避免多次 init 同时执行导致 Provider 泄漏\n if (initInProgress) {\n logger.warn('Storage init already in progress, skipping concurrent call')\n return err(\n HaiStorageError.OPERATION_FAILED,\n storageM('storage_operationFailed', { params: { error: 'Concurrent initialization detected' } }),\n )\n }\n initInProgress = true\n\n try {\n if (currentProvider) {\n logger.warn('Storage module is already initialized, reinitializing')\n const closeResult = await storage.close()\n if (!closeResult.success) {\n return closeResult\n }\n }\n\n logger.info('Initializing storage module')\n\n const parseResult = StorageConfigSchema.safeParse(config)\n if (!parseResult.success) {\n logger.error('Storage config validation failed', { error: parseResult.error.message })\n return err(\n HaiStorageError.CONFIG_ERROR,\n storageM('storage_configError', { params: { error: parseResult.error.message } }),\n parseResult.error,\n )\n }\n const parsed = parseResult.data\n\n const provider = createProvider(parsed)\n const connectResult = await provider.connect(parsed)\n if (!connectResult.success) {\n logger.error('Storage module initialization failed', {\n code: connectResult.error.code,\n message: connectResult.error.message,\n })\n return connectResult\n }\n currentProvider = provider\n currentConfig = parsed\n logger.info('Storage module initialized')\n return ok(undefined)\n }\n catch (error) {\n logger.error('Storage module initialization failed', { error })\n return err(\n HaiStorageError.CONNECTION_FAILED,\n storageM('storage_operationFailed', {\n params: { error: error instanceof Error ? error.message : String(error) },\n }),\n error,\n )\n }\n finally {\n initInProgress = false\n }\n },\n\n /** 文件操作接口。未初始化时所有方法返回 NOT_INITIALIZED 错误 */\n get file(): FileOperations { return currentProvider?.file ?? notInitializedFile },\n\n /** 目录操作接口。未初始化时所有方法返回 NOT_INITIALIZED 错误 */\n get dir(): DirOperations { return currentProvider?.dir ?? notInitializedDir },\n\n /** 签名 URL 操作接口。未初始化时 publicUrl 返回 null,其余返回错误 */\n get presign(): PresignOperations { return currentProvider?.presign ?? notInitializedPresign },\n\n /** 当前脱敏后的存储配置快照;未初始化或已关闭时为 null */\n get config() {\n return currentConfig ? core.sanitize.sanitizeSensitiveFields(currentConfig) : null\n },\n\n /** 是否已完成初始化 */\n get isInitialized() { return currentProvider !== null },\n\n /**\n * 关闭存储连接并释放资源\n *\n * 关闭后 file/dir/presign 操作将返回 NOT_INITIALIZED 错误。\n * 重复调用不会报错。\n */\n async close(): Promise<HaiResult<void>> {\n if (!currentProvider) {\n currentConfig = null\n logger.info('Storage module already closed, skipping')\n return ok(undefined)\n }\n\n logger.info('Closing storage module')\n\n try {\n await currentProvider.close()\n logger.info('Storage module closed')\n return ok(undefined)\n }\n catch (error) {\n logger.error('Storage module close failed', { error })\n return err(\n HaiStorageError.CONNECTION_FAILED,\n storageM('storage_operationFailed', {\n params: { error: error instanceof Error ? error.message : String(error) },\n }),\n error,\n )\n }\n finally {\n currentProvider = null\n currentConfig = null\n }\n },\n}\n"]}
1
+ {"version":3,"sources":["../src/storage-stream.ts","../src/providers/storage-operation-log.ts","../src/providers/storage-provider-local.ts","../src/providers/storage-provider-s3.ts","../src/storage-checksum.ts","../src/storage-main.ts"],"names":["logger","getMimeType","stat","dir","Buffer","core","toStorageError","Readable","ok","err","createHash"],"mappings":"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","file":"node.js","sourcesContent":["/**\n * @h-ai/storage — 字节流规范化\n *\n * 供存储 Provider 与 checksum 共用,保持消费有界且支持取消。\n */\nimport type { StorageData } from './storage-types.js'\nimport { Buffer } from 'node:buffer'\nimport { storageM } from './storage-i18n.js'\n\n/** 规范化字节输入;取消时终止等待并关闭迭代器,拒绝对象模式流中的非字节块。 */\nexport async function* toByteStream(data: StorageData, signal?: AbortSignal): AsyncIterable<Uint8Array> {\n signal?.throwIfAborted()\n if (typeof data === 'string' || data instanceof Uint8Array) {\n yield typeof data === 'string' ? Buffer.from(data) : data\n return\n }\n const iterator = data[Symbol.asyncIterator]()\n let completed = false\n try {\n while (true) {\n signal?.throwIfAborted()\n let result: IteratorResult<Uint8Array>\n let onAbort: (() => void) | undefined\n try {\n // 每块独立等待并释放监听,避免在长流上累积共享 Promise 的回调。\n const cancelled = signal\n ? new Promise<never>((_, reject) => {\n onAbort = () => reject(signal.reason)\n signal.addEventListener('abort', onAbort, { once: true })\n })\n : undefined\n const next = iterator.next()\n result = await (cancelled ? Promise.race([next, cancelled]) : next)\n }\n finally {\n if (onAbort)\n signal?.removeEventListener('abort', onAbort)\n }\n if (result.done) {\n completed = true\n break\n }\n if (!(result.value instanceof Uint8Array))\n throw new TypeError(storageM('storage_streamInvalidChunk'))\n yield result.value\n }\n signal?.throwIfAborted()\n }\n finally {\n if (!completed && iterator.return) {\n // 不可取消的源可能把 return 排在挂起的 next 后面;取消结果不能继续等待源释放。\n if (signal?.aborted)\n void Promise.resolve(iterator.return()).catch(() => {})\n else\n await iterator.return()\n }\n }\n}\n","import type { StorageOperationLogConfig } from '../storage-config.js'\n\ninterface StorageOperationLogger {\n info: (message: string, context?: Record<string, unknown>) => void\n debug: (message: string, context?: Record<string, unknown>) => void\n trace: (message: string, context?: Record<string, unknown>) => void\n}\n\ntype StorageOperationCategory = 'read' | 'write'\n\nfunction normalizePayloadValue(value: unknown): unknown {\n if (value instanceof Uint8Array) {\n return { byteLength: value.byteLength }\n }\n return value\n}\n\nfunction stringifyOperationPayload(payload: unknown, maxLength: number): string {\n if (maxLength <= 0) {\n return ''\n }\n\n try {\n const text = JSON.stringify(payload, (_key, value: unknown) => {\n if (typeof value === 'bigint') {\n return value.toString()\n }\n if (typeof value === 'function') {\n return '[Function]'\n }\n return normalizePayloadValue(value)\n })\n return text.length > maxLength ? `${text.slice(0, maxLength)}...` : text\n }\n catch {\n const text = String(payload)\n return text.length > maxLength ? `${text.slice(0, maxLength)}...` : text\n }\n}\n\nexport function logStorageOperation(\n logger: StorageOperationLogger,\n config: StorageOperationLogConfig | undefined,\n category: StorageOperationCategory,\n operation: string,\n payload: unknown,\n): void {\n if (!config?.[category]) {\n return\n }\n\n logger[config.level]('Storage operation executed', {\n category,\n operation,\n payload: stringifyOperationPayload(payload, config.maxLength),\n })\n}\n","/**\n * @h-ai/storage — Local Provider 实现\n *\n * 基于本地文件系统的存储 Provider。\n * @module storage-provider-local\n */\n\nimport type { HaiError, HaiResult } from '@h-ai/core'\nimport type { LocalConfig, StorageConfig } from '../storage-config.js'\nimport type { DirOperations, FileMetadata, FileOperations, ListResult, PresignOperations, StorageProvider } from '../storage-types.js'\nimport { Buffer } from 'node:buffer'\nimport * as crypto from 'node:crypto'\nimport * as fs from 'node:fs'\nimport * as fsp from 'node:fs/promises'\nimport * as path from 'node:path'\nimport { Readable } from 'node:stream'\n\nimport { pipeline } from 'node:stream/promises'\nimport { core, err, ok } from '@h-ai/core'\n\nimport { storageM } from '../storage-i18n.js'\nimport { MIME_TYPE_DEFAULT, MIME_TYPES } from '../storage-mime.js'\nimport { toByteStream } from '../storage-stream.js'\nimport {\n\n HaiStorageError,\n\n} from '../storage-types.js'\nimport { logStorageOperation } from './storage-operation-log.js'\n\nconst logger = core.logger.child({ module: 'storage', scope: 'provider-local' })\nconst PATH_SEGMENT_SPLIT_REGEX = /[\\\\/]+/\n\n// ─── 辅助函数 ───\n\n/**\n * 将 Node.js 文件系统错误转换为 StorageError\n *\n * 映射规则:\n * - ENOENT → NOT_FOUND\n * - EACCES / EPERM → PERMISSION_DENIED\n * - ENOSPC → QUOTA_EXCEEDED\n * - EISDIR → INVALID_PATH\n * - 其他 → IO_ERROR\n *\n * @param error - 原始错误对象\n * @param key - 相关文件键(可选,用于错误消息)\n * @returns 统一的 StorageError\n */\nfunction toStorageError(error: unknown, key?: string): HaiError {\n const e = error as NodeJS.ErrnoException\n\n if (e.code === 'EEXIST') {\n return { ...HaiStorageError.ALREADY_EXISTS, message: storageM('storage_fileAlreadyExists', { params: { key: key ?? '' } }), cause: error }\n }\n\n if (e.code === 'ENOENT') {\n return {\n ...HaiStorageError.NOT_FOUND,\n message: storageM('storage_fileNotFound', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.code === 'EACCES' || e.code === 'EPERM') {\n return {\n ...HaiStorageError.PERMISSION_DENIED,\n message: storageM('storage_permissionDenied', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.code === 'ENOSPC') {\n return {\n ...HaiStorageError.QUOTA_EXCEEDED,\n message: storageM('storage_diskSpaceInsufficient'),\n cause: error,\n }\n }\n\n if (e.code === 'EISDIR') {\n return {\n ...HaiStorageError.INVALID_PATH,\n message: storageM('storage_pathIsDir', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.code === 'EINVAL') {\n return {\n ...HaiStorageError.INVALID_PATH,\n message: e.message || storageM('storage_pathTraversal'),\n cause: error,\n }\n }\n\n return {\n code: HaiStorageError.IO_ERROR.code,\n message: storageM('storage_ioError', { params: { error: e.message ?? '' } }),\n cause: error,\n }\n}\n\n/**\n * 根据文件扩展名获取 MIME 类型\n *\n * 使用共享 MIME_TYPES 映射表,未匹配时返回 'application/octet-stream'。\n *\n * @param filePath - 文件路径或文件名\n * @returns MIME 类型字符串\n */\nfunction getMimeType(filePath: string): string {\n const ext = path.extname(filePath).toLowerCase().replace('.', '')\n return MIME_TYPES[ext] || MIME_TYPE_DEFAULT\n}\n\n/**\n * 根据文件 stat 信息计算 ETag\n *\n * 使用 \"size-mtime\" 格式,与 head 操作保持一致,无需读取文件内容。\n *\n * @param stat - 文件的 fs.Stats 对象\n * @returns 带双引号的 ETag 字符串,如 '\"a-18f7ec3b4c0\"'\n */\nfunction calculateEtag(stat: fs.Stats): string {\n return `\"${stat.size.toString(16)}-${stat.mtimeMs.toString(16)}\"`\n}\n\n/**\n * 检查 key 是否指向内部元数据文件(.meta.json)\n *\n * 拦截外部对 .meta.json 文件的直接访问,防止元数据泄漏。\n */\nfunction isMetaFile(key: string): boolean {\n return key.endsWith('.meta.json') || key.endsWith('.upload.tmp')\n}\n\n/**\n * 创建非法路径错误(用于统一映射为 INVALID_PATH)\n */\nfunction createInvalidPathError(): NodeJS.ErrnoException {\n const error = new Error(storageM('storage_pathTraversal')) as NodeJS.ErrnoException\n error.code = 'EINVAL'\n return error\n}\n\n/**\n * 判断路径片段中是否包含路径穿越标记(..)\n */\nfunction hasPathTraversalSegment(key: string): boolean {\n const segments = key.split(PATH_SEGMENT_SPLIT_REGEX).filter(Boolean)\n return segments.includes('..')\n}\n\n/**\n * 安全的路径解析,防止路径穿越攻击\n *\n * 处理流程:\n * 1. 拒绝绝对路径、空字节、`..` 路径穿越片段\n * 2. resolve(root, key) 得到完整路径\n * 3. 校验完整路径必须位于 root 内部(或等于 root)\n *\n * @param root - 存储根目录绝对路径\n * @param key - 用户提供的文件键\n * @returns 安全的完整文件路径\n * @throws 当解析后路径超出 root 范围时抛出异常\n */\nfunction safePath(root: string, key: string): string {\n // 拒绝绝对路径输入,防止 Windows 盘符绕过(如 D:\\etc\\passwd)\n if (path.isAbsolute(key)) {\n throw createInvalidPathError()\n }\n\n // 拒绝空字节与路径穿越片段\n if (key.includes('\\0') || hasPathTraversalSegment(key)) {\n throw createInvalidPathError()\n }\n\n const normalized = path.normalize(key)\n const fullPath = path.resolve(root, normalized)\n\n // 确保路径在 root 目录下\n const realRoot = path.resolve(root)\n const inRoot = fullPath === realRoot || fullPath.startsWith(`${realRoot}${path.sep}`)\n\n if (!inRoot) {\n throw createInvalidPathError()\n }\n\n return fullPath\n}\n\n// ─── Local Provider 实现 ───\n\n/**\n * 创建本地存储 Provider\n *\n * 基于本地文件系统实现的 StorageProvider。\n * connect 时会自动创建根目录;文件操作会自动创建中间目录。\n *\n * 已知限制:\n * - list 不支持 continuationToken 分页(仅支持 maxKeys 截断)\n * - presign 返回虚拟 `local://` URL,应用层需自行处理\n * - publicUrl 始终返回 null\n *\n * @returns Local Provider 实例\n */\nexport function createLocalProvider(): StorageProvider {\n let config: LocalConfig | null = null\n let connected = false\n\n // -------------------------------------------------------------------------\n // 辅助方法\n // -------------------------------------------------------------------------\n\n /** 获取当前 Local 配置,未初始化时抛异常 */\n function getConfig(): LocalConfig {\n if (!config) {\n throw new Error(storageM('storage_localNotInitialized'))\n }\n return config\n }\n\n /** 将用户 key 转为安全的绝对路径(经 safePath 防穿越处理) */\n function fullPath(key: string): string {\n return safePath(getConfig().root, key)\n }\n\n function logOperation(category: 'read' | 'write', operation: string, payload: unknown): void {\n logStorageOperation(logger, config?.operationLog, category, operation, payload)\n }\n\n // -------------------------------------------------------------------------\n // 文件操作实现\n // -------------------------------------------------------------------------\n\n const file: FileOperations = {\n async put(key, data, options = {}): Promise<HaiResult<FileMetadata>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n let temporaryPath: string | undefined\n try {\n const filePath = fullPath(key)\n logOperation('write', 'file.put', { key, options })\n\n // 确保目录存在\n const dir = path.dirname(filePath)\n await fsp.mkdir(dir, { recursive: true, mode: getConfig().directoryMode })\n\n // 先流式写入同目录临时文件,失败不破坏已有对象;发布采用原子文件系统操作。\n temporaryPath = `${filePath}.${crypto.randomUUID()}.upload.tmp`\n await pipeline(\n Readable.from(toByteStream(data, options.signal), { objectMode: false }),\n fs.createWriteStream(temporaryPath, { flags: 'wx', mode: getConfig().fileMode }),\n { signal: options.signal },\n )\n options.signal?.throwIfAborted()\n if (options.ifNoneMatch === '*')\n await fsp.link(temporaryPath, filePath)\n else\n await fsp.rename(temporaryPath, filePath)\n\n // 获取元数据\n const stat = await fsp.stat(filePath)\n const metadata: FileMetadata = {\n key,\n size: stat.size,\n contentType: options.contentType || getMimeType(key),\n lastModified: stat.mtime,\n etag: calculateEtag(stat),\n metadata: options.metadata,\n }\n\n // 存储自定义元数据(使用扩展属性或元数据文件)\n if (options.metadata || options.contentType) {\n const metaPath = `${filePath}.meta.json`\n await fsp.writeFile(metaPath, JSON.stringify({\n contentType: options.contentType,\n metadata: options.metadata,\n }))\n }\n\n return ok(metadata)\n }\n catch (error) {\n logger.warn('Put file failed', { key, error })\n return err(toStorageError(error, key))\n }\n finally {\n if (temporaryPath)\n await fsp.unlink(temporaryPath).catch(() => {})\n }\n },\n\n async get(key, options = {}): Promise<HaiResult<Buffer>> {\n const streamed = await file.getStream(key, options)\n if (!streamed.success)\n return streamed\n try {\n const chunks: Uint8Array[] = []\n for await (const chunk of streamed.data)\n chunks.push(chunk)\n return ok(Buffer.concat(chunks))\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async getStream(key, options = {}) {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n logOperation('read', 'file.getStream', { key, options })\n options.signal?.throwIfAborted()\n const stat = await fsp.stat(filePath)\n if (stat.isDirectory())\n return err(HaiStorageError.INVALID_PATH, storageM('storage_pathIsDir', { params: { key } }))\n // 延迟打开,未消费的返回值不会占用文件句柄;迭代错误交由流消费者处理。\n async function* read(): AsyncIterable<Uint8Array> {\n const stream = fs.createReadStream(filePath, { start: options.rangeStart, end: options.rangeEnd, signal: options.signal })\n for await (const chunk of stream)\n yield chunk\n }\n return ok(read())\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async head(key): Promise<HaiResult<FileMetadata>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n logOperation('read', 'file.head', { key })\n const stat = await fsp.stat(filePath)\n\n if (stat.isDirectory()) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_pathIsDir', { params: { key } }),\n )\n }\n\n // 尝试读取元数据文件\n let customMetadata: { contentType?: string, metadata?: Record<string, string> } = {}\n try {\n const metaPath = `${filePath}.meta.json`\n const metaContent = await fsp.readFile(metaPath, 'utf-8')\n customMetadata = JSON.parse(metaContent)\n }\n catch {\n // 元数据文件不存在,忽略\n }\n\n // 基于文件大小和修改时间生成 ETag(无需读取整个文件)\n const etag = `\"${stat.size.toString(16)}-${stat.mtimeMs.toString(16)}\"`\n\n return ok({\n key,\n size: stat.size,\n contentType: customMetadata.contentType || getMimeType(key),\n lastModified: stat.mtime,\n etag,\n metadata: customMetadata.metadata,\n })\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async exists(key): Promise<HaiResult<boolean>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n logOperation('read', 'file.exists', { key })\n await fsp.access(filePath, fs.constants.F_OK)\n return ok(true)\n }\n catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return ok(false)\n }\n return err(toStorageError(error, key))\n }\n },\n\n async delete(key): Promise<HaiResult<void>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n logOperation('write', 'file.delete', { key })\n logger.debug('Deleting file', { key })\n await fsp.unlink(filePath)\n\n // 尝试删除元数据文件\n try {\n await fsp.unlink(`${filePath}.meta.json`)\n }\n catch {\n // 忽略\n }\n\n return ok(undefined)\n }\n catch (error) {\n // 如果文件不存在,视为删除成功\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return ok(undefined)\n }\n logger.warn('Delete file failed', { key, error })\n return err(toStorageError(error, key))\n }\n },\n\n async deleteMany(keys): Promise<HaiResult<void>> {\n logOperation('write', 'file.deleteMany', { count: keys.length, keys })\n // 并行删除,避免 await-in-loop(N+1)\n const results = await Promise.allSettled(keys.map(key => file.delete(key)))\n\n const errors: HaiError[] = []\n for (const result of results) {\n if (result.status === 'fulfilled' && !result.value.success) {\n errors.push(result.value.error)\n }\n }\n\n if (errors.length > 0) {\n return err(\n HaiStorageError.OPERATION_FAILED,\n storageM('storage_deleteManyFailed', { params: { count: errors.length } }),\n errors,\n )\n }\n\n return ok(undefined)\n },\n\n async copy(sourceKey, destKey, options = {}): Promise<HaiResult<FileMetadata>> {\n if (isMetaFile(sourceKey) || isMetaFile(destKey)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key: sourceKey } }),\n sourceKey,\n )\n }\n try {\n const sourcePath = fullPath(sourceKey)\n const destPath = fullPath(destKey)\n logOperation('write', 'file.copy', { sourceKey, destKey, options })\n logger.debug('Copying file', { sourceKey, destKey })\n\n // 确保目标目录存在\n await fsp.mkdir(path.dirname(destPath), { recursive: true, mode: getConfig().directoryMode })\n\n // 复制文件\n await fsp.copyFile(sourcePath, destPath)\n\n // 获取新文件的元数据(无需读取文件内容,使用 stat 计算 ETag)\n const stat = await fsp.stat(destPath)\n\n const metadata: FileMetadata = {\n key: destKey,\n size: stat.size,\n contentType: options.contentType || getMimeType(destKey),\n lastModified: stat.mtime,\n etag: calculateEtag(stat),\n metadata: options.metadata,\n }\n\n // 存储自定义元数据\n if (options.contentType || options.metadata) {\n const metaPath = `${destPath}.meta.json`\n await fsp.writeFile(metaPath, JSON.stringify({\n contentType: options.contentType,\n metadata: options.metadata,\n }))\n }\n\n return ok(metadata)\n }\n catch (error) {\n return err(toStorageError(error, sourceKey))\n }\n },\n }\n\n // -------------------------------------------------------------------------\n // 目录操作实现\n // -------------------------------------------------------------------------\n\n const dir: DirOperations = {\n async list(options = {}): Promise<HaiResult<ListResult>> {\n try {\n const cfg = getConfig()\n const prefix = options.prefix || ''\n const delimiter = options.delimiter || ''\n const maxKeys = options.maxKeys || 1000\n logOperation('read', 'dir.list', { options })\n\n const basePath = safePath(cfg.root, prefix)\n const files: FileMetadata[] = []\n const commonPrefixes = new Set<string>()\n\n // 递归读取目录\n async function readDir(dirPath: string): Promise<void> {\n let entries: fs.Dirent[]\n try {\n entries = await fsp.readdir(dirPath, { withFileTypes: true })\n }\n catch {\n return // 目录不存在,返回空\n }\n\n for (const entry of entries) {\n if (files.length >= maxKeys)\n return\n\n const fullEntryPath = path.join(dirPath, entry.name)\n const key = path.relative(cfg.root, fullEntryPath).replace(/\\\\/g, '/')\n\n // 跳过元数据文件\n if (isMetaFile(entry.name))\n continue\n\n if (entry.isDirectory()) {\n if (delimiter) {\n // 有分隔符时,目录作为公共前缀\n commonPrefixes.add(`${key}/`)\n }\n else {\n // 无分隔符时,递归遍历\n await readDir(fullEntryPath)\n }\n }\n else if (entry.isFile()) {\n const stat = await fsp.stat(fullEntryPath)\n files.push({\n key,\n size: stat.size,\n contentType: getMimeType(key),\n lastModified: stat.mtime,\n })\n }\n }\n }\n\n await readDir(basePath)\n\n return ok({\n files,\n commonPrefixes: Array.from(commonPrefixes),\n isTruncated: files.length >= maxKeys,\n })\n }\n catch (error) {\n return err(toStorageError(error))\n }\n },\n\n async delete(prefix): Promise<HaiResult<void>> {\n try {\n const dirPath = fullPath(prefix)\n logOperation('write', 'dir.delete', { prefix })\n\n // 递归删除目录\n await fsp.rm(dirPath, { recursive: true, force: true })\n\n return ok(undefined)\n }\n catch (error) {\n // 如果目录不存在,视为删除成功\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return ok(undefined)\n }\n return err(toStorageError(error, prefix))\n }\n },\n }\n\n // -------------------------------------------------------------------------\n // 签名 URL 操作实现\n // -------------------------------------------------------------------------\n\n const presign: PresignOperations = {\n async getUrl(key, options?): Promise<HaiResult<string>> {\n logOperation('read', 'presign.getUrl', { key, options })\n // 本地存储不支持真正的签名 URL\n // 返回一个带签名参数的虚拟 URL,应用层需要自行处理\n const expiresIn = options?.expiresIn || 3600\n const expires = Math.floor(Date.now() / 1000) + expiresIn\n const signature = crypto\n .createHash('sha256')\n .update(`${key}:${expires}`)\n .digest('hex')\n .slice(0, 16)\n\n return ok(`local://${key}?expires=${expires}&signature=${signature}`)\n },\n\n async putUrl(key, options?): Promise<HaiResult<string>> {\n // 同上,返回虚拟 URL\n logOperation('write', 'presign.putUrl', { key, options })\n const expiresIn = options?.expiresIn || 3600\n const expires = Math.floor(Date.now() / 1000) + expiresIn\n const signature = crypto\n .createHash('sha256')\n .update(`put:${key}:${expires}`)\n .digest('hex')\n .slice(0, 16)\n\n return ok(`local://${key}?action=put&expires=${expires}&signature=${signature}`)\n },\n\n publicUrl(key): string | null {\n logOperation('read', 'presign.publicUrl', { key })\n // 本地存储不支持公开 URL\n return null\n },\n }\n\n // -------------------------------------------------------------------------\n // Provider 接口实现\n // -------------------------------------------------------------------------\n\n return {\n name: 'local',\n\n file,\n dir,\n presign,\n\n async connect(cfg: StorageConfig): Promise<HaiResult<void>> {\n if (cfg.type !== 'local') {\n return err(\n HaiStorageError.CONFIG_ERROR,\n storageM('storage_localConfigTypeError'),\n )\n }\n\n try {\n config = cfg\n logger.info('Connecting local provider', { root: cfg.root })\n\n // 确保根目录存在\n await fsp.mkdir(cfg.root, { recursive: true, mode: cfg.directoryMode })\n\n connected = true\n logger.info('Local provider connected', { root: cfg.root })\n return ok(undefined)\n }\n catch (error) {\n logger.error('Local provider connect failed', { error })\n config = null\n return err(toStorageError(error))\n }\n },\n\n async close(): Promise<void> {\n logger.info('Local provider disconnected')\n config = null\n connected = false\n },\n\n isConnected(): boolean {\n return connected\n },\n }\n}\n","/**\n * @h-ai/storage — S3 Provider 实现\n *\n * 基于 AWS SDK v3 实现的 S3 协议存储 Provider。\n * 支持 AWS S3、MinIO、RustFS、阿里云 OSS 等兼容 S3 协议的存储服务。\n * @module storage-provider-s3\n */\n\nimport type { HaiError, HaiResult } from '@h-ai/core'\nimport type { S3Config, StorageConfig } from '../storage-config.js'\nimport type { DirOperations, FileMetadata, FileOperations, ListResult, PresignOperations, StorageProvider } from '../storage-types.js'\nimport { Buffer } from 'node:buffer'\nimport { Readable } from 'node:stream'\nimport {\n CopyObjectCommand,\n DeleteObjectCommand,\n DeleteObjectsCommand,\n GetObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n S3Client,\n} from '@aws-sdk/client-s3'\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner'\n\nimport { core, err, ok } from '@h-ai/core'\n\nimport { storageM } from '../storage-i18n.js'\nimport { toByteStream } from '../storage-stream.js'\nimport {\n\n HaiStorageError,\n\n} from '../storage-types.js'\nimport { logStorageOperation } from './storage-operation-log.js'\n\nconst logger = core.logger.child({ module: 'storage', scope: 'provider-s3' })\nconst TRAILING_SLASHES_REGEX = /\\/+$/\nconst LEADING_SLASHES_REGEX = /^\\/+/\n\n/**\n * 脱敏 S3 endpoint,避免日志暴露认证信息\n */\nfunction sanitizeEndpointUrl(url: string): string {\n try {\n const parsed = new URL(url)\n if (parsed.username)\n parsed.username = '***'\n if (parsed.password)\n parsed.password = '***'\n return parsed.toString()\n }\n catch {\n return '(invalid url)'\n }\n}\n\n// ─── 辅助函数 ───\n\n/**\n * 将 S3 SDK 错误转换为 StorageError\n *\n * 映射规则:\n * - NoSuchKey / NotFound / 404 → NOT_FOUND\n * - AccessDenied → PERMISSION_DENIED\n * - NoSuchBucket → CONFIG_ERROR\n * - NetworkError / ECONNREFUSED → NETWORK_ERROR\n * - 其他 → OPERATION_FAILED\n *\n * @param error - AWS SDK 抛出的原始错误\n * @param key - 相关文件键(可选,用于错误消息)\n * @returns 统一的 StorageError\n */\nfunction toStorageError(error: unknown, key?: string): HaiError {\n const e = error as { name?: string, message?: string, Code?: string }\n\n if (e.name === 'PreconditionFailed' || (e as { $metadata?: { httpStatusCode?: number } }).$metadata?.httpStatusCode === 412)\n return { ...HaiStorageError.ALREADY_EXISTS, message: storageM('storage_fileAlreadyExists', { params: { key: key ?? '' } }), cause: error }\n\n // S3 特定错误处理\n if (e.name === 'NoSuchKey' || e.Code === 'NoSuchKey'\n || e.name === 'NotFound' || e.Code === 'NotFound'\n || e.name === '404' || (e as { $metadata?: { httpStatusCode?: number } }).$metadata?.httpStatusCode === 404) {\n return {\n ...HaiStorageError.NOT_FOUND,\n message: storageM('storage_fileNotFound', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.name === 'AccessDenied' || e.Code === 'AccessDenied') {\n return {\n ...HaiStorageError.PERMISSION_DENIED,\n message: storageM('storage_permissionDenied', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.name === 'NoSuchBucket' || e.Code === 'NoSuchBucket') {\n return {\n ...HaiStorageError.CONFIG_ERROR,\n message: storageM('storage_bucketNotExist'),\n cause: error,\n }\n }\n\n // 网络相关错误\n if (e.name === 'NetworkError' || e.name?.includes('ECONNREFUSED')) {\n return {\n ...HaiStorageError.NETWORK_ERROR,\n message: storageM('storage_networkError'),\n cause: error,\n }\n }\n\n return {\n ...HaiStorageError.OPERATION_FAILED,\n message: storageM('storage_operationFailed', { params: { error: e.message ?? '' } }),\n cause: error,\n }\n}\n\n/**\n * 为 key 添加配置中的路径前缀\n *\n * @param key - 用户提供的文件键\n * @param prefix - 配置中的前缀(可能为空)\n * @returns 拼接后的完整 S3 key\n */\nfunction withPrefix(key: string, prefix: string): string {\n if (!prefix)\n return key\n return `${prefix.replace(TRAILING_SLASHES_REGEX, '')}/${key.replace(LEADING_SLASHES_REGEX, '')}`\n}\n\n/**\n * 从 S3 key 中移除配置前缀,还原为用户视角的文件键\n *\n * @param key - 包含前缀的完整 S3 key\n * @param prefix - 配置中的前缀\n * @returns 去除前缀后的文件键\n */\nfunction withoutPrefix(key: string, prefix: string): string {\n if (!prefix)\n return key\n const normalizedPrefix = `${prefix.replace(TRAILING_SLASHES_REGEX, '')}/`\n if (key.startsWith(normalizedPrefix)) {\n return key.slice(normalizedPrefix.length)\n }\n return key\n}\n\n// ─── S3 Provider 实现 ───\n\n/**\n * 创建 S3 存储 Provider\n *\n * 基于 AWS SDK v3 实现的 StorageProvider。\n * connect 时会创建 S3Client 并通过 ListObjectsV2 验证连接。\n * 支持配置 prefix 重定向所有 key,对用户透明。\n *\n * 实现说明:\n * - copy 时若指定了 contentType 或 metadata,使用 MetadataDirective='REPLACE'\n * - dir.delete 通过先 list 再批量删除实现\n * - presign 使用 @aws-sdk/s3-request-presigner 签名\n *\n * @returns S3 Provider 实例\n */\nexport function createS3Provider(): StorageProvider {\n let client: S3Client | null = null\n let config: S3Config | null = null\n\n // -------------------------------------------------------------------------\n // 内部辅助\n // -------------------------------------------------------------------------\n\n /** 获取当前 S3Client,未初始化时抛异常 */\n function getClient(): S3Client {\n if (!client) {\n throw new Error(storageM('storage_s3ClientNotInitialized'))\n }\n return client\n }\n\n /** 获取当前 S3 配置,未初始化时抛异常 */\n function getConfig(): S3Config {\n if (!config) {\n throw new Error(storageM('storage_s3ConfigNotInitialized'))\n }\n return config\n }\n\n /** 将用户 key 拼接配置前缀,得到完整 S3 对象键 */\n function fullKey(key: string): string {\n return withPrefix(key, getConfig().prefix)\n }\n\n function logOperation(category: 'read' | 'write', operation: string, payload: unknown): void {\n logStorageOperation(logger, config?.operationLog, category, operation, payload)\n }\n\n // -------------------------------------------------------------------------\n // 文件操作实现\n // -------------------------------------------------------------------------\n\n const file: FileOperations = {\n async put(key, data, options = {}): Promise<HaiResult<FileMetadata>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n\n options.signal?.throwIfAborted()\n const body = typeof data === 'string' ? Buffer.from(data) : data\n const buffered = body instanceof Uint8Array\n let size = buffered ? body.byteLength : 0\n const params = {\n Bucket: s3Config.bucket,\n Key: fullPath,\n ContentType: options.contentType,\n Metadata: options.metadata,\n CacheControl: options.cacheControl,\n ContentDisposition: options.contentDisposition,\n IfNoneMatch: options.ifNoneMatch,\n }\n logOperation('write', 'file.put', { key, fullPath, byteLength: buffered ? size : undefined, options })\n if (buffered) {\n await s3Client.send(new PutObjectCommand({ ...params, Body: body }), { abortSignal: options.signal })\n }\n else {\n async function* counted(): AsyncIterable<Uint8Array> {\n for await (const chunk of toByteStream(data, options.signal)) {\n size += chunk.byteLength\n yield chunk\n }\n }\n const stream = Readable.from(counted(), { objectMode: false })\n const controller = new AbortController()\n const abort = () => controller.abort()\n options.signal?.addEventListener('abort', abort, { once: true })\n try {\n if (options.signal?.aborted)\n abort()\n // 标准 S3 multipart;SDK 管理分片与失败清理,最多缓冲两片。\n const { Upload } = await import('@aws-sdk/lib-storage')\n await new Upload({ client: s3Client, params: { ...params, Body: stream }, queueSize: 2, leavePartsOnError: false, abortController: controller }).done()\n }\n finally {\n options.signal?.removeEventListener('abort', abort)\n stream.destroy()\n }\n }\n\n // 获取上传后的元数据\n const headResult = await file.head(key)\n if (headResult.success) {\n return headResult\n }\n\n // 如果获取元数据失败,返回基本信息\n return ok({\n key,\n size,\n contentType: options.contentType || 'application/octet-stream',\n lastModified: new Date(),\n })\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async get(key, options = {}): Promise<HaiResult<Buffer>> {\n const streamed = await file.getStream(key, options)\n if (!streamed.success)\n return streamed\n try {\n const chunks: Uint8Array[] = []\n for await (const chunk of streamed.data)\n chunks.push(chunk)\n return ok(Buffer.concat(chunks))\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async getStream(key, options = {}) {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n logOperation('read', 'file.getStream', { key, fullPath, options })\n\n let Range: string | undefined\n if (options.rangeStart !== undefined || options.rangeEnd !== undefined) {\n const start = options.rangeStart ?? 0\n const end = options.rangeEnd !== undefined ? options.rangeEnd : ''\n Range = `bytes=${start}-${end}`\n }\n\n const response = await s3Client.send(new GetObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n Range,\n }), { abortSignal: options.signal })\n\n if (!response.Body) {\n return err(\n HaiStorageError.OPERATION_FAILED,\n storageM('storage_responseBodyEmpty'),\n )\n }\n\n // Node SDK 的 Body 是字节可迭代流;SDK 类型联合也包含其他运行时的 Blob。\n const stream = response.Body as AsyncIterable<Uint8Array>\n return ok(stream)\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async head(key): Promise<HaiResult<FileMetadata>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n logOperation('read', 'file.head', { key, fullPath })\n\n const response = await s3Client.send(new HeadObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n }))\n\n return ok({\n key,\n size: response.ContentLength || 0,\n contentType: response.ContentType || 'application/octet-stream',\n lastModified: response.LastModified || new Date(),\n etag: response.ETag,\n metadata: response.Metadata,\n })\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async exists(key): Promise<HaiResult<boolean>> {\n logOperation('read', 'file.exists', { key })\n const result = await file.head(key)\n if (result.success) {\n return ok(true)\n }\n if (result.error.code === HaiStorageError.NOT_FOUND.code) {\n return ok(false)\n }\n return err(result.error)\n },\n\n async delete(key): Promise<HaiResult<void>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n logOperation('write', 'file.delete', { key, fullPath })\n\n await s3Client.send(new DeleteObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n }))\n\n return ok(undefined)\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async deleteMany(keys): Promise<HaiResult<void>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n\n if (keys.length === 0) {\n return ok(undefined)\n }\n\n logOperation('write', 'file.deleteMany', { count: keys.length, keys })\n\n await s3Client.send(new DeleteObjectsCommand({\n Bucket: s3Config.bucket,\n Delete: {\n Objects: keys.map(key => ({ Key: fullKey(key) })),\n },\n }))\n\n return ok(undefined)\n }\n catch (error) {\n return err(toStorageError(error))\n }\n },\n\n async copy(sourceKey, destKey, options = {}): Promise<HaiResult<FileMetadata>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const sourceFullPath = fullKey(sourceKey)\n const destFullPath = fullKey(destKey)\n logOperation('write', 'file.copy', { sourceKey, destKey, sourceFullPath, destFullPath, options })\n\n await s3Client.send(new CopyObjectCommand({\n Bucket: s3Config.bucket,\n CopySource: `${s3Config.bucket}/${sourceFullPath}`,\n Key: destFullPath,\n ContentType: options.contentType,\n Metadata: options.metadata,\n MetadataDirective: (options.metadata || options.contentType) ? 'REPLACE' : 'COPY',\n }))\n\n return file.head(destKey)\n }\n catch (error) {\n return err(toStorageError(error, sourceKey))\n }\n },\n }\n\n // -------------------------------------------------------------------------\n // 目录操作实现\n // -------------------------------------------------------------------------\n\n const dir: DirOperations = {\n async list(options = {}): Promise<HaiResult<ListResult>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n logOperation('read', 'dir.list', { options })\n\n // 构建完整前缀\n let prefix = s3Config.prefix || ''\n if (options.prefix) {\n prefix = withPrefix(options.prefix, s3Config.prefix)\n }\n\n const response = await s3Client.send(new ListObjectsV2Command({\n Bucket: s3Config.bucket,\n Prefix: prefix || undefined,\n Delimiter: options.delimiter,\n MaxKeys: options.maxKeys,\n ContinuationToken: options.continuationToken,\n }))\n\n const files: FileMetadata[] = (response.Contents || []).map(item => ({\n key: withoutPrefix(item.Key || '', s3Config.prefix),\n size: item.Size || 0,\n contentType: 'application/octet-stream', // ListObjects 不返回 ContentType\n lastModified: item.LastModified || new Date(),\n etag: item.ETag,\n }))\n\n const commonPrefixes = (response.CommonPrefixes || [])\n .map(p => withoutPrefix(p.Prefix || '', s3Config.prefix))\n .filter(p => p.length > 0)\n\n return ok({\n files,\n commonPrefixes,\n nextContinuationToken: response.NextContinuationToken,\n isTruncated: response.IsTruncated || false,\n })\n }\n catch (error) {\n return err(toStorageError(error))\n }\n },\n\n async delete(prefix): Promise<HaiResult<void>> {\n try {\n logOperation('write', 'dir.delete', { prefix })\n // 分页列出并逐批删除,每批最多 1000 个对象\n let continuationToken: string | undefined\n\n do {\n const listResult = await dir.list({\n prefix,\n continuationToken,\n maxKeys: 1000,\n })\n\n if (!listResult.success) {\n return err(listResult.error)\n }\n\n const keys = listResult.data.files.map(f => f.key)\n if (keys.length > 0) {\n const deleteResult = await file.deleteMany(keys)\n if (!deleteResult.success) {\n return deleteResult\n }\n }\n\n continuationToken = listResult.data.nextContinuationToken\n } while (continuationToken)\n\n return ok(undefined)\n }\n catch (error) {\n return err(toStorageError(error))\n }\n },\n }\n\n // -------------------------------------------------------------------------\n // 签名 URL 操作实现\n // -------------------------------------------------------------------------\n\n const presign: PresignOperations = {\n async getUrl(key, options?): Promise<HaiResult<string>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n logOperation('read', 'presign.getUrl', { key, fullPath, options })\n\n const command = new GetObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n ResponseContentType: options?.responseContentType,\n ResponseContentDisposition: options?.responseContentDisposition,\n })\n\n const url = await getSignedUrl(s3Client, command, {\n expiresIn: options?.expiresIn || 3600,\n })\n\n return ok(url)\n }\n catch (error) {\n return err(\n HaiStorageError.PRESIGN_FAILED,\n storageM('storage_presignUrlFailed'),\n error,\n )\n }\n },\n\n async putUrl(key, options?): Promise<HaiResult<string>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n logOperation('write', 'presign.putUrl', { key, fullPath, options })\n\n const command = new PutObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n ContentType: options?.contentType || 'application/octet-stream',\n })\n\n const url = await getSignedUrl(s3Client, command, {\n expiresIn: options?.expiresIn || 3600,\n })\n\n return ok(url)\n }\n catch (error) {\n return err(\n HaiStorageError.PRESIGN_FAILED,\n storageM('storage_presignUploadUrlFailed'),\n error,\n )\n }\n },\n\n publicUrl(key): string | null {\n const s3Config = getConfig()\n if (!s3Config.publicUrl) {\n return null\n }\n const fullPath = fullKey(key)\n logOperation('read', 'presign.publicUrl', { key, fullPath })\n return `${s3Config.publicUrl.replace(TRAILING_SLASHES_REGEX, '')}/${fullPath}`\n },\n }\n\n // -------------------------------------------------------------------------\n // Provider 接口实现\n // -------------------------------------------------------------------------\n\n return {\n name: 's3',\n\n file,\n dir,\n presign,\n\n async connect(cfg: StorageConfig): Promise<HaiResult<void>> {\n if (cfg.type !== 's3') {\n return err(\n HaiStorageError.CONFIG_ERROR,\n storageM('storage_s3ConfigTypeError'),\n )\n }\n\n try {\n config = cfg\n logger.info('Connecting S3 provider', {\n bucket: cfg.bucket,\n region: cfg.region,\n endpoint: cfg.endpoint ? sanitizeEndpointUrl(cfg.endpoint) : undefined,\n })\n\n client = new S3Client({\n region: cfg.region,\n endpoint: cfg.endpoint,\n forcePathStyle: cfg.forcePathStyle,\n // 不强制兼容服务实现 AWS checksum 扩展;校验工具按实际字节独立计算。\n requestChecksumCalculation: 'WHEN_REQUIRED',\n responseChecksumValidation: 'WHEN_REQUIRED',\n credentials: {\n accessKeyId: cfg.accessKeyId,\n secretAccessKey: cfg.secretAccessKey,\n },\n })\n\n // 测试连接:尝试列出一个对象\n await client.send(new ListObjectsV2Command({\n Bucket: cfg.bucket,\n MaxKeys: 1,\n }))\n\n logger.info('S3 provider connected', { bucket: cfg.bucket })\n return ok(undefined)\n }\n catch (error) {\n logger.error('S3 provider connect failed', { error })\n config = null\n client = null\n return err(toStorageError(error))\n }\n },\n\n async close(): Promise<void> {\n if (client) {\n client.destroy()\n client = null\n config = null\n logger.info('S3 provider disconnected')\n }\n },\n\n isConnected(): boolean {\n return client !== null && config !== null\n },\n }\n}\n","/** @h-ai/storage — Node 字节内容 checksum 生成与校验,无存储生命周期依赖。 */\nimport type { HaiResult } from '@h-ai/core'\nimport type { ChecksumOperations, ChecksumOptions, StorageData } from './storage-types.js'\nimport { Buffer } from 'node:buffer'\nimport { createHash, timingSafeEqual } from 'node:crypto'\nimport { err, ok } from '@h-ai/core'\nimport { storageM } from './storage-i18n.js'\nimport { toByteStream } from './storage-stream.js'\nimport { HaiStorageError } from './storage-types.js'\n\nconst HEX_CHECKSUM = /^[\\da-f]+$/i\nconst BASE64_CHECKSUM = /^(?:[A-Z\\d+/]{4})*(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?$/i\nconst ALGORITHMS = new Set(['sha256', 'sha384', 'sha512', 'sha1', 'md5'])\n\nasync function generate(data: StorageData, options: ChecksumOptions = {}): Promise<HaiResult<string>> {\n try {\n const algorithm = options.algorithm ?? 'sha256'\n if (!ALGORITHMS.has(algorithm))\n return err(HaiStorageError.INVALID_CHECKSUM, storageM('storage_checksumInvalid'))\n const hash = createHash(algorithm)\n for await (const chunk of toByteStream(data, options.signal))\n hash.update(chunk)\n return ok(hash.digest(options.encoding ?? 'hex'))\n }\n catch (error) {\n return err(HaiStorageError.CHECKSUM_FAILED, storageM('storage_checksumFailed'), error)\n }\n}\n\nexport const checksum: ChecksumOperations = {\n generate,\n async verify(data, expected, options = {}) {\n const encoding = options.encoding ?? 'hex'\n const valid = encoding === 'hex'\n ? expected.length % 2 === 0 && HEX_CHECKSUM.test(expected)\n : BASE64_CHECKSUM.test(expected) && expected.length > 0\n if (!valid)\n return err(HaiStorageError.INVALID_CHECKSUM, storageM('storage_checksumInvalid'))\n const calculated = await generate(data, options)\n if (!calculated.success)\n return calculated\n const actualBytes = Buffer.from(calculated.data, encoding)\n const expectedBytes = Buffer.from(expected, encoding)\n return ok(actualBytes.length === expectedBytes.length && timingSafeEqual(actualBytes, expectedBytes))\n },\n}\n","/**\n * @h-ai/storage — 存储服务主入口\n *\n * 提供统一的 `storage` 对象,管理运行时状态与生命周期。\n * @module storage-main\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { StorageConfig, StorageConfigInput } from './storage-config.js'\nimport type { DirOperations, FileOperations, PresignOperations, StorageFunctions, StorageProvider } from './storage-types.js'\n\nimport { core, err, ok } from '@h-ai/core'\n\nimport { createLocalProvider } from './providers/storage-provider-local.js'\nimport { createS3Provider } from './providers/storage-provider-s3.js'\nimport { checksum } from './storage-checksum.js'\nimport { StorageConfigSchema } from './storage-config.js'\nimport { storageM } from './storage-i18n.js'\nimport {\n\n HaiStorageError,\n\n} from './storage-types.js'\n\nconst logger = core.logger.child({ module: 'storage', scope: 'main' })\n\n// ─── 内部状态 ───\n\n/** 当前使用的存储 Provider 实例(init 后非空,close 后置空) */\nlet currentProvider: StorageProvider | null = null\n\n/** 当前解析后的存储配置(init 后非空,close 后置空) */\nlet currentConfig: StorageConfig | null = null\n\n/** 并发初始化防护标志;防止多次 init() 同时执行导致资源泄漏 */\nlet initInProgress = false\n\n// ─── Provider 工厂 ───\n\n/**\n * 根据配置类型创建对应的存储 Provider\n *\n * @param config - 经 Schema 校验后的存储配置\n * @returns 对应类型的 Provider 实例(尚未连接)\n * @throws 理论上仅在出现未覆盖分支时抛出异常(正常情况下由 Schema 保证不会发生)\n */\nfunction createProvider(config: StorageConfig): StorageProvider {\n switch (config.type) {\n case 's3': return createS3Provider()\n case 'local': return createLocalProvider()\n default:\n throw new Error(storageM('storage_unsupportedType', { params: { type: (config as StorageConfig).type } }))\n }\n}\n\n// ─── 未初始化占位 ───\n\n/**\n * 创建未初始化状态的错误工具包\n *\n * 当 storage 未调用 init() 就直接使用 file/dir/presign 操作时,\n * 所有方法调用都会返回 NOT_INITIALIZED 错误。\n */\nconst notInitialized = core.module.createNotInitializedKit(\n HaiStorageError.NOT_INITIALIZED,\n () => storageM('storage_notInitialized'),\n)\n\n/** 未初始化时的文件操作占位代理 */\nconst notInitializedFile = notInitialized.proxy<FileOperations>()\n\n/** 未初始化时的目录操作占位代理 */\nconst notInitializedDir = notInitialized.proxy<DirOperations>()\n\n/**\n * 未初始化时的签名 URL 操作占位代理\n *\n * publicUrl 始终返回 null;其余方法返回 NOT_INITIALIZED 错误。\n */\nconst notInitializedPresignBase = notInitialized.proxy<PresignOperations>()\nconst notInitializedPresign = new Proxy(\n notInitializedPresignBase,\n {\n get: (target, prop, receiver) => prop === 'publicUrl' ? () => null : Reflect.get(target, prop, receiver),\n },\n)\n\n// ─── 存储服务对象 ───\n\n/**\n * 存储服务单例\n *\n * 使用前必须先调用 `storage.init()` 初始化,传入 S3 或本地存储配置。\n * 初始化后通过 `storage.file`、`storage.dir`、`storage.presign` 访问各操作接口。\n *\n * @example\n * ```ts\n * import { storage } from '@h-ai/storage'\n *\n * // 初始化\n * await storage.init({ type: 'local', root: '/data/uploads' })\n *\n * // 上传文件\n * await storage.file.put('hello.txt', 'Hello World')\n *\n * // 关闭连接\n * await storage.close()\n * ```\n */\nexport const storage: StorageFunctions = {\n checksum,\n /**\n * 初始化存储连接。\n *\n * 如果当前已有活跃连接,会先自动 close 再重新初始化。\n * 配置会通过 Zod Schema 校验;校验失败或连接异常会返回 `CONNECTION_FAILED`。\n *\n * @param config 存储配置(S3 或本地),支持省略带默认值的字段。\n * @returns 成功时返回 ok(undefined);失败时返回包含错误码和消息的 err。\n */\n async init(config: StorageConfigInput): Promise<HaiResult<void>> {\n // 并发初始化防护:避免多次 init 同时执行导致 Provider 泄漏\n if (initInProgress) {\n logger.warn('Storage init already in progress, skipping concurrent call')\n return err(\n HaiStorageError.OPERATION_FAILED,\n storageM('storage_operationFailed', { params: { error: 'Concurrent initialization detected' } }),\n )\n }\n initInProgress = true\n\n try {\n if (currentProvider) {\n logger.warn('Storage module is already initialized, reinitializing')\n const closeResult = await storage.close()\n if (!closeResult.success) {\n return closeResult\n }\n }\n\n logger.info('Initializing storage module')\n\n const parseResult = StorageConfigSchema.safeParse(config)\n if (!parseResult.success) {\n logger.error('Storage config validation failed', { error: parseResult.error.message })\n return err(\n HaiStorageError.CONFIG_ERROR,\n storageM('storage_configError', { params: { error: parseResult.error.message } }),\n parseResult.error,\n )\n }\n const parsed = parseResult.data\n\n const provider = createProvider(parsed)\n const connectResult = await provider.connect(parsed)\n if (!connectResult.success) {\n logger.error('Storage module initialization failed', {\n code: connectResult.error.code,\n message: connectResult.error.message,\n })\n return connectResult\n }\n currentProvider = provider\n currentConfig = parsed\n logger.info('Storage module initialized')\n return ok(undefined)\n }\n catch (error) {\n logger.error('Storage module initialization failed', { error })\n return err(\n HaiStorageError.CONNECTION_FAILED,\n storageM('storage_operationFailed', {\n params: { error: error instanceof Error ? error.message : String(error) },\n }),\n error,\n )\n }\n finally {\n initInProgress = false\n }\n },\n\n /** 文件操作接口。未初始化时所有方法返回 NOT_INITIALIZED 错误 */\n get file(): FileOperations { return currentProvider?.file ?? notInitializedFile },\n\n /** 目录操作接口。未初始化时所有方法返回 NOT_INITIALIZED 错误 */\n get dir(): DirOperations { return currentProvider?.dir ?? notInitializedDir },\n\n /** 签名 URL 操作接口。未初始化时 publicUrl 返回 null,其余返回错误 */\n get presign(): PresignOperations { return currentProvider?.presign ?? notInitializedPresign },\n\n /** 当前脱敏后的存储配置快照;未初始化或已关闭时为 null */\n get config() {\n return currentConfig ? core.sanitize.sanitizeSensitiveFields(currentConfig) : null\n },\n\n /** 是否已完成初始化 */\n get isInitialized() { return currentProvider !== null },\n\n /**\n * 关闭存储连接并释放资源\n *\n * 关闭后 file/dir/presign 操作将返回 NOT_INITIALIZED 错误。\n * 重复调用不会报错。\n */\n async close(): Promise<HaiResult<void>> {\n if (!currentProvider) {\n currentConfig = null\n logger.info('Storage module already closed, skipping')\n return ok(undefined)\n }\n\n logger.info('Closing storage module')\n\n try {\n await currentProvider.close()\n logger.info('Storage module closed')\n return ok(undefined)\n }\n catch (error) {\n logger.error('Storage module close failed', { error })\n return err(\n HaiStorageError.CONNECTION_FAILED,\n storageM('storage_operationFailed', {\n params: { error: error instanceof Error ? error.message : String(error) },\n }),\n error,\n )\n }\n finally {\n currentProvider = null\n currentConfig = null\n }\n },\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@h-ai/storage",
3
3
  "type": "module",
4
- "version": "0.1.0-alpha.61",
4
+ "version": "0.1.0-alpha.62",
5
5
  "description": "Hai Framework object storage module for S3-compatible and local backends.",
6
6
  "license": "Apache-2.0",
7
7
  "exports": {
@@ -30,8 +30,9 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@aws-sdk/client-s3": "^3.1149.0",
33
+ "@aws-sdk/lib-storage": "^3.1149.0",
33
34
  "@aws-sdk/s3-request-presigner": "^3.1149.0",
34
- "@h-ai/core": "0.1.0-alpha.61",
35
+ "@h-ai/core": "0.1.0-alpha.62",
35
36
  "zod": "^4.6.5"
36
37
  },
37
38
  "devDependencies": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../messages/en-US.json","../messages/zh-CN.json","../src/storage-i18n.ts","../src/storage-mime.ts","../src/client/storage-client.ts"],"names":[],"mappings":"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","file":"chunk-U427BQIQ.js","sourcesContent":["{\n \"$schema\": \"https://inlang.com/schema/inlang-message-format\",\n \"storage_notInitialized\": \"Storage not initialized, please call storage.init() first\",\n \"storage_bucketNotExist\": \"Bucket does not exist\",\n \"storage_networkError\": \"Network error\",\n \"storage_responseBodyEmpty\": \"Response body is empty\",\n \"storage_presignUrlFailed\": \"Failed to generate presigned URL\",\n \"storage_presignUploadUrlFailed\": \"Failed to generate presigned upload URL\",\n \"storage_s3ConfigTypeError\": \"Config type error: S3 provider only supports type=s3\",\n \"storage_diskSpaceInsufficient\": \"Insufficient disk space\",\n \"storage_localConfigTypeError\": \"Config type error: Local provider only supports type=local\",\n \"storage_operationFailed\": \"Operation failed: {error}\",\n \"storage_unsupportedType\": \"Unsupported storage type: {type}\",\n \"storage_config_bucketRequired\": \"Bucket is required\",\n \"storage_config_regionRequired\": \"Region is required\",\n \"storage_config_accessKeyIdRequired\": \"Access Key ID is required\",\n \"storage_config_secretAccessKeyRequired\": \"Secret Access Key is required\",\n \"storage_config_rootRequired\": \"Root path is required\",\n \"storage_fileNotFound\": \"File not found: {key}\",\n \"storage_permissionDenied\": \"Permission denied: {key}\",\n \"storage_pathIsDir\": \"Path is a directory: {key}\",\n \"storage_ioError\": \"IO error: {error}\",\n \"storage_deleteManyFailed\": \"Failed to delete {count} files\",\n \"storage_s3ClientNotInitialized\": \"S3 client not initialized\",\n \"storage_s3ConfigNotInitialized\": \"S3 config not initialized\",\n \"storage_uploadCanceled\": \"Upload canceled\",\n \"storage_downloadCanceled\": \"Download canceled\",\n \"storage_uploadFailedStatus\": \"Upload failed (status {status}): {statusText}\",\n \"storage_downloadFailedStatus\": \"Download failed (status {status}): {statusText}\",\n \"storage_unknownError\": \"Unknown error\",\n \"storage_saveFileFailed\": \"Failed to save file\",\n \"storage_downloadDefaultFilename\": \"download\",\n \"storage_pathTraversal\": \"Path traversal attack detected\",\n \"storage_localNotInitialized\": \"Local storage not initialized\",\n \"storage_configError\": \"Storage config validation failed: {error}\",\n \"storage_metaFileAccess\": \"Direct access to metadata files is not allowed: {key}\"\n}\n","{\n \"$schema\": \"https://inlang.com/schema/inlang-message-format\",\n \"storage_notInitialized\": \"存储未初始化,请先调用 storage.init()\",\n \"storage_bucketNotExist\": \"存储桶不存在\",\n \"storage_networkError\": \"网络错误\",\n \"storage_responseBodyEmpty\": \"响应体为空\",\n \"storage_presignUrlFailed\": \"生成签名 URL 失败\",\n \"storage_presignUploadUrlFailed\": \"生成上传签名 URL 失败\",\n \"storage_s3ConfigTypeError\": \"配置类型错误:S3 provider 仅支持 type=s3\",\n \"storage_diskSpaceInsufficient\": \"磁盘空间不足\",\n \"storage_localConfigTypeError\": \"配置类型错误:Local provider 仅支持 type=local\",\n \"storage_operationFailed\": \"操作失败: {error}\",\n \"storage_unsupportedType\": \"不支持的存储类型:{type}\",\n \"storage_config_bucketRequired\": \"存储桶不能为空\",\n \"storage_config_regionRequired\": \"区域不能为空\",\n \"storage_config_accessKeyIdRequired\": \"Access Key ID 不能为空\",\n \"storage_config_secretAccessKeyRequired\": \"Secret Access Key 不能为空\",\n \"storage_config_rootRequired\": \"Root 不能为空\",\n \"storage_fileNotFound\": \"文件不存在: {key}\",\n \"storage_permissionDenied\": \"权限不足: {key}\",\n \"storage_pathIsDir\": \"路径是目录: {key}\",\n \"storage_ioError\": \"IO 错误: {error}\",\n \"storage_deleteManyFailed\": \"删除 {count} 个文件失败\",\n \"storage_s3ClientNotInitialized\": \"S3 客户端未初始化\",\n \"storage_s3ConfigNotInitialized\": \"S3 配置未初始化\",\n \"storage_uploadCanceled\": \"上传已取消\",\n \"storage_downloadCanceled\": \"下载已取消\",\n \"storage_uploadFailedStatus\": \"上传失败(状态码 {status}):{statusText}\",\n \"storage_downloadFailedStatus\": \"下载失败(状态码 {status}):{statusText}\",\n \"storage_unknownError\": \"未知错误\",\n \"storage_saveFileFailed\": \"保存文件失败\",\n \"storage_downloadDefaultFilename\": \"下载\",\n \"storage_pathTraversal\": \"检测到路径穿越攻击\",\n \"storage_localNotInitialized\": \"本地存储未初始化\",\n \"storage_configError\": \"存储配置校验失败:{error}\",\n \"storage_metaFileAccess\": \"不允许直接访问元数据文件: {key}\"\n}\n","/**\n * @h-ai/storage — i18n 消息获取器\n *\n * 加载存储模块的中英文翻译文件,提供统一的文案获取函数。\n * 内部使用,不对外导出。\n * @module storage-i18n\n */\n\nimport { core } from '@h-ai/core'\nimport messagesEnUS from '../messages/en-US.json'\nimport messagesZhCN from '../messages/zh-CN.json'\n\n/** 消息键类型(从 zh-CN 翻译文件自动推断) */\ntype StorageMessageKey = keyof typeof messagesZhCN\n\n/**\n * 获取存储模块本地化文案\n *\n * 根据当前 locale 自动选择 zh-CN 或 en-US 的文案,\n * 支持带参数插值(通过 `{ params: { key: value } }`)。\n *\n * @example\n * ```ts\n * storageM('storage_fileNotFound', { params: { key: 'a.txt' } })\n * ```\n */\nexport const storageM = core.i18n.createMessageGetter<StorageMessageKey>({\n 'zh-CN': messagesZhCN,\n 'en-US': messagesEnUS,\n})\n","/**\n * @h-ai/storage — MIME 类型映射\n *\n * 集中维护文件扩展名到 MIME 类型的映射表。 供 local provider 和 client 端共用,避免重复定义。\n * @module storage-mime\n */\n\n/**\n * 文件扩展名 → MIME 类型映射表\n *\n * 键为小写扩展名(不含点号),值为标准 MIME 类型字符串。\n * 未匹配项统一回退到 `application/octet-stream`。\n */\nexport const MIME_TYPES: Record<string, string> = {\n // 图片\n 'jpg': 'image/jpeg',\n 'jpeg': 'image/jpeg',\n 'png': 'image/png',\n 'gif': 'image/gif',\n 'webp': 'image/webp',\n 'svg': 'image/svg+xml',\n 'ico': 'image/x-icon',\n\n // 文档\n 'pdf': 'application/pdf',\n 'doc': 'application/msword',\n 'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'xls': 'application/vnd.ms-excel',\n 'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'ppt': 'application/vnd.ms-powerpoint',\n 'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n\n // 文本\n 'txt': 'text/plain',\n 'html': 'text/html',\n 'css': 'text/css',\n 'js': 'text/javascript',\n 'json': 'application/json',\n 'xml': 'application/xml',\n 'csv': 'text/csv',\n 'md': 'text/markdown',\n\n // 音视频\n 'mp3': 'audio/mpeg',\n 'wav': 'audio/wav',\n 'mp4': 'video/mp4',\n 'webm': 'video/webm',\n 'ogg': 'audio/ogg',\n\n // 压缩包\n 'zip': 'application/zip',\n 'rar': 'application/x-rar-compressed',\n '7z': 'application/x-7z-compressed',\n 'tar': 'application/x-tar',\n 'gz': 'application/gzip',\n\n // 字体\n 'woff': 'font/woff',\n 'woff2': 'font/woff2',\n 'ttf': 'font/ttf',\n 'otf': 'font/otf',\n}\n\n/**\n * MIME 类型默认值(未知扩展名回退)\n */\nexport const MIME_TYPE_DEFAULT = 'application/octet-stream'\n","/**\n * @h-ai/storage — 前端存储客户端\n *\n * 提供前端通过签名 URL 直接上传下载文件的功能。\n * @module storage-client\n */\n\nimport { storageM } from '../storage-i18n.js'\nimport { MIME_TYPE_DEFAULT, MIME_TYPES } from '../storage-mime.js'\n\n// ─── 客户端类型 ───\n\n/**\n * 上传进度回调数据\n *\n * 由 `uploadWithPresignedUrl` 在上传过程中通过 `onProgress` 回调返回。\n */\nexport interface UploadProgress {\n /** 已上传字节数 */\n loaded: number\n /** 总字节数(仅在 lengthComputable 时可靠) */\n total: number\n /** 进度百分比 (0–100),四舍五入到整数 */\n percent: number\n}\n\n/**\n * 前端上传选项\n *\n * 控制 `uploadWithPresignedUrl` 的行为,包括内容类型、进度监听和取消。\n */\nexport interface ClientUploadOptions {\n /** 内容类型(设置后会加到请求 Content-Type 头) */\n contentType?: string\n /** 进度回调(设置后会使用 XMLHttpRequest;仅 lengthComputable=true 时回调) */\n onProgress?: (progress: UploadProgress) => void\n /** AbortController 用于取消上传(abort 后返回 {success: false}) */\n abortController?: AbortController\n}\n\n/**\n * 前端下载选项\n *\n * 控制 `downloadWithPresignedUrl` / `downloadAndSave` 的行为。\n */\nexport interface ClientDownloadOptions {\n /** 保存的文件名(仅 downloadAndSave 使用,默认为 'download') */\n filename?: string\n /** AbortController 用于取消下载(abort 后返回 {success: false}) */\n abortController?: AbortController\n}\n\n// ─── 上传功能 ───\n\n/**\n * 使用签名 URL 上传文件\n *\n * @param url - 后端生成的上传签名 URL\n * @param data - 要上传的数据(File、Blob、ArrayBuffer 或字符串)\n * @param options - 上传选项\n * @returns 上传结果\n *\n * @example\n * ```ts\n * // 获取签名 URL(通常从后端 API 获取)\n * const response = await fetch('/api/storage/presign', {\n * method: 'POST',\n * body: JSON.stringify({ key: 'uploads/image.png', contentType: 'image/png' })\n * })\n * const { uploadUrl } = await response.json()\n *\n * // 使用签名 URL 上传\n * const file = document.getElementById('fileInput').files[0]\n * const result = await uploadWithPresignedUrl(uploadUrl, file, {\n * contentType: 'image/png',\n * onProgress: (p) => {\n * // 在此更新进度条:p.percent\n * }\n * })\n *\n * if (result.success) {\n * // 上传成功\n * }\n * ```\n */\nexport async function uploadWithPresignedUrl(\n url: string,\n data: File | Blob | ArrayBuffer | string,\n options: ClientUploadOptions = {},\n): Promise<{ success: boolean, error?: string }> {\n try {\n // 准备请求体\n const body: BodyInit = data\n\n // 使用 XMLHttpRequest 以支持上传进度\n if (options.onProgress) {\n return new Promise((resolve) => {\n const xhr = new XMLHttpRequest()\n\n // 设置取消控制\n if (options.abortController) {\n options.abortController.signal.addEventListener('abort', () => {\n xhr.abort()\n resolve({ success: false, error: storageM('storage_uploadCanceled') })\n })\n }\n\n // 监听进度\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable && options.onProgress) {\n options.onProgress({\n loaded: event.loaded,\n total: event.total,\n percent: Math.round((event.loaded / event.total) * 100),\n })\n }\n })\n\n // 监听完成\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve({ success: true })\n }\n else {\n resolve({\n success: false,\n error: storageM('storage_uploadFailedStatus', { params: { status: xhr.status, statusText: xhr.statusText } }),\n })\n }\n })\n\n // 监听错误\n xhr.addEventListener('error', () => {\n resolve({ success: false, error: storageM('storage_networkError') })\n })\n\n // 发送请求\n xhr.open('PUT', url)\n if (options.contentType) {\n xhr.setRequestHeader('Content-Type', options.contentType)\n }\n xhr.send(body)\n })\n }\n\n // 不需要进度时使用 fetch\n const response = await fetch(url, {\n method: 'PUT',\n body,\n headers: options.contentType\n ? { 'Content-Type': options.contentType }\n : undefined,\n signal: options.abortController?.signal,\n })\n\n if (!response.ok) {\n return {\n success: false,\n error: storageM('storage_uploadFailedStatus', { params: { status: response.status, statusText: response.statusText } }),\n }\n }\n\n return { success: true }\n }\n catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n return { success: false, error: storageM('storage_uploadCanceled') }\n }\n return {\n success: false,\n error: error instanceof Error ? error.message : storageM('storage_unknownError'),\n }\n }\n}\n\n// ─── 下载功能 ───\n\n/**\n * 使用签名 URL 下载文件\n *\n * @param url - 后端生成的下载签名 URL\n * @param options - 下载选项\n * @returns 下载结果(成功时 data 为 Blob)\n *\n * @example\n * ```ts\n * // 下载为 Blob\n * const result = await downloadWithPresignedUrl(downloadUrl)\n * if (result.success) {\n * const blob = result.data\n * // 处理 blob...\n * }\n *\n * // 下载并保存为文件(浏览器环境)\n * await downloadAndSave(downloadUrl, { filename: 'document.pdf' })\n * ```\n */\nexport async function downloadWithPresignedUrl(\n url: string,\n options: ClientDownloadOptions = {},\n): Promise<{ success: boolean, data?: Blob, error?: string }> {\n try {\n const response = await fetch(url, {\n method: 'GET',\n signal: options.abortController?.signal,\n })\n\n if (!response.ok) {\n return {\n success: false,\n error: storageM('storage_downloadFailedStatus', { params: { status: response.status, statusText: response.statusText } }),\n }\n }\n\n const blob = await response.blob()\n return { success: true, data: blob }\n }\n catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n return { success: false, error: storageM('storage_downloadCanceled') }\n }\n return {\n success: false,\n error: error instanceof Error ? error.message : storageM('storage_unknownError'),\n }\n }\n}\n\n/**\n * 下载并保存到本地(浏览器环境)\n *\n * @param url - 下载 URL\n * @param options - 下载选项\n *\n * @example\n * ```ts\n * await downloadAndSave(presignedUrl, {\n * filename: 'report.pdf'\n * })\n * ```\n */\nexport async function downloadAndSave(\n url: string,\n options: ClientDownloadOptions = {},\n): Promise<{ success: boolean, error?: string }> {\n const result = await downloadWithPresignedUrl(url, options)\n\n if (!result.success || !result.data) {\n return { success: false, error: result.error }\n }\n\n try {\n // 创建下载链接\n const blobUrl = URL.createObjectURL(result.data)\n const link = document.createElement('a')\n link.href = blobUrl\n link.download = options.filename || storageM('storage_downloadDefaultFilename')\n\n // 触发下载\n document.body.appendChild(link)\n link.click()\n document.body.removeChild(link)\n\n // 清理 URL\n URL.revokeObjectURL(blobUrl)\n\n return { success: true }\n }\n catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : storageM('storage_saveFileFailed'),\n }\n }\n}\n\n// ─── 辅助功能 ───\n\n/**\n * 从 File 对象获取文件扩展名\n *\n * 取文件名中最后一个 '.' 之后的部分,转为小写。\n * 若文件名无点号或以点号开头且无其他点号,返回空字符串。\n *\n * @param file - 浏览器 File 对象\n * @returns 扩展名(不含点),如 'png'、'tar.gz' 返回 'gz'\n *\n * @example\n * ```ts\n * getFileExtension(new File([], 'photo.PNG')) // 'png'\n * getFileExtension(new File([], '.gitignore')) // 'gitignore'\n * getFileExtension(new File([], 'noext')) // ''\n * ```\n */\nexport function getFileExtension(file: File): string {\n const name = file.name\n const lastDot = name.lastIndexOf('.')\n return lastDot > -1 ? name.slice(lastDot + 1).toLowerCase() : ''\n}\n\n/**\n * 根据文件扩展名获取 MIME 类型\n *\n * 内置常见扩展名映射(图片/文档/文本/音视频/压缩包),\n * 未匹配时返回 'application/octet-stream'。\n *\n * @param extension - 文件扩展名(不含点,大小写不敏感)\n * @returns MIME 类型字符串\n *\n * @example\n * ```ts\n * getMimeType('png') // 'image/png'\n * getMimeType('xyz') // 'application/octet-stream'\n * ```\n */\nexport function getMimeType(extension: string): string {\n return MIME_TYPES[extension.toLowerCase()] || MIME_TYPE_DEFAULT\n}\n\n/**\n * 格式化文件大小为人类可读字符串\n *\n * 使用 1024 进制,输出单位为 B/KB/MB/GB/TB。\n * 大于 1 B 时保留 2 位小数。\n *\n * @param bytes - 字节数(非负整数)\n * @returns 格式化后的字符串,如 '0 B'、'1.50 KB'、'2.30 MB'\n *\n * @example\n * ```ts\n * formatFileSize(0) // '0 B'\n * formatFileSize(1536) // '1.50 KB'\n * formatFileSize(1048576) // '1.00 MB'\n * ```\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0)\n return '0 B'\n\n const units = ['B', 'KB', 'MB', 'GB', 'TB']\n const i = Math.floor(Math.log(bytes) / Math.log(1024))\n const size = bytes / 1024 ** i\n\n return `${size.toFixed(i > 0 ? 2 : 0)} ${units[i]}`\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/storage-config.ts","../src/storage-types.ts"],"names":[],"mappings":"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","file":"chunk-W44BX2GI.js","sourcesContent":["/**\n * @h-ai/storage — 错误码 + 配置 Schema\n * @module storage-config\n */\n\nimport { z } from 'zod'\nimport { storageM } from './storage-i18n.js'\n\n// ─── 配置 Schema ───\n\n/**\n * 存储类型枚举\n *\n * 支持的存储类型:\n * - `s3` - S3 协议对象存储(兼容 AWS S3、MinIO、RustFS、阿里云 OSS 等)\n * - `local` - 本地文件存储(仅后端)\n */\nexport const StorageTypeSchema = z.enum(['s3', 'local'])\n\n/** 存储类型 */\nexport type StorageType = z.infer<typeof StorageTypeSchema>\n\nexport const StorageOperationLogConfigSchema = z.object({\n read: z.boolean().default(false),\n write: z.boolean().default(false),\n maxLength: z.number().int().min(0).default(1000),\n level: z.enum(['info', 'debug', 'trace']).default('debug'),\n})\n\nexport type StorageOperationLogConfig = z.infer<typeof StorageOperationLogConfigSchema>\n\nconst OperationLogConfigFieldSchema = {\n operationLog: StorageOperationLogConfigSchema.optional(),\n}\n\n/**\n * S3 存储配置 Schema\n *\n * 支持标准 AWS S3 以及兼容 S3 协议的存储服务(MinIO、RustFS、阿里云 OSS 等)。\n * 必填字段:bucket、region、accessKeyId、secretAccessKey。\n */\nexport const S3ConfigSchema = z.object({\n /** 存储类型 */\n type: z.literal('s3'),\n\n /** 存储桶名称 */\n bucket: z.string().min(1, storageM('storage_config_bucketRequired')),\n\n /** 区域 */\n region: z.string().min(1, storageM('storage_config_regionRequired')),\n\n /** Access Key ID */\n accessKeyId: z.string().min(1, storageM('storage_config_accessKeyIdRequired')),\n\n /** Secret Access Key */\n secretAccessKey: z.string().min(1, storageM('storage_config_secretAccessKeyRequired')),\n\n /** 自定义端点(用于 MinIO、RustFS、阿里云 OSS 等) */\n endpoint: z.string().url().optional(),\n\n /** 强制使用路径风格 URL(某些 S3 兼容服务需要) */\n forcePathStyle: z.boolean().default(false),\n\n /** 路径前缀(所有操作都会加上此前缀) */\n prefix: z.string().default(''),\n\n /** 公开访问基础 URL(用于生成公开 URL) */\n publicUrl: z.string().url().optional(),\n\n ...OperationLogConfigFieldSchema,\n})\n\n/** S3 配置类型(parse 后,所有字段已填充默认值) */\nexport type S3Config = z.infer<typeof S3ConfigSchema>\n\n/**\n * 本地存储配置 Schema\n *\n * 基于本地文件系统,仅后端可用。必填字段:root。\n */\nexport const LocalConfigSchema = z.object({\n /** 存储类型 */\n type: z.literal('local'),\n\n /** 根目录路径 */\n root: z.string().min(1, storageM('storage_config_rootRequired')),\n\n /** 目录创建权限 */\n directoryMode: z.number().default(0o755),\n\n /** 文件创建权限 */\n fileMode: z.number().default(0o644),\n\n ...OperationLogConfigFieldSchema,\n})\n\n/** 本地配置类型(parse 后,所有字段已填充默认值) */\nexport type LocalConfig = z.infer<typeof LocalConfigSchema>\n\n/**\n * 统一存储配置 Schema\n *\n * 支持 S3 和本地文件两种类型的配置验证。\n *\n * @example\n * ```ts\n * // S3 配置\n * const s3Config = StorageConfigSchema.parse({\n * type: 's3',\n * bucket: 'my-bucket',\n * region: 'us-east-1',\n * accessKeyId: 'AKIAXXXXXXXX',\n * secretAccessKey: 'xxxxx'\n * })\n *\n * // MinIO 配置\n * const minioConfig = StorageConfigSchema.parse({\n * type: 's3',\n * bucket: 'my-bucket',\n * region: 'us-east-1',\n * endpoint: 'http://localhost:9000',\n * accessKeyId: 'minioadmin',\n * secretAccessKey: 'minioadmin',\n * forcePathStyle: true\n * })\n *\n * // 本地存储配置\n * const localConfig = StorageConfigSchema.parse({\n * type: 'local',\n * root: '/data/uploads'\n * })\n * ```\n */\nexport const StorageConfigSchema = z.discriminatedUnion('type', [\n S3ConfigSchema,\n LocalConfigSchema,\n])\n\n/** 存储配置类型(parse 后的完整配置) */\nexport type StorageConfig = z.infer<typeof StorageConfigSchema>\n\n/** 存储配置输入类型(parse 前,允许省略带默认值的字段) */\nexport type StorageConfigInput = z.input<typeof StorageConfigSchema>\n\n// ─── 签名 URL 配置 ───\n\n/**\n * 签名 URL 配置 Schema\n *\n * 用于控制 `storage.presign.getUrl()` 生成的下载签名 URL 参数。\n *\n * @example\n * ```ts\n * const url = await storage.presign.getUrl('report.pdf', {\n * expiresIn: 600, // 10 分钟过期\n * responseContentDisposition: 'attachment; filename=\"report.pdf\"',\n * })\n * ```\n */\nexport const PresignOptionsSchema = z.object({\n /** 过期时间(秒),范围 1~604800(7 天),默认 3600(1 小时) */\n expiresIn: z.number().min(1).max(604800).default(3600), // 最长 7 天\n\n /** 响应内容类型(用于下载时设置 Content-Type) */\n responseContentType: z.string().optional(),\n\n /** 响应内容处置(用于设置下载文件名) */\n responseContentDisposition: z.string().optional(),\n})\n\n/** 签名 URL 配置类型(parse 后) */\nexport type PresignOptions = z.infer<typeof PresignOptionsSchema>\n\n/**\n * 上传签名 URL 配置 Schema\n *\n * 继承 PresignOptionsSchema 并扩展上传专属参数,用于 `storage.presign.putUrl()`。\n *\n * @example\n * ```ts\n * const url = await storage.presign.putUrl('uploads/avatar.png', {\n * contentType: 'image/png',\n * expiresIn: 300,\n * })\n * ```\n */\nexport const PresignUploadOptionsSchema = PresignOptionsSchema.extend({\n /** 上传内容类型(必须与实际上传匹配) */\n contentType: z.string().default('application/octet-stream'),\n})\n\n/** 上传签名 URL 配置类型(parse 后) */\nexport type PresignUploadOptions = z.infer<typeof PresignUploadOptionsSchema>\n","/**\n * @h-ai/storage — 公共类型定义\n *\n * 包含:\n * - 错误类型(StorageError)\n * - 文件元数据(FileMetadata)\n * - 列表结果(ListResult)\n * - 文件操作接口(FileOperations)\n * - 目录操作接口(DirOperations)\n * - 签名 URL 操作接口(PresignOperations)\n * - 函数接口(StorageFunctions)\n * - Provider 接口(StorageProvider)\n * @module storage-types\n */\n\nimport type { ErrorInfo, HaiResult } from '@h-ai/core'\nimport type { Buffer } from 'node:buffer'\nimport type { PresignOptions, PresignUploadOptions, StorageConfig, StorageConfigInput } from './storage-config.js'\nimport { core } from '@h-ai/core'\n\n// ─── 错误定义(照 @h-ai/core 范式) ───\n\nconst StorageErrorInfo = {\n CONNECTION_FAILED: '001:500',\n OPERATION_FAILED: '002:500',\n NOT_FOUND: '003:404',\n ALREADY_EXISTS: '004:409',\n PERMISSION_DENIED: '005:403',\n QUOTA_EXCEEDED: '006:413',\n INVALID_PATH: '007:400',\n IO_ERROR: '008:500',\n NETWORK_ERROR: '009:502',\n NOT_INITIALIZED: '010:500',\n UNSUPPORTED_TYPE: '011:400',\n CONFIG_ERROR: '012:500',\n PRESIGN_FAILED: '013:500',\n UPLOAD_FAILED: '014:500',\n DOWNLOAD_FAILED: '015:500',\n} as const satisfies ErrorInfo\n\nexport const HaiStorageError = core.error.buildHaiErrorsDef('storage', StorageErrorInfo)\n\n// ─── 文件元数据 ───\n\n/**\n * 文件元数据接口\n *\n * 表示存储中单个文件的元信息,由 put/head/copy/list 等操作返回。\n */\nexport interface FileMetadata {\n /** 文件键/路径(不含配置 prefix,对用户透明) */\n key: string\n /** 文件大小(字节) */\n size: number\n /** 内容类型(MIME),未指定时根据扩展名自动推断 */\n contentType: string\n /** 最后修改时间 */\n lastModified: Date\n /** ETag(用于缓存验证,本地存储为 MD5,S3 由服务端生成) */\n etag?: string\n /** 自定义元数据键值对 */\n metadata?: Record<string, string>\n}\n\n/**\n * 列表选项\n *\n * 用于控制 `storage.dir.list()` 的过滤、分页和目录模拟行为。\n */\nexport interface ListOptions {\n /** 前缀过滤(只返回以此前缀开头的文件) */\n prefix?: string\n /**\n * 分页标记(从上次 ListResult.nextContinuationToken 获取)\n *\n * 注意:本地存储目前不支持此字段,仅 S3 Provider 有效。\n */\n continuationToken?: string\n /** 每页最大数量(默认 1000) */\n maxKeys?: number\n /** 分隔符(用于模拟目录结构,通常为 '/';传入后子目录作为 commonPrefixes 返回) */\n delimiter?: string\n}\n\n/**\n * 列表结果\n *\n * 包含当前页的文件列表、目录前缀、分页信息。\n */\nexport interface ListResult {\n /** 当前页文件元数据列表 */\n files: FileMetadata[]\n /** 公共前缀(使用 delimiter 时由子目录路径组成) */\n commonPrefixes: string[]\n /** 下一页标记;无后续页时为 undefined */\n nextContinuationToken?: string\n /** 是否被截断(true 表示还有更多数据,可用 nextContinuationToken 继续拉取) */\n isTruncated: boolean\n}\n\n// ─── 上传下载选项 ───\n\n/**\n * 上传选项\n *\n * 用于控制 `storage.file.put()` 的元数据与缓存行为。\n */\nexport interface UploadOptions {\n /** 内容类型(不指定时根据文件扩展名自动推断) */\n contentType?: string\n /** 自定义元数据键值对(存储后可通过 head 获取) */\n metadata?: Record<string, string>\n /** Cache-Control 响应头(例如 'max-age=3600') */\n cacheControl?: string\n /** Content-Disposition 响应头(例如 'attachment; filename=\"a.txt\"') */\n contentDisposition?: string\n}\n\n/**\n * 下载选项\n *\n * 用于控制 `storage.file.get()` 的范围读取。\n * 两个字段均为字节偏移(含),与 HTTP Range 语义一致。\n */\nexport interface DownloadOptions {\n /** 范围起始位置(字节,含);不指定时从文件开头读取 */\n rangeStart?: number\n /** 范围结束位置(字节,含);不指定时读取到文件末尾 */\n rangeEnd?: number\n}\n\n/**\n * 复制选项\n *\n * 用于控制 `storage.file.copy()` 的目标文件元数据。\n * 提供任意字段时,S3 Provider 会使用 MetadataDirective='REPLACE' 覆盖源文件元数据。\n */\nexport interface CopyOptions {\n /** 目标文件的内容类型(不指定时继承源文件) */\n contentType?: string\n /** 目标文件的自定义元数据(不指定时继承源文件) */\n metadata?: Record<string, string>\n}\n\n// ─── 操作接口 ───\n\n/**\n * 文件操作接口\n *\n * 提供文件的读写操作。\n */\nexport interface FileOperations {\n /**\n * 上传文件\n *\n * @param key - 文件键/路径\n * @param data - 文件内容(Buffer、Uint8Array 或字符串)\n * @param options - 上传选项\n * @returns 上传结果包含文件元数据\n * @example\n * ```ts\n * const result = await storage.file.put('uploads/a.txt', 'hello')\n * if (result.success) {\n * result.data.key\n * }\n * ```\n */\n put: (key: string, data: Buffer | Uint8Array | string, options?: UploadOptions) => Promise<HaiResult<FileMetadata>>\n\n /**\n * 下载文件\n *\n * @param key - 文件键/路径\n * @param options - 下载选项\n * @returns 文件内容\n * @example\n * ```ts\n * const result = await storage.file.get('uploads/a.txt')\n * if (result.success) {\n * const buffer = result.data\n * }\n * ```\n */\n get: (key: string, options?: DownloadOptions) => Promise<HaiResult<Buffer>>\n\n /**\n * 获取文件元数据\n *\n * @param key - 文件键/路径\n * @returns 文件元数据\n * @example\n * ```ts\n * const result = await storage.file.head('uploads/a.txt')\n * if (result.success) {\n * result.data.size\n * }\n * ```\n */\n head: (key: string) => Promise<HaiResult<FileMetadata>>\n\n /**\n * 检查文件是否存在\n *\n * @param key - 文件键/路径\n * @returns 是否存在\n * @example\n * ```ts\n * const result = await storage.file.exists('uploads/a.txt')\n * if (result.success) {\n * result.data\n * }\n * ```\n */\n exists: (key: string) => Promise<HaiResult<boolean>>\n\n /**\n * 删除文件\n *\n * @param key - 文件键/路径\n * @returns 删除结果\n * @example\n * ```ts\n * await storage.file.delete('uploads/a.txt')\n * ```\n */\n delete: (key: string) => Promise<HaiResult<void>>\n\n /**\n * 批量删除文件\n *\n * @param keys - 文件键/路径列表\n * @returns 删除结果\n * @example\n * ```ts\n * await storage.file.deleteMany(['a.txt', 'b.txt'])\n * ```\n */\n deleteMany: (keys: string[]) => Promise<HaiResult<void>>\n\n /**\n * 复制文件\n *\n * @param sourceKey - 源文件键\n * @param destKey - 目标文件键\n * @param options - 复制选项\n * @returns 目标文件元数据\n * @example\n * ```ts\n * await storage.file.copy('a.txt', 'b.txt')\n * ```\n */\n copy: (sourceKey: string, destKey: string, options?: CopyOptions) => Promise<HaiResult<FileMetadata>>\n}\n\n/**\n * 目录操作接口\n *\n * 提供目录(前缀)的列表操作。\n */\nexport interface DirOperations {\n /**\n * 列出目录内容\n *\n * @param options - 列表选项\n * @returns 文件和目录列表\n * @example\n * ```ts\n * const result = await storage.dir.list({ prefix: 'uploads/' })\n * if (result.success) {\n * result.data.files\n * }\n * ```\n */\n list: (options?: ListOptions) => Promise<HaiResult<ListResult>>\n\n /**\n * 删除目录(删除指定前缀下的所有文件)\n *\n * @param prefix - 目录前缀\n * @returns 删除结果\n * @example\n * ```ts\n * await storage.dir.delete('uploads/tmp/')\n * ```\n */\n delete: (prefix: string) => Promise<HaiResult<void>>\n}\n\n/**\n * 签名 URL 操作接口\n *\n * 提供生成临时授权 URL 的功能,支持前端直接上传下载。\n */\nexport interface PresignOperations {\n /**\n * 生成下载签名 URL\n *\n * @param key - 文件键/路径\n * @param options - 签名选项\n * @returns 签名 URL\n * @example\n * ```ts\n * const result = await storage.presign.getUrl('a.txt', { expiresIn: 60 })\n * ```\n */\n getUrl: (key: string, options?: PresignOptions) => Promise<HaiResult<string>>\n\n /**\n * 生成上传签名 URL\n *\n * @param key - 文件键/路径\n * @param options - 上传签名选项\n * @returns 签名 URL\n * @example\n * ```ts\n * const result = await storage.presign.putUrl('a.txt', { contentType: 'text/plain' })\n * ```\n */\n putUrl: (key: string, options?: PresignUploadOptions) => Promise<HaiResult<string>>\n\n /**\n * 获取公开访问 URL(如果配置了 publicUrl)\n *\n * @param key - 文件键/路径\n * @returns 公开 URL,如果未配置 publicUrl 则返回 null\n * @example\n * ```ts\n * const url = storage.presign.publicUrl('a.txt')\n * ```\n */\n publicUrl: (key: string) => string | null\n}\n\n// ─── 函数接口 ───\n\n/**\n * 存储函数接口\n *\n * 统一的存储访问入口:\n * - `storage.init()` — 初始化存储连接\n * - `storage.close()` — 关闭连接并释放资源\n * - `storage.file` — 文件操作(上传/下载/删除/复制等)\n * - `storage.dir` — 目录操作(列表/删除)\n * - `storage.presign` — 签名 URL 操作\n * - `storage.config` — 当前脱敏配置快照(未初始化时为 null)\n * - `storage.isInitialized` — 初始化状态\n */\nexport interface StorageFunctions {\n /**\n * 初始化存储连接\n *\n * 已有连接时会先 close 再重新初始化。\n *\n * @param config - 存储配置(支持省略带默认值的字段)\n * @returns 成功 ok(undefined);失败返回 err(含 StorageError)\n */\n init: (config: StorageConfigInput) => Promise<HaiResult<void>>\n\n /**\n * 关闭存储连接并释放资源\n *\n * 重复调用不会报错。\n */\n close: () => Promise<HaiResult<void>>\n\n /** 当前脱敏后的存储配置快照;未初始化或已关闭时为 null */\n readonly config: StorageConfig | null\n\n /** 是否已完成初始化 */\n readonly isInitialized: boolean\n\n /** 文件操作接口(put / get / head / exists / delete / deleteMany / copy) */\n readonly file: FileOperations\n\n /** 目录操作接口(list / delete) */\n readonly dir: DirOperations\n\n /** 签名 URL 操作接口(getUrl / putUrl / publicUrl) */\n readonly presign: PresignOperations\n}\n\n// ─── Provider 接口 ───\n\n/**\n * 存储 Provider 接口\n *\n * 由各具体存储实现(S3、Local)实现此接口。\n */\nexport interface StorageProvider {\n /** Provider 名称标识(如 's3'、'local') */\n readonly name: string\n\n /**\n * 建立与存储服务的连接\n *\n * @param config - 经 Schema 校验后的完整配置\n * @returns 连接结果\n */\n connect: (config: StorageConfig) => Promise<HaiResult<void>>\n\n /** 关闭连接并释放资源 */\n close: () => Promise<void>\n\n /** 检查是否已连接 */\n isConnected: () => boolean\n\n /** 文件操作接口 */\n readonly file: FileOperations\n\n /** 目录操作接口 */\n readonly dir: DirOperations\n\n /** 签名 URL 操作接口 */\n readonly presign: PresignOperations\n}\n"]}