@uofx/cli 1.0.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 (238) hide show
  1. package/LICENSE +40 -0
  2. package/README.md +444 -0
  3. package/THIRD-PARTY-NOTICES.txt +894 -0
  4. package/dist/application/dtos/index.js +24 -0
  5. package/dist/application/dtos/request/delete-instance.request.dto.js +3 -0
  6. package/dist/application/dtos/request/get-config.request.dto.js +3 -0
  7. package/dist/application/dtos/request/get-credentials.request.dto.js +3 -0
  8. package/dist/application/dtos/request/index.js +27 -0
  9. package/dist/application/dtos/request/install-instance.request.dto.js +3 -0
  10. package/dist/application/dtos/request/list-charts.request.dto.js +3 -0
  11. package/dist/application/dtos/request/set-config.request.dto.js +16 -0
  12. package/dist/application/dtos/request/setup-environment.request.dto.js +16 -0
  13. package/dist/application/dtos/request/show-logs.request.dto.js +19 -0
  14. package/dist/application/dtos/request/start-instance.request.dto.js +3 -0
  15. package/dist/application/dtos/request/stop-instance.request.dto.js +3 -0
  16. package/dist/application/dtos/response/credentials.response.dto.js +3 -0
  17. package/dist/application/dtos/response/delete-instance.response.dto.js +3 -0
  18. package/dist/application/dtos/response/index.js +26 -0
  19. package/dist/application/dtos/response/install-instance.response.dto.js +3 -0
  20. package/dist/application/dtos/response/instance-list.response.dto.js +3 -0
  21. package/dist/application/dtos/response/instance-status.response.dto.js +3 -0
  22. package/dist/application/dtos/response/setup-result.response.dto.js +3 -0
  23. package/dist/application/dtos/response/show-logs.response.dto.js +3 -0
  24. package/dist/application/dtos/response/start-instance.response.dto.js +3 -0
  25. package/dist/application/dtos/response/stop-instance.response.dto.js +3 -0
  26. package/dist/application/index.js +25 -0
  27. package/dist/application/interfaces/index.js +24 -0
  28. package/dist/application/interfaces/use-case.interface.js +3 -0
  29. package/dist/application/use-cases/config/get-config.use-case.js +66 -0
  30. package/dist/application/use-cases/config/set-config.use-case.js +49 -0
  31. package/dist/application/use-cases/credentials/get-credentials.use-case.js +57 -0
  32. package/dist/application/use-cases/index.js +28 -0
  33. package/dist/application/use-cases/instance/delete-instance.use-case.js +81 -0
  34. package/dist/application/use-cases/instance/index.js +23 -0
  35. package/dist/application/use-cases/instance/install-instance.use-case.js +424 -0
  36. package/dist/application/use-cases/instance/list-charts.use-case.js +43 -0
  37. package/dist/application/use-cases/instance/list-instances.use-case.js +62 -0
  38. package/dist/application/use-cases/instance/start-instance.use-case.js +154 -0
  39. package/dist/application/use-cases/instance/stop-instance.use-case.js +55 -0
  40. package/dist/application/use-cases/logs/show-logs.use-case.js +66 -0
  41. package/dist/application/use-cases/setup/setup-environment.use-case.js +53 -0
  42. package/dist/cli.js +286 -0
  43. package/dist/constants/config-defaults.js +23 -0
  44. package/dist/constants/defaults.js +89 -0
  45. package/dist/constants/deployment.js +39 -0
  46. package/dist/constants/environments.js +93 -0
  47. package/dist/constants/index.js +53 -0
  48. package/dist/constants/oci-artifacts.js +25 -0
  49. package/dist/constants/paths.js +92 -0
  50. package/dist/constants/timeouts.js +60 -0
  51. package/dist/di/container.js +34 -0
  52. package/dist/di/index.js +22 -0
  53. package/dist/di/modules/application.module.js +54 -0
  54. package/dist/di/modules/infrastructure.module.js +206 -0
  55. package/dist/di/modules/interceptor.module.js +68 -0
  56. package/dist/di/modules/presentation.module.js +31 -0
  57. package/dist/di/tokens.js +149 -0
  58. package/dist/domain/decorators/sensitive.decorator.js +39 -0
  59. package/dist/domain/entities/credentials-resolver.entity.js +127 -0
  60. package/dist/domain/entities/credentials.entity.js +65 -0
  61. package/dist/domain/entities/delete-instance-validation.entity.js +100 -0
  62. package/dist/domain/entities/deployment-parameters.entity.js +120 -0
  63. package/dist/domain/entities/environment-validation.entity.js +125 -0
  64. package/dist/domain/entities/index.js +29 -0
  65. package/dist/domain/entities/instance-lifecycle-state.entity.js +100 -0
  66. package/dist/domain/entities/instance-list-aggregator.entity.js +104 -0
  67. package/dist/domain/entities/instance-metadata.entity.js +86 -0
  68. package/dist/domain/entities/instance-status.entity.js +79 -0
  69. package/dist/domain/entities/instance.entity.js +128 -0
  70. package/dist/domain/entities/log-filter.entity.js +141 -0
  71. package/dist/domain/index.js +29 -0
  72. package/dist/domain/interfaces/safe-loggable.interface.js +3 -0
  73. package/dist/domain/ports/app-config.port.js +3 -0
  74. package/dist/domain/ports/base-image.port.js +3 -0
  75. package/dist/domain/ports/chart-version.port.js +3 -0
  76. package/dist/domain/ports/correlation-id.port.js +3 -0
  77. package/dist/domain/ports/credentials.port.js +3 -0
  78. package/dist/domain/ports/deployment.port.js +3 -0
  79. package/dist/domain/ports/error-handler.port.js +3 -0
  80. package/dist/domain/ports/index.js +46 -0
  81. package/dist/domain/ports/instance-manager.port.js +3 -0
  82. package/dist/domain/ports/instance-metadata.port.js +8 -0
  83. package/dist/domain/ports/instance-storage.port.js +3 -0
  84. package/dist/domain/ports/k8s-deployer.port.js +3 -0
  85. package/dist/domain/ports/logger.port.js +14 -0
  86. package/dist/domain/ports/output.port.js +3 -0
  87. package/dist/domain/ports/runtime-environment.port.js +6 -0
  88. package/dist/domain/ports/user-interaction.port.js +9 -0
  89. package/dist/domain/ports/user-settings.port.js +3 -0
  90. package/dist/domain/types/index.js +22 -0
  91. package/dist/domain/types/logger.types.js +29 -0
  92. package/dist/domain/types/validation.types.js +9 -0
  93. package/dist/domain/value-objects/acr-credentials.value-object.js +92 -0
  94. package/dist/domain/value-objects/chart-version.value-object.js +124 -0
  95. package/dist/domain/value-objects/config-log-level.value-object.js +84 -0
  96. package/dist/domain/value-objects/connection-info.value-object.js +65 -0
  97. package/dist/domain/value-objects/index.js +25 -0
  98. package/dist/domain/value-objects/instance-name.value-object.js +91 -0
  99. package/dist/domain/value-objects/jwt-key.value-object.js +97 -0
  100. package/dist/domain/value-objects/mssql-password.value-object.js +140 -0
  101. package/dist/domain/value-objects/rsa-key-pair.value-object.js +181 -0
  102. package/dist/index.js +6 -0
  103. package/dist/infrastructure/config/app-config.interface.js +3 -0
  104. package/dist/infrastructure/config/app-config.service.js +280 -0
  105. package/dist/infrastructure/config/config-validator.js +31 -0
  106. package/dist/infrastructure/config/crypto.service.js +125 -0
  107. package/dist/infrastructure/deployment/deployment.adapter.js +118 -0
  108. package/dist/infrastructure/deployment/interfaces/acr-credential-manager.interface.js +3 -0
  109. package/dist/infrastructure/deployment/interfaces/app-manager.interface.js +3 -0
  110. package/dist/infrastructure/deployment/interfaces/helm-registry.interface.js +3 -0
  111. package/dist/infrastructure/deployment/interfaces/infra-manager.interface.js +3 -0
  112. package/dist/infrastructure/deployment/interfaces/k8s-job-runner.interface.js +3 -0
  113. package/dist/infrastructure/deployment/interfaces/mssql-database-init.interface.js +3 -0
  114. package/dist/infrastructure/deployment/interfaces/mssql-helm-deployment.interface.js +3 -0
  115. package/dist/infrastructure/deployment/interfaces/mssql-storage.interface.js +3 -0
  116. package/dist/infrastructure/deployment/interfaces/mssql-user-manager.interface.js +3 -0
  117. package/dist/infrastructure/deployment/interfaces/oci-artifact.interface.js +3 -0
  118. package/dist/infrastructure/deployment/interfaces/secret-manager.interface.js +3 -0
  119. package/dist/infrastructure/deployment/interfaces/service-manager.interface.js +3 -0
  120. package/dist/infrastructure/deployment/interfaces/version-compatibility.interface.js +3 -0
  121. package/dist/infrastructure/deployment/services/acr-credential-manager.service.js +144 -0
  122. package/dist/infrastructure/deployment/services/app-manager.service.js +193 -0
  123. package/dist/infrastructure/deployment/services/base-helm-deployment.service.js +163 -0
  124. package/dist/infrastructure/deployment/services/helm-registry.service.js +126 -0
  125. package/dist/infrastructure/deployment/services/infra-manager.service.js +130 -0
  126. package/dist/infrastructure/deployment/services/k8s-job-runner.service.js +194 -0
  127. package/dist/infrastructure/deployment/services/mssql-database-init.service.js +139 -0
  128. package/dist/infrastructure/deployment/services/mssql-helm-deployment.service.js +100 -0
  129. package/dist/infrastructure/deployment/services/mssql-storage.service.js +54 -0
  130. package/dist/infrastructure/deployment/services/mssql-user-manager.service.js +66 -0
  131. package/dist/infrastructure/deployment/services/oci-artifact.service.js +289 -0
  132. package/dist/infrastructure/deployment/services/secret-manager.service.js +179 -0
  133. package/dist/infrastructure/deployment/services/service-manager.service.js +82 -0
  134. package/dist/infrastructure/deployment/services/version-compatibility.service.js +291 -0
  135. package/dist/infrastructure/environment/interfaces/hardware-info.interface.js +3 -0
  136. package/dist/infrastructure/environment/interfaces/network-checker.interface.js +3 -0
  137. package/dist/infrastructure/environment/services/hardware-info.service.js +135 -0
  138. package/dist/infrastructure/environment/services/network-checker.service.js +142 -0
  139. package/dist/infrastructure/environment/windows-environment.adapter.js +162 -0
  140. package/dist/infrastructure/errors/app-error.js +73 -0
  141. package/dist/infrastructure/errors/error-handler.interface.js +3 -0
  142. package/dist/infrastructure/errors/error-handler.js +218 -0
  143. package/dist/infrastructure/errors/exit-codes.js +27 -0
  144. package/dist/infrastructure/errors/index.js +25 -0
  145. package/dist/infrastructure/execution/builders/base-command.builder.js +122 -0
  146. package/dist/infrastructure/execution/builders/host-command.builder.js +58 -0
  147. package/dist/infrastructure/execution/builders/windows-host-command.builder.js +50 -0
  148. package/dist/infrastructure/execution/builders/wsl-command.builder.js +29 -0
  149. package/dist/infrastructure/execution/command-builder.js +252 -0
  150. package/dist/infrastructure/execution/command-executor.service.js +230 -0
  151. package/dist/infrastructure/execution/environments/wsl-execution.environment.js +70 -0
  152. package/dist/infrastructure/execution/execution-environment.factory.js +53 -0
  153. package/dist/infrastructure/execution/index.js +25 -0
  154. package/dist/infrastructure/execution/interfaces/command-builder.interface.js +3 -0
  155. package/dist/infrastructure/execution/interfaces/command-executor.interface.js +3 -0
  156. package/dist/infrastructure/execution/interfaces/execution-environment-factory.interface.js +3 -0
  157. package/dist/infrastructure/execution/interfaces/execution-environment.interface.js +7 -0
  158. package/dist/infrastructure/execution/interfaces/host-command-builder.interface.js +3 -0
  159. package/dist/infrastructure/execution/interfaces/index.js +23 -0
  160. package/dist/infrastructure/execution/interfaces/script-executor.interface.js +3 -0
  161. package/dist/infrastructure/execution/script-executor.service.js +171 -0
  162. package/dist/infrastructure/http/http-client.service.js +176 -0
  163. package/dist/infrastructure/http/index.js +18 -0
  164. package/dist/infrastructure/http/interfaces/http-client.interface.js +3 -0
  165. package/dist/infrastructure/interceptors/index.js +8 -0
  166. package/dist/infrastructure/interceptors/interceptor.factory.js +44 -0
  167. package/dist/infrastructure/interceptors/interceptor.interface.js +3 -0
  168. package/dist/infrastructure/interceptors/logging.interceptor.js +171 -0
  169. package/dist/infrastructure/logger/correlation-id.adapter.js +68 -0
  170. package/dist/infrastructure/logger/index.js +23 -0
  171. package/dist/infrastructure/logger/interfaces/index.js +22 -0
  172. package/dist/infrastructure/logger/interfaces/log-reader.repository.interface.js +7 -0
  173. package/dist/infrastructure/logger/interfaces/log-writer.repository.interface.js +7 -0
  174. package/dist/infrastructure/logger/logger.adapter.js +274 -0
  175. package/dist/infrastructure/logger/services/file-log-reader.repository.js +148 -0
  176. package/dist/infrastructure/logger/services/file-log-writer.repository.js +307 -0
  177. package/dist/infrastructure/logger/services/index.js +22 -0
  178. package/dist/infrastructure/persistence/index.js +25 -0
  179. package/dist/infrastructure/persistence/instance-metadata.adapter.js +100 -0
  180. package/dist/infrastructure/persistence/instance-storage.adapter.js +64 -0
  181. package/dist/infrastructure/persistence/interfaces/config.repository.interface.js +3 -0
  182. package/dist/infrastructure/persistence/interfaces/index.js +22 -0
  183. package/dist/infrastructure/persistence/interfaces/instance.repository.interface.js +3 -0
  184. package/dist/infrastructure/persistence/services/file-system-config.repository.js +168 -0
  185. package/dist/infrastructure/persistence/services/file-system-instance.repository.js +170 -0
  186. package/dist/infrastructure/persistence/services/index.js +22 -0
  187. package/dist/infrastructure/persistence/user-settings.adapter.js +55 -0
  188. package/dist/infrastructure/platform-detector.js +71 -0
  189. package/dist/infrastructure/platforms/windows/interfaces/microk8s.interface.js +3 -0
  190. package/dist/infrastructure/platforms/windows/interfaces/rootfs-manager.interface.js +3 -0
  191. package/dist/infrastructure/platforms/windows/interfaces/windows-features.interface.js +3 -0
  192. package/dist/infrastructure/platforms/windows/interfaces/windows-info.interface.js +3 -0
  193. package/dist/infrastructure/platforms/windows/interfaces/wsl-config.interface.js +3 -0
  194. package/dist/infrastructure/platforms/windows/interfaces/wsl-info.interface.js +3 -0
  195. package/dist/infrastructure/platforms/windows/interfaces/wsl-instance-inspection.interface.js +3 -0
  196. package/dist/infrastructure/platforms/windows/interfaces/wsl-instance-lifecycle.interface.js +3 -0
  197. package/dist/infrastructure/platforms/windows/interfaces/wsl-instance-naming.interface.js +3 -0
  198. package/dist/infrastructure/platforms/windows/interfaces/wsl-manager.interface.js +3 -0
  199. package/dist/infrastructure/platforms/windows/interfaces/wsl-resources.interface.js +8 -0
  200. package/dist/infrastructure/platforms/windows/interfaces/wsl-updater.interface.js +3 -0
  201. package/dist/infrastructure/platforms/windows/interfaces/wslconfig-parser.interface.js +3 -0
  202. package/dist/infrastructure/platforms/windows/parsers/wsl-version.parser.js +133 -0
  203. package/dist/infrastructure/platforms/windows/services/microk8s.service.js +168 -0
  204. package/dist/infrastructure/platforms/windows/services/rootfs-manager.service.js +336 -0
  205. package/dist/infrastructure/platforms/windows/services/windows-features.service.js +191 -0
  206. package/dist/infrastructure/platforms/windows/services/windows-info.service.js +138 -0
  207. package/dist/infrastructure/platforms/windows/services/wsl-config.service.js +171 -0
  208. package/dist/infrastructure/platforms/windows/services/wsl-info.service.js +226 -0
  209. package/dist/infrastructure/platforms/windows/services/wsl-instance-inspection.service.js +325 -0
  210. package/dist/infrastructure/platforms/windows/services/wsl-instance-lifecycle.service.js +442 -0
  211. package/dist/infrastructure/platforms/windows/services/wsl-instance-naming.service.js +93 -0
  212. package/dist/infrastructure/platforms/windows/services/wsl-updater.service.js +273 -0
  213. package/dist/infrastructure/platforms/windows/services/wslconfig-parser.service.js +222 -0
  214. package/dist/infrastructure/platforms/windows/wsl-base-image.adapter.js +41 -0
  215. package/dist/infrastructure/platforms/windows/wsl-instance-manager.adapter.js +150 -0
  216. package/dist/infrastructure/utils/error-formatter.util.js +29 -0
  217. package/dist/infrastructure/utils/file-operations.util.js +201 -0
  218. package/dist/infrastructure/utils/input-validator.util.js +152 -0
  219. package/dist/infrastructure/utils/retry.util.js +98 -0
  220. package/dist/presentation/controllers/config.controller.js +146 -0
  221. package/dist/presentation/controllers/credentials.controller.js +105 -0
  222. package/dist/presentation/controllers/index.js +25 -0
  223. package/dist/presentation/controllers/instance.controller.js +363 -0
  224. package/dist/presentation/controllers/logs.controller.js +103 -0
  225. package/dist/presentation/controllers/setup.controller.js +175 -0
  226. package/dist/presentation/interfaces/cli-options.interface.js +8 -0
  227. package/dist/presentation/prompts/acr-credentials.prompt.js +76 -0
  228. package/dist/presentation/prompts/index.js +21 -0
  229. package/dist/presentation/ui/cli-progress.service.js +193 -0
  230. package/dist/presentation/ui/constants/output-symbols.js +42 -0
  231. package/dist/presentation/ui/index.js +27 -0
  232. package/dist/presentation/ui/interaction.service.js +276 -0
  233. package/dist/presentation/ui/interfaces/cli-progress.interface.js +9 -0
  234. package/dist/presentation/ui/interfaces/output-formatter.interface.js +23 -0
  235. package/dist/presentation/ui/log-level.enum.js +66 -0
  236. package/dist/presentation/ui/output-builder.service.js +378 -0
  237. package/dist/presentation/ui/output-formatter.service.js +393 -0
  238. package/package.json +65 -0
