@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
package/src/validators.ts CHANGED
@@ -1,192 +1,145 @@
1
- import { execa } from 'execa';
2
- import { logError, logSuccess, logWarning } from './utils.js';
3
- import {
4
- DockerNotInstalledError,
5
- DockerNotRunningError,
6
- DockerPermissionError,
7
- } from './types/errors.js';
8
- import { VALIDATION } from './config/constants.js';
9
-
10
- export type OSType = 'windows' | 'linux' | 'darwin';
11
-
12
- /**
13
- * Checks if Docker is installed on the system by running `docker --version`.
14
- *
15
- * @throws {DockerNotInstalledError} When Docker is not found in PATH or not installed
16
- *
17
- * @example
18
- * ```ts
19
- * try {
20
- * await checkDockerInstalled();
21
- * console.log('Docker is available');
22
- * } catch (error) {
23
- * // Handle DockerNotInstalledError
24
- * }
25
- * ```
26
- */
27
- export async function checkDockerInstalled(): Promise<void> {
28
- try {
29
- await execa('docker', ['--version']);
30
- } catch (error: any) {
31
- throw new DockerNotInstalledError();
32
- }
33
- }
34
-
35
- /**
36
- * Checks if the Docker daemon is running by executing `docker ps`.
37
- * Provides specific error types based on the failure reason.
38
- *
39
- * @throws {DockerPermissionError} When user lacks permissions to access Docker socket
40
- * @throws {DockerNotRunningError} When Docker daemon is not running or unreachable
41
- *
42
- * @example
43
- * ```ts
44
- * try {
45
- * await checkDockerRunning();
46
- * } catch (error) {
47
- * if (error instanceof DockerPermissionError) {
48
- * // Guide user to add sudo or docker group
49
- * }
50
- * }
51
- * ```
52
- */
53
- export async function checkDockerRunning(): Promise<void> {
54
- try {
55
- await execa('docker', ['ps']);
56
- } catch (error: any) {
57
- const errorMessage = error.message?.toLowerCase() || '';
58
-
59
- if (errorMessage.includes('permission denied') || errorMessage.includes('eacces')) {
60
- throw new DockerPermissionError();
61
- }
62
-
63
- if (errorMessage.includes('cannot connect') || errorMessage.includes('is the docker daemon running')) {
64
- throw new DockerNotRunningError();
65
- }
66
-
67
- // Generic docker error
68
- throw new DockerNotRunningError();
69
- }
70
- }
71
-
72
- /**
73
- * Detects the operating system platform.
74
- *
75
- * @returns OS type: 'windows', 'darwin' (macOS), or 'linux'
76
- * @note Defaults to 'linux' for unknown platforms
77
- *
78
- * @example
79
- * ```ts
80
- * const os = detectOS();
81
- * if (os === 'linux') {
82
- * // Linux-specific configuration
83
- * }
84
- * ```
85
- */
86
- export function detectOS(): OSType {
87
- const platform = process.platform;
88
- if (platform === 'win32') return 'windows';
89
- if (platform === 'linux') return 'linux';
90
- if (platform === 'darwin') return 'darwin';
91
- return 'linux'; // Default fallback
92
- }
93
-
94
- /**
95
- * Validates all system prerequisites before starting the onboarding process.
96
- * Checks Docker installation, daemon status, and detects the operating system.
97
- *
98
- * @throws {DockerNotInstalledError} If Docker is not installed
99
- * @throws {DockerNotRunningError} If Docker daemon is not running
100
- * @throws {DockerPermissionError} If user lacks Docker permissions
101
- *
102
- * @example
103
- * ```ts
104
- * try {
105
- * await validatePrerequisites();
106
- * // Proceed with setup
107
- * } catch (error) {
108
- * // Handle prerequisite errors
109
- * }
110
- * ```
111
- */
112
- export async function validatePrerequisites(): Promise<void> {
113
- console.log('\n🔍 Checking prerequisites...\n');
114
-
115
- // Check Docker installation
116
- try {
117
- await checkDockerInstalled();
118
- logSuccess('Docker is installed');
119
- } catch (error) {
120
- // Error will be caught by main() and displayed with troubleshooting
121
- throw error;
122
- }
123
-
124
- // Check Docker daemon
125
- try {
126
- await checkDockerRunning();
127
- logSuccess('Docker daemon is running');
128
- } catch (error) {
129
- // Error will be caught by main() and displayed with troubleshooting
130
- throw error;
131
- }
132
-
133
- // Detect OS
134
- const os = detectOS();
135
- logSuccess(`Detected OS: ${os}`);
136
-
137
- if (os === 'linux') {
138
- logWarning('Linux detected: You may need to provide your machine IP for infra services.');
139
- }
140
- }
141
-
142
- /**
143
- * Validates environment variable values based on the variable name/type.
144
- * Performs format-specific validation for URLs, ports, and security credentials.
145
- *
146
- * @param key - Environment variable name (e.g., 'MONGO_URI', 'PORT', 'API_KEY')
147
- * @param value - Value to validate
148
- * @returns `true` if valid, `false` otherwise
149
- *
150
- * @remarks
151
- * Validation rules:
152
- * - URLs: Must contain protocol (mongodb://, redis://)
153
- * - Ports: Must be 1-65535
154
- * - Secrets/Keys/Passwords: Must be at least 8 characters
155
- *
156
- * @example
157
- * ```ts
158
- * validateEnvValue('MONGO_URI', 'mongodb://localhost:27017'); // true
159
- * validateEnvValue('PORT', '3000'); // true
160
- * validateEnvValue('API_KEY', 'short'); // false (too short)
161
- * ```
162
- */
163
- export function validateEnvValue(key: string, value: string): boolean {
164
- // URL validation
165
- if (key.includes('URL') || key.includes('URI')) {
166
- if (!value) return false;
167
- // Basic URL format check
168
- if (key === 'MONGO_URI' && !value.includes('mongodb://')) {
169
- return false;
170
- }
171
- if (key === 'REDIS_URL' && !value.includes('redis://')) {
172
- return false;
173
- }
174
- }
175
-
176
- // Port validation
177
- if (key.includes('PORT')) {
178
- const port = parseInt(value, 10);
179
- if (isNaN(port) || port < VALIDATION.PORT_MIN || port > VALIDATION.PORT_MAX) {
180
- return false;
181
- }
182
- }
183
-
184
- // API Key validation (should not be empty for security)
185
- if (key.includes('API_KEY') || key.includes('SECRET') || key.includes('PASSWORD')) {
186
- if (!value || value.length < VALIDATION.MIN_PASSWORD_LENGTH) {
187
- return false;
188
- }
189
- }
190
-
191
- return true;
192
- }
1
+ import { execa } from 'execa';
2
+ import { logSuccess, logWarning } from './utils.js';
3
+ import { spinner } from './ui.js';
4
+ import {
5
+ DockerNotInstalledError,
6
+ DockerNotRunningError,
7
+ DockerPermissionError,
8
+ } from './types/errors.js';
9
+ import { VALIDATION } from './config/constants.js';
10
+
11
+ export type OSType = 'windows' | 'linux' | 'darwin';
12
+
13
+ /**
14
+ * Checks if Docker is installed on the system by running `docker --version`.
15
+ *
16
+ * @throws {DockerNotInstalledError} When Docker is not found in PATH or not installed
17
+ */
18
+ export async function checkDockerInstalled(): Promise<void> {
19
+ try {
20
+ await execa('docker', ['--version']);
21
+ } catch (error: unknown) {
22
+ throw new DockerNotInstalledError();
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Checks if the Docker daemon is running by executing `docker ps`.
28
+ * Provides specific error types based on the failure reason.
29
+ *
30
+ * @throws {DockerPermissionError} When user lacks permissions to access Docker socket
31
+ * @throws {DockerNotRunningError} When Docker daemon is not running or unreachable
32
+ */
33
+ export async function checkDockerRunning(): Promise<void> {
34
+ try {
35
+ await execa('docker', ['ps']);
36
+ } catch (error: unknown) {
37
+ const errorMessage = (error as Error).message?.toLowerCase() || '';
38
+
39
+ if (errorMessage.includes('permission denied') || errorMessage.includes('eacces')) {
40
+ throw new DockerPermissionError();
41
+ }
42
+
43
+ if (errorMessage.includes('cannot connect') || errorMessage.includes('is the docker daemon running')) {
44
+ throw new DockerNotRunningError();
45
+ }
46
+
47
+ // Generic docker error
48
+ throw new DockerNotRunningError();
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Detects the operating system platform.
54
+ *
55
+ * @returns OS type: 'windows', 'darwin' (macOS), or 'linux'
56
+ * @note Defaults to 'linux' for unknown platforms
57
+ */
58
+ export function detectOS(): OSType {
59
+ const platform = process.platform;
60
+ if (platform === 'win32') return 'windows';
61
+ if (platform === 'linux') return 'linux';
62
+ if (platform === 'darwin') return 'darwin';
63
+ return 'linux'; // Default fallback
64
+ }
65
+
66
+ /**
67
+ * Validates all system prerequisites before starting the onboarding process.
68
+ * Checks Docker installation, daemon status, and detects the operating system.
69
+ * Uses clack spinners for visual feedback.
70
+ *
71
+ * @throws {DockerNotInstalledError} If Docker is not installed
72
+ * @throws {DockerNotRunningError} If Docker daemon is not running
73
+ * @throws {DockerPermissionError} If user lacks Docker permissions
74
+ */
75
+ export async function validatePrerequisites(): Promise<void> {
76
+ logSuccess('Running prerequisite checks...');
77
+
78
+ // Check Docker installation
79
+ const s = spinner();
80
+ s.start('Checking Docker installation...');
81
+ try {
82
+ await checkDockerInstalled();
83
+ s.stop('Docker installation detected');
84
+ } catch (error) {
85
+ s.error('Docker installation check failed');
86
+ throw error;
87
+ }
88
+
89
+ // Check Docker daemon
90
+ s.start('Checking Docker daemon status...');
91
+ try {
92
+ await checkDockerRunning();
93
+ s.stop('Docker daemon is running');
94
+ } catch (error) {
95
+ s.error('Docker daemon check failed');
96
+ throw error;
97
+ }
98
+
99
+ // Detect OS
100
+ const os = detectOS();
101
+ logSuccess(`Detected OS: ${os}`);
102
+
103
+ if (os === 'linux') {
104
+ logWarning('Linux detected: You may need to provide your machine IP for infra services.');
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Validates environment variable values based on the variable name/type.
110
+ * Performs format-specific validation for URLs, ports, and security credentials.
111
+ *
112
+ * @param key - Environment variable name (e.g., 'MONGO_URI', 'PORT', 'API_KEY')
113
+ * @param value - Value to validate
114
+ * @returns `true` if valid, `false` otherwise
115
+ */
116
+ export function validateEnvValue(key: string, value: string): boolean {
117
+ // URL validation
118
+ if (key.includes('URL') || key.includes('URI')) {
119
+ if (!value) return false;
120
+ // Basic URL format check
121
+ if (key === 'MONGO_URI' && !value.includes('mongodb://')) {
122
+ return false;
123
+ }
124
+ if (key === 'REDIS_URL' && !value.includes('redis://')) {
125
+ return false;
126
+ }
127
+ }
128
+
129
+ // Port validation
130
+ if (key.includes('PORT')) {
131
+ const port = parseInt(value, 10);
132
+ if (isNaN(port) || port < VALIDATION.PORT_MIN || port > VALIDATION.PORT_MAX) {
133
+ return false;
134
+ }
135
+ }
136
+
137
+ // API Key validation (should not be empty for security)
138
+ if (key.includes('API_KEY') || key.includes('SECRET') || key.includes('PASSWORD')) {
139
+ if (!value || value.length < VALIDATION.MIN_PASSWORD_LENGTH) {
140
+ return false;
141
+ }
142
+ }
143
+
144
+ return true;
145
+ }
package/tsconfig.json CHANGED
@@ -1,19 +1,19 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "node",
6
- "rootDir": "./src",
7
- "outDir": "./dist",
8
- "esModuleInterop": true,
9
- "resolveJsonModule": true,
10
- "forceConsistentCasingInFileNames": true,
11
- "strict": true,
12
- "skipLibCheck": true,
13
- "declaration": true,
14
- "declarationMap": true,
15
- "sourceMap": true
16
- },
17
- "include": ["src/**/*"],
18
- "exclude": ["node_modules", "dist"]
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "Node16",
5
+ "moduleResolution": "Node16",
6
+ "rootDir": "./src",
7
+ "outDir": "./dist",
8
+ "esModuleInterop": true,
9
+ "resolveJsonModule": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "strict": true,
12
+ "skipLibCheck": true,
13
+ "declaration": true,
14
+ "declarationMap": true,
15
+ "sourceMap": true
16
+ },
17
+ "include": ["src/**/*"],
18
+ "exclude": ["node_modules", "dist"]
19
19
  }
package/vitest.config.ts CHANGED
@@ -1,22 +1,22 @@
1
- import { defineConfig } from 'vitest/config';
2
-
3
- export default defineConfig({
4
- test: {
5
- globals: true,
6
- environment: 'node',
7
- include: ['src/**/*.{test,spec}.{js,ts}'],
8
- exclude: ['node_modules/**', 'dist/**'],
9
- coverage: {
10
- provider: 'v8',
11
- reporter: ['text', 'json', 'html'],
12
- exclude: [
13
- 'node_modules/**',
14
- 'dist/**',
15
- '**/*.test.ts',
16
- '**/*.spec.ts',
17
- 'src/templates.ts', // Large constant file
18
- ],
19
- },
20
- testTimeout: 10000,
21
- },
22
- });
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ environment: 'node',
7
+ include: ['src/**/*.{test,spec}.{js,ts}'],
8
+ exclude: ['node_modules/**', 'dist/**'],
9
+ coverage: {
10
+ provider: 'v8',
11
+ reporter: ['text', 'json', 'html'],
12
+ exclude: [
13
+ 'node_modules/**',
14
+ 'dist/**',
15
+ '**/*.test.ts',
16
+ '**/*.spec.ts',
17
+ 'src/templates.ts', // Large constant file
18
+ ],
19
+ },
20
+ testTimeout: 10000,
21
+ },
22
+ });