@shohojdhara/atomix 0.4.7 → 0.4.9

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 (176) hide show
  1. package/atomix.config.ts +58 -1
  2. package/dist/atomix.css +172 -157
  3. package/dist/atomix.css.map +1 -1
  4. package/dist/atomix.min.css +4 -4
  5. package/dist/atomix.min.css.map +1 -1
  6. package/dist/charts.d.ts +33 -0
  7. package/dist/charts.js +1274 -164
  8. package/dist/charts.js.map +1 -1
  9. package/dist/core.d.ts +33 -10
  10. package/dist/core.js +1099 -83
  11. package/dist/core.js.map +1 -1
  12. package/dist/forms.d.ts +33 -0
  13. package/dist/forms.js +2106 -1050
  14. package/dist/forms.js.map +1 -1
  15. package/dist/heavy.d.ts +42 -1
  16. package/dist/heavy.js +1663 -638
  17. package/dist/heavy.js.map +1 -1
  18. package/dist/index.d.ts +442 -270
  19. package/dist/index.esm.js +1947 -680
  20. package/dist/index.esm.js.map +1 -1
  21. package/dist/index.js +1982 -712
  22. package/dist/index.js.map +1 -1
  23. package/dist/index.min.js +1 -1
  24. package/dist/index.min.js.map +1 -1
  25. package/package.json +6 -3
  26. package/scripts/atomix-cli.js +136 -1827
  27. package/scripts/cli/__tests__/basic.test.js +3 -2
  28. package/scripts/cli/__tests__/clean.test.js +278 -0
  29. package/scripts/cli/__tests__/component-validator.test.js +433 -0
  30. package/scripts/cli/__tests__/generator.test.js +613 -0
  31. package/scripts/cli/__tests__/glass-motion.test.js +256 -0
  32. package/scripts/cli/__tests__/integration.test.js +719 -108
  33. package/scripts/cli/__tests__/migrate.test.js +74 -0
  34. package/scripts/cli/__tests__/security.test.js +206 -0
  35. package/scripts/cli/__tests__/test-setup.js +3 -1
  36. package/scripts/cli/__tests__/theme-bridge.test.js +507 -0
  37. package/scripts/cli/__tests__/token-provider.test.js +361 -0
  38. package/scripts/cli/__tests__/utils.test.js +5 -5
  39. package/scripts/cli/commands/benchmark.js +105 -0
  40. package/scripts/cli/commands/build-theme.js +115 -0
  41. package/scripts/cli/commands/clean.js +109 -0
  42. package/scripts/cli/commands/doctor.js +88 -0
  43. package/scripts/cli/commands/generate.js +218 -0
  44. package/scripts/cli/commands/init.js +73 -0
  45. package/scripts/cli/commands/migrate.js +106 -0
  46. package/scripts/cli/commands/sync-tokens.js +206 -0
  47. package/scripts/cli/commands/theme-bridge.js +248 -0
  48. package/scripts/cli/commands/tokens.js +157 -0
  49. package/scripts/cli/commands/validate.js +194 -0
  50. package/scripts/cli/internal/ai-engine.js +156 -0
  51. package/scripts/cli/internal/compiler.js +114 -0
  52. package/scripts/cli/internal/component-validator.js +443 -0
  53. package/scripts/cli/internal/config-loader.js +162 -0
  54. package/scripts/cli/internal/filesystem.js +158 -0
  55. package/scripts/cli/internal/generator.js +430 -0
  56. package/scripts/cli/internal/glass-generator.js +398 -0
  57. package/scripts/cli/internal/hook-generator.js +369 -0
  58. package/scripts/cli/internal/hooks.js +61 -0
  59. package/scripts/cli/internal/itcss-generator.js +565 -0
  60. package/scripts/cli/internal/motion-generator.js +679 -0
  61. package/scripts/cli/internal/template-engine.js +301 -0
  62. package/scripts/cli/internal/theme-bridge.js +664 -0
  63. package/scripts/cli/internal/tokens/engine.js +122 -0
  64. package/scripts/cli/internal/tokens/provider.js +34 -0
  65. package/scripts/cli/internal/tokens/providers/figma.js +50 -0
  66. package/scripts/cli/internal/tokens/providers/style-dictionary.js +48 -0
  67. package/scripts/cli/internal/tokens/providers/w3c.js +48 -0
  68. package/scripts/cli/internal/tokens/token-provider.js +443 -0
  69. package/scripts/cli/internal/tokens/token-validator.js +513 -0
  70. package/scripts/cli/internal/validator.js +276 -0
  71. package/scripts/cli/internal/wizard.js +115 -0
  72. package/scripts/cli/mappings.js +23 -0
  73. package/scripts/cli/migration-tools.js +164 -94
  74. package/scripts/cli/plugins/style-dictionary.js +46 -0
  75. package/scripts/cli/templates/README.md +525 -95
  76. package/scripts/cli/templates/common-templates.js +40 -14
  77. package/scripts/cli/templates/components/react-component.ts +282 -0
  78. package/scripts/cli/templates/config/project-config.ts +112 -0
  79. package/scripts/cli/templates/hooks/use-component.ts +477 -0
  80. package/scripts/cli/templates/index.js +19 -4
  81. package/scripts/cli/templates/index.ts +171 -0
  82. package/scripts/cli/templates/next-templates.js +72 -0
  83. package/scripts/cli/templates/react-templates.js +70 -126
  84. package/scripts/cli/templates/scss-templates.js +35 -35
  85. package/scripts/cli/templates/stories/storybook-story.ts +241 -0
  86. package/scripts/cli/templates/styles/scss-component.ts +255 -0
  87. package/scripts/cli/templates/tests/vitest-test.ts +229 -0
  88. package/scripts/cli/templates/token-templates.js +337 -1
  89. package/scripts/cli/templates/tokens/token-generators.ts +1088 -0
  90. package/scripts/cli/templates/types/component-types.ts +145 -0
  91. package/scripts/cli/templates/utils/testing-utils.ts +144 -0
  92. package/scripts/cli/templates/vanilla-templates.js +39 -0
  93. package/scripts/cli/token-manager.js +8 -2
  94. package/scripts/cli/utils/cache-manager.js +240 -0
  95. package/scripts/cli/utils/detector.js +46 -0
  96. package/scripts/cli/utils/diagnostics.js +289 -0
  97. package/scripts/cli/utils/error.js +89 -0
  98. package/scripts/cli/utils/helpers.js +67 -0
  99. package/scripts/cli/utils/logger.js +75 -0
  100. package/scripts/cli/utils/security.js +302 -0
  101. package/scripts/cli/utils/telemetry.js +115 -0
  102. package/scripts/cli/utils/validation.js +37 -0
  103. package/scripts/cli/utils.js +28 -341
  104. package/src/components/Accordion/Accordion.stories.tsx +0 -18
  105. package/src/components/Accordion/Accordion.test.tsx +0 -17
  106. package/src/components/Accordion/Accordion.tsx +0 -4
  107. package/src/components/AtomixGlass/AtomixGlass.test.tsx +37 -3
  108. package/src/components/AtomixGlass/AtomixGlass.tsx +143 -31
  109. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +129 -31
  110. package/src/components/AtomixGlass/PerformanceDashboard.tsx +219 -0
  111. package/src/components/AtomixGlass/README.md +25 -10
  112. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +216 -0
  113. package/src/components/AtomixGlass/animation-system.ts +578 -0
  114. package/src/components/AtomixGlass/shader-utils.ts +4 -1
  115. package/src/components/AtomixGlass/stories/Overview.stories.tsx +157 -6
  116. package/src/components/AtomixGlass/stories/Phase1-Animation.stories.tsx +653 -0
  117. package/src/components/AtomixGlass/stories/Phase1-Test.stories.tsx +95 -0
  118. package/src/components/AtomixGlass/stories/Playground.stories.tsx +51 -51
  119. package/src/components/AtomixGlass/stories/shared-components.tsx +6 -0
  120. package/src/components/Avatar/Avatar.tsx +1 -1
  121. package/src/components/Button/Button.stories.disabled-link.tsx +10 -0
  122. package/src/components/Button/Button.stories.tsx +10 -0
  123. package/src/components/Button/Button.test.tsx +16 -11
  124. package/src/components/Button/Button.tsx +4 -4
  125. package/src/components/Card/Card.tsx +1 -1
  126. package/src/components/Dropdown/Dropdown.tsx +12 -12
  127. package/src/components/Form/Select.tsx +62 -3
  128. package/src/components/Modal/Modal.tsx +14 -3
  129. package/src/components/Navigation/Navbar/Navbar.tsx +44 -0
  130. package/src/components/Slider/Slider.stories.tsx +3 -3
  131. package/src/components/Slider/Slider.tsx +38 -0
  132. package/src/components/Steps/Steps.tsx +3 -3
  133. package/src/components/Tabs/Tabs.tsx +77 -8
  134. package/src/components/Testimonial/Testimonial.tsx +1 -1
  135. package/src/components/TypedButton/TypedButton.stories.tsx +59 -0
  136. package/src/components/TypedButton/TypedButton.tsx +39 -0
  137. package/src/components/TypedButton/index.ts +2 -0
  138. package/src/components/VideoPlayer/VideoPlayer.tsx +11 -4
  139. package/src/lib/composables/index.ts +4 -7
  140. package/src/lib/composables/types.ts +45 -0
  141. package/src/lib/composables/useAccordion.ts +0 -7
  142. package/src/lib/composables/useAtomixGlass.ts +148 -6
  143. package/src/lib/composables/useAtomixGlassStyles.ts +9 -7
  144. package/src/lib/composables/useChartExport.ts +3 -13
  145. package/src/lib/composables/useDropdown.ts +66 -0
  146. package/src/lib/composables/useFocusTrap.ts +80 -0
  147. package/src/lib/composables/usePerformanceMonitor.ts +448 -0
  148. package/src/lib/composables/useResponsiveGlass.presets.ts +192 -0
  149. package/src/lib/composables/useResponsiveGlass.ts +441 -0
  150. package/src/lib/composables/useTooltip.ts +16 -0
  151. package/src/lib/composables/useTypedButton.ts +66 -0
  152. package/src/lib/config/index.ts +62 -5
  153. package/src/lib/constants/components.ts +62 -7
  154. package/src/lib/theme/devtools/__tests__/useHistory.test.tsx +150 -0
  155. package/src/lib/theme/tokens/centralized-tokens.ts +120 -0
  156. package/src/lib/theme/utils/__tests__/domUtils.test.ts +101 -0
  157. package/src/lib/types/components.ts +37 -11
  158. package/src/lib/types/glass.ts +35 -0
  159. package/src/lib/types/index.ts +1 -0
  160. package/src/lib/utils/displacement-generator.ts +1 -1
  161. package/src/styles/01-settings/_settings.testtypecheck.scss +53 -0
  162. package/src/styles/01-settings/_settings.typedbutton.scss +53 -0
  163. package/src/styles/06-components/_components.atomix-glass.scss +17 -21
  164. package/src/styles/06-components/_components.edge-panel.scss +1 -5
  165. package/src/styles/06-components/_components.modal.scss +1 -4
  166. package/src/styles/06-components/_components.navbar.scss +1 -1
  167. package/src/styles/06-components/_components.testbutton.scss +212 -0
  168. package/src/styles/06-components/_components.testtypecheck.scss +212 -0
  169. package/src/styles/06-components/_components.tooltip.scss +9 -5
  170. package/src/styles/06-components/_components.typedbutton.scss +212 -0
  171. package/src/styles/99-utilities/_index.scss +1 -0
  172. package/src/styles/99-utilities/_utilities.text.scss +1 -1
  173. package/src/styles/99-utilities/_utilities.touch-target.scss +36 -0
  174. package/scripts/cli/component-generator.js +0 -564
  175. package/scripts/cli/interactive-init.js +0 -357
  176. package/src/styles/06-components/old.chart.styles.scss +0 -2788
