@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.
- package/README.md +551 -0
- package/bin/wundr.js +39 -0
- package/dist/ai/ai-service.d.ts +152 -0
- package/dist/ai/ai-service.d.ts.map +1 -0
- package/dist/ai/ai-service.js +430 -0
- package/dist/ai/ai-service.js.map +1 -0
- package/dist/ai/claude-client.d.ts +130 -0
- package/dist/ai/claude-client.d.ts.map +1 -0
- package/dist/ai/claude-client.js +339 -0
- package/dist/ai/claude-client.js.map +1 -0
- package/dist/ai/conversation-manager.d.ts +164 -0
- package/dist/ai/conversation-manager.d.ts.map +1 -0
- package/dist/ai/conversation-manager.js +612 -0
- package/dist/ai/conversation-manager.js.map +1 -0
- package/dist/ai/index.d.ts +5 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +8 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/cli.d.ts +36 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +173 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/ai.d.ts +89 -0
- package/dist/commands/ai.d.ts.map +1 -0
- package/dist/commands/ai.js +735 -0
- package/dist/commands/ai.js.map +1 -0
- package/dist/commands/analyze-optimized.d.ts +14 -0
- package/dist/commands/analyze-optimized.d.ts.map +1 -0
- package/dist/commands/analyze-optimized.js +437 -0
- package/dist/commands/analyze-optimized.js.map +1 -0
- package/dist/commands/analyze.d.ts +65 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +435 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/batch.d.ts +71 -0
- package/dist/commands/batch.d.ts.map +1 -0
- package/dist/commands/batch.js +738 -0
- package/dist/commands/batch.js.map +1 -0
- package/dist/commands/chat.d.ts +71 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +674 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/claude-init.d.ts +28 -0
- package/dist/commands/claude-init.d.ts.map +1 -0
- package/dist/commands/claude-init.js +587 -0
- package/dist/commands/claude-init.js.map +1 -0
- package/dist/commands/claude-setup.d.ts +32 -0
- package/dist/commands/claude-setup.d.ts.map +1 -0
- package/dist/commands/claude-setup.js +570 -0
- package/dist/commands/claude-setup.js.map +1 -0
- package/dist/commands/computer-setup-commands.d.ts +39 -0
- package/dist/commands/computer-setup-commands.d.ts.map +1 -0
- package/dist/commands/computer-setup-commands.js +563 -0
- package/dist/commands/computer-setup-commands.js.map +1 -0
- package/dist/commands/computer-setup.d.ts +7 -0
- package/dist/commands/computer-setup.d.ts.map +1 -0
- package/dist/commands/computer-setup.js +481 -0
- package/dist/commands/computer-setup.js.map +1 -0
- package/dist/commands/create-command.d.ts +7 -0
- package/dist/commands/create-command.d.ts.map +1 -0
- package/dist/commands/create-command.js +158 -0
- package/dist/commands/create-command.js.map +1 -0
- package/dist/commands/create.d.ts +74 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +556 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/dashboard.d.ts +91 -0
- package/dist/commands/dashboard.d.ts.map +1 -0
- package/dist/commands/dashboard.js +537 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/govern.d.ts +70 -0
- package/dist/commands/govern.d.ts.map +1 -0
- package/dist/commands/govern.js +480 -0
- package/dist/commands/govern.js.map +1 -0
- package/dist/commands/init.d.ts +55 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +584 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/performance-optimizer.d.ts +30 -0
- package/dist/commands/performance-optimizer.d.ts.map +1 -0
- package/dist/commands/performance-optimizer.js +649 -0
- package/dist/commands/performance-optimizer.js.map +1 -0
- package/dist/commands/plugins.d.ts +87 -0
- package/dist/commands/plugins.d.ts.map +1 -0
- package/dist/commands/plugins.js +685 -0
- package/dist/commands/plugins.js.map +1 -0
- package/dist/commands/setup.d.ts +29 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +399 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/test-init.d.ts +9 -0
- package/dist/commands/test-init.d.ts.map +1 -0
- package/dist/commands/test-init.js +222 -0
- package/dist/commands/test-init.js.map +1 -0
- package/dist/commands/test.d.ts +25 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +217 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/watch.d.ts +76 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +610 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/context/context-manager.d.ts +155 -0
- package/dist/context/context-manager.d.ts.map +1 -0
- package/dist/context/context-manager.js +383 -0
- package/dist/context/context-manager.js.map +1 -0
- package/dist/context/index.d.ts +3 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +6 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/session-manager.d.ts +207 -0
- package/dist/context/session-manager.d.ts.map +1 -0
- package/dist/context/session-manager.js +682 -0
- package/dist/context/session-manager.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/interactive/interactive-mode.d.ts +76 -0
- package/dist/interactive/interactive-mode.d.ts.map +1 -0
- package/dist/interactive/interactive-mode.js +730 -0
- package/dist/interactive/interactive-mode.js.map +1 -0
- package/dist/nlp/command-mapper.d.ts +174 -0
- package/dist/nlp/command-mapper.d.ts.map +1 -0
- package/dist/nlp/command-mapper.js +623 -0
- package/dist/nlp/command-mapper.js.map +1 -0
- package/dist/nlp/command-parser.d.ts +106 -0
- package/dist/nlp/command-parser.d.ts.map +1 -0
- package/dist/nlp/command-parser.js +416 -0
- package/dist/nlp/command-parser.js.map +1 -0
- package/dist/nlp/index.d.ts +5 -0
- package/dist/nlp/index.d.ts.map +1 -0
- package/dist/nlp/index.js +8 -0
- package/dist/nlp/index.js.map +1 -0
- package/dist/nlp/intent-classifier.d.ts +59 -0
- package/dist/nlp/intent-classifier.d.ts.map +1 -0
- package/dist/nlp/intent-classifier.js +384 -0
- package/dist/nlp/intent-classifier.js.map +1 -0
- package/dist/nlp/intent-parser.d.ts +152 -0
- package/dist/nlp/intent-parser.d.ts.map +1 -0
- package/dist/nlp/intent-parser.js +739 -0
- package/dist/nlp/intent-parser.js.map +1 -0
- package/dist/plugins/plugin-manager.d.ts +120 -0
- package/dist/plugins/plugin-manager.d.ts.map +1 -0
- package/dist/plugins/plugin-manager.js +595 -0
- package/dist/plugins/plugin-manager.js.map +1 -0
- package/dist/types/index.d.ts +224 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/config-manager.d.ts +73 -0
- package/dist/utils/config-manager.d.ts.map +1 -0
- package/dist/utils/config-manager.js +339 -0
- package/dist/utils/config-manager.js.map +1 -0
- package/dist/utils/error-handler.d.ts +46 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js +169 -0
- package/dist/utils/error-handler.js.map +1 -0
- package/dist/utils/logger.d.ts +25 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +94 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +119 -0
- package/src/ai/ai-service.ts +595 -0
- package/src/ai/claude-client.ts +490 -0
- package/src/ai/conversation-manager.ts +907 -0
- package/src/ai/index.ts +8 -0
- package/src/cli.ts +202 -0
- package/src/commands/ai.ts +995 -0
- package/src/commands/analyze-optimized.ts +641 -0
- package/src/commands/analyze.ts +576 -0
- package/src/commands/batch.ts +935 -0
- package/src/commands/chat.ts +876 -0
- package/src/commands/claude-init.ts +715 -0
- package/src/commands/claude-setup.ts +697 -0
- package/src/commands/computer-setup-commands.ts +709 -0
- package/src/commands/computer-setup.ts +565 -0
- package/src/commands/create-command.ts +175 -0
- package/src/commands/create.ts +727 -0
- package/src/commands/dashboard.ts +691 -0
- package/src/commands/govern.ts +635 -0
- package/src/commands/init.ts +677 -0
- package/src/commands/performance-optimizer.ts +864 -0
- package/src/commands/plugins.ts +848 -0
- package/src/commands/setup.ts +508 -0
- package/src/commands/test-init.ts +242 -0
- package/src/commands/test.ts +264 -0
- package/src/commands/watch.ts +755 -0
- package/src/context/context-manager.ts +546 -0
- package/src/context/index.ts +9 -0
- package/src/context/session-manager.ts +1019 -0
- package/src/index.ts +64 -0
- package/src/interactive/interactive-mode.ts +830 -0
- package/src/nlp/command-mapper.ts +885 -0
- package/src/nlp/command-parser.ts +564 -0
- package/src/nlp/index.ts +4 -0
- package/src/nlp/intent-classifier.ts +458 -0
- package/src/nlp/intent-parser.ts +1101 -0
- package/src/plugins/plugin-manager.ts +744 -0
- package/src/types/index.ts +252 -0
- package/src/types/modules.d.ts +56 -0
- package/src/utils/config-manager.ts +391 -0
- package/src/utils/error-handler.ts +192 -0
- package/src/utils/logger.ts +104 -0
- package/templates/batch/ci-cd.yaml +62 -0
- package/templates/component/{{fileName}}.test.tsx +17 -0
- package/templates/component/{{fileName}}.tsx +21 -0
- package/templates/service/{{fileName}}.ts +98 -0
- package/templates/wundr-test.config.js +0 -0
- package/test-suites/api/health.spec.ts +134 -0
- package/test-suites/helpers/test-config.ts +84 -0
- package/test-suites/ui/accessibility.spec.ts +102 -0
- 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
|
+
});
|