@cloudbase/manager-node 5.8.3 → 5.8.5

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/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## @cloudbase/manager-node v5.8.4 版本发布 🚀(2026-09-02)
2
+
3
+ - 新增 move declarative image config preparation to manager
4
+ - 新增 移除本地 state 快照与 skip 判定
5
+ - 重构 ProjectValidator 移除 version 字段
6
+ - 修复 resolve build context path and fix Rego package declaration
7
+
8
+ ---
9
+
10
+ ## @cloudbase/manager-node v5.8.3 版本发布 🚀(2026-08-28)
11
+
12
+ - 新增 完善云函数镜像部署闭环
13
+ - 新增 support consistency deploy options in hosting declarative deploy
14
+ - 修复 仅 cdn/空 domainType 调用 CDN 接口
15
+ - 修复 handle missing document database config
16
+
17
+ ---
18
+
1
19
  ## @cloudbase/manager-node v5.8.2 版本发布 🚀(2026-08-25)
2
20
 
3
21
  - 新增 support requestFn injection to bypass TC3 signing
@@ -17,6 +17,10 @@ const fs_extra_1 = require("fs-extra");
17
17
  const path_1 = __importDefault(require("path"));
18
18
  const utils_1 = require("../utils");
19
19
  const type_1 = require("./type");
20
+ /**
21
+ * 默认开放访问类型(serverConfig.OpenAccessTypes 未显式传入时使用)
22
+ */
23
+ const DEFAULT_OPEN_ACCESS_TYPES = ['OA', 'PUBLIC', 'MINIAPP'];
20
24
  /**
21
25
  * 云托管服务管理类
22
26
  * 提供云托管服务的初始化、下载、列表查询和删除等功能
@@ -331,7 +335,7 @@ class CloudRunService {
331
335
  * @param {string} params.serverName 要部署的服务名称
332
336
  * @param {string} params.targetPath 本地代码路径
333
337
  * @param {Object} [params.serverConfig] 服务配置项(可选)
334
- * @param {string[]} [params.serverConfig.OpenAccessTypes] 开放访问类型
338
+ * @param {string[]} [params.serverConfig.OpenAccessTypes] 开放访问类型,不传时默认 ['OA', 'PUBLIC', 'MINIAPP']
335
339
  * @param {number} [params.serverConfig.Cpu] CPU规格
336
340
  * @param {number} [params.serverConfig.Mem] 内存规格
337
341
  * @param {number} [params.serverConfig.MinNum] 最小实例数
@@ -349,7 +353,7 @@ class CloudRunService {
349
353
  * @returns {Promise<IResponseInfo>} 返回部署操作的响应信息
350
354
  */
