create-skweb 2.0.0 → 3.0.1

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.
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env node
2
+ //
3
+ // sync-template-versions.mjs
4
+ //
5
+ // 把本工程 packages 下的 skweb-* 包版本号
6
+ // 同步到 tools/create-skweb/templates 下的 package.json。
7
+ //
8
+ // 用法:
9
+ // node scripts/sync-template-versions.mjs # 默认 dry-run,仅打印
10
+ // node scripts/sync-template-versions.mjs --apply # 写回文件
11
+ // node scripts/sync-template-versions.mjs --strict # 严格模式:发现不一致退出码 1
12
+ // node scripts/sync-template-versions.mjs --help # 查看帮助
13
+ //
14
+ // 同步规则:
15
+ // - workspace 的 X.Y.Z 映射到模板的 ^X.Y.0
16
+ // - 仅修改 skweb-* 开头的依赖(其他依赖不动)
17
+ // - 仅处理 templates 目录下 .json 文件
18
+ //
19
+ // 退出码:
20
+ // 0 全部一致,或已 --apply 写回
21
+ // 1 --strict 模式下发现不一致且未 --apply
22
+ // 2 执行过程异常
23
+ //
24
+ // 建议在以下时机执行:
25
+ // - 每次 changeset version 之后
26
+ // - 提交前
27
+ // - CI 发布前
28
+ //
29
+
30
+ import fs from 'node:fs'
31
+ import path from 'node:path'
32
+ import { fileURLToPath } from 'node:url'
33
+
34
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
35
+ const ROOT = path.join(__dirname, '..', '..', '..') // -> skweb-mono/
36
+ const PACKAGES_DIR = path.join(ROOT, 'packages')
37
+ const TEMPLATES_DIR = path.join(__dirname, '..', 'templates')
38
+
39
+ // ANSI 颜色(仅在 TTY 启用,避免污染 CI 日志)
40
+ const useColor = process.stdout.isTTY && !process.env.NO_COLOR
41
+ const color = (code) => (useColor ? `\x1b[${code}m` : '')
42
+ const cBold = color('1')
43
+ const cYellow = color('33')
44
+ const cRed = color('31')
45
+ const cGreen = color('32')
46
+ const cCyan = color('36')
47
+ const cReset = color('0')
48
+
49
+ function showHelp() {
50
+ console.log(`${cBold}sync-template-versions${cReset}
51
+ 把 packages/ 下的 skweb-* 版本号同步到 templates/ 下的 package.json。
52
+
53
+ ${cBold}用法${cReset}
54
+ ${cCyan}node scripts/sync-template-versions.mjs${cReset} ${cYellow}# 默认 dry-run${cReset}
55
+ ${cCyan}node scripts/sync-template-versions.mjs --apply${cReset} # 写回文件
56
+ ${cCyan}node scripts/sync-template-versions.mjs --strict${cReset} # 严格模式(CI 推荐)
57
+ ${cCyan}node scripts/sync-template-versions.mjs --help${cReset} # 查看帮助
58
+
59
+ ${cBold}同步规则${cReset}
60
+ - workspace 的 X.Y.Z 映射到模板的 ^X.Y.0
61
+ - 仅修改 skweb-* 开头的依赖
62
+ - 仅处理 templates 目录下 *.json
63
+
64
+ ${cBold}退出码${cReset}
65
+ 0 全部一致,或已 --apply 写回
66
+ 1 --strict 模式下发现不一致且未 --apply
67
+ 2 执行过程异常
68
+
69
+ ${cBold}同步规则说明${cReset}
70
+ 使用 ${cCyan}--apply${cReset} 真正写入文件;不带 --apply 永远只打印,不会改动模板。`)
71
+ }
72
+
73
+ const APPLY = process.argv.includes('--apply')
74
+ const STRICT = process.argv.includes('--strict')
75
+ const HELP = process.argv.includes('--help') || process.argv.includes('-h')
76
+
77
+ if (HELP) {
78
+ showHelp()
79
+ process.exit(0)
80
+ }
81
+
82
+ // 扫描 packages/ 下所有 package.json,返回 { 包名: 版本 } 映射。
83
+ function loadWorkspaceVersions() {
84
+ const result = {}
85
+ if (!fs.existsSync(PACKAGES_DIR)) {
86
+ throw new Error(`packages dir not found: ${PACKAGES_DIR}`)
87
+ }
88
+ for (const dir of fs.readdirSync(PACKAGES_DIR)) {
89
+ const pkgJsonPath = path.join(PACKAGES_DIR, dir, 'package.json')
90
+ if (!fs.existsSync(pkgJsonPath)) continue
91
+ const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'))
92
+ if (pkg.name && pkg.version) {
93
+ result[pkg.name] = pkg.version
94
+ }
95
+ }
96
+ return result
97
+ }
98
+
99
+ // 把 X.Y.Z 转换为 ^X.Y.0 范围,允许 minor/patch 升级。
100
+ function toRange(version) {
101
+ const parts = version.split('.')
102
+ if (parts.length < 2) {
103
+ throw new Error(`unexpected version format: ${version}`)
104
+ }
105
+ return `^${parts[0]}.${parts[1]}.0`
106
+ }
107
+
108
+ // 同步单个模板文件,返回变更列表。
109
+ function syncTemplate(templatePath, workspaceVersions) {
110
+ const raw = fs.readFileSync(templatePath, 'utf-8')
111
+ const pkg = JSON.parse(raw)
112
+ const deps = pkg.dependencies || {}
113
+ const changes = []
114
+
115
+ for (const depName of Object.keys(deps)) {
116
+ if (!depName.startsWith('skweb-')) continue
117
+ if (!workspaceVersions[depName]) continue
118
+ const newRange = toRange(workspaceVersions[depName])
119
+ const oldRange = deps[depName]
120
+ if (oldRange !== newRange) {
121
+ changes.push({ dep: depName, old: oldRange, new: newRange })
122
+ deps[depName] = newRange
123
+ }
124
+ }
125
+
126
+ if (changes.length > 0 && APPLY) {
127
+ const updated = JSON.stringify(pkg, null, 2) + '\n'
128
+ fs.writeFileSync(templatePath, updated, 'utf-8')
129
+ }
130
+
131
+ return changes
132
+ }
133
+
134
+ function main() {
135
+ // 在最顶部打印运行模式,避免用户漏看后续信息
136
+ if (APPLY) {
137
+ console.log(`${cGreen}[sync] mode: APPLY (will write to files)${cReset}`)
138
+ } else {
139
+ console.log(`${cYellow}[sync] mode: DRY-RUN (no files will be modified; pass --apply to write)${cReset}`)
140
+ }
141
+ if (STRICT) {
142
+ console.log(`${cYellow}[sync] mode: STRICT (will exit 1 on pending changes)${cReset}`)
143
+ }
144
+ console.log('')
145
+
146
+ const workspaceVersions = loadWorkspaceVersions()
147
+ console.log(`[sync] workspace versions: ${Object.keys(workspaceVersions).length} packages`)
148
+ for (const [name, ver] of Object.entries(workspaceVersions)) {
149
+ console.log(` ${name}: ${ver} -> ${toRange(ver)}`)
150
+ }
151
+ console.log('')
152
+
153
+ if (!fs.existsSync(TEMPLATES_DIR)) {
154
+ throw new Error(`templates dir not found: ${TEMPLATES_DIR}`)
155
+ }
156
+
157
+ let totalChanges = 0
158
+ const templateDirs = fs.readdirSync(TEMPLATES_DIR).filter(d => {
159
+ const full = path.join(TEMPLATES_DIR, d)
160
+ return fs.statSync(full).isDirectory()
161
+ })
162
+
163
+ for (const tpl of templateDirs) {
164
+ const pkgJson = path.join(TEMPLATES_DIR, tpl, 'package.json')
165
+ if (!fs.existsSync(pkgJson)) continue
166
+ const changes = syncTemplate(pkgJson, workspaceVersions)
167
+ if (changes.length === 0) {
168
+ console.log(`[sync] ${tpl}/package.json: in sync`)
169
+ } else {
170
+ totalChanges += changes.length
171
+ const tag = APPLY ? `${cGreen}[APPLIED]${cReset}` : `${cYellow}[dry-run]${cReset}`
172
+ console.log(`[sync] ${tpl}/package.json: ${changes.length} update(s) ${tag}`)
173
+ for (const c of changes) {
174
+ console.log(` ${c.dep}: ${c.old} -> ${c.new}`)
175
+ }
176
+ }
177
+ }
178
+
179
+ console.log('')
180
+ if (totalChanges === 0) {
181
+ console.log(`${cGreen}[sync] All templates are in sync with workspace versions.${cReset}`)
182
+ } else if (APPLY) {
183
+ console.log(`${cGreen}[sync] Applied ${totalChanges} update(s).${cReset}`)
184
+ } else {
185
+ console.log(
186
+ `${cYellow}[sync] ${totalChanges} update(s) pending.${cReset} ` +
187
+ `${cBold}Re-run with ${cCyan}--apply${cReset}${cBold} to write.${cReset}`
188
+ )
189
+ if (STRICT) {
190
+ console.log(`${cRed}[sync] STRICT mode: exiting with code 1.${cReset}`)
191
+ process.exit(1)
192
+ }
193
+ }
194
+ }
195
+
196
+ try {
197
+ main()
198
+ } catch (err) {
199
+ console.error('[sync] FAILED:', err.message)
200
+ process.exit(2)
201
+ }
package/src/cli.ts CHANGED
@@ -3,13 +3,13 @@
3
3
  import { Command } from 'commander'
