@cloudbase/manager-node 5.7.0-beta.2 → 5.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/jest.config.unit.js +14 -0
  2. package/lib/cloudApp/index.js +3 -12
  3. package/lib/environment.js +0 -11
  4. package/lib/function/index.js +67 -26
  5. package/lib/hosting/index.js +1 -4
  6. package/lib/index.js +0 -31
  7. package/lib/storage/index.js +7 -6
  8. package/lib/utils/index.js +1 -41
  9. package/package.json +1 -2
  10. package/types/cloudApp/index.d.ts +0 -4
  11. package/types/cloudApp/types.d.ts +6 -87
  12. package/types/env/type.d.ts +4 -4
  13. package/types/environment.d.ts +0 -7
  14. package/types/function/index.d.ts +3 -0
  15. package/types/function/types.d.ts +0 -15
  16. package/types/hosting/index.d.ts +0 -6
  17. package/types/index.d.ts +0 -18
  18. package/types/interfaces/function.interface.d.ts +1 -1
  19. package/types/storage/index.d.ts +0 -6
  20. package/types/utils/index.d.ts +0 -13
  21. package/lib/deploy/DatabaseDeployer.js +0 -238
  22. package/lib/deploy/DeployOrchestrator.js +0 -564
  23. package/lib/deploy/FunctionDeployer.js +0 -611
  24. package/lib/deploy/GatewayDeployer.js +0 -612
  25. package/lib/deploy/StateStore.js +0 -327
  26. package/lib/deploy/StaticDeployer.js +0 -335
  27. package/lib/deploy/domain.js +0 -39
  28. package/lib/deploy/framework.js +0 -105
  29. package/lib/deploy/function/artifact.js +0 -207
  30. package/lib/deploy/function/builders/cloud.js +0 -454
  31. package/lib/deploy/function/cam-preflight.js +0 -157
  32. package/lib/deploy/function/cloud-build-service.js +0 -170
  33. package/lib/deploy/function/config-guard.js +0 -588
  34. package/lib/deploy/function/docker-preflight.js +0 -87
  35. package/lib/deploy/function/image-preflight.js +0 -164
  36. package/lib/deploy/function/local-builder.js +0 -110
  37. package/lib/deploy/function/planner.js +0 -282
  38. package/lib/deploy/function/preflight.js +0 -264
  39. package/lib/deploy/types.js +0 -99
  40. package/lib/projectValidator/index.js +0 -479
  41. package/lib/projectValidator/types.js +0 -2
  42. package/types/deploy/DatabaseDeployer.d.ts +0 -70
  43. package/types/deploy/DeployOrchestrator.d.ts +0 -150
  44. package/types/deploy/FunctionDeployer.d.ts +0 -158
  45. package/types/deploy/GatewayDeployer.d.ts +0 -194
  46. package/types/deploy/StateStore.d.ts +0 -170
  47. package/types/deploy/StaticDeployer.d.ts +0 -104
  48. package/types/deploy/domain.d.ts +0 -17
  49. package/types/deploy/framework.d.ts +0 -23
  50. package/types/deploy/function/artifact.d.ts +0 -40
  51. package/types/deploy/function/builders/cloud.d.ts +0 -110
  52. package/types/deploy/function/cam-preflight.d.ts +0 -51
  53. package/types/deploy/function/cloud-build-service.d.ts +0 -10
  54. package/types/deploy/function/config-guard.d.ts +0 -41
  55. package/types/deploy/function/docker-preflight.d.ts +0 -17
  56. package/types/deploy/function/image-preflight.d.ts +0 -21
  57. package/types/deploy/function/local-builder.d.ts +0 -26
  58. package/types/deploy/function/planner.d.ts +0 -15
  59. package/types/deploy/function/preflight.d.ts +0 -9
  60. package/types/deploy/types.d.ts +0 -429
  61. package/types/projectValidator/index.d.ts +0 -43
  62. package/types/projectValidator/types.d.ts +0 -26
