@vizzly-testing/cli 0.39.0 → 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 +12 -3
- package/bin/vizzly.js +1 -1
- package/dist/{cli.js → adapters/cli/cli.js} +50 -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} +54 -14
- package/dist/{commands/init.js → domains/setup/init.commands.js} +11 -44
- 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/domains/updates/cli-update.js +103 -0
- package/dist/domains/updates/update-check.js +2 -0
- package/dist/domains/updates/update-notifications.js +175 -0
- 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 +3 -6
- 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 +37 -36
- 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/{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,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate builds command options
|
|
3
|
+
*/
|
|
4
|
+
export function validateBuildsOptions(options = {}) {
|
|
5
|
+
let errors = [];
|
|
6
|
+
if (options.limit !== undefined && (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > 250)) {
|
|
7
|
+
errors.push('--limit must be an integer between 1 and 250');
|
|
8
|
+
}
|
|
9
|
+
if (options.offset !== undefined && (!Number.isInteger(options.offset) || options.offset < 0)) {
|
|
10
|
+
errors.push('--offset must be a non-negative integer');
|
|
11
|
+
}
|
|
12
|
+
return errors;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Validate status options
|
|
17
|
+
* @param {string} buildId - Build ID to check
|
|
18
|
+
* @param {Object} options - Command options
|
|
19
|
+
*/
|
|
20
|
+
export function validateStatusOptions(buildId) {
|
|
21
|
+
let errors = [];
|
|
22
|
+
if (!buildId || buildId.trim() === '') {
|
|
23
|
+
errors.push('Build ID is required');
|
|
24
|
+
}
|
|
25
|
+
return errors;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Validate finalize options
|
|
30
|
+
* @param {string} parallelId - Parallel ID to finalize
|
|
31
|
+
* @param {Object} options - Command options
|
|
32
|
+
*/
|
|
33
|
+
export function validateFinalizeOptions(parallelId, _options) {
|
|
34
|
+
let errors = [];
|
|
35
|
+
if (!parallelId || parallelId.trim() === '') {
|
|
36
|
+
errors.push('Parallel ID is required');
|
|
37
|
+
}
|
|
38
|
+
return errors;
|
|
39
|
+
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import { formatBuildForJson } from './build.serializers.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Builds command - list and query builds
|
|
3
5
|
*/
|
|
4
6
|
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
7
|
+
import * as defaultOutput from '../../shared/output.js';
|
|
8
|
+
import { createApiClient as defaultCreateApiClient } from '../auth/cloud-client.js';
|
|
9
|
+
import { loadConfig as defaultLoadConfig } from '../config/config-loader.js';
|
|
10
|
+
import { getBuild as defaultGetBuild, getBuilds as defaultGetBuilds } from './build.service.js';
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
13
|
* Builds list command - list builds with optional filters
|
|
@@ -153,63 +156,6 @@ export async function buildsCommand(options = {}, globalOptions = {}, deps = {})
|
|
|
153
156
|
}
|
|
154
157
|
}
|
|
155
158
|
|
|
156
|
-
/**
|
|
157
|
-
* Format a build for JSON output
|
|
158
|
-
*/
|
|
159
|
-
function formatBuildForJson(build, includeComparisons = false) {
|
|
160
|
-
let result = {
|
|
161
|
-
id: build.id,
|
|
162
|
-
name: build.name,
|
|
163
|
-
status: build.status,
|
|
164
|
-
branch: build.branch,
|
|
165
|
-
commit: build.commit_sha,
|
|
166
|
-
commitMessage: build.commit_message,
|
|
167
|
-
environment: build.environment,
|
|
168
|
-
screenshotCount: build.screenshot_count || 0,
|
|
169
|
-
comparisons: {
|
|
170
|
-
total: build.total_comparisons || 0,
|
|
171
|
-
new: build.new_comparisons || 0,
|
|
172
|
-
changed: build.changed_comparisons || 0,
|
|
173
|
-
identical: build.identical_comparisons || 0
|
|
174
|
-
},
|
|
175
|
-
visual_review: build.visual_review || null,
|
|
176
|
-
createdAt: build.created_at,
|
|
177
|
-
completedAt: build.completed_at
|
|
178
|
-
};
|
|
179
|
-
if (includeComparisons && build.comparisons) {
|
|
180
|
-
result.comparisonDetails = build.comparisons.map(c => {
|
|
181
|
-
let diffUrl = c.diff_image?.url || c.diff_image_url || c.diff_url || null;
|
|
182
|
-
let diffImage = c.diff_image || {};
|
|
183
|
-
let clusterMetadata = c.cluster_metadata || diffImage.cluster_metadata || null;
|
|
184
|
-
let ssimScore = c.ssim_score ?? diffImage.ssim_score ?? null;
|
|
185
|
-
let gmsdScore = c.gmsd_score ?? diffImage.gmsd_score ?? null;
|
|
186
|
-
let fingerprintHash = c.fingerprint_hash || diffImage.fingerprint_hash || null;
|
|
187
|
-
let hasHoneydiff = clusterMetadata || ssimScore != null || gmsdScore != null || fingerprintHash;
|
|
188
|
-
return {
|
|
189
|
-
id: c.id,
|
|
190
|
-
name: c.name || c.current_name,
|
|
191
|
-
status: c.status,
|
|
192
|
-
diffPercentage: c.diff_percentage,
|
|
193
|
-
visual_review: c.visual_review || null,
|
|
194
|
-
urls: {
|
|
195
|
-
baseline: c.baseline_screenshot?.original_url || c.baseline_original_url || c.baseline_screenshot_url || null,
|
|
196
|
-
current: c.current_screenshot?.original_url || c.current_original_url || c.current_screenshot_url || null,
|
|
197
|
-
diff: diffUrl
|
|
198
|
-
},
|
|
199
|
-
honeydiff: hasHoneydiff ? {
|
|
200
|
-
ssimScore,
|
|
201
|
-
gmsdScore,
|
|
202
|
-
clusterClassification: clusterMetadata?.classification || null,
|
|
203
|
-
clusterMetadata,
|
|
204
|
-
fingerprintHash,
|
|
205
|
-
diffRegions: c.diff_regions ?? diffImage.diff_regions ?? null
|
|
206
|
-
} : null
|
|
207
|
-
};
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
return result;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
159
|
/**
|
|
214
160
|
* Display a single build in human-readable format
|
|
215
161
|
*/
|
|
@@ -319,18 +265,4 @@ function getComparisonStatusIcon(colors, status) {
|
|
|
319
265
|
default:
|
|
320
266
|
return colors.dim('○');
|
|
321
267
|
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Validate builds command options
|
|
326
|
-
*/
|
|
327
|
-
export function validateBuildsOptions(options = {}) {
|
|
328
|
-
let errors = [];
|
|
329
|
-
if (options.limit !== undefined && (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > 250)) {
|
|
330
|
-
errors.push('--limit must be an integer between 1 and 250');
|
|
331
|
-
}
|
|
332
|
-
if (options.offset !== undefined && (!Number.isInteger(options.offset) || options.offset < 0)) {
|
|
333
|
-
errors.push('--offset must be a non-negative integer');
|
|
334
|
-
}
|
|
335
|
-
return errors;
|
|
336
268
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { sendError, sendSuccess } from '../../adapters/http/middleware/response.js';
|
|
2
|
+
import * as output from '../../shared/output.js';
|
|
3
|
+
export function createCloudBuildsRouter({
|
|
4
|
+
authService
|
|
5
|
+
}) {
|
|
6
|
+
return async function handleCloudBuildsRoute(req, res, pathname, parsedUrl) {
|
|
7
|
+
let buildsMatch = pathname.match(/^\/api\/cloud\/organizations\/([^/]+)\/projects\/([^/]+)\/builds$/);
|
|
8
|
+
if (req.method !== 'GET' || !buildsMatch) return false;
|
|
9
|
+
try {
|
|
10
|
+
let organizationSlug = decodeURIComponent(buildsMatch[1]);
|
|
11
|
+
let projectSlug = decodeURIComponent(buildsMatch[2]);
|
|
12
|
+
let limit = parsedUrl.searchParams.get('limit') || '20';
|
|
13
|
+
let branch = parsedUrl.searchParams.get('branch');
|
|
14
|
+
let queryParams = new URLSearchParams();
|
|
15
|
+
if (limit) queryParams.append('limit', limit);
|
|
16
|
+
if (branch) queryParams.append('branch', branch);
|
|
17
|
+
let query = queryParams.toString();
|
|
18
|
+
let endpoint = `/api/cli/organizations/${organizationSlug}/projects/${projectSlug}/builds${query ? `?${query}` : ''}`;
|
|
19
|
+
let response = await authService.authenticatedRequest(endpoint, {
|
|
20
|
+
method: 'GET'
|
|
21
|
+
});
|
|
22
|
+
sendSuccess(res, {
|
|
23
|
+
builds: response.builds || []
|
|
24
|
+
});
|
|
25
|
+
} catch (error) {
|
|
26
|
+
output.error('Error fetching builds from cloud:', error);
|
|
27
|
+
sendError(res, 500, error.message);
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
* Uses functional API operations directly
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import { writeSession as defaultWriteSession } from '../
|
|
6
|
+
import * as defaultOutput from '../../shared/output.js';
|
|
7
|
+
import { createApiClient as defaultCreateApiClient } from '../auth/cloud-client.js';
|
|
8
|
+
import { loadConfig as defaultLoadConfig } from '../config/config-loader.js';
|
|
9
|
+
import { writeSession as defaultWriteSession } from '../runs/session-store.js';
|
|
10
|
+
import { finalizeParallelBuild as defaultFinalizeParallelBuild } from './build.service.js';
|
|
10
11
|
let MISSING_BUILD_HINTS = [' • No screenshots were uploaded with this parallel-id', ' • Tests were skipped or failed before capturing screenshots', ' • The parallel-id does not match what was used during test runs'];
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -150,17 +151,4 @@ export async function finalizeCommand(parallelId, options = {}, globalOptions =
|
|
|
150
151
|
} finally {
|
|
151
152
|
output.cleanup();
|
|
152
153
|
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Validate finalize options
|
|
157
|
-
* @param {string} parallelId - Parallel ID to finalize
|
|
158
|
-
* @param {Object} options - Command options
|
|
159
|
-
*/
|
|
160
|
-
export function validateFinalizeOptions(parallelId, _options) {
|
|
161
|
-
let errors = [];
|
|
162
|
-
if (!parallelId || parallelId.trim() === '') {
|
|
163
|
-
errors.push('Parallel ID is required');
|
|
164
|
-
}
|
|
165
|
-
return errors;
|
|
166
154
|
}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
+
import { getBuildReviewState, getComparisonStatus, getProcessingStatus, normalizeBuildStatus } from './build.normalizers.js';
|
|
2
|
+
import { createStatusData, createStatusSuggestedCommands } from './build.serializers.js';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Status command implementation
|
|
3
6
|
* Uses functional API operations directly
|
|
4
7
|
*/
|
|
5
8
|
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
9
|
+
import { getAppBaseUrl } from '../../shared/api-url.js';
|
|
10
|
+
import * as defaultOutput from '../../shared/output.js';
|
|
11
|
+
import { createApiClient as defaultCreateApiClient } from '../auth/cloud-client.js';
|
|
12
|
+
import { loadConfig as defaultLoadConfig } from '../config/config-loader.js';
|
|
13
|
+
import { getPreviewInfo as defaultGetPreviewInfo } from '../previews/preview.service.js';
|
|
14
|
+
import { getBuildStatus as defaultGetBuildStatus } from './build.service.js';
|
|
11
15
|
function createStatusDeps(deps = {}) {
|
|
12
16
|
return {
|
|
13
17
|
loadConfig: deps.loadConfig || defaultLoadConfig,
|
|
@@ -48,108 +52,6 @@ async function fetchOptionalPreviewInfo(getPreviewInfo, client, buildId) {
|
|
|
48
52
|
throw error;
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
|
-
export function normalizeBuildStatus(buildStatus) {
|
|
52
|
-
return buildStatus.build;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Read the processing counts from the status response.
|
|
57
|
-
*
|
|
58
|
-
* @param {Object} status - Status response.
|
|
59
|
-
* @returns {Object} API-provided processing facts.
|
|
60
|
-
*/
|
|
61
|
-
export function getProcessingStatus(status = {}) {
|
|
62
|
-
return status.processing;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Read comparison totals without deriving them from screenshot counts.
|
|
67
|
-
*
|
|
68
|
-
* @param {Object} status - Status response.
|
|
69
|
-
* @returns {Object} API-provided comparison facts.
|
|
70
|
-
*/
|
|
71
|
-
export function getComparisonStatus(status = {}) {
|
|
72
|
-
return status.comparisons;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Read the build review state without mixing it with processing status.
|
|
77
|
-
*
|
|
78
|
-
* @param {Object} status - Status response.
|
|
79
|
-
* @returns {string|null} API-provided review state when available.
|
|
80
|
-
*/
|
|
81
|
-
export function getBuildReviewState(status = {}) {
|
|
82
|
-
let build = normalizeBuildStatus(status);
|
|
83
|
-
return getVisualReviewState(build);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Give agents concrete paths from lifecycle status into visual evidence.
|
|
88
|
-
*
|
|
89
|
-
* @param {Object} build - Build record from the status response.
|
|
90
|
-
* @returns {{label: string, command: string}[]} Executable CLI commands.
|
|
91
|
-
*/
|
|
92
|
-
export function createStatusSuggestedCommands(build = {}) {
|
|
93
|
-
if (!build.id) {
|
|
94
|
-
return [];
|
|
95
|
-
}
|
|
96
|
-
return [{
|
|
97
|
-
label: 'Discover build context API',
|
|
98
|
-
command: 'vizzly api schema sdk.getBuildContext --json'
|
|
99
|
-
}, {
|
|
100
|
-
label: 'List comparisons',
|
|
101
|
-
command: `vizzly --json comparisons --build ${build.id}`
|
|
102
|
-
}];
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Every lifecycle, processing, comparison, and review value comes directly
|
|
107
|
-
* from the API.
|
|
108
|
-
*
|
|
109
|
-
* @param {Object} status - Status response.
|
|
110
|
-
* @param {Object|null} previewInfo - Optional preview response.
|
|
111
|
-
* @returns {Object} Machine-readable status payload.
|
|
112
|
-
*/
|
|
113
|
-
export function createStatusData(status, previewInfo = null) {
|
|
114
|
-
let build = normalizeBuildStatus(status);
|
|
115
|
-
let processing = getProcessingStatus(status);
|
|
116
|
-
let comparisons = getComparisonStatus(status);
|
|
117
|
-
return {
|
|
118
|
-
resource: status.resource,
|
|
119
|
-
schemaVersion: status.schema_version,
|
|
120
|
-
buildId: build.id,
|
|
121
|
-
status: build.status,
|
|
122
|
-
conclusion: status.conclusion,
|
|
123
|
-
name: build.name,
|
|
124
|
-
createdAt: build.created_at,
|
|
125
|
-
updatedAt: build.updated_at,
|
|
126
|
-
completedAt: build.completed_at,
|
|
127
|
-
environment: build.environment,
|
|
128
|
-
branch: build.branch,
|
|
129
|
-
commit: build.commit_sha,
|
|
130
|
-
commitMessage: build.commit_message,
|
|
131
|
-
screenshotsTotal: processing.total,
|
|
132
|
-
processing,
|
|
133
|
-
comparisonsTotal: comparisons.total,
|
|
134
|
-
comparisons,
|
|
135
|
-
newComparisons: comparisons.new,
|
|
136
|
-
changedComparisons: comparisons.changed,
|
|
137
|
-
identicalComparisons: comparisons.identical,
|
|
138
|
-
visual_review: build.visual_review || null,
|
|
139
|
-
executionTime: build.execution_time_ms,
|
|
140
|
-
isBaseline: build.is_baseline,
|
|
141
|
-
userAgent: build.user_agent,
|
|
142
|
-
scope: status.scope,
|
|
143
|
-
links: status.links,
|
|
144
|
-
suggestedCommands: createStatusSuggestedCommands(build),
|
|
145
|
-
preview: previewInfo ? {
|
|
146
|
-
url: previewInfo.preview_url,
|
|
147
|
-
status: previewInfo.status,
|
|
148
|
-
fileCount: previewInfo.file_count,
|
|
149
|
-
expiresAt: previewInfo.expires_at
|
|
150
|
-
} : null
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
55
|
export function createBuildInfo(build) {
|
|
154
56
|
let buildInfo = {
|
|
155
57
|
Name: build.name || build.id,
|
|
@@ -490,17 +392,4 @@ export async function statusCommand(buildId, options = {}, globalOptions = {}, d
|
|
|
490
392
|
error
|
|
491
393
|
};
|
|
492
394
|
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
/**
|
|
496
|
-
* Validate status options
|
|
497
|
-
* @param {string} buildId - Build ID to check
|
|
498
|
-
* @param {Object} options - Command options
|
|
499
|
-
*/
|
|
500
|
-
export function validateStatusOptions(buildId) {
|
|
501
|
-
let errors = [];
|
|
502
|
-
if (!buildId || buildId.trim() === '') {
|
|
503
|
-
errors.push('Build ID is required');
|
|
504
|
-
}
|
|
505
|
-
return errors;
|
|
506
395
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { buildEndpointWithParams } from '../../shared/cloud-protocol.js';
|
|
2
|
+
import { VizzlyError } from '../../shared/vizzly-error.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get comparison information
|
|
6
|
+
* @param {Object} client - API client
|
|
7
|
+
* @param {string} comparisonId - Comparison ID
|
|
8
|
+
* @returns {Promise<Object>} Comparison data
|
|
9
|
+
*/
|
|
10
|
+
export async function getComparison(client, comparisonId) {
|
|
11
|
+
let response = await client.request(`/api/sdk/comparisons/${comparisonId}`);
|
|
12
|
+
return response.comparison;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Search for comparisons by name
|
|
17
|
+
* @param {Object} client - API client
|
|
18
|
+
* @param {string} name - Screenshot name to search for
|
|
19
|
+
* @param {Object} filters - Optional filters (branch, project, organization, limit, offset)
|
|
20
|
+
* @returns {Promise<Object>} Search results with comparisons and pagination
|
|
21
|
+
*/
|
|
22
|
+
export async function searchComparisons(client, name, filters = {}) {
|
|
23
|
+
if (!name || typeof name !== 'string') {
|
|
24
|
+
throw new VizzlyError('name is required and must be a non-empty string');
|
|
25
|
+
}
|
|
26
|
+
let {
|
|
27
|
+
branch,
|
|
28
|
+
project,
|
|
29
|
+
organization,
|
|
30
|
+
limit = 50,
|
|
31
|
+
offset = 0
|
|
32
|
+
} = filters;
|
|
33
|
+
let params = {
|
|
34
|
+
name,
|
|
35
|
+
limit: String(limit),
|
|
36
|
+
offset: String(offset)
|
|
37
|
+
};
|
|
38
|
+
if (branch) params.branch = branch;
|
|
39
|
+
if (project) params.project = project;
|
|
40
|
+
if (organization) params.organization = organization;
|
|
41
|
+
let endpoint = buildEndpointWithParams('/api/sdk/comparisons/search', params);
|
|
42
|
+
return client.request(endpoint);
|
|
43
|
+
}
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
* Comparisons command - query and search comparisons
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
5
|
+
import * as defaultOutput from '../../shared/output.js';
|
|
6
|
+
import { createWildcardMatcher } from '../../shared/patterns.js';
|
|
7
|
+
import { createApiClient as defaultCreateApiClient } from '../auth/cloud-client.js';
|
|
8
|
+
import { getBuild as defaultGetBuild } from '../builds/build.service.js';
|
|
9
|
+
import { loadConfig as defaultLoadConfig } from '../config/config-loader.js';
|
|
10
|
+
import { getComparisonBrowser, getComparisonName, getComparisonResult, getComparisonViewport } from '../context/context.normalizers.js';
|
|
11
|
+
import { getComparison as defaultGetComparison, searchComparisons as defaultSearchComparisons } from './cloud-comparisons.js';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Comparisons command - query comparisons with various filters
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default configuration values
|
|
3
|
+
*/
|
|
4
|
+
export let CONFIG_DEFAULTS = {
|
|
5
|
+
apiUrl: 'https://app.vizzly.dev',
|
|
6
|
+
server: {
|
|
7
|
+
port: 47392,
|
|
8
|
+
timeout: 30000
|
|
9
|
+
},
|
|
10
|
+
build: {
|
|
11
|
+
name: 'Build {timestamp}',
|
|
12
|
+
environment: 'test'
|
|
13
|
+
},
|
|
14
|
+
upload: {
|
|
15
|
+
screenshotsDir: './screenshots',
|
|
16
|
+
batchSize: 10,
|
|
17
|
+
timeout: 30000
|
|
18
|
+
},
|
|
19
|
+
comparison: {
|
|
20
|
+
threshold: 2.0,
|
|
21
|
+
minClusterSize: 2
|
|
22
|
+
},
|
|
23
|
+
tdd: {
|
|
24
|
+
openReport: false
|
|
25
|
+
},
|
|
26
|
+
plugins: []
|
|
27
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
1
|
import path from 'node:path';
|
|
3
2
|
import { cosmiconfigSync, defaultLoadersSync, getDefaultSearchPlacesSync } from 'cosmiconfig';
|
|
3
|
+
import { readOptionalFile } from '../../shared/files.js';
|
|
4
4
|
let searchPlaces = getDefaultSearchPlacesSync('vizzly');
|
|
5
5
|
export function createConfigExplorer() {
|
|
6
6
|
return cosmiconfigSync('vizzly', {
|
|
@@ -8,14 +8,6 @@ export function createConfigExplorer() {
|
|
|
8
8
|
searchStrategy: 'none'
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
|
-
export async function readOptionalFile(filename) {
|
|
12
|
-
try {
|
|
13
|
-
return await readFile(filename, 'utf8');
|
|
14
|
-
} catch (error) {
|
|
15
|
-
if (error.code === 'ENOENT') return null;
|
|
16
|
-
throw error;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
11
|
export function configFormat(filename) {
|
|
20
12
|
if (path.basename(filename) === 'package.json') return 'package';
|
|
21
13
|
let extension = path.extname(filename);
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
|
-
import
|
|
2
|
+
import * as output from '../../shared/output.js';
|
|
3
|
+
import { getAccessToken } from '../auth/token-store.js';
|
|
4
|
+
import { getProjectLink } from '../projects/project-link-store.js';
|
|
5
|
+
import { deepMerge } from './config.normalizers.js';
|
|
6
|
+
import { validateVizzlyConfigWithDefaults } from './config.validations.js';
|
|
7
|
+
import { CONFIG_DEFAULTS } from './config-defaults.js';
|
|
3
8
|
import { createConfigExplorer } from './config-discovery.js';
|
|
4
|
-
import {
|
|
5
|
-
import { getApiToken, getApiUrl, getBuildName, getMinClusterSize, getParallelId, getThreshold } from './environment-config.js';
|
|
6
|
-
import { getAccessToken } from './global-config.js';
|
|
7
|
-
import * as output from './output.js';
|
|
8
|
-
import { getProjectLink } from './project-link-store.js';
|
|
9
|
+
import { getApiToken, getApiUrl, getBuildName, getMinClusterSize, getParallelId, getThreshold } from './environment.js';
|
|
9
10
|
export async function loadConfig(configPath = null, cliOverrides = {}) {
|
|
10
11
|
// 1. Load from config file using cosmiconfig
|
|
11
12
|
let explorer = createConfigExplorer();
|
|
@@ -152,7 +153,7 @@ function applyCLIOverrides(config, cliOverrides = {}) {
|
|
|
152
153
|
if (cliOverrides.allowNoToken !== undefined) config.allowNoToken = cliOverrides.allowNoToken;
|
|
153
154
|
}
|
|
154
155
|
function mergeConfig(target, source) {
|
|
155
|
-
for (
|
|
156
|
+
for (let key in source) {
|
|
156
157
|
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
157
158
|
if (!target[key]) target[key] = {};
|
|
158
159
|
mergeConfig(target[key], source[key]);
|
|
@@ -162,7 +163,7 @@ function mergeConfig(target, source) {
|
|
|
162
163
|
}
|
|
163
164
|
}
|
|
164
165
|
export function getScreenshotPaths(config) {
|
|
165
|
-
|
|
166
|
-
|
|
166
|
+
let screenshotsDir = config.upload?.screenshotsDir || './screenshots';
|
|
167
|
+
let paths = Array.isArray(screenshotsDir) ? screenshotsDir : [screenshotsDir];
|
|
167
168
|
return paths.map(p => resolve(process.cwd(), p));
|
|
168
169
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import * as defaultOutput from '../../shared/output.js';
|
|
2
|
+
import { CONFIG_DEFAULTS } from './config-defaults.js';
|
|
3
|
+
import { loadConfig as defaultLoadConfig } from './config-loader.js';
|
|
4
|
+
|
|
1
5
|
/**
|
|
2
6
|
* Config command - query and display configuration
|
|
3
7
|
*/
|
|
4
8
|
|
|
5
|
-
import { CONFIG_DEFAULTS } from '../config/core.js';
|
|
6
|
-
import { loadConfig as defaultLoadConfig } from '../utils/config-loader.js';
|
|
7
|
-
import * as defaultOutput from '../utils/output.js';
|
|
8
|
-
|
|
9
9
|
/**
|
|
10
10
|
* Config command - display current configuration
|
|
11
11
|
* @param {string|null} key - Optional specific key to get (dot notation)
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @module @vizzly-testing/cli/config
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export { VizzlyConfig } from '
|
|
7
|
+
export { VizzlyConfig } from '../../index';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Define Vizzly configuration with type hints
|
|
@@ -26,5 +26,5 @@ export { VizzlyConfig } from './index';
|
|
|
26
26
|
* });
|
|
27
27
|
*/
|
|
28
28
|
export function defineConfig(
|
|
29
|
-
config: import('
|
|
30
|
-
): import('
|
|
29
|
+
config: import('../../index').VizzlyConfig
|
|
30
|
+
): import('../../index').VizzlyConfig;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { CONFIG_DEFAULTS } from './config-defaults.js';
|
|
2
|
+
function isPlainConfigObject(value) {
|
|
3
|
+
return value && typeof value === 'object' && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
function cloneConfigValue(value) {
|
|
6
|
+
if (Array.isArray(value)) {
|
|
7
|
+
return value.map(cloneConfigValue);
|
|
8
|
+
}
|
|
9
|
+
if (isPlainConfigObject(value)) {
|
|
10
|
+
let cloned = {};
|
|
11
|
+
for (let key of Object.keys(value)) {
|
|
12
|
+
cloned[key] = cloneConfigValue(value[key]);
|
|
13
|
+
}
|
|
14
|
+
return cloned;
|
|
15
|
+
}
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Deep merge two objects
|
|
21
|
+
* @param {Object} target - Target object
|
|
22
|
+
* @param {Object} source - Source object
|
|
23
|
+
* @returns {Object} Merged object (new object, inputs not mutated)
|
|
24
|
+
*/
|
|
25
|
+
export function deepMerge(target, source) {
|
|
26
|
+
let output = cloneConfigValue(target);
|
|
27
|
+
for (let key of Object.keys(source)) {
|
|
28
|
+
if (isPlainConfigObject(source[key])) {
|
|
29
|
+
if (isPlainConfigObject(target[key])) {
|
|
30
|
+
output[key] = deepMerge(target[key], source[key]);
|
|
31
|
+
} else {
|
|
32
|
+
output[key] = cloneConfigValue(source[key]);
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
output[key] = cloneConfigValue(source[key]);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return output;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Ensure value is a plain object, return empty object otherwise
|
|
43
|
+
* @param {*} value - Value to check
|
|
44
|
+
* @returns {Object} The value if it's an object, empty object otherwise
|
|
45
|
+
*/
|
|
46
|
+
function ensureObject(value) {
|
|
47
|
+
if (isPlainConfigObject(value)) {
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
return {};
|
|
51
|
+
}
|
|
52
|
+
function applyConfigLayer(config, sources, layer, sourceName) {
|
|
53
|
+
let merged = deepMerge(config, layer);
|
|
54
|
+
for (let key of Object.keys(layer)) {
|
|
55
|
+
sources[key] = sourceName;
|
|
56
|
+
}
|
|
57
|
+
return merged;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Build merged config from layers with source tracking
|
|
62
|
+
* @param {Object} options - Config layers
|
|
63
|
+
* @param {Object} options.projectConfig - Project config (from vizzly.config.js)
|
|
64
|
+
* @param {Object} options.globalConfig - Global config (from ~/.vizzly/config.json)
|
|
65
|
+
* @param {Object} [options.envOverrides] - Environment variable overrides
|
|
66
|
+
* @returns {{ config: Object, sources: Object }}
|
|
67
|
+
*/
|
|
68
|
+
export function buildMergedConfig({
|
|
69
|
+
projectConfig = {},
|
|
70
|
+
globalConfig = {},
|
|
71
|
+
envOverrides = {}
|
|
72
|
+
} = {}) {
|
|
73
|
+
// Ensure all inputs are plain objects
|
|
74
|
+
let safeProjectConfig = ensureObject(projectConfig);
|
|
75
|
+
let safeGlobalConfig = ensureObject(globalConfig);
|
|
76
|
+
let safeEnvOverrides = ensureObject(envOverrides);
|
|
77
|
+
let mergedConfig = cloneConfigValue(CONFIG_DEFAULTS);
|
|
78
|
+
let sources = {};
|
|
79
|
+
for (let key of Object.keys(CONFIG_DEFAULTS)) {
|
|
80
|
+
sources[key] = 'default';
|
|
81
|
+
}
|
|
82
|
+
mergedConfig = applyConfigLayer(mergedConfig, sources, safeGlobalConfig, 'global');
|
|
83
|
+
mergedConfig = applyConfigLayer(mergedConfig, sources, safeProjectConfig, 'project');
|
|
84
|
+
mergedConfig = applyConfigLayer(mergedConfig, sources, safeEnvOverrides, 'env');
|
|
85
|
+
return {
|
|
86
|
+
config: mergedConfig,
|
|
87
|
+
sources
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Extract environment variable overrides
|
|
93
|
+
* @param {Object} env - Environment variables object (defaults to process.env)
|
|
94
|
+
* @returns {Object} Overrides from environment
|
|
95
|
+
*/
|
|
96
|
+
export function extractEnvOverrides(env = process.env) {
|
|
97
|
+
let overrides = {};
|
|
98
|
+
if (env.VIZZLY_TOKEN) {
|
|
99
|
+
overrides.apiKey = env.VIZZLY_TOKEN;
|
|
100
|
+
}
|
|
101
|
+
if (env.VIZZLY_API_URL) {
|
|
102
|
+
overrides.apiUrl = env.VIZZLY_API_URL;
|
|
103
|
+
}
|
|
104
|
+
return overrides;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Extract config from cosmiconfig result (handles .default exports)
|
|
109
|
+
* @param {Object|null} result - Cosmiconfig result
|
|
110
|
+
* @returns {{ config: Object|null, filepath: string|null }}
|
|
111
|
+
*/
|
|
112
|
+
export function extractCosmiconfigResult(result) {
|
|
113
|
+
if (!result?.config) {
|
|
114
|
+
return {
|
|
115
|
+
config: null,
|
|
116
|
+
filepath: null
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Handle both `export default` and `module.exports`
|
|
121
|
+
let config = result.config.default || result.config;
|
|
122
|
+
return {
|
|
123
|
+
config,
|
|
124
|
+
filepath: result.filepath
|
|
125
|
+
};
|
|
126
|
+
}
|