@vizzly-testing/cli 0.30.0 → 0.31.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 (77) hide show
  1. package/README.md +9 -9
  2. package/dist/api/client.js +6 -3
  3. package/dist/cli.js +58 -29
  4. package/dist/client/index.js +21 -29
  5. package/dist/commands/api.js +145 -90
  6. package/dist/commands/baselines.js +78 -47
  7. package/dist/commands/builds.js +9 -8
  8. package/dist/commands/comparisons.js +7 -8
  9. package/dist/commands/config-cmd.js +15 -7
  10. package/dist/commands/context.js +329 -18
  11. package/dist/commands/doctor.js +116 -81
  12. package/dist/commands/init.js +202 -201
  13. package/dist/commands/login.js +134 -65
  14. package/dist/commands/logout.js +17 -6
  15. package/dist/commands/preview.js +29 -57
  16. package/dist/commands/project.js +120 -0
  17. package/dist/commands/projects.js +9 -2
  18. package/dist/commands/review.js +236 -202
  19. package/dist/commands/run.js +75 -72
  20. package/dist/commands/status.js +228 -150
  21. package/dist/commands/tdd-daemon-process.js +98 -0
  22. package/dist/commands/tdd-daemon.js +318 -212
  23. package/dist/commands/tdd.js +23 -8
  24. package/dist/commands/upload.js +10 -9
  25. package/dist/commands/whoami.js +63 -25
  26. package/dist/config/core.js +36 -35
  27. package/dist/context/local-workspace-provider.js +132 -12
  28. package/dist/context/provider-resolver.js +5 -1
  29. package/dist/index.js +5 -4
  30. package/dist/plugin-loader.js +55 -49
  31. package/dist/project/core.js +2 -2
  32. package/dist/project/operations.js +6 -3
  33. package/dist/reporter/reporter-bundle.css +1 -1
  34. package/dist/reporter/reporter-bundle.iife.js +7 -7
  35. package/dist/sdk/index.js +162 -107
  36. package/dist/server/handlers/tdd-handler.js +30 -54
  37. package/dist/server/routers/baseline.js +195 -178
  38. package/dist/server/routers/events.js +212 -174
  39. package/dist/server/routers/projects.js +14 -5
  40. package/dist/server/routers/screenshot.js +1 -31
  41. package/dist/services/build-manager.js +19 -97
  42. package/dist/services/config-service.js +26 -34
  43. package/dist/services/index.js +2 -4
  44. package/dist/services/static-report-generator.js +11 -12
  45. package/dist/services/test-runner.js +21 -27
  46. package/dist/tdd/index.js +1 -1
  47. package/dist/tdd/metadata/region-metadata.js +0 -37
  48. package/dist/tdd/runtime-context.js +78 -0
  49. package/dist/tdd/server-registry.js +38 -17
  50. package/dist/tdd/services/baseline-manager.js +23 -0
  51. package/dist/tdd/services/hotspot-service.js +0 -13
  52. package/dist/tdd/tdd-service.js +82 -113
  53. package/dist/types/client.d.ts +36 -1
  54. package/dist/types/index.d.ts +114 -22
  55. package/dist/types/sdk.d.ts +40 -0
  56. package/dist/uploader/core.js +5 -4
  57. package/dist/uploader/index.js +79 -2
  58. package/dist/uploader/operations.js +40 -5
  59. package/dist/utils/api-url.js +14 -0
  60. package/dist/utils/async-utils.js +26 -0
  61. package/dist/utils/ci-env.js +19 -12
  62. package/dist/utils/config-loader.js +39 -65
  63. package/dist/utils/config-schema.js +19 -55
  64. package/dist/utils/fetch-utils.js +12 -5
  65. package/dist/utils/git.js +35 -72
  66. package/dist/utils/global-config.js +20 -1
  67. package/dist/utils/output.js +19 -7
  68. package/dist/utils/patterns.js +14 -0
  69. package/dist/utils/project-link-store.js +105 -0
  70. package/dist/utils/screenshot-options.js +28 -0
  71. package/dist/utils/secret-store.js +65 -0
  72. package/package.json +11 -8
  73. package/dist/services/screenshot-server.js +0 -65
  74. package/dist/services/server-manager.js +0 -81
  75. package/dist/services/uploader.js +0 -100
  76. package/dist/tdd/services/region-service.js +0 -75
  77. package/dist/utils/build-history.js +0 -103
