@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
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/world-packs.ts"],
4
+ "sourcesContent": ["/**\n * world-packs.ts \u2014 \u901A\u7528\u4E16\u754C BP/RP \u53D1\u73B0 / \u5B89\u88C5 / bump\uFF08\u975E SFMC \u6A21\u5757\u805A\u5408\uFF09\n */\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { copyDirAsync } from \"./fsx.js\";\nimport {\n bdsWorldLevelDir,\n bdsWorldsDir,\n disablePackInWorld,\n enablePackInWorld,\n readPackManifestHeader,\n readWorldPackList,\n readWorldPackListResult,\n type WorldPackListReadResult,\n} from \"./pack-manager.js\";\nimport { extractZipFileToDir } from \"./zipx.js\";\n\nexport type WorldPackKind = \"behavior\" | \"resource\";\n\nexport interface PackManifestInfo {\n name: string;\n uuid: string;\n version: [number, number, number];\n kind: WorldPackKind;\n description?: string;\n}\n\nexport interface InstalledWorldPack {\n kind: WorldPackKind;\n folderName: string;\n dir: string;\n name: string;\n uuid: string;\n version: [number, number, number];\n enabled: boolean;\n}\n\nconst ARCHIVE_EXTS = [\".zip\", \".mcpack\", \".mcaddon\"] as const;\n\n/** \u6EE4\u6389 \u00A7x\uFF08Minecraft \u683C\u5F0F\u7801\uFF09\u3001\u6B8B\u4F59\u538B\u7F29\u540E\u7F00\uFF0C\u52A0 [BP]/[RP] \u524D\u7F00 */\nexport function formatWorldPackFolderName(rawName: string, kind: WorldPackKind): string {\n let s = String(rawName ?? \"\");\n s = s.replace(/\u00A7[0-9a-zA-Z]/g, \"\");\n s = s.replace(/\\.(zip|mcpack|mcaddon)$/i, \"\");\n s = s.replace(/\\s+/g, \" \").trim();\n if (!s) s = \"pack\";\n const prefix = kind === \"resource\" ? \"[RP]\" : \"[BP]\";\n if (s.startsWith(\"[RP]\") || s.startsWith(\"[BP]\")) return s;\n return `${prefix} ${s}`;\n}\n\n/** \u6839\u636E manifest modules[].type \u5224\u5B9A BP / RP */\nexport function detectPackKindFromManifest(raw: {\n modules?: Array<{ type?: string }>;\n}): WorldPackKind | null {\n const types = (raw.modules ?? []).map((m) => String(m.type ?? \"\").toLowerCase());\n if (types.includes(\"resources\")) return \"resource\";\n if (types.some((t) => t === \"script\" || t === \"data\" || t === \"javascript\")) return \"behavior\";\n return null;\n}\n\nexport function readPackManifestInfo(packDir: string): PackManifestInfo | null {\n const file = path.join(packDir, \"manifest.json\");\n if (!fs.existsSync(file)) return null;\n try {\n const raw = JSON.parse(fs.readFileSync(file, \"utf8\")) as {\n header?: { name?: string; uuid?: string; version?: number[]; description?: string };\n modules?: Array<{ type?: string }>;\n };\n const kind = detectPackKindFromManifest(raw);\n const uuid = raw.header?.uuid;\n const ver = raw.header?.version;\n const name = raw.header?.name;\n if (!kind || typeof uuid !== \"string\" || !Array.isArray(ver) || ver.length < 3) return null;\n if (typeof name !== \"string\" || !name) return null;\n const version: [number, number, number] = [Number(ver[0]), Number(ver[1]), Number(ver[2])];\n const description = raw.header?.description;\n return {\n name,\n uuid,\n version,\n kind,\n ...(typeof description === \"string\" ? { description } : {}),\n };\n } catch {\n return null;\n }\n}\n\n/** \u6DF1\u5EA6\u4F18\u5148\u627E\u542B manifest.json \u7684\u5305\u6839\uFF08\u9ED8\u8BA4 maxDepth=2\uFF0Croot \u81EA\u8EAB\u4E3A depth 0\uFF09 */\nexport function discoverPackRoots(root: string, opts?: { maxDepth?: number }): string[] {\n const maxDepth = opts?.maxDepth ?? 2;\n const out: string[] = [];\n const abs = path.resolve(root);\n if (!fs.existsSync(abs)) return out;\n\n function walk(dir: string, depth: number): void {\n const manifest = path.join(dir, \"manifest.json\");\n if (fs.existsSync(manifest) && fs.statSync(manifest).isFile()) {\n out.push(dir);\n return; // \u5305\u6839\u4E0D\u518D\u4E0B\u94BB\n }\n if (depth >= maxDepth) return;\n let entries;\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const e of entries) {\n if (!e.isDirectory()) continue;\n if (e.name.startsWith(\".\") || e.name === \"node_modules\") continue;\n walk(path.join(dir, e.name), depth + 1);\n }\n }\n\n const st = fs.statSync(abs);\n if (st.isDirectory()) walk(abs, 0);\n return out;\n}\n\nexport function isPackArchive(filePath: string): boolean {\n const lower = filePath.toLowerCase();\n return ARCHIVE_EXTS.some((ext) => lower.endsWith(ext));\n}\n\n/** \u89E3\u538B zip/mcpack/mcaddon \u5230\u4E34\u65F6\u76EE\u5F55\uFF0C\u8FD4\u56DE\u8BE5\u76EE\u5F55\uFF08\u7ECF zipx \u9632 zip-slip\uFF09 */\nexport async function extractArchiveToTemp(filePath: string): Promise<string> {\n const tmp = fs.mkdtempSync(path.join(os.tmpdir(), \"sfmc-pack-\"));\n await extractZipFileToDir(filePath, tmp);\n return tmp;\n}\n\nfunction listPackDirsIn(parent: string): string[] {\n if (!fs.existsSync(parent)) return [];\n const out: string[] = [];\n for (const e of fs.readdirSync(parent, { withFileTypes: true })) {\n if (!e.isDirectory()) continue;\n const dir = path.join(parent, e.name);\n if (fs.existsSync(path.join(dir, \"manifest.json\"))) out.push(dir);\n }\n return out;\n}\n\n/** \u626B\u63CF\u4E16\u754C\u5185\u5DF2\u5B89\u88C5 BP/RP \u76EE\u5F55 */\nexport function listInstalledWorldPacks(bdsRoot: string, levelName: string): InstalledWorldPack[] {\n const worldRoot = bdsWorldLevelDir(bdsRoot, levelName);\n const worldsDir = bdsWorldsDir(bdsRoot);\n const result: InstalledWorldPack[] = [];\n\n for (const kind of [\"behavior\", \"resource\"] as const) {\n const parent = path.join(\n worldRoot,\n kind === \"behavior\" ? \"behavior_packs\" : \"resource_packs\"\n );\n const enabledList = readWorldPackList(worldsDir, levelName, kind);\n const enabledMap = new Map(enabledList.map((e) => [e.pack_id, e.version] as const));\n\n for (const dir of listPackDirsIn(parent)) {\n const info = readPackManifestInfo(dir);\n if (!info || info.kind !== kind) {\n // \u4ECD\u5C1D\u8BD5\u7528 header \u5C55\u793A\uFF08enabled \u4E0E\u6B63\u5E38\u8DEF\u5F84\u540C\u5951\u7EA6\uFF1Aenable-list \u542B uuid \u2192 LSP\uFF09\n const header = readPackManifestHeader(dir);\n if (!header) continue;\n result.push({\n kind,\n folderName: path.basename(dir),\n dir,\n name: path.basename(dir),\n uuid: header.uuid,\n version: header.version,\n enabled: enabledMap.has(header.uuid),\n });\n continue;\n }\n result.push({\n kind: info.kind,\n folderName: path.basename(dir),\n dir,\n name: info.name,\n uuid: info.uuid,\n version: info.version,\n enabled: enabledMap.has(info.uuid),\n });\n }\n }\n return result;\n}\n\n/** RP/BP \u7B2C\u4E09\u4F4D\u7248\u672C +1\uFF0C\u5199\u56DE manifest */\nexport function bumpPackPatchVersion(packDir: string): [number, number, number] {\n const file = path.join(packDir, \"manifest.json\");\n if (!fs.existsSync(file)) throw new Error(`manifest.json missing: ${packDir}`);\n const raw = JSON.parse(fs.readFileSync(file, \"utf8\")) as {\n header?: { version?: number[] };\n modules?: Array<{ version?: number[] }>;\n };\n const ver = raw.header?.version;\n if (!Array.isArray(ver) || ver.length < 3) throw new Error(`invalid header.version in ${file}`);\n const next: [number, number, number] = [Number(ver[0]), Number(ver[1]), Number(ver[2]) + 1];\n return writePackHeaderVersion(packDir, next);\n}\n\n/** \u5199\u5165 header.version\uFF08\u5E76\u540C\u6B65 modules[].version \u7684 patch\uFF09 */\nexport function writePackHeaderVersion(\n packDir: string,\n next: [number, number, number]\n): [number, number, number] {\n const file = path.join(packDir, \"manifest.json\");\n if (!fs.existsSync(file)) throw new Error(`manifest.json missing: ${packDir}`);\n const raw = JSON.parse(fs.readFileSync(file, \"utf8\")) as {\n header?: { version?: number[] };\n modules?: Array<{ version?: number[] }>;\n };\n if (!raw.header) raw.header = {};\n raw.header.version = next;\n if (Array.isArray(raw.modules)) {\n for (const m of raw.modules) {\n if (Array.isArray(m.version) && m.version.length >= 3) {\n m.version = [Number(m.version[0]), Number(m.version[1]), next[2]];\n }\n }\n }\n fs.writeFileSync(file, `${JSON.stringify(raw, null, 2)}\\n`, \"utf8\");\n return next;\n}\n\n/** \u8BFB\u53D6 BP manifest \u4E2D\u4F9D\u8D56\u7684\u8D44\u6E90\u5305 uuid\uFF08\u975E @minecraft \u6A21\u5757\uFF09 */\nexport function readPackDependencyUuids(packDir: string): string[] {\n const file = path.join(packDir, \"manifest.json\");\n if (!fs.existsSync(file)) return [];\n try {\n const raw = JSON.parse(fs.readFileSync(file, \"utf8\")) as {\n dependencies?: Array<{ uuid?: string; module_name?: string }>;\n };\n return (raw.dependencies ?? [])\n .map((d) => d.uuid)\n .filter((u): u is string => typeof u === \"string\" && u.length > 0);\n } catch {\n return [];\n }\n}\n\n/** \u786E\u4FDD\u7248\u672C\u4E25\u683C\u5927\u4E8E floor\uFF08patch/minor \u62AC\u4E00\u7EA7\uFF09 */\nexport function ensureVersionGreaterThan(\n packDir: string,\n floor: [number, number, number],\n component: \"patch\" | \"minor\" = \"patch\"\n): [number, number, number] {\n const info = readPackManifestInfo(packDir);\n if (!info) throw new Error(`cannot read pack version: ${packDir}`);\n let next: [number, number, number] =\n component === \"minor\"\n ? [info.version[0], info.version[1] + 1, 0]\n : [info.version[0], info.version[1], info.version[2] + 1];\n const cmp = (a: [number, number, number], b: [number, number, number]) => {\n for (let i = 0; i < 3; i++) {\n const d = a[i]! - b[i]!;\n if (d !== 0) return d;\n }\n return 0;\n };\n while (cmp(next, floor) <= 0) {\n if (component === \"minor\") next = [next[0], next[1] + 1, 0];\n else next = [next[0], next[1], next[2] + 1];\n }\n return writePackHeaderVersion(packDir, next);\n}\n\nexport interface InstallPackResult {\n ok: boolean;\n skipped?: boolean;\n reason?: string;\n destDir?: string;\n folderName?: string;\n info?: PackManifestInfo;\n conflict?: { existing: PackManifestInfo & { dir: string }; incoming: PackManifestInfo };\n}\n\n/**\n * \u5C06\u5305\u76EE\u5F55\u5B89\u88C5\u5230 destParent\uFF08behavior_packs \u6216 resource_packs\uFF09\u3002\n * force=true \u65F6\u8986\u76D6\u51B2\u7A81\uFF1B\u5426\u5219\u8FD4\u56DE conflict \u4F9B\u4E0A\u5C42\u8BE2\u95EE\u3002\n */\nexport async function installPackDirectory(opts: {\n srcDir: string;\n destParent: string;\n folderName?: string;\n force?: boolean;\n}): Promise<InstallPackResult> {\n const info = readPackManifestInfo(opts.srcDir);\n if (!info) {\n return { ok: false, reason: \"invalid or unrecognized manifest.json\" };\n }\n\n const folderName = formatWorldPackFolderName(\n opts.folderName ?? path.basename(opts.srcDir),\n info.kind\n );\n await fs.promises.mkdir(opts.destParent, { recursive: true });\n\n // \u51B2\u7A81\uFF1A\u540C uuid\uFF0C\u6216\u540C folderName\uFF08\u5373\u4F7F\u65E7\u5305 manifest \u65E0\u6CD5\u5B8C\u6574\u8BC6\u522B\u4E5F\u8981\u62E6 \u2014 \u7981\u6B62\u9759\u9ED8\u8986\u76D6\uFF09\n let conflictDir: string | null = null;\n let conflictExisting: PackManifestInfo | null = null;\n for (const dir of listPackDirsIn(opts.destParent)) {\n const existing = readPackManifestInfo(dir);\n const sameFolder = path.basename(dir) === folderName;\n const sameUuid = !!existing && existing.uuid === info.uuid;\n if (!sameFolder && !sameUuid) continue;\n conflictDir = dir;\n if (existing) {\n conflictExisting = existing;\n } else {\n const header = readPackManifestHeader(dir);\n conflictExisting = {\n name: path.basename(dir),\n uuid: header?.uuid ?? \"(unknown)\",\n version: header?.version ?? [0, 0, 0],\n kind: info.kind,\n };\n }\n break;\n }\n\n if (conflictDir && !opts.force) {\n return {\n ok: false,\n skipped: true,\n reason: \"conflict\",\n conflict: {\n existing: { ...(conflictExisting ?? info), dir: conflictDir },\n incoming: info,\n },\n };\n }\n\n const destDir = path.join(opts.destParent, folderName);\n if (conflictDir && path.resolve(conflictDir) !== path.resolve(destDir)) {\n await fs.promises.rm(conflictDir, { recursive: true, force: true });\n }\n if (fs.existsSync(destDir)) {\n await fs.promises.rm(destDir, { recursive: true, force: true });\n }\n await copyDirAsync(opts.srcDir, destDir);\n return { ok: true, destDir, folderName, info };\n}\n\n/** \u5B89\u88C5\u540E\u5199\u5165\u4E16\u754C enable \u6E05\u5355 */\nexport async function enableInstalledPack(opts: {\n bdsRoot: string;\n levelName: string;\n info: PackManifestInfo;\n}): Promise<void> {\n await enablePackInWorld({\n worldsDir: bdsWorldsDir(opts.bdsRoot),\n levelName: opts.levelName,\n kind: opts.info.kind,\n packUuid: opts.info.uuid,\n version: opts.info.version,\n });\n}\n\n/** \u4ECE\u4E16\u754C enable \u6E05\u5355\u79FB\u9664 */\nexport async function disableInstalledPack(opts: {\n bdsRoot: string;\n levelName: string;\n kind: WorldPackKind;\n packUuid: string;\n version: [number, number, number];\n}): Promise<void> {\n await disablePackInWorld({\n worldsDir: bdsWorldsDir(opts.bdsRoot),\n levelName: opts.levelName,\n kind: opts.kind,\n packUuid: opts.packUuid,\n version: opts.version,\n });\n}\n\nexport type UninstallPackResult =\n | { action: \"trashed\"; dest: string }\n | { action: \"deleted\" }\n | { action: \"missing\" };\n\nexport type UninstallPackAction = UninstallPackResult[\"action\"];\n\n/**\n * \u5378\u8F7D\u5DF2\u5B89\u88C5\u4E16\u754C\u5305\uFF1A\u5148 disable enable-list\uFF0C\u518D\u79FB\u5165 trashDir \u6216\u76F4\u63A5\u5220\u9664\u76EE\u5F55\u3002\n * trashDir \u8DE8\u76D8\u7B26\u65F6 fallback \u4E3A copy + rm\u3002\n */\nexport async function uninstallInstalledPack(opts: {\n bdsRoot: string;\n levelName: string;\n pack: InstalledWorldPack;\n /** \u63D0\u4F9B\u5219\u79FB\u5165\u8BE5\u76EE\u5F55\uFF1Bnull/\u7701\u7565\u5219\u76F4\u63A5\u5220\u9664 */\n trashDir?: string | null;\n}): Promise<UninstallPackResult> {\n await disableInstalledPack({\n bdsRoot: opts.bdsRoot,\n levelName: opts.levelName,\n kind: opts.pack.kind,\n packUuid: opts.pack.uuid,\n version: opts.pack.version,\n });\n\n if (!fs.existsSync(opts.pack.dir)) {\n return { action: \"missing\" };\n }\n\n const trashDir = opts.trashDir?.trim() || null;\n if (trashDir) {\n await fs.promises.mkdir(trashDir, { recursive: true });\n const stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n let dest = path.join(trashDir, opts.pack.folderName);\n if (fs.existsSync(dest)) {\n dest = path.join(trashDir, `${opts.pack.folderName}-${stamp}`);\n }\n try {\n await fs.promises.rename(opts.pack.dir, dest);\n } catch {\n await copyDirAsync(opts.pack.dir, dest);\n await fs.promises.rm(opts.pack.dir, { recursive: true, force: true });\n }\n return { action: \"trashed\", dest };\n }\n\n await fs.promises.rm(opts.pack.dir, { recursive: true, force: true });\n return { action: \"deleted\" };\n}\n\nexport function worldPackParentDir(\n bdsRoot: string,\n levelName: string,\n kind: WorldPackKind\n): string {\n return path.join(\n bdsWorldLevelDir(bdsRoot, levelName),\n kind === \"behavior\" ? \"behavior_packs\" : \"resource_packs\"\n );\n}\n\n/** \u4E16\u754C enable-list \u6743\u5A01\u8BFB\u53D6 \u2014 \u59D4\u6258 pack-manager\uFF08\u4F9B doctor \u7B49\uFF0C\u907F\u514D\u786C\u7F16\u7801\u6587\u4EF6\u540D\uFF09 */\nexport function listWorldEnableEntries(\n bdsRoot: string,\n levelName: string,\n kind: WorldPackKind\n): Array<{ pack_id: string; version: [number, number, number] }> {\n return listWorldEnableListResult(bdsRoot, levelName, kind).entries;\n}\n\n/** \u542B parseFail \u4FE1\u53F7\u7684 enable-list \u5FEB\u7167\uFF08doctor \u7528\uFF1B\u4E0D\u66B4\u9732 JSON \u8DEF\u5F84\u6784\u9020\u7EC6\u8282\uFF09 */\nexport function listWorldEnableListResult(\n bdsRoot: string,\n levelName: string,\n kind: WorldPackKind\n): WorldPackListReadResult {\n return readWorldPackListResult(bdsWorldsDir(bdsRoot), levelName, kind);\n}\n\nexport function findInstalledPackById(\n packs: InstalledWorldPack[],\n id: string\n): InstalledWorldPack | null {\n const q = id.trim().toLowerCase();\n return (\n packs.find(\n (p) =>\n p.uuid.toLowerCase() === q ||\n p.folderName.toLowerCase() === q ||\n p.name.toLowerCase() === q ||\n p.folderName.toLowerCase().includes(q) ||\n p.name.toLowerCase().includes(q)\n ) ?? null\n );\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
package/dist/zipx.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 将 zip 条目名解析为 destRoot 下的绝对路径。
3
+ * 拒绝 `..`、绝对路径(含前导 `/`、盘符)、以及 resolve 后逃逸 destRoot 的条目。
4
+ */
5
+ export declare function resolveSafeZipEntryPath(destRoot: string, entryName: string): string;
6
+ /** 将 zip Buffer 解压到 destDir(已存在则写入其下)。 */
7
+ export declare function extractZipBufferToDir(data: Buffer, destDir: string): Promise<void>;
8
+ /** 从磁盘 zip 文件解压到 destDir。 */
9
+ export declare function extractZipFileToDir(zipPath: string, destDir: string): Promise<void>;
10
+ //# sourceMappingURL=zipx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zipx.d.ts","sourceRoot":"","sources":["../src/zipx.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAiBnF;AAED,0CAA0C;AAC1C,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAexF;AAED,6BAA6B;AAC7B,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKzF"}
package/dist/zipx.js ADDED
@@ -0,0 +1,48 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import JSZip from "jszip";
4
+ function resolveSafeZipEntryPath(destRoot, entryName) {
5
+ const raw = String(entryName ?? "").replace(/\\/g, "/");
6
+ if (!raw || raw.includes("..") || raw.startsWith("/") || /^[a-zA-Z]:/.test(raw)) {
7
+ throw new Error(`unsafe zip entry path: ${entryName}`);
8
+ }
9
+ const parts = raw.replace(/\/$/, "").split("/").filter(Boolean);
10
+ if (parts.length === 0) {
11
+ throw new Error(`unsafe zip entry path: ${entryName}`);
12
+ }
13
+ const rootResolved = path.resolve(destRoot);
14
+ const out = path.resolve(rootResolved, ...parts);
15
+ const rootPrefix = rootResolved.endsWith(path.sep) ? rootResolved : rootResolved + path.sep;
16
+ if (out !== rootResolved && !out.startsWith(rootPrefix)) {
17
+ throw new Error(`zip entry escapes dest: ${entryName}`);
18
+ }
19
+ return out;
20
+ }
21
+ async function extractZipBufferToDir(data, destDir) {
22
+ const zip = await JSZip.loadAsync(data);
23
+ await fs.promises.mkdir(destDir, { recursive: true });
24
+ for (const name of Object.keys(zip.files)) {
25
+ const entry = zip.files[name];
26
+ if (!entry) continue;
27
+ const out = resolveSafeZipEntryPath(destDir, name);
28
+ if (entry.dir || name.endsWith("/") || name.endsWith("\\")) {
29
+ await fs.promises.mkdir(out, { recursive: true });
30
+ continue;
31
+ }
32
+ await fs.promises.mkdir(path.dirname(out), { recursive: true });
33
+ const buf = await entry.async("nodebuffer");
34
+ await fs.promises.writeFile(out, buf);
35
+ }
36
+ }
37
+ async function extractZipFileToDir(zipPath, destDir) {
38
+ const abs = path.resolve(zipPath);
39
+ if (!fs.existsSync(abs)) throw new Error(`archive not found: ${abs}`);
40
+ const data = await fs.promises.readFile(abs);
41
+ await extractZipBufferToDir(data, destDir);
42
+ }
43
+ export {
44
+ extractZipBufferToDir,
45
+ extractZipFileToDir,
46
+ resolveSafeZipEntryPath
47
+ };
48
+ //# sourceMappingURL=zipx.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/zipx.ts"],
4
+ "sourcesContent": ["/**\n * zipx.ts \u2014 JSZip \u5B89\u5168\u89E3\u538B\uFF08\u9632 zip-slip / \u7EDD\u5BF9\u8DEF\u5F84 / Windows `\\`\uFF09\n * \u5355\u4E00\u6743\u5A01\uFF0C\u4F9B world-packs / check-update / wizard / fetch-module \u590D\u7528\uFF08DRY\uFF09\u3002\n */\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport JSZip from \"jszip\";\n\n/**\n * \u5C06 zip \u6761\u76EE\u540D\u89E3\u6790\u4E3A destRoot \u4E0B\u7684\u7EDD\u5BF9\u8DEF\u5F84\u3002\n * \u62D2\u7EDD `..`\u3001\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u542B\u524D\u5BFC `/`\u3001\u76D8\u7B26\uFF09\u3001\u4EE5\u53CA resolve \u540E\u9003\u9038 destRoot \u7684\u6761\u76EE\u3002\n */\nexport function resolveSafeZipEntryPath(destRoot: string, entryName: string): string {\n const raw = String(entryName ?? \"\").replace(/\\\\/g, \"/\");\n // \u5148\u4E8E\u53BB\u524D\u5BFC\u659C\u6760\u68C0\u67E5\uFF1A\u907F\u514D `/etc/passwd` \u88AB\u5265\u6210\u76F8\u5BF9\u8DEF\u5F84\u9759\u9ED8\u5199\u5165 dest\n if (!raw || raw.includes(\"..\") || raw.startsWith(\"/\") || /^[a-zA-Z]:/.test(raw)) {\n throw new Error(`unsafe zip entry path: ${entryName}`);\n }\n const parts = raw.replace(/\\/$/, \"\").split(\"/\").filter(Boolean);\n if (parts.length === 0) {\n throw new Error(`unsafe zip entry path: ${entryName}`);\n }\n const rootResolved = path.resolve(destRoot);\n const out = path.resolve(rootResolved, ...parts);\n const rootPrefix = rootResolved.endsWith(path.sep) ? rootResolved : rootResolved + path.sep;\n if (out !== rootResolved && !out.startsWith(rootPrefix)) {\n throw new Error(`zip entry escapes dest: ${entryName}`);\n }\n return out;\n}\n\n/** \u5C06 zip Buffer \u89E3\u538B\u5230 destDir\uFF08\u5DF2\u5B58\u5728\u5219\u5199\u5165\u5176\u4E0B\uFF09\u3002 */\nexport async function extractZipBufferToDir(data: Buffer, destDir: string): Promise<void> {\n const zip = await JSZip.loadAsync(data);\n await fs.promises.mkdir(destDir, { recursive: true });\n for (const name of Object.keys(zip.files)) {\n const entry = zip.files[name];\n if (!entry) continue;\n const out = resolveSafeZipEntryPath(destDir, name);\n if (entry.dir || name.endsWith(\"/\") || name.endsWith(\"\\\\\")) {\n await fs.promises.mkdir(out, { recursive: true });\n continue;\n }\n await fs.promises.mkdir(path.dirname(out), { recursive: true });\n const buf = await entry.async(\"nodebuffer\");\n await fs.promises.writeFile(out, buf);\n }\n}\n\n/** \u4ECE\u78C1\u76D8 zip \u6587\u4EF6\u89E3\u538B\u5230 destDir\u3002 */\nexport async function extractZipFileToDir(zipPath: string, destDir: string): Promise<void> {\n const abs = path.resolve(zipPath);\n if (!fs.existsSync(abs)) throw new Error(`archive not found: ${abs}`);\n const data = await fs.promises.readFile(abs);\n await extractZipBufferToDir(data, destDir);\n}\n"],
5
+ "mappings": "AAIA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,WAAW;AAMX,SAAS,wBAAwB,UAAkB,WAA2B;AACnF,QAAM,MAAM,OAAO,aAAa,EAAE,EAAE,QAAQ,OAAO,GAAG;AAEtD,MAAI,CAAC,OAAO,IAAI,SAAS,IAAI,KAAK,IAAI,WAAW,GAAG,KAAK,aAAa,KAAK,GAAG,GAAG;AAC/E,UAAM,IAAI,MAAM,0BAA0B,SAAS,EAAE;AAAA,EACvD;AACA,QAAM,QAAQ,IAAI,QAAQ,OAAO,EAAE,EAAE,MAAM,GAAG,EAAE,OAAO,OAAO;AAC9D,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,IAAI,MAAM,0BAA0B,SAAS,EAAE;AAAA,EACvD;AACA,QAAM,eAAe,KAAK,QAAQ,QAAQ;AAC1C,QAAM,MAAM,KAAK,QAAQ,cAAc,GAAG,KAAK;AAC/C,QAAM,aAAa,aAAa,SAAS,KAAK,GAAG,IAAI,eAAe,eAAe,KAAK;AACxF,MAAI,QAAQ,gBAAgB,CAAC,IAAI,WAAW,UAAU,GAAG;AACvD,UAAM,IAAI,MAAM,2BAA2B,SAAS,EAAE;AAAA,EACxD;AACA,SAAO;AACT;AAGA,eAAsB,sBAAsB,MAAc,SAAgC;AACxF,QAAM,MAAM,MAAM,MAAM,UAAU,IAAI;AACtC,QAAM,GAAG,SAAS,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AACpD,aAAW,QAAQ,OAAO,KAAK,IAAI,KAAK,GAAG;AACzC,UAAM,QAAQ,IAAI,MAAM,IAAI;AAC5B,QAAI,CAAC,MAAO;AACZ,UAAM,MAAM,wBAAwB,SAAS,IAAI;AACjD,QAAI,MAAM,OAAO,KAAK,SAAS,GAAG,KAAK,KAAK,SAAS,IAAI,GAAG;AAC1D,YAAM,GAAG,SAAS,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAChD;AAAA,IACF;AACA,UAAM,GAAG,SAAS,MAAM,KAAK,QAAQ,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9D,UAAM,MAAM,MAAM,MAAM,MAAM,YAAY;AAC1C,UAAM,GAAG,SAAS,UAAU,KAAK,GAAG;AAAA,EACtC;AACF;AAGA,eAAsB,oBAAoB,SAAiB,SAAgC;AACzF,QAAM,MAAM,KAAK,QAAQ,OAAO;AAChC,MAAI,CAAC,GAAG,WAAW,GAAG,EAAG,OAAM,IAAI,MAAM,sBAAsB,GAAG,EAAE;AACpE,QAAM,OAAO,MAAM,GAAG,SAAS,SAAS,GAAG;AAC3C,QAAM,sBAAsB,MAAM,OAAO;AAC3C;",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sfmc-bds/bds-tools",
3
- "version": "0.1.0",
3
+ "version": "0.2.0-beta.0",
4
4
  "description": "SFMC BDS auto-updater, lifecycle manager, and behavior-pack assembler",
5
5
  "author": "Shiroha7z",
6
6
  "type": "module",
@@ -27,15 +27,27 @@
27
27
  "README.md"
28
28
  ],
