@vizzly-testing/cli 0.14.0 → 0.15.1
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/dist/cli.js +70 -68
- package/dist/commands/doctor.js +30 -34
- package/dist/commands/finalize.js +24 -23
- package/dist/commands/init.js +30 -28
- package/dist/commands/login.js +49 -55
- package/dist/commands/logout.js +14 -19
- package/dist/commands/project.js +83 -103
- package/dist/commands/run.js +77 -89
- package/dist/commands/status.js +48 -49
- package/dist/commands/tdd-daemon.js +90 -86
- package/dist/commands/tdd.js +59 -88
- package/dist/commands/upload.js +57 -57
- package/dist/commands/whoami.js +40 -45
- package/dist/index.js +2 -5
- package/dist/plugin-loader.js +15 -17
- package/dist/reporter/reporter-bundle.css +1 -1
- package/dist/reporter/reporter-bundle.iife.js +74 -41
- package/dist/sdk/index.js +36 -45
- package/dist/server/handlers/api-handler.js +14 -15
- package/dist/server/handlers/tdd-handler.js +34 -37
- package/dist/server/http-server.js +75 -869
- package/dist/server/middleware/cors.js +22 -0
- package/dist/server/middleware/json-parser.js +35 -0
- package/dist/server/middleware/response.js +79 -0
- package/dist/server/routers/assets.js +91 -0
- package/dist/server/routers/auth.js +144 -0
- package/dist/server/routers/baseline.js +163 -0
- package/dist/server/routers/cloud-proxy.js +146 -0
- package/dist/server/routers/config.js +126 -0
- package/dist/server/routers/dashboard.js +130 -0
- package/dist/server/routers/health.js +61 -0
- package/dist/server/routers/projects.js +168 -0
- package/dist/server/routers/screenshot.js +86 -0
- package/dist/services/auth-service.js +1 -1
- package/dist/services/build-manager.js +13 -40
- package/dist/services/config-service.js +2 -4
- package/dist/services/html-report-generator.js +6 -5
- package/dist/services/index.js +64 -0
- package/dist/services/project-service.js +121 -40
- package/dist/services/screenshot-server.js +9 -9
- package/dist/services/server-manager.js +11 -18
- package/dist/services/static-report-generator.js +3 -4
- package/dist/services/tdd-service.js +246 -103
- package/dist/services/test-runner.js +24 -25
- package/dist/services/uploader.js +5 -4
- package/dist/types/commands/init.d.ts +1 -2
- package/dist/types/index.d.ts +2 -3
- package/dist/types/plugin-loader.d.ts +1 -2
- package/dist/types/reporter/src/api/client.d.ts +178 -0
- package/dist/types/reporter/src/components/app-router.d.ts +1 -3
- package/dist/types/reporter/src/components/code-block.d.ts +4 -0
- package/dist/types/reporter/src/components/comparison/comparison-modes/onion-skin-mode.d.ts +10 -0
- package/dist/types/reporter/src/components/comparison/comparison-modes/overlay-mode.d.ts +11 -0
- package/dist/types/reporter/src/components/comparison/comparison-modes/shared/base-comparison-mode.d.ts +14 -0
- package/dist/types/reporter/src/components/comparison/comparison-modes/shared/image-renderer.d.ts +30 -0
- package/dist/types/reporter/src/components/comparison/comparison-modes/toggle-view.d.ts +8 -0
- package/dist/types/reporter/src/components/comparison/comparison-viewer.d.ts +4 -0
- package/dist/types/reporter/src/components/comparison/screenshot-display.d.ts +16 -0
- package/dist/types/reporter/src/components/design-system/alert.d.ts +9 -0
- package/dist/types/reporter/src/components/design-system/badge.d.ts +17 -0
- package/dist/types/reporter/src/components/design-system/button.d.ts +19 -0
- package/dist/types/reporter/src/components/design-system/card.d.ts +31 -0
- package/dist/types/reporter/src/components/design-system/empty-state.d.ts +13 -0
- package/dist/types/reporter/src/components/design-system/form-controls.d.ts +44 -0
- package/dist/types/reporter/src/components/design-system/health-ring.d.ts +7 -0
- package/dist/types/reporter/src/components/design-system/index.d.ts +11 -0
- package/dist/types/reporter/src/components/design-system/modal.d.ts +10 -0
- package/dist/types/reporter/src/components/design-system/skeleton.d.ts +19 -0
- package/dist/types/reporter/src/components/design-system/spinner.d.ts +10 -0
- package/dist/types/reporter/src/components/design-system/tabs.d.ts +13 -0
- package/dist/types/reporter/src/components/layout/header.d.ts +5 -0
- package/dist/types/reporter/src/components/layout/index.d.ts +2 -0
- package/dist/types/reporter/src/components/layout/layout.d.ts +6 -0
- package/dist/types/reporter/src/components/views/builds-view.d.ts +1 -0
- package/dist/types/reporter/src/components/views/comparison-detail-view.d.ts +1 -4
- package/dist/types/reporter/src/components/views/comparisons-view.d.ts +1 -6
- package/dist/types/reporter/src/components/views/stats-view.d.ts +1 -6
- package/dist/types/reporter/src/components/waiting-for-screenshots.d.ts +1 -0
- package/dist/types/reporter/src/hooks/queries/use-auth-queries.d.ts +15 -0
- package/dist/types/reporter/src/hooks/queries/use-cloud-queries.d.ts +6 -0
- package/dist/types/reporter/src/hooks/queries/use-config-queries.d.ts +6 -0
- package/dist/types/reporter/src/hooks/queries/use-tdd-queries.d.ts +9 -0
- package/dist/types/reporter/src/lib/query-client.d.ts +2 -0
- package/dist/types/reporter/src/lib/query-keys.d.ts +13 -0
- package/dist/types/sdk/index.d.ts +2 -4
- package/dist/types/server/handlers/tdd-handler.d.ts +2 -0
- package/dist/types/server/http-server.d.ts +1 -1
- package/dist/types/server/middleware/cors.d.ts +11 -0
- package/dist/types/server/middleware/json-parser.d.ts +10 -0
- package/dist/types/server/middleware/response.d.ts +50 -0
- package/dist/types/server/routers/assets.d.ts +6 -0
- package/dist/types/server/routers/auth.d.ts +9 -0
- package/dist/types/server/routers/baseline.d.ts +13 -0
- package/dist/types/server/routers/cloud-proxy.d.ts +11 -0
- package/dist/types/server/routers/config.d.ts +9 -0
- package/dist/types/server/routers/dashboard.d.ts +6 -0
- package/dist/types/server/routers/health.d.ts +11 -0
- package/dist/types/server/routers/projects.d.ts +9 -0
- package/dist/types/server/routers/screenshot.d.ts +11 -0
- package/dist/types/services/build-manager.d.ts +4 -3
- package/dist/types/services/config-service.d.ts +2 -3
- package/dist/types/services/index.d.ts +7 -0
- package/dist/types/services/project-service.d.ts +6 -4
- package/dist/types/services/screenshot-server.d.ts +5 -5
- package/dist/types/services/server-manager.d.ts +5 -3
- package/dist/types/services/tdd-service.d.ts +12 -1
- package/dist/types/services/test-runner.d.ts +3 -3
- package/dist/types/utils/output.d.ts +84 -0
- package/dist/utils/config-loader.js +24 -48
- package/dist/utils/global-config.js +2 -17
- package/dist/utils/output.js +445 -0
- package/dist/utils/security.js +3 -4
- package/docs/api-reference.md +0 -1
- package/docs/plugins.md +33 -34
- package/package.json +3 -2
- package/dist/container/index.js +0 -215
- package/dist/services/base-service.js +0 -154
- package/dist/types/container/index.d.ts +0 -59
- package/dist/types/reporter/src/components/comparison/viewer-modes/onion-viewer.d.ts +0 -3
- package/dist/types/reporter/src/components/comparison/viewer-modes/overlay-viewer.d.ts +0 -3
- package/dist/types/reporter/src/components/comparison/viewer-modes/side-by-side-viewer.d.ts +0 -3
- package/dist/types/reporter/src/components/comparison/viewer-modes/toggle-viewer.d.ts +0 -3
- package/dist/types/reporter/src/components/dashboard/dashboard-header.d.ts +0 -5
- package/dist/types/reporter/src/components/dashboard/dashboard-stats.d.ts +0 -4
- package/dist/types/reporter/src/components/dashboard/empty-state.d.ts +0 -8
- package/dist/types/reporter/src/components/ui/form-field.d.ts +0 -16
- package/dist/types/reporter/src/components/ui/status-badge.d.ts +0 -5
- package/dist/types/reporter/src/hooks/use-auth.d.ts +0 -10
- package/dist/types/reporter/src/hooks/use-baseline-actions.d.ts +0 -5
- package/dist/types/reporter/src/hooks/use-config.d.ts +0 -9
- package/dist/types/reporter/src/hooks/use-projects.d.ts +0 -10
- package/dist/types/reporter/src/hooks/use-report-data.d.ts +0 -7
- package/dist/types/reporter/src/hooks/use-vizzly-api.d.ts +0 -9
- package/dist/types/services/base-service.d.ts +0 -71
- package/dist/types/utils/console-ui.d.ts +0 -61
- package/dist/types/utils/logger-factory.d.ts +0 -26
- package/dist/types/utils/logger.d.ts +0 -79
- package/dist/utils/console-ui.js +0 -241
- package/dist/utils/logger-factory.js +0 -76
- package/dist/utils/logger.js +0 -231
package/docs/plugins.md
CHANGED
|
@@ -13,7 +13,7 @@ file.
|
|
|
13
13
|
## Benefits
|
|
14
14
|
|
|
15
15
|
- **Zero Configuration** - Just `npm install` and the plugin is available
|
|
16
|
-
- **Shared Infrastructure** - Plugins get access to config,
|
|
16
|
+
- **Shared Infrastructure** - Plugins get access to config, output utilities, and services
|
|
17
17
|
- **Independent Releases** - Plugins can iterate without requiring CLI updates
|
|
18
18
|
- **Smaller Core** - Keep the main CLI lean by moving optional features to plugins
|
|
19
19
|
- **Community Extensible** - Anyone can build and share plugins
|
|
@@ -95,14 +95,14 @@ export default {
|
|
|
95
95
|
name: 'my-plugin',
|
|
96
96
|
version: '1.0.0', // Optional but recommended
|
|
97
97
|
|
|
98
|
-
register(program, { config,
|
|
98
|
+
register(program, { config, output, services }) {
|
|
99
99
|
// Register your command with Commander.js
|
|
100
100
|
program
|
|
101
101
|
.command('my-command <arg>')
|
|
102
102
|
.description('Description of my command')
|
|
103
103
|
.option('--option <value>', 'An option')
|
|
104
104
|
.action(async (arg, options) => {
|
|
105
|
-
|
|
105
|
+
output.info(`Running my-command with ${arg}`);
|
|
106
106
|
|
|
107
107
|
// Access shared services if needed
|
|
108
108
|
let apiService = await services.get('apiService');
|
|
@@ -131,14 +131,13 @@ The `register` function receives two arguments:
|
|
|
131
131
|
1. **`program`** - [Commander.js](https://github.com/tj/commander.js) program instance for registering commands
|
|
132
132
|
2. **`context`** - Object containing:
|
|
133
133
|
- `config` - Merged Vizzly configuration object
|
|
134
|
-
- `
|
|
134
|
+
- `output` - Unified output module with `.debug()`, `.info()`, `.warn()`, `.error()`, `.success()` methods
|
|
135
135
|
- `services` - Service container with access to internal Vizzly services
|
|
136
136
|
|
|
137
137
|
### Available Services
|
|
138
138
|
|
|
139
139
|
Plugins can access these services from the container:
|
|
140
140
|
|
|
141
|
-
- **`logger`** - Component logger for consistent output
|
|
142
141
|
- **`apiService`** - Vizzly API client for interacting with the platform
|
|
143
142
|
- **`uploader`** - Screenshot upload service
|
|
144
143
|
- **`buildManager`** - Build lifecycle management
|
|
@@ -149,7 +148,7 @@ Plugins can access these services from the container:
|
|
|
149
148
|
Example accessing a service:
|
|
150
149
|
|
|
151
150
|
```javascript
|
|
152
|
-
register(program, { config,
|
|
151
|
+
register(program, { config, output, services }) {
|
|
153
152
|
program
|
|
154
153
|
.command('upload-screenshots <dir>')
|
|
155
154
|
.action(async (dir) => {
|
|
@@ -177,12 +176,12 @@ touch plugins/my-plugin.js
|
|
|
177
176
|
export default {
|
|
178
177
|
name: 'my-plugin',
|
|
179
178
|
version: '1.0.0',
|
|
180
|
-
register(program, { config,
|
|
179
|
+
register(program, { config, output }) {
|
|
181
180
|
program
|
|
182
181
|
.command('greet <name>')
|
|
183
182
|
.description('Greet someone')
|
|
184
183
|
.action((name) => {
|
|
185
|
-
|
|
184
|
+
output.info(`Hello, ${name}!`);
|
|
186
185
|
});
|
|
187
186
|
}
|
|
188
187
|
};
|
|
@@ -255,33 +254,34 @@ export default {
|
|
|
255
254
|
Always handle errors gracefully and provide helpful error messages:
|
|
256
255
|
|
|
257
256
|
```javascript
|
|
258
|
-
register(program, {
|
|
257
|
+
register(program, { output }) {
|
|
259
258
|
program
|
|
260
259
|
.command('process <file>')
|
|
261
260
|
.action(async (file) => {
|
|
262
261
|
try {
|
|
263
262
|
if (!existsSync(file)) {
|
|
264
|
-
|
|
263
|
+
output.error(`File not found: ${file}`);
|
|
265
264
|
process.exit(1);
|
|
266
265
|
}
|
|
267
266
|
// Process file...
|
|
268
267
|
} catch (error) {
|
|
269
|
-
|
|
268
|
+
output.error(`Failed to process file: ${error.message}`);
|
|
270
269
|
process.exit(1);
|
|
271
270
|
}
|
|
272
271
|
});
|
|
273
272
|
}
|
|
274
273
|
```
|
|
275
274
|
|
|
276
|
-
###
|
|
275
|
+
### Output
|
|
277
276
|
|
|
278
|
-
Use the provided
|
|
277
|
+
Use the provided output module for consistent CLI output:
|
|
279
278
|
|
|
280
279
|
```javascript
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
280
|
+
output.debug('Detailed debug info'); // Only shown with --verbose (stderr)
|
|
281
|
+
output.info('Normal information'); // Info messages (stdout)
|
|
282
|
+
output.success('Completed!'); // Success messages (stdout)
|
|
283
|
+
output.warn('Warning message'); // Warning messages (stderr)
|
|
284
|
+
output.error('Error message'); // Error messages (stderr)
|
|
285
285
|
```
|
|
286
286
|
|
|
287
287
|
### Async Operations
|
|
@@ -292,7 +292,7 @@ Use async/await for asynchronous operations:
|
|
|
292
292
|
.action(async (options) => {
|
|
293
293
|
let service = await services.get('apiService');
|
|
294
294
|
let result = await service.doSomething();
|
|
295
|
-
|
|
295
|
+
output.info(`Result: ${result}`);
|
|
296
296
|
});
|
|
297
297
|
```
|
|
298
298
|
|
|
@@ -318,13 +318,13 @@ Validate user input and provide helpful error messages:
|
|
|
318
318
|
```javascript
|
|
319
319
|
.action(async (path, options) => {
|
|
320
320
|
if (!path) {
|
|
321
|
-
|
|
321
|
+
output.error('Path is required');
|
|
322
322
|
process.exit(1);
|
|
323
323
|
}
|
|
324
324
|
|
|
325
325
|
if (!existsSync(path)) {
|
|
326
|
-
|
|
327
|
-
|
|
326
|
+
output.error(`Path not found: ${path}`);
|
|
327
|
+
output.info('Please provide a valid path to your build directory');
|
|
328
328
|
process.exit(1);
|
|
329
329
|
}
|
|
330
330
|
|
|
@@ -337,7 +337,7 @@ Validate user input and provide helpful error messages:
|
|
|
337
337
|
Import heavy dependencies only when needed to keep CLI startup fast:
|
|
338
338
|
|
|
339
339
|
```javascript
|
|
340
|
-
register(program, {
|
|
340
|
+
register(program, { output }) {
|
|
341
341
|
program
|
|
342
342
|
.command('process-images <dir>')
|
|
343
343
|
.action(async (dir) => {
|
|
@@ -356,7 +356,7 @@ register(program, { logger }) {
|
|
|
356
356
|
export default {
|
|
357
357
|
name: 'hello',
|
|
358
358
|
version: '1.0.0',
|
|
359
|
-
register(program, {
|
|
359
|
+
register(program, { output }) {
|
|
360
360
|
program
|
|
361
361
|
.command('hello <name>')
|
|
362
362
|
.description('Say hello')
|
|
@@ -366,7 +366,7 @@ export default {
|
|
|
366
366
|
if (options.loud) {
|
|
367
367
|
greeting = greeting.toUpperCase();
|
|
368
368
|
}
|
|
369
|
-
|
|
369
|
+
output.info(greeting);
|
|
370
370
|
});
|
|
371
371
|
}
|
|
372
372
|
};
|
|
@@ -378,13 +378,13 @@ export default {
|
|
|
378
378
|
export default {
|
|
379
379
|
name: 'storybook',
|
|
380
380
|
version: '1.0.0',
|
|
381
|
-
register(program, { config,
|
|
381
|
+
register(program, { config, output, services }) {
|
|
382
382
|
program
|
|
383
383
|
.command('storybook <path>')
|
|
384
384
|
.description('Capture screenshots from Storybook build')
|
|
385
385
|
.option('--viewports <list>', 'Comma-separated viewports', '1280x720')
|
|
386
386
|
.action(async (path, options) => {
|
|
387
|
-
|
|
387
|
+
output.info(`Crawling Storybook at ${path}`);
|
|
388
388
|
|
|
389
389
|
// Import dependencies lazily
|
|
390
390
|
let { crawlStorybook } = await import('./crawler.js');
|
|
@@ -392,16 +392,15 @@ export default {
|
|
|
392
392
|
// Capture screenshots
|
|
393
393
|
let screenshots = await crawlStorybook(path, {
|
|
394
394
|
viewports: options.viewports.split(','),
|
|
395
|
-
logger,
|
|
396
395
|
});
|
|
397
396
|
|
|
398
|
-
|
|
397
|
+
output.info(`Captured ${screenshots.length} screenshots`);
|
|
399
398
|
|
|
400
399
|
// Upload using Vizzly's uploader service
|
|
401
400
|
let uploader = await services.get('uploader');
|
|
402
401
|
await uploader.uploadScreenshots(screenshots);
|
|
403
402
|
|
|
404
|
-
|
|
403
|
+
output.success('Upload complete!');
|
|
405
404
|
});
|
|
406
405
|
}
|
|
407
406
|
};
|
|
@@ -413,7 +412,7 @@ export default {
|
|
|
413
412
|
export default {
|
|
414
413
|
name: 'reports',
|
|
415
414
|
version: '1.0.0',
|
|
416
|
-
register(program, {
|
|
415
|
+
register(program, { output }) {
|
|
417
416
|
let reports = program
|
|
418
417
|
.command('reports')
|
|
419
418
|
.description('Report generation commands');
|
|
@@ -422,14 +421,14 @@ export default {
|
|
|
422
421
|
.command('generate')
|
|
423
422
|
.description('Generate a new report')
|
|
424
423
|
.action(() => {
|
|
425
|
-
|
|
424
|
+
output.info('Generating report...');
|
|
426
425
|
});
|
|
427
426
|
|
|
428
427
|
reports
|
|
429
428
|
.command('list')
|
|
430
429
|
.description('List all reports')
|
|
431
430
|
.action(() => {
|
|
432
|
-
|
|
431
|
+
output.info('Listing reports...');
|
|
433
432
|
});
|
|
434
433
|
}
|
|
435
434
|
};
|
|
@@ -474,10 +473,10 @@ If you're using TypeScript or want better IDE support, you can add JSDoc types:
|
|
|
474
473
|
* @param {import('commander').Command} program
|
|
475
474
|
* @param {Object} context
|
|
476
475
|
* @param {Object} context.config
|
|
477
|
-
* @param {Object} context.
|
|
476
|
+
* @param {Object} context.output
|
|
478
477
|
* @param {Object} context.services
|
|
479
478
|
*/
|
|
480
|
-
function register(program, { config,
|
|
479
|
+
function register(program, { config, output, services }) {
|
|
481
480
|
// Your plugin code with full autocomplete!
|
|
482
481
|
}
|
|
483
482
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vizzly-testing/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"description": "Visual review platform for UI developers and designers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"visual-testing",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"cosmiconfig": "^9.0.0",
|
|
85
85
|
"dotenv": "^17.2.1",
|
|
86
86
|
"form-data": "^4.0.0",
|
|
87
|
-
"glob": "^
|
|
87
|
+
"glob": "^13.0.0",
|
|
88
88
|
"zod": "^4.1.12"
|
|
89
89
|
},
|
|
90
90
|
"devDependencies": {
|
|
@@ -98,6 +98,7 @@
|
|
|
98
98
|
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
99
99
|
"@playwright/test": "^1.55.1",
|
|
100
100
|
"@tailwindcss/postcss": "^4.1.13",
|
|
101
|
+
"@tanstack/react-query": "^5.90.11",
|
|
101
102
|
"@vitejs/plugin-react": "^5.0.3",
|
|
102
103
|
"@vitest/coverage-v8": "^4.0.3",
|
|
103
104
|
"autoprefixer": "^10.4.21",
|
package/dist/container/index.js
DELETED
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'events';
|
|
2
|
-
import { VizzlyError } from '../errors/vizzly-error.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @typedef {Object} ServiceDefinition
|
|
6
|
-
* @property {Function} factory - Factory function to create service instance
|
|
7
|
-
* @property {boolean} [singleton=true] - Whether to cache the instance
|
|
8
|
-
* @property {string[]} [dependencies=[]] - Array of dependency names
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Service container for dependency injection and lifecycle management
|
|
13
|
-
*/
|
|
14
|
-
export class ServiceContainer extends EventEmitter {
|
|
15
|
-
constructor() {
|
|
16
|
-
super();
|
|
17
|
-
this.services = new Map();
|
|
18
|
-
this.instances = new Map();
|
|
19
|
-
this.starting = new Map();
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Register a service
|
|
24
|
-
* @param {string} name - Service name
|
|
25
|
-
* @param {Function|ServiceDefinition} factoryOrDefinition - Factory function or service definition
|
|
26
|
-
*/
|
|
27
|
-
register(name, factoryOrDefinition) {
|
|
28
|
-
const definition = typeof factoryOrDefinition === 'function' ? {
|
|
29
|
-
factory: factoryOrDefinition,
|
|
30
|
-
singleton: true,
|
|
31
|
-
dependencies: []
|
|
32
|
-
} : factoryOrDefinition;
|
|
33
|
-
this.services.set(name, definition);
|
|
34
|
-
this.emit('service:registered', {
|
|
35
|
-
name,
|
|
36
|
-
definition
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Get a service instance
|
|
42
|
-
* @param {string} name - Service name
|
|
43
|
-
* @returns {Promise<any>} Service instance
|
|
44
|
-
*/
|
|
45
|
-
async get(name) {
|
|
46
|
-
if (!this.services.has(name)) {
|
|
47
|
-
throw new VizzlyError(`Service '${name}' not registered`, 'SERVICE_NOT_FOUND', {
|
|
48
|
-
name
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
const definition = this.services.get(name);
|
|
52
|
-
|
|
53
|
-
// Return cached instance for singletons
|
|
54
|
-
if (definition.singleton && this.instances.has(name)) {
|
|
55
|
-
return this.instances.get(name);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Prevent circular dependencies during startup
|
|
59
|
-
if (this.starting.has(name)) {
|
|
60
|
-
throw new VizzlyError(`Circular dependency detected for service '${name}'`, 'CIRCULAR_DEPENDENCY', {
|
|
61
|
-
name
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
try {
|
|
65
|
-
this.starting.set(name, true);
|
|
66
|
-
|
|
67
|
-
// Resolve dependencies
|
|
68
|
-
const deps = await Promise.all((definition.dependencies || []).map(dep => this.get(dep)));
|
|
69
|
-
|
|
70
|
-
// Create instance
|
|
71
|
-
const instance = await definition.factory(...deps);
|
|
72
|
-
|
|
73
|
-
// Cache singleton instances
|
|
74
|
-
if (definition.singleton) {
|
|
75
|
-
this.instances.set(name, instance);
|
|
76
|
-
}
|
|
77
|
-
this.emit('service:created', {
|
|
78
|
-
name,
|
|
79
|
-
instance
|
|
80
|
-
});
|
|
81
|
-
return instance;
|
|
82
|
-
} finally {
|
|
83
|
-
this.starting.delete(name);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Start all registered services
|
|
89
|
-
*/
|
|
90
|
-
async startAll() {
|
|
91
|
-
const services = Array.from(this.services.keys());
|
|
92
|
-
for (const name of services) {
|
|
93
|
-
const instance = await this.get(name);
|
|
94
|
-
if (instance && typeof instance.start === 'function') {
|
|
95
|
-
await instance.start();
|
|
96
|
-
this.emit('service:started', {
|
|
97
|
-
name,
|
|
98
|
-
instance
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Stop all services in reverse order
|
|
106
|
-
*/
|
|
107
|
-
async stopAll() {
|
|
108
|
-
const instances = Array.from(this.instances.entries()).reverse();
|
|
109
|
-
for (const [name, instance] of instances) {
|
|
110
|
-
if (instance && typeof instance.stop === 'function') {
|
|
111
|
-
await instance.stop();
|
|
112
|
-
this.emit('service:stopped', {
|
|
113
|
-
name,
|
|
114
|
-
instance
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Clear all services and instances
|
|
122
|
-
*/
|
|
123
|
-
clear() {
|
|
124
|
-
this.services.clear();
|
|
125
|
-
this.instances.clear();
|
|
126
|
-
this.starting.clear();
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Export singleton instance
|
|
131
|
-
export const container = new ServiceContainer();
|
|
132
|
-
/**
|
|
133
|
-
* Create a configured service container
|
|
134
|
-
* @param {Object} config - Configuration object
|
|
135
|
-
* @returns {ServiceContainer}
|
|
136
|
-
*/
|
|
137
|
-
export async function createServiceContainer(config, command = 'run') {
|
|
138
|
-
const container = new ServiceContainer();
|
|
139
|
-
|
|
140
|
-
// Dynamic ESM imports to avoid circular deps
|
|
141
|
-
const [{
|
|
142
|
-
createComponentLogger
|
|
143
|
-
}, {
|
|
144
|
-
ApiService
|
|
145
|
-
}, {
|
|
146
|
-
createUploader
|
|
147
|
-
}, {
|
|
148
|
-
createTDDService
|
|
149
|
-
}, {
|
|
150
|
-
TestRunner
|
|
151
|
-
}, {
|
|
152
|
-
BuildManager
|
|
153
|
-
}, {
|
|
154
|
-
ServerManager
|
|
155
|
-
}, {
|
|
156
|
-
AuthService
|
|
157
|
-
}, {
|
|
158
|
-
ConfigService
|
|
159
|
-
}, {
|
|
160
|
-
ProjectService
|
|
161
|
-
}] = await Promise.all([import('../utils/logger-factory.js'), import('../services/api-service.js'), import('../services/uploader.js'), import('../services/tdd-service.js'), import('../services/test-runner.js'), import('../services/build-manager.js'), import('../services/server-manager.js'), import('../services/auth-service.js'), import('../services/config-service.js'), import('../services/project-service.js')]);
|
|
162
|
-
|
|
163
|
-
// Create logger instance once
|
|
164
|
-
const logger = createComponentLogger('CONTAINER', {
|
|
165
|
-
level: config.logLevel || (config.verbose ? 'debug' : 'warn'),
|
|
166
|
-
verbose: config.verbose || false
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
// Register services without circular dependencies
|
|
170
|
-
container.register('logger', () => logger);
|
|
171
|
-
container.register('apiService', () => new ApiService(config, {
|
|
172
|
-
logger,
|
|
173
|
-
allowNoToken: true
|
|
174
|
-
}));
|
|
175
|
-
container.register('authService', () => new AuthService({
|
|
176
|
-
baseUrl: config.apiUrl
|
|
177
|
-
}));
|
|
178
|
-
container.register('configService', () => new ConfigService(config, {
|
|
179
|
-
logger,
|
|
180
|
-
projectRoot: process.cwd()
|
|
181
|
-
}));
|
|
182
|
-
container.register('projectService', {
|
|
183
|
-
factory: async apiService => new ProjectService(config, {
|
|
184
|
-
logger,
|
|
185
|
-
apiService
|
|
186
|
-
}),
|
|
187
|
-
dependencies: ['apiService']
|
|
188
|
-
});
|
|
189
|
-
container.register('uploader', () => createUploader({
|
|
190
|
-
...config,
|
|
191
|
-
command
|
|
192
|
-
}, {
|
|
193
|
-
logger
|
|
194
|
-
}));
|
|
195
|
-
container.register('buildManager', () => new BuildManager(config, logger));
|
|
196
|
-
container.register('serverManager', {
|
|
197
|
-
factory: async (configService, authService, projectService) => new ServerManager(config, {
|
|
198
|
-
logger,
|
|
199
|
-
services: {
|
|
200
|
-
configService,
|
|
201
|
-
authService,
|
|
202
|
-
projectService
|
|
203
|
-
}
|
|
204
|
-
}),
|
|
205
|
-
dependencies: ['configService', 'authService', 'projectService']
|
|
206
|
-
});
|
|
207
|
-
container.register('tddService', () => createTDDService(config, {
|
|
208
|
-
logger
|
|
209
|
-
}));
|
|
210
|
-
container.register('testRunner', {
|
|
211
|
-
factory: async (buildManager, serverManager, tddService) => new TestRunner(config, logger, buildManager, serverManager, tddService),
|
|
212
|
-
dependencies: ['buildManager', 'serverManager', 'tddService']
|
|
213
|
-
});
|
|
214
|
-
return container;
|
|
215
|
-
}
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Base Service Class
|
|
3
|
-
* Provides common functionality for all services
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { EventEmitter } from 'events';
|
|
7
|
-
import { VizzlyError } from '../errors/vizzly-error.js';
|
|
8
|
-
import { createStandardLogger } from '../utils/logger-factory.js';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @typedef {Object} ServiceOptions
|
|
12
|
-
* @property {Object} logger - Logger instance
|
|
13
|
-
* @property {AbortSignal} [signal] - Abort signal for cancellation
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Base class for all services
|
|
18
|
-
* @extends EventEmitter
|
|
19
|
-
*/
|
|
20
|
-
export class BaseService extends EventEmitter {
|
|
21
|
-
/**
|
|
22
|
-
* @param {Object} config - Service configuration
|
|
23
|
-
* @param {ServiceOptions} options - Service options
|
|
24
|
-
*/
|
|
25
|
-
constructor(config, options = {}) {
|
|
26
|
-
super();
|
|
27
|
-
this.config = config;
|
|
28
|
-
this.logger = options.logger || createStandardLogger({
|
|
29
|
-
level: 'info'
|
|
30
|
-
});
|
|
31
|
-
this.signal = options.signal;
|
|
32
|
-
this.started = false;
|
|
33
|
-
this.stopping = false;
|
|
34
|
-
|
|
35
|
-
// Setup signal handling
|
|
36
|
-
if (this.signal) {
|
|
37
|
-
this.signal.addEventListener('abort', () => this.stop());
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Start the service
|
|
43
|
-
* @returns {Promise<void>}
|
|
44
|
-
*/
|
|
45
|
-
async start() {
|
|
46
|
-
if (this.started) {
|
|
47
|
-
this.logger.warn(`${this.constructor.name} already started`);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
try {
|
|
51
|
-
this.emit('starting');
|
|
52
|
-
await this.onStart();
|
|
53
|
-
this.started = true;
|
|
54
|
-
this.emit('started');
|
|
55
|
-
} catch (error) {
|
|
56
|
-
this.emit('error', error);
|
|
57
|
-
throw new VizzlyError(`Failed to start ${this.constructor.name}`, 'SERVICE_START_FAILED', {
|
|
58
|
-
service: this.constructor.name,
|
|
59
|
-
error: error.message
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Stop the service
|
|
66
|
-
* @returns {Promise<void>}
|
|
67
|
-
*/
|
|
68
|
-
async stop() {
|
|
69
|
-
if (!this.started || this.stopping) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
this.stopping = true;
|
|
73
|
-
try {
|
|
74
|
-
this.emit('stopping');
|
|
75
|
-
await this.onStop();
|
|
76
|
-
this.started = false;
|
|
77
|
-
this.emit('stopped');
|
|
78
|
-
} catch (error) {
|
|
79
|
-
this.emit('error', error);
|
|
80
|
-
throw new VizzlyError(`Failed to stop ${this.constructor.name}`, 'SERVICE_STOP_FAILED', {
|
|
81
|
-
service: this.constructor.name,
|
|
82
|
-
error: error.message
|
|
83
|
-
});
|
|
84
|
-
} finally {
|
|
85
|
-
this.stopping = false;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Hook for service-specific start logic
|
|
91
|
-
* @protected
|
|
92
|
-
* @returns {Promise<void>}
|
|
93
|
-
*/
|
|
94
|
-
async onStart() {
|
|
95
|
-
// Override in subclasses
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Hook for service-specific stop logic
|
|
100
|
-
* @protected
|
|
101
|
-
* @returns {Promise<void>}
|
|
102
|
-
*/
|
|
103
|
-
async onStop() {
|
|
104
|
-
// Override in subclasses
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Emit a progress event
|
|
109
|
-
* @param {string} phase - Progress phase
|
|
110
|
-
* @param {string} message - Progress message
|
|
111
|
-
* @param {Object} [data] - Additional data
|
|
112
|
-
*/
|
|
113
|
-
emitProgress(phase, message, data = {}) {
|
|
114
|
-
this.emit('progress', {
|
|
115
|
-
phase,
|
|
116
|
-
message,
|
|
117
|
-
timestamp: new Date().toISOString(),
|
|
118
|
-
...data
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Check if service is running
|
|
124
|
-
* @returns {boolean}
|
|
125
|
-
*/
|
|
126
|
-
isRunning() {
|
|
127
|
-
return this.started && !this.stopping;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Wait for service to be ready
|
|
132
|
-
* @param {number} [timeout=30000] - Timeout in milliseconds
|
|
133
|
-
* @returns {Promise<void>}
|
|
134
|
-
*/
|
|
135
|
-
async waitForReady(timeout = 30000) {
|
|
136
|
-
if (this.started) return;
|
|
137
|
-
return new Promise((resolve, reject) => {
|
|
138
|
-
const timer = setTimeout(() => {
|
|
139
|
-
reject(new VizzlyError('Service start timeout', 'SERVICE_TIMEOUT', {
|
|
140
|
-
service: this.constructor.name,
|
|
141
|
-
timeout
|
|
142
|
-
}));
|
|
143
|
-
}, timeout);
|
|
144
|
-
this.once('started', () => {
|
|
145
|
-
clearTimeout(timer);
|
|
146
|
-
resolve();
|
|
147
|
-
});
|
|
148
|
-
this.once('error', error => {
|
|
149
|
-
clearTimeout(timer);
|
|
150
|
-
reject(error);
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Create a configured service container
|
|
3
|
-
* @param {Object} config - Configuration object
|
|
4
|
-
* @returns {ServiceContainer}
|
|
5
|
-
*/
|
|
6
|
-
export function createServiceContainer(config: any, command?: string): ServiceContainer;
|
|
7
|
-
/**
|
|
8
|
-
* @typedef {Object} ServiceDefinition
|
|
9
|
-
* @property {Function} factory - Factory function to create service instance
|
|
10
|
-
* @property {boolean} [singleton=true] - Whether to cache the instance
|
|
11
|
-
* @property {string[]} [dependencies=[]] - Array of dependency names
|
|
12
|
-
*/
|
|
13
|
-
/**
|
|
14
|
-
* Service container for dependency injection and lifecycle management
|
|
15
|
-
*/
|
|
16
|
-
export class ServiceContainer {
|
|
17
|
-
services: Map<any, any>;
|
|
18
|
-
instances: Map<any, any>;
|
|
19
|
-
starting: Map<any, any>;
|
|
20
|
-
/**
|
|
21
|
-
* Register a service
|
|
22
|
-
* @param {string} name - Service name
|
|
23
|
-
* @param {Function|ServiceDefinition} factoryOrDefinition - Factory function or service definition
|
|
24
|
-
*/
|
|
25
|
-
register(name: string, factoryOrDefinition: Function | ServiceDefinition): void;
|
|
26
|
-
/**
|
|
27
|
-
* Get a service instance
|
|
28
|
-
* @param {string} name - Service name
|
|
29
|
-
* @returns {Promise<any>} Service instance
|
|
30
|
-
*/
|
|
31
|
-
get(name: string): Promise<any>;
|
|
32
|
-
/**
|
|
33
|
-
* Start all registered services
|
|
34
|
-
*/
|
|
35
|
-
startAll(): Promise<void>;
|
|
36
|
-
/**
|
|
37
|
-
* Stop all services in reverse order
|
|
38
|
-
*/
|
|
39
|
-
stopAll(): Promise<void>;
|
|
40
|
-
/**
|
|
41
|
-
* Clear all services and instances
|
|
42
|
-
*/
|
|
43
|
-
clear(): void;
|
|
44
|
-
}
|
|
45
|
-
export const container: ServiceContainer;
|
|
46
|
-
export type ServiceDefinition = {
|
|
47
|
-
/**
|
|
48
|
-
* - Factory function to create service instance
|
|
49
|
-
*/
|
|
50
|
-
factory: Function;
|
|
51
|
-
/**
|
|
52
|
-
* - Whether to cache the instance
|
|
53
|
-
*/
|
|
54
|
-
singleton?: boolean;
|
|
55
|
-
/**
|
|
56
|
-
* - Array of dependency names
|
|
57
|
-
*/
|
|
58
|
-
dependencies?: string[];
|
|
59
|
-
};
|