4
4
  import inquirer from 'inquirer'
5
5
  import ora from 'ora'
6
- import {
7
- createProject,
8
- installDependencies,
9
- initGit,
10
- printBanner,
11
- printSuccess
12
- } from './index'
6
+ import {
7
+ createProject,
8
+ installDependencies,
9
+ initGit,
10
+ printBanner,
11
+ printSuccess
12
+ } from './index.js'
13
13
 
14
14
  async function main() {
15
15
  const program = new Command()
package/src/index.ts CHANGED
@@ -82,7 +82,7 @@ export async function initGit(dest: string, name?: string): Promise<void> {
82
82
  export function printBanner(): void {
83
83
  console.log(`
84
84
  ╔════════════════════════════════════════════════════════════════════╗
85
- Create SKWeb Project ║
85
+ Create SKWeb Project ║
86
86
  ╚════════════════════════════════════════════════════════════════════╝
87
87
  `)
88
88
  }
@@ -91,7 +91,7 @@ export function printSuccess(name: string, dest: string): void {
91
91
  const projectPath = path.join(path.resolve(dest), name)
92
92
  console.log(`
93
93
  ╔════════════════════════════════════════════════════════════════════╗
94
- 项目创建成功! ║
94
+ 项目创建成功! ║
95
95
  ╚════════════════════════════════════════════════════════════════════╝
96
96
 
97
97
  项目名称: ${name}
@@ -20,27 +20,27 @@
20
20
  "clean:temp": "rm -rf .rollup.cache"
21
21
  },
22
22
  "dependencies": {
23
- "@dotenvx/dotenvx": "^1.71.0",
24
- "http-errors": "^2.0.0",
25
- "skweb-express": "^1.0.0",
26
- "skweb-express-jwt": "^1.2.0",
27
- "skweb-express-logger": "^1.0.0",
28
- "skweb-sequelize": "^1.0.0",
29
- "skweb-redis": "^1.0.0",
30
- "skweb-cron": "^1.0.0",
31
- "skweb-framework": "^1.0.0",
32
- "skweb-plugin": "^1.0.0",
33
- "skweb-util": "^1.0.0",
34
- "winston": "^3.17.0",
35
- "sequelize": "^6.37.7",
36
- "swagger-jsdoc": "^6.2.8",
23
+ "@dotenvx/dotenvx": "^1.75.1",
24
+ "http-errors": "^2.0.1",
25
+ "skweb-express": "^2.0.0",
26
+ "skweb-express-jwt": "^3.0.0",
27
+ "skweb-express-logger": "^2.0.0",
28
+ "skweb-sequelize": "^2.0.0",
29
+ "skweb-redis": "^2.0.0",
30
+ "skweb-cron": "^2.0.0",
31
+ "skweb-framework": "^2.0.0",
32
+ "skweb-plugin": "^2.0.0",
33
+ "skweb-util": "^2.0.0",
34
+ "winston": "^3.19.0",
35
+ "sequelize": "^6.37.8",
36
+ "swagger-jsdoc": "^6.3.0",
37
37
  "swagger-ui-express": "^5.0.1"
38
38
  },
39
39
  "devDependencies": {
40
- "chai": "^6.2.1",
41
- "mocha": "^11.7.5",
42
- "eslint": "^9.39.1",
43
- "@eslint/js": "^9.39.1"
40
+ "chai": "^6.2.2",
41
+ "mocha": "^11.7.6",
42
+ "eslint": "^10.5.0",
43
+ "@eslint/js": "^10.0.1"
44
44
  },
45
45
  "engines": {
46
46
  "node": ">=20.18.1"
@@ -1,8 +1,12 @@
1
1
  require('dotenv/config')
2
2
 
3
3
  const path = require('path')
4
- const express = require('skweb-express')
5
- const expressLogger = require('skweb-express-logger').default || require('skweb-express-logger')
4
+ // skweb-express / skweb-express-logger 的 CJS 产物形态是
5
+ // `{ default: fn, errorHandler, ... }`。直接 `require('skweb-express')` 拿到的是
6
+ // 命名空间对象,`express()` 会 TypeError。这里走 .default。
7
+ const express = require('skweb-express').default
8
+ const expressLogger = require('skweb-express-logger').default
9
+ const expressErrorHandler = require('skweb-express-logger').errorHandler
6
10
  const swaggerUi = require('swagger-ui-express')
7
11
  const { WebFramework } = require('skweb-framework')
8
12
  const { logger } = require('./utils/logger.js')
@@ -126,20 +130,21 @@ async function bootstrap() {
126
130
  await framework.loadControllers(path.join(__dirname, './controllers'))
127
131
  await framework.loadPluginControllers()
128
132
 
133
+ // 错误日志中间件:挂在所有路由之后,记录业务错误并继续传递给默认错误处理。
134
+ app.use(expressErrorHandler())
135
+
129
136
  framework.start(Number(process.env.PORT) || 3000)
130
137
 
131
138
  logger.info('[app] Initialization complete (web process)')
132
139
 
133
- process.on('SIGINT', async () => {
134
- logger.info('[app] Received SIGINT, shutting down gracefully...')
135
- await framework.stop()
136
- process.exit(0)
140
+ // 未捕获异常:打印堆栈后直接退出。OS / 进程管理器负责回收资源。
141
+ process.on('uncaughtException', (err) => {
142
+ logger.error(`[app] uncaughtException: ${err.stack || err.message}`)
143
+ process.exit(1)
137
144
  })
138
-
139
- process.on('SIGTERM', async () => {
140
- logger.info('[app] Received SIGTERM, shutting down gracefully...')
141
- await framework.stop()
142
- process.exit(0)
145
+ process.on('unhandledRejection', (reason) => {
146
+ logger.error(`[app] unhandledRejection: ${reason instanceof Error ? reason.stack : String(reason)}`)
147
+ process.exit(1)
143
148
  })
144
149
  }
145
150
 
@@ -62,16 +62,14 @@ async function startCronWorker() {
62
62
  framework.start()
63
63
  logger.info('[cron-worker] Cron worker started')
64
64
 
65
- process.on('SIGINT', async () => {
66
- logger.info('[cron-worker] Received SIGINT, shutting down...')
67
- await framework.stop()
68
- process.exit(0)
65
+ // 未捕获异常:打印堆栈后直接退出。OS / 进程管理器负责回收资源。
66
+ process.on('uncaughtException', (err) => {
67
+ logger.error(`[cron-worker] uncaughtException: ${err.stack || err.message}`)
68
+ process.exit(1)
69
69
  })
70
-
71
- process.on('SIGTERM', async () => {
72
- logger.info('[cron-worker] Received SIGTERM, shutting down...')
73
- await framework.stop()
74
- process.exit(0)
70
+ process.on('unhandledRejection', (reason) => {
71
+ logger.error(`[cron-worker] unhandledRejection: ${reason instanceof Error ? reason.stack : String(reason)}`)
72
+ process.exit(1)
75
73
  })
76
74
  }
77
75
 
@@ -20,27 +20,27 @@
20
20
  "clean:temp": "rm -rf .rollup.cache"
21
21
  },
22
22
  "dependencies": {
23
- "@dotenvx/dotenvx": "^1.71.0",
24
- "http-errors": "^2.0.0",
25
- "skweb-express": "^1.0.0",
26
- "skweb-express-jwt": "^1.2.0",
27
- "skweb-express-logger": "^1.0.0",
28
- "skweb-sequelize": "^1.0.0",
29
- "skweb-redis": "^1.0.0",
30
- "skweb-cron": "^1.0.0",
31
- "skweb-framework": "^1.0.0",
32
- "skweb-plugin": "^1.0.0",
33
- "skweb-util": "^1.0.0",
34
- "winston": "^3.17.0",
35
- "sequelize": "^6.37.7",
36
- "swagger-jsdoc": "^6.2.8",
23
+ "@dotenvx/dotenvx": "^1.75.1",
24
+ "http-errors": "^2.0.1",
25
+ "skweb-express": "^2.0.0",
26
+ "skweb-express-jwt": "^3.0.0",
27
+ "skweb-express-logger": "^2.0.0",
28
+ "skweb-sequelize": "^2.0.0",
29
+ "skweb-redis": "^2.0.0",
30
+ "skweb-cron": "^2.0.0",
31
+ "skweb-framework": "^2.0.0",
32
+ "skweb-plugin": "^2.0.0",
33
+ "skweb-util": "^2.0.0",
34
+ "winston": "^3.19.0",
35
+ "sequelize": "^6.37.8",
36
+ "swagger-jsdoc": "^6.3.0",
37
37
  "swagger-ui-express": "^5.0.1"
38
38
  },
39
39
  "devDependencies": {
40
- "chai": "^6.2.1",
41
- "mocha": "^11.7.5",
42
- "eslint": "^9.39.1",
43
- "@eslint/js": "^9.39.1"
40
+ "chai": "^6.2.2",
41
+ "mocha": "^11.7.6",
42
+ "eslint": "^10.5.0",
43
+ "@eslint/js": "^10.0.1"
44
44
  },
45
45
  "engines": {
46
46
  "node": ">=20.18.1"
@@ -3,7 +3,7 @@ import 'dotenv/config'
3
3
  import path from 'path'
4
4
  import { fileURLToPath } from 'url'
5
5
  import express from 'skweb-express'
6
- import expressLogger from 'skweb-express-logger'
6
+ import expressLogger, { errorHandler as expressErrorHandler } from 'skweb-express-logger'
7
7
  import swaggerUi from 'swagger-ui-express'
8
8
  import logger from './utils/logger.js'
9
9
  import { checkRequiredEnv } from './utils/config.js'
@@ -135,16 +135,14 @@ async function bootstrap() {
135
135
 
136
136
  logger.info('[app] Initialization complete (web process)')
137
137
 
138
- process.on('SIGINT', async () => {
139
- logger.info('[app] Received SIGINT, shutting down gracefully...')
140
- await framework.stop()
141
- process.exit(0)
138
+ // 未捕获异常:打印堆栈后直接退出。OS / 进程管理器负责回收资源。
139
+ process.on('uncaughtException', (err) => {
140
+ logger.error(`[app] uncaughtException: ${err.stack || err.message}`)
141
+ process.exit(1)
142
142
  })
143
-
144
- process.on('SIGTERM', async () => {
145
- logger.info('[app] Received SIGTERM, shutting down gracefully...')
146
- await framework.stop()
147
- process.exit(0)
143
+ process.on('unhandledRejection', (reason) => {
144
+ logger.error(`[app] unhandledRejection: ${reason instanceof Error ? reason.stack : String(reason)}`)
145
+ process.exit(1)
148
146
  })
149
147
  }
150
148
 
@@ -67,16 +67,14 @@ async function startCronWorker() {
67
67
  framework.start()
68
68
  logger.info('[cron-worker] Cron worker started')
69
69
 
70
- process.on('SIGINT', async () => {
71
- logger.info('[cron-worker] Received SIGINT, shutting down...')
72
- await framework.stop()
73
- process.exit(0)
70
+ // 未捕获异常:打印堆栈后直接退出。OS / 进程管理器负责回收资源。
71
+ process.on('uncaughtException', (err) => {
72
+ logger.error(`[cron-worker] uncaughtException: ${err.stack || err.message}`)
73
+ process.exit(1)
74
74
  })
75
-
76
- process.on('SIGTERM', async () => {
77
- logger.info('[cron-worker] Received SIGTERM, shutting down...')
78
- await framework.stop()
79
- process.exit(0)
75
+ process.on('unhandledRejection', (reason) => {
76
+ logger.error(`[cron-worker] unhandledRejection: ${reason instanceof Error ? reason.stack : String(reason)}`)
77
+ process.exit(1)
80
78
  })
81
79
  }
82
80
 
@@ -22,38 +22,41 @@
22
22
  "clean:temp": "rm -rf .rollup.cache"
23
23
  },
24
24
  "dependencies": {
25
- "@dotenvx/dotenvx": "^1.71.0",
26
- "http-errors": "^2.0.0",
27
- "skweb-express": "^1.0.0",
28
- "skweb-express-jwt": "^1.2.0",
29
- "skweb-express-logger": "^1.0.0",
30
- "skweb-sequelize": "^1.0.0",
31
- "skweb-redis": "^1.0.0",
32
- "skweb-cron": "^1.0.0",
33
- "skweb-framework": "^1.0.0",
34
- "skweb-plugin": "^1.0.0",
35
- "skweb-util": "^1.0.0",
36
- "winston": "^3.17.0",
37
- "sequelize": "^6.37.7",
38
- "swagger-jsdoc": "^6.2.8",
25
+ "@dotenvx/dotenvx": "^1.75.1",
26
+ "http-errors": "^2.0.1",
27
+ "skweb-express": "^2.0.0",
28
+ "skweb-express-jwt": "^3.0.0",
29
+ "skweb-express-logger": "^2.0.0",
30
+ "skweb-sequelize": "^2.0.0",
31
+ "skweb-redis": "^2.0.0",
32
+ "skweb-cron": "^2.0.0",
33
+ "skweb-framework": "^2.0.0",
34
+ "skweb-plugin": "^2.0.0",
35
+ "skweb-util": "^2.0.0",
36
+ "winston": "^3.19.0",
37
+ "sequelize": "^6.37.8",
38
+ "swagger-jsdoc": "^6.3.0",
39
39
  "swagger-ui-express": "^5.0.1"
40
40
  },
41
41
  "devDependencies": {
42
- "@types/node": "^25.0.1",
42
+ "@types/node": "^26.0.0",
43
43
  "@types/mocha": "^10.0.10",
44
- "@types/http-errors": "^2.0.4",
44
+ "@types/http-errors": "^2.0.5",
45
45
  "@types/swagger-jsdoc": "^6.0.4",
46
46
  "@types/swagger-ui-express": "^4.1.8",
47
- "chai": "^6.2.1",
48
- "mocha": "^11.7.5",
47
+ "chai": "^6.2.2",
48
+ "mocha": "^11.7.6",
49
49
  "tsx": "^4.22.4",
50
- "typescript": "^5.9.3",
51
- "rollup": "^2.79.2",
50
+ "typescript": "^6.0.3",
51
+ "tslib": "^2.8.1",
52
+ "rollup": "^4.62.2",
52
53
  "@rollup/plugin-typescript": "^12.3.0",
54
+ "@rollup/plugin-json": "^6.1.0",
53
55
  "@rollup/plugin-node-resolve": "^16.0.3",
54
- "@rollup/plugin-commonjs": "^29.0.0",
55
- "eslint": "^9.39.1",
56
- "typescript-eslint": "^8.49.0"
56
+ "@rollup/plugin-commonjs": "^29.0.3",
57
+ "@rollup/plugin-terser": "^1.0.0",
58
+ "eslint": "^10.5.0",
59
+ "typescript-eslint": "^8.62.0"
57
60
  },
58
61
  "engines": {
59
62
  "node": ">=20.18.1"
@@ -2,6 +2,7 @@ import resolve from '@rollup/plugin-node-resolve'
2
2
  import commonjs from '@rollup/plugin-commonjs'
3
3
  import typescript from '@rollup/plugin-typescript'
4
4
  import terser from '@rollup/plugin-terser'
5
+ import json from '@rollup/plugin-json'
5
6
 
6
7
  const isProduction = process.env.NODE_ENV === 'production'
7
8
 
@@ -15,6 +16,7 @@ export default {
15
16
  plugins: [
16
17
  resolve(),
17
18
  commonjs(),
19
+ json(),
18
20
  typescript({ tsconfig: './tsconfig.json' }),
19
21
  isProduction && terser()
20
22
  ],
@@ -33,6 +35,8 @@ export default {
33
35
  'express',
34
36
  'sequelize',
35
37
  'ioredis',
38
+ 'swagger-ui-express',
39
+ 'swagger-jsdoc',
36
40
  'path',
37
41
  'fs'
38
42
  ]
@@ -3,7 +3,7 @@ import 'dotenv/config'
3
3
  import path from 'path'
4
4
  import { fileURLToPath } from 'url'
5
5
  import express from 'skweb-express'
6
- import expressLogger from 'skweb-express-logger'
6
+ import expressLogger, { errorHandler as expressErrorHandler } from 'skweb-express-logger'
7
7
  import { WebFramework } from 'skweb-framework'
8
8
  import type { JWTOptions } from 'skweb-express-jwt'
9
9
  import swaggerUi from 'swagger-ui-express'
@@ -142,16 +142,15 @@ async function bootstrap(): Promise<void> {
142
142
 
143
143
  logger.info('[app] Initialization complete (web process)')
144
144
 
145
- process.on('SIGINT', async () => {
146
- logger.info('[app] Received SIGINT, shutting down gracefully...')
147
- await framework.stop()
148
- process.exit(0)
145
+ // 未捕获异常:打印堆栈后直接退出。OS / 进程管理器(pm2 / docker / k8s)
146
+ // 负责回收 socket / fd,框架不在此路径上做资源清理——简单可靠。
147
+ process.on('uncaughtException', (err) => {
148
+ logger.error(`[app] uncaughtException: ${err.stack || err.message}`)
149
+ process.exit(1)
149
150
  })
150
-
151
- process.on('SIGTERM', async () => {
152
- logger.info('[app] Received SIGTERM, shutting down gracefully...')
153
- await framework.stop()
154
- process.exit(0)
151
+ process.on('unhandledRejection', (reason) => {
152
+ logger.error(`[app] unhandledRejection: ${reason instanceof Error ? reason.stack : String(reason)}`)
153
+ process.exit(1)
155
154
  })
156
155
  }
157
156
 
@@ -37,7 +37,7 @@ async function startCronWorker(): Promise<void> {
37
37
  channel: 'cron:task:cmd',
38
38
  logger,
39
39
  onSaveTask: async (taskData: { taskId: string; cache: unknown; data: unknown }) => {
40
- const SysCronTask = framework.db?.sequelize?.models.SysCronTask as any
40
+ const SysCronTask = framework.dbInstance?.sequelize?.models.SysCronTask as any
41
41
  if (SysCronTask) {
42
42
  await SysCronTask.update(
43
43
  { cache: taskData.cache, data: taskData.data },
@@ -46,7 +46,7 @@ async function startCronWorker(): Promise<void> {
46
46
  }
47
47
  },
48
48
  loadTasks: async () => {
49
- const SysCronTask = framework.db?.sequelize?.models.SysCronTask as any
49
+ const SysCronTask = framework.dbInstance?.sequelize?.models.SysCronTask as any
50
50
  if (!SysCronTask) return []
51
51
  return (await SysCronTask.findAll()).map((t: any) => ({
52
52
  id: String(t.id),
@@ -66,16 +66,14 @@ async function startCronWorker(): Promise<void> {
66
66
  framework.start()
67
67
  logger.info('[cron-worker] Cron worker started')
68
68
 
69
- process.on('SIGINT', async () => {
70
- logger.info('[cron-worker] Received SIGINT, shutting down...')
71
- await framework.stop()
72
- process.exit(0)
69
+ // 未捕获异常:打印堆栈后直接退出。OS / 进程管理器负责回收资源。
70
+ process.on('uncaughtException', (err) => {
71
+ logger.error(`[cron-worker] uncaughtException: ${err.stack || err.message}`)
72
+ process.exit(1)
73
73
  })
74
-
75
- process.on('SIGTERM', async () => {
76
- logger.info('[cron-worker] Received SIGTERM, shutting down...')
77
- await framework.stop()
78
- process.exit(0)
74
+ process.on('unhandledRejection', (reason) => {
75
+ logger.error(`[cron-worker] unhandledRejection: ${reason instanceof Error ? reason.stack : String(reason)}`)
76
+ process.exit(1)
79
77
  })
80
78
  }
81
79
 
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "ES2020",
3
+ "target": "ES2022",
4
4
  "module": "ESNext",
5
- "lib": ["ES2020"],
6
- "moduleResolution": "Node",
5
+ "lib": ["ES2022"],
6
+ "moduleResolution": "Bundler",
7
7
  "strict": true,
8
8
  "esModuleInterop": true,
9
9
  "skipLibCheck": true,
@@ -12,8 +12,7 @@
12
12
  "declaration": true,
13
13
  "sourceMap": true,
14
14
  "rootDir": "src",
15
- "outDir": "dist",
16
- "baseUrl": "."
15
+ "outDir": "dist"
17
16
  },
18
17
  "include": ["src/**/*.ts"],
19
18
  "exclude": ["node_modules", "dist", "test"]