@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.
Files changed (195) hide show
  1. package/README.md +12 -3
  2. package/bin/vizzly.js +1 -1
  3. package/dist/{cli.js → adapters/cli/cli.js} +50 -35
  4. package/dist/adapters/cli/setup.js +34 -0
  5. package/dist/{client → adapters/client}/index.js +6 -7
  6. package/dist/adapters/http/cloud-proxy.js +29 -0
  7. package/dist/{server → adapters/http}/http-server.js +12 -13
  8. package/dist/{server-manager/operations.js → adapters/http/server-lifecycle.js} +10 -13
  9. package/dist/{server-manager/index.js → adapters/http/server-manager.js} +23 -27
  10. package/dist/{plugin-api.js → adapters/plugins/plugin-api.js} +1 -1
  11. package/dist/{plugin-loader.js → adapters/plugins/plugin-loader.js} +1 -1
  12. package/dist/{services/index.js → adapters/plugins/services.js} +4 -9
  13. package/dist/adapters/sdk/index.d.ts +89 -0
  14. package/dist/{sdk → adapters/sdk}/index.js +22 -19
  15. package/dist/{commands/api.js → domains/api/api.commands.js} +3 -3
  16. package/dist/{auth/client.js → domains/auth/auth-client.js} +4 -3
  17. package/dist/domains/auth/auth-errors.js +50 -0
  18. package/dist/domains/auth/auth-headers.js +41 -0
  19. package/dist/domains/auth/auth.normalizers.js +50 -0
  20. package/dist/{server/routers/auth.js → domains/auth/auth.routes.js} +9 -9
  21. package/dist/domains/auth/auth.rules.js +19 -0
  22. package/dist/domains/auth/auth.serializers.js +32 -0
  23. package/dist/{auth/operations.js → domains/auth/auth.service.js} +115 -17
  24. package/dist/domains/auth/auth.validations.js +21 -0
  25. package/dist/{api/client.js → domains/auth/cloud-client.js} +11 -18
  26. package/dist/{commands/login.js → domains/auth/login.commands.js} +11 -8
  27. package/dist/{commands/logout.js → domains/auth/logout.commands.js} +6 -4
  28. package/dist/domains/auth/token-store.js +81 -0
  29. package/dist/{commands/whoami.js → domains/auth/whoami.commands.js} +6 -3
  30. package/dist/domains/baselines/baseline-download.js +192 -0
  31. package/dist/domains/baselines/baseline-write.js +22 -0
  32. package/dist/{server/routers/baseline.js → domains/baselines/baseline.routes.js} +3 -3
  33. package/dist/{tdd/services/baseline-manager.js → domains/baselines/baseline.service.js} +1 -39
  34. package/dist/{commands/baselines.js → domains/baselines/baselines.commands.js} +3 -3
  35. package/dist/domains/baselines/cloud-baselines.js +9 -0
  36. package/dist/domains/baselines/cloud-baselines.routes.js +42 -0
  37. package/dist/domains/builds/build.normalizers.js +35 -0
  38. package/dist/domains/builds/build.serializers.js +164 -0
  39. package/dist/domains/builds/build.service.js +119 -0
  40. package/dist/domains/builds/build.validations.js +39 -0
  41. package/dist/{commands/builds.js → domains/builds/builds.commands.js} +6 -74
  42. package/dist/domains/builds/cloud-builds.routes.js +31 -0
  43. package/dist/{commands/finalize.js → domains/builds/finalize.commands.js} +5 -17
  44. package/dist/{commands/status.js → domains/builds/status.commands.js} +9 -120
  45. package/dist/domains/comparisons/cloud-comparisons.js +43 -0
  46. package/dist/{tdd/services/comparison-service.js → domains/comparisons/comparison.service.js} +1 -1
  47. package/dist/{commands/comparisons.js → domains/comparisons/comparisons.commands.js} +7 -5
  48. package/dist/domains/config/config-defaults.js +27 -0
  49. package/dist/{utils → domains/config}/config-discovery.js +1 -9
  50. package/dist/{utils → domains/config}/config-loader.js +10 -9
  51. package/dist/{commands/config-cmd.js → domains/config/config.commands.js} +4 -4
  52. package/dist/{types → domains/config}/config.d.ts +3 -3
  53. package/dist/domains/config/config.normalizers.js +126 -0
  54. package/dist/{server/routers/config.js → domains/config/config.routes.js} +13 -13
  55. package/dist/domains/config/config.rules.js +47 -0
  56. package/dist/domains/config/config.serializers.js +202 -0
  57. package/dist/domains/config/config.service.js +625 -0
  58. package/dist/{utils/config-schema.js → domains/config/config.validations.js} +9 -9
  59. package/dist/{utils/environment-config.js → domains/config/environment.js} +1 -8
  60. package/dist/domains/context/compact-context.validations.js +60 -0
  61. package/dist/domains/context/context-presentation.js +368 -0
  62. package/dist/domains/context/context.commands.js +385 -0
  63. package/dist/domains/context/context.serializers.js +157 -0
  64. package/dist/domains/context/context.service.js +76 -0
  65. package/dist/domains/context/context.validations.js +100 -0
  66. package/dist/{context → domains/context}/local-workspace-provider.js +1 -1
  67. package/dist/{commands/doctor.js → domains/diagnostics/doctor.commands.js} +6 -5
  68. package/dist/{server/routers/health.js → domains/diagnostics/health.routes.js} +11 -20
  69. package/dist/{commands/preview.js → domains/previews/preview.commands.js} +9 -7
  70. package/dist/domains/previews/preview.service.js +37 -0
  71. package/dist/domains/projects/cloud-projects.routes.js +28 -0
  72. package/dist/{commands/orgs.js → domains/projects/orgs.commands.js} +6 -8
  73. package/dist/{utils → domains/projects}/project-binding.js +3 -3
  74. package/dist/domains/projects/project-errors.js +61 -0
  75. package/dist/{utils → domains/projects}/project-link-store.js +2 -2
  76. package/dist/domains/projects/project-requests.js +61 -0
  77. package/dist/{commands/project.js → domains/projects/project.commands.js} +9 -41
  78. package/dist/domains/projects/project.normalizers.js +69 -0
  79. package/dist/{project/operations.js → domains/projects/project.service.js} +89 -16
  80. package/dist/domains/projects/project.validations.js +31 -0
  81. package/dist/{commands/projects.js → domains/projects/projects.commands.js} +6 -8
  82. package/dist/{server/routers/projects.js → domains/projects/projects.routes.js} +5 -5
  83. package/dist/domains/reports/dashboard.routes.js +96 -0
  84. package/dist/{server/routers/events.js → domains/reports/events.routes.js} +14 -36
  85. package/dist/domains/reports/report-assets.routes.js +81 -0
  86. package/dist/domains/reports/report-state.js +51 -0
  87. package/dist/domains/reports/report-store.js +85 -0
  88. package/dist/domains/reports/report.serializers.js +99 -0
  89. package/dist/{services/static-report-generator.js → domains/reports/report.service.js} +11 -33
  90. package/dist/{commands/review.js → domains/review/review.commands.js} +4 -4
  91. package/dist/domains/runs/run-environment.js +69 -0
  92. package/dist/{services/test-runner.js → domains/runs/run-events.js} +11 -9
  93. package/dist/{commands/run.js → domains/runs/run.commands.js} +16 -13
  94. package/dist/domains/runs/run.normalizers.js +8 -0
  95. package/dist/domains/runs/run.rules.js +56 -0
  96. package/dist/domains/runs/run.serializers.js +101 -0
  97. package/dist/{test-runner/operations.js → domains/runs/run.service.js} +35 -7
  98. package/dist/domains/runs/run.validations.js +17 -0
  99. package/dist/{utils/session.js → domains/runs/session-store.js} +3 -2
  100. package/dist/{server/handlers/api-handler.js → domains/screenshots/cloud-handler.js} +4 -4
  101. package/dist/domains/screenshots/cloud-screenshots.js +109 -0
  102. package/dist/{utils/file-helpers.js → domains/screenshots/image-files.js} +1 -1
  103. package/dist/domains/screenshots/screenshot-hash.js +53 -0
  104. package/dist/domains/screenshots/screenshot-server-options.js +28 -0
  105. package/dist/{screenshot-server/operations.js → domains/screenshots/screenshot-server.js} +14 -1
  106. package/dist/domains/screenshots/screenshot.normalizers.js +40 -0
  107. package/dist/{server/routers/screenshot.js → domains/screenshots/screenshot.routes.js} +4 -4
  108. package/dist/domains/screenshots/screenshot.serializers.js +146 -0
  109. package/dist/{utils/security.js → domains/screenshots/screenshot.validations.js} +20 -70
  110. package/dist/{init/agents.js → domains/setup/agent-setup.js} +54 -14
  111. package/dist/{commands/init.js → domains/setup/init.commands.js} +11 -44
  112. package/dist/{server/handlers/tdd-handler.js → domains/tdd/screenshot-handler.js} +46 -281
  113. package/dist/{commands/tdd-daemon.js → domains/tdd/tdd-daemon.commands.js} +6 -6
  114. package/dist/{commands/tdd-screenshots.js → domains/tdd/tdd-screenshots.commands.js} +5 -5
  115. package/dist/{commands/tdd.js → domains/tdd/tdd.commands.js} +16 -15
  116. package/dist/{tdd/tdd-service.js → domains/tdd/tdd.service.js} +81 -405
  117. package/dist/domains/updates/cli-update.js +103 -0
  118. package/dist/domains/updates/update-check.js +2 -0
  119. package/dist/domains/updates/update-notifications.js +175 -0
  120. package/dist/domains/uploads/upload-defaults.js +4 -0
  121. package/dist/domains/uploads/upload-files.js +32 -0
  122. package/dist/domains/uploads/upload-settings.js +21 -0
  123. package/dist/domains/uploads/upload-timing.js +18 -0
  124. package/dist/{commands/upload.js → domains/uploads/upload.commands.js} +11 -9
  125. package/dist/domains/uploads/upload.normalizers.js +60 -0
  126. package/dist/domains/uploads/upload.serializers.js +179 -0
  127. package/dist/{uploader/operations.js → domains/uploads/upload.service.js} +85 -28
  128. package/dist/domains/uploads/upload.validations.js +77 -0
  129. package/dist/index.js +12 -12
  130. package/dist/reporter/reporter-bundle.iife.js +86 -86
  131. package/dist/reporter-ssr/ssr-entry.js +3 -6
  132. package/dist/shared/cloud-protocol.js +171 -0
  133. package/dist/{utils → shared}/file-changes.js +1 -1
  134. package/dist/shared/files.js +9 -0
  135. package/dist/{utils → shared}/global-config.js +7 -78
  136. package/dist/shared/log-level.js +7 -0
  137. package/dist/{utils → shared}/output.js +1 -1
  138. package/dist/{utils → shared}/package-info.js +5 -4
  139. package/dist/shared/paths.js +57 -0
  140. package/package.json +37 -36
  141. package/dist/api/core.js +0 -382
  142. package/dist/api/endpoints.js +0 -421
  143. package/dist/api/index.js +0 -19
  144. package/dist/auth/core.js +0 -237
  145. package/dist/auth/index.js +0 -31
  146. package/dist/commands/context.js +0 -1066
  147. package/dist/config/core.js +0 -439
  148. package/dist/config/operations.js +0 -327
  149. package/dist/project/core.js +0 -211
  150. package/dist/screenshot-server/core.js +0 -161
  151. package/dist/screenshot-server/index.js +0 -11
  152. package/dist/server/routers/assets.js +0 -95
  153. package/dist/server/routers/cloud-proxy.js +0 -146
  154. package/dist/server/routers/dashboard.js +0 -172
  155. package/dist/services/auth-service.js +0 -117
  156. package/dist/services/config-service.js +0 -313
  157. package/dist/services/project-service.js +0 -88
  158. package/dist/test-runner/core.js +0 -293
  159. package/dist/test-runner/index.js +0 -13
  160. package/dist/types/sdk.d.ts +0 -186
  161. package/dist/uploader/core.js +0 -432
  162. package/dist/uploader/index.js +0 -88
  163. /package/dist/{types/client.d.ts → adapters/client/index.d.ts} +0 -0
  164. /package/dist/{server → adapters/http}/middleware/cors.js +0 -0
  165. /package/dist/{server → adapters/http}/middleware/json-parser.js +0 -0
  166. /package/dist/{server → adapters/http}/middleware/response.js +0 -0
  167. /package/dist/{server-manager/core.js → adapters/http/server-options.js} +0 -0
  168. /package/dist/{utils/login-poll-wait.js → domains/auth/login-wait.js} +0 -0
  169. /package/dist/{tdd/metadata → domains/baselines}/baseline-metadata.js +0 -0
  170. /package/dist/{services/build-manager.js → domains/builds/build-info.js} +0 -0
  171. /package/dist/{tdd/services/result-service.js → domains/comparisons/comparison-results.js} +0 -0
  172. /package/dist/{init/core.js → domains/config/config-plan.js} +0 -0
  173. /package/dist/{utils/config-helpers.js → domains/config/config.js} +0 -0
  174. /package/dist/{utils/visual-context-normalizers.js → domains/context/context.normalizers.js} +0 -0
  175. /package/dist/{context → domains/context}/provider-resolver.js +0 -0
  176. /package/dist/{utils/context.js → domains/diagnostics/environment-context.js} +0 -0
  177. /package/dist/{utils/report-data.js → domains/reports/report.normalizers.js} +0 -0
  178. /package/dist/{utils/image-input-detector.js → domains/screenshots/image-input.js} +0 -0
  179. /package/dist/{tdd/core/signature.js → domains/screenshots/screenshot-identity.js} +0 -0
  180. /package/dist/{utils → domains/screenshots}/screenshot-options.js +0 -0
  181. /package/dist/{init/prompts.js → domains/setup/setup-prompts.js} +0 -0
  182. /package/dist/{tdd → domains/tdd}/runtime-context.js +0 -0
  183. /package/dist/{tdd → domains/tdd}/server-registry.js +0 -0
  184. /package/dist/{commands/tdd-daemon-process.js → domains/tdd/tdd-daemon-process.commands.js} +0 -0
  185. /package/dist/{types/index.d.ts → index.d.ts} +0 -0
  186. /package/dist/{utils → shared}/api-url.js +0 -0
  187. /package/dist/{utils → shared}/async-utils.js +0 -0
  188. /package/dist/{utils → shared}/browser.js +0 -0
  189. /package/dist/{utils → shared}/ci-env.js +0 -0
  190. /package/dist/{utils → shared}/colors.js +0 -0
  191. /package/dist/{utils → shared}/fetch-utils.js +0 -0
  192. /package/dist/{utils → shared}/git.js +0 -0
  193. /package/dist/{utils → shared}/patterns.js +0 -0
  194. /package/dist/{utils → shared}/secret-store.js +0 -0
  195. /package/dist/{errors → shared}/vizzly-error.js +0 -0