@@ -1,81 +0,0 @@
1
- /**
2
- * Server Manager Service
3
- * Manages the HTTP server with functional handlers
4
- *
5
- * This class is a thin wrapper around the functional operations in
6
- * src/server-manager/. It maintains backwards compatibility while
7
- * delegating to pure functions for testability.
8
- */
9
-
10
- import { existsSync, mkdirSync, unlinkSync, writeFileSync } from 'node:fs';
11
- import { createApiClient } from '../api/index.js';
12
- import { createApiHandler } from '../server/handlers/api-handler.js';
13
- import { createTddHandler } from '../server/handlers/tdd-handler.js';
14
- import { createHttpServer } from '../server/http-server.js';
15
- import { buildServerInterface, getTddResults, startServer, stopServer } from '../server-manager/index.js';
16
- export class ServerManager {
17
- constructor(config, options = {}) {
18
- this.config = config;
19
- this.httpServer = null;
20
- this.handler = null;
21
- this.services = options.services || {};
22
- this.tddMode = false;
23
-
24
- // Dependency injection for testing - defaults to real implementations
25
- this.deps = options.deps || {
26
- createHttpServer,
27
- createTddHandler,
28
- createApiHandler,
29
- createApiClient,
30
- fs: {
31
- mkdirSync,
32
- writeFileSync,
33
- existsSync,
34
- unlinkSync
35
- }
36
- };
37
- }
38
- async start(buildId = null, tddMode = false, setBaseline = false) {
39
- this.buildId = buildId;
40
- this.tddMode = tddMode;
41
- this.setBaseline = setBaseline;
42
- let result = await startServer({
43
- config: this.config,
44
- buildId,
45
- tddMode,
46
- setBaseline,
47
- projectRoot: process.cwd(),
48
- services: this.services,
49
- deps: this.deps
50
- });
51
- this.httpServer = result.httpServer;
52
- this.handler = result.handler;
53
- }
54
- async stop() {
55
- await stopServer({
56
- httpServer: this.httpServer,
57
- handler: this.handler,
58
- projectRoot: process.cwd(),
59
- deps: this.deps
60
- });
61
- }
62
-
63
- // Expose server interface for compatibility
64
- get server() {
65
- return buildServerInterface({
66
- handler: this.handler,
67
- httpServer: this.httpServer
68
- });
69
- }
70
-
71
- /**
72
- * Get TDD results (comparisons, screenshot count, etc.)
73
- * Only available in TDD mode after tests have run
74
- */
75
- async getTddResults() {
76
- return getTddResults({
77
- tddMode: this.tddMode,
78
- handler: this.handler
79
- });
80
- }
81
- }
@@ -1,100 +0,0 @@
1
- /**
2
- * Vizzly Screenshot Uploader
3
- * Handles screenshot uploads to the Vizzly platform
4
- *
5
- * This module is a thin wrapper around the functional operations in
6
- * src/uploader/. It maintains backwards compatibility while
7
- * delegating to pure functions for testability.
8
- */
9
-
10
- import { readFile, stat } from 'node:fs/promises';
11
- import { glob } from 'glob';
12
- import { checkShas, createApiClient, createBuild } from '../api/index.js';
13
- import { TimeoutError, UploadError, ValidationError } from '../errors/vizzly-error.js';
14
- import { resolveBatchSize, resolveTimeout } from '../uploader/index.js';
15
- import { upload as uploadOperation, waitForBuild as waitForBuildOperation } from '../uploader/operations.js';
16
- import { getDefaultBranch } from '../utils/git.js';
17
- import * as output from '../utils/output.js';
18
-
19
- /**
20
- * Create a new uploader instance
21
- */
22
- export function createUploader({
23
- apiKey,
24
- apiUrl,
25
- userAgent,
26
- command,
27
- upload: uploadConfig = {}
28
- } = {}, options = {}) {
29
- let signal = options.signal || new AbortController().signal;
30
- let client = createApiClient({
31
- baseUrl: apiUrl,
32
- token: apiKey,
33
- command: command || 'upload',
34
- sdkUserAgent: userAgent,
35
- allowNoToken: true
36
- });
37
-
38
- // Resolve tunable parameters
39
- let batchSize = resolveBatchSize(options, uploadConfig);
40
- let timeout = resolveTimeout(options, uploadConfig);
41
-
42
- // Dependency injection for testing
43
- let deps = options.deps || {
44
- client,
45
- createBuild,
46
- getDefaultBranch,
47
- glob,
48
- readFile,
49
- stat,
50
- checkShas,
51
- createError: (message, code, context) => {
52
- let error = new UploadError(message, context);
53
- error.code = code;
54
- return error;
55
- },
56
- createValidationError: (message, context) => new ValidationError(message, context),
57
- createUploadError: (message, context) => new UploadError(message, context),
58
- createTimeoutError: (message, context) => new TimeoutError(message, context),
59
- output
60
- };
61
-
62
- /**
63
- * Upload screenshots to Vizzly
64
- */
65
- async function upload(uploadOptions) {
66
- return uploadOperation({
67
- uploadOptions,
68
- config: {
69
- apiKey,
70
- apiUrl
71
- },
72
- signal,
73
- batchSize,
74
- deps: {
75
- ...deps,
76
- client: deps.client || client
77
- }
78
- });
79
- }
80
-
81
- /**
82
- * Wait for a build to complete
83
- */
84
- async function waitForBuild(buildId, waitTimeout = timeout) {
85
- return waitForBuildOperation({
86
- buildId,
87
- timeout: waitTimeout,
88
- signal,
89
- client: deps.client || client,
90
- deps: {
91
- createError: deps.createError,
92
- createTimeoutError: deps.createTimeoutError
93
- }
94
- });
95
- }
96
- return {
97
- upload,
98
- waitForBuild
99
- };
100
- }
@@ -1,75 +0,0 @@
1
- /**
2
- * Region Service
3
- *
4
- * Functions for downloading and managing user-defined hotspot regions from the cloud.
5
- * Regions are 2D bounding boxes that users have confirmed as dynamic content areas.
6
- */
7
-
8
- import { saveRegionMetadata } from '../metadata/region-metadata.js';
9
-
10
- /**
11
- * Download user-defined regions from cloud API
12
- *
13
- * @param {Object} options
14
- * @param {Object} options.api - ApiService instance
15
- * @param {string} options.workingDir - Working directory
16
- * @param {string[]} options.screenshotNames - Names of screenshots to get regions for
17
- * @param {boolean} options.includeCandidates - Include candidate regions (default: false)
18
- * @returns {Promise<{ success: boolean, count: number, regionCount: number, error?: string }>}
19
- */
20
- export async function downloadRegions(options) {
21
- let {
22
- api,
23
- workingDir,
24
- screenshotNames,
25
- includeCandidates = false
26
- } = options;
27
- if (!screenshotNames || screenshotNames.length === 0) {
28
- return {
29
- success: true,
30
- count: 0,
31
- regionCount: 0
32
- };
33
- }
34
- try {
35
- let response = await api.getRegions(screenshotNames, {
36
- includeCandidates
37
- });
38
- if (!response?.regions) {
39
- return {
40
- success: false,
41
- error: 'API returned no region data'
42
- };
43
- }
44
-
45
- // Save regions to disk
46
- saveRegionMetadata(workingDir, response.regions, response.summary);
47
-
48
- // Calculate stats
49
- let count = Object.keys(response.regions).length;
50
- let regionCount = response.summary?.total_regions || 0;
51
- return {
52
- success: true,
53
- count,
54
- regionCount
55
- };
56
- } catch (error) {
57
- return {
58
- success: false,
59
- error: error.message
60
- };
61
- }
62
- }
63
-
64
- /**
65
- * Extract screenshot names from a list of screenshots
66
- *
67
- * @param {Array} screenshots - Screenshots with name property
68
- * @returns {string[]}
69
- */
70
- export function extractScreenshotNames(screenshots) {
71
- if (!screenshots || !Array.isArray(screenshots)) {
72
- return [];
73
- }
74
- return screenshots.map(s => s.name).filter(Boolean);
75
- }
@@ -1,103 +0,0 @@
1
- import { existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
2
- import { join } from 'node:path';
3
-
4
- /**
5
- * Archive a build to history directory
6
- * @param {string} workingDir - Working directory
7
- * @param {string} buildId - Build ID to archive
8
- * @param {Array} builds - Build data
9
- * @param {Array} comparisons - Comparison data
10
- * @param {Object} summary - Summary stats
11
- * @param {number} maxHistory - Maximum number of builds to keep (default: 3)
12
- */
13
- export function archiveBuild(workingDir, buildId, builds, comparisons, summary, maxHistory = 3) {
14
- const historyDir = join(workingDir, '.vizzly', 'history');
15
-
16
- // Create history directory if it doesn't exist
17
- if (!existsSync(historyDir)) {
18
- mkdirSync(historyDir, {
19
- recursive: true
20
- });
21
- }
22
-
23
- // Save current build to history
24
- const buildDir = join(historyDir, buildId);
25
- if (!existsSync(buildDir)) {
26
- mkdirSync(buildDir, {
27
- recursive: true
28
- });
29
- }
30
- const buildData = {
31
- buildId,
32
- timestamp: Date.now(),
33
- builds,
34
- comparisons,
35
- summary
36
- };
37
- writeFileSync(join(buildDir, 'report.json'), JSON.stringify(buildData, null, 2));
38
-
39
- // Clean up old builds (keep last N)
40
- cleanupOldBuilds(historyDir, maxHistory);
41
- }
42
-
43
- /**
44
- * Get list of archived builds
45
- * @param {string} workingDir - Working directory
46
- * @returns {Array} Array of build metadata
47
- */
48
- export function getArchivedBuilds(workingDir) {
49
- const historyDir = join(workingDir, '.vizzly', 'history');
50
- if (!existsSync(historyDir)) {
51
- return [];
52
- }
53
- try {
54
- const buildDirs = readdirSync(historyDir, {
55
- withFileTypes: true
56
- }).filter(dirent => dirent.isDirectory()).map(dirent => dirent.name).sort().reverse(); // Newest first
57
-
58
- return buildDirs.map(buildId => {
59
- const reportPath = join(historyDir, buildId, 'report.json');
60
- if (existsSync(reportPath)) {
61
- try {
62
- const data = JSON.parse(require('node:fs').readFileSync(reportPath, 'utf8'));
63
- return {
64
- buildId: data.buildId,
65
- timestamp: data.timestamp,
66
- summary: data.summary
67
- };
68
- } catch {
69
- return null;
70
- }
71
- }
72
- return null;
73
- }).filter(Boolean);
74
- } catch {
75
- return [];
76
- }
77
- }
78
-
79
- /**
80
- * Remove old builds, keeping only the last N
81
- * @private
82
- */
83
- function cleanupOldBuilds(historyDir, maxHistory) {
84
- try {
85
- const buildDirs = readdirSync(historyDir, {
86
- withFileTypes: true
87
- }).filter(dirent => dirent.isDirectory()).map(dirent => dirent.name).sort().reverse(); // Newest first
88
-
89
- // Remove builds beyond maxHistory
90
- if (buildDirs.length > maxHistory) {
91
- const toRemove = buildDirs.slice(maxHistory);
92
- toRemove.forEach(buildId => {
93
- const buildDir = join(historyDir, buildId);
94
- rmSync(buildDir, {
95
- recursive: true,
96
- force: true
97
- });
98
- });
99
- }
100
- } catch {
101
- // Ignore cleanup errors
102
- }
103
- }