@simplens/onboard 1.0.1 → 1.0.3

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
package/src/services.ts CHANGED
@@ -1,190 +1,175 @@
1
- import inquirer from 'inquirer';
2
- import { execa } from 'execa';
3
- import ora from 'ora';
4
- import { logInfo, logSuccess, logError, logWarning } from './utils.js';
5
- import chalk from 'chalk';
6
- import { HEALTH_CHECK, SERVICE_PORTS, getServiceURL } from './config/constants.js';
7
-
8
- /**
9
- * Prompts user whether to start the services immediately after setup.
10
- *
11
- * @returns `true` if user wants to start services, `false` otherwise
12
- *
13
- * @example
14
- * ```ts
15
- * if (await promptStartServices()) {
16
- * await startInfraServices(targetDir);
17
- * }
18
- * ```
19
- */
20
- export async function promptStartServices(): Promise<boolean> {
21
- const answer = await inquirer.prompt<{ start: boolean }>([
22
- {
23
- type: 'confirm',
24
- name: 'start',
25
- message: 'Do you want to start the services now?',
26
- default: true,
27
- },
28
- ]);
29
-
30
- return answer.start;
31
- }
32
-
33
- /**
34
- * Starts infrastructure services using docker-compose.
35
- * Runs `docker-compose -f docker-compose.infra.yaml up -d` in the target directory.
36
- *
37
- * @param targetDir - Directory containing docker-compose.infra.yaml
38
- * @throws Error if docker-compose command fails
39
- *
40
- * @example
41
- * ```ts
42
- * await startInfraServices('/opt/simplens');
43
- * ```
44
- */
45
- export async function startInfraServices(targetDir: string): Promise<void> {
46
- logInfo('Starting infrastructure services...');
47
-
48
- const spinner = ora('Starting docker-compose.infra.yaml...').start();
49
-
50
- try {
51
- await execa(
52
- 'docker-compose',
53
- ['-f', 'docker-compose.infra.yaml', 'up', '-d'],
54
- { cwd: targetDir }
55
- );
56
- spinner.succeed('Infrastructure services started');
57
- } catch (error: any) {
58
- spinner.fail('Failed to start infrastructure services');
59
- throw error;
60
- }
61
- }
62
-
63
- /**
64
- * Waits for infrastructure services to become healthy.
65
- * Polls Docker health checks for up to 60 seconds (30 retries × 2s).
66
- *
67
- * @param targetDir - Directory where services are running
68
- *
69
- * @remarks
70
- * Checks for healthy containers running MongoDB or Redis.
71
- * Configuration: 30 max retries, 2000ms delay between retries.
72
- *
73
- * @example
74
- * ```ts
75
- * await startInfraServices(targetDir);
76
- * await waitForInfraHealth(targetDir); // Wait for services to be ready
77
- * ```
78
- */
79
- export async function waitForInfraHealth(targetDir: string): Promise<void> {
80
- logInfo('Waiting for infrastructure services to be healthy...');
81
-
82
- const spinner = ora('Checking service health...').start();
83
-
84
- // Wait for mongo, redis health checks
85
- const maxRetries = HEALTH_CHECK.MAX_RETRIES;
86
- const retryDelay = HEALTH_CHECK.RETRY_DELAY_MS;
87
-
88
- for (let i = 0; i < maxRetries; i++) {
89
- try {
90
- // Check if containers are healthy
91
- const { stdout } = await execa('docker', ['ps', '--filter', 'health=healthy', '--format', '{{.Names}}']);
92
- const healthyContainers = stdout.split('\n').filter(Boolean);
93
-
94
- // Check for critical services
95
- const hasMongoOrRedis = healthyContainers.some(name =>
96
- name.includes('mongo') || name.includes('redis')
97
- );
98
-
99
- if (hasMongoOrRedis) {
100
- spinner.succeed('Infrastructure services are healthy');
101
- return;
102
- }
103
-
104
- spinner.text = `Waiting for services... (${i + 1}/${maxRetries})`;
105
- await sleep(retryDelay);
106
- } catch (error) {
107
- spinner.text = `Checking health... (${i + 1}/${maxRetries})`;
108
- await sleep(retryDelay);
109
- }
110
- }
111
-
112
- spinner.warn('Health check timed out, but services may still be starting');
113
- logWarning('You may need to wait a bit longer for all services to be ready.');
114
- }
115
-
116
- /**
117
- * Start application services
118
- */
119
- export async function startAppServices(targetDir: string): Promise<void> {
120
- logInfo('Starting application services...');
121
-
122
- const spinner = ora('Starting docker-compose.yaml...').start();
123
-
124
- try {
125
- await execa(
126
- 'docker-compose',
127
- ['up', '-d'],
128
- { cwd: targetDir }
129
- );
130
- spinner.succeed('Application services started');
131
- } catch (error: any) {
132
- spinner.fail('Failed to start application services');
133
- throw error;
134
- }
135
- }
136
-
137
- /**
138
- * Display service status and URLs
139
- */
140
- export async function displayServiceStatus(): Promise<void> {
141
- console.log('\n' + chalk.green(''.repeat(60)));
142
- console.log(chalk.green.bold(' Services Started Successfully!'));
143
- console.log(chalk.green('═'.repeat(60)) + '\n');
144
-
145
- try {
146
- // Get running containers
147
- const { stdout } = await execa('docker', ['ps', '--format', '{{.Names}}']);
148
- const containers = stdout.split('\n').filter(Boolean);
149
-
150
- console.log(chalk.cyan.bold('🔗 Access URLs:\n'));
151
-
152
- // Display URLs for known services
153
- if (containers.some(c => c.includes('api'))) {
154
- console.log(` ${chalk.bold('API Server:')} ${chalk.underline(getServiceURL('API'))}`);
155
- console.log(` ${chalk.bold('API Health:')} ${chalk.underline(getServiceURL('API') + '/health')}\n`);
156
- }
157
-
158
- if (containers.some(c => c.includes('dashboard'))) {
159
- console.log(` ${chalk.bold('Dashboard:')} ${chalk.underline(getServiceURL('DASHBOARD'))}\n`);
160
- }
161
-
162
- if (containers.some(c => c.includes('kafka-ui'))) {
163
- console.log(` ${chalk.bold('Kafka UI:')} ${chalk.underline(getServiceURL('KAFKA_UI'))}\n`);
164
- }
165
-
166
- if (containers.some(c => c.includes('grafana'))) {
167
- console.log(` ${chalk.bold('Grafana:')} ${chalk.underline(getServiceURL('GRAFANA'))}`);
168
- console.log(` ${chalk.gray('(default login: admin/admin)')}\n`);
169
- }
170
-
171
- console.log(chalk.cyan.bold('📦 Running Containers:\n'));
172
- for (const container of containers) {
173
- console.log(` ${chalk.green('✓')} ${container}`);
174
- }
175
-
176
- console.log('\n' + chalk.cyan('To view logs:') + ' docker-compose logs -f');
177
- console.log(chalk.cyan('To stop services:') + ' docker-compose down\n');
178
- console.log(chalk.green('═'.repeat(60)) + '\n');
179
-
180
- } catch (error) {
181
- logWarning('Could not fetch container status');
182
- }
183
- }
184
-
185
- /**
186
- * Helper: Sleep for specified milliseconds
187
- */
188
- function sleep(ms: number): Promise<void> {
189
- return new Promise(resolve => setTimeout(resolve, ms));
190
- }
1
+ import { execa } from 'execa';
2
+ import chalk from 'chalk';
3
+ import { logInfo, logWarning, divider, printSummaryCard, printCommandHints } from './utils.js';
4
+ import { confirm } from '@clack/prompts';
5
+ import { handleCancel, spinner } from './ui.js';
6
+ import { HEALTH_CHECK, getServiceURL } from './config/constants.js';
7
+
8
+ /**
9
+ * Prompts user whether to start the services immediately after setup.
10
+ *
11
+ * @returns `true` if user wants to start services, `false` otherwise
12
+ */
13
+ export async function promptStartServices(): Promise<boolean> {
14
+ const shouldStart = await confirm({
15
+ message: 'Start services now after setup?',
16
+ initialValue: true,
17
+ withGuide: true,
18
+ });
19
+
20
+ handleCancel(shouldStart);
21
+ return shouldStart as boolean;
22
+ }
23
+
24
+ /**
25
+ * Starts infrastructure services using docker-compose.
26
+ * Runs `docker-compose -f docker-compose.infra.yaml up -d` in the target directory.
27
+ *
28
+ * @param targetDir - Directory containing docker-compose.infra.yaml
29
+ * @throws Error if docker-compose command fails
30
+ */
31
+ export async function startInfraServices(targetDir: string): Promise<void> {
32
+ logInfo('Starting infrastructure services...');
33
+
34
+ const s = spinner();
35
+ s.start('Starting docker-compose.infra.yaml...');
36
+
37
+ try {
38
+ await execa(
39
+ 'docker-compose',
40
+ ['-f', 'docker-compose.infra.yaml', 'up', '-d'],
41
+ { cwd: targetDir }
42
+ );
43
+ s.stop('Infrastructure services started');
44
+ } catch (error: unknown) {
45
+ s.error('Failed to start infrastructure services');
46
+ throw error;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Waits for infrastructure services to become healthy.
52
+ * Polls Docker health checks for up to 60 seconds (30 retries x 2s).
53
+ *
54
+ * @param targetDir - Directory where services are running
55
+ */
56
+ export async function waitForInfraHealth(targetDir: string): Promise<void> {
57
+ logInfo('Waiting for infrastructure services to be healthy...');
58
+
59
+ const s = spinner();
60
+ s.start('Checking service health...');
61
+
62
+ // Wait for mongo, redis health checks
63
+ const maxRetries = HEALTH_CHECK.MAX_RETRIES;
64
+ const retryDelay = HEALTH_CHECK.RETRY_DELAY_MS;
65
+
66
+ for (let i = 0; i < maxRetries; i++) {
67
+ try {
68
+ // Check if containers are healthy
69
+ const { stdout } = await execa('docker', ['ps', '--filter', 'health=healthy', '--format', '{{.Names}}']);
70
+ const healthyContainers = stdout.split('\n').filter(Boolean);
71
+
72
+ // Check for critical services
73
+ const hasMongoOrRedis = healthyContainers.some(name =>
74
+ name.includes('mongo') || name.includes('redis')
75
+ );
76
+
77
+ if (hasMongoOrRedis) {
78
+ s.stop('Infrastructure services are healthy');
79
+ return;
80
+ }
81
+
82
+ s.message(`Waiting for services... (${i + 1}/${maxRetries})`);
83
+ await sleep(retryDelay);
84
+ } catch (error) {
85
+ s.message(`Checking health... (${i + 1}/${maxRetries})`);
86
+ await sleep(retryDelay);
87
+ }
88
+ }
89
+
90
+ s.stop('Health check timed out, but services may still be starting');
91
+ logWarning('You may need to wait a bit longer for all services to be ready.');
92
+ }
93
+
94
+ /**
95
+ * Start application services
96
+ */
97
+ export async function startAppServices(targetDir: string): Promise<void> {
98
+ logInfo('Starting application services...');
99
+
100
+ const s = spinner();
101
+ s.start('Starting docker-compose.yaml...');
102
+
103
+ try {
104
+ await execa(
105
+ 'docker-compose',
106
+ ['up', '-d'],
107
+ { cwd: targetDir }
108
+ );
109
+ s.stop('Application services started');
110
+ } catch (error: unknown) {
111
+ s.error('Failed to start application services');
112
+ throw error;
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Display service status and URLs
118
+ */
119
+ export async function displayServiceStatus(): Promise<void> {
120
+ console.log(`\n${divider('green', '═')}`);
121
+ console.log(chalk.greenBright(chalk.bold('Services Started')));
122
+ console.log(divider('green', '═'));
123
+
124
+ try {
125
+ // Get running containers
126
+ const { stdout } = await execa('docker', ['ps', '--format', '{{.Names}}']);
127
+ const containers = stdout.split('\n').filter(Boolean).sort();
128
+
129
+ const accessRows: Array<{ label: string; value: string }> = [];
130
+
131
+ // Display URLs for known services
132
+ if (containers.some(c => c.includes('api'))) {
133
+ accessRows.push({ label: 'API Server', value: getServiceURL('API') });
134
+ accessRows.push({ label: 'API Health', value: `${getServiceURL('API')}/health` });
135
+ }
136
+
137
+ if (containers.some(c => c.includes('dashboard'))) {
138
+ accessRows.push({ label: 'Dashboard', value: getServiceURL('DASHBOARD') });
139
+ }
140
+
141
+ if (containers.some(c => c.includes('kafka-ui'))) {
142
+ accessRows.push({ label: 'Kafka UI', value: getServiceURL('KAFKA_UI') });
143
+ }
144
+
145
+ if (containers.some(c => c.includes('grafana'))) {
146
+ accessRows.push({ label: 'Grafana', value: `${getServiceURL('GRAFANA')} (admin/admin)` });
147
+ }
148
+
149
+ if (accessRows.length > 0) {
150
+ printSummaryCard('Access URLs', accessRows);
151
+ }
152
+
153
+ console.log(chalk.cyanBright('Running Containers'));
154
+ console.log(divider());
155
+ for (const container of containers) {
156
+ console.log(` ${chalk.greenBright('•')} ${container}`);
157
+ }
158
+ console.log('');
159
+
160
+ printCommandHints('Helpful commands', [
161
+ 'docker-compose logs -f',
162
+ 'docker-compose down',
163
+ ]);
164
+ console.log(`${divider('green', '═')}\n`);
165
+ } catch (error) {
166
+ logWarning('Could not fetch container status');
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Helper: Sleep for specified milliseconds
172
+ */
173
+ function sleep(ms: number): Promise<void> {
174
+ return new Promise(resolve => setTimeout(resolve, ms));
175
+ }