@vizzly-testing/cli 0.27.0 → 0.28.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/dist/api/endpoints.js +2 -0
- package/dist/cli.js +3 -35
- package/dist/commands/builds.js +1 -0
- package/dist/commands/comparisons.js +1 -0
- package/dist/commands/config-cmd.js +1 -20
- package/dist/commands/orgs.js +24 -9
- package/dist/commands/projects.js +24 -9
- package/dist/project/core.js +0 -84
- package/dist/project/operations.js +1 -77
- package/dist/reporter/reporter-bundle.css +1 -1
- package/dist/reporter/reporter-bundle.iife.js +55 -55
- package/dist/server/routers/projects.js +1 -101
- package/dist/services/project-service.js +1 -49
- package/dist/utils/config-loader.js +4 -27
- package/dist/utils/context.js +4 -38
- package/dist/utils/global-config.js +5 -78
- package/package.json +1 -1
- package/dist/commands/project.js +0 -414
package/dist/api/endpoints.js
CHANGED
|
@@ -223,6 +223,7 @@ export async function searchComparisons(client, name, filters = {}) {
|
|
|
223
223
|
}
|
|
224
224
|
let {
|
|
225
225
|
branch,
|
|
226
|
+
project,
|
|
226
227
|
limit = 50,
|
|
227
228
|
offset = 0
|
|
228
229
|
} = filters;
|
|
@@ -232,6 +233,7 @@ export async function searchComparisons(client, name, filters = {}) {
|
|
|
232
233
|
offset: String(offset)
|
|
233
234
|
};
|
|
234
235
|
if (branch) params.branch = branch;
|
|
236
|
+
if (project) params.project = project;
|
|
235
237
|
let endpoint = buildEndpointWithParams('/api/sdk/comparisons/search', params);
|
|
236
238
|
return client.request(endpoint);
|
|
237
239
|
}
|
package/dist/cli.js
CHANGED
|
@@ -13,7 +13,6 @@ import { loginCommand, validateLoginOptions } from './commands/login.js';
|
|
|
13
13
|
import { logoutCommand, validateLogoutOptions } from './commands/logout.js';
|
|
14
14
|
import { orgsCommand, validateOrgsOptions } from './commands/orgs.js';
|
|
15
15
|
import { previewCommand, validatePreviewOptions } from './commands/preview.js';
|
|
16
|
-
import { projectListCommand, projectRemoveCommand, projectSelectCommand, projectTokenCommand, validateProjectOptions } from './commands/project.js';
|
|
17
16
|
import { projectsCommand, validateProjectsOptions } from './commands/projects.js';
|
|
18
17
|
import { approveCommand, commentCommand, rejectCommand, validateApproveOptions, validateCommentOptions, validateRejectOptions } from './commands/review.js';
|
|
19
18
|
import { runCommand, validateRunOptions } from './commands/run.js';
|
|
@@ -93,11 +92,6 @@ const formatHelp = (cmd, helper) => {
|
|
|
93
92
|
icon: '▸',
|
|
94
93
|
title: 'Account',
|
|
95
94
|
names: ['login', 'logout', 'whoami', 'orgs', 'projects']
|
|
96
|
-
}, {
|
|
97
|
-
key: 'project',
|
|
98
|
-
icon: '▸',
|
|
99
|
-
title: 'Projects',
|
|
100
|
-
names: ['project:select', 'project:list', 'project:token', 'project:remove']
|
|
101
95
|
}];
|
|
102
96
|
let grouped = {
|
|
103
97
|
core: [],
|
|
@@ -105,7 +99,6 @@ const formatHelp = (cmd, helper) => {
|
|
|
105
99
|
setup: [],
|
|
106
100
|
advanced: [],
|
|
107
101
|
auth: [],
|
|
108
|
-
project: [],
|
|
109
102
|
other: []
|
|
110
103
|
};
|
|
111
104
|
for (let command of commands) {
|
|
@@ -439,10 +432,11 @@ program.command('status').description('Check the status of a build').argument('<
|
|
|
439
432
|
}
|
|
440
433
|
await statusCommand(buildId, options, globalOptions);
|
|
441
434
|
});
|
|
442
|
-
program.command('builds').description('List and query builds').option('-b, --build <id>', 'Get a specific build by ID').option('--branch <branch>', 'Filter by branch').option('--status <status>', 'Filter by status (created, pending, processing, completed, failed)').option('--environment <env>', 'Filter by environment').option('--limit <n>', 'Maximum results to return (1-250)', val => parseInt(val, 10), 20).option('--offset <n>', 'Skip first N results', val => parseInt(val, 10), 0).option('--comparisons', 'Include comparisons when fetching a specific build').addHelpText('after', `
|
|
435
|
+
program.command('builds').description('List and query builds').option('-b, --build <id>', 'Get a specific build by ID').option('--branch <branch>', 'Filter by branch').option('--status <status>', 'Filter by status (created, pending, processing, completed, failed)').option('--environment <env>', 'Filter by environment').option('-p, --project <slug>', 'Filter by project slug').option('--limit <n>', 'Maximum results to return (1-250)', val => parseInt(val, 10), 20).option('--offset <n>', 'Skip first N results', val => parseInt(val, 10), 0).option('--comparisons', 'Include comparisons when fetching a specific build').addHelpText('after', `
|
|
443
436
|
Examples:
|
|
444
437
|
$ vizzly builds # List recent builds
|
|
445
438
|
$ vizzly builds --branch main # Filter by branch
|
|
439
|
+
$ vizzly builds --project abc123 # Filter by project
|
|
446
440
|
$ vizzly builds --status completed # Filter by status
|
|
447
441
|
$ vizzly builds -b abc123-def456 # Get specific build by ID
|
|
448
442
|
$ vizzly builds -b abc123 --comparisons # Include comparisons
|
|
@@ -461,7 +455,7 @@ Examples:
|
|
|
461
455
|
}
|
|
462
456
|
await buildsCommand(options, globalOptions);
|
|
463
457
|
});
|
|
464
|
-
program.command('comparisons').description('Query and search comparisons').option('-b, --build <id>', 'Get comparisons for a specific build').option('--id <id>', 'Get a specific comparison by ID').option('--name <pattern>', 'Search comparisons by name (supports wildcards)').option('--status <status>', 'Filter by status (identical, new, changed)').option('--branch <branch>', 'Filter by branch (for name search)').option('--limit <n>', 'Maximum results to return (1-250)', val => parseInt(val, 10), 50).option('--offset <n>', 'Skip first N results', val => parseInt(val, 10), 0).addHelpText('after', `
|
|
458
|
+
program.command('comparisons').description('Query and search comparisons').option('-b, --build <id>', 'Get comparisons for a specific build').option('--id <id>', 'Get a specific comparison by ID').option('--name <pattern>', 'Search comparisons by name (supports wildcards)').option('--status <status>', 'Filter by status (identical, new, changed)').option('--branch <branch>', 'Filter by branch (for name search)').option('--limit <n>', 'Maximum results to return (1-250)', val => parseInt(val, 10), 50).option('--offset <n>', 'Skip first N results', val => parseInt(val, 10), 0).option('-p, --project <slug>', 'Filter by project slug').addHelpText('after', `
|
|
465
459
|
Examples:
|
|
466
460
|
$ vizzly comparisons -b abc123 # List comparisons for a build
|
|
467
461
|
$ vizzly comparisons --id def456 # Get specific comparison by ID
|
|
@@ -747,32 +741,6 @@ program.command('whoami').description('Show current authentication status and us
|
|
|
747
741
|
}
|
|
748
742
|
await whoamiCommand(options, globalOptions);
|
|
749
743
|
});
|
|
750
|
-
program.command('project:select').description('Configure project for current directory').option('--api-url <url>', 'API URL override').action(async options => {
|
|
751
|
-
const globalOptions = program.opts();
|
|
752
|
-
|
|
753
|
-
// Validate options
|
|
754
|
-
const validationErrors = validateProjectOptions(options);
|
|
755
|
-
if (validationErrors.length > 0) {
|
|
756
|
-
output.error('Validation errors:');
|
|
757
|
-
for (let error of validationErrors) {
|
|
758
|
-
output.printErr(` - ${error}`);
|
|
759
|
-
}
|
|
760
|
-
process.exit(1);
|
|
761
|
-
}
|
|
762
|
-
await projectSelectCommand(options, globalOptions);
|
|
763
|
-
});
|
|
764
|
-
program.command('project:list').description('Show all configured projects').action(async options => {
|
|
765
|
-
const globalOptions = program.opts();
|
|
766
|
-
await projectListCommand(options, globalOptions);
|
|
767
|
-
});
|
|
768
|
-
program.command('project:token').description('Show project token for current directory').action(async options => {
|
|
769
|
-
const globalOptions = program.opts();
|
|
770
|
-
await projectTokenCommand(options, globalOptions);
|
|
771
|
-
});
|
|
772
|
-
program.command('project:remove').description('Remove project configuration for current directory').action(async options => {
|
|
773
|
-
const globalOptions = program.opts();
|
|
774
|
-
await projectRemoveCommand(options, globalOptions);
|
|
775
|
-
});
|
|
776
744
|
|
|
777
745
|
// Save user's PATH for menubar app (non-blocking, runs in background)
|
|
778
746
|
// This auto-configures the menubar app so it can find npx/node
|
package/dist/commands/builds.js
CHANGED
|
@@ -74,6 +74,7 @@ export async function buildsCommand(options = {}, globalOptions = {}, deps = {})
|
|
|
74
74
|
if (options.branch) filters.branch = options.branch;
|
|
75
75
|
if (options.status) filters.status = options.status;
|
|
76
76
|
if (options.environment) filters.environment = options.environment;
|
|
77
|
+
if (options.project) filters.project = options.project;
|
|
77
78
|
let response = await getBuilds(client, filters);
|
|
78
79
|
output.stopSpinner();
|
|
79
80
|
let builds = response.builds || [];
|
|
@@ -108,6 +108,7 @@ export async function comparisonsCommand(options = {}, globalOptions = {}, deps
|
|
|
108
108
|
output.startSpinner('Searching comparisons...');
|
|
109
109
|
let filters = {
|
|
110
110
|
branch: options.branch,
|
|
111
|
+
project: options.project,
|
|
111
112
|
limit: options.limit || 50,
|
|
112
113
|
offset: options.offset || 0
|
|
113
114
|
};
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
* Config command - query and display configuration
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { resolve } from 'node:path';
|
|
6
5
|
import { loadConfig as defaultLoadConfig } from '../utils/config-loader.js';
|
|
7
|
-
import { getProjectMapping as defaultGetProjectMapping } from '../utils/global-config.js';
|
|
8
6
|
import * as defaultOutput from '../utils/output.js';
|
|
9
7
|
|
|
10
8
|
/**
|
|
@@ -17,7 +15,6 @@ import * as defaultOutput from '../utils/output.js';
|
|
|
17
15
|
export async function configCommand(key = null, options = {}, globalOptions = {}, deps = {}) {
|
|
18
16
|
let {
|
|
19
17
|
loadConfig = defaultLoadConfig,
|
|
20
|
-
getProjectMapping = defaultGetProjectMapping,
|
|
21
18
|
output = defaultOutput,
|
|
22
19
|
exit = code => process.exit(code)
|
|
23
20
|
} = deps;
|
|
@@ -34,10 +31,6 @@ export async function configCommand(key = null, options = {}, globalOptions = {}
|
|
|
34
31
|
});
|
|
35
32
|
let configFile = config._configPath || null;
|
|
36
33
|
|
|
37
|
-
// Get project mapping if available
|
|
38
|
-
let currentDir = resolve(process.cwd());
|
|
39
|
-
let projectMapping = await getProjectMapping(currentDir);
|
|
40
|
-
|
|
41
34
|
// Build the config object to display
|
|
42
35
|
let displayConfig = {
|
|
43
36
|
server: config.server || {
|
|
@@ -90,12 +83,7 @@ export async function configCommand(key = null, options = {}, globalOptions = {}
|
|
|
90
83
|
if (globalOptions.json) {
|
|
91
84
|
output.data({
|
|
92
85
|
configFile,
|
|
93
|
-
config: displayConfig
|
|
94
|
-
project: projectMapping ? {
|
|
95
|
-
name: projectMapping.projectName,
|
|
96
|
-
slug: projectMapping.projectSlug,
|
|
97
|
-
organization: projectMapping.organizationSlug
|
|
98
|
-
} : null
|
|
86
|
+
config: displayConfig
|
|
99
87
|
});
|
|
100
88
|
output.cleanup();
|
|
101
89
|
return;
|
|
@@ -112,13 +100,6 @@ export async function configCommand(key = null, options = {}, globalOptions = {}
|
|
|
112
100
|
}
|
|
113
101
|
output.blank();
|
|
114
102
|
|
|
115
|
-
// Project context if available
|
|
116
|
-
if (projectMapping) {
|
|
117
|
-
output.labelValue('Project', `${projectMapping.projectName} (${projectMapping.projectSlug})`);
|
|
118
|
-
output.labelValue('Organization', projectMapping.organizationSlug);
|
|
119
|
-
output.blank();
|
|
120
|
-
}
|
|
121
|
-
|
|
122
103
|
// Display configuration sections
|
|
123
104
|
displaySection(output, 'Server', displayConfig.server);
|
|
124
105
|
displaySection(output, 'Comparison', displayConfig.comparison);
|
package/dist/commands/orgs.js
CHANGED
|
@@ -2,17 +2,27 @@
|
|
|
2
2
|
* Organizations command - List organizations the user has access to
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { createApiClient } from '../api/client.js';
|
|
6
|
-
import { loadConfig } from '../utils/config-loader.js';
|
|
7
|
-
import { getApiUrl } from '../utils/environment-config.js';
|
|
8
|
-
import
|
|
5
|
+
import { createApiClient as defaultCreateApiClient } from '../api/client.js';
|
|
6
|
+
import { loadConfig as defaultLoadConfig } from '../utils/config-loader.js';
|
|
7
|
+
import { getApiUrl as defaultGetApiUrl } from '../utils/environment-config.js';
|
|
8
|
+
import { getAccessToken as defaultGetAccessToken } from '../utils/global-config.js';
|
|
9
|
+
import * as defaultOutput from '../utils/output.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Organizations command implementation
|
|
12
13
|
* @param {Object} options - Command options
|
|
13
14
|
* @param {Object} globalOptions - Global CLI options
|
|
15
|
+
* @param {Object} deps - Dependencies for testing
|
|
14
16
|
*/
|
|
15
|
-
export async function orgsCommand(_options = {}, globalOptions = {}) {
|
|
17
|
+
export async function orgsCommand(_options = {}, globalOptions = {}, deps = {}) {
|
|
18
|
+
let {
|
|
19
|
+
loadConfig = defaultLoadConfig,
|
|
20
|
+
createApiClient = defaultCreateApiClient,
|
|
21
|
+
getApiUrl = defaultGetApiUrl,
|
|
22
|
+
getAccessToken = defaultGetAccessToken,
|
|
23
|
+
output = defaultOutput,
|
|
24
|
+
exit = code => process.exit(code)
|
|
25
|
+
} = deps;
|
|
16
26
|
output.configure({
|
|
17
27
|
json: globalOptions.json,
|
|
18
28
|
verbose: globalOptions.verbose,
|
|
@@ -20,14 +30,19 @@ export async function orgsCommand(_options = {}, globalOptions = {}) {
|
|
|
20
30
|
});
|
|
21
31
|
try {
|
|
22
32
|
let config = await loadConfig(globalOptions.config, globalOptions);
|
|
23
|
-
|
|
33
|
+
|
|
34
|
+
// Prefer user auth token for listing orgs (project tokens are org-scoped).
|
|
35
|
+
// Falls back to config.apiKey which may be: VIZZLY_TOKEN, --token flag, or project token.
|
|
36
|
+
let token = (await getAccessToken()) || config.apiKey;
|
|
37
|
+
if (!token) {
|
|
24
38
|
output.error('API token required. Use --token, set VIZZLY_TOKEN, or run "vizzly login"');
|
|
25
39
|
output.cleanup();
|
|
26
|
-
|
|
40
|
+
exit(1);
|
|
41
|
+
return;
|
|
27
42
|
}
|
|
28
43
|
let client = createApiClient({
|
|
29
44
|
baseUrl: config.apiUrl || getApiUrl(),
|
|
30
|
-
token
|
|
45
|
+
token
|
|
31
46
|
});
|
|
32
47
|
output.startSpinner('Fetching organizations...');
|
|
33
48
|
let response = await client.request('/api/sdk/organizations');
|
|
@@ -65,7 +80,7 @@ export async function orgsCommand(_options = {}, globalOptions = {}) {
|
|
|
65
80
|
output.stopSpinner();
|
|
66
81
|
output.error('Failed to fetch organizations', error);
|
|
67
82
|
output.cleanup();
|
|
68
|
-
|
|
83
|
+
exit(1);
|
|
69
84
|
}
|
|
70
85
|
}
|
|
71
86
|
|
|
@@ -2,17 +2,27 @@
|
|
|
2
2
|
* Projects command - List projects the user has access to
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { createApiClient } from '../api/client.js';
|
|
6
|
-
import { loadConfig } from '../utils/config-loader.js';
|
|
7
|
-
import { getApiUrl } from '../utils/environment-config.js';
|
|
8
|
-
import
|
|
5
|
+
import { createApiClient as defaultCreateApiClient } from '../api/client.js';
|
|
6
|
+
import { loadConfig as defaultLoadConfig } from '../utils/config-loader.js';
|
|
7
|
+
import { getApiUrl as defaultGetApiUrl } from '../utils/environment-config.js';
|
|
8
|
+
import { getAccessToken as defaultGetAccessToken } from '../utils/global-config.js';
|
|
9
|
+
import * as defaultOutput from '../utils/output.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Projects command implementation
|
|
12
13
|
* @param {Object} options - Command options
|
|
13
14
|
* @param {Object} globalOptions - Global CLI options
|
|
15
|
+
* @param {Object} deps - Dependencies for testing
|
|
14
16
|
*/
|
|
15
|
-
export async function projectsCommand(options = {}, globalOptions = {}) {
|
|
17
|
+
export async function projectsCommand(options = {}, globalOptions = {}, deps = {}) {
|
|
18
|
+
let {
|
|
19
|
+
loadConfig = defaultLoadConfig,
|
|
20
|
+
createApiClient = defaultCreateApiClient,
|
|
21
|
+
getApiUrl = defaultGetApiUrl,
|
|
22
|
+
getAccessToken = defaultGetAccessToken,
|
|
23
|
+
output = defaultOutput,
|
|
24
|
+
exit = code => process.exit(code)
|
|
25
|
+
} = deps;
|
|
16
26
|
output.configure({
|
|
17
27
|
json: globalOptions.json,
|
|
18
28
|
verbose: globalOptions.verbose,
|
|
@@ -20,14 +30,19 @@ export async function projectsCommand(options = {}, globalOptions = {}) {
|
|
|
20
30
|
});
|
|
21
31
|
try {
|
|
22
32
|
let config = await loadConfig(globalOptions.config, globalOptions);
|
|
23
|
-
|
|
33
|
+
|
|
34
|
+
// Prefer user auth token for listing projects (project tokens are org-scoped).
|
|
35
|
+
// Falls back to config.apiKey which may be: VIZZLY_TOKEN, --token flag, or project token.
|
|
36
|
+
let token = (await getAccessToken()) || config.apiKey;
|
|
37
|
+
if (!token) {
|
|
24
38
|
output.error('API token required. Use --token, set VIZZLY_TOKEN, or run "vizzly login"');
|
|
25
39
|
output.cleanup();
|
|
26
|
-
|
|
40
|
+
exit(1);
|
|
41
|
+
return;
|
|
27
42
|
}
|
|
28
43
|
let client = createApiClient({
|
|
29
44
|
baseUrl: config.apiUrl || getApiUrl(),
|
|
30
|
-
token
|
|
45
|
+
token
|
|
31
46
|
});
|
|
32
47
|
|
|
33
48
|
// Build query params
|
|
@@ -82,7 +97,7 @@ export async function projectsCommand(options = {}, globalOptions = {}) {
|
|
|
82
97
|
output.stopSpinner();
|
|
83
98
|
output.error('Failed to fetch projects', error);
|
|
84
99
|
output.cleanup();
|
|
85
|
-
|
|
100
|
+
exit(1);
|
|
86
101
|
}
|
|
87
102
|
}
|
|
88
103
|
|
package/dist/project/core.js
CHANGED
|
@@ -6,90 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
import { VizzlyError } from '../errors/vizzly-error.js';
|
|
8
8
|
|
|
9
|
-
// ============================================================================
|
|
10
|
-
// Validation
|
|
11
|
-
// ============================================================================
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Validate that a directory path is provided
|
|
15
|
-
* @param {string} directory - Directory path to validate
|
|
16
|
-
* @returns {{ valid: boolean, error: Error|null }}
|
|
17
|
-
*/
|
|
18
|
-
export function validateDirectory(directory) {
|
|
19
|
-
if (!directory) {
|
|
20
|
-
return {
|
|
21
|
-
valid: false,
|
|
22
|
-
error: new VizzlyError('Directory path is required', 'INVALID_DIRECTORY')
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
return {
|
|
26
|
-
valid: true,
|
|
27
|
-
error: null
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Validate project data for mapping creation
|
|
33
|
-
* @param {Object} projectData - Project data to validate
|
|
34
|
-
* @param {string} [projectData.projectSlug] - Project slug
|
|
35
|
-
* @param {string} [projectData.organizationSlug] - Organization slug
|
|
36
|
-
* @param {string} [projectData.token] - Project token
|
|
37
|
-
* @returns {{ valid: boolean, error: Error|null }}
|
|
38
|
-
*/
|
|
39
|
-
export function validateProjectData(projectData) {
|
|
40
|
-
if (!projectData.projectSlug) {
|
|
41
|
-
return {
|
|
42
|
-
valid: false,
|
|
43
|
-
error: new VizzlyError('Project slug is required', 'INVALID_PROJECT_DATA')
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
if (!projectData.organizationSlug) {
|
|
47
|
-
return {
|
|
48
|
-
valid: false,
|
|
49
|
-
error: new VizzlyError('Organization slug is required', 'INVALID_PROJECT_DATA')
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
if (!projectData.token) {
|
|
53
|
-
return {
|
|
54
|
-
valid: false,
|
|
55
|
-
error: new VizzlyError('Project token is required', 'INVALID_PROJECT_DATA')
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
return {
|
|
59
|
-
valid: true,
|
|
60
|
-
error: null
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// ============================================================================
|
|
65
|
-
// Mapping Transformations
|
|
66
|
-
// ============================================================================
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Convert mappings object to array with directory paths included
|
|
70
|
-
* @param {Object} mappings - Object with directory paths as keys
|
|
71
|
-
* @returns {Array<Object>} Array of mappings with directory property
|
|
72
|
-
*/
|
|
73
|
-
export function mappingsToArray(mappings) {
|
|
74
|
-
return Object.entries(mappings).map(([directory, data]) => ({
|
|
75
|
-
directory,
|
|
76
|
-
...data
|
|
77
|
-
}));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Build a mapping result object
|
|
82
|
-
* @param {string} directory - Directory path
|
|
83
|
-
* @param {Object} projectData - Project data
|
|
84
|
-
* @returns {Object} Mapping result with directory included
|
|
85
|
-
*/
|
|
86
|
-
export function buildMappingResult(directory, projectData) {
|
|
87
|
-
return {
|
|
88
|
-
directory,
|
|
89
|
-
...projectData
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
9
|
// ============================================================================
|
|
94
10
|
// API Request Helpers
|
|
95
11
|
// ============================================================================
|
|
@@ -2,88 +2,12 @@
|
|
|
2
2
|
* Project Operations - Project operations with dependency injection
|
|
3
3
|
*
|
|
4
4
|
* Each operation takes its dependencies as parameters:
|
|
5
|
-
* - mappingStore: for reading/writing project mappings
|
|
6
5
|
* - httpClient: for making API requests (OAuth or API token based)
|
|
7
6
|
*
|
|
8
7
|
* This makes them trivially testable without mocking modules.
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
|
-
import { buildBuildsUrl,
|
|
12
|
-
|
|
13
|
-
// ============================================================================
|
|
14
|
-
// Mapping Operations
|
|
15
|
-
// ============================================================================
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* List all project mappings
|
|
19
|
-
* @param {Object} mappingStore - Store with getMappings method
|
|
20
|
-
* @returns {Promise<Array>} Array of project mappings with directory included
|
|
21
|
-
*/
|
|
22
|
-
export async function listMappings(mappingStore) {
|
|
23
|
-
let mappings = await mappingStore.getMappings();
|
|
24
|
-
return mappingsToArray(mappings);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Get project mapping for a specific directory
|
|
29
|
-
* @param {Object} mappingStore - Store with getMapping method
|
|
30
|
-
* @param {string} directory - Directory path
|
|
31
|
-
* @returns {Promise<Object|null>} Project mapping or null
|
|
32
|
-
*/
|
|
33
|
-
export async function getMapping(mappingStore, directory) {
|
|
34
|
-
return mappingStore.getMapping(directory);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Create or update project mapping
|
|
39
|
-
* @param {Object} mappingStore - Store with saveMapping method
|
|
40
|
-
* @param {string} directory - Directory path
|
|
41
|
-
* @param {Object} projectData - Project data
|
|
42
|
-
* @returns {Promise<Object>} Created mapping with directory included
|
|
43
|
-
*/
|
|
44
|
-
export async function createMapping(mappingStore, directory, projectData) {
|
|
45
|
-
let dirValidation = validateDirectory(directory);
|
|
46
|
-
if (!dirValidation.valid) {
|
|
47
|
-
throw dirValidation.error;
|
|
48
|
-
}
|
|
49
|
-
let dataValidation = validateProjectData(projectData);
|
|
50
|
-
if (!dataValidation.valid) {
|
|
51
|
-
throw dataValidation.error;
|
|
52
|
-
}
|
|
53
|
-
await mappingStore.saveMapping(directory, projectData);
|
|
54
|
-
return buildMappingResult(directory, projectData);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Remove project mapping
|
|
59
|
-
* @param {Object} mappingStore - Store with deleteMapping method
|
|
60
|
-
* @param {string} directory - Directory path
|
|
61
|
-
* @returns {Promise<void>}
|
|
62
|
-
*/
|
|
63
|
-
export async function removeMapping(mappingStore, directory) {
|
|
64
|
-
let validation = validateDirectory(directory);
|
|
65
|
-
if (!validation.valid) {
|
|
66
|
-
throw validation.error;
|
|
67
|
-
}
|
|
68
|
-
await mappingStore.deleteMapping(directory);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Switch project for a directory (convenience wrapper for createMapping)
|
|
73
|
-
* @param {Object} mappingStore - Store with saveMapping method
|
|
74
|
-
* @param {string} directory - Directory path
|
|
75
|
-
* @param {string} projectSlug - Project slug
|
|
76
|
-
* @param {string} organizationSlug - Organization slug
|
|
77
|
-
* @param {string} token - Project token
|
|
78
|
-
* @returns {Promise<Object>} Updated mapping
|
|
79
|
-
*/
|
|
80
|
-
export async function switchProject(mappingStore, directory, projectSlug, organizationSlug, token) {
|
|
81
|
-
return createMapping(mappingStore, directory, {
|
|
82
|
-
projectSlug,
|
|
83
|
-
organizationSlug,
|
|
84
|
-
token
|
|
85
|
-
});
|
|
86
|
-
}
|
|
10
|
+
import { buildBuildsUrl, buildNoApiServiceError, buildNoAuthError, buildOrgHeader, buildProjectFetchError, buildProjectUrl, buildTokenCreateError, buildTokenRevokeError, buildTokensFetchError, buildTokensUrl, enrichProjectsWithOrg, extractBuilds, extractOrganizations, extractProject, extractProjects, extractToken, extractTokens } from './core.js';
|
|
87
11
|
|
|
88
12
|
// ============================================================================
|
|
89
13
|
// API Operations - List Projects
|