zen-fs-webdav 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/constants.ts","../src/utils.ts","../src/webdav-fs.ts","../src/webdav.ts"],"sourcesContent":["/**\r\n * WebDAV文件系统库的错误类型定义\r\n */\r\n\r\n/**\r\n * WebDAV错误基类\r\n */\r\nexport class WebDAVError extends Error {\r\n /**\r\n * HTTP状态码\r\n */\r\n status?: number;\r\n /**\r\n * 原始错误对象(如果有)\r\n */\r\n cause?: unknown;\r\n\r\n constructor(message: string, status?: number, cause?: unknown) {\r\n super(message);\r\n this.name = 'WebDAVError';\r\n this.status = status;\r\n this.cause = cause;\r\n // 兼容 TS 的 Error 子类\r\n Object.setPrototypeOf(this, WebDAVError.prototype);\r\n }\r\n\r\n toString(): string {\r\n if (typeof this.status === 'number') {\r\n return `${this.name}: ${this.message} (Status: ${this.status})`;\r\n }\r\n return `${this.name}: ${this.message}`;\r\n }\r\n\r\n static fromResponse(response: { status: number; statusText: string }, message?: string): WebDAVError {\r\n const msg = message\r\n ? `${message}: ${response.status} ${response.statusText}`\r\n : `${response.status} ${response.statusText}`;\r\n return new WebDAVError(msg, response.status);\r\n }\r\n\r\n static fromError(error: unknown): WebDAVError {\r\n if (error instanceof WebDAVError) {\r\n return error;\r\n }\r\n if (error instanceof Error) {\r\n const msg = error.message || 'Unknown WebDAV error';\r\n return new WebDAVError(msg, undefined, error);\r\n }\r\n return new WebDAVError('Unknown WebDAV error');\r\n }\r\n}\r\n\r\n/**\r\n * 文件或目录不存在错误\r\n */\r\nexport class NotFoundError extends WebDAVError {\r\n constructor(path: string) {\r\n super(`文件未找到: ${path}`, 404);\r\n this.name = 'NotFoundError';\r\n Object.setPrototypeOf(this, NotFoundError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * 认证错误\r\n */\r\nexport class AuthenticationError extends WebDAVError {\r\n constructor(message = '认证失败') {\r\n super(message, 401);\r\n this.name = 'AuthenticationError';\r\n Object.setPrototypeOf(this, AuthenticationError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * 授权错误\r\n */\r\nexport class AuthorizationError extends WebDAVError {\r\n constructor(message = '权限被拒绝') {\r\n super(message, 403);\r\n this.name = 'AuthorizationError';\r\n Object.setPrototypeOf(this, AuthorizationError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * 服务器错误\r\n */\r\nexport class ServerError extends WebDAVError {\r\n constructor(message = '服务器错误', statusCode = 500) {\r\n super(message, statusCode);\r\n this.name = 'ServerError';\r\n Object.setPrototypeOf(this, ServerError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * 文件已存在错误\r\n */\r\nexport class FileExistsError extends WebDAVError {\r\n constructor(path: string) {\r\n super(`文件已存在: ${path}`, 412);\r\n this.name = 'FileExistsError';\r\n Object.setPrototypeOf(this, FileExistsError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * 锁定错误\r\n */\r\nexport class LockError extends WebDAVError {\r\n constructor(path: string, message = '资源被锁定') {\r\n super(`${message}: ${path}`, 423);\r\n this.name = 'LockError';\r\n Object.setPrototypeOf(this, LockError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * 超时错误\r\n */\r\nexport class TimeoutError extends WebDAVError {\r\n constructor(message = '连接超时') {\r\n super(message, 408);\r\n this.name = 'TimeoutError';\r\n Object.setPrototypeOf(this, TimeoutError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * 网络错误\r\n */\r\nexport class NetworkError extends WebDAVError {\r\n constructor(originalError?: Error, message = '网络错误') {\r\n super(originalError ? `${message}: ${originalError.message}` : message, 0, originalError);\r\n this.name = 'NetworkError';\r\n Object.setPrototypeOf(this, NetworkError.prototype);\r\n }\r\n}\r\n\r\n/**\r\n * 参数错误\r\n */\r\nexport class ArgumentError extends WebDAVError {\r\n constructor(message: string) {\r\n super(`无效参数: ${message}`);\r\n this.name = 'ArgumentError';\r\n Object.setPrototypeOf(this, ArgumentError.prototype);\r\n }\r\n}","/**\n * WebDAV 相关常量\n */\n\n/**\n * WebDAV 方法\n */\nexport enum WebDAVMethod {\n GET = 'GET',\n PUT = 'PUT',\n POST = 'POST',\n DELETE = 'DELETE',\n PROPFIND = 'PROPFIND',\n PROPPATCH = 'PROPPATCH',\n MKCOL = 'MKCOL',\n COPY = 'COPY',\n MOVE = 'MOVE',\n LOCK = 'LOCK',\n UNLOCK = 'UNLOCK',\n}\n\n/**\n * WebDAV 命名空间\n */\nexport const WebDAVNamespace = {\n DAV: 'DAV:',\n CALDAV: 'urn:ietf:params:xml:ns:caldav',\n CARDDAV: 'urn:ietf:params:xml:ns:carddav',\n OWNCLOUD: 'http://owncloud.org/ns',\n NEXTCLOUD: 'http://nextcloud.org/ns',\n};\n\n/**\n * WebDAV 属性名称\n */\nexport const WebDAVPropName = {\n RESOURCE_TYPE: 'resourcetype',\n DISPLAY_NAME: 'displayname',\n GET_CONTENT_LENGTH: 'getcontentlength',\n GET_CONTENT_TYPE: 'getcontenttype',\n GET_LAST_MODIFIED: 'getlastmodified',\n CREATION_DATE: 'creationdate',\n ETAG: 'getetag',\n};\n\n/**\n * WebDAV 资源类型\n */\nexport enum WebDAVResourceType {\n FILE = 'file',\n DIRECTORY = 'directory',\n COLLECTION = 'collection',\n}\n\n/**\n * WebDAV 深度\n */\nexport enum WebDAVDepth {\n /**\n * 仅当前资源\n */\n ZERO = '0',\n \n /**\n * 当前资源及其直接子资源\n */\n ONE = '1',\n \n /**\n * 当前资源及其所有后代资源\n */\n INFINITY = 'infinity',\n}\n\n/**\n * 默认超时时间(毫秒)\n */\nexport const DEFAULT_TIMEOUT = 30000;\n\n/**\n * 默认请求头\n */\nexport const DEFAULT_HEADERS = {\n 'Content-Type': 'application/xml; charset=utf-8',\n 'Accept': 'application/xml, */*',\n};\n\n/**\n * XML 命名空间前缀\n */\nexport const XML_NAMESPACE_PREFIX = {\n 'd': WebDAVNamespace.DAV,\n 'oc': WebDAVNamespace.OWNCLOUD,\n 'nc': WebDAVNamespace.NEXTCLOUD,\n};\n\n/**\n * 错误消息\n */\nexport const ErrorMessage = {\n INVALID_URL: '无效的 URL',\n TIMEOUT: '请求超时',\n NETWORK_ERROR: '网络错误',\n UNAUTHORIZED: '未授权',\n FORBIDDEN: '禁止访问',\n NOT_FOUND: '资源不存在',\n METHOD_NOT_ALLOWED: '方法不允许',\n CONFLICT: '资源冲突',\n PRECONDITION_FAILED: '前提条件失败',\n INTERNAL_SERVER_ERROR: '服务器内部错误',\n NOT_IMPLEMENTED: '未实现',\n BAD_GATEWAY: '错误的网关',\n SERVICE_UNAVAILABLE: '服务不可用',\n GATEWAY_TIMEOUT: '网关超时',\n};\n\n/**\n * HTTP 状态码\n */\nexport enum HttpStatus {\n OK = 200,\n CREATED = 201,\n NO_CONTENT = 204,\n MULTI_STATUS = 207,\n BAD_REQUEST = 400,\n UNAUTHORIZED = 401,\n FORBIDDEN = 403,\n NOT_FOUND = 404,\n METHOD_NOT_ALLOWED = 405,\n CONFLICT = 409,\n PRECONDITION_FAILED = 412,\n INTERNAL_SERVER_ERROR = 500,\n NOT_IMPLEMENTED = 501,\n BAD_GATEWAY = 502,\n SERVICE_UNAVAILABLE = 503,\n GATEWAY_TIMEOUT = 504,\n}","import { WebDAVNamespace, WebDAVPropName, WebDAVResourceType } from './constants';\r\nimport { Stats } from './types';\r\nimport { WebDAVError } from './errors';\r\nimport { XMLParser } from 'fast-xml-parser'\r\n\r\n/**\r\n * 规范化路径,确保以 / 开头,不以 / 结尾(除非是根路径)\r\n * @param path 路径\r\n * @returns 规范化后的路径\r\n */\r\nexport function normalizePath(path: string): string {\r\n // 确保路径以 / 开头\r\n if (!path.startsWith('/')) {\r\n path = '/' + path;\r\n }\r\n // 合并多个连续的斜杠为一个\r\n path = path.replace(/\\/+/g, '/');\r\n // 如果不是根路径,则确保不以 / 结尾\r\n if (path.length > 1 && path.endsWith('/')) {\r\n path = path.slice(0, -1);\r\n }\r\n return path;\r\n}\r\n\r\n// /**\r\n// * 连接路径\r\n// * @param base 基础路径\r\n// * @param paths 要连接的路径\r\n// * @returns 连接后的路径\r\n// */\r\n// export function joinPaths(base: string, ...paths: string[]): string {\r\n// let result = base;\r\n \r\n// for (const path of paths) {\r\n// if (!path) continue;\r\n \r\n// if (result.endsWith('/')) {\r\n// result = result + (path.startsWith('/') ? path.slice(1) : path);\r\n// } else {\r\n// result = result + (path.startsWith('/') ? path : '/' + path);\r\n// }\r\n// }\r\n \r\n// return normalizePath(result);\r\n// }\r\n\r\n/**\r\n * 获取路径的父目录\r\n * @param path 路径\r\n * @returns 父目录路径\r\n */\r\nexport function getParentPath(path: string): string {\r\n path = normalizePath(path);\r\n \r\n // 根路径没有父目录\r\n if (path === '/') {\r\n return '/';\r\n }\r\n \r\n const lastSlashIndex = path.lastIndexOf('/');\r\n if (lastSlashIndex <= 0) {\r\n return '/';\r\n }\r\n \r\n return path.slice(0, lastSlashIndex) || '/';\r\n}\r\n\r\n/**\r\n * 获取路径的基本名称(最后一个部分)\r\n * @param path 路径\r\n * @returns 基本名称\r\n */\r\nexport function getBasename(path: string): string {\r\n path = normalizePath(path);\r\n \r\n // 根路径的基本名称为空字符串\r\n if (path === '/') {\r\n return '';\r\n }\r\n \r\n const lastSlashIndex = path.lastIndexOf('/');\r\n return path.slice(lastSlashIndex + 1);\r\n}\r\n\r\n/**\r\n * 创建基本认证头\r\n * @param username 用户名\r\n * @param password 密码\r\n * @returns 基本认证头\r\n */\r\nexport function createBasicAuthHeader(username: string, password: string): string {\r\n // 在浏览器和 Node.js 环境中都可用的 btoa 实现\r\n const btoa = (str: string) => {\r\n if (typeof window !== 'undefined' && window.btoa) {\r\n return window.btoa(str);\r\n } else if (typeof Buffer !== 'undefined') {\r\n return Buffer.from(str).toString('base64');\r\n } else {\r\n throw new Error('Base64 encoding not available');\r\n }\r\n };\r\n \r\n return `Basic ${btoa(`${username}:${password}`)}`;\r\n}\r\n\r\n/**\r\n * 解析 WebDAV 响应中的属性\r\n * @param xml XML 文档\r\n * @returns 解析后的属性对象\r\n */\r\nexport function parseWebDAVProperties(xml: Document): Record<string, any> {\r\n const result: Record<string, any> = {};\r\n \r\n // 查找所有 prop 元素\r\n const propElements = xml.getElementsByTagNameNS(WebDAVNamespace.DAV, 'prop');\r\n \r\n for (let i = 0; i < propElements.length; i++) {\r\n const propElement = propElements[i];\r\n \r\n // 遍历 prop 元素的子元素\r\n for (let j = 0; j < propElement.childNodes.length; j++) {\r\n const childNode = propElement.childNodes[j];\r\n \r\n if (childNode.nodeType === Node.ELEMENT_NODE) {\r\n const element = childNode as Element;\r\n const localName = element.localName;\r\n const namespace = element.namespaceURI;\r\n \r\n // 根据属性类型进行特殊处理\r\n if (localName === WebDAVPropName.RESOURCE_TYPE) {\r\n // 资源类型\r\n if (element.getElementsByTagNameNS(WebDAVNamespace.DAV, 'collection').length > 0) {\r\n result.resourceType = WebDAVResourceType.DIRECTORY;\r\n } else {\r\n result.resourceType = WebDAVResourceType.FILE;\r\n }\r\n } else if (localName === WebDAVPropName.GET_CONTENT_LENGTH) {\r\n // 内容长度\r\n result.size = parseInt(element.textContent || '0', 10);\r\n } else if (localName === WebDAVPropName.GET_LAST_MODIFIED) {\r\n // 最后修改时间\r\n result.lastModified = new Date(element.textContent || '');\r\n } else if (localName === WebDAVPropName.CREATION_DATE) {\r\n // 创建时间\r\n result.createdAt = new Date(element.textContent || '');\r\n } else if (localName === WebDAVPropName.DISPLAY_NAME) {\r\n // 显示名称\r\n result.displayName = element.textContent || '';\r\n } else if (localName === WebDAVPropName.GET_CONTENT_TYPE) {\r\n // 内容类型\r\n result.mimeType = element.textContent || '';\r\n } else if (localName === WebDAVPropName.ETAG) {\r\n // ETag\r\n result.etag = element.textContent || '';\r\n } else {\r\n // 其他属性\r\n const key = `${namespace || ''}:${localName}`;\r\n result[key] = element.textContent || '';\r\n }\r\n }\r\n }\r\n }\r\n \r\n return result;\r\n}\r\n\r\n/**\r\n * 解析 WebDAV 多状态响应\r\n * @param xml XML 文档\r\n * @returns 解析后的响应数组\r\n */\r\nexport function parseMultiStatus(xml: Document): Array<{\r\n href: string;\r\n status?: string;\r\n statusCode?: number;\r\n properties: Record<string, any>;\r\n}> {\r\n const result: Array<{\r\n href: string;\r\n status?: string;\r\n statusCode?: number;\r\n properties: Record<string, any>;\r\n }> = [];\r\n \r\n // 查找所有 response 元素\r\n const responseElements = xml.getElementsByTagNameNS(WebDAVNamespace.DAV, 'response');\r\n \r\n for (let i = 0; i < responseElements.length; i++) {\r\n const responseElement = responseElements[i];\r\n \r\n // 获取 href\r\n const hrefElement = responseElement.getElementsByTagNameNS(WebDAVNamespace.DAV, 'href')[0];\r\n const href = hrefElement ? decodeURIComponent(hrefElement.textContent || '') : '';\r\n \r\n // 获取状态\r\n const statusElement = responseElement.getElementsByTagNameNS(WebDAVNamespace.DAV, 'status')[0];\r\n const status = statusElement ? statusElement.textContent || '' : undefined;\r\n \r\n // 解析状态码\r\n let statusCode: number | undefined;\r\n if (status) {\r\n const match = status.match(/HTTP\\/\\d+\\.\\d+\\s+(\\d+)/);\r\n if (match) {\r\n statusCode = parseInt(match[1], 10);\r\n }\r\n }\r\n \r\n // 解析属性\r\n const properties = parseWebDAVProperties(responseElement as unknown as Document);\r\n \r\n result.push({\r\n href,\r\n status,\r\n statusCode,\r\n properties,\r\n });\r\n }\r\n \r\n return result;\r\n}\r\n\r\n/**\r\n * 创建 PROPFIND 请求体\r\n * @param props 要查询的属性\r\n * @returns XML 字符串\r\n */\r\nexport function createPropfindXml(props: string[] = []): string {\r\n if (props.length === 0) {\r\n // 查询所有属性\r\n return `<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n<d:propfind xmlns:d=\"${WebDAVNamespace.DAV}\">\r\n <d:allprop/>\r\n</d:propfind>`;\r\n } else {\r\n // 查询指定属性\r\n const propXml = props.map(prop => `<d:${prop}/>`).join('');\r\n \r\n return `<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n<d:propfind xmlns:d=\"${WebDAVNamespace.DAV}\">\r\n <d:prop>\r\n ${propXml}\r\n </d:prop>\r\n</d:propfind>`;\r\n }\r\n}\r\n\r\n/**\r\n * 创建 PROPPATCH 请求体\r\n * @param props 要设置的属性\r\n * @returns XML 字符串\r\n */\r\nexport function createProppatchXml(props: Record<string, string>): string {\r\n const propXml = Object.entries(props)\r\n .map(([key, value]) => `<d:${key}>${value}</d:${key}>`)\r\n .join('');\r\n \r\n return `<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n<d:propertyupdate xmlns:d=\"${WebDAVNamespace.DAV}\">\r\n <d:set>\r\n <d:prop>\r\n ${propXml}\r\n </d:prop>\r\n </d:set>\r\n</d:propertyupdate>`;\r\n}\r\n\r\n/**\r\n * 将 WebDAV 属性转换为 Stats 对象\r\n * @param href 资源路径\r\n * @param properties 属性\r\n * @returns Stats 对象\r\n */\r\nexport function propertiesToStats(href: string, properties: Record<string, any>): Stats {\r\n const name = getBasename(href);\r\n const isDirectory = properties.resourceType === WebDAVResourceType.DIRECTORY;\r\n \r\n return {\r\n isDirectory,\r\n isFile: !isDirectory,\r\n size: properties.size || 0,\r\n createdAt: properties.createdAt || new Date(),\r\n lastModified: properties.modifiedAt || new Date(),\r\n name,\r\n path: href,\r\n mimeType: properties.mimeType,\r\n etag: properties.etag,\r\n ...properties,\r\n };\r\n}\r\n\r\n/**\r\n * 解析 XML 字符串\r\n * @param xmlString XML 字符串\r\n * @returns XML 文档\r\n */\r\nexport function parseXml(xmlString: string): Document {\r\n if (typeof DOMParser !== 'undefined') {\r\n // 浏览器环境\r\n const parser = new DOMParser();\r\n return parser.parseFromString(xmlString, 'application/xml');\r\n } else if (typeof window === 'undefined') {\r\n // Node.js 环境\r\n try {\r\n // 尝试使用 xmldom\r\n const { DOMParser } = require('xmldom');\r\n return new DOMParser().parseFromString(xmlString, 'application/xml');\r\n } catch (error) {\r\n const errorMessage = error instanceof Error ? error.message : String(error);\r\n throw new WebDAVError(`无法解析 XML: ${errorMessage}。在 Node.js 环境中,请安装 xmldom 包。`);\r\n }\r\n } else {\r\n throw new WebDAVError('无法解析 XML。未找到 XML 解析器。');\r\n }\r\n}\r\n\r\n/**\r\n * 检查响应是否成功\r\n * @param response 响应对象\r\n * @returns 是否成功\r\n */\r\nexport function isSuccessStatus(status: number): boolean {\r\n return status >= 200 && status < 300;\r\n}\r\n\r\n/**\r\n * 从 URL 中提取路径\r\n * @param url URL\r\n * @param baseUrl 基础 URL\r\n * @returns 路径\r\n */\r\nexport function extractPathFromUrl(url: string, baseUrl: string): string {\r\n // 移除基础 URL\r\n if (url.startsWith(baseUrl)) {\r\n url = url.slice(baseUrl.length);\r\n }\r\n \r\n // 解码 URL\r\n url = decodeURIComponent(url);\r\n \r\n // 规范化路径\r\n return normalizePath(url);\r\n}\r\n\r\n/**\r\n * 创建超时 Promise\r\n * @param ms 超时时间(毫秒)\r\n * @returns Promise\r\n */\r\nexport function createTimeoutPromise(ms: number): Promise<never> {\r\n return new Promise((_, reject) => {\r\n setTimeout(() => {\r\n reject(new WebDAVError(`请求超时(${ms}ms)`));\r\n }, ms);\r\n });\r\n}\r\n\r\n/**\r\n * 创建 AbortController\r\n * @returns AbortController 或 undefined\r\n */\r\nexport function createAbortController(): AbortController | undefined {\r\n if (typeof AbortController !== 'undefined') {\r\n return new AbortController();\r\n }\r\n return undefined;\r\n}\r\n\r\n/**\r\n * 检查是否支持 Blob\r\n * @returns 是否支持\r\n */\r\nexport function isBlobSupported(): boolean {\r\n return typeof Blob !== 'undefined';\r\n}\r\n\r\n/**\r\n * 检查是否支持 ArrayBuffer\r\n * @returns 是否支持\r\n */\r\nexport function isArrayBufferSupported(): boolean {\r\n return typeof ArrayBuffer !== 'undefined';\r\n}\r\n\r\n/**\r\n * 将数据转换为 ArrayBuffer\r\n * @param data 数据\r\n * @returns ArrayBuffer\r\n */\r\nexport async function toArrayBuffer(data: string | ArrayBuffer | Blob): Promise<ArrayBuffer> {\r\n if (data instanceof ArrayBuffer) {\r\n return data;\r\n } else if (typeof Blob !== 'undefined' && data instanceof Blob) {\r\n return await data.arrayBuffer();\r\n } else if (typeof data === 'string') {\r\n if (typeof TextEncoder !== 'undefined') {\r\n return new TextEncoder().encode(data).buffer;\r\n } else if (typeof Buffer !== 'undefined') {\r\n return Buffer.from(data).buffer;\r\n } else {\r\n throw new WebDAVError('无法将字符串转换为 ArrayBuffer');\r\n }\r\n } else {\r\n throw new WebDAVError('不支持的数据类型');\r\n }\r\n}\r\n\r\n/**\r\n * 将 ArrayBuffer 转换为字符串\r\n * @param buffer ArrayBuffer\r\n * @param encoding 编码\r\n * @returns 字符串\r\n */\r\nexport function arrayBufferToString(buffer: ArrayBuffer, encoding = 'utf-8'): string {\r\n if (typeof TextDecoder !== 'undefined') {\r\n // 浏览器环境\r\n const decoder = new TextDecoder(encoding);\r\n return decoder.decode(buffer);\r\n } else if (typeof Buffer !== 'undefined') {\r\n // Node.js 环境\r\n return Buffer.from(buffer).toString(encoding as BufferEncoding);\r\n } else {\r\n throw new WebDAVError('无法将 ArrayBuffer 转换为字符串,当前环境不支持 TextDecoder 或 Buffer');\r\n }\r\n}\r\n\r\n/**\r\n * 根据文件名获取Content-Type\r\n * @param filename 文件名\r\n * @returns Content-Type字符串\r\n */\r\nexport function getContentType(filename: string): string {\r\n const ext = filename.split('.').pop()?.toLowerCase();\r\n switch (ext) {\r\n case 'txt': return 'text/plain';\r\n case 'html': case 'htm': return 'text/html';\r\n case 'json': return 'application/json';\r\n case 'xml': return 'application/xml';\r\n case 'jpg': case 'jpeg': return 'image/jpeg';\r\n case 'png': return 'image/png';\r\n case 'gif': return 'image/gif';\r\n case 'pdf': return 'application/pdf';\r\n case 'csv': return 'text/csv';\r\n case 'js': return 'application/javascript';\r\n case 'css': return 'text/css';\r\n case 'zip': return 'application/zip';\r\n default: return 'application/octet-stream';\r\n }\r\n}\r\n\r\n// 获取路径的父目录\r\nexport function getDirFromPath(path: string): string {\r\n if (!path || path === '/') return '/';\r\n const normalized = path.replace(/\\/+$/, '');\r\n const idx = normalized.lastIndexOf('/');\r\n if (idx <= 0) return '/';\r\n return normalized.slice(0, idx) || '/';\r\n}\r\n\r\n/**\r\n * 解析WebDAV PROPFIND XML响应,返回文件/目录信息数组\r\n * @param xml XML字符串\r\n * @param basePath 基础路径\r\n * @returns Stats[]\r\n */\r\n\r\nexport function parseWebDAVXml(xml: string, basePath: string): Stats[] {\r\n // 简单实现,实际可用 xml2js、fast-xml-parser 等库解析\r\n // 这里只做最基础的兼容,建议根据实际WebDAV响应完善\r\n const decode = require('he').decode;\r\n const result: Stats[] = [];\r\n if (!xml) return result;\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n attributeNamePrefix: \"@_\",\r\n trimValues: true\r\n });\r\n const json = parser.parse(xml);\r\n // 兼容不同大小写的 multistatus/response 属性\r\n function getCaseInsensitive(obj: unknown, ...keys: string[]): unknown {\r\n if (!obj) return undefined;\r\n for (const key of keys) {\r\n if ((obj as Record<string, unknown>)[key] !== undefined) return (obj as Record<string, unknown>)[key];\r\n const found = Object.keys(obj).find(k => k.toLowerCase() === key.toLowerCase());\r\n if (found) return (obj as Record<string, unknown>)[found];\r\n }\r\n return undefined;\r\n }\r\n const multistatus = getCaseInsensitive(json, 'd:multistatus', 'multistatus');\r\n const responses = getCaseInsensitive(multistatus, 'd:response', 'response') || [];\r\n const arr = Array.isArray(responses) ? responses : [responses];\r\n const isBasePathFile = basePath && !basePath.endsWith('/');\r\n for (const item of arr) {\r\n const href = decode(getCaseInsensitive(item, 'd:href', 'href') || '');\r\n const propstat = getCaseInsensitive(item, 'd:propstat', 'propstat', 'd:prop', 'prop') || {};\r\n const prop = getCaseInsensitive(propstat, 'd:prop', 'prop') || propstat;\r\n const resourcetype = getCaseInsensitive(prop, 'd:resourcetype', 'resourcetype');\r\n const collection = resourcetype && getCaseInsensitive(resourcetype, 'd:collection', 'collection');\r\n const isDirectory = !!(resourcetype && collection !== undefined);\r\n\r\n // 如果basePath是文件,则直接取文件名,否则去除basePath前缀\r\n let name: string;\r\n if (isBasePathFile) {\r\n name = href.split('/').filter(Boolean).pop() || '';\r\n } else {\r\n name = href.replace(basePath, '').replace(/^\\//, '').replace(/\\/$/, '');\r\n }\r\n if (!name) continue;\r\n result.push({\r\n path: href,\r\n name,\r\n isDirectory,\r\n isFile: !isDirectory,\r\n size: parseInt(String(getCaseInsensitive(prop, 'd:getcontentlength', 'getcontentlength') ?? '0'), 10),\r\n lastModified: getCaseInsensitive(prop, 'd:getlastmodified', 'getlastmodified')\r\n ? new Date(String(getCaseInsensitive(prop, 'd:getlastmodified', 'getlastmodified')))\r\n : undefined,\r\n });\r\n }\r\n return result;\r\n}\r\n\r\n/**\r\n * 拼接URL路径,自动处理斜杠\r\n * @param base 基础URL\r\n * @param paths 追加的路径\r\n * @returns 拼接后的URL\r\n */\r\nexport function joinUrl(base: string, ...paths: string[]): string {\r\n let url = base;\r\n let basePath = '';\r\n try {\r\n const u = new URL(base);\r\n basePath = u.pathname.replace(/\\/+$/, '');\r\n } catch {\r\n basePath = base.startsWith('/') ? base.replace(/\\/+$/, '') : '';\r\n }\r\n // 只处理第一个 path,只去除与 basePath 第一部分相同的部分\r\n if (paths.length > 0 && basePath) {\r\n let p = paths[0];\r\n if (p) {\r\n // 取 basePath 的第一部分\r\n const baseFirst = basePath.split('/').filter(Boolean)[0];\r\n const pParts = p.split('/').filter(Boolean);\r\n if (baseFirst && pParts[0] === baseFirst) {\r\n pParts.shift();\r\n p = pParts.join('/');\r\n if (p && !p.startsWith('/')) p = '/' + p;\r\n paths[0] = p;\r\n }\r\n }\r\n }\r\n for (const p of paths) {\r\n if (!p) continue;\r\n if (!url.endsWith('/')) url += '/';\r\n url += p.startsWith('/') ? p.slice(1) : p;\r\n }\r\n url = url.replace(/([^:]\\/)\\/+/g, '$1');\r\n return url;\r\n}","/**\r\n * WebDAV文件系统实现\r\n */\r\n\r\nimport {\r\n WebDAVOptions,\r\n Stats,\r\n ReadFileOptions,\r\n WriteFileOptions,\r\n ReaddirOptions,\r\n MkdirOptions,\r\n WebDAVResult,\r\n} from './types';\r\n\r\nimport { WebDAVFileSystem } from './WebDAVFileSystem';\r\n\r\nimport {\r\n WebDAVError,\r\n NotFoundError,\r\n AuthenticationError,\r\n AuthorizationError,\r\n FileExistsError,\r\n NetworkError,\r\n TimeoutError,\r\n ArgumentError,\r\n ServerError,\r\n} from './errors';\r\n\r\nimport {\r\n normalizePath,\r\n joinUrl,\r\n getDirFromPath,\r\n parseWebDAVXml,\r\n createBasicAuthHeader,\r\n getContentType,\r\n} from './utils';\r\n\r\n/**\r\n * WebDAV文件系统实现类\r\n */\r\nexport class WebDAVFS implements WebDAVFileSystem {\r\n private baseUrl: string;\r\n private auth?: { username: string; password: string };\r\n private timeout: number;\r\n private headers: Record<string, string>;\r\n\r\n /**\r\n * 创建WebDAV文件系统实例\r\n * @param options WebDAV选项\r\n */\r\n constructor(options: WebDAVOptions) {\r\n if (!options.baseUrl) {\r\n throw new ArgumentError('必须提供baseUrl');\r\n }\r\n\r\n this.baseUrl = options.baseUrl.endsWith('/') \r\n ? options.baseUrl.slice(0, -1) \r\n : options.baseUrl;\r\n if (options.username && options.password) {\r\n this.auth = { username: options.username, password: options.password };\r\n } else {\r\n this.auth = undefined;\r\n }\r\n this.timeout = options.timeout || 30000;\r\n this.headers = options.headers || {};\r\n }\r\n\r\n /**\r\n * 创建请求头\r\n * @param customHeaders 自定义请求头\r\n * @returns 合并后的请求头\r\n */\r\n private createHeaders(customHeaders?: Record<string, string>): Record<string, string> {\r\n const headers: Record<string, string> = {\r\n ...this.headers,\r\n ...customHeaders,\r\n };\r\n\r\n // 添加认证头\r\n if (this.auth) {\r\n headers['Authorization'] = createBasicAuthHeader(this.auth.username, this.auth.password);\r\n }\r\n\r\n return headers;\r\n }\r\n\r\n /**\r\n * 执行HTTP请求\r\n * @param method HTTP方法\r\n * @param path 请求路径\r\n * @param options 请求选项\r\n * @returns 响应对象\r\n */\r\n private async request(\r\n method: string,\r\n path: string,\r\n options: {\r\n headers?: Record<string, string>;\r\n body?: string | ArrayBuffer | Blob | Buffer;\r\n responseType?: 'text' | 'arraybuffer' | 'blob' | 'json';\r\n } = {}\r\n ): Promise<{ data: any; status: number; headers: Record<string, string> }> {\r\n const normalizedPath = normalizePath(path);\r\n const url = joinUrl(this.baseUrl, normalizedPath);\r\n const headers = this.createHeaders(options.headers);\r\n \r\n // 创建AbortController用于超时处理\r\n const controller = typeof AbortController !== 'undefined' ? new AbortController() : undefined;\r\n const timeoutId = controller ? setTimeout(() => controller.abort(), this.timeout) : undefined;\r\n \r\n try {\r\n // 使用fetch API(浏览器和现代Node.js都支持)\r\n const response = await fetch(url, {\r\n method,\r\n headers,\r\n body: options.body,\r\n signal: controller?.signal,\r\n credentials: 'include',\r\n });\r\n \r\n // 清除超时\r\n if (timeoutId) clearTimeout(timeoutId);\r\n \r\n // 提取响应头\r\n const responseHeaders: Record<string, string> = {};\r\n response.headers.forEach((value, key) => {\r\n responseHeaders[key.toLowerCase()] = value;\r\n });\r\n \r\n // 根据请求的responseType处理响应数据\r\n let data;\r\n if (options.responseType === 'arraybuffer') {\r\n data = await response.arrayBuffer();\r\n } else if (options.responseType === 'blob') {\r\n data = await response.blob();\r\n } else if (options.responseType === 'json') {\r\n data = await response.json();\r\n } else {\r\n data = await response.text();\r\n }\r\n \r\n return {\r\n data,\r\n status: response.status,\r\n headers: responseHeaders,\r\n };\r\n } catch (error: unknown) {\r\n // 清除超时\r\n if (timeoutId) clearTimeout(timeoutId);\r\n \r\n // 处理错误\r\n if (error instanceof Error && error.name === 'AbortError') {\r\n throw new TimeoutError(`请求超时: ${url}`);\r\n } else {\r\n const errorMessage = error instanceof Error ? error.message : String(error);\r\n throw new NetworkError(error instanceof Error ? error : new Error(String(error)), `网络错误: ${errorMessage}`);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 处理响应错误\r\n * @param status HTTP状态码\r\n * @param path 请求路径\r\n * @param error 原始错误\r\n */\r\n private handleResponseError(status: number, path: string, error?: Error): never {\r\n switch (status) {\r\n case 401:\r\n throw new AuthenticationError(`认证失败: ${path}`);\r\n case 403:\r\n throw new AuthorizationError(`无权限访问: ${path}`);\r\n case 404:\r\n throw new NotFoundError(`资源不存在: ${path}`);\r\n case 409:\r\n throw new FileExistsError(`资源已存在: ${path}`);\r\n default:\r\n throw new ServerError(`服务器错误 (${status}): ${path}`, status);\r\n }\r\n }\r\n\r\n /**\r\n * 读取文件内容\r\n * @param path 文件路径\r\n * @param options 读取选项\r\n * @returns 文件内容\r\n */\r\n async readFile(path: string, options: ReadFileOptions = {}): Promise<Buffer | string> {\r\n const normalizedPath = normalizePath(path);\r\n \r\n try {\r\n const response = await this.request('GET', normalizedPath, {\r\n headers: options.headers,\r\n responseType: 'arraybuffer',\r\n });\r\n \r\n if (response.status >= 400) {\r\n this.handleResponseError(response.status, normalizedPath);\r\n }\r\n \r\n // 创建Buffer\r\n const arrayBuffer = response.data as ArrayBuffer;\r\n const uint8Array = new Uint8Array(arrayBuffer);\r\n const buffer = Buffer.from(uint8Array);\r\n\r\n // 根据编码返回字符串或Buffer\r\n if (options.encoding) {\r\n return buffer.toString(options.encoding as BufferEncoding);\r\n } else {\r\n return buffer;\r\n }\r\n } catch (error: unknown) {\r\n if (error instanceof WebDAVError) {\r\n throw error;\r\n }\r\n throw new WebDAVError(`读取文件失败: ${normalizedPath}`, undefined, error);\r\n }\r\n }\r\n\r\n /**\r\n * 写入文件内容\r\n * @param path 文件路径\r\n * @param data 文件内容\r\n * @param options 写入选项\r\n * @returns 操作结果\r\n */\r\n async writeFile(\r\n path: string,\r\n data: Buffer | string,\r\n options: WriteFileOptions = {}\r\n ): Promise<WebDAVResult> {\r\n const normalizedPath = normalizePath(path);\r\n const getFilenameFromPath = (p: string) => {\r\n const parts = p.split('/');\r\n return parts[parts.length - 1] || '';\r\n };\r\n const contentType = options.contentType || getContentType(getFilenameFromPath(normalizedPath));\r\n // 检查文件是否存在(如果不允许覆盖)\r\n if (options.overwrite === false) {\r\n const exists = await this.exists(normalizedPath);\r\n if (exists) {\r\n throw new FileExistsError(normalizedPath);\r\n }\r\n }\r\n \r\n // 准备请求头\r\n const headers = {\r\n 'Content-Type': contentType,\r\n };\r\n \r\n try {\r\n const response = await this.request('PUT', normalizedPath, {\r\n headers,\r\n body: data,\r\n });\r\n \r\n if (response.status >= 400) {\r\n this.handleResponseError(response.status, normalizedPath);\r\n }\r\n \r\n return {\r\n success: response.status >= 200 && response.status < 300,\r\n statusCode: response.status,\r\n };\r\n } catch (error: unknown) {\r\n if (error instanceof WebDAVError) {\r\n throw error;\r\n }\r\n throw new WebDAVError(`写入文件失败: ${normalizedPath}`);\r\n }\r\n }\r\n\r\n /**\r\n * 删除文件\r\n * @param path 文件路径\r\n * @returns 操作结果\r\n */\r\n async deleteFile(path: string): Promise<WebDAVResult> {\r\n const normalizedPath = normalizePath(path);\r\n \r\n try {\r\n // 确保是文件而不是目录\r\n const stat = await this.stat(normalizedPath);\r\n if (stat.isDirectory) {\r\n throw new ArgumentError(`路径指向一个目录,请使用rmdir方法: ${normalizedPath}`);\r\n }\r\n \r\n const response = await this.request('DELETE', normalizedPath);\r\n \r\n if (response.status >= 400) {\r\n this.handleResponseError(response.status, normalizedPath);\r\n }\r\n \r\n return {\r\n success: response.status >= 200 && response.status < 300,\r\n statusCode: response.status,\r\n };\r\n } catch (error: unknown) {\r\n if (error instanceof WebDAVError) {\r\n throw error;\r\n }\r\n throw new WebDAVError(`删除文件失败: ${normalizedPath}`);\r\n }\r\n }\r\n\r\n /**\r\n * 读取目录内容\r\n * @param path 目录路径\r\n * @param options 读取选项\r\n * @returns 文件统计信息数组\r\n */\r\n async readDir(path: string, options: ReaddirOptions = {}): Promise<Stats[]> {\r\n const normalizedPath = normalizePath(path);\r\n \r\n try {\r\n // 准备PROPFIND请求\r\n const headers = {\r\n 'Depth': options.recursive ? 'infinity' : '1',\r\n 'Content-Type': 'application/xml',\r\n };\r\n \r\n const response = await this.request('PROPFIND', normalizedPath, {\r\n headers,\r\n });\r\n \r\n if (response.status >= 400) {\r\n this.handleResponseError(response.status, normalizedPath);\r\n }\r\n \r\n // 解析XML响应\r\n const files = parseWebDAVXml(response.data, normalizedPath);\r\n \r\n // 过滤结果\r\n const result = files.filter(file => {\r\n // 排除当前目录\r\n if (file.path === normalizedPath) {\r\n return false;\r\n }\r\n \r\n // 处理隐藏文件\r\n if (!options.includeHidden && file.name.startsWith('.')) {\r\n return false;\r\n }\r\n \r\n return true;\r\n });\r\n \r\n return result;\r\n } catch (error: unknown) {\r\n if (error instanceof WebDAVError) {\r\n throw error;\r\n }\r\n throw new WebDAVError(`读取目录失败: ${normalizedPath}`);\r\n }\r\n }\r\n\r\n /**\r\n * 创建目录\r\n * @param path 目录路径\r\n * @param options 创建选项\r\n * @returns 操作结果\r\n */\r\n async mkdir(path: string, options: MkdirOptions = {}): Promise<void> {\r\n const normalizedPath = normalizePath(path);\r\n \r\n try {\r\n // 检查目录是否已存在\r\n try {\r\n const stat = await this.stat(normalizedPath);\r\n if (stat.isDirectory) {\r\n return; // 目录已存在,视为成功\r\n }\r\n throw new FileExistsError(normalizedPath); // 路径存在但不是目录\r\n } catch (error) {\r\n if (!(error instanceof NotFoundError)) {\r\n throw error;\r\n }\r\n // 目录不存在,继续创建\r\n }\r\n \r\n // 如果需要递归创建父目录\r\n if (options.recursive !== false) {\r\n const parentDir = getDirFromPath(normalizedPath);\r\n if (parentDir !== '/' && parentDir !== normalizedPath) {\r\n try {\r\n await this.stat(parentDir);\r\n } catch (error) {\r\n if (error instanceof NotFoundError) {\r\n // 递归创建父目录\r\n await this.mkdir(parentDir, options);\r\n } else {\r\n throw error;\r\n }\r\n }\r\n }\r\n }\r\n \r\n const response = await this.request('MKCOL', normalizedPath);\r\n \r\n if (response.status >= 400) {\r\n this.handleResponseError(response.status, normalizedPath);\r\n }\r\n \r\n return;\r\n } catch (error: unknown) {\r\n if (error instanceof WebDAVError) {\r\n throw error;\r\n }\r\n throw new WebDAVError(`创建目录失败: ${normalizedPath}`);\r\n }\r\n }\r\n\r\n /**\r\n * 删除文件或目录,行为类似于 Node.js 的 fs.rm\r\n * @param path 路径\r\n * @param options { recursive?: boolean, force?: boolean }\r\n */\r\n async rm(path: string, options?: { recursive?: boolean, force?: boolean }) {\r\n const { recursive = false, force = false } = options || {};\r\n try {\r\n const stat = await this.stat(path);\r\n if (stat.isDirectory) {\r\n if (recursive) {\r\n // 递归删除目录内容\r\n const files = await this.readDir(path);\r\n for (const file of files) {\r\n const childPath = path.replace(/\\/$/, '') + '/' + file.name;\r\n await this.rm(childPath, { recursive: true, force });\r\n }\r\n } else {\r\n // 非递归时,目录必须为空\r\n const files = await this.readDir(path);\r\n if (files.length > 0) {\r\n throw new WebDAVError(`Directory not empty: ${path}`);\r\n }\r\n }\r\n }\r\n // 删除文件或空目录\r\n await this._delete(path);\r\n } catch (err: unknown) {\r\n if (force && (\r\n (err instanceof Error && 'code' in err && err.code === 'ENOENT') || \r\n (err instanceof Error && 'status' in err && err.status === 404)\r\n )) {\r\n // force=true 时忽略不存在\r\n return;\r\n }\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * 兼容旧的 rmdir 方法,内部重定向到 rm\r\n * @deprecated 请使用 rm\r\n */\r\n async rmdir(path: string, options?: boolean | { recursive?: boolean, force?: boolean }) {\r\n // 兼容 boolean 递归参数\r\n let opts: { recursive?: boolean, force?: boolean } = {};\r\n if (typeof options === 'boolean') {\r\n opts.recursive = options;\r\n } else if (typeof options === 'object' && options !== null) {\r\n opts = options;\r\n }\r\n return this.rm(path, opts);\r\n }\r\n\r\n /**\r\n * 删除文件或目录\r\n * @param path 文件或目录路径\r\n * @returns 操作结果\r\n */\r\n private async _delete(path: string) {\r\n // 实现 WebDAV DELETE 请求\r\n const normalizedPath = normalizePath(path);\r\n \r\n try {\r\n const response = await this.request('DELETE', normalizedPath);\r\n \r\n if (response.status >= 400) {\r\n this.handleResponseError(response.status, normalizedPath);\r\n }\r\n \r\n return {\r\n success: response.status >= 200 && response.status < 300,\r\n statusCode: response.status,\r\n };\r\n } catch (error: unknown) {\r\n if (error instanceof WebDAVError) {\r\n throw error;\r\n }\r\n throw new WebDAVError(`删除失败: ${normalizedPath}`, undefined, error);\r\n }\r\n }\r\n\r\n /**\r\n * 获取文件或目录的统计信息\r\n * @param path 文件或目录路径\r\n * @returns 文件统计信息\r\n */\r\n async stat(path: string): Promise<Stats> {\r\n const normalizedPath = normalizePath(path);\r\n \r\n try {\r\n // 准备PROPFIND请求\r\n const headers = {\r\n 'Depth': '0',\r\n 'Content-Type': 'application/xml',\r\n };\r\n \r\n const response = await this.request('PROPFIND', normalizedPath, {\r\n headers,\r\n });\r\n \r\n if (response.status === 404) {\r\n throw new NotFoundError(normalizedPath);\r\n } else if (response.status >= 400) {\r\n this.handleResponseError(response.status, normalizedPath);\r\n }\r\n \r\n // 解析XML响应\r\n const files = parseWebDAVXml(response.data, normalizedPath);\r\n \r\n if (files.length === 0) {\r\n throw new NotFoundError(normalizedPath);\r\n }\r\n \r\n const stat = files[0];\r\n \r\n return stat;\r\n } catch (error: unknown) {\r\n if (error instanceof WebDAVError) {\r\n throw error;\r\n }\r\n throw new WebDAVError(`获取文件信息失败: ${normalizedPath}`, undefined, error);\r\n }\r\n }\r\n\r\n /**\r\n * 检查文件或目录是否存在\r\n * @param path 文件或目录路径\r\n * @returns 是否存在\r\n */\r\n async exists(path: string): Promise<boolean> {\r\n try {\r\n await this.stat(path);\r\n return true;\r\n } catch (error) {\r\n if (error instanceof NotFoundError) {\r\n return false;\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * 复制文件或目录\r\n * @param source 源路径\r\n * @param destination 目标路径\r\n * @param overwrite 是否覆盖已存在的文件\r\n * @returns 操作结果\r\n */\r\n async copy(source: string, destination: string, overwrite = true): Promise<WebDAVResult> {\r\n const normalizedSource = normalizePath(source);\r\n const normalizedDestination = normalizePath(destination);\r\n \r\n try {\r\n // 检查源文件是否存在\r\n await this.stat(normalizedSource);\r\n \r\n // 检查目标文件是否存在(如果不允许覆盖)\r\n if (!overwrite) {\r\n const exists = await this.exists(normalizedDestination);\r\n if (exists) {\r\n throw new FileExistsError(normalizedDestination);\r\n }\r\n }\r\n \r\n // 准备COPY请求\r\n const headers = {\r\n 'Destination': joinUrl(this.baseUrl, normalizedDestination),\r\n 'Overwrite': overwrite ? 'T' : 'F',\r\n };\r\n \r\n const response = await this.request('COPY', normalizedSource, {\r\n headers,\r\n });\r\n \r\n if (response.status >= 400) {\r\n this.handleResponseError(response.status, normalizedSource);\r\n }\r\n \r\n return {\r\n success: response.status >= 200 && response.status < 300,\r\n statusCode: response.status,\r\n };\r\n } catch (error: unknown) {\r\n if (error instanceof WebDAVError) {\r\n throw error;\r\n }\r\n throw new WebDAVError(`复制失败: ${normalizedSource} -> ${normalizedDestination}`, undefined, error);\r\n }\r\n }\r\n\r\n /**\r\n * 移动文件或目录\r\n * @param source 源路径\r\n * @param destination 目标路径\r\n * @param overwrite 是否覆盖已存在的文件\r\n * @returns 操作结果\r\n */\r\n async move(source: string, destination: string, overwrite = true): Promise<WebDAVResult> {\r\n const normalizedSource = normalizePath(source);\r\n const normalizedDestination = normalizePath(destination);\r\n \r\n try {\r\n // 检查源文件是否存在\r\n await this.stat(normalizedSource);\r\n \r\n // 检查目标文件是否存在(如果不允许覆盖)\r\n if (!overwrite) {\r\n const exists = await this.exists(normalizedDestination);\r\n if (exists) {\r\n throw new FileExistsError(normalizedDestination);\r\n }\r\n }\r\n \r\n // 准备MOVE请求\r\n const headers = {\r\n 'Destination': joinUrl(this.baseUrl, normalizedDestination),\r\n 'Overwrite': overwrite ? 'T' : 'F',\r\n };\r\n \r\n const response = await this.request('MOVE', normalizedSource, {\r\n headers,\r\n });\r\n \r\n if (response.status >= 400) {\r\n this.handleResponseError(response.status, normalizedSource);\r\n }\r\n \r\n return {\r\n success: response.status >= 200 && response.status < 300,\r\n statusCode: response.status,\r\n };\r\n } catch (error: unknown) {\r\n if (error instanceof WebDAVError) {\r\n throw error;\r\n }\r\n throw new WebDAVError(`移动失败: ${normalizedSource} -> ${normalizedDestination}`, undefined, error);\r\n }\r\n }\r\n\r\n /**\r\n * 删除文件(fs/unlink 兼容方法)\r\n * @param path 文件路径\r\n */\r\n async unlink(path: string): Promise<void> {\r\n await this.deleteFile(path);\r\n }\r\n}","import { WebDAVOptions } from './types';\r\nimport { WebDAVFS } from './webdav-fs';\r\nimport { WebDAVFileSystem } from './WebDAVFileSystem';\r\n\r\n/**\r\n * 创建 WebDAV 文件系统实例(工厂函数)\r\n * @param options WebDAV 配置选项\r\n * @returns WebDAVFileSystem 实例\r\n */\r\nexport function createWebDAVFileSystem(options: WebDAVOptions): WebDAVFileSystem {\r\n return new WebDAVFS(options);\r\n}"],"mappings":"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","names":[]}