@sfmc-bds/bds-tools 0.1.0 → 0.2.0-beta.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 (71) hide show
  1. package/dist/bds-manager.d.ts.map +1 -1
  2. package/dist/bds-manager.js +236 -263
  3. package/dist/bds-manager.js.map +7 -1
  4. package/dist/changelog.js +39 -50
  5. package/dist/changelog.js.map +7 -1
  6. package/dist/check-update.d.ts +2 -2
  7. package/dist/check-update.d.ts.map +1 -1
  8. package/dist/check-update.js +361 -401
  9. package/dist/check-update.js.map +7 -1
  10. package/dist/cli-pack-manager.d.ts +10 -6
  11. package/dist/cli-pack-manager.d.ts.map +1 -1
  12. package/dist/cli-pack-manager.js +226 -135
  13. package/dist/cli-pack-manager.js.map +7 -1
  14. package/dist/fsx.js +86 -100
  15. package/dist/fsx.js.map +7 -1
  16. package/dist/http.d.ts.map +1 -1
  17. package/dist/http.js +166 -185
  18. package/dist/http.js.map +7 -1
  19. package/dist/is-main.d.ts +17 -0
  20. package/dist/is-main.d.ts.map +1 -0
  21. package/dist/is-main.js +32 -0
  22. package/dist/is-main.js.map +7 -0
  23. package/dist/log.d.ts +2 -4
  24. package/dist/log.d.ts.map +1 -1
  25. package/dist/log.js +12 -19
  26. package/dist/log.js.map +7 -1
  27. package/dist/logger.js +6 -10
  28. package/dist/logger.js.map +7 -1
  29. package/dist/pack-manager.d.ts +88 -24
  30. package/dist/pack-manager.d.ts.map +1 -1
  31. package/dist/pack-manager.js +280 -245
  32. package/dist/pack-manager.js.map +7 -1
  33. package/dist/paths.d.ts +8 -3
  34. package/dist/paths.d.ts.map +1 -1
  35. package/dist/paths.js +53 -39
  36. package/dist/paths.js.map +7 -1
  37. package/dist/qqutil.d.ts.map +1 -1
  38. package/dist/qqutil.js +90 -87
  39. package/dist/qqutil.js.map +7 -1
  40. package/dist/recovery.js +26 -37
  41. package/dist/recovery.js.map +7 -1
  42. package/dist/rollback.d.ts +1 -1
  43. package/dist/rollback.js +55 -74
  44. package/dist/rollback.js.map +7 -1
  45. package/dist/server-properties.d.ts +12 -0
  46. package/dist/server-properties.d.ts.map +1 -0
  47. package/dist/server-properties.js +28 -0
  48. package/dist/server-properties.js.map +7 -0
  49. package/dist/taskbar.d.ts +2 -0
  50. package/dist/taskbar.d.ts.map +1 -1
  51. package/dist/taskbar.js +51 -82
  52. package/dist/taskbar.js.map +7 -1
  53. package/dist/types.js +1 -5
  54. package/dist/types.js.map +7 -1
  55. package/dist/update-result.d.ts +12 -0
  56. package/dist/update-result.d.ts.map +1 -0
  57. package/dist/update-result.js +15 -0
  58. package/dist/update-result.js.map +7 -0
  59. package/dist/upstream.js +94 -108
  60. package/dist/upstream.js.map +7 -1
  61. package/dist/version.js +63 -81
  62. package/dist/version.js.map +7 -1
  63. package/dist/world-packs.d.ts +112 -0
  64. package/dist/world-packs.d.ts.map +1 -0
  65. package/dist/world-packs.js +335 -0
  66. package/dist/world-packs.js.map +7 -0
  67. package/dist/zipx.d.ts +10 -0
  68. package/dist/zipx.d.ts.map +1 -0
  69. package/dist/zipx.js +48 -0
  70. package/dist/zipx.js.map +7 -0
  71. package/package.json +17 -6
package/dist/version.js CHANGED
@@ -1,97 +1,79 @@
1
- /**
2
- * version.ts — 版本号工具 / 版本缓存
3
- */
4
1
  import { readJson, writeJson } from "@sfmc-bds/sdk/node/config";
5
2
  import fs from "node:fs";
6
3
  import { VERSION_CACHE } from "./paths.js";
7
4
  import { hashFileSync, hashFileAsync } from "./fsx.js";
8
5
  import { log } from "./log.js";
