@alliance-droid/svelte-docs-system 0.0.1

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 (134) hide show
  1. package/COMPONENTS.md +365 -0
  2. package/COVERAGE_REPORT.md +663 -0
  3. package/README.md +42 -0
  4. package/SEARCH_VERIFICATION.md +229 -0
  5. package/TEST_SUMMARY.md +344 -0
  6. package/bin/init.js +821 -0
  7. package/docs/E2E_TESTS.md +354 -0
  8. package/docs/TESTING.md +754 -0
  9. package/docs/de/index.md +41 -0
  10. package/docs/en/COMPONENTS.md +443 -0
  11. package/docs/en/api/examples.md +100 -0
  12. package/docs/en/api/overview.md +69 -0
  13. package/docs/en/components/index.md +622 -0
  14. package/docs/en/config/navigation.md +505 -0
  15. package/docs/en/config/theme-and-colors.md +395 -0
  16. package/docs/en/getting-started/integration.md +406 -0
  17. package/docs/en/guides/common-setups.md +651 -0
  18. package/docs/en/index.md +243 -0
  19. package/docs/en/markdown.md +102 -0
  20. package/docs/en/routing.md +64 -0
  21. package/docs/en/setup.md +52 -0
  22. package/docs/en/troubleshooting.md +704 -0
  23. package/docs/es/index.md +41 -0
  24. package/docs/fr/index.md +41 -0
  25. package/docs/ja/index.md +41 -0
  26. package/package.json +40 -0
  27. package/pagefind.toml +8 -0
  28. package/postcss.config.js +5 -0
  29. package/src/app.css +119 -0
  30. package/src/app.d.ts +13 -0
  31. package/src/app.html +11 -0
  32. package/src/lib/assets/favicon.svg +1 -0
  33. package/src/lib/components/APITable.svelte +120 -0
  34. package/src/lib/components/APITable.test.ts +153 -0
  35. package/src/lib/components/Breadcrumbs.svelte +85 -0
  36. package/src/lib/components/Breadcrumbs.test.ts +148 -0
  37. package/src/lib/components/Callout.svelte +60 -0
  38. package/src/lib/components/Callout.test.ts +100 -0
  39. package/src/lib/components/CodeBlock.svelte +68 -0
  40. package/src/lib/components/CodeBlock.test.ts +133 -0
  41. package/src/lib/components/DocLayout.svelte +84 -0
  42. package/src/lib/components/Footer.svelte +78 -0
  43. package/src/lib/components/Image.svelte +100 -0
  44. package/src/lib/components/Image.test.ts +163 -0
  45. package/src/lib/components/Navbar.svelte +141 -0
  46. package/src/lib/components/Search.svelte +248 -0
  47. package/src/lib/components/Sidebar.svelte +110 -0
  48. package/src/lib/components/Tabs.svelte +48 -0
  49. package/src/lib/components/Tabs.test.ts +102 -0
  50. package/src/lib/config.test.ts +140 -0
  51. package/src/lib/config.ts +179 -0
  52. package/src/lib/configIntegration.test.ts +272 -0
  53. package/src/lib/configLoader.ts +231 -0
  54. package/src/lib/configParser.test.ts +217 -0
  55. package/src/lib/configParser.ts +234 -0
  56. package/src/lib/index.ts +34 -0
  57. package/src/lib/integration.test.ts +426 -0
  58. package/src/lib/navigationBuilder.test.ts +338 -0
  59. package/src/lib/navigationBuilder.ts +268 -0
  60. package/src/lib/performance.test.ts +369 -0
  61. package/src/lib/routing.test.ts +202 -0
  62. package/src/lib/routing.ts +127 -0
  63. package/src/lib/search-functionality.test.ts +493 -0
  64. package/src/lib/stores/i18n.test.ts +180 -0
  65. package/src/lib/stores/i18n.ts +143 -0
  66. package/src/lib/stores/nav.ts +36 -0
  67. package/src/lib/stores/search.test.ts +140 -0
  68. package/src/lib/stores/search.ts +162 -0
  69. package/src/lib/stores/theme.ts +59 -0
  70. package/src/lib/stores/version.test.ts +139 -0
  71. package/src/lib/stores/version.ts +111 -0
  72. package/src/lib/themeCustomization.test.ts +223 -0
  73. package/src/lib/themeCustomization.ts +212 -0
  74. package/src/lib/utils/highlight.test.ts +136 -0
  75. package/src/lib/utils/highlight.ts +100 -0
  76. package/src/lib/utils/index.ts +7 -0
  77. package/src/lib/utils/markdown.test.ts +357 -0
  78. package/src/lib/utils/markdown.ts +77 -0
  79. package/src/routes/+layout.server.ts +1 -0
  80. package/src/routes/+layout.svelte +28 -0
  81. package/src/routes/+page.svelte +165 -0
  82. package/static/robots.txt +3 -0
  83. package/svelte.config.js +18 -0
  84. package/tailwind.config.ts +55 -0
  85. package/template-starter/.github/workflows/build.yml +40 -0
  86. package/template-starter/.github/workflows/deploy-github-pages.yml +47 -0
  87. package/template-starter/.github/workflows/deploy-netlify.yml +41 -0
  88. package/template-starter/.github/workflows/deploy-vercel.yml +64 -0
  89. package/template-starter/NPM-PACKAGE-SETUP.md +233 -0
  90. package/template-starter/README.md +320 -0
  91. package/template-starter/docs/_config.json +39 -0
  92. package/template-starter/docs/api/components.md +257 -0
  93. package/template-starter/docs/api/overview.md +169 -0
  94. package/template-starter/docs/guides/configuration.md +145 -0
  95. package/template-starter/docs/guides/github-pages-deployment.md +254 -0
  96. package/template-starter/docs/guides/netlify-deployment.md +159 -0
  97. package/template-starter/docs/guides/vercel-deployment.md +131 -0
  98. package/template-starter/docs/index.md +49 -0
  99. package/template-starter/docs/setup.md +149 -0
  100. package/template-starter/package.json +31 -0
  101. package/template-starter/pagefind.toml +3 -0
  102. package/template-starter/postcss.config.js +5 -0
  103. package/template-starter/src/app.css +34 -0
  104. package/template-starter/src/app.d.ts +13 -0
  105. package/template-starter/src/app.html +11 -0
  106. package/template-starter/src/lib/components/APITable.svelte +120 -0
  107. package/template-starter/src/lib/components/APITable.test.ts +19 -0
  108. package/template-starter/src/lib/components/Breadcrumbs.svelte +85 -0
  109. package/template-starter/src/lib/components/Breadcrumbs.test.ts +19 -0
  110. package/template-starter/src/lib/components/Callout.svelte +60 -0
  111. package/template-starter/src/lib/components/Callout.test.ts +16 -0
  112. package/template-starter/src/lib/components/CodeBlock.svelte +68 -0
  113. package/template-starter/src/lib/components/CodeBlock.test.ts +12 -0
  114. package/template-starter/src/lib/components/DocLayout.svelte +84 -0
  115. package/template-starter/src/lib/components/Footer.svelte +78 -0
  116. package/template-starter/src/lib/components/Image.svelte +100 -0
  117. package/template-starter/src/lib/components/Image.test.ts +15 -0
  118. package/template-starter/src/lib/components/Navbar.svelte +141 -0
  119. package/template-starter/src/lib/components/Search.svelte +248 -0
  120. package/template-starter/src/lib/components/Sidebar.svelte +110 -0
  121. package/template-starter/src/lib/components/Tabs.svelte +48 -0
  122. package/template-starter/src/lib/components/Tabs.test.ts +17 -0
  123. package/template-starter/src/lib/index.ts +15 -0
  124. package/template-starter/src/routes/+layout.svelte +28 -0
  125. package/template-starter/src/routes/+page.svelte +92 -0
  126. package/template-starter/svelte.config.js +17 -0
  127. package/template-starter/tailwind.config.ts +17 -0
  128. package/template-starter/tsconfig.json +13 -0
  129. package/template-starter/vite.config.ts +6 -0
  130. package/tests/e2e/example.spec.ts +345 -0
  131. package/tsconfig.json +20 -0
  132. package/vite.config.ts +6 -0
  133. package/vitest.config.ts +34 -0
  134. package/vitest.setup.ts +21 -0
