@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.
- package/README.md +8 -0
- package/bin/vizzly.js +1 -1
- package/dist/{cli.js → adapters/cli/cli.js} +41 -35
- package/dist/adapters/cli/setup.js +34 -0
- package/dist/{client → adapters/client}/index.js +6 -7
- package/dist/adapters/http/cloud-proxy.js +29 -0
- package/dist/{server → adapters/http}/http-server.js +12 -13
- package/dist/{server-manager/operations.js → adapters/http/server-lifecycle.js} +10 -13
- package/dist/{server-manager/index.js → adapters/http/server-manager.js} +23 -27
- package/dist/{plugin-api.js → adapters/plugins/plugin-api.js} +1 -1
- package/dist/{plugin-loader.js → adapters/plugins/plugin-loader.js} +1 -1
- package/dist/{services/index.js → adapters/plugins/services.js} +4 -9
- package/dist/adapters/sdk/index.d.ts +89 -0
- package/dist/{sdk → adapters/sdk}/index.js +22 -19
- package/dist/{commands/api.js → domains/api/api.commands.js} +3 -3
- package/dist/{auth/client.js → domains/auth/auth-client.js} +4 -3
- package/dist/domains/auth/auth-errors.js +50 -0
- package/dist/domains/auth/auth-headers.js +41 -0
- package/dist/domains/auth/auth.normalizers.js +50 -0
- package/dist/{server/routers/auth.js → domains/auth/auth.routes.js} +9 -9
- package/dist/domains/auth/auth.rules.js +19 -0
- package/dist/domains/auth/auth.serializers.js +32 -0
- package/dist/{auth/operations.js → domains/auth/auth.service.js} +115 -17
- package/dist/domains/auth/auth.validations.js +21 -0
- package/dist/{api/client.js → domains/auth/cloud-client.js} +11 -18
- package/dist/{commands/login.js → domains/auth/login.commands.js} +11 -8
- package/dist/{commands/logout.js → domains/auth/logout.commands.js} +6 -4
- package/dist/domains/auth/token-store.js +81 -0
- package/dist/{commands/whoami.js → domains/auth/whoami.commands.js} +6 -3
- package/dist/domains/baselines/baseline-download.js +192 -0
- package/dist/domains/baselines/baseline-write.js +22 -0
- package/dist/{server/routers/baseline.js → domains/baselines/baseline.routes.js} +3 -3
- package/dist/{tdd/services/baseline-manager.js → domains/baselines/baseline.service.js} +1 -39
- package/dist/{commands/baselines.js → domains/baselines/baselines.commands.js} +3 -3
- package/dist/domains/baselines/cloud-baselines.js +9 -0
- package/dist/domains/baselines/cloud-baselines.routes.js +42 -0
- package/dist/domains/builds/build.normalizers.js +35 -0
- package/dist/domains/builds/build.serializers.js +164 -0
- package/dist/domains/builds/build.service.js +119 -0
- package/dist/domains/builds/build.validations.js +39 -0
- package/dist/{commands/builds.js → domains/builds/builds.commands.js} +6 -74
- package/dist/domains/builds/cloud-builds.routes.js +31 -0
- package/dist/{commands/finalize.js → domains/builds/finalize.commands.js} +5 -17
- package/dist/{commands/status.js → domains/builds/status.commands.js} +9 -120
- package/dist/domains/comparisons/cloud-comparisons.js +43 -0
- package/dist/{tdd/services/comparison-service.js → domains/comparisons/comparison.service.js} +1 -1
- package/dist/{commands/comparisons.js → domains/comparisons/comparisons.commands.js} +7 -5
- package/dist/domains/config/config-defaults.js +27 -0
- package/dist/{utils → domains/config}/config-discovery.js +1 -9
- package/dist/{utils → domains/config}/config-loader.js +10 -9
- package/dist/{commands/config-cmd.js → domains/config/config.commands.js} +4 -4
- package/dist/{types → domains/config}/config.d.ts +3 -3
- package/dist/domains/config/config.normalizers.js +126 -0
- package/dist/{server/routers/config.js → domains/config/config.routes.js} +13 -13
- package/dist/domains/config/config.rules.js +47 -0
- package/dist/domains/config/config.serializers.js +202 -0
- package/dist/domains/config/config.service.js +625 -0
- package/dist/{utils/config-schema.js → domains/config/config.validations.js} +9 -9
- package/dist/{utils/environment-config.js → domains/config/environment.js} +1 -8
- package/dist/domains/context/compact-context.validations.js +60 -0
- package/dist/domains/context/context-presentation.js +368 -0
- package/dist/domains/context/context.commands.js +385 -0
- package/dist/domains/context/context.serializers.js +157 -0
- package/dist/domains/context/context.service.js +76 -0
- package/dist/domains/context/context.validations.js +100 -0
- package/dist/{context → domains/context}/local-workspace-provider.js +1 -1
- package/dist/{commands/doctor.js → domains/diagnostics/doctor.commands.js} +6 -5
- package/dist/{server/routers/health.js → domains/diagnostics/health.routes.js} +11 -20
- package/dist/{commands/preview.js → domains/previews/preview.commands.js} +9 -7
- package/dist/domains/previews/preview.service.js +37 -0
- package/dist/domains/projects/cloud-projects.routes.js +28 -0
- package/dist/{commands/orgs.js → domains/projects/orgs.commands.js} +6 -8
- package/dist/{utils → domains/projects}/project-binding.js +3 -3
- package/dist/domains/projects/project-errors.js +61 -0
- package/dist/{utils → domains/projects}/project-link-store.js +2 -2
- package/dist/domains/projects/project-requests.js +61 -0
- package/dist/{commands/project.js → domains/projects/project.commands.js} +9 -41
- package/dist/domains/projects/project.normalizers.js +69 -0
- package/dist/{project/operations.js → domains/projects/project.service.js} +89 -16
- package/dist/domains/projects/project.validations.js +31 -0
- package/dist/{commands/projects.js → domains/projects/projects.commands.js} +6 -8
- package/dist/{server/routers/projects.js → domains/projects/projects.routes.js} +5 -5
- package/dist/domains/reports/dashboard.routes.js +96 -0
- package/dist/{server/routers/events.js → domains/reports/events.routes.js} +14 -36
- package/dist/domains/reports/report-assets.routes.js +81 -0
- package/dist/domains/reports/report-state.js +51 -0
- package/dist/domains/reports/report-store.js +85 -0
- package/dist/domains/reports/report.serializers.js +99 -0
- package/dist/{services/static-report-generator.js → domains/reports/report.service.js} +11 -33
- package/dist/{commands/review.js → domains/review/review.commands.js} +4 -4
- package/dist/domains/runs/run-environment.js +69 -0
- package/dist/{services/test-runner.js → domains/runs/run-events.js} +11 -9
- package/dist/{commands/run.js → domains/runs/run.commands.js} +16 -13
- package/dist/domains/runs/run.normalizers.js +8 -0
- package/dist/domains/runs/run.rules.js +56 -0
- package/dist/domains/runs/run.serializers.js +101 -0
- package/dist/{test-runner/operations.js → domains/runs/run.service.js} +35 -7
- package/dist/domains/runs/run.validations.js +17 -0
- package/dist/{utils/session.js → domains/runs/session-store.js} +3 -2
- package/dist/{server/handlers/api-handler.js → domains/screenshots/cloud-handler.js} +4 -4
- package/dist/domains/screenshots/cloud-screenshots.js +109 -0
- package/dist/{utils/file-helpers.js → domains/screenshots/image-files.js} +1 -1
- package/dist/domains/screenshots/screenshot-hash.js +53 -0
- package/dist/domains/screenshots/screenshot-server-options.js +28 -0
- package/dist/{screenshot-server/operations.js → domains/screenshots/screenshot-server.js} +14 -1
- package/dist/domains/screenshots/screenshot.normalizers.js +40 -0
- package/dist/{server/routers/screenshot.js → domains/screenshots/screenshot.routes.js} +4 -4
- package/dist/domains/screenshots/screenshot.serializers.js +146 -0
- package/dist/{utils/security.js → domains/screenshots/screenshot.validations.js} +20 -70
- package/dist/{init/agents.js → domains/setup/agent-setup.js} +4 -5
- package/dist/{commands/init.js → domains/setup/init.commands.js} +10 -41
- package/dist/{server/handlers/tdd-handler.js → domains/tdd/screenshot-handler.js} +46 -281
- package/dist/{commands/tdd-daemon.js → domains/tdd/tdd-daemon.commands.js} +6 -6
- package/dist/{commands/tdd-screenshots.js → domains/tdd/tdd-screenshots.commands.js} +5 -5
- package/dist/{commands/tdd.js → domains/tdd/tdd.commands.js} +16 -15
- package/dist/{tdd/tdd-service.js → domains/tdd/tdd.service.js} +81 -405
- package/dist/{utils → domains/updates}/cli-update.js +4 -6
- package/dist/{utils → domains/updates}/update-notifications.js +7 -7
- package/dist/domains/uploads/upload-defaults.js +4 -0
- package/dist/domains/uploads/upload-files.js +32 -0
- package/dist/domains/uploads/upload-settings.js +21 -0
- package/dist/domains/uploads/upload-timing.js +18 -0
- package/dist/{commands/upload.js → domains/uploads/upload.commands.js} +11 -9
- package/dist/domains/uploads/upload.normalizers.js +60 -0
- package/dist/domains/uploads/upload.serializers.js +179 -0
- package/dist/{uploader/operations.js → domains/uploads/upload.service.js} +85 -28
- package/dist/domains/uploads/upload.validations.js +77 -0
- package/dist/index.js +12 -12
- package/dist/reporter/reporter-bundle.iife.js +86 -86
- package/dist/reporter-ssr/ssr-entry.js +2 -2
- package/dist/shared/cloud-protocol.js +171 -0
- package/dist/{utils → shared}/file-changes.js +1 -1
- package/dist/shared/files.js +9 -0
- package/dist/{utils → shared}/global-config.js +7 -78
- package/dist/shared/log-level.js +7 -0
- package/dist/{utils → shared}/output.js +1 -1
- package/dist/{utils → shared}/package-info.js +5 -4
- package/dist/shared/paths.js +57 -0
- package/package.json +23 -22
- package/dist/api/core.js +0 -382
- package/dist/api/endpoints.js +0 -421
- package/dist/api/index.js +0 -19
- package/dist/auth/core.js +0 -237
- package/dist/auth/index.js +0 -31
- package/dist/commands/context.js +0 -1066
- package/dist/config/core.js +0 -439
- package/dist/config/operations.js +0 -327
- package/dist/project/core.js +0 -211
- package/dist/screenshot-server/core.js +0 -161
- package/dist/screenshot-server/index.js +0 -11
- package/dist/server/routers/assets.js +0 -95
- package/dist/server/routers/cloud-proxy.js +0 -146
- package/dist/server/routers/dashboard.js +0 -172
- package/dist/services/auth-service.js +0 -117
- package/dist/services/config-service.js +0 -313
- package/dist/services/project-service.js +0 -88
- package/dist/test-runner/core.js +0 -293
- package/dist/test-runner/index.js +0 -13
- package/dist/types/sdk.d.ts +0 -186
- package/dist/uploader/core.js +0 -432
- package/dist/uploader/index.js +0 -88
- /package/dist/{types/client.d.ts → adapters/client/index.d.ts} +0 -0
- /package/dist/{server → adapters/http}/middleware/cors.js +0 -0
- /package/dist/{server → adapters/http}/middleware/json-parser.js +0 -0
- /package/dist/{server → adapters/http}/middleware/response.js +0 -0
- /package/dist/{server-manager/core.js → adapters/http/server-options.js} +0 -0
- /package/dist/{utils/login-poll-wait.js → domains/auth/login-wait.js} +0 -0
- /package/dist/{tdd/metadata → domains/baselines}/baseline-metadata.js +0 -0
- /package/dist/{services/build-manager.js → domains/builds/build-info.js} +0 -0
- /package/dist/{tdd/services/result-service.js → domains/comparisons/comparison-results.js} +0 -0
- /package/dist/{init/core.js → domains/config/config-plan.js} +0 -0
- /package/dist/{utils/config-helpers.js → domains/config/config.js} +0 -0
- /package/dist/{utils/visual-context-normalizers.js → domains/context/context.normalizers.js} +0 -0
- /package/dist/{context → domains/context}/provider-resolver.js +0 -0
- /package/dist/{utils/context.js → domains/diagnostics/environment-context.js} +0 -0
- /package/dist/{utils/report-data.js → domains/reports/report.normalizers.js} +0 -0
- /package/dist/{utils/image-input-detector.js → domains/screenshots/image-input.js} +0 -0
- /package/dist/{tdd/core/signature.js → domains/screenshots/screenshot-identity.js} +0 -0
- /package/dist/{utils → domains/screenshots}/screenshot-options.js +0 -0
- /package/dist/{init/prompts.js → domains/setup/setup-prompts.js} +0 -0
- /package/dist/{tdd → domains/tdd}/runtime-context.js +0 -0
- /package/dist/{tdd → domains/tdd}/server-registry.js +0 -0
- /package/dist/{commands/tdd-daemon-process.js → domains/tdd/tdd-daemon-process.commands.js} +0 -0
- /package/dist/{utils → domains/updates}/update-check.js +0 -0
- /package/dist/{types/index.d.ts → index.d.ts} +0 -0
- /package/dist/{utils → shared}/api-url.js +0 -0
- /package/dist/{utils → shared}/async-utils.js +0 -0
- /package/dist/{utils → shared}/browser.js +0 -0
- /package/dist/{utils → shared}/ci-env.js +0 -0
- /package/dist/{utils → shared}/colors.js +0 -0
- /package/dist/{utils → shared}/fetch-utils.js +0 -0
- /package/dist/{utils → shared}/git.js +0 -0
- /package/dist/{utils → shared}/patterns.js +0 -0
- /package/dist/{utils → shared}/secret-store.js +0 -0
- /package/dist/{errors → shared}/vizzly-error.js +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Compute SHA256 hash of a buffer
|
|
5
|
+
* @param {Buffer} buffer - File buffer
|
|
6
|
+
* @returns {string} Hex-encoded SHA256 hash
|
|
7
|
+
*/
|
|
8
|
+
export function computeSha256(buffer) {
|
|
9
|
+
return crypto.createHash('sha256').update(buffer).digest('hex');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Partition files into those that need upload and those that exist
|
|
14
|
+
* @param {Array} fileMetadata - All file metadata
|
|
15
|
+
* @param {Set} existingShas - Set of SHAs that already exist
|
|
16
|
+
* @returns {{ toUpload: Array, existing: Array }}
|
|
17
|
+
*/
|
|
18
|
+
export function partitionFilesByExistence(fileMetadata, existingShas) {
|
|
19
|
+
return {
|
|
20
|
+
toUpload: fileMetadata.filter(f => !existingShas.has(f.sha256)),
|
|
21
|
+
existing: fileMetadata.filter(f => existingShas.has(f.sha256))
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Build glob pattern for screenshots
|
|
27
|
+
* @param {string} directory - Base directory
|
|
28
|
+
* @returns {string} Glob pattern
|
|
29
|
+
*/
|
|
30
|
+
export function buildScreenshotPattern(directory) {
|
|
31
|
+
return `${directory}/**/*.png`;
|
|
32
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { DEFAULT_BATCH_SIZE, DEFAULT_TIMEOUT } from './upload-defaults.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Resolve batch size from options and config
|
|
5
|
+
* @param {Object} options - Runtime options
|
|
6
|
+
* @param {Object} uploadConfig - Upload configuration
|
|
7
|
+
* @returns {number} Resolved batch size
|
|
8
|
+
*/
|
|
9
|
+
export function resolveBatchSize(options, uploadConfig) {
|
|
10
|
+
return Number(options?.batchSize ?? uploadConfig?.batchSize ?? DEFAULT_BATCH_SIZE);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Resolve timeout from options and config
|
|
15
|
+
* @param {Object} options - Runtime options
|
|
16
|
+
* @param {Object} uploadConfig - Upload configuration
|
|
17
|
+
* @returns {number} Resolved timeout in ms
|
|
18
|
+
*/
|
|
19
|
+
export function resolveTimeout(options, uploadConfig) {
|
|
20
|
+
return Number(options?.timeout ?? uploadConfig?.timeout ?? DEFAULT_TIMEOUT);
|
|
21
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if timeout has been exceeded
|
|
3
|
+
* @param {number} startTime - Start timestamp
|
|
4
|
+
* @param {number} timeout - Timeout in ms
|
|
5
|
+
* @returns {boolean} True if timed out
|
|
6
|
+
*/
|
|
7
|
+
export function isTimedOut(startTime, timeout, now = Date.now()) {
|
|
8
|
+
return now - startTime >= timeout;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get elapsed time since start
|
|
13
|
+
* @param {number} startTime - Start timestamp
|
|
14
|
+
* @returns {number} Elapsed time in ms
|
|
15
|
+
*/
|
|
16
|
+
export function getElapsedTime(startTime, now = Date.now()) {
|
|
17
|
+
return now - startTime;
|
|
18
|
+
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
1
|
+
import { getAppBaseUrl } from '../../shared/api-url.js';
|
|
2
|
+
import { detectBranch as defaultDetectBranch, detectCommit as defaultDetectCommit, detectCommitAuthor as defaultDetectCommitAuthor, detectCommitMessage as defaultDetectCommitMessage, detectPullRequestNumber as defaultDetectPullRequestNumber, generateBuildNameWithGit as defaultGenerateBuildNameWithGit } from '../../shared/git.js';
|
|
3
|
+
import * as defaultOutput from '../../shared/output.js';
|
|
4
|
+
import { createApiClient as defaultCreateApiClient } from '../auth/cloud-client.js';
|
|
5
|
+
import { finalizeBuild as defaultFinalizeBuild } from '../builds/build.service.js';
|
|
6
|
+
import { CONFIG_DEFAULTS } from '../config/config-defaults.js';
|
|
7
|
+
import { loadConfig as defaultLoadConfig } from '../config/config-loader.js';
|
|
8
|
+
import { getTokenContext as defaultGetTokenContext } from '../context/context.service.js';
|
|
9
|
+
import { writeSession as defaultWriteSession } from '../runs/session-store.js';
|
|
10
|
+
import { createUploader as defaultCreateUploader } from './upload.service.js';
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* Construct proper build URL with org/project context
|
|
@@ -380,7 +382,7 @@ export async function uploadCommand(screenshotsPath, options = {}, globalOptions
|
|
|
380
382
|
* @param {Object} options - Command options
|
|
381
383
|
*/
|
|
382
384
|
export function validateUploadOptions(screenshotsPath, options) {
|
|
383
|
-
|
|
385
|
+
let errors = [];
|
|
384
386
|
if (!screenshotsPath) {
|
|
385
387
|
errors.push('Screenshots path is required');
|
|
386
388
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
2
|
+
import { computeSha256 } from './upload-files.js';
|
|
3
|
+
let KNOWN_BROWSERS = ['chrome', 'firefox', 'safari', 'edge', 'webkit'];
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Extract browser name from filename
|
|
7
|
+
* @param {string} filename - The screenshot filename
|
|
8
|
+
* @returns {string|null} Browser name or null if not found
|
|
9
|
+
*/
|
|
10
|
+
export function extractBrowserFromFilename(filename) {
|
|
11
|
+
let lowerFilename = filename.toLowerCase();
|
|
12
|
+
for (let browser of KNOWN_BROWSERS) {
|
|
13
|
+
if (lowerFilename.includes(browser)) {
|
|
14
|
+
return browser;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Build file metadata object
|
|
22
|
+
* @param {string} filePath - Path to file
|
|
23
|
+
* @param {Buffer} buffer - File contents
|
|
24
|
+
* @returns {Object} File metadata
|
|
25
|
+
*/
|
|
26
|
+
export function buildFileMetadata(filePath, buffer) {
|
|
27
|
+
return {
|
|
28
|
+
path: filePath,
|
|
29
|
+
filename: basename(filePath),
|
|
30
|
+
buffer,
|
|
31
|
+
sha256: computeSha256(buffer)
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Convert file metadata to screenshot check format
|
|
37
|
+
* @param {Object} file - File metadata
|
|
38
|
+
* @returns {Object} Screenshot format for SHA check
|
|
39
|
+
*/
|
|
40
|
+
export function fileToScreenshotFormat(file) {
|
|
41
|
+
let browser = extractBrowserFromFilename(file.filename);
|
|
42
|
+
return {
|
|
43
|
+
sha256: file.sha256,
|
|
44
|
+
name: file.filename.replace(/\.png$/, ''),
|
|
45
|
+
filename: file.filename,
|
|
46
|
+
properties: browser ? {
|
|
47
|
+
browser
|
|
48
|
+
} : {}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Extract status code from error message
|
|
54
|
+
* @param {string} errorMessage - Error message
|
|
55
|
+
* @returns {string} Status code or 'unknown'
|
|
56
|
+
*/
|
|
57
|
+
export function extractStatusCodeFromError(errorMessage) {
|
|
58
|
+
let match = String(errorMessage || '').match(/API request failed: (\d+)/);
|
|
59
|
+
return match ? match[1] : 'unknown';
|
|
60
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
function buildUploadMetadata(options) {
|
|
2
|
+
let metadata = {
|
|
3
|
+
...(options.metadata || {})
|
|
4
|
+
};
|
|
5
|
+
let comparison = {
|
|
6
|
+
...(metadata.comparison || {})
|
|
7
|
+
};
|
|
8
|
+
if (options.threshold !== undefined) {
|
|
9
|
+
comparison.threshold = options.threshold;
|
|
10
|
+
}
|
|
11
|
+
if (options.minClusterSize !== undefined) {
|
|
12
|
+
comparison.minClusterSize = options.minClusterSize;
|
|
13
|
+
}
|
|
14
|
+
if (Object.keys(comparison).length > 0) {
|
|
15
|
+
metadata.comparison = comparison;
|
|
16
|
+
}
|
|
17
|
+
return Object.keys(metadata).length > 0 ? metadata : undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Build API build info payload
|
|
22
|
+
* @param {Object} options - Upload options
|
|
23
|
+
* @param {string} [defaultBranch] - Default branch to use
|
|
24
|
+
* @returns {Object} Build info payload
|
|
25
|
+
*/
|
|
26
|
+
export function buildBuildInfo(options, defaultBranch = 'main') {
|
|
27
|
+
let buildInfo = {
|
|
28
|
+
name: options.buildName || `Upload ${new Date().toISOString()}`,
|
|
29
|
+
branch: options.branch || defaultBranch || 'main',
|
|
30
|
+
commit_sha: options.commit,
|
|
31
|
+
commit_message: options.message,
|
|
32
|
+
commit_author_name: options.commitAuthorName,
|
|
33
|
+
commit_author_email: options.commitAuthorEmail,
|
|
34
|
+
environment: options.environment || 'production',
|
|
35
|
+
threshold: options.threshold,
|
|
36
|
+
github_pull_request_number: options.pullRequestNumber,
|
|
37
|
+
parallel_id: options.parallelId
|
|
38
|
+
};
|
|
39
|
+
let metadata = buildUploadMetadata(options);
|
|
40
|
+
if (metadata) {
|
|
41
|
+
buildInfo.metadata = metadata;
|
|
42
|
+
}
|
|
43
|
+
return buildInfo;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Build scanning phase progress
|
|
48
|
+
* @param {number} total - Total files found
|
|
49
|
+
* @returns {Object} Progress object
|
|
50
|
+
*/
|
|
51
|
+
export function buildScanningProgress(total) {
|
|
52
|
+
return {
|
|
53
|
+
phase: 'scanning',
|
|
54
|
+
message: `Found ${total} screenshots`,
|
|
55
|
+
total
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Build processing phase progress
|
|
61
|
+
* @param {number} current - Current file number
|
|
62
|
+
* @param {number} total - Total files
|
|
63
|
+
* @returns {Object} Progress object
|
|
64
|
+
*/
|
|
65
|
+
export function buildProcessingProgress(current, total) {
|
|
66
|
+
return {
|
|
67
|
+
phase: 'processing',
|
|
68
|
+
message: 'Processing files',
|
|
69
|
+
current,
|
|
70
|
+
total
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Build deduplication phase progress
|
|
76
|
+
* @param {number} toUpload - Files to upload
|
|
77
|
+
* @param {number} existing - Existing files
|
|
78
|
+
* @param {number} total - Total files
|
|
79
|
+
* @returns {Object} Progress object
|
|
80
|
+
*/
|
|
81
|
+
export function buildDeduplicationProgress(toUpload, existing, total) {
|
|
82
|
+
return {
|
|
83
|
+
phase: 'deduplication',
|
|
84
|
+
message: `Checking for duplicates (${toUpload} to upload, ${existing} existing)`,
|
|
85
|
+
toUpload,
|
|
86
|
+
existing,
|
|
87
|
+
total
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Build uploading phase progress
|
|
93
|
+
* @param {number} current - Current upload number
|
|
94
|
+
* @param {number} total - Total to upload
|
|
95
|
+
* @returns {Object} Progress object
|
|
96
|
+
*/
|
|
97
|
+
export function buildUploadingProgress(current, total) {
|
|
98
|
+
return {
|
|
99
|
+
phase: 'uploading',
|
|
100
|
+
message: 'Uploading screenshots',
|
|
101
|
+
current,
|
|
102
|
+
total
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Build completed phase progress
|
|
108
|
+
* @param {string} buildId - Build ID
|
|
109
|
+
* @param {string|null} url - Build URL
|
|
110
|
+
* @returns {Object} Progress object
|
|
111
|
+
*/
|
|
112
|
+
export function buildCompletedProgress(buildId, url) {
|
|
113
|
+
return {
|
|
114
|
+
phase: 'completed',
|
|
115
|
+
message: 'Upload completed',
|
|
116
|
+
buildId,
|
|
117
|
+
url
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Build successful upload result
|
|
123
|
+
* @param {Object} options - Options
|
|
124
|
+
* @param {string} options.buildId - Build ID
|
|
125
|
+
* @param {string|null} options.url - Build URL
|
|
126
|
+
* @param {number} options.total - Total files
|
|
127
|
+
* @param {number} options.uploaded - Files uploaded
|
|
128
|
+
* @param {number} options.skipped - Files skipped
|
|
129
|
+
* @returns {Object} Upload result
|
|
130
|
+
*/
|
|
131
|
+
export function buildUploadResult({
|
|
132
|
+
buildId,
|
|
133
|
+
url,
|
|
134
|
+
total,
|
|
135
|
+
uploaded,
|
|
136
|
+
skipped
|
|
137
|
+
}) {
|
|
138
|
+
return {
|
|
139
|
+
success: true,
|
|
140
|
+
buildId,
|
|
141
|
+
url,
|
|
142
|
+
stats: {
|
|
143
|
+
total,
|
|
144
|
+
uploaded,
|
|
145
|
+
skipped
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Build wait result from build response
|
|
152
|
+
* @param {Object} build - Build object from API
|
|
153
|
+
* @returns {Object} Wait result
|
|
154
|
+
*/
|
|
155
|
+
export function buildWaitResult(build) {
|
|
156
|
+
let totalComparisons = build.total_comparisons;
|
|
157
|
+
let passedComparisons = build.identical_comparisons;
|
|
158
|
+
let failedComparisons = build.changed_comparisons;
|
|
159
|
+
let newComparisons = build.new_comparisons;
|
|
160
|
+
let identicalComparisons = build.identical_comparisons;
|
|
161
|
+
let hasComparisonCounts = build.total_comparisons !== undefined || build.new_comparisons !== undefined || build.changed_comparisons !== undefined || build.identical_comparisons !== undefined;
|
|
162
|
+
let result = {
|
|
163
|
+
status: build.status,
|
|
164
|
+
build,
|
|
165
|
+
passedComparisons,
|
|
166
|
+
failedComparisons,
|
|
167
|
+
newComparisons,
|
|
168
|
+
identicalComparisons,
|
|
169
|
+
visual_review: build.visual_review || null,
|
|
170
|
+
totalComparisons
|
|
171
|
+
};
|
|
172
|
+
if (hasComparisonCounts) {
|
|
173
|
+
result.comparisons = totalComparisons;
|
|
174
|
+
}
|
|
175
|
+
if (build.url) {
|
|
176
|
+
result.url = build.url;
|
|
177
|
+
}
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
@@ -1,10 +1,91 @@
|
|
|
1
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
2
|
+
import { glob } from 'glob';
|
|
3
|
+
import { getDefaultBranch } from '../../shared/git.js';
|
|
4
|
+
import * as output from '../../shared/output.js';
|
|
5
|
+
import { TimeoutError, UploadError, ValidationError } from '../../shared/vizzly-error.js';
|
|
6
|
+
import { createApiClient } from '../auth/cloud-client.js';
|
|
7
|
+
import { createBuild } from '../builds/build.service.js';
|
|
8
|
+
import { checkShas } from '../screenshots/cloud-screenshots.js';
|
|
9
|
+
import { buildFileMetadata, extractStatusCodeFromError, fileToScreenshotFormat } from './upload.normalizers.js';
|
|
10
|
+
import { buildBuildInfo, buildCompletedProgress, buildDeduplicationProgress, buildProcessingProgress, buildScanningProgress, buildUploadingProgress, buildUploadResult, buildWaitResult } from './upload.serializers.js';
|
|
11
|
+
import { validateApiKey, validateDirectoryStats, validateFilesFound, validateScreenshotsDir } from './upload.validations.js';
|
|
12
|
+
import { DEFAULT_BUILD_POLL_INTERVAL, DEFAULT_SHA_CHECK_BATCH_SIZE } from './upload-defaults.js';
|
|
13
|
+
import { buildScreenshotPattern, partitionFilesByExistence } from './upload-files.js';
|
|
14
|
+
import { resolveBatchSize, resolveTimeout } from './upload-settings.js';
|
|
15
|
+
import { getElapsedTime, isTimedOut } from './upload-timing.js';
|
|
16
|
+
|
|
1
17
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Each operation takes its dependencies as parameters for testability.
|
|
18
|
+
* Screenshot upload operations and the public uploader factory.
|
|
19
|
+
* Dependencies are explicit so callers can choose external integrations.
|
|
5
20
|
*/
|
|
6
21
|
|
|
7
|
-
|
|
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
|
+
let batchSize = resolveBatchSize(options, uploadConfig);
|
|
38
|
+
let timeout = resolveTimeout(options, uploadConfig);
|
|
39
|
+
let deps = options.deps || {
|
|
40
|
+
client,
|
|
41
|
+
createBuild,
|
|
42
|
+
getDefaultBranch,
|
|
43
|
+
glob,
|
|
44
|
+
readFile,
|
|
45
|
+
stat,
|
|
46
|
+
checkShas,
|
|
47
|
+
createError: (message, code, context) => {
|
|
48
|
+
let error = new UploadError(message, context);
|
|
49
|
+
error.code = code;
|
|
50
|
+
return error;
|
|
51
|
+
},
|
|
52
|
+
createValidationError: (message, context) => new ValidationError(message, context),
|
|
53
|
+
createUploadError: (message, context) => new UploadError(message, context),
|
|
54
|
+
createTimeoutError: (message, context) => new TimeoutError(message, context),
|
|
55
|
+
output
|
|
56
|
+
};
|
|
57
|
+
async function uploadScreenshots(uploadOptions) {
|
|
58
|
+
return upload({
|
|
59
|
+
uploadOptions,
|
|
60
|
+
config: {
|
|
61
|
+
apiKey,
|
|
62
|
+
apiUrl
|
|
63
|
+
},
|
|
64
|
+
signal,
|
|
65
|
+
batchSize,
|
|
66
|
+
deps: {
|
|
67
|
+
...deps,
|
|
68
|
+
client: deps.client || client
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
async function waitForUploadedBuild(buildId, waitTimeout = timeout) {
|
|
73
|
+
return waitForBuild({
|
|
74
|
+
buildId,
|
|
75
|
+
timeout: waitTimeout,
|
|
76
|
+
signal,
|
|
77
|
+
client: deps.client || client,
|
|
78
|
+
deps: {
|
|
79
|
+
createError: deps.createError,
|
|
80
|
+
createTimeoutError: deps.createTimeoutError
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
upload: uploadScreenshots,
|
|
86
|
+
waitForBuild: waitForUploadedBuild
|
|
87
|
+
};
|
|
88
|
+
}
|
|
8
89
|
function delay(ms, signal) {
|
|
9
90
|
if (ms <= 0) {
|
|
10
91
|
return Promise.resolve();
|
|
@@ -33,10 +114,6 @@ function delay(ms, signal) {
|
|
|
33
114
|
});
|
|
34
115
|
}
|
|
35
116
|
|
|
36
|
-
// ============================================================================
|
|
37
|
-
// File Discovery
|
|
38
|
-
// ============================================================================
|
|
39
|
-
|
|
40
117
|
/**
|
|
41
118
|
* Find all PNG screenshots in a directory
|
|
42
119
|
* @param {Object} options - Options
|
|
@@ -58,10 +135,6 @@ export async function findScreenshots({
|
|
|
58
135
|
});
|
|
59
136
|
}
|
|
60
137
|
|
|
61
|
-
// ============================================================================
|
|
62
|
-
// File Processing
|
|
63
|
-
// ============================================================================
|
|
64
|
-
|
|
65
138
|
/**
|
|
66
139
|
* Process files to extract metadata and compute hashes
|
|
67
140
|
* @param {Object} options - Options
|
|
@@ -100,10 +173,6 @@ export async function processFiles({
|
|
|
100
173
|
return results;
|
|
101
174
|
}
|
|
102
175
|
|
|
103
|
-
// ============================================================================
|
|
104
|
-
// SHA Checking / Deduplication
|
|
105
|
-
// ============================================================================
|
|
106
|
-
|
|
107
176
|
/**
|
|
108
177
|
* Check which files already exist on the server
|
|
109
178
|
* @param {Object} options - Options
|
|
@@ -161,10 +230,6 @@ export async function checkExistingFiles({
|
|
|
161
230
|
};
|
|
162
231
|
}
|
|
163
232
|
|
|
164
|
-
// ============================================================================
|
|
165
|
-
// File Upload
|
|
166
|
-
// ============================================================================
|
|
167
|
-
|
|
168
233
|
/**
|
|
169
234
|
* Upload files to Vizzly
|
|
170
235
|
* @param {Object} options - Options
|
|
@@ -230,10 +295,6 @@ export async function uploadFiles({
|
|
|
230
295
|
};
|
|
231
296
|
}
|
|
232
297
|
|
|
233
|
-
// ============================================================================
|
|
234
|
-
// Build Waiting
|
|
235
|
-
// ============================================================================
|
|
236
|
-
|
|
237
298
|
/**
|
|
238
299
|
* Wait for a build to complete
|
|
239
300
|
* @param {Object} options - Options
|
|
@@ -299,10 +360,6 @@ export async function waitForBuild({
|
|
|
299
360
|
});
|
|
300
361
|
}
|
|
301
362
|
|
|
302
|
-
// ============================================================================
|
|
303
|
-
// Main Upload Operation
|
|
304
|
-
// ============================================================================
|
|
305
|
-
|
|
306
363
|
/**
|
|
307
364
|
* Upload screenshots to Vizzly
|
|
308
365
|
* @param {Object} options - Options
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate API key is present
|
|
3
|
+
* @param {string|undefined} apiKey - API key to validate
|
|
4
|
+
* @returns {{ valid: boolean, error: string|null }}
|
|
5
|
+
*/
|
|
6
|
+
export function validateApiKey(apiKey) {
|
|
7
|
+
if (!apiKey) {
|
|
8
|
+
return {
|
|
9
|
+
valid: false,
|
|
10
|
+
error: 'API key is required'
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
valid: true,
|
|
15
|
+
error: null
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Validate screenshots directory path
|
|
21
|
+
* @param {string|undefined} screenshotsDir - Directory path
|
|
22
|
+
* @returns {{ valid: boolean, error: string|null }}
|
|
23
|
+
*/
|
|
24
|
+
export function validateScreenshotsDir(screenshotsDir) {
|
|
25
|
+
if (!screenshotsDir) {
|
|
26
|
+
return {
|
|
27
|
+
valid: false,
|
|
28
|
+
error: 'Screenshots directory is required'
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
valid: true,
|
|
33
|
+
error: null
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Validate directory stats
|
|
39
|
+
* @param {Object} stats - fs.stat result
|
|
40
|
+
* @param {string} path - Directory path for error message
|
|
41
|
+
* @returns {{ valid: boolean, error: string|null }}
|
|
42
|
+
*/
|
|
43
|
+
export function validateDirectoryStats(stats, path) {
|
|
44
|
+
if (!stats.isDirectory()) {
|
|
45
|
+
return {
|
|
46
|
+
valid: false,
|
|
47
|
+
error: `${path} is not a directory`
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
valid: true,
|
|
52
|
+
error: null
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Validate files were found
|
|
58
|
+
* @param {Array} files - Array of file paths
|
|
59
|
+
* @param {string} directory - Directory that was searched
|
|
60
|
+
* @returns {{ valid: boolean, error: string|null, context?: Object }}
|
|
61
|
+
*/
|
|
62
|
+
export function validateFilesFound(files, directory) {
|
|
63
|
+
if (!files || files.length === 0) {
|
|
64
|
+
return {
|
|
65
|
+
valid: false,
|
|
66
|
+
error: 'No screenshot files found',
|
|
67
|
+
context: {
|
|
68
|
+
directory,
|
|
69
|
+
pattern: '**/*.png'
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
valid: true,
|
|
75
|
+
error: null
|
|
76
|
+
};
|
|
77
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -9,18 +9,18 @@ import 'dotenv/config';
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
// Client exports for convenience
|
|
12
|
-
export { configure, setEnabled, vizzlyScreenshot } from './client/index.js';
|
|
13
|
-
|
|
14
|
-
export {
|
|
15
|
-
export { createPluginServices } from './plugin-api.js';
|
|
12
|
+
export { configure, setEnabled, vizzlyScreenshot } from './adapters/client/index.js';
|
|
13
|
+
export { createPluginServices } from './adapters/plugins/plugin-api.js';
|
|
14
|
+
export { createServices } from './adapters/plugins/services.js';
|
|
16
15
|
// Primary SDK export
|
|
17
|
-
export { createVizzly, VizzlySDK } from './sdk/index.js';
|
|
18
|
-
export { createServices } from './services/index.js';
|
|
19
|
-
export { createTDDService } from './tdd/tdd-service.js';
|
|
20
|
-
// Core services (for advanced usage)
|
|
21
|
-
export { createUploader } from './uploader/index.js';
|
|
16
|
+
export { createVizzly, VizzlySDK } from './adapters/sdk/index.js';
|
|
22
17
|
// Configuration helper
|
|
23
|
-
export { defineConfig } from './
|
|
18
|
+
export { defineConfig } from './domains/config/config.js';
|
|
24
19
|
// Utilities
|
|
25
|
-
export { loadConfig } from './
|
|
26
|
-
export
|
|
20
|
+
export { loadConfig } from './domains/config/config-loader.js';
|
|
21
|
+
export { createTDDService } from './domains/tdd/tdd.service.js';
|
|
22
|
+
// Core services (for advanced usage)
|
|
23
|
+
export { createUploader } from './domains/uploads/upload.service.js';
|
|
24
|
+
export * as output from './shared/output.js';
|
|
25
|
+
// Errors
|
|
26
|
+
export { AuthError, BuildError, ConfigError, NetworkError, ScreenshotError, TimeoutError, UploadError, ValidationError, VizzlyError } from './shared/vizzly-error.js';
|