@geek-fun/serverlessinsight 0.3.0 → 0.3.2

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 (47) hide show
  1. package/dist/package.json +40 -37
  2. package/dist/src/commands/deploy.js +2 -2
  3. package/dist/src/commands/destroy.js +2 -1
  4. package/dist/src/commands/index.js +32 -16
  5. package/dist/src/commands/local.js +35 -0
  6. package/dist/src/commands/template.js +3 -3
  7. package/dist/src/commands/validate.js +3 -2
  8. package/dist/src/common/constants.js +5 -2
  9. package/dist/src/common/{actionContext.js → context.js} +30 -9
  10. package/dist/src/common/iacHelper.js +54 -27
  11. package/dist/src/common/index.d.ts +1 -1
  12. package/dist/src/common/index.js +1 -1
  13. package/dist/src/common/rosAssets.js +34 -8
  14. package/dist/src/common/rosClient.js +6 -4
  15. package/dist/src/parser/index.js +2 -0
  16. package/dist/src/parser/tableParser.js +41 -0
  17. package/dist/src/stack/deploy.js +30 -13
  18. package/dist/src/stack/localStack/event.js +38 -0
  19. package/dist/src/stack/localStack/index.d.ts +2 -0
  20. package/dist/src/stack/localStack/index.js +23 -0
  21. package/dist/src/stack/rfsStack/index.d.ts +2 -2
  22. package/dist/src/stack/rfsStack/index.js +2 -1
  23. package/dist/src/stack/rosStack/bootstrap.js +157 -8
  24. package/dist/src/stack/rosStack/bucket.js +13 -12
  25. package/dist/src/stack/rosStack/database.js +11 -12
  26. package/dist/src/stack/rosStack/event.js +21 -21
  27. package/dist/src/stack/rosStack/function.js +66 -46
  28. package/dist/src/stack/rosStack/index.d.ts +2 -2
  29. package/dist/src/stack/rosStack/index.js +6 -3
  30. package/dist/src/stack/rosStack/stage.js +1 -1
  31. package/dist/src/stack/rosStack/table.js +95 -0
  32. package/dist/src/stack/rosStack/tag.js +1 -1
  33. package/dist/src/stack/rosStack/vars.js +3 -3
  34. package/dist/src/types/domains/table.js +16 -0
  35. package/dist/src/types/index.d.ts +5 -0
  36. package/dist/src/validator/functionSchema.js +1 -1
  37. package/dist/src/validator/iacSchema.js +2 -0
  38. package/dist/src/validator/rootSchema.js +3 -0
  39. package/dist/src/validator/tableschema.js +72 -0
  40. package/dist/tsconfig.tsbuildinfo +1 -1
  41. package/layers/si-bootstrap-sdk/Dockerfile-aliyuncli +12 -0
  42. package/layers/si-bootstrap-sdk/README.md +1 -0
  43. package/layers/si-bootstrap-sdk/package-lock.json +875 -0
  44. package/layers/si-bootstrap-sdk/package.json +33 -0
  45. package/package.json +40 -37
  46. package/samples/aliyun-poc-fc-gpu.yml +23 -22
  47. package/samples/aliyun-poc-table.yml +48 -0
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geek-fun/serverlessinsight",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Full life cycle cross providers serverless application management for your fast-growing business.",
5
5
  "homepage": "https://serverlessinsight.geekfun.club",
6
6
  "main": "dist/src/index.js",
@@ -49,51 +49,54 @@
49
49
  "function"
50
50
  ],
