@wundr.io/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 (213) hide show
  1. package/README.md +551 -0
  2. package/bin/wundr.js +39 -0
  3. package/dist/ai/ai-service.d.ts +152 -0
  4. package/dist/ai/ai-service.d.ts.map +1 -0
  5. package/dist/ai/ai-service.js +430 -0
  6. package/dist/ai/ai-service.js.map +1 -0
  7. package/dist/ai/claude-client.d.ts +130 -0
  8. package/dist/ai/claude-client.d.ts.map +1 -0
  9. package/dist/ai/claude-client.js +339 -0
  10. package/dist/ai/claude-client.js.map +1 -0
  11. package/dist/ai/conversation-manager.d.ts +164 -0
  12. package/dist/ai/conversation-manager.d.ts.map +1 -0
  13. package/dist/ai/conversation-manager.js +612 -0
  14. package/dist/ai/conversation-manager.js.map +1 -0
  15. package/dist/ai/index.d.ts +5 -0
  16. package/dist/ai/index.d.ts.map +1 -0
  17. package/dist/ai/index.js +8 -0
  18. package/dist/ai/index.js.map +1 -0
  19. package/dist/cli.d.ts +36 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +173 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/commands/ai.d.ts +89 -0
  24. package/dist/commands/ai.d.ts.map +1 -0
  25. package/dist/commands/ai.js +735 -0
  26. package/dist/commands/ai.js.map +1 -0
  27. package/dist/commands/analyze-optimized.d.ts +14 -0
  28. package/dist/commands/analyze-optimized.d.ts.map +1 -0
  29. package/dist/commands/analyze-optimized.js +437 -0
  30. package/dist/commands/analyze-optimized.js.map +1 -0
  31. package/dist/commands/analyze.d.ts +65 -0
  32. package/dist/commands/analyze.d.ts.map +1 -0
  33. package/dist/commands/analyze.js +435 -0
  34. package/dist/commands/analyze.js.map +1 -0
  35. package/dist/commands/batch.d.ts +71 -0
  36. package/dist/commands/batch.d.ts.map +1 -0
  37. package/dist/commands/batch.js +738 -0
  38. package/dist/commands/batch.js.map +1 -0
  39. package/dist/commands/chat.d.ts +71 -0
  40. package/dist/commands/chat.d.ts.map +1 -0
  41. package/dist/commands/chat.js +674 -0
  42. package/dist/commands/chat.js.map +1 -0
  43. package/dist/commands/claude-init.d.ts +28 -0
  44. package/dist/commands/claude-init.d.ts.map +1 -0
  45. package/dist/commands/claude-init.js +587 -0
  46. package/dist/commands/claude-init.js.map +1 -0
  47. package/dist/commands/claude-setup.d.ts +32 -0
  48. package/dist/commands/claude-setup.d.ts.map +1 -0
  49. package/dist/commands/claude-setup.js +570 -0
  50. package/dist/commands/claude-setup.js.map +1 -0
  51. package/dist/commands/computer-setup-commands.d.ts +39 -0
  52. package/dist/commands/computer-setup-commands.d.ts.map +1 -0
  53. package/dist/commands/computer-setup-commands.js +563 -0
  54. package/dist/commands/computer-setup-commands.js.map +1 -0
  55. package/dist/commands/computer-setup.d.ts +7 -0
  56. package/dist/commands/computer-setup.d.ts.map +1 -0
  57. package/dist/commands/computer-setup.js +481 -0
  58. package/dist/commands/computer-setup.js.map +1 -0
  59. package/dist/commands/create-command.d.ts +7 -0
  60. package/dist/commands/create-command.d.ts.map +1 -0
  61. package/dist/commands/create-command.js +158 -0
  62. package/dist/commands/create-command.js.map +1 -0
  63. package/dist/commands/create.d.ts +74 -0
  64. package/dist/commands/create.d.ts.map +1 -0
  65. package/dist/commands/create.js +556 -0
  66. package/dist/commands/create.js.map +1 -0
  67. package/dist/commands/dashboard.d.ts +91 -0
  68. package/dist/commands/dashboard.d.ts.map +1 -0
  69. package/dist/commands/dashboard.js +537 -0
  70. package/dist/commands/dashboard.js.map +1 -0
  71. package/dist/commands/govern.d.ts +70 -0
  72. package/dist/commands/govern.d.ts.map +1 -0
  73. package/dist/commands/govern.js +480 -0
  74. package/dist/commands/govern.js.map +1 -0
  75. package/dist/commands/init.d.ts +55 -0
  76. package/dist/commands/init.d.ts.map +1 -0
  77. package/dist/commands/init.js +584 -0
  78. package/dist/commands/init.js.map +1 -0
  79. package/dist/commands/performance-optimizer.d.ts +30 -0
  80. package/dist/commands/performance-optimizer.d.ts.map +1 -0
  81. package/dist/commands/performance-optimizer.js +649 -0
  82. package/dist/commands/performance-optimizer.js.map +1 -0
  83. package/dist/commands/plugins.d.ts +87 -0
  84. package/dist/commands/plugins.d.ts.map +1 -0
  85. package/dist/commands/plugins.js +685 -0
  86. package/dist/commands/plugins.js.map +1 -0
  87. package/dist/commands/setup.d.ts +29 -0
  88. package/dist/commands/setup.d.ts.map +1 -0
  89. package/dist/commands/setup.js +399 -0
  90. package/dist/commands/setup.js.map +1 -0
  91. package/dist/commands/test-init.d.ts +9 -0
  92. package/dist/commands/test-init.d.ts.map +1 -0
  93. package/dist/commands/test-init.js +222 -0
  94. package/dist/commands/test-init.js.map +1 -0
  95. package/dist/commands/test.d.ts +25 -0
  96. package/dist/commands/test.d.ts.map +1 -0
  97. package/dist/commands/test.js +217 -0
  98. package/dist/commands/test.js.map +1 -0
  99. package/dist/commands/watch.d.ts +76 -0
  100. package/dist/commands/watch.d.ts.map +1 -0
  101. package/dist/commands/watch.js +610 -0
  102. package/dist/commands/watch.js.map +1 -0
  103. package/dist/context/context-manager.d.ts +155 -0
  104. package/dist/context/context-manager.d.ts.map +1 -0
  105. package/dist/context/context-manager.js +383 -0
  106. package/dist/context/context-manager.js.map +1 -0
  107. package/dist/context/index.d.ts +3 -0
  108. package/dist/context/index.d.ts.map +1 -0
  109. package/dist/context/index.js +6 -0
  110. package/dist/context/index.js.map +1 -0
  111. package/dist/context/session-manager.d.ts +207 -0
  112. package/dist/context/session-manager.d.ts.map +1 -0
  113. package/dist/context/session-manager.js +682 -0
  114. package/dist/context/session-manager.js.map +1 -0
  115. package/dist/index.d.ts +8 -0
  116. package/dist/index.d.ts.map +1 -0
  117. package/dist/index.js +51 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/interactive/interactive-mode.d.ts +76 -0
  120. package/dist/interactive/interactive-mode.d.ts.map +1 -0
  121. package/dist/interactive/interactive-mode.js +730 -0
  122. package/dist/interactive/interactive-mode.js.map +1 -0
  123. package/dist/nlp/command-mapper.d.ts +174 -0
  124. package/dist/nlp/command-mapper.d.ts.map +1 -0
  125. package/dist/nlp/command-mapper.js +623 -0
  126. package/dist/nlp/command-mapper.js.map +1 -0
  127. package/dist/nlp/command-parser.d.ts +106 -0
  128. package/dist/nlp/command-parser.d.ts.map +1 -0
  129. package/dist/nlp/command-parser.js +416 -0
  130. package/dist/nlp/command-parser.js.map +1 -0
  131. package/dist/nlp/index.d.ts +5 -0
  132. package/dist/nlp/index.d.ts.map +1 -0
  133. package/dist/nlp/index.js +8 -0
  134. package/dist/nlp/index.js.map +1 -0
  135. package/dist/nlp/intent-classifier.d.ts +59 -0
  136. package/dist/nlp/intent-classifier.d.ts.map +1 -0
  137. package/dist/nlp/intent-classifier.js +384 -0
  138. package/dist/nlp/intent-classifier.js.map +1 -0
  139. package/dist/nlp/intent-parser.d.ts +152 -0
  140. package/dist/nlp/intent-parser.d.ts.map +1 -0
  141. package/dist/nlp/intent-parser.js +739 -0
  142. package/dist/nlp/intent-parser.js.map +1 -0
  143. package/dist/plugins/plugin-manager.d.ts +120 -0
  144. package/dist/plugins/plugin-manager.d.ts.map +1 -0
  145. package/dist/plugins/plugin-manager.js +595 -0
  146. package/dist/plugins/plugin-manager.js.map +1 -0
  147. package/dist/types/index.d.ts +224 -0
  148. package/dist/types/index.d.ts.map +1 -0
  149. package/dist/types/index.js +3 -0
  150. package/dist/types/index.js.map +1 -0
  151. package/dist/utils/config-manager.d.ts +73 -0
  152. package/dist/utils/config-manager.d.ts.map +1 -0
  153. package/dist/utils/config-manager.js +339 -0
  154. package/dist/utils/config-manager.js.map +1 -0
  155. package/dist/utils/error-handler.d.ts +46 -0
  156. package/dist/utils/error-handler.d.ts.map +1 -0
  157. package/dist/utils/error-handler.js +169 -0
  158. package/dist/utils/error-handler.js.map +1 -0
  159. package/dist/utils/logger.d.ts +25 -0
  160. package/dist/utils/logger.d.ts.map +1 -0
  161. package/dist/utils/logger.js +94 -0
  162. package/dist/utils/logger.js.map +1 -0
  163. package/package.json +119 -0
  164. package/src/ai/ai-service.ts +595 -0
  165. package/src/ai/claude-client.ts +490 -0
  166. package/src/ai/conversation-manager.ts +907 -0
  167. package/src/ai/index.ts +8 -0
  168. package/src/cli.ts +202 -0
  169. package/src/commands/ai.ts +995 -0
  170. package/src/commands/analyze-optimized.ts +641 -0
  171. package/src/commands/analyze.ts +576 -0
  172. package/src/commands/batch.ts +935 -0
  173. package/src/commands/chat.ts +876 -0
  174. package/src/commands/claude-init.ts +715 -0
  175. package/src/commands/claude-setup.ts +697 -0
  176. package/src/commands/computer-setup-commands.ts +709 -0
  177. package/src/commands/computer-setup.ts +565 -0
  178. package/src/commands/create-command.ts +175 -0
  179. package/src/commands/create.ts +727 -0
  180. package/src/commands/dashboard.ts +691 -0
  181. package/src/commands/govern.ts +635 -0
  182. package/src/commands/init.ts +677 -0
  183. package/src/commands/performance-optimizer.ts +864 -0
  184. package/src/commands/plugins.ts +848 -0
  185. package/src/commands/setup.ts +508 -0
  186. package/src/commands/test-init.ts +242 -0
  187. package/src/commands/test.ts +264 -0
  188. package/src/commands/watch.ts +755 -0
  189. package/src/context/context-manager.ts +546 -0
  190. package/src/context/index.ts +9 -0
  191. package/src/context/session-manager.ts +1019 -0
  192. package/src/index.ts +64 -0
  193. package/src/interactive/interactive-mode.ts +830 -0
  194. package/src/nlp/command-mapper.ts +885 -0
  195. package/src/nlp/command-parser.ts +564 -0
  196. package/src/nlp/index.ts +4 -0
  197. package/src/nlp/intent-classifier.ts +458 -0
  198. package/src/nlp/intent-parser.ts +1101 -0
  199. package/src/plugins/plugin-manager.ts +744 -0
  200. package/src/types/index.ts +252 -0
  201. package/src/types/modules.d.ts +56 -0
  202. package/src/utils/config-manager.ts +391 -0
  203. package/src/utils/error-handler.ts +192 -0
  204. package/src/utils/logger.ts +104 -0
  205. package/templates/batch/ci-cd.yaml +62 -0
  206. package/templates/component/{{fileName}}.test.tsx +17 -0
  207. package/templates/component/{{fileName}}.tsx +21 -0
  208. package/templates/service/{{fileName}}.ts +98 -0
  209. package/templates/wundr-test.config.js +0 -0
  210. package/test-suites/api/health.spec.ts +134 -0
  211. package/test-suites/helpers/test-config.ts +84 -0
  212. package/test-suites/ui/accessibility.spec.ts +102 -0
  213. package/test-suites/ui/smoke.spec.ts +92 -0
