@simplens/onboard 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +331 -214
  2. package/dist/__tests__/env-config.test.d.ts +2 -0
  3. package/dist/__tests__/env-config.test.d.ts.map +1 -0
  4. package/dist/__tests__/env-config.test.js +23 -0
  5. package/dist/__tests__/env-config.test.js.map +1 -0
  6. package/dist/__tests__/infra-prompts.test.d.ts +2 -0
  7. package/dist/__tests__/infra-prompts.test.d.ts.map +1 -0
  8. package/dist/__tests__/infra-prompts.test.js +43 -0
  9. package/dist/__tests__/infra-prompts.test.js.map +1 -0
  10. package/dist/__tests__/infra.test.d.ts +2 -0
  11. package/dist/__tests__/infra.test.d.ts.map +1 -0
  12. package/dist/__tests__/infra.test.js +14 -0
  13. package/dist/__tests__/infra.test.js.map +1 -0
  14. package/dist/__tests__/nginx.test.d.ts +2 -0
  15. package/dist/__tests__/nginx.test.d.ts.map +1 -0
  16. package/dist/__tests__/nginx.test.js +16 -0
  17. package/dist/__tests__/nginx.test.js.map +1 -0
  18. package/dist/env-config.d.ts +27 -12
  19. package/dist/env-config.d.ts.map +1 -1
  20. package/dist/env-config.js +253 -128
  21. package/dist/env-config.js.map +1 -1
  22. package/dist/index.js +340 -69
  23. package/dist/index.js.map +1 -1
  24. package/dist/infra.d.ts +19 -8
  25. package/dist/infra.d.ts.map +1 -1
  26. package/dist/infra.js +267 -128
  27. package/dist/infra.js.map +1 -1
  28. package/dist/plugins.d.ts +5 -10
  29. package/dist/plugins.d.ts.map +1 -1
  30. package/dist/plugins.js +75 -44
  31. package/dist/plugins.js.map +1 -1
  32. package/dist/services.d.ts +1 -23
  33. package/dist/services.d.ts.map +1 -1
  34. package/dist/services.js +47 -62
  35. package/dist/services.js.map +1 -1
  36. package/dist/templates.d.ts +2 -1
  37. package/dist/templates.d.ts.map +1 -1
  38. package/dist/templates.js +203 -191
  39. package/dist/templates.js.map +1 -1
  40. package/dist/types/domain.d.ts +2 -0
  41. package/dist/types/domain.d.ts.map +1 -1
  42. package/dist/ui.d.ts +45 -0
  43. package/dist/ui.d.ts.map +1 -0
  44. package/dist/ui.js +93 -0
  45. package/dist/ui.js.map +1 -0
  46. package/dist/utils/logger.d.ts +1 -0
  47. package/dist/utils/logger.d.ts.map +1 -1
  48. package/dist/utils/logger.js +32 -7
  49. package/dist/utils/logger.js.map +1 -1
  50. package/dist/utils.d.ts +8 -0
  51. package/dist/utils.d.ts.map +1 -1
  52. package/dist/utils.js +66 -2
  53. package/dist/utils.js.map +1 -1
  54. package/dist/validators.d.ts +1 -52
  55. package/dist/validators.d.ts.map +1 -1
  56. package/dist/validators.js +10 -57
  57. package/dist/validators.js.map +1 -1
  58. package/package.json +3 -5
  59. package/src/__tests__/env-config.test.ts +28 -0
  60. package/src/__tests__/errors.test.ts +187 -187
  61. package/src/__tests__/infra-prompts.test.ts +54 -0
  62. package/src/__tests__/infra.test.ts +15 -0
  63. package/src/__tests__/utils.test.ts +142 -142
  64. package/src/__tests__/validators.test.ts +195 -195
  65. package/src/config/constants.ts +86 -86
  66. package/src/config/index.ts +1 -1
  67. package/src/env-config.ts +455 -311
  68. package/src/index.ts +534 -202
  69. package/src/infra.ts +404 -245
  70. package/src/plugins.ts +221 -190
  71. package/src/services.ts +175 -190
  72. package/src/templates.ts +209 -196
  73. package/src/types/domain.ts +129 -127
  74. package/src/types/errors.ts +173 -173
  75. package/src/types/index.ts +2 -2
  76. package/src/ui.ts +91 -0
  77. package/src/utils/index.ts +1 -1
  78. package/src/utils/logger.ts +144 -118
  79. package/src/utils.ts +183 -105
  80. package/src/validators.ts +145 -192
  81. package/tsconfig.json +18 -18
  82. package/vitest.config.ts +22 -22
