meno-core 1.0.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.
Files changed (231) hide show
  1. package/bin/cli.ts +281 -0
  2. package/build-static.ts +298 -0
  3. package/bunfig.toml +39 -0
  4. package/entries/client-router.tsx +111 -0
  5. package/entries/server-router.tsx +71 -0
  6. package/lib/client/ClientInitializer.test.ts +9 -0
  7. package/lib/client/ClientInitializer.test.ts.skip +92 -0
  8. package/lib/client/ClientInitializer.ts +60 -0
  9. package/lib/client/ErrorBoundary.test.tsx +595 -0
  10. package/lib/client/ErrorBoundary.tsx +230 -0
  11. package/lib/client/componentRegistry.test.ts +165 -0
  12. package/lib/client/componentRegistry.ts +18 -0
  13. package/lib/client/contexts/ThemeContext.tsx +73 -0
  14. package/lib/client/core/ComponentBuilder.test.ts +677 -0
  15. package/lib/client/core/ComponentBuilder.ts +660 -0
  16. package/lib/client/core/ComponentRenderer.test.tsx +176 -0
  17. package/lib/client/core/ComponentRenderer.tsx +83 -0
  18. package/lib/client/core/cmsTemplateProcessor.ts +129 -0
  19. package/lib/client/elementRegistry.ts +81 -0
  20. package/lib/client/hmr/HMRManager.tsx +179 -0
  21. package/lib/client/hmr/index.ts +5 -0
  22. package/lib/client/hmrWebSocket.test.ts +9 -0
  23. package/lib/client/hmrWebSocket.ts +250 -0
  24. package/lib/client/hooks/useColorVariables.test.ts +166 -0
  25. package/lib/client/hooks/useColorVariables.ts +249 -0
  26. package/lib/client/hooks/usePropertyAutocomplete.test.ts +9 -0
  27. package/lib/client/hooks/usePropertyAutocomplete.ts +40 -0
  28. package/lib/client/hydration/HydrationUtils.test.ts +154 -0
  29. package/lib/client/hydration/HydrationUtils.ts +35 -0
  30. package/lib/client/i18nConfigService.test.ts +74 -0
  31. package/lib/client/i18nConfigService.ts +78 -0
  32. package/lib/client/index.ts +56 -0
  33. package/lib/client/navigation.test.ts +441 -0
  34. package/lib/client/navigation.ts +23 -0
  35. package/lib/client/responsiveStyleResolver.test.ts +491 -0
  36. package/lib/client/responsiveStyleResolver.ts +184 -0
  37. package/lib/client/routing/RouteLoader.test.ts +635 -0
  38. package/lib/client/routing/RouteLoader.ts +347 -0
  39. package/lib/client/routing/Router.tsx +382 -0
  40. package/lib/client/scripts/ScriptExecutor.test.ts +489 -0
  41. package/lib/client/scripts/ScriptExecutor.ts +171 -0
  42. package/lib/client/scripts/formHandler.ts +103 -0
  43. package/lib/client/styleProcessor.test.ts +126 -0
  44. package/lib/client/styleProcessor.ts +92 -0
  45. package/lib/client/styles/StyleInjector.test.ts +354 -0
  46. package/lib/client/styles/StyleInjector.ts +154 -0
  47. package/lib/client/templateEngine.test.ts +660 -0
  48. package/lib/client/templateEngine.ts +667 -0
  49. package/lib/client/theme.test.ts +173 -0
  50. package/lib/client/theme.ts +159 -0
  51. package/lib/client/utils/toast.ts +46 -0
  52. package/lib/server/createServer.ts +170 -0
  53. package/lib/server/cssGenerator.test.ts +172 -0
  54. package/lib/server/cssGenerator.ts +58 -0
  55. package/lib/server/fileWatcher.ts +134 -0
  56. package/lib/server/index.ts +55 -0
  57. package/lib/server/jsonLoader.test.ts +103 -0
  58. package/lib/server/jsonLoader.ts +350 -0
  59. package/lib/server/middleware/cors.test.ts +177 -0
  60. package/lib/server/middleware/cors.ts +69 -0
  61. package/lib/server/middleware/errorHandler.test.ts +208 -0
  62. package/lib/server/middleware/errorHandler.ts +63 -0
  63. package/lib/server/middleware/index.ts +9 -0
  64. package/lib/server/middleware/logger.test.ts +233 -0
  65. package/lib/server/middleware/logger.ts +99 -0
  66. package/lib/server/pageCache.test.ts +167 -0
  67. package/lib/server/pageCache.ts +97 -0
  68. package/lib/server/projectContext.ts +51 -0
  69. package/lib/server/providers/fileSystemCMSProvider.test.ts +292 -0
  70. package/lib/server/providers/fileSystemCMSProvider.ts +227 -0
  71. package/lib/server/providers/fileSystemPageProvider.ts +83 -0
  72. package/lib/server/routes/api/cms.test.ts +177 -0
  73. package/lib/server/routes/api/cms.ts +82 -0
  74. package/lib/server/routes/api/colors.ts +59 -0
  75. package/lib/server/routes/api/components.ts +70 -0
  76. package/lib/server/routes/api/config.test.ts +9 -0
  77. package/lib/server/routes/api/config.ts +28 -0
  78. package/lib/server/routes/api/core-routes.ts +182 -0
  79. package/lib/server/routes/api/functions.ts +170 -0
  80. package/lib/server/routes/api/index.ts +69 -0
  81. package/lib/server/routes/api/pages.ts +95 -0
  82. package/lib/server/routes/api/shared.test.ts +81 -0
  83. package/lib/server/routes/api/shared.ts +31 -0
  84. package/lib/server/routes/editor.test.ts +9 -0
  85. package/lib/server/routes/index.ts +104 -0
  86. package/lib/server/routes/pages.ts +161 -0
  87. package/lib/server/routes/static.ts +107 -0
  88. package/lib/server/services/ColorService.ts +193 -0
  89. package/lib/server/services/cmsService.test.ts +388 -0
  90. package/lib/server/services/cmsService.ts +296 -0
  91. package/lib/server/services/componentService.test.ts +276 -0
  92. package/lib/server/services/componentService.ts +346 -0
  93. package/lib/server/services/configService.ts +156 -0
  94. package/lib/server/services/fileWatcherService.ts +67 -0
  95. package/lib/server/services/index.ts +10 -0
  96. package/lib/server/services/pageService.test.ts +258 -0
  97. package/lib/server/services/pageService.ts +240 -0
  98. package/lib/server/ssrRenderer.test.ts +1005 -0
  99. package/lib/server/ssrRenderer.ts +878 -0
  100. package/lib/server/utilityClassGenerator.ts +11 -0
  101. package/lib/server/utils/index.ts +5 -0
  102. package/lib/server/utils/jsonLineMapper.test.ts +100 -0
  103. package/lib/server/utils/jsonLineMapper.ts +166 -0
  104. package/lib/server/validateStyleCoverage.test.ts +9 -0
  105. package/lib/server/validateStyleCoverage.ts +167 -0
  106. package/lib/server/websocketManager.test.ts +9 -0
  107. package/lib/server/websocketManager.ts +95 -0
  108. package/lib/shared/attributeNodeUtils.test.ts +152 -0
  109. package/lib/shared/attributeNodeUtils.ts +50 -0
  110. package/lib/shared/breakpoints.test.ts +166 -0
  111. package/lib/shared/breakpoints.ts +65 -0
  112. package/lib/shared/colorProperties.test.ts +111 -0
  113. package/lib/shared/colorProperties.ts +40 -0
  114. package/lib/shared/colorVariableUtils.test.ts +319 -0
  115. package/lib/shared/colorVariableUtils.ts +97 -0
  116. package/lib/shared/constants.test.ts +175 -0
  117. package/lib/shared/constants.ts +116 -0
  118. package/lib/shared/cssGeneration.ts +481 -0
  119. package/lib/shared/cssProperties.test.ts +252 -0
  120. package/lib/shared/cssProperties.ts +338 -0
  121. package/lib/shared/elementUtils.test.ts +245 -0
  122. package/lib/shared/elementUtils.ts +90 -0
  123. package/lib/shared/fontLoader.ts +97 -0
  124. package/lib/shared/i18n.test.ts +313 -0
  125. package/lib/shared/i18n.ts +286 -0
  126. package/lib/shared/index.ts +50 -0
  127. package/lib/shared/interfaces/contentProvider.test.ts +9 -0
  128. package/lib/shared/interfaces/contentProvider.ts +121 -0
  129. package/lib/shared/nodeUtils.test.ts +320 -0
  130. package/lib/shared/nodeUtils.ts +220 -0
  131. package/lib/shared/pathArrayUtils.test.ts +315 -0
  132. package/lib/shared/pathArrayUtils.ts +17 -0
  133. package/lib/shared/pathUtils.test.ts +260 -0
  134. package/lib/shared/pathUtils.ts +244 -0
  135. package/lib/shared/paths/Path.test.ts +74 -0
  136. package/lib/shared/paths/Path.ts +23 -0
  137. package/lib/shared/paths/PathConverter.test.ts +232 -0
  138. package/lib/shared/paths/PathConverter.ts +141 -0
  139. package/lib/shared/paths/PathUtils.ts +290 -0
  140. package/lib/shared/paths/PathValidator.test.ts +193 -0
  141. package/lib/shared/paths/PathValidator.ts +53 -0
  142. package/lib/shared/paths/index.ts +48 -0
  143. package/lib/shared/propResolver.test.ts +639 -0
  144. package/lib/shared/propResolver.ts +124 -0
  145. package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +190 -0
  146. package/lib/shared/registry/BaseNodeTypeRegistry.ts +200 -0
  147. package/lib/shared/registry/ClientNodeTypeRegistry.ts +34 -0
  148. package/lib/shared/registry/ClientRegistry.test.ts +26 -0
  149. package/lib/shared/registry/ClientRegistry.ts +15 -0
  150. package/lib/shared/registry/ComponentRegistry.test.ts +293 -0
  151. package/lib/shared/registry/ComponentRegistry.ts +100 -0
  152. package/lib/shared/registry/NodeTypeDefinition.ts +198 -0
  153. package/lib/shared/registry/NodeTypeManager.ts +94 -0
  154. package/lib/shared/registry/RegistryManager.test.ts +58 -0
  155. package/lib/shared/registry/RegistryManager.ts +60 -0
  156. package/lib/shared/registry/SSRNodeTypeRegistry.ts +33 -0
  157. package/lib/shared/registry/SSRRegistry.test.ts +26 -0
  158. package/lib/shared/registry/SSRRegistry.ts +15 -0
  159. package/lib/shared/registry/createNodeType.ts +175 -0
  160. package/lib/shared/registry/defineNodeType.ts +73 -0
  161. package/lib/shared/registry/fieldPresets.ts +109 -0
  162. package/lib/shared/registry/index.ts +50 -0
  163. package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +71 -0
  164. package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +61 -0
  165. package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +88 -0
  166. package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +66 -0
  167. package/lib/shared/registry/nodeTypes/ObjectLinkNodeType.ts +75 -0
  168. package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +49 -0
  169. package/lib/shared/registry/nodeTypes/TextNodeType.ts +52 -0
  170. package/lib/shared/registry/nodeTypes/index.ts +75 -0
  171. package/lib/shared/responsiveScaling.test.ts +268 -0
  172. package/lib/shared/responsiveScaling.ts +194 -0
  173. package/lib/shared/responsiveStyleUtils.test.ts +300 -0
  174. package/lib/shared/responsiveStyleUtils.ts +139 -0
  175. package/lib/shared/slugTranslator.test.ts +325 -0
  176. package/lib/shared/slugTranslator.ts +177 -0
  177. package/lib/shared/styleNodeUtils.test.ts +132 -0
  178. package/lib/shared/styleNodeUtils.ts +102 -0
  179. package/lib/shared/styleUtils.test.ts +238 -0
  180. package/lib/shared/styleUtils.ts +63 -0
  181. package/lib/shared/themeDefaults.test.ts +113 -0
  182. package/lib/shared/themeDefaults.ts +103 -0
  183. package/lib/shared/tree/PathBuilder.ts +383 -0
  184. package/lib/shared/treePathUtils.test.ts +539 -0
  185. package/lib/shared/treePathUtils.ts +339 -0
  186. package/lib/shared/types/api.ts +58 -0
  187. package/lib/shared/types/cms.ts +95 -0
  188. package/lib/shared/types/colors.ts +45 -0
  189. package/lib/shared/types/components.ts +121 -0
  190. package/lib/shared/types/errors.test.ts +103 -0
  191. package/lib/shared/types/errors.ts +69 -0
  192. package/lib/shared/types/index.ts +96 -0
  193. package/lib/shared/types/nodes.ts +20 -0
  194. package/lib/shared/types/rendering.ts +61 -0
  195. package/lib/shared/types/styles.ts +38 -0
  196. package/lib/shared/types.ts +11 -0
  197. package/lib/shared/utilityClassConfig.ts +287 -0
  198. package/lib/shared/utilityClassMapper.test.ts +140 -0
  199. package/lib/shared/utilityClassMapper.ts +229 -0
  200. package/lib/shared/utils/fileUtils.test.ts +99 -0
  201. package/lib/shared/utils/fileUtils.ts +56 -0
  202. package/lib/shared/utils.test.ts +261 -0
  203. package/lib/shared/utils.ts +84 -0
  204. package/lib/shared/validation/index.ts +7 -0
  205. package/lib/shared/validation/propValidator.test.ts +178 -0
  206. package/lib/shared/validation/propValidator.ts +238 -0
  207. package/lib/shared/validation/schemas.test.ts +177 -0
  208. package/lib/shared/validation/schemas.ts +401 -0
  209. package/lib/shared/validation/validators.test.ts +109 -0
  210. package/lib/shared/validation/validators.ts +304 -0
  211. package/lib/test-utils/dom-setup.ts +55 -0
  212. package/lib/test-utils/factories/ConsoleMockFactory.ts +200 -0
  213. package/lib/test-utils/factories/DomMockFactory.ts +487 -0
  214. package/lib/test-utils/factories/EventMockFactory.ts +244 -0
  215. package/lib/test-utils/factories/FetchMockFactory.ts +210 -0
  216. package/lib/test-utils/factories/ServerMockFactory.ts +223 -0
  217. package/lib/test-utils/factories/StoreMockFactory.ts +370 -0
  218. package/lib/test-utils/factories/index.ts +11 -0
  219. package/lib/test-utils/fixtures.ts +134 -0
  220. package/lib/test-utils/helpers/asyncHelpers.test.ts +112 -0
  221. package/lib/test-utils/helpers/asyncHelpers.ts +196 -0
  222. package/lib/test-utils/helpers/index.ts +6 -0
  223. package/lib/test-utils/helpers.test.ts +73 -0
  224. package/lib/test-utils/helpers.ts +90 -0
  225. package/lib/test-utils/index.ts +17 -0
  226. package/lib/test-utils/mockFactories.ts +92 -0
  227. package/lib/test-utils/mocks.ts +341 -0
  228. package/package.json +38 -0
  229. package/templates/index-router.html +34 -0
  230. package/tsconfig.json +14 -0
  231. package/vite.config.ts +43 -0
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Route Registration
3
+ * Centralized route handling with middleware
4
+ */
5
+
6
+ import type { PageService } from '../services/pageService';
7
+ import type { ComponentService } from '../services/componentService';
8
+ import type { CMSService } from '../services/cmsService';
9
+ import type { CMSProvider } from '../../shared/interfaces/contentProvider';
10
+ import { HMR_ROUTE } from '../../shared/constants';
11
+ import { handleCorsPreflight } from '../middleware/cors';
12
+ import { handleRouteError } from '../middleware/errorHandler';
13
+ import { logRequest, logResponseTime } from '../middleware/logger';
14
+ import { handleApiRoutes, type CMSRouteContext } from './api/index';
15
+ import { handlePageRoute } from './pages';
16
+ import { handleStaticRoute } from './static';
17
+
18
+ export interface RouteContext {
19
+ pageService: PageService;
20
+ componentService: ComponentService;
21
+ cmsService?: CMSService;
22
+ cmsProvider?: CMSProvider;
23
+ }
24
+
25
+ /**
26
+ * Handle all routes with middleware
27
+ */
28
+ export async function handleRoutes(
29
+ req: Request,
30
+ url: URL,
31
+ server: any,
32
+ context: RouteContext
33
+ ): Promise<Response | undefined> {
34
+ const startTime = Date.now();
35
+ const { pageService, componentService, cmsService, cmsProvider } = context;
36
+
37
+ // Log request
38
+ logRequest(req);
39
+
40
+ // Handle CORS preflight requests
41
+ const corsResponse = handleCorsPreflight(req);
42
+ if (corsResponse) {
43
+ logResponseTime(startTime, req);
44
+ return corsResponse;
45
+ }
46
+
47
+ try {
48
+ // WebSocket upgrade for custom YAML reload notifications
49
+ if (url.pathname === HMR_ROUTE) {
50
+ const success = server.upgrade(req);
51
+ logResponseTime(startTime, req);
52
+ return success
53
+ ? undefined
54
+ : new Response('WebSocket upgrade failed', { status: 500 });
55
+ }
56
+
57
+ // Build CMS context if CMS service and provider are available
58
+ const cmsContext: CMSRouteContext | undefined =
59
+ cmsService && cmsProvider
60
+ ? { cmsService, cmsProvider }
61
+ : undefined;
62
+
63
+ // API Routes
64
+ const apiResponse = await handleApiRoutes(
65
+ req,
66
+ url,
67
+ pageService,
68
+ componentService,
69
+ cmsContext
70
+ );
71
+ if (apiResponse) {
72
+ logResponseTime(startTime, req);
73
+ return apiResponse;
74
+ }
75
+
76
+ // Static asset directories - serve before page routes
77
+ if (url.pathname.startsWith('/images/') || url.pathname.startsWith('/videos/') || url.pathname.startsWith('/assets/') || url.pathname.startsWith('/public/') || url.pathname.startsWith('/icons/')) {
78
+ const response = await handleStaticRoute(url);
79
+ if (response) {
80
+ logResponseTime(startTime, req);
81
+ return response;
82
+ }
83
+ }
84
+
85
+ // Page routes (SSR)
86
+ if (url.pathname === '/' || (url.pathname.startsWith('/') && !url.pathname.includes('.'))) {
87
+ const response = await handlePageRoute(url, context);
88
+ logResponseTime(startTime, req);
89
+ return response;
90
+ }
91
+
92
+ // Static files
93
+ const response = await handleStaticRoute(url);
94
+ logResponseTime(startTime, req);
95
+ return response;
96
+ } catch (error) {
97
+ logResponseTime(startTime, req);
98
+ return handleRouteError(
99
+ () => Promise.reject(error),
100
+ `Error handling route: ${url.pathname}`
101
+ );
102
+ }
103
+ }
104
+
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Page Route Handler
3
+ * Handles page route requests with SSR, i18n, and CMS support
4
+ */
5
+
6
+ import type { RouteContext } from './index';
7
+ import { generateSSRHTML } from '../ssrRenderer';
8
+ import { getStaticFilePath } from '../../shared/pathUtils';
9
+ import { parseJSON, loadI18nConfig } from '../jsonLoader';
10
+ import { packagePaths } from '../projectContext';
11
+ import { parseLocaleFromPath } from '../../shared/i18n';
12
+ import { buildSlugIndex, resolveSlugToPageId } from '../../shared/slugTranslator';
13
+ import type { CMSItem } from '../../shared/types';
14
+
15
+ /**
16
+ * Handle page route requests
17
+ */
18
+ export async function handlePageRoute(
19
+ url: URL,
20
+ context: RouteContext
21
+ ): Promise<Response | undefined> {
22
+ const { pageService, componentService, cmsService } = context;
23
+ const pagePath = url.pathname;
24
+
25
+ // Load i18n config for locale extraction
26
+ const i18nConfig = await loadI18nConfig();
27
+
28
+ // Parse locale from URL path (e.g., /pl/o-nas -> locale: 'pl', path: '/o-nas')
29
+ const { locale, pathWithoutLocale } = parseLocaleFromPath(pagePath, i18nConfig);
30
+
31
+ // Build slug index for translation lookup
32
+ const slugMappings = pageService.getSlugMappings();
33
+ const slugIndex = buildSlugIndex(slugMappings);
34
+
35
+ // 1. Try CMS route match first
36
+ if (cmsService) {
37
+ const cmsMatch = await cmsService.matchRoute(pathWithoutLocale);
38
+
39
+ if (cmsMatch) {
40
+ // Load template page content by file path
41
+ const templatePageContent = await loadPageByFilePath(cmsMatch.pagePath);
42
+
43
+ if (templatePageContent) {
44
+ try {
45
+ // Parse page JSON
46
+ const pageData = parseJSON(templatePageContent);
47
+
48
+ // Convert global components Map to Record for SSR
49
+ const globalComponentsRecord = componentService.getAllComponents();
50
+
51
+ // Generate SSR HTML with CMS context
52
+ const baseUrl = `${url.protocol}//${url.host}`;
53
+ const ssrHTML = await generateSSRHTML(
54
+ pageData,
55
+ globalComponentsRecord,
56
+ pagePath,
57
+ baseUrl,
58
+ false,
59
+ locale,
60
+ slugMappings,
61
+ { cms: cmsMatch.item } // Pass CMS item as context
62
+ );
63
+
64
+ return new Response(ssrHTML, {
65
+ headers: {
66
+ 'Content-Type': 'text/html; charset=utf-8',
67
+ 'Cache-Control': 'no-store, max-age=0',
68
+ 'Pragma': 'no-cache',
69
+ 'Expires': '0',
70
+ },
71
+ });
72
+ } catch (error) {
73
+ console.error('Error rendering CMS page:', error);
74
+ return new Response(Bun.file(packagePaths.indexHtml()));
75
+ }
76
+ }
77
+ }
78
+ }
79
+
80
+ // 2. Fall back to static page routing
81
+
82
+ // Extract slug from path (remove leading slash)
83
+ const slug = pathWithoutLocale === '/' ? '' : pathWithoutLocale.substring(1);
84
+
85
+ // Try to resolve translated slug to pageId (e.g., "o-nas" + "pl" -> "about")
86
+ const pageId = resolveSlugToPageId(slug, locale, slugIndex);
87
+
88
+ // Determine lookup path: use resolved pageId or fall back to original path
89
+ let lookupPath: string;
90
+ if (pageId) {
91
+ lookupPath = pageId === 'index' ? '/' : `/${pageId}`;
92
+ } else {
93
+ // No translation found, use original path without locale
94
+ lookupPath = pathWithoutLocale;
95
+ }
96
+
97
+ // Skip static files in development mode - always use fresh SSR from JSON files
98
+ const isDevelopment = true; // Always true when running dev server
99
+
100
+ if (!isDevelopment) {
101
+ // Production mode: Check for pre-built static file first
102
+ const staticFilePath = getStaticFilePath(pagePath);
103
+ const staticFile = Bun.file(staticFilePath);
104
+
105
+ if (await staticFile.exists()) {
106
+ return new Response(staticFile, {
107
+ headers: {
108
+ 'Content-Type': 'text/html; charset=utf-8',
109
+ 'Cache-Control': 'public, max-age=31536000', // 1 year cache for static files
110
+ },
111
+ });
112
+ }
113
+ }
114
+
115
+ // Development mode: Always use fresh SSR from JSON files (no caching)
116
+ const pageContent = pageService.getPage(lookupPath);
117
+
118
+ if (pageContent) {
119
+ try {
120
+ // Parse page JSON
121
+ const pageData = parseJSON(pageContent);
122
+
123
+ // Convert global components Map to Record for SSR
124
+ const globalComponentsRecord = componentService.getAllComponents();
125
+
126
+ // Generate SSR HTML with pre-rendered content and locale
127
+ const baseUrl = `${url.protocol}//${url.host}`;
128
+ const ssrHTML = await generateSSRHTML(pageData, globalComponentsRecord, pagePath, baseUrl, false, locale, slugMappings);
129
+
130
+ return new Response(ssrHTML, {
131
+ headers: {
132
+ 'Content-Type': 'text/html; charset=utf-8',
133
+ 'Cache-Control': 'no-store, max-age=0',
134
+ 'Pragma': 'no-cache',
135
+ 'Expires': '0',
136
+ },
137
+ });
138
+ } catch (error) {
139
+ // Fallback to client-side rendering on error
140
+ return new Response(Bun.file(packagePaths.indexHtml()));
141
+ }
142
+ } else {
143
+ // Page not found - still return HTML shell for client-side 404 handling
144
+ return new Response(Bun.file(packagePaths.indexHtml()));
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Load page content by file path (for CMS template pages)
150
+ */
151
+ async function loadPageByFilePath(filePath: string): Promise<string | null> {
152
+ try {
153
+ const file = Bun.file(filePath);
154
+ if (await file.exists()) {
155
+ return await file.text();
156
+ }
157
+ return null;
158
+ } catch {
159
+ return null;
160
+ }
161
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Static File Route Handler
3
+ * Serves static files and handles TypeScript/TSX file requests
4
+ */
5
+
6
+ import { join } from 'path';
7
+ import { resolveProjectPath, resolvePackagePath, packagePaths } from '../projectContext';
8
+
9
+ export async function handleStaticRoute(url: URL): Promise<Response | undefined> {
10
+ // Handle client-router requests (both .js and .tsx)
11
+ if (url.pathname === '/client-router.js' || url.pathname === '/client-router.tsx') {
12
+ const result = await Bun.build({
13
+ entrypoints: [packagePaths.clientRouter()],
14
+ target: 'browser',
15
+ format: 'esm',
16
+ minify: false,
17
+ sourcemap: 'inline',
18
+ });
19
+
20
+ if (result.success && result.outputs[0]) {
21
+ return new Response(result.outputs[0], {
22
+ headers: {
23
+ 'Content-Type': 'application/javascript',
24
+ 'Cache-Control': 'no-cache, no-store, must-revalidate',
25
+ },
26
+ });
27
+ }
28
+ }
29
+
30
+ // Serve client bundle with HMR enabled (editor TypeScript files)
31
+ if (url.pathname.endsWith('.tsx') || url.pathname.endsWith('.ts')) {
32
+ const entrypoint = resolvePackagePath(url.pathname.slice(1));
33
+ const result = await Bun.build({
34
+ entrypoints: [entrypoint],
35
+ target: 'browser',
36
+ format: 'esm',
37
+ minify: false,
38
+ sourcemap: 'inline',
39
+ });
40
+
41
+ if (result.success && result.outputs[0]) {
42
+ return new Response(result.outputs[0], {
43
+ headers: {
44
+ 'Content-Type': 'application/javascript',
45
+ 'Cache-Control': 'no-cache, no-store, must-revalidate',
46
+ },
47
+ });
48
+ }
49
+ }
50
+
51
+ // Serve static files (including fonts and images)
52
+ const decodedPathname = decodeURIComponent(url.pathname);
53
+
54
+ // For fonts, images, and icons, resolve relative to project root
55
+ // Other static files (editor assets) resolve relative to package root
56
+ let filePath: string;
57
+ if (decodedPathname.startsWith('/fonts/') || decodedPathname.startsWith('/images/') || decodedPathname.startsWith('/icons/')) {
58
+ filePath = resolveProjectPath(decodedPathname.slice(1)); // Remove leading /
59
+ } else {
60
+ filePath = resolvePackagePath(decodedPathname.slice(1)); // Remove leading /
61
+ }
62
+
63
+ const file = Bun.file(filePath);
64
+ if (await file.exists()) {
65
+ const pathname = decodedPathname.toLowerCase();
66
+ let contentType = 'application/octet-stream';
67
+
68
+ // Set proper MIME types for font files
69
+ if (pathname.endsWith('.woff2')) {
70
+ contentType = 'font/woff2';
71
+ } else if (pathname.endsWith('.woff')) {
72
+ contentType = 'font/woff';
73
+ } else if (pathname.endsWith('.ttf')) {
74
+ contentType = 'font/ttf';
75
+ } else if (pathname.endsWith('.otf')) {
76
+ contentType = 'font/otf';
77
+ }
78
+ // Set proper MIME types for image files
79
+ else if (pathname.endsWith('.jpg') || pathname.endsWith('.jpeg')) {
80
+ contentType = 'image/jpeg';
81
+ } else if (pathname.endsWith('.png')) {
82
+ contentType = 'image/png';
83
+ } else if (pathname.endsWith('.gif')) {
84
+ contentType = 'image/gif';
85
+ } else if (pathname.endsWith('.webp')) {
86
+ contentType = 'image/webp';
87
+ } else if (pathname.endsWith('.svg')) {
88
+ contentType = 'image/svg+xml';
89
+ } else if (pathname.endsWith('.ico')) {
90
+ contentType = 'image/x-icon';
91
+ } else if (pathname.endsWith('.bmp')) {
92
+ contentType = 'image/bmp';
93
+ } else if (pathname.endsWith('.tiff') || pathname.endsWith('.tif')) {
94
+ contentType = 'image/tiff';
95
+ }
96
+
97
+ return new Response(file, {
98
+ headers: {
99
+ 'Content-Type': contentType,
100
+ 'Cache-Control': 'public, max-age=31536000, immutable', // Cache static files for 1 year
101
+ },
102
+ });
103
+ }
104
+
105
+ return undefined;
106
+ }
107
+
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Color Service
3
+ * Manages loading and caching of color variables and themes from colors.json
4
+ */
5
+
6
+ import { loadJSONFile, parseJSON } from '../jsonLoader';
7
+ import { projectPaths } from '../projectContext';
8
+ import type { ColorVariables, ColorVariableEntry, ThemeConfig, ThemeEntry } from '../../shared/types';
9
+
10
+ export class ColorService {
11
+ private themeConfig: ThemeConfig | null = null;
12
+ private loadingPromise: Promise<ThemeConfig> | null = null;
13
+
14
+ /**
15
+ * Load theme configuration from colors.json
16
+ */
17
+ async loadThemeConfig(): Promise<ThemeConfig> {
18
+ // Return cached config if already loaded
19
+ if (this.themeConfig) {
20
+ return this.themeConfig;
21
+ }
22
+
23
+ // Return existing loading promise if already in progress
24
+ if (this.loadingPromise) {
25
+ return this.loadingPromise;
26
+ }
27
+
28
+ // Start loading
29
+ this.loadingPromise = this.performLoad();
30
+ this.themeConfig = await this.loadingPromise;
31
+ this.loadingPromise = null;
32
+
33
+ return this.themeConfig;
34
+ }
35
+
36
+ /**
37
+ * Perform the actual loading from file
38
+ */
39
+ private async performLoad(): Promise<ThemeConfig> {
40
+ try {
41
+ const content = await loadJSONFile(projectPaths.colors());
42
+ if (content) {
43
+ const data = parseJSON(content);
44
+ if (data && typeof data === 'object' && 'themes' in data && 'default' in data) {
45
+ return data as ThemeConfig;
46
+ }
47
+ }
48
+ } catch (error) {
49
+ console.warn('Failed to load colors.json:', error);
50
+ }
51
+
52
+ // Return default theme config if file not found or invalid
53
+ return this.getDefaultThemeConfig();
54
+ }
55
+
56
+ /**
57
+ * Get default theme configuration
58
+ */
59
+ private getDefaultThemeConfig(): ThemeConfig {
60
+ return {
61
+ default: 'dark',
62
+ themes: {
63
+ dark: {
64
+ label: 'Dark',
65
+ colors: {
66
+ 'primary': '#007acc',
67
+ 'primary-light': '#0099ff',
68
+ 'primary-dark': '#005a99',
69
+ 'secondary': '#6c757d',
70
+ 'success': '#28a745',
71
+ 'warning': '#ffc107',
72
+ 'danger': '#dc3545',
73
+ 'error': '#d32f2f',
74
+ 'info': '#17a2b8',
75
+ 'light': '#f8f9fa',
76
+ 'dark': '#343a40',
77
+ 'text': '#cccccc',
78
+ 'text-dark': '#1e1e1e',
79
+ 'background': '#1e1e1e',
80
+ 'background-light': '#252526',
81
+ 'border': '#444444',
82
+ 'border-light': '#d0d0d0',
83
+ },
84
+ },
85
+ light: {
86
+ label: 'Light',
87
+ colors: {
88
+ 'primary': '#0066ff',
89
+ 'primary-light': '#3385ff',
90
+ 'primary-dark': '#0052cc',
91
+ 'secondary': '#888888',
92
+ 'success': '#22c55e',
93
+ 'warning': '#f59e0b',
94
+ 'danger': '#ef4444',
95
+ 'error': '#dc2626',
96
+ 'info': '#06b6d4',
97
+ 'light': '#f8f9fa',
98
+ 'dark': '#1f2937',
99
+ 'text': '#1f2937',
100
+ 'text-dark': '#ffffff',
101
+ 'background': '#ffffff',
102
+ 'background-light': '#f3f4f6',
103
+ 'border': '#d1d5db',
104
+ 'border-light': '#e5e7eb',
105
+ },
106
+ },
107
+ },
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Get colors for a specific theme (or default if not found)
113
+ */
114
+ async getThemeColors(themeName?: string): Promise<ColorVariables> {
115
+ const config = await this.loadThemeConfig();
116
+ const theme = themeName && config.themes[themeName] ? config.themes[themeName] : config.themes[config.default];
117
+ return { colors: theme.colors };
118
+ }
119
+
120
+ /**
121
+ * Load color variables from colors.json (for backward compatibility)
122
+ */
123
+ async loadColors(): Promise<ColorVariables> {
124
+ return this.getThemeColors();
125
+ }
126
+
127
+ /**
128
+ * Get all available themes
129
+ */
130
+ async getAvailableThemes(): Promise<ThemeEntry[]> {
131
+ const config = await this.loadThemeConfig();
132
+ return Object.entries(config.themes).map(([name, theme]) => ({
133
+ name,
134
+ label: theme.label,
135
+ }));
136
+ }
137
+
138
+ /**
139
+ * Get default theme name
140
+ */
141
+ async getDefaultTheme(): Promise<string> {
142
+ const config = await this.loadThemeConfig();
143
+ return config.default;
144
+ }
145
+
146
+ /**
147
+ * Get all color variables as entries for a theme
148
+ */
149
+ async getColorEntries(themeName?: string): Promise<ColorVariableEntry[]> {
150
+ const colors = await this.getThemeColors(themeName);
151
+ return Object.entries(colors.colors).map(([name, value]) => ({
152
+ name,
153
+ value,
154
+ }));
155
+ }
156
+
157
+ /**
158
+ * Get a specific color by name from a theme
159
+ */
160
+ async getColor(name: string, themeName?: string): Promise<string | null> {
161
+ const colors = await this.getThemeColors(themeName);
162
+ return colors.colors[name] || null;
163
+ }
164
+
165
+ /**
166
+ * Clear cache (used for hot reload)
167
+ */
168
+ clearCache(): void {
169
+ this.themeConfig = null;
170
+ }
171
+
172
+ /**
173
+ * Get the full theme configuration (for editor)
174
+ */
175
+ async getFullConfig(): Promise<ThemeConfig> {
176
+ return this.loadThemeConfig();
177
+ }
178
+
179
+ /**
180
+ * Save theme configuration to colors.json
181
+ */
182
+ async saveThemeConfig(config: ThemeConfig): Promise<void> {
183
+ const colorsPath = projectPaths.colors();
184
+ const content = JSON.stringify(config, null, 2);
185
+ await Bun.write(colorsPath, content);
186
+
187
+ // Update cache with new config
188
+ this.themeConfig = config;
189
+ }
190
+ }
191
+
192
+ // Export singleton instance
193
+ export const colorService = new ColorService();