@vizzly-testing/cli 0.39.1 → 0.39.2

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 (195) hide show
  1. package/README.md +8 -0
  2. package/bin/vizzly.js +1 -1
  3. package/dist/{cli.js → adapters/cli/cli.js} +41 -35
  4. package/dist/adapters/cli/setup.js +34 -0
  5. package/dist/{client → adapters/client}/index.js +6 -7
  6. package/dist/adapters/http/cloud-proxy.js +29 -0
  7. package/dist/{server → adapters/http}/http-server.js +12 -13
  8. package/dist/{server-manager/operations.js → adapters/http/server-lifecycle.js} +10 -13
  9. package/dist/{server-manager/index.js → adapters/http/server-manager.js} +23 -27
  10. package/dist/{plugin-api.js → adapters/plugins/plugin-api.js} +1 -1
  11. package/dist/{plugin-loader.js → adapters/plugins/plugin-loader.js} +1 -1
  12. package/dist/{services/index.js → adapters/plugins/services.js} +4 -9
  13. package/dist/adapters/sdk/index.d.ts +89 -0
  14. package/dist/{sdk → adapters/sdk}/index.js +22 -19
  15. package/dist/{commands/api.js → domains/api/api.commands.js} +3 -3
  16. package/dist/{auth/client.js → domains/auth/auth-client.js} +4 -3
  17. package/dist/domains/auth/auth-errors.js +50 -0
  18. package/dist/domains/auth/auth-headers.js +41 -0
  19. package/dist/domains/auth/auth.normalizers.js +50 -0
  20. package/dist/{server/routers/auth.js → domains/auth/auth.routes.js} +9 -9
  21. package/dist/domains/auth/auth.rules.js +19 -0
  22. package/dist/domains/auth/auth.serializers.js +32 -0
  23. package/dist/{auth/operations.js → domains/auth/auth.service.js} +115 -17
  24. package/dist/domains/auth/auth.validations.js +21 -0
  25. package/dist/{api/client.js → domains/auth/cloud-client.js} +11 -18
  26. package/dist/{commands/login.js → domains/auth/login.commands.js} +11 -8
  27. package/dist/{commands/logout.js → domains/auth/logout.commands.js} +6 -4
  28. package/dist/domains/auth/token-store.js +81 -0
  29. package/dist/{commands/whoami.js → domains/auth/whoami.commands.js} +6 -3
  30. package/dist/domains/baselines/baseline-download.js +192 -0
  31. package/dist/domains/baselines/baseline-write.js +22 -0
  32. package/dist/{server/routers/baseline.js → domains/baselines/baseline.routes.js} +3 -3
  33. package/dist/{tdd/services/baseline-manager.js → domains/baselines/baseline.service.js} +1 -39
  34. package/dist/{commands/baselines.js → domains/baselines/baselines.commands.js} +3 -3
  35. package/dist/domains/baselines/cloud-baselines.js +9 -0
  36. package/dist/domains/baselines/cloud-baselines.routes.js +42 -0
  37. package/dist/domains/builds/build.normalizers.js +35 -0
  38. package/dist/domains/builds/build.serializers.js +164 -0
  39. package/dist/domains/builds/build.service.js +119 -0
  40. package/dist/domains/builds/build.validations.js +39 -0
  41. package/dist/{commands/builds.js → domains/builds/builds.commands.js} +6 -74
  42. package/dist/domains/builds/cloud-builds.routes.js +31 -0
  43. package/dist/{commands/finalize.js → domains/builds/finalize.commands.js} +5 -17
  44. package/dist/{commands/status.js → domains/builds/status.commands.js} +9 -120
  45. package/dist/domains/comparisons/cloud-comparisons.js +43 -0
  46. package/dist/{tdd/services/comparison-service.js → domains/comparisons/comparison.service.js} +1 -1
  47. package/dist/{commands/comparisons.js → domains/comparisons/comparisons.commands.js} +7 -5
  48. package/dist/domains/config/config-defaults.js +27 -0
  49. package/dist/{utils → domains/config}/config-discovery.js +1 -9
  50. package/dist/{utils → domains/config}/config-loader.js +10 -9
  51. package/dist/{commands/config-cmd.js → domains/config/config.commands.js} +4 -4
  52. package/dist/{types → domains/config}/config.d.ts +3 -3
  53. package/dist/domains/config/config.normalizers.js +126 -0
  54. package/dist/{server/routers/config.js → domains/config/config.routes.js} +13 -13
  55. package/dist/domains/config/config.rules.js +47 -0
  56. package/dist/domains/config/config.serializers.js +202 -0
  57. package/dist/domains/config/config.service.js +625 -0
  58. package/dist/{utils/config-schema.js → domains/config/config.validations.js} +9 -9
  59. package/dist/{utils/environment-config.js → domains/config/environment.js} +1 -8
  60. package/dist/domains/context/compact-context.validations.js +60 -0
  61. package/dist/domains/context/context-presentation.js +368 -0
  62. package/dist/domains/context/context.commands.js +385 -0
  63. package/dist/domains/context/context.serializers.js +157 -0
  64. package/dist/domains/context/context.service.js +76 -0
  65. package/dist/domains/context/context.validations.js +100 -0
  66. package/dist/{context → domains/context}/local-workspace-provider.js +1 -1
  67. package/dist/{commands/doctor.js → domains/diagnostics/doctor.commands.js} +6 -5
  68. package/dist/{server/routers/health.js → domains/diagnostics/health.routes.js} +11 -20
  69. package/dist/{commands/preview.js → domains/previews/preview.commands.js} +9 -7
  70. package/dist/domains/previews/preview.service.js +37 -0
  71. package/dist/domains/projects/cloud-projects.routes.js +28 -0
  72. package/dist/{commands/orgs.js → domains/projects/orgs.commands.js} +6 -8
  73. package/dist/{utils → domains/projects}/project-binding.js +3 -3
  74. package/dist/domains/projects/project-errors.js +61 -0
  75. package/dist/{utils → domains/projects}/project-link-store.js +2 -2
  76. package/dist/domains/projects/project-requests.js +61 -0
  77. package/dist/{commands/project.js → domains/projects/project.commands.js} +9 -41
  78. package/dist/domains/projects/project.normalizers.js +69 -0
  79. package/dist/{project/operations.js → domains/projects/project.service.js} +89 -16
  80. package/dist/domains/projects/project.validations.js +31 -0
  81. package/dist/{commands/projects.js → domains/projects/projects.commands.js} +6 -8
  82. package/dist/{server/routers/projects.js → domains/projects/projects.routes.js} +5 -5
  83. package/dist/domains/reports/dashboard.routes.js +96 -0
  84. package/dist/{server/routers/events.js → domains/reports/events.routes.js} +14 -36
  85. package/dist/domains/reports/report-assets.routes.js +81 -0
  86. package/dist/domains/reports/report-state.js +51 -0
  87. package/dist/domains/reports/report-store.js +85 -0
  88. package/dist/domains/reports/report.serializers.js +99 -0
  89. package/dist/{services/static-report-generator.js → domains/reports/report.service.js} +11 -33
  90. package/dist/{commands/review.js → domains/review/review.commands.js} +4 -4
  91. package/dist/domains/runs/run-environment.js +69 -0
  92. package/dist/{services/test-runner.js → domains/runs/run-events.js} +11 -9
  93. package/dist/{commands/run.js → domains/runs/run.commands.js} +16 -13
  94. package/dist/domains/runs/run.normalizers.js +8 -0
  95. package/dist/domains/runs/run.rules.js +56 -0
  96. package/dist/domains/runs/run.serializers.js +101 -0
  97. package/dist/{test-runner/operations.js → domains/runs/run.service.js} +35 -7
  98. package/dist/domains/runs/run.validations.js +17 -0
  99. package/dist/{utils/session.js → domains/runs/session-store.js} +3 -2
  100. package/dist/{server/handlers/api-handler.js → domains/screenshots/cloud-handler.js} +4 -4
  101. package/dist/domains/screenshots/cloud-screenshots.js +109 -0
  102. package/dist/{utils/file-helpers.js → domains/screenshots/image-files.js} +1 -1
  103. package/dist/domains/screenshots/screenshot-hash.js +53 -0
  104. package/dist/domains/screenshots/screenshot-server-options.js +28 -0
  105. package/dist/{screenshot-server/operations.js → domains/screenshots/screenshot-server.js} +14 -1
  106. package/dist/domains/screenshots/screenshot.normalizers.js +40 -0
  107. package/dist/{server/routers/screenshot.js → domains/screenshots/screenshot.routes.js} +4 -4
  108. package/dist/domains/screenshots/screenshot.serializers.js +146 -0
  109. package/dist/{utils/security.js → domains/screenshots/screenshot.validations.js} +20 -70
  110. package/dist/{init/agents.js → domains/setup/agent-setup.js} +4 -5
  111. package/dist/{commands/init.js → domains/setup/init.commands.js} +10 -41
  112. package/dist/{server/handlers/tdd-handler.js → domains/tdd/screenshot-handler.js} +46 -281
  113. package/dist/{commands/tdd-daemon.js → domains/tdd/tdd-daemon.commands.js} +6 -6
  114. package/dist/{commands/tdd-screenshots.js → domains/tdd/tdd-screenshots.commands.js} +5 -5
  115. package/dist/{commands/tdd.js → domains/tdd/tdd.commands.js} +16 -15
  116. package/dist/{tdd/tdd-service.js → domains/tdd/tdd.service.js} +81 -405
  117. package/dist/{utils → domains/updates}/cli-update.js +4 -6
  118. package/dist/{utils → domains/updates}/update-notifications.js +7 -7
  119. package/dist/domains/uploads/upload-defaults.js +4 -0
  120. package/dist/domains/uploads/upload-files.js +32 -0
  121. package/dist/domains/uploads/upload-settings.js +21 -0
  122. package/dist/domains/uploads/upload-timing.js +18 -0
  123. package/dist/{commands/upload.js → domains/uploads/upload.commands.js} +11 -9
  124. package/dist/domains/uploads/upload.normalizers.js +60 -0
  125. package/dist/domains/uploads/upload.serializers.js +179 -0
  126. package/dist/{uploader/operations.js → domains/uploads/upload.service.js} +85 -28
  127. package/dist/domains/uploads/upload.validations.js +77 -0
  128. package/dist/index.js +12 -12
  129. package/dist/reporter/reporter-bundle.iife.js +86 -86
  130. package/dist/reporter-ssr/ssr-entry.js +2 -2
  131. package/dist/shared/cloud-protocol.js +171 -0
  132. package/dist/{utils → shared}/file-changes.js +1 -1
  133. package/dist/shared/files.js +9 -0
  134. package/dist/{utils → shared}/global-config.js +7 -78
  135. package/dist/shared/log-level.js +7 -0
  136. package/dist/{utils → shared}/output.js +1 -1
  137. package/dist/{utils → shared}/package-info.js +5 -4
  138. package/dist/shared/paths.js +57 -0
  139. package/package.json +23 -22
  140. package/dist/api/core.js +0 -382
  141. package/dist/api/endpoints.js +0 -421
  142. package/dist/api/index.js +0 -19
  143. package/dist/auth/core.js +0 -237
  144. package/dist/auth/index.js +0 -31
  145. package/dist/commands/context.js +0 -1066
  146. package/dist/config/core.js +0 -439
  147. package/dist/config/operations.js +0 -327
  148. package/dist/project/core.js +0 -211
  149. package/dist/screenshot-server/core.js +0 -161
  150. package/dist/screenshot-server/index.js +0 -11
  151. package/dist/server/routers/assets.js +0 -95
  152. package/dist/server/routers/cloud-proxy.js +0 -146
  153. package/dist/server/routers/dashboard.js +0 -172
  154. package/dist/services/auth-service.js +0 -117
  155. package/dist/services/config-service.js +0 -313
  156. package/dist/services/project-service.js +0 -88
  157. package/dist/test-runner/core.js +0 -293
  158. package/dist/test-runner/index.js +0 -13
  159. package/dist/types/sdk.d.ts +0 -186
  160. package/dist/uploader/core.js +0 -432
  161. package/dist/uploader/index.js +0 -88
  162. /package/dist/{types/client.d.ts → adapters/client/index.d.ts} +0 -0
  163. /package/dist/{server → adapters/http}/middleware/cors.js +0 -0
  164. /package/dist/{server → adapters/http}/middleware/json-parser.js +0 -0
  165. /package/dist/{server → adapters/http}/middleware/response.js +0 -0
  166. /package/dist/{server-manager/core.js → adapters/http/server-options.js} +0 -0
  167. /package/dist/{utils/login-poll-wait.js → domains/auth/login-wait.js} +0 -0
  168. /package/dist/{tdd/metadata → domains/baselines}/baseline-metadata.js +0 -0
  169. /package/dist/{services/build-manager.js → domains/builds/build-info.js} +0 -0
  170. /package/dist/{tdd/services/result-service.js → domains/comparisons/comparison-results.js} +0 -0
  171. /package/dist/{init/core.js → domains/config/config-plan.js} +0 -0
  172. /package/dist/{utils/config-helpers.js → domains/config/config.js} +0 -0
  173. /package/dist/{utils/visual-context-normalizers.js → domains/context/context.normalizers.js} +0 -0
  174. /package/dist/{context → domains/context}/provider-resolver.js +0 -0
  175. /package/dist/{utils/context.js → domains/diagnostics/environment-context.js} +0 -0
  176. /package/dist/{utils/report-data.js → domains/reports/report.normalizers.js} +0 -0
  177. /package/dist/{utils/image-input-detector.js → domains/screenshots/image-input.js} +0 -0
  178. /package/dist/{tdd/core/signature.js → domains/screenshots/screenshot-identity.js} +0 -0
  179. /package/dist/{utils → domains/screenshots}/screenshot-options.js +0 -0
  180. /package/dist/{init/prompts.js → domains/setup/setup-prompts.js} +0 -0
  181. /package/dist/{tdd → domains/tdd}/runtime-context.js +0 -0
  182. /package/dist/{tdd → domains/tdd}/server-registry.js +0 -0
  183. /package/dist/{commands/tdd-daemon-process.js → domains/tdd/tdd-daemon-process.commands.js} +0 -0
  184. /package/dist/{utils → domains/updates}/update-check.js +0 -0
  185. /package/dist/{types/index.d.ts → index.d.ts} +0 -0
  186. /package/dist/{utils → shared}/api-url.js +0 -0
  187. /package/dist/{utils → shared}/async-utils.js +0 -0
  188. /package/dist/{utils → shared}/browser.js +0 -0
  189. /package/dist/{utils → shared}/ci-env.js +0 -0
  190. /package/dist/{utils → shared}/colors.js +0 -0
  191. /package/dist/{utils → shared}/fetch-utils.js +0 -0
  192. /package/dist/{utils → shared}/git.js +0 -0
  193. /package/dist/{utils → shared}/patterns.js +0 -0
  194. /package/dist/{utils → shared}/secret-store.js +0 -0
  195. /package/dist/{errors → shared}/vizzly-error.js +0 -0