@@ -0,0 +1,393 @@
1
+ "use strict";
2
+ /**
3
+ * Output Formatter Service
4
+ *
5
+ * 提供統一的輸出格式化功能,支援顏色輸出和表格格式化
6
+ * 可根據 TTY 狀態或配置啟用/停用顏色輸出
7
+ */
8
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
9
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
10
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
11
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
12
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
13
+ };
14
+ var __metadata = (this && this.__metadata) || function (k, v) {
15
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
16
+ };
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.OutputFormatter = void 0;
19
+ const tsyringe_1 = require("tsyringe");
20
+ const output_formatter_interface_1 = require("./interfaces/output-formatter.interface");
21
+ /**
22
+ * ANSI 顏色碼
23
+ */
24
+ const ANSI = {
25
+ reset: '\x1b[0m',
26
+ bold: '\x1b[1m',
27
+ dim: '\x1b[2m',
28
+ // 前景色
29
+ green: '\x1b[32m',
30
+ red: '\x1b[31m',
31
+ yellow: '\x1b[33m',
32
+ blue: '\x1b[34m',
33
+ cyan: '\x1b[36m',
34
+ white: '\x1b[37m',
35
+ };
36
+ /**
37
+ * OutputFormatter 實作
38
+ */
39
+ let OutputFormatter = class OutputFormatter {
40
+ constructor() {
41
+ // 預設根據 TTY 狀態決定是否啟用顏色
42
+ this.colorEnabled = process.stdout.isTTY ?? false;
43
+ }
44
+ /**
45
+ * 套用顏色格式
46
+ */
47
+ applyColor(text, colorCode) {
48
+ if (!this.colorEnabled) {
49
+ return text;
50
+ }
51
+ return `${colorCode}${text}${ANSI.reset}`;
52
+ }
53
+ /**
54
+ * 根據顏色名稱取得對應的 ANSI 碼
55
+ */
56
+ getColorCode(colorName) {
57
+ const colorMap = {
58
+ green: ANSI.green,
59
+ red: ANSI.red,
60
+ yellow: ANSI.yellow,
61
+ blue: ANSI.blue,
62
+ cyan: ANSI.cyan,
63
+ white: ANSI.white,
64
+ dim: ANSI.dim,
65
+ };
66
+ return colorMap[colorName.toLowerCase()] || '';
67
+ }
68
+ /**
69
+ * 檢查字串是否為數字(用於自動對齊)
70
+ */
71
+ isNumericValue(value) {
72
+ if (!value || value.trim() === '')
73
+ return false;
74
+ // 移除千分位符號和貨幣符號後檢查
75
+ const cleaned = value.replace(/[,$\s]/g, '');
76
+ return !isNaN(Number(cleaned)) && cleaned.length > 0;
77
+ }
78
+ /**
79
+ * 對齊文字
80
+ */
81
+ alignText(text, width, alignment) {
82
+ const strippedText = this.stripAnsi(text);
83
+ const textLength = strippedText.length;
84
+ const padding = width - textLength;
85
+ if (padding <= 0) {
86
+ return text;
87
+ }
88
+ switch (alignment) {
89
+ case 'right':
90
+ return ' '.repeat(padding) + text;
91
+ case 'center': {
92
+ // 使用 round 讓置中更精準,奇數時平均分配
93
+ const leftPad = Math.round(padding / 2);
94
+ const rightPad = padding - leftPad;
95
+ return ' '.repeat(leftPad) + text + ' '.repeat(rightPad);
96
+ }
97
+ case 'left':
98
+ default:
99
+ return text + ' '.repeat(padding);
100
+ }
101
+ }
102
+ /**
103
+ * 移除 ANSI 控制碼(用於計算實際文字長度)
104
+ */
105
+ stripAnsi(text) {
106
+ // eslint-disable-next-line no-control-regex
107
+ return text.replace(/\x1b\[[0-9;]*m/g, '');
108
+ }
109
+ /**
110
+ * 計算文字實際顯示長度(不包含 ANSI 碼)
111
+ */
112
+ getDisplayLength(text) {
113
+ return this.stripAnsi(text).length;
114
+ }
115
+ /**
116
+ * 格式化表格
117
+ */
118
+ formatTable(headers, rows, options) {
119
+ // 提取選項
120
+ const padding = options?.padding ?? 2;
121
+ const headerSeparator = options?.headerSeparator ?? true;
122
+ const borders = options?.borders ?? false;
123
+ const colorize = options?.colorize ?? true;
124
+ const statusColumn = options?.statusColumn;
125
+ const statusColors = options?.statusColors ?? output_formatter_interface_1.DEFAULT_STATUS_COLORS;
126
+ const emptyMessage = options?.emptyMessage ?? 'No data found';
127
+ // 處理空表格
128
+ if (rows.length === 0) {
129
+ return this.formatDim(emptyMessage);
130
+ }
131
+ // 找出狀態欄位的索引
132
+ const statusColumnIndex = statusColumn
133
+ ? headers.findIndex((h) => h.toLowerCase() === statusColumn.toLowerCase())
134
+ : -1;
135
+ // 計算欄位對齊方式和寬度
136
+ const columnAlignments = this.determineColumnAlignments(headers, rows, options);
137
+ const columnWidths = this.calculateColumnWidths(headers, rows);
138
+ // 根據邊框模式選擇格式化方法
139
+ if (borders) {
140
+ return this.formatTableWithBorders(headers, rows, columnWidths, columnAlignments, padding, headerSeparator, statusColumnIndex, colorize, statusColors);
141
+ }
142
+ else {
143
+ return this.formatTableWithoutBorders(headers, rows, columnWidths, columnAlignments, padding, headerSeparator, statusColumnIndex, colorize, statusColors);
144
+ }
145
+ }
146
+ /**
147
+ * 格式化成功訊息
148
+ */
149
+ formatSuccess(message) {
150
+ const icon = this.colorEnabled ? '✓' : '[OK]';
151
+ return this.applyColor(`${icon} ${message}`, ANSI.green);
152
+ }
153
+ /**
154
+ * 格式化錯誤訊息
155
+ */
156
+ formatError(message) {
157
+ const icon = this.colorEnabled ? '✗' : '[ERROR]';
158
+ return this.applyColor(`${icon} ${message}`, ANSI.red);
159
+ }
160
+ /**
161
+ * 格式化警告訊息
162
+ */
163
+ formatWarning(message) {
164
+ const icon = this.colorEnabled ? '⚠' : '[WARN]';
165
+ return this.applyColor(`${icon} ${message}`, ANSI.yellow);
166
+ }
167
+ /**
168
+ * 格式化資訊訊息
169
+ */
170
+ formatInfo(message) {
171
+ const icon = this.colorEnabled ? 'ℹ' : '[INFO]';
172
+ return this.applyColor(`${icon} ${message}`, ANSI.cyan);
173
+ }
174
+ /**
175
+ * 格式化淡化文字
176
+ */
177
+ formatDim(message) {
178
+ return this.applyColor(message, ANSI.dim);
179
+ }
180
+ /**
181
+ * 格式化粗體文字
182
+ */
183
+ formatBold(message) {
184
+ return this.applyColor(message, ANSI.bold);
185
+ }
186
+ /**
187
+ * 格式化帶有前綴圖示的訊息
188
+ */
189
+ formatWithIcon(icon, message) {
190
+ return `${icon} ${message}`;
191
+ }
192
+ /**
193
+ * 格式化分隔線
194
+ */
195
+ formatDivider(length = 60, char = '─') {
196
+ return this.applyColor(char.repeat(length), ANSI.dim);
197
+ }
198
+ /**
199
+ * 格式化縮排文字
200
+ */
201
+ formatIndent(message, level = 1) {
202
+ const indent = ' '.repeat(level);
203
+ return message
204
+ .split('\n')
205
+ .map((line) => `${indent}${line}`)
206
+ .join('\n');
207
+ }
208
+ /**
209
+ * 啟用或停用顏色輸出
210
+ */
211
+ setColorEnabled(enabled) {
212
+ this.colorEnabled = enabled;
213
+ }
214
+ /**
215
+ * 檢查顏色輸出是否啟用
216
+ */
217
+ isColorEnabled() {
218
+ return this.colorEnabled;
219
+ }
220
+ // ============================================================================
221
+ // Private 方法 - 表格格式化輔助方法
222
+ // ============================================================================
223
+ /**
224
+ * 格式化有邊框的表格
225
+ */
226
+ formatTableWithBorders(headers, rows, columnWidths, columnAlignments, padding, headerSeparator, statusColumnIndex, colorize, statusColors) {
227
+ const lines = [];
228
+ const actualPadding = Math.max(1, padding - 1); // 邊框模式下調整間距
229
+ // 頂部邊框
230
+ lines.push(this.drawTopBorder(columnWidths, actualPadding));
231
+ // 表頭行
232
+ lines.push(this.formatHeaderRowWithBorders(headers, columnWidths, columnAlignments, actualPadding));
233
+ // 分隔線
234
+ if (headerSeparator) {
235
+ lines.push(this.drawMiddleSeparator(columnWidths, actualPadding));
236
+ }
237
+ // 資料行
238
+ for (const row of rows) {
239
+ lines.push(this.formatDataRowWithBorders(row, columnWidths, columnAlignments, actualPadding, statusColumnIndex, colorize, statusColors));
240
+ }
241
+ // 底部邊框
242
+ lines.push(this.drawBottomBorder(columnWidths, actualPadding));
243
+ return lines.join('\n');
244
+ }
245
+ /**
246
+ * 格式化無邊框的表格
247
+ */
248
+ formatTableWithoutBorders(headers, rows, columnWidths, columnAlignments, padding, headerSeparator, statusColumnIndex, colorize, statusColors) {
249
+ const lines = [];
250
+ // 表頭行
251
+ lines.push(this.formatHeaderRowWithoutBorders(headers, columnWidths, columnAlignments, padding));
252
+ // 分隔線
253
+ if (headerSeparator) {
254
+ lines.push(this.drawSeparatorWithoutBorders(columnWidths, padding));
255
+ }
256
+ // 資料行
257
+ for (const row of rows) {
258
+ lines.push(this.formatDataRowWithoutBorders(row, columnWidths, columnAlignments, padding, statusColumnIndex, colorize, statusColors));
259
+ }
260
+ return lines.join('\n');
261
+ }
262
+ /**
263
+ * 決定每欄的對齊方式
264
+ */
265
+ determineColumnAlignments(headers, rows, options) {
266
+ if (options?.columnAlignments) {
267
+ return options.columnAlignments;
268
+ }
269
+ const autoAlignNumbers = options?.autoAlignNumbers ?? true;
270
+ return headers.map((_, colIndex) => {
271
+ if (!autoAlignNumbers)
272
+ return 'left';
273
+ // 檢查此欄是否所有值都是數字
274
+ const allNumeric = rows.every((row) => colIndex < row.length && this.isNumericValue(row[colIndex]));
275
+ return allNumeric ? 'right' : 'left';
276
+ });
277
+ }
278
+ /**
279
+ * 計算每欄的最大寬度
280
+ */
281
+ calculateColumnWidths(headers, rows) {
282
+ const columnWidths = headers.map((h) => h.length);
283
+ for (const row of rows) {
284
+ row.forEach((cell, i) => {
285
+ if (i < columnWidths.length) {
286
+ columnWidths[i] = Math.max(columnWidths[i], this.getDisplayLength(cell));
287
+ }
288
+ });
289
+ }
290
+ return columnWidths;
291
+ }
292
+ /**
293
+ * 繪製頂部邊框 ┌─┬─┐
294
+ */
295
+ drawTopBorder(columnWidths, padding) {
296
+ const border = '┌' + columnWidths
297
+ .map((w, i) => '─'.repeat(w + padding * 2) + (i < columnWidths.length - 1 ? '┬' : ''))
298
+ .join('') + '┐';
299
+ return this.applyColor(border, ANSI.dim);
300
+ }
301
+ /**
302
+ * 繪製中間分隔線 ├─┼─┤
303
+ */
304
+ drawMiddleSeparator(columnWidths, padding) {
305
+ const separator = '├' + columnWidths
306
+ .map((w, i) => '─'.repeat(w + padding * 2) + (i < columnWidths.length - 1 ? '┼' : ''))
307
+ .join('') + '┤';
308
+ return this.applyColor(separator, ANSI.dim);
309
+ }
310
+ /**
311
+ * 繪製底部邊框 └─┴─┘
312
+ */
313
+ drawBottomBorder(columnWidths, padding) {
314
+ const border = '└' + columnWidths
315
+ .map((w, i) => '─'.repeat(w + padding * 2) + (i < columnWidths.length - 1 ? '┴' : ''))
316
+ .join('') + '┘';
317
+ return this.applyColor(border, ANSI.dim);
318
+ }
319
+ /**
320
+ * 格式化表頭行(有邊框)
321
+ */
322
+ formatHeaderRowWithBorders(headers, columnWidths, columnAlignments, padding) {
323
+ const headerCells = headers.map((h, i) => {
324
+ const cell = ' '.repeat(padding) + this.alignText(h, columnWidths[i], columnAlignments[i] || 'left') + ' '.repeat(padding);
325
+ return this.applyColor(cell, ANSI.bold);
326
+ });
327
+ return this.applyColor('│', ANSI.dim) + headerCells.join(this.applyColor('│', ANSI.dim)) + this.applyColor('│', ANSI.dim);
328
+ }
329
+ /**
330
+ * 格式化資料行(有邊框)
331
+ */
332
+ formatDataRowWithBorders(row, columnWidths, columnAlignments, padding, statusColumnIndex, colorize, statusColors) {
333
+ const formattedCells = row.map((cell, i) => {
334
+ const width = i < columnWidths.length ? columnWidths[i] : this.getDisplayLength(cell);
335
+ const alignment = columnAlignments[i] || 'left';
336
+ const formattedCell = this.formatCellWithStatus(cell, i, statusColumnIndex, colorize, statusColors, width, alignment);
337
+ return ' '.repeat(padding) + formattedCell + ' '.repeat(padding);
338
+ });
339
+ return this.applyColor('│', ANSI.dim) + formattedCells.join(this.applyColor('│', ANSI.dim)) + this.applyColor('│', ANSI.dim);
340
+ }
341
+ /**
342
+ * 格式化表頭行(無邊框)
343
+ */
344
+ formatHeaderRowWithoutBorders(headers, columnWidths, columnAlignments, padding) {
345
+ const headerCells = headers.map((h, i) => this.alignText(h, columnWidths[i], columnAlignments[i] || 'left'));
346
+ const headerLine = headerCells.map((cell, i) => i < headerCells.length - 1 ? cell + ' '.repeat(padding) : cell).join('');
347
+ return this.applyColor(headerLine, ANSI.bold);
348
+ }
349
+ /**
350
+ * 格式化資料行(無邊框)
351
+ */
352
+ formatDataRowWithoutBorders(row, columnWidths, columnAlignments, padding, statusColumnIndex, colorize, statusColors) {
353
+ const formattedCells = row.map((cell, i) => {
354
+ const width = i < columnWidths.length ? columnWidths[i] : this.getDisplayLength(cell);
355
+ const alignment = columnAlignments[i] || 'left';
356
+ return this.formatCellWithStatus(cell, i, statusColumnIndex, colorize, statusColors, width, alignment);
357
+ });
358
+ const rowLine = formattedCells.map((cell, i) => i < formattedCells.length - 1 ? cell + ' '.repeat(padding) : cell).join('');
359
+ return rowLine;
360
+ }
361
+ /**
362
+ * 繪製無邊框分隔線
363
+ */
364
+ drawSeparatorWithoutBorders(columnWidths, padding) {
365
+ const separator = columnWidths
366
+ .map((w, i) => i < columnWidths.length - 1 ? '─'.repeat(w + padding) : '─'.repeat(w))
367
+ .join('');
368
+ return this.applyColor(separator, ANSI.dim);
369
+ }
370
+ /**
371
+ * 格式化帶狀態顏色的儲存格
372
+ */
373
+ formatCellWithStatus(cell, columnIndex, statusColumnIndex, colorize, statusColors, width, alignment) {
374
+ // 狀態欄位顏色編碼
375
+ if (colorize && columnIndex === statusColumnIndex && this.colorEnabled) {
376
+ const colorName = statusColors[cell];
377
+ if (colorName) {
378
+ const colorCode = this.getColorCode(colorName);
379
+ if (colorCode) {
380
+ const coloredCell = this.applyColor(cell, colorCode);
381
+ return this.alignText(coloredCell, width, alignment);
382
+ }
383
+ }
384
+ }
385
+ return this.alignText(cell, width, alignment);
386
+ }
387
+ };
388
+ exports.OutputFormatter = OutputFormatter;
389
+ exports.OutputFormatter = OutputFormatter = __decorate([
390
+ (0, tsyringe_1.injectable)(),
391
+ __metadata("design:paramtypes", [])
392
+ ], OutputFormatter);
393
+ //# sourceMappingURL=output-formatter.service.js.map
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@uofx/cli",
3
+ "version": "1.0.0",
4
+ "description": "CLI tool for installing and managing the UOFX development environment on WSL",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "uofx": "./dist/index.js"
8
+ },
9
+ "engines": {
10
+ "node": ">=18.20.0"
11
+ },
12
+ "scripts": {
13
+ "start": "node dist/index.js",
14
+ "build": "tsc -p tsconfig.prod.json && tsc-alias -p tsconfig.prod.json",
15
+ "build:prod": "tsc -p tsconfig.prod.json && tsc-alias -p tsconfig.prod.json && javascript-obfuscator dist --output dist --config obfuscator.config.js --exclude '**/di/tokens.js'",
16
+ "lint": "eslint src/**/*.ts",
17
+ "format": "prettier --write src/**/*.ts",
18
+ "test": "jest",
19
+ "test:coverage": "jest --coverage",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "keywords": [
23
+ "UOF X"
24
+ ],
25
+ "author": "e-Excellence Inc.",
26
+ "license": "SEE LICENSE IN LICENSE",
27
+ "files": [
28
+ "dist",
29
+ "!dist/**/*.map",
30
+ "!dist/**/*.ts",
31
+ "README.md",
32
+ "LICENSE",
33
+ "THIRD-PARTY-NOTICES.txt"
34
+ ],
35
+ "devDependencies": {
36
+ "@types/cli-progress": "^3.11.6",
37
+ "@types/jest": "^29.5.0",
38
+ "@types/node": "^18.19.0",
39
+ "@types/prompts": "^2.4.9",
40
+ "@types/semver": "^7.7.1",
41
+ "@typescript-eslint/eslint-plugin": "^7.18.0",
42
+ "@typescript-eslint/parser": "^7.18.0",
43
+ "eslint": "^8.57.1",
44
+ "eslint-config-prettier": "^10.1.8",
45
+ "generate-license-file": "^3.8.1",
46
+ "javascript-obfuscator": "4.1.1",
47
+ "jest": "^29.7.0",
48
+ "jest-junit": "^16.0.0",
49
+ "prettier": "^3.6.2",
50
+ "ts-jest": "^29.4.5",
51
+ "tsc-alias": "^1.8.16",
52
+ "tsconfig-paths": "^4.2.0",
53
+ "typescript": "^5.9.3"
54
+ },
55
+ "dependencies": {
56
+ "axios": "^1.13.2",
57
+ "chalk": "^4.1.2",
58
+ "cli-progress": "^3.12.0",
59
+ "commander": "^12.1.0",
60
+ "prompts": "^2.4.2",
61
+ "reflect-metadata": "^0.2.2",
62
+ "semver": "^7.7.3",
63
+ "tsyringe": "^4.10.0"
64
+ }
65
+ }