@meadmin/cli 1.3.7 → 1.3.8
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/commanders/index.js +4 -2
- package/dist/commanders/update.d.ts +3 -0
- package/dist/commanders/update.js +132 -0
- package/dist/update/backup.d.ts +21 -0
- package/dist/update/backup.js +120 -0
- package/dist/update/merge.d.ts +7 -0
- package/dist/update/merge.js +432 -0
- package/dist/update/planner.d.ts +17 -0
- package/dist/update/planner.js +116 -0
- package/dist/update/rules.d.ts +12 -0
- package/dist/update/rules.js +55 -0
- package/dist/update/source.d.ts +10 -0
- package/dist/update/source.js +750 -0
- package/dist/update/sql.d.ts +12 -0
- package/dist/update/sql.js +589 -0
- package/dist/update/template.d.ts +20 -0
- package/dist/update/template.js +147 -0
- package/package.json +4 -4
- package/template/update.defaults.json +29 -0
package/dist/commanders/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { setEnv } from '../utils/env.js';
|
|
2
2
|
import { addonInit } from './addon.js';
|
|
3
|
+
import { compressInit } from './compress.js';
|
|
3
4
|
import { crudInit } from './crud.js';
|
|
4
5
|
import { syncInit } from './sync.js';
|
|
5
|
-
import {
|
|
6
|
+
import { updateInit } from './update.js';
|
|
6
7
|
export const init = (program) => {
|
|
7
8
|
program.option('-m <char>', 'env环境变量').hook('preAction', (thisCommand) => {
|
|
8
9
|
setEnv(thisCommand.opts().m);
|
|
@@ -11,5 +12,6 @@ export const init = (program) => {
|
|
|
11
12
|
crudInit(program);
|
|
12
13
|
addonInit(program);
|
|
13
14
|
compressInit(program);
|
|
15
|
+
updateInit(program);
|
|
14
16
|
};
|
|
15
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
17
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29tbWFuZGVycy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDekMsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUN2QyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzdDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDckMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUNyQyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3pDLE1BQU0sQ0FBQyxNQUFNLElBQUksR0FBRyxDQUFDLE9BQWdCLEVBQUUsRUFBRTtJQUN2QyxPQUFPLENBQUMsTUFBTSxDQUFDLFdBQVcsRUFBRSxTQUFTLENBQUMsQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUMsV0FBVyxFQUFFLEVBQUU7UUFDdkUsTUFBTSxDQUFDLFdBQVcsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMvQixDQUFDLENBQUMsQ0FBQztJQUNILFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUNsQixRQUFRLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDbEIsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ25CLFlBQVksQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUN0QixVQUFVLENBQUMsT0FBTyxDQUFDLENBQUM7QUFDdEIsQ0FBQyxDQUFDIn0=
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
export declare const databaseReminder = "\n\u540E\u7EED\u5FC5\u987B\u624B\u52A8\u5904\u7406\uFF1A\n1. \u6587\u4EF6\u5347\u7EA7\u4F1A\u5C06\u6A21\u677F\u65B0\u589E\u4F9D\u8D56\u5199\u5165\u6E05\u5355\uFF1B\u5347\u7EA7\u540E\u8BF7\u6838\u5BF9\u4EBA\u5DE5\u5408\u5E76\u9879\uFF0C\u624B\u52A8\u6267\u884C pnpm install\uFF08\u5982\u5C1A\u672A\u5B89\u88C5\uFF09\uFF0C\u518D\u66F4\u65B0\u540E\u7AEF\u7F16\u8BD1\u4EA7\u7269\u3002\n2. \u5907\u4EFD\u6570\u636E\u5E93\u540E\u624B\u52A8\u6267\u884C\uFF1Apnpm exec meadmin sync * \u540C\u6B65\u6570\u636E\u5E93\u8868\u7ED3\u6784\u3002\n3. \u8C28\u614E\u6267\u884C update.sql\uFF01\u5EFA\u8BAE\u81EA\u884C\u6BD4\u8F83\u7248\u672CSQL\u3001update.sql\u4E0E\u73B0\u6709\u6570\u636E\u5E93\u540E\u518D\u6267\u884C\uFF0C\u5148\u5728\u6D4B\u8BD5\u73AF\u5883\u9A8C\u8BC1\u3002\n4. \u6570\u636E\u5904\u7406\u5B8C\u6210\u540E\uFF0C\u624B\u52A8\u4F9D\u6B21\u6267\u884C\u83DC\u5355\u3001\u7EC4\u7EC7\u3001\u89D2\u8272\u7684\u201C\u4FEE\u590D\u6811\u5173\u7CFB\u201D\u3002\n5. \u5237\u65B0/\u91CD\u65B0\u767B\u5F55\u5E76\u6838\u5BF9\u83DC\u5355\u6388\u6743\u3001\u7EC4\u7EC7\u6570\u636E\u8303\u56F4\u548C\u4E1A\u52A1\u529F\u80FD\u3002\n\u672C\u5DE5\u5177\u4E0D\u81EA\u52A8\u540C\u6B65\u8868\u7ED3\u6784\u3001\u6267\u884CSQL\u6216\u4FEE\u590D\u6811\u5173\u7CFB\u3002\u539F\u59CB\u7248\u672CSQL\u4E0D\u80FD\u76F4\u63A5\u5B8C\u6574\u5BFC\u5165\u5DF2\u6709\u4E1A\u52A1\u5E93\u3002\n\u6587\u4EF6\u56DE\u6EDA\u4E0D\u56DE\u6EDA\u6570\u636E\u5E93\u3002\u65B0\u589E\u6388\u6743\u5173\u8054\u4E5F\u53EF\u80FD\u5F71\u54CD\u5DF2\u6709\u89D2\u8272\u6743\u9650\u3002\n";
|
|
3
|
+
export declare function updateInit(program: Command): void;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { basename, join, resolve } from 'node:path';
|
|
5
|
+
import { stdin, stdout } from 'node:process';
|
|
6
|
+
import { createInterface } from 'node:readline/promises';
|
|
7
|
+
import { applyPlan, rollback, saveRecord } from '../update/backup.js';
|
|
8
|
+
import { makePlan } from '../update/planner.js';
|
|
9
|
+
import { validateConfig } from '../update/rules.js';
|
|
10
|
+
import { compareVersions, currentVersion, downloadTemplate, registryManifest, selectVersion } from '../update/template.js';
|
|
11
|
+
import { Log } from '../utils/log.js';
|
|
12
|
+
async function confirm(message) {
|
|
13
|
+
if (!stdin.isTTY || !stdout.isTTY)
|
|
14
|
+
throw new Error('需要交互式终端确认;非交互场景请使用 --dry-run');
|
|
15
|
+
const prompt = createInterface({ input: stdin, output: stdout });
|
|
16
|
+
try {
|
|
17
|
+
return (await prompt.question(message + ' [y/N] ')).trim().toLowerCase() === 'y';
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
prompt.close();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export const databaseReminder = `
|
|
24
|
+
后续必须手动处理:
|
|
25
|
+
1. 文件升级会将模板新增依赖写入清单;升级后请核对人工合并项,手动执行 pnpm install(如尚未安装),再更新后端编译产物。
|
|
26
|
+
2. 备份数据库后手动执行:pnpm exec meadmin sync * 同步数据库表结构。
|
|
27
|
+
3. 谨慎执行 update.sql!建议自行比较版本SQL、update.sql与现有数据库后再执行,先在测试环境验证。
|
|
28
|
+
4. 数据处理完成后,手动依次执行菜单、组织、角色的“修复树关系”。
|
|
29
|
+
5. 刷新/重新登录并核对菜单授权、组织数据范围和业务功能。
|
|
30
|
+
本工具不自动同步表结构、执行SQL或修复树关系。原始版本SQL不能直接完整导入已有业务库。
|
|
31
|
+
文件回滚不回滚数据库。新增授权关联也可能影响已有角色权限。
|
|
32
|
+
`;
|
|
33
|
+
export function updateInit(program) {
|
|
34
|
+
program
|
|
35
|
+
.command('update')
|
|
36
|
+
.description('基于官方模板升级框架(需确认覆盖,不自动操作数据库)')
|
|
37
|
+
.option('--version <version>', '指定准确的目标稳定版本')
|
|
38
|
+
.option('--config <path>', '自定义跳过规则文件')
|
|
39
|
+
.option('--registry <url>', 'npm registry 地址')
|
|
40
|
+
.option('--dry-run', '仅预览,不写入项目')
|
|
41
|
+
.option('--rollback <id>', '恢复升级批次的文件(不恢复数据库)')
|
|
42
|
+
.action(async (options) => {
|
|
43
|
+
try {
|
|
44
|
+
const root = process.cwd();
|
|
45
|
+
if (!existsSync(join(root, 'package.json')))
|
|
46
|
+
throw new Error('请在目标项目根目录运行');
|
|
47
|
+
if (options.rollback) {
|
|
48
|
+
if (options.version || options.config || options.dryRun)
|
|
49
|
+
throw new Error('--rollback 不能与升级选项组合');
|
|
50
|
+
if (!/^[a-f0-9-]{36}$/.test(options.rollback))
|
|
51
|
+
throw new Error('备份ID无效');
|
|
52
|
+
if (await confirm('将恢复本批次文件,数据库不会恢复,是否继续?'))
|
|
53
|
+
rollback(root, join(root, '.meadmin/updates', options.rollback));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const from = currentVersion(root);
|
|
57
|
+
const updates = join(root, '.meadmin/updates');
|
|
58
|
+
if (existsSync(updates))
|
|
59
|
+
for (const id of readdirSync(updates)) {
|
|
60
|
+
const file = join(updates, id, 'record.json');
|
|
61
|
+
if (!existsSync(file))
|
|
62
|
+
continue;
|
|
63
|
+
const record = JSON.parse(readFileSync(file, 'utf8'));
|
|
64
|
+
if (record.phase !== 'rolled-back' && (record.phase !== 'files-complete' || compareVersions(record.to, from) > 0 || ['failed', 'running'].includes(record.installation)))
|
|
65
|
+
throw new Error(`存在待处理升级批次 ${id},请先按报告完成依赖/人工处理或回滚`);
|
|
66
|
+
}
|
|
67
|
+
const npmRegistry = spawnSync(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['config', 'get', 'registry'], { encoding: 'utf8', shell: process.platform === 'win32' });
|
|
68
|
+
const registry = options.registry || process.env.npm_config_registry || (npmRegistry.status === 0 ? npmRegistry.stdout.trim() : '') || 'https://registry.npmjs.org';
|
|
69
|
+
const manifest = await registryManifest(registry);
|
|
70
|
+
const to = selectVersion(from, manifest.versions, options.version);
|
|
71
|
+
if (from === to) {
|
|
72
|
+
console.log(`已是目标版本 ${to},不重置本地文件`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const rulePath = options.config ? resolve(root, options.config) : join(root, 'meadmin.update.json');
|
|
76
|
+
if (options.config && !existsSync(rulePath))
|
|
77
|
+
throw new Error('指定规则文件不存在');
|
|
78
|
+
const config = existsSync(rulePath) ? validateConfig(JSON.parse(readFileSync(rulePath, 'utf8'))) : { skipExisting: {}, mergeSource: {} };
|
|
79
|
+
const rules = config.skipExisting;
|
|
80
|
+
const sourceRules = config.mergeSource;
|
|
81
|
+
const workspace = mkdtempSync(join(tmpdir(), 'meadmin-update-'));
|
|
82
|
+
const [oldTemplate, targetTemplate] = await Promise.all([downloadTemplate(manifest, from, join(workspace, 'old')), downloadTemplate(manifest, to, join(workspace, 'target'))]);
|
|
83
|
+
const plan = makePlan(root, oldTemplate, targetTemplate, to, rules, sourceRules);
|
|
84
|
+
const git = spawnSync('git', ['status', '--porcelain'], { cwd: root, encoding: 'utf8' });
|
|
85
|
+
Log.log(`升级 ${from} → ${to}${from.split('.')[0] !== to.split('.')[0] ? '(跨主版本)' : ''}`);
|
|
86
|
+
if (git.status === 0 && git.stdout.trim())
|
|
87
|
+
console.warn('工作区存在未提交修改,请先审查或提交。');
|
|
88
|
+
for (const item of plan.changes)
|
|
89
|
+
console.log(`${item.action}${item.conflict ? ' [本地冲突]' : ''}: ${item.path}`);
|
|
90
|
+
for (const item of plan.skipped)
|
|
91
|
+
console.log('跳过: ' + item);
|
|
92
|
+
for (const item of plan.manual)
|
|
93
|
+
console.warn('人工处理: ' + item);
|
|
94
|
+
console.log('SQL候选插入(不代表数据库实际新增):', plan.sqlTables);
|
|
95
|
+
Log.log(`参考模板保留于:${workspace}`);
|
|
96
|
+
Log.log(databaseReminder);
|
|
97
|
+
if (options.dryRun)
|
|
98
|
+
return;
|
|
99
|
+
if (!(await confirm('警告:将覆盖上述文件,可能丢失业务修改;特殊文件仅按规则合并。是否继续?')))
|
|
100
|
+
return;
|
|
101
|
+
const directory = applyPlan(root, plan, from, to);
|
|
102
|
+
Log.log(`文件处理完成${plan.manual.length ? ',存在人工处理项' : ''}。备份:${directory};批次:${basename(directory)}`);
|
|
103
|
+
Log.log('依赖清单已合并(包含目标模板新增依赖),请执行 pnpm install 安装。');
|
|
104
|
+
if (await confirm('是否现在执行 pnpm install?可能运行生命周期脚本;选择否后请手动执行。')) {
|
|
105
|
+
const record = JSON.parse(readFileSync(join(directory, 'record.json'), 'utf8'));
|
|
106
|
+
const installBackup = join(directory, 'before-install');
|
|
107
|
+
mkdirSync(installBackup, { recursive: true });
|
|
108
|
+
for (const name of new Set(['pnpm-lock.yaml', 'package.json', ...plan.changes.filter((change) => change.path.endsWith('/package.json')).map((change) => change.path)])) {
|
|
109
|
+
if (existsSync(join(root, name))) {
|
|
110
|
+
mkdirSync(join(installBackup, name, '..'), { recursive: true });
|
|
111
|
+
copyFileSync(join(root, name), join(installBackup, name));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
record.installation = 'running';
|
|
115
|
+
saveRecord(directory, record);
|
|
116
|
+
const install = spawnSync(process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm', ['install'], { cwd: root, stdio: 'inherit', shell: process.platform === 'win32' });
|
|
117
|
+
record.installation = install.status === 0 ? 'completed' : 'failed';
|
|
118
|
+
saveRecord(directory, record);
|
|
119
|
+
if (install.status !== 0) {
|
|
120
|
+
console.warn('依赖安装失败,请手动处理,文件备份仍保留');
|
|
121
|
+
process.exitCode = 1;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
Log.success(databaseReminder);
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
Log.error(error instanceof Error ? error.message : '', error);
|
|
128
|
+
process.exitCode = 1;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBkYXRlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbW1hbmRlcnMvdXBkYXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUMvQyxPQUFPLEVBQUUsWUFBWSxFQUFFLFVBQVUsRUFBRSxTQUFTLEVBQUUsV0FBVyxFQUFFLFlBQVksRUFBRSxXQUFXLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDdEcsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUNqQyxPQUFPLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDcEQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDN0MsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3pELE9BQU8sRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBc0IsTUFBTSxxQkFBcUIsQ0FBQztBQUMxRixPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDaEQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ3BELE9BQU8sRUFBRSxlQUFlLEVBQUUsY0FBYyxFQUFFLGdCQUFnQixFQUFFLGdCQUFnQixFQUFFLGFBQWEsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQzNILE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUV0QyxLQUFLLFVBQVUsT0FBTyxDQUFDLE9BQWU7SUFDcEMsSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSztRQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsOEJBQThCLENBQUMsQ0FBQztJQUNuRixNQUFNLE1BQU0sR0FBRyxlQUFlLENBQUMsRUFBRSxLQUFLLEVBQUUs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
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Plan } from './planner.js';
|
|
2
|
+
type RecordFile = {
|
|
3
|
+
path: string;
|
|
4
|
+
before: string | null;
|
|
5
|
+
after: string;
|
|
6
|
+
state: 'pending' | 'writing' | 'written';
|
|
7
|
+
};
|
|
8
|
+
export type UpgradeRecord = {
|
|
9
|
+
root: string;
|
|
10
|
+
from: string;
|
|
11
|
+
to: string;
|
|
12
|
+
phase: string;
|
|
13
|
+
files: RecordFile[];
|
|
14
|
+
manual: string[];
|
|
15
|
+
installation: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function safePath(root: string, path: string): string;
|
|
18
|
+
export declare function saveRecord(directory: string, record: UpgradeRecord): void;
|
|
19
|
+
export declare function applyPlan(root: string, plan: Plan, from: string, to: string): string;
|
|
20
|
+
export declare function rollback(root: string, directory: string): void;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
4
|
+
import { hash, readLocal } from './planner.js';
|
|
5
|
+
export function safePath(root, path) {
|
|
6
|
+
if (!path || path.includes('\\') || path.includes(':') || path.startsWith('/') || path.split('/').some((x) => x === '..' || x === '.' || !x || ['.git', 'node_modules', '.meadmin', '.workbuddy'].includes(x.toLowerCase())))
|
|
7
|
+
throw new Error('恢复清单路径无效');
|
|
8
|
+
readLocal(root, path);
|
|
9
|
+
return join(root, path);
|
|
10
|
+
}
|
|
11
|
+
function atomic(file, content) {
|
|
12
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
13
|
+
const temporary = file + '.meadmin-' + randomUUID();
|
|
14
|
+
try {
|
|
15
|
+
writeFileSync(temporary, content, { mode: 0o600 });
|
|
16
|
+
renameSync(temporary, file);
|
|
17
|
+
}
|
|
18
|
+
finally {
|
|
19
|
+
if (existsSync(temporary))
|
|
20
|
+
unlinkSync(temporary);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function saveRecord(directory, record) {
|
|
24
|
+
atomic(join(directory, 'record.json'), JSON.stringify(record, null, 2));
|
|
25
|
+
}
|
|
26
|
+
export function applyPlan(root, plan, from, to) {
|
|
27
|
+
for (const part of ['.meadmin', '.meadmin/updates']) {
|
|
28
|
+
const location = join(root, part);
|
|
29
|
+
if (existsSync(location) && (lstatSync(location).isSymbolicLink() || !lstatSync(location).isDirectory()))
|
|
30
|
+
throw new Error('升级备份目录不安全');
|
|
31
|
+
}
|
|
32
|
+
for (const change of plan.changes) {
|
|
33
|
+
const before = readLocal(root, change.path);
|
|
34
|
+
if ((before ? hash(before) : null) !== change.previous)
|
|
35
|
+
throw new Error(`预览后文件发生变化:${change.path}`);
|
|
36
|
+
}
|
|
37
|
+
const directory = join(root, '.meadmin/updates', randomUUID());
|
|
38
|
+
mkdirSync(directory, { recursive: true });
|
|
39
|
+
const record = { root: resolve(root), from, to, phase: 'prepared', files: [], manual: plan.manual, installation: 'not-run' };
|
|
40
|
+
for (const change of plan.changes) {
|
|
41
|
+
const before = readLocal(root, change.path);
|
|
42
|
+
if ((before ? hash(before) : null) !== change.previous)
|
|
43
|
+
throw new Error(`备份时文件发生变化:${change.path}`);
|
|
44
|
+
if (before)
|
|
45
|
+
atomic(join(directory, 'original', change.path), before);
|
|
46
|
+
atomic(join(directory, 'target', change.path), change.content);
|
|
47
|
+
record.files.push({ path: change.path, before: change.previous, after: hash(change.content), state: 'pending' });
|
|
48
|
+
}
|
|
49
|
+
saveRecord(directory, record);
|
|
50
|
+
try {
|
|
51
|
+
for (const [index, change] of plan.changes.entries()) {
|
|
52
|
+
const current = readLocal(root, change.path);
|
|
53
|
+
if ((current ? hash(current) : null) !== change.previous)
|
|
54
|
+
throw new Error(`写入前文件发生变化:${change.path}`);
|
|
55
|
+
record.files[index].state = 'writing';
|
|
56
|
+
saveRecord(directory, record);
|
|
57
|
+
atomic(safePath(root, change.path), change.content);
|
|
58
|
+
record.files[index].state = 'written';
|
|
59
|
+
saveRecord(directory, record);
|
|
60
|
+
}
|
|
61
|
+
record.phase = 'files-complete';
|
|
62
|
+
saveRecord(directory, record);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
record.phase = 'failed';
|
|
66
|
+
try {
|
|
67
|
+
saveRecord(directory, record);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
/* 日志写入失败也必须尝试恢复。 */
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
rollback(root, directory);
|
|
74
|
+
}
|
|
75
|
+
catch (recovery) {
|
|
76
|
+
throw new Error(`${String(error)};恢复未完成:${String(recovery)}。备份:${directory}`, { cause: recovery });
|
|
77
|
+
}
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
return directory;
|
|
81
|
+
}
|
|
82
|
+
export function rollback(root, directory) {
|
|
83
|
+
const rel = relative(resolve(root), resolve(directory)).replaceAll('\\', '/');
|
|
84
|
+
if (!/^\.meadmin\/updates\/[a-f0-9-]{36}$/.test(rel))
|
|
85
|
+
throw new Error('备份路径不在项目升级目录');
|
|
86
|
+
let currentDirectory = resolve(root);
|
|
87
|
+
for (const part of rel.split('/')) {
|
|
88
|
+
currentDirectory = join(currentDirectory, part);
|
|
89
|
+
if (!existsSync(currentDirectory) || lstatSync(currentDirectory).isSymbolicLink() || !lstatSync(currentDirectory).isDirectory())
|
|
90
|
+
throw new Error('备份路径不安全');
|
|
91
|
+
}
|
|
92
|
+
readLocal(directory, 'record.json');
|
|
93
|
+
const record = JSON.parse(readFileSync(join(directory, 'record.json'), 'utf8'));
|
|
94
|
+
if (record.root !== resolve(root))
|
|
95
|
+
throw new Error('备份所属项目不匹配');
|
|
96
|
+
if (record.installation !== 'not-run')
|
|
97
|
+
console.warn('安装已执行:仅恢复内容未被再次修改的升级文件;锁文件、node_modules及生命周期脚本影响需人工恢复并重新安装依赖。');
|
|
98
|
+
const candidates = record.files.filter((file) => file.state !== 'pending');
|
|
99
|
+
for (const file of candidates) {
|
|
100
|
+
safePath(root, file.path);
|
|
101
|
+
const current = readLocal(root, file.path), currentHash = current ? hash(current) : null;
|
|
102
|
+
if (currentHash !== file.after && currentHash !== file.before)
|
|
103
|
+
throw new Error(`升级后文件已被修改,拒绝覆盖:${file.path}`);
|
|
104
|
+
if (file.before) {
|
|
105
|
+
const backup = readLocal(directory, 'original/' + file.path);
|
|
106
|
+
if (!backup || hash(backup) !== file.before)
|
|
107
|
+
throw new Error(`备份不完整:${file.path}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
for (const file of [...candidates].reverse()) {
|
|
111
|
+
const destination = safePath(root, file.path);
|
|
112
|
+
if (file.before)
|
|
113
|
+
atomic(destination, readFileSync(join(directory, 'original', file.path)));
|
|
114
|
+
else if (existsSync(destination))
|
|
115
|
+
unlinkSync(destination);
|
|
116
|
+
}
|
|
117
|
+
record.phase = 'rolled-back';
|
|
118
|
+
saveRecord(directory, record);
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFja3VwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3VwZGF0ZS9iYWNrdXAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUN6QyxPQUFPLEVBQUUsVUFBVSxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsYUFBYSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ2hILE9BQU8sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDN0QsT0FBTyxFQUFFLElBQUksRUFBRSxTQUFTLEVBQWEsTUFBTSxjQUFjLENBQUM7QUFJMUQsTUFBTSxVQUFVLFFBQVEsQ0FBQyxJQUFZLEVBQUUsSUFBWTtJQUNqRCxJQUFJLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxJQUFJLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssSUFBSSxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsY0FBYyxFQUFFLFVBQVUsRUFBRSxZQUFZLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUM7UUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQzFQLFNBQVMsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDdEIsT0FBTyxJQUFJLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO0FBQzFCLENBQUM7QUFDRCxTQUFTLE1BQU0sQ0FBQyxJQUFZLEVBQUUsT0FBd0I7SUFDcEQsU0FBUyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQzlDLE1BQU0sU0FBUyxHQUFHLElBQUksR0FBRyxXQUFXLEdBQUcsVUFBVSxFQUFFLENBQUM7SUFDcEQsSUFBSSxDQUFDO1FBQ0gsYUFBYSxDQUFDLFNBQVMsRUFBRSxPQUFPLEVBQUUsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztRQUNuRCxVQUFVLENBQUMsU0FBUyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQzlCLENBQUM7WUFBUyxDQUFDO1FBQ1QsSUFBSSxVQUFVLENBQUMsU0FBUyxDQUFDO1lBQUUsVUFBVSxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ25ELENBQUM7QUFDSCxDQUFDO0FBQ0QsTUFBTSxVQUFVLFVBQVUsQ0FBQyxTQUFpQixFQUFFLE1BQXFCO0lBQ2pFLE1BQU0sQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLGFBQWEsQ0FBQyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQzFFLENBQUM7QUFDRCxNQUFNLFVBQVUsU0FBUyxDQUFDLElBQVksRUFBRSxJQUFVLEVBQUUsSUFBWSxFQUFFLEVBQVU7SUFDMUUsS0FBSyxNQUFNLElBQUksSUFBSSxDQUFDLFVBQVUsRUFBRSxrQkFBa0IsQ0FBQyxFQUFFLENBQUM7UUFDcEQsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztRQUNsQyxJQUFJLFVBQVUsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxjQUFjLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDekksQ0FBQztJQUNELEtBQUssTUFBTSxNQUFNLElBQUksSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ2xDLE1BQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzVDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEtBQUssTUFBTSxDQUFDLFFBQVE7WUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLGFBQWEsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7SUFDdEcsQ0FBQztJQUNELE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxJQUFJLEVBQUUsa0JBQWtCLEVBQUUsVUFBVSxFQUFFLENBQUMsQ0FBQztJQUMvRCxTQUFTLENBQUMsU0FBUyxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7SUFDMUMsTUFBTSxNQUFNLEdBQWtCLEVBQUUsSUFBSSxFQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxVQUFVLEVBQUUsS0FBSyxFQUFFLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU0sRUFBRSxZQUFZLEVBQUUsU0FBUyxFQUFFLENBQUM7SUFDNUksS0FBSyxNQUFNLE1BQU0sSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDbEMsTUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDNUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsS0FBSyxNQUFNLENBQUMsUUFBUTtZQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsYUFBYSxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUNwRyxJQUFJLE1BQU07WUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxVQUFVLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3JFLE1BQU0sQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLFFBQVEsRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQy9ELE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLEVBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQyxRQUFRLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUM7SUFDbkgsQ0FBQztJQUNELFVBQVUsQ0FBQyxTQUFTLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDOUIsSUFBSSxDQUFDO1FBQ0gsS0FBSyxNQUFNLENBQUMsS0FBSyxFQUFFLE1BQU0sQ0FBQyxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQztZQUNyRCxNQUFNLE9BQU8sR0FBRyxTQUFTLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUM3QyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLE1BQU0sQ0FBQyxRQUFRO2dCQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsYUFBYSxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztZQUN0RyxNQUFNLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLEtBQUssR0FBRyxTQUFTLENBQUM7WUFDdEMsVUFBVSxDQUFDLFNBQVMsRUFBRSxNQUFNLENBQUMsQ0FBQztZQUM5QixNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQ3BELE1BQU0sQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsS0FBSyxHQUFHLFNBQVMsQ0FBQztZQUN0QyxVQUFVLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ2hDLENBQUM7UUFDRCxNQUFNLENBQUMsS0FBSyxHQUFHLGdCQUFnQixDQUFDO1FBQ2hDLFVBQVUsQ0FBQyxTQUFTLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDaEMsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixNQUFNLENBQUMsS0FBSyxHQUFHLFFBQVEsQ0FBQztRQUN4QixJQUFJLENBQUM7WUFDSCxVQUFVLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ2hDLENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCxvQkFBb0I7UUFDdEIsQ0FBQztRQUNELElBQUksQ0FBQztZQUNILFFBQVEsQ0FBQyxJQUFJLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFDNUIsQ0FBQztRQUFDLE9BQU8sUUFBUSxFQUFFLENBQUM7WUFDbEIsTUFBTSxJQUFJLEtBQUssQ0FBQyxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsVUFBVSxNQUFNLENBQUMsUUFBUSxDQUFDLE9BQU8sU0FBUyxFQUFFLEVBQUUsRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQztRQUNyRyxDQUFDO1FBQ0QsTUFBTSxLQUFLLENBQUM7SUFDZCxDQUFDO0lBQ0QsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQztBQUNELE1BQU0sVUFBVSxRQUFRLENBQUMsSUFBWSxFQUFFLFNBQWlCO0lBQ3RELE1BQU0sR0FBRyxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsQ0FBQztJQUM5RSxJQUFJLENBQUMscUNBQXFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQztRQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsY0FBYyxDQUFDLENBQUM7SUFDdEYsSUFBSSxnQkFBZ0IsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDckMsS0FBSyxNQUFNLElBQUksSUFBSSxHQUFHLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDbEMsZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixFQUFFLElBQUksQ0FBQyxDQUFDO1FBQ2hELElBQUksQ0FBQyxVQUFVLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxTQUFTLENBQUMsZ0JBQWdCLENBQUMsQ0FBQyxjQUFjLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLFdBQVcsRUFBRTtZQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDOUosQ0FBQztJQUNELFNBQVMsQ0FBQyxTQUFTLEVBQUUsYUFBYSxDQUFDLENBQUM7SUFDcEMsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxhQUFhLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBa0IsQ0FBQztJQUNqRyxJQUFJLE1BQU0sQ0FBQyxJQUFJLEtBQUssT0FBTyxDQUFDLElBQUksQ0FBQztRQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDaEUsSUFBSSxNQUFNLENBQUMsWUFBWSxLQUFLLFNBQVM7UUFBRSxPQUFPLENBQUMsSUFBSSxDQUFDLCtEQUErRCxDQUFDLENBQUM7SUFDckgsTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxLQUFLLEtBQUssU0FBUyxDQUFDLENBQUM7SUFDM0UsS0FBSyxNQUFNLElBQUksSUFBSSxVQUFVLEVBQUUsQ0FBQztRQUM5QixRQUFRLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUMxQixNQUFNLE9BQU8sR0FBRyxTQUFTLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsRUFDeEMsV0FBVyxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7UUFDL0MsSUFBSSxXQUFXLEtBQUssSUFBSSxDQUFDLEtBQUssSUFBSSxXQUFXLEtBQUssSUFBSSxDQUFDLE1BQU07WUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLGtCQUFrQixJQUFJLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUM5RyxJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNoQixNQUFNLE1BQU0sR0FBRyxTQUFTLENBQUMsU0FBUyxFQUFFLFdBQVcsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDN0QsSUFBSSxDQUFDLE1BQU0sSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssSUFBSSxDQUFDLE1BQU07Z0JBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyxTQUFTLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQ3JGLENBQUM7SUFDSCxDQUFDO0lBQ0QsS0FBSyxNQUFNLElBQUksSUFBSSxDQUFDLEdBQUcsVUFBVSxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQztRQUM3QyxNQUFNLFdBQVcsR0FBRyxRQUFRLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM5QyxJQUFJLElBQUksQ0FBQyxNQUFNO1lBQUUsTUFBTSxDQUFDLFdBQVcsRUFBRSxZQUFZLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxVQUFVLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQzthQUN0RixJQUFJLFVBQVUsQ0FBQyxXQUFXLENBQUM7WUFBRSxVQUFVLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDNUQsQ0FBQztJQUNELE1BQU0sQ0FBQyxLQUFLLEdBQUcsYUFBYSxDQUFDO0lBQzdCLFVBQVUsQ0FBQyxTQUFTLEVBQUUsTUFBTSxDQUFDLENBQUM7QUFDaEMsQ0FBQyJ9
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type MergeResult = {
|
|
2
|
+
content: string;
|
|
3
|
+
manual: string[];
|
|
4
|
+
};
|
|
5
|
+
export declare function mergePackage(local: string, target: string, base?: string): MergeResult;
|
|
6
|
+
export declare function mergeConfig(local: string, target: string, base?: string): MergeResult;
|
|
7
|
+
export declare function mergeEntity(local: string, target: string, base?: string): MergeResult;
|