9
- /** 4段 → 3段 (去尾) */
10
- export function toVer3(v) {
11
- return v.split(".").slice(0, 3).join(".");
6
+ function toVer3(v) {
7
+ return v.split(".").slice(0, 3).join(".");
12
8
  }
13
- /** 3段 → 4段 (补 0) */
14
- export function toVer4(v) {
15
- const p = v.split(".");
16
- while (p.length < 4)
17
- p.push("0");
18
- return p.join(".");
9
+ function toVer4(v) {
10
+ const p = v.split(".");
11
+ while (p.length < 4) p.push("0");
12
+ return p.join(".");
19
13
  }
20
- /** 版本比较 ( 4 段数字排序) */
21
- export function compareVersions(a, b) {
22
- const sa = a.replace(/-preview$/, "");
23
- const sb = b.replace(/-preview$/, "");
24
- const pa = sa.split(".").map(Number);
25
- const pb = sb.split(".").map(Number);
26
- for (let i = 0; i < 4; i++) {
27
- const ai = pa[i] ?? 0;
28
- const bi = pb[i] ?? 0;
29
- if (ai !== bi)
30
- return ai - bi;
31
- }
32
- // preview 低于不带 -preview 的同一数字
33
- if (a.endsWith("-preview") && !b.endsWith("-preview"))
34
- return -1;
35
- if (!a.endsWith("-preview") && b.endsWith("-preview"))
36
- return 1;
37
- return 0;
14
+ function compareVersions(a, b) {
15
+ const sa = a.replace(/-preview$/, "");
16
+ const sb = b.replace(/-preview$/, "");
17
+ const pa = sa.split(".").map(Number);
18
+ const pb = sb.split(".").map(Number);
19
+ for (let i = 0; i < 4; i++) {
20
+ const ai = pa[i] ?? 0;
21
+ const bi = pb[i] ?? 0;
22
+ if (ai !== bi) return ai - bi;
23
+ }
24
+ if (a.endsWith("-preview") && !b.endsWith("-preview")) return -1;
25
+ if (!a.endsWith("-preview") && b.endsWith("-preview")) return 1;
26
+ return 0;
38
27
  }
39
- /** 简单的真值校验 (按版本号外形) */
40
- export function isValidVersion(v) {
41
- return /^\d+\.\d+\.\d+(\.\d+)?(-preview)?$/.test(v);
28
+ function isValidVersion(v) {
29
+ return /^\d+\.\d+\.\d+(\.\d+)?(-preview)?$/.test(v);
42
30
  }
43
31
  function readCache() {
44
- return readJson(VERSION_CACHE) ?? {};
32
+ return readJson(VERSION_CACHE) ?? {};
45
33
  }
46
- /** 仅保留最近 3 条版本缓存 (避免无限增长) */
47
34
  function trimCache(cache) {
48
- const keys = Object.keys(cache).sort(compareVersions).slice(-3);
49
- const trimmed = {};
50
- for (const k of keys)
51
- trimmed[k] = cache[k];
52
- return trimmed;
35
+ const keys = Object.keys(cache).sort(compareVersions).slice(-3);
36
+ const trimmed = {};
37
+ for (const k of keys) trimmed[k] = cache[k];
38
+ return trimmed;
53
39
  }
54
- export function saveVersionCache(version, sha256) {
55
- const cache = readCache();
56
- cache[version] = { sha256, verified_at: Date.now() };
57
- try {
58
- writeJson(VERSION_CACHE, trimCache(cache));
59
- }
60
- catch {
61
- /* ignore */
62
- }
40
+ function saveVersionCache(version, sha256) {
41
+ const cache = readCache();
42
+ cache[version] = { sha256, verified_at: Date.now() };
43
+ try {
44
+ writeJson(VERSION_CACHE, trimCache(cache));
45
+ } catch {
46
+ }
63
47
  }
64
- /**
65
- * 异步识别当前 BDS 版本
66
- * 优先通过 SHA256 反查缓存,失败则回退 `current_version.txt`(如果存在)
67
- */
68
- export async function getCurrentVersionAsync(exePath) {
69
- if (!fs.existsSync(exePath))
70
- return "0.0.0.0";
71
- const actual = await hashFileAsync(exePath, "sha256");
72
- if (!actual)
73
- return "0.0.0.0";
74
- const cache = readCache();
75
- for (const [ver, entry] of Object.entries(cache)) {
76
- if (entry.sha256 === actual)
77
- return ver;
78
- }
79
- log.warn("[BDSUpdater] bedrock_server.exe 哈希未匹配缓存,疑似被改动或首次启动");
80
- return "0.0.0.0";
48
+ async function getCurrentVersionAsync(exePath) {
49
+ if (!fs.existsSync(exePath)) return "0.0.0.0";
50
+ const actual = await hashFileAsync(exePath, "sha256");
51
+ if (!actual) return "0.0.0.0";
52
+ const cache = readCache();
53
+ for (const [ver, entry] of Object.entries(cache)) {
54
+ if (entry.sha256 === actual) return ver;
55
+ }
56
+ log.warn("[BDSUpdater] bedrock_server.exe \u54C8\u5E0C\u672A\u5339\u914D\u7F13\u5B58\uFF0C\u7591\u4F3C\u88AB\u6539\u52A8\u6216\u9996\u6B21\u542F\u52A8");
57
+ return "0.0.0.0";
81
58
  }
82
- /** 同步版本 (兼容旧流程) */
83
- export function getCurrentVersionSync(exePath) {
84
- if (!fs.existsSync(exePath))
85
- return "0.0.0.0";
86
- const actual = hashFileSync(exePath, "sha256");
87
- if (!actual)
88
- return "0.0.0.0";
89
- const cache = readCache();
90
- for (const [ver, entry] of Object.entries(cache)) {
91
- if (entry.sha256 === actual)
92
- return ver;
93
- }
94
- log.warn("[BDSUpdater] bedrock_server.exe 哈希未匹配缓存");
95
- return "0.0.0.0";
59
+ function getCurrentVersionSync(exePath) {
60
+ if (!fs.existsSync(exePath)) return "0.0.0.0";
61
+ const actual = hashFileSync(exePath, "sha256");
62
+ if (!actual) return "0.0.0.0";
63
+ const cache = readCache();
64
+ for (const [ver, entry] of Object.entries(cache)) {
65
+ if (entry.sha256 === actual) return ver;
66
+ }
67
+ log.warn("[BDSUpdater] bedrock_server.exe \u54C8\u5E0C\u672A\u5339\u914D\u7F13\u5B58");
68
+ return "0.0.0.0";
96
69
  }
97
- //# sourceMappingURL=version.js.map
70
+ export {
71
+ compareVersions,
72
+ getCurrentVersionAsync,
73
+ getCurrentVersionSync,
74
+ isValidVersion,
75
+ saveVersionCache,
76
+ toVer3,
77
+ toVer4
78
+ };
79
+ //# sourceMappingURL=version.js.map
@@ -1 +1,7 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"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"}
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/version.ts"],
4
+ "sourcesContent": ["/**\n * version.ts \u2014 \u7248\u672C\u53F7\u5DE5\u5177 / \u7248\u672C\u7F13\u5B58\n */\n\nimport { readJson, writeJson } from \"@sfmc-bds/sdk/node/config\";\nimport fs from \"node:fs\";\nimport { VERSION_CACHE } from \"./paths.js\";\nimport { hashFileSync, hashFileAsync } from \"./fsx.js\";\nimport { log } from \"./log.js\";\n\n/** 4\u6BB5 \u2192 3\u6BB5 (\u53BB\u5C3E) */\nexport function toVer3(v: string): string {\n return v.split(\".\").slice(0, 3).join(\".\");\n}\n\n/** 3\u6BB5 \u2192 4\u6BB5 (\u8865 0) */\nexport function toVer4(v: string): string {\n const p = v.split(\".\");\n while (p.length < 4) p.push(\"0\");\n return p.join(\".\");\n}\n\n/** \u7248\u672C\u6BD4\u8F83 (\u6309 4 \u6BB5\u6570\u5B57\u6392\u5E8F) */\nexport function compareVersions(a: string, b: string): number {\n const sa = a.replace(/-preview$/, \"\");\n const sb = b.replace(/-preview$/, \"\");\n const pa = sa.split(\".\").map(Number);\n const pb = sb.split(\".\").map(Number);\n for (let i = 0; i < 4; i++) {\n const ai = pa[i] ?? 0;\n const bi = pb[i] ?? 0;\n if (ai !== bi) return ai - bi;\n }\n // preview \u4F4E\u4E8E\u4E0D\u5E26 -preview \u7684\u540C\u4E00\u6570\u5B57\n if (a.endsWith(\"-preview\") && !b.endsWith(\"-preview\")) return -1;\n if (!a.endsWith(\"-preview\") && b.endsWith(\"-preview\")) return 1;\n return 0;\n}\n\n/** \u7B80\u5355\u7684\u771F\u503C\u6821\u9A8C (\u6309\u7248\u672C\u53F7\u5916\u5F62) */\nexport function isValidVersion(v: string): boolean {\n return /^\\d+\\.\\d+\\.\\d+(\\.\\d+)?(-preview)?$/.test(v);\n}\n\n/** \u7F13\u5B58\u6761\u76EE\u7C7B\u578B */\ninterface CacheEntry {\n sha256: string;\n verified_at: number;\n}\n\nfunction readCache(): Record<string, CacheEntry> {\n return readJson<Record<string, CacheEntry>>(VERSION_CACHE) ?? {};\n}\n\n/** \u4EC5\u4FDD\u7559\u6700\u8FD1 3 \u6761\u7248\u672C\u7F13\u5B58 (\u907F\u514D\u65E0\u9650\u589E\u957F) */\nfunction trimCache(cache: Record<string, CacheEntry>): Record<string, CacheEntry> {\n const keys = Object.keys(cache).sort(compareVersions).slice(-3);\n const trimmed: Record<string, CacheEntry> = {};\n for (const k of keys) trimmed[k] = cache[k]!;\n return trimmed;\n}\n\nexport function saveVersionCache(version: string, sha256: string): void {\n const cache = readCache();\n cache[version] = { sha256, verified_at: Date.now() };\n try {\n writeJson(VERSION_CACHE, trimCache(cache));\n } catch {\n /* ignore */\n }\n}\n\n/**\n * \u5F02\u6B65\u8BC6\u522B\u5F53\u524D BDS \u7248\u672C\n * \u4F18\u5148\u901A\u8FC7 SHA256 \u53CD\u67E5\u7F13\u5B58\uFF0C\u5931\u8D25\u5219\u56DE\u9000 `current_version.txt`\uFF08\u5982\u679C\u5B58\u5728\uFF09\n */\nexport async function getCurrentVersionAsync(exePath: string): Promise<string> {\n if (!fs.existsSync(exePath)) return \"0.0.0.0\";\n const actual = await hashFileAsync(exePath, \"sha256\");\n if (!actual) return \"0.0.0.0\";\n\n const cache = readCache();\n for (const [ver, entry] of Object.entries(cache)) {\n if (entry.sha256 === actual) return ver;\n }\n log.warn(\"[BDSUpdater] bedrock_server.exe \u54C8\u5E0C\u672A\u5339\u914D\u7F13\u5B58\uFF0C\u7591\u4F3C\u88AB\u6539\u52A8\u6216\u9996\u6B21\u542F\u52A8\");\n return \"0.0.0.0\";\n}\n\n/** \u540C\u6B65\u7248\u672C (\u517C\u5BB9\u65E7\u6D41\u7A0B) */\nexport function getCurrentVersionSync(exePath: string): string {\n if (!fs.existsSync(exePath)) return \"0.0.0.0\";\n const actual = hashFileSync(exePath, \"sha256\");\n if (!actual) return \"0.0.0.0\";\n const cache = readCache();\n for (const [ver, entry] of Object.entries(cache)) {\n if (entry.sha256 === actual) return ver;\n }\n log.warn(\"[BDSUpdater] bedrock_server.exe \u54C8\u5E0C\u672A\u5339\u914D\u7F13\u5B58\");\n return \"0.0.0.0\";\n}\n"],
5
+ "mappings": "AAIA,SAAS,UAAU,iBAAiB;AACpC,OAAO,QAAQ;AACf,SAAS,qBAAqB;AAC9B,SAAS,cAAc,qBAAqB;AAC5C,SAAS,WAAW;AAGb,SAAS,OAAO,GAAmB;AACxC,SAAO,EAAE,MAAM,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AAC1C;AAGO,SAAS,OAAO,GAAmB;AACxC,QAAM,IAAI,EAAE,MAAM,GAAG;AACrB,SAAO,EAAE,SAAS,EAAG,GAAE,KAAK,GAAG;AAC/B,SAAO,EAAE,KAAK,GAAG;AACnB;AAGO,SAAS,gBAAgB,GAAW,GAAmB;AAC5D,QAAM,KAAK,EAAE,QAAQ,aAAa,EAAE;AACpC,QAAM,KAAK,EAAE,QAAQ,aAAa,EAAE;AACpC,QAAM,KAAK,GAAG,MAAM,GAAG,EAAE,IAAI,MAAM;AACnC,QAAM,KAAK,GAAG,MAAM,GAAG,EAAE,IAAI,MAAM;AACnC,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,KAAK,GAAG,CAAC,KAAK;AACpB,UAAM,KAAK,GAAG,CAAC,KAAK;AACpB,QAAI,OAAO,GAAI,QAAO,KAAK;AAAA,EAC7B;AAEA,MAAI,EAAE,SAAS,UAAU,KAAK,CAAC,EAAE,SAAS,UAAU,EAAG,QAAO;AAC9D,MAAI,CAAC,EAAE,SAAS,UAAU,KAAK,EAAE,SAAS,UAAU,EAAG,QAAO;AAC9D,SAAO;AACT;AAGO,SAAS,eAAe,GAAoB;AACjD,SAAO,qCAAqC,KAAK,CAAC;AACpD;AAQA,SAAS,YAAwC;AAC/C,SAAO,SAAqC,aAAa,KAAK,CAAC;AACjE;AAGA,SAAS,UAAU,OAA+D;AAChF,QAAM,OAAO,OAAO,KAAK,KAAK,EAAE,KAAK,eAAe,EAAE,MAAM,EAAE;AAC9D,QAAM,UAAsC,CAAC;AAC7C,aAAW,KAAK,KAAM,SAAQ,CAAC,IAAI,MAAM,CAAC;AAC1C,SAAO;AACT;AAEO,SAAS,iBAAiB,SAAiB,QAAsB;AACtE,QAAM,QAAQ,UAAU;AACxB,QAAM,OAAO,IAAI,EAAE,QAAQ,aAAa,KAAK,IAAI,EAAE;AACnD,MAAI;AACF,cAAU,eAAe,UAAU,KAAK,CAAC;AAAA,EAC3C,QAAQ;AAAA,EAER;AACF;AAMA,eAAsB,uBAAuB,SAAkC;AAC7E,MAAI,CAAC,GAAG,WAAW,OAAO,EAAG,QAAO;AACpC,QAAM,SAAS,MAAM,cAAc,SAAS,QAAQ;AACpD,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,QAAQ,UAAU;AACxB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,MAAM,WAAW,OAAQ,QAAO;AAAA,EACtC;AACA,MAAI,KAAK,8IAAoD;AAC7D,SAAO;AACT;AAGO,SAAS,sBAAsB,SAAyB;AAC7D,MAAI,CAAC,GAAG,WAAW,OAAO,EAAG,QAAO;AACpC,QAAM,SAAS,aAAa,SAAS,QAAQ;AAC7C,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,QAAQ,UAAU;AACxB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,MAAM,WAAW,OAAQ,QAAO;AAAA,EACtC;AACA,MAAI,KAAK,4EAAyC;AAClD,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,112 @@
1
+ import { type WorldPackListReadResult } from "./pack-manager.js";
2
+ export type WorldPackKind = "behavior" | "resource";
3
+ export interface PackManifestInfo {
4
+ name: string;
5
+ uuid: string;
6
+ version: [number, number, number];
7
+ kind: WorldPackKind;
8
+ description?: string;
9
+ }
10
+ export interface InstalledWorldPack {
11
+ kind: WorldPackKind;
12
+ folderName: string;
13
+ dir: string;
14
+ name: string;
15
+ uuid: string;
16
+ version: [number, number, number];
17
+ enabled: boolean;
18
+ }
19
+ /** 滤掉 §x(Minecraft 格式码)、残余压缩后缀,加 [BP]/[RP] 前缀 */
20
+ export declare function formatWorldPackFolderName(rawName: string, kind: WorldPackKind): string;
21
+ /** 根据 manifest modules[].type 判定 BP / RP */
22
+ export declare function detectPackKindFromManifest(raw: {
23
+ modules?: Array<{
24
+ type?: string;
25
+ }>;
26
+ }): WorldPackKind | null;
27
+ export declare function readPackManifestInfo(packDir: string): PackManifestInfo | null;
28
+ /** 深度优先找含 manifest.json 的包根(默认 maxDepth=2,root 自身为 depth 0) */
29
+ export declare function discoverPackRoots(root: string, opts?: {
30
+ maxDepth?: number;
31
+ }): string[];
32
+ export declare function isPackArchive(filePath: string): boolean;
33
+ /** 解压 zip/mcpack/mcaddon 到临时目录,返回该目录(经 zipx 防 zip-slip) */
34
+ export declare function extractArchiveToTemp(filePath: string): Promise<string>;
35
+ /** 扫描世界内已安装 BP/RP 目录 */
36
+ export declare function listInstalledWorldPacks(bdsRoot: string, levelName: string): InstalledWorldPack[];
37
+ /** RP/BP 第三位版本 +1,写回 manifest */
38
+ export declare function bumpPackPatchVersion(packDir: string): [number, number, number];
39
+ /** 写入 header.version(并同步 modules[].version 的 patch) */
40
+ export declare function writePackHeaderVersion(packDir: string, next: [number, number, number]): [number, number, number];
41
+ /** 读取 BP manifest 中依赖的资源包 uuid(非 @minecraft 模块) */
42
+ export declare function readPackDependencyUuids(packDir: string): string[];
43
+ /** 确保版本严格大于 floor(patch/minor 抬一级) */
44
+ export declare function ensureVersionGreaterThan(packDir: string, floor: [number, number, number], component?: "patch" | "minor"): [number, number, number];
45
+ export interface InstallPackResult {
46
+ ok: boolean;
47
+ skipped?: boolean;
48
+ reason?: string;
49
+ destDir?: string;
50
+ folderName?: string;
51
+ info?: PackManifestInfo;
52
+ conflict?: {
53
+ existing: PackManifestInfo & {
54
+ dir: string;
55
+ };
56
+ incoming: PackManifestInfo;
57
+ };
58
+ }
59
+ /**
60
+ * 将包目录安装到 destParent(behavior_packs 或 resource_packs)。
61
+ * force=true 时覆盖冲突;否则返回 conflict 供上层询问。
62
+ */
63
+ export declare function installPackDirectory(opts: {
64
+ srcDir: string;
65
+ destParent: string;
66
+ folderName?: string;
67
+ force?: boolean;
68
+ }): Promise<InstallPackResult>;
69
+ /** 安装后写入世界 enable 清单 */
70
+ export declare function enableInstalledPack(opts: {
71
+ bdsRoot: string;
72
+ levelName: string;
73
+ info: PackManifestInfo;
74
+ }): Promise<void>;
75
+ /** 从世界 enable 清单移除 */
76
+ export declare function disableInstalledPack(opts: {
77
+ bdsRoot: string;
78
+ levelName: string;
79
+ kind: WorldPackKind;
80
+ packUuid: string;
81
+ version: [number, number, number];
82
+ }): Promise<void>;
83
+ export type UninstallPackResult = {
84
+ action: "trashed";
85
+ dest: string;
86
+ } | {
87
+ action: "deleted";
88
+ } | {
89
+ action: "missing";
90
+ };
91
+ export type UninstallPackAction = UninstallPackResult["action"];
92
+ /**
93
+ * 卸载已安装世界包:先 disable enable-list,再移入 trashDir 或直接删除目录。
94
+ * trashDir 跨盘符时 fallback 为 copy + rm。
95
+ */
96
+ export declare function uninstallInstalledPack(opts: {
97
+ bdsRoot: string;
98
+ levelName: string;
99
+ pack: InstalledWorldPack;
100
+ /** 提供则移入该目录;null/省略则直接删除 */
101
+ trashDir?: string | null;
102
+ }): Promise<UninstallPackResult>;
103
+ export declare function worldPackParentDir(bdsRoot: string, levelName: string, kind: WorldPackKind): string;
104
+ /** 世界 enable-list 权威读取 — 委托 pack-manager(供 doctor 等,避免硬编码文件名) */
105
+ export declare function listWorldEnableEntries(bdsRoot: string, levelName: string, kind: WorldPackKind): Array<{
106
+ pack_id: string;
107
+ version: [number, number, number];
108
+ }>;
109
+ /** 含 parseFail 信号的 enable-list 快照(doctor 用;不暴露 JSON 路径构造细节) */
110
+ export declare function listWorldEnableListResult(bdsRoot: string, levelName: string, kind: WorldPackKind): WorldPackListReadResult;
111
+ export declare function findInstalledPackById(packs: InstalledWorldPack[], id: string): InstalledWorldPack | null;
112
+ //# sourceMappingURL=world-packs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"world-packs.d.ts","sourceRoot":"","sources":["../src/world-packs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,335 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { copyDirAsync } from "./fsx.js";
5
+ import {
6
+ bdsWorldLevelDir,
7
+ bdsWorldsDir,
8
+ disablePackInWorld,
9
+ enablePackInWorld,
10
+ readPackManifestHeader,
11
+ readWorldPackList,
12
+ readWorldPackListResult
13
+ } from "./pack-manager.js";
14
+ import { extractZipFileToDir } from "./zipx.js";
15
+ const ARCHIVE_EXTS = [".zip", ".mcpack", ".mcaddon"];
16
+ function formatWorldPackFolderName(rawName, kind) {
17
+ let s = String(rawName ?? "");
18
+ s = s.replace(/§[0-9a-zA-Z]/g, "");
19
+ s = s.replace(/\.(zip|mcpack|mcaddon)$/i, "");
20
+ s = s.replace(/\s+/g, " ").trim();
21
+ if (!s) s = "pack";
22
+ const prefix = kind === "resource" ? "[RP]" : "[BP]";
23
+ if (s.startsWith("[RP]") || s.startsWith("[BP]")) return s;
24
+ return `${prefix} ${s}`;
25
+ }
26
+ function detectPackKindFromManifest(raw) {
27
+ const types = (raw.modules ?? []).map((m) => String(m.type ?? "").toLowerCase());
28
+ if (types.includes("resources")) return "resource";
29
+ if (types.some((t) => t === "script" || t === "data" || t === "javascript")) return "behavior";
30
+ return null;
31
+ }
32
+ function readPackManifestInfo(packDir) {
33
+ const file = path.join(packDir, "manifest.json");
34
+ if (!fs.existsSync(file)) return null;
35
+ try {
36
+ const raw = JSON.parse(fs.readFileSync(file, "utf8"));
37
+ const kind = detectPackKindFromManifest(raw);
38
+ const uuid = raw.header?.uuid;
39
+ const ver = raw.header?.version;
40
+ const name = raw.header?.name;
41
+ if (!kind || typeof uuid !== "string" || !Array.isArray(ver) || ver.length < 3) return null;
42
+ if (typeof name !== "string" || !name) return null;
43
+ const version = [Number(ver[0]), Number(ver[1]), Number(ver[2])];
44
+ const description = raw.header?.description;
45
+ return {
46
+ name,
47
+ uuid,
48
+ version,
49
+ kind,
50
+ ...typeof description === "string" ? { description } : {}
51
+ };
52
+ } catch {
53
+ return null;
54
+ }
55
+ }
56
+ function discoverPackRoots(root, opts) {
57
+ const maxDepth = opts?.maxDepth ?? 2;
58
+ const out = [];
59
+ const abs = path.resolve(root);
60
+ if (!fs.existsSync(abs)) return out;
61
+ function walk(dir, depth) {
62
+ const manifest = path.join(dir, "manifest.json");
63
+ if (fs.existsSync(manifest) && fs.statSync(manifest).isFile()) {
64
+ out.push(dir);
65
+ return;
66
+ }
67
+ if (depth >= maxDepth) return;
68
+ let entries;
69
+ try {
70
+ entries = fs.readdirSync(dir, { withFileTypes: true });
71
+ } catch {
72
+ return;
73
+ }
74
+ for (const e of entries) {
75
+ if (!e.isDirectory()) continue;
76
+ if (e.name.startsWith(".") || e.name === "node_modules") continue;
77
+ walk(path.join(dir, e.name), depth + 1);
78
+ }
79
+ }
80
+ const st = fs.statSync(abs);
81
+ if (st.isDirectory()) walk(abs, 0);
82
+ return out;
83
+ }
84
+ function isPackArchive(filePath) {
85
+ const lower = filePath.toLowerCase();
86
+ return ARCHIVE_EXTS.some((ext) => lower.endsWith(ext));
87
+ }
88
+ async function extractArchiveToTemp(filePath) {
89
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "sfmc-pack-"));
90
+ await extractZipFileToDir(filePath, tmp);
91
+ return tmp;
92
+ }
93
+ function listPackDirsIn(parent) {
94
+ if (!fs.existsSync(parent)) return [];
95
+ const out = [];
96
+ for (const e of fs.readdirSync(parent, { withFileTypes: true })) {
97
+ if (!e.isDirectory()) continue;
98
+ const dir = path.join(parent, e.name);
99
+ if (fs.existsSync(path.join(dir, "manifest.json"))) out.push(dir);
100
+ }
101
+ return out;
102
+ }
103
+ function listInstalledWorldPacks(bdsRoot, levelName) {
104
+ const worldRoot = bdsWorldLevelDir(bdsRoot, levelName);
105
+ const worldsDir = bdsWorldsDir(bdsRoot);
106
+ const result = [];
107
+ for (const kind of ["behavior", "resource"]) {
108
+ const parent = path.join(
109
+ worldRoot,
110
+ kind === "behavior" ? "behavior_packs" : "resource_packs"
111
+ );
112
+ const enabledList = readWorldPackList(worldsDir, levelName, kind);
113
+ const enabledMap = new Map(enabledList.map((e) => [e.pack_id, e.version]));
114
+ for (const dir of listPackDirsIn(parent)) {
115
+ const info = readPackManifestInfo(dir);
116
+ if (!info || info.kind !== kind) {
117
+ const header = readPackManifestHeader(dir);
118
+ if (!header) continue;
119
+ result.push({
120
+ kind,
121
+ folderName: path.basename(dir),
122
+ dir,
123
+ name: path.basename(dir),
124
+ uuid: header.uuid,
125
+ version: header.version,
126
+ enabled: enabledMap.has(header.uuid)
127
+ });
128
+ continue;
129
+ }
130
+ result.push({
131
+ kind: info.kind,
132
+ folderName: path.basename(dir),
133
+ dir,
134
+ name: info.name,
135
+ uuid: info.uuid,
136
+ version: info.version,
137
+ enabled: enabledMap.has(info.uuid)
138
+ });
139
+ }
140
+ }
141
+ return result;
142
+ }
143
+ function bumpPackPatchVersion(packDir) {
144
+ const file = path.join(packDir, "manifest.json");
145
+ if (!fs.existsSync(file)) throw new Error(`manifest.json missing: ${packDir}`);
146
+ const raw = JSON.parse(fs.readFileSync(file, "utf8"));
147
+ const ver = raw.header?.version;
148
+ if (!Array.isArray(ver) || ver.length < 3) throw new Error(`invalid header.version in ${file}`);
149
+ const next = [Number(ver[0]), Number(ver[1]), Number(ver[2]) + 1];
150
+ return writePackHeaderVersion(packDir, next);
151
+ }
152
+ function writePackHeaderVersion(packDir, next) {
153
+ const file = path.join(packDir, "manifest.json");
154
+ if (!fs.existsSync(file)) throw new Error(`manifest.json missing: ${packDir}`);
155
+ const raw = JSON.parse(fs.readFileSync(file, "utf8"));
156
+ if (!raw.header) raw.header = {};
157
+ raw.header.version = next;
158
+ if (Array.isArray(raw.modules)) {
159
+ for (const m of raw.modules) {
160
+ if (Array.isArray(m.version) && m.version.length >= 3) {
161
+ m.version = [Number(m.version[0]), Number(m.version[1]), next[2]];
162
+ }
163
+ }
164
+ }
165
+ fs.writeFileSync(file, `${JSON.stringify(raw, null, 2)}
166
+ `, "utf8");
167
+ return next;
168
+ }
169
+ function readPackDependencyUuids(packDir) {
170
+ const file = path.join(packDir, "manifest.json");
171
+ if (!fs.existsSync(file)) return [];
172
+ try {
173
+ const raw = JSON.parse(fs.readFileSync(file, "utf8"));
174
+ return (raw.dependencies ?? []).map((d) => d.uuid).filter((u) => typeof u === "string" && u.length > 0);
175
+ } catch {
176
+ return [];
177
+ }
178
+ }
179
+ function ensureVersionGreaterThan(packDir, floor, component = "patch") {
180
+ const info = readPackManifestInfo(packDir);
181
+ if (!info) throw new Error(`cannot read pack version: ${packDir}`);
182
+ let next = component === "minor" ? [info.version[0], info.version[1] + 1, 0] : [info.version[0], info.version[1], info.version[2] + 1];
183
+ const cmp = (a, b) => {
184
+ for (let i = 0; i < 3; i++) {
185
+ const d = a[i] - b[i];
186
+ if (d !== 0) return d;
187
+ }
188
+ return 0;
189
+ };
190
+ while (cmp(next, floor) <= 0) {
191
+ if (component === "minor") next = [next[0], next[1] + 1, 0];
192
+ else next = [next[0], next[1], next[2] + 1];
193
+ }
194
+ return writePackHeaderVersion(packDir, next);
195
+ }
196
+ async function installPackDirectory(opts) {
197
+ const info = readPackManifestInfo(opts.srcDir);
198
+ if (!info) {
199
+ return { ok: false, reason: "invalid or unrecognized manifest.json" };
200
+ }
201
+ const folderName = formatWorldPackFolderName(
202
+ opts.folderName ?? path.basename(opts.srcDir),
203
+ info.kind
204
+ );
205
+ await fs.promises.mkdir(opts.destParent, { recursive: true });
206
+ let conflictDir = null;
207
+ let conflictExisting = null;
208
+ for (const dir of listPackDirsIn(opts.destParent)) {
209
+ const existing = readPackManifestInfo(dir);
210
+ const sameFolder = path.basename(dir) === folderName;
211
+ const sameUuid = !!existing && existing.uuid === info.uuid;
212
+ if (!sameFolder && !sameUuid) continue;
213
+ conflictDir = dir;
214
+ if (existing) {
215
+ conflictExisting = existing;
216
+ } else {
217
+ const header = readPackManifestHeader(dir);
218
+ conflictExisting = {
219
+ name: path.basename(dir),
220
+ uuid: header?.uuid ?? "(unknown)",
221
+ version: header?.version ?? [0, 0, 0],
222
+ kind: info.kind
223
+ };
224
+ }
225
+ break;
226
+ }
227
+ if (conflictDir && !opts.force) {
228
+ return {
229
+ ok: false,
230
+ skipped: true,
231
+ reason: "conflict",
232
+ conflict: {
233
+ existing: { ...conflictExisting ?? info, dir: conflictDir },
234
+ incoming: info
235
+ }
236
+ };
237
+ }
238
+ const destDir = path.join(opts.destParent, folderName);
239
+ if (conflictDir && path.resolve(conflictDir) !== path.resolve(destDir)) {
240
+ await fs.promises.rm(conflictDir, { recursive: true, force: true });
241
+ }
242
+ if (fs.existsSync(destDir)) {
243
+ await fs.promises.rm(destDir, { recursive: true, force: true });
244
+ }
245
+ await copyDirAsync(opts.srcDir, destDir);
246
+ return { ok: true, destDir, folderName, info };
247
+ }
248
+ async function enableInstalledPack(opts) {
249
+ await enablePackInWorld({
250
+ worldsDir: bdsWorldsDir(opts.bdsRoot),
251
+ levelName: opts.levelName,
252
+ kind: opts.info.kind,
253
+ packUuid: opts.info.uuid,
254
+ version: opts.info.version
255
+ });
256
+ }
257
+ async function disableInstalledPack(opts) {
258
+ await disablePackInWorld({
259
+ worldsDir: bdsWorldsDir(opts.bdsRoot),
260
+ levelName: opts.levelName,
261
+ kind: opts.kind,
262
+ packUuid: opts.packUuid,
263
+ version: opts.version
264
+ });
265
+ }
266
+ async function uninstallInstalledPack(opts) {
267
+ await disableInstalledPack({
268
+ bdsRoot: opts.bdsRoot,
269
+ levelName: opts.levelName,
270
+ kind: opts.pack.kind,
271
+ packUuid: opts.pack.uuid,
272
+ version: opts.pack.version
273
+ });
274
+ if (!fs.existsSync(opts.pack.dir)) {
275
+ return { action: "missing" };
276
+ }
277
+ const trashDir = opts.trashDir?.trim() || null;
278
+ if (trashDir) {
279
+ await fs.promises.mkdir(trashDir, { recursive: true });
280
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
281
+ let dest = path.join(trashDir, opts.pack.folderName);
282
+ if (fs.existsSync(dest)) {
283
+ dest = path.join(trashDir, `${opts.pack.folderName}-${stamp}`);
284
+ }
285
+ try {
286
+ await fs.promises.rename(opts.pack.dir, dest);
287
+ } catch {
288
+ await copyDirAsync(opts.pack.dir, dest);
289
+ await fs.promises.rm(opts.pack.dir, { recursive: true, force: true });
290
+ }
291
+ return { action: "trashed", dest };
292
+ }
293
+ await fs.promises.rm(opts.pack.dir, { recursive: true, force: true });
294
+ return { action: "deleted" };
295
+ }
296
+ function worldPackParentDir(bdsRoot, levelName, kind) {
297
+ return path.join(
298
+ bdsWorldLevelDir(bdsRoot, levelName),
299
+ kind === "behavior" ? "behavior_packs" : "resource_packs"
300
+ );
301
+ }
302
+ function listWorldEnableEntries(bdsRoot, levelName, kind) {
303
+ return listWorldEnableListResult(bdsRoot, levelName, kind).entries;
304
+ }
305
+ function listWorldEnableListResult(bdsRoot, levelName, kind) {
306
+ return readWorldPackListResult(bdsWorldsDir(bdsRoot), levelName, kind);
307
+ }
308
+ function findInstalledPackById(packs, id) {
309
+ const q = id.trim().toLowerCase();
310
+ return packs.find(
311
+ (p) => p.uuid.toLowerCase() === q || p.folderName.toLowerCase() === q || p.name.toLowerCase() === q || p.folderName.toLowerCase().includes(q) || p.name.toLowerCase().includes(q)
312
+ ) ?? null;
313
+ }
314
+ export {
315
+ bumpPackPatchVersion,
316
+ detectPackKindFromManifest,
317
+ disableInstalledPack,
318
+ discoverPackRoots,
319
+ enableInstalledPack,
320
+ ensureVersionGreaterThan,
321
+ extractArchiveToTemp,
322
+ findInstalledPackById,
323
+ formatWorldPackFolderName,
324
+ installPackDirectory,
325
+ isPackArchive,
326
+ listInstalledWorldPacks,
327
+ listWorldEnableEntries,
328
+ listWorldEnableListResult,
329
+ readPackDependencyUuids,
330
+ readPackManifestInfo,
331
+ uninstallInstalledPack,
332
+ worldPackParentDir,
333
+ writePackHeaderVersion
334
+ };
335
+ //# sourceMappingURL=world-packs.js.map