@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,217 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parseConfigFile, parseFrontmatter } from './configParser';
3
+
4
+ describe('Config Parser - JSON', () => {
5
+ it('should parse valid JSON config', () => {
6
+ const content = JSON.stringify({
7
+ name: 'My Docs',
8
+ docsRoute: '/docs',
9
+ });
10
+
11
+ const result = parseConfigFile(content, 'config.json');
12
+ expect(result.source).toBe('json');
13
+ expect(result.config.name).toBe('My Docs');
14
+ expect(result.config.docsRoute).toBe('/docs');
15
+ });
16
+
17
+ it('should detect JSON by file extension', () => {
18
+ const content = '{ "name": "test" }';
19
+ const result = parseConfigFile(content, 'config.json');
20
+ expect(result.source).toBe('json');
21
+ });
22
+
23
+ it('should handle invalid JSON gracefully', () => {
24
+ const content = '{ invalid json }';
25
+ const result = parseConfigFile(content, 'config.json');
26
+ expect(result.errors.length).toBeGreaterThan(0);
27
+ expect(result.errors[0]).toContain('Invalid JSON');
28
+ });
29
+
30
+ it('should handle empty JSON object', () => {
31
+ const content = '{}';
32
+ const result = parseConfigFile(content, 'config.json');
33
+ expect(result.source).toBe('json');
34
+ expect(result.config).toEqual({});
35
+ });
36
+ });
37
+
38
+ describe('Config Parser - YAML', () => {
39
+ it('should parse valid YAML config', () => {
40
+ const content = `
41
+ name: My Docs
42
+ docsRoute: /docs
43
+ enableSearch: true
44
+ `.trim();
45
+
46
+ const result = parseConfigFile(content, 'config.yaml');
47
+ expect(result.source).toBe('yaml');
48
+ expect(result.config.name).toBe('My Docs');
49
+ expect(result.config.docsRoute).toBe('/docs');
50
+ expect(result.config.enableSearch).toBe(true);
51
+ });
52
+
53
+ it('should detect YAML by file extension', () => {
54
+ const content = 'name: test';
55
+ const result = parseConfigFile(content, 'config.yml');
56
+ expect(result.source).toBe('yaml');
57
+ });
58
+
59
+ it('should parse YAML with nested objects', () => {
60
+ const content = `
61
+ name: My Docs
62
+ theme:
63
+ primary: "#ff0000"
64
+ secondary: "#00ff00"
65
+ `.trim();
66
+
67
+ const result = parseConfigFile(content, 'config.yaml');
68
+ expect(result.config.theme?.primary).toBe('#ff0000');
69
+ expect(result.config.theme?.secondary).toBe('#00ff00');
70
+ });
71
+
72
+ it('should parse YAML boolean values', () => {
73
+ const content = `
74
+ enableSearch: true
75
+ darkMode: false
76
+ breadcrumbs: true
77
+ `.trim();
78
+
79
+ const result = parseConfigFile(content, 'config.yaml');
80
+ expect(result.config.enableSearch).toBe(true);
81
+ expect(result.config.darkMode).toBe(false);
82
+ expect(result.config.breadcrumbs).toBe(true);
83
+ });
84
+
85
+ it('should parse YAML with comments', () => {
86
+ const content = `
87
+ name: My Docs
88
+ docsRoute: /docs
89
+ `.trim();
90
+
91
+ const result = parseConfigFile(content, 'config.yaml');
92
+ expect(result.config.name).toBe('My Docs');
93
+ expect(result.config.docsRoute).toBe('/docs');
94
+ });
95
+ });
96
+
97
+ describe('Frontmatter Parser', () => {
98
+ it('should parse JSON frontmatter', () => {
99
+ const content = `---
100
+ {
101
+ "title": "My Page",
102
+ "author": "John Doe"
103
+ }
104
+ ---
105
+ # Page Content
106
+
107
+ This is the body.`;
108
+
109
+ const result = parseFrontmatter(content);
110
+ expect(result.frontmatter?.title).toBe('My Page');
111
+ expect(result.frontmatter?.author).toBe('John Doe');
112
+ expect(result.body).toContain('# Page Content');
113
+ });
114
+
115
+ it('should parse YAML frontmatter', () => {
116
+ const content = `---
117
+ title: My Page
118
+ author: John Doe
119
+ date: 2024-01-01
120
+ ---
121
+ # Page Content
122
+
123
+ This is the body.`;
124
+
125
+ const result = parseFrontmatter(content);
126
+ expect(result.frontmatter?.title).toBe('My Page');
127
+ expect(result.frontmatter?.author).toBe('John Doe');
128
+ expect(result.body).toContain('# Page Content');
129
+ });
130
+
131
+ it('should handle missing frontmatter', () => {
132
+ const content = `# Page Content
133
+
134
+ This is the body.`;
135
+
136
+ const result = parseFrontmatter(content);
137
+ expect(result.frontmatter).toBeNull();
138
+ expect(result.body).toBe(content);
139
+ });
140
+
141
+ it('should extract body correctly', () => {
142
+ const content = `---
143
+ title: Test
144
+ ---
145
+ # Heading
146
+
147
+ Content here.`;
148
+
149
+ const result = parseFrontmatter(content);
150
+ expect(result.body).toBe('# Heading\n\nContent here.');
151
+ });
152
+
153
+ it('should handle empty content after frontmatter', () => {
154
+ const content = `---
155
+ title: Test
156
+ ---
157
+ Content`;
158
+
159
+ const result = parseFrontmatter(content);
160
+ expect(result.body).toBe('Content');
161
+ expect(result.frontmatter?.title).toBe('Test');
162
+ });
163
+
164
+ it('should handle invalid frontmatter gracefully', () => {
165
+ const content = `---
166
+ title: Test
167
+ author: John
168
+ ---
169
+ Content`;
170
+
171
+ const result = parseFrontmatter(content);
172
+ // Should parse YAML successfully if it's valid YAML
173
+ expect(result.frontmatter?.title).toBe('Test');
174
+ expect(result.body).toBe('Content');
175
+ });
176
+
177
+ it('should preserve multi-line content in body', () => {
178
+ const content = `---
179
+ title: Test
180
+ ---
181
+ # Heading
182
+
183
+ - Item 1
184
+ - Item 2
185
+
186
+ Paragraph 1
187
+
188
+ Paragraph 2`;
189
+
190
+ const result = parseFrontmatter(content);
191
+ expect(result.body).toContain('- Item 1');
192
+ expect(result.body).toContain('Paragraph 2');
193
+ });
194
+ });
195
+
196
+ describe('File Format Detection', () => {
197
+ it('should detect JSON by content starting with {', () => {
198
+ const result = parseConfigFile('{ "test": true }', 'unknown');
199
+ expect(result.source).toBe('json');
200
+ });
201
+
202
+ it('should detect JSON by content starting with [', () => {
203
+ const result = parseConfigFile('[]', 'unknown');
204
+ expect(result.source).toBe('json');
205
+ });
206
+
207
+ it('should default to YAML for unknown format', () => {
208
+ const result = parseConfigFile('name: test', 'unknown');
209
+ expect(result.source).toBe('yaml');
210
+ });
211
+
212
+ it('should prefer file extension over content detection', () => {
213
+ const jsonContent = '{ "valid": "json" }';
214
+ const result = parseConfigFile(jsonContent, 'config.json');
215
+ expect(result.source).toBe('json');
216
+ });
217
+ });
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Config file parser supporting JSON and YAML formats
3
+ */
4
+
5
+ import type { ConfigOptions } from './config';
6
+ import { validateConfig } from './config';
7
+
8
+ export interface ParseResult {
9
+ config: ConfigOptions;
10
+ errors: string[];
11
+ source: 'json' | 'yaml' | 'unknown';
12
+ }
13
+
14
+ /**
15
+ * Parse JSON config file
16
+ */
17
+ function parseJSON(content: string): { data: unknown; error?: string } {
18
+ try {
19
+ return { data: JSON.parse(content) };
20
+ } catch (err) {
21
+ return {
22
+ data: null,
23
+ error: `Invalid JSON: ${err instanceof Error ? err.message : 'Unknown error'}`,
24
+ };
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Parse YAML config file (simplified parser)
30
+ * Handles basic YAML key-value pairs and nested objects
31
+ */
32
+ function parseYAML(content: string): { data: unknown; error?: string } {
33
+ try {
34
+ const result: Record<string, unknown> = {};
35
+ const lines = content.split('\n');
36
+ let currentObject: Record<string, unknown> | null = null;
37
+ let currentKey: string | null = null;
38
+ let indentStack: Array<{ indent: number; key: string; obj: Record<string, unknown> }> = [];
39
+
40
+ for (let i = 0; i < lines.length; i++) {
41
+ const line = lines[i];
42
+
43
+ // Skip empty lines and comments
44
+ if (!line.trim() || line.trim().startsWith('#')) {
45
+ continue;
46
+ }
47
+
48
+ // Calculate indentation
49
+ const indent = line.length - line.trimLeft().length;
50
+ const trimmedLine = line.trim();
51
+
52
+ // Pop stack if we've decreased indentation
53
+ while (indentStack.length > 0 && indentStack[indentStack.length - 1].indent >= indent) {
54
+ indentStack.pop();
55
+ }
56
+
57
+ // Parse key: value
58
+ if (trimmedLine.includes(':')) {
59
+ const [key, ...valueParts] = trimmedLine.split(':');
60
+ const trimmedKey = key.trim();
61
+ const value = valueParts.join(':').trim();
62
+
63
+ // Determine parent object
64
+ const parent = indentStack.length > 0 ? indentStack[indentStack.length - 1].obj : result;
65
+
66
+ // Parse value
67
+ let parsedValue: unknown = value;
68
+
69
+ if (value === '') {
70
+ // Empty value - next items might be children
71
+ parsedValue = {};
72
+ indentStack.push({ indent, key: trimmedKey, obj: parsedValue as Record<string, unknown> });
73
+ } else if (value === 'true') {
74
+ parsedValue = true;
75
+ } else if (value === 'false') {
76
+ parsedValue = false;
77
+ } else if (/^\d+$/.test(value)) {
78
+ parsedValue = parseInt(value, 10);
79
+ } else if (/^\d+\.\d+$/.test(value)) {
80
+ parsedValue = parseFloat(value);
81
+ } else if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
82
+ parsedValue = value.slice(1, -1);
83
+ } else if (value.startsWith('[') && value.endsWith(']')) {
84
+ try {
85
+ parsedValue = JSON.parse(value);
86
+ } catch {
87
+ parsedValue = value;
88
+ }
89
+ } else if (value.startsWith('{') && value.endsWith('}')) {
90
+ try {
91
+ parsedValue = JSON.parse(value);
92
+ } catch {
93
+ parsedValue = value;
94
+ }
95
+ }
96
+
97
+ parent[trimmedKey] = parsedValue;
98
+ currentKey = trimmedKey;
99
+ }
100
+ }
101
+
102
+ return { data: result };
103
+ } catch (err) {
104
+ return {
105
+ data: null,
106
+ error: `Invalid YAML: ${err instanceof Error ? err.message : 'Unknown error'}`,
107
+ };
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Parse frontmatter from markdown content
113
+ * Supports both JSON and YAML frontmatter
114
+ */
115
+ export function parseFrontmatter(content: string): {
116
+ frontmatter: Record<string, unknown> | null;
117
+ body: string;
118
+ error?: string;
119
+ } {
120
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
121
+
122
+ if (!frontmatterMatch) {
123
+ return { frontmatter: null, body: content };
124
+ }
125
+
126
+ const frontmatterContent = frontmatterMatch[1];
127
+ const body = frontmatterMatch[2];
128
+
129
+ // Try JSON first
130
+ let parsed = parseJSON(frontmatterContent);
131
+
132
+ // If JSON fails, try YAML
133
+ if (parsed.error) {
134
+ parsed = parseYAML(frontmatterContent);
135
+ }
136
+
137
+ if (parsed.error) {
138
+ return {
139
+ frontmatter: null,
140
+ body: content,
141
+ error: `Failed to parse frontmatter: ${parsed.error}`,
142
+ };
143
+ }
144
+
145
+ return {
146
+ frontmatter: parsed.data as Record<string, unknown>,
147
+ body,
148
+ };
149
+ }
150
+
151
+ /**
152
+ * Detect file format from content or filename
153
+ */
154
+ function detectFormat(content: string, filename: string): 'json' | 'yaml' {
155
+ // Check filename extension
156
+ if (filename.endsWith('.json')) return 'json';
157
+ if (filename.endsWith('.yaml') || filename.endsWith('.yml')) return 'yaml';
158
+
159
+ // Try to detect by content
160
+ const trimmed = content.trim();
161
+ if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
162
+ return 'json';
163
+ }
164
+
165
+ return 'yaml';
166
+ }
167
+
168
+ /**
169
+ * Parse config file content (JSON or YAML)
170
+ */
171
+ export function parseConfigFile(content: string, filename: string = 'config.json'): ParseResult {
172
+ const format = detectFormat(content, filename);
173
+ let parsed;
174
+
175
+ if (format === 'json') {
176
+ parsed = parseJSON(content);
177
+ } else {
178
+ parsed = parseYAML(content);
179
+ }
180
+
181
+ if (parsed.error) {
182
+ return {
183
+ config: {},
184
+ errors: [parsed.error],
185
+ source: format,
186
+ };
187
+ }
188
+
189
+ const validation = validateConfig(parsed.data);
190
+
191
+ return {
192
+ config: (parsed.data as ConfigOptions) || {},
193
+ errors: validation.errors,
194
+ source: format,
195
+ };
196
+ }
197
+
198
+ /**
199
+ * Load and parse a config file from a file path (browser-safe)
200
+ * In a browser environment, use fetch; in Node, use fs
201
+ */
202
+ export async function loadConfigFile(filePath: string): Promise<ParseResult> {
203
+ try {
204
+ let content: string;
205
+
206
+ if (typeof window !== 'undefined') {
207
+ // Browser environment - use fetch
208
+ const response = await fetch(filePath);
209
+ if (!response.ok) {
210
+ return {
211
+ config: {},
212
+ errors: [`Failed to load config file: ${response.statusText}`],
213
+ source: 'unknown',
214
+ };
215
+ }
216
+ content = await response.text();
217
+ } else {
218
+ // Node environment - would need fs module
219
+ return {
220
+ config: {},
221
+ errors: ['Cannot load config file in this environment'],
222
+ source: 'unknown',
223
+ };
224
+ }
225
+
226
+ return parseConfigFile(content, filePath);
227
+ } catch (err) {
228
+ return {
229
+ config: {},
230
+ errors: [`Error loading config file: ${err instanceof Error ? err.message : 'Unknown error'}`],
231
+ source: 'unknown',
232
+ };
233
+ }
234
+ }
@@ -0,0 +1,34 @@
1
+ // Re-export all doc components
2
+ export { default as Callout } from './components/Callout.svelte';
3
+ export { default as Tabs } from './components/Tabs.svelte';
4
+ export { default as CodeBlock } from './components/CodeBlock.svelte';
5
+ export { default as Image } from './components/Image.svelte';
6
+ export { default as APITable } from './components/APITable.svelte';
7
+ export { default as Breadcrumbs } from './components/Breadcrumbs.svelte';
8
+ export { default as Search } from './components/Search.svelte';
9
+
10
+ // Re-export all theme components
11
+ export { default as DocLayout } from './components/DocLayout.svelte';
12
+ export { default as Navbar } from './components/Navbar.svelte';
13
+ export { default as Sidebar } from './components/Sidebar.svelte';
14
+ export { default as Footer } from './components/Footer.svelte';
15
+
16
+ // Re-export stores
17
+ export { theme } from './stores/theme';
18
+ export { nav, sidebarOpen } from './stores/nav';
19
+ export { version } from './stores/version';
20
+ export { i18n } from './stores/i18n';
21
+ export { query, results, loading, resultCount, initPagefind, updateSearch, clearSearch, performSearch, subscribeToSearch } from './stores/search';
22
+ export type { NavItem, NavConfig, NavSection } from './stores/nav';
23
+ export type { VersionMetadata, VersionConfig } from './stores/version';
24
+ export type { Language, LanguageMetadata, I18nConfig } from './stores/i18n';
25
+ export type { SearchResult } from './stores/search';
26
+
27
+ // Re-export utilities
28
+ export {
29
+ renderMarkdown,
30
+ extractMetadata,
31
+ getExcerpt,
32
+ type MarkdownMetadata,
33
+ type MarkdownResult
34
+ } from './utils';