@sfmc-bds/bds-tools 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.
Files changed (70) hide show
  1. package/README.md +18 -0
  2. package/dist/bds-manager.d.ts +27 -0
  3. package/dist/bds-manager.d.ts.map +1 -0
  4. package/dist/bds-manager.js +294 -0
  5. package/dist/bds-manager.js.map +1 -0
  6. package/dist/changelog.d.ts +9 -0
  7. package/dist/changelog.d.ts.map +1 -0
  8. package/dist/changelog.js +59 -0
  9. package/dist/changelog.js.map +1 -0
  10. package/dist/check-update.d.ts +12 -0
  11. package/dist/check-update.d.ts.map +1 -0
  12. package/dist/check-update.js +442 -0
  13. package/dist/check-update.js.map +1 -0
  14. package/dist/cli-pack-manager.d.ts +19 -0
  15. package/dist/cli-pack-manager.d.ts.map +1 -0
  16. package/dist/cli-pack-manager.js +154 -0
  17. package/dist/cli-pack-manager.js.map +1 -0
  18. package/dist/fsx.d.ts +25 -0
  19. package/dist/fsx.d.ts.map +1 -0
  20. package/dist/fsx.js +117 -0
  21. package/dist/fsx.js.map +1 -0
  22. package/dist/http.d.ts +37 -0
  23. package/dist/http.d.ts.map +1 -0
  24. package/dist/http.js +203 -0
  25. package/dist/http.js.map +1 -0
  26. package/dist/log.d.ts +13 -0
  27. package/dist/log.d.ts.map +1 -0
  28. package/dist/log.js +22 -0
  29. package/dist/log.js.map +1 -0
  30. package/dist/logger.d.ts +10 -0
  31. package/dist/logger.d.ts.map +1 -0
  32. package/dist/logger.js +10 -0
  33. package/dist/logger.js.map +1 -0
  34. package/dist/pack-manager.d.ts +136 -0
  35. package/dist/pack-manager.d.ts.map +1 -0
  36. package/dist/pack-manager.js +264 -0
  37. package/dist/pack-manager.js.map +1 -0
  38. package/dist/paths.d.ts +28 -0
  39. package/dist/paths.d.ts.map +1 -0
  40. package/dist/paths.js +47 -0
  41. package/dist/paths.js.map +1 -0
  42. package/dist/qqutil.d.ts +13 -0
  43. package/dist/qqutil.d.ts.map +1 -0
  44. package/dist/qqutil.js +101 -0
  45. package/dist/qqutil.js.map +1 -0
  46. package/dist/recovery.d.ts +8 -0
  47. package/dist/recovery.d.ts.map +1 -0
  48. package/dist/recovery.js +45 -0
  49. package/dist/recovery.js.map +1 -0
  50. package/dist/rollback.d.ts +36 -0
  51. package/dist/rollback.d.ts.map +1 -0
  52. package/dist/rollback.js +87 -0
  53. package/dist/rollback.js.map +1 -0
  54. package/dist/taskbar.d.ts +42 -0
  55. package/dist/taskbar.d.ts.map +1 -0
  56. package/dist/taskbar.js +94 -0
  57. package/dist/taskbar.js.map +1 -0
  58. package/dist/types.d.ts +56 -0
  59. package/dist/types.d.ts.map +1 -0
  60. package/dist/types.js +5 -0
  61. package/dist/types.js.map +1 -0
  62. package/dist/upstream.d.ts +20 -0
  63. package/dist/upstream.d.ts.map +1 -0
  64. package/dist/upstream.js +126 -0
  65. package/dist/upstream.js.map +1 -0
  66. package/dist/version.d.ts +20 -0
  67. package/dist/version.d.ts.map +1 -0
  68. package/dist/version.js +97 -0
  69. package/dist/version.js.map +1 -0
  70. package/package.json +62 -0
