@vizzly-testing/cli 0.13.4 → 0.15.0

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 (143) hide show
  1. package/dist/cli.js +68 -68
  2. package/dist/commands/doctor.js +30 -34
  3. package/dist/commands/finalize.js +24 -23
  4. package/dist/commands/init.js +30 -28
  5. package/dist/commands/login.js +49 -55
  6. package/dist/commands/logout.js +14 -19
  7. package/dist/commands/project.js +83 -103
  8. package/dist/commands/run.js +77 -89
  9. package/dist/commands/status.js +48 -49
  10. package/dist/commands/tdd-daemon.js +90 -86
  11. package/dist/commands/tdd.js +59 -88
  12. package/dist/commands/upload.js +57 -57
  13. package/dist/commands/whoami.js +40 -45
  14. package/dist/index.js +2 -5
  15. package/dist/plugin-loader.js +15 -17
  16. package/dist/reporter/reporter-bundle.css +1 -1
  17. package/dist/reporter/reporter-bundle.iife.js +78 -32
  18. package/dist/sdk/index.js +36 -45
  19. package/dist/server/handlers/api-handler.js +14 -15
  20. package/dist/server/handlers/tdd-handler.js +34 -37
  21. package/dist/server/http-server.js +75 -869
  22. package/dist/server/middleware/cors.js +22 -0
  23. package/dist/server/middleware/json-parser.js +35 -0
  24. package/dist/server/middleware/response.js +79 -0
  25. package/dist/server/routers/assets.js +91 -0
  26. package/dist/server/routers/auth.js +144 -0
  27. package/dist/server/routers/baseline.js +163 -0
  28. package/dist/server/routers/cloud-proxy.js +146 -0
  29. package/dist/server/routers/config.js +126 -0
  30. package/dist/server/routers/dashboard.js +130 -0
  31. package/dist/server/routers/health.js +61 -0
  32. package/dist/server/routers/projects.js +168 -0
  33. package/dist/server/routers/screenshot.js +86 -0
  34. package/dist/services/auth-service.js +1 -1
  35. package/dist/services/build-manager.js +13 -40
  36. package/dist/services/config-service.js +2 -4
  37. package/dist/services/html-report-generator.js +6 -5
  38. package/dist/services/index.js +64 -0
  39. package/dist/services/project-service.js +121 -40
  40. package/dist/services/screenshot-server.js +9 -9
  41. package/dist/services/server-manager.js +11 -18
  42. package/dist/services/static-report-generator.js +3 -4
  43. package/dist/services/tdd-service.js +246 -103
  44. package/dist/services/test-runner.js +24 -25
  45. package/dist/services/uploader.js +5 -4
  46. package/dist/types/commands/init.d.ts +1 -2
  47. package/dist/types/index.d.ts +2 -3
  48. package/dist/types/plugin-loader.d.ts +1 -2
  49. package/dist/types/reporter/src/api/client.d.ts +178 -0
  50. package/dist/types/reporter/src/components/app-router.d.ts +1 -3
  51. package/dist/types/reporter/src/components/code-block.d.ts +4 -0
  52. package/dist/types/reporter/src/components/comparison/comparison-modes/onion-skin-mode.d.ts +10 -0
  53. package/dist/types/reporter/src/components/comparison/comparison-modes/overlay-mode.d.ts +11 -0
  54. package/dist/types/reporter/src/components/comparison/comparison-modes/shared/base-comparison-mode.d.ts +14 -0
  55. package/dist/types/reporter/src/components/comparison/comparison-modes/shared/image-renderer.d.ts +30 -0
  56. package/dist/types/reporter/src/components/comparison/comparison-modes/toggle-view.d.ts +8 -0
  57. package/dist/types/reporter/src/components/comparison/comparison-viewer.d.ts +4 -0
  58. package/dist/types/reporter/src/components/comparison/fullscreen-viewer.d.ts +13 -0
  59. package/dist/types/reporter/src/components/comparison/screenshot-display.d.ts +16 -0
  60. package/dist/types/reporter/src/components/comparison/screenshot-list.d.ts +9 -0
  61. package/dist/types/reporter/src/components/comparison/variant-selector.d.ts +1 -1
  62. package/dist/types/reporter/src/components/design-system/alert.d.ts +9 -0
  63. package/dist/types/reporter/src/components/design-system/badge.d.ts +17 -0
  64. package/dist/types/reporter/src/components/design-system/button.d.ts +19 -0
  65. package/dist/types/reporter/src/components/design-system/card.d.ts +31 -0
  66. package/dist/types/reporter/src/components/design-system/empty-state.d.ts +13 -0
  67. package/dist/types/reporter/src/components/design-system/form-controls.d.ts +44 -0
  68. package/dist/types/reporter/src/components/design-system/health-ring.d.ts +7 -0
  69. package/dist/types/reporter/src/components/design-system/index.d.ts +11 -0
  70. package/dist/types/reporter/src/components/design-system/modal.d.ts +10 -0
  71. package/dist/types/reporter/src/components/design-system/skeleton.d.ts +19 -0
  72. package/dist/types/reporter/src/components/design-system/spinner.d.ts +10 -0
  73. package/dist/types/reporter/src/components/design-system/tabs.d.ts +13 -0
  74. package/dist/types/reporter/src/components/layout/header.d.ts +5 -0
  75. package/dist/types/reporter/src/components/layout/index.d.ts +2 -0
  76. package/dist/types/reporter/src/components/layout/layout.d.ts +6 -0
  77. package/dist/types/reporter/src/components/views/builds-view.d.ts +1 -0
  78. package/dist/types/reporter/src/components/views/comparison-detail-view.d.ts +5 -0
  79. package/dist/types/reporter/src/components/views/comparisons-view.d.ts +5 -6
  80. package/dist/types/reporter/src/components/views/stats-view.d.ts +1 -6
  81. package/dist/types/reporter/src/components/waiting-for-screenshots.d.ts +1 -0
  82. package/dist/types/reporter/src/hooks/queries/use-auth-queries.d.ts +15 -0
  83. package/dist/types/reporter/src/hooks/queries/use-cloud-queries.d.ts +6 -0
  84. package/dist/types/reporter/src/hooks/queries/use-config-queries.d.ts +6 -0
  85. package/dist/types/reporter/src/hooks/queries/use-tdd-queries.d.ts +9 -0
  86. package/dist/types/reporter/src/lib/query-client.d.ts +2 -0
  87. package/dist/types/reporter/src/lib/query-keys.d.ts +13 -0
  88. package/dist/types/sdk/index.d.ts +2 -4
  89. package/dist/types/server/handlers/tdd-handler.d.ts +2 -0
  90. package/dist/types/server/http-server.d.ts +1 -1
  91. package/dist/types/server/middleware/cors.d.ts +11 -0
  92. package/dist/types/server/middleware/json-parser.d.ts +10 -0
  93. package/dist/types/server/middleware/response.d.ts +50 -0
  94. package/dist/types/server/routers/assets.d.ts +6 -0
  95. package/dist/types/server/routers/auth.d.ts +9 -0
  96. package/dist/types/server/routers/baseline.d.ts +13 -0
  97. package/dist/types/server/routers/cloud-proxy.d.ts +11 -0
  98. package/dist/types/server/routers/config.d.ts +9 -0
  99. package/dist/types/server/routers/dashboard.d.ts +6 -0
  100. package/dist/types/server/routers/health.d.ts +11 -0
  101. package/dist/types/server/routers/projects.d.ts +9 -0
  102. package/dist/types/server/routers/screenshot.d.ts +11 -0
  103. package/dist/types/services/build-manager.d.ts +4 -3
  104. package/dist/types/services/config-service.d.ts +2 -3
  105. package/dist/types/services/index.d.ts +7 -0
  106. package/dist/types/services/project-service.d.ts +6 -4
  107. package/dist/types/services/screenshot-server.d.ts +5 -5
  108. package/dist/types/services/server-manager.d.ts +5 -3
  109. package/dist/types/services/tdd-service.d.ts +12 -1
  110. package/dist/types/services/test-runner.d.ts +3 -3
  111. package/dist/types/utils/output.d.ts +84 -0
  112. package/dist/utils/config-loader.js +24 -48
  113. package/dist/utils/global-config.js +2 -17
  114. package/dist/utils/output.js +445 -0
  115. package/dist/utils/security.js +3 -4
  116. package/docs/api-reference.md +0 -1
  117. package/docs/plugins.md +22 -22
  118. package/package.json +3 -2
  119. package/dist/container/index.js +0 -215
  120. package/dist/services/base-service.js +0 -154
  121. package/dist/types/container/index.d.ts +0 -59
  122. package/dist/types/reporter/src/components/comparison/viewer-modes/onion-viewer.d.ts +0 -3
  123. package/dist/types/reporter/src/components/comparison/viewer-modes/overlay-viewer.d.ts +0 -3
  124. package/dist/types/reporter/src/components/comparison/viewer-modes/side-by-side-viewer.d.ts +0 -3
  125. package/dist/types/reporter/src/components/comparison/viewer-modes/toggle-viewer.d.ts +0 -3
  126. package/dist/types/reporter/src/components/dashboard/dashboard-header.d.ts +0 -5
  127. package/dist/types/reporter/src/components/dashboard/dashboard-stats.d.ts +0 -4
  128. package/dist/types/reporter/src/components/dashboard/empty-state.d.ts +0 -8
  129. package/dist/types/reporter/src/components/ui/form-field.d.ts +0 -16
  130. package/dist/types/reporter/src/components/ui/status-badge.d.ts +0 -5
  131. package/dist/types/reporter/src/hooks/use-auth.d.ts +0 -10
  132. package/dist/types/reporter/src/hooks/use-baseline-actions.d.ts +0 -5
  133. package/dist/types/reporter/src/hooks/use-config.d.ts +0 -9
  134. package/dist/types/reporter/src/hooks/use-projects.d.ts +0 -10
  135. package/dist/types/reporter/src/hooks/use-report-data.d.ts +0 -7
  136. package/dist/types/reporter/src/hooks/use-vizzly-api.d.ts +0 -9
  137. package/dist/types/services/base-service.d.ts +0 -71
  138. package/dist/types/utils/console-ui.d.ts +0 -61
  139. package/dist/types/utils/logger-factory.d.ts +0 -26
  140. package/dist/types/utils/logger.d.ts +0 -79
  141. package/dist/utils/console-ui.js +0 -241
  142. package/dist/utils/logger-factory.js +0 -76
  143. package/dist/utils/logger.js +0 -231
