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,230 @@
1
+ /**
2
+ * Error Boundary Component
3
+ * Catches and handles React rendering errors gracefully
4
+ */
5
+
6
+ import { Component, createElement as h, ReactNode } from 'react';
7
+
8
+ interface ErrorBoundaryProps {
9
+ children?: ReactNode;
10
+ fallback?: (error: Error, errorInfo: any) => ReactNode;
11
+ onError?: (error: Error, errorInfo: any) => void;
12
+ componentName?: string;
13
+ level?: 'component' | 'page';
14
+ }
15
+
16
+ interface ErrorBoundaryState {
17
+ hasError: boolean;
18
+ error: Error | null;
19
+ errorInfo: any;
20
+ }
21
+
22
+ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
23
+ constructor(props: ErrorBoundaryProps) {
24
+ super(props);
25
+ this.state = {
26
+ hasError: false,
27
+ error: null,
28
+ errorInfo: null,
29
+ };
30
+ }
31
+
32
+ static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {
33
+ return {
34
+ hasError: true,
35
+ error,
36
+ };
37
+ }
38
+
39
+ componentDidCatch(error: Error, errorInfo: any) {
40
+ const { onError, componentName, level = 'component' } = this.props;
41
+
42
+ // Log error to console
43
+ console.error(
44
+ `❌ [${level.toUpperCase()} ERROR]${componentName ? ` in ${componentName}` : ''}:`,
45
+ error,
46
+ errorInfo
47
+ );
48
+
49
+ // Call custom error handler if provided
50
+ if (onError) {
51
+ onError(error, errorInfo);
52
+ }
53
+
54
+ // Update state with error info
55
+ this.setState({
56
+ errorInfo,
57
+ });
58
+ }
59
+
60
+ render() {
61
+ const { hasError, error, errorInfo } = this.state;
62
+ const { children, fallback, componentName, level = 'component' } = this.props;
63
+
64
+ if (hasError && error) {
65
+ // Use custom fallback if provided
66
+ if (fallback) {
67
+ return fallback(error, errorInfo);
68
+ }
69
+
70
+ // Default fallback UI
71
+ if (level === 'page') {
72
+ return h('div', {
73
+ style: {
74
+ padding: '40px',
75
+ maxWidth: '800px',
76
+ margin: '0 auto',
77
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
78
+ }
79
+ },
80
+ h('div', {
81
+ style: {
82
+ background: '#fee',
83
+ border: '2px solid #fcc',
84
+ borderRadius: '8px',
85
+ padding: '24px',
86
+ }
87
+ },
88
+ h('h2', {
89
+ style: {
90
+ margin: '0 0 16px 0',
91
+ color: '#c00',
92
+ fontSize: '24px',
93
+ fontWeight: '600',
94
+ }
95
+ }, '⚠️ Page Rendering Error'),
96
+ h('p', {
97
+ style: {
98
+ margin: '0 0 16px 0',
99
+ color: '#666',
100
+ fontSize: '16px',
101
+ lineHeight: '1.5',
102
+ }
103
+ }, 'An error occurred while rendering this page. Please check your JSON configuration.'),
104
+ h('div', {
105
+ style: {
106
+ background: '#fff',
107
+ border: '1px solid #ddd',
108
+ borderRadius: '4px',
109
+ padding: '16px',
110
+ marginBottom: '16px',
111
+ fontFamily: 'monospace',
112
+ fontSize: '14px',
113
+ color: '#c00',
114
+ overflowX: 'auto',
115
+ }
116
+ }, error.message),
117
+ process.env.NODE_ENV === 'development' && errorInfo?.componentStack && h('details', {
118
+ style: {
119
+ marginTop: '16px',
120
+ fontSize: '14px',
121
+ }
122
+ },
123
+ h('summary', {
124
+ style: {
125
+ cursor: 'pointer',
126
+ color: '#666',
127
+ fontWeight: '600',
128
+ marginBottom: '8px',
129
+ }
130
+ }, 'Stack Trace'),
131
+ h('pre', {
132
+ style: {
133
+ background: '#f5f5f5',
134
+ border: '1px solid #ddd',
135
+ borderRadius: '4px',
136
+ padding: '12px',
137
+ fontSize: '12px',
138
+ overflowX: 'auto',
139
+ color: '#333',
140
+ }
141
+ }, errorInfo.componentStack)
142
+ ),
143
+ h('button', {
144
+ onClick: () => window.location.reload(),
145
+ style: {
146
+ marginTop: '16px',
147
+ padding: '10px 20px',
148
+ background: '#007acc',
149
+ color: '#fff',
150
+ border: 'none',
151
+ borderRadius: '4px',
152
+ fontSize: '14px',
153
+ fontWeight: '600',
154
+ cursor: 'pointer',
155
+ }
156
+ }, '🔄 Reload Page')
157
+ )
158
+ );
159
+ }
160
+
161
+ // Component-level error
162
+ return h('div', {
163
+ style: {
164
+ border: '2px dashed #fcc',
165
+ borderRadius: '4px',
166
+ padding: '12px',
167
+ margin: '8px 0',
168
+ background: '#fef5f5',
169
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
170
+ }
171
+ },
172
+ h('div', {
173
+ style: {
174
+ display: 'flex',
175
+ alignItems: 'center',
176
+ gap: '8px',
177
+ marginBottom: '8px',
178
+ }
179
+ },
180
+ h('span', {
181
+ style: {
182
+ fontSize: '18px',
183
+ }
184
+ }, '⚠️'),
185
+ h('strong', {
186
+ style: {
187
+ color: '#c00',
188
+ fontSize: '14px',
189
+ }
190
+ }, `Component Error${componentName ? `: ${componentName}` : ''}`)
191
+ ),
192
+ h('div', {
193
+ style: {
194
+ fontSize: '12px',
195
+ color: '#666',
196
+ fontFamily: 'monospace',
197
+ background: '#fff',
198
+ padding: '8px',
199
+ borderRadius: '4px',
200
+ border: '1px solid #fcc',
201
+ }
202
+ }, error.message)
203
+ );
204
+ }
205
+
206
+ return children;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Higher-order function to wrap component building with error boundary
212
+ */
213
+ export function withErrorBoundary<T>(
214
+ renderFn: () => T,
215
+ options: {
216
+ componentName?: string;
217
+ level?: 'component' | 'page';
218
+ onError?: (error: Error, errorInfo: any) => void;
219
+ } = {}
220
+ ): T | ReturnType<typeof h> {
221
+ try {
222
+ return renderFn();
223
+ } catch (error) {
224
+ return h(ErrorBoundary, {
225
+ ...options,
226
+ children: null,
227
+ });
228
+ }
229
+ }
230
+
@@ -0,0 +1,165 @@
1
+ import { test, expect, describe, beforeEach } from "bun:test";
2
+ import { ComponentRegistry } from "./componentRegistry";
3
+
4
+ describe("ComponentRegistry", () => {
5
+ let registry: ComponentRegistry;
6
+
7
+ beforeEach(() => {
8
+ registry = new ComponentRegistry();
9
+ });
10
+
11
+ describe("register and get", () => {
12
+ test("should register and retrieve a component", () => {
13
+ const buttonDef = {
14
+ type: "button",
15
+ props: { className: "btn" }
16
+ };
17
+
18
+ registry.register("Button", buttonDef);
19
+ const retrieved = registry.get("Button");
20
+
21
+ expect(retrieved).toEqual(buttonDef);
22
+ });
23
+
24
+ test("should return undefined for non-existent component", () => {
25
+ const result = registry.get("NonExistent");
26
+ expect(result).toBeUndefined();
27
+ });
28
+
29
+ test("should overwrite existing component with same name", () => {
30
+ registry.register("Button", { type: "button", props: { color: "red" } });
31
+ registry.register("Button", { type: "button", props: { color: "blue" } });
32
+
33
+ const result = registry.get("Button");
34
+ expect(result?.props.color).toBe("blue");
35
+ });
36
+ });
37
+
38
+ describe("has", () => {
39
+ test("should return true for registered component", () => {
40
+ registry.register("Card", { type: "div" });
41
+ expect(registry.has("Card")).toBe(true);
42
+ });
43
+
44
+ test("should return false for non-existent component", () => {
45
+ expect(registry.has("NonExistent")).toBe(false);
46
+ });
47
+ });
48
+
49
+ describe("clear", () => {
50
+ test("should clear all components", () => {
51
+ registry.register("Button", { type: "button" });
52
+ registry.register("Card", { type: "div" });
53
+
54
+ expect(registry.getNames().length).toBe(2);
55
+
56
+ registry.clear();
57
+
58
+ expect(registry.getNames().length).toBe(0);
59
+ expect(registry.has("Button")).toBe(false);
60
+ expect(registry.has("Card")).toBe(false);
61
+ });
62
+ });
63
+
64
+ describe("merge", () => {
65
+ test("should merge multiple components", () => {
66
+ registry.register("Button", { type: "button" });
67
+
68
+ registry.merge({
69
+ Card: { type: "div" },
70
+ Link: { type: "a" }
71
+ });
72
+
73
+ expect(registry.getNames().length).toBe(3);
74
+ expect(registry.has("Button")).toBe(true);
75
+ expect(registry.has("Card")).toBe(true);
76
+ expect(registry.has("Link")).toBe(true);
77
+ });
78
+
79
+ test("should overwrite existing components when merging", () => {
80
+ registry.register("Button", { type: "button", props: { color: "red" } });
81
+
82
+ registry.merge({
83
+ Button: { type: "button", props: { color: "blue" } }
84
+ });
85
+
86
+ const button = registry.get("Button");
87
+ expect(button?.props.color).toBe("blue");
88
+ });
89
+ });
90
+
91
+ describe("getAll", () => {
92
+ test("should return all registered components", () => {
93
+ registry.register("Button", { type: "button" });
94
+ registry.register("Card", { type: "div" });
95
+
96
+ const all = registry.getAll();
97
+
98
+ expect(Object.keys(all).length).toBe(2);
99
+ expect(all.Button).toBeDefined();
100
+ expect(all.Card).toBeDefined();
101
+ });
102
+
103
+ test("should return a copy, not the original registry", () => {
104
+ registry.register("Button", { type: "button" });
105
+
106
+ const all = registry.getAll();
107
+ all.NewComponent = { type: "span" };
108
+
109
+ expect(registry.has("NewComponent")).toBe(false);
110
+ });
111
+ });
112
+
113
+ describe("getNames", () => {
114
+ test("should return list of component names", () => {
115
+ registry.register("Button", { type: "button" });
116
+ registry.register("Card", { type: "div" });
117
+
118
+ const names = registry.getNames();
119
+
120
+ expect(names).toEqual(["Button", "Card"]);
121
+ });
122
+
123
+ test("should return empty array when no components", () => {
124
+ const names = registry.getNames();
125
+ expect(names).toEqual([]);
126
+ });
127
+ });
128
+
129
+ describe("remove", () => {
130
+ test("should remove a component and return true", () => {
131
+ registry.register("Button", { type: "button" });
132
+
133
+ const result = registry.remove("Button");
134
+
135
+ expect(result).toBe(true);
136
+ expect(registry.has("Button")).toBe(false);
137
+ });
138
+
139
+ test("should return false when removing non-existent component", () => {
140
+ const result = registry.remove("NonExistent");
141
+ expect(result).toBe(false);
142
+ });
143
+ });
144
+
145
+ describe("component with variants", () => {
146
+ test("should handle components with variant definitions", () => {
147
+ const buttonDef = {
148
+ type: "button",
149
+ variants: {
150
+ primary: { backgroundColor: "blue", color: "white" },
151
+ secondary: { backgroundColor: "gray", color: "black" }
152
+ },
153
+ defaultVariant: "primary"
154
+ };
155
+
156
+ registry.register("Button", buttonDef);
157
+ const retrieved = registry.get("Button");
158
+
159
+ expect(retrieved?.variants?.primary).toBeDefined();
160
+ expect(retrieved?.variants?.primary?.backgroundColor).toBe("blue");
161
+ expect(retrieved?.defaultVariant).toBe("primary");
162
+ });
163
+ });
164
+ });
165
+
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Component Registry
3
+ * Manages registration and retrieval of custom components
4
+ *
5
+ * This now extends the base registry for consistency across client and SSR
6
+ */
7
+
8
+ import { BaseComponentRegistry } from '../shared/registry/ComponentRegistry';
9
+ import type { ComponentDefinition } from '../shared/types';
10
+
11
+ export class ComponentRegistry extends BaseComponentRegistry {
12
+ // Client-specific methods can be added here
13
+ // For now, it uses all functionality from BaseComponentRegistry
14
+ }
15
+
16
+ // Export a singleton instance for convenience
17
+ export const globalComponentRegistry = new ComponentRegistry();
18
+
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Theme Context
3
+ * Provides theme switching functionality to components
4
+ */
5
+
6
+ import React, { createContext, useContext, useState, useEffect } from 'react';
7
+ import { useThemes } from '../hooks/useColorVariables';
8
+
9
+ interface ThemeContextType {
10
+ currentTheme: string;
11
+ setTheme: (themeName: string) => void;
12
+ themes: string[];
13
+ loading: boolean;
14
+ }
15
+
16
+ const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
17
+
18
+ /**
19
+ * Theme Provider Component
20
+ * Wraps the app and provides theme context to all children
21
+ */
22
+ export function ThemeProvider({ children }: { children: React.ReactNode }) {
23
+ const { themes, defaultTheme, loading } = useThemes();
24
+ const [currentTheme, setCurrentTheme] = useState<string>('dark');
25
+
26
+ // Initialize theme from defaultTheme or localStorage
27
+ useEffect(() => {
28
+ if (defaultTheme) {
29
+ const savedTheme = localStorage.getItem('theme');
30
+ const themeToUse = savedTheme || defaultTheme;
31
+ setCurrentTheme(themeToUse);
32
+ applyTheme(themeToUse);
33
+ }
34
+ }, [defaultTheme]);
35
+
36
+ const setTheme = (themeName: string) => {
37
+ setCurrentTheme(themeName);
38
+ localStorage.setItem('theme', themeName);
39
+ applyTheme(themeName);
40
+ };
41
+
42
+ const applyTheme = (themeName: string) => {
43
+ // Set the theme attribute on the root element
44
+ const root = document.documentElement;
45
+ root.setAttribute('theme', themeName);
46
+ };
47
+
48
+ const themeNames = themes?.map(theme => theme.name) || [];
49
+
50
+ return (
51
+ <ThemeContext.Provider
52
+ value={{
53
+ currentTheme,
54
+ setTheme,
55
+ themes: themeNames,
56
+ loading,
57
+ }}
58
+ >
59
+ {children}
60
+ </ThemeContext.Provider>
61
+ );
62
+ }
63
+
64
+ /**
65
+ * Hook for using the theme context
66
+ */
67
+ export function useTheme() {
68
+ const context = useContext(ThemeContext);
69
+ if (context === undefined) {
70
+ throw new Error('useTheme must be used within a ThemeProvider');
71
+ }
72
+ return context;
73
+ }