@sfmc-bds/cli 0.5.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/logs.d.ts +21 -0
- package/dist/logs.d.ts.map +1 -1
- package/dist/logs.js +67 -1
- package/dist/logs.js.map +2 -2
- package/dist/management/deployment.js +1 -1
- package/dist/management/deployment.js.map +2 -2
- package/dist/management/domain.d.ts +46 -2
- package/dist/management/domain.d.ts.map +1 -1
- package/dist/management/domain.js +112 -26
- package/dist/management/domain.js.map +2 -2
- package/dist/management/maintenance.d.ts +14 -1
- package/dist/management/maintenance.d.ts.map +1 -1
- package/dist/management/maintenance.js +46 -38
- package/dist/management/maintenance.js.map +2 -2
- package/dist/management/pack-import.d.ts +29 -0
- package/dist/management/pack-import.d.ts.map +1 -0
- package/dist/management/pack-import.js +108 -0
- package/dist/management/pack-import.js.map +7 -0
- package/dist/management/platform-worker.js +5 -6
- package/dist/management/platform-worker.js.map +2 -2
- package/dist/management/server.d.ts.map +1 -1
- package/dist/management/server.js +113 -33
- package/dist/management/server.js.map +2 -2
- package/dist/management/tasks.js +1 -1
- package/dist/management/tasks.js.map +2 -2
- package/dist/registry.d.ts +11 -5
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +35 -11
- package/dist/registry.js.map +2 -2
- package/dist/services.d.ts +2 -1
- package/dist/services.d.ts.map +1 -1
- package/dist/services.js +16 -3
- package/dist/services.js.map +2 -2
- package/dist/world-packs.d.ts +47 -0
- package/dist/world-packs.d.ts.map +1 -1
- package/dist/world-packs.js +38 -11
- package/dist/world-packs.js.map +2 -2
- package/package.json +6 -6
- package/scripts/module-install/lib/registry-index.mjs +50 -13
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/management/domain.ts"],
|
|
4
|
-
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { spawn } from \"node:child_process\";\nimport { resolveRegistryIndex } from \"../registry.js\";\nimport { ROOT, resolveFetchModule } from \"../runtime.js\";\nimport { resolveBdsContext, scanLocalModules } from \"../pack-lifecycle.js\";\nimport { listInstalledWorldPacks, resolvePackRoots, readPackManifestInfo, installPackDirectory, enableInstalledPack, disableInstalledPack, worldPackParentDir } from \"@sfmc-bds/bds-tools/world-packs\";\nimport { atomicJson } from \"./tasks.js\";\nimport { isRetiredPlatformModule } from \"@sfmc-bds/sdk/contracts\";\n\n/**\n * \u53BB\u6389 UTF-8 BOM\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u7F16\u8F91\u5668\u6216 BDS \u5199\u51FA\u7684 JSON\uFF08\u4F8B\u5982 configs/permissions.json\uFF09\u5E38\u5E26 BOM\uFF0C\u76F4\u63A5 JSON.parse \u4F1A\u5931\u8D25\u3002\n */\nexport function stripBom(text: string): string {\n return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;\n}\nexport function readJson<T>(file: string, fallback: T): T {\n try { return JSON.parse(stripBom(fs.readFileSync(file, \"utf8\"))) as T; }\n catch (error) { if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return fallback; throw error; }\n}\nexport function identifier(value: unknown): string {\n if (typeof value !== \"string\" || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/.test(value) || value.includes(\"..\")) throw new Error(\"\u975E\u6CD5\u6807\u8BC6\u7B26\");\n return value;\n}\nexport async function listModules() {\n const rows = (await scanLocalModules()).filter(row => !isRetiredPlatformModule(row.logicalId));\n // \u76EE\u5F55\u91CC\u7684\u5C55\u793A\u540D\u6765\u81EA modules/catalog.json\uFF1B\u6CA1\u6709\u4E2D\u6587\u540D\u65F6\u4ECD\u7528\u903B\u8F91 id\uFF0C\u907F\u514D\u754C\u9762\u53EA\u5269\u6587\u4EF6\u5939\u540D\u3002\n const catalog = readJson<{ modules?: { id?: string; name?: string }[] }>(path.join(ROOT, \"modules\", \"catalog.json\"), {});\n const titles = new Map((catalog.modules ?? []).filter(row => row.id && row.name).map(row => [row.id!, row.name!]));\n return { modules: rows.map(row => ({ id: row.logicalId, folder: row.folderId, name: titles.get(row.logicalId) || row.logicalId, version: row.version ?? \"unknown\", enabled: row.enabled, linked: fs.lstatSync(path.join(ROOT, \"modules\", \"packages\", row.folderId)).isSymbolicLink() })) };\n}\nfunction fetchEntry() { const entry = resolveFetchModule(); if (!entry) throw new Error(\"\u627E\u4E0D\u5230\u6A21\u5757\u5B89\u88C5\u5668\"); return entry; }\nexport async function searchModules(query: string) {\n const result = await resolveRegistryIndex();\n return { modules: Object.entries(result.index).filter(([id]) => !isRetiredPlatformModule(id) && id.includes(query)).map(([id, metadata]) => ({ id, ...metadata })), stale: result.stale };\n}\nexport async function runFetch(args: string[]) {\n await runProcess(process.execPath, [fetchEntry(), ...args], ROOT);\n return { changed: true };\n}\nexport function runProcess(command: string, args: string[], cwd: string, onLine?: (text: string) => void): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, { cwd, env: { ...process.env, SFMC_ROOT: ROOT, FORCE_COLOR: \"0\" }, windowsHide: true, stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n let diagnostic = \"\";\n for (const stream of [child.stdout, child.stderr]) stream.on(\"data\", (chunk: Buffer) => { const text = chunk.toString(); diagnostic = (diagnostic + text).slice(-6000); onLine?.(text); });\n child.on(\"error\", reject); child.on(\"close\", code => code === 0 ? resolve() : reject(new Error(`\u5B50\u8FDB\u7A0B\u5931\u8D25 (${code}): ${diagnostic}`)));\n });\n}\nexport async function modulePlan(ids: string[] = []) {\n const url = pathToFileURL(path.join(path.dirname(fetchEntry()), \"lib\", \"module-update.mjs\")).href;\n const api = await import(url) as { planModuleUpdates: (options: { root: string; ids: string[] }) => Promise<unknown> };\n return api.planModuleUpdates({ root: ROOT, ids });\n}\nexport async function applyModulePlan(ids: string[]) {\n const directory = path.dirname(fetchEntry());\n const api = await import(pathToFileURL(path.join(directory, \"lib\", \"module-update.mjs\")).href) as {\n planModuleUpdates: (options: { root: string; ids: string[] }) => Promise<{ upgrades: { id: string; spec: string }[]; failMode: \"abort\" | \"continue\" }>;\n applyModuleUpgrades: (upgrades: unknown[], deps: object) => Promise<{ failed: { id: string; message: string }[] }>;\n resyncInstalledCatalog: (id: string) => void;\n };\n const fetchApi = await import(pathToFileURL(fetchEntry()).href) as { installPreservingLock: (id: string, spec: string) => Promise<void> };\n const plan = await api.planModuleUpdates({ root: ROOT, ids });\n const result = await api.applyModuleUpgrades(plan.upgrades, { root: ROOT, failMode: plan.failMode, install: fetchApi.installPreservingLock, resync: api.resyncInstalledCatalog });\n if (result.failed.length) throw new Error(JSON.stringify(result.failed));\n return result;\n}\nexport function listPacks() {\n const context = resolveBdsContext();\n return { packs: listInstalledWorldPacks(context.bdsRoot, context.levelName).map(pack => ({ id: pack.uuid, name: pack.name, kind: pack.kind, enabled: pack.enabled, version: pack.version.join(\".\") })) };\n}\nexport async function togglePack(id: string, enabled: boolean) {\n const context = resolveBdsContext();\n const pack = listInstalledWorldPacks(context.bdsRoot, context.levelName).find(row => row.uuid === id);\n if (!pack) throw new Error(\"\u627E\u4E0D\u5230\u4E16\u754C\u5305\");\n if (enabled) await enableInstalledPack({ ...context, info: { uuid: pack.uuid, version: pack.version, kind: pack.kind, name: pack.name } });\n else await disableInstalledPack({ ...context, kind: pack.kind, packUuid: pack.uuid, version: pack.version });\n return { changed: true, restartRequired: true };\n}\nexport async function importPack(filename: string) {\n const inbox = path.resolve(ROOT, \"packs\", \"_desktop-inbox\");\n const file = path.resolve(inbox, identifier(filename));\n if (!file.startsWith(inbox + path.sep) || !fs.existsSync(file)) throw new Error(\"\u4E16\u754C\u5305\u5C1A\u672A\u4E0A\u4F20\u5230\u5B9E\u4F8B\u6536\u4EF6\u7BB1\");\n const context = resolveBdsContext();\n const resolved = await resolvePackRoots(file);\n try {\n if (!resolved.roots.length) throw new Error(\"\u5F52\u6863\u4E0D\u542B\u53EF\u8BC6\u522B\u7684\u4E16\u754C\u5305\");\n for (const root of resolved.roots) {\n const info = readPackManifestInfo(root);\n if (!info) throw new Error(\"\u4E16\u754C\u5305 manifest \u65E0\u6548\");\n const result = await installPackDirectory({ srcDir: root, destParent: worldPackParentDir(context.bdsRoot, context.levelName, info.kind) });\n if (!result.ok) throw new Error(result.reason ?? \"\u5B89\u88C5\u5931\u8D25\uFF1B\u53EF\u80FD\u4E0E\u5DF2\u6709\u5305\u51B2\u7A81\");\n await enableInstalledPack({ ...context, info });\n }\n } finally { resolved.dispose?.(); }\n return { changed: true, restartRequired: true };\n}\nexport function playerFiles() {\n const bds = resolveBdsContext().bdsRoot;\n return { allowlist: path.join(bds, \"allowlist.json\"), permissions: path.join(bds, \"permissions.json\"), sfmcPermissions: path.join(ROOT, \"configs\", \"permissions.json\") };\n}\nexport function savePlayerPermissions(kind: string, entries: unknown) {\n const files = playerFiles();\n if (!(kind in files) || !Array.isArray(entries)) throw new Error(\"\u975E\u6CD5\u73A9\u5BB6\u6743\u9650\u7C7B\u578B\u6216\u5185\u5BB9\");\n for (const entry of entries) {\n if (!entry || typeof entry !== \"object\") throw new Error(\"\u6743\u9650\u6761\u76EE\u5FC5\u987B\u4E3A\u5BF9\u8C61\");\n const row = entry as Record<string, unknown>;\n if (kind === \"allowlist\" && (typeof row.name !== \"string\" || /[\\r\\n]/.test(row.name))) throw new Error(\"\u5141\u8BB8\u540D\u5355\u9700\u63D0\u4F9B\u5408\u6CD5\u73A9\u5BB6\u540D\u79F0\");\n if (kind === \"permissions\" && (typeof row.xuid !== \"string\" || !/^\\d+$/.test(row.xuid) || ![\"visitor\", \"member\", \"operator\"].includes(String(row.permission)))) throw new Error(\"BDS \u6743\u9650\u9700\u6709\u6548 XUID \u548C\u6743\u9650\u7B49\u7EA7\");\n if (kind === \"sfmcPermissions\" && (typeof row.player_name !== \"string\" || !row.player_name.trim() || /[\\r\\n\\0]/.test(row.player_name) || !Number.isInteger(row.level) || Number(row.level) < 0 || Number(row.level) > 3)) throw new Error(\"SFMC \u6743\u9650\u9700\u5408\u6CD5\u73A9\u5BB6\u540D\u53CA 0 \u5230 3 \u7684\u6574\u6570\u7B49\u7EA7\");\n }\n atomicJson(files[kind as keyof typeof files], entries);\n return { saved: true };\n}\n"],
|
|
5
|
-
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAC9B,SAAS,aAAa;AACtB,SAAS,4BAA4B;AACrC,SAAS,MAAM,0BAA0B;AACzC,SAAS,mBAAmB,wBAAwB;AACpD,SAAS,yBAAyB,
|
|
4
|
+
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { spawn } from \"node:child_process\";\nimport { resolveRegistryIndex } from \"../registry.js\";\nimport { ROOT, resolveFetchModule } from \"../runtime.js\";\nimport { resolveBdsContext, scanLocalModules } from \"../pack-lifecycle.js\";\nimport { listInstalledWorldPacks, enableInstalledPack, disableInstalledPack, writePackHeaderVersion } from \"@sfmc-bds/bds-tools/world-packs\";\nimport { buildPackUninstallQueue, isProtectedSfmcPack, resolveUninstallTargets, runPackUninstallQueue } from \"../world-packs.js\";\nimport { atomicJson } from \"./tasks.js\";\nimport { assessModuleRequires, moduleRequireBlockMessage } from \"@sfmc-bds/management\";\nimport { isRetiredPlatformModule } from \"@sfmc-bds/sdk/contracts\";\n\n/**\n * \u53BB\u6389 UTF-8 BOM\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u7F16\u8F91\u5668\u6216 BDS \u5199\u51FA\u7684 JSON\uFF08\u4F8B\u5982 configs/permissions.json\uFF09\u5E38\u5E26 BOM\uFF0C\u76F4\u63A5 JSON.parse \u4F1A\u5931\u8D25\u3002\n */\nexport function stripBom(text: string): string {\n return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;\n}\nexport function readJson<T>(file: string, fallback: T): T {\n try { return JSON.parse(stripBom(fs.readFileSync(file, \"utf8\"))) as T; }\n catch (error) { if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return fallback; throw error; }\n}\nexport function identifier(value: unknown): string {\n if (typeof value !== \"string\" || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/.test(value) || value.includes(\"..\")) throw new Error(\"\u975E\u6CD5\u6807\u8BC6\u7B26\");\n return value;\n}\nexport async function listModules() {\n const rows = (await scanLocalModules()).filter(row => !isRetiredPlatformModule(row.logicalId));\n // \u76EE\u5F55\u91CC\u7684\u5C55\u793A\u540D\u6765\u81EA modules/catalog.json\uFF1B\u6CA1\u6709\u4E2D\u6587\u540D\u65F6\u4ECD\u7528\u903B\u8F91 id\uFF0C\u907F\u514D\u754C\u9762\u53EA\u5269\u6587\u4EF6\u5939\u540D\u3002\n const catalog = readJson<{ modules?: { id?: string; name?: string }[] }>(path.join(ROOT, \"modules\", \"catalog.json\"), {});\n const titles = new Map((catalog.modules ?? []).filter(row => row.id && row.name).map(row => [row.id!, row.name!]));\n return { modules: rows.map(row => ({ id: row.logicalId, folder: row.folderId, name: titles.get(row.logicalId) || row.logicalId, version: row.version ?? \"unknown\", enabled: row.enabled, linked: fs.lstatSync(path.join(ROOT, \"modules\", \"packages\", row.folderId)).isSymbolicLink() })) };\n}\nfunction fetchEntry() { const entry = resolveFetchModule(); if (!entry) throw new Error(\"\u627E\u4E0D\u5230\u6A21\u5757\u5B89\u88C5\u5668\"); return entry; }\nexport async function searchModules(query: string) {\n let result = await resolveRegistryIndex();\n // \u65E7\u7F13\u5B58\u662F\u6309\u7CBE\u7B80\u5B57\u6BB5\u5199\u5165\u7684\uFF0C\u6CA1\u6709 requires\u3002\u5F3A\u5236\u5237\u65B0\u4E00\u6B21\uFF0C\u907F\u514D\u628A\u201C\u6CA1\u58F0\u660E\u201D\u548C\u201C\u7F13\u5B58\u592A\u65E7\u201D\u6DF7\u5728\u4E00\u8D77\u3002\n if (Object.values(result.index).some((entry) => !Array.isArray(entry.requires))) {\n result = await resolveRegistryIndex({ force: true });\n }\n const needle = query.trim().toLowerCase();\n return {\n modules: Object.entries(result.index).filter(([id, metadata]) => {\n if (isRetiredPlatformModule(id)) return false;\n if (!needle) return true;\n return [id, metadata.name, metadata.description, metadata.npm].some((value) => value?.toLowerCase().includes(needle));\n }).map(([id, metadata]) => ({ id, ...metadata })),\n stale: result.stale,\n };\n}\n\n/**\n * \u5B89\u88C5\u524D\u6838\u5BF9\u7D22\u5F15\u91CC\u7684 requires\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1Amodules.install \u5728\u521B\u5EFA\u4E0B\u8F7D\u4EFB\u52A1\u4E4B\u524D\u8C03\u7528\u3002\u7D22\u5F15\u6CA1\u6709\u8FD9\u4E2A id \u65F6\u8FD4\u56DE null\uFF0C\u7EE7\u7EED\u539F\u6765\u7684\u5B89\u88C5\u8DEF\u5F84\u3002\n * \u6709\u6761\u76EE\u4F46\u6CA1\u6709\u5408\u6CD5 requires\uFF0C\u6216\u4F9D\u8D56\u672A\u5B89\u88C5\u3001\u672A\u542F\u7528\u65F6\u8FD4\u56DE\u8BF4\u660E\uFF0C\u8C03\u7528\u65B9\u4E0D\u5F97\u5F00\u59CB\u5B89\u88C5\u3002\n */\nexport async function moduleInstallBlock(id: string): Promise<string | null> {\n let result = await resolveRegistryIndex();\n const cached = result.index[id];\n if (cached && !Array.isArray(cached.requires)) result = await resolveRegistryIndex({ force: true });\n const entry = result.index[id];\n if (!entry) return null;\n const installed = (await listModules()).modules;\n return moduleRequireBlockMessage(id, assessModuleRequires(entry.requires, installed));\n}\nexport async function runFetch(args: string[]) {\n await runProcess(process.execPath, [fetchEntry(), ...args], ROOT);\n return { changed: true };\n}\nexport function runProcess(command: string, args: string[], cwd: string, onLine?: (text: string) => void): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, { cwd, env: { ...process.env, SFMC_ROOT: ROOT, FORCE_COLOR: \"0\" }, windowsHide: true, stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n let diagnostic = \"\";\n for (const stream of [child.stdout, child.stderr]) stream.on(\"data\", (chunk: Buffer) => { const text = chunk.toString(); diagnostic = (diagnostic + text).slice(-6000); onLine?.(text); });\n child.on(\"error\", reject); child.on(\"close\", code => code === 0 ? resolve() : reject(new Error(`\u5B50\u8FDB\u7A0B\u5931\u8D25 (${code}): ${diagnostic}`)));\n });\n}\nexport async function modulePlan(ids: string[] = []) {\n const url = pathToFileURL(path.join(path.dirname(fetchEntry()), \"lib\", \"module-update.mjs\")).href;\n const api = await import(url) as { planModuleUpdates: (options: { root: string; ids: string[] }) => Promise<unknown> };\n return api.planModuleUpdates({ root: ROOT, ids });\n}\nexport async function applyModulePlan(ids: string[]) {\n const directory = path.dirname(fetchEntry());\n const api = await import(pathToFileURL(path.join(directory, \"lib\", \"module-update.mjs\")).href) as {\n planModuleUpdates: (options: { root: string; ids: string[] }) => Promise<{ upgrades: { id: string; spec: string }[]; failMode: \"abort\" | \"continue\" }>;\n applyModuleUpgrades: (upgrades: unknown[], deps: object) => Promise<{ failed: { id: string; message: string }[] }>;\n resyncInstalledCatalog: (id: string) => void;\n };\n const fetchApi = await import(pathToFileURL(fetchEntry()).href) as { installPreservingLock: (id: string, spec: string) => Promise<void> };\n const plan = await api.planModuleUpdates({ root: ROOT, ids });\n const result = await api.applyModuleUpgrades(plan.upgrades, { root: ROOT, failMode: plan.failMode, install: fetchApi.installPreservingLock, resync: api.resyncInstalledCatalog });\n if (result.failed.length) throw new Error(JSON.stringify(result.failed));\n return result;\n}\n/** pack_icon.png \u4E0A\u9650\u3002\u8D85\u8FC7\u5219\u4E0D\u63D0\u4F9B\u56FE\u6807\uFF0C\u907F\u514D\u628A\u8D85\u5927\u6587\u4EF6\u585E\u8FDB\u7BA1\u7406\u5E27\u3002 */\nconst PACK_ICON_MAX_BYTES = 1_572_864;\nconst PNG_MAGIC = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);\n\n/**\n * \u4E16\u754C\u5305\u6839\u76EE\u5F55\u91CC\u7684 pack_icon.png \u4FEE\u8BA2\u6807\u8BB0\uFF08size:mtime\uFF09\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1Apacks.list \u544A\u8BC9\u684C\u9762\u7AEF\u8981\u4E0D\u8981\u518D\u62C9\u56FE\u6807\uFF0C\u4EE5\u53CA\u7F13\u5B58\u662F\u5426\u5931\u6548\u3002\n * \u7B26\u53F7\u94FE\u63A5\u3001\u7A7A\u6587\u4EF6\u548C\u8D85\u9650\u6587\u4EF6\u4E00\u5F8B\u89C6\u4E3A\u6CA1\u6709\u56FE\u6807\u3002\n */\nfunction packIconStamp(dir: string): string | null {\n const file = path.join(dir, \"pack_icon.png\");\n let stat: fs.Stats;\n try { stat = fs.lstatSync(file); } catch { return null; }\n if (!stat.isFile() || stat.isSymbolicLink() || stat.size <= 0 || stat.size > PACK_ICON_MAX_BYTES) return null;\n return `${stat.size}:${Math.trunc(stat.mtimeMs)}`;\n}\n\nexport function listPacks() {\n const context = resolveBdsContext();\n return { packs: listInstalledWorldPacks(context.bdsRoot, context.levelName).map(pack => {\n const iconStamp = packIconStamp(pack.dir);\n return {\n id: pack.uuid,\n name: pack.name,\n kind: pack.kind,\n enabled: pack.enabled,\n version: pack.version.join(\".\"),\n ...(iconStamp ? { iconStamp } : {}),\n ...(isProtectedSfmcPack(pack) ? { protected: true } : {}),\n };\n }) };\n}\n\n/**\n * \u8BFB\u53D6\u67D0\u4E2A\u5DF2\u5B89\u88C5\u4E16\u754C\u5305\u7684 pack_icon.png\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u684C\u9762\u4E16\u754C\u5305\u5217\u8868\u6309 iconStamp \u6309\u9700\u62C9\u53D6\uFF0C\u4E0D\u653E\u8FDB\u6BCF 5 \u79D2\u7684 packs.list\u3002\n * \u627E\u4E0D\u5230\u5305\u3001\u6587\u4EF6\u7F3A\u5931\u6216\u5185\u5BB9\u4E0D\u662F PNG \u65F6\u8FD4\u56DE\u7A7A\u56FE\u6807\uFF0C\u4E0D\u629B\u9519\u3002\n */\nexport function readPackIcon(id: string): { id: string; mime: \"image/png\" | null; data: string | null } {\n const empty: { id: string; mime: null; data: null } = { id, mime: null, data: null };\n const context = resolveBdsContext();\n const pack = listInstalledWorldPacks(context.bdsRoot, context.levelName).find(row => row.uuid.toLowerCase() === id.toLowerCase());\n if (!pack || !packIconStamp(pack.dir)) return empty;\n const file = path.join(pack.dir, \"pack_icon.png\");\n let data: Buffer;\n try { data = fs.readFileSync(file); } catch { return empty; }\n if (data.length < PNG_MAGIC.length || !data.subarray(0, PNG_MAGIC.length).equals(PNG_MAGIC) || data.length > PACK_ICON_MAX_BYTES) return empty;\n return { id: pack.uuid, mime: \"image/png\", data: data.toString(\"base64\") };\n}\n\n/**\n * \u5378\u8F7D\u4E00\u4E2A\u4E16\u754C\u5305\uFF0C\u5E76\u6CBF\u7528 CLI \u7684\u914D\u5BF9\u8D44\u6E90\u5305\u4E0E\u56DE\u6536\u7AD9\u89C4\u5219\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u684C\u9762 packs.uninstall\u3002\u5E73\u53F0\u805A\u5408\u5305\u76F4\u63A5\u62D2\u7EDD\u3002\n */\nexport async function uninstallPack(id: string) {\n const context = resolveBdsContext();\n const packs = listInstalledWorldPacks(context.bdsRoot, context.levelName);\n const resolved = resolveUninstallTargets(packs, [id]);\n if (resolved.status === \"not_found\") throw new Error(\"\u627E\u4E0D\u5230\u4E16\u754C\u5305\");\n if (resolved.status === \"protected\") throw new Error(\"\u5E73\u53F0\u805A\u5408\u5305\u4E0D\u53EF\u901A\u8FC7\u4E16\u754C\u5305\u5378\u8F7D\uFF0C\u8BF7\u4F7F\u7528\u6A21\u5757\u7BA1\u7406\");\n const queue = buildPackUninstallQueue(resolved.selected, packs, false);\n const removed = await runPackUninstallQueue(queue, { bdsRoot: context.bdsRoot, levelName: context.levelName, purge: false });\n return { changed: true, restartRequired: true, removed };\n}\n/**\n * \u89E3\u6790\u684C\u9762\u4F20\u5165\u7684\u8D44\u6E90\u5305\u7248\u672C\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1Apacks.version\u3002\u53EA\u63A5\u53D7 major.minor.patch\uFF0C\u6BCF\u4E00\u6BB5\u90FD\u662F\u975E\u8D1F\u5B89\u5168\u6574\u6570\u3002\n */\nexport function parsePackVersion(value: unknown): [number, number, number] {\n if (typeof value !== \"string\") throw new Error(\"\u7248\u672C\u53F7\u5FC5\u987B\u4E3A major.minor.patch\");\n const parts = value.split(\".\");\n if (parts.length !== 3) throw new Error(\"\u7248\u672C\u53F7\u5FC5\u987B\u4E3A major.minor.patch\");\n const numbers = parts.map((part) => {\n if (!/^\\d+$/.test(part)) throw new Error(\"\u7248\u672C\u53F7\u5FC5\u987B\u4E3A\u975E\u8D1F\u6574\u6570\");\n const number = Number(part);\n if (!Number.isSafeInteger(number)) throw new Error(\"\u7248\u672C\u53F7\u8D85\u51FA\u53EF\u7CBE\u786E\u8868\u793A\u7684\u6574\u6570\u8303\u56F4\");\n return number;\n });\n const major = numbers[0];\n const minor = numbers[1];\n const patch = numbers[2];\n if (major === undefined || minor === undefined || patch === undefined) throw new Error(\"\u7248\u672C\u53F7\u5FC5\u987B\u4E3A major.minor.patch\");\n return [major, minor, patch];\n}\n\n/**\n * \u4FEE\u6539\u4E00\u4E2A\u5DF2\u5B89\u88C5\u8D44\u6E90\u5305\u7684\u6E05\u5355\u7248\u672C\uFF0C\u5E76\u5728\u5DF2\u542F\u7528\u65F6\u540C\u6B65\u4E16\u754C\u542F\u7528\u5217\u8868\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u684C\u9762 packs.version\u3002\u884C\u4E3A\u5305\u76F4\u63A5\u62D2\u7EDD\u3002\n * \u6A21\u5757\u6761\u76EE\u5199\u6210\u4E0E header \u76F8\u540C\u7684\u5B8C\u6574\u7248\u672C\uFF1B\u547D\u4EE4\u884C packs bump \u4ECD\u53EA\u62AC\u4FEE\u8BA2\u53F7\u3002\n */\nexport async function setPackVersion(id: string, versionText: unknown) {\n const version = parsePackVersion(versionText);\n const context = resolveBdsContext();\n const pack = listInstalledWorldPacks(context.bdsRoot, context.levelName).find(row => row.uuid === id);\n if (!pack) throw new Error(\"\u627E\u4E0D\u5230\u4E16\u754C\u5305\");\n if (pack.kind !== \"resource\") throw new Error(\"\u53EA\u80FD\u4FEE\u6539\u8D44\u6E90\u5305\u7684\u7248\u672C\u53F7\");\n if (pack.version[0] === version[0] && pack.version[1] === version[1] && pack.version[2] === version[2]) {\n return { changed: false, restartRequired: false, version: pack.version.join(\".\") };\n }\n const next = writePackHeaderVersion(pack.dir, version, \"full\");\n if (pack.enabled) {\n await enableInstalledPack({ ...context, info: { uuid: pack.uuid, version: next, kind: \"resource\", name: pack.name } });\n }\n return { changed: true, restartRequired: true, version: next.join(\".\") };\n}\n\nexport async function togglePack(id: string, enabled: boolean) {\n const context = resolveBdsContext();\n const pack = listInstalledWorldPacks(context.bdsRoot, context.levelName).find(row => row.uuid === id);\n if (!pack) throw new Error(\"\u627E\u4E0D\u5230\u4E16\u754C\u5305\");\n if (enabled) await enableInstalledPack({ ...context, info: { uuid: pack.uuid, version: pack.version, kind: pack.kind, name: pack.name } });\n else await disableInstalledPack({ ...context, kind: pack.kind, packUuid: pack.uuid, version: pack.version });\n return { changed: true, restartRequired: true };\n}\nexport function playerFiles() {\n const bds = resolveBdsContext().bdsRoot;\n return { allowlist: path.join(bds, \"allowlist.json\"), permissions: path.join(bds, \"permissions.json\"), sfmcPermissions: path.join(ROOT, \"configs\", \"permissions.json\") };\n}\nexport function savePlayerPermissions(kind: string, entries: unknown) {\n const files = playerFiles();\n if (!(kind in files) || !Array.isArray(entries)) throw new Error(\"\u975E\u6CD5\u73A9\u5BB6\u6743\u9650\u7C7B\u578B\u6216\u5185\u5BB9\");\n for (const entry of entries) {\n if (!entry || typeof entry !== \"object\") throw new Error(\"\u6743\u9650\u6761\u76EE\u5FC5\u987B\u4E3A\u5BF9\u8C61\");\n const row = entry as Record<string, unknown>;\n if (kind === \"allowlist\" && (typeof row.name !== \"string\" || /[\\r\\n]/.test(row.name))) throw new Error(\"\u5141\u8BB8\u540D\u5355\u9700\u63D0\u4F9B\u5408\u6CD5\u73A9\u5BB6\u540D\u79F0\");\n if (kind === \"permissions\" && (typeof row.xuid !== \"string\" || !/^\\d+$/.test(row.xuid) || ![\"visitor\", \"member\", \"operator\"].includes(String(row.permission)))) throw new Error(\"BDS \u6743\u9650\u9700\u6709\u6548 XUID \u548C\u6743\u9650\u7B49\u7EA7\");\n if (kind === \"sfmcPermissions\" && (typeof row.player_name !== \"string\" || !row.player_name.trim() || /[\\r\\n\\0]/.test(row.player_name) || !Number.isInteger(row.level) || Number(row.level) < 0 || Number(row.level) > 3)) throw new Error(\"SFMC \u6743\u9650\u9700\u5408\u6CD5\u73A9\u5BB6\u540D\u53CA 0 \u5230 3 \u7684\u6574\u6570\u7B49\u7EA7\");\n }\n atomicJson(files[kind as keyof typeof files], entries);\n return { saved: true };\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAC9B,SAAS,aAAa;AACtB,SAAS,4BAA4B;AACrC,SAAS,MAAM,0BAA0B;AACzC,SAAS,mBAAmB,wBAAwB;AACpD,SAAS,yBAAyB,qBAAqB,sBAAsB,8BAA8B;AAC3G,SAAS,yBAAyB,qBAAqB,yBAAyB,6BAA6B;AAC7G,SAAS,kBAAkB;AAC3B,SAAS,sBAAsB,iCAAiC;AAChE,SAAS,+BAA+B;AAMjC,SAAS,SAAS,MAAsB;AAC7C,SAAO,KAAK,WAAW,CAAC,MAAM,QAAS,KAAK,MAAM,CAAC,IAAI;AACzD;AACO,SAAS,SAAY,MAAc,UAAgB;AACxD,MAAI;AAAE,WAAO,KAAK,MAAM,SAAS,GAAG,aAAa,MAAM,MAAM,CAAC,CAAC;AAAA,EAAQ,SAChE,OAAO;AAAE,QAAK,MAAgC,SAAS,SAAU,QAAO;AAAU,UAAM;AAAA,EAAO;AACxG;AACO,SAAS,WAAW,OAAwB;AACjD,MAAI,OAAO,UAAU,YAAY,CAAC,qCAAqC,KAAK,KAAK,KAAK,MAAM,SAAS,IAAI,EAAG,OAAM,IAAI,MAAM,gCAAO;AACnI,SAAO;AACT;AACA,eAAsB,cAAc;AAClC,QAAM,QAAQ,MAAM,iBAAiB,GAAG,OAAO,SAAO,CAAC,wBAAwB,IAAI,SAAS,CAAC;AAE7F,QAAM,UAAU,SAAyD,KAAK,KAAK,MAAM,WAAW,cAAc,GAAG,CAAC,CAAC;AACvH,QAAM,SAAS,IAAI,KAAK,QAAQ,WAAW,CAAC,GAAG,OAAO,SAAO,IAAI,MAAM,IAAI,IAAI,EAAE,IAAI,SAAO,CAAC,IAAI,IAAK,IAAI,IAAK,CAAC,CAAC;AACjH,SAAO,EAAE,SAAS,KAAK,IAAI,UAAQ,EAAE,IAAI,IAAI,WAAW,QAAQ,IAAI,UAAU,MAAM,OAAO,IAAI,IAAI,SAAS,KAAK,IAAI,WAAW,SAAS,IAAI,WAAW,WAAW,SAAS,IAAI,SAAS,QAAQ,GAAG,UAAU,KAAK,KAAK,MAAM,WAAW,YAAY,IAAI,QAAQ,CAAC,EAAE,eAAe,EAAE,EAAE,EAAE;AAC3R;AACA,SAAS,aAAa;AAAE,QAAM,QAAQ,mBAAmB;AAAG,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,kDAAU;AAAG,SAAO;AAAO;AACnH,eAAsB,cAAc,OAAe;AACjD,MAAI,SAAS,MAAM,qBAAqB;AAExC,MAAI,OAAO,OAAO,OAAO,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,MAAM,QAAQ,MAAM,QAAQ,CAAC,GAAG;AAC/E,aAAS,MAAM,qBAAqB,EAAE,OAAO,KAAK,CAAC;AAAA,EACrD;AACA,QAAM,SAAS,MAAM,KAAK,EAAE,YAAY;AACxC,SAAO;AAAA,IACL,SAAS,OAAO,QAAQ,OAAO,KAAK,EAAE,OAAO,CAAC,CAAC,IAAI,QAAQ,MAAM;AAC/D,UAAI,wBAAwB,EAAE,EAAG,QAAO;AACxC,UAAI,CAAC,OAAQ,QAAO;AACpB,aAAO,CAAC,IAAI,SAAS,MAAM,SAAS,aAAa,SAAS,GAAG,EAAE,KAAK,CAAC,UAAU,OAAO,YAAY,EAAE,SAAS,MAAM,CAAC;AAAA,IACtH,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,QAAQ,OAAO,EAAE,IAAI,GAAG,SAAS,EAAE;AAAA,IAChD,OAAO,OAAO;AAAA,EAChB;AACF;AAOA,eAAsB,mBAAmB,IAAoC;AAC3E,MAAI,SAAS,MAAM,qBAAqB;AACxC,QAAM,SAAS,OAAO,MAAM,EAAE;AAC9B,MAAI,UAAU,CAAC,MAAM,QAAQ,OAAO,QAAQ,EAAG,UAAS,MAAM,qBAAqB,EAAE,OAAO,KAAK,CAAC;AAClG,QAAM,QAAQ,OAAO,MAAM,EAAE;AAC7B,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,aAAa,MAAM,YAAY,GAAG;AACxC,SAAO,0BAA0B,IAAI,qBAAqB,MAAM,UAAU,SAAS,CAAC;AACtF;AACA,eAAsB,SAAS,MAAgB;AAC7C,QAAM,WAAW,QAAQ,UAAU,CAAC,WAAW,GAAG,GAAG,IAAI,GAAG,IAAI;AAChE,SAAO,EAAE,SAAS,KAAK;AACzB;AACO,SAAS,WAAW,SAAiB,MAAgB,KAAa,QAAgD;AACvH,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,QAAQ,MAAM,SAAS,MAAM,EAAE,KAAK,KAAK,EAAE,GAAG,QAAQ,KAAK,WAAW,MAAM,aAAa,IAAI,GAAG,aAAa,MAAM,OAAO,CAAC,UAAU,QAAQ,MAAM,EAAE,CAAC;AAC5J,QAAI,aAAa;AACjB,eAAW,UAAU,CAAC,MAAM,QAAQ,MAAM,MAAM,EAAG,QAAO,GAAG,QAAQ,CAAC,UAAkB;AAAE,YAAM,OAAO,MAAM,SAAS;AAAG,oBAAc,aAAa,MAAM,MAAM,IAAK;AAAG,eAAS,IAAI;AAAA,IAAG,CAAC;AACzL,UAAM,GAAG,SAAS,MAAM;AAAG,UAAM,GAAG,SAAS,UAAQ,SAAS,IAAI,QAAQ,IAAI,OAAO,IAAI,MAAM,mCAAU,IAAI,MAAM,UAAU,EAAE,CAAC,CAAC;AAAA,EACnI,CAAC;AACH;AACA,eAAsB,WAAW,MAAgB,CAAC,GAAG;AACnD,QAAM,MAAM,cAAc,KAAK,KAAK,KAAK,QAAQ,WAAW,CAAC,GAAG,OAAO,mBAAmB,CAAC,EAAE;AAC7F,QAAM,MAAM,MAAM,OAAO;AACzB,SAAO,IAAI,kBAAkB,EAAE,MAAM,MAAM,IAAI,CAAC;AAClD;AACA,eAAsB,gBAAgB,KAAe;AACnD,QAAM,YAAY,KAAK,QAAQ,WAAW,CAAC;AAC3C,QAAM,MAAM,MAAM,OAAO,cAAc,KAAK,KAAK,WAAW,OAAO,mBAAmB,CAAC,EAAE;AAKzF,QAAM,WAAW,MAAM,OAAO,cAAc,WAAW,CAAC,EAAE;AAC1D,QAAM,OAAO,MAAM,IAAI,kBAAkB,EAAE,MAAM,MAAM,IAAI,CAAC;AAC5D,QAAM,SAAS,MAAM,IAAI,oBAAoB,KAAK,UAAU,EAAE,MAAM,MAAM,UAAU,KAAK,UAAU,SAAS,SAAS,uBAAuB,QAAQ,IAAI,uBAAuB,CAAC;AAChL,MAAI,OAAO,OAAO,OAAQ,OAAM,IAAI,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC;AACvE,SAAO;AACT;AAEA,MAAM,sBAAsB;AAC5B,MAAM,YAAY,OAAO,KAAK,CAAC,KAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,EAAI,CAAC;AAO9E,SAAS,cAAc,KAA4B;AACjD,QAAM,OAAO,KAAK,KAAK,KAAK,eAAe;AAC3C,MAAI;AACJ,MAAI;AAAE,WAAO,GAAG,UAAU,IAAI;AAAA,EAAG,QAAQ;AAAE,WAAO;AAAA,EAAM;AACxD,MAAI,CAAC,KAAK,OAAO,KAAK,KAAK,eAAe,KAAK,KAAK,QAAQ,KAAK,KAAK,OAAO,oBAAqB,QAAO;AACzG,SAAO,GAAG,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,OAAO,CAAC;AACjD;AAEO,SAAS,YAAY;AAC1B,QAAM,UAAU,kBAAkB;AAClC,SAAO,EAAE,OAAO,wBAAwB,QAAQ,SAAS,QAAQ,SAAS,EAAE,IAAI,UAAQ;AACtF,UAAM,YAAY,cAAc,KAAK,GAAG;AACxC,WAAO;AAAA,MACL,IAAI,KAAK;AAAA,MACT,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,SAAS,KAAK,QAAQ,KAAK,GAAG;AAAA,MAC9B,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,MACjC,GAAI,oBAAoB,IAAI,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;AAAA,IACzD;AAAA,EACF,CAAC,EAAE;AACL;AAOO,SAAS,aAAa,IAA2E;AACtG,QAAM,QAAgD,EAAE,IAAI,MAAM,MAAM,MAAM,KAAK;AACnF,QAAM,UAAU,kBAAkB;AAClC,QAAM,OAAO,wBAAwB,QAAQ,SAAS,QAAQ,SAAS,EAAE,KAAK,SAAO,IAAI,KAAK,YAAY,MAAM,GAAG,YAAY,CAAC;AAChI,MAAI,CAAC,QAAQ,CAAC,cAAc,KAAK,GAAG,EAAG,QAAO;AAC9C,QAAM,OAAO,KAAK,KAAK,KAAK,KAAK,eAAe;AAChD,MAAI;AACJ,MAAI;AAAE,WAAO,GAAG,aAAa,IAAI;AAAA,EAAG,QAAQ;AAAE,WAAO;AAAA,EAAO;AAC5D,MAAI,KAAK,SAAS,UAAU,UAAU,CAAC,KAAK,SAAS,GAAG,UAAU,MAAM,EAAE,OAAO,SAAS,KAAK,KAAK,SAAS,oBAAqB,QAAO;AACzI,SAAO,EAAE,IAAI,KAAK,MAAM,MAAM,aAAa,MAAM,KAAK,SAAS,QAAQ,EAAE;AAC3E;AAMA,eAAsB,cAAc,IAAY;AAC9C,QAAM,UAAU,kBAAkB;AAClC,QAAM,QAAQ,wBAAwB,QAAQ,SAAS,QAAQ,SAAS;AACxE,QAAM,WAAW,wBAAwB,OAAO,CAAC,EAAE,CAAC;AACpD,MAAI,SAAS,WAAW,YAAa,OAAM,IAAI,MAAM,sCAAQ;AAC7D,MAAI,SAAS,WAAW,YAAa,OAAM,IAAI,MAAM,sIAAwB;AAC7E,QAAM,QAAQ,wBAAwB,SAAS,UAAU,OAAO,KAAK;AACrE,QAAM,UAAU,MAAM,sBAAsB,OAAO,EAAE,SAAS,QAAQ,SAAS,WAAW,QAAQ,WAAW,OAAO,MAAM,CAAC;AAC3H,SAAO,EAAE,SAAS,MAAM,iBAAiB,MAAM,QAAQ;AACzD;AAKO,SAAS,iBAAiB,OAA0C;AACzE,MAAI,OAAO,UAAU,SAAU,OAAM,IAAI,MAAM,wDAA0B;AACzE,QAAM,QAAQ,MAAM,MAAM,GAAG;AAC7B,MAAI,MAAM,WAAW,EAAG,OAAM,IAAI,MAAM,wDAA0B;AAClE,QAAM,UAAU,MAAM,IAAI,CAAC,SAAS;AAClC,QAAI,CAAC,QAAQ,KAAK,IAAI,EAAG,OAAM,IAAI,MAAM,8DAAY;AACrD,UAAM,SAAS,OAAO,IAAI;AAC1B,QAAI,CAAC,OAAO,cAAc,MAAM,EAAG,OAAM,IAAI,MAAM,4FAAiB;AACpE,WAAO;AAAA,EACT,CAAC;AACD,QAAM,QAAQ,QAAQ,CAAC;AACvB,QAAM,QAAQ,QAAQ,CAAC;AACvB,QAAM,QAAQ,QAAQ,CAAC;AACvB,MAAI,UAAU,UAAa,UAAU,UAAa,UAAU,OAAW,OAAM,IAAI,MAAM,wDAA0B;AACjH,SAAO,CAAC,OAAO,OAAO,KAAK;AAC7B;AAOA,eAAsB,eAAe,IAAY,aAAsB;AACrE,QAAM,UAAU,iBAAiB,WAAW;AAC5C,QAAM,UAAU,kBAAkB;AAClC,QAAM,OAAO,wBAAwB,QAAQ,SAAS,QAAQ,SAAS,EAAE,KAAK,SAAO,IAAI,SAAS,EAAE;AACpG,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,sCAAQ;AACnC,MAAI,KAAK,SAAS,WAAY,OAAM,IAAI,MAAM,oEAAa;AAC3D,MAAI,KAAK,QAAQ,CAAC,MAAM,QAAQ,CAAC,KAAK,KAAK,QAAQ,CAAC,MAAM,QAAQ,CAAC,KAAK,KAAK,QAAQ,CAAC,MAAM,QAAQ,CAAC,GAAG;AACtG,WAAO,EAAE,SAAS,OAAO,iBAAiB,OAAO,SAAS,KAAK,QAAQ,KAAK,GAAG,EAAE;AAAA,EACnF;AACA,QAAM,OAAO,uBAAuB,KAAK,KAAK,SAAS,MAAM;AAC7D,MAAI,KAAK,SAAS;AAChB,UAAM,oBAAoB,EAAE,GAAG,SAAS,MAAM,EAAE,MAAM,KAAK,MAAM,SAAS,MAAM,MAAM,YAAY,MAAM,KAAK,KAAK,EAAE,CAAC;AAAA,EACvH;AACA,SAAO,EAAE,SAAS,MAAM,iBAAiB,MAAM,SAAS,KAAK,KAAK,GAAG,EAAE;AACzE;AAEA,eAAsB,WAAW,IAAY,SAAkB;AAC7D,QAAM,UAAU,kBAAkB;AAClC,QAAM,OAAO,wBAAwB,QAAQ,SAAS,QAAQ,SAAS,EAAE,KAAK,SAAO,IAAI,SAAS,EAAE;AACpG,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,sCAAQ;AACnC,MAAI,QAAS,OAAM,oBAAoB,EAAE,GAAG,SAAS,MAAM,EAAE,MAAM,KAAK,MAAM,SAAS,KAAK,SAAS,MAAM,KAAK,MAAM,MAAM,KAAK,KAAK,EAAE,CAAC;AAAA,MACpI,OAAM,qBAAqB,EAAE,GAAG,SAAS,MAAM,KAAK,MAAM,UAAU,KAAK,MAAM,SAAS,KAAK,QAAQ,CAAC;AAC3G,SAAO,EAAE,SAAS,MAAM,iBAAiB,KAAK;AAChD;AACO,SAAS,cAAc;AAC5B,QAAM,MAAM,kBAAkB,EAAE;AAChC,SAAO,EAAE,WAAW,KAAK,KAAK,KAAK,gBAAgB,GAAG,aAAa,KAAK,KAAK,KAAK,kBAAkB,GAAG,iBAAiB,KAAK,KAAK,MAAM,WAAW,kBAAkB,EAAE;AACzK;AACO,SAAS,sBAAsB,MAAc,SAAkB;AACpE,QAAM,QAAQ,YAAY;AAC1B,MAAI,EAAE,QAAQ,UAAU,CAAC,MAAM,QAAQ,OAAO,EAAG,OAAM,IAAI,MAAM,oEAAa;AAC9E,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,SAAS,OAAO,UAAU,SAAU,OAAM,IAAI,MAAM,wDAAW;AACpE,UAAM,MAAM;AACZ,QAAI,SAAS,gBAAgB,OAAO,IAAI,SAAS,YAAY,SAAS,KAAK,IAAI,IAAI,GAAI,OAAM,IAAI,MAAM,gFAAe;AACtH,QAAI,SAAS,kBAAkB,OAAO,IAAI,SAAS,YAAY,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC,WAAW,UAAU,UAAU,EAAE,SAAS,OAAO,IAAI,UAAU,CAAC,GAAI,OAAM,IAAI,MAAM,wEAAsB;AACtM,QAAI,SAAS,sBAAsB,OAAO,IAAI,gBAAgB,YAAY,CAAC,IAAI,YAAY,KAAK,KAAK,WAAW,KAAK,IAAI,WAAW,KAAK,CAAC,OAAO,UAAU,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,IAAI,KAAK,OAAO,IAAI,KAAK,IAAI,GAAI,OAAM,IAAI,MAAM,uGAA4B;AAAA,EACxQ;AACA,aAAW,MAAM,IAA0B,GAAG,OAAO;AACrD,SAAO,EAAE,OAAO,KAAK;AACvB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -7,10 +7,23 @@ export declare function stopForMaintenance(names: ServiceName[], phase: TaskCont
|
|
|
7
7
|
export declare function restoreRunning(names: ServiceName[], phase: TaskContext["phase"]): Promise<void>;
|
|
8
8
|
export declare function verifyRunning(names: ServiceName[]): Promise<void>;
|
|
9
9
|
export declare function createSnapshot(id: string, phase: TaskContext["phase"]): Promise<string>;
|
|
10
|
+
/** 备份只读取实例文件,即使复制失败也恢复原先运行的服务。 */
|
|
11
|
+
export declare function createBackup(context: TaskContext): Promise<{
|
|
12
|
+
backupId: string;
|
|
13
|
+
}>;
|
|
10
14
|
export declare function restoreSnapshot(id: string, phase: TaskContext["phase"]): Promise<{
|
|
11
15
|
restored: string;
|
|
12
16
|
}>;
|
|
13
|
-
|
|
17
|
+
/**
|
|
18
|
+
* 删除一份已完成的备份目录。
|
|
19
|
+
* 使用场景:桌面 backups.delete。只删除 `.sfmc/backups/<id>` 这一层,且必须带 manifest.json。
|
|
20
|
+
* 不跟随符号链接,避免编号被解析到备份目录之外。
|
|
21
|
+
*/
|
|
22
|
+
export declare function deleteSnapshot(id: string): Promise<{
|
|
23
|
+
deleted: string;
|
|
24
|
+
}>;
|
|
25
|
+
/** 普通维护只负责停服与恢复;全量快照仅由手动备份任务创建。 */
|
|
26
|
+
export declare function withStoppedServices<T>(context: TaskContext, names: ServiceName[], action: () => Promise<T>): Promise<T>;
|
|
14
27
|
export declare function launchPlatformUpdate(store: TaskStore, context: TaskContext, target: string): Promise<{
|
|
15
28
|
detached: boolean;
|
|
16
29
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maintenance.d.ts","sourceRoot":"","sources":["../../src/management/maintenance.ts"],"names":[],"mappings":"AAMA,OAAO,EAAoD,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAEpG,OAAO,EAAc,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAE1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAa3D,wBAAgB,eAAe,IAAI,MAAM,CASxC;AACD,wBAAsB,cAAc,CAAC,mBAAmB,UAAO,GAAG,OAAO,CAAC,cAAc,CAAC,CAaxF;AACD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAUlH;AACD,wBAAsB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,iBAOrF;AACD,wBAAsB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,iBAWvD;AAMD,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"maintenance.d.ts","sourceRoot":"","sources":["../../src/management/maintenance.ts"],"names":[],"mappings":"AAMA,OAAO,EAAoD,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAEpG,OAAO,EAAc,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAE1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAa3D,wBAAgB,eAAe,IAAI,MAAM,CASxC;AACD,wBAAsB,cAAc,CAAC,mBAAmB,UAAO,GAAG,OAAO,CAAC,cAAc,CAAC,CAaxF;AACD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAUlH;AACD,wBAAsB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,iBAOrF;AACD,wBAAsB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,iBAWvD;AAMD,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,mBAsB3E;AACD,kCAAkC;AAClC,wBAAsB,YAAY,CAAC,OAAO,EAAE,WAAW;;GAatD;AACD,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC;;GAiB5E;AACD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM;;GAY9C;AACD,mCAAmC;AACnC,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAa7H;AACD,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM;;GAmBhG;AACD,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,iBASxD"}
|
|
@@ -43,7 +43,7 @@ async function attachmentPlan(includeReleaseNotes = true) {
|
|
|
43
43
|
targetVersion = metadata.version;
|
|
44
44
|
}
|
|
45
45
|
const [rows, releaseNotes] = await Promise.all([runtimeRows(), includeReleaseNotes && !development ? platformReleaseNotes(targetVersion) : void 0]);
|
|
46
|
-
return { currentVersion, targetVersion, development, upgradeRequired: !development && targetVersion !== currentVersion, externalServices: rows.filter((row) => row.running && row.ownership === "external").map((row) => row.name), steps: development ? ["\u4FDD\u7559\u6E90\u7801\u5DE5\u4F5C\u533A\uFF0C\u8FDE\u63A5\u5F53\u524D\u5F00\u53D1\u90E8\u7F72"] : ["\u4E0B\u8F7D\u56FA\u5B9A\u7248\u672C\u7684\u5E73\u53F0\u4F9D\u8D56\u7EC4\u5408", "\u505C\u6B62\u53D7\u5F71\u54CD\u670D\u52A1\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA", "\
|
|
46
|
+
return { currentVersion, targetVersion, development, upgradeRequired: !development && targetVersion !== currentVersion, externalServices: rows.filter((row) => row.running && row.ownership === "external").map((row) => row.name), steps: development ? ["\u4FDD\u7559\u6E90\u7801\u5DE5\u4F5C\u533A\uFF0C\u8FDE\u63A5\u5F53\u524D\u5F00\u53D1\u90E8\u7F72"] : ["\u4E0B\u8F7D\u56FA\u5B9A\u7248\u672C\u7684\u5E73\u53F0\u4F9D\u8D56\u7EC4\u5408", "\u505C\u6B62\u53D7\u5F71\u54CD\u670D\u52A1\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA", "\u5207\u6362\u5E73\u53F0\u5E76\u9A8C\u8BC1", "\u6062\u590D\u6B64\u524D\u8FD0\u884C\u7684\u670D\u52A1"], ...releaseNotes ? { releaseNotes } : {} };
|
|
47
47
|
}
|
|
48
48
|
async function stopForMaintenance(names, phase) {
|
|
49
49
|
const before = await queryServicesRuntimeLocal();
|
|
@@ -82,20 +82,42 @@ function snapshotTargets() {
|
|
|
82
82
|
return { configs: path.join(ROOT, "configs"), modules: path.join(ROOT, "modules"), data: path.dirname(path.resolve(ROOT, db.dbDir ?? "data/sfmc_data.db")), bds };
|
|
83
83
|
}
|
|
84
84
|
async function createSnapshot(id, phase) {
|
|
85
|
+
if (!/^[a-zA-Z0-9-]{1,80}$/.test(id)) throw new Error("\u975E\u6CD5\u5907\u4EFD\u7F16\u53F7");
|
|
85
86
|
phase("backup", "\u521B\u5EFA\u5DF2\u505C\u670D\u7684\u4E00\u81F4\u5907\u4EFD");
|
|
86
87
|
const destination = path.join(ROOT, ".sfmc", "backups", id);
|
|
87
|
-
fs.mkdirSync(destination, { recursive: true });
|
|
88
88
|
const targets = snapshotTargets();
|
|
89
89
|
for (const target of Object.values(targets)) {
|
|
90
90
|
const absolute = path.resolve(target);
|
|
91
91
|
if (absolute === path.parse(absolute).root || absolute === path.resolve(ROOT) || destination.startsWith(absolute + path.sep)) throw new Error("\u5907\u4EFD\u76EE\u6807\u5305\u542B\u5B9E\u4F8B\u6839\u76EE\u5F55\u6216\u5907\u4EFD\u76EE\u5F55\uFF0C\u5DF2\u4E2D\u6B62");
|
|
92
92
|
}
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
94
|
+
fs.mkdirSync(destination);
|
|
95
|
+
try {
|
|
96
|
+
for (const [key, target] of Object.entries(targets)) {
|
|
97
|
+
if (fs.existsSync(target)) await fs.promises.cp(target, path.join(destination, key), { recursive: true, dereference: false });
|
|
98
|
+
}
|
|
99
|
+
atomicJson(path.join(destination, "manifest.json"), { createdAt: (/* @__PURE__ */ new Date()).toISOString(), targets, complete: true });
|
|
100
|
+
} catch (error) {
|
|
101
|
+
await fs.promises.rm(destination, { recursive: true, force: true });
|
|
102
|
+
throw error;
|
|
95
103
|
}
|
|
96
|
-
atomicJson(path.join(destination, "manifest.json"), { createdAt: (/* @__PURE__ */ new Date()).toISOString(), targets, complete: true });
|
|
97
104
|
return destination;
|
|
98
105
|
}
|
|
106
|
+
async function createBackup(context) {
|
|
107
|
+
const running = await stopForMaintenance([...START_ORDER], context.phase);
|
|
108
|
+
let completed = false;
|
|
109
|
+
try {
|
|
110
|
+
await createSnapshot(context.id, context.phase);
|
|
111
|
+
completed = true;
|
|
112
|
+
} finally {
|
|
113
|
+
try {
|
|
114
|
+
await restoreRunning(running, context.phase);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
throw Object.assign(new Error(`\u5907\u4EFD${completed ? "\u5DF2\u5B8C\u6210" : "\u672A\u5B8C\u6210"}\uFF0C\u6062\u590D\u670D\u52A1\u8FD0\u884C\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`), { code: "io", details: completed ? { backupId: context.id } : void 0 });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return { backupId: context.id };
|
|
120
|
+
}
|
|
99
121
|
async function restoreSnapshot(id, phase) {
|
|
100
122
|
if (!/^[a-zA-Z0-9-]{1,80}$/.test(id)) throw new Error("\u975E\u6CD5\u5907\u4EFD\u7F16\u53F7");
|
|
101
123
|
const source = path.join(ROOT, ".sfmc", "backups", id);
|
|
@@ -114,31 +136,26 @@ async function restoreSnapshot(id, phase) {
|
|
|
114
136
|
}
|
|
115
137
|
return { restored: id };
|
|
116
138
|
}
|
|
117
|
-
async function
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
const
|
|
121
|
-
if (
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
139
|
+
async function deleteSnapshot(id) {
|
|
140
|
+
if (!/^[a-zA-Z0-9-]{1,80}$/.test(id)) throw new Error("\u975E\u6CD5\u5907\u4EFD\u7F16\u53F7");
|
|
141
|
+
const directory = path.resolve(ROOT, ".sfmc", "backups");
|
|
142
|
+
const target = path.resolve(directory, id);
|
|
143
|
+
if (path.dirname(target) !== directory) throw new Error("\u975E\u6CD5\u5907\u4EFD\u8DEF\u5F84");
|
|
144
|
+
let stat;
|
|
145
|
+
try {
|
|
146
|
+
stat = fs.lstatSync(target);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
if (error.code === "ENOENT") throw new Error("\u627E\u4E0D\u5230\u5907\u4EFD");
|
|
149
|
+
throw error;
|
|
128
150
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (target === path.parse(target).root || target === path.resolve(ROOT)) throw new Error("\u4E0D\u5B89\u5168\u7684\u7A0B\u5E8F\u56DE\u9000\u76EE\u6807");
|
|
134
|
-
fs.mkdirSync(target, { recursive: true });
|
|
135
|
-
for (const entry of fs.readdirSync(target)) if (!preserved.has(entry)) await fs.promises.rm(path.join(target, entry), { recursive: true, force: true });
|
|
136
|
-
for (const entry of fs.readdirSync(saved)) if (!preserved.has(entry)) await fs.promises.cp(path.join(saved, entry), path.join(target, entry), { recursive: true, dereference: false });
|
|
151
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) throw new Error("\u62D2\u7EDD\u5220\u9664\u975E\u5907\u4EFD\u76EE\u5F55");
|
|
152
|
+
if (!fs.existsSync(path.join(target, "manifest.json"))) throw new Error("\u5907\u4EFD\u4E0D\u5B8C\u6574\uFF0C\u5DF2\u62D2\u7EDD\u5220\u9664");
|
|
153
|
+
await fs.promises.rm(target, { recursive: true, force: true });
|
|
154
|
+
return { deleted: id };
|
|
137
155
|
}
|
|
138
|
-
async function withStoppedServices(context, names, action
|
|
156
|
+
async function withStoppedServices(context, names, action) {
|
|
139
157
|
const running = await stopForMaintenance(names, context.phase);
|
|
140
158
|
try {
|
|
141
|
-
if (backup) await createSnapshot(context.id, context.phase);
|
|
142
159
|
context.phase("execute");
|
|
143
160
|
const result = await action();
|
|
144
161
|
context.phase("verify");
|
|
@@ -147,18 +164,7 @@ async function withStoppedServices(context, names, action, backup = true, rollba
|
|
|
147
164
|
} catch (error) {
|
|
148
165
|
for (const name of [...START_ORDER].reverse().filter((name2) => running.includes(name2))) await services[name].stop(false, false).catch(() => {
|
|
149
166
|
});
|
|
150
|
-
|
|
151
|
-
let rollbackError;
|
|
152
|
-
if (rollbackPrograms) {
|
|
153
|
-
context.phase("rollback-program", "\u56DE\u9000\u7A0B\u5E8F\u7248\u672C\uFF0C\u4FDD\u7559\u5F53\u524D\u73A9\u5BB6\u6570\u636E");
|
|
154
|
-
try {
|
|
155
|
-
await restoreProgramSnapshot(context.id, rollbackPrograms);
|
|
156
|
-
programRolledBack = true;
|
|
157
|
-
} catch (failure) {
|
|
158
|
-
rollbackError = String(failure);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
throw Object.assign(new Error(`\u7EF4\u62A4\u5931\u8D25\uFF0C\u670D\u52A1\u4FDD\u6301\u505C\u6B62\uFF1B\u5907\u4EFD\u7F16\u53F7 ${context.id}: ${error instanceof Error ? error.message : String(error)}`), { code: "io", details: { backupId: context.id, programRolledBack, dataRestoreRequired: Boolean(rollbackPrograms), ...rollbackError ? { rollbackError } : {}, ...error.details ?? {} } });
|
|
167
|
+
throw Object.assign(new Error(`\u7EF4\u62A4\u5931\u8D25\uFF0C\u670D\u52A1\u4FDD\u6301\u505C\u6B62: ${error instanceof Error ? error.message : String(error)}`), { code: "io", details: error.details });
|
|
162
168
|
}
|
|
163
169
|
}
|
|
164
170
|
async function launchPlatformUpdate(store, context, target) {
|
|
@@ -197,7 +203,9 @@ async function runPnpm(args, cwd) {
|
|
|
197
203
|
}
|
|
198
204
|
export {
|
|
199
205
|
attachmentPlan,
|
|
206
|
+
createBackup,
|
|
200
207
|
createSnapshot,
|
|
208
|
+
deleteSnapshot,
|
|
201
209
|
launchPlatformUpdate,
|
|
202
210
|
platformVersion,
|
|
203
211
|
restoreRunning,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/management/maintenance.ts"],
|
|
4
|
-
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { spawn } from \"node:child_process\";\nimport { fileURLToPath } from \"node:url\";\nimport { ROOT, isMonorepoLayout, isSourceCheckout } from \"../runtime.js\";\nimport { queryServicesRuntimeLocal, services, START_ORDER, type ServiceName } from \"../services.js\";\nimport { resolveBdsContext } from \"../pack-lifecycle.js\";\nimport { atomicJson, type TaskContext, type TaskStore } from \"./tasks.js\";\nimport { readJson, runProcess } from \"./domain.js\";\nimport type { AttachmentPlan } from \"@sfmc-bds/management\";\nimport { isDaemonServer } from \"../daemon/role.js\";\nimport { callDaemon } from \"../daemon/client.js\";\nimport { platformReleaseNotes } from \"@sfmc-bds/management/node\";\n\nasync function runtimeRows() {\n if (isDaemonServer()) return queryServicesRuntimeLocal();\n const result = await callDaemon(\"status\");\n if (result.kind !== \"status\") throw new Error(\"\u65E0\u6CD5\u8BFB\u53D6\u5B88\u62A4\u8FDB\u7A0B\u670D\u52A1\u72B6\u6001\");\n return result.rows;\n}\n\nconst require = createRequire(import.meta.url);\nexport function platformVersion(): string {\n if (isMonorepoLayout(ROOT)) return readJson<{ version: string }>(path.join(ROOT, \"packages\", \"meta\", \"package.json\"), { version: \"unknown\" }).version;\n if (process.env.SFMC_PLATFORM_VERSION) return process.env.SFMC_PLATFORM_VERSION;\n const active = readJson<{ version?: string }>(path.join(ROOT, \".sfmc\", \"runtime\", \"active.json\"), {});\n if (active.version) return active.version;\n const deployed = readJson<{ version?: string }>(path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"package.json\"), {});\n if (deployed.version) return deployed.version;\n try { return (require(\"@sfmc-bds/sfmc/package.json\") as { version: string }).version; }\n catch { return (readJson<{ version: string }>(path.join(fileURLToPath(new URL(\"../../..\", import.meta.url)), \"package.json\"), { version: \"unknown\" })).version; }\n}\nexport async function attachmentPlan(includeReleaseNotes = true): Promise<AttachmentPlan> {\n const development = isMonorepoLayout(ROOT) || isSourceCheckout();\n const currentVersion = platformVersion();\n let targetVersion = currentVersion;\n if (!development) {\n const response = await fetch(\"https://registry.npmjs.org/@sfmc-bds%2fsfmc/latest\", { signal: AbortSignal.timeout(15_000) });\n if (!response.ok) throw new Error(`\u5E73\u53F0\u7248\u672C\u68C0\u67E5\u5931\u8D25 (${response.status})`);\n const metadata = await response.json() as { version?: string };\n if (!metadata.version || !/^\\d+\\.\\d+\\.\\d+$/.test(metadata.version)) throw new Error(\"latest \u6CA1\u6709\u6307\u5411\u6709\u6548\u7684\u7A33\u5B9A\u5E73\u53F0\u7248\u672C\");\n targetVersion = metadata.version;\n }\n const [rows, releaseNotes] = await Promise.all([runtimeRows(), includeReleaseNotes && !development ? platformReleaseNotes(targetVersion) : undefined]);\n return { currentVersion, targetVersion, development, upgradeRequired: !development && targetVersion !== currentVersion, externalServices: rows.filter(row => row.running && row.ownership === \"external\").map(row => row.name), steps: development ? [\"\u4FDD\u7559\u6E90\u7801\u5DE5\u4F5C\u533A\uFF0C\u8FDE\u63A5\u5F53\u524D\u5F00\u53D1\u90E8\u7F72\"] : [\"\u4E0B\u8F7D\u56FA\u5B9A\u7248\u672C\u7684\u5E73\u53F0\u4F9D\u8D56\u7EC4\u5408\", \"\u505C\u6B62\u53D7\u5F71\u54CD\u670D\u52A1\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA\", \"\u5907\u4EFD\u914D\u7F6E\u3001\u6A21\u5757\u3001\u6570\u636E\u5E93\u4E0E\u4E16\u754C\", \"\u5207\u6362\u5E73\u53F0\u5E76\u9A8C\u8BC1\", \"\u6062\u590D\u6B64\u524D\u8FD0\u884C\u7684\u670D\u52A1\"], ...(releaseNotes ? { releaseNotes } : {}) };\n}\nexport async function stopForMaintenance(names: ServiceName[], phase: TaskContext[\"phase\"]): Promise<ServiceName[]> {\n const before = await queryServicesRuntimeLocal();\n const affected = before.filter(row => names.includes(row.name) && row.running);\n if (affected.some(row => row.ownership === \"external\")) throw Object.assign(new Error(\"\u5B58\u5728\u65E0\u6CD5\u901A\u8FC7\u5F53\u524D\u5B88\u62A4\u8FDB\u7A0B\u4F18\u96C5\u505C\u6B62\u7684\u5916\u90E8\u670D\u52A1\uFF1B\u8BF7\u5148\u901A\u8FC7\u539F\u7BA1\u7406\u5668\u505C\u6B62\u540E\u91CD\u65B0\u63A5\u5165\"), { code: \"conflict\" });\n phase(\"stop-services\", \"\u4F18\u96C5\u505C\u6B62\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA\");\n const running = affected.map(row => row.name);\n for (const name of [...START_ORDER].reverse().filter(name => names.includes(name))) await services[name].stop(false, false);\n const after = await queryServicesRuntimeLocal();\n if (after.some(row => names.includes(row.name) && row.running)) throw new Error(\"\u670D\u52A1\u672A\u5B8C\u5168\u9000\u51FA\uFF0C\u5DF2\u4E2D\u6B62\u6587\u4EF6\u64CD\u4F5C\");\n return running;\n}\nexport async function restoreRunning(names: ServiceName[], phase: TaskContext[\"phase\"]) {\n phase(\"restore\", \"\u6062\u590D\u7EF4\u62A4\u524D\u8FD0\u884C\u7684\u670D\u52A1\");\n for (const name of START_ORDER.filter(name => names.includes(name))) {\n const result = await services[name].start();\n if (result.status === \"skipped\") throw new Error(`\u6062\u590D ${name} \u5931\u8D25: ${result.reason}`);\n }\n await verifyRunning(names);\n}\nexport async function verifyRunning(names: ServiceName[]) {\n if (!names.length) return;\n await new Promise(resolve => setTimeout(resolve, 2000));\n for (const name of names) {\n if (!services[name].running || !services[name].pid) throw new Error(`${name} \u542F\u52A8\u540E\u5DF2\u9000\u51FA`);\n }\n if (names.includes(\"db\")) {\n const cfg = readJson<{ db_port?: number; http_auth?: string }>(path.join(ROOT, \"configs\", \"db_config.json\"), {});\n const response = await fetch(`http://127.0.0.1:${cfg.db_port ?? 3001}/api/sfmc/status`, { headers: cfg.http_auth ? { authorization: `Bearer ${cfg.http_auth}` } : {}, signal: AbortSignal.timeout(5000) });\n if (!response.ok) throw new Error(`DB \u5065\u5EB7\u68C0\u67E5\u5931\u8D25 (${response.status})`);\n }\n}\nfunction snapshotTargets() {\n const db = readJson<{ dbDir?: string }>(path.join(ROOT, \"configs\", \"db_config.json\"), {});\n const bds = resolveBdsContext().bdsRoot;\n return { configs: path.join(ROOT, \"configs\"), modules: path.join(ROOT, \"modules\"), data: path.dirname(path.resolve(ROOT, db.dbDir ?? \"data/sfmc_data.db\")), bds };\n}\nexport async function createSnapshot(id: string, phase: TaskContext[\"phase\"]) {\n phase(\"backup\", \"\u521B\u5EFA\u5DF2\u505C\u670D\u7684\u4E00\u81F4\u5907\u4EFD\");\n const destination = path.join(ROOT, \".sfmc\", \"backups\", id);\n fs.mkdirSync(destination, { recursive: true });\n const targets = snapshotTargets();\n for (const target of Object.values(targets)) {\n const absolute = path.resolve(target);\n if (absolute === path.parse(absolute).root || absolute === path.resolve(ROOT) || destination.startsWith(absolute + path.sep)) throw new Error(\"\u5907\u4EFD\u76EE\u6807\u5305\u542B\u5B9E\u4F8B\u6839\u76EE\u5F55\u6216\u5907\u4EFD\u76EE\u5F55\uFF0C\u5DF2\u4E2D\u6B62\");\n }\n for (const [key, target] of Object.entries(targets)) {\n if (fs.existsSync(target)) await fs.promises.cp(target, path.join(destination, key), { recursive: true, dereference: false });\n }\n atomicJson(path.join(destination, \"manifest.json\"), { createdAt: new Date().toISOString(), targets, complete: true });\n return destination;\n}\nexport async function restoreSnapshot(id: string, phase: TaskContext[\"phase\"]) {\n if (!/^[a-zA-Z0-9-]{1,80}$/.test(id)) throw new Error(\"\u975E\u6CD5\u5907\u4EFD\u7F16\u53F7\");\n const source = path.join(ROOT, \".sfmc\", \"backups\", id);\n const manifest = readJson<{ complete: boolean; targets: Record<string, string> }>(path.join(source, \"manifest.json\"), { complete: false, targets: {} });\n if (!manifest.complete) throw new Error(\"\u5907\u4EFD\u4E0D\u5B8C\u6574\uFF0C\u7981\u6B62\u6062\u590D\");\n const targets = snapshotTargets();\n phase(\"restore-data\", \"\u6309\u7528\u6237\u660E\u786E\u9009\u62E9\u6062\u590D\u914D\u7F6E\u3001\u6A21\u5757\u3001\u6570\u636E\u5E93\u548C\u4E16\u754C\");\n for (const [key, target] of Object.entries(targets)) {\n if (path.resolve(manifest.targets[key] ?? \"\") !== path.resolve(target)) throw new Error(\"\u5907\u4EFD\u76EE\u6807\u4E0E\u5F53\u524D\u90E8\u7F72\u4E0D\u4E00\u81F4\");\n const saved = path.join(source, key);\n if (!fs.existsSync(saved)) continue;\n const absolute = path.resolve(target);\n if (absolute === path.parse(absolute).root || absolute === path.resolve(ROOT) || fs.lstatSync(saved).isSymbolicLink()) throw new Error(\"\u4E0D\u5B89\u5168\u7684\u6062\u590D\u76EE\u6807\");\n await fs.promises.rm(absolute, { recursive: true, force: true });\n await fs.promises.cp(saved, absolute, { recursive: true, dereference: false });\n }\n return { restored: id };\n}\n/** \u53EA\u56DE\u9000\u7A0B\u5E8F\u5185\u5BB9\uFF0C\u4E16\u754C\u3001\u73A9\u5BB6\u6743\u9650\u548C\u6570\u636E\u5E93\u59CB\u7EC8\u4FDD\u7559\u5728\u5F53\u524D\u72B6\u6001\u3002 */\nasync function restoreProgramSnapshot(id: string, kind: \"modules\" | \"bds\") {\n const source = path.join(ROOT, \".sfmc\", \"backups\", id);\n const manifest = readJson<{ complete: boolean; targets: Record<string, string> }>(path.join(source, \"manifest.json\"), { complete: false, targets: {} });\n const targets = snapshotTargets();\n if (!manifest.complete || path.resolve(manifest.targets[kind] ?? \"\") !== path.resolve(targets[kind])) throw new Error(\"\u7A0B\u5E8F\u5907\u4EFD\u4E0D\u5B8C\u6574\u6216\u76EE\u6807\u4E0D\u5339\u914D\");\n const saved = path.join(source, kind);\n if (kind === \"modules\") {\n if (!fs.existsSync(saved)) return;\n await fs.promises.rm(targets.modules, { recursive: true, force: true });\n await fs.promises.cp(saved, targets.modules, { recursive: true, dereference: false });\n return;\n }\n const cfg = readJson<{ preserve?: string[] }>(path.join(ROOT, \"configs\", \"bds_updater.json\"), {});\n const preserved = new Set([\"worlds\", \"config\", \"server.properties\", \"allowlist.json\", \"whitelist.json\", \"permissions.json\", ...(cfg.preserve ?? []).map(name => name.split(/[\\\\/]/)[0]!)]);\n if (!fs.existsSync(saved)) throw new Error(\"\u7F3A\u5C11\u539F BDS \u7A0B\u5E8F\u5907\u4EFD\");\n const target = path.resolve(targets.bds);\n if (target === path.parse(target).root || target === path.resolve(ROOT)) throw new Error(\"\u4E0D\u5B89\u5168\u7684\u7A0B\u5E8F\u56DE\u9000\u76EE\u6807\");\n fs.mkdirSync(target, { recursive: true });\n for (const entry of fs.readdirSync(target)) if (!preserved.has(entry)) await fs.promises.rm(path.join(target, entry), { recursive: true, force: true });\n for (const entry of fs.readdirSync(saved)) if (!preserved.has(entry)) await fs.promises.cp(path.join(saved, entry), path.join(target, entry), { recursive: true, dereference: false });\n}\nexport async function withStoppedServices<T>(context: TaskContext, names: ServiceName[], action: () => Promise<T>, backup = true, rollbackPrograms?: \"modules\" | \"bds\"): Promise<T> {\n const running = await stopForMaintenance(names, context.phase);\n try {\n if (backup) await createSnapshot(context.id, context.phase);\n context.phase(\"execute\");\n const result = await action();\n context.phase(\"verify\");\n await restoreRunning(running, context.phase);\n return result;\n } catch (error) {\n // \u5931\u8D25\u540E\u4FDD\u6301\u505C\u670D\uFF0C\u4E0D\u731C\u6D4B\u672A\u77E5\u6570\u636E\u8FC1\u79FB\u662F\u5426\u53EF\u4EE5\u5B89\u5168\u6062\u590D\u8FD0\u884C\u3002\n for (const name of [...START_ORDER].reverse().filter(name => running.includes(name))) await services[name].stop(false, false).catch(() => {});\n let programRolledBack = false;\n let rollbackError: string | undefined;\n if (rollbackPrograms) {\n context.phase(\"rollback-program\", \"\u56DE\u9000\u7A0B\u5E8F\u7248\u672C\uFF0C\u4FDD\u7559\u5F53\u524D\u73A9\u5BB6\u6570\u636E\");\n try { await restoreProgramSnapshot(context.id, rollbackPrograms); programRolledBack = true; }\n catch (failure) { rollbackError = String(failure); }\n }\n throw Object.assign(new Error(`\u7EF4\u62A4\u5931\u8D25\uFF0C\u670D\u52A1\u4FDD\u6301\u505C\u6B62\uFF1B\u5907\u4EFD\u7F16\u53F7 ${context.id}: ${error instanceof Error ? error.message : String(error)}`), { code: \"io\", details: { backupId: context.id, programRolledBack, dataRestoreRequired: Boolean(rollbackPrograms), ...(rollbackError ? { rollbackError } : {}), ...((error as { details?: object }).details ?? {}) } });\n }\n}\nexport async function launchPlatformUpdate(store: TaskStore, context: TaskContext, target: string) {\n if (isMonorepoLayout(ROOT)) throw new Error(\"\u6E90\u7801\u90E8\u7F72\u4E0D\u8986\u76D6\u5DE5\u4F5C\u533A\uFF1B\u8BF7\u4F7F\u7528\u6B63\u5E38\u5F00\u53D1\u53D1\u5E03\u6D41\u7A0B\");\n if (!/^\\d+\\.\\d+\\.\\d+$/.test(target)) throw new Error(\"\u9700\u8981\u56FA\u5B9A\u7A33\u5B9A\u7248\u672C\");\n const rows = await runtimeRows();\n if (rows.some(row => row.running && row.ownership === \"external\")) throw new Error(\"\u5916\u90E8\u670D\u52A1\u5FC5\u987B\u5148\u901A\u8FC7\u539F\u7BA1\u7406\u5668\u4F18\u96C5\u505C\u6B62\");\n const request = path.join(ROOT, \".sfmc\", \"operations\", `${context.id}.request.json`);\n const previous = readJson(path.join(ROOT, \".sfmc\", \"runtime\", \"active.json\"), { entry: fs.existsSync(path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"bin\", \"sfmc.mjs\")) ? path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"bin\", \"sfmc.mjs\") : process.env.SFMC_DAEMON_ENTRY, version: platformVersion() });\n atomicJson(request, { root: ROOT, id: context.id, target, running: rows.filter(row => row.running).map(row => row.name), previous });\n const entry = fileURLToPath(new URL(\"./platform-worker.js\", import.meta.url));\n const log = path.join(ROOT, \".sfmc\", \"logs\", \"maintenance.log\");\n fs.mkdirSync(path.dirname(log), { recursive: true });\n // \u4E0D\u628A\u7236\u8FDB\u7A0B\u7684\u65E5\u5FD7\u53E5\u67C4\u4EA4\u7ED9\u5B50\u8FDB\u7A0B\u3002Windows \u4E0A\u7EE7\u627F\u53E5\u67C4\u65F6\uFF0C\u5B88\u62A4\u8FDB\u7A0B\u9000\u51FA\u4F1A\u7ED3\u675F\u5C1A\u672A\u8131\u79BB\u7684\u7EF4\u62A4\u8FDB\u7A0B\uFF0C\u5907\u4EFD\u4F1A\u505C\u5728\u534A\u622A\u4E14\u9501\u65E0\u4EBA\u91CA\u653E\u3002\n const child = spawn(process.execPath, [entry, request], { cwd: ROOT, env: { ...process.env, SFMC_ROOT: ROOT, SFMC_MAINTENANCE_LOG: log }, detached: true, windowsHide: true, stdio: \"ignore\" });\n await new Promise<void>((resolve, reject) => { child.once(\"spawn\", resolve); child.once(\"error\", reject); });\n child.unref();\n const record = store.get(context.id) as ReturnType<TaskStore[\"get\"]> & { workerPid?: number };\n record.status = \"running\"; if (child.pid) record.workerPid = child.pid;\n store.save(record);\n return { detached: true };\n}\nexport async function runPnpm(args: string[], cwd: string) {\n const script = process.env.SFMC_PNPM_ENTRY;\n if (script) return runProcess(process.execPath, [script, ...args], cwd);\n const command = process.platform === \"win32\" ? \"pnpm.cmd\" : \"pnpm\";\n const crossSpawn = require(\"cross-spawn\") as typeof spawn;\n await new Promise<void>((resolve, reject) => {\n const child = crossSpawn(command, args, { cwd, env: process.env, windowsHide: true, stdio: \"ignore\" });\n child.on(\"error\", reject); child.on(\"close\", code => code === 0 ? resolve() : reject(new Error(`pnpm \u6267\u884C\u5931\u8D25 (${code})`)));\n });\n}\n"],
|
|
5
|
-
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAC9B,SAAS,aAAa;AACtB,SAAS,qBAAqB;AAC9B,SAAS,MAAM,kBAAkB,wBAAwB;AACzD,SAAS,2BAA2B,UAAU,mBAAqC;AACnF,SAAS,yBAAyB;AAClC,SAAS,kBAAoD;AAC7D,SAAS,UAAU,kBAAkB;AAErC,SAAS,sBAAsB;AAC/B,SAAS,kBAAkB;AAC3B,SAAS,4BAA4B;AAErC,eAAe,cAAc;AAC3B,MAAI,eAAe,EAAG,QAAO,0BAA0B;AACvD,QAAM,SAAS,MAAM,WAAW,QAAQ;AACxC,MAAI,OAAO,SAAS,SAAU,OAAM,IAAI,MAAM,0EAAc;AAC5D,SAAO,OAAO;AAChB;AAEA,MAAMA,WAAU,cAAc,YAAY,GAAG;AACtC,SAAS,kBAA0B;AACxC,MAAI,iBAAiB,IAAI,EAAG,QAAO,SAA8B,KAAK,KAAK,MAAM,YAAY,QAAQ,cAAc,GAAG,EAAE,SAAS,UAAU,CAAC,EAAE;AAC9I,MAAI,QAAQ,IAAI,sBAAuB,QAAO,QAAQ,IAAI;AAC1D,QAAM,SAAS,SAA+B,KAAK,KAAK,MAAM,SAAS,WAAW,aAAa,GAAG,CAAC,CAAC;AACpG,MAAI,OAAO,QAAS,QAAO,OAAO;AAClC,QAAM,WAAW,SAA+B,KAAK,KAAK,MAAM,gBAAgB,aAAa,QAAQ,cAAc,GAAG,CAAC,CAAC;AACxH,MAAI,SAAS,QAAS,QAAO,SAAS;AACtC,MAAI;AAAE,WAAQA,SAAQ,6BAA6B,EAA0B;AAAA,EAAS,QAChF;AAAE,WAAQ,SAA8B,KAAK,KAAK,cAAc,IAAI,IAAI,YAAY,YAAY,GAAG,CAAC,GAAG,cAAc,GAAG,EAAE,SAAS,UAAU,CAAC,EAAG;AAAA,EAAS;AAClK;AACA,eAAsB,eAAe,sBAAsB,MAA+B;AACxF,QAAM,cAAc,iBAAiB,IAAI,KAAK,iBAAiB;AAC/D,QAAM,iBAAiB,gBAAgB;AACvC,MAAI,gBAAgB;AACpB,MAAI,CAAC,aAAa;AAChB,UAAM,WAAW,MAAM,MAAM,sDAAsD,EAAE,QAAQ,YAAY,QAAQ,IAAM,EAAE,CAAC;AAC1H,QAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,qDAAa,SAAS,MAAM,GAAG;AACjE,UAAM,WAAW,MAAM,SAAS,KAAK;AACrC,QAAI,CAAC,SAAS,WAAW,CAAC,kBAAkB,KAAK,SAAS,OAAO,EAAG,OAAM,IAAI,MAAM,uFAAsB;AAC1G,oBAAgB,SAAS;AAAA,EAC3B;AACA,QAAM,CAAC,MAAM,YAAY,IAAI,MAAM,QAAQ,IAAI,CAAC,YAAY,GAAG,uBAAuB,CAAC,cAAc,qBAAqB,aAAa,IAAI,MAAS,CAAC;AACrJ,SAAO,EAAE,gBAAgB,eAAe,aAAa,iBAAiB,CAAC,eAAe,kBAAkB,gBAAgB,kBAAkB,KAAK,OAAO,SAAO,IAAI,WAAW,IAAI,cAAc,UAAU,EAAE,IAAI,SAAO,IAAI,IAAI,GAAG,OAAO,cAAc,CAAC,kGAAkB,IAAI,CAAC,kFAAiB,wFAAkB,
|
|
4
|
+
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { spawn } from \"node:child_process\";\nimport { fileURLToPath } from \"node:url\";\nimport { ROOT, isMonorepoLayout, isSourceCheckout } from \"../runtime.js\";\nimport { queryServicesRuntimeLocal, services, START_ORDER, type ServiceName } from \"../services.js\";\nimport { resolveBdsContext } from \"../pack-lifecycle.js\";\nimport { atomicJson, type TaskContext, type TaskStore } from \"./tasks.js\";\nimport { readJson, runProcess } from \"./domain.js\";\nimport type { AttachmentPlan } from \"@sfmc-bds/management\";\nimport { isDaemonServer } from \"../daemon/role.js\";\nimport { callDaemon } from \"../daemon/client.js\";\nimport { platformReleaseNotes } from \"@sfmc-bds/management/node\";\n\nasync function runtimeRows() {\n if (isDaemonServer()) return queryServicesRuntimeLocal();\n const result = await callDaemon(\"status\");\n if (result.kind !== \"status\") throw new Error(\"\u65E0\u6CD5\u8BFB\u53D6\u5B88\u62A4\u8FDB\u7A0B\u670D\u52A1\u72B6\u6001\");\n return result.rows;\n}\n\nconst require = createRequire(import.meta.url);\nexport function platformVersion(): string {\n if (isMonorepoLayout(ROOT)) return readJson<{ version: string }>(path.join(ROOT, \"packages\", \"meta\", \"package.json\"), { version: \"unknown\" }).version;\n if (process.env.SFMC_PLATFORM_VERSION) return process.env.SFMC_PLATFORM_VERSION;\n const active = readJson<{ version?: string }>(path.join(ROOT, \".sfmc\", \"runtime\", \"active.json\"), {});\n if (active.version) return active.version;\n const deployed = readJson<{ version?: string }>(path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"package.json\"), {});\n if (deployed.version) return deployed.version;\n try { return (require(\"@sfmc-bds/sfmc/package.json\") as { version: string }).version; }\n catch { return (readJson<{ version: string }>(path.join(fileURLToPath(new URL(\"../../..\", import.meta.url)), \"package.json\"), { version: \"unknown\" })).version; }\n}\nexport async function attachmentPlan(includeReleaseNotes = true): Promise<AttachmentPlan> {\n const development = isMonorepoLayout(ROOT) || isSourceCheckout();\n const currentVersion = platformVersion();\n let targetVersion = currentVersion;\n if (!development) {\n const response = await fetch(\"https://registry.npmjs.org/@sfmc-bds%2fsfmc/latest\", { signal: AbortSignal.timeout(15_000) });\n if (!response.ok) throw new Error(`\u5E73\u53F0\u7248\u672C\u68C0\u67E5\u5931\u8D25 (${response.status})`);\n const metadata = await response.json() as { version?: string };\n if (!metadata.version || !/^\\d+\\.\\d+\\.\\d+$/.test(metadata.version)) throw new Error(\"latest \u6CA1\u6709\u6307\u5411\u6709\u6548\u7684\u7A33\u5B9A\u5E73\u53F0\u7248\u672C\");\n targetVersion = metadata.version;\n }\n const [rows, releaseNotes] = await Promise.all([runtimeRows(), includeReleaseNotes && !development ? platformReleaseNotes(targetVersion) : undefined]);\n return { currentVersion, targetVersion, development, upgradeRequired: !development && targetVersion !== currentVersion, externalServices: rows.filter(row => row.running && row.ownership === \"external\").map(row => row.name), steps: development ? [\"\u4FDD\u7559\u6E90\u7801\u5DE5\u4F5C\u533A\uFF0C\u8FDE\u63A5\u5F53\u524D\u5F00\u53D1\u90E8\u7F72\"] : [\"\u4E0B\u8F7D\u56FA\u5B9A\u7248\u672C\u7684\u5E73\u53F0\u4F9D\u8D56\u7EC4\u5408\", \"\u505C\u6B62\u53D7\u5F71\u54CD\u670D\u52A1\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA\", \"\u5207\u6362\u5E73\u53F0\u5E76\u9A8C\u8BC1\", \"\u6062\u590D\u6B64\u524D\u8FD0\u884C\u7684\u670D\u52A1\"], ...(releaseNotes ? { releaseNotes } : {}) };\n}\nexport async function stopForMaintenance(names: ServiceName[], phase: TaskContext[\"phase\"]): Promise<ServiceName[]> {\n const before = await queryServicesRuntimeLocal();\n const affected = before.filter(row => names.includes(row.name) && row.running);\n if (affected.some(row => row.ownership === \"external\")) throw Object.assign(new Error(\"\u5B58\u5728\u65E0\u6CD5\u901A\u8FC7\u5F53\u524D\u5B88\u62A4\u8FDB\u7A0B\u4F18\u96C5\u505C\u6B62\u7684\u5916\u90E8\u670D\u52A1\uFF1B\u8BF7\u5148\u901A\u8FC7\u539F\u7BA1\u7406\u5668\u505C\u6B62\u540E\u91CD\u65B0\u63A5\u5165\"), { code: \"conflict\" });\n phase(\"stop-services\", \"\u4F18\u96C5\u505C\u6B62\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA\");\n const running = affected.map(row => row.name);\n for (const name of [...START_ORDER].reverse().filter(name => names.includes(name))) await services[name].stop(false, false);\n const after = await queryServicesRuntimeLocal();\n if (after.some(row => names.includes(row.name) && row.running)) throw new Error(\"\u670D\u52A1\u672A\u5B8C\u5168\u9000\u51FA\uFF0C\u5DF2\u4E2D\u6B62\u6587\u4EF6\u64CD\u4F5C\");\n return running;\n}\nexport async function restoreRunning(names: ServiceName[], phase: TaskContext[\"phase\"]) {\n phase(\"restore\", \"\u6062\u590D\u7EF4\u62A4\u524D\u8FD0\u884C\u7684\u670D\u52A1\");\n for (const name of START_ORDER.filter(name => names.includes(name))) {\n const result = await services[name].start();\n if (result.status === \"skipped\") throw new Error(`\u6062\u590D ${name} \u5931\u8D25: ${result.reason}`);\n }\n await verifyRunning(names);\n}\nexport async function verifyRunning(names: ServiceName[]) {\n if (!names.length) return;\n await new Promise(resolve => setTimeout(resolve, 2000));\n for (const name of names) {\n if (!services[name].running || !services[name].pid) throw new Error(`${name} \u542F\u52A8\u540E\u5DF2\u9000\u51FA`);\n }\n if (names.includes(\"db\")) {\n const cfg = readJson<{ db_port?: number; http_auth?: string }>(path.join(ROOT, \"configs\", \"db_config.json\"), {});\n const response = await fetch(`http://127.0.0.1:${cfg.db_port ?? 3001}/api/sfmc/status`, { headers: cfg.http_auth ? { authorization: `Bearer ${cfg.http_auth}` } : {}, signal: AbortSignal.timeout(5000) });\n if (!response.ok) throw new Error(`DB \u5065\u5EB7\u68C0\u67E5\u5931\u8D25 (${response.status})`);\n }\n}\nfunction snapshotTargets() {\n const db = readJson<{ dbDir?: string }>(path.join(ROOT, \"configs\", \"db_config.json\"), {});\n const bds = resolveBdsContext().bdsRoot;\n return { configs: path.join(ROOT, \"configs\"), modules: path.join(ROOT, \"modules\"), data: path.dirname(path.resolve(ROOT, db.dbDir ?? \"data/sfmc_data.db\")), bds };\n}\nexport async function createSnapshot(id: string, phase: TaskContext[\"phase\"]) {\n if (!/^[a-zA-Z0-9-]{1,80}$/.test(id)) throw new Error(\"\u975E\u6CD5\u5907\u4EFD\u7F16\u53F7\");\n phase(\"backup\", \"\u521B\u5EFA\u5DF2\u505C\u670D\u7684\u4E00\u81F4\u5907\u4EFD\");\n const destination = path.join(ROOT, \".sfmc\", \"backups\", id);\n const targets = snapshotTargets();\n for (const target of Object.values(targets)) {\n const absolute = path.resolve(target);\n if (absolute === path.parse(absolute).root || absolute === path.resolve(ROOT) || destination.startsWith(absolute + path.sep)) throw new Error(\"\u5907\u4EFD\u76EE\u6807\u5305\u542B\u5B9E\u4F8B\u6839\u76EE\u5F55\u6216\u5907\u4EFD\u76EE\u5F55\uFF0C\u5DF2\u4E2D\u6B62\");\n }\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n fs.mkdirSync(destination);\n try {\n for (const [key, target] of Object.entries(targets)) {\n if (fs.existsSync(target)) await fs.promises.cp(target, path.join(destination, key), { recursive: true, dereference: false });\n }\n atomicJson(path.join(destination, \"manifest.json\"), { createdAt: new Date().toISOString(), targets, complete: true });\n } catch (error) {\n // \u53EA\u6E05\u7406\u672C\u6B21\u521B\u5EFA\u7684\u4E0D\u5B8C\u6574\u5FEB\u7167\uFF0C\u907F\u514D\u5907\u4EFD\u5931\u8D25\u540E\u7EE7\u7EED\u5360\u7528\u7A7A\u95F4\u3002\n await fs.promises.rm(destination, { recursive: true, force: true });\n throw error;\n }\n return destination;\n}\n/** \u5907\u4EFD\u53EA\u8BFB\u53D6\u5B9E\u4F8B\u6587\u4EF6\uFF0C\u5373\u4F7F\u590D\u5236\u5931\u8D25\u4E5F\u6062\u590D\u539F\u5148\u8FD0\u884C\u7684\u670D\u52A1\u3002 */\nexport async function createBackup(context: TaskContext) {\n const running = await stopForMaintenance([...START_ORDER], context.phase);\n let completed = false;\n try {\n await createSnapshot(context.id, context.phase);\n completed = true;\n } finally {\n try { await restoreRunning(running, context.phase); }\n catch (error) {\n throw Object.assign(new Error(`\u5907\u4EFD${completed ? \"\u5DF2\u5B8C\u6210\" : \"\u672A\u5B8C\u6210\"}\uFF0C\u6062\u590D\u670D\u52A1\u8FD0\u884C\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`), { code: \"io\", details: completed ? { backupId: context.id } : undefined });\n }\n }\n return { backupId: context.id };\n}\nexport async function restoreSnapshot(id: string, phase: TaskContext[\"phase\"]) {\n if (!/^[a-zA-Z0-9-]{1,80}$/.test(id)) throw new Error(\"\u975E\u6CD5\u5907\u4EFD\u7F16\u53F7\");\n const source = path.join(ROOT, \".sfmc\", \"backups\", id);\n const manifest = readJson<{ complete: boolean; targets: Record<string, string> }>(path.join(source, \"manifest.json\"), { complete: false, targets: {} });\n if (!manifest.complete) throw new Error(\"\u5907\u4EFD\u4E0D\u5B8C\u6574\uFF0C\u7981\u6B62\u6062\u590D\");\n const targets = snapshotTargets();\n phase(\"restore-data\", \"\u6309\u7528\u6237\u660E\u786E\u9009\u62E9\u6062\u590D\u914D\u7F6E\u3001\u6A21\u5757\u3001\u6570\u636E\u5E93\u548C\u4E16\u754C\");\n for (const [key, target] of Object.entries(targets)) {\n if (path.resolve(manifest.targets[key] ?? \"\") !== path.resolve(target)) throw new Error(\"\u5907\u4EFD\u76EE\u6807\u4E0E\u5F53\u524D\u90E8\u7F72\u4E0D\u4E00\u81F4\");\n const saved = path.join(source, key);\n if (!fs.existsSync(saved)) continue;\n const absolute = path.resolve(target);\n if (absolute === path.parse(absolute).root || absolute === path.resolve(ROOT) || fs.lstatSync(saved).isSymbolicLink()) throw new Error(\"\u4E0D\u5B89\u5168\u7684\u6062\u590D\u76EE\u6807\");\n await fs.promises.rm(absolute, { recursive: true, force: true });\n await fs.promises.cp(saved, absolute, { recursive: true, dereference: false });\n }\n return { restored: id };\n}\n/**\n * \u5220\u9664\u4E00\u4EFD\u5DF2\u5B8C\u6210\u7684\u5907\u4EFD\u76EE\u5F55\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u684C\u9762 backups.delete\u3002\u53EA\u5220\u9664 `.sfmc/backups/<id>` \u8FD9\u4E00\u5C42\uFF0C\u4E14\u5FC5\u987B\u5E26 manifest.json\u3002\n * \u4E0D\u8DDF\u968F\u7B26\u53F7\u94FE\u63A5\uFF0C\u907F\u514D\u7F16\u53F7\u88AB\u89E3\u6790\u5230\u5907\u4EFD\u76EE\u5F55\u4E4B\u5916\u3002\n */\nexport async function deleteSnapshot(id: string) {\n if (!/^[a-zA-Z0-9-]{1,80}$/.test(id)) throw new Error(\"\u975E\u6CD5\u5907\u4EFD\u7F16\u53F7\");\n const directory = path.resolve(ROOT, \".sfmc\", \"backups\");\n const target = path.resolve(directory, id);\n if (path.dirname(target) !== directory) throw new Error(\"\u975E\u6CD5\u5907\u4EFD\u8DEF\u5F84\");\n let stat: fs.Stats;\n try { stat = fs.lstatSync(target); }\n catch (error) { if ((error as NodeJS.ErrnoException).code === \"ENOENT\") throw new Error(\"\u627E\u4E0D\u5230\u5907\u4EFD\"); throw error; }\n if (stat.isSymbolicLink() || !stat.isDirectory()) throw new Error(\"\u62D2\u7EDD\u5220\u9664\u975E\u5907\u4EFD\u76EE\u5F55\");\n if (!fs.existsSync(path.join(target, \"manifest.json\"))) throw new Error(\"\u5907\u4EFD\u4E0D\u5B8C\u6574\uFF0C\u5DF2\u62D2\u7EDD\u5220\u9664\");\n await fs.promises.rm(target, { recursive: true, force: true });\n return { deleted: id };\n}\n/** \u666E\u901A\u7EF4\u62A4\u53EA\u8D1F\u8D23\u505C\u670D\u4E0E\u6062\u590D\uFF1B\u5168\u91CF\u5FEB\u7167\u4EC5\u7531\u624B\u52A8\u5907\u4EFD\u4EFB\u52A1\u521B\u5EFA\u3002 */\nexport async function withStoppedServices<T>(context: TaskContext, names: ServiceName[], action: () => Promise<T>): Promise<T> {\n const running = await stopForMaintenance(names, context.phase);\n try {\n context.phase(\"execute\");\n const result = await action();\n context.phase(\"verify\");\n await restoreRunning(running, context.phase);\n return result;\n } catch (error) {\n // \u5931\u8D25\u540E\u4FDD\u6301\u505C\u670D\uFF0C\u4E0D\u731C\u6D4B\u672A\u77E5\u6570\u636E\u8FC1\u79FB\u662F\u5426\u53EF\u4EE5\u5B89\u5168\u6062\u590D\u8FD0\u884C\u3002\n for (const name of [...START_ORDER].reverse().filter(name => running.includes(name))) await services[name].stop(false, false).catch(() => {});\n throw Object.assign(new Error(`\u7EF4\u62A4\u5931\u8D25\uFF0C\u670D\u52A1\u4FDD\u6301\u505C\u6B62: ${error instanceof Error ? error.message : String(error)}`), { code: \"io\", details: (error as { details?: object }).details });\n }\n}\nexport async function launchPlatformUpdate(store: TaskStore, context: TaskContext, target: string) {\n if (isMonorepoLayout(ROOT)) throw new Error(\"\u6E90\u7801\u90E8\u7F72\u4E0D\u8986\u76D6\u5DE5\u4F5C\u533A\uFF1B\u8BF7\u4F7F\u7528\u6B63\u5E38\u5F00\u53D1\u53D1\u5E03\u6D41\u7A0B\");\n if (!/^\\d+\\.\\d+\\.\\d+$/.test(target)) throw new Error(\"\u9700\u8981\u56FA\u5B9A\u7A33\u5B9A\u7248\u672C\");\n const rows = await runtimeRows();\n if (rows.some(row => row.running && row.ownership === \"external\")) throw new Error(\"\u5916\u90E8\u670D\u52A1\u5FC5\u987B\u5148\u901A\u8FC7\u539F\u7BA1\u7406\u5668\u4F18\u96C5\u505C\u6B62\");\n const request = path.join(ROOT, \".sfmc\", \"operations\", `${context.id}.request.json`);\n const previous = readJson(path.join(ROOT, \".sfmc\", \"runtime\", \"active.json\"), { entry: fs.existsSync(path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"bin\", \"sfmc.mjs\")) ? path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"bin\", \"sfmc.mjs\") : process.env.SFMC_DAEMON_ENTRY, version: platformVersion() });\n atomicJson(request, { root: ROOT, id: context.id, target, running: rows.filter(row => row.running).map(row => row.name), previous });\n const entry = fileURLToPath(new URL(\"./platform-worker.js\", import.meta.url));\n const log = path.join(ROOT, \".sfmc\", \"logs\", \"maintenance.log\");\n fs.mkdirSync(path.dirname(log), { recursive: true });\n // \u4E0D\u628A\u7236\u8FDB\u7A0B\u7684\u65E5\u5FD7\u53E5\u67C4\u4EA4\u7ED9\u5B50\u8FDB\u7A0B\uFF0C\u907F\u514D\u5B88\u62A4\u8FDB\u7A0B\u9000\u51FA\u65F6\u4E2D\u65AD\u7EF4\u62A4\u8FDB\u7A0B\u3002\n const child = spawn(process.execPath, [entry, request], { cwd: ROOT, env: { ...process.env, SFMC_ROOT: ROOT, SFMC_MAINTENANCE_LOG: log }, detached: true, windowsHide: true, stdio: \"ignore\" });\n await new Promise<void>((resolve, reject) => { child.once(\"spawn\", resolve); child.once(\"error\", reject); });\n child.unref();\n const record = store.get(context.id) as ReturnType<TaskStore[\"get\"]> & { workerPid?: number };\n record.status = \"running\"; if (child.pid) record.workerPid = child.pid;\n store.save(record);\n return { detached: true };\n}\nexport async function runPnpm(args: string[], cwd: string) {\n const script = process.env.SFMC_PNPM_ENTRY;\n if (script) return runProcess(process.execPath, [script, ...args], cwd);\n const command = process.platform === \"win32\" ? \"pnpm.cmd\" : \"pnpm\";\n const crossSpawn = require(\"cross-spawn\") as typeof spawn;\n await new Promise<void>((resolve, reject) => {\n const child = crossSpawn(command, args, { cwd, env: process.env, windowsHide: true, stdio: \"ignore\" });\n child.on(\"error\", reject); child.on(\"close\", code => code === 0 ? resolve() : reject(new Error(`pnpm \u6267\u884C\u5931\u8D25 (${code})`)));\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["require", "name"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { PackImportPlan, PackImportOptions } from "@sfmc-bds/management";
|
|
2
|
+
export declare function planPackImport(filename: string): Promise<PackImportPlan>;
|
|
3
|
+
/**
|
|
4
|
+
* 从管理请求取出收件箱文件名。
|
|
5
|
+
* 使用场景:桌面一次拖入或选中多个世界包时传 filenames;旧客户端仍只传 filename。
|
|
6
|
+
*/
|
|
7
|
+
export declare function readImportFilenames(params: {
|
|
8
|
+
filename?: unknown;
|
|
9
|
+
filenames?: unknown;
|
|
10
|
+
}): string[];
|
|
11
|
+
/**
|
|
12
|
+
* 按顺序导入多个收件箱文件。
|
|
13
|
+
* 使用场景:批量导入。只有一个文件时仍走 importPack,保留 force 与 revision 的原有校验。
|
|
14
|
+
* 中途失败时,前面已经安装的包保留,错误里带上已导入数量。
|
|
15
|
+
*/
|
|
16
|
+
export declare function importPacks(filenames: string[], options?: PackImportOptions): Promise<{
|
|
17
|
+
changed: boolean;
|
|
18
|
+
restartRequired: boolean;
|
|
19
|
+
imported: number;
|
|
20
|
+
} | {
|
|
21
|
+
changed: boolean;
|
|
22
|
+
restartRequired: boolean;
|
|
23
|
+
}>;
|
|
24
|
+
/** force 必须绑定用户看过的归档及已有包版本,排队期间有变化时重新确认。 */
|
|
25
|
+
export declare function importPack(filename: string, options?: PackImportOptions): Promise<{
|
|
26
|
+
changed: boolean;
|
|
27
|
+
restartRequired: boolean;
|
|
28
|
+
}>;
|
|
29
|
+
//# sourceMappingURL=pack-import.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack-import.d.ts","sourceRoot":"","sources":["../../src/management/pack-import.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAsD9E,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAExE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,EAAE,CAOjG;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,iBAAsB;;;;;;;GAkBrF;AAED,4CAA4C;AAC5C,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB;;;GAY3E"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { decidePackInstallPlan, enableInstalledPack, installPackDirectory, readPackManifestInfo, resolvePackRoots, scanDestOccupancy, worldPackParentDir } from "@sfmc-bds/bds-tools/world-packs";
|
|
5
|
+
import { ROOT } from "../runtime.js";
|
|
6
|
+
import { resolveBdsContext } from "../pack-lifecycle.js";
|
|
7
|
+
import { identifier } from "./domain.js";
|
|
8
|
+
async function inspectImport(filename, action) {
|
|
9
|
+
const inbox = path.resolve(ROOT, "packs", "_desktop-inbox");
|
|
10
|
+
const file = path.resolve(inbox, identifier(filename));
|
|
11
|
+
if (!file.startsWith(inbox + path.sep) || !fs.existsSync(file)) throw new Error("\u4E16\u754C\u5305\u5C1A\u672A\u4E0A\u4F20\u5230\u5B9E\u4F8B\u6536\u4EF6\u7BB1");
|
|
12
|
+
const context = resolveBdsContext();
|
|
13
|
+
const resolved = await resolvePackRoots(file);
|
|
14
|
+
try {
|
|
15
|
+
if (!resolved.roots.length) throw new Error("\u5F52\u6863\u4E0D\u542B\u53EF\u8BC6\u522B\u7684\u4E16\u754C\u5305");
|
|
16
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17
|
+
const prepared = resolved.roots.map((source) => {
|
|
18
|
+
const info = readPackManifestInfo(source);
|
|
19
|
+
if (!info) throw new Error("\u4E16\u754C\u5305 manifest \u65E0\u6548");
|
|
20
|
+
const id = info.uuid.toLowerCase();
|
|
21
|
+
if (seen.has(id)) throw new Error(`\u5F52\u6863\u5305\u542B\u91CD\u590D\u7684\u4E16\u754C\u5305 UUID: ${info.uuid}`);
|
|
22
|
+
seen.add(id);
|
|
23
|
+
const destParent = worldPackParentDir(context.bdsRoot, context.levelName, info.kind);
|
|
24
|
+
const decision = decidePackInstallPlan({ incoming: info, hint: path.basename(source), occupancy: scanDestOccupancy(destParent) });
|
|
25
|
+
return { source, info, destParent, decision };
|
|
26
|
+
});
|
|
27
|
+
const hash = createHash("sha256");
|
|
28
|
+
for await (const chunk of fs.createReadStream(file)) hash.update(chunk);
|
|
29
|
+
hash.update(JSON.stringify({
|
|
30
|
+
context,
|
|
31
|
+
existing: prepared.map(({ info, decision }) => ({
|
|
32
|
+
id: info.uuid,
|
|
33
|
+
dir: decision.kind === "fresh" ? null : decision.dir,
|
|
34
|
+
manifest: decision.kind === "fresh" ? null : fs.readFileSync(path.join(decision.dir, "manifest.json"), "utf8")
|
|
35
|
+
}))
|
|
36
|
+
}));
|
|
37
|
+
const plan = {
|
|
38
|
+
revision: hash.digest("hex"),
|
|
39
|
+
packs: prepared.map(({ info, decision }) => ({
|
|
40
|
+
id: info.uuid,
|
|
41
|
+
name: info.name,
|
|
42
|
+
kind: info.kind,
|
|
43
|
+
version: info.version.join("."),
|
|
44
|
+
action: decision.kind === "fresh" ? "install" : decision.kind === "needConfirm" ? "conflict" : "update",
|
|
45
|
+
...decision.kind === "fresh" ? {} : { existing: { name: decision.existing.name, version: decision.existing.version.join(".") } }
|
|
46
|
+
}))
|
|
47
|
+
};
|
|
48
|
+
return await action(plan, async (force) => {
|
|
49
|
+
for (const { source, info, destParent } of prepared) {
|
|
50
|
+
const result = await installPackDirectory({ srcDir: source, destParent, force });
|
|
51
|
+
if (!result.ok) throw Object.assign(new Error(result.reason ?? "\u4E16\u754C\u5305\u5B89\u88C5\u5931\u8D25"), { code: result.conflict ? "conflict" : "io", details: result.conflict });
|
|
52
|
+
await enableInstalledPack({ ...context, info });
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
} finally {
|
|
56
|
+
resolved.dispose();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function planPackImport(filename) {
|
|
60
|
+
return inspectImport(filename, async (plan) => plan);
|
|
61
|
+
}
|
|
62
|
+
function readImportFilenames(params) {
|
|
63
|
+
if (Array.isArray(params.filenames)) {
|
|
64
|
+
const names = [...new Set(params.filenames.map((value) => identifier(value)))];
|
|
65
|
+
if (!names.length) throw new Error("\u6CA1\u6709\u8981\u5BFC\u5165\u7684\u4E16\u754C\u5305");
|
|
66
|
+
return names;
|
|
67
|
+
}
|
|
68
|
+
return [identifier(params.filename)];
|
|
69
|
+
}
|
|
70
|
+
async function importPacks(filenames, options = {}) {
|
|
71
|
+
if (filenames.length === 1) {
|
|
72
|
+
const only = filenames[0];
|
|
73
|
+
if (!only) throw new Error("\u6CA1\u6709\u8981\u5BFC\u5165\u7684\u4E16\u754C\u5305");
|
|
74
|
+
return importPack(only, options);
|
|
75
|
+
}
|
|
76
|
+
const imported = [];
|
|
77
|
+
for (const name of filenames) {
|
|
78
|
+
try {
|
|
79
|
+
await importPack(name);
|
|
80
|
+
imported.push(name);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
83
|
+
const code = error.code;
|
|
84
|
+
throw Object.assign(new Error(imported.length ? `\u5DF2\u5BFC\u5165 ${imported.length} \u4E2A\uFF0C\u7EE7\u7EED\u5BFC\u5165 ${name} \u5931\u8D25\uFF1A${message}` : message), typeof code === "string" ? { code } : {});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return { changed: true, restartRequired: true, imported: imported.length };
|
|
88
|
+
}
|
|
89
|
+
function importPack(filename, options = {}) {
|
|
90
|
+
return inspectImport(filename, async (plan, install) => {
|
|
91
|
+
if (options.force && !options.revision || options.revision !== void 0 && options.revision !== plan.revision) {
|
|
92
|
+
throw Object.assign(new Error("\u5BFC\u5165\u6587\u4EF6\u6216\u5DF2\u5B89\u88C5\u5305\u5DF2\u53D8\u66F4\uFF0C\u8BF7\u91CD\u65B0\u5BFC\u5165\u5E76\u786E\u8BA4"), { code: "conflict" });
|
|
93
|
+
}
|
|
94
|
+
const conflicts = plan.packs.filter((pack) => pack.action === "conflict");
|
|
95
|
+
if (conflicts.length && !options.force) {
|
|
96
|
+
throw Object.assign(new Error(`\u4E16\u754C\u5305\u9700\u8981\u786E\u8BA4\u8986\u76D6\uFF1A${conflicts.map((pack) => `${pack.name}\uFF08\u5DF2\u6709 ${pack.existing.version}\uFF0C\u5BFC\u5165 ${pack.version}\uFF09`).join("\u3001")}`), { code: "conflict", details: { conflicts } });
|
|
97
|
+
}
|
|
98
|
+
await install(options.force === true);
|
|
99
|
+
return { changed: true, restartRequired: true };
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
export {
|
|
103
|
+
importPack,
|
|
104
|
+
importPacks,
|
|
105
|
+
planPackImport,
|
|
106
|
+
readImportFilenames
|
|
107
|
+
};
|
|
108
|
+
//# sourceMappingURL=pack-import.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/management/pack-import.ts"],
|
|
4
|
+
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport type { PackImportPlan, PackImportOptions } from \"@sfmc-bds/management\";\nimport { decidePackInstallPlan, enableInstalledPack, installPackDirectory, readPackManifestInfo, resolvePackRoots, scanDestOccupancy, worldPackParentDir } from \"@sfmc-bds/bds-tools/world-packs\";\nimport { ROOT } from \"../runtime.js\";\nimport { resolveBdsContext } from \"../pack-lifecycle.js\";\nimport { identifier } from \"./domain.js\";\n\n/** \u4E0E REPL \u5171\u7528\u5B89\u88C5\u51B3\u7B56\uFF1B\u5148\u68C0\u67E5\u6574\u4E2A\u5F52\u6863\uFF0C\u907F\u514D\u540E\u9762\u7684\u51B2\u7A81\u5BFC\u81F4\u524D\u9762\u7684\u5305\u5DF2\u88AB\u5199\u5165\u3002 */\nasync function inspectImport<T>(filename: string, action: (plan: PackImportPlan, install: (force: boolean) => Promise<void>) => Promise<T>): Promise<T> {\n const inbox = path.resolve(ROOT, \"packs\", \"_desktop-inbox\");\n const file = path.resolve(inbox, identifier(filename));\n if (!file.startsWith(inbox + path.sep) || !fs.existsSync(file)) throw new Error(\"\u4E16\u754C\u5305\u5C1A\u672A\u4E0A\u4F20\u5230\u5B9E\u4F8B\u6536\u4EF6\u7BB1\");\n const context = resolveBdsContext();\n const resolved = await resolvePackRoots(file);\n try {\n if (!resolved.roots.length) throw new Error(\"\u5F52\u6863\u4E0D\u542B\u53EF\u8BC6\u522B\u7684\u4E16\u754C\u5305\");\n const seen = new Set<string>();\n const prepared = resolved.roots.map(source => {\n const info = readPackManifestInfo(source);\n if (!info) throw new Error(\"\u4E16\u754C\u5305 manifest \u65E0\u6548\");\n const id = info.uuid.toLowerCase();\n if (seen.has(id)) throw new Error(`\u5F52\u6863\u5305\u542B\u91CD\u590D\u7684\u4E16\u754C\u5305 UUID: ${info.uuid}`);\n seen.add(id);\n const destParent = worldPackParentDir(context.bdsRoot, context.levelName, info.kind);\n const decision = decidePackInstallPlan({ incoming: info, hint: path.basename(source), occupancy: scanDestOccupancy(destParent) });\n return { source, info, destParent, decision };\n });\n const hash = createHash(\"sha256\");\n for await (const chunk of fs.createReadStream(file)) hash.update(chunk);\n hash.update(JSON.stringify({\n context,\n existing: prepared.map(({ info, decision }) => ({\n id: info.uuid,\n dir: decision.kind === \"fresh\" ? null : decision.dir,\n manifest: decision.kind === \"fresh\" ? null : fs.readFileSync(path.join(decision.dir, \"manifest.json\"), \"utf8\"),\n })),\n }));\n const plan: PackImportPlan = {\n revision: hash.digest(\"hex\"),\n packs: prepared.map(({ info, decision }) => ({\n id: info.uuid, name: info.name, kind: info.kind, version: info.version.join(\".\"),\n action: decision.kind === \"fresh\" ? \"install\" : decision.kind === \"needConfirm\" ? \"conflict\" : \"update\",\n ...(decision.kind === \"fresh\" ? {} : { existing: { name: decision.existing.name, version: decision.existing.version.join(\".\") } }),\n })),\n };\n return await action(plan, async force => {\n for (const { source, info, destParent } of prepared) {\n const result = await installPackDirectory({ srcDir: source, destParent, force });\n if (!result.ok) throw Object.assign(new Error(result.reason ?? \"\u4E16\u754C\u5305\u5B89\u88C5\u5931\u8D25\"), { code: result.conflict ? \"conflict\" : \"io\", details: result.conflict });\n await enableInstalledPack({ ...context, info });\n }\n });\n } finally { resolved.dispose(); }\n}\n\nexport function planPackImport(filename: string): Promise<PackImportPlan> {\n return inspectImport(filename, async plan => plan);\n}\n\n/**\n * \u4ECE\u7BA1\u7406\u8BF7\u6C42\u53D6\u51FA\u6536\u4EF6\u7BB1\u6587\u4EF6\u540D\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u684C\u9762\u4E00\u6B21\u62D6\u5165\u6216\u9009\u4E2D\u591A\u4E2A\u4E16\u754C\u5305\u65F6\u4F20 filenames\uFF1B\u65E7\u5BA2\u6237\u7AEF\u4ECD\u53EA\u4F20 filename\u3002\n */\nexport function readImportFilenames(params: { filename?: unknown; filenames?: unknown }): string[] {\n if (Array.isArray(params.filenames)) {\n const names = [...new Set(params.filenames.map(value => identifier(value)))];\n if (!names.length) throw new Error(\"\u6CA1\u6709\u8981\u5BFC\u5165\u7684\u4E16\u754C\u5305\");\n return names;\n }\n return [identifier(params.filename)];\n}\n\n/**\n * \u6309\u987A\u5E8F\u5BFC\u5165\u591A\u4E2A\u6536\u4EF6\u7BB1\u6587\u4EF6\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u6279\u91CF\u5BFC\u5165\u3002\u53EA\u6709\u4E00\u4E2A\u6587\u4EF6\u65F6\u4ECD\u8D70 importPack\uFF0C\u4FDD\u7559 force \u4E0E revision \u7684\u539F\u6709\u6821\u9A8C\u3002\n * \u4E2D\u9014\u5931\u8D25\u65F6\uFF0C\u524D\u9762\u5DF2\u7ECF\u5B89\u88C5\u7684\u5305\u4FDD\u7559\uFF0C\u9519\u8BEF\u91CC\u5E26\u4E0A\u5DF2\u5BFC\u5165\u6570\u91CF\u3002\n */\nexport async function importPacks(filenames: string[], options: PackImportOptions = {}) {\n if (filenames.length === 1) {\n const only = filenames[0];\n if (!only) throw new Error(\"\u6CA1\u6709\u8981\u5BFC\u5165\u7684\u4E16\u754C\u5305\");\n return importPack(only, options);\n }\n const imported: string[] = [];\n for (const name of filenames) {\n try {\n await importPack(name);\n imported.push(name);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const code = (error as { code?: unknown }).code;\n throw Object.assign(new Error(imported.length ? `\u5DF2\u5BFC\u5165 ${imported.length} \u4E2A\uFF0C\u7EE7\u7EED\u5BFC\u5165 ${name} \u5931\u8D25\uFF1A${message}` : message), typeof code === \"string\" ? { code } : {});\n }\n }\n return { changed: true, restartRequired: true, imported: imported.length };\n}\n\n/** force \u5FC5\u987B\u7ED1\u5B9A\u7528\u6237\u770B\u8FC7\u7684\u5F52\u6863\u53CA\u5DF2\u6709\u5305\u7248\u672C\uFF0C\u6392\u961F\u671F\u95F4\u6709\u53D8\u5316\u65F6\u91CD\u65B0\u786E\u8BA4\u3002 */\nexport function importPack(filename: string, options: PackImportOptions = {}) {\n return inspectImport(filename, async (plan, install) => {\n if ((options.force && !options.revision) || (options.revision !== undefined && options.revision !== plan.revision)) {\n throw Object.assign(new Error(\"\u5BFC\u5165\u6587\u4EF6\u6216\u5DF2\u5B89\u88C5\u5305\u5DF2\u53D8\u66F4\uFF0C\u8BF7\u91CD\u65B0\u5BFC\u5165\u5E76\u786E\u8BA4\"), { code: \"conflict\" });\n }\n const conflicts = plan.packs.filter(pack => pack.action === \"conflict\");\n if (conflicts.length && !options.force) {\n throw Object.assign(new Error(`\u4E16\u754C\u5305\u9700\u8981\u786E\u8BA4\u8986\u76D6\uFF1A${conflicts.map(pack => `${pack.name}\uFF08\u5DF2\u6709 ${pack.existing!.version}\uFF0C\u5BFC\u5165 ${pack.version}\uFF09`).join(\"\u3001\")}`), { code: \"conflict\", details: { conflicts } });\n }\n await install(options.force === true);\n return { changed: true, restartRequired: true };\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|