51
51
  "dependencies": {
52
- "@alicloud/ims20190815": "^2.1.4",
53
- "@alicloud/openapi-client": "^0.4.12",
54
- "@alicloud/ros-cdk-apigateway": "^1.6.0",
55
- "@alicloud/ros-cdk-core": "^1.6.0",
56
- "@alicloud/ros-cdk-dns": "^1.6.0",
57
- "@alicloud/ros-cdk-ecs": "^1.6.0",
58
- "@alicloud/ros-cdk-elasticsearchserverless": "^1.6.0",
59
- "@alicloud/ros-cdk-fc3": "^1.6.0",
60
- "@alicloud/ros-cdk-nas": "^1.6.0",
61
- "@alicloud/ros-cdk-oss": "^1.6.0",
62
- "@alicloud/ros-cdk-ossdeployment": "^1.6.0",
63
- "@alicloud/ros-cdk-ram": "^1.6.0",
64
- "@alicloud/ros-cdk-rds": "^1.6.0",
65
- "@alicloud/ros-cdk-sls": "^1.6.0",
66
- "@alicloud/ros20190910": "^3.5.2",
52
+ "@alicloud/ims20190815": "^2.3.2",
53
+ "@alicloud/openapi-client": "^0.4.15",
54
+ "@alicloud/ros-cdk-apigateway": "^1.10.0",
55
+ "@alicloud/ros-cdk-core": "^1.10.0",
56
+ "@alicloud/ros-cdk-dns": "^1.10.0",
57
+ "@alicloud/ros-cdk-ecs": "^1.10.0",
58
+ "@alicloud/ros-cdk-elasticsearchserverless": "^1.10.0",
59
+ "@alicloud/ros-cdk-fc3": "^1.10.0",
60
+ "@alicloud/ros-cdk-nas": "^1.10.0",
61
+ "@alicloud/ros-cdk-oss": "^1.10.0",
62
+ "@alicloud/ros-cdk-ossdeployment": "^1.10.0",
63
+ "@alicloud/ros-cdk-ots": "^1.10.0",
64
+ "@alicloud/ros-cdk-ram": "^1.10.0",
65
+ "@alicloud/ros-cdk-rds": "^1.10.0",
66
+ "@alicloud/ros-cdk-ros": "^1.10.0",
67
+ "@alicloud/ros-cdk-sls": "^1.10.0",
68
+ "@alicloud/ros-cdk-vpc": "^1.10.0",
69
+ "@alicloud/ros20190910": "^3.6.0",
67
70
  "ajv": "^8.17.1",
68
- "ali-oss": "^6.22.0",
69
- "chalk": "^5.4.1",
70
- "commander": "^12.1.0",
71
+ "ali-oss": "^6.23.0",
72
+ "chalk": "^5.6.2",
73
+ "commander": "^14.0.1",
71
74
  "i": "^0.3.7",
72
- "i18n": "^0.15.1",
75
+ "i18n": "^0.15.2",
73
76
  "jszip": "^3.10.1",
74
77
  "lodash": "^4.17.21",
75
- "npm": "^11.1.0",
76
- "pino": "^9.5.0",
77
- "pino-pretty": "^13.0.0",
78
- "yaml": "^2.7.0"
78
+ "npm": "^11.5.2",
79
+ "pino": "^9.9.4",
80
+ "pino-pretty": "^13.1.1",
81
+ "yaml": "^2.8.1"
79
82
  },
80
83
  "devDependencies": {
81
84
  "@types/ali-oss": "^6.16.11",
82
85
  "@types/i18n": "^0.13.12",
83
- "@types/jest": "^29.5.14",
84
- "@types/lodash": "^4.17.13",
85
- "@types/node": "^22.10.1",
86
- "@typescript-eslint/eslint-plugin": "^8.17.0",
87
- "@typescript-eslint/parser": "^8.17.0",
88
- "eslint": "^9.16.0",
89
- "eslint-config-prettier": "^9.1.0",
90
- "eslint-plugin-prettier": "^5.2.1",
91
- "globals": "^15.13.0",
86
+ "@types/jest": "^30.0.0",
87
+ "@types/lodash": "^4.17.20",
88
+ "@types/node": "^24.3.0",
89
+ "@typescript-eslint/eslint-plugin": "^8.39.1",
90
+ "@typescript-eslint/parser": "^8.39.1",
91
+ "eslint": "^9.33.0",
92
+ "eslint-config-prettier": "^10.1.8",
93
+ "eslint-plugin-prettier": "^5.5.4",
94
+ "globals": "^16.3.0",
92
95
  "husky": "^9.1.7",
93
- "jest": "^29.7.0",
94
- "prettier": "^3.4.2",
95
- "ts-jest": "^29.2.5",
96
+ "jest": "^30.0.5",
97
+ "prettier": "^3.6.2",
98
+ "ts-jest": "^29.4.1",
96
99
  "ts-node": "^10.9.2",
97
- "typescript": "^5.7.2"
100
+ "typescript": "^5.9.2"
98
101
  }
99
102
  }
@@ -8,9 +8,9 @@ const deploy = async (stackName, options) => {
8
8
  common_1.logger.info('Validating yaml...');
9
9
  const iac = (0, parser_1.parseYaml)((0, common_1.getIacLocation)(options.location));
10
10
  common_1.logger.info('Yaml is valid! 🎉');
11
- const context = (0, common_1.constructActionContext)({ ...options, stackName, iacProvider: iac.provider });
11
+ await (0, common_1.setContext)({ ...options, stackName, iacProvider: iac.provider }, true);
12
12
  common_1.logger.info('Deploying stack...');
13
- await (0, stack_1.deployStack)(stackName, iac, context);
13
+ await (0, stack_1.deployStack)(stackName, iac);
14
14
  common_1.logger.info('Stack deployed! 🎉');
15
15
  };
16
16
  exports.deploy = deploy;
@@ -5,7 +5,8 @@ const common_1 = require("../common");
5
5
  const parser_1 = require("../parser");
