@sfmc-bds/bds-tools 0.2.0-beta.10 → 0.2.0-beta.11
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/check-update.js +3 -3
- package/dist/check-update.js.map +2 -2
- package/dist/fsx.d.ts +15 -7
- package/dist/fsx.d.ts.map +1 -1
- package/dist/fsx.js +67 -8
- package/dist/fsx.js.map +2 -2
- package/dist/fsx.test.js +60 -0
- package/dist/fsx.test.js.map +7 -0
- package/dist/rollback.d.ts.map +1 -1
- package/dist/rollback.js +2 -2
- package/dist/rollback.js.map +2 -2
- package/package.json +3 -3
package/dist/check-update.js
CHANGED
|
@@ -8,7 +8,7 @@ import os from "node:os";
|
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { createBdsManager } from "./bds-manager.js";
|
|
10
10
|
import { CHANGELOG_BASE, fetchChangelog } from "./changelog.js";
|
|
11
|
-
import { copyDirSync, emptyDirSync, hashFileAsync, rmSafe } from "./fsx.js";
|
|
11
|
+
import { copyDirSync, copyFileSyncSafe, emptyDirSync, hashFileAsync, rmSafe } from "./fsx.js";
|
|
12
12
|
import { bdsExePath, bdsInstallRequiredFiles, ensureBdsExecutable } from "./host-platform.js";
|
|
13
13
|
import { httpDownload } from "./http.js";
|
|
14
14
|
import { isMainModule } from "./is-main.js";
|
|
@@ -71,7 +71,7 @@ async function doBackup(bdsPath, backupDir, preserve) {
|
|
|
71
71
|
if (!fs.existsSync(src)) continue;
|
|
72
72
|
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
73
73
|
if (fs.statSync(src).isDirectory()) copyDirSync(src, target);
|
|
74
|
-
else
|
|
74
|
+
else copyFileSyncSafe(src, target);
|
|
75
75
|
anyCopied = true;
|
|
76
76
|
log.info(`\u5DF2\u5907\u4EFD: ${item}`);
|
|
77
77
|
}
|
|
@@ -91,7 +91,7 @@ async function restorePreserves(bdsPath, backupDir, preserve) {
|
|
|
91
91
|
copyDirSync(src, dest);
|
|
92
92
|
} else {
|
|
93
93
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
94
|
-
|
|
94
|
+
copyFileSyncSafe(src, dest);
|
|
95
95
|
}
|
|
96
96
|
log.info(`\u5DF2\u6062\u590D: ${item}`);
|
|
97
97
|
} catch (e) {
|
package/dist/check-update.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/check-update.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * check-update.ts \u2014 BDS \u81EA\u52A8\u66F4\u65B0\u5668 (\u4E3B\u6D41\u7A0B)\n *\n * 1. \u4E34\u65F6 staging \u76EE\u5F55 (mkdtempSync) - \u907F\u514D\u6C61\u67D3\u6570\u636E\u6839\n * 2. \u4E0B\u8F7D/\u89E3\u538B\u5931\u8D25 \u2192 \u81EA\u52A8\u4ECE\u5907\u4EFD\u56DE\u6EDA preserves\n * 3. \u6D41\u5F0F SHA256 \u6821\u9A8C - \u4E0D\u628A 250MB \u6587\u4EF6\u8BFB\u5165\u5185\u5B58\n * 4. \u517C\u5BB9\u7248\u672C\u767D\u540D\u5355 - \u4E0D\u5339\u914D\u5219\u8DF3\u8FC7\u5347\u7EA7\n * 5. QQ \u901A\u77E5\u7EDF\u4E00\u52A0 5s \u8D85\u65F6 - \u6C38\u8FDC\u4E0D\u963B\u585E\u4E3B\u6D41\u7A0B\n * 6. Rollback marker \u843D\u76D8\u5230 <ROOT>/.sfmc/ - \u8DE8\u8FDB\u7A0B / \u8DE8\u91CD\u542F\u53EF\u6062\u590D\n */\n\nimport {\n bindByteProgressToBar,\n createNodeServiceLogger,\n createTerminalProgress,\n} from \"@sfmc-bds/sdk/logs\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { createBdsManager } from \"./bds-manager.js\";\nimport { CHANGELOG_BASE, fetchChangelog } from \"./changelog.js\";\nimport { copyDirSync, emptyDirSync, hashFileAsync, rmSafe } from \"./fsx.js\";\nimport { bdsExePath, bdsInstallRequiredFiles, ensureBdsExecutable } from \"./host-platform.js\";\nimport { httpDownload } from \"./http.js\";\nimport { isMainModule } from \"./is-main.js\";\nimport { loadConfig, LOG_PATH, resolvePaths } from \"./paths.js\";\nimport { sendText, sendWithImage } from \"./qqutil.js\";\nimport {\n clearRollbackMarker,\n getDirSize,\n rollbackFromBackup,\n verifyBdsInstall,\n writeRollbackMarker,\n} from \"./rollback.js\";\nimport { ensureEmitServerTelemetry } from \"./server-properties.js\";\nimport { clearTaskbarProgress, isTaskbarSupported, setTaskbarProgress } from \"./taskbar.js\";\nimport { emitUpdateKv, emitUpdateResult } from \"./update-result.js\";\nimport {\n buildDownloadUrls,\n fetchVersionDetails,\n getVersionInfo,\n isVersionCompatible,\n verifyFileHash,\n} from \"./upstream.js\";\nimport { compareVersions, getCurrentVersionAsync, getCurrentVersionSync, saveVersionCache } from \"./version.js\";\nimport { extractZipFileToDir } from \"./zipx.js\";\n\n// \u72EC\u7ACB\u5165\u53E3:source = \"updater\",\u4E0E bds-manager \u7684 \"bds-tools\" \u533A\u5206;\u540C\u843D\u76D8 bds-update.log\nconst log = createNodeServiceLogger({ source: \"updater\", logPath: LOG_PATH });\nconst closeLog = (): void => log.close();\n\nfunction parseArgs(argv: string[]): Record<string, string | boolean> {\n const out: Record<string, string | boolean> = {};\n for (const a of argv) {\n const m = a.match(/^--([\\w-]+)(?:=(.+))?$/);\n if (m) out[m[1]!] = m[2] || true;\n }\n return out;\n}\n\ninterface UpdateContext {\n cfg: ReturnType<typeof loadConfig>;\n channel: \"release\" | \"preview\" | string;\n checkOnly: boolean;\n force: boolean;\n bdsPath: string;\n backupDir: string;\n preserve: string[];\n autoRestart: boolean;\n qqNotify: boolean;\n}\n\nfunction buildContext(): UpdateContext {\n const cfg = loadConfig();\n const args = parseArgs(process.argv.slice(2));\n const { bds_path, backup_dir, preserve } = resolvePaths(cfg);\n const channel = String(args[\"channel\"] || cfg.channel || \"release\");\n return {\n cfg,\n channel,\n checkOnly: !!args[\"check-only\"],\n force: !!args[\"force\"],\n bdsPath: bds_path,\n backupDir: backup_dir,\n preserve,\n autoRestart: !args[\"no-start\"] && cfg.auto_restart !== false,\n qqNotify: !!cfg.qq_notify,\n };\n}\n\nasync function doBackup(\n bdsPath: string,\n backupDir: string,\n preserve: string[]\n): Promise<{ path: string; size: number }> {\n const dateStr = new Date().toISOString().slice(0, 10);\n const dest = path.join(backupDir, dateStr);\n fs.mkdirSync(dest, { recursive: true });\n\n let anyCopied = false;\n for (const item of preserve) {\n const src = path.join(bdsPath, item);\n const target = path.join(dest, item);\n if (!fs.existsSync(src)) continue;\n fs.mkdirSync(path.dirname(target), { recursive: true });\n if (fs.statSync(src).isDirectory()) copyDirSync(src, target);\n else fs.copyFileSync(src, target);\n anyCopied = true;\n log.info(`\u5DF2\u5907\u4EFD: ${item}`);\n }\n\n // \u5373\u4F7F\u5168\u90E8 preserve \u90FD\u8DF3\u8FC7\uFF0C\u4E5F\u8BA9\u76EE\u5F55\u5B58\u5728\u4EE5\u4FBF\u8BB0\u5F55\u65F6\u95F4\u6233\n if (!anyCopied) {\n log.info(`\u6CA1\u6709\u9700\u8981\u5907\u4EFD\u7684\u6587\u4EF6 (preserve \u5217\u8868\u4E3A\u7A7A\u6216\u5168\u90E8\u7F3A\u5931)`);\n }\n return { path: dest, size: getDirSize(dest) };\n}\n\n/** \u5F02\u6B65\u6062\u590D preserves \u4ECE\u5907\u4EFD\u5230 BDS \u8DEF\u5F84 */\nasync function restorePreserves(bdsPath: string, backupDir: string, preserve: string[]): Promise<void> {\n for (const item of preserve) {\n const src = path.join(backupDir, item);\n const dest = path.join(bdsPath, item);\n if (!fs.existsSync(src)) continue;\n try {\n if (fs.statSync(src).isDirectory()) {\n if (fs.existsSync(dest)) emptyDirSync(dest);\n copyDirSync(src, dest);\n } else {\n fs.mkdirSync(path.dirname(dest), { recursive: true });\n fs.copyFileSync(src, dest);\n }\n log.info(`\u5DF2\u6062\u590D: ${item}`);\n } catch (e) {\n log.warn(`\u6062\u590D\u5931\u8D25 ${item}: ${(e as Error).message}`);\n }\n }\n}\n\n/** \u628A zip \u89E3\u538B\u5230 destDir (\u8986\u76D6) \u2014 \u7ECF zipx \u5B89\u5168\u89E3\u538B */\nasync function extractZipToBds(zipPath: string, destDir: string): Promise<void> {\n // \u62BD\u51FA\u5230\u4E34\u65F6\u76EE\u5F55\uFF0C\u907F\u514D\u65E7\u5185\u5BB9\u5E72\u6270\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), \"bds-extract-\"));\n try {\n await extractZipFileToDir(zipPath, tmpDir);\n // \u628A\u4E34\u65F6\u76EE\u5F55\u91CC\u7684\u5185\u5BB9\u590D\u5236\u5230 BDS \u8DEF\u5F84\n for (const entry of fs.readdirSync(tmpDir)) {\n const srcPath = path.join(tmpDir, entry);\n const destPath = path.join(destDir, entry);\n if (fs.statSync(srcPath).isDirectory()) {\n if (fs.existsSync(destPath)) emptyDirSync(destPath);\n copyDirSync(srcPath, destPath);\n } else {\n fs.copyFileSync(srcPath, destPath);\n }\n }\n } finally {\n rmSafe(tmpDir);\n }\n}\n\nexport async function runUpdate(): Promise<number> {\n const startTime = Date.now();\n const ctx = buildContext();\n const { cfg, channel, checkOnly, force, bdsPath, backupDir, preserve, autoRestart, qqNotify } = ctx;\n\n log.info(`===== \u5F00\u59CB\u68C0\u67E5\u66F4\u65B0 (${channel}) =====`);\n fs.mkdirSync(bdsPath, { recursive: true });\n fs.mkdirSync(backupDir, { recursive: true });\n\n // 1. \u5F53\u524D\u7248\u672C\n const exePath = bdsExePath(bdsPath);\n const currentVer = await getCurrentVersionAsync(exePath).catch(() => getCurrentVersionSync(exePath));\n log.info(`\u5F53\u524D\u7248\u672C: ${currentVer}`);\n\n // 2. \u83B7\u53D6\u6700\u65B0\u7248\u672C\n let latestVer: string;\n try {\n const info = await getVersionInfo(cfg, channel);\n latestVer = info.version;\n log.info(`\u6700\u65B0\u7248\u672C: ${latestVer}`);\n const cmp = compareVersions(latestVer, currentVer);\n if (cmp <= 0 && !force) {\n log.info(\"\u5DF2\u662F\u6700\u65B0\u7248\u672C\uFF0C\u65E0\u9700\u66F4\u65B0\");\n if (qqNotify) {\n await sendText(`\u2705 BDS \u5DF2\u662F\u6700\u65B0\u7248\u672C\\n\\n\u5F53\u524D: ${currentVer}\\n\u6700\u65B0: ${latestVer}`);\n }\n emitUpdateKv(\"CURRENT\", currentVer, log);\n emitUpdateKv(\"LATEST\", latestVer, log);\n emitUpdateResult(\"uptodate\", log);\n return 0;\n }\n } catch (e) {\n log.error(`\u83B7\u53D6\u7248\u672C\u4FE1\u606F\u5931\u8D25: ${(e as Error).message}`);\n await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25\\n\\n\u65E0\u6CD5\u83B7\u53D6\u7248\u672C\u4FE1\u606F: ${(e as Error).message}`);\n return 1;\n }\n\n // \u517C\u5BB9\u6027\u68C0\u67E5\n if (!force && !isVersionCompatible(cfg, latestVer)) {\n log.warn(`${latestVer} \u4E0D\u5728\u517C\u5BB9\u7248\u672C\u767D\u540D\u5355\u4E2D\uFF0C\u8DF3\u8FC7\u5347\u7EA7`);\n if (qqNotify) {\n await sendText(`\u26A0\uFE0F BDS ${latestVer} \u4E0D\u5728\u517C\u5BB9\u6027\u767D\u540D\u5355\uFF0C\u5DF2\u8DF3\u8FC7\u5347\u7EA7\u3002\u8BF7\u4EBA\u5DE5\u786E\u8BA4\u3002`);\n }\n emitUpdateResult(\"skipped\", log);\n return 2;\n }\n\n // 3. \u53D6\u4E0B\u8F7D\u94FE\u63A5\n let downloadUrls: string[];\n let hash: { sha1: string; sha256: string };\n try {\n const details = await fetchVersionDetails(cfg, channel, latestVer);\n downloadUrls = buildDownloadUrls(cfg, channel, latestVer, details);\n hash = { sha1: details.sha1, sha256: details.sha256 };\n } catch (e) {\n log.error(`\u83B7\u53D6\u4E0B\u8F7D\u8BE6\u60C5\u5931\u8D25: ${(e as Error).message}`);\n await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25\\n\\n\u4E0B\u8F7D\u8BE6\u60C5\u83B7\u53D6\u5931\u8D25: ${(e as Error).message}`);\n return 1;\n }\n\n if (checkOnly) {\n emitUpdateKv(\"CURRENT\", currentVer, log);\n emitUpdateKv(\"LATEST\", latestVer, log);\n emitUpdateKv(\"CHANNEL\", channel, log);\n emitUpdateKv(\"URLS\", String(downloadUrls.length), log);\n emitUpdateResult(\"check-only\", log);\n return 0;\n }\n\n log.info(`\u53D1\u73B0\u65B0\u7248\u672C: ${currentVer} \u2192 ${latestVer}`);\n\n // 4. QQ \u9884\u544A\n if (qqNotify) {\n await sendText(\n `\uD835\uDC0C\uD835\uDC22\uD835\uDC27\uD835\uDC1E\uD835\uDC1C\uD835\uDC2B\uD835\uDC1A\uD835\uDC1F\uD835\uDC2D \uD835\uDC01\uD835\uDC03\uD835\uDC12 \u66F4\u65B0\u901A\u77E5\\n\\n` +\n `\u68C0\u6D4B\u5230\u65B0\u7248\u672C\uFF01\\n\\n` +\n `\u5F53\u524D: ${currentVer}\\n` +\n `\u6700\u65B0: ${latestVer}\\n` +\n `\u9891\u9053: ${channel === \"preview\" ? \"\u9884\u89C8\u7248\" : \"\u6B63\u5F0F\u7248\"}\\n\\n` +\n `\u670D\u52A1\u5668\u5373\u5C06\u5F00\u59CB\u66F4\u65B0~ \u8BF7\u8010\u5FC3\u7B49\u5F85^(*\uFFE3(oo)\uFFE3)^`\n );\n const cl = await fetchChangelog(channel);\n if (cl) {\n const text = `\uD83D\uDCCB \u66F4\u65B0\u5185\u5BB9\u6982\u8981\\n\\n${cl.text.slice(0, 1500)}\\n\\n\u5B8C\u6574\u65E5\u5FD7: ${CHANGELOG_BASE}`;\n if (cl.imageBase64) await sendWithImage(text, cl.imageBase64);\n else await sendText(text);\n } else {\n await sendText(`\uD83D\uDCCB \u66F4\u65B0\u65E5\u5FD7: ${CHANGELOG_BASE}`);\n }\n }\n\n // 5. \u5907\u4EFD\n let backupInfo: { path: string; size: number };\n try {\n backupInfo = await doBackup(bdsPath, backupDir, preserve);\n log.info(`\u5907\u4EFD\u5B8C\u6210: ${backupInfo.path} (${(backupInfo.size / 1024 / 1024).toFixed(1)} MB)`);\n if (qqNotify) {\n await sendText(`\u2705 \u5907\u4EFD\u5B8C\u6210\\n\\n\u5927\u5C0F: ${(backupInfo.size / 1024 / 1024).toFixed(1)} MB\\n\u4F4D\u7F6E: ${backupInfo.path}`);\n }\n } catch (e) {\n log.error(`\u5907\u4EFD\u5931\u8D25: ${(e as Error).message}`);\n await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25\\n\\n\u5907\u4EFD\u5931\u8D25: ${(e as Error).message}\\n\u64CD\u4F5C\u5DF2\u4E2D\u6B62`);\n return 1;\n }\n\n // 6. \u5199\u56DE\u6EDA\u6807\u8BB0 (\u8DE8\u8FDB\u7A0B\u53EF\u6062\u590D)\n writeRollbackMarker({\n timestamp: Date.now(),\n bds_path: bdsPath,\n backup_dir: backupInfo.path,\n preserve,\n previous_version: currentVer,\n });\n\n // 7. \u505C\u670D\n const bds = createBdsManager({ detached: true });\n try {\n log.info(\"\u505C\u6B62 BDS \u670D\u52A1...\");\n await bds.stop();\n log.info(\"BDS \u5DF2\u505C\u6B62\");\n } catch (e) {\n log.warn(`BDS \u505C\u6B62\u5F02\u5E38: ${(e as Error).message}`);\n }\n\n // 8. \u4E0B\u8F7D (\u5168\u90E8\u5931\u8D25\u5219\u56DE\u6EDA)\n // \u7528 mkdtempSync \u521B\u5EFA\u4E34\u65F6 staging \u76EE\u5F55 - \u907F\u514D\u6C61\u67D3 SCRIPT_DIR\n const stagingDir = fs.mkdtempSync(path.join(os.tmpdir(), \"bds-update-\"));\n const zipPath = path.join(stagingDir, `bedrock-server-${latestVer}.zip`);\n let downloaded = false;\n try {\n log.info(`\u5F00\u59CB\u4E0B\u8F7D ${latestVer}...`);\n if (qqNotify) await sendText(`\uD83D\uDCE5 \u6B63\u5728\u4E0B\u8F7D BDS ${latestVer}...`);\n\n let lastErr: Error | null = null;\n const downloadTimeoutMs = (cfg.download_timeout ?? 120) * 1000;\n /* stderr \u88AB pipe \u65F6\u4E0D\u753B bar\uFF1B\u7528\u6587\u672C\u8FDB\u5EA6\u7ED9 sfmc REPL \u7B49\u7236\u8FDB\u7A0B */\n if (isTaskbarSupported() && process.stdout.isTTY) {\n log.info(\"\u68C0\u6D4B\u5230 Windows Terminal,\u4EFB\u52A1\u680F\u8FDB\u5EA6\u5DF2\u542F\u7528 (OSC 9;4)\");\n }\n for (const url of downloadUrls) {\n /* \u6BCF\u6B21\u5C1D\u8BD5\u65B0\u5EFA bar + binder\uFF0C\u907F\u514D\u5931\u8D25\u91CD\u8BD5\u65F6 started \u72B6\u6001\u6B8B\u7559\uFF08LSP\uFF09 */\n const progressBar = createTerminalProgress({\n stream: process.stderr,\n logger: (msg) => log.info(msg.startsWith(\"\u8FDB\u5EA6\") ? `\u4E0B\u8F7D${msg.slice(2)}` : msg),\n format: \"\u4E0B\u8F7D\u8FDB\u5EA6 | {bar} | {percentage}% | {value}/{total} MB | \u901F\u5EA6: {speed}\",\n });\n const onByteProgress = bindByteProgressToBar(progressBar, {\n speedSampleMs: 0,\n onProgress: (dl, total) => {\n const pct = total > 0 ? (dl / total) * 100 : 0;\n setTaskbarProgress(pct);\n },\n });\n try {\n setTaskbarProgress(0);\n await httpDownload(url, zipPath, {\n totalTimeoutMs: Math.max(downloadTimeoutMs, 600_000), // \u4E0D\u5C11\u4E8E 10 \u5206\u949F\n onProgress: onByteProgress,\n });\n if (progressBar.active) progressBar.stop();\n // \u4E0B\u8F7D\u5B8C\u6210,\u4EFB\u52A1\u680F\u6536\u5230 100% \u7EFF\u6761\u540E\u6E05\u6389\n setTaskbarProgress(100);\n clearTaskbarProgress();\n downloaded = true;\n lastErr = null;\n break;\n } catch (e) {\n if (progressBar.active) progressBar.stop();\n // \u4E0B\u8F7D\u5931\u8D25,\u4EFB\u52A1\u680F\u4EAE\u7EA2\n setTaskbarProgress(100, \"error\");\n clearTaskbarProgress();\n lastErr = e as Error;\n log.warn(`${url} \u4E0D\u53EF\u7528: ${lastErr.message}\uFF0C\u5C1D\u8BD5\u5907\u7528\u5730\u5740...`);\n }\n }\n if (!downloaded && lastErr) throw lastErr;\n\n const zipSizeMB = (fs.statSync(zipPath).size / 1024 / 1024).toFixed(1);\n log.info(`\u4E0B\u8F7D\u5B8C\u6210 (${zipSizeMB} MB)`);\n\n // \u6D41\u5F0F\u54C8\u5E0C\u6821\u9A8C\n if (hash.sha1 || hash.sha256) {\n const algo = hash.sha256 ? \"SHA256\" : \"SHA1\";\n log.info(`\u6821\u9A8C\u6587\u4EF6\u5B8C\u6574\u6027 (${algo})...`);\n const ok = await verifyFileHash(zipPath, hash.sha1, hash.sha256);\n if (!ok) throw new Error(`${algo} \u6821\u9A8C\u4E0D\u901A\u8FC7\uFF0C\u6587\u4EF6\u53EF\u80FD\u635F\u574F`);\n log.info(`${algo} \u6821\u9A8C\u901A\u8FC7`);\n }\n } catch (e) {\n log.error(`\u4E0B\u8F7D\u5931\u8D25: ${(e as Error).message}`);\n await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25\\n\\n\u4E0B\u8F7D\u5931\u8D25: ${(e as Error).message}\\nBDS \u5DF2\u505C\u6B62\uFF0C\u8BF7\u624B\u52A8\u6062\u590D`);\n // \u81EA\u52A8\u56DE\u6EDA\n rollbackFromBackup({\n timestamp: Date.now(),\n bds_path: bdsPath,\n backup_dir: backupInfo.path,\n preserve,\n previous_version: currentVer,\n });\n rmSafe(stagingDir);\n return 1;\n }\n\n // 9. \u89E3\u538B + \u90E8\u7F72\n try {\n log.info(\"\u89E3\u538B\u4E2D...\");\n // \u5148\u6E05\u7A7A BDS \u76EE\u5F55\n emptyDirSync(bdsPath);\n await extractZipToBds(zipPath, bdsPath);\n ensureBdsExecutable(exePath);\n log.info(\"\u89E3\u538B\u5B8C\u6210\");\n } catch (e) {\n log.error(`\u89E3\u538B\u5931\u8D25: ${(e as Error).message}`);\n await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25\\n\\n\u89E3\u538B\u5931\u8D25: ${(e as Error).message}\\nBDS \u5DF2\u505C\u6B62\uFF0C\u8BF7\u624B\u52A8\u6062\u590D`);\n // \u56DE\u6EDA: \u6E05\u7A7A + \u4ECE\u5907\u4EFD\u6062\u590D preserves\n emptyDirSync(bdsPath);\n await restorePreserves(bdsPath, backupInfo.path, preserve);\n rmSafe(stagingDir);\n return 1;\n } finally {\n rmSafe(zipPath); // \u6E05\u6389 staging zip\n }\n\n // 10. \u5B8C\u6574\u6027\u68C0\u67E5 (\u907F\u514D\u89E3\u538B\u51FA\u7A7A/\u7F3A\u6587\u4EF6\u7684\u75C5\u6001\u5305)\n const verify = verifyBdsInstall(bdsPath, bdsInstallRequiredFiles());\n if (!verify.ok) {\n log.error(`\u89E3\u538B\u540E\u7684 BDS \u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5931\u6587\u4EF6: ${verify.missing.join(\", \")}`);\n await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25\\n\\n\u90E8\u7F72\u540E\u7F3A\u5931\u5173\u952E\u6587\u4EF6: ${verify.missing.join(\", \")}`);\n emptyDirSync(bdsPath);\n await restorePreserves(bdsPath, backupInfo.path, preserve);\n rmSafe(stagingDir);\n return 1;\n }\n\n // 11. \u6062\u590D preserves (zip \u89E3\u538B\u65F6\u8FD9\u4E9B\u76EE\u5F55\u53EF\u80FD\u88AB\u8986\u76D6)\n await restorePreserves(bdsPath, backupInfo.path, preserve);\n\n // 11b. \u5B89\u88C5\u6536\u5C3E\uFF1A\u56E0 EULA \u5DF2\u540C\u610F\uFF0C\u786E\u4FDD\u9065\u6D4B\u5F00\u5173\uFF08\u5DF2\u6709\u5219\u8DF3\u8FC7\uFF09\n ensureEmitServerTelemetry(bdsPath, log);\n\n // 12. \u5199\u5165\u7248\u672C\u7F13\u5B58\n const newExeHash = await hashFileAsync(exePath, \"sha256\").catch(() => \"\");\n if (newExeHash) {\n saveVersionCache(latestVer, newExeHash);\n log.info(`\u5DF2\u8BB0\u5F55\u7248\u672C ${latestVer} \u7684 SHA256`);\n }\n\n // 13. \u542F\u52A8 BDS\n if (autoRestart) {\n try {\n log.info(\"\u542F\u52A8 BDS...\");\n await bds.start();\n } catch (e) {\n log.error(`\u542F\u52A8\u5931\u8D25: ${(e as Error).message}`);\n await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25\\n\\n\u542F\u52A8\u5931\u8D25: ${(e as Error).message}\\n\u8BF7\u624B\u52A8\u542F\u52A8 BDS`);\n // \u6CE8\u610F: \u6B64\u65F6 BDS \u5DF2\u7ECF\u90E8\u7F72\u5B8C\u6210\uFF0C\u542F\u52A8\u5931\u8D25 \u2192 \u542F\u52A8\u95EE\u9898\uFF0C\u4E0D\u505A\u6587\u4EF6\u7EA7\u56DE\u6EDA\n rmSafe(stagingDir);\n clearRollbackMarker();\n return 1;\n }\n }\n\n rmSafe(stagingDir);\n clearRollbackMarker();\n\n // 14. QQ \u901A\u77E5\u7ED3\u679C\n const durationMs = Date.now() - startTime;\n if (qqNotify) {\n await sendText(\n `\u2705 BDS \u66F4\u65B0\u5B8C\u6210\\n\\n` +\n `${currentVer} \u2192 ${latestVer}\\n` +\n `\u8017\u65F6: ${(durationMs / 1000).toFixed(1)}s\\n` +\n `${autoRestart ? \"\u670D\u52A1\u5668\u5DF2\u91CD\u65B0\u542F\u52A8\" : \"\u8BF7\u624B\u52A8\u91CD\u542F\u670D\u52A1\u5668\"}`\n );\n }\n log.info(`===== \u66F4\u65B0\u5B8C\u6210 (${(durationMs / 1000).toFixed(1)}s) =====`);\n /* \u673A\u5668\u53EF\u8BFB\u7ED3\u679C\u6807\u8BB0\uFF1A\u4F9B sfmc \u7B49\u76D1\u7763\u5668\u5224\u65AD\u300C\u771F\u6B63\u5B8C\u6210\u90E8\u7F72\u300D\uFF0C\u52FF\u4F9D\u8D56\u672C\u5730\u5316\u65E5\u5FD7\u6587\u6848\u3002 */\n emitUpdateResult(\"deployed\", log);\n return 0;\n}\n\nfunction isMain(): boolean {\n // sfmc supervisor \u901A\u8FC7 SFMC_SERVICE \u62C9\u8D77\u5B50\u8FDB\u7A0B\u65F6\u4F18\u5148\u5224\u5B9A\n if (process.env.SFMC_SERVICE === \"update\") return true;\n return isMainModule(import.meta.url);\n}\n\nif (isMain()) {\n runUpdate()\n .then((code) => {\n closeLog();\n process.exit(code);\n })\n .catch((e) => {\n log.error(`\u672A\u6355\u83B7\u9519\u8BEF: ${(e as Error).message}`);\n closeLog();\n process.exit(1);\n });\n}\n"],
|
|
5
|
-
"mappings": "AAWA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,wBAAwB;AACjC,SAAS,gBAAgB,sBAAsB;AAC/C,SAAS,aAAa,cAAc,eAAe,cAAc;
|
|
4
|
+
"sourcesContent": ["/**\n * check-update.ts \u2014 BDS \u81EA\u52A8\u66F4\u65B0\u5668 (\u4E3B\u6D41\u7A0B)\n *\n * 1. \u4E34\u65F6 staging \u76EE\u5F55 (mkdtempSync) - \u907F\u514D\u6C61\u67D3\u6570\u636E\u6839\n * 2. \u4E0B\u8F7D/\u89E3\u538B\u5931\u8D25 \u2192 \u81EA\u52A8\u4ECE\u5907\u4EFD\u56DE\u6EDA preserves\n * 3. \u6D41\u5F0F SHA256 \u6821\u9A8C - \u4E0D\u628A 250MB \u6587\u4EF6\u8BFB\u5165\u5185\u5B58\n * 4. \u517C\u5BB9\u7248\u672C\u767D\u540D\u5355 - \u4E0D\u5339\u914D\u5219\u8DF3\u8FC7\u5347\u7EA7\n * 5. QQ \u901A\u77E5\u7EDF\u4E00\u52A0 5s \u8D85\u65F6 - \u6C38\u8FDC\u4E0D\u963B\u585E\u4E3B\u6D41\u7A0B\n * 6. Rollback marker \u843D\u76D8\u5230 <ROOT>/.sfmc/ - \u8DE8\u8FDB\u7A0B / \u8DE8\u91CD\u542F\u53EF\u6062\u590D\n */\n\nimport {\n bindByteProgressToBar,\n createNodeServiceLogger,\n createTerminalProgress,\n} from \"@sfmc-bds/sdk/logs\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { createBdsManager } from \"./bds-manager.js\";\nimport { CHANGELOG_BASE, fetchChangelog } from \"./changelog.js\";\nimport { copyDirSync, copyFileSyncSafe, emptyDirSync, hashFileAsync, rmSafe } from \"./fsx.js\";\nimport { bdsExePath, bdsInstallRequiredFiles, ensureBdsExecutable } from \"./host-platform.js\";\nimport { httpDownload } from \"./http.js\";\nimport { isMainModule } from \"./is-main.js\";\nimport { loadConfig, LOG_PATH, resolvePaths } from \"./paths.js\";\nimport { sendText, sendWithImage } from \"./qqutil.js\";\nimport {\n clearRollbackMarker,\n getDirSize,\n rollbackFromBackup,\n verifyBdsInstall,\n writeRollbackMarker,\n} from \"./rollback.js\";\nimport { ensureEmitServerTelemetry } from \"./server-properties.js\";\nimport { clearTaskbarProgress, isTaskbarSupported, setTaskbarProgress } from \"./taskbar.js\";\nimport { emitUpdateKv, emitUpdateResult } from \"./update-result.js\";\nimport {\n buildDownloadUrls,\n fetchVersionDetails,\n getVersionInfo,\n isVersionCompatible,\n verifyFileHash,\n} from \"./upstream.js\";\nimport { compareVersions, getCurrentVersionAsync, getCurrentVersionSync, saveVersionCache } from \"./version.js\";\nimport { extractZipFileToDir } from \"./zipx.js\";\n\n// \u72EC\u7ACB\u5165\u53E3:source = \"updater\",\u4E0E bds-manager \u7684 \"bds-tools\" \u533A\u5206;\u540C\u843D\u76D8 bds-update.log\nconst log = createNodeServiceLogger({ source: \"updater\", logPath: LOG_PATH });\nconst closeLog = (): void => log.close();\n\nfunction parseArgs(argv: string[]): Record<string, string | boolean> {\n const out: Record<string, string | boolean> = {};\n for (const a of argv) {\n const m = a.match(/^--([\\w-]+)(?:=(.+))?$/);\n if (m) out[m[1]!] = m[2] || true;\n }\n return out;\n}\n\ninterface UpdateContext {\n cfg: ReturnType<typeof loadConfig>;\n channel: \"release\" | \"preview\" | string;\n checkOnly: boolean;\n force: boolean;\n bdsPath: string;\n backupDir: string;\n preserve: string[];\n autoRestart: boolean;\n qqNotify: boolean;\n}\n\nfunction buildContext(): UpdateContext {\n const cfg = loadConfig();\n const args = parseArgs(process.argv.slice(2));\n const { bds_path, backup_dir, preserve } = resolvePaths(cfg);\n const channel = String(args[\"channel\"] || cfg.channel || \"release\");\n return {\n cfg,\n channel,\n checkOnly: !!args[\"check-only\"],\n force: !!args[\"force\"],\n bdsPath: bds_path,\n backupDir: backup_dir,\n preserve,\n autoRestart: !args[\"no-start\"] && cfg.auto_restart !== false,\n qqNotify: !!cfg.qq_notify,\n };\n}\n\nasync function doBackup(\n bdsPath: string,\n backupDir: string,\n preserve: string[]\n): Promise<{ path: string; size: number }> {\n const dateStr = new Date().toISOString().slice(0, 10);\n const dest = path.join(backupDir, dateStr);\n fs.mkdirSync(dest, { recursive: true });\n\n let anyCopied = false;\n for (const item of preserve) {\n const src = path.join(bdsPath, item);\n const target = path.join(dest, item);\n if (!fs.existsSync(src)) continue;\n fs.mkdirSync(path.dirname(target), { recursive: true });\n if (fs.statSync(src).isDirectory()) copyDirSync(src, target);\n else copyFileSyncSafe(src, target);\n anyCopied = true;\n log.info(`\u5DF2\u5907\u4EFD: ${item}`);\n }\n\n // \u5373\u4F7F\u5168\u90E8 preserve \u90FD\u8DF3\u8FC7\uFF0C\u4E5F\u8BA9\u76EE\u5F55\u5B58\u5728\u4EE5\u4FBF\u8BB0\u5F55\u65F6\u95F4\u6233\n if (!anyCopied) {\n log.info(`\u6CA1\u6709\u9700\u8981\u5907\u4EFD\u7684\u6587\u4EF6 (preserve \u5217\u8868\u4E3A\u7A7A\u6216\u5168\u90E8\u7F3A\u5931)`);\n }\n return { path: dest, size: getDirSize(dest) };\n}\n\n/** \u5F02\u6B65\u6062\u590D preserves \u4ECE\u5907\u4EFD\u5230 BDS \u8DEF\u5F84 */\nasync function restorePreserves(bdsPath: string, backupDir: string, preserve: string[]): Promise<void> {\n for (const item of preserve) {\n const src = path.join(backupDir, item);\n const dest = path.join(bdsPath, item);\n if (!fs.existsSync(src)) continue;\n try {\n if (fs.statSync(src).isDirectory()) {\n if (fs.existsSync(dest)) emptyDirSync(dest);\n copyDirSync(src, dest);\n } else {\n fs.mkdirSync(path.dirname(dest), { recursive: true });\n copyFileSyncSafe(src, dest);\n }\n log.info(`\u5DF2\u6062\u590D: ${item}`);\n } catch (e) {\n log.warn(`\u6062\u590D\u5931\u8D25 ${item}: ${(e as Error).message}`);\n }\n }\n}\n\n/** \u628A zip \u89E3\u538B\u5230 destDir (\u8986\u76D6) \u2014 \u7ECF zipx \u5B89\u5168\u89E3\u538B */\nasync function extractZipToBds(zipPath: string, destDir: string): Promise<void> {\n // \u62BD\u51FA\u5230\u4E34\u65F6\u76EE\u5F55\uFF0C\u907F\u514D\u65E7\u5185\u5BB9\u5E72\u6270\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), \"bds-extract-\"));\n try {\n await extractZipFileToDir(zipPath, tmpDir);\n // \u628A\u4E34\u65F6\u76EE\u5F55\u91CC\u7684\u5185\u5BB9\u590D\u5236\u5230 BDS \u8DEF\u5F84\n for (const entry of fs.readdirSync(tmpDir)) {\n const srcPath = path.join(tmpDir, entry);\n const destPath = path.join(destDir, entry);\n if (fs.statSync(srcPath).isDirectory()) {\n if (fs.existsSync(destPath)) emptyDirSync(destPath);\n copyDirSync(srcPath, destPath);\n } else {\n fs.copyFileSync(srcPath, destPath);\n }\n }\n } finally {\n rmSafe(tmpDir);\n }\n}\n\nexport async function runUpdate(): Promise<number> {\n const startTime = Date.now();\n const ctx = buildContext();\n const { cfg, channel, checkOnly, force, bdsPath, backupDir, preserve, autoRestart, qqNotify } = ctx;\n\n log.info(`===== \u5F00\u59CB\u68C0\u67E5\u66F4\u65B0 (${channel}) =====`);\n fs.mkdirSync(bdsPath, { recursive: true });\n fs.mkdirSync(backupDir, { recursive: true });\n\n // 1. \u5F53\u524D\u7248\u672C\n const exePath = bdsExePath(bdsPath);\n const currentVer = await getCurrentVersionAsync(exePath).catch(() => getCurrentVersionSync(exePath));\n log.info(`\u5F53\u524D\u7248\u672C: ${currentVer}`);\n\n // 2. \u83B7\u53D6\u6700\u65B0\u7248\u672C\n let latestVer: string;\n try {\n const info = await getVersionInfo(cfg, channel);\n latestVer = info.version;\n log.info(`\u6700\u65B0\u7248\u672C: ${latestVer}`);\n const cmp = compareVersions(latestVer, currentVer);\n if (cmp <= 0 && !force) {\n log.info(\"\u5DF2\u662F\u6700\u65B0\u7248\u672C\uFF0C\u65E0\u9700\u66F4\u65B0\");\n if (qqNotify) {\n await sendText(`\u2705 BDS \u5DF2\u662F\u6700\u65B0\u7248\u672C\\n\\n\u5F53\u524D: ${currentVer}\\n\u6700\u65B0: ${latestVer}`);\n }\n emitUpdateKv(\"CURRENT\", currentVer, log);\n emitUpdateKv(\"LATEST\", latestVer, log);\n emitUpdateResult(\"uptodate\", log);\n return 0;\n }\n } catch (e) {\n log.error(`\u83B7\u53D6\u7248\u672C\u4FE1\u606F\u5931\u8D25: ${(e as Error).message}`);\n await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25\\n\\n\u65E0\u6CD5\u83B7\u53D6\u7248\u672C\u4FE1\u606F: ${(e as Error).message}`);\n return 1;\n }\n\n // \u517C\u5BB9\u6027\u68C0\u67E5\n if (!force && !isVersionCompatible(cfg, latestVer)) {\n log.warn(`${latestVer} \u4E0D\u5728\u517C\u5BB9\u7248\u672C\u767D\u540D\u5355\u4E2D\uFF0C\u8DF3\u8FC7\u5347\u7EA7`);\n if (qqNotify) {\n await sendText(`\u26A0\uFE0F BDS ${latestVer} \u4E0D\u5728\u517C\u5BB9\u6027\u767D\u540D\u5355\uFF0C\u5DF2\u8DF3\u8FC7\u5347\u7EA7\u3002\u8BF7\u4EBA\u5DE5\u786E\u8BA4\u3002`);\n }\n emitUpdateResult(\"skipped\", log);\n return 2;\n }\n\n // 3. \u53D6\u4E0B\u8F7D\u94FE\u63A5\n let downloadUrls: string[];\n let hash: { sha1: string; sha256: string };\n try {\n const details = await fetchVersionDetails(cfg, channel, latestVer);\n downloadUrls = buildDownloadUrls(cfg, channel, latestVer, details);\n hash = { sha1: details.sha1, sha256: details.sha256 };\n } catch (e) {\n log.error(`\u83B7\u53D6\u4E0B\u8F7D\u8BE6\u60C5\u5931\u8D25: ${(e as Error).message}`);\n await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25\\n\\n\u4E0B\u8F7D\u8BE6\u60C5\u83B7\u53D6\u5931\u8D25: ${(e as Error).message}`);\n return 1;\n }\n\n if (checkOnly) {\n emitUpdateKv(\"CURRENT\", currentVer, log);\n emitUpdateKv(\"LATEST\", latestVer, log);\n emitUpdateKv(\"CHANNEL\", channel, log);\n emitUpdateKv(\"URLS\", String(downloadUrls.length), log);\n emitUpdateResult(\"check-only\", log);\n return 0;\n }\n\n log.info(`\u53D1\u73B0\u65B0\u7248\u672C: ${currentVer} \u2192 ${latestVer}`);\n\n // 4. QQ \u9884\u544A\n if (qqNotify) {\n await sendText(\n `\uD835\uDC0C\uD835\uDC22\uD835\uDC27\uD835\uDC1E\uD835\uDC1C\uD835\uDC2B\uD835\uDC1A\uD835\uDC1F\uD835\uDC2D \uD835\uDC01\uD835\uDC03\uD835\uDC12 \u66F4\u65B0\u901A\u77E5\\n\\n` +\n `\u68C0\u6D4B\u5230\u65B0\u7248\u672C\uFF01\\n\\n` +\n `\u5F53\u524D: ${currentVer}\\n` +\n `\u6700\u65B0: ${latestVer}\\n` +\n `\u9891\u9053: ${channel === \"preview\" ? \"\u9884\u89C8\u7248\" : \"\u6B63\u5F0F\u7248\"}\\n\\n` +\n `\u670D\u52A1\u5668\u5373\u5C06\u5F00\u59CB\u66F4\u65B0~ \u8BF7\u8010\u5FC3\u7B49\u5F85^(*\uFFE3(oo)\uFFE3)^`\n );\n const cl = await fetchChangelog(channel);\n if (cl) {\n const text = `\uD83D\uDCCB \u66F4\u65B0\u5185\u5BB9\u6982\u8981\\n\\n${cl.text.slice(0, 1500)}\\n\\n\u5B8C\u6574\u65E5\u5FD7: ${CHANGELOG_BASE}`;\n if (cl.imageBase64) await sendWithImage(text, cl.imageBase64);\n else await sendText(text);\n } else {\n await sendText(`\uD83D\uDCCB \u66F4\u65B0\u65E5\u5FD7: ${CHANGELOG_BASE}`);\n }\n }\n\n // 5. \u5907\u4EFD\n let backupInfo: { path: string; size: number };\n try {\n backupInfo = await doBackup(bdsPath, backupDir, preserve);\n log.info(`\u5907\u4EFD\u5B8C\u6210: ${backupInfo.path} (${(backupInfo.size / 1024 / 1024).toFixed(1)} MB)`);\n if (qqNotify) {\n await sendText(`\u2705 \u5907\u4EFD\u5B8C\u6210\\n\\n\u5927\u5C0F: ${(backupInfo.size / 1024 / 1024).toFixed(1)} MB\\n\u4F4D\u7F6E: ${backupInfo.path}`);\n }\n } catch (e) {\n log.error(`\u5907\u4EFD\u5931\u8D25: ${(e as Error).message}`);\n await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25\\n\\n\u5907\u4EFD\u5931\u8D25: ${(e as Error).message}\\n\u64CD\u4F5C\u5DF2\u4E2D\u6B62`);\n return 1;\n }\n\n // 6. \u5199\u56DE\u6EDA\u6807\u8BB0 (\u8DE8\u8FDB\u7A0B\u53EF\u6062\u590D)\n writeRollbackMarker({\n timestamp: Date.now(),\n bds_path: bdsPath,\n backup_dir: backupInfo.path,\n preserve,\n previous_version: currentVer,\n });\n\n // 7. \u505C\u670D\n const bds = createBdsManager({ detached: true });\n try {\n log.info(\"\u505C\u6B62 BDS \u670D\u52A1...\");\n await bds.stop();\n log.info(\"BDS \u5DF2\u505C\u6B62\");\n } catch (e) {\n log.warn(`BDS \u505C\u6B62\u5F02\u5E38: ${(e as Error).message}`);\n }\n\n // 8. \u4E0B\u8F7D (\u5168\u90E8\u5931\u8D25\u5219\u56DE\u6EDA)\n // \u7528 mkdtempSync \u521B\u5EFA\u4E34\u65F6 staging \u76EE\u5F55 - \u907F\u514D\u6C61\u67D3 SCRIPT_DIR\n const stagingDir = fs.mkdtempSync(path.join(os.tmpdir(), \"bds-update-\"));\n const zipPath = path.join(stagingDir, `bedrock-server-${latestVer}.zip`);\n let downloaded = false;\n try {\n log.info(`\u5F00\u59CB\u4E0B\u8F7D ${latestVer}...`);\n if (qqNotify) await sendText(`\uD83D\uDCE5 \u6B63\u5728\u4E0B\u8F7D BDS ${latestVer}...`);\n\n let lastErr: Error | null = null;\n const downloadTimeoutMs = (cfg.download_timeout ?? 120) * 1000;\n /* stderr \u88AB pipe \u65F6\u4E0D\u753B bar\uFF1B\u7528\u6587\u672C\u8FDB\u5EA6\u7ED9 sfmc REPL \u7B49\u7236\u8FDB\u7A0B */\n if (isTaskbarSupported() && process.stdout.isTTY) {\n log.info(\"\u68C0\u6D4B\u5230 Windows Terminal,\u4EFB\u52A1\u680F\u8FDB\u5EA6\u5DF2\u542F\u7528 (OSC 9;4)\");\n }\n for (const url of downloadUrls) {\n /* \u6BCF\u6B21\u5C1D\u8BD5\u65B0\u5EFA bar + binder\uFF0C\u907F\u514D\u5931\u8D25\u91CD\u8BD5\u65F6 started \u72B6\u6001\u6B8B\u7559\uFF08LSP\uFF09 */\n const progressBar = createTerminalProgress({\n stream: process.stderr,\n logger: (msg) => log.info(msg.startsWith(\"\u8FDB\u5EA6\") ? `\u4E0B\u8F7D${msg.slice(2)}` : msg),\n format: \"\u4E0B\u8F7D\u8FDB\u5EA6 | {bar} | {percentage}% | {value}/{total} MB | \u901F\u5EA6: {speed}\",\n });\n const onByteProgress = bindByteProgressToBar(progressBar, {\n speedSampleMs: 0,\n onProgress: (dl, total) => {\n const pct = total > 0 ? (dl / total) * 100 : 0;\n setTaskbarProgress(pct);\n },\n });\n try {\n setTaskbarProgress(0);\n await httpDownload(url, zipPath, {\n totalTimeoutMs: Math.max(downloadTimeoutMs, 600_000), // \u4E0D\u5C11\u4E8E 10 \u5206\u949F\n onProgress: onByteProgress,\n });\n if (progressBar.active) progressBar.stop();\n // \u4E0B\u8F7D\u5B8C\u6210,\u4EFB\u52A1\u680F\u6536\u5230 100% \u7EFF\u6761\u540E\u6E05\u6389\n setTaskbarProgress(100);\n clearTaskbarProgress();\n downloaded = true;\n lastErr = null;\n break;\n } catch (e) {\n if (progressBar.active) progressBar.stop();\n // \u4E0B\u8F7D\u5931\u8D25,\u4EFB\u52A1\u680F\u4EAE\u7EA2\n setTaskbarProgress(100, \"error\");\n clearTaskbarProgress();\n lastErr = e as Error;\n log.warn(`${url} \u4E0D\u53EF\u7528: ${lastErr.message}\uFF0C\u5C1D\u8BD5\u5907\u7528\u5730\u5740...`);\n }\n }\n if (!downloaded && lastErr) throw lastErr;\n\n const zipSizeMB = (fs.statSync(zipPath).size / 1024 / 1024).toFixed(1);\n log.info(`\u4E0B\u8F7D\u5B8C\u6210 (${zipSizeMB} MB)`);\n\n // \u6D41\u5F0F\u54C8\u5E0C\u6821\u9A8C\n if (hash.sha1 || hash.sha256) {\n const algo = hash.sha256 ? \"SHA256\" : \"SHA1\";\n log.info(`\u6821\u9A8C\u6587\u4EF6\u5B8C\u6574\u6027 (${algo})...`);\n const ok = await verifyFileHash(zipPath, hash.sha1, hash.sha256);\n if (!ok) throw new Error(`${algo} \u6821\u9A8C\u4E0D\u901A\u8FC7\uFF0C\u6587\u4EF6\u53EF\u80FD\u635F\u574F`);\n log.info(`${algo} \u6821\u9A8C\u901A\u8FC7`);\n }\n } catch (e) {\n log.error(`\u4E0B\u8F7D\u5931\u8D25: ${(e as Error).message}`);\n await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25\\n\\n\u4E0B\u8F7D\u5931\u8D25: ${(e as Error).message}\\nBDS \u5DF2\u505C\u6B62\uFF0C\u8BF7\u624B\u52A8\u6062\u590D`);\n // \u81EA\u52A8\u56DE\u6EDA\n rollbackFromBackup({\n timestamp: Date.now(),\n bds_path: bdsPath,\n backup_dir: backupInfo.path,\n preserve,\n previous_version: currentVer,\n });\n rmSafe(stagingDir);\n return 1;\n }\n\n // 9. \u89E3\u538B + \u90E8\u7F72\n try {\n log.info(\"\u89E3\u538B\u4E2D...\");\n // \u5148\u6E05\u7A7A BDS \u76EE\u5F55\n emptyDirSync(bdsPath);\n await extractZipToBds(zipPath, bdsPath);\n ensureBdsExecutable(exePath);\n log.info(\"\u89E3\u538B\u5B8C\u6210\");\n } catch (e) {\n log.error(`\u89E3\u538B\u5931\u8D25: ${(e as Error).message}`);\n await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25\\n\\n\u89E3\u538B\u5931\u8D25: ${(e as Error).message}\\nBDS \u5DF2\u505C\u6B62\uFF0C\u8BF7\u624B\u52A8\u6062\u590D`);\n // \u56DE\u6EDA: \u6E05\u7A7A + \u4ECE\u5907\u4EFD\u6062\u590D preserves\n emptyDirSync(bdsPath);\n await restorePreserves(bdsPath, backupInfo.path, preserve);\n rmSafe(stagingDir);\n return 1;\n } finally {\n rmSafe(zipPath); // \u6E05\u6389 staging zip\n }\n\n // 10. \u5B8C\u6574\u6027\u68C0\u67E5 (\u907F\u514D\u89E3\u538B\u51FA\u7A7A/\u7F3A\u6587\u4EF6\u7684\u75C5\u6001\u5305)\n const verify = verifyBdsInstall(bdsPath, bdsInstallRequiredFiles());\n if (!verify.ok) {\n log.error(`\u89E3\u538B\u540E\u7684 BDS \u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5931\u6587\u4EF6: ${verify.missing.join(\", \")}`);\n await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25\\n\\n\u90E8\u7F72\u540E\u7F3A\u5931\u5173\u952E\u6587\u4EF6: ${verify.missing.join(\", \")}`);\n emptyDirSync(bdsPath);\n await restorePreserves(bdsPath, backupInfo.path, preserve);\n rmSafe(stagingDir);\n return 1;\n }\n\n // 11. \u6062\u590D preserves (zip \u89E3\u538B\u65F6\u8FD9\u4E9B\u76EE\u5F55\u53EF\u80FD\u88AB\u8986\u76D6)\n await restorePreserves(bdsPath, backupInfo.path, preserve);\n\n // 11b. \u5B89\u88C5\u6536\u5C3E\uFF1A\u56E0 EULA \u5DF2\u540C\u610F\uFF0C\u786E\u4FDD\u9065\u6D4B\u5F00\u5173\uFF08\u5DF2\u6709\u5219\u8DF3\u8FC7\uFF09\n ensureEmitServerTelemetry(bdsPath, log);\n\n // 12. \u5199\u5165\u7248\u672C\u7F13\u5B58\n const newExeHash = await hashFileAsync(exePath, \"sha256\").catch(() => \"\");\n if (newExeHash) {\n saveVersionCache(latestVer, newExeHash);\n log.info(`\u5DF2\u8BB0\u5F55\u7248\u672C ${latestVer} \u7684 SHA256`);\n }\n\n // 13. \u542F\u52A8 BDS\n if (autoRestart) {\n try {\n log.info(\"\u542F\u52A8 BDS...\");\n await bds.start();\n } catch (e) {\n log.error(`\u542F\u52A8\u5931\u8D25: ${(e as Error).message}`);\n await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25\\n\\n\u542F\u52A8\u5931\u8D25: ${(e as Error).message}\\n\u8BF7\u624B\u52A8\u542F\u52A8 BDS`);\n // \u6CE8\u610F: \u6B64\u65F6 BDS \u5DF2\u7ECF\u90E8\u7F72\u5B8C\u6210\uFF0C\u542F\u52A8\u5931\u8D25 \u2192 \u542F\u52A8\u95EE\u9898\uFF0C\u4E0D\u505A\u6587\u4EF6\u7EA7\u56DE\u6EDA\n rmSafe(stagingDir);\n clearRollbackMarker();\n return 1;\n }\n }\n\n rmSafe(stagingDir);\n clearRollbackMarker();\n\n // 14. QQ \u901A\u77E5\u7ED3\u679C\n const durationMs = Date.now() - startTime;\n if (qqNotify) {\n await sendText(\n `\u2705 BDS \u66F4\u65B0\u5B8C\u6210\\n\\n` +\n `${currentVer} \u2192 ${latestVer}\\n` +\n `\u8017\u65F6: ${(durationMs / 1000).toFixed(1)}s\\n` +\n `${autoRestart ? \"\u670D\u52A1\u5668\u5DF2\u91CD\u65B0\u542F\u52A8\" : \"\u8BF7\u624B\u52A8\u91CD\u542F\u670D\u52A1\u5668\"}`\n );\n }\n log.info(`===== \u66F4\u65B0\u5B8C\u6210 (${(durationMs / 1000).toFixed(1)}s) =====`);\n /* \u673A\u5668\u53EF\u8BFB\u7ED3\u679C\u6807\u8BB0\uFF1A\u4F9B sfmc \u7B49\u76D1\u7763\u5668\u5224\u65AD\u300C\u771F\u6B63\u5B8C\u6210\u90E8\u7F72\u300D\uFF0C\u52FF\u4F9D\u8D56\u672C\u5730\u5316\u65E5\u5FD7\u6587\u6848\u3002 */\n emitUpdateResult(\"deployed\", log);\n return 0;\n}\n\nfunction isMain(): boolean {\n // sfmc supervisor \u901A\u8FC7 SFMC_SERVICE \u62C9\u8D77\u5B50\u8FDB\u7A0B\u65F6\u4F18\u5148\u5224\u5B9A\n if (process.env.SFMC_SERVICE === \"update\") return true;\n return isMainModule(import.meta.url);\n}\n\nif (isMain()) {\n runUpdate()\n .then((code) => {\n closeLog();\n process.exit(code);\n })\n .catch((e) => {\n log.error(`\u672A\u6355\u83B7\u9519\u8BEF: ${(e as Error).message}`);\n closeLog();\n process.exit(1);\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/fsx.d.ts
CHANGED
|
@@ -8,14 +8,22 @@
|
|
|
8
8
|
export declare function hashFileAsync(filePath: string, algo?: "sha1" | "sha256"): Promise<string>;
|
|
9
9
|
/** 同步版本(用于小文件) */
|
|
10
10
|
export declare function hashFileSync(filePath: string, algo?: "sha1" | "sha256"): string;
|
|
11
|
-
/**
|
|
11
|
+
/** 排除无需备份/复制的仓库与系统垃圾元数据 */
|
|
12
|
+
export declare const DEFAULT_COPY_IGNORE: Set<string>;
|
|
13
|
+
/**
|
|
14
|
+
* Windows 兼容的安全单文件拷贝。
|
|
15
|
+
* 若目标文件已存在且处于只读状态(例如 git objects 属性为只读),先赋写权限并覆写,
|
|
16
|
+
* 并确保拷贝后的目标文件具有可写权限,避免后续重复备份报错。
|
|
17
|
+
*/
|
|
18
|
+
export declare function copyFileSyncSafe(src: string, dest: string): void;
|
|
19
|
+
/** 同步流式拷贝单文件,目标只读时自动解除并覆写 */
|
|
12
20
|
export declare function copyFileAsync(src: string, dest: string): Promise<void>;
|
|
13
|
-
/** 同步目录复制 (递归) */
|
|
14
|
-
export declare function copyDirSync(src: string, dest: string): void;
|
|
15
|
-
/** 异步目录复制 (基于流) */
|
|
16
|
-
export declare function copyDirAsync(src: string, dest: string): Promise<void>;
|
|
17
|
-
/** 计算目录大小 (字节) */
|
|
18
|
-
export declare function getDirSize(dir: string): number;
|
|
21
|
+
/** 同步目录复制 (递归),默认排除 .git / node_modules 等元数据 */
|
|
22
|
+
export declare function copyDirSync(src: string, dest: string, ignore?: Set<string>): void;
|
|
23
|
+
/** 异步目录复制 (基于流),默认排除 .git / node_modules 等元数据 */
|
|
24
|
+
export declare function copyDirAsync(src: string, dest: string, ignore?: Set<string>): Promise<void>;
|
|
25
|
+
/** 计算目录大小 (字节),默认排除 .git / node_modules 等元数据 */
|
|
26
|
+
export declare function getDirSize(dir: string, ignore?: Set<string>): number;
|
|
19
27
|
/** 强制清空目录内容 (但保留目录本身) */
|
|
20
28
|
export declare function emptyDirSync(dir: string): void;
|
|
21
29
|
/** 安全删除整个目录 */
|
package/dist/fsx.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fsx.d.ts","sourceRoot":"","sources":["../src/fsx.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH;;;GAGG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,GAAG,QAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAUzG;AAED,kBAAkB;AAClB,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,GAAG,QAAmB,GAAG,MAAM,CAQzF;AAED,
|
|
1
|
+
{"version":3,"file":"fsx.d.ts","sourceRoot":"","sources":["../src/fsx.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH;;;GAGG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,GAAG,QAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAUzG;AAED,kBAAkB;AAClB,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,GAAG,QAAmB,GAAG,MAAM,CAQzF;AAED,2BAA2B;AAC3B,eAAO,MAAM,mBAAmB,aAQ9B,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAwBhE;AAED,6BAA6B;AAC7B,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB5E;AAED,gDAAgD;AAChD,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,GAAG,CAAC,MAAM,CAAuB,GACxC,IAAI,CAYN;AAED,iDAAiD;AACjD,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,GAAG,CAAC,MAAM,CAAuB,GACxC,OAAO,CAAC,IAAI,CAAC,CAef;AAED,gDAAgD;AAChD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,GAAG,CAAC,MAAM,CAAuB,GAAG,MAAM,CAWzF;AAED,yBAAyB;AACzB,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAY9C;AAED,eAAe;AACf,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAMzC;AAED,iCAAiC;AACjC,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM3E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,CAM7D"}
|
package/dist/fsx.js
CHANGED
|
@@ -22,42 +22,99 @@ function hashFileSync(filePath, algo = "sha256") {
|
|
|
22
22
|
return "";
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
+
const DEFAULT_COPY_IGNORE = /* @__PURE__ */ new Set([
|
|
26
|
+
".git",
|
|
27
|
+
".svn",
|
|
28
|
+
".hg",
|
|
29
|
+
"node_modules",
|
|
30
|
+
".DS_Store",
|
|
31
|
+
"Thumbs.db",
|
|
32
|
+
"desktop.ini"
|
|
33
|
+
]);
|
|
34
|
+
function copyFileSyncSafe(src, dest) {
|
|
35
|
+
try {
|
|
36
|
+
fs.copyFileSync(src, dest);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
const code = err?.code;
|
|
39
|
+
if (code === "EPERM" || code === "EACCES") {
|
|
40
|
+
try {
|
|
41
|
+
if (fs.existsSync(dest)) {
|
|
42
|
+
fs.chmodSync(dest, 438);
|
|
43
|
+
fs.unlinkSync(dest);
|
|
44
|
+
}
|
|
45
|
+
fs.copyFileSync(src, dest);
|
|
46
|
+
} catch {
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
fs.chmodSync(dest, 438);
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
}
|
|
25
58
|
async function copyFileAsync(src, dest) {
|
|
26
|
-
|
|
59
|
+
try {
|
|
60
|
+
await pipeline(fs.createReadStream(src), fs.createWriteStream(dest));
|
|
61
|
+
} catch (err) {
|
|
62
|
+
const code = err?.code;
|
|
63
|
+
if (code === "EPERM" || code === "EACCES") {
|
|
64
|
+
try {
|
|
65
|
+
if (fs.existsSync(dest)) {
|
|
66
|
+
fs.chmodSync(dest, 438);
|
|
67
|
+
fs.unlinkSync(dest);
|
|
68
|
+
}
|
|
69
|
+
await pipeline(fs.createReadStream(src), fs.createWriteStream(dest));
|
|
70
|
+
} catch {
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
throw err;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
fs.chmodSync(dest, 438);
|
|
79
|
+
} catch {
|
|
80
|
+
}
|
|
27
81
|
}
|
|
28
|
-
function copyDirSync(src, dest) {
|
|
82
|
+
function copyDirSync(src, dest, ignore = DEFAULT_COPY_IGNORE) {
|
|
29
83
|
fs.mkdirSync(dest, { recursive: true });
|
|
30
84
|
for (const entry of fs.readdirSync(src)) {
|
|
85
|
+
if (ignore.has(entry)) continue;
|
|
31
86
|
const srcPath = path.join(src, entry);
|
|
32
87
|
const destPath = path.join(dest, entry);
|
|
33
88
|
if (fs.statSync(srcPath).isDirectory()) {
|
|
34
|
-
copyDirSync(srcPath, destPath);
|
|
89
|
+
copyDirSync(srcPath, destPath, ignore);
|
|
35
90
|
} else {
|
|
36
|
-
|
|
91
|
+
copyFileSyncSafe(srcPath, destPath);
|
|
37
92
|
}
|
|
38
93
|
}
|
|
39
94
|
}
|
|
40
|
-
async function copyDirAsync(src, dest) {
|
|
95
|
+
async function copyDirAsync(src, dest, ignore = DEFAULT_COPY_IGNORE) {
|
|
41
96
|
fs.mkdirSync(dest, { recursive: true });
|
|
42
97
|
await Promise.all(
|
|
43
98
|
fs.readdirSync(src).map(async (entry) => {
|
|
99
|
+
if (ignore.has(entry)) return;
|
|
44
100
|
const srcPath = path.join(src, entry);
|
|
45
101
|
const destPath = path.join(dest, entry);
|
|
46
102
|
const stat = fs.statSync(srcPath);
|
|
47
103
|
if (stat.isDirectory()) {
|
|
48
|
-
await copyDirAsync(srcPath, destPath);
|
|
104
|
+
await copyDirAsync(srcPath, destPath, ignore);
|
|
49
105
|
} else {
|
|
50
106
|
await copyFileAsync(srcPath, destPath);
|
|
51
107
|
}
|
|
52
108
|
})
|
|
53
109
|
);
|
|
54
110
|
}
|
|
55
|
-
function getDirSize(dir) {
|
|
111
|
+
function getDirSize(dir, ignore = DEFAULT_COPY_IGNORE) {
|
|
56
112
|
let total = 0;
|
|
57
113
|
try {
|
|
58
114
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
115
|
+
if (ignore.has(entry.name)) continue;
|
|
59
116
|
const full = path.join(dir, entry.name);
|
|
60
|
-
if (entry.isDirectory()) total += getDirSize(full);
|
|
117
|
+
if (entry.isDirectory()) total += getDirSize(full, ignore);
|
|
61
118
|
else if (entry.isFile()) total += fs.statSync(full).size;
|
|
62
119
|
}
|
|
63
120
|
} catch {
|
|
@@ -97,9 +154,11 @@ function readJsonFile(filePath) {
|
|
|
97
154
|
return JSON.parse(text);
|
|
98
155
|
}
|
|
99
156
|
export {
|
|
157
|
+
DEFAULT_COPY_IGNORE,
|
|
100
158
|
copyDirAsync,
|
|
101
159
|
copyDirSync,
|
|
102
160
|
copyFileAsync,
|
|
161
|
+
copyFileSyncSafe,
|
|
103
162
|
emptyDirSync,
|
|
104
163
|
getDirSize,
|
|
105
164
|
hashFileAsync,
|
package/dist/fsx.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/fsx.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * fsx.ts \u2014 \u5F02\u6B65/\u6D41\u5F0F\u6587\u4EF6\u64CD\u4F5C (\u907F\u514D\u5927\u6587\u4EF6\u52A0\u8F7D\u8FDB\u5185\u5B58)\n */\n\nimport crypto from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\n\n/**\n * \u6D41\u5F0F\u8BA1\u7B97\u6587\u4EF6\u54C8\u5E0C (sha1 / sha256)\uFF0C\n * \u7528\u4E8E\u5927\u6587\u4EF6 (\u4F8B\u5982 BDS \u53EF\u6267\u884C\u6587\u4EF6 ~80MB)\uFF0C\u4E0D\u4F1A OOM\u3002\n */\nexport async function hashFileAsync(filePath: string, algo: \"sha1\" | \"sha256\" = \"sha256\"): Promise<string> {\n return new Promise((resolve, reject) => {\n const h = crypto.createHash(algo);\n const stream = fs.createReadStream(filePath);\n stream.on(\"data\", (chunk: Buffer | string) => {\n h.update(typeof chunk === \"string\" ? Buffer.from(chunk) : chunk);\n });\n stream.on(\"end\", () => resolve(h.digest(\"hex\").toLowerCase()));\n stream.on(\"error\", reject);\n });\n}\n\n/** \u540C\u6B65\u7248\u672C\uFF08\u7528\u4E8E\u5C0F\u6587\u4EF6\uFF09 */\nexport function hashFileSync(filePath: string, algo: \"sha1\" | \"sha256\" = \"sha256\"): string {\n try {\n const h = crypto.createHash(algo);\n h.update(fs.readFileSync(filePath));\n return h.digest(\"hex\").toLowerCase();\n } catch {\n return \"\";\n }\n}\n\n/** \u540C\u6B65\u6D41\u5F0F\u62F7\u8D1D\u5355\u6587\u4EF6
|
|
5
|
-
"mappings": "AAIA,OAAO,YAAY;AACnB,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,gBAAgB;AAMzB,eAAsB,cAAc,UAAkB,OAA0B,UAA2B;AACzG,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,IAAI,OAAO,WAAW,IAAI;AAChC,UAAM,SAAS,GAAG,iBAAiB,QAAQ;AAC3C,WAAO,GAAG,QAAQ,CAAC,UAA2B;AAC5C,QAAE,OAAO,OAAO,UAAU,WAAW,OAAO,KAAK,KAAK,IAAI,KAAK;AAAA,IACjE,CAAC;AACD,WAAO,GAAG,OAAO,MAAM,QAAQ,EAAE,OAAO,KAAK,EAAE,YAAY,CAAC,CAAC;AAC7D,WAAO,GAAG,SAAS,MAAM;AAAA,EAC3B,CAAC;AACH;AAGO,SAAS,aAAa,UAAkB,OAA0B,UAAkB;AACzF,MAAI;AACF,UAAM,IAAI,OAAO,WAAW,IAAI;AAChC,MAAE,OAAO,GAAG,aAAa,QAAQ,CAAC;AAClC,WAAO,EAAE,OAAO,KAAK,EAAE,YAAY;AAAA,EACrC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,eAAsB,cAAc,KAAa,MAA6B;AAC5E,
|
|
4
|
+
"sourcesContent": ["/**\n * fsx.ts \u2014 \u5F02\u6B65/\u6D41\u5F0F\u6587\u4EF6\u64CD\u4F5C (\u907F\u514D\u5927\u6587\u4EF6\u52A0\u8F7D\u8FDB\u5185\u5B58)\n */\n\nimport crypto from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\n\n/**\n * \u6D41\u5F0F\u8BA1\u7B97\u6587\u4EF6\u54C8\u5E0C (sha1 / sha256)\uFF0C\n * \u7528\u4E8E\u5927\u6587\u4EF6 (\u4F8B\u5982 BDS \u53EF\u6267\u884C\u6587\u4EF6 ~80MB)\uFF0C\u4E0D\u4F1A OOM\u3002\n */\nexport async function hashFileAsync(filePath: string, algo: \"sha1\" | \"sha256\" = \"sha256\"): Promise<string> {\n return new Promise((resolve, reject) => {\n const h = crypto.createHash(algo);\n const stream = fs.createReadStream(filePath);\n stream.on(\"data\", (chunk: Buffer | string) => {\n h.update(typeof chunk === \"string\" ? Buffer.from(chunk) : chunk);\n });\n stream.on(\"end\", () => resolve(h.digest(\"hex\").toLowerCase()));\n stream.on(\"error\", reject);\n });\n}\n\n/** \u540C\u6B65\u7248\u672C\uFF08\u7528\u4E8E\u5C0F\u6587\u4EF6\uFF09 */\nexport function hashFileSync(filePath: string, algo: \"sha1\" | \"sha256\" = \"sha256\"): string {\n try {\n const h = crypto.createHash(algo);\n h.update(fs.readFileSync(filePath));\n return h.digest(\"hex\").toLowerCase();\n } catch {\n return \"\";\n }\n}\n\n/** \u6392\u9664\u65E0\u9700\u5907\u4EFD/\u590D\u5236\u7684\u4ED3\u5E93\u4E0E\u7CFB\u7EDF\u5783\u573E\u5143\u6570\u636E */\nexport const DEFAULT_COPY_IGNORE = new Set([\n \".git\",\n \".svn\",\n \".hg\",\n \"node_modules\",\n \".DS_Store\",\n \"Thumbs.db\",\n \"desktop.ini\",\n]);\n\n/**\n * Windows \u517C\u5BB9\u7684\u5B89\u5168\u5355\u6587\u4EF6\u62F7\u8D1D\u3002\n * \u82E5\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\u5728\u4E14\u5904\u4E8E\u53EA\u8BFB\u72B6\u6001\uFF08\u4F8B\u5982 git objects \u5C5E\u6027\u4E3A\u53EA\u8BFB\uFF09\uFF0C\u5148\u8D4B\u5199\u6743\u9650\u5E76\u8986\u5199\uFF0C\n * \u5E76\u786E\u4FDD\u62F7\u8D1D\u540E\u7684\u76EE\u6807\u6587\u4EF6\u5177\u6709\u53EF\u5199\u6743\u9650\uFF0C\u907F\u514D\u540E\u7EED\u91CD\u590D\u5907\u4EFD\u62A5\u9519\u3002\n */\nexport function copyFileSyncSafe(src: string, dest: string): void {\n try {\n fs.copyFileSync(src, dest);\n } catch (err: unknown) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code === \"EPERM\" || code === \"EACCES\") {\n try {\n if (fs.existsSync(dest)) {\n fs.chmodSync(dest, 0o666);\n fs.unlinkSync(dest);\n }\n fs.copyFileSync(src, dest);\n } catch {\n throw err;\n }\n } else {\n throw err;\n }\n }\n try {\n fs.chmodSync(dest, 0o666);\n } catch {\n /* ignore */\n }\n}\n\n/** \u540C\u6B65\u6D41\u5F0F\u62F7\u8D1D\u5355\u6587\u4EF6\uFF0C\u76EE\u6807\u53EA\u8BFB\u65F6\u81EA\u52A8\u89E3\u9664\u5E76\u8986\u5199 */\nexport async function copyFileAsync(src: string, dest: string): Promise<void> {\n try {\n await pipeline(fs.createReadStream(src), fs.createWriteStream(dest));\n } catch (err: unknown) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code === \"EPERM\" || code === \"EACCES\") {\n try {\n if (fs.existsSync(dest)) {\n fs.chmodSync(dest, 0o666);\n fs.unlinkSync(dest);\n }\n await pipeline(fs.createReadStream(src), fs.createWriteStream(dest));\n } catch {\n throw err;\n }\n } else {\n throw err;\n }\n }\n try {\n fs.chmodSync(dest, 0o666);\n } catch {\n /* ignore */\n }\n}\n\n/** \u540C\u6B65\u76EE\u5F55\u590D\u5236 (\u9012\u5F52)\uFF0C\u9ED8\u8BA4\u6392\u9664 .git / node_modules \u7B49\u5143\u6570\u636E */\nexport function copyDirSync(\n src: string,\n dest: string,\n ignore: Set<string> = DEFAULT_COPY_IGNORE\n): void {\n fs.mkdirSync(dest, { recursive: true });\n for (const entry of fs.readdirSync(src)) {\n if (ignore.has(entry)) continue;\n const srcPath = path.join(src, entry);\n const destPath = path.join(dest, entry);\n if (fs.statSync(srcPath).isDirectory()) {\n copyDirSync(srcPath, destPath, ignore);\n } else {\n copyFileSyncSafe(srcPath, destPath);\n }\n }\n}\n\n/** \u5F02\u6B65\u76EE\u5F55\u590D\u5236 (\u57FA\u4E8E\u6D41)\uFF0C\u9ED8\u8BA4\u6392\u9664 .git / node_modules \u7B49\u5143\u6570\u636E */\nexport async function copyDirAsync(\n src: string,\n dest: string,\n ignore: Set<string> = DEFAULT_COPY_IGNORE\n): Promise<void> {\n fs.mkdirSync(dest, { recursive: true });\n await Promise.all(\n fs.readdirSync(src).map(async (entry) => {\n if (ignore.has(entry)) return;\n const srcPath = path.join(src, entry);\n const destPath = path.join(dest, entry);\n const stat = fs.statSync(srcPath);\n if (stat.isDirectory()) {\n await copyDirAsync(srcPath, destPath, ignore);\n } else {\n await copyFileAsync(srcPath, destPath);\n }\n })\n );\n}\n\n/** \u8BA1\u7B97\u76EE\u5F55\u5927\u5C0F (\u5B57\u8282)\uFF0C\u9ED8\u8BA4\u6392\u9664 .git / node_modules \u7B49\u5143\u6570\u636E */\nexport function getDirSize(dir: string, ignore: Set<string> = DEFAULT_COPY_IGNORE): number {\n let total = 0;\n try {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n if (ignore.has(entry.name)) continue;\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) total += getDirSize(full, ignore);\n else if (entry.isFile()) total += fs.statSync(full).size;\n }\n } catch {}\n return total;\n}\n\n/** \u5F3A\u5236\u6E05\u7A7A\u76EE\u5F55\u5185\u5BB9 (\u4F46\u4FDD\u7559\u76EE\u5F55\u672C\u8EAB) */\nexport function emptyDirSync(dir: string): void {\n if (!fs.existsSync(dir)) return;\n for (const entry of fs.readdirSync(dir)) {\n const full = path.join(dir, entry);\n try {\n const stat = fs.statSync(full);\n if (stat.isDirectory()) fs.rmSync(full, { recursive: true, force: true });\n else fs.unlinkSync(full);\n } catch (e) {\n // \u5FFD\u7565: \u9876\u5C42\u4ECD\u4FDD\u7559\u76EE\u5F55\uFF0C\u5373\u4F7F\u90E8\u5206\u5B50\u9879\u65E0\u6CD5\u5220\u9664\n }\n }\n}\n\n/** \u5B89\u5168\u5220\u9664\u6574\u4E2A\u76EE\u5F55 */\nexport function rmSafe(path: string): void {\n try {\n fs.rmSync(path, { recursive: true, force: true });\n } catch {\n /* ignore */\n }\n}\n\n/** \u5B89\u5168\u5199\u6587\u4EF6 (\u539F\u5B50: \u5199\u4E34\u65F6\u6587\u4EF6 \u2192 rename) */\nexport function writeFileSafe(filePath: string, data: string | Buffer): void {\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n const tmp = `${filePath}.${process.pid}.tmp`;\n fs.writeFileSync(tmp, data);\n fs.renameSync(tmp, filePath);\n}\n\n/**\n * \u8BFB JSON \u6587\u4EF6\uFF08utf8\uFF09\u3002\n * \u82E5\u6587\u4EF6\u4EE5 UTF-8 BOM\uFF08U+FEFF\uFF09\u5F00\u5934\u5219\u5265\u79BB\u540E\u518D\u89E3\u6790\uFF08\u8BB0\u4E8B\u672C\u300CUTF-8 with BOM\u300D\uFF09\u3002\n */\nexport function readJsonFile<T = unknown>(filePath: string): T {\n let text = fs.readFileSync(filePath, \"utf8\");\n if (text.length > 0 && text.charCodeAt(0) === 0xfeff) {\n text = text.slice(1);\n }\n return JSON.parse(text) as T;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["path"]
|
|
7
7
|
}
|
package/dist/fsx.test.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { describe, it } from "node:test";
|
|
6
|
+
import { copyDirSync, copyFileSyncSafe, getDirSize, rmSafe } from "./fsx.js";
|
|
7
|
+
describe("fsx: copyDirSync & copyFileSyncSafe", () => {
|
|
8
|
+
it("copyDirSync \u81EA\u52A8\u5FFD\u7565 .git / node_modules \u7B49\u5143\u6570\u636E", () => {
|
|
9
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "sfmc-fsx-test-"));
|
|
10
|
+
try {
|
|
11
|
+
const src = path.join(tmp, "src");
|
|
12
|
+
const dest = path.join(tmp, "dest");
|
|
13
|
+
fs.mkdirSync(path.join(src, ".git", "objects"), { recursive: true });
|
|
14
|
+
fs.mkdirSync(path.join(src, "scripts"), { recursive: true });
|
|
15
|
+
fs.writeFileSync(path.join(src, ".git", "HEAD"), "ref: refs/heads/main\n");
|
|
16
|
+
fs.writeFileSync(path.join(src, "scripts", "main.js"), "console.log('hello');\n");
|
|
17
|
+
fs.writeFileSync(path.join(src, ".DS_Store"), "junk");
|
|
18
|
+
copyDirSync(src, dest);
|
|
19
|
+
assert.ok(fs.existsSync(path.join(dest, "scripts", "main.js")));
|
|
20
|
+
assert.ok(!fs.existsSync(path.join(dest, ".git")));
|
|
21
|
+
assert.ok(!fs.existsSync(path.join(dest, ".DS_Store")));
|
|
22
|
+
} finally {
|
|
23
|
+
rmSafe(tmp);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
it("copyFileSyncSafe \u80FD\u591F\u5B89\u5168\u8986\u5199 Windows \u5E73\u53F0\u4E0B\u7684\u53EA\u8BFB\u53EA\u8BFB\u5C5E\u6027\u6587\u4EF6", () => {
|
|
27
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "sfmc-fsx-ro-"));
|
|
28
|
+
try {
|
|
29
|
+
const src = path.join(tmp, "src.txt");
|
|
30
|
+
const dest = path.join(tmp, "dest.txt");
|
|
31
|
+
fs.writeFileSync(src, "version 2");
|
|
32
|
+
fs.writeFileSync(dest, "version 1");
|
|
33
|
+
fs.chmodSync(dest, 292);
|
|
34
|
+
copyFileSyncSafe(src, dest);
|
|
35
|
+
assert.equal(fs.readFileSync(dest, "utf8"), "version 2");
|
|
36
|
+
fs.writeFileSync(src, "version 3");
|
|
37
|
+
copyFileSyncSafe(src, dest);
|
|
38
|
+
assert.equal(fs.readFileSync(dest, "utf8"), "version 3");
|
|
39
|
+
} finally {
|
|
40
|
+
try {
|
|
41
|
+
fs.chmodSync(path.join(tmp, "dest.txt"), 438);
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
rmSafe(tmp);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
it("getDirSize \u5FFD\u7565 .git \u76EE\u5F55\u5927\u5C0F", () => {
|
|
48
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "sfmc-fsx-size-"));
|
|
49
|
+
try {
|
|
50
|
+
fs.mkdirSync(path.join(tmp, ".git"), { recursive: true });
|
|
51
|
+
fs.writeFileSync(path.join(tmp, ".git", "pack"), "x".repeat(1e4));
|
|
52
|
+
fs.writeFileSync(path.join(tmp, "data.txt"), "hello");
|
|
53
|
+
const size = getDirSize(tmp);
|
|
54
|
+
assert.equal(size, 5);
|
|
55
|
+
} finally {
|
|
56
|
+
rmSafe(tmp);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=fsx.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/fsx.test.ts"],
|
|
4
|
+
"sourcesContent": ["import assert from \"node:assert/strict\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { describe, it } from \"node:test\";\nimport { copyDirSync, copyFileSyncSafe, getDirSize, rmSafe } from \"./fsx.js\";\n\ndescribe(\"fsx: copyDirSync & copyFileSyncSafe\", () => {\n it(\"copyDirSync \u81EA\u52A8\u5FFD\u7565 .git / node_modules \u7B49\u5143\u6570\u636E\", () => {\n const tmp = fs.mkdtempSync(path.join(os.tmpdir(), \"sfmc-fsx-test-\"));\n try {\n const src = path.join(tmp, \"src\");\n const dest = path.join(tmp, \"dest\");\n fs.mkdirSync(path.join(src, \".git\", \"objects\"), { recursive: true });\n fs.mkdirSync(path.join(src, \"scripts\"), { recursive: true });\n fs.writeFileSync(path.join(src, \".git\", \"HEAD\"), \"ref: refs/heads/main\\n\");\n fs.writeFileSync(path.join(src, \"scripts\", \"main.js\"), \"console.log('hello');\\n\");\n fs.writeFileSync(path.join(src, \".DS_Store\"), \"junk\");\n\n copyDirSync(src, dest);\n\n assert.ok(fs.existsSync(path.join(dest, \"scripts\", \"main.js\")));\n assert.ok(!fs.existsSync(path.join(dest, \".git\")));\n assert.ok(!fs.existsSync(path.join(dest, \".DS_Store\")));\n } finally {\n rmSafe(tmp);\n }\n });\n\n it(\"copyFileSyncSafe \u80FD\u591F\u5B89\u5168\u8986\u5199 Windows \u5E73\u53F0\u4E0B\u7684\u53EA\u8BFB\u53EA\u8BFB\u5C5E\u6027\u6587\u4EF6\", () => {\n const tmp = fs.mkdtempSync(path.join(os.tmpdir(), \"sfmc-fsx-ro-\"));\n try {\n const src = path.join(tmp, \"src.txt\");\n const dest = path.join(tmp, \"dest.txt\");\n fs.writeFileSync(src, \"version 2\");\n fs.writeFileSync(dest, \"version 1\");\n // \u8BBE\u7F6E\u76EE\u6807\u6587\u4EF6\u53EA\u8BFB (Windows 0444)\n fs.chmodSync(dest, 0o444);\n\n // copyFileSyncSafe \u5E94\u5F53\u6355\u83B7 EPERM \u5E76\u8986\u5199\u6210\u529F\uFF0C\u800C\u4E0D\u662F\u5D29\u6E83\n copyFileSyncSafe(src, dest);\n\n assert.equal(fs.readFileSync(dest, \"utf8\"), \"version 2\");\n\n // \u518D\u6B21\u8986\u5199\uFF0C\u786E\u4FDD\u4E0D\u4F1A\u9057\u7559\u53EA\u8BFB\u963B\u65AD\u4E0B\u4E00\u6B21\u5907\u4EFD\n fs.writeFileSync(src, \"version 3\");\n copyFileSyncSafe(src, dest);\n assert.equal(fs.readFileSync(dest, \"utf8\"), \"version 3\");\n } finally {\n try {\n fs.chmodSync(path.join(tmp, \"dest.txt\"), 0o666);\n } catch {}\n rmSafe(tmp);\n }\n });\n\n it(\"getDirSize \u5FFD\u7565 .git \u76EE\u5F55\u5927\u5C0F\", () => {\n const tmp = fs.mkdtempSync(path.join(os.tmpdir(), \"sfmc-fsx-size-\"));\n try {\n fs.mkdirSync(path.join(tmp, \".git\"), { recursive: true });\n fs.writeFileSync(path.join(tmp, \".git\", \"pack\"), \"x\".repeat(10000));\n fs.writeFileSync(path.join(tmp, \"data.txt\"), \"hello\");\n\n const size = getDirSize(tmp);\n assert.equal(size, 5);\n } finally {\n rmSafe(tmp);\n }\n });\n});\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,YAAY;AACnB,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,UAAU,UAAU;AAC7B,SAAS,aAAa,kBAAkB,YAAY,cAAc;AAElE,SAAS,uCAAuC,MAAM;AACpD,KAAG,qFAA6C,MAAM;AACpD,UAAM,MAAM,GAAG,YAAY,KAAK,KAAK,GAAG,OAAO,GAAG,gBAAgB,CAAC;AACnE,QAAI;AACF,YAAM,MAAM,KAAK,KAAK,KAAK,KAAK;AAChC,YAAM,OAAO,KAAK,KAAK,KAAK,MAAM;AAClC,SAAG,UAAU,KAAK,KAAK,KAAK,QAAQ,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AACnE,SAAG,UAAU,KAAK,KAAK,KAAK,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AAC3D,SAAG,cAAc,KAAK,KAAK,KAAK,QAAQ,MAAM,GAAG,wBAAwB;AACzE,SAAG,cAAc,KAAK,KAAK,KAAK,WAAW,SAAS,GAAG,yBAAyB;AAChF,SAAG,cAAc,KAAK,KAAK,KAAK,WAAW,GAAG,MAAM;AAEpD,kBAAY,KAAK,IAAI;AAErB,aAAO,GAAG,GAAG,WAAW,KAAK,KAAK,MAAM,WAAW,SAAS,CAAC,CAAC;AAC9D,aAAO,GAAG,CAAC,GAAG,WAAW,KAAK,KAAK,MAAM,MAAM,CAAC,CAAC;AACjD,aAAO,GAAG,CAAC,GAAG,WAAW,KAAK,KAAK,MAAM,WAAW,CAAC,CAAC;AAAA,IACxD,UAAE;AACA,aAAO,GAAG;AAAA,IACZ;AAAA,EACF,CAAC;AAED,KAAG,0IAAgD,MAAM;AACvD,UAAM,MAAM,GAAG,YAAY,KAAK,KAAK,GAAG,OAAO,GAAG,cAAc,CAAC;AACjE,QAAI;AACF,YAAM,MAAM,KAAK,KAAK,KAAK,SAAS;AACpC,YAAM,OAAO,KAAK,KAAK,KAAK,UAAU;AACtC,SAAG,cAAc,KAAK,WAAW;AACjC,SAAG,cAAc,MAAM,WAAW;AAElC,SAAG,UAAU,MAAM,GAAK;AAGxB,uBAAiB,KAAK,IAAI;AAE1B,aAAO,MAAM,GAAG,aAAa,MAAM,MAAM,GAAG,WAAW;AAGvD,SAAG,cAAc,KAAK,WAAW;AACjC,uBAAiB,KAAK,IAAI;AAC1B,aAAO,MAAM,GAAG,aAAa,MAAM,MAAM,GAAG,WAAW;AAAA,IACzD,UAAE;AACA,UAAI;AACF,WAAG,UAAU,KAAK,KAAK,KAAK,UAAU,GAAG,GAAK;AAAA,MAChD,QAAQ;AAAA,MAAC;AACT,aAAO,GAAG;AAAA,IACZ;AAAA,EACF,CAAC;AAED,KAAG,yDAA2B,MAAM;AAClC,UAAM,MAAM,GAAG,YAAY,KAAK,KAAK,GAAG,OAAO,GAAG,gBAAgB,CAAC;AACnE,QAAI;AACF,SAAG,UAAU,KAAK,KAAK,KAAK,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;AACxD,SAAG,cAAc,KAAK,KAAK,KAAK,QAAQ,MAAM,GAAG,IAAI,OAAO,GAAK,CAAC;AAClE,SAAG,cAAc,KAAK,KAAK,KAAK,UAAU,GAAG,OAAO;AAEpD,YAAM,OAAO,WAAW,GAAG;AAC3B,aAAO,MAAM,MAAM,CAAC;AAAA,IACtB,UAAE;AACA,aAAO,GAAG;AAAA,IACZ;AAAA,EACF,CAAC;AACH,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/rollback.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rollback.d.ts","sourceRoot":"","sources":["../src/rollback.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,
|
|
1
|
+
{"version":3,"file":"rollback.d.ts","sourceRoot":"","sources":["../src/rollback.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,EAAiC,UAAU,EAAgB,MAAM,UAAU,CAAC;AAGnF,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,yBAAyB;AACzB,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAOhE;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAM1C;AAED,wBAAgB,kBAAkB,IAAI,cAAc,GAAG,IAAI,CAE1D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CA8B3F;AAED,mDAAmD;AACnD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAMzG;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
package/dist/rollback.js
CHANGED
|
@@ -2,7 +2,7 @@ import { readJson, writeJson } from "@sfmc-bds/sdk/node/config";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { ROLLBACK_MARKER } from "./paths.js";
|
|
5
|
-
import { copyDirSync, getDirSize, emptyDirSync } from "./fsx.js";
|
|
5
|
+
import { copyDirSync, copyFileSyncSafe, getDirSize, emptyDirSync } from "./fsx.js";
|
|
6
6
|
import { log } from "./log.js";
|
|
7
7
|
function writeRollbackMarker(marker) {
|
|
8
8
|
try {
|
|
@@ -41,7 +41,7 @@ function rollbackFromBackup(marker) {
|
|
|
41
41
|
if (fs.existsSync(dest)) emptyDirSync(dest);
|
|
42
42
|
copyDirSync(src, dest);
|
|
43
43
|
} else {
|
|
44
|
-
|
|
44
|
+
copyFileSyncSafe(src, dest);
|
|
45
45
|
}
|
|
46
46
|
log.info(`[\u56DE\u6EDA] \u5DF2\u6062\u590D: ${item}`);
|
|
47
47
|
} catch (e) {
|
package/dist/rollback.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/rollback.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * rollback.ts \u2014 \u56DE\u6EDA\u652F\u6301\n *\n * \u5728\u66F4\u65B0\u5F00\u59CB\u524D\u8BB0\u5F55: \u5F53\u524D BDS \u76EE\u5F55 + \u5907\u4EFD\u76EE\u5F55\u3002\n * \u4E3B\u6D41\u7A0B\u4EFB\u610F\u9636\u6BB5\u5931\u8D25\u65F6\uFF0C\u5C1D\u8BD5\u53CD\u5411\u6062\u590D preserves \u5230 BDS_PATH\uFF0C\n * \u7136\u540E\u4ECE\u5907\u4EFD\u91CD\u65B0\u653E\u56DE preserve \u5185\u5BB9\uFF0C\u6062\u590D\u65E7\u914D\u7F6E\u3002\n *\n * \u6301\u4E45\u5316: <SFMC_ROOT>/.sfmc/last_update_rollback.json \u7528\u4E8E\u8DE8\u8FDB\u7A0B\u8BB0\u5F55\u3002\n */\n\nimport { readJson, writeJson } from \"@sfmc-bds/sdk/node/config\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { ROLLBACK_MARKER } from \"./paths.js\";\nimport { copyDirSync, getDirSize, emptyDirSync } from \"./fsx.js\";\nimport { log } from \"./log.js\";\n\nexport interface RollbackMarker {\n timestamp: number;\n bds_path: string;\n backup_dir: string;\n preserve: string[];\n previous_version: string;\n}\n\n/** \u5728\u5907\u4EFD\u5B8C\u6210\u540E\u8C03\u7528: \u8BB0\u5F55\u5FEB\u7167\u6307\u793A\u7B26\u3002 */\nexport function writeRollbackMarker(marker: RollbackMarker): void {\n try {\n writeJson(ROLLBACK_MARKER, marker);\n log.info(`[\u56DE\u6EDA] \u5DF2\u8BB0\u5F55\u56DE\u6EDA\u6807\u8BB0 -> ${ROLLBACK_MARKER}`);\n } catch (e) {\n log.warn(`[\u56DE\u6EDA] \u65E0\u6CD5\u5199\u5165\u6807\u8BB0: ${(e as Error).message}`);\n }\n}\n\nexport function clearRollbackMarker(): void {\n try {\n if (fs.existsSync(ROLLBACK_MARKER)) fs.unlinkSync(ROLLBACK_MARKER);\n } catch {\n /* ignore */\n }\n}\n\nexport function readRollbackMarker(): RollbackMarker | null {\n return readJson<RollbackMarker>(ROLLBACK_MARKER) ?? null;\n}\n\n/**\n * \u7D27\u6025\u56DE\u6EDA: \u628A preserve \u9879\u4ECE backup_dir \u62F7\u56DE bds_path\u3002\n * \u7528\u6CD5: \u5728 catch \u5206\u652F\u8C03\u7528\uFF0C\u5982\u679C\u4E3B\u6D41\u7A0B\u5931\u8D25\u5BFC\u81F4 BDS \u76EE\u5F55\u7834\u574F\u3002\n */\nexport function rollbackFromBackup(marker: RollbackMarker): { ok: boolean; reason?: string } {\n const { bds_path, backup_dir, preserve } = marker;\n if (!fs.existsSync(backup_dir)) {\n return { ok: false, reason: `\u5907\u4EFD\u76EE\u5F55\u4E0D\u5B58\u5728: ${backup_dir}` };\n }\n log.warn(`[\u56DE\u6EDA] \u5F00\u59CB\u6062\u590D ${preserve.length} \u9879\u5230 ${bds_path}`);\n for (const item of preserve) {\n const src = path.join(backup_dir, item);\n const dest = path.join(bds_path, item);\n if (!fs.existsSync(src)) {\n log.warn(`[\u56DE\u6EDA] \u8DF3\u8FC7 (\u5907\u4EFD\u4E2D\u4E0D\u5B58\u5728): ${item}`);\n continue;\n }\n try {\n // \u5148\u786E\u4FDD\u76EE\u6807\u76EE\u5F55\u5B58\u5728 (BDS \u76EE\u5F55\u53EF\u80FD\u5DF2\u88AB\u89E3\u538B\u8986\u76D6\u4E3A\u7A7A)\n const destDir = path.dirname(dest);\n if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });\n if (fs.statSync(src).isDirectory()) {\n // \u5148\u5220\u9664\u76EE\u6807\uFF0C\u518D\u590D\u5236\u76EE\u5F55\n if (fs.existsSync(dest)) emptyDirSync(dest);\n copyDirSync(src, dest);\n } else {\n
|
|
5
|
-
"mappings": "AAUA,SAAS,UAAU,iBAAiB;AACpC,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,uBAAuB;AAChC,SAAS,aAAa,YAAY,oBAAoB;
|
|
4
|
+
"sourcesContent": ["/**\n * rollback.ts \u2014 \u56DE\u6EDA\u652F\u6301\n *\n * \u5728\u66F4\u65B0\u5F00\u59CB\u524D\u8BB0\u5F55: \u5F53\u524D BDS \u76EE\u5F55 + \u5907\u4EFD\u76EE\u5F55\u3002\n * \u4E3B\u6D41\u7A0B\u4EFB\u610F\u9636\u6BB5\u5931\u8D25\u65F6\uFF0C\u5C1D\u8BD5\u53CD\u5411\u6062\u590D preserves \u5230 BDS_PATH\uFF0C\n * \u7136\u540E\u4ECE\u5907\u4EFD\u91CD\u65B0\u653E\u56DE preserve \u5185\u5BB9\uFF0C\u6062\u590D\u65E7\u914D\u7F6E\u3002\n *\n * \u6301\u4E45\u5316: <SFMC_ROOT>/.sfmc/last_update_rollback.json \u7528\u4E8E\u8DE8\u8FDB\u7A0B\u8BB0\u5F55\u3002\n */\n\nimport { readJson, writeJson } from \"@sfmc-bds/sdk/node/config\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { ROLLBACK_MARKER } from \"./paths.js\";\nimport { copyDirSync, copyFileSyncSafe, getDirSize, emptyDirSync } from \"./fsx.js\";\nimport { log } from \"./log.js\";\n\nexport interface RollbackMarker {\n timestamp: number;\n bds_path: string;\n backup_dir: string;\n preserve: string[];\n previous_version: string;\n}\n\n/** \u5728\u5907\u4EFD\u5B8C\u6210\u540E\u8C03\u7528: \u8BB0\u5F55\u5FEB\u7167\u6307\u793A\u7B26\u3002 */\nexport function writeRollbackMarker(marker: RollbackMarker): void {\n try {\n writeJson(ROLLBACK_MARKER, marker);\n log.info(`[\u56DE\u6EDA] \u5DF2\u8BB0\u5F55\u56DE\u6EDA\u6807\u8BB0 -> ${ROLLBACK_MARKER}`);\n } catch (e) {\n log.warn(`[\u56DE\u6EDA] \u65E0\u6CD5\u5199\u5165\u6807\u8BB0: ${(e as Error).message}`);\n }\n}\n\nexport function clearRollbackMarker(): void {\n try {\n if (fs.existsSync(ROLLBACK_MARKER)) fs.unlinkSync(ROLLBACK_MARKER);\n } catch {\n /* ignore */\n }\n}\n\nexport function readRollbackMarker(): RollbackMarker | null {\n return readJson<RollbackMarker>(ROLLBACK_MARKER) ?? null;\n}\n\n/**\n * \u7D27\u6025\u56DE\u6EDA: \u628A preserve \u9879\u4ECE backup_dir \u62F7\u56DE bds_path\u3002\n * \u7528\u6CD5: \u5728 catch \u5206\u652F\u8C03\u7528\uFF0C\u5982\u679C\u4E3B\u6D41\u7A0B\u5931\u8D25\u5BFC\u81F4 BDS \u76EE\u5F55\u7834\u574F\u3002\n */\nexport function rollbackFromBackup(marker: RollbackMarker): { ok: boolean; reason?: string } {\n const { bds_path, backup_dir, preserve } = marker;\n if (!fs.existsSync(backup_dir)) {\n return { ok: false, reason: `\u5907\u4EFD\u76EE\u5F55\u4E0D\u5B58\u5728: ${backup_dir}` };\n }\n log.warn(`[\u56DE\u6EDA] \u5F00\u59CB\u6062\u590D ${preserve.length} \u9879\u5230 ${bds_path}`);\n for (const item of preserve) {\n const src = path.join(backup_dir, item);\n const dest = path.join(bds_path, item);\n if (!fs.existsSync(src)) {\n log.warn(`[\u56DE\u6EDA] \u8DF3\u8FC7 (\u5907\u4EFD\u4E2D\u4E0D\u5B58\u5728): ${item}`);\n continue;\n }\n try {\n // \u5148\u786E\u4FDD\u76EE\u6807\u76EE\u5F55\u5B58\u5728 (BDS \u76EE\u5F55\u53EF\u80FD\u5DF2\u88AB\u89E3\u538B\u8986\u76D6\u4E3A\u7A7A)\n const destDir = path.dirname(dest);\n if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });\n if (fs.statSync(src).isDirectory()) {\n // \u5148\u5220\u9664\u76EE\u6807\uFF0C\u518D\u590D\u5236\u76EE\u5F55\n if (fs.existsSync(dest)) emptyDirSync(dest);\n copyDirSync(src, dest);\n } else {\n copyFileSyncSafe(src, dest);\n }\n log.info(`[\u56DE\u6EDA] \u5DF2\u6062\u590D: ${item}`);\n } catch (e) {\n log.warn(`[\u56DE\u6EDA] \u6062\u590D\u5931\u8D25 ${item}: ${(e as Error).message}`);\n }\n }\n return { ok: true };\n}\n\n/** \u68C0\u67E5 BDS_PATH \u4E2D\u5173\u952E\u6587\u4EF6\u662F\u5426\u90FD\u8FD8\u5728 (\u66F4\u65B0\u540E\u5B8C\u6574\u6027 sanity check) */\nexport function verifyBdsInstall(bds_path: string, expected: string[]): { ok: boolean; missing: string[] } {\n const missing: string[] = [];\n for (const f of expected) {\n if (!fs.existsSync(path.join(bds_path, f))) missing.push(f);\n }\n return { ok: missing.length === 0, missing };\n}\n\nexport { getDirSize };\n"],
|
|
5
|
+
"mappings": "AAUA,SAAS,UAAU,iBAAiB;AACpC,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,uBAAuB;AAChC,SAAS,aAAa,kBAAkB,YAAY,oBAAoB;AACxE,SAAS,WAAW;AAWb,SAAS,oBAAoB,QAA8B;AAChE,MAAI;AACF,cAAU,iBAAiB,MAAM;AACjC,QAAI,KAAK,gEAAmB,eAAe,EAAE;AAAA,EAC/C,SAAS,GAAG;AACV,QAAI,KAAK,wDAAiB,EAAY,OAAO,EAAE;AAAA,EACjD;AACF;AAEO,SAAS,sBAA4B;AAC1C,MAAI;AACF,QAAI,GAAG,WAAW,eAAe,EAAG,IAAG,WAAW,eAAe;AAAA,EACnE,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,qBAA4C;AAC1D,SAAO,SAAyB,eAAe,KAAK;AACtD;AAMO,SAAS,mBAAmB,QAA0D;AAC3F,QAAM,EAAE,UAAU,YAAY,SAAS,IAAI;AAC3C,MAAI,CAAC,GAAG,WAAW,UAAU,GAAG;AAC9B,WAAO,EAAE,IAAI,OAAO,QAAQ,+CAAY,UAAU,GAAG;AAAA,EACvD;AACA,MAAI,KAAK,2CAAa,SAAS,MAAM,iBAAO,QAAQ,EAAE;AACtD,aAAW,QAAQ,UAAU;AAC3B,UAAM,MAAM,KAAK,KAAK,YAAY,IAAI;AACtC,UAAM,OAAO,KAAK,KAAK,UAAU,IAAI;AACrC,QAAI,CAAC,GAAG,WAAW,GAAG,GAAG;AACvB,UAAI,KAAK,uEAAqB,IAAI,EAAE;AACpC;AAAA,IACF;AACA,QAAI;AAEF,YAAM,UAAU,KAAK,QAAQ,IAAI;AACjC,UAAI,CAAC,GAAG,WAAW,OAAO,EAAG,IAAG,UAAU,SAAS,EAAE,WAAW,KAAK,CAAC;AACtE,UAAI,GAAG,SAAS,GAAG,EAAE,YAAY,GAAG;AAElC,YAAI,GAAG,WAAW,IAAI,EAAG,cAAa,IAAI;AAC1C,oBAAY,KAAK,IAAI;AAAA,MACvB,OAAO;AACL,yBAAiB,KAAK,IAAI;AAAA,MAC5B;AACA,UAAI,KAAK,sCAAa,IAAI,EAAE;AAAA,IAC9B,SAAS,GAAG;AACV,UAAI,KAAK,2CAAa,IAAI,KAAM,EAAY,OAAO,EAAE;AAAA,IACvD;AAAA,EACF;AACA,SAAO,EAAE,IAAI,KAAK;AACpB;AAGO,SAAS,iBAAiB,UAAkB,UAAwD;AACzG,QAAM,UAAoB,CAAC;AAC3B,aAAW,KAAK,UAAU;AACxB,QAAI,CAAC,GAAG,WAAW,KAAK,KAAK,UAAU,CAAC,CAAC,EAAG,SAAQ,KAAK,CAAC;AAAA,EAC5D;AACA,SAAO,EAAE,IAAI,QAAQ,WAAW,GAAG,QAAQ;AAC7C;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sfmc-bds/bds-tools",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.11",
|
|
4
4
|
"description": "SFMC BDS auto-updater, lifecycle manager, and behavior-pack assembler",
|
|
5
5
|
"author": "Shiroha7z",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"jszip": "^3.10.1",
|
|
52
52
|
"node-html-parser": "^9.0.0",
|
|
53
|
-
"@sfmc-bds/sdk": "0.2.0-beta.
|
|
53
|
+
"@sfmc-bds/sdk": "0.2.0-beta.11"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@sfmc-bds/tools": "0.2.0-beta.
|
|
56
|
+
"@sfmc-bds/tools": "0.2.0-beta.12"
|
|
57
57
|
},
|
|
58
58
|
"publishConfig": {
|
|
59
59
|
"access": "public",
|