@vizzly-testing/cli 0.29.6 → 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 (87) hide show
  1. package/README.md +34 -6
  2. package/dist/api/client.js +6 -3
  3. package/dist/api/endpoints.js +59 -0
  4. package/dist/api/index.js +1 -1
  5. package/dist/auth/core.js +1 -1
  6. package/dist/auth/index.js +2 -1
  7. package/dist/cli.js +206 -51
  8. package/dist/client/index.js +21 -29
  9. package/dist/commands/api.js +145 -90
  10. package/dist/commands/baselines.js +78 -47
  11. package/dist/commands/builds.js +9 -8
  12. package/dist/commands/comparisons.js +7 -8
  13. package/dist/commands/config-cmd.js +15 -7
  14. package/dist/commands/context.js +870 -0
  15. package/dist/commands/doctor.js +116 -81
  16. package/dist/commands/init.js +202 -201
  17. package/dist/commands/login.js +134 -65
  18. package/dist/commands/logout.js +18 -7
  19. package/dist/commands/preview.js +29 -57
  20. package/dist/commands/project.js +120 -0
  21. package/dist/commands/projects.js +9 -2
  22. package/dist/commands/review.js +236 -202
  23. package/dist/commands/run.js +75 -72
  24. package/dist/commands/status.js +228 -150
  25. package/dist/commands/tdd-daemon-process.js +98 -0
  26. package/dist/commands/tdd-daemon.js +318 -212
  27. package/dist/commands/tdd.js +23 -8
  28. package/dist/commands/upload.js +10 -9
  29. package/dist/commands/whoami.js +64 -26
  30. package/dist/config/core.js +37 -36
  31. package/dist/context/local-workspace-provider.js +532 -0
  32. package/dist/context/provider-resolver.js +36 -0
  33. package/dist/index.js +5 -4
  34. package/dist/plugin-loader.js +55 -49
  35. package/dist/project/core.js +2 -2
  36. package/dist/project/operations.js +6 -3
  37. package/dist/reporter/reporter-bundle.css +2 -1
  38. package/dist/reporter/reporter-bundle.iife.js +119 -119
  39. package/dist/reporter-ssr/ssr-entry.js +605 -509
  40. package/dist/sdk/index.js +162 -107
  41. package/dist/server/handlers/tdd-handler.js +30 -54
  42. package/dist/server/routers/assets.js +4 -0
  43. package/dist/server/routers/baseline.js +195 -178
  44. package/dist/server/routers/events.js +226 -148
  45. package/dist/server/routers/projects.js +14 -5
  46. package/dist/server/routers/screenshot.js +1 -31
  47. package/dist/services/build-manager.js +19 -97
  48. package/dist/services/config-service.js +26 -34
  49. package/dist/services/index.js +2 -4
  50. package/dist/services/static-report-generator.js +11 -12
  51. package/dist/services/test-runner.js +21 -27
  52. package/dist/tdd/core/hotspot-coverage.js +1 -1
  53. package/dist/tdd/index.js +1 -1
  54. package/dist/tdd/metadata/region-metadata.js +0 -37
  55. package/dist/tdd/runtime-context.js +78 -0
  56. package/dist/tdd/server-registry.js +38 -17
  57. package/dist/tdd/services/baseline-manager.js +23 -0
  58. package/dist/tdd/services/hotspot-service.js +1 -14
  59. package/dist/tdd/tdd-service.js +82 -113
  60. package/dist/types/client.d.ts +36 -1
  61. package/dist/types/index.d.ts +114 -22
  62. package/dist/types/sdk.d.ts +47 -7
  63. package/dist/uploader/core.js +5 -4
  64. package/dist/uploader/index.js +79 -2
  65. package/dist/uploader/operations.js +40 -5
  66. package/dist/utils/api-url.js +14 -0
  67. package/dist/utils/async-utils.js +26 -0
  68. package/dist/utils/ci-env.js +19 -12
  69. package/dist/utils/colors.js +46 -44
  70. package/dist/utils/config-loader.js +39 -65
  71. package/dist/utils/config-schema.js +19 -55
  72. package/dist/utils/fetch-utils.js +12 -5
  73. package/dist/utils/git.js +35 -72
  74. package/dist/utils/global-config.js +22 -3
  75. package/dist/utils/output.js +19 -7
  76. package/dist/utils/patterns.js +14 -0
  77. package/dist/utils/project-link-store.js +105 -0
  78. package/dist/utils/report-data.js +31 -0
  79. package/dist/utils/screenshot-options.js +28 -0
  80. package/dist/utils/secret-store.js +65 -0
  81. package/dist/utils/security.js +3 -2
  82. package/package.json +14 -11
  83. package/dist/services/screenshot-server.js +0 -65
  84. package/dist/services/server-manager.js +0 -81
  85. package/dist/services/uploader.js +0 -100
  86. package/dist/tdd/services/region-service.js +0 -75
  87. package/dist/utils/build-history.js +0 -103