@@ -0,0 +1,192 @@
1
+ import chalk from 'chalk';
2
+ import { WundrError } from '../types';
3
+ import { logger } from './logger';
4
+
5
+ /**
6
+ * Centralized error handling system
7
+ */
8
+ class ErrorHandler {
9
+ private errorCodes: Map<string, string> = new Map([
10
+ ['ENOENT', 'File or directory not found'],
11
+ ['EACCES', 'Permission denied'],
12
+ ['EEXIST', 'File or directory already exists'],
13
+ ['ENOTDIR', 'Not a directory'],
14
+ ['EISDIR', 'Is a directory'],
15
+ ['EMFILE', 'Too many open files'],
16
+ ['ENOTFOUND', 'Command or resource not found'],
17
+ ['WUNDR_CONFIG_INVALID', 'Invalid configuration file'],
18
+ ['WUNDR_PLUGIN_LOAD_FAILED', 'Failed to load plugin'],
19
+ ['WUNDR_COMMAND_FAILED', 'Command execution failed'],
20
+ ['WUNDR_ANALYSIS_FAILED', 'Analysis operation failed'],
21
+ ['WUNDR_NETWORK_ERROR', 'Network operation failed'],
22
+ ]);
23
+
24
+ /**
25
+ * Handle various types of errors with appropriate formatting
26
+ */
27
+ handle(error: Error | WundrError): void {
28
+ const wundrError = error as WundrError;
29
+
30
+ // Log the raw error in debug mode
31
+ logger.debug('Raw error:', error);
32
+
33
+ if (this.isKnownError(wundrError)) {
34
+ this.handleKnownError(wundrError);
35
+ } else if (this.isSystemError(error)) {
36
+ this.handleSystemError(error);
37
+ } else {
38
+ this.handleUnknownError(error);
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Check if error is a known Wundr error
44
+ */
45
+ private isKnownError(error: WundrError): boolean {
46
+ return !!error.code && error.code.startsWith('WUNDR_');
47
+ }
48
+
49
+ /**
50
+ * Check if error is a system/Node.js error
51
+ */
52
+ private isSystemError(error: Error): boolean {
53
+ return 'code' in error && typeof (error as any).code === 'string';
54
+ }
55
+
56
+ /**
57
+ * Handle known Wundr errors
58
+ */
59
+ private handleKnownError(error: WundrError): void {
60
+ const description =
61
+ this.errorCodes.get(error.code || '') || 'Unknown error';
62
+
63
+ console.error(chalk.red('✖ Wundr Error'));
64
+ console.error(chalk.red(` Code: ${error.code}`));
65
+ console.error(chalk.red(` Message: ${error.message}`));
66
+ console.error(chalk.gray(` Description: ${description}`));
67
+
68
+ if (error.context) {
69
+ console.error(chalk.gray(' Context:'));
70
+ Object.entries(error.context).forEach(([key, value]) => {
71
+ console.error(chalk.gray(` ${key}: ${value}`));
72
+ });
73
+ }
74
+
75
+ if (error.recoverable) {
76
+ console.error(chalk.yellow('\n💡 This error might be recoverable. Try:'));
77
+ this.suggestRecoveryActions(error.code || '');
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Handle system errors
83
+ */
84
+ private handleSystemError(error: any): void {
85
+ const description = this.errorCodes.get(error.code) || 'System error';
86
+
87
+ console.error(chalk.red('✖ System Error'));
88
+ console.error(chalk.red(` Code: ${error.code}`));
89
+ console.error(chalk.red(` Message: ${error.message}`));
90
+ console.error(chalk.gray(` Description: ${description}`));
91
+
92
+ if (error.path) {
93
+ console.error(chalk.gray(` Path: ${error.path}`));
94
+ }
95
+
96
+ if (error.syscall) {
97
+ console.error(chalk.gray(` System Call: ${error.syscall}`));
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Handle unknown errors
103
+ */
104
+ private handleUnknownError(error: Error): void {
105
+ console.error(chalk.red('✖ Unexpected Error'));
106
+ console.error(chalk.red(` Message: ${error.message}`));
107
+
108
+ if (error.stack) {
109
+ console.error(chalk.gray(' Stack Trace:'));
110
+ error.stack.split('\n').forEach(line => {
111
+ console.error(chalk.gray(` ${line}`));
112
+ });
113
+ }
114
+
115
+ console.error(chalk.yellow('\n💡 This appears to be an unexpected error.'));
116
+ console.error(
117
+ chalk.yellow(' Please report this issue with the above details.')
118
+ );
119
+ }
120
+
121
+ /**
122
+ * Suggest recovery actions based on error code
123
+ */
124
+ private suggestRecoveryActions(code: string): void {
125
+ const suggestions: Record<string, string[]> = {
126
+ WUNDR_CONFIG_INVALID: [
127
+ '• Check your wundr.config.json syntax',
128
+ '• Run `wundr init` to create a new configuration',
129
+ '• Validate your configuration with `wundr config validate`',
130
+ ],
131
+ WUNDR_PLUGIN_LOAD_FAILED: [
132
+ '• Check if the plugin is properly installed',
133
+ '• Verify plugin compatibility with current Wundr version',
134
+ '• Run `wundr plugins list` to see available plugins',
135
+ ],
136
+ WUNDR_COMMAND_FAILED: [
137
+ '• Check command syntax and arguments',
138
+ '• Verify you have necessary permissions',
139
+ '• Run with --verbose for more details',
140
+ ],
141
+ ENOENT: [
142
+ '• Verify the file or directory path',
143
+ "• Check if you're in the correct directory",
144
+ '• Run `wundr init` if in a new project',
145
+ ],
146
+ EACCES: [
147
+ '• Check file permissions',
148
+ '• Run with appropriate user privileges',
149
+ '• Verify directory access rights',
150
+ ],
151
+ };
152
+
153
+ const actions = suggestions[code];
154
+ if (actions) {
155
+ actions.forEach(action => {
156
+ console.error(chalk.yellow(action));
157
+ });
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Create a standardized Wundr error
163
+ */
164
+ createError(
165
+ code: string,
166
+ message: string,
167
+ context?: Record<string, any>,
168
+ recoverable = false
169
+ ): WundrError {
170
+ const error = new Error(message) as WundrError;
171
+ error.code = code;
172
+ error.context = context;
173
+ error.recoverable = recoverable;
174
+ return error;
175
+ }
176
+
177
+ /**
178
+ * Wrap async operations with error handling
179
+ */
180
+ async wrap<T>(operation: () => Promise<T>, context?: string): Promise<T> {
181
+ try {
182
+ return await operation();
183
+ } catch (error) {
184
+ if (context) {
185
+ logger.error(`Error in ${context}:`, error);
186
+ }
187
+ throw error;
188
+ }
189
+ }
190
+ }
191
+
192
+ export const errorHandler = new ErrorHandler();
@@ -0,0 +1,104 @@
1
+ import chalk from 'chalk';
2
+ import { Logger } from '../types';
3
+
4
+ /**
5
+ * Enhanced logger with multiple levels and colored output
6
+ */
7
+ class WundrLogger implements Logger {
8
+ private level: 'debug' | 'info' | 'warn' | 'error' = 'info';
9
+ private silent = false;
10
+
11
+ setLevel(level: 'debug' | 'info' | 'warn' | 'error'): void {
12
+ this.level = level;
13
+ }
14
+
15
+ setSilent(silent: boolean): void {
16
+ this.silent = silent;
17
+ }
18
+
19
+ private shouldLog(level: string): boolean {
20
+ if (this.silent) return false;
21
+
22
+ const levels = { debug: 0, info: 1, warn: 2, error: 3 };
23
+ return levels[level as keyof typeof levels] >= levels[this.level];
24
+ }
25
+
26
+ private formatMessage(
27
+ level: string,
28
+ message: string,
29
+ ...args: any[]
30
+ ): string {
31
+ const timestamp = new Date().toISOString();
32
+ const prefix = `[${timestamp}] [${level.toUpperCase()}]`;
33
+ const fullMessage =
34
+ args.length > 0 ? `${message} ${args.join(' ')}` : message;
35
+ return `${prefix} ${fullMessage}`;
36
+ }
37
+
38
+ debug(message: string, ...args: any[]): void {
39
+ if (!this.shouldLog('debug')) return;
40
+ console.log(chalk.gray(this.formatMessage('debug', message, ...args)));
41
+ }
42
+
43
+ info(message: string, ...args: any[]): void {
44
+ if (!this.shouldLog('info')) return;
45
+ console.log(chalk.blue(this.formatMessage('info', message, ...args)));
46
+ }
47
+
48
+ warn(message: string, ...args: any[]): void {
49
+ if (!this.shouldLog('warn')) return;
50
+ console.warn(chalk.yellow(this.formatMessage('warn', message, ...args)));
51
+ }
52
+
53
+ error(message: string, ...args: any[]): void {
54
+ if (!this.shouldLog('error')) return;
55
+ console.error(chalk.red(this.formatMessage('error', message, ...args)));
56
+ }
57
+
58
+ success(message: string, ...args: any[]): void {
59
+ if (!this.shouldLog('info')) return;
60
+ console.log(chalk.green(this.formatMessage('success', message, ...args)));
61
+ }
62
+
63
+ // Utility methods for structured logging
64
+ table(data: any[]): void {
65
+ if (this.silent) return;
66
+ console.table(data);
67
+ }
68
+
69
+ json(data: any): void {
70
+ if (this.silent) return;
71
+ console.log(JSON.stringify(data, null, 2));
72
+ }
73
+
74
+ group(label: string): void {
75
+ if (this.silent) return;
76
+ console.group(chalk.cyan(label));
77
+ }
78
+
79
+ groupEnd(): void {
80
+ if (this.silent) return;
81
+ console.groupEnd();
82
+ }
83
+
84
+ // Progress logging
85
+ progress(current: number, total: number, message?: string): void {
86
+ if (this.silent) return;
87
+ const percentage = Math.round((current / total) * 100);
88
+ const bar = '█'.repeat(Math.round(percentage / 2));
89
+ const empty = '░'.repeat(50 - Math.round(percentage / 2));
90
+ const progress = `[${bar}${empty}] ${percentage}%`;
91
+
92
+ const output = message
93
+ ? `${chalk.cyan(progress)} ${message}`
94
+ : chalk.cyan(progress);
95
+
96
+ process.stdout.write(`\r${output}`);
97
+
98
+ if (current === total) {
99
+ process.stdout.write('\n');
100
+ }
101
+ }
102
+ }
103
+
104
+ export const logger = new WundrLogger();
@@ -0,0 +1,62 @@
1
+ # CI/CD Batch Job Template
2
+ # Generated with Wundr CLI
3
+
4
+ name: ci-cd-pipeline
5
+ description: Continuous Integration and Deployment pipeline
6
+
7
+ # Global settings
8
+ parallel: false
9
+ continueOnError: false
10
+ timeout: 1800000 # 30 minutes
11
+
12
+ # Environment variables
13
+ variables:
14
+ NODE_VERSION: "18"
15
+ BUILD_OUTPUT: "dist"
16
+
17
+ # Job steps
18
+ commands:
19
+ # Setup
20
+ - command: "echo 'Starting CI/CD pipeline...'"
21
+ condition: "always"
22
+
23
+ # Install dependencies
24
+ - command: "npm ci"
25
+ retry: 2
26
+ timeout: 300000 # 5 minutes
27
+
28
+ # Code quality checks
29
+ - command: "npm run lint"
30
+ condition: "test-files"
31
+
32
+ - command: "npm run typecheck"
33
+ condition: "typescript-files"
34
+
35
+ # Testing
36
+ - command: "npm run test"
37
+ timeout: 600000 # 10 minutes
38
+
39
+ - command: "npm run test:e2e"
40
+ condition: "e2e-tests-exist"
41
+ timeout: 900000 # 15 minutes
42
+
43
+ # Security audit
44
+ - command: "npm audit --audit-level high"
45
+ continueOnError: true
46
+
47
+ # Build
48
+ - command: "npm run build"
49
+ timeout: 300000 # 5 minutes
50
+
51
+ # Package
52
+ - command: "npm pack"
53
+ condition: "production"
54
+
55
+ # Deploy (conditional)
56
+ - command: "npm run deploy"
57
+ condition: "production"
58
+ timeout: 600000 # 10 minutes
59
+
60
+ # Cleanup
61
+ - command: "echo 'CI/CD pipeline completed'"
62
+ condition: "always"
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+ import { {{className}} } from './{{fileName}}';
5
+
6
+ describe('{{className}}', () => {
7
+ it('renders without crashing', () => {
8
+ render(<{{className}} />);
9
+ });
10
+
11
+ it('displays the component name', () => {
12
+ render(<{{className}} />);
13
+ expect(screen.getByText('{{className}}')).toBeInTheDocument();
14
+ });
15
+
16
+ // Add more tests here
17
+ });
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+
3
+ interface {{className}}Props {
4
+ // Define props here
5
+ }
6
+
7
+ /**
8
+ * {{className}} component
9
+ * Generated with Wundr CLI
10
+ */
11
+ export const {{className}}: React.FC<{{className}}Props> = (props) => {
12
+ return (
13
+ <div className="{{fileName}}">
14
+ <h1>{{className}}</h1>
15
+ {/* Component implementation */}
16
+ </div>
17
+ );
18
+ };
19
+
20
+ const {{className}}Component = {{className}};
21
+ export default {{className}}Component;
@@ -0,0 +1,98 @@
1
+ import express, { Request, Response, Router } from 'express';
2
+
3
+ /**
4
+ * {{className}} Service
5
+ * Generated with Wundr CLI on {{timestamp}}
6
+ */
7
+ export class {{className}}Service {
8
+ private router: Router;
9
+
10
+ constructor() {
11
+ this.router = express.Router();
12
+ this.initializeRoutes();
13
+ }
14
+
15
+ /**
16
+ * Initialize service routes
17
+ */
18
+ private initializeRoutes(): void {
19
+ this.router.get('/{{fileName}}', this.getItems.bind(this));
20
+ this.router.post('/{{fileName}}', this.createItem.bind(this));
21
+ this.router.get('/{{fileName}}/:id', this.getItem.bind(this));
22
+ this.router.put('/{{fileName}}/:id', this.updateItem.bind(this));
23
+ this.router.delete('/{{fileName}}/:id', this.deleteItem.bind(this));
24
+ }
25
+
26
+ /**
27
+ * Get all items
28
+ */
29
+ private async getItems(req: Request, res: Response): Promise<void> {
30
+ try {
31
+ // Implementation here
32
+ res.json({ message: '{{className}} items retrieved' });
33
+ } catch (error) {
34
+ res.status(500).json({ error: 'Failed to get items' });
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Create new item
40
+ */
41
+ private async createItem(req: Request, res: Response): Promise<void> {
42
+ try {
43
+ // Implementation here
44
+ res.status(201).json({ message: '{{className}} item created' });
45
+ } catch (error) {
46
+ res.status(500).json({ error: 'Failed to create item' });
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Get single item by ID
52
+ */
53
+ private async getItem(req: Request, res: Response): Promise<void> {
54
+ try {
55
+ const { id } = req.params;
56
+ // Implementation here
57
+ res.json({ message: `{{className}} item ${id} retrieved` });
58
+ } catch (error) {
59
+ res.status(500).json({ error: 'Failed to get item' });
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Update item by ID
65
+ */
66
+ private async updateItem(req: Request, res: Response): Promise<void> {
67
+ try {
68
+ const { id } = req.params;
69
+ // Implementation here
70
+ res.json({ message: `{{className}} item ${id} updated` });
71
+ } catch (error) {
72
+ res.status(500).json({ error: 'Failed to update item' });
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Delete item by ID
78
+ */
79
+ private async deleteItem(req: Request, res: Response): Promise<void> {
80
+ try {
81
+ const { id } = req.params;
82
+ // Implementation here
83
+ res.json({ message: `{{className}} item ${id} deleted` });
84
+ } catch (error) {
85
+ res.status(500).json({ error: 'Failed to delete item' });
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Get router instance
91
+ */
92
+ public getRouter(): Router {
93
+ return this.router;
94
+ }
95
+ }
96
+
97
+ const {{className}}ServiceClass = {{className}}Service;
98
+ export default {{className}}ServiceClass;
File without changes
@@ -0,0 +1,134 @@
1
+ import { test, expect } from '@playwright/test';
2
+
3
+ /**
4
+ * Portable API health checks
5
+ * These tests work with common API patterns
6
+ */
7
+
8
+ test.describe('API Health Checks', () => {
9
+ test('health endpoint responds', async ({ request, baseURL }) => {
10
+ // Try common health check endpoints
11
+ const healthEndpoints = [
12
+ '/health',
13
+ '/api/health',
14
+ '/healthz',
15
+ '/status',
16
+ '/api/status',
17
+ '/_health',
18
+ '/ping'
19
+ ];
20
+
21
+ let healthyEndpoint = null;
22
+
23
+ for (const endpoint of healthEndpoints) {
24
+ try {
25
+ const response = await request.get(`${baseURL}${endpoint}`);
26
+ if (response.ok()) {
27
+ healthyEndpoint = endpoint;
28
+ break;
29
+ }
30
+ } catch {
31
+ // Continue to next endpoint
32
+ }
33
+ }
34
+
35
+ // At least one health endpoint should work
36
+ expect(healthyEndpoint).toBeTruthy();
37
+ });
38
+
39
+ test('API returns proper content types', async ({ request, baseURL }) => {
40
+ // Try to find an API endpoint
41
+ const apiEndpoints = [
42
+ '/api',
43
+ '/api/v1',
44
+ '/api/v2',
45
+ '/graphql'
46
+ ];
47
+
48
+ for (const endpoint of apiEndpoints) {
49
+ try {
50
+ const response = await request.get(`${baseURL}${endpoint}`);
51
+ if (response.ok() || response.status() === 404) {
52
+ const contentType = response.headers()['content-type'];
53
+
54
+ // Should return JSON or HTML
55
+ expect(
56
+ contentType?.includes('application/json') ||
57
+ contentType?.includes('text/html')
58
+ ).toBeTruthy();
59
+ }
60
+ } catch {
61
+ // Continue to next endpoint
62
+ }
63
+ }
64
+ });
65
+
66
+ test('API handles errors gracefully', async ({ request, baseURL }) => {
67
+ // Test 404 handling
68
+ const response = await request.get(`${baseURL}/api/nonexistent-endpoint-${Date.now()}`);
69
+
70
+ // Should return appropriate status code
71
+ expect([404, 400, 401, 403]).toContain(response.status());
72
+
73
+ // Should not expose sensitive information
74
+ const body = await response.text();
75
+ expect(body).not.toContain('stack');
76
+ expect(body).not.toContain('traceback');
77
+ });
78
+
79
+ test('API responds within acceptable time', async ({ request, baseURL }) => {
80
+ const startTime = Date.now();
81
+
82
+ // Make a simple request
83
+ await request.get(`${baseURL}/`);
84
+
85
+ const responseTime = Date.now() - startTime;
86
+
87
+ // Should respond within 5 seconds
88
+ expect(responseTime).toBeLessThan(5000);
89
+ });
90
+
91
+ test('CORS headers are properly configured', async ({ request, baseURL }) => {
92
+ try {
93
+ const response = await request.get(`${baseURL}/api`, {
94
+ headers: {
95
+ 'Origin': 'https://example.com'
96
+ }
97
+ });
98
+
99
+ const headers = response.headers();
100
+
101
+ // Check for CORS headers if API exists
102
+ if (response.status() !== 404) {
103
+ const hasCorsHeaders =
104
+ headers['access-control-allow-origin'] !== undefined ||
105
+ headers['access-control-allow-methods'] !== undefined;
106
+
107
+ // Log CORS configuration
108
+ console.log('CORS configured:', hasCorsHeaders);
109
+ }
110
+ } catch {
111
+ // API might not exist, which is okay for generic tests
112
+ }
113
+ });
114
+
115
+ test('API supports common HTTP methods', async ({ request, baseURL }) => {
116
+ const methods = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'];
117
+ const results: Record<string, number> = {};
118
+
119
+ for (const method of methods) {
120
+ try {
121
+ const response = await request.fetch(`${baseURL}/api`, {
122
+ method
123
+ });
124
+ results[method] = response.status();
125
+ } catch {
126
+ results[method] = 0;
127
+ }
128
+ }
129
+
130
+ // At least GET and OPTIONS should be supported
131
+ expect(results['GET']).toBeGreaterThan(0);
132
+ expect(results['OPTIONS']).toBeGreaterThan(0);
133
+ });
134
+ });