@@ -1,12 +1,12 @@
1
+ import { parseJsonBody } from '../../adapters/http/middleware/json-parser.js';
2
+ import { sendError, sendServiceUnavailable, sendSuccess } from '../../adapters/http/middleware/response.js';
3
+ import * as output from '../../shared/output.js';
4
+
1
5
  /**
2
6
  * Config Router
3
7
  * Handles configuration management endpoints
4
8
  */
5
9
 
6
- import * as output from '../../utils/output.js';
7
- import { parseJsonBody } from '../middleware/json-parser.js';
8
- import { sendError, sendServiceUnavailable, sendSuccess } from '../middleware/response.js';
9
-
10
10
  /**
11
11
  * Create config router
12
12
  * @param {Object} context - Router context
@@ -26,7 +26,7 @@ export function createConfigRouter({
26
26
  // Get merged config with sources
27
27
  if (req.method === 'GET' && pathname === '/api/config') {
28
28
  try {
29
- const configData = await configService.getConfig('merged');
29
+ let configData = await configService.getConfig('merged');
30
30
  sendSuccess(res, configData);
31
31
  return true;
32
32
  } catch (error) {
@@ -41,7 +41,7 @@ export function createConfigRouter({
41
41
  // Get project-level config
42
42
  if (req.method === 'GET' && pathname === '/api/config/project') {
43
43
  try {
44
- const configData = await configService.getConfig('project');
44
+ let configData = await configService.getConfig('project');
45
45
  sendSuccess(res, configData);
46
46
  return true;
47
47
  } catch (error) {
@@ -56,7 +56,7 @@ export function createConfigRouter({
56
56
  // Get global config
57
57
  if (req.method === 'GET' && pathname === '/api/config/global') {
58
58
  try {
59
- const configData = await configService.getConfig('global');
59
+ let configData = await configService.getConfig('global');
60
60
  sendSuccess(res, configData);
61
61
  return true;
62
62
  } catch (error) {
@@ -71,8 +71,8 @@ export function createConfigRouter({
71
71
  // Update project config
72
72
  if (req.method === 'POST' && pathname === '/api/config/project') {
73
73
  try {
74
- const body = await parseJsonBody(req);
75
- const result = await configService.updateConfig('project', body);
74
+ let body = await parseJsonBody(req);
75
+ let result = await configService.updateConfig('project', body);
76
76
  sendSuccess(res, {
77
77
  success: true,
78
78
  ...result
@@ -90,8 +90,8 @@ export function createConfigRouter({
90
90
  // Update global config
91
91
  if (req.method === 'POST' && pathname === '/api/config/global') {
92
92
  try {
93
- const body = await parseJsonBody(req);
94
- const result = await configService.updateConfig('global', body);
93
+ let body = await parseJsonBody(req);
94
+ let result = await configService.updateConfig('global', body);
95
95
  sendSuccess(res, {
96
96
  success: true,
97
97
  ...result
@@ -109,8 +109,8 @@ export function createConfigRouter({
109
109
  // Validate config
110
110
  if (req.method === 'POST' && pathname === '/api/config/validate') {
111
111
  try {
112
- const body = await parseJsonBody(req);
113
- const result = await configService.validateConfig(body);
112
+ let body = await parseJsonBody(req);
113
+ let result = await configService.validateConfig(body);
114
114
  sendSuccess(res, result);
115
115
  return true;
116
116
  } catch (error) {
@@ -0,0 +1,47 @@
1
+ import { VizzlyError } from '../../shared/vizzly-error.js';
2
+
3
+ /**
4
+ * Valid config scopes for reading
5
+ */
6
+ export let READ_SCOPES = ['project', 'global', 'merged'];
7
+
8
+ /**
9
+ * Valid config scopes for writing
10
+ */
11
+ export let WRITE_SCOPES = ['project', 'global'];
12
+
13
+ /**
14
+ * Validate that a scope is valid for reading
15
+ * @param {string} scope - Scope to validate
16
+ * @returns {{ valid: boolean, error: Error|null }}
17
+ */
18
+ export function validateReadScope(scope) {
19
+ if (!READ_SCOPES.includes(scope)) {
20
+ return {
21
+ valid: false,
22
+ error: new VizzlyError(`Invalid config scope: ${scope}. Must be 'project', 'global', or 'merged'`, 'INVALID_CONFIG_SCOPE')
23
+ };
24
+ }
25
+ return {
26
+ valid: true,
27
+ error: null
28
+ };
29
+ }
30
+
31
+ /**
32
+ * Validate that a scope is valid for writing
33
+ * @param {string} scope - Scope to validate
34
+ * @returns {{ valid: boolean, error: Error|null }}
35
+ */
36
+ export function validateWriteScope(scope) {
37
+ if (!WRITE_SCOPES.includes(scope)) {
38
+ return {
39
+ valid: false,
40
+ error: new VizzlyError(`Invalid config scope for update: ${scope}. Must be 'project' or 'global'`, 'INVALID_CONFIG_SCOPE')
41
+ };
42
+ }
43
+ return {
44
+ valid: true,
45
+ error: null
46
+ };
47
+ }
@@ -0,0 +1,202 @@
1
+ import { VizzlyError } from '../../shared/vizzly-error.js';
2
+ import { buildMergedConfig } from './config.normalizers.js';
3
+
4
+ /**
5
+ * Build a project config result object
6
+ * @param {Object|null} config - Config object or null if not found
7
+ * @param {string|null} filepath - Path to config file or null
8
+ * @returns {{ config: Object, filepath: string|null, isEmpty: boolean }}
9
+ */
10
+ export function buildProjectConfigResult(config, filepath) {
11
+ if (!config) {
12
+ return {
13
+ config: {},
14
+ filepath: null,
15
+ isEmpty: true
16
+ };
17
+ }
18
+ return {
19
+ config,
20
+ filepath,
21
+ isEmpty: Object.keys(config).length === 0
22
+ };
23
+ }
24
+
25
+ /**
26
+ * Build a global config result object
27
+ * @param {Object} config - Global config object
28
+ * @param {string} filepath - Path to global config file
29
+ * @returns {{ config: Object, filepath: string, isEmpty: boolean }}
30
+ */
31
+ export function buildGlobalConfigResult(config, filepath) {
32
+ return {
33
+ config,
34
+ filepath,
35
+ isEmpty: Object.keys(config).length === 0
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Build a merged config result object
41
+ * @param {Object} options - Build options
42
+ * @returns {{ config: Object, sources: Object, projectFilepath: string|null, globalFilepath: string }}
43
+ */
44
+ export function buildMergedConfigResult({
45
+ projectConfig,
46
+ globalConfig,
47
+ envOverrides,
48
+ projectFilepath,
49
+ globalFilepath
50
+ }) {
51
+ let {
52
+ config,
53
+ sources
54
+ } = buildMergedConfig({
55
+ projectConfig,
56
+ globalConfig,
57
+ envOverrides
58
+ });
59
+ return {
60
+ config,
61
+ sources,
62
+ projectFilepath,
63
+ globalFilepath
64
+ };
65
+ }
66
+
67
+ /**
68
+ * Stringify a value with proper indentation for JavaScript output
69
+ * @param {*} value - Value to stringify
70
+ * @param {number} depth - Current depth for indentation
71
+ * @returns {string} JavaScript representation of value
72
+ */
73
+ export function stringifyWithIndent(value, depth = 0) {
74
+ let indent = ' '.repeat(depth);
75
+ let prevIndent = depth > 0 ? ' '.repeat(depth - 1) : '';
76
+ if (value === null || value === undefined) {
77
+ return String(value);
78
+ }
79
+ if (typeof value === 'string') {
80
+ return `'${value.replace(/'/g, "\\'")}'`;
81
+ }
82
+ if (typeof value === 'number' || typeof value === 'boolean') {
83
+ return String(value);
84
+ }
85
+ if (Array.isArray(value)) {
86
+ if (value.length === 0) return '[]';
87
+ let items = value.map(item => `${indent}${stringifyWithIndent(item, depth + 1)}`);
88
+ return `[\n${items.join(',\n')}\n${prevIndent}]`;
89
+ }
90
+ if (typeof value === 'object') {
91
+ let keys = Object.keys(value);
92
+ if (keys.length === 0) return '{}';
93
+ let items = keys.map(key => {
94
+ let val = stringifyWithIndent(value[key], depth + 1);
95
+ return `${indent}${key}: ${val}`;
96
+ });
97
+ return `{\n${items.join(',\n')}\n${prevIndent}}`;
98
+ }
99
+ return String(value);
100
+ }
101
+
102
+ /**
103
+ * Serialize config to JavaScript module format
104
+ * @param {Object} config - Config object to serialize
105
+ * @returns {string} JavaScript source code
106
+ */
107
+ export function serializeToJavaScript(config) {
108
+ let lines = ['/**', ' * Vizzly Configuration', ' * @see https://docs.vizzly.dev/cli/configuration', ' */', '', "import { defineConfig } from '@vizzly-testing/cli/config';", '', 'export default defineConfig(', stringifyWithIndent(config, 1), ');', ''];
109
+ return lines.join('\n');
110
+ }
111
+
112
+ /**
113
+ * Serialize config to JSON format
114
+ * @param {Object} config - Config object to serialize
115
+ * @returns {string} JSON string with 2-space indentation
116
+ */
117
+ export function serializeToJson(config) {
118
+ return JSON.stringify(config, null, 2);
119
+ }
120
+
121
+ /**
122
+ * Determine the serialization format based on filepath
123
+ * @param {string} filepath - Path to config file
124
+ * @returns {'javascript'|'json'|'package'|'unknown'} Format type
125
+ */
126
+ export function getConfigFormat(filepath) {
127
+ if (filepath.endsWith('.js') || filepath.endsWith('.mjs')) {
128
+ return 'javascript';
129
+ }
130
+ if (filepath.endsWith('.json') && !filepath.endsWith('package.json')) {
131
+ return 'json';
132
+ }
133
+ if (filepath.endsWith('package.json')) {
134
+ return 'package';
135
+ }
136
+ return 'unknown';
137
+ }
138
+
139
+ /**
140
+ * Serialize config for writing to file
141
+ * @param {Object} config - Config object to serialize
142
+ * @param {string} filepath - Target file path
143
+ * @returns {{ content: string|null, format: string, error: Error|null }}
144
+ */
145
+ export function serializeConfig(config, filepath) {
146
+ let format = getConfigFormat(filepath);
147
+ if (format === 'javascript') {
148
+ return {
149
+ content: serializeToJavaScript(config),
150
+ format,
151
+ error: null
152
+ };
153
+ }
154
+ if (format === 'json') {
155
+ return {
156
+ content: serializeToJson(config),
157
+ format,
158
+ error: null
159
+ };
160
+ }
161
+ if (format === 'package') {
162
+ // Can't serialize standalone, need existing package.json
163
+ return {
164
+ content: null,
165
+ format,
166
+ error: null
167
+ };
168
+ }
169
+ return {
170
+ content: null,
171
+ format,
172
+ error: new VizzlyError(`Unsupported config file format: ${filepath}`, 'UNSUPPORTED_CONFIG_FORMAT')
173
+ };
174
+ }
175
+
176
+ /**
177
+ * Build a validation success result
178
+ * @param {Object} validatedConfig - Validated config
179
+ * @returns {{ valid: true, config: Object, errors: [] }}
180
+ */
181
+ export function buildValidationSuccess(validatedConfig) {
182
+ return {
183
+ valid: true,
184
+ config: validatedConfig,
185
+ errors: []
186
+ };
187
+ }
188
+
189
+ /**
190
+ * Build a validation failure result
191
+ * @param {Error} error - Validation error
192
+ * @returns {{ valid: false, config: null, errors: Array }}
193
+ */
194
+ export function buildValidationFailure(error) {
195
+ return {
196
+ valid: false,
197
+ config: null,
198
+ errors: error.errors || [{
199
+ message: error.message
200
+ }]
201
+ };
202
+ }