6
6
  const destroyStack = async (stackName, options) => {
7
7
  const iac = (0, parser_1.parseYaml)((0, common_1.getIacLocation)(options.location));
8
- const context = (0, common_1.constructActionContext)({ stackName, ...options, iacProvider: iac.provider });
8
+ await (0, common_1.setContext)({ stackName, ...options, iacProvider: iac.provider }, true);
9
+ const context = (0, common_1.getContext)();
9
10
  common_1.logger.info(`Destroying stack: ${stackName}, provider: ${context.provider}, region: ${context.region}...`);
10
11
  await (0, common_1.rosStackDelete)(context);
11
12
  };
@@ -7,15 +7,16 @@ const validate_1 = require("./validate");
7
7
  const deploy_1 = require("./deploy");
8
8
  const template_1 = require("./template");
9
9
  const destroy_1 = require("./destroy");
10
- const common_2 = require("../common");
10
+ const local_1 = require("./local");
11
11
  const program = new commander_1.Command();
12
12
  program.name('si').description('CLI for ServerlessInsight').version((0, common_1.getVersion)());
13
13
  program
14
14
  .command('show')
15
15
  .description('show string')
16
16
  .action(async (options) => {
17
- const context = (0, common_1.constructActionContext)({ ...options });
18
- const result = await (0, common_2.getIamInfo)(context);
17
+ await (0, common_1.setContext)({ ...options });
18
+ const context = (0, common_1.getContext)();
19
+ const result = await (0, common_1.getIamInfo)(context);
19
20
  console.log('result:', JSON.stringify(result));
20
21
  });
21
22
  program
@@ -23,9 +24,9 @@ program
23
24
  .description('validate serverless Iac yaml')
24
25
  .option('-f, --file <path>', 'specify the yaml file')
25
26
  .option('-s, --stage <stage>', 'specify the stage')
