@vizzly-testing/cli 0.10.3 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/.mcp.json +8 -0
- package/.claude-plugin/README.md +114 -0
- package/.claude-plugin/commands/debug-diff.md +153 -0
- package/.claude-plugin/commands/setup.md +137 -0
- package/.claude-plugin/commands/suggest-screenshots.md +111 -0
- package/.claude-plugin/commands/tdd-status.md +43 -0
- package/.claude-plugin/marketplace.json +28 -0
- package/.claude-plugin/mcp/vizzly-server/cloud-api-provider.js +354 -0
- package/.claude-plugin/mcp/vizzly-server/index.js +861 -0
- package/.claude-plugin/mcp/vizzly-server/local-tdd-provider.js +422 -0
- package/.claude-plugin/mcp/vizzly-server/token-resolver.js +185 -0
- package/.claude-plugin/plugin.json +14 -0
- package/README.md +168 -8
- package/dist/cli.js +64 -0
- package/dist/client/index.js +13 -3
- package/dist/commands/login.js +195 -0
- package/dist/commands/logout.js +71 -0
- package/dist/commands/project.js +351 -0
- package/dist/commands/run.js +30 -0
- package/dist/commands/whoami.js +162 -0
- package/dist/plugin-loader.js +4 -2
- package/dist/sdk/index.js +16 -4
- package/dist/services/api-service.js +50 -7
- package/dist/services/auth-service.js +226 -0
- package/dist/types/client/index.d.ts +9 -3
- package/dist/types/commands/login.d.ts +11 -0
- package/dist/types/commands/logout.d.ts +11 -0
- package/dist/types/commands/project.d.ts +28 -0
- package/dist/types/commands/whoami.d.ts +11 -0
- package/dist/types/sdk/index.d.ts +9 -4
- package/dist/types/services/api-service.d.ts +2 -1
- package/dist/types/services/auth-service.d.ts +59 -0
- package/dist/types/utils/browser.d.ts +6 -0
- package/dist/types/utils/config-loader.d.ts +1 -1
- package/dist/types/utils/config-schema.d.ts +8 -174
- package/dist/types/utils/file-helpers.d.ts +18 -0
- package/dist/types/utils/global-config.d.ts +84 -0
- package/dist/utils/browser.js +44 -0
- package/dist/utils/config-loader.js +69 -3
- package/dist/utils/file-helpers.js +64 -0
- package/dist/utils/global-config.js +259 -0
- package/docs/api-reference.md +177 -6
- package/docs/authentication.md +334 -0
- package/docs/getting-started.md +21 -2
- package/docs/plugins.md +27 -0
- package/docs/test-integration.md +60 -10
- package/package.json +5 -3
|
@@ -21,197 +21,31 @@ export let vizzlyConfigSchema: z.ZodDefault<z.ZodObject<{
|
|
|
21
21
|
server: z.ZodDefault<z.ZodObject<{
|
|
22
22
|
port: z.ZodDefault<z.ZodNumber>;
|
|
23
23
|
timeout: z.ZodDefault<z.ZodNumber>;
|
|
24
|
-
},
|
|
25
|
-
port?: number;
|
|
26
|
-
timeout?: number;
|
|
27
|
-
}, {
|
|
28
|
-
port?: number;
|
|
29
|
-
timeout?: number;
|
|
30
|
-
}>>;
|
|
24
|
+
}, z.core.$strip>>;
|
|
31
25
|
build: z.ZodDefault<z.ZodObject<{
|
|
32
26
|
name: z.ZodDefault<z.ZodString>;
|
|
33
27
|
environment: z.ZodDefault<z.ZodString>;
|
|
34
28
|
branch: z.ZodOptional<z.ZodString>;
|
|
35
29
|
commit: z.ZodOptional<z.ZodString>;
|
|
36
30
|
message: z.ZodOptional<z.ZodString>;
|
|
37
|
-
},
|
|
38
|
-
name?: string;
|
|
39
|
-
message?: string;
|
|
40
|
-
environment?: string;
|
|
41
|
-
branch?: string;
|
|
42
|
-
commit?: string;
|
|
43
|
-
}, {
|
|
44
|
-
name?: string;
|
|
45
|
-
message?: string;
|
|
46
|
-
environment?: string;
|
|
47
|
-
branch?: string;
|
|
48
|
-
commit?: string;
|
|
49
|
-
}>>;
|
|
31
|
+
}, z.core.$strip>>;
|
|
50
32
|
upload: z.ZodDefault<z.ZodObject<{
|
|
51
|
-
screenshotsDir: z.ZodDefault<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString
|
|
33
|
+
screenshotsDir: z.ZodDefault<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
52
34
|
batchSize: z.ZodDefault<z.ZodNumber>;
|
|
53
35
|
timeout: z.ZodDefault<z.ZodNumber>;
|
|
54
|
-
},
|
|
55
|
-
timeout?: number;
|
|
56
|
-
screenshotsDir?: string | string[];
|
|
57
|
-
batchSize?: number;
|
|
58
|
-
}, {
|
|
59
|
-
timeout?: number;
|
|
60
|
-
screenshotsDir?: string | string[];
|
|
61
|
-
batchSize?: number;
|
|
62
|
-
}>>;
|
|
36
|
+
}, z.core.$strip>>;
|
|
63
37
|
comparison: z.ZodDefault<z.ZodObject<{
|
|
64
38
|
threshold: z.ZodDefault<z.ZodNumber>;
|
|
65
|
-
},
|
|
66
|
-
threshold?: number;
|
|
67
|
-
}, {
|
|
68
|
-
threshold?: number;
|
|
69
|
-
}>>;
|
|
39
|
+
}, z.core.$strip>>;
|
|
70
40
|
tdd: z.ZodDefault<z.ZodObject<{
|
|
71
41
|
openReport: z.ZodDefault<z.ZodBoolean>;
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
}, {
|
|
75
|
-
openReport?: boolean;
|
|
76
|
-
}>>;
|
|
77
|
-
plugins: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
42
|
+
}, z.core.$strip>>;
|
|
43
|
+
plugins: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
78
44
|
parallelId: z.ZodOptional<z.ZodString>;
|
|
79
45
|
baselineBuildId: z.ZodOptional<z.ZodString>;
|
|
80
46
|
baselineComparisonId: z.ZodOptional<z.ZodString>;
|
|
81
47
|
eager: z.ZodOptional<z.ZodBoolean>;
|
|
82
48
|
wait: z.ZodOptional<z.ZodBoolean>;
|
|
83
49
|
allowNoToken: z.ZodOptional<z.ZodBoolean>;
|
|
84
|
-
},
|
|
85
|
-
apiKey: z.ZodOptional<z.ZodString>;
|
|
86
|
-
apiUrl: z.ZodOptional<z.ZodString>;
|
|
87
|
-
server: z.ZodDefault<z.ZodObject<{
|
|
88
|
-
port: z.ZodDefault<z.ZodNumber>;
|
|
89
|
-
timeout: z.ZodDefault<z.ZodNumber>;
|
|
90
|
-
}, "strip", z.ZodTypeAny, {
|
|
91
|
-
port?: number;
|
|
92
|
-
timeout?: number;
|
|
93
|
-
}, {
|
|
94
|
-
port?: number;
|
|
95
|
-
timeout?: number;
|
|
96
|
-
}>>;
|
|
97
|
-
build: z.ZodDefault<z.ZodObject<{
|
|
98
|
-
name: z.ZodDefault<z.ZodString>;
|
|
99
|
-
environment: z.ZodDefault<z.ZodString>;
|
|
100
|
-
branch: z.ZodOptional<z.ZodString>;
|
|
101
|
-
commit: z.ZodOptional<z.ZodString>;
|
|
102
|
-
message: z.ZodOptional<z.ZodString>;
|
|
103
|
-
}, "strip", z.ZodTypeAny, {
|
|
104
|
-
name?: string;
|
|
105
|
-
message?: string;
|
|
106
|
-
environment?: string;
|
|
107
|
-
branch?: string;
|
|
108
|
-
commit?: string;
|
|
109
|
-
}, {
|
|
110
|
-
name?: string;
|
|
111
|
-
message?: string;
|
|
112
|
-
environment?: string;
|
|
113
|
-
branch?: string;
|
|
114
|
-
commit?: string;
|
|
115
|
-
}>>;
|
|
116
|
-
upload: z.ZodDefault<z.ZodObject<{
|
|
117
|
-
screenshotsDir: z.ZodDefault<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>>;
|
|
118
|
-
batchSize: z.ZodDefault<z.ZodNumber>;
|
|
119
|
-
timeout: z.ZodDefault<z.ZodNumber>;
|
|
120
|
-
}, "strip", z.ZodTypeAny, {
|
|
121
|
-
timeout?: number;
|
|
122
|
-
screenshotsDir?: string | string[];
|
|
123
|
-
batchSize?: number;
|
|
124
|
-
}, {
|
|
125
|
-
timeout?: number;
|
|
126
|
-
screenshotsDir?: string | string[];
|
|
127
|
-
batchSize?: number;
|
|
128
|
-
}>>;
|
|
129
|
-
comparison: z.ZodDefault<z.ZodObject<{
|
|
130
|
-
threshold: z.ZodDefault<z.ZodNumber>;
|
|
131
|
-
}, "strip", z.ZodTypeAny, {
|
|
132
|
-
threshold?: number;
|
|
133
|
-
}, {
|
|
134
|
-
threshold?: number;
|
|
135
|
-
}>>;
|
|
136
|
-
tdd: z.ZodDefault<z.ZodObject<{
|
|
137
|
-
openReport: z.ZodDefault<z.ZodBoolean>;
|
|
138
|
-
}, "strip", z.ZodTypeAny, {
|
|
139
|
-
openReport?: boolean;
|
|
140
|
-
}, {
|
|
141
|
-
openReport?: boolean;
|
|
142
|
-
}>>;
|
|
143
|
-
plugins: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
144
|
-
parallelId: z.ZodOptional<z.ZodString>;
|
|
145
|
-
baselineBuildId: z.ZodOptional<z.ZodString>;
|
|
146
|
-
baselineComparisonId: z.ZodOptional<z.ZodString>;
|
|
147
|
-
eager: z.ZodOptional<z.ZodBoolean>;
|
|
148
|
-
wait: z.ZodOptional<z.ZodBoolean>;
|
|
149
|
-
allowNoToken: z.ZodOptional<z.ZodBoolean>;
|
|
150
|
-
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
151
|
-
apiKey: z.ZodOptional<z.ZodString>;
|
|
152
|
-
apiUrl: z.ZodOptional<z.ZodString>;
|
|
153
|
-
server: z.ZodDefault<z.ZodObject<{
|
|
154
|
-
port: z.ZodDefault<z.ZodNumber>;
|
|
155
|
-
timeout: z.ZodDefault<z.ZodNumber>;
|
|
156
|
-
}, "strip", z.ZodTypeAny, {
|
|
157
|
-
port?: number;
|
|
158
|
-
timeout?: number;
|
|
159
|
-
}, {
|
|
160
|
-
port?: number;
|
|
161
|
-
timeout?: number;
|
|
162
|
-
}>>;
|
|
163
|
-
build: z.ZodDefault<z.ZodObject<{
|
|
164
|
-
name: z.ZodDefault<z.ZodString>;
|
|
165
|
-
environment: z.ZodDefault<z.ZodString>;
|
|
166
|
-
branch: z.ZodOptional<z.ZodString>;
|
|
167
|
-
commit: z.ZodOptional<z.ZodString>;
|
|
168
|
-
message: z.ZodOptional<z.ZodString>;
|
|
169
|
-
}, "strip", z.ZodTypeAny, {
|
|
170
|
-
name?: string;
|
|
171
|
-
message?: string;
|
|
172
|
-
environment?: string;
|
|
173
|
-
branch?: string;
|
|
174
|
-
commit?: string;
|
|
175
|
-
}, {
|
|
176
|
-
name?: string;
|
|
177
|
-
message?: string;
|
|
178
|
-
environment?: string;
|
|
179
|
-
branch?: string;
|
|
180
|
-
commit?: string;
|
|
181
|
-
}>>;
|
|
182
|
-
upload: z.ZodDefault<z.ZodObject<{
|
|
183
|
-
screenshotsDir: z.ZodDefault<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>>;
|
|
184
|
-
batchSize: z.ZodDefault<z.ZodNumber>;
|
|
185
|
-
timeout: z.ZodDefault<z.ZodNumber>;
|
|
186
|
-
}, "strip", z.ZodTypeAny, {
|
|
187
|
-
timeout?: number;
|
|
188
|
-
screenshotsDir?: string | string[];
|
|
189
|
-
batchSize?: number;
|
|
190
|
-
}, {
|
|
191
|
-
timeout?: number;
|
|
192
|
-
screenshotsDir?: string | string[];
|
|
193
|
-
batchSize?: number;
|
|
194
|
-
}>>;
|
|
195
|
-
comparison: z.ZodDefault<z.ZodObject<{
|
|
196
|
-
threshold: z.ZodDefault<z.ZodNumber>;
|
|
197
|
-
}, "strip", z.ZodTypeAny, {
|
|
198
|
-
threshold?: number;
|
|
199
|
-
}, {
|
|
200
|
-
threshold?: number;
|
|
201
|
-
}>>;
|
|
202
|
-
tdd: z.ZodDefault<z.ZodObject<{
|
|
203
|
-
openReport: z.ZodDefault<z.ZodBoolean>;
|
|
204
|
-
}, "strip", z.ZodTypeAny, {
|
|
205
|
-
openReport?: boolean;
|
|
206
|
-
}, {
|
|
207
|
-
openReport?: boolean;
|
|
208
|
-
}>>;
|
|
209
|
-
plugins: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
210
|
-
parallelId: z.ZodOptional<z.ZodString>;
|
|
211
|
-
baselineBuildId: z.ZodOptional<z.ZodString>;
|
|
212
|
-
baselineComparisonId: z.ZodOptional<z.ZodString>;
|
|
213
|
-
eager: z.ZodOptional<z.ZodBoolean>;
|
|
214
|
-
wait: z.ZodOptional<z.ZodBoolean>;
|
|
215
|
-
allowNoToken: z.ZodOptional<z.ZodBoolean>;
|
|
216
|
-
}, z.ZodTypeAny, "passthrough">>>;
|
|
50
|
+
}, z.core.$loose>>;
|
|
217
51
|
import { z } from 'zod';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve image buffer from file path or return buffer as-is
|
|
3
|
+
* Handles both Buffer inputs and file path strings, with proper validation and error handling
|
|
4
|
+
*
|
|
5
|
+
* @param {Buffer|string} imageBufferOrPath - Image data as Buffer or file path
|
|
6
|
+
* @param {string} contextName - Context for error messages (e.g., 'screenshot', 'compare')
|
|
7
|
+
* @returns {Buffer} The image buffer
|
|
8
|
+
* @throws {VizzlyError} When file not found, unreadable, or invalid input type
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // With Buffer
|
|
12
|
+
* const buffer = resolveImageBuffer(myBuffer, 'screenshot');
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // With file path
|
|
16
|
+
* const buffer = resolveImageBuffer('./my-image.png', 'screenshot');
|
|
17
|
+
*/
|
|
18
|
+
export function resolveImageBuffer(imageBufferOrPath: Buffer | string, contextName: string): Buffer;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the path to the global Vizzly directory
|
|
3
|
+
* @returns {string} Path to ~/.vizzly
|
|
4
|
+
*/
|
|
5
|
+
export function getGlobalConfigDir(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Get the path to the global config file
|
|
8
|
+
* @returns {string} Path to ~/.vizzly/config.json
|
|
9
|
+
*/
|
|
10
|
+
export function getGlobalConfigPath(): string;
|
|
11
|
+
/**
|
|
12
|
+
* Load the global configuration
|
|
13
|
+
* @returns {Promise<Object>} Global config object
|
|
14
|
+
*/
|
|
15
|
+
export function loadGlobalConfig(): Promise<any>;
|
|
16
|
+
/**
|
|
17
|
+
* Save the global configuration
|
|
18
|
+
* @param {Object} config - Configuration object to save
|
|
19
|
+
* @returns {Promise<void>}
|
|
20
|
+
*/
|
|
21
|
+
export function saveGlobalConfig(config: any): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Clear all global configuration
|
|
24
|
+
* @returns {Promise<void>}
|
|
25
|
+
*/
|
|
26
|
+
export function clearGlobalConfig(): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Get authentication tokens from global config
|
|
29
|
+
* @returns {Promise<Object|null>} Token object with accessToken, refreshToken, expiresAt, user, or null if not found
|
|
30
|
+
*/
|
|
31
|
+
export function getAuthTokens(): Promise<any | null>;
|
|
32
|
+
/**
|
|
33
|
+
* Save authentication tokens to global config
|
|
34
|
+
* @param {Object} auth - Auth object with accessToken, refreshToken, expiresAt, user
|
|
35
|
+
* @returns {Promise<void>}
|
|
36
|
+
*/
|
|
37
|
+
export function saveAuthTokens(auth: any): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Clear authentication tokens from global config
|
|
40
|
+
* @returns {Promise<void>}
|
|
41
|
+
*/
|
|
42
|
+
export function clearAuthTokens(): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Check if authentication tokens exist and are not expired
|
|
45
|
+
* @returns {Promise<boolean>} True if valid tokens exist
|
|
46
|
+
*/
|
|
47
|
+
export function hasValidTokens(): Promise<boolean>;
|
|
48
|
+
/**
|
|
49
|
+
* Get the access token from global config if available
|
|
50
|
+
* @returns {Promise<string|null>} Access token or null
|
|
51
|
+
*/
|
|
52
|
+
export function getAccessToken(): Promise<string | null>;
|
|
53
|
+
/**
|
|
54
|
+
* Get project mapping for a directory
|
|
55
|
+
* Walks up the directory tree to find the closest mapping
|
|
56
|
+
* @param {string} directoryPath - Absolute path to project directory
|
|
57
|
+
* @returns {Promise<Object|null>} Project data or null
|
|
58
|
+
*/
|
|
59
|
+
export function getProjectMapping(directoryPath: string): Promise<any | null>;
|
|
60
|
+
/**
|
|
61
|
+
* Save project mapping for a directory
|
|
62
|
+
* @param {string} directoryPath - Absolute path to project directory
|
|
63
|
+
* @param {Object} projectData - Project configuration
|
|
64
|
+
* @param {string} projectData.token - Project API token (vzt_...)
|
|
65
|
+
* @param {string} projectData.projectSlug - Project slug
|
|
66
|
+
* @param {string} projectData.organizationSlug - Organization slug
|
|
67
|
+
* @param {string} projectData.projectName - Project name
|
|
68
|
+
*/
|
|
69
|
+
export function saveProjectMapping(directoryPath: string, projectData: {
|
|
70
|
+
token: string;
|
|
71
|
+
projectSlug: string;
|
|
72
|
+
organizationSlug: string;
|
|
73
|
+
projectName: string;
|
|
74
|
+
}): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Get all project mappings
|
|
77
|
+
* @returns {Promise<Object>} Map of directory paths to project data
|
|
78
|
+
*/
|
|
79
|
+
export function getProjectMappings(): Promise<any>;
|
|
80
|
+
/**
|
|
81
|
+
* Delete project mapping for a directory
|
|
82
|
+
* @param {string} directoryPath - Absolute path to project directory
|
|
83
|
+
*/
|
|
84
|
+
export function deleteProjectMapping(directoryPath: string): Promise<void>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser utilities for opening URLs
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { execFile } from 'child_process';
|
|
6
|
+
import { platform } from 'os';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Open a URL in the default browser
|
|
10
|
+
* @param {string} url - URL to open
|
|
11
|
+
* @returns {Promise<boolean>} True if successful
|
|
12
|
+
*/
|
|
13
|
+
export async function openBrowser(url) {
|
|
14
|
+
return new Promise(resolve => {
|
|
15
|
+
let command;
|
|
16
|
+
let args;
|
|
17
|
+
let os = platform();
|
|
18
|
+
switch (os) {
|
|
19
|
+
case 'darwin':
|
|
20
|
+
// macOS
|
|
21
|
+
command = 'open';
|
|
22
|
+
args = [url];
|
|
23
|
+
break;
|
|
24
|
+
case 'win32':
|
|
25
|
+
// Windows
|
|
26
|
+
command = 'cmd.exe';
|
|
27
|
+
args = ['/c', 'start', '""', url];
|
|
28
|
+
break;
|
|
29
|
+
default:
|
|
30
|
+
// Linux and others
|
|
31
|
+
command = 'xdg-open';
|
|
32
|
+
args = [url];
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
execFile(command, args, error => {
|
|
36
|
+
if (error) {
|
|
37
|
+
// Browser opening failed, but don't throw - user can manually open
|
|
38
|
+
resolve(false);
|
|
39
|
+
} else {
|
|
40
|
+
resolve(true);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
@@ -2,9 +2,11 @@ import { cosmiconfigSync } from 'cosmiconfig';
|
|
|
2
2
|
import { resolve } from 'path';
|
|
3
3
|
import { getApiToken, getApiUrl, getParallelId } from './environment-config.js';
|
|
4
4
|
import { validateVizzlyConfigWithDefaults } from './config-schema.js';
|
|
5
|
+
import { getAccessToken, getProjectMapping } from './global-config.js';
|
|
5
6
|
const DEFAULT_CONFIG = {
|
|
6
7
|
// API Configuration
|
|
7
|
-
apiKey:
|
|
8
|
+
apiKey: undefined,
|
|
9
|
+
// Will be set from env, global config, or CLI overrides
|
|
8
10
|
apiUrl: getApiUrl(),
|
|
9
11
|
// Server Configuration (for run command)
|
|
10
12
|
server: {
|
|
@@ -70,16 +72,80 @@ export async function loadConfig(configPath = null, cliOverrides = {}) {
|
|
|
70
72
|
// Merge validated file config
|
|
71
73
|
mergeConfig(config, validatedFileConfig);
|
|
72
74
|
|
|
73
|
-
// 3.
|
|
75
|
+
// 3. Check project mapping for current directory (if no CLI flag)
|
|
76
|
+
if (!cliOverrides.token) {
|
|
77
|
+
const currentDir = process.cwd();
|
|
78
|
+
if (process.env.DEBUG_CONFIG) {
|
|
79
|
+
console.log('[CONFIG] Looking up project mapping for:', currentDir);
|
|
80
|
+
}
|
|
81
|
+
const projectMapping = await getProjectMapping(currentDir);
|
|
82
|
+
if (projectMapping && projectMapping.token) {
|
|
83
|
+
// Handle both string tokens and token objects (backward compatibility)
|
|
84
|
+
let token;
|
|
85
|
+
if (typeof projectMapping.token === 'string') {
|
|
86
|
+
token = projectMapping.token;
|
|
87
|
+
} else if (typeof projectMapping.token === 'object' && projectMapping.token.token) {
|
|
88
|
+
// Handle nested token object from old API responses
|
|
89
|
+
token = projectMapping.token.token;
|
|
90
|
+
} else {
|
|
91
|
+
token = String(projectMapping.token);
|
|
92
|
+
}
|
|
93
|
+
config.apiKey = token;
|
|
94
|
+
config.projectSlug = projectMapping.projectSlug;
|
|
95
|
+
config.organizationSlug = projectMapping.organizationSlug;
|
|
96
|
+
|
|
97
|
+
// Debug logging
|
|
98
|
+
if (process.env.DEBUG_CONFIG) {
|
|
99
|
+
console.log('[CONFIG] Found project mapping:', {
|
|
100
|
+
dir: currentDir,
|
|
101
|
+
projectSlug: projectMapping.projectSlug,
|
|
102
|
+
hasToken: !!projectMapping.token,
|
|
103
|
+
tokenType: typeof projectMapping.token,
|
|
104
|
+
tokenPrefix: token ? token.substring(0, 8) + '***' : 'none'
|
|
105
|
+
});
|
|
106
|
+
console.log('[CONFIG] Set config.apiKey to:', config.apiKey ? config.apiKey.substring(0, 8) + '***' : 'NONE');
|
|
107
|
+
}
|
|
108
|
+
} else if (process.env.DEBUG_CONFIG) {
|
|
109
|
+
console.log('[CONFIG] No project mapping found for:', currentDir);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 3.5. Check global config for user access token (if no CLI flag)
|
|
114
|
+
if (!config.apiKey && !cliOverrides.token) {
|
|
115
|
+
const globalToken = await getAccessToken();
|
|
116
|
+
if (globalToken) {
|
|
117
|
+
config.apiKey = globalToken;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 4. Override with environment variables (higher priority than fallbacks)
|
|
74
122
|
const envApiKey = getApiToken();
|
|
75
123
|
const envApiUrl = getApiUrl();
|
|
76
124
|
const envParallelId = getParallelId();
|
|
125
|
+
if (process.env.DEBUG_CONFIG) {
|
|
126
|
+
console.log('[CONFIG] Step 4 - env vars:', JSON.stringify({
|
|
127
|
+
hasEnvApiKey: !!envApiKey,
|
|
128
|
+
envApiKeyPrefix: envApiKey ? envApiKey.substring(0, 8) + '***' : 'none',
|
|
129
|
+
configApiKeyBefore: config.apiKey ? config.apiKey.substring(0, 8) + '***' : 'NONE'
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
77
132
|
if (envApiKey) config.apiKey = envApiKey;
|
|
78
133
|
if (envApiUrl !== 'https://app.vizzly.dev') config.apiUrl = envApiUrl;
|
|
79
134
|
if (envParallelId) config.parallelId = envParallelId;
|
|
80
135
|
|
|
81
|
-
//
|
|
136
|
+
// 5. Apply CLI overrides (highest priority)
|
|
137
|
+
if (process.env.DEBUG_CONFIG) {
|
|
138
|
+
console.log('[CONFIG] Step 5 - before CLI overrides:', {
|
|
139
|
+
configApiKey: config.apiKey ? config.apiKey.substring(0, 8) + '***' : 'NONE',
|
|
140
|
+
cliToken: cliOverrides.token ? cliOverrides.token.substring(0, 8) + '***' : 'none'
|
|
141
|
+
});
|
|
142
|
+
}
|
|
82
143
|
applyCLIOverrides(config, cliOverrides);
|
|
144
|
+
if (process.env.DEBUG_CONFIG) {
|
|
145
|
+
console.log('[CONFIG] Step 6 - after CLI overrides:', {
|
|
146
|
+
configApiKey: config.apiKey ? config.apiKey.substring(0, 8) + '***' : 'NONE'
|
|
147
|
+
});
|
|
148
|
+
}
|
|
83
149
|
return config;
|
|
84
150
|
}
|
|
85
151
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module file-helpers
|
|
3
|
+
* @description Utilities for handling file-based screenshot inputs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync, readFileSync } from 'fs';
|
|
7
|
+
import { resolve } from 'path';
|
|
8
|
+
import { VizzlyError } from '../errors/vizzly-error.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resolve image buffer from file path or return buffer as-is
|
|
12
|
+
* Handles both Buffer inputs and file path strings, with proper validation and error handling
|
|
13
|
+
*
|
|
14
|
+
* @param {Buffer|string} imageBufferOrPath - Image data as Buffer or file path
|
|
15
|
+
* @param {string} contextName - Context for error messages (e.g., 'screenshot', 'compare')
|
|
16
|
+
* @returns {Buffer} The image buffer
|
|
17
|
+
* @throws {VizzlyError} When file not found, unreadable, or invalid input type
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // With Buffer
|
|
21
|
+
* const buffer = resolveImageBuffer(myBuffer, 'screenshot');
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // With file path
|
|
25
|
+
* const buffer = resolveImageBuffer('./my-image.png', 'screenshot');
|
|
26
|
+
*/
|
|
27
|
+
export function resolveImageBuffer(imageBufferOrPath, contextName) {
|
|
28
|
+
// Return Buffer as-is
|
|
29
|
+
if (Buffer.isBuffer(imageBufferOrPath)) {
|
|
30
|
+
return imageBufferOrPath;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Validate input type
|
|
34
|
+
if (typeof imageBufferOrPath !== 'string') {
|
|
35
|
+
throw new VizzlyError(`Invalid image input: expected Buffer or file path string`, 'INVALID_INPUT', {
|
|
36
|
+
contextName,
|
|
37
|
+
type: typeof imageBufferOrPath
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Resolve to absolute path for consistent behavior
|
|
42
|
+
const filePath = resolve(imageBufferOrPath);
|
|
43
|
+
|
|
44
|
+
// Check file exists
|
|
45
|
+
if (!existsSync(filePath)) {
|
|
46
|
+
throw new VizzlyError(`Screenshot file not found: ${imageBufferOrPath}`, 'FILE_NOT_FOUND', {
|
|
47
|
+
contextName,
|
|
48
|
+
filePath,
|
|
49
|
+
originalPath: imageBufferOrPath
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Read file with error handling
|
|
54
|
+
try {
|
|
55
|
+
return readFileSync(filePath);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
throw new VizzlyError(`Failed to read screenshot file: ${imageBufferOrPath} - ${error.message}`, 'FILE_READ_ERROR', {
|
|
58
|
+
contextName,
|
|
59
|
+
filePath,
|
|
60
|
+
originalPath: imageBufferOrPath,
|
|
61
|
+
originalError: error.message
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|