@@ -1,118 +1,144 @@
1
- import chalk from 'chalk';
2
- import { appendFile as fsAppendFile } from 'fs/promises';
3
- import path from 'path';
4
- import { FILES } from '../config/constants.js';
5
-
6
- /**
7
- * Logging level
8
- */
9
- export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
10
-
11
- /**
12
- * Logger configuration
13
- */
14
- interface LoggerConfig {
15
- verbose: boolean;
16
- debug: boolean;
17
- logFile?: string;
18
- }
19
-
20
- let loggerConfig: LoggerConfig = {
21
- verbose: false,
22
- debug: false,
23
- };
24
-
25
- /**
26
- * Initialize logger with configuration
27
- */
28
- export function initLogger(config: Partial<LoggerConfig>): void {
29
- loggerConfig = { ...loggerConfig, ...config };
30
- }
31
-
32
- /**
33
- * Get current logger configuration
34
- */
35
- export function getLoggerConfig(): Readonly<LoggerConfig> {
36
- return { ...loggerConfig };
37
- }
38
-
39
- /**
40
- * Write log message to file if configured
41
- */
42
- async function writeToLogFile(level: LogLevel, message: string): Promise<void> {
43
- if (!loggerConfig.logFile) return;
44
-
45
- try {
46
- const timestamp = new Date().toISOString();
47
- const logEntry = `[${timestamp}] [${level.toUpperCase()}] ${message}\n`;
48
- await fsAppendFile(loggerConfig.logFile, logEntry, 'utf-8');
49
- } catch (error) {
50
- // Silently fail on log file errors
51
- }
52
- }
53
-
54
- /**
55
- * Log debug message (only shown with --debug flag)
56
- */
57
- export function logDebug(message: string): void {
58
- if (loggerConfig.debug) {
59
- console.log(chalk.gray(`🔧 ${message}`));
60
- }
61
- writeToLogFile('debug', message);
62
- }
63
-
64
- /**
65
- * Log verbose message (shown with --verbose or --debug)
66
- */
67
- export function logVerbose(message: string): void {
68
- if (loggerConfig.verbose || loggerConfig.debug) {
69
- console.log(chalk.cyan(`ℹ️ ${message}`));
70
- }
71
- writeToLogFile('info', message);
72
- }
73
-
74
- /**
75
- * Log info message (always displayed)
76
- */
77
- export function logInfo(message: string): void {
78
- console.log(chalk.blue(`ℹ️ ${message}`));
79
- writeToLogFile('info', message);
80
- }
81
-
82
- /**
83
- * Log success message (always displayed)
84
- */
85
- export function logSuccess(message: string): void {
86
- console.log(chalk.green(`✅ ${message}`));
87
- writeToLogFile('info', message);
88
- }
89
-
90
- /**
91
- * Log warning message (always displayed)
92
- */
93
- export function logWarning(message: string): void {
94
- console.log(chalk.yellow(`⚠️ ${message}`));
95
- writeToLogFile('warn', message);
96
- }
97
-
98
- /**
99
- * Log error message (always displayed)
100
- */
101
- export function logError(message: string): void {
102
- console.log(chalk.red(`❌ ${message}`));
103
- writeToLogFile('error', message);
104
- }
105
-
106
- /**
107
- * Log command execution (debug level)
108
- */
109
- export function logCommand(command: string, args: string[]): void {
110
- logDebug(`Executing: ${command} ${args.join(' ')}`);
111
- }
112
-
113
- /**
114
- * Log file operation (debug level)
115
- */
116
- export function logFileOperation(operation: string, filePath: string): void {
117
- logDebug(`${operation}: ${filePath}`);
118
- }
1
+ import chalk from 'chalk';
2
+ import { appendFile as fsAppendFile } from 'fs/promises';
3
+
4
+ /**
5
+ * Logging level
6
+ */
7
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
8
+
9
+ /**
10
+ * Logger configuration
11
+ */
12
+ interface LoggerConfig {
13
+ verbose: boolean;
14
+ debug: boolean;
15
+ silent: boolean;
16
+ logFile?: string;
17
+ }
18
+
19
+ let loggerConfig: LoggerConfig = {
20
+ verbose: false,
21
+ debug: false,
22
+ silent: false,
23
+ };
24
+
25
+ function formatTag(tag: string, color: (text: string) => string): string {
26
+ return color(`[${tag.toUpperCase().padEnd(7)}]`);
27
+ }
28
+
29
+ function printLogLine(tag: 'debug' | 'info' | 'ok' | 'warn' | 'error', message: string): void {
30
+ if (loggerConfig.silent) return;
31
+
32
+ switch (tag) {
33
+ case 'debug':
34
+ console.log(`${formatTag('debug', chalk.gray)} ${chalk.gray(message)}`);
35
+ return;
36
+ case 'info':
37
+ console.log(`${formatTag('info', chalk.blueBright)} ${message}`);
38
+ return;
39
+ case 'ok':
40
+ console.log(`${formatTag('ok', chalk.greenBright)} ${message}`);
41
+ return;
42
+ case 'warn':
43
+ console.log(`${formatTag('warn', chalk.yellowBright)} ${message}`);
44
+ return;
45
+ case 'error':
46
+ console.log(`${formatTag('error', chalk.redBright)} ${message}`);
47
+ return;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Initialize logger with configuration
53
+ */
54
+ export function initLogger(config: Partial<LoggerConfig>): void {
55
+ loggerConfig = { ...loggerConfig, ...config };
56
+ }
57
+
58
+ /**
59
+ * Get current logger configuration
60
+ */
61
+ export function getLoggerConfig(): Readonly<LoggerConfig> {
62
+ return { ...loggerConfig };
63
+ }
64
+
65
+ /**
66
+ * Write log message to file if configured
67
+ */
68
+ async function writeToLogFile(level: LogLevel, message: string): Promise<void> {
69
+ if (!loggerConfig.logFile) return;
70
+
71
+ try {
72
+ const timestamp = new Date().toISOString();
73
+ const logEntry = `[${timestamp}] [${level.toUpperCase()}] ${message}\n`;
74
+ await fsAppendFile(loggerConfig.logFile, logEntry, 'utf-8');
75
+ } catch (error) {
76
+ // Silently fail on log file errors
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Log debug message (only shown with --debug flag)
82
+ */
83
+ export function logDebug(message: string): void {
84
+ if (loggerConfig.debug) {
85
+ printLogLine('debug', message);
86
+ }
87
+ writeToLogFile('debug', message);
88
+ }
89
+
90
+ /**
91
+ * Log verbose message (shown with --verbose or --debug)
92
+ */
93
+ export function logVerbose(message: string): void {
94
+ if (!loggerConfig.silent && (loggerConfig.verbose || loggerConfig.debug)) {
95
+ console.log(`${formatTag('verbose', chalk.cyanBright)} ${message}`);
96
+ }
97
+ writeToLogFile('info', message);
98
+ }
99
+
100
+ /**
101
+ * Log info message (always displayed)
102
+ */
103
+ export function logInfo(message: string): void {
104
+ printLogLine('info', message);
105
+ writeToLogFile('info', message);
106
+ }
107
+
108
+ /**
109
+ * Log success message (always displayed)
110
+ */
111
+ export function logSuccess(message: string): void {
112
+ printLogLine('ok', message);
113
+ writeToLogFile('info', message);
114
+ }
115
+
116
+ /**
117
+ * Log warning message (always displayed)
118
+ */
119
+ export function logWarning(message: string): void {
120
+ printLogLine('warn', message);
121
+ writeToLogFile('warn', message);
122
+ }
123
+
124
+ /**
125
+ * Log error message (always displayed)
126
+ */
127
+ export function logError(message: string): void {
128
+ printLogLine('error', message);
129
+ writeToLogFile('error', message);
130
+ }
131
+
132
+ /**
133
+ * Log command execution (debug level)
134
+ */
135
+ export function logCommand(command: string, args: string[]): void {
136
+ logDebug(`Executing: ${command} ${args.join(' ')}`);
137
+ }
138
+
139
+ /**
140
+ * Log file operation (debug level)
141
+ */
142
+ export function logFileOperation(operation: string, filePath: string): void {
143
+ logDebug(`${operation}: ${filePath}`);
144
+ }
package/src/utils.ts CHANGED
@@ -1,105 +1,183 @@
1
- import chalk from 'chalk';
2
- import { execa } from 'execa';
3
- import fs from 'fs/promises';
4
- import path from 'path';
5
- import figlet from 'figlet';
6
-
7
- // Re-export logger functions for backward compatibility
8
- export {
9
- logSuccess,
10
- logError,
11
- logWarning,
12
- logInfo,
13
- logDebug,
14
- logVerbose,
15
- logCommand,
16
- logFileOperation,
17
- initLogger,
18
- getLoggerConfig,
19
- } from './utils/logger.js';
20
-
21
- /**
22
- * Display SimpleNS banner in blue using figlet
23
- */
24
- export function displayBanner(): void {
25
- const simpleNS = figlet.textSync('SimpleNS', {
26
- font: 'Standard',
27
- horizontalLayout: 'default',
28
- });
29
-
30
- const onboard = figlet.textSync('Onboard', {
31
- font: 'Standard',
32
- horizontalLayout: 'default',
33
- });
34
-
35
- console.log(chalk.blue(simpleNS));
36
- console.log(chalk.blue(onboard));
37
- console.log('');
38
- }
39
-
40
- /**
41
- * Execute a shell command with error handling
42
- */
43
- export async function executeCommand(
44
- command: string,
45
- args: string[],
46
- options?: { cwd?: string; silent?: boolean }
47
- ): Promise<{ stdout: string; stderr: string }> {
48
- // Import dynamically to avoid circular dependency
49
- const { logCommand } = await import('./utils/logger.js');
50
-
51
- logCommand(command, args);
52
-
53
- try {
54
- const result = await execa(command, args, {
55
- cwd: options?.cwd || process.cwd(),
56
- stdio: options?.silent ? 'pipe' : 'inherit',
57
- });
58
- return { stdout: result.stdout, stderr: result.stderr };
59
- } catch (error: any) {
60
- throw new Error(`Command failed: ${command} ${args.join(' ')}\n${error.message}`);
61
- }
62
- }
63
-
64
- /**
65
- * Check if file exists
66
- */
67
- export async function fileExists(filePath: string): Promise<boolean> {
68
- try {
69
- await fs.access(filePath);
70
- return true;
71
- } catch {
72
- return false;
73
- }
74
- }
75
-
76
- /**
77
- * Write file content
78
- */
79
- export async function writeFile(filePath: string, content: string): Promise<void> {
80
- const { logFileOperation } = await import('./utils/logger.js');
81
-
82
- logFileOperation('Writing file', filePath);
83
-
84
- const dir = path.dirname(filePath);
85
- await fs.mkdir(dir, { recursive: true });
86
- await fs.writeFile(filePath, content, 'utf-8');
87
- }
88
-
89
- /**
90
- * Read file content
91
- */
92
- export async function readFile(filePath: string): Promise<string> {
93
- return await fs.readFile(filePath, 'utf-8');
94
- }
95
-
96
- /**
97
- * Append content to file
98
- */
99
- export async function appendFile(filePath: string, content: string): Promise<void> {
100
- // Ensure parent directory exists, similar to writeFile
101
- const dir = path.dirname(filePath);
102
- await fs.mkdir(dir, { recursive: true });
103
-
104
- await fs.appendFile(filePath, content, 'utf-8');
105
- }
1
+ import chalk from 'chalk';
2
+ import { execa } from 'execa';
3
+ import fs from 'fs/promises';
4
+ import path from 'path';
5
+ import figlet from 'figlet';
6
+ import { getLoggerConfig } from './utils/logger.js';
7
+
8
+ // Re-export logger functions for backward compatibility
9
+ export {
10
+ logSuccess,
11
+ logError,
12
+ logWarning,
13
+ logInfo,
14
+ logDebug,
15
+ logVerbose,
16
+ logCommand,
17
+ logFileOperation,
18
+ initLogger,
19
+ getLoggerConfig,
20
+ } from './utils/logger.js';
21
+
22
+ const MAX_TUI_WIDTH = 78;
23
+ const MIN_TUI_WIDTH = 52;
24
+
25
+ type AccentColor = 'blue' | 'cyan' | 'green' | 'yellow' | 'red' | 'gray';
26
+
27
+ function getTerminalWidth(): number {
28
+ const columns = process.stdout.columns ?? MAX_TUI_WIDTH;
29
+ return Math.max(MIN_TUI_WIDTH, Math.min(MAX_TUI_WIDTH, columns));
30
+ }
31
+
32
+ function accent(text: string, color: AccentColor): string {
33
+ switch (color) {
34
+ case 'blue':
35
+ return chalk.blueBright(text);
36
+ case 'cyan':
37
+ return chalk.cyanBright(text);
38
+ case 'green':
39
+ return chalk.greenBright(text);
40
+ case 'yellow':
41
+ return chalk.yellowBright(text);
42
+ case 'red':
43
+ return chalk.redBright(text);
44
+ case 'gray':
45
+ default:
46
+ return chalk.gray(text);
47
+ }
48
+ }
49
+
50
+ export function divider(color: AccentColor = 'gray', char: string = '─'): string {
51
+ return accent(char.repeat(getTerminalWidth()), color);
52
+ }
53
+
54
+ export function printStepHeader(step: number, total: number, title: string): void {
55
+ if (getLoggerConfig().silent) return;
56
+
57
+ const filled = '■'.repeat(Math.max(0, step));
58
+ const empty = '·'.repeat(Math.max(0, total - step));
59
+ const progress = chalk.gray(`${filled}${empty}`);
60
+
61
+ console.log(`\n${divider()}`);
62
+ console.log(`${accent(`[${step}/${total}]`, 'cyan')} ${chalk.whiteBright(title)} ${progress}`);
63
+ console.log(divider());
64
+ }
65
+
66
+ export function printSummaryCard(
67
+ title: string,
68
+ rows: Array<{ label: string; value: string }>
69
+ ): void {
70
+ if (getLoggerConfig().silent) return;
71
+
72
+ const labelWidth = Math.max(...rows.map(row => row.label.length), 0);
73
+
74
+ console.log(`\n${accent(title, 'cyan')}`);
75
+ console.log(divider());
76
+ for (const row of rows) {
77
+ const label = chalk.gray(row.label.padEnd(labelWidth));
78
+ console.log(`${label} ${chalk.white(row.value)}`);
79
+ }
80
+ console.log(`${divider()}\n`);
81
+ }
82
+
83
+ export function printCommandHints(title: string, commands: string[]): void {
84
+ if (getLoggerConfig().silent) return;
85
+
86
+ console.log(accent(title, 'cyan'));
87
+ for (const command of commands) {
88
+ console.log(` ${accent('›', 'gray')} ${chalk.white(command)}`);
89
+ }
90
+ console.log('');
91
+ }
92
+
93
+ /**
94
+ * Display SimpleNS banner in blue using figlet
95
+ */
96
+ export function displayBanner(): void {
97
+ if (getLoggerConfig().silent) return;
98
+
99
+ const simpleNS = figlet.textSync('SimpleNS', {
100
+ font: 'Standard',
101
+ horizontalLayout: 'default',
102
+ });
103
+
104
+ const onboard = figlet.textSync('Onboard', {
105
+ font: 'Standard',
106
+ horizontalLayout: 'default',
107
+ });
108
+
109
+ console.log('');
110
+ console.log(divider('blue', '═'));
111
+ console.log(chalk.blueBright(simpleNS));
112
+ console.log(chalk.cyanBright(onboard));
113
+ console.log(chalk.gray('SimpleNS local setup assistant'));
114
+ console.log(divider('blue', '═'));
115
+ console.log('');
116
+ }
117
+
118
+ /**
119
+ * Execute a shell command with error handling
120
+ */
121
+ export async function executeCommand(
122
+ command: string,
123
+ args: string[],
124
+ options?: { cwd?: string; silent?: boolean }
125
+ ): Promise<{ stdout: string; stderr: string }> {
126
+ // Import dynamically to avoid circular dependency
127
+ const { logCommand } = await import('./utils/logger.js');
128
+
129
+ logCommand(command, args);
130
+
131
+ try {
132
+ const result = await execa(command, args, {
133
+ cwd: options?.cwd || process.cwd(),
134
+ stdio: options?.silent ? 'pipe' : 'inherit',
135
+ });
136
+ return { stdout: result.stdout, stderr: result.stderr };
137
+ } catch (error: any) {
138
+ throw new Error(`Command failed: ${command} ${args.join(' ')}\n${error.message}`);
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Check if file exists
144
+ */
145
+ export async function fileExists(filePath: string): Promise<boolean> {
146
+ try {
147
+ await fs.access(filePath);
148
+ return true;
149
+ } catch {
150
+ return false;
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Write file content
156
+ */
157
+ export async function writeFile(filePath: string, content: string): Promise<void> {
158
+ const { logFileOperation } = await import('./utils/logger.js');
159
+
160
+ logFileOperation('Writing file', filePath);
161
+
162
+ const dir = path.dirname(filePath);
163
+ await fs.mkdir(dir, { recursive: true });
164
+ await fs.writeFile(filePath, content, 'utf-8');
165
+ }
166
+
167
+ /**
168
+ * Read file content
169
+ */
170
+ export async function readFile(filePath: string): Promise<string> {
171
+ return await fs.readFile(filePath, 'utf-8');
172
+ }
173
+
174
+ /**
175
+ * Append content to file
176
+ */
177
+ export async function appendFile(filePath: string, content: string): Promise<void> {
178
+ // Ensure parent directory exists, similar to writeFile
179
+ const dir = path.dirname(filePath);
180
+ await fs.mkdir(dir, { recursive: true });
181
+
182
+ await fs.appendFile(filePath, content, 'utf-8');
183
+ }