26
- .action((stackName, { file, stage }) => {
27
+ .action(async (stackName, { file, stage }) => {
27
28
  common_1.logger.debug('log command info');
28
- (0, validate_1.validate)(stackName, { stage, location: file });
29
+ await (0, validate_1.validate)(stackName, { stage, location: file });
29
30
  });
30
31
  program
31
32
  .command('deploy <stackName>')
@@ -33,10 +34,10 @@ program
33
34
  .option('-f, --file <path>', 'specify the yaml file')
34
35
  .option('-s, --stage <stage>', 'specify the stage')
35
36
  .option('-r, --region <region>', 'specify the region')
36
- .option('-pr, --provider <provider>', 'specify the provider')
37
- .option('-ak, --accessKeyId <accessKeyId>', 'specify the AccessKeyId')
38
- .option('-as, --accessKeySecret <accessKeySecret>', 'specify the AccessKeySecret')
39
- .option('-at, --securityToken <securityToken>', 'specify the SecurityToken')
37
+ .option('-v, --provider <provider>', 'specify the provider')
38
+ .option('-k, --accessKeyId <accessKeyId>', 'specify the AccessKeyId')
39
+ .option('-x, --accessKeySecret <accessKeySecret>', 'specify the AccessKeySecret')
40
+ .option('-n, --securityToken <securityToken>', 'specify the SecurityToken')
40
41
  .option('-p, --parameter <key=value>', 'override parameters', (value, previous) => {
41
42
  const [key, val] = value.split('=');
42
43
  previous[key] = val;
@@ -56,21 +57,21 @@ program
56
57
  });
57
58
  program
58
59
  .command('template <stackName>')
59
- .description('print ROS template')
60
+ .description('print platform specific infrastructure as code template')
60
61
  .option('-f, --file <path>', 'specify the yaml file')
61
62
  .option('-s, --stage <stage>', 'specify the stage')
62
63
  .option('-t, --format <type>', 'output content type (JSON or YAML)', 'JSON')
63
- .action((stackName, { format, file, stage }) => {
64
- (0, template_1.template)(stackName, { format, location: file, stage });
64
+ .action(async (stackName, { format, file, stage }) => {
65
+ await (0, template_1.template)(stackName, { format, location: file, stage });
65
66
  });
66
67
  program
67
68
  .command('destroy <stackName>')
68
69
  .option('-f, --file <path>', 'specify the yaml file')
69
70
  .option('-r, --region <region>', 'specify the region')
70
- .option('-pr, --provider <provider>', 'specify the provider')
71
- .option('-ak, --accessKeyId <accessKeyId>', 'specify the AccessKeyId')
72
- .option('-as, --accessKeySecret <accessKeySecret>', 'specify the AccessKeySecret')
73
- .option('-at, --securityToken <securityToken>', 'specify the SecurityToken')
71
+ .option('-v, --provider <provider>', 'specify the provider')
72
+ .option('-k, --accessKeyId <accessKeyId>', 'specify the AccessKeyId')
73
+ .option('-x, --accessKeySecret <accessKeySecret>', 'specify the AccessKeySecret')
74
+ .option('-n, --securityToken <securityToken>', 'specify the SecurityToken')
74
75
  .description('destroy serverless stack')
75
76
  .action(async (stackName, { file, region, provider, accessKeyId, accessKeySecret, securityToken }) => {
76
77
  await (0, destroy_1.destroyStack)(stackName, {
@@ -82,4 +83,19 @@ program
82
83
  securityToken,
83
84
  });
84
85
  });
86
+ program
87
+ .command('local <stackName>')
88
+ .description('run Serverless application locally for debugging')
89
+ .option('-s, --stage <stage>', 'specify the stage', 'default')
90
+ .option('-p, --port <port>', 'specify the port', '3000')
91
+ .option('-d, --debug', 'enable debug mode')
92
+ .option('-w, --watch', 'enable file watch', true)
93
+ .action(async (stackName, { stage, port, debug, watch }) => {
94
+ await (0, local_1.runLocal)(stackName, {
95
+ stage,
96
+ port: Number(port) || 3000,
97
+ debug: !!debug,
98
+ watch: typeof watch === 'boolean' ? watch : true,
99
+ });
100
+ });
85
101
  program.parse();
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runLocal = void 0;
4
+ const common_1 = require("../common");
5
+ const localStack_1 = require("../stack/localStack");
6
+ const runLocal = async (stackName, opts) => {
7
+ const { stage, port, debug, watch } = opts;
8
+ await (0, common_1.setContext)({ stage });
9
+ common_1.logger.info(`run-local starting: stack=${stackName} stage=${stage} port=${port} debug=${debug} watch=${watch}`);
10
+ await (0, localStack_1.startLocalStack)();
11
+ // if (watch) {
12
+ // const cwd = process.cwd();
13
+ // try {
14
+ // fs.watch(cwd, { recursive: true }, (eventType, filename) => {
15
+ // if (!filename) return;
16
+ // const filePath = path.join(cwd, filename);
17
+ // logger.info(`file change detected: ${eventType} ${filePath}`);
18
+ // });
19
+ // logger.info(`watching files under ${process.cwd()}`);
20
+ // } catch (err) {
21
+ // logger.warn(`file watch not available: ${String(err)}`);
22
+ // }
23
+ // }
24
+ //
25
+ // const shutdown = () => {
26
+ // logger.info('shutting down run-local server');
27
+ // server.close(() => process.exit(0));
28
+ // };
29
+ // process.on('SIGINT', shutdown);
30
+ // process.on('SIGTERM', shutdown);
31
+ //
32
+ // // return server for tests if needed
33
+ // return { server, port, stage, debug, watch };
34
+ };
35
+ exports.runLocal = runLocal;
@@ -9,10 +9,10 @@ const yaml_1 = __importDefault(require("yaml"));
9
9
  const deploy_1 = require("../stack/deploy");
10
10
  const common_1 = require("../common");
11
11
  const parser_1 = require("../parser");
12
- const template = (stackName, options) => {
12
+ const template = async (stackName, options) => {
13
13
  const iac = (0, parser_1.parseYaml)((0, common_1.getIacLocation)(options.location));
14
- const context = (0, common_1.constructActionContext)({ ...options, stackName, provider: iac.provider.name });
15
- const { template } = (0, deploy_1.generateStackTemplate)(stackName, iac, context);
14
+ await (0, common_1.setContext)({ ...options, stackName, provider: iac.provider.name }, true);
15
+ const { template } = (0, deploy_1.generateStackTemplate)(stackName, iac);
16
16
  if (typeof template === 'string') {
17
17
  common_1.logger.info(`\n${template}`);
18
18
  }
@@ -3,8 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.validate = void 0;
4
4
  const common_1 = require("../common");
5
5
  const parser_1 = require("../parser");
6
- const validate = (stackName, options) => {
7
- const context = (0, common_1.constructActionContext)({ stackName, ...options });
6
+ const validate = async (stackName, options) => {
7
+ await (0, common_1.setContext)({ stackName, ...options });
8
+ const context = (0, common_1.getContext)();
8
9
  (0, parser_1.parseYaml)(context.iacLocation);
9
10
  common_1.logger.info('Yaml is valid! 🎉');
10
11
  };
@@ -1,4 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CODE_ZIP_SIZE_LIMIT = void 0;
4
- exports.CODE_ZIP_SIZE_LIMIT = 15 * 1000 * 1000;
3
+ exports.SI_BOOTSTRAP_BUCKET_PREFIX = exports.SI_BOOTSTRAP_FC_PREFIX = exports.OSS_DEPLOYMENT_TIMEOUT = exports.CODE_ZIP_SIZE_LIMIT = void 0;
4
+ exports.CODE_ZIP_SIZE_LIMIT = 300 * 1000; // 300 KB ROS TemplateBody size limit 512 KB
5
+ exports.OSS_DEPLOYMENT_TIMEOUT = 3000; // in seconds
6
+ exports.SI_BOOTSTRAP_FC_PREFIX = 'si-bootstrap-api';
7
+ exports.SI_BOOTSTRAP_BUCKET_PREFIX = 'si-bootstrap-artifacts';
@@ -3,9 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.constructActionContext = exports.getIacLocation = void 0;
6
+ exports.getContext = exports.setContext = exports.getIacLocation = void 0;
7
7
  const node_path_1 = __importDefault(require("node:path"));
8
8
  const providerEnum_1 = require("./providerEnum");
9
+ const node_async_hooks_1 = require("node:async_hooks");
10
+ const imsClient_1 = require("./imsClient");
11
+ const asyncLocalStorage = new node_async_hooks_1.AsyncLocalStorage();
9
12
  const getIacLocation = (location) => {
10
13
  const projectRoot = node_path_1.default.resolve(process.cwd());
11
14
  return location
@@ -16,21 +19,39 @@ const getIacLocation = (location) => {
16
19
  node_path_1.default.resolve(projectRoot, 'serverless-insight.yml');
17
20
  };
18
21
  exports.getIacLocation = getIacLocation;
19
- const constructActionContext = (config) => {
20
- return {
22
+ const setContext = async (config, reaValToken = false) => {
23
+ const region = config.region ??
24
+ config.iacProvider?.region ??
25
+ process.env.ROS_REGION_ID ??
26
+ process.env.ALIYUN_REGION ??
27
+ 'cn-hangzhou';
28
+ const context = {
21
29
  stage: config.stage ?? 'default',
22
30
  stackName: config.stackName ?? '',
23
31
  provider: (config.provider ?? config.iacProvider?.name ?? providerEnum_1.ProviderEnum.ALIYUN),
24
- region: config.region ??
25
- config.iacProvider?.region ??
26
- process.env.ROS_REGION_ID ??
27
- process.env.ALIYUN_REGION ??
28
- 'cn-hangzhou',
32
+ region,
29
33
  accessKeyId: config.accessKeyId ?? process.env.ALIYUN_ACCESS_KEY_ID,
30
34
  accessKeySecret: config.accessKeySecret ?? process.env.ALIYUN_ACCESS_KEY_SECRET,
31
35
  securityToken: config.securityToken ?? process.env.ALIYUN_SECURITY_TOKEN,
32
36
  iacLocation: (0, exports.getIacLocation)(config.location),
33
37
  parameters: Object.entries(config.parameters ?? {}).map(([key, value]) => ({ key, value })),
38
+ stages: Object.entries(config.stages ?? {}).reduce((acc, [stage, parameters]) => ({
39
+ ...acc,
40
+ [stage]: Object.entries(parameters).map(([key, value]) => ({ key, value })),
41
+ }), {}),
34
42
  };
43
+ if (reaValToken) {
44
+ const iamInfo = await (0, imsClient_1.getIamInfo)(context);
45
+ context.accountId = iamInfo?.accountId;
46
+ }
47
+ asyncLocalStorage.enterWith(context);
35
48
  };
36
- exports.constructActionContext = constructActionContext;
49
+ exports.setContext = setContext;
50
+ const getContext = () => {
51
+ const context = asyncLocalStorage.getStore();
52
+ if (!context) {
53
+ throw new Error('No context found');
54
+ }
55
+ return context;
56
+ };
57
+ exports.getContext = getContext;
@@ -36,12 +36,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.replaceReference = exports.getFileSource = exports.readCodeSize = exports.resolveCode = void 0;
39
+ exports.formatRosId = exports.calcValue = exports.calcRefs = exports.getFileSource = exports.readCodeSize = exports.resolveCode = void 0;
40
40
  const node_path_1 = __importDefault(require("node:path"));
41
41
  const node_fs_1 = __importDefault(require("node:fs"));
42
42
  const ros = __importStar(require("@alicloud/ros-cdk-core"));
43
43
  const ossDeployment = __importStar(require("@alicloud/ros-cdk-ossdeployment"));
44
44
  const node_crypto_1 = __importDefault(require("node:crypto"));
45
+ const lodash_1 = require("lodash");
45
46
  const resolveCode = (location) => {
46
47
  const filePath = node_path_1.default.resolve(process.cwd(), location);
47
48
  const fileContent = node_fs_1.default.readFileSync(filePath);
@@ -65,19 +66,17 @@ const getFileSource = (fcName, location) => {
65
66
  return { source, objectKey };
66
67
  };
67
68
  exports.getFileSource = getFileSource;
68
- const evalCtx = (value, ctx) => {
69
- const containsStage = value.match(/\$\{ctx.\w+}/);
70
- return containsStage ? value.replace(/\$\{ctx.stage}/g, ctx.stage) : value;
71
- };
72
- const replaceReference = (value, ctx) => {
73
- if (typeof value === 'string') {
74
- const matchVar = value.match(/^\$\{vars\.(\w+)}$/);
75
- const containsVar = value.match(/\$\{vars\.(\w+)}/);
76
- const matchMap = value.match(/^\$\{stages\.(\w+)}$/);
77
- const containsMap = value.match(/\$\{stages\.(\w+)}/);
78
- const matchFn = value.match(/^\$\{functions\.(\w+(\.\w+)?)}$/);
79
- if (value.match(/\$\{ctx.\w+}/)) {
80
- return evalCtx(value, ctx);
69
+ const calcRefs = (rawValue, ctx) => {
70
+ if (typeof rawValue === 'string') {
71
+ const containsStage = rawValue.match(/\$\{ctx.\w+}/);
72
+ const matchVar = rawValue.match(/^\$\{vars\.(\w+)}$/);
73
+ const containsVar = rawValue.match(/\$\{vars\.(\w+)}/);
74
+ const matchMap = rawValue.match(/^\$\{stages\.(\w+)}$/);
75
+ const containsMap = rawValue.match(/\$\{stages\.(\w+)}/);
76
+ const matchFn = rawValue.match(/^\$\{functions\.(\w+(\.\w+)?)}$/);
77
+ let value = rawValue;
78
+ if (containsStage) {
79
+ value = value.replace(/\$\{ctx.stage}/g, ctx.stage);
81
80
  }
82
81
  if (matchVar?.length) {
83
82
  return ros.Fn.ref(matchVar[1]);
@@ -88,23 +87,51 @@ const replaceReference = (value, ctx) => {
88
87
  if (matchFn?.length) {
89
88
  return ros.Fn.getAtt(matchFn[1], 'FunctionName');
90
89
  }
91
- if (containsMap?.length && containsVar?.length) {
92
- return ros.Fn.sub(value.replace(/\$\{stages\.(\w+)}/g, '${$1}').replace(/\$\{vars\.(\w+)}/g, '${$1}'));
93
- }
94
- if (containsVar?.length) {
95
- return ros.Fn.sub(value.replace(/\$\{vars\.(\w+)}/g, '${$1}'));
96
- }
97
- if (containsMap?.length) {
98
- return ros.Fn.sub(value.replace(/\$\{stages\.(\w+)}/g, '${$1}'));
90
+ if (containsMap?.length || containsVar?.length) {
91
+ value = ros.Fn.sub(rawValue.replace(/\$\{stages\.(\w+)}/g, '${$1}').replace(/\$\{vars\.(\w+)}/g, '${$1}'));
99
92
  }
100
93
  return value;
101
94
  }
102
- if (Array.isArray(value)) {
103
- return value.map((item) => (0, exports.replaceReference)(item, ctx));
95
+ if (Array.isArray(rawValue)) {
96
+ return rawValue.map((item) => (0, exports.calcRefs)(item, ctx));
104
97
  }
105
- if (typeof value === 'object' && value !== null) {
106
- return Object.fromEntries(Object.entries(value).map(([key, val]) => [key, (0, exports.replaceReference)(val, ctx)]));
98
+ if (typeof rawValue === 'object' && rawValue !== null) {
99
+ return Object.fromEntries(Object.entries(rawValue).map(([key, val]) => [key, (0, exports.calcRefs)(val, ctx)]));
100
+ }
101
+ return rawValue;
102
+ };
103
+ exports.calcRefs = calcRefs;
104
+ const getParam = (key, records) => {
105
+ return records?.find((param) => param.key === key)?.value;
106
+ };
107
+ const calcValue = (rawValue, ctx) => {
108
+ const containsStage = rawValue.match(/\$\{ctx.stage}/);
109
+ const containsVar = rawValue.match(/\$\{vars.\w+}/);
110
+ const containsMap = rawValue.match(/\$\{stages\.(\w+)}/);
111
+ let value = rawValue;
112
+ if (containsStage?.length) {
113
+ value = rawValue.replace(/\$\{ctx.stage}/g, ctx.stage);
114
+ }
115
+ if (containsVar?.length) {
116
+ value = value.replace(/\$\{vars\.(\w+)}/g, (_, key) => getParam(key, ctx.parameters));
117
+ }
118
+ if (containsMap?.length) {
119
+ value = value.replace(/\$\{stages\.(\w+)}/g, (_, key) => getParam(key, (0, lodash_1.get)(ctx.stages, `${ctx.stage}`)));
107
120
  }
108
121
  return value;
109
122
  };
110
- exports.replaceReference = replaceReference;
123
+ exports.calcValue = calcValue;
124
+ const formatRosId = (id) => {
125
+ // Insert underscore before uppercase letters, but only when they follow a lowercase letter
126
+ let result = id.replace(/([a-z])([A-Z])/g, '$1_$2');
127
+ // Convert to lowercase
128
+ result = result.toLowerCase();
129
+ // Replace special characters with underscores
130
+ result = result.replace(/[/*,/#,-]/g, '_');
131
+ // Remove any number of underscores to single one
132
+ result = result.replace(/_+/g, '_');
133
+ // Remove leading underscores
134
+ result = result.replace(/^_/, '');
135
+ return result;
136
+ };
137
+ exports.formatRosId = formatRosId;
@@ -2,7 +2,7 @@ export * from './providerEnum';
2
2
  export * from './logger';
3
3
  export * from './getVersion';
4
4
  export * from './rosClient';
5
- export * from './actionContext';
5
+ export * from './context';
6
6
  export * from './iacHelper';
7
7
  export * from './constants';
8
8
  export * from './imsClient';
@@ -18,7 +18,7 @@ __exportStar(require("./providerEnum"), exports);
18
18
  __exportStar(require("./logger"), exports);
19
19
  __exportStar(require("./getVersion"), exports);
20
20
  __exportStar(require("./rosClient"), exports);
21
- __exportStar(require("./actionContext"), exports);
21
+ __exportStar(require("./context"), exports);
22
22
  __exportStar(require("./iacHelper"), exports);
23
23
  __exportStar(require("./constants"), exports);
24
24
  __exportStar(require("./imsClient"), exports);
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.publishAssets = exports.getAssets = void 0;
39
+ exports.cleanupAssets = exports.publishAssets = exports.constructAssets = exports.getAssets = void 0;
40
40
  const node_fs_1 = __importDefault(require("node:fs"));
41
41
  const ossDeployment = __importStar(require("@alicloud/ros-cdk-ossdeployment"));
42
42
  const node_path_1 = __importDefault(require("node:path"));
@@ -44,6 +44,7 @@ const jszip_1 = __importDefault(require("jszip"));
44
44
  const logger_1 = require("./logger");
45
45
  const lodash_1 = require("lodash");
46
46
  const ali_oss_1 = __importDefault(require("ali-oss"));
47
+ const context_1 = require("./context");
47
48
  const buildAssets = (rootPath, relativePath) => {
48
49
  const location = node_path_1.default.resolve(rootPath, relativePath);
49
50
  if (!node_fs_1.default.existsSync(location)) {
@@ -95,7 +96,8 @@ const zipAssets = async (assetsPath) => {
95
96
  });
96
97
  return zipPath;
97
98
  };
98
- const constructAssets = async ({ files, rootPath }, region) => {
99
+ const constructAssets = async ({ files, rootPath, }) => {
100
+ const { region } = (0, context_1.getContext)();
99
101
  const assets = await Promise.all(Object.entries(files)
100
102
  .filter(([, fileItem]) => !fileItem.source.path.endsWith('.template.json'))
101
103
  .map(async ([, fileItem]) => {
@@ -111,6 +113,7 @@ const constructAssets = async ({ files, rootPath }, region) => {
111
113
  }));
112
114
  return !(0, lodash_1.isEmpty)(assets) ? assets : undefined;
113
115
  };
116
+ exports.constructAssets = constructAssets;
114
117
  const ensureBucketExits = async (bucketName, ossClient) => await ossClient.getBucketInfo(bucketName).catch((err) => {
115
118
  if (err.code === 'NoSuchBucket') {
116
119
  logger_1.logger.info(`Bucket: ${bucketName} not exists, creating...`);
@@ -124,18 +127,19 @@ const ensureBucketExits = async (bucketName, ossClient) => await ossClient.getBu
124
127
  throw err;
125
128
  }
126
129
  });
127
- const publishAssets = async (assets, context) => {
128
- const constructedAssets = await constructAssets(assets, context.region);
129
- if (!constructedAssets?.length) {
130
+ const publishAssets = async (assets) => {
131
+ if (!assets?.length) {
130
132
  logger_1.logger.info('No assets to publish, skipped!');
131
133
  return;
132
134
  }
133
- const bucketName = constructedAssets[0].bucketName;
135
+ const context = (0, context_1.getContext)();
136
+ const bucketName = assets[0].bucketName;
134
137
  const client = new ali_oss_1.default({
135
138
  region: `oss-${context.region}`,
136
139
  accessKeyId: context.accessKeyId,
137
140
  accessKeySecret: context.accessKeySecret,
138
141
  bucket: bucketName,
142
+ timeout: 600000, // 10 minutes
139
143
  });
140
144
  await ensureBucketExits(bucketName, client);
141
145
  const headers = {
@@ -143,10 +147,32 @@ const publishAssets = async (assets, context) => {
143
147
  'x-oss-object-acl': 'default',
144
148
  'x-oss-forbid-overwrite': 'false',
145
149
  };
146
- await Promise.all(constructedAssets.map(async ({ source, objectKey }) => {
150
+ await Promise.all(assets.map(async ({ source, objectKey }) => {
147
151
  await client.put(objectKey, node_path_1.default.normalize(source), { headers });
148
- logger_1.logger.info(`Upload file: ${source}) to bucket: ${bucketName} successfully!`);
152
+ logger_1.logger.debug(`Upload file: ${source} to bucket: ${bucketName} successfully!`);
149
153
  }));
150
154
  return bucketName;
151
155
  };
152
156
  exports.publishAssets = publishAssets;
157
+ const cleanupAssets = async (assets) => {
158
+ if (!assets?.length) {
159
+ logger_1.logger.info('No assets to cleanup, skipped!');
160
+ return;
161
+ }
162
+ const context = (0, context_1.getContext)();
163
+ const bucketName = assets[0].bucketName;
164
+ const client = new ali_oss_1.default({
165
+ region: `oss-${context.region}`,
166
+ accessKeyId: context.accessKeyId,
167
+ accessKeySecret: context.accessKeySecret,
168
+ bucket: bucketName,
169
+ });
170
+ await Promise.all(assets.map(async ({ objectKey }) => {
171
+ await client.delete(objectKey);
172
+ logger_1.logger.debug(`Cleanup file: ${objectKey} from bucket: ${bucketName} successfully!`);
173
+ }));
174
+ // delete the bucket
175
+ await client.deleteBucket(bucketName);
176
+ logger_1.logger.debug(`Cleanup bucket: ${bucketName} successfully!`);
177
+ };
178
+ exports.cleanupAssets = cleanupAssets;
@@ -37,11 +37,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.rosStackDelete = exports.rosStackDeploy = void 0;
40
- const tea_util_1 = __importDefault(require("@alicloud/tea-util"));
41
40
  const ros20190910_1 = __importStar(require("@alicloud/ros20190910"));
42
41
  const openapi_client_1 = require("@alicloud/openapi-client");
42
+ const tea_util_1 = __importDefault(require("@alicloud/tea-util"));
43
43
  const logger_1 = require("./logger");
44
44
  const lang_1 = require("../lang");
45
+ const context_1 = require("./context");
45
46
  const client = new ros20190910_1.default(new openapi_client_1.Config({
46
47
  accessKeyId: process.env.ALIYUN_ACCESS_KEY_ID,
47
48
  accessKeySecret: process.env.ALIYUN_ACCESS_KEY_SECRET,
@@ -85,7 +86,7 @@ const updateStack = async (stackId, templateBody, context) => {
85
86
  return await getStackActionResult(response.body?.stackId || '', context.region);
86
87
  }
87
88
  catch (err) {
88
- const { Message: message, statusCode } = err?.data || {};
89
+ const { message, statusCode } = err || {};
89
90
  if (statusCode === 400 && message.includes('Update the completely same stack')) {
90
91
  logger_1.logger.warn(`${lang_1.lang.__('UPDATE_COMPLETELY_SAME_STACK')}`);
91
92
  return null;
@@ -154,7 +155,8 @@ const getStackActionResult = async (stackId, region) => {
154
155
  }, 5000); // 5 seconds interval
155
156
  });
156
157
  };
157
- const rosStackDeploy = async (stackName, templateBody, context) => {
158
+ const rosStackDeploy = async (stackName, templateBody) => {
159
+ const context = (0, context_1.getContext)();
158
160
  const stackInfo = await getStackByName(stackName, context.region);
159
161
  if (stackInfo) {
160
162
  const { Status: stackStatus } = stackInfo;
@@ -186,7 +188,7 @@ const rosStackDelete = async ({ stackName, region, }) => {
186
188
  });
187
189
  await client.deleteStack(deleteStackRequest);
188
190
  await getStackActionResult(stackInfo.stackId, region);
189
- logger_1.logger.info(`Stack: ${stackName} deleted! ♻️`);
191
+ logger_1.logger.info(`Stack: ${stackName} deleted!🗑 `);
190
192
  }
191
193
  catch (err) {
192
194
  logger_1.logger.error(`Stack: ${stackName} delete failed! ❌, error: ${JSON.stringify(err)}`);
@@ -9,6 +9,7 @@ const tagParser_1 = require("./tagParser");
9
9
  const yaml_1 = require("yaml");
10
10
  const validator_1 = require("../validator");
11
11
  const bucketParser_1 = require("./bucketParser");
12
+ const tableParser_1 = require("./tableParser");
12
13
  const validateExistence = (path) => {
13
14
  if (!(0, node_fs_1.existsSync)(path)) {
14
15
  throw new Error(`File does not exist at path: ${path}`);
@@ -24,6 +25,7 @@ const transformYaml = (iacJson) => {
24
25
  functions: (0, functionParser_1.parseFunction)(iacJson.functions),
25
26
  events: (0, eventParser_1.parseEvent)(iacJson.events),
26
27
  databases: (0, databaseParser_1.parseDatabase)(iacJson.databases),
28
+ tables: (0, tableParser_1.parseTable)(iacJson.tables),
27
29
  tags: (0, tagParser_1.parseTag)(iacJson.tags),
28
30
  buckets: (0, bucketParser_1.parseBucket)(iacJson.buckets),
29
31
  };