@@ -1,479 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- var __importDefault = (this && this.__importDefault) || function (mod) {
17
- return (mod && mod.__esModule) ? mod : { "default": mod };
18
- };
19
- Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.ProjectValidator = void 0;
21
- const fs_1 = __importDefault(require("fs"));
22
- const path_1 = __importDefault(require("path"));
23
- const toolbox_1 = require("@cloudbase/toolbox");
24
- class ProjectValidator {
25
- async validate(options = {}) {
26
- var _a;
27
- const cwd = path_1.default.resolve(options.cwd || process.cwd());
28
- const errors = [];
29
- const warnings = [];
30
- const loaded = await this.loadProjectConfig(cwd, {
31
- configPath: options.configPath,
32
- mode: options.mode,
33
- errors,
34
- });
35
- const version = String(((_a = loaded.rawConfig) === null || _a === void 0 ? void 0 : _a.version) || '2.1');
36
- if (loaded.rawConfig) {
37
- this.validateSchema(loaded.rawConfig, errors);
38
- }
39
- if (loaded.config) {
40
- this.validateEnvId(cwd, { raw: loaded.rawConfig || {}, resolved: loaded.config }, errors);
41
- this.validatePersonalRegistryConfig(loaded.config, errors);
42
- const resourceIssues = this.validateResources(cwd, loaded.config);
43
- errors.push(...resourceIssues.errors);
44
- warnings.push(...resourceIssues.warnings);
45
- this.validateConsistency(loaded.config, errors, warnings);
46
- }
47
- const summary = this.getSummary(loaded.config);
48
- return {
49
- valid: errors.length === 0,
50
- version,
51
- errors,
52
- warnings,
53
- summary,
54
- };
55
- }
56
- async loadProjectConfig(cwd, options) {
57
- const { configPath, mode, errors } = options;
58
- let filepath = configPath ? path_1.default.resolve(cwd, configPath) : undefined;
59
- try {
60
- if (!filepath) {
61
- const result = await (0, toolbox_1.searchConfig)(cwd);
62
- filepath = result === null || result === void 0 ? void 0 : result.filepath;
63
- }
64
- if (!filepath) {
65
- errors.push({
66
- level: 'error',
67
- field: 'cloudbaserc',
68
- message: '未找到 cloudbaserc 配置文件',
69
- fix: '在项目根目录新增 cloudbaserc.json / cloudbaserc.js / cloudbaserc.yaml / cloudbaserc.yml',
70
- });
71
- return {};
72
- }
73
- const rawConfig = (await (0, toolbox_1.loadConfig)({ configPath: filepath }));
74
- const config = (await toolbox_1.ConfigParser.parseRawConfig(rawConfig || {}, cwd, {
75
- mode,
76
- }));
77
- return {
78
- filepath,
79
- rawConfig,
80
- config,
81
- };
82
- }
83
- catch (e) {
84
- errors.push({
85
- level: 'error',
86
- field: 'cloudbaserc',
87
- message: `配置文件解析失败:${this.getErrorMessage(e)}`,
88
- fix: '检查 cloudbaserc 配置文件语法是否正确',
89
- });
90
- return { filepath };
91
- }
92
- }
93
- validateSchema(config, errors) {
94
- const result = (0, toolbox_1.validateCloudBaseConfigBySchema)(config, { version: '2.1' });
95
- result.errors.forEach((error) => {
96
- errors.push({
97
- level: 'error',
98
- field: this.normalizeSchemaPath(error.dataPath),
99
- message: `配置不符合 v2.1 Schema:${error.message}`,
100
- fix: `请检查 ${this.normalizeSchemaPath(error.dataPath) || 'cloudbaserc'} 字段`,
101
- });
102
- });
103
- }
104
- validateEnvId(cwd, config, errors) {
105
- if (this.hasPersonalCloudFunction(config.resolved)) {
106
- if (typeof config.raw.envId !== 'string' || !config.raw.envId.trim()) {
107
- errors.push({
108
- level: 'error',
109
- field: 'envId',
110
- message: '个人版 TCR 云端构建必须在 cloudbaserc 中显式声明 envId',
111
- fix: '声明 envId: "{{env.ENV_ID}}",并在 .env* 中设置 ENV_ID',
112
- });
113
- return;
114
- }
115
- if (typeof config.resolved.envId !== 'string' || !config.resolved.envId.trim()) {
116
- errors.push({
117
- level: 'error',
118
- field: 'envId',
119
- message: '个人版 TCR 云端构建的 envId 未赋值',
120
- fix: '在 .env* 中设置 cloudbaserc 通过 {{env.ENV_ID}} 引用的 ENV_ID',
121
- });
122
- }
123
- return;
124
- }
125
- if (config.resolved.envId || this.getLinkedEnvId(cwd)) {
126
- return;
127
- }
128
- errors.push({
129
- level: 'error',
130
- field: 'envId',
131
- message: '缺少 envId,且未检测到 .tcb/project.json 关联环境',
132
- fix: '在 cloudbaserc 中配置 envId,或先执行环境关联流程',
133
- });
134
- }
135
- /**
136
- * 个人版 TCR 的 username/password/namespace 必须在 cloudbaserc 中显式声明。
137
- * ConfigParser 已先解析 {{env.*}},因此这里同时承担缺失 env 值的前置校验。
138
- */
139
- validatePersonalRegistryConfig(config, errors) {
140
- ;
141
- (config.functions || []).forEach((func, index) => {
142
- var _a;
143
- if (!this.isPersonalCloudFunction(func)) {
144
- return;
145
- }
146
- const build = (_a = func.imageConfig) === null || _a === void 0 ? void 0 : _a.build;
147
- const namespace = build === null || build === void 0 ? void 0 : build.namespace;
148
- if (typeof namespace !== 'string' || !namespace.trim()) {
149
- errors.push({
150
- level: 'error',
151
- resource: `functions[${index}]`,
152
- field: 'imageConfig.build.namespace',
153
- message: '个人版 TCR 缺少 namespace,或对应环境变量未赋值',
154
- fix: '声明 namespace: "{{env.TCB_TCR_NAMESPACE}}",并在 .env* 中设置该变量',
155
- });
156
- }
157
- const credential = build === null || build === void 0 ? void 0 : build.registryCredential;
158
- if (!credential || typeof credential !== 'object') {
159
- errors.push({
160
- level: 'error',
161
- resource: `functions[${index}]`,
162
- field: 'imageConfig.build.registryCredential',
163
- message: '个人版 TCR 缺少 username/password 凭证声明',
164
- fix: '在 registryCredential 中使用 {{env.TCB_TCR_USERNAME}} 与 {{env.TCB_TCR_PASSWORD}}',
165
- });
166
- return;
167
- }
168
- const { username, password } = credential;
169
- if (typeof username !== 'string' || !/^\d{5,20}$/.test(username.trim())) {
170
- errors.push({
171
- level: 'error',
172
- resource: `functions[${index}]`,
173
- field: 'imageConfig.build.registryCredential.username',
174
- message: '个人版 TCR username 未赋值或不是有效腾讯云账号 ID',
175
- fix: '声明 username: "{{env.TCB_TCR_USERNAME}}",并在 .env* 中设置该变量',
176
- });
177
- }
178
- if (typeof password !== 'string' || !password || password.length > 16 * 1024) {
179
- errors.push({
180
- level: 'error',
181
- resource: `functions[${index}]`,
182
- field: 'imageConfig.build.registryCredential.password',
183
- message: '个人版 TCR password 未赋值或长度不合法',
184
- fix: '声明 password: "{{env.TCB_TCR_PASSWORD}}",并在 .env* 中设置该变量',
185
- });
186
- }
187
- });
188
- }
189
- hasPersonalCloudFunction(config) {
190
- return (config.functions || []).some((func) => this.isPersonalCloudFunction(func));
191
- }
192
- isPersonalCloudFunction(func) {
193
- var _a;
194
- return (func.type === 'HTTP' &&
195
- func.buildStrategy === 'cloud' &&
196
- ((_a = func.imageConfig) === null || _a === void 0 ? void 0 : _a.imageType) === 'personal');
197
- }
198
- validateResources(cwd, config) {
199
- const errors = [];
200
- const warnings = [];
201
- const functionRoot = config.functionRoot || './functions';
202
- (config.functions || []).forEach((func, index) => {
203
- const resource = `functions[${index}]`;
204
- const functionDir = path_1.default.resolve(cwd, func.dir || path_1.default.join(functionRoot, func.name));
205
- if (!fs_1.default.existsSync(functionDir)) {
206
- errors.push({
207
- level: 'error',
208
- resource,
209
- field: 'dir',
210
- message: `函数目录不存在:${path_1.default.relative(cwd, functionDir)}`,
211
- fix: '创建函数目录,或修正 functions[].dir / functionRoot 配置',
212
- });
213
- return;
214
- }
215
- const functionType = func.type || 'Event';
216
- const buildStrategy = func.buildStrategy || 'zip';
217
- if (functionType === 'HTTP' && buildStrategy === 'zip') {
218
- const bootstrapPath = path_1.default.join(functionDir, 'scf_bootstrap');
219
- if (!fs_1.default.existsSync(bootstrapPath)) {
220
- errors.push({
221
- level: 'error',
222
- resource,
223
- field: 'scf_bootstrap',
224
- message: '缺少 scf_bootstrap 文件(HTTP 函数 zip 部署必须)',
225
- fix: '在函数目录中新增 scf_bootstrap,并确保其具备执行权限',
226
- });
227
- }
228
- }
229
- if (functionType === 'Event') {
230
- const handlerFile = `${(func.handler || 'index.main').split('.')[0]}.js`;
231
- if (!fs_1.default.existsSync(path_1.default.join(functionDir, handlerFile))) {
232
- warnings.push({
233
- level: 'warning',
234
- resource,
235
- field: 'handler',
236
- message: `未找到入口文件 ${handlerFile}`,
237
- fix: '确认 handler 配置与函数入口文件一致',
238
- });
239
- }
240
- }
241
- });
242
- (config.hosting || []).forEach((hosting, index) => {
243
- const root = path_1.default.resolve(cwd, hosting.root || '.');
244
- if (!fs_1.default.existsSync(root)) {
245
- errors.push({
246
- level: 'error',
247
- resource: `hosting[${index}]`,
248
- field: 'root',
249
- message: `静态托管根目录不存在:${path_1.default.relative(cwd, root)}`,
250
- fix: '创建目录,或修正 hosting[].root 配置',
251
- });
252
- }
253
- });
254
- if (config.database) {
255
- const migrations = config.database.migrations || './cloudbase/migrations';
256
- if (path_1.default.isAbsolute(migrations) || migrations.startsWith('/')) {
257
- errors.push({
258
- level: 'error',
259
- resource: 'database',
260
- field: 'migrations',
261
- message: 'database.migrations 仅支持相对路径',
262
- fix: '请使用相对路径,例如 ./cloudbase/migrations',
263
- });
264
- }
265
- else {
266
- const migrationsDir = path_1.default.resolve(cwd, migrations);
267
- if (!fs_1.default.existsSync(migrationsDir)) {
268
- errors.push({
269
- level: 'error',
270
- resource: 'database',
271
- field: 'migrations',
272
- message: `数据库迁移目录不存在:${path_1.default.relative(cwd, migrationsDir)}`,
273
- fix: '创建迁移目录,或修正 database.migrations 配置',
274
- });
275
- }
276
- }
277
- }
278
- return { errors, warnings };
279
- }
280
- validateConsistency(config, errors, warnings = []) {
281
- this.validateUniqueNames('functions', config.functions || [], errors);
282
- this.validateUniqueNames('hosting', config.hosting || [], errors);
283
- this.validateUniqueField('hosting', config.hosting || [], { field: 'deployPath', errors });
284
- this.validateGatewayTargets(config, errors);
285
- this.validateGatewayPathRewriteConflicts(config, warnings);
286
- this.validateHostingSubPathBaseRisks(config, warnings);
287
- this.validateSpaFallbackPrefixIsolation(config, warnings);
288
- }
289
- /**
290
- * 检测 hosting 路由中 enablePathTransmission=true 与自动生成 pathRewrite 的冲突
291
- *
292
- * hosting 目标未显式配置 pathRewrite 时,部署会自动生成 PathRewrite.Prefix = hosting.deployPath。
293
- * 若同时显式开启 enablePathTransmission=true,路径透传会覆盖路径重写,导致请求打到错误的托管路径(404)。
294
- */
295
- validateGatewayPathRewriteConflicts(config, warnings) {
296
- var _a;
297
- const hostingMap = new Map((config.hosting || []).map((h) => [h.name, h]));
298
- (((_a = config.gateway) === null || _a === void 0 ? void 0 : _a.routes) || []).forEach((route, index) => {
299
- var _a, _b, _c;
300
- if (!((_a = route.target) === null || _a === void 0 ? void 0 : _a.startsWith('hosting:')))
301
- return;
302
- // 用户显式配置了 pathRewrite → 无自动重写,无冲突
303
- if (((_b = route.pathRewrite) === null || _b === void 0 ? void 0 : _b.prefix) || ((_c = route.pathRewrite) === null || _c === void 0 ? void 0 : _c.staticStorePrefix))
304
- return;
305
- // 未显式开启透传 → 无冲突
306
- if (route.enablePathTransmission !== true)
307
- return;
308
- const hostingName = route.target.slice('hosting:'.length);
309
- const hosting = hostingMap.get(hostingName);
310
- // hosting 无 deployPath → 不会自动生成 PathRewrite,不冲突
311
- if (!(hosting === null || hosting === void 0 ? void 0 : hosting.deployPath))
312
- return;
313
- warnings.push({
314
- level: 'warning',
315
- resource: `gateway.routes[${index}]`,
316
- field: 'enablePathTransmission',
317
- message: `hosting 路由 ${route.target} 未显式配置 pathRewrite,部署将自动生成 ` +
318
- `PathRewrite.Prefix=${hosting.deployPath},但 enablePathTransmission=true 会覆盖路径重写,` +
319
- `导致请求打不到托管实际部署路径`,
320
- fix: '删除 enablePathTransmission,让自动生成的 pathRewrite 生效;' +
321
- '或显式配置 pathRewrite.prefix 指向托管部署路径',
322
- });
323
- });
324
- }
325
- /**
326
- * 子路径部署(deployPath !== '/')下,前端构建产物若仍使用根路径 base(如 Vite 默认 '/'),
327
- * 会导致资源请求落到 /assets/* 而非 /{deployPath}/assets/*,出现 404/白屏。
328
- *
329
- * 这里在“可能存在构建步骤”的 hosting 配置上给出防坑告警。
330
- */
331
- validateHostingSubPathBaseRisks(config, warnings) {
332
- ;
333
- (config.hosting || []).forEach((hosting, index) => {
334
- const deployPath = typeof hosting.deployPath === 'string' ? hosting.deployPath.trim() : '';
335
- if (!deployPath || deployPath === '/')
336
- return;
337
- const framework = typeof hosting.framework === 'string' ? hosting.framework.trim().toLowerCase() : '';
338
- const hasBuildStep = (typeof hosting.buildCommand === 'string' && hosting.buildCommand.trim().length > 0) ||
339
- (!!framework && framework !== 'static' && framework !== 'custom');
340
- if (!hasBuildStep)
341
- return;
342
- warnings.push({
343
- level: 'warning',
344
- resource: `hosting[${index}]`,
345
- field: 'deployPath',
346
- message: `静态托管部署在子路径 ${deployPath},请确认前端构建 base 与子路径匹配,` +
347
- `否则资源可能请求到根路径(如 /assets/*)导致 404/白屏`,
348
- fix: "建议将前端 base 配置为 './' 或与 deployPath 对齐(例如 '/serviceName/')",
349
- });
350
- });
351
- }
352
- /**
353
- * spaFallback 为站点级回退能力,不是按网关前缀独立生效。
354
- * 同一 hosting 在 spaFallback=true 下被多个网关前缀复用时,容易出现不同前缀回退串扰。
355
- */
356
- validateSpaFallbackPrefixIsolation(config, warnings) {
357
- var _a;
358
- const routePathsByHosting = new Map();
359
- (((_a = config.gateway) === null || _a === void 0 ? void 0 : _a.routes) || []).forEach((route) => {
360
- var _a;
361
- if (!((_a = route.target) === null || _a === void 0 ? void 0 : _a.startsWith('hosting:')))
362
- return;
363
- const hostingName = route.target.slice('hosting:'.length);
364
- const pathValue = typeof route.path === 'string' ? route.path.trim() : '';
365
- if (!pathValue)
366
- return;
367
- if (!routePathsByHosting.has(hostingName)) {
368
- routePathsByHosting.set(hostingName, new Set());
369
- }
370
- routePathsByHosting.get(hostingName).add(pathValue);
371
- });
372
- (config.hosting || []).forEach((hosting, index) => {
373
- if (hosting.spaFallback !== true)
374
- return;
375
- if (!hosting.name)
376
- return;
377
- const routePaths = [...(routePathsByHosting.get(hosting.name) || new Set())];
378
- if (routePaths.length <= 1)
379
- return;
380
- warnings.push({
381
- level: 'warning',
382
- resource: `hosting[${index}]`,
383
- field: 'spaFallback',
384
- message: `hosting:${hosting.name} 在 spaFallback=true 下被多个网关前缀复用(${routePaths.join(', ')}),` +
385
- '可能出现前缀间 404 回退串扰',
386
- fix: '建议将不同前缀拆分为独立 hosting 服务,或为每个前缀显式配置独立回退策略',
387
- });
388
- });
389
- }
390
- validateUniqueNames(resource, items, errors) {
391
- const names = new Map();
392
- items.forEach((item, index) => {
393
- if (!item.name)
394
- return;
395
- const prev = names.get(item.name);
396
- if (typeof prev === 'number') {
397
- errors.push({
398
- level: 'error',
399
- resource: `${resource}[${index}]`,
400
- field: 'name',
401
- message: `${resource} 名称重复:${item.name}`,
402
- fix: `修改 ${resource}[${index}].name,避免与 ${resource}[${prev}].name 重复`,
403
- });
404
- }
405
- else {
406
- names.set(item.name, index);
407
- }
408
- });
409
- }
410
- validateUniqueField(resource, items, options) {
411
- const { field, errors } = options;
412
- const values = new Map();
413
- items.forEach((item, index) => {
414
- const value = item[field];
415
- if (!value)
416
- return;
417
- const prev = values.get(value);
418
- if (typeof prev === 'number') {
419
- errors.push({
420
- level: 'error',
421
- resource: `${resource}[${index}]`,
422
- field,
423
- message: `${resource}.${field} 配置重复:${value}`,
424
- fix: `修改 ${resource}[${index}].${field},避免与 ${resource}[${prev}].${field} 重复`,
425
- });
426
- }
427
- else {
428
- values.set(value, index);
429
- }
430
- });
431
- }
432
- validateGatewayTargets(config, errors) {
433
- var _a;
434
- const functionNames = new Set((config.functions || []).map((item) => item.name));
435
- const hostingNames = new Set((config.hosting || []).map((item) => item.name));
436
- (((_a = config.gateway) === null || _a === void 0 ? void 0 : _a.routes) || []).forEach((route, index) => {
437
- const [type, name] = route.target.split(':');
438
- if ((type === 'function' && !functionNames.has(name)) || (type === 'hosting' && !hostingNames.has(name))) {
439
- errors.push({
440
- level: 'error',
441
- resource: `gateway.routes[${index}]`,
442
- field: 'target',
443
- message: `网关目标不存在:${route.target}`,
444
- fix: '确认 gateway.routes[].target 引用了已声明的 function 或 hosting 资源',
445
- });
446
- }
447
- });
448
- }
449
- getSummary(config) {
450
- var _a, _b, _c, _d;
451
- return {
452
- functions: ((_a = config === null || config === void 0 ? void 0 : config.functions) === null || _a === void 0 ? void 0 : _a.length) || 0,
453
- hosting: ((_b = config === null || config === void 0 ? void 0 : config.hosting) === null || _b === void 0 ? void 0 : _b.length) || 0,
454
- databases: (config === null || config === void 0 ? void 0 : config.database) ? 1 : 0,
455
- gateways: ((_d = (_c = config === null || config === void 0 ? void 0 : config.gateway) === null || _c === void 0 ? void 0 : _c.routes) === null || _d === void 0 ? void 0 : _d.length) || 0,
456
- };
457
- }
458
- getLinkedEnvId(cwd) {
459
- const projectPath = path_1.default.join(cwd, '.tcb', 'project.json');
460
- if (!fs_1.default.existsSync(projectPath)) {
461
- return '';
462
- }
463
- try {
464
- const project = JSON.parse(fs_1.default.readFileSync(projectPath, 'utf8'));
465
- return project.envId || project.EnvId || '';
466
- }
467
- catch (e) {
468
- return '';
469
- }
470
- }
471
- normalizeSchemaPath(dataPath) {
472
- return dataPath.replace(/^\//, '').replace(/\//g, '.');
473
- }
474
- getErrorMessage(error) {
475
- return (error === null || error === void 0 ? void 0 : error.message) || String(error);
476
- }
477
- }
478
- exports.ProjectValidator = ProjectValidator;
479
- __exportStar(require("./types"), exports);
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,70 +0,0 @@
1
- import { Environment } from '../environment';
2
- import { IPGUserMigrationInput } from '../database/type';
3
- /** 数据库迁移部署配置(对应 cloudbaserc.json 中的 database 块) */
4
- export interface IDatabaseDeployConfig {
5
- /** 数据库类型(v2.1 schema: 目前仅支持 postgresql) */
6
- type: 'postgresql';
7
- /** migration SQL 文件目录(相对项目根) */
8
- migrations?: string;
9
- }
10
- /** Plan 中单条 migration 的状态 */
11
- export interface IDatabasePlanItem {
12
- /** 14位数字时间串 */
13
- version: string;
14
- /** migration 名称 */
15
- name: string;
16
- /** pending=待执行;applied=已应用;conflict=冲突 */
17
- status: 'pending' | 'applied' | 'conflict';
18
- /** 冲突原因(仅 conflict 时有值) */
19
- reason?: string;
20
- }
21
- /** deploy 返回结果 */
22
- export interface IDatabaseDeployResult {
23
- /** 异步任务 ID(无pending时为空字符串) */
24
- taskId: string;
25
- }
26
- export declare class DatabaseDeployer {
27
- private environment;
28
- constructor(environment: Environment);
29
- /**
30
- * 从本地目录解析 migration 文件列表
31
- *
32
- * 文件名格式:{14位时间戳}_{小写下划线名称}.sql
33
- * 例如:20260526000000_create_users.sql
34
- *
35
- * 按文件名升序排列(即按版本号从早到晚)
36
- */
37
- parseMigrationFiles(migrationsDir: string): IPGUserMigrationInput[];
38
- /**
39
- * 生成部署计划
40
- *
41
- * 调用 previewPGUserMigrations 获取云端对比结果,
42
- * 返回每条migration 的状态(pending / applied / conflict)
43
- */
44
- plan(config: IDatabaseDeployConfig, cwd: string, envId: string): Promise<IDatabasePlanItem[]>;
45
- /**
46
- * 执行数据库迁移
47
- *
48
- * 流程:
49
- * 1. 解析本地 migration 文件
50
- * 2. 预览确认无冲突(有冲突直接 throw,中断整个部署)
51
- * 3. 无 pending 则跳过
52
- * 4. 提交 pushPGUserMigrations
53
- * 5. 轮询describeTaskResult 等待完成
54
- */
55
- deploy(config: IDatabaseDeployConfig, cwd: string, envId: string): Promise<IDatabaseDeployResult>;
56
- /**
57
- * 解析 migrations 目录路径
58
- * - 未配置:默认 ./cloudbase/migrations
59
- * - 仅允许相对路径(禁止 / 开头)
60
- */
61
- private resolveMigrationsDir;
62
- /**
63
- * 校验 database.type(当前仅支持 postgresql)
64
- */
65
- private assertDatabaseType;
66
- /**
67
- * 轮询等待异步任务完成
68
- */
69
- private waitForTask;
70
- }