29
29
  "exports": {
30
+ ".": "./dist/bds-manager.js",
31
+ "./check-update": "./dist/check-update.js",
32
+ "./bds-manager": "./dist/bds-manager.js",
33
+ "./pack-manager": "./dist/cli-pack-manager.js",
34
+ "./pack-manager-lib": "./dist/pack-manager.js",
35
+ "./world-packs": "./dist/world-packs.js",
36
+ "./http": "./dist/http.js",
37
+ "./taskbar": "./dist/taskbar.js",
38
+ "./update-result": "./dist/update-result.js",
39
+ "./zipx": "./dist/zipx.js",
40
+ "./package.json": "./package.json",
30
41
  "./types": {
31
42
  "types": "./dist/types.d.ts"
32
43
  }
33
44
  },
34
45
  "scripts": {
35
- "build": "tsc -p tsconfig.json",
36
- "dev": "tsx src/dist/bds-manager.ts",
37
- "typecheck": "tsc --noEmit",
46
+ "build": "node ../scripts/esbuild-transpile.mjs --dts",
47
+ "dev": "tsx src/bds-manager.ts",
48
+ "typecheck": "tsc7 --noEmit",
38
49
  "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
50
+ "test": "node --test is-main.test.mjs pack-manager-lifecycle.test.mjs server-properties.test.mjs world-packs.test.mjs zipx.test.mjs",
39
51
  "update": "node dist/check-update.js",
40
52
  "update:check": "node dist/check-update.js --check-only",
41
53
  "update:force": "node dist/check-update.js --force",
@@ -47,8 +59,7 @@
47
59
  "prepublishOnly": "npm run build"
48
60
  },
49
61
  "dependencies": {
50
- "@sfmc-bds/sdk": "^0.1.0",
51
- "cli-progress": "^3.12.0",
62
+ "@sfmc-bds/sdk": "^0.2.0-beta.0",
52
63
  "jszip": "^3.10.1",
53
64
  "node-html-parser": "^9.0.0"
54
65
  },