package/dist/fsx.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * fsx.ts — 异步/流式文件操作 (避免大文件加载进内存)
3
+ */
4
+ /**
5
+ * 流式计算文件哈希 (sha1 / sha256),
6
+ * 用于大文件 (例如 bedrock_server.exe ~80MB),不会 OOM。
7
+ */
8
+ export declare function hashFileAsync(filePath: string, algo?: "sha1" | "sha256"): Promise<string>;
9
+ /** 同步版本(用于小文件) */
10
+ export declare function hashFileSync(filePath: string, algo?: "sha1" | "sha256"): string;
11
+ /** 同步流式拷贝单文件 (复制中) */
12
+ export declare function copyFileAsync(src: string, dest: string): Promise<void>;
13
+ /** 同步目录复制 (递归) */
14
+ export declare function copyDirSync(src: string, dest: string): void;
15
+ /** 异步目录复制 (基于流) */
16
+ export declare function copyDirAsync(src: string, dest: string): Promise<void>;
17
+ /** 计算目录大小 (字节) */
18
+ export declare function getDirSize(dir: string): number;
19
+ /** 强制清空目录内容 (但保留目录本身) */
20
+ export declare function emptyDirSync(dir: string): void;
21
+ /** 安全删除整个目录 */
22
+ export declare function rmSafe(path: string): void;
23
+ /** 安全写文件 (原子: 写临时文件 → rename) */
24
+ export declare function writeFileSafe(filePath: string, data: string | Buffer): void;
25
+ //# sourceMappingURL=fsx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fsx.d.ts","sourceRoot":"","sources":["../src/fsx.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH;;;GAGG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,GAAG,QAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAUzG;AAED,kBAAkB;AAClB,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,GAAG,QAAmB,GAAG,MAAM,CAQzF;AAED,sBAAsB;AACtB,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5E;AAED,kBAAkB;AAClB,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAW3D;AAED,mBAAmB;AACnB,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAc3E;AAED,kBAAkB;AAClB,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAU9C;AAED,yBAAyB;AACzB,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAY9C;AAED,eAAe;AACf,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAMzC;AAED,iCAAiC;AACjC,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM3E"}
package/dist/fsx.js ADDED
@@ -0,0 +1,117 @@
1
+ /**
2
+ * fsx.ts — 异步/流式文件操作 (避免大文件加载进内存)
3
+ */
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import crypto from "node:crypto";
7
+ import { pipeline } from "node:stream/promises";
8
+ /**
9
+ * 流式计算文件哈希 (sha1 / sha256),
10
+ * 用于大文件 (例如 bedrock_server.exe ~80MB),不会 OOM。
11
+ */
12
+ export async function hashFileAsync(filePath, algo = "sha256") {
13
+ return new Promise((resolve, reject) => {
14
+ const h = crypto.createHash(algo);
15
+ const stream = fs.createReadStream(filePath);
16
+ stream.on("data", (chunk) => {
17
+ h.update(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
18
+ });
19
+ stream.on("end", () => resolve(h.digest("hex").toLowerCase()));
20
+ stream.on("error", reject);
21
+ });
22
+ }
23
+ /** 同步版本(用于小文件) */
24
+ export function hashFileSync(filePath, algo = "sha256") {
25
+ try {
26
+ const h = crypto.createHash(algo);
27
+ h.update(fs.readFileSync(filePath));
28
+ return h.digest("hex").toLowerCase();
29
+ }
30
+ catch {
31
+ return "";
32
+ }
33
+ }
34
+ /** 同步流式拷贝单文件 (复制中) */
35
+ export async function copyFileAsync(src, dest) {
36
+ await pipeline(fs.createReadStream(src), fs.createWriteStream(dest));
37
+ }
38
+ /** 同步目录复制 (递归) */
39
+ export function copyDirSync(src, dest) {
40
+ fs.mkdirSync(dest, { recursive: true });
41
+ for (const entry of fs.readdirSync(src)) {
42
+ const srcPath = path.join(src, entry);
43
+ const destPath = path.join(dest, entry);
44
+ if (fs.statSync(srcPath).isDirectory()) {
45
+ copyDirSync(srcPath, destPath);
46
+ }
47
+ else {
48
+ fs.copyFileSync(srcPath, destPath);
49
+ }
50
+ }
51
+ }
52
+ /** 异步目录复制 (基于流) */
53
+ export async function copyDirAsync(src, dest) {
54
+ fs.mkdirSync(dest, { recursive: true });
55
+ await Promise.all(fs.readdirSync(src).map(async (entry) => {
56
+ const srcPath = path.join(src, entry);
57
+ const destPath = path.join(dest, entry);
58
+ const stat = fs.statSync(srcPath);
59
+ if (stat.isDirectory()) {
60
+ await copyDirAsync(srcPath, destPath);
61
+ }
62
+ else {
63
+ await copyFileAsync(srcPath, destPath);
64
+ }
65
+ }));
66
+ }
67
+ /** 计算目录大小 (字节) */
68
+ export function getDirSize(dir) {
69
+ let total = 0;
70
+ try {
71
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
72
+ const full = path.join(dir, entry.name);
73
+ if (entry.isDirectory())
74
+ total += getDirSize(full);
75
+ else if (entry.isFile())
76
+ total += fs.statSync(full).size;
77
+ }
78
+ }
79
+ catch { }
80
+ return total;
81
+ }
82
+ /** 强制清空目录内容 (但保留目录本身) */
83
+ export function emptyDirSync(dir) {
84
+ if (!fs.existsSync(dir))
85
+ return;
86
+ for (const entry of fs.readdirSync(dir)) {
87
+ const full = path.join(dir, entry);
88
+ try {
89
+ const stat = fs.statSync(full);
90
+ if (stat.isDirectory())
91
+ fs.rmSync(full, { recursive: true, force: true });
92
+ else
93
+ fs.unlinkSync(full);
94
+ }
95
+ catch (e) {
96
+ // 忽略: 顶层仍保留目录,即使部分子项无法删除
97
+ }
98
+ }
99
+ }
100
+ /** 安全删除整个目录 */
101
+ export function rmSafe(path) {
102
+ try {
103
+ fs.rmSync(path, { recursive: true, force: true });
104
+ }
105
+ catch {
106
+ /* ignore */
107
+ }
108
+ }
109
+ /** 安全写文件 (原子: 写临时文件 → rename) */
110
+ export function writeFileSafe(filePath, data) {
111
+ const dir = path.dirname(filePath);
112
+ fs.mkdirSync(dir, { recursive: true });
113
+ const tmp = `${filePath}.${process.pid}.tmp`;
114
+ fs.writeFileSync(tmp, data);
115
+ fs.renameSync(tmp, filePath);
116
+ }
117
+ //# sourceMappingURL=fsx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fsx.js","sourceRoot":"","sources":["../src/fsx.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAgB,EAAE,OAA0B,QAAQ;IACtF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAsB,EAAE,EAAE;YAC3C,CAAC,CAAC,MAAM,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QAC/D,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,kBAAkB;AAClB,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,OAA0B,QAAQ;IAC/E,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC;QACpC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,sBAAsB;AACtB,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAW,EAAE,IAAY;IAC3D,MAAM,QAAQ,CAAC,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;AACvE,CAAC;AAED,kBAAkB;AAClB,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,IAAY;IACnD,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACxC,IAAI,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;YACvC,WAAW,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;AACH,CAAC;AAED,mBAAmB;AACnB,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW,EAAE,IAAY;IAC1D,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,MAAM,OAAO,CAAC,GAAG,CACf,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACvB,MAAM,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,MAAM,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACzC,CAAC;IACH,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,kBAAkB;AAClB,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,CAAC;QACH,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YACjE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,KAAK,CAAC,WAAW,EAAE;gBAAE,KAAK,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;iBAC9C,IAAI,KAAK,CAAC,MAAM,EAAE;gBAAE,KAAK,IAAI,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;QAC3D,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAO,KAAK,CAAC;AACf,CAAC;AAED,yBAAyB;AACzB,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO;IAChC,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACnC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC/B,IAAI,IAAI,CAAC,WAAW,EAAE;gBAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;;gBACrE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,yBAAyB;QAC3B,CAAC;IACH,CAAC;AACH,CAAC;AAED,eAAe;AACf,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,IAAI,CAAC;QACH,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,YAAY;IACd,CAAC;AACH,CAAC;AAED,iCAAiC;AACjC,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,IAAqB;IACnE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,GAAG,QAAQ,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;IAC7C,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5B,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AAC/B,CAAC"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * http.ts — 稳定 HTTP 工具 (分阶段超时 + 自动重定向 + 流式下载)
3
+ *
4
+ * 改进:
5
+ * - connect timeout vs total timeout 分开
6
+ * - 下载通过流管道,避免大文件加载到内存
7
+ * - 失败后已写入的临时文件可被清理
8
+ */
9
+ interface HttpOptions {
10
+ method?: "GET" | "HEAD" | "POST";
11
+ headers?: Record<string, string>;
12
+ body?: string;
13
+ connectTimeoutMs?: number;
14
+ totalTimeoutMs?: number;
15
+ redirects?: number;
16
+ }
17
+ export declare function httpRequest(url: string, opts?: HttpOptions): Promise<{
18
+ statusCode: number;
19
+ body: Buffer;
20
+ headers: Record<string, string | string[] | undefined>;
21
+ }>;
22
+ export declare function httpGetJson<T = unknown>(url: string, opts?: HttpOptions): Promise<T>;
23
+ export declare function httpGetText(url: string, opts?: HttpOptions): Promise<string>;
24
+ /** 多个源并发请求,谁先成功返回(每个源都用 JSON 解析,解析失败也失败) */
25
+ export declare function fetchJsonWithFallback<T = unknown>(sources: string[], timeoutMs?: number): Promise<T>;
26
+ /**
27
+ * 流式下载到指定路径,返回字节数。
28
+ * - 失败时自动清理 partial 文件
29
+ * - 支持 progress 回调
30
+ * - 支持 connect/total 阶段超时
31
+ */
32
+ export interface DownloadOptions extends HttpOptions {
33
+ onProgress?: (downloaded: number, total: number) => void;
34
+ }
35
+ export declare function httpDownload(url: string, destPath: string, opts?: DownloadOptions): Promise<number>;
36
+ export {};
37
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,UAAU,WAAW;IACnB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD,wBAAsB,WAAW,CAC/B,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;CAAE,CAAC,CA2DvG;AAED,wBAAsB,WAAW,CAAC,CAAC,GAAG,OAAO,EAC3C,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAGtF;AAED,4CAA4C;AAC5C,wBAAsB,qBAAqB,CAAC,CAAC,GAAG,OAAO,EACrD,OAAO,EAAE,MAAM,EAAE,EACjB,SAAS,SAAS,GACjB,OAAO,CAAC,CAAC,CAAC,CAcZ;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1D;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,eAAoB,GACzB,OAAO,CAAC,MAAM,CAAC,CAwGjB"}
package/dist/http.js ADDED
@@ -0,0 +1,203 @@
1
+ /**
2
+ * http.ts — 稳定 HTTP 工具 (分阶段超时 + 自动重定向 + 流式下载)
3
+ *
4
+ * 改进:
5
+ * - connect timeout vs total timeout 分开
6
+ * - 下载通过流管道,避免大文件加载到内存
7
+ * - 失败后已写入的临时文件可被清理
8
+ */
9
+ import http from "node:http";
10
+ import https from "node:https";
11
+ import { pipeline } from "node:stream/promises";
12
+ import { createWriteStream, statSync } from "node:fs";
13
+ import { log } from "./log.js";
14
+ const MAX_REDIRECTS = 5;
15
+ const DEFAULT_CONNECT_TIMEOUT = 15_000;
16
+ const DEFAULT_TOTAL_TIMEOUT = 600_000;
17
+ export async function httpRequest(url, opts = {}) {
18
+ const redirects = opts.redirects ?? 0;
19
+ const totalTimeoutMs = opts.totalTimeoutMs ?? DEFAULT_TOTAL_TIMEOUT;
20
+ const connectTimeoutMs = opts.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT;
21
+ const u = new URL(url);
22
+ const isHttps = u.protocol === "https:";
23
+ const mod = isHttps ? https : http;
24
+ return new Promise((resolve, reject) => {
25
+ const req = mod.request({
26
+ hostname: u.hostname,
27
+ port: u.port || (isHttps ? 443 : 80),
28
+ path: u.pathname + u.search,
29
+ method: opts.method ?? "GET",
30
+ headers: { "User-Agent": "BDSUpdater/2.0", ...(opts.headers ?? {}) },
31
+ }, (res) => {
32
+ const chunks = [];
33
+ res.on("data", (c) => chunks.push(c));
34
+ res.on("end", () => {
35
+ const body = Buffer.concat(chunks);
36
+ const status = res.statusCode ?? 0;
37
+ // 3xx → 跟随 Location
38
+ if (status >= 300 && status < 400 && redirects < MAX_REDIRECTS) {
39
+ const loc = res.headers.location;
40
+ if (!loc)
41
+ return reject(new Error(`HTTP ${status} 但缺少 Location`));
42
+ const next = loc.startsWith("http") ? loc : new URL(loc, url).href;
43
+ httpRequest(next, { ...opts, redirects: redirects + 1 })
44
+ .then(resolve)
45
+ .catch(reject);
46
+ return;
47
+ }
48
+ if (status >= 400) {
49
+ return reject(new Error(`HTTP ${status} for ${url}`));
50
+ }
51
+ resolve({ statusCode: status, body, headers: res.headers });
52
+ });
53
+ });
54
+ const totalTimer = setTimeout(() => {
55
+ req.destroy(new Error(`HTTP 总超时 ${totalTimeoutMs}ms: ${url}`));
56
+ }, totalTimeoutMs);
57
+ const connectTimer = setTimeout(() => {
58
+ req.destroy(new Error(`HTTP 连接超时 ${connectTimeoutMs}ms: ${url}`));
59
+ }, connectTimeoutMs);
60
+ req.on("socket", () => clearTimeout(connectTimer));
61
+ req.on("error", (err) => {
62
+ clearTimeout(totalTimer);
63
+ clearTimeout(connectTimer);
64
+ reject(err);
65
+ });
66
+ if (opts.body)
67
+ req.write(opts.body);
68
+ req.end();
69
+ });
70
+ }
71
+ export async function httpGetJson(url, opts = {}) {
72
+ const res = await httpRequest(url, opts);
73
+ try {
74
+ return JSON.parse(res.body.toString("utf-8"));
75
+ }
76
+ catch (e) {
77
+ throw new Error(`JSON 解析失败 ${url}: ${e.message}`);
78
+ }
79
+ }
80
+ export async function httpGetText(url, opts = {}) {
81
+ const res = await httpRequest(url, opts);
82
+ return res.body.toString("utf-8");
83
+ }
84
+ /** 多个源并发请求,谁先成功返回(每个源都用 JSON 解析,解析失败也失败) */
85
+ export async function fetchJsonWithFallback(sources, timeoutMs = 15_000) {
86
+ const tasks = sources.map(async (url) => {
87
+ const res = await httpGetJson(url, { totalTimeoutMs: timeoutMs });
88
+ return { url, value: res };
89
+ });
90
+ const settled = await Promise.allSettled(tasks);
91
+ for (const r of settled) {
92
+ if (r.status === "fulfilled")
93
+ return r.value.value;
94
+ }
95
+ const errors = settled
96
+ .filter((r) => r.status === "rejected")
97
+ .map((r) => r.reason?.message ?? "unknown")
98
+ .join("; ");
99
+ throw new Error(`所有源均不可用: ${errors}`);
100
+ }
101
+ export async function httpDownload(url, destPath, opts = {}) {
102
+ const totalTimeoutMs = opts.totalTimeoutMs ?? DEFAULT_TOTAL_TIMEOUT;
103
+ const connectTimeoutMs = opts.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT;
104
+ const u = new URL(url);
105
+ const isHttps = u.protocol === "https:";
106
+ const mod = isHttps ? https : http;
107
+ let redirects = opts.redirects ?? 0;
108
+ return new Promise((resolve, reject) => {
109
+ const req = mod.request({
110
+ hostname: u.hostname,
111
+ port: u.port || (isHttps ? 443 : 80),
112
+ path: u.pathname + u.search,
113
+ method: "GET",
114
+ headers: { "User-Agent": "BDSUpdater/2.0", ...(opts.headers ?? {}) },
115
+ }, (res) => {
116
+ const status = res.statusCode ?? 0;
117
+ if (status >= 300 && status < 400 && redirects < MAX_REDIRECTS) {
118
+ const loc = res.headers.location;
119
+ if (!loc)
120
+ return reject(new Error(`HTTP ${status} 缺少 Location`));
121
+ redirects++;
122
+ const next = loc.startsWith("http") ? loc : new URL(loc, url).href;
123
+ res.resume();
124
+ httpDownload(next, destPath, { ...opts, redirects })
125
+ .then(resolve)
126
+ .catch(reject);
127
+ return;
128
+ }
129
+ if (status >= 400)
130
+ return reject(new Error(`HTTP ${status} for ${url}`));
131
+ const total = parseInt(String(res.headers["content-length"] ?? 0), 10) || 0;
132
+ const file = createWriteStream(destPath);
133
+ let downloaded = 0;
134
+ let failed = false;
135
+ // 节流进度回调:每个 tick 最多 10 次/秒(100ms 间隔),
136
+ // 避免 cli-progress 频繁重绘拖慢下载
137
+ let lastProgressAt = 0;
138
+ const PROGRESS_INTERVAL_MS = 100;
139
+ const handleError = (e) => {
140
+ if (failed)
141
+ return;
142
+ failed = true;
143
+ file.close();
144
+ // 清理 partial 文件
145
+ import("node:fs").then((fs) => {
146
+ try {
147
+ fs.unlinkSync(destPath);
148
+ }
149
+ catch { }
150
+ });
151
+ reject(e);
152
+ };
153
+ const stream = res;
154
+ stream.on("data", (chunk) => {
155
+ downloaded += chunk.length;
156
+ if (opts.onProgress && total) {
157
+ const now = Date.now();
158
+ if (now - lastProgressAt >= PROGRESS_INTERVAL_MS) {
159
+ lastProgressAt = now;
160
+ opts.onProgress(downloaded, total);
161
+ }
162
+ }
163
+ });
164
+ stream.on("error", (e) => handleError(e));
165
+ file.on("error", (e) => handleError(e));
166
+ pipeline(stream, file).catch((e) => {
167
+ if (!failed)
168
+ handleError(e);
169
+ });
170
+ // 不能直接监听 file 'finish' 因为我们在错误时手动 handleError
171
+ file.on("finish", () => {
172
+ if (failed)
173
+ return;
174
+ try {
175
+ const finalBytes = statSync(destPath).size;
176
+ // 收尾:确保最后一次回调让进度条走到 100%(即便最近
177
+ // 一个 tick 的节流没触发,也补一次)
178
+ if (opts.onProgress && total)
179
+ opts.onProgress(finalBytes, total);
180
+ resolve(finalBytes);
181
+ }
182
+ catch (e) {
183
+ reject(e);
184
+ }
185
+ });
186
+ });
187
+ const totalTimer = setTimeout(() => {
188
+ req.destroy(new Error(`下载总超时 ${totalTimeoutMs}ms: ${url}`));
189
+ }, totalTimeoutMs);
190
+ const connectTimer = setTimeout(() => {
191
+ req.destroy(new Error(`下载连接超时 ${connectTimeoutMs}ms: ${url}`));
192
+ }, connectTimeoutMs);
193
+ req.on("socket", () => clearTimeout(connectTimer));
194
+ req.on("error", (e) => {
195
+ clearTimeout(totalTimer);
196
+ clearTimeout(connectTimer);
197
+ log.warn(`HTTP 错误 ${url}: ${e.message}`);
198
+ reject(e);
199
+ });
200
+ req.end();
201
+ });
202
+ }
203
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,KAAK,MAAM,YAAY,CAAC;AAE/B,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAW/B,MAAM,aAAa,GAAG,CAAC,CAAC;AACxB,MAAM,uBAAuB,GAAG,MAAM,CAAC;AACvC,MAAM,qBAAqB,GAAG,OAAO,CAAC;AAEtC,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAAW,EACX,OAAoB,EAAE;IAEtB,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;IACtC,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,IAAI,qBAAqB,CAAC;IACpE,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,IAAI,uBAAuB,CAAC;IAE1E,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,MAAM,OAAO,GAAG,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC;IACxC,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAEnC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CACrB;YACE,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACpC,IAAI,EAAE,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,MAAM;YAC3B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK;YAC5B,OAAO,EAAE,EAAE,YAAY,EAAE,gBAAgB,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE;SACrE,EACD,CAAC,GAAG,EAAE,EAAE;YACN,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9C,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;gBACjB,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACnC,MAAM,MAAM,GAAG,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC;gBACnC,oBAAoB;gBACpB,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG,IAAI,SAAS,GAAG,aAAa,EAAE,CAAC;oBAC/D,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC;oBACjC,IAAI,CAAC,GAAG;wBAAE,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,MAAM,eAAe,CAAC,CAAC,CAAC;oBAClE,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC;oBACnE,WAAW,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,SAAS,GAAG,CAAC,EAAE,CAAC;yBACrD,IAAI,CAAC,OAAO,CAAC;yBACb,KAAK,CAAC,MAAM,CAAC,CAAC;oBACjB,OAAO;gBACT,CAAC;gBACD,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;oBAClB,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,MAAM,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC;gBACxD,CAAC;gBACD,OAAO,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YAC9D,CAAC,CAAC,CAAC;QACL,CAAC,CACF,CAAC;QAEF,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE;YACjC,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,YAAY,cAAc,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC;QACjE,CAAC,EAAE,cAAc,CAAC,CAAC;QACnB,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,EAAE;YACnC,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,aAAa,gBAAgB,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC;QACpE,CAAC,EAAE,gBAAgB,CAAC,CAAC;QAErB,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC;QACnD,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACtB,YAAY,CAAC,UAAU,CAAC,CAAC;YACzB,YAAY,CAAC,YAAY,CAAC,CAAC;YAC3B,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;QAEH,IAAI,IAAI,CAAC,IAAI;YAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAAW,EACX,OAAoB,EAAE;IAEtB,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACzC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAM,CAAC;IACrD,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,aAAa,GAAG,KAAM,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/D,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAW,EAAE,OAAoB,EAAE;IACnE,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACzC,OAAO,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AACpC,CAAC;AAED,4CAA4C;AAC5C,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAAiB,EACjB,SAAS,GAAG,MAAM;IAElB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACtC,MAAM,GAAG,GAAG,MAAM,WAAW,CAAI,GAAG,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,CAAC,CAAC;QACrE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IAC7B,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAChD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW;YAAE,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;IACrD,CAAC;IACD,MAAM,MAAM,GAAG,OAAO;SACnB,MAAM,CAAC,CAAC,CAAC,EAA8B,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC;SAClE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,IAAI,SAAS,CAAC;SAC1C,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,IAAI,KAAK,CAAC,YAAY,MAAM,EAAE,CAAC,CAAC;AACxC,CAAC;AAYD,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAW,EACX,QAAgB,EAChB,OAAwB,EAAE;IAE1B,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,IAAI,qBAAqB,CAAC;IACpE,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,IAAI,uBAAuB,CAAC;IAE1E,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,MAAM,OAAO,GAAG,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC;IACxC,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACnC,IAAI,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;IAEpC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CACrB;YACE,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACpC,IAAI,EAAE,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,MAAM;YAC3B,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,EAAE,YAAY,EAAE,gBAAgB,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE;SACrE,EACD,CAAC,GAAG,EAAE,EAAE;YACN,MAAM,MAAM,GAAG,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC;YACnC,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG,IAAI,SAAS,GAAG,aAAa,EAAE,CAAC;gBAC/D,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC;gBACjC,IAAI,CAAC,GAAG;oBAAE,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,MAAM,cAAc,CAAC,CAAC,CAAC;gBACjE,SAAS,EAAE,CAAC;gBACZ,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnE,GAAG,CAAC,MAAM,EAAE,CAAC;gBACb,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,CAAC;qBACjD,IAAI,CAAC,OAAO,CAAC;qBACb,KAAK,CAAC,MAAM,CAAC,CAAC;gBACjB,OAAO;YACT,CAAC;YACD,IAAI,MAAM,IAAI,GAAG;gBAAE,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,MAAM,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC;YAEzE,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;YAC5E,MAAM,IAAI,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;YACzC,IAAI,UAAU,GAAG,CAAC,CAAC;YACnB,IAAI,MAAM,GAAG,KAAK,CAAC;YACnB,sCAAsC;YACtC,2BAA2B;YAC3B,IAAI,cAAc,GAAG,CAAC,CAAC;YACvB,MAAM,oBAAoB,GAAG,GAAG,CAAC;YAEjC,MAAM,WAAW,GAAG,CAAC,CAAQ,EAAQ,EAAE;gBACrC,IAAI,MAAM;oBAAE,OAAO;gBACnB,MAAM,GAAG,IAAI,CAAC;gBACd,IAAI,CAAC,KAAK,EAAE,CAAC;gBACb,gBAAgB;gBAChB,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE;oBAC5B,IAAI,CAAC;wBAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;oBAAC,CAAC;oBAAC,MAAM,CAAC,CAAA,CAAC;gBAC3C,CAAC,CAAC,CAAC;gBACH,MAAM,CAAC,CAAC,CAAC,CAAC;YACZ,CAAC,CAAC;YAEF,MAAM,MAAM,GAAa,GAAG,CAAC;YAC7B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;gBAClC,UAAU,IAAI,KAAK,CAAC,MAAM,CAAC;gBAC3B,IAAI,IAAI,CAAC,UAAU,IAAI,KAAK,EAAE,CAAC;oBAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;oBACvB,IAAI,GAAG,GAAG,cAAc,IAAI,oBAAoB,EAAE,CAAC;wBACjD,cAAc,GAAG,GAAG,CAAC;wBACrB,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;oBACrC,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAU,CAAC,CAAC,CAAC;YAEnD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAU,CAAC,CAAC,CAAC;YAEjD,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;gBACjC,IAAI,CAAC,MAAM;oBAAE,WAAW,CAAC,CAAU,CAAC,CAAC;YACvC,CAAC,CAAC,CAAC;YAEH,8CAA8C;YAC9C,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;gBACrB,IAAI,MAAM;oBAAE,OAAO;gBACnB,IAAI,CAAC;oBACH,MAAM,UAAU,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;oBAC3C,8BAA8B;oBAC9B,uBAAuB;oBACvB,IAAI,IAAI,CAAC,UAAU,IAAI,KAAK;wBAAE,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;oBACjE,OAAO,CAAC,UAAU,CAAC,CAAC;gBACtB,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACX,MAAM,CAAC,CAAU,CAAC,CAAC;gBACrB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CACF,CAAC;QAEF,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE;YACjC,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,SAAS,cAAc,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC;QAC9D,CAAC,EAAE,cAAc,CAAC,CAAC;QACnB,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,EAAE;YACnC,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,UAAU,gBAAgB,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC;QACjE,CAAC,EAAE,gBAAgB,CAAC,CAAC;QAErB,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC;QACnD,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;YACpB,YAAY,CAAC,UAAU,CAAC,CAAC;YACzB,YAAY,CAAC,YAAY,CAAC,CAAC;YAC3B,GAAG,CAAC,IAAI,CAAC,WAAW,GAAG,KAAM,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;YACpD,MAAM,CAAC,CAAC,CAAC,CAAC;QACZ,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC"}
package/dist/log.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * log.ts — bds-tools 统一日志实例
3
+ *
4
+ * 通过 @sfmc-bds/logs 共享包接入,source = "bds-tools"。
5
+ * 同时输出 stdout + 落盘到 LOG_PATH (update.log)。
6
+ *
7
+ * check-update.ts 是独立入口,用 source = "updater" 单独创建 logger,
8
+ * 见该文件内的 createUpdaterLogger()。
9
+ */
10
+ export declare const log: import("@sfmc-bds/sdk/logs").Logger;
11
+ /** 关闭文件流 (进程退出前调用,确保缓冲落盘) */
12
+ export declare function closeLog(): void;
13
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,eAAO,MAAM,GAAG,qCAGd,CAAC;AAEH,6BAA6B;AAC7B,wBAAgB,QAAQ,IAAI,IAAI,CAE/B"}
package/dist/log.js ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * log.ts — bds-tools 统一日志实例
3
+ *
4
+ * 通过 @sfmc-bds/logs 共享包接入,source = "bds-tools"。
5
+ * 同时输出 stdout + 落盘到 LOG_PATH (update.log)。
6
+ *
7
+ * check-update.ts 是独立入口,用 source = "updater" 单独创建 logger,
8
+ * 见该文件内的 createUpdaterLogger()。
9
+ */
10
+ import { createLogger, createStdoutSink, createFileSink } from "@sfmc-bds/sdk/logs";
11
+ import { LOG_PATH } from "./paths.js";
12
+ const fileSink = createFileSink(LOG_PATH);
13
+ process.on("exit", () => fileSink.close());
14
+ export const log = createLogger({
15
+ source: "bds-tools",
16
+ sinks: [createStdoutSink(), fileSink],
17
+ });
18
+ /** 关闭文件流 (进程退出前调用,确保缓冲落盘) */
19
+ export function closeLog() {
20
+ fileSink.close();
21
+ }
22
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;AAC1C,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;AAE3C,MAAM,CAAC,MAAM,GAAG,GAAG,YAAY,CAAC;IAC9B,MAAM,EAAE,WAAW;IACnB,KAAK,EAAE,CAAC,gBAAgB,EAAE,EAAE,QAAQ,CAAC;CACtC,CAAC,CAAC;AAEH,6BAA6B;AAC7B,MAAM,UAAU,QAAQ;IACtB,QAAQ,CAAC,KAAK,EAAE,CAAC;AACnB,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * logger.ts — 已废弃,统一改用 log.ts
3
+ *
4
+ * 此文件保留仅为向后兼容 (safe-delete 限制无法删除)。
5
+ * 实际日志逻辑在 log.ts,通过 @sfmc-bds/logs 共享包接入。
6
+ *
7
+ * 新代码请直接:import { log } from "./log.js"
8
+ */
9
+ export { log as logger, closeLog as closeLogger } from "./log.js";
10
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,GAAG,IAAI,MAAM,EAAE,QAAQ,IAAI,WAAW,EAAE,MAAM,UAAU,CAAC"}
package/dist/logger.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * logger.ts — 已废弃,统一改用 log.ts
3
+ *
4
+ * 此文件保留仅为向后兼容 (safe-delete 限制无法删除)。
5
+ * 实际日志逻辑在 log.ts,通过 @sfmc-bds/logs 共享包接入。
6
+ *
7
+ * 新代码请直接:import { log } from "./log.js"
8
+ */
9
+ export { log as logger, closeLog as closeLogger } from "./log.js";
10
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,GAAG,IAAI,MAAM,EAAE,QAAQ,IAAI,WAAW,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * pack-manager.ts — Bedrock 行为包/资源包 装配 + 部署到 BDS 根。
3
+ *
4
+ * 这是 SEA 与 npm 两条用户路径共用的纯函数库:
5
+ *
6
+ * - SEA:用户在 REPL/wizard 里跑 `sfmc behavior-pack build/deploy`
7
+ * → sfmc 通过 spawnService 启 `bds-tools/dist/pack-manager.js`,
8
+ * SEA 自己跑 esbuild 把脚本 bundle 写到 <modulesDir>/build/sfmc-modules-bp/scripts/main.js,
9
+ * 然后调 pack-manager#assembleBehaviorPack 装配。
10
+ * - npm: 用户跑 `sfmc behavior-pack build`,
11
+ * 同一 spawnService 路径。或者直接在同进程 import 这个库。
12
+ *
13
+ * 关键约束:本文件不允许引 esbuild / 任何 npm-only 包 — SEA 要把它打成 SEA
14
+ * 的一部分内嵌起来,而 SEA 是个单 exe (除 Node 内置 + bds-tools 已有依赖外)。
15
+ *
16
+ * 已有依赖 (bds-tools/package.json):
17
+ * - cli-progress: 进度条
18
+ * - node-html-parser: changelog 抓取 (本文件用不到)
19
+ * - jszip: zip 解压(模块 zip / BDS server.zip),与 sfmc/wizard 共享同一个库
20
+ *
21
+ * 没有外部依赖意味着:SEA 静态把 `bds-tools/dist/*` 解压到 exe 旁边即可,
22
+ *不需把 npm 整棵树带进去。
23
+ */
24
+ import { copyDirAsync, copyFileAsync } from "./fsx.js";
25
+ export interface AssembleBehaviorPackOpts {
26
+ /** Where the bundled scripts/main.js already exists (esbuild output dir) */
27
+ srcDir: string;
28
+ /** Output behavior pack directory (created if missing) */
29
+ outDir: string;
30
+ /** BP 目录名 — SEA 固定 'sfmc-modules', npm 自定义 BP 可改名 */
31
+ projectName: string;
32
+ /** manifest.json 的 header.version [major, minor, patch]; 默认 [1, 0, 0] */
33
+ version?: [number, number, number] | undefined;
34
+ /** manifest.json 的 description */
35
+ description?: string | undefined;
36
+ /** pack_icon.png 源文件 (optional, 不提供则跳过) */
37
+ iconSrc?: string | undefined;
38
+ }
39
+ export interface AssembleResourcePackOpts {
40
+ /** Modules whose resource_pack/** should be merged. Map<moduleId, resourcePackDir>. */
41
+ moduleResourceDirs: Record<string, string>;
42
+ /** Output resource pack directory (created if missing) */
43
+ outDir: string;
44
+ /** RP 目录名 */
45
+ projectName: string;
46
+ /** manifest.json 的 header.version [major, minor, patch]; 默认 [1, 0, 0] */
47
+ version?: [number, number, number] | undefined;
48
+ /** manifest.json 的 description */
49
+ description?: string | undefined;
50
+ }
51
+ export interface DeployOpts {
52
+ bdsRoot: string;
53
+ levelName: string;
54
+ /** 已组装好的 BP 源目录 */
55
+ behaviorPackSrc: string;
56
+ /** 已组装好的 RP 源目录 (可省略,只用 BP 也合法) */
57
+ resourcePackSrc?: string;
58
+ /** BP 目标目录名 (worlds/<level>/behavior_packs/<bpName>) */
59
+ bpName: string;
60
+ /** RP 目标目录名 (worlds/<level>/resource_packs/<rpName>);省略时复用 bpName + '-rp' */
61
+ rpName?: string;
62
+ }
63
+ export interface EnablePackOpts {
64
+ worldsDir: string;
65
+ levelName: string;
66
+ kind: "behavior" | "resource";
67
+ packUuid: string;
68
+ version: [number, number, number];
69
+ }
70
+ /** SFMC 合成 BP 的固定 @minecraft/* permissions,所有用到的命名空间都打开。 */
71
+ export declare const SFMC_PERMISSIONS: readonly ["@minecraft/server", "@minecraft/server-ui", "@minecraft/server-admin", "@minecraft/server-gametest", "@minecraft/server-net", "@minecraft/server-editor", "@minecraft/diagnostics"];
72
+ /** 随机生成 BP/RP manifest.json header.uuid (RFC 4122 v4) */
73
+ export declare function randomUuid(): string;
74
+ /**
75
+ * Assemble a Bedrock behavior pack directory at `outDir` from a prebuilt
76
+ * `srcDir` (which must already contain `scripts/main.js` plus whatever else
77
+ * was bundled by esbuild) and write a fresh `manifest.json` + `permissions.json`.
78
+ *
79
+ * The caller is responsible for running esbuild first; pack-manager does not
80
+ * bundle scripts. That decoupling is what lets the SEA ship without esbuild.
81
+ */
82
+ export declare function assembleBehaviorPack(opts: AssembleBehaviorPackOpts): Promise<void>;
83
+ /**
84
+ * Merge every enabled module's `resource_pack/**` tree into one RP directory
85
+ * at `outDir`, with a single fresh `manifest.json`. Per-module subfolders are
86
+ * preserved so resource pack authors can disambiguate (e.g. `peace/textures/...`).
87
+ */
88
+ export declare function assembleResourcePack(opts: AssembleResourcePackOpts): Promise<void>;
89
+ /**
90
+ * Read the `level-name=` value from `<bdsRoot>/server.properties`.
91
+ * Falls back to "Bedrock level" (BDS default) when the file is missing or
92
+ * the key is absent.
93
+ */
94
+ export declare function readLevelName(bdsRoot: string): Promise<string>;
95
+ /**
96
+ * Copy BP/RP source dirs into the world's behavior_packs/ + resource_packs/
97
+ * folders, and write a fresh `permissions.json` into the BP target.
98
+ *
99
+ * Does NOT edit world_behavior_packs.json — that's a separate step the caller
100
+ * must invoke after restart, because BDS only reads that file at startup.
101
+ */
102
+ export declare function deployToBDS(opts: DeployOpts): Promise<void>;
103
+ /**
104
+ * Write the seven `@minecraft/*` allowed_modules into a behavior pack's
105
+ * `permissions.json`. Idempotent: rewrites any pre-existing file.
106
+ */
107
+ export declare function writePermissionsJson(bpDir: string): Promise<void>;
108
+ /**
109
+ * Add `{ pack_id, version }` to `worlds/<level>/world_behavior_packs.json`
110
+ * (or `world_resource_packs.json`). If the pack_id already exists, replace its
111
+ * version. File missing → initialize as `[]`.
112
+ */
113
+ export declare function enablePackInWorld(opts: EnablePackOpts): Promise<void>;
114
+ /** Remove a pack_id from the world's enable-list JSON. */
115
+ export declare function disablePackInWorld(opts: EnablePackOpts): Promise<void>;
116
+ export declare function writeBehaviorPackManifest(outDir: string, header: {
117
+ name: string;
118
+ uuid: string;
119
+ version: [number, number, number];
120
+ description?: string | undefined;
121
+ }): Promise<void>;
122
+ export declare function writeResourcePackManifest(outDir: string, header: {
123
+ name: string;
124
+ uuid: string;
125
+ version: [number, number, number];
126
+ description?: string | undefined;
127
+ }): Promise<void>;
128
+ export { copyFileAsync, copyDirAsync };
129
+ /**
130
+ * Walk `<modulesDir>/<id>/resource_pack/` for every installed module and
131
+ * return a map { id: absoluteDir }. Modules without a resource_pack folder
132
+ * are silently skipped. Used by `assemble-rp` and the sfmc CLI to know which
133
+ * modules to merge into the resource pack output.
134
+ */
135
+ export declare function scanModuleResourcePacks(modulesDir: string): Record<string, string>;
136
+ //# sourceMappingURL=pack-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack-manager.d.ts","sourceRoot":"","sources":["../src/pack-manager.ts"],"names":[],"mappings":"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"}