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,238 @@
1
+ /**
2
+ * Prop Validator
3
+ * Validates component props against their interface definitions
4
+ * Provides type checking and coercion for runtime safety
5
+ */
6
+
7
+ import type { PropDefinition } from '../types/components';
8
+ import { isI18nValue } from '../i18n';
9
+
10
+ /**
11
+ * Validation error with context
12
+ */
13
+ export interface ValidationError {
14
+ propName: string;
15
+ message: string;
16
+ receivedValue: unknown;
17
+ expectedType: string;
18
+ }
19
+
20
+ /**
21
+ * Validation result
22
+ */
23
+ export type PropValidationResult =
24
+ | { valid: true; props: Record<string, unknown> }
25
+ | { valid: false; errors: ValidationError[]; props: Record<string, unknown> };
26
+
27
+ /**
28
+ * Coerce a value to the expected type if safe
29
+ * Returns the coerced value or undefined if coercion is not safe
30
+ * Note: i18n values are passed through unchanged for later resolution
31
+ */
32
+ function coerceValue(value: unknown, expectedType: PropDefinition['type']): unknown {
33
+ if (value === null || value === undefined) {
34
+ return undefined;
35
+ }
36
+
37
+ // Pass through i18n values unchanged - they will be resolved later by resolveI18nInProps
38
+ if (isI18nValue(value)) {
39
+ return value;
40
+ }
41
+
42
+ switch (expectedType) {
43
+ case 'string':
44
+ return String(value);
45
+
46
+ case 'number':
47
+ if (typeof value === 'number') {
48
+ return value;
49
+ }
50
+ if (typeof value === 'string') {
51
+ const num = Number(value);
52
+ if (!isNaN(num) && isFinite(num)) {
53
+ return num;
54
+ }
55
+ }
56
+ return undefined;
57
+
58
+ case 'boolean':
59
+ if (typeof value === 'boolean') {
60
+ return value;
61
+ }
62
+ if (typeof value === 'string') {
63
+ const lower = value.toLowerCase();
64
+ if (lower === 'true' || lower === '1') {
65
+ return true;
66
+ }
67
+ if (lower === 'false' || lower === '0') {
68
+ return false;
69
+ }
70
+ }
71
+ if (typeof value === 'number') {
72
+ return value !== 0;
73
+ }
74
+ return undefined;
75
+
76
+ case 'select':
77
+ return String(value);
78
+
79
+ default:
80
+ return value;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Validate a single prop value against its definition
86
+ */
87
+ function validateSingleProp(
88
+ propName: string,
89
+ propDef: PropDefinition,
90
+ value: unknown
91
+ ): { valid: boolean; error?: ValidationError; coercedValue?: unknown } {
92
+ // If value is undefined, it's valid (will use default)
93
+ if (value === undefined) {
94
+ return { valid: true };
95
+ }
96
+
97
+ const { type, options } = propDef;
98
+
99
+ // Type checking and coercion
100
+ let coercedValue: unknown;
101
+ let typeValid = false;
102
+
103
+ switch (type) {
104
+ case 'string':
105
+ coercedValue = coerceValue(value, 'string');
106
+ // Accept both strings and i18n values (i18n values will be resolved later)
107
+ typeValid = typeof coercedValue === 'string' || isI18nValue(coercedValue);
108
+ break;
109
+
110
+ case 'number':
111
+ coercedValue = coerceValue(value, 'number');
112
+ typeValid = typeof coercedValue === 'number';
113
+ break;
114
+
115
+ case 'boolean':
116
+ coercedValue = coerceValue(value, 'boolean');
117
+ typeValid = typeof coercedValue === 'boolean';
118
+ break;
119
+
120
+ case 'select':
121
+ coercedValue = coerceValue(value, 'select');
122
+ typeValid = typeof coercedValue === 'string';
123
+ if (typeValid && options) {
124
+ // Validate against allowed options
125
+ if (!options.includes(coercedValue as string)) {
126
+ return {
127
+ valid: false,
128
+ error: {
129
+ propName,
130
+ message: `Value "${coercedValue}" is not in allowed options: ${options.join(', ')}`,
131
+ receivedValue: value,
132
+ expectedType: `select(${options.join('|')})`,
133
+ },
134
+ };
135
+ }
136
+ }
137
+ break;
138
+
139
+ case 'link':
140
+ // Link type expects an object with href (and optional target)
141
+ if (typeof value === 'object' && value !== null && 'href' in value) {
142
+ coercedValue = value;
143
+ typeValid = true;
144
+ } else {
145
+ typeValid = false;
146
+ }
147
+ break;
148
+
149
+ default:
150
+ return {
151
+ valid: false,
152
+ error: {
153
+ propName,
154
+ message: `Unknown prop type: ${type}`,
155
+ receivedValue: value,
156
+ expectedType: type,
157
+ },
158
+ };
159
+ }
160
+
161
+ if (!typeValid) {
162
+ return {
163
+ valid: false,
164
+ error: {
165
+ propName,
166
+ message: `Expected ${type}, got ${typeof value}`,
167
+ receivedValue: value,
168
+ expectedType: type,
169
+ },
170
+ };
171
+ }
172
+
173
+ return { valid: true, coercedValue };
174
+ }
175
+
176
+ /**
177
+ * Validate component props against their interface definition
178
+ *
179
+ * @param propDefs - Component interface definition (prop name -> PropDefinition)
180
+ * @param passedProps - Props passed to the component instance
181
+ * @returns Validation result with validated/coerced props and any errors
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * const result = validateComponentProps(
186
+ * { title: { type: 'string', default: 'Hello' } },
187
+ * { title: 'World', count: 123 }
188
+ * );
189
+ * if (result.valid) {
190
+ * console.log(result.props); // { title: 'World', count: 123 }
191
+ * }
192
+ * ```
193
+ */
194
+ export function validateComponentProps(
195
+ propDefs: Record<string, PropDefinition>,
196
+ passedProps: Record<string, unknown>
197
+ ): PropValidationResult {
198
+ const errors: ValidationError[] = [];
199
+ const validatedProps: Record<string, unknown> = {};
200
+
201
+ // Validate props defined in interface
202
+ for (const [propName, propDef] of Object.entries(propDefs)) {
203
+ const value = passedProps[propName];
204
+ const validation = validateSingleProp(propName, propDef, value);
205
+
206
+ if (validation.valid) {
207
+ // Use coerced value if available, otherwise use original or default
208
+ validatedProps[propName] = validation.coercedValue !== undefined
209
+ ? validation.coercedValue
210
+ : (value !== undefined ? value : propDef.default);
211
+ } else {
212
+ // Validation failed - log error but use default if available (backward compatibility)
213
+ if (validation.error) {
214
+ errors.push(validation.error);
215
+ }
216
+ // Use default value if available, otherwise use original value (graceful degradation)
217
+ validatedProps[propName] = propDef.default !== undefined
218
+ ? propDef.default
219
+ : value;
220
+ }
221
+ }
222
+
223
+ // Preserve unknown props for backward compatibility (but log warnings)
224
+ for (const [propName, value] of Object.entries(passedProps)) {
225
+ if (!(propName in propDefs)) {
226
+ // Unknown prop - preserve it but log a warning (backward compatibility)
227
+ validatedProps[propName] = value;
228
+ }
229
+ }
230
+
231
+ if (errors.length > 0) {
232
+ return { valid: false, errors, props: validatedProps };
233
+ }
234
+
235
+ return { valid: true, props: validatedProps };
236
+ }
237
+
238
+
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Schema Validation Tests
3
+ * Critical path tests for zod schema validation
4
+ */
5
+
6
+ import { describe, test, expect } from 'bun:test';
7
+ import {
8
+ PropDefinitionSchema,
9
+ ComponentNodeSchema,
10
+ ComponentDefinitionSchema,
11
+ PageDataSchema,
12
+ } from './schemas';
13
+
14
+ describe('Schema Validation', () => {
15
+ describe('Valid component definitions pass', () => {
16
+ test('valid prop definition', () => {
17
+ const propDef = {
18
+ type: 'string',
19
+ default: 'Hello',
20
+ };
21
+
22
+ const result = PropDefinitionSchema.safeParse(propDef);
23
+
24
+ expect(result.success).toBe(true);
25
+ if (result.success) {
26
+ expect(result.data.type).toBe('string');
27
+ expect(result.data.default).toBe('Hello');
28
+ }
29
+ });
30
+
31
+ test('valid select prop definition', () => {
32
+ const propDef = {
33
+ type: 'select',
34
+ default: 'primary',
35
+ options: ['primary', 'secondary'],
36
+ };
37
+
38
+ const result = PropDefinitionSchema.safeParse(propDef);
39
+
40
+ expect(result.success).toBe(true);
41
+ if (result.success) {
42
+ expect(result.data.type).toBe('select');
43
+ expect(result.data.options).toEqual(['primary', 'secondary']);
44
+ }
45
+ });
46
+
47
+ test('valid component node', () => {
48
+ const node = {
49
+ type: 'node',
50
+ tag: 'div',
51
+ children: [],
52
+ };
53
+
54
+ const result = ComponentNodeSchema.safeParse(node);
55
+
56
+ expect(result.success).toBe(true);
57
+ });
58
+
59
+ test('valid component definition', () => {
60
+ const def = {
61
+ type: 'component',
62
+ component: {
63
+ interface: {
64
+ title: { type: 'string', default: 'Hello' },
65
+ },
66
+ structure: {
67
+ type: 'node',
68
+ tag: 'div',
69
+ children: [],
70
+ },
71
+ },
72
+ };
73
+
74
+ const result = ComponentDefinitionSchema.safeParse(def);
75
+
76
+ expect(result.success).toBe(true);
77
+ });
78
+ });
79
+
80
+ describe('Invalid structures caught', () => {
81
+ test('invalid prop type', () => {
82
+ const propDef = {
83
+ type: 'invalid-type',
84
+ default: 'Hello',
85
+ };
86
+
87
+ const result = PropDefinitionSchema.safeParse(propDef);
88
+
89
+ expect(result.success).toBe(false);
90
+ });
91
+
92
+ test('missing required node type', () => {
93
+ const node = {
94
+ tag: 'div',
95
+ children: [],
96
+ };
97
+
98
+ const result = ComponentNodeSchema.safeParse(node);
99
+
100
+ expect(result.success).toBe(false);
101
+ });
102
+
103
+ test('invalid component definition structure', () => {
104
+ const def = {
105
+ component: {
106
+ // Missing required structure
107
+ },
108
+ };
109
+
110
+ const result = ComponentDefinitionSchema.safeParse(def);
111
+
112
+ // Should fail or pass with passthrough (backward compatibility)
113
+ // The schema uses passthrough, so it might pass but log warnings
114
+ expect(result).toBeDefined();
115
+ });
116
+ });
117
+
118
+ describe('Missing required fields handled', () => {
119
+ test('prop definition without default', () => {
120
+ const propDef = {
121
+ type: 'string',
122
+ };
123
+
124
+ const result = PropDefinitionSchema.safeParse(propDef);
125
+
126
+ // Should pass (default is optional)
127
+ expect(result.success).toBe(true);
128
+ });
129
+
130
+ test('component node without children', () => {
131
+ const node = {
132
+ type: 'node',
133
+ tag: 'div',
134
+ };
135
+
136
+ const result = ComponentNodeSchema.safeParse(node);
137
+
138
+ // Should pass (children is optional)
139
+ expect(result.success).toBe(true);
140
+ });
141
+ });
142
+
143
+ describe('Unknown fields preserved (backward compatibility)', () => {
144
+ test('prop definition with unknown fields', () => {
145
+ const propDef = {
146
+ type: 'string',
147
+ default: 'Hello',
148
+ unknownField: 'preserved',
149
+ };
150
+
151
+ const result = PropDefinitionSchema.safeParse(propDef);
152
+
153
+ expect(result.success).toBe(true);
154
+ if (result.success) {
155
+ // Passthrough should preserve unknown fields
156
+ expect((propDef as any).unknownField).toBe('preserved');
157
+ }
158
+ });
159
+
160
+ test('component node with unknown fields', () => {
161
+ const node = {
162
+ type: 'node',
163
+ tag: 'div',
164
+ unknownField: 'preserved',
165
+ children: [],
166
+ };
167
+
168
+ const result = ComponentNodeSchema.safeParse(node);
169
+
170
+ expect(result.success).toBe(true);
171
+ // Passthrough preserves unknown fields
172
+ expect((node as any).unknownField).toBe('preserved');
173
+ });
174
+ });
175
+ });
176
+
177
+