351
355
  async deploy(params) {
352
- var _a;
356
+ var _a, _b, _c;
353
357
  const { serverName, targetPath = process.cwd(), serverConfig, vpcInfo, imageUrl } = params;
354
358
  /**
355
359
  * 参数校验和默认值设置
@@ -401,7 +405,7 @@ class CloudRunService {
401
405
  if (await this._checkFunctionExist(serverName)) {
402
406
  // 更新
403
407
  const serverDetail = await this.detail({ serverName });
404
- const _serverConfig = Object.assign(Object.assign(Object.assign({}, serverConfig), { OpenAccessTypes: ['OA', 'PUBLIC', 'MINIAPP'] }), ((serverDetail === null || serverDetail === void 0 ? void 0 : serverDetail.ServerConfig.Tag) === 'function:' ? { Port: 3000 } : {}) // 函数型不能指定端口,需要固定为3000
408
+ const _serverConfig = Object.assign(Object.assign(Object.assign({}, serverConfig), { OpenAccessTypes: (_a = serverConfig === null || serverConfig === void 0 ? void 0 : serverConfig.OpenAccessTypes) !== null && _a !== void 0 ? _a : DEFAULT_OPEN_ACCESS_TYPES }), ((serverDetail === null || serverDetail === void 0 ? void 0 : serverDetail.ServerConfig.Tag) === 'function:' ? { Port: 3000 } : {}) // 函数型不能指定端口,需要固定为3000
405
409
  );
406
410
  if ((serverDetail === null || serverDetail === void 0 ? void 0 : serverDetail.ServerConfig.Tag) === 'function:') {
407
411
  deployInfo.BuildPacks = {
@@ -409,7 +413,7 @@ class CloudRunService {
409
413
  RepoLanguage: 'Node.js'
410
414
  };
411
415
  }
412
- deployInfo.ReleaseType = ((_a = params.deployInfo) === null || _a === void 0 ? void 0 : _a.ReleaseType) || "FULL";
416
+ deployInfo.ReleaseType = ((_b = params.deployInfo) === null || _b === void 0 ? void 0 : _b.ReleaseType) || "FULL";
413
417
  return this._upsertFunction(false, {
414
418
  name: serverName,
415
419
  deployInfo,
@@ -436,7 +440,7 @@ class CloudRunService {
436
440
  RepoLanguage: 'Node.js'
437
441
  };
438
442
  }
439
- const _serverConfig = Object.assign(Object.assign(Object.assign({ OpenAccessTypes: ['OA', 'PUBLIC', 'MINIAPP'],
443
+ const _serverConfig = Object.assign(Object.assign(Object.assign({ OpenAccessTypes: (_c = serverConfig === null || serverConfig === void 0 ? void 0 : serverConfig.OpenAccessTypes) !== null && _c !== void 0 ? _c : DEFAULT_OPEN_ACCESS_TYPES,
440
444
  // Cpu: 0,
441
445
  // Mem: 0,
442
446
  MinNum: 0,
@@ -10,8 +10,7 @@ const DatabaseDeployer_1 = require("./DatabaseDeployer");
10
10
  const FunctionDeployer_1 = require("./FunctionDeployer");
11
11
  const StaticDeployer_1 = require("./StaticDeployer");
12
12
  const GatewayDeployer_1 = require("./GatewayDeployer");
13
- const StateStore_1 = require("./StateStore");
14
- const framework_1 = require("./framework");
13
+ const config_guard_1 = require("./function/config-guard");
15
14
  const parallel_1 = require("../utils/parallel");
16
15
  /** 部署顺序(按依赖:database 最先,函数依赖新 Schema;gateway 最后,依赖函数/hosting) */
17
16
  const DEPLOY_ORDER = ['database', 'functions', 'app', 'hosting', 'gateway'];
@@ -32,7 +31,8 @@ class DeployOrchestrator {
32
31
  * 计算部署计划(dry-run 输出)
33
32
  */
34
33
  async deployPlan(options) {
35
- return this.buildPlan(options.config, options);
34
+ const config = this.normalizeFunctionConfigs(options.config, options.envId, options.cwd);
35
+ return this.buildPlan(config, options);
36
36
  }
37
37
  /**
38
38
  * 执行部署
@@ -45,7 +45,8 @@ class DeployOrchestrator {
45
45
  */
46
46
  async deploy(options) {
47
47
  var _a, _b, _c, _d, _e, _f, _g, _h;
48
- const { config, envId, dryRun, cwd = process.cwd(), log, concurrency = 5, continueOnError = false } = options;
48
+ const { config: inputConfig, envId, dryRun, cwd = process.cwd(), log, concurrency = 5, continueOnError = false } = options;
49
+ const config = this.normalizeFunctionConfigs(inputConfig, envId, cwd);
49
50
  // 1. 轻量 inline 守卫(validateProject 未接入时的兜底)
50
51
  this.guardConfig(config);
51
52
  const plan = await this.buildPlan(config, options);
@@ -59,132 +60,157 @@ class DeployOrchestrator {
59
60
  // gateway 段强制串行:executeStep 的 gateway 分支部署 config.gateway.routes 全部路由
60
61
  // (逐路由 plan item 仅用于展示 diff),并发执行会对同一批路由发起重复全量部署(竞态 create/modify 路由、bindCustomDomain)
61
62
  const globalLimit = Math.max(1, Math.min(Number(concurrency) || 1, 20));
62
- try {
63
- for (const segment of segments) {
64
- // 段内并行度:gateway 段固定 1(全量部署语义);其余段用全局 concurrency
65
- const limit = ((_b = segment[0]) === null || _b === void 0 ? void 0 : _b.type) === 'gateway' ? 1 : globalLimit;
66
- // 段内预判定(顺序执行,保留交互/冲突中断语义)
67
- const toExecute = [];
68
- let segmentFailed = false;
69
- let segmentDbFailed = false;
70
- for (const item of segment) {
71
- // skip:产物/配置未变更,静默跳过(不执行、不询问)
72
- if (item.status === 'skip') {
73
- (_c = log === null || log === void 0 ? void 0 : log.info) === null || _c === void 0 ? void 0 : _c.call(log, `[${item.type}] ${item.name}: ${item.action}`);
74
- results.push({ type: item.type, name: item.name, ok: true, reason: 'skip' });
75
- continue;
76
- }
77
- (_d = log === null || log === void 0 ? void 0 : log.info) === null || _d === void 0 ? void 0 : _d.call(log, `[${item.type}] ${item.name}: ${item.action}`);
78
- // conflict:数据库迁移冲突 → 中断整个部署(后续资源可能依赖新 Schema)
79
- if (item.status === 'conflict') {
80
- const message = '检测到冲突,已中断部署';
81
- (_e = log === null || log === void 0 ? void 0 : log.error) === null || _e === void 0 ? void 0 : _e.call(log, `[${item.type}] ${item.name} ${message}`);
82
- results.push({ type: item.type, name: item.name, ok: false, error: message });
83
- if (item.type === 'database') {
84
- return { plan, results };
85
- }
86
- segmentFailed = true;
87
- continue;
88
- }
89
- // 覆盖确认:仅函数已存在(update)时触发,对齐 tcb fn deploy 的 FunctionConflictChecker 语义
90
- // yes=true 直接放行;注入 confirmUpdate 按回调决定;两者都无 → 保守跳过(no-confirm)
91
- if (item.type === 'functions' && item.status === 'update') {
92
- let confirmed = false;
93
- if (options.yes) {
94
- confirmed = true;
95
- }
96
- else if (options.confirmUpdate) {
97
- confirmed = await options.confirmUpdate(item);
98
- }
99
- if (!confirmed) {
100
- const reason = options.confirmUpdate
101
- ? 'skipped-by-user'
102
- : 'no-confirm';
103
- (_f = log === null || log === void 0 ? void 0 : log.warn) === null || _f === void 0 ? void 0 : _f.call(log, `[${item.type}] ${item.name} 跳过覆盖更新(${reason})`);
104
- results.push({ type: item.type, name: item.name, ok: false, reason });
105
- continue;
106
- }
107
- }
108
- toExecute.push(item);
63
+ for (const segment of segments) {
64
+ // 段内并行度:gateway 段固定 1(全量部署语义);其余段用全局 concurrency
65
+ const limit = ((_b = segment[0]) === null || _b === void 0 ? void 0 : _b.type) === 'gateway' ? 1 : globalLimit;
66
+ // 段内预判定(顺序执行,保留交互/冲突中断语义)
67
+ const toExecute = [];
68
+ let segmentFailed = false;
69
+ let segmentDbFailed = false;
70
+ for (const item of segment) {
71
+ // skip:产物/配置未变更,静默跳过(不执行、不询问)
72
+ if (item.status === 'skip') {
73
+ (_c = log === null || log === void 0 ? void 0 : log.info) === null || _c === void 0 ? void 0 : _c.call(log, `[${item.type}] ${item.name}: ${item.action}`);
74
+ results.push({ type: item.type, name: item.name, ok: true, reason: 'skip' });
75
+ continue;
109
76
  }
110
- if (toExecute.length === 0) {
111
- // 段内无实际执行项:若本段已因 conflict 标记失败且 fail-fast,则中断
112
- if (segmentFailed && !continueOnError) {
77
+ (_d = log === null || log === void 0 ? void 0 : log.info) === null || _d === void 0 ? void 0 : _d.call(log, `[${item.type}] ${item.name}: ${item.action}`);
78
+ // conflict:数据库迁移冲突 → 中断整个部署(后续资源可能依赖新 Schema)
79
+ if (item.status === 'conflict') {
80
+ const message = '检测到冲突,已中断部署';
81
+ (_e = log === null || log === void 0 ? void 0 : log.error) === null || _e === void 0 ? void 0 : _e.call(log, `[${item.type}] ${item.name} ${message}`);
82
+ results.push({ type: item.type, name: item.name, ok: false, error: message });
83
+ if (item.type === 'database') {
113
84
  return { plan, results };
114
85
  }
86
+ segmentFailed = true;
115
87
  continue;
116
88
  }
117
- // 段内并行执行
118
- const controller = new parallel_1.AsyncTaskParallelController(limit);
119
- const tasks = toExecute.map((item) => async () => {
120
- var _a, _b, _c, _d, _e;
121
- try {
122
- const stepResult = await this.executeStep({
123
- item,
124
- config,
125
- envId,
126
- cwd,
127
- onProgress: options.onProgress
128
- });
129
- if (item.type === 'functions') {
130
- results.push({
131
- type: item.type,
132
- name: item.name,
133
- ok: true,
134
- url: stepResult === null || stepResult === void 0 ? void 0 : stepResult.url,
135
- detail: stepResult
136
- });
137
- }
138
- else {
139
- results.push({ type: item.type, name: item.name, ok: true, url: stepResult });
140
- }
89
+ // 覆盖确认:仅函数已存在(update)时触发,对齐 tcb fn deploy 的 FunctionConflictChecker 语义
90
+ // yes=true 直接放行;注入 confirmUpdate 按回调决定;两者都无 → 保守跳过(no-confirm)
91
+ if (item.type === 'functions' && item.status === 'update') {
92
+ let confirmed = false;
93
+ if (options.yes) {
94
+ confirmed = true;
141
95
  }
142
- catch (e) {
143
- const message = (e === null || e === void 0 ? void 0 : e.message) || String(e);
144
- const requestId = (e === null || e === void 0 ? void 0 : e.requestId) ||
145
- (e === null || e === void 0 ? void 0 : e.RequestId) ||
146
- ((_a = e === null || e === void 0 ? void 0 : e.original) === null || _a === void 0 ? void 0 : _a.requestId) ||
147
- ((_b = e === null || e === void 0 ? void 0 : e.original) === null || _b === void 0 ? void 0 : _b.RequestId) ||
148
- ((_c = e === null || e === void 0 ? void 0 : e.response) === null || _c === void 0 ? void 0 : _c.requestId) ||
149
- ((_d = e === null || e === void 0 ? void 0 : e.response) === null || _d === void 0 ? void 0 : _d.RequestId);
150
- if (!(item.type === 'functions' && options.onProgress)) {
151
- (_e = log === null || log === void 0 ? void 0 : log.error) === null || _e === void 0 ? void 0 : _e.call(log, `[${item.type}] ${item.name} 部署失败: ${message}`);
152
- }
153
- results.push(Object.assign(Object.assign(Object.assign({ type: item.type, name: item.name, ok: false, error: message }, ((e === null || e === void 0 ? void 0 : e.code) ? { errorCode: e.code } : {})), ((e === null || e === void 0 ? void 0 : e.failedStage) ? { failedStage: e.failedStage } : {})), (requestId ? { requestId } : {})));
154
- segmentFailed = true;
155
- if (item.type === 'database') {
156
- segmentDbFailed = true;
157
- }
96
+ else if (options.confirmUpdate) {
97
+ confirmed = await options.confirmUpdate(item);
158
98
  }
159
- });
160
- controller.loadTasks(tasks);
161
- await controller.run();
162
- // 段内失败中断决策
163
- if (segmentFailed) {
164
- if (segmentDbFailed) {
165
- (_g = log === null || log === void 0 ? void 0 : log.error) === null || _g === void 0 ? void 0 : _g.call(log, '数据库迁移失败,中断部署');
166
- return { plan, results };
167
- }
168
- if (!continueOnError) {
169
- return { plan, results };
99
+ if (!confirmed) {
100
+ const reason = options.confirmUpdate
101
+ ? 'skipped-by-user'
102
+ : 'no-confirm';
103
+ (_f = log === null || log === void 0 ? void 0 : log.warn) === null || _f === void 0 ? void 0 : _f.call(log, `[${item.type}] ${item.name} 跳过覆盖更新(${reason})`);
104
+ results.push({ type: item.type, name: item.name, ok: false, reason });
105
+ continue;
170
106
  }
171
107
  }
108
+ toExecute.push(item);
172
109
  }
173
- }
174
- finally {
175
- // 无论部署全部成功还是 fail-fast 中断,都将已成功资源的状态快照落盘
176
- // (失败资源保留旧快照,下次 diff 仍会检测到差异);快照写入失败不影响部署结果
177
- if (!dryRun) {
110
+ if (toExecute.length === 0) {
111
+ // 段内无实际执行项:若本段已因 conflict 标记失败且 fail-fast,则中断
112
+ if (segmentFailed && !continueOnError) {
113
+ return { plan, results };
114
+ }
115
+ continue;
116
+ }
117
+ // 段内并行执行
118
+ const controller = new parallel_1.AsyncTaskParallelController(limit);
119
+ const tasks = toExecute.map((item) => async () => {
120
+ var _a, _b, _c, _d, _e;
178
121
  try {
179
- await this.persistState({ config, plan, results, cwd, envId: options.envId });
122
+ const stepResult = await this.executeStep({
123
+ item,
124
+ config,
125
+ envId,
126
+ cwd,
127
+ onProgress: options.onProgress
128
+ });
129
+ if (item.type === 'functions') {
130
+ results.push({
131
+ type: item.type,
132
+ name: item.name,
133
+ ok: true,
134
+ url: stepResult === null || stepResult === void 0 ? void 0 : stepResult.url,
135
+ detail: stepResult
136
+ });
137
+ }
138
+ else {
139
+ results.push({ type: item.type, name: item.name, ok: true, url: stepResult });
140
+ }
180
141
  }
181
142
  catch (e) {
182
- (_h = log === null || log === void 0 ? void 0 : log.warn) === null || _h === void 0 ? void 0 : _h.call(log, `状态快照写入失败:${(e === null || e === void 0 ? void 0 : e.message) || String(e)}`);
143
+ const message = (e === null || e === void 0 ? void 0 : e.message) || String(e);
144
+ const requestId = (e === null || e === void 0 ? void 0 : e.requestId) ||
145
+ (e === null || e === void 0 ? void 0 : e.RequestId) ||
146
+ ((_a = e === null || e === void 0 ? void 0 : e.original) === null || _a === void 0 ? void 0 : _a.requestId) ||
147
+ ((_b = e === null || e === void 0 ? void 0 : e.original) === null || _b === void 0 ? void 0 : _b.RequestId) ||
148
+ ((_c = e === null || e === void 0 ? void 0 : e.response) === null || _c === void 0 ? void 0 : _c.requestId) ||
149
+ ((_d = e === null || e === void 0 ? void 0 : e.response) === null || _d === void 0 ? void 0 : _d.RequestId);
150
+ if (!(item.type === 'functions' && options.onProgress)) {
151
+ (_e = log === null || log === void 0 ? void 0 : log.error) === null || _e === void 0 ? void 0 : _e.call(log, `[${item.type}] ${item.name} 部署失败: ${message}`);
152
+ }
153
+ results.push(Object.assign(Object.assign(Object.assign({ type: item.type, name: item.name, ok: false, error: message }, ((e === null || e === void 0 ? void 0 : e.code) ? { errorCode: e.code } : {})), ((e === null || e === void 0 ? void 0 : e.failedStage) ? { failedStage: e.failedStage } : {})), (requestId ? { requestId } : {})));
154
+ segmentFailed = true;
155
+ if (item.type === 'database') {
156
+ segmentDbFailed = true;
157
+ }
158
+ }
159
+ });
160
+ controller.loadTasks(tasks);
161
+ await controller.run();
162
+ // 段内失败中断决策
163
+ if (segmentFailed) {
164
+ if (segmentDbFailed) {
165
+ (_g = log === null || log === void 0 ? void 0 : log.error) === null || _g === void 0 ? void 0 : _g.call(log, '数据库迁移失败,中断部署');
166
+ return { plan, results };
167
+ }
168
+ if (!continueOnError) {
169
+ (_h = log === null || log === void 0 ? void 0 : log.error) === null || _h === void 0 ? void 0 : _h.call(log, '存在资源部署失败,中断部署');
170
+ return { plan, results };
183
171
  }
184
172
  }
185
173
  }
186
174
  return { plan, results };
187
175
  }
176
+ /**
177
+ * 归一化声明式函数配置:直接消费 cloudbaserc 的嵌套形状(imageConfig.build),
178
+ * 不做字段平铺映射;非 HTTP 函数原样返回。
179
+ */
180
+ normalizeFunctionConfigs(config, envId, cwd = process.cwd()) {
181
+ if (!Array.isArray(config === null || config === void 0 ? void 0 : config.functions)) {
182
+ return config;
183
+ }
184
+ const functionRootPath = path_1.default.resolve(cwd, config.functionRoot || 'functions');
185
+ return Object.assign(Object.assign({}, config), { functions: config.functions.map((func) => {
186
+ var _a, _b;
187
+ // 直接消费 cloudbaserc 的嵌套形状(imageConfig.build),不再做字段平铺映射。
188
+ // 非镜像/构建策略由 prepareMinimalDeployConfig 内部识别并原样返回。
189
+ if ((func === null || func === void 0 ? void 0 : func.type) !== 'HTTP') {
190
+ return func;
191
+ }
192
+ const funcAny = func;
193
+ const explicitCwd = (_b = (_a = funcAny.imageConfig) === null || _a === void 0 ? void 0 : _a.build) === null || _b === void 0 ? void 0 : _b.cwd;
194
+ const dir = funcAny.dir;
195
+ // 构建上下文根解析优先级:显式 build.cwd > dir > functionRoot/name。
196
+ // 相对路径一律以部署传入的项目 cwd 为基准解析为绝对路径,避免 preflight
197
+ // 与云端构建(builders/cloud.ts)以进程 CWD 兜底导致目录错位。
198
+ const resolvedContextRoot = typeof explicitCwd === 'string' && explicitCwd.trim()
199
+ ? path_1.default.resolve(cwd, explicitCwd)
200
+ : typeof dir === 'string' && dir.trim()
201
+ ? path_1.default.resolve(cwd, dir)
202
+ : undefined;
203
+ // 显式 build.cwd 归一为绝对路径写回,避免 applyMinimalDeployConfigDefaults
204
+ // 因 build.cwd 已存在而保留原始相对值。
205
+ const normalizedFunc = resolvedContextRoot && typeof explicitCwd === 'string' && explicitCwd.trim()
206
+ ? Object.assign(Object.assign({}, funcAny), { imageConfig: Object.assign(Object.assign({}, funcAny.imageConfig), { build: Object.assign(Object.assign({}, funcAny.imageConfig.build), { cwd: resolvedContextRoot }) }) }) : func;
207
+ return (0, config_guard_1.prepareMinimalDeployConfig)(normalizedFunc, {
208
+ envId,
209
+ functionPath: resolvedContextRoot,
210
+ functionRootPath
211
+ }).config;
212
+ }) });
213
+ }
188
214
  /**
189
215
  * 将扁平 plan 按「连续相同资源类型」切成有序 segments
190
216
  *
@@ -212,91 +238,19 @@ class DeployOrchestrator {
212
238
  }
213
239
  return segments;
214
240
  }
215
- /**
216
- * 部署成功后写回本地 state 快照
217
- *
218
- * - hosting:成功部署/未变更(skip)的项重算产物指纹;失败项保留旧快照
219
- * - app:成功部署写 buildAppConfig 快照;skip 复用旧快照
220
- * - gateway:保留旧快照(本轮不做路由级实时快照)
221
- */
222
- async persistState(options) {
223
- var _a, _b, _c, _d, _e, _f;
224
- const { config, plan, results, cwd, envId } = options;
225
- const stateStore = new StateStore_1.StateStore({ cwd });
226
- const prev = stateStore.read();
227
- // hosting
228
- const hostingEntries = [];
229
- for (const h of config.hosting || []) {
230
- const name = h.name || 'default';
231
- const planItem = plan.find(p => p.type === 'hosting' && p.name === name);
232
- const result = results.find(r => r.type === 'hosting' && r.name === name);
233
- if (!result || !result.ok) {
234
- // 未执行(fail-fast 中断,results 中无对应条目)或部署失败:
235
- // 保留旧快照(下次 diff 仍会检测到差异)
236
- const prevEntry = (_b = (_a = prev === null || prev === void 0 ? void 0 : prev.resources) === null || _a === void 0 ? void 0 : _a.hosting) === null || _b === void 0 ? void 0 : _b.find(s => s.name === name);
237
- if (prevEntry) {
238
- hostingEntries.push(prevEntry);
239
- }
240
- continue;
241
- }
242
- if ((planItem === null || planItem === void 0 ? void 0 : planItem.status) === 'skip') {
243
- const prevEntry = (_d = (_c = prev === null || prev === void 0 ? void 0 : prev.resources) === null || _c === void 0 ? void 0 : _c.hosting) === null || _d === void 0 ? void 0 : _d.find(s => s.name === name);
244
- if (prevEntry) {
245
- hostingEntries.push(prevEntry);
246
- continue;
247
- }
248
- }
249
- // 成功部署:重算当前指纹(含 size,供下次 diff 做 size 预判)
250
- // 注意:deployHosting 可能执行本地构建,产物已更新,必须独立重算而非复用 plan 指纹
251
- const root = path_1.default.resolve(cwd, h.root || '.');
252
- // outputDir 与 StaticDeployer.deployHosting 完全复用同一解析逻辑
253
- const outputDir = (0, framework_1.resolveHostingOutputDir)(h, root);
254
- try {
255
- const files = await stateStore.computeHostingFingerprints(outputDir, h.ignore || []);
256
- hostingEntries.push({ name, deployPath: h.deployPath, files });
257
- }
258
- catch (_g) {
259
- // 目录缺失 / 文件数超限:不写入(下次 diff 走云端兜底或全量部署)
260
- }
261
- }
262
- // app
263
- let appEntry;
264
- if (config.app) {
265
- const appResult = results.find(r => r.type === 'app');
266
- if (appResult === null || appResult === void 0 ? void 0 : appResult.ok) {
267
- appEntry = stateStore.buildAppConfig(config.app);
268
- }
269
- else {
270
- appEntry = (_e = prev === null || prev === void 0 ? void 0 : prev.resources) === null || _e === void 0 ? void 0 : _e.app;
271
- }
272
- }
273
- const resources = {};
274
- if (hostingEntries.length) {
275
- resources.hosting = hostingEntries;
276
- }
277
- if (appEntry) {
278
- resources.app = appEntry;
279
- }
280
- if ((_f = prev === null || prev === void 0 ? void 0 : prev.resources) === null || _f === void 0 ? void 0 : _f.gateway) {
281
- resources.gateway = prev.resources.gateway;
282
- }
283
- if (Object.keys(resources).length) {
284
- stateStore.write({ resources, envId });
285
- }
286
- }
287
241
  /**
288
242
  * 构建完整部署计划(按依赖顺序 + only/skip 过滤)
289
243
  *
290
244
  * 通过查询云端状态判定每个资源的动作:
291
245
  * - database:先按迁移文件做 preview,再聚合成单条资源级计划项(pending→create,存在 conflict→conflict,全部 applied→不出现在 plan)
292
246
  * - functions:ListFunctions 判断是否已存在 → create / update(不做本地 hash / 字段 diff)
293
- * - app:describeAppInfo 判断是否已存在 → create / update;本地 state 配置一致 → skip
294
- * - hosting:本地 state 指纹 diff → 一致 skip(不重新上传)/ 有差异 deploy(带文件级明细)
247
+ * - app:describeAppInfo 判断是否已存在 → create / update
248
+ * - hosting:直传覆盖(无存在性概念)
295
249
  * - gateway:planRoutes 对比云端路由 → create / update / skip(含变更字段明细)
296
250
  * 云端查询失败时降级为保守判定(全 create),不影响 dry-run 展示。
297
251
  */
298
252
  async buildPlan(config, options) {
299
- const { only, skip, refresh } = options;
253
+ const { only, skip } = options;
300
254
  const cwd = options.cwd || process.cwd();
301
255
  const envId = options.envId;
302
256
  const plan = [];
@@ -307,14 +261,7 @@ class DeployOrchestrator {
307
261
  return false;
308
262
  return true;
309
263
  };
310
- // 本地 state 快照(diff 基准):--refresh 时忽略本地 skip 判定
311
- const stateStore = new StateStore_1.StateStore({ cwd });
312
- const rawState = refresh ? null : stateStore.read();
313
- // 跨环境保护:快照记录的 envId 与目标环境不一致(或旧格式快照无 envId)时,
314
- // 快照是其他环境部署后的指纹,不可作为 skip 依据 → 视为无快照,强制走全量部署,
315
- // 避免切换环境后误判「产物未变更」导致新环境 COS 404
316
- const state = rawState && rawState.envId === envId ? rawState : null;
317
- const planCtx = { config, envId, stateStore, state, refresh, cwd };
264
+ const planCtx = { config, envId, cwd };
318
265
  for (const type of DEPLOY_ORDER) {
319
266
  if (!isEnabled(type))
320
267
  continue;
@@ -327,7 +274,7 @@ class DeployOrchestrator {
327
274
  * 按资源类型构建计划(拆分自 buildPlan,控制圈复杂度)
328
275
  */
329
276
  async buildPlanForType(type, ctx) {
330
- const { config, envId, stateStore, state, refresh, cwd } = ctx;
277
+ const { config, envId, cwd } = ctx;
331
278
  if (type === 'database') {
332
279
  return this.buildDatabasePlan(config, cwd, envId);
333
280
  }
@@ -365,10 +312,10 @@ class DeployOrchestrator {
365
312
  });
366
313
  }
367
314
  if (type === 'app') {
368
- return this.buildAppPlan({ config, envId, stateStore, state, refresh });
315
+ return this.buildAppPlan({ config, envId });
369
316
  }
370
317
  if (type === 'hosting') {
371
- return this.buildHostingPlans(config, ctx);
318
+ return this.buildHostingPlans(config);
372
319
  }
373
320
  return this.buildGatewayPlans(config, envId);
374
321
  }
@@ -425,100 +372,51 @@ class DeployOrchestrator {
425
372
  return [];
426
373
  }
427
374
  /**
428
- * app 计划:本地 state 配置一致 → skip;否则按云端存在性 → create / update
375
+ * app 计划:按云端存在性 → create / update
429
376
  */
430
377
  async buildAppPlan(options) {
431
- var _a;
432
- const { config, envId, stateStore, state, refresh } = options;
378
+ const { config, envId } = options;
433
379
  if (!config.app)
434
380
  return [];
435
381
  const name = config.app.serviceName || 'app';
436
- // 本地 state 配置一致 → skip(真增量,不重复构建版本)
437
- const appEntry = stateStore.buildAppConfig(config.app);
438
- const stateApp = (_a = state === null || state === void 0 ? void 0 : state.resources) === null || _a === void 0 ? void 0 : _a.app;
439
- const appUnchanged = !refresh &&
440
- stateApp !== undefined &&
441
- appEntry !== undefined &&
442
- this.deepEqual(stateApp, appEntry);
443
- if (appUnchanged) {
444
- return [
445
- {
446
- type: 'app',
447
- name,
448
- status: 'skip',
449
- action: `云应用 ${name} 配置未变更,跳过`
450
- }
451
- ];
452
- }
453
382
  const appExists = await this.appExists(name);
454
- const changes = this.appConfigChanges(stateApp, appEntry);
455
383
  return [
456
- Object.assign({ type: 'app', name, status: appExists ? 'update' : 'create', action: appExists
384
+ {
385
+ type: 'app',
386
+ name,
387
+ status: appExists ? 'update' : 'create',
388
+ action: appExists
457
389
  ? `覆盖更新云应用 ${name}`
458
- : `新建云应用 ${name}` }, (changes.length ? { changes } : {}))
390
+ : `新建云应用 ${name}`
391
+ }
459
392
  ];
460
393
  }
461
394
  /**
462
- * hosting 计划:本地 state 指纹 diff → 一致 skip / 有差异 deploy(带文件级明细)
395
+ * hosting 计划:每次直传覆盖(无本地指纹 skip)
463
396
  */
464
- async buildHostingPlans(config, ctx) {
465
- var _a, _b;
466
- const { stateStore, state, refresh, cwd } = ctx;
397
+ buildHostingPlans(config) {
467
398
  const plans = [];
468
399
  for (const h of config.hosting || []) {
469
400
  const name = h.name || 'default';
470
- const root = path_1.default.resolve(cwd, h.root || '.');
471
- const buildCommand = (0, framework_1.resolveBuildCommand)(h, root);
472
- // outputDir 与 StaticDeployer.deployHosting 完全复用同一解析逻辑
473
- const outputDir = (0, framework_1.resolveHostingOutputDir)(h, root);
474
- const stateHosting = (_b = (_a = state === null || state === void 0 ? void 0 : state.resources) === null || _a === void 0 ? void 0 : _a.hosting) === null || _b === void 0 ? void 0 : _b.find(s => s.name === name);
475
- // 懒加载 diff:size 预判 + 按需 hash(目录缺失 / 文件数超限 → 保守部署)
476
- let fileDiff = null;
477
- try {
478
- fileDiff = await stateStore.diffHostingFiles(outputDir, stateHosting === null || stateHosting === void 0 ? void 0 : stateHosting.files, {
479
- ignore: h.ignore || []
480
- });
481
- }
482
- catch (_c) {
483
- fileDiff = null;
484
- }
485
- const unchanged = !refresh && fileDiff !== null && fileDiff.unchanged;
486
- if (unchanged) {
487
- plans.push({
488
- type: 'hosting',
489
- name,
490
- status: 'skip',
491
- action: `静态托管 ${name} 产物未变更,跳过`
492
- });
493
- continue;
494
- }
495
401
  const hasBuild = !!h.buildCommand ||
496
402
  (!!h.framework && h.framework !== 'static' && h.framework !== 'custom');
497
403
  const details = [
498
404
  { label: '框架', value: h.framework || 'static' },
499
405
  {
500
406
  label: '构建',
501
- value: hasBuild
502
- ? `本地构建(${buildCommand || 'npm run build'})→ 上传 ${outputDir}`
503
- : '无构建,直接上传'
407
+ value: hasBuild ? '本地构建后上传' : '无构建,直接上传'
504
408
  },
505
409
  { label: '部署路径', value: h.deployPath || '/' }
506
410
  ];
507
- if (fileDiff === null || fileDiff === void 0 ? void 0 : fileDiff.overLimit) {
508
- details.push({ label: '指纹', value: '文件数超上限,降级为全量部署' });
509
- }
510
- plans.push(Object.assign({ type: 'hosting', name, status: 'deploy', action: hasBuild
411
+ plans.push({
412
+ type: 'hosting',
413
+ name,
414
+ status: 'deploy',
415
+ action: hasBuild
511
416
  ? `本地构建后直传静态托管 ${name}`
512
- : `直传覆盖静态托管 ${name}`, details }, (fileDiff && !fileDiff.overLimit
513
- ? {
514
- fileDiff: {
515
- added: fileDiff.added,
516
- modified: fileDiff.modified,
517
- deleted: fileDiff.deleted,
518
- totalChanged: fileDiff.totalChanged
519
- }
520
- }
521
- : {})));
417
+ : `直传覆盖静态托管 ${name}`,
418
+ details
419
+ });
522
420
  }
523
421
  return plans;
524
422
  }
@@ -628,26 +526,6 @@ class DeployOrchestrator {
628
526
  }
629
527
  }
630
528
  }
631
- /** 深比较(JSON 快照语义):仅用于 app 配置快照等简单对象 */
632
- deepEqual(a, b) {
633
- return JSON.stringify(a) === JSON.stringify(b);
634
- }
635
- /** app 配置差异明细(from → to) */
636
- appConfigChanges(prev, curr) {
637
- const changes = [];
638
- const keys = new Set([
639
- ...Object.keys(prev || {}),
640
- ...Object.keys(curr || {})
641
- ]);
642
- for (const key of keys) {
643
- const from = prev === null || prev === void 0 ? void 0 : prev[key];
644
- const to = curr === null || curr === void 0 ? void 0 : curr[key];
645
- if (from !== to) {
646
- changes.push({ field: String(key), from, to });
647
- }
648
- }
649
- return changes;
650
- }
651
529
  /**
652
530
  * 轻量 inline 守卫:配置合法 + 目录存在(validateProject 接线点)
653
531
  */