@simplens/onboard 1.0.0 → 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.
- package/README.md +331 -214
- package/dist/__tests__/env-config.test.d.ts +2 -0
- package/dist/__tests__/env-config.test.d.ts.map +1 -0
- package/dist/__tests__/env-config.test.js +23 -0
- package/dist/__tests__/env-config.test.js.map +1 -0
- package/dist/__tests__/infra-prompts.test.d.ts +2 -0
- package/dist/__tests__/infra-prompts.test.d.ts.map +1 -0
- package/dist/__tests__/infra-prompts.test.js +43 -0
- package/dist/__tests__/infra-prompts.test.js.map +1 -0
- package/dist/__tests__/infra.test.d.ts +2 -0
- package/dist/__tests__/infra.test.d.ts.map +1 -0
- package/dist/__tests__/infra.test.js +14 -0
- package/dist/__tests__/infra.test.js.map +1 -0
- package/dist/__tests__/nginx.test.d.ts +2 -0
- package/dist/__tests__/nginx.test.d.ts.map +1 -0
- package/dist/__tests__/nginx.test.js +16 -0
- package/dist/__tests__/nginx.test.js.map +1 -0
- package/dist/env-config.d.ts +27 -12
- package/dist/env-config.d.ts.map +1 -1
- package/dist/env-config.js +258 -141
- package/dist/env-config.js.map +1 -1
- package/dist/index.js +341 -71
- package/dist/index.js.map +1 -1
- package/dist/infra.d.ts +17 -14
- package/dist/infra.d.ts.map +1 -1
- package/dist/infra.js +265 -176
- package/dist/infra.js.map +1 -1
- package/dist/plugins.d.ts +5 -10
- package/dist/plugins.d.ts.map +1 -1
- package/dist/plugins.js +75 -44
- package/dist/plugins.js.map +1 -1
- package/dist/services.d.ts +1 -23
- package/dist/services.d.ts.map +1 -1
- package/dist/services.js +47 -62
- package/dist/services.js.map +1 -1
- package/dist/templates.d.ts +3 -2
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +203 -198
- package/dist/templates.js.map +1 -1
- package/dist/types/domain.d.ts +2 -0
- package/dist/types/domain.d.ts.map +1 -1
- package/dist/ui.d.ts +45 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +93 -0
- package/dist/ui.js.map +1 -0
- package/dist/utils/logger.d.ts +1 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +32 -7
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +66 -2
- package/dist/utils.js.map +1 -1
- package/dist/validators.d.ts +1 -52
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +10 -57
- package/dist/validators.js.map +1 -1
- package/package.json +3 -5
- package/src/__tests__/env-config.test.ts +28 -0
- package/src/__tests__/errors.test.ts +187 -187
- package/src/__tests__/infra-prompts.test.ts +54 -0
- package/src/__tests__/infra.test.ts +15 -0
- package/src/__tests__/utils.test.ts +142 -142
- package/src/__tests__/validators.test.ts +195 -195
- package/src/config/constants.ts +86 -86
- package/src/config/index.ts +1 -1
- package/src/env-config.ts +455 -320
- package/src/index.ts +534 -203
- package/src/infra.ts +404 -300
- package/src/plugins.ts +221 -190
- package/src/services.ts +175 -190
- package/src/templates.ts +209 -203
- package/src/types/domain.ts +129 -127
- package/src/types/errors.ts +173 -173
- package/src/types/index.ts +2 -2
- package/src/ui.ts +91 -0
- package/src/utils/index.ts +1 -1
- package/src/utils/logger.ts +144 -118
- package/src/utils.ts +183 -105
- package/src/validators.ts +145 -192
- package/tsconfig.json +18 -18
- package/vitest.config.ts +22 -20
package/src/services.ts
CHANGED
|
@@ -1,190 +1,175 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import { HEALTH_CHECK,
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
*
|
|
118
|
-
*/
|
|
119
|
-
export async function
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
try {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
+
}
|