@@ -2,7 +2,7 @@
2
2
  import fs from 'fs/promises';
3
3
  import path from 'path';
4
4
  import { VizzlyError } from '../errors/vizzly-error.js';
5
- import { createComponentLogger } from '../utils/logger-factory.js';
5
+ import * as output from '../utils/output.js';
6
6
  import { loadPlugins } from '../plugin-loader.js';
7
7
  import { loadConfig } from '../utils/config-loader.js';
8
8
  import { z } from 'zod';
@@ -11,20 +11,18 @@ import { z } from 'zod';
11
11
  * Simple configuration setup for Vizzly CLI
12
12
  */
13
13
  export class InitCommand {
14
- constructor(logger, plugins = []) {
15
- this.logger = logger || createComponentLogger('INIT', {
16
- level: 'info'
17
- });
14
+ constructor(plugins = []) {
18
15
  this.plugins = plugins;
19
16
  }
20
17
  async run(options = {}) {
21
- this.logger.info('🎯 Initializing Vizzly configuration...\n');
18
+ output.info('🎯 Initializing Vizzly configuration...');
19
+ output.blank();
22
20
  try {
23
21
  // Check for existing config
24
- const configPath = path.join(process.cwd(), 'vizzly.config.js');
25
- const hasConfig = await this.fileExists(configPath);
22
+ let configPath = path.join(process.cwd(), 'vizzly.config.js');
23
+ let hasConfig = await this.fileExists(configPath);
26
24
  if (hasConfig && !options.force) {
27
- this.logger.info('❌ A vizzly.config.js file already exists. Use --force to overwrite.');
25
+ output.info('❌ A vizzly.config.js file already exists. Use --force to overwrite.');
28
26
  return;
29
27
  }
30
28
 
@@ -33,7 +31,8 @@ export class InitCommand {
33
31
 
34
32
  // Show next steps
35
33
  this.showNextSteps();
36
- this.logger.info('\n✅ Vizzly CLI setup complete!');
34
+ output.blank();
35
+ output.success('Vizzly CLI setup complete!');
37
36
  } catch (error) {
38
37
  throw new VizzlyError('Failed to initialize Vizzly configuration', 'INIT_FAILED', {
39
38
  error: error.message
@@ -78,14 +77,14 @@ export class InitCommand {
78
77
  }
79
78
  coreConfig += '\n};\n';
80
79
  await fs.writeFile(configPath, coreConfig, 'utf8');
81
- this.logger.info(`📄 Created vizzly.config.js`);
80
+ output.info(`📄 Created vizzly.config.js`);
82
81
 
83
82
  // Log discovered plugins
84
83
  let pluginsWithConfig = this.plugins.filter(p => p.configSchema);
85
84
  if (pluginsWithConfig.length > 0) {
86
- this.logger.info(` Added config for ${pluginsWithConfig.length} plugin(s):`);
85
+ output.info(` Added config for ${pluginsWithConfig.length} plugin(s):`);
87
86
  pluginsWithConfig.forEach(p => {
88
- this.logger.info(` - ${p.name}`);
87
+ output.info(` - ${p.name}`);
89
88
  });
90
89
  }
91
90
  }
@@ -127,9 +126,9 @@ export class InitCommand {
127
126
  } catch (error) {
128
127
  if (error instanceof z.ZodError) {
129
128
  let messages = error.errors.map(e => `${e.path.join('.')}: ${e.message}`);
130
- this.logger.warn(`Invalid config schema for plugin ${plugin.name}: ${messages.join(', ')}`);
129
+ output.warn(`Invalid config schema for plugin ${plugin.name}: ${messages.join(', ')}`);
131
130
  } else {
132
- this.logger.warn(`Failed to format config for plugin ${plugin.name}: ${error.message}`);
131
+ output.warn(`Failed to format config for plugin ${plugin.name}: ${error.message}`);
133
132
  }
134
133
  return '';
135
134
  }
@@ -168,13 +167,14 @@ export class InitCommand {
168
167
  return String(value);
169
168
  }
170
169
  showNextSteps() {
171
- this.logger.info('\n📚 Next steps:');
172
- this.logger.info(' 1. Set your API token:');
173
- this.logger.info(' export VIZZLY_TOKEN="your-api-key"');
174
- this.logger.info(' 2. Run your tests with Vizzly:');
175
- this.logger.info(' npx vizzly run "npm test"');
176
- this.logger.info(' 3. Upload screenshots:');
177
- this.logger.info(' npx vizzly upload ./screenshots');
170
+ output.blank();
171
+ output.info('📚 Next steps:');
172
+ output.info(' 1. Set your API token:');
173
+ output.info(' export VIZZLY_TOKEN="your-api-key"');
174
+ output.info(' 2. Run your tests with Vizzly:');
175
+ output.info(' npx vizzly run "npm test"');
176
+ output.info(' 3. Upload screenshots:');
177
+ output.info(' npx vizzly upload ./screenshots');
178
178
  }
179
179
  async fileExists(filePath) {
180
180
  try {
@@ -188,28 +188,30 @@ export class InitCommand {
188
188
 
189
189
  // Export factory function for CLI
190
190
  export function createInitCommand(options) {
191
- const command = new InitCommand(options.logger, options.plugins);
191
+ let command = new InitCommand(options.plugins);
192
192
  return () => command.run(options);
193
193
  }
194
194
 
195
195
  // Simple export for direct CLI usage
196
196
  export async function init(options = {}) {
197
+ output.configure({
198
+ json: options.json || false,
199
+ verbose: options.verbose || false,
200
+ color: options.color !== false
201
+ });
197
202
  let plugins = [];
198
203
 
199
204
  // Try to load plugins if not provided
200
205
  if (!options.plugins) {
201
206
  try {
202
207
  let config = await loadConfig(options.config, {});
203
- let logger = createComponentLogger('INIT', {
204
- level: 'debug'
205
- });
206
- plugins = await loadPlugins(options.config, config, logger);
208
+ plugins = await loadPlugins(options.config, config, null);
207
209
  } catch {
208
210
  // Silent fail - plugins are optional for init
209
211
  }
210
212
  } else {
211
213
  plugins = options.plugins;
212
214
  }
213
- const command = new InitCommand(null, plugins);
215
+ let command = new InitCommand(plugins);
214
216
  return await command.run(options);
215
217
  }
@@ -3,7 +3,7 @@
3
3
  * Authenticates user via OAuth device flow
4
4
  */
5
5
 
6
- import { ConsoleUI } from '../utils/console-ui.js';
6
+ import * as output from '../utils/output.js';
7
7
  import { AuthService } from '../services/auth-service.js';
8
8
  import { getApiUrl } from '../utils/environment-config.js';
9
9
  import { openBrowser } from '../utils/browser.js';
@@ -14,15 +14,15 @@ import { openBrowser } from '../utils/browser.js';
14
14
  * @param {Object} globalOptions - Global CLI options
15
15
  */
16
16
  export async function loginCommand(options = {}, globalOptions = {}) {
17
- // Create UI handler
18
- let ui = new ConsoleUI({
17
+ output.configure({
19
18
  json: globalOptions.json,
20
19
  verbose: globalOptions.verbose,
21
20
  color: !globalOptions.noColor
22
21
  });
22
+ let colors = output.getColors();
23
23
  try {
24
- ui.info('Starting Vizzly authentication...');
25
- console.log(''); // Empty line for spacing
24
+ output.info('Starting Vizzly authentication...');
25
+ output.blank();
26
26
 
27
27
  // Create auth service
28
28
  let authService = new AuthService({
@@ -30,9 +30,9 @@ export async function loginCommand(options = {}, globalOptions = {}) {
30
30
  });
31
31
 
32
32
  // Initiate device flow
33
- ui.startSpinner('Connecting to Vizzly...');
33
+ output.startSpinner('Connecting to Vizzly...');
34
34
  let deviceFlow = await authService.initiateDeviceFlow();
35
- ui.stopSpinner();
35
+ output.stopSpinner();
36
36
 
37
37
  // Handle both snake_case and camelCase field names
38
38
  let verificationUri = deviceFlow.verification_uri || deviceFlow.verificationUri;
@@ -46,29 +46,29 @@ export async function loginCommand(options = {}, globalOptions = {}) {
46
46
  let urlWithCode = `${verificationUri}?code=${userCode}`;
47
47
 
48
48
  // Display user code prominently
49
- console.log(''); // Empty line for spacing
50
- console.log('='.repeat(50));
51
- console.log('');
52
- console.log(' Please visit the following URL to authorize this device:');
53
- console.log('');
54
- console.log(` ${urlWithCode}`);
55
- console.log('');
56
- console.log(' Your code (pre-filled):');
57
- console.log('');
58
- console.log(` ${ui.colors.bold(ui.colors.cyan(userCode))}`);
59
- console.log('');
60
- console.log('='.repeat(50));
61
- console.log(''); // Empty line for spacing
49
+ output.blank();
50
+ output.print('='.repeat(50));
51
+ output.blank();
52
+ output.print(' Please visit the following URL to authorize this device:');
53
+ output.blank();
54
+ output.print(` ${urlWithCode}`);
55
+ output.blank();
56
+ output.print(' Your code (pre-filled):');
57
+ output.blank();
58
+ output.print(` ${colors.bold(colors.cyan(userCode))}`);
59
+ output.blank();
60
+ output.print('='.repeat(50));
61
+ output.blank();
62
62
 
63
63
  // Try to open browser with pre-filled code
64
64
  let browserOpened = await openBrowser(urlWithCode);
65
65
  if (browserOpened) {
66
- ui.info('Opening browser...');
66
+ output.info('Opening browser...');
67
67
  } else {
68
- ui.warning('Could not open browser automatically. Please open the URL manually.');
68
+ output.warn('Could not open browser automatically. Please open the URL manually.');
69
69
  }
70
- console.log(''); // Empty line for spacing
71
- ui.info('After authorizing in your browser, press Enter to continue...');
70
+ output.blank();
71
+ output.info('After authorizing in your browser, press Enter to continue...');
72
72
 
73
73
  // Wait for user to press Enter
74
74
  await new Promise(resolve => {
@@ -82,9 +82,9 @@ export async function loginCommand(options = {}, globalOptions = {}) {
82
82
  });
83
83
 
84
84
  // Check authorization status
85
- ui.startSpinner('Checking authorization status...');
85
+ output.startSpinner('Checking authorization status...');
86
86
  let pollResponse = await authService.pollDeviceAuthorization(deviceCode);
87
- ui.stopSpinner();
87
+ output.stopSpinner();
88
88
  let tokenData = null;
89
89
 
90
90
  // Check if authorization was successful by looking for tokens
@@ -116,66 +116,60 @@ export async function loginCommand(options = {}, globalOptions = {}) {
116
116
  await authService.completeDeviceFlow(tokens);
117
117
 
118
118
  // Display success message
119
- ui.success('Successfully authenticated!');
120
- console.log(''); // Empty line for spacing
119
+ output.success('Successfully authenticated!');
120
+ output.blank();
121
121
 
122
122
  // Show user info
123
123
  if (tokens.user) {
124
- ui.info(`User: ${tokens.user.name || tokens.user.username}`);
125
- ui.info(`Email: ${tokens.user.email}`);
124
+ output.info(`User: ${tokens.user.name || tokens.user.username}`);
125
+ output.info(`Email: ${tokens.user.email}`);
126
126
  }
127
127
 
128
128
  // Show organization info
129
129
  if (tokens.organizations && tokens.organizations.length > 0) {
130
- console.log(''); // Empty line for spacing
131
- ui.info('Organizations:');
130
+ output.blank();
131
+ output.info('Organizations:');
132
132
  for (let org of tokens.organizations) {
133
- console.log(` - ${org.name}${org.slug ? ` (@${org.slug})` : ''}`);
133
+ output.print(` - ${org.name}${org.slug ? ` (@${org.slug})` : ''}`);
134
134
  }
135
135
  }
136
136
 
137
137
  // Show token expiry info
138
138
  if (tokens.expiresAt) {
139
- console.log(''); // Empty line for spacing
139
+ output.blank();
140
140
  let expiresAt = new Date(tokens.expiresAt);
141
141
  let msUntilExpiry = expiresAt.getTime() - Date.now();
142
142
  let daysUntilExpiry = Math.floor(msUntilExpiry / (1000 * 60 * 60 * 24));
143
143
  let hoursUntilExpiry = Math.floor(msUntilExpiry / (1000 * 60 * 60));
144
144
  let minutesUntilExpiry = Math.floor(msUntilExpiry / (1000 * 60));
145
145
  if (daysUntilExpiry > 0) {
146
- ui.info(`Token expires in ${daysUntilExpiry} day${daysUntilExpiry !== 1 ? 's' : ''} (${expiresAt.toLocaleDateString()})`);
146
+ output.info(`Token expires in ${daysUntilExpiry} day${daysUntilExpiry !== 1 ? 's' : ''} (${expiresAt.toLocaleDateString()})`);
147
147
  } else if (hoursUntilExpiry > 0) {
148
- ui.info(`Token expires in ${hoursUntilExpiry} hour${hoursUntilExpiry !== 1 ? 's' : ''}`);
148
+ output.info(`Token expires in ${hoursUntilExpiry} hour${hoursUntilExpiry !== 1 ? 's' : ''}`);
149
149
  } else if (minutesUntilExpiry > 0) {
150
- ui.info(`Token expires in ${minutesUntilExpiry} minute${minutesUntilExpiry !== 1 ? 's' : ''}`);
150
+ output.info(`Token expires in ${minutesUntilExpiry} minute${minutesUntilExpiry !== 1 ? 's' : ''}`);
151
151
  }
152
152
  }
153
- console.log(''); // Empty line for spacing
154
- ui.info('You can now use Vizzly CLI commands without setting VIZZLY_TOKEN');
155
- ui.cleanup();
153
+ output.blank();
154
+ output.info('You can now use Vizzly CLI commands without setting VIZZLY_TOKEN');
155
+ output.cleanup();
156
156
  } catch (error) {
157
- ui.stopSpinner();
157
+ output.stopSpinner();
158
158
 
159
159
  // Handle authentication errors with helpful messages
160
160
  if (error.name === 'AuthError') {
161
- ui.error('Authentication failed', error, 0);
162
- console.log(''); // Empty line for spacing
163
- console.log('Please try logging in again.');
164
- console.log("If you don't have an account, sign up at https://vizzly.dev");
161
+ output.error('Authentication failed', error);
162
+ output.blank();
163
+ output.print('Please try logging in again.');
164
+ output.print("If you don't have an account, sign up at https://vizzly.dev");
165
165
  process.exit(1);
166
166
  } else if (error.code === 'RATE_LIMIT_ERROR') {
167
- ui.error('Too many login attempts', error, 0);
168
- console.log(''); // Empty line for spacing
169
- console.log('Please wait a few minutes before trying again.');
167
+ output.error('Too many login attempts', error);
168
+ output.blank();
169
+ output.print('Please wait a few minutes before trying again.');
170
170
  process.exit(1);
171
171
  } else {
172
- ui.error('Login failed', error, 0);
173
- console.log(''); // Empty line for spacing
174
- console.log('Error details:', error.message);
175
- if (globalOptions.verbose && error.stack) {
176
- console.error(''); // Empty line for spacing
177
- console.error(error.stack);
178
- }
172
+ output.error('Login failed', error);
179
173
  process.exit(1);
180
174
  }
181
175
  }
@@ -3,7 +3,7 @@
3
3
  * Clears stored authentication tokens
4
4
  */
5
5
 
6
- import { ConsoleUI } from '../utils/console-ui.js';
6
+ import * as output from '../utils/output.js';
7
7
  import { AuthService } from '../services/auth-service.js';
8
8
  import { getApiUrl } from '../utils/environment-config.js';
9
9
  import { getAuthTokens } from '../utils/global-config.js';
@@ -14,8 +14,7 @@ import { getAuthTokens } from '../utils/global-config.js';
14
14
  * @param {Object} globalOptions - Global CLI options
15
15
  */
16
16
  export async function logoutCommand(options = {}, globalOptions = {}) {
17
- // Create UI handler
18
- let ui = new ConsoleUI({
17
+ output.configure({
19
18
  json: globalOptions.json,
20
19
  verbose: globalOptions.verbose,
21
20
  color: !globalOptions.noColor
@@ -24,36 +23,32 @@ export async function logoutCommand(options = {}, globalOptions = {}) {
24
23
  // Check if user is logged in
25
24
  let auth = await getAuthTokens();
26
25
  if (!auth || !auth.accessToken) {
27
- ui.info('You are not logged in');
28
- ui.cleanup();
26
+ output.info('You are not logged in');
27
+ output.cleanup();
29
28
  return;
30
29
  }
31
30
 
32
31
  // Logout
33
- ui.startSpinner('Logging out...');
32
+ output.startSpinner('Logging out...');
34
33
  let authService = new AuthService({
35
34
  baseUrl: options.apiUrl || getApiUrl()
36
35
  });
37
36
  await authService.logout();
38
- ui.stopSpinner();
39
- ui.success('Successfully logged out');
37
+ output.stopSpinner();
38
+ output.success('Successfully logged out');
40
39
  if (globalOptions.json) {
41
- ui.data({
40
+ output.data({
42
41
  loggedOut: true
43
42
  });
44
43
  } else {
45
- console.log(''); // Empty line for spacing
46
- ui.info('Your authentication tokens have been cleared');
47
- ui.info('Run "vizzly login" to authenticate again');
44
+ output.blank();
45
+ output.info('Your authentication tokens have been cleared');
46
+ output.info('Run "vizzly login" to authenticate again');
48
47
  }
49
- ui.cleanup();
48
+ output.cleanup();
50
49
  } catch (error) {
51
- ui.stopSpinner();
52
- ui.error('Logout failed', error, 0);
53
- if (globalOptions.verbose && error.stack) {
54
- console.error(''); // Empty line for spacing
55
- console.error(error.stack);
56
- }
50
+ output.stopSpinner();
51
+ output.error('Logout failed', error);
57
52
  process.exit(1);
58
53
  }
59
54
  }