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.
package/dist/index.js ADDED
@@ -0,0 +1,768 @@
1
+ // zen-fs-webdav - https://github.com/weijia/zen-fs-webdav
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ WebDAVError: () => WebDAVError,
25
+ createWebDAVFileSystem: () => createWebDAVFileSystem
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/errors.ts
30
+ var WebDAVError = class _WebDAVError extends Error {
31
+ constructor(message, status, cause) {
32
+ super(message);
33
+ this.name = "WebDAVError";
34
+ this.status = status;
35
+ this.cause = cause;
36
+ Object.setPrototypeOf(this, _WebDAVError.prototype);
37
+ }
38
+ toString() {
39
+ if (typeof this.status === "number") {
40
+ return `${this.name}: ${this.message} (Status: ${this.status})`;
41
+ }
42
+ return `${this.name}: ${this.message}`;
43
+ }
44
+ static fromResponse(response, message) {
45
+ const msg = message ? `${message}: ${response.status} ${response.statusText}` : `${response.status} ${response.statusText}`;
46
+ return new _WebDAVError(msg, response.status);
47
+ }
48
+ static fromError(error) {
49
+ if (error instanceof _WebDAVError) {
50
+ return error;
51
+ }
52
+ if (error instanceof Error) {
53
+ const msg = error.message || "Unknown WebDAV error";
54
+ return new _WebDAVError(msg, void 0, error);
55
+ }
56
+ return new _WebDAVError("Unknown WebDAV error");
57
+ }
58
+ };
59
+ var NotFoundError = class _NotFoundError extends WebDAVError {
60
+ constructor(path) {
61
+ super(`\u6587\u4EF6\u672A\u627E\u5230: ${path}`, 404);
62
+ this.name = "NotFoundError";
63
+ Object.setPrototypeOf(this, _NotFoundError.prototype);
64
+ }
65
+ };
66
+ var AuthenticationError = class _AuthenticationError extends WebDAVError {
67
+ constructor(message = "\u8BA4\u8BC1\u5931\u8D25") {
68
+ super(message, 401);
69
+ this.name = "AuthenticationError";
70
+ Object.setPrototypeOf(this, _AuthenticationError.prototype);
71
+ }
72
+ };
73
+ var AuthorizationError = class _AuthorizationError extends WebDAVError {
74
+ constructor(message = "\u6743\u9650\u88AB\u62D2\u7EDD") {
75
+ super(message, 403);
76
+ this.name = "AuthorizationError";
77
+ Object.setPrototypeOf(this, _AuthorizationError.prototype);
78
+ }
79
+ };
80
+ var ServerError = class _ServerError extends WebDAVError {
81
+ constructor(message = "\u670D\u52A1\u5668\u9519\u8BEF", statusCode = 500) {
82
+ super(message, statusCode);
83
+ this.name = "ServerError";
84
+ Object.setPrototypeOf(this, _ServerError.prototype);
85
+ }
86
+ };
87
+ var FileExistsError = class _FileExistsError extends WebDAVError {
88
+ constructor(path) {
89
+ super(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${path}`, 412);
90
+ this.name = "FileExistsError";
91
+ Object.setPrototypeOf(this, _FileExistsError.prototype);
92
+ }
93
+ };
94
+ var TimeoutError = class _TimeoutError extends WebDAVError {
95
+ constructor(message = "\u8FDE\u63A5\u8D85\u65F6") {
96
+ super(message, 408);
97
+ this.name = "TimeoutError";
98
+ Object.setPrototypeOf(this, _TimeoutError.prototype);
99
+ }
100
+ };
101
+ var NetworkError = class _NetworkError extends WebDAVError {
102
+ constructor(originalError, message = "\u7F51\u7EDC\u9519\u8BEF") {
103
+ super(originalError ? `${message}: ${originalError.message}` : message, 0, originalError);
104
+ this.name = "NetworkError";
105
+ Object.setPrototypeOf(this, _NetworkError.prototype);
106
+ }
107
+ };
108
+ var ArgumentError = class _ArgumentError extends WebDAVError {
109
+ constructor(message) {
110
+ super(`\u65E0\u6548\u53C2\u6570: ${message}`);
111
+ this.name = "ArgumentError";
112
+ Object.setPrototypeOf(this, _ArgumentError.prototype);
113
+ }
114
+ };
115
+
116
+ // src/constants.ts
117
+ var WebDAVNamespace = {
118
+ DAV: "DAV:",
119
+ CALDAV: "urn:ietf:params:xml:ns:caldav",
120
+ CARDDAV: "urn:ietf:params:xml:ns:carddav",
121
+ OWNCLOUD: "http://owncloud.org/ns",
122
+ NEXTCLOUD: "http://nextcloud.org/ns"
123
+ };
124
+ var XML_NAMESPACE_PREFIX = {
125
+ "d": WebDAVNamespace.DAV,
126
+ "oc": WebDAVNamespace.OWNCLOUD,
127
+ "nc": WebDAVNamespace.NEXTCLOUD
128
+ };
129
+
130
+ // src/utils.ts
131
+ var import_fast_xml_parser = require("fast-xml-parser");
132
+ function normalizePath(path) {
133
+ if (!path.startsWith("/")) {
134
+ path = "/" + path;
135
+ }
136
+ path = path.replace(/\/+/g, "/");
137
+ if (path.length > 1 && path.endsWith("/")) {
138
+ path = path.slice(0, -1);
139
+ }
140
+ return path;
141
+ }
142
+ function createBasicAuthHeader(username, password) {
143
+ const btoa = (str) => {
144
+ if (typeof window !== "undefined" && window.btoa) {
145
+ return window.btoa(str);
146
+ } else if (typeof Buffer !== "undefined") {
147
+ return Buffer.from(str).toString("base64");
148
+ } else {
149
+ throw new Error("Base64 encoding not available");
150
+ }
151
+ };
152
+ return `Basic ${btoa(`${username}:${password}`)}`;
153
+ }
154
+ function getContentType(filename) {
155
+ const ext = filename.split(".").pop()?.toLowerCase();
156
+ switch (ext) {
157
+ case "txt":
158
+ return "text/plain";
159
+ case "html":
160
+ case "htm":
161
+ return "text/html";
162
+ case "json":
163
+ return "application/json";
164
+ case "xml":
165
+ return "application/xml";
166
+ case "jpg":
167
+ case "jpeg":
168
+ return "image/jpeg";
169
+ case "png":
170
+ return "image/png";
171
+ case "gif":
172
+ return "image/gif";
173
+ case "pdf":
174
+ return "application/pdf";
175
+ case "csv":
176
+ return "text/csv";
177
+ case "js":
178
+ return "application/javascript";
179
+ case "css":
180
+ return "text/css";
181
+ case "zip":
182
+ return "application/zip";
183
+ default:
184
+ return "application/octet-stream";
185
+ }
186
+ }
187
+ function getDirFromPath(path) {
188
+ if (!path || path === "/") return "/";
189
+ const normalized = path.replace(/\/+$/, "");
190
+ const idx = normalized.lastIndexOf("/");
191
+ if (idx <= 0) return "/";
192
+ return normalized.slice(0, idx) || "/";
193
+ }
194
+ function parseWebDAVXml(xml, basePath) {
195
+ const decode = require("he").decode;
196
+ const result = [];
197
+ if (!xml) return result;
198
+ const parser = new import_fast_xml_parser.XMLParser({
199
+ ignoreAttributes: false,
200
+ attributeNamePrefix: "@_",
201
+ trimValues: true
202
+ });
203
+ const json = parser.parse(xml);
204
+ function getCaseInsensitive(obj, ...keys) {
205
+ if (!obj) return void 0;
206
+ for (const key of keys) {
207
+ if (obj[key] !== void 0) return obj[key];
208
+ const found = Object.keys(obj).find((k) => k.toLowerCase() === key.toLowerCase());
209
+ if (found) return obj[found];
210
+ }
211
+ return void 0;
212
+ }
213
+ const multistatus = getCaseInsensitive(json, "d:multistatus", "multistatus");
214
+ const responses = getCaseInsensitive(multistatus, "d:response", "response") || [];
215
+ const arr = Array.isArray(responses) ? responses : [responses];
216
+ const isBasePathFile = basePath && !basePath.endsWith("/");
217
+ for (const item of arr) {
218
+ const href = decode(getCaseInsensitive(item, "d:href", "href") || "");
219
+ const propstat = getCaseInsensitive(item, "d:propstat", "propstat", "d:prop", "prop") || {};
220
+ const prop = getCaseInsensitive(propstat, "d:prop", "prop") || propstat;
221
+ const resourcetype = getCaseInsensitive(prop, "d:resourcetype", "resourcetype");
222
+ const collection = resourcetype && getCaseInsensitive(resourcetype, "d:collection", "collection");
223
+ const isDirectory = !!(resourcetype && collection !== void 0);
224
+ let name;
225
+ if (isBasePathFile) {
226
+ name = href.split("/").filter(Boolean).pop() || "";
227
+ } else {
228
+ name = href.replace(basePath, "").replace(/^\//, "").replace(/\/$/, "");
229
+ }
230
+ if (!name) continue;
231
+ result.push({
232
+ path: href,
233
+ name,
234
+ isDirectory,
235
+ isFile: !isDirectory,
236
+ size: parseInt(String(getCaseInsensitive(prop, "d:getcontentlength", "getcontentlength") ?? "0"), 10),
237
+ lastModified: getCaseInsensitive(prop, "d:getlastmodified", "getlastmodified") ? new Date(String(getCaseInsensitive(prop, "d:getlastmodified", "getlastmodified"))) : void 0
238
+ });
239
+ }
240
+ return result;
241
+ }
242
+ function joinUrl(base, ...paths) {
243
+ let url = base;
244
+ let basePath = "";
245
+ try {
246
+ const u = new URL(base);
247
+ basePath = u.pathname.replace(/\/+$/, "");
248
+ } catch {
249
+ basePath = base.startsWith("/") ? base.replace(/\/+$/, "") : "";
250
+ }
251
+ if (paths.length > 0 && basePath) {
252
+ let p = paths[0];
253
+ if (p) {
254
+ const baseFirst = basePath.split("/").filter(Boolean)[0];
255
+ const pParts = p.split("/").filter(Boolean);
256
+ if (baseFirst && pParts[0] === baseFirst) {
257
+ pParts.shift();
258
+ p = pParts.join("/");
259
+ if (p && !p.startsWith("/")) p = "/" + p;
260
+ paths[0] = p;
261
+ }
262
+ }
263
+ }
264
+ for (const p of paths) {
265
+ if (!p) continue;
266
+ if (!url.endsWith("/")) url += "/";
267
+ url += p.startsWith("/") ? p.slice(1) : p;
268
+ }
269
+ url = url.replace(/([^:]\/)\/+/g, "$1");
270
+ return url;
271
+ }
272
+
273
+ // src/webdav-fs.ts
274
+ var WebDAVFS = class {
275
+ /**
276
+ * 创建WebDAV文件系统实例
277
+ * @param options WebDAV选项
278
+ */
279
+ constructor(options) {
280
+ if (!options.baseUrl) {
281
+ throw new ArgumentError("\u5FC5\u987B\u63D0\u4F9BbaseUrl");
282
+ }
283
+ this.baseUrl = options.baseUrl.endsWith("/") ? options.baseUrl.slice(0, -1) : options.baseUrl;
284
+ if (options.username && options.password) {
285
+ this.auth = { username: options.username, password: options.password };
286
+ } else {
287
+ this.auth = void 0;
288
+ }
289
+ this.timeout = options.timeout || 3e4;
290
+ this.headers = options.headers || {};
291
+ }
292
+ /**
293
+ * 创建请求头
294
+ * @param customHeaders 自定义请求头
295
+ * @returns 合并后的请求头
296
+ */
297
+ createHeaders(customHeaders) {
298
+ const headers = {
299
+ ...this.headers,
300
+ ...customHeaders
301
+ };
302
+ if (this.auth) {
303
+ headers["Authorization"] = createBasicAuthHeader(this.auth.username, this.auth.password);
304
+ }
305
+ return headers;
306
+ }
307
+ /**
308
+ * 执行HTTP请求
309
+ * @param method HTTP方法
310
+ * @param path 请求路径
311
+ * @param options 请求选项
312
+ * @returns 响应对象
313
+ */
314
+ async request(method, path, options = {}) {
315
+ const normalizedPath = normalizePath(path);
316
+ const url = joinUrl(this.baseUrl, normalizedPath);
317
+ const headers = this.createHeaders(options.headers);
318
+ const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
319
+ const timeoutId = controller ? setTimeout(() => controller.abort(), this.timeout) : void 0;
320
+ try {
321
+ const response = await fetch(url, {
322
+ method,
323
+ headers,
324
+ body: options.body,
325
+ signal: controller?.signal,
326
+ credentials: "include"
327
+ });
328
+ if (timeoutId) clearTimeout(timeoutId);
329
+ const responseHeaders = {};
330
+ response.headers.forEach((value, key) => {
331
+ responseHeaders[key.toLowerCase()] = value;
332
+ });
333
+ let data;
334
+ if (options.responseType === "arraybuffer") {
335
+ data = await response.arrayBuffer();
336
+ } else if (options.responseType === "blob") {
337
+ data = await response.blob();
338
+ } else if (options.responseType === "json") {
339
+ data = await response.json();
340
+ } else {
341
+ data = await response.text();
342
+ }
343
+ return {
344
+ data,
345
+ status: response.status,
346
+ headers: responseHeaders
347
+ };
348
+ } catch (error) {
349
+ if (timeoutId) clearTimeout(timeoutId);
350
+ if (error instanceof Error && error.name === "AbortError") {
351
+ throw new TimeoutError(`\u8BF7\u6C42\u8D85\u65F6: ${url}`);
352
+ } else {
353
+ const errorMessage = error instanceof Error ? error.message : String(error);
354
+ throw new NetworkError(error instanceof Error ? error : new Error(String(error)), `\u7F51\u7EDC\u9519\u8BEF: ${errorMessage}`);
355
+ }
356
+ }
357
+ }
358
+ /**
359
+ * 处理响应错误
360
+ * @param status HTTP状态码
361
+ * @param path 请求路径
362
+ * @param error 原始错误
363
+ */
364
+ handleResponseError(status, path, error) {
365
+ switch (status) {
366
+ case 401:
367
+ throw new AuthenticationError(`\u8BA4\u8BC1\u5931\u8D25: ${path}`);
368
+ case 403:
369
+ throw new AuthorizationError(`\u65E0\u6743\u9650\u8BBF\u95EE: ${path}`);
370
+ case 404:
371
+ throw new NotFoundError(`\u8D44\u6E90\u4E0D\u5B58\u5728: ${path}`);
372
+ case 409:
373
+ throw new FileExistsError(`\u8D44\u6E90\u5DF2\u5B58\u5728: ${path}`);
374
+ default:
375
+ throw new ServerError(`\u670D\u52A1\u5668\u9519\u8BEF (${status}): ${path}`, status);
376
+ }
377
+ }
378
+ /**
379
+ * 读取文件内容
380
+ * @param path 文件路径
381
+ * @param options 读取选项
382
+ * @returns 文件内容
383
+ */
384
+ async readFile(path, options = {}) {
385
+ const normalizedPath = normalizePath(path);
386
+ try {
387
+ const response = await this.request("GET", normalizedPath, {
388
+ headers: options.headers,
389
+ responseType: "arraybuffer"
390
+ });
391
+ if (response.status >= 400) {
392
+ this.handleResponseError(response.status, normalizedPath);
393
+ }
394
+ const arrayBuffer = response.data;
395
+ const uint8Array = new Uint8Array(arrayBuffer);
396
+ const buffer = Buffer.from(uint8Array);
397
+ if (options.encoding) {
398
+ return buffer.toString(options.encoding);
399
+ } else {
400
+ return buffer;
401
+ }
402
+ } catch (error) {
403
+ if (error instanceof WebDAVError) {
404
+ throw error;
405
+ }
406
+ throw new WebDAVError(`\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25: ${normalizedPath}`, void 0, error);
407
+ }
408
+ }
409
+ /**
410
+ * 写入文件内容
411
+ * @param path 文件路径
412
+ * @param data 文件内容
413
+ * @param options 写入选项
414
+ * @returns 操作结果
415
+ */
416
+ async writeFile(path, data, options = {}) {
417
+ const normalizedPath = normalizePath(path);
418
+ const getFilenameFromPath = (p) => {
419
+ const parts = p.split("/");
420
+ return parts[parts.length - 1] || "";
421
+ };
422
+ const contentType = options.contentType || getContentType(getFilenameFromPath(normalizedPath));
423
+ if (options.overwrite === false) {
424
+ const exists = await this.exists(normalizedPath);
425
+ if (exists) {
426
+ throw new FileExistsError(normalizedPath);
427
+ }
428
+ }
429
+ const headers = {
430
+ "Content-Type": contentType
431
+ };
432
+ try {
433
+ const response = await this.request("PUT", normalizedPath, {
434
+ headers,
435
+ body: data
436
+ });
437
+ if (response.status >= 400) {
438
+ this.handleResponseError(response.status, normalizedPath);
439
+ }
440
+ return {
441
+ success: response.status >= 200 && response.status < 300,
442
+ statusCode: response.status
443
+ };
444
+ } catch (error) {
445
+ if (error instanceof WebDAVError) {
446
+ throw error;
447
+ }
448
+ throw new WebDAVError(`\u5199\u5165\u6587\u4EF6\u5931\u8D25: ${normalizedPath}`);
449
+ }
450
+ }
451
+ /**
452
+ * 删除文件
453
+ * @param path 文件路径
454
+ * @returns 操作结果
455
+ */
456
+ async deleteFile(path) {
457
+ const normalizedPath = normalizePath(path);
458
+ try {
459
+ const stat = await this.stat(normalizedPath);
460
+ if (stat.isDirectory) {
461
+ throw new ArgumentError(`\u8DEF\u5F84\u6307\u5411\u4E00\u4E2A\u76EE\u5F55\uFF0C\u8BF7\u4F7F\u7528rmdir\u65B9\u6CD5: ${normalizedPath}`);
462
+ }
463
+ const response = await this.request("DELETE", normalizedPath);
464
+ if (response.status >= 400) {
465
+ this.handleResponseError(response.status, normalizedPath);
466
+ }
467
+ return {
468
+ success: response.status >= 200 && response.status < 300,
469
+ statusCode: response.status
470
+ };
471
+ } catch (error) {
472
+ if (error instanceof WebDAVError) {
473
+ throw error;
474
+ }
475
+ throw new WebDAVError(`\u5220\u9664\u6587\u4EF6\u5931\u8D25: ${normalizedPath}`);
476
+ }
477
+ }
478
+ /**
479
+ * 读取目录内容
480
+ * @param path 目录路径
481
+ * @param options 读取选项
482
+ * @returns 文件统计信息数组
483
+ */
484
+ async readDir(path, options = {}) {
485
+ const normalizedPath = normalizePath(path);
486
+ try {
487
+ const headers = {
488
+ "Depth": options.recursive ? "infinity" : "1",
489
+ "Content-Type": "application/xml"
490
+ };
491
+ const response = await this.request("PROPFIND", normalizedPath, {
492
+ headers
493
+ });
494
+ if (response.status >= 400) {
495
+ this.handleResponseError(response.status, normalizedPath);
496
+ }
497
+ const files = parseWebDAVXml(response.data, normalizedPath);
498
+ const result = files.filter((file) => {
499
+ if (file.path === normalizedPath) {
500
+ return false;
501
+ }
502
+ if (!options.includeHidden && file.name.startsWith(".")) {
503
+ return false;
504
+ }
505
+ return true;
506
+ });
507
+ return result;
508
+ } catch (error) {
509
+ if (error instanceof WebDAVError) {
510
+ throw error;
511
+ }
512
+ throw new WebDAVError(`\u8BFB\u53D6\u76EE\u5F55\u5931\u8D25: ${normalizedPath}`);
513
+ }
514
+ }
515
+ /**
516
+ * 创建目录
517
+ * @param path 目录路径
518
+ * @param options 创建选项
519
+ * @returns 操作结果
520
+ */
521
+ async mkdir(path, options = {}) {
522
+ const normalizedPath = normalizePath(path);
523
+ try {
524
+ try {
525
+ const stat = await this.stat(normalizedPath);
526
+ if (stat.isDirectory) {
527
+ return;
528
+ }
529
+ throw new FileExistsError(normalizedPath);
530
+ } catch (error) {
531
+ if (!(error instanceof NotFoundError)) {
532
+ throw error;
533
+ }
534
+ }
535
+ if (options.recursive !== false) {
536
+ const parentDir = getDirFromPath(normalizedPath);
537
+ if (parentDir !== "/" && parentDir !== normalizedPath) {
538
+ try {
539
+ await this.stat(parentDir);
540
+ } catch (error) {
541
+ if (error instanceof NotFoundError) {
542
+ await this.mkdir(parentDir, options);
543
+ } else {
544
+ throw error;
545
+ }
546
+ }
547
+ }
548
+ }
549
+ const response = await this.request("MKCOL", normalizedPath);
550
+ if (response.status >= 400) {
551
+ this.handleResponseError(response.status, normalizedPath);
552
+ }
553
+ return;
554
+ } catch (error) {
555
+ if (error instanceof WebDAVError) {
556
+ throw error;
557
+ }
558
+ throw new WebDAVError(`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${normalizedPath}`);
559
+ }
560
+ }
561
+ /**
562
+ * 删除文件或目录,行为类似于 Node.js 的 fs.rm
563
+ * @param path 路径
564
+ * @param options { recursive?: boolean, force?: boolean }
565
+ */
566
+ async rm(path, options) {
567
+ const { recursive = false, force = false } = options || {};
568
+ try {
569
+ const stat = await this.stat(path);
570
+ if (stat.isDirectory) {
571
+ if (recursive) {
572
+ const files = await this.readDir(path);
573
+ for (const file of files) {
574
+ const childPath = path.replace(/\/$/, "") + "/" + file.name;
575
+ await this.rm(childPath, { recursive: true, force });
576
+ }
577
+ } else {
578
+ const files = await this.readDir(path);
579
+ if (files.length > 0) {
580
+ throw new WebDAVError(`Directory not empty: ${path}`);
581
+ }
582
+ }
583
+ }
584
+ await this._delete(path);
585
+ } catch (err) {
586
+ if (force && (err instanceof Error && "code" in err && err.code === "ENOENT" || err instanceof Error && "status" in err && err.status === 404)) {
587
+ return;
588
+ }
589
+ throw err;
590
+ }
591
+ }
592
+ /**
593
+ * 兼容旧的 rmdir 方法,内部重定向到 rm
594
+ * @deprecated 请使用 rm
595
+ */
596
+ async rmdir(path, options) {
597
+ let opts = {};
598
+ if (typeof options === "boolean") {
599
+ opts.recursive = options;
600
+ } else if (typeof options === "object" && options !== null) {
601
+ opts = options;
602
+ }
603
+ return this.rm(path, opts);
604
+ }
605
+ /**
606
+ * 删除文件或目录
607
+ * @param path 文件或目录路径
608
+ * @returns 操作结果
609
+ */
610
+ async _delete(path) {
611
+ const normalizedPath = normalizePath(path);
612
+ try {
613
+ const response = await this.request("DELETE", normalizedPath);
614
+ if (response.status >= 400) {
615
+ this.handleResponseError(response.status, normalizedPath);
616
+ }
617
+ return {
618
+ success: response.status >= 200 && response.status < 300,
619
+ statusCode: response.status
620
+ };
621
+ } catch (error) {
622
+ if (error instanceof WebDAVError) {
623
+ throw error;
624
+ }
625
+ throw new WebDAVError(`\u5220\u9664\u5931\u8D25: ${normalizedPath}`, void 0, error);
626
+ }
627
+ }
628
+ /**
629
+ * 获取文件或目录的统计信息
630
+ * @param path 文件或目录路径
631
+ * @returns 文件统计信息
632
+ */
633
+ async stat(path) {
634
+ const normalizedPath = normalizePath(path);
635
+ try {
636
+ const headers = {
637
+ "Depth": "0",
638
+ "Content-Type": "application/xml"
639
+ };
640
+ const response = await this.request("PROPFIND", normalizedPath, {
641
+ headers
642
+ });
643
+ if (response.status === 404) {
644
+ throw new NotFoundError(normalizedPath);
645
+ } else if (response.status >= 400) {
646
+ this.handleResponseError(response.status, normalizedPath);
647
+ }
648
+ const files = parseWebDAVXml(response.data, normalizedPath);
649
+ if (files.length === 0) {
650
+ throw new NotFoundError(normalizedPath);
651
+ }
652
+ const stat = files[0];
653
+ return stat;
654
+ } catch (error) {
655
+ if (error instanceof WebDAVError) {
656
+ throw error;
657
+ }
658
+ throw new WebDAVError(`\u83B7\u53D6\u6587\u4EF6\u4FE1\u606F\u5931\u8D25: ${normalizedPath}`, void 0, error);
659
+ }
660
+ }
661
+ /**
662
+ * 检查文件或目录是否存在
663
+ * @param path 文件或目录路径
664
+ * @returns 是否存在
665
+ */
666
+ async exists(path) {
667
+ try {
668
+ await this.stat(path);
669
+ return true;
670
+ } catch (error) {
671
+ if (error instanceof NotFoundError) {
672
+ return false;
673
+ }
674
+ throw error;
675
+ }
676
+ }
677
+ /**
678
+ * 复制文件或目录
679
+ * @param source 源路径
680
+ * @param destination 目标路径
681
+ * @param overwrite 是否覆盖已存在的文件
682
+ * @returns 操作结果
683
+ */
684
+ async copy(source, destination, overwrite = true) {
685
+ const normalizedSource = normalizePath(source);
686
+ const normalizedDestination = normalizePath(destination);
687
+ try {
688
+ await this.stat(normalizedSource);
689
+ if (!overwrite) {
690
+ const exists = await this.exists(normalizedDestination);
691
+ if (exists) {
692
+ throw new FileExistsError(normalizedDestination);
693
+ }
694
+ }
695
+ const headers = {
696
+ "Destination": joinUrl(this.baseUrl, normalizedDestination),
697
+ "Overwrite": overwrite ? "T" : "F"
698
+ };
699
+ const response = await this.request("COPY", normalizedSource, {
700
+ headers
701
+ });
702
+ if (response.status >= 400) {
703
+ this.handleResponseError(response.status, normalizedSource);
704
+ }
705
+ return {
706
+ success: response.status >= 200 && response.status < 300,
707
+ statusCode: response.status
708
+ };
709
+ } catch (error) {
710
+ if (error instanceof WebDAVError) {
711
+ throw error;
712
+ }
713
+ throw new WebDAVError(`\u590D\u5236\u5931\u8D25: ${normalizedSource} -> ${normalizedDestination}`, void 0, error);
714
+ }
715
+ }
716
+ /**
717
+ * 移动文件或目录
718
+ * @param source 源路径
719
+ * @param destination 目标路径
720
+ * @param overwrite 是否覆盖已存在的文件
721
+ * @returns 操作结果
722
+ */
723
+ async move(source, destination, overwrite = true) {
724
+ const normalizedSource = normalizePath(source);
725
+ const normalizedDestination = normalizePath(destination);
726
+ try {
727
+ await this.stat(normalizedSource);
728
+ if (!overwrite) {
729
+ const exists = await this.exists(normalizedDestination);
730
+ if (exists) {
731
+ throw new FileExistsError(normalizedDestination);
732
+ }
733
+ }
734
+ const headers = {
735
+ "Destination": joinUrl(this.baseUrl, normalizedDestination),
736
+ "Overwrite": overwrite ? "T" : "F"
737
+ };
738
+ const response = await this.request("MOVE", normalizedSource, {
739
+ headers
740
+ });
741
+ if (response.status >= 400) {
742
+ this.handleResponseError(response.status, normalizedSource);
743
+ }
744
+ return {
745
+ success: response.status >= 200 && response.status < 300,
746
+ statusCode: response.status
747
+ };
748
+ } catch (error) {
749
+ if (error instanceof WebDAVError) {
750
+ throw error;
751
+ }
752
+ throw new WebDAVError(`\u79FB\u52A8\u5931\u8D25: ${normalizedSource} -> ${normalizedDestination}`, void 0, error);
753
+ }
754
+ }
755
+ /**
756
+ * 删除文件(fs/unlink 兼容方法)
757
+ * @param path 文件路径
758
+ */
759
+ async unlink(path) {
760
+ await this.deleteFile(path);
761
+ }
762
+ };
763
+
764
+ // src/webdav.ts
765
+ function createWebDAVFileSystem(options) {
766
+ return new WebDAVFS(options);
767
+ }
768
+ //# sourceMappingURL=index.js.map