@@ -22,7 +22,8 @@ import { generateBaselineFilename as defaultGenerateBaselineFilename, generateCo
22
22
  import { createEmptyBaselineMetadata as defaultCreateEmptyBaselineMetadata, loadBaselineMetadata as defaultLoadBaselineMetadata, saveBaselineMetadata as defaultSaveBaselineMetadata, upsertScreenshotInMetadata as defaultUpsertScreenshotInMetadata } from './metadata/baseline-metadata.js';
23
23
  import { loadHotspotMetadata as defaultLoadHotspotMetadata, saveHotspotMetadata as defaultSaveHotspotMetadata } from './metadata/hotspot-metadata.js';
24
24
  import { loadRegionMetadata as defaultLoadRegionMetadata, saveRegionMetadata as defaultSaveRegionMetadata } from './metadata/region-metadata.js';
25
- import { baselineExists as defaultBaselineExists, clearBaselineData as defaultClearBaselineData, getBaselinePath as defaultGetBaselinePath, getCurrentPath as defaultGetCurrentPath, getDiffPath as defaultGetDiffPath, initializeDirectories as defaultInitializeDirectories, saveBaseline as defaultSaveBaseline, saveCurrent as defaultSaveCurrent } from './services/baseline-manager.js';
25
+ import { buildTddDependencyOps, resolveTddPaths, resolveTddWorkingDirectory } from './runtime-context.js';
26
+ import { baselineExists as defaultBaselineExists, clearBaselineData as defaultClearBaselineData, clearRunData as defaultClearRunData, getBaselinePath as defaultGetBaselinePath, getCurrentPath as defaultGetCurrentPath, getDiffPath as defaultGetDiffPath, initializeDirectories as defaultInitializeDirectories, saveBaseline as defaultSaveBaseline, saveCurrent as defaultSaveCurrent } from './services/baseline-manager.js';
26
27
  import { buildErrorComparison as defaultBuildErrorComparison, buildFailedComparison as defaultBuildFailedComparison, buildNewComparison as defaultBuildNewComparison, buildPassedComparison as defaultBuildPassedComparison, compareImages as defaultCompareImages, isDimensionMismatchError as defaultIsDimensionMismatchError } from './services/comparison-service.js';
27
28
  import { buildResults as defaultBuildResults, getFailedComparisons as defaultGetFailedComparisons, getNewComparisons as defaultGetNewComparisons } from './services/result-service.js';
28
29
 
@@ -40,110 +41,79 @@ export function createTDDService(config, options = {}, deps = {}) {
40
41
  }
41
42
  export class TddService {
42
43
  constructor(config, workingDir = process.cwd(), setBaseline = false, authService = null, deps = {}) {
43
- // Grouped dependencies with defaults
44
- let {
45
- // Core utilities
46
- output = defaultOutput,
47
- colors = defaultColors,
48
- validatePathSecurity = defaultValidatePathSecurity,
49
- initializeDirectories = defaultInitializeDirectories,
50
- // File system operations
51
- fs = {},
52
- // API operations
53
- api = {},
54
- // Baseline metadata operations
55
- metadata = {},
56
- // Baseline file management
57
- baseline = {},
58
- // Screenshot comparison
59
- comparison = {},
60
- // Signature generation and security
61
- signature = {},
62
- // Result building
63
- results = {},
64
- // Other
65
- calculateHotspotCoverage = defaultCalculateHotspotCoverage
66
- } = deps;
67
-
68
- // Merge grouped deps with defaults
69
- let fsOps = {
70
- existsSync: defaultExistsSync,
71
- mkdirSync: defaultMkdirSync,
72
- readFileSync: defaultReadFileSync,
73
- writeFileSync: defaultWriteFileSync,
74
- ...fs
75
- };
76
- let apiOps = {
77
- createApiClient: defaultCreateApiClient,
78
- getTddBaselines: defaultGetTddBaselines,
79
- getBuilds: defaultGetBuilds,
80
- getComparison: defaultGetComparison,
81
- getBatchHotspots: defaultGetBatchHotspots,
82
- fetchWithTimeout: defaultFetchWithTimeout,
83
- getDefaultBranch: defaultGetDefaultBranch,
84
- ...api
85
- };
86
- let metadataOps = {
87
- loadBaselineMetadata: defaultLoadBaselineMetadata,
88
- saveBaselineMetadata: defaultSaveBaselineMetadata,
89
- createEmptyBaselineMetadata: defaultCreateEmptyBaselineMetadata,
90
- upsertScreenshotInMetadata: defaultUpsertScreenshotInMetadata,
91
- loadHotspotMetadata: defaultLoadHotspotMetadata,
92
- saveHotspotMetadata: defaultSaveHotspotMetadata,
93
- loadRegionMetadata: defaultLoadRegionMetadata,
94
- saveRegionMetadata: defaultSaveRegionMetadata,
95
- ...metadata
96
- };
97
- let baselineOps = {
98
- baselineExists: defaultBaselineExists,
99
- clearBaselineData: defaultClearBaselineData,
100
- getBaselinePath: defaultGetBaselinePath,
101
- getCurrentPath: defaultGetCurrentPath,
102
- getDiffPath: defaultGetDiffPath,
103
- saveBaseline: defaultSaveBaseline,
104
- saveCurrent: defaultSaveCurrent,
105
- ...baseline
106
- };
107
- let comparisonOps = {
108
- compareImages: defaultCompareImages,
109
- buildPassedComparison: defaultBuildPassedComparison,
110
- buildNewComparison: defaultBuildNewComparison,
111
- buildFailedComparison: defaultBuildFailedComparison,
112
- buildErrorComparison: defaultBuildErrorComparison,
113
- isDimensionMismatchError: defaultIsDimensionMismatchError,
114
- ...comparison
115
- };
116
- let signatureOps = {
117
- generateScreenshotSignature: defaultGenerateScreenshotSignature,
118
- generateBaselineFilename: defaultGenerateBaselineFilename,
119
- generateComparisonId: defaultGenerateComparisonId,
120
- sanitizeScreenshotName: defaultSanitizeScreenshotName,
121
- validateScreenshotProperties: defaultValidateScreenshotProperties,
122
- safePath: defaultSafePath,
123
- ...signature
124
- };
125
- let resultsOps = {
126
- buildResults: defaultBuildResults,
127
- getFailedComparisons: defaultGetFailedComparisons,
128
- getNewComparisons: defaultGetNewComparisons,
129
- ...results
44
+ let defaults = {
45
+ output: defaultOutput,
46
+ colors: defaultColors,
47
+ validatePathSecurity: defaultValidatePathSecurity,
48
+ initializeDirectories: defaultInitializeDirectories,
49
+ calculateHotspotCoverage: defaultCalculateHotspotCoverage,
50
+ fs: {
51
+ existsSync: defaultExistsSync,
52
+ mkdirSync: defaultMkdirSync,
53
+ readFileSync: defaultReadFileSync,
54
+ writeFileSync: defaultWriteFileSync
55
+ },
56
+ api: {
57
+ createApiClient: defaultCreateApiClient,
58
+ getTddBaselines: defaultGetTddBaselines,
59
+ getBuilds: defaultGetBuilds,
60
+ getComparison: defaultGetComparison,
61
+ getBatchHotspots: defaultGetBatchHotspots,
62
+ fetchWithTimeout: defaultFetchWithTimeout,
63
+ getDefaultBranch: defaultGetDefaultBranch
64
+ },
65
+ metadata: {
66
+ loadBaselineMetadata: defaultLoadBaselineMetadata,
67
+ saveBaselineMetadata: defaultSaveBaselineMetadata,
68
+ createEmptyBaselineMetadata: defaultCreateEmptyBaselineMetadata,
69
+ upsertScreenshotInMetadata: defaultUpsertScreenshotInMetadata,
70
+ loadHotspotMetadata: defaultLoadHotspotMetadata,
71
+ saveHotspotMetadata: defaultSaveHotspotMetadata,
72
+ loadRegionMetadata: defaultLoadRegionMetadata,
73
+ saveRegionMetadata: defaultSaveRegionMetadata
74
+ },
75
+ baseline: {
76
+ baselineExists: defaultBaselineExists,
77
+ clearBaselineData: defaultClearBaselineData,
78
+ clearRunData: defaultClearRunData,
79
+ getBaselinePath: defaultGetBaselinePath,
80
+ getCurrentPath: defaultGetCurrentPath,
81
+ getDiffPath: defaultGetDiffPath,
82
+ saveBaseline: defaultSaveBaseline,
83
+ saveCurrent: defaultSaveCurrent
84
+ },
85
+ comparison: {
86
+ compareImages: defaultCompareImages,
87
+ buildPassedComparison: defaultBuildPassedComparison,
88
+ buildNewComparison: defaultBuildNewComparison,
89
+ buildFailedComparison: defaultBuildFailedComparison,
90
+ buildErrorComparison: defaultBuildErrorComparison,
91
+ isDimensionMismatchError: defaultIsDimensionMismatchError
92
+ },
93
+ signature: {
94
+ generateScreenshotSignature: defaultGenerateScreenshotSignature,
95
+ generateBaselineFilename: defaultGenerateBaselineFilename,
96
+ generateComparisonId: defaultGenerateComparisonId,
97
+ sanitizeScreenshotName: defaultSanitizeScreenshotName,
98
+ validateScreenshotProperties: defaultValidateScreenshotProperties,
99
+ safePath: defaultSafePath
100
+ },
101
+ results: {
102
+ buildResults: defaultBuildResults,
103
+ getFailedComparisons: defaultGetFailedComparisons,
104
+ getNewComparisons: defaultGetNewComparisons
105
+ }
130
106
  };
131
-
132
- // Store flattened dependencies for use in methods
133
- this._deps = {
107
+ let {
108
+ runtimeDeps,
109
+ apiOps
110
+ } = buildTddDependencyOps(deps, defaults);
111
+ let {
134
112
  output,
135
- colors,
136
113
  validatePathSecurity,
137
- initializeDirectories,
138
- calculateHotspotCoverage,
139
- ...fsOps,
140
- ...apiOps,
141
- ...metadataOps,
142
- ...baselineOps,
143
- ...comparisonOps,
144
- ...signatureOps,
145
- ...resultsOps
146
- };
114
+ initializeDirectories
115
+ } = runtimeDeps;
116
+ this._deps = runtimeDeps;
147
117
  this.config = config;
148
118
  this.setBaseline = setBaseline;
149
119
  this.authService = authService;
@@ -155,15 +125,8 @@ export class TddService {
155
125
  });
156
126
 
157
127
  // Validate and secure the working directory
158
- try {
159
- this.workingDir = validatePathSecurity(workingDir, workingDir);
160
- } catch (error) {
161
- output.error(`Invalid working directory: ${error.message}`);
162
- throw new Error(`Working directory validation failed: ${error.message}`);
163
- }
164
-
165
- // Initialize directories using extracted module
166
- let paths = initializeDirectories(this.workingDir);
128
+ this.workingDir = resolveTddWorkingDirectory(workingDir, validatePathSecurity, output);
129
+ let paths = resolveTddPaths(this.workingDir, initializeDirectories);
167
130
  this.baselinePath = paths.baselinePath;
168
131
  this.currentPath = paths.currentPath;
169
132
  this.diffPath = paths.diffPath;
@@ -171,7 +134,7 @@ export class TddService {
171
134
  // State
172
135
  this.baselineData = null;
173
136
  this.comparisons = [];
174
- this.threshold = config.comparison?.threshold || 2.0;
137
+ this.threshold = config.comparison?.threshold ?? 2.0;
175
138
  this.minClusterSize = config.comparison?.minClusterSize ?? 2;
176
139
  this.signatureProperties = config.signatureProperties ?? [];
177
140
 
@@ -187,6 +150,12 @@ export class TddService {
187
150
  output.info('[vizzly] Baseline update mode - will overwrite existing baselines with new ones');
188
151
  }
189
152
  }
153
+ clearRunData() {
154
+ this._deps.clearRunData({
155
+ currentPath: this.currentPath,
156
+ diffPath: this.diffPath
157
+ });
158
+ }
190
159
 
191
160
  /**
192
161
  * Download baselines from cloud
@@ -915,7 +884,7 @@ export class TddService {
915
884
  return null;
916
885
  }
917
886
  this.baselineData = metadata;
918
- this.threshold = metadata.threshold || this.threshold;
887
+ this.threshold = metadata.threshold ?? this.threshold;
919
888
  this.signatureProperties = metadata.signatureProperties || this.signatureProperties;
920
889
  if (this.signatureProperties.length > 0) {
921
890
  output.debug('tdd', `loaded signature properties: ${this.signatureProperties.join(', ')}`);
@@ -4,6 +4,40 @@
4
4
  * @module @vizzly-testing/cli/client
5
5
  */
6
6
 
7
+ export type ClientLogLevel = 'debug' | 'info' | 'warn' | 'error';
8
+
9
+ export const LOG_LEVELS: Readonly<Record<ClientLogLevel, number>>;
10
+
11
+ /**
12
+ * Check whether client SDK output should log at the requested level.
13
+ */
14
+ export function shouldLogClient(
15
+ level: string,
16
+ configuredLevel?: string
17
+ ): boolean;
18
+
19
+ /**
20
+ * Auto-discover a local TDD server by searching for `.vizzly/server.json`.
21
+ */
22
+ export function autoDiscoverTddServer(
23
+ startDir?: string,
24
+ deps?: {
25
+ exists?: (path: string) => boolean;
26
+ readFile?: (path: string, encoding: BufferEncoding) => string | Buffer;
27
+ }
28
+ ): string | null;
29
+
30
+ /**
31
+ * Result returned by a successful screenshot capture.
32
+ */
33
+ export interface ScreenshotResult {
34
+ success: boolean;
35
+ status?: 'passed' | 'failed' | 'new';
36
+ name?: string;
37
+ diffPercentage?: number;
38
+ [key: string]: unknown;
39
+ }
40
+
7
41
  /**
8
42
  * Take a screenshot for visual regression testing
9
43
  *
@@ -38,8 +72,9 @@ export function vizzlyScreenshot(
38
72
  threshold?: number;
39
73
  minClusterSize?: number;
40
74
  fullPage?: boolean;
75
+ [key: string]: unknown;
41
76
  }
42
- ): Promise<void>;
77
+ ): Promise<ScreenshotResult | null>;
43
78
 
44
79
  /**
45
80
  * Flush result summary returned by vizzlyFlush
@@ -77,6 +77,7 @@ export interface ScreenshotOptions {
77
77
  minClusterSize?: number;
78
78
  fullPage?: boolean;
79
79
  buildId?: string;
80
+ [key: string]: unknown;
80
81
  }
81
82
 
82
83
  export interface ScreenshotResult {
@@ -84,6 +85,7 @@ export interface ScreenshotResult {
84
85
  status?: 'passed' | 'failed' | 'new';
85
86
  name?: string;
86
87
  diffPercentage?: number;
88
+ [key: string]: unknown;
87
89
  }
88
90
 
89
91
  // ============================================================================
@@ -262,6 +264,12 @@ export class ValidationError extends VizzlyError {
262
264
  export interface VizzlySDKInstance extends EventEmitter {
263
265
  config: VizzlyConfig;
264
266
 
267
+ /** Reload file config and re-apply current in-memory overrides */
268
+ init(): Promise<VizzlyConfig>;
269
+
270
+ /** Merge new config values into the active SDK config */
271
+ updateConfig(newConfig: Partial<VizzlyConfig>): VizzlyConfig;
272
+
265
273
  /** Start the Vizzly server */
266
274
  start(): Promise<{ port: number; url: string }>;
267
275
 
@@ -286,6 +294,28 @@ export interface VizzlySDKInstance extends EventEmitter {
286
294
  name: string,
287
295
  imageBuffer: Buffer | string
288
296
  ): Promise<ComparisonResult>;
297
+
298
+ /** Create an uploader using the SDK config */
299
+ createUploader(options?: {
300
+ upload?: UploadConfig;
301
+ signal?: AbortSignal;
302
+ batchSize?: number;
303
+ timeout?: number;
304
+ }): Uploader;
305
+
306
+ /** Create a local TDD service using the SDK config */
307
+ createTDDService(options?: {
308
+ workingDir?: string;
309
+ setBaseline?: boolean;
310
+ authService?: unknown;
311
+ }): TddService;
312
+
313
+ /** Start a local TDD service */
314
+ startTDD(options?: {
315
+ workingDir?: string;
316
+ setBaseline?: boolean;
317
+ authService?: unknown;
318
+ }): Promise<unknown>;
289
319
  }
290
320
 
291
321
  export class VizzlySDK extends EventEmitter implements VizzlySDKInstance {
@@ -293,6 +323,8 @@ export class VizzlySDK extends EventEmitter implements VizzlySDKInstance {
293
323
 
294
324
  constructor(config: VizzlyConfig, services: unknown);
295
325
 
326
+ init(): Promise<VizzlyConfig>;
327
+ updateConfig(newConfig: Partial<VizzlyConfig>): VizzlyConfig;
296
328
  start(): Promise<{ port: number; url: string }>;
297
329
  stop(): Promise<void>;
298
330
  getConfig(): VizzlyConfig;
@@ -306,6 +338,22 @@ export class VizzlySDK extends EventEmitter implements VizzlySDKInstance {
306
338
  name: string,
307
339
  imageBuffer: Buffer | string
308
340
  ): Promise<ComparisonResult>;
341
+ createUploader(options?: {
342
+ upload?: UploadConfig;
343
+ signal?: AbortSignal;
344
+ batchSize?: number;
345
+ timeout?: number;
346
+ }): Uploader;
347
+ createTDDService(options?: {
348
+ workingDir?: string;
349
+ setBaseline?: boolean;
350
+ authService?: unknown;
351
+ }): TddService;
352
+ startTDD(options?: {
353
+ workingDir?: string;
354
+ setBaseline?: boolean;
355
+ authService?: unknown;
356
+ }): Promise<unknown>;
309
357
  }
310
358
 
311
359
  // ============================================================================
@@ -344,15 +392,8 @@ export interface TddService {
344
392
  }
345
393
 
346
394
  export interface Services {
347
- apiService: unknown;
348
- authService: unknown;
349
- configService: unknown;
350
- projectService: unknown;
351
- uploader: Uploader;
352
- buildManager: unknown;
353
- serverManager: unknown;
354
- tddService: TddService;
355
- testRunner: unknown;
395
+ serverManager: ServerManager;
396
+ testRunner: TestRunnerService;
356
397
  }
357
398
 
358
399
  // ============================================================================
@@ -392,11 +433,47 @@ export interface PluginServerManager {
392
433
  stop(): Promise<void>;
393
434
  }
394
435
 
436
+ /**
437
+ * Internal server manager returned by createServices().
438
+ */
439
+ export interface ServerManager extends PluginServerManager {
440
+ /** Get current TDD results from the local server handler */
441
+ getTddResults(): Promise<TddResults | null>;
442
+ /** Current HTTP server facade, if running */
443
+ readonly server: unknown;
444
+ }
445
+
446
+ /**
447
+ * Internal TestRunner returned by createServices().
448
+ */
449
+ export interface TestRunnerService extends PluginTestRunner {
450
+ /** Initialize daemon mode without running tests */
451
+ initialize(options: Record<string, unknown>): Promise<void>;
452
+ /** Run tests through the configured test command */
453
+ run(options: Record<string, unknown>): Promise<unknown>;
454
+ /** Cancel the active test process and stop the server */
455
+ cancel(): Promise<void>;
456
+ }
457
+
458
+ export interface PluginGitInfo {
459
+ branch: string;
460
+ commit: string | null;
461
+ message: string | null;
462
+ prNumber: number | null;
463
+ buildName: string;
464
+ }
465
+
466
+ export interface PluginGit {
467
+ /** Detect branch, commit, commit message, PR number, and build name */
468
+ detect(options?: { buildPrefix?: string }): Promise<PluginGitInfo>;
469
+ }
470
+
395
471
  /**
396
472
  * Stable services interface for plugins.
397
473
  * This is the public API contract - internal services are NOT exposed.
398
474
  */
399
475
  export interface PluginServices {
476
+ git: PluginGit;
400
477
  testRunner: PluginTestRunner;
401
478
  serverManager: PluginServerManager;
402
479
  }
@@ -432,8 +509,6 @@ export interface PluginContext {
432
509
  services: PluginServices;
433
510
  /** Output utilities for logging */
434
511
  output: OutputUtils;
435
- /** @deprecated Use output instead. Alias for backwards compatibility. */
436
- logger: OutputUtils;
437
512
  }
438
513
 
439
514
  /** Create stable plugin services from internal services */
@@ -444,12 +519,31 @@ export function createPluginServices(services: Services): PluginServices;
444
519
  // ============================================================================
445
520
 
446
521
  export interface OutputUtils {
447
- info(message: string): void;
448
- warn(message: string): void;
449
- error(message: string): void;
450
- success(message: string): void;
451
- debug(category: string, ...args: unknown[]): void;
452
- configure(options: { verbose?: boolean }): void;
522
+ info(message: string, data?: Record<string, unknown>): void;
523
+ warn(message: string, data?: Record<string, unknown>): void;
524
+ error(
525
+ message: string,
526
+ error?: Error | null,
527
+ data?: Record<string, unknown>
528
+ ): void;
529
+ success(message: string, data?: Record<string, unknown>): void;
530
+ debug(
531
+ component: string,
532
+ message: string,
533
+ data?: Record<string, unknown>
534
+ ): void;
535
+ configure(options: OutputConfigureOptions): void;
536
+ }
537
+
538
+ export interface OutputConfigureOptions {
539
+ json?: boolean | string;
540
+ jsonFields?: string[] | null;
541
+ logLevel?: 'debug' | 'info' | 'warn' | 'error';
542
+ verbose?: boolean;
543
+ color?: boolean;
544
+ silent?: boolean;
545
+ logFile?: string | null;
546
+ resetTimer?: boolean;
453
547
  }
454
548
 
455
549
  // ============================================================================
@@ -471,8 +565,9 @@ export function vizzlyScreenshot(
471
565
  threshold?: number;
472
566
  minClusterSize?: number;
473
567
  fullPage?: boolean;
568
+ [key: string]: unknown;
474
569
  }
475
- ): Promise<void>;
570
+ ): Promise<ScreenshotResult | null>;
476
571
 
477
572
  /** Configure the Vizzly client */
478
573
  export function configure(config?: {
@@ -510,10 +605,7 @@ export function createTDDService(
510
605
  ): TddService;
511
606
 
512
607
  /** Create all services with dependencies */
513
- export function createServices(
514
- config: VizzlyConfig,
515
- command?: string
516
- ): Services;
608
+ export function createServices(config: VizzlyConfig): Services;
517
609
 
518
610
  /** Load configuration from file and environment */
519
611
  export function loadConfig(options?: { cwd?: string }): Promise<VizzlyConfig>;
@@ -8,16 +8,16 @@ import { EventEmitter } from 'node:events';
8
8
 
9
9
  // Re-export common types
10
10
  export {
11
- VizzlyConfig,
12
- ScreenshotOptions,
13
- UploadOptions,
14
- UploadResult,
11
+ BaselineData,
15
12
  ComparisonResult,
13
+ OutputUtils,
14
+ ScreenshotOptions,
16
15
  TddResults,
17
- BaselineData,
18
- Uploader,
19
16
  TddService,
20
- OutputUtils,
17
+ Uploader,
18
+ UploadOptions,
19
+ UploadResult,
20
+ VizzlyConfig,
21
21
  } from './index';
22
22
 
23
23
  /**
@@ -41,6 +41,18 @@ export class VizzlySDK extends EventEmitter {
41
41
 
42
42
  constructor(config: import('./index').VizzlyConfig, services: unknown);
43
43
 
44
+ /**
45
+ * Reload file config and re-apply current in-memory overrides.
46
+ */
47
+ init(): Promise<import('./index').VizzlyConfig>;
48
+
49
+ /**
50
+ * Merge new config values into the active SDK config.
51
+ */
52
+ updateConfig(
53
+ newConfig: Partial<import('./index').VizzlyConfig>
54
+ ): import('./index').VizzlyConfig;
55
+
44
56
  /**
45
57
  * Start the Vizzly server
46
58
  * @returns Server information including port and URL
@@ -90,6 +102,34 @@ export class VizzlySDK extends EventEmitter {
90
102
  name: string,
91
103
  imageBuffer: Buffer | string
92
104
  ): Promise<import('./index').ComparisonResult>;
105
+
106
+ /**
107
+ * Create an uploader using the SDK config.
108
+ */
109
+ createUploader(options?: {
110
+ upload?: import('./index').UploadConfig;
111
+ signal?: AbortSignal;
112
+ batchSize?: number;
113
+ timeout?: number;
114
+ }): import('./index').Uploader;
115
+
116
+ /**
117
+ * Create a local TDD service using the SDK config.
118
+ */
119
+ createTDDService(options?: {
120
+ workingDir?: string;
121
+ setBaseline?: boolean;
122
+ authService?: unknown;
123
+ }): import('./index').TddService;
124
+
125
+ /**
126
+ * Start a local TDD service.
127
+ */
128
+ startTDD(options?: {
129
+ workingDir?: string;
130
+ setBaseline?: boolean;
131
+ authService?: unknown;
132
+ }): Promise<unknown>;
93
133
  }
94
134
 
95
135
  /**
@@ -14,6 +14,7 @@ import { basename } from 'node:path';
14
14
  export const DEFAULT_BATCH_SIZE = 50;
15
15
  export const DEFAULT_SHA_CHECK_BATCH_SIZE = 100;
16
16
  export const DEFAULT_TIMEOUT = 30000; // 30 seconds
17
+ export const DEFAULT_BUILD_POLL_INTERVAL = 1000; // 1 second
17
18
 
18
19
  // ============================================================================
19
20
  // Validation
@@ -363,8 +364,8 @@ export function resolveTimeout(options, uploadConfig) {
363
364
  * @param {number} timeout - Timeout in ms
364
365
  * @returns {boolean} True if timed out
365
366
  */
366
- export function isTimedOut(startTime, timeout) {
367
- return Date.now() - startTime >= timeout;
367
+ export function isTimedOut(startTime, timeout, now = Date.now()) {
368
+ return now - startTime >= timeout;
368
369
  }
369
370
 
370
371
  /**
@@ -372,8 +373,8 @@ export function isTimedOut(startTime, timeout) {
372
373
  * @param {number} startTime - Start timestamp
373
374
  * @returns {number} Elapsed time in ms
374
375
  */
375
- export function getElapsedTime(startTime) {
376
- return Date.now() - startTime;
376
+ export function getElapsedTime(startTime, now = Date.now()) {
377
+ return now - startTime;
377
378
  }
378
379
 
379
380
  /**