@vizzly-testing/cli 0.20.1-beta.1 → 0.21.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.
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Project Service
3
+ * Wraps project operations for use by the HTTP server
4
+ *
5
+ * Provides the interface expected by src/server/routers/projects.js:
6
+ * - listProjects() - Returns [] if not authenticated
7
+ * - listMappings() - Returns [] if no mappings
8
+ * - getMapping(directory) - Returns null if not found
9
+ * - createMapping(directory, projectData) - Throws on invalid input
10
+ * - removeMapping(directory) - Throws on invalid directory
11
+ * - getRecentBuilds(projectSlug, organizationSlug, options) - Returns [] if not authenticated
12
+ *
13
+ * Error handling:
14
+ * - API methods (listProjects, getRecentBuilds) return empty arrays when not authenticated
15
+ * - Local methods (listMappings, getMapping) never require authentication
16
+ * - Validation errors (createMapping, removeMapping) throw with descriptive messages
17
+ */
18
+
19
+ import { createAuthClient } from '../auth/client.js';
20
+ import * as projectOps from '../project/operations.js';
21
+ import { getApiUrl } from '../utils/environment-config.js';
22
+ import { deleteProjectMapping, getAuthTokens, getProjectMapping, getProjectMappings, saveProjectMapping } from '../utils/global-config.js';
23
+
24
+ /**
25
+ * Create a project service instance
26
+ * @param {Object} [options]
27
+ * @param {string} [options.apiUrl] - API base URL (defaults to VIZZLY_API_URL or https://app.vizzly.dev)
28
+ * @param {Object} [options.httpClient] - Injectable HTTP client (for testing)
29
+ * @param {Object} [options.mappingStore] - Injectable mapping store (for testing)
30
+ * @param {Function} [options.getAuthTokens] - Injectable token getter (for testing)
31
+ * @returns {Object} Project service
32
+ */
33
+ export function createProjectService(options = {}) {
34
+ let apiUrl = options.apiUrl || getApiUrl();
35
+
36
+ // Create HTTP client once at service creation (not per-request)
37
+ // Allow injection for testing
38
+ let httpClient = options.httpClient || createAuthClient({
39
+ baseUrl: apiUrl
40
+ });
41
+
42
+ // Create mapping store adapter for global config
43
+ // Allow injection for testing
44
+ let mappingStore = options.mappingStore || {
45
+ getMappings: getProjectMappings,
46
+ getMapping: getProjectMapping,
47
+ saveMapping: saveProjectMapping,
48
+ deleteMapping: deleteProjectMapping
49
+ };
50
+
51
+ // Allow injection of getAuthTokens for testing
52
+ let tokenGetter = options.getAuthTokens || getAuthTokens;
53
+
54
+ /**
55
+ * Create an OAuth client with current access token
56
+ * @returns {Promise<Object|null>} OAuth client or null if not authenticated
57
+ */
58
+ async function createOAuthClient() {
59
+ let auth = await tokenGetter();
60
+ if (!auth?.accessToken) {
61
+ return null;
62
+ }
63
+
64
+ // Wrap authenticatedRequest to auto-inject the access token
65
+ return {
66
+ authenticatedRequest: (endpoint, fetchOptions = {}) => httpClient.authenticatedRequest(endpoint, auth.accessToken, fetchOptions)
67
+ };
68
+ }
69
+ return {
70
+ /**
71
+ * List all projects from API
72
+ * Returns empty array if not authenticated (projectOps handles null oauthClient)
73
+ * @returns {Promise<Array>} Array of projects, empty if not authenticated
74
+ */
75
+ async listProjects() {
76
+ let oauthClient = await createOAuthClient();
77
+ // projectOps.listProjects handles null oauthClient by returning []
78
+ return projectOps.listProjects({
79
+ oauthClient,
80
+ apiClient: null
81
+ });
82
+ },
83
+ /**
84
+ * List all project mappings
85
+ * @returns {Promise<Array>} Array of project mappings
86
+ */
87
+ async listMappings() {
88
+ return projectOps.listMappings(mappingStore);
89
+ },
90
+ /**
91
+ * Get project mapping for a specific directory
92
+ * @param {string} directory - Directory path
93
+ * @returns {Promise<Object|null>} Project mapping or null
94
+ */
95
+ async getMapping(directory) {
96
+ return projectOps.getMapping(mappingStore, directory);
97
+ },
98
+ /**
99
+ * Create or update project mapping
100
+ * @param {string} directory - Directory path
101
+ * @param {Object} projectData - Project data
102
+ * @returns {Promise<Object>} Created mapping
103
+ */
104
+ async createMapping(directory, projectData) {
105
+ return projectOps.createMapping(mappingStore, directory, projectData);
106
+ },
107
+ /**
108
+ * Remove project mapping
109
+ * @param {string} directory - Directory path
110
+ * @returns {Promise<void>}
111
+ */
112
+ async removeMapping(directory) {
113
+ return projectOps.removeMapping(mappingStore, directory);
114
+ },
115
+ /**
116
+ * Get recent builds for a project
117
+ * Returns empty array if not authenticated (projectOps handles null oauthClient)
118
+ * @param {string} projectSlug - Project slug
119
+ * @param {string} organizationSlug - Organization slug
120
+ * @param {Object} options - Query options
121
+ * @returns {Promise<Array>} Array of builds, empty if not authenticated
122
+ */
123
+ async getRecentBuilds(projectSlug, organizationSlug, options = {}) {
124
+ let oauthClient = await createOAuthClient();
125
+ // projectOps.getRecentBuilds handles null oauthClient by returning []
126
+ return projectOps.getRecentBuilds({
127
+ oauthClient,
128
+ apiClient: null,
129
+ projectSlug,
130
+ organizationSlug,
131
+ limit: options.limit,
132
+ branch: options.branch
133
+ });
134
+ }
135
+ };
136
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vizzly-testing/cli",
3
- "version": "0.20.1-beta.1",
3
+ "version": "0.21.0",
4
4
  "description": "Visual review platform for UI developers and designers",
5
5
  "keywords": [
6
6
  "visual-testing",
@@ -119,6 +119,7 @@
119
119
  "@tanstack/react-query": "^5.90.11",
120
120
  "@types/node": "^25.0.2",
121
121
  "@vitejs/plugin-react": "^5.0.3",
122
+ "@vizzly-testing/observatory": "^0.2.1",
122
123
  "autoprefixer": "^10.4.21",
123
124
  "babel-plugin-transform-remove-console": "^6.9.4",
124
125
  "postcss": "^8.5.6",