@@ -0,0 +1,272 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { mergeConfig, validateConfig, DEFAULT_CONFIG } from './config';
3
+ import { parseConfigFile } from './configParser';
4
+ import { createRouteConfig, validateRoute, buildNavLink, isDocsRoute } from './routing';
5
+ import { generateNavigationFromFiles } from './navigationBuilder';
6
+ import { generateCSSVariables, validateTheme, getThemeTemplate } from './themeCustomization';
7
+
8
+ describe('Configuration System Integration', () => {
9
+ describe('Full Config Workflow', () => {
10
+ it('should load and validate a complete configuration', () => {
11
+ const configContent = JSON.stringify({
12
+ name: 'My Documentation',
13
+ docsRoute: '/help',
14
+ theme: {
15
+ primary: '#0066cc',
16
+ secondary: '#ff6b6b',
17
+ },
18
+ navigation: {
19
+ title: 'Help Center',
20
+ autoGenerate: true,
21
+ },
22
+ });
23
+
24
+ const parseResult = parseConfigFile(configContent, 'config.json');
25
+ expect(parseResult.errors).toHaveLength(0);
26
+
27
+ const validation = validateConfig(parseResult.config);
28
+ expect(validation.valid).toBe(true);
29
+
30
+ const merged = mergeConfig(parseResult.config);
31
+ expect(merged.docsRoute).toBe('/help');
32
+ expect(merged.theme?.primary).toBe('#0066cc');
33
+ });
34
+
35
+ it('should handle config with custom route mount point', () => {
36
+ const config = mergeConfig({
37
+ docsRoute: '/guides',
38
+ docsFolderPath: './guides',
39
+ basePath: '/guides',
40
+ });
41
+
42
+ expect(config.docsRoute).toBe('/guides');
43
+ expect(config.basePath).toBe('/guides');
44
+
45
+ // Validate the route
46
+ expect(validateRoute(config.docsRoute!)).toBe(true);
47
+
48
+ // Create route config
49
+ const routeConfig = createRouteConfig(config);
50
+ expect(routeConfig.docsRoute).toBe('/guides');
51
+ });
52
+
53
+ it('should build correct navigation links with custom route', () => {
54
+ const config = mergeConfig({ docsRoute: '/help' });
55
+ const docsRoute = config.docsRoute || '/docs';
56
+
57
+ const link1 = buildNavLink('setup', docsRoute);
58
+ expect(link1).toBe('/help/setup');
59
+
60
+ const link2 = buildNavLink('api/overview', docsRoute);
61
+ expect(link2).toBe('/help/api/overview');
62
+
63
+ const link3 = buildNavLink('', docsRoute);
64
+ expect(link3).toBe('/help');
65
+ });
66
+
67
+ it('should verify routes with custom mount point', () => {
68
+ const config = mergeConfig({ docsRoute: '/documentation' });
69
+ const docsRoute = config.docsRoute || '/docs';
70
+
71
+ expect(isDocsRoute('/documentation', docsRoute)).toBe(true);
72
+ expect(isDocsRoute('/documentation/api', docsRoute)).toBe(true);
73
+ expect(isDocsRoute('/help', docsRoute)).toBe(false);
74
+ });
75
+ });
76
+
77
+ describe('Theme Configuration Integration', () => {
78
+ it('should apply theme configuration from config file', () => {
79
+ const themeConfig = getThemeTemplate('default');
80
+ const cssVars = generateCSSVariables(themeConfig);
81
+
82
+ expect(cssVars['--color-primary']).toBeDefined();
83
+ expect(cssVars['--color-secondary']).toBeDefined();
84
+ expect(cssVars['--font-family-body']).toBeDefined();
85
+ });
86
+
87
+ it('should validate custom theme config', () => {
88
+ const config = mergeConfig({
89
+ theme: {
90
+ primary: '#ff0000',
91
+ secondary: '#00ff00',
92
+ fontFamily: 'Arial, sans-serif',
93
+ },
94
+ });
95
+
96
+ const themeValidation = validateTheme(config.theme!);
97
+ expect(themeValidation.valid).toBe(true);
98
+ });
99
+
100
+ it('should override default theme with config', () => {
101
+ const config = mergeConfig({
102
+ theme: {
103
+ primary: '#custom-color',
104
+ },
105
+ });
106
+
107
+ expect(config.theme?.primary).toBe('#custom-color');
108
+ // Should still have default secondary
109
+ expect(config.theme?.secondary).toBe(DEFAULT_CONFIG.theme?.secondary);
110
+ });
111
+
112
+ it('should generate CSS for theme customization', () => {
113
+ const darkTheme = getThemeTemplate('dark');
114
+ const cssVars = generateCSSVariables(darkTheme);
115
+
116
+ const stylesheet = Object.entries(cssVars)
117
+ .map(([key, value]) => ` ${key}: ${value};`)
118
+ .join('\n');
119
+
120
+ expect(stylesheet).toContain('--color-primary');
121
+ expect(stylesheet).toContain('--font-family');
122
+ });
123
+ });
124
+
125
+ describe('Navigation Configuration Integration', () => {
126
+ it('should generate navigation from config', () => {
127
+ const files = [
128
+ { path: 'docs/index.md', name: 'index', title: 'Home', isIndex: true },
129
+ { path: 'docs/setup.md', name: 'setup', title: 'Setup', isIndex: false },
130
+ { path: 'docs/api/overview.md', name: 'overview', title: 'Overview', isIndex: false },
131
+ { path: 'docs/api/reference.md', name: 'reference', title: 'Reference', isIndex: false },
132
+ ];
133
+
134
+ const nav = generateNavigationFromFiles(files, '/docs');
135
+ expect(nav.length).toBeGreaterThan(0);
136
+
137
+ // Should have an API section
138
+ const apiSection = nav.find((s) => s.title.includes('api') || s.title.includes('API'));
139
+ if (apiSection) {
140
+ expect(apiSection.items.length).toBeGreaterThan(0);
141
+ }
142
+ });
143
+
144
+ it('should respect exclude patterns in navigation', () => {
145
+ const configContent = JSON.stringify({
146
+ navigation: {
147
+ excludePaths: ['draft', 'private'],
148
+ autoGenerate: true,
149
+ },
150
+ });
151
+
152
+ const parseResult = parseConfigFile(configContent, 'config.json');
153
+ expect(parseResult.errors).toHaveLength(0);
154
+ expect(parseResult.config.navigation?.excludePaths).toEqual(['draft', 'private']);
155
+ });
156
+ });
157
+
158
+ describe('Multi-Route Configuration', () => {
159
+ it('should support switching between different doc routes', () => {
160
+ // Project 1: /docs route
161
+ const config1 = mergeConfig({ docsRoute: '/docs' });
162
+ expect(config1.docsRoute).toBe('/docs');
163
+ expect(isDocsRoute('/docs/setup', config1.docsRoute!)).toBe(true);
164
+ expect(isDocsRoute('/help/setup', config1.docsRoute!)).toBe(false);
165
+
166
+ // Project 2: /help route
167
+ const config2 = mergeConfig({ docsRoute: '/help' });
168
+ expect(config2.docsRoute).toBe('/help');
169
+ expect(isDocsRoute('/help/setup', config2.docsRoute!)).toBe(true);
170
+ expect(isDocsRoute('/docs/setup', config2.docsRoute!)).toBe(false);
171
+
172
+ // Project 3: /guides route
173
+ const config3 = mergeConfig({ docsRoute: '/guides' });
174
+ expect(config3.docsRoute).toBe('/guides');
175
+ expect(isDocsRoute('/guides/tutorial', config3.docsRoute!)).toBe(true);
176
+ });
177
+ });
178
+
179
+ describe('End-to-End Configuration Scenario', () => {
180
+ it('should handle a complete doc system setup', () => {
181
+ // Simulate loading a config file
182
+ const configJSON = JSON.stringify({
183
+ name: 'API Documentation',
184
+ docsRoute: '/api-docs',
185
+ docsFolderPath: './docs/api',
186
+ enableSearch: true,
187
+ layout: 'full-width',
188
+ theme: {
189
+ primary: '#2563eb',
190
+ secondary: '#dc2626',
191
+ fontFamily: 'Inter, sans-serif',
192
+ headingFont: 'Poppins, sans-serif',
193
+ },
194
+ navigation: {
195
+ title: 'API Reference',
196
+ autoGenerate: true,
197
+ excludePaths: ['examples', 'draft'],
198
+ },
199
+ });
200
+
201
+ // Parse the config
202
+ const parseResult = parseConfigFile(configJSON, 'config.json');
203
+ expect(parseResult.errors).toHaveLength(0);
204
+
205
+ // Validate it
206
+ const validation = validateConfig(parseResult.config);
207
+ expect(validation.valid).toBe(true);
208
+
209
+ // Merge with defaults
210
+ const finalConfig = mergeConfig(parseResult.config);
211
+
212
+ // Verify all parts work together
213
+ expect(finalConfig.name).toBe('API Documentation');
214
+ expect(finalConfig.docsRoute).toBe('/api-docs');
215
+ expect(finalConfig.enableSearch).toBe(true);
216
+ expect(finalConfig.layout).toBe('full-width');
217
+
218
+ // Verify theme
219
+ const themeValidation = validateTheme(finalConfig.theme!);
220
+ expect(themeValidation.valid).toBe(true);
221
+
222
+ const cssVars = generateCSSVariables(finalConfig.theme!);
223
+ expect(cssVars['--color-primary']).toBe('#2563eb');
224
+ expect(cssVars['--font-family-body']).toBe('Inter, sans-serif');
225
+
226
+ // Verify routing
227
+ const routeConfig = createRouteConfig(finalConfig);
228
+ expect(validateRoute(routeConfig.docsRoute)).toBe(true);
229
+
230
+ // Verify navigation config
231
+ expect(finalConfig.navigation?.title).toBe('API Reference');
232
+ expect(finalConfig.navigation?.excludePaths).toEqual(['examples', 'draft']);
233
+
234
+ // Verify navigation with custom route
235
+ const testLink = buildNavLink('endpoints/users', routeConfig.docsRoute);
236
+ expect(testLink).toBe('/api-docs/endpoints/users');
237
+
238
+ // Verify route detection
239
+ expect(isDocsRoute('/api-docs', routeConfig.docsRoute)).toBe(true);
240
+ expect(isDocsRoute('/api-docs/endpoints', routeConfig.docsRoute)).toBe(true);
241
+ expect(isDocsRoute('/docs', routeConfig.docsRoute)).toBe(false);
242
+ });
243
+ });
244
+
245
+ describe('Configuration Validation Scenarios', () => {
246
+ it('should reject invalid configurations', () => {
247
+ const invalidConfigs = [
248
+ { docsRoute: 123 }, // Not a string
249
+ { layout: 'invalid-layout' },
250
+ { defaultTheme: 'blue' }, // Invalid theme value
251
+ { theme: 'not-an-object' }, // Theme must be object
252
+ ];
253
+
254
+ invalidConfigs.forEach((config) => {
255
+ const validation = validateConfig(config);
256
+ expect(validation.valid).toBe(false);
257
+ expect(validation.errors.length).toBeGreaterThan(0);
258
+ });
259
+ });
260
+
261
+ it('should provide helpful error messages', () => {
262
+ const invalidConfig = {
263
+ layout: 'unknown',
264
+ defaultTheme: 'invalid',
265
+ };
266
+
267
+ const validation = validateConfig(invalidConfig);
268
+ expect(validation.errors.length).toBeGreaterThan(0);
269
+ expect(validation.errors.some((e) => e.includes('layout') || e.includes('defaultTheme'))).toBe(true);
270
+ });
271
+ });
272
+ });
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Configuration loader and manager
3
+ * Loads, validates, and manages the docs system configuration
4
+ */
5
+
6
+ import type { ConfigOptions } from './config';
7
+ import { DEFAULT_CONFIG, mergeConfig, validateConfig } from './config';
8
+ import { parseConfigFile, loadConfigFile, parseFrontmatter } from './configParser';
9
+ import { createRouteConfig, validateRoute } from './routing';
10
+ import { validateTheme } from './themeCustomization';
11
+ import { validateNavigation } from './navigationBuilder';
12
+
13
+ export interface ConfigLoadResult {
14
+ success: boolean;
15
+ config: ConfigOptions;
16
+ errors: string[];
17
+ warnings: string[];
18
+ source: 'default' | 'loaded' | 'merged';
19
+ }
20
+
21
+ /**
22
+ * Load configuration from a file
23
+ */
24
+ export async function loadConfiguration(
25
+ configPath: string = '/docs/_config.json'
26
+ ): Promise<ConfigLoadResult> {
27
+ const errors: string[] = [];
28
+ const warnings: string[] = [];
29
+ let userConfig: ConfigOptions = {};
30
+
31
+ // Try to load the config file
32
+ const result = await loadConfigFile(configPath);
33
+
34
+ if (result.errors.length > 0 && !configPath.includes('_config')) {
35
+ // If it's not a standard config file, it might not exist - that's ok
36
+ errors.push(...result.errors);
37
+ } else if (result.errors.length > 0) {
38
+ // For standard config file, log errors but continue with defaults
39
+ warnings.push(...result.errors);
40
+ } else {
41
+ userConfig = result.config;
42
+ }
43
+
44
+ // Validate the loaded config
45
+ const validation = validateConfig(userConfig);
46
+ if (!validation.valid) {
47
+ warnings.push(...validation.errors);
48
+ }
49
+
50
+ // Validate sub-configs
51
+ if (userConfig.theme) {
52
+ const themeValidation = validateTheme(userConfig.theme);
53
+ if (!themeValidation.valid) {
54
+ warnings.push(`Theme validation errors: ${themeValidation.errors.join(', ')}`);
55
+ }
56
+ }
57
+
58
+ if (userConfig.navigation) {
59
+ const navValidation = validateNavigation(userConfig.navigation);
60
+ if (!navValidation.valid) {
61
+ warnings.push(`Navigation validation errors: ${navValidation.errors.join(', ')}`);
62
+ }
63
+ }
64
+
65
+ // Validate route
66
+ if (userConfig.docsRoute && !validateRoute(userConfig.docsRoute)) {
67
+ errors.push(`Invalid docsRoute: ${userConfig.docsRoute}`);
68
+ }
69
+
70
+ // Merge with defaults
71
+ const finalConfig = mergeConfig(userConfig, DEFAULT_CONFIG);
72
+
73
+ // Validate final config
74
+ const finalValidation = validateConfig(finalConfig);
75
+ if (!finalValidation.valid) {
76
+ errors.push(...finalValidation.errors);
77
+ }
78
+
79
+ const success = errors.length === 0;
80
+
81
+ return {
82
+ success,
83
+ config: finalConfig,
84
+ errors,
85
+ warnings,
86
+ source: success ? 'loaded' : warnings.length > 0 ? 'merged' : 'default',
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Load and cache configuration (for browser environment)
92
+ */
93
+ let cachedConfig: ConfigOptions | null = null;
94
+ let configLoadPromise: Promise<ConfigLoadResult> | null = null;
95
+
96
+ export async function getConfiguration(configPath: string = '/docs/_config.json'): Promise<ConfigOptions> {
97
+ if (cachedConfig) {
98
+ return cachedConfig;
99
+ }
100
+
101
+ if (!configLoadPromise) {
102
+ configLoadPromise = loadConfiguration(configPath);
103
+ }
104
+
105
+ const result = await configLoadPromise;
106
+ cachedConfig = result.config;
107
+ return result.config;
108
+ }
109
+
110
+ /**
111
+ * Clear configuration cache (useful for testing or reloading)
112
+ */
113
+ export function clearConfigCache(): void {
114
+ cachedConfig = null;
115
+ configLoadPromise = null;
116
+ }
117
+
118
+ /**
119
+ * Parse frontmatter from markdown content and merge with config
120
+ */
121
+ export function parseMarkdownFrontmatter(content: string): {
122
+ frontmatter: Record<string, unknown>;
123
+ body: string;
124
+ errors: string[];
125
+ } {
126
+ const result = parseFrontmatter(content);
127
+
128
+ if (result.error) {
129
+ return {
130
+ frontmatter: {},
131
+ body: result.body,
132
+ errors: [result.error],
133
+ };
134
+ }
135
+
136
+ return {
137
+ frontmatter: result.frontmatter || {},
138
+ body: result.body,
139
+ errors: [],
140
+ };
141
+ }
142
+
143
+ /**
144
+ * Validate a custom config object
145
+ */
146
+ export function validateCustomConfig(config: unknown): { valid: boolean; errors: string[] } {
147
+ const validation = validateConfig(config);
148
+
149
+ if (!validation.valid) {
150
+ return validation;
151
+ }
152
+
153
+ const cfg = config as ConfigOptions;
154
+ const errors: string[] = [];
155
+
156
+ // Additional validations
157
+ if (cfg.theme) {
158
+ const themeValidation = validateTheme(cfg.theme);
159
+ if (!themeValidation.valid) {
160
+ errors.push(`Theme: ${themeValidation.errors.join(', ')}`);
161
+ }
162
+ }
163
+
164
+ if (cfg.navigation) {
165
+ const navValidation = validateNavigation(cfg.navigation);
166
+ if (!navValidation.valid) {
167
+ errors.push(`Navigation: ${navValidation.errors.join(', ')}`);
168
+ }
169
+ }
170
+
171
+ if (cfg.docsRoute && !validateRoute(cfg.docsRoute)) {
172
+ errors.push(`Invalid docsRoute: ${cfg.docsRoute}`);
173
+ }
174
+
175
+ return { valid: errors.length === 0, errors };
176
+ }
177
+
178
+ /**
179
+ * Create a configuration from minimal user input
180
+ */
181
+ export function createMinimalConfig(options: Partial<ConfigOptions> = {}): ConfigOptions {
182
+ return mergeConfig(options, DEFAULT_CONFIG);
183
+ }
184
+
185
+ /**
186
+ * Export configuration as JSON
187
+ */
188
+ export function exportConfigAsJSON(config: ConfigOptions): string {
189
+ return JSON.stringify(config, null, 2);
190
+ }
191
+
192
+ /**
193
+ * Export configuration as YAML
194
+ */
195
+ export function exportConfigAsYAML(config: ConfigOptions): string {
196
+ const lines: string[] = [];
197
+
198
+ function addValue(key: string, value: unknown, indent: number = 0): void {
199
+ const indentStr = ' '.repeat(indent);
200
+
201
+ if (value === null || value === undefined) {
202
+ return;
203
+ }
204
+
205
+ if (typeof value === 'string') {
206
+ lines.push(`${indentStr}${key}: ${value}`);
207
+ } else if (typeof value === 'boolean' || typeof value === 'number') {
208
+ lines.push(`${indentStr}${key}: ${value}`);
209
+ } else if (Array.isArray(value)) {
210
+ lines.push(`${indentStr}${key}:`);
211
+ value.forEach((item, idx) => {
212
+ if (typeof item === 'object') {
213
+ addValue(`- `, item, indent + 1);
214
+ } else {
215
+ lines.push(`${indentStr} - ${item}`);
216
+ }
217
+ });
218
+ } else if (typeof value === 'object') {
219
+ lines.push(`${indentStr}${key}:`);
220
+ Object.entries(value).forEach(([k, v]) => {
221
+ addValue(k, v, indent + 1);
222
+ });
223
+ }
224
+ }
225
+
226
+ Object.entries(config).forEach(([key, value]) => {
227
+ addValue(key, value);
228
+ });
229
+
230
+ return lines.join('\n');
231
+ }