@@ -0,0 +1,289 @@
1
+ /**
2
+ * Atomix CLI Diagnostics Utility
3
+ * Shared logic for environment and project health checks
4
+ */
5
+
6
+ import { execSync } from 'child_process';
7
+ import { existsSync, accessSync, constants, readdirSync } from 'fs';
8
+ import { join } from 'path';
9
+ import { readFile } from 'fs/promises';
10
+ import { logger } from './logger.js';
11
+ import { configLoader } from '../internal/config-loader.js';
12
+
13
+ /**
14
+ * Check Node.js and NPM versions
15
+ */
16
+ export async function checkRuntimes() {
17
+ const results = [];
18
+
19
+ // Node.js version check
20
+ const nodeVersion = process.version;
21
+ const minNodeVersion = 'v18.0.0';
22
+ const nodeOk = parseInt(nodeVersion.slice(1)) >= 18;
23
+
24
+ results.push({
25
+ name: 'Node.js',
26
+ status: nodeOk ? 'pass' : 'fail',
27
+ message: `Current: ${nodeVersion}, Required: >=${minNodeVersion}`,
28
+ suggestion: nodeOk ? null : 'Please upgrade Node.js to version 18 or higher.'
29
+ });
30
+
31
+ // NPM version check
32
+ try {
33
+ const npmVersion = execSync('npm -v').toString().trim();
34
+ const npmOk = parseInt(npmVersion.split('.')[0]) >= 8;
35
+ results.push({
36
+ name: 'NPM',
37
+ status: npmOk ? 'pass' : 'fail',
38
+ message: `Current: ${npmVersion}, Required: >=8.0.0`,
39
+ suggestion: npmOk ? null : 'Please upgrade NPM to version 8 or higher.'
40
+ });
41
+ } catch (error) {
42
+ results.push({
43
+ name: 'NPM',
44
+ status: 'fail',
45
+ message: 'NPM not found in system path',
46
+ suggestion: 'Please install NPM (comes with Node.js).'
47
+ });
48
+ }
49
+
50
+ return results;
51
+ }
52
+
53
+ /**
54
+ * Check CLI plugins
55
+ */
56
+ export async function checkPlugins() {
57
+ const results = [];
58
+ const config = await configLoader.load();
59
+
60
+ if (!config.plugins || config.plugins.length === 0) {
61
+ results.push({
62
+ name: 'Plugins',
63
+ status: 'pass',
64
+ message: 'No plugins registered',
65
+ suggestion: null
66
+ });
67
+ return results;
68
+ }
69
+
70
+ for (const pluginEntry of config.plugins) {
71
+ const pluginName = typeof pluginEntry === 'string' ? pluginEntry : pluginEntry.name;
72
+
73
+ try {
74
+ // In a real scenario, we'd check if the module is resolvable
75
+ // For now, we'll just report their presence
76
+ results.push({
77
+ name: `Plugin: ${pluginName}`,
78
+ status: 'pass',
79
+ message: 'Registered in configuration',
80
+ suggestion: null
81
+ });
82
+ } catch (error) {
83
+ results.push({
84
+ name: `Plugin: ${pluginName}`,
85
+ status: 'fail',
86
+ message: `Error checking plugin: ${error.message}`,
87
+ suggestion: 'Verify the plugin path or package name.'
88
+ });
89
+ }
90
+ }
91
+
92
+ return results;
93
+ }
94
+
95
+ /**
96
+ * Check if running in the Atomix monorepo (internal development)
97
+ */
98
+ async function isAtomixMonorepo(projectRoot) {
99
+ try {
100
+ const packageJsonPath = join(projectRoot, 'package.json');
101
+ if (!existsSync(packageJsonPath)) return false;
102
+
103
+ const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8'));
104
+ // Check if this is the Atomix monorepo by package name or structure
105
+ const packageName = packageJson.name || '';
106
+ const isAtomixPackage = packageName.includes('atomix') &&
107
+ (packageName.startsWith('@shohojdhara/') ||
108
+ packageName.startsWith('@atomix/') ||
109
+ packageName === 'atomix');
110
+
111
+ return isAtomixPackage || existsSync(join(projectRoot, 'packages'));
112
+ } catch {
113
+ return false;
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Check project structure
119
+ */
120
+ export async function checkProjectStructure(projectRoot = process.cwd()) {
121
+ const results = [];
122
+ const requiredDirs = ['src'];
123
+ const recommendedDirs = ['themes', 'docs'];
124
+ const internalDirs = ['scripts/cli']; // Only required for Atomix monorepo development
125
+
126
+ const isMonorepo = await isAtomixMonorepo(projectRoot);
127
+
128
+ // Check required directories (always checked)
129
+ for (const dir of requiredDirs) {
130
+ const dirPath = join(projectRoot, dir);
131
+ const exists = existsSync(dirPath);
132
+ results.push({
133
+ name: `Directory: ${dir}`,
134
+ status: exists ? 'pass' : 'fail',
135
+ message: exists ? 'Exists' : 'Missing',
136
+ suggestion: exists ? null : `Create the '${dir}' directory in your project root.`
137
+ });
138
+ }
139
+
140
+ // Check internal directories (only for Atomix monorepo)
141
+ for (const dir of internalDirs) {
142
+ const dirPath = join(projectRoot, dir);
143
+ const exists = existsSync(dirPath);
144
+
145
+ if (isMonorepo) {
146
+ // In monorepo, this is required
147
+ results.push({
148
+ name: `Directory: ${dir}`,
149
+ status: exists ? 'pass' : 'fail',
150
+ message: exists ? 'Exists' : 'Missing',
151
+ suggestion: exists ? null : `Create the '${dir}' directory in your project root.`
152
+ });
153
+ } else {
154
+ // In external projects, skip this check entirely (don't show as fail/warn/pass)
155
+ // This directory is only needed for internal Atomix development
156
+ }
157
+ }
158
+
159
+ // Check recommended directories
160
+ for (const dir of recommendedDirs) {
161
+ const dirPath = join(projectRoot, dir);
162
+ const exists = existsSync(dirPath);
163
+ results.push({
164
+ name: `Directory: ${dir} (Recommended)`,
165
+ status: exists ? 'pass' : 'warn',
166
+ message: exists ? 'Exists' : 'Missing',
167
+ suggestion: exists ? null : `Consider creating a '${dir}' directory for better organization.`
168
+ });
169
+ }
170
+
171
+ return results;
172
+ }
173
+
174
+ /**
175
+ * Check configuration file
176
+ */
177
+ export async function checkConfig(projectRoot = process.cwd()) {
178
+ const results = [];
179
+ const configFiles = ['atomix.config.ts', 'atomix.config.js'];
180
+ let found = false;
181
+
182
+ for (const file of configFiles) {
183
+ const configPath = join(projectRoot, file);
184
+ if (existsSync(configPath)) {
185
+ found = true;
186
+ try {
187
+ // Simple syntax check by reading it
188
+ await readFile(configPath, 'utf8');
189
+ results.push({
190
+ name: 'Configuration',
191
+ status: 'pass',
192
+ message: `Found ${file} and readable`,
193
+ suggestion: null
194
+ });
195
+ } catch (error) {
196
+ results.push({
197
+ name: 'Configuration',
198
+ status: 'fail',
199
+ message: `Error reading ${file}: ${error.message}`,
200
+ suggestion: 'Check file permissions and syntax.'
201
+ });
202
+ }
203
+ break;
204
+ }
205
+ }
206
+
207
+ if (!found) {
208
+ results.push({
209
+ name: 'Configuration',
210
+ status: 'warn',
211
+ message: 'No atomix.config.ts or atomix.config.js found',
212
+ suggestion: 'Run `atomix init` to create a default configuration.'
213
+ });
214
+ }
215
+
216
+ return results;
217
+ }
218
+
219
+ /**
220
+ * Check design tokens (01-settings). CLI discovers tokens from src/styles/01-settings/_settings.*.scss
221
+ * and optionally from atomix.config tokenEngine / tokens entry.
222
+ */
223
+ export async function checkTokens(projectRoot = process.cwd()) {
224
+ const results = [];
225
+ const settingsDir = join(projectRoot, 'src/styles/01-settings');
226
+
227
+ if (!existsSync(settingsDir)) {
228
+ results.push({
229
+ name: 'Tokens',
230
+ status: 'warn',
231
+ message: 'None configured (optional)',
232
+ suggestion: 'Add token files or a theme for design token support. Token discovery: src/styles/01-settings/_settings.*.scss. See Atomix token docs.'
233
+ });
234
+ return results;
235
+ }
236
+
237
+ try {
238
+ const files = readdirSync(settingsDir);
239
+ const tokenFiles = files.filter((f) => f.startsWith('_settings.') && f.endsWith('.scss'));
240
+ const count = tokenFiles.length;
241
+ results.push({
242
+ name: 'Tokens',
243
+ status: 'pass',
244
+ message: count ? `Found ${count} token categor${count === 1 ? 'y' : 'ies'}` : 'None configured (optional)',
245
+ suggestion: count ? null : 'Add _settings.*.scss in src/styles/01-settings for design tokens.'
246
+ });
247
+ } catch (error) {
248
+ results.push({
249
+ name: 'Tokens',
250
+ status: 'warn',
251
+ message: `Could not read 01-settings: ${error.message}`,
252
+ suggestion: null
253
+ });
254
+ }
255
+
256
+ return results;
257
+ }
258
+
259
+ /**
260
+ * Check directory permissions
261
+ */
262
+ export async function checkPermissions(projectRoot = process.cwd()) {
263
+ const results = [];
264
+ const dirsToCheck = ['.', 'src'];
265
+
266
+ for (const dir of dirsToCheck) {
267
+ const dirPath = join(projectRoot, dir);
268
+ if (!existsSync(dirPath)) continue;
269
+
270
+ try {
271
+ accessSync(dirPath, constants.R_OK | constants.W_OK);
272
+ results.push({
273
+ name: `Permissions: ${dir}`,
274
+ status: 'pass',
275
+ message: 'Read/Write access granted',
276
+ suggestion: null
277
+ });
278
+ } catch (error) {
279
+ results.push({
280
+ name: `Permissions: ${dir}`,
281
+ status: 'fail',
282
+ message: 'Insufficient permissions',
283
+ suggestion: `Grant read/write permissions to the '${dir}' directory.`
284
+ });
285
+ }
286
+ }
287
+
288
+ return results;
289
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Atomix CLI Error Class
3
+ * Standardized error handling with actionable suggestions
4
+ */
5
+
6
+ /**
7
+ * Common CLI Error Categories
8
+ */
9
+ export const ErrorCategory = {
10
+ CONFIG: 'CONFIG_ERROR',
11
+ VALIDATION: 'VALIDATION_ERROR',
12
+ GENERATION: 'GENERATION_ERROR',
13
+ ENVIRONMENT: 'ENVIRONMENT_ERROR',
14
+ FILESYSTEM: 'FILESYSTEM_ERROR',
15
+ INVALID_PATH: 'INVALID_PATH'
16
+ };
17
+
18
+ /**
19
+ * Suggestions for common error codes
20
+ */
21
+ const COMMON_SUGGESTIONS = {
22
+ [ErrorCategory.CONFIG]: [
23
+ 'Run `atomix init` to create a new configuration file.',
24
+ 'Check if `atomix.config.ts` has syntax errors.',
25
+ 'Ensure you have exported the configuration correctly.'
26
+ ],
27
+ [ErrorCategory.ENVIRONMENT]: [
28
+ 'Run `atomix doctor` to check your environment health.',
29
+ 'Ensure Node.js version is >=18.0.0.',
30
+ 'Check if all peer dependencies are installed.'
31
+ ],
32
+ [ErrorCategory.FILESYSTEM]: [
33
+ 'Check if you have write permissions for the target directory.',
34
+ 'Ensure the path provided is valid and within the project root.',
35
+ 'Verify if the file exists and is not locked by another process.'
36
+ ],
37
+ [ErrorCategory.INVALID_PATH]: [
38
+ 'Source must be a directory. Pass the project root (e.g. . or ./my-app).',
39
+ 'Do not pass a file path (e.g. package.json).'
40
+ ]
41
+ };
42
+
43
+ export class AtomixCLIError extends Error {
44
+ /**
45
+ * @param {string} message - Human-readable error message
46
+ * @param {string} code - Unique error code (e.g., ErrorCategory.CONFIG)
47
+ * @param {string[]} suggestions - Specific steps to resolve the issue
48
+ */
49
+ constructor(message, code, suggestions = []) {
50
+ super(message);
51
+ this.name = 'AtomixCLIError';
52
+ this.code = code;
53
+ this.suggestions = suggestions.length > 0 ? suggestions : (COMMON_SUGGESTIONS[code] || []);
54
+ }
55
+ }
56
+
57
+ import chalk from 'chalk';
58
+ import { telemetry } from './telemetry.js';
59
+
60
+ /**
61
+ * Global CLI error handler
62
+ * @param {Error} error - The error object to handle
63
+ * @param {object} spinner - Optional ora spinner to stop
64
+ */
65
+ export async function handleCLIError(error, spinner = null) {
66
+ if (spinner) {
67
+ spinner.fail('Operation failed');
68
+ }
69
+
70
+ // Log failure to telemetry
71
+ await telemetry.stop(false, { error: error.message, code: error.code });
72
+
73
+ console.error(chalk.bold.red(`\n❌ ${error.message}`));
74
+
75
+ if (error instanceof AtomixCLIError && error.suggestions && error.suggestions.length > 0) {
76
+ console.log(chalk.yellow('\n💡 Suggestions:'));
77
+ error.suggestions.forEach((suggestion, index) => {
78
+ console.log(chalk.gray(` ${index + 1}. ${suggestion}`));
79
+ });
80
+ }
81
+
82
+ if (process.env.ATOMIX_DEBUG === 'true' && error.stack) {
83
+ console.error(chalk.gray('\nStack trace:'));
84
+ console.error(chalk.gray(error.stack));
85
+ }
86
+
87
+ // Single exit point for CLI failure; commands should throw, not call process.exit(1).
88
+ process.exit(1);
89
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Atomix CLI Helper Utilities
3
+ */
4
+
5
+ import { access } from 'fs/promises';
6
+
7
+ /**
8
+ * Sanitizes user input to prevent injection attacks
9
+ */
10
+ export function sanitizeInput(input) {
11
+ if (typeof input !== 'string') return String(input);
12
+ return input.replace(/[;&|`$<>\\"']/g, '').replace(/\0/g, '').trim();
13
+ }
14
+
15
+ /**
16
+ * Checks Node.js version compatibility
17
+ */
18
+ export function checkNodeVersion(requiredVersion = '18.0.0') {
19
+ const currentVersion = process.version.substring(1);
20
+ const current = currentVersion.split('.').map(Number);
21
+ const required = requiredVersion.split('.').map(Number);
22
+
23
+ for (let i = 0; i < required.length; i++) {
24
+ if (current[i] < required[i]) return { compatible: false, current: currentVersion, required: requiredVersion };
25
+ if (current[i] > required[i]) break;
26
+ }
27
+ return { compatible: true, current: currentVersion, required: requiredVersion };
28
+ }
29
+
30
+ /**
31
+ * Formats file size
32
+ */
33
+ export function formatFileSize(bytes) {
34
+ const sizes = ['B', 'KB', 'MB', 'GB'];
35
+ if (bytes === 0) return '0 B';
36
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
37
+ return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${sizes[i]}`;
38
+ }
39
+
40
+ /**
41
+ * Checks if running in CI environment
42
+ */
43
+ export function isCI() {
44
+ return !!(process.env.CI || process.env.GITHUB_ACTIONS || process.env.JENKINS_URL);
45
+ }
46
+
47
+ /**
48
+ * Checks if CLI should run in non-interactive mode (no prompts)
49
+ * Use for CI/scripts: set ATOMIX_NON_INTERACTIVE=1 or run in CI
50
+ */
51
+ export function isNonInteractive() {
52
+ return !!(process.env.ATOMIX_NON_INTERACTIVE === '1' || process.env.ATOMIX_NON_INTERACTIVE === 'true' || isCI());
53
+ }
54
+
55
+ /**
56
+ * Check if a file exists at the given path
57
+ * @param {string} path - File path to check
58
+ * @returns {Promise<boolean>}
59
+ */
60
+ export async function fileExists(path) {
61
+ try {
62
+ await access(path);
63
+ return true;
64
+ } catch {
65
+ return false;
66
+ }
67
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Atomix CLI Logger
3
+ * Standardized logging with support for spinners and debug levels
4
+ */
5
+
6
+ import chalk from 'chalk';
7
+ import ora from 'ora';
8
+ import boxen from 'boxen';
9
+
10
+ const DEBUG = process.env.ATOMIX_DEBUG === 'true';
11
+
12
+ export const logger = {
13
+ /**
14
+ * Log a debug message (only visible in debug mode)
15
+ */
16
+ debug: (message, data = null) => {
17
+ if (DEBUG) {
18
+ console.log(chalk.gray(`[DEBUG] ${message}`));
19
+ if (data) {
20
+ console.log(chalk.gray(JSON.stringify(data, null, 2)));
21
+ }
22
+ }
23
+ },
24
+
25
+ /**
26
+ * Log an info message
27
+ */
28
+ info: (message) => {
29
+ console.log(chalk.blue(message));
30
+ },
31
+
32
+ /**
33
+ * Log a success message
34
+ */
35
+ success: (message) => {
36
+ console.log(chalk.green(`✓ ${message}`));
37
+ },
38
+
39
+ /**
40
+ * Log a warning message
41
+ */
42
+ warn: (message) => {
43
+ console.log(chalk.yellow(`! ${message}`));
44
+ },
45
+
46
+ /**
47
+ * Log an error message
48
+ */
49
+ error: (message) => {
50
+ console.error(chalk.bold.red(`\n❌ ${message}`));
51
+ },
52
+
53
+ /**
54
+ * Create and return a spinner
55
+ */
56
+ spinner: (text) => {
57
+ return ora({
58
+ text,
59
+ color: 'cyan'
60
+ });
61
+ },
62
+
63
+ /**
64
+ * Create a boxed message
65
+ */
66
+ box: (message, options = {}) => {
67
+ console.log(boxen(message, {
68
+ padding: 1,
69
+ margin: 1,
70
+ borderStyle: 'round',
71
+ borderColor: 'cyan',
72
+ ...options
73
+ }));
74
+ }
75
+ };