@@ -1,186 +0,0 @@
1
- /**
2
- * Vizzly SDK Type Definitions
3
- * Full SDK for custom integrations
4
- * @module @vizzly-testing/cli/sdk
5
- */
6
-
7
- import { EventEmitter } from 'node:events';
8
-
9
- // Re-export common types
10
- export {
11
- BaselineData,
12
- ComparisonResult,
13
- OutputUtils,
14
- ScreenshotOptions,
15
- TddResults,
16
- TddService,
17
- Uploader,
18
- UploadOptions,
19
- UploadResult,
20
- VizzlyConfig,
21
- } from './index';
22
-
23
- /**
24
- * VizzlySDK class - Full SDK for custom integrations
25
- *
26
- * @example
27
- * import { createVizzly } from '@vizzly-testing/cli/sdk';
28
- *
29
- * const vizzly = await createVizzly({
30
- * apiKey: process.env.VIZZLY_TOKEN,
31
- * server: { port: 3003 }
32
- * });
33
- *
34
- * await vizzly.start();
35
- * await vizzly.screenshot('test', buffer);
36
- * await vizzly.upload();
37
- * await vizzly.stop();
38
- */
39
- export class VizzlySDK extends EventEmitter {
40
- config: import('./index').VizzlyConfig;
41
-
42
- constructor(config: import('./index').VizzlyConfig, services: unknown);
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
-
56
- /**
57
- * Start the Vizzly server
58
- * @returns Server information including port and URL
59
- */
60
- start(): Promise<{ port: number; url: string }>;
61
-
62
- /**
63
- * Stop the Vizzly server
64
- */
65
- stop(): Promise<void>;
66
-
67
- /**
68
- * Get current configuration
69
- */
70
- getConfig(): import('./index').VizzlyConfig;
71
-
72
- /**
73
- * Capture a screenshot
74
- * @param name - Screenshot name
75
- * @param imageBuffer - Image data as a Buffer, or a file path to an image
76
- * @param options - Screenshot options
77
- * @throws VizzlyError when server is not running
78
- * @throws VizzlyError when file path is provided but file doesn't exist
79
- */
80
- screenshot(
81
- name: string,
82
- imageBuffer: Buffer | string,
83
- options?: import('./index').ScreenshotOptions
84
- ): Promise<void>;
85
-
86
- /**
87
- * Upload all captured screenshots
88
- * @param options - Upload options
89
- * @returns Upload result with build URL and stats
90
- */
91
- upload(
92
- options?: import('./index').UploadOptions
93
- ): Promise<import('./index').UploadResult>;
94
-
95
- /**
96
- * Run local comparison in TDD mode
97
- * @param name - Screenshot name
98
- * @param imageBuffer - Current image as a Buffer, or a file path to an image
99
- * @returns Comparison result
100
- */
101
- compare(
102
- name: string,
103
- imageBuffer: Buffer | string
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>;
133
- }
134
-
135
- /**
136
- * Create a new Vizzly SDK instance
137
- *
138
- * @param config - Configuration options
139
- * @param options - Additional options
140
- * @returns Configured Vizzly SDK instance
141
- *
142
- * @example
143
- * const vizzly = await createVizzly({
144
- * apiKey: process.env.VIZZLY_TOKEN,
145
- * apiUrl: 'https://app.vizzly.dev',
146
- * server: { port: 3003 }
147
- * });
148
- */
149
- export function createVizzly(
150
- config?: import('./index').VizzlyConfig,
151
- options?: { verbose?: boolean }
152
- ): Promise<VizzlySDK>;
153
-
154
- /** Load configuration from file and environment */
155
- export function loadConfig(options?: {
156
- cwd?: string;
157
- }): Promise<import('./index').VizzlyConfig>;
158
-
159
- /** Output utilities */
160
- export const output: import('./index').OutputUtils;
161
-
162
- /** Create an uploader instance */
163
- export function createUploader(
164
- config?: {
165
- apiKey?: string;
166
- apiUrl?: string;
167
- userAgent?: string;
168
- command?: string;
169
- upload?: import('./index').UploadConfig;
170
- },
171
- options?: {
172
- signal?: AbortSignal;
173
- batchSize?: number;
174
- timeout?: number;
175
- }
176
- ): import('./index').Uploader;
177
-
178
- /** Create a TDD service instance */
179
- export function createTDDService(
180
- config: import('./index').VizzlyConfig,
181
- options?: {
182
- workingDir?: string;
183
- setBaseline?: boolean;
184
- authService?: unknown;
185
- }
186
- ): import('./index').TddService;
@@ -1,432 +0,0 @@
1
- /**
2
- * Uploader Core - Pure functions for upload logic
3
- *
4
- * No I/O, no side effects - just data transformations.
5
- */
6
-
7
- import crypto from 'node:crypto';
8
- import { basename } from 'node:path';
9
-
10
- // ============================================================================
11
- // Constants
12
- // ============================================================================
13
-
14
- export const DEFAULT_BATCH_SIZE = 50;
15
- export const DEFAULT_SHA_CHECK_BATCH_SIZE = 100;
16
- export const DEFAULT_TIMEOUT = 30000; // 30 seconds
17
- export const DEFAULT_BUILD_POLL_INTERVAL = 1000; // 1 second
18
-
19
- // ============================================================================
20
- // Validation
21
- // ============================================================================
22
-
23
- /**
24
- * Validate API key is present
25
- * @param {string|undefined} apiKey - API key to validate
26
- * @returns {{ valid: boolean, error: string|null }}
27
- */
28
- export function validateApiKey(apiKey) {
29
- if (!apiKey) {
30
- return {
31
- valid: false,
32
- error: 'API key is required'
33
- };
34
- }
35
- return {
36
- valid: true,
37
- error: null
38
- };
39
- }
40
-
41
- /**
42
- * Validate screenshots directory path
43
- * @param {string|undefined} screenshotsDir - Directory path
44
- * @returns {{ valid: boolean, error: string|null }}
45
- */
46
- export function validateScreenshotsDir(screenshotsDir) {
47
- if (!screenshotsDir) {
48
- return {
49
- valid: false,
50
- error: 'Screenshots directory is required'
51
- };
52
- }
53
- return {
54
- valid: true,
55
- error: null
56
- };
57
- }
58
-
59
- /**
60
- * Validate directory stats
61
- * @param {Object} stats - fs.stat result
62
- * @param {string} path - Directory path for error message
63
- * @returns {{ valid: boolean, error: string|null }}
64
- */
65
- export function validateDirectoryStats(stats, path) {
66
- if (!stats.isDirectory()) {
67
- return {
68
- valid: false,
69
- error: `${path} is not a directory`
70
- };
71
- }
72
- return {
73
- valid: true,
74
- error: null
75
- };
76
- }
77
-
78
- /**
79
- * Validate files were found
80
- * @param {Array} files - Array of file paths
81
- * @param {string} directory - Directory that was searched
82
- * @returns {{ valid: boolean, error: string|null, context?: Object }}
83
- */
84
- export function validateFilesFound(files, directory) {
85
- if (!files || files.length === 0) {
86
- return {
87
- valid: false,
88
- error: 'No screenshot files found',
89
- context: {
90
- directory,
91
- pattern: '**/*.png'
92
- }
93
- };
94
- }
95
- return {
96
- valid: true,
97
- error: null
98
- };
99
- }
100
-
101
- // ============================================================================
102
- // Browser Extraction
103
- // ============================================================================
104
-
105
- const KNOWN_BROWSERS = ['chrome', 'firefox', 'safari', 'edge', 'webkit'];
106
-
107
- /**
108
- * Extract browser name from filename
109
- * @param {string} filename - The screenshot filename
110
- * @returns {string|null} Browser name or null if not found
111
- */
112
- export function extractBrowserFromFilename(filename) {
113
- let lowerFilename = filename.toLowerCase();
114
- for (let browser of KNOWN_BROWSERS) {
115
- if (lowerFilename.includes(browser)) {
116
- return browser;
117
- }
118
- }
119
- return null;
120
- }
121
-
122
- // ============================================================================
123
- // Build Info Construction
124
- // ============================================================================
125
-
126
- function buildUploadMetadata(options) {
127
- let metadata = {
128
- ...(options.metadata || {})
129
- };
130
- let comparison = {
131
- ...(metadata.comparison || {})
132
- };
133
- if (options.threshold !== undefined) {
134
- comparison.threshold = options.threshold;
135
- }
136
- if (options.minClusterSize !== undefined) {
137
- comparison.minClusterSize = options.minClusterSize;
138
- }
139
- if (Object.keys(comparison).length > 0) {
140
- metadata.comparison = comparison;
141
- }
142
- return Object.keys(metadata).length > 0 ? metadata : undefined;
143
- }
144
-
145
- /**
146
- * Build API build info payload
147
- * @param {Object} options - Upload options
148
- * @param {string} [defaultBranch] - Default branch to use
149
- * @returns {Object} Build info payload
150
- */
151
- export function buildBuildInfo(options, defaultBranch = 'main') {
152
- let buildInfo = {
153
- name: options.buildName || `Upload ${new Date().toISOString()}`,
154
- branch: options.branch || defaultBranch || 'main',
155
- commit_sha: options.commit,
156
- commit_message: options.message,
157
- commit_author_name: options.commitAuthorName,
158
- commit_author_email: options.commitAuthorEmail,
159
- environment: options.environment || 'production',
160
- threshold: options.threshold,
161
- github_pull_request_number: options.pullRequestNumber,
162
- parallel_id: options.parallelId
163
- };
164
- let metadata = buildUploadMetadata(options);
165
- if (metadata) {
166
- buildInfo.metadata = metadata;
167
- }
168
- return buildInfo;
169
- }
170
-
171
- // ============================================================================
172
- // File Metadata Processing
173
- // ============================================================================
174
-
175
- /**
176
- * Compute SHA256 hash of a buffer
177
- * @param {Buffer} buffer - File buffer
178
- * @returns {string} Hex-encoded SHA256 hash
179
- */
180
- export function computeSha256(buffer) {
181
- return crypto.createHash('sha256').update(buffer).digest('hex');
182
- }
183
-
184
- /**
185
- * Build file metadata object
186
- * @param {string} filePath - Path to file
187
- * @param {Buffer} buffer - File contents
188
- * @returns {Object} File metadata
189
- */
190
- export function buildFileMetadata(filePath, buffer) {
191
- return {
192
- path: filePath,
193
- filename: basename(filePath),
194
- buffer,
195
- sha256: computeSha256(buffer)
196
- };
197
- }
198
-
199
- /**
200
- * Convert file metadata to screenshot check format
201
- * @param {Object} file - File metadata
202
- * @returns {Object} Screenshot format for SHA check
203
- */
204
- export function fileToScreenshotFormat(file) {
205
- let browser = extractBrowserFromFilename(file.filename);
206
- return {
207
- sha256: file.sha256,
208
- name: file.filename.replace(/\.png$/, ''),
209
- filename: file.filename,
210
- properties: browser ? {
211
- browser
212
- } : {}
213
- };
214
- }
215
-
216
- /**
217
- * Partition files into those that need upload and those that exist
218
- * @param {Array} fileMetadata - All file metadata
219
- * @param {Set} existingShas - Set of SHAs that already exist
220
- * @returns {{ toUpload: Array, existing: Array }}
221
- */
222
- export function partitionFilesByExistence(fileMetadata, existingShas) {
223
- return {
224
- toUpload: fileMetadata.filter(f => !existingShas.has(f.sha256)),
225
- existing: fileMetadata.filter(f => existingShas.has(f.sha256))
226
- };
227
- }
228
-
229
- // ============================================================================
230
- // Progress Reporting
231
- // ============================================================================
232
-
233
- /**
234
- * Build scanning phase progress
235
- * @param {number} total - Total files found
236
- * @returns {Object} Progress object
237
- */
238
- export function buildScanningProgress(total) {
239
- return {
240
- phase: 'scanning',
241
- message: `Found ${total} screenshots`,
242
- total
243
- };
244
- }
245
-
246
- /**
247
- * Build processing phase progress
248
- * @param {number} current - Current file number
249
- * @param {number} total - Total files
250
- * @returns {Object} Progress object
251
- */
252
- export function buildProcessingProgress(current, total) {
253
- return {
254
- phase: 'processing',
255
- message: 'Processing files',
256
- current,
257
- total
258
- };
259
- }
260
-
261
- /**
262
- * Build deduplication phase progress
263
- * @param {number} toUpload - Files to upload
264
- * @param {number} existing - Existing files
265
- * @param {number} total - Total files
266
- * @returns {Object} Progress object
267
- */
268
- export function buildDeduplicationProgress(toUpload, existing, total) {
269
- return {
270
- phase: 'deduplication',
271
- message: `Checking for duplicates (${toUpload} to upload, ${existing} existing)`,
272
- toUpload,
273
- existing,
274
- total
275
- };
276
- }
277
-
278
- /**
279
- * Build uploading phase progress
280
- * @param {number} current - Current upload number
281
- * @param {number} total - Total to upload
282
- * @returns {Object} Progress object
283
- */
284
- export function buildUploadingProgress(current, total) {
285
- return {
286
- phase: 'uploading',
287
- message: 'Uploading screenshots',
288
- current,
289
- total
290
- };
291
- }
292
-
293
- /**
294
- * Build completed phase progress
295
- * @param {string} buildId - Build ID
296
- * @param {string|null} url - Build URL
297
- * @returns {Object} Progress object
298
- */
299
- export function buildCompletedProgress(buildId, url) {
300
- return {
301
- phase: 'completed',
302
- message: 'Upload completed',
303
- buildId,
304
- url
305
- };
306
- }
307
-
308
- // ============================================================================
309
- // Result Building
310
- // ============================================================================
311
-
312
- /**
313
- * Build successful upload result
314
- * @param {Object} options - Options
315
- * @param {string} options.buildId - Build ID
316
- * @param {string|null} options.url - Build URL
317
- * @param {number} options.total - Total files
318
- * @param {number} options.uploaded - Files uploaded
319
- * @param {number} options.skipped - Files skipped
320
- * @returns {Object} Upload result
321
- */
322
- export function buildUploadResult({
323
- buildId,
324
- url,
325
- total,
326
- uploaded,
327
- skipped
328
- }) {
329
- return {
330
- success: true,
331
- buildId,
332
- url,
333
- stats: {
334
- total,
335
- uploaded,
336
- skipped
337
- }
338
- };
339
- }
340
-
341
- /**
342
- * Build wait result from build response
343
- * @param {Object} build - Build object from API
344
- * @returns {Object} Wait result
345
- */
346
- export function buildWaitResult(build) {
347
- let totalComparisons = build.total_comparisons;
348
- let passedComparisons = build.identical_comparisons;
349
- let failedComparisons = build.changed_comparisons;
350
- let newComparisons = build.new_comparisons;
351
- let identicalComparisons = build.identical_comparisons;
352
- let hasComparisonCounts = build.total_comparisons !== undefined || build.new_comparisons !== undefined || build.changed_comparisons !== undefined || build.identical_comparisons !== undefined;
353
- let result = {
354
- status: build.status,
355
- build,
356
- passedComparisons,
357
- failedComparisons,
358
- newComparisons,
359
- identicalComparisons,
360
- visual_review: build.visual_review || null,
361
- totalComparisons
362
- };
363
- if (hasComparisonCounts) {
364
- result.comparisons = totalComparisons;
365
- }
366
- if (build.url) {
367
- result.url = build.url;
368
- }
369
- return result;
370
- }
371
-
372
- // ============================================================================
373
- // Configuration
374
- // ============================================================================
375
-
376
- /**
377
- * Resolve batch size from options and config
378
- * @param {Object} options - Runtime options
379
- * @param {Object} uploadConfig - Upload configuration
380
- * @returns {number} Resolved batch size
381
- */
382
- export function resolveBatchSize(options, uploadConfig) {
383
- return Number(options?.batchSize ?? uploadConfig?.batchSize ?? DEFAULT_BATCH_SIZE);
384
- }
385
-
386
- /**
387
- * Resolve timeout from options and config
388
- * @param {Object} options - Runtime options
389
- * @param {Object} uploadConfig - Upload configuration
390
- * @returns {number} Resolved timeout in ms
391
- */
392
- export function resolveTimeout(options, uploadConfig) {
393
- return Number(options?.timeout ?? uploadConfig?.timeout ?? DEFAULT_TIMEOUT);
394
- }
395
-
396
- /**
397
- * Check if timeout has been exceeded
398
- * @param {number} startTime - Start timestamp
399
- * @param {number} timeout - Timeout in ms
400
- * @returns {boolean} True if timed out
401
- */
402
- export function isTimedOut(startTime, timeout, now = Date.now()) {
403
- return now - startTime >= timeout;
404
- }
405
-
406
- /**
407
- * Get elapsed time since start
408
- * @param {number} startTime - Start timestamp
409
- * @returns {number} Elapsed time in ms
410
- */
411
- export function getElapsedTime(startTime, now = Date.now()) {
412
- return now - startTime;
413
- }
414
-
415
- /**
416
- * Build glob pattern for screenshots
417
- * @param {string} directory - Base directory
418
- * @returns {string} Glob pattern
419
- */
420
- export function buildScreenshotPattern(directory) {
421
- return `${directory}/**/*.png`;
422
- }
423
-
424
- /**
425
- * Extract status code from error message
426
- * @param {string} errorMessage - Error message
427
- * @returns {string} Status code or 'unknown'
428
- */
429
- export function extractStatusCodeFromError(errorMessage) {
430
- let match = String(errorMessage || '').match(/API request failed: (\d+)/);
431
- return match ? match[1] : 'unknown';
432
- }
@@ -1,88 +0,0 @@
1
- /**
2
- * Uploader Module
3
- *
4
- * Exports pure functions (core), I/O operations, and the public uploader
5
- * factory for screenshot uploading.
6
- */
7
-
8
- import { readFile, stat } from 'node:fs/promises';
9
- import { glob } from 'glob';
10
- import { checkShas, createApiClient, createBuild } from '../api/index.js';
11
- import { TimeoutError, UploadError, ValidationError } from '../errors/vizzly-error.js';
12
- import { getDefaultBranch } from '../utils/git.js';
13
- import * as output from '../utils/output.js';
14
- import { resolveBatchSize, resolveTimeout } from './core.js';
15
- import { upload as uploadOperation, waitForBuild as waitForBuildOperation } from './operations.js';
16
- export function createUploader({
17
- apiKey,
18
- apiUrl,
19
- userAgent,
20
- command,
21
- upload: uploadConfig = {}
22
- } = {}, options = {}) {
23
- let signal = options.signal || new AbortController().signal;
24
- let client = createApiClient({
25
- baseUrl: apiUrl,
26
- token: apiKey,
27
- command: command || 'upload',
28
- sdkUserAgent: userAgent,
29
- allowNoToken: true
30
- });
31
- let batchSize = resolveBatchSize(options, uploadConfig);
32
- let timeout = resolveTimeout(options, uploadConfig);
33
- let deps = options.deps || {
34
- client,
35
- createBuild,
36
- getDefaultBranch,
37
- glob,
38
- readFile,
39
- stat,
40
- checkShas,
41
- createError: (message, code, context) => {
42
- let error = new UploadError(message, context);
43
- error.code = code;
44
- return error;
45
- },
46
- createValidationError: (message, context) => new ValidationError(message, context),
47
- createUploadError: (message, context) => new UploadError(message, context),
48
- createTimeoutError: (message, context) => new TimeoutError(message, context),
49
- output
50
- };
51
- async function upload(uploadOptions) {
52
- return uploadOperation({
53
- uploadOptions,
54
- config: {
55
- apiKey,
56
- apiUrl
57
- },
58
- signal,
59
- batchSize,
60
- deps: {
61
- ...deps,
62
- client: deps.client || client
63
- }
64
- });
65
- }
66
- async function waitForBuild(buildId, waitTimeout = timeout) {
67
- return waitForBuildOperation({
68
- buildId,
69
- timeout: waitTimeout,
70
- signal,
71
- client: deps.client || client,
72
- deps: {
73
- createError: deps.createError,
74
- createTimeoutError: deps.createTimeoutError
75
- }
76
- });
77
- }
78
- return {
79
- upload,
80
- waitForBuild
81
- };
82
- }
83
-
84
- // Core - pure functions
85
- export { buildBuildInfo, buildCompletedProgress, buildDeduplicationProgress, buildFileMetadata, buildProcessingProgress, buildScanningProgress, buildScreenshotPattern, buildUploadingProgress, buildUploadResult, buildWaitResult, computeSha256, DEFAULT_BATCH_SIZE, DEFAULT_BUILD_POLL_INTERVAL, DEFAULT_SHA_CHECK_BATCH_SIZE, DEFAULT_TIMEOUT, extractBrowserFromFilename, extractStatusCodeFromError, fileToScreenshotFormat, getElapsedTime, isTimedOut, partitionFilesByExistence, resolveBatchSize, resolveTimeout, validateApiKey, validateDirectoryStats, validateFilesFound, validateScreenshotsDir } from './core.js';
86
-
87
- // Operations - I/O with dependency injection
88
- export { checkExistingFiles, findScreenshots, processFiles, upload, uploadFiles, waitForBuild } from './operations.js';