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,339 @@
1
+ /**
2
+ * Tree path utilities for parsing and navigating tree structure paths
3
+ */
4
+
5
+ import { ComponentNode, PageData, JSONPage } from './types';
6
+ import type { PropDefinition } from './types';
7
+ import type { Path } from './pathArrayUtils';
8
+ import { stringToPath } from './pathArrayUtils';
9
+
10
+ // Type for page data that can have component structure
11
+ type PageDataWithComponent = PageData & {
12
+ component?: {
13
+ structure?: ComponentNode;
14
+ interface?: Record<string, PropDefinition>;
15
+ };
16
+ };
17
+
18
+ export interface NodeLocation {
19
+ parent: ComponentNode | PageData | null;
20
+ index: number;
21
+ node: ComponentNode | null;
22
+ }
23
+
24
+ /**
25
+ * Validate that component data has a valid structure
26
+ * @param pageData - PageData to validate
27
+ * @returns true if component has valid structure, false otherwise
28
+ */
29
+ export function validateComponentStructure(pageData: PageData | null): boolean {
30
+ if (!pageData) return false;
31
+
32
+ // Check for page format (has root property)
33
+ if ('root' in pageData && pageData.root) {
34
+ return true;
35
+ }
36
+
37
+ // Check for component format (has component.structure property)
38
+ if ('component' in pageData && pageData.component) {
39
+ if ('structure' in pageData.component && pageData.component.structure) {
40
+ return true;
41
+ }
42
+ // Component exists but structure is missing
43
+ return false;
44
+ }
45
+
46
+ return false;
47
+ }
48
+
49
+ /**
50
+ * Get the root data from pageData (handles both page and component structures
51
+ * Logs warnings when structure is missing for better debugging
52
+ */
53
+ export function getRootData(pageData: PageData | null): ComponentNode | null {
54
+ if (!pageData) return null;
55
+
56
+ // Check for page format (has root property)
57
+ if ('root' in pageData && pageData.root) {
58
+ return pageData.root;
59
+ }
60
+
61
+ // Check for component format (has component.structure property)
62
+ if ('component' in pageData && pageData.component) {
63
+ if ('structure' in pageData.component) {
64
+ const structure = pageData.component.structure;
65
+ if (structure) {
66
+ return structure;
67
+ }
68
+ // Structure field exists but is null/undefined
69
+ } else {
70
+ // Component exists but structure field is missing
71
+ }
72
+ }
73
+
74
+ return null;
75
+ }
76
+
77
+ /**
78
+ * Parse a tree path and return the parent, index, and node
79
+ * Handles paths like: root_0, root_0_children_0, root_0_children_0_children_1
80
+ * Also accepts array paths: [0], [0,0], [0,0,1]
81
+ */
82
+ export function getParentAndIndexFromPath(
83
+ path: Path | string,
84
+ pageData: PageData
85
+ ): NodeLocation | null {
86
+ const rootData = getRootData(pageData);
87
+ if (!rootData) return null;
88
+
89
+ // Convert string to array if needed
90
+ const pathArray = typeof path === 'string' ? stringToPath(path) : path;
91
+
92
+ // Handle root [0] specially
93
+ if (pathArray.length === 1 && pathArray[0] === 0) {
94
+ return {
95
+ parent: pageData,
96
+ index: 0,
97
+ node: rootData
98
+ };
99
+ }
100
+
101
+ // Navigate through path array
102
+ let current: ComponentNode = rootData;
103
+ let parent: ComponentNode | PageData | null = null;
104
+ let lastIndex = -1;
105
+
106
+ // Skip first element (root index 0), navigate through rest
107
+ for (let i = 1; i < pathArray.length; i++) {
108
+ const childIndex = pathArray[i];
109
+
110
+ if (!current || !('children' in current) || !Array.isArray(current.children)) {
111
+ return null;
112
+ }
113
+
114
+ if (isNaN(childIndex) || childIndex < 0 || childIndex >= current.children.length) {
115
+ return null;
116
+ }
117
+
118
+ parent = current;
119
+ const child = current.children[childIndex];
120
+ if (typeof child === 'string') {
121
+ return null;
122
+ }
123
+ current = child;
124
+ lastIndex = childIndex;
125
+
126
+ // If this is the last navigation step, we found our node
127
+ if (i === pathArray.length - 1) {
128
+ return { parent, index: childIndex, node: current };
129
+ }
130
+ }
131
+
132
+ // Return the last valid location if we got here
133
+ if (parent && lastIndex >= 0) {
134
+ return { parent, index: lastIndex, node: current };
135
+ }
136
+
137
+ return null;
138
+ }
139
+
140
+ /**
141
+ * Extract structure without styles/props for tree comparison
142
+ * This allows tree to only regenerate when structure changes, not styles
143
+ */
144
+ export function extractStructureWithoutStyles(node: ComponentNode | string | null | undefined): ComponentNode | string | null | undefined {
145
+ if (!node || typeof node === 'string') {
146
+ return node;
147
+ }
148
+
149
+ if (typeof node !== 'object') {
150
+ return node;
151
+ }
152
+
153
+ // Create a new object with only structure-relevant properties
154
+ const { type } = node;
155
+ const tag = 'tag' in node ? node.tag : undefined;
156
+ const children = 'children' in node ? node.children : undefined;
157
+
158
+ // Recursively process children
159
+ const processedChildren = children
160
+ ? Array.isArray(children)
161
+ ? children.map((child: ComponentNode | string) =>
162
+ typeof child === 'string' ? child : extractStructureWithoutStyles(child) as ComponentNode
163
+ )
164
+ : typeof children === 'string'
165
+ ? children
166
+ : extractStructureWithoutStyles(children) as ComponentNode
167
+ : undefined;
168
+
169
+ return {
170
+ type,
171
+ ...(tag !== undefined ? { tag } : {}),
172
+ ...(processedChildren !== undefined ? { children: processedChildren } : {})
173
+ } as ComponentNode;
174
+ }
175
+
176
+ /**
177
+ * Calculate insertion index based on drop target information
178
+ */
179
+ export function calculateInsertionIndex(
180
+ target: {
181
+ childIndex?: number;
182
+ childAfterItem?: { index: string };
183
+ childBeforeItem?: { index: string };
184
+ linearIndex?: number;
185
+ },
186
+ parentChildren: Array<ComponentNode | string>,
187
+ nodeDataMap: Map<string, ComponentNode | string>
188
+ ): number {
189
+ if (target.childIndex !== undefined && target.childIndex >= 0) {
190
+ // Most reliable: use childIndex directly
191
+ return Math.min(Math.max(0, target.childIndex), parentChildren.length);
192
+ }
193
+
194
+ if (target.childAfterItem?.index) {
195
+ const afterPath = target.childAfterItem.index;
196
+ const afterNode = nodeDataMap.get(afterPath);
197
+ if (afterNode) {
198
+ const afterIndex = parentChildren.findIndex((child: ComponentNode | string) => child === afterNode);
199
+ if (afterIndex >= 0) {
200
+ return afterIndex + 1;
201
+ }
202
+ }
203
+ // Fallback: append
204
+ return parentChildren.length;
205
+ }
206
+
207
+ if (target.childBeforeItem?.index) {
208
+ const beforePath = target.childBeforeItem.index;
209
+ const beforeNode = nodeDataMap.get(beforePath);
210
+ if (beforeNode) {
211
+ const beforeIndex = parentChildren.findIndex((child: ComponentNode | string) => child === beforeNode);
212
+ if (beforeIndex >= 0) {
213
+ return beforeIndex;
214
+ }
215
+ }
216
+ // Fallback: prepend
217
+ return 0;
218
+ }
219
+
220
+ // Last resort: use linearIndex or append
221
+ if (target.linearIndex !== undefined) {
222
+ return Math.min(Math.max(0, target.linearIndex), parentChildren.length);
223
+ }
224
+
225
+ return parentChildren.length;
226
+ }
227
+
228
+ /**
229
+ * Type guard to check if a value is a valid NodeLocation
230
+ */
231
+ export function isValidNodeLocation(location: unknown): location is NodeLocation {
232
+ if (!location || typeof location !== 'object') {
233
+ return false;
234
+ }
235
+
236
+ // Use proper type narrowing instead of assertion
237
+ const loc = location as Record<string, unknown>;
238
+ const hasParent = 'parent' in loc && (loc.parent === null || typeof loc.parent === 'object');
239
+ const hasIndex = 'index' in loc && typeof loc.index === 'number' && loc.index >= 0;
240
+ const hasNode = 'node' in loc && (loc.node === null || typeof loc.node === 'object');
241
+
242
+ return hasParent && hasIndex && hasNode;
243
+ }
244
+
245
+ /**
246
+ * Type guard to check if a value is a valid Path
247
+ */
248
+ export function isValidPath(path: unknown): path is Path {
249
+ if (!Array.isArray(path)) {
250
+ return false;
251
+ }
252
+
253
+ // Path is an array of numbers
254
+ return path.every((item): item is number => typeof item === 'number' && item >= 0);
255
+ }
256
+
257
+ /**
258
+ * Type guard to check if parent is PageData
259
+ */
260
+ export function isPageDataParent(
261
+ parent: ComponentNode | PageData | null
262
+ ): parent is PageData {
263
+ if (!parent) return false;
264
+
265
+ // Check for JSONPage format (has root property)
266
+ if ('root' in parent) return true;
267
+
268
+ // Check for component format - component must be an object, not a string
269
+ if ('component' in parent) {
270
+ return typeof parent.component === 'object' && parent.component !== null;
271
+ }
272
+
273
+ return false;
274
+ }
275
+
276
+ /**
277
+ * Type guard to check if parent is ComponentNode
278
+ */
279
+ export function isComponentNodeParent(
280
+ parent: ComponentNode | PageData | null
281
+ ): parent is ComponentNode {
282
+ if (!parent) return false;
283
+ // Check for 'type' property (all ComponentNodes have it)
284
+ // Then check for specific node types that can have children:
285
+ // - HtmlNode: has 'tag' property
286
+ // - ComponentInstanceNode: has 'component' property
287
+ // - SlotMarker: type === 'slot'
288
+ // - ObjectLinkNode: type === 'object-link'
289
+ // - EmbedNode: type === 'embed'
290
+ return 'type' in parent && (
291
+ 'tag' in parent ||
292
+ 'component' in parent ||
293
+ parent.type === 'slot' ||
294
+ parent.type === 'object-link' ||
295
+ parent.type === 'embed'
296
+ );
297
+ }
298
+
299
+ /**
300
+ * Get children array from parent (handles both PageData and ComponentNode)
301
+ */
302
+ export function getParentChildren(
303
+ parent: ComponentNode | PageData | null
304
+ ): Array<ComponentNode | string> | null {
305
+ if (!parent) return null;
306
+
307
+ if (isPageDataParent(parent)) {
308
+ const rootData = getRootData(parent);
309
+ return rootData && 'children' in rootData ? rootData.children || null : null;
310
+ }
311
+
312
+ if (isComponentNodeParent(parent)) {
313
+ return 'children' in parent ? parent.children || null : null;
314
+ }
315
+
316
+ return null;
317
+ }
318
+
319
+ /**
320
+ * Set children array on parent (handles both PageData and ComponentNode)
321
+ */
322
+ export function setParentChildren(
323
+ parent: ComponentNode | PageData | null,
324
+ children: Array<ComponentNode | string>
325
+ ): void {
326
+ if (!parent) return;
327
+
328
+ if (isPageDataParent(parent)) {
329
+ const rootData = getRootData(parent);
330
+ if (rootData && 'children' in rootData) {
331
+ rootData.children = children;
332
+ }
333
+ } else if (isComponentNodeParent(parent)) {
334
+ if ('children' in parent) {
335
+ parent.children = children;
336
+ }
337
+ }
338
+ }
339
+
@@ -0,0 +1,58 @@
1
+ /**
2
+ * API Types
3
+ */
4
+
5
+ import type { I18nValue } from './components';
6
+ import type { CMSFieldDefinition } from './cms';
7
+
8
+ export interface HMRMessage {
9
+ type: 'hmr:update' | 'hmr:colors-update';
10
+ path?: string;
11
+ }
12
+
13
+ export interface PageListResponse {
14
+ pages: string[];
15
+ }
16
+
17
+ export interface RouteHandler {
18
+ pattern: string | RegExp;
19
+ handler: (req: Request, url: URL) => Response | Promise<Response> | undefined;
20
+ }
21
+
22
+ /** Value that can be either a plain string or an i18n object */
23
+ type StringOrI18n = string | I18nValue;
24
+
25
+ /** CMS configuration embedded in page meta (includes full schema) */
26
+ export interface PageCmsConfig {
27
+ /** Unique identifier for the collection */
28
+ id: string;
29
+ /** Display name for the collection */
30
+ name: string;
31
+ /** Which field to use for URL slug */
32
+ slugField: string;
33
+ /** URL pattern, e.g., "/blog/{{slug}}" */
34
+ urlPattern: string;
35
+ /** Field definitions */
36
+ fields: Record<string, CMSFieldDefinition>;
37
+ }
38
+
39
+ /**
40
+ * Page metadata structure
41
+ * Text fields can be either plain strings or i18n objects
42
+ */
43
+ export interface PageMetaData {
44
+ title?: StringOrI18n;
45
+ description?: StringOrI18n;
46
+ keywords?: StringOrI18n;
47
+ ogTitle?: StringOrI18n;
48
+ ogDescription?: StringOrI18n;
49
+ ogImage?: string;
50
+ ogType?: string;
51
+ /** Translated slugs for each locale, e.g., { "en": "about", "pl": "o-nas" } */
52
+ slugs?: Record<string, string>;
53
+ /** Data source: 'static' (default) or 'cms' */
54
+ source?: 'static' | 'cms';
55
+ /** CMS configuration with embedded schema (required when source: 'cms') */
56
+ cms?: PageCmsConfig;
57
+ }
58
+
@@ -0,0 +1,95 @@
1
+ /**
2
+ * CMS Types
3
+ * Defines schema structure and content items for CMS collections
4
+ */
5
+
6
+ /** Field types supported by CMS */
7
+ export type CMSFieldType =
8
+ | 'string' // Single-line text
9
+ | 'text' // Multi-line text / rich text
10
+ | 'number' // Numeric value
11
+ | 'boolean' // True/false
12
+ | 'image' // Image URL/path
13
+ | 'date' // ISO date string
14
+ | 'select' // Single selection from options
15
+ | 'reference' // Reference to another collection item
16
+ | 'i18n' // Internationalized text (single-line)
17
+ | 'i18n-text'; // Internationalized text (multi-line)
18
+
19
+ /** Single field definition in a schema */
20
+ export interface CMSFieldDefinition {
21
+ type: CMSFieldType;
22
+ required?: boolean;
23
+ default?: unknown;
24
+ label?: string; // Display label in editor
25
+ description?: string; // Help text
26
+ options?: string[]; // For 'select' type
27
+ collection?: string; // For 'reference' type
28
+ }
29
+
30
+ /** CMS Schema - defines collection structure (embedded in page meta.cms) */
31
+ export interface CMSSchema {
32
+ /** Unique identifier for the collection */
33
+ id: string;
34
+ /** Display name for the collection */
35
+ name: string;
36
+ /** Which field to use for URL slug */
37
+ slugField: string;
38
+ /** URL pattern, e.g., "/blog/{{slug}}" */
39
+ urlPattern: string;
40
+ /** Field definitions */
41
+ fields: Record<string, CMSFieldDefinition>;
42
+ }
43
+
44
+ /** CMS Item - actual content entry (stored as individual JSON file) */
45
+ export interface CMSItem {
46
+ _id: string;
47
+ _slug?: string; // Derived from filename (e.g., hello-world.json -> "hello-world")
48
+ _createdAt?: string; // ISO timestamp
49
+ _updatedAt?: string; // ISO timestamp
50
+ [field: string]: unknown;
51
+ }
52
+
53
+ /** CMS Collection - schema + items */
54
+ export interface CMSCollection {
55
+ schema: CMSSchema;
56
+ items: CMSItem[];
57
+ }
58
+
59
+ /** Resolved CMS route match */
60
+ export interface CMSRouteMatch {
61
+ collection: string;
62
+ slug: string;
63
+ item: CMSItem;
64
+ pagePath: string; // Path to the template page file
65
+ }
66
+
67
+ /** Filter operators for CMS queries */
68
+ export type CMSFilterOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'contains' | 'in';
69
+
70
+ /** Single filter condition */
71
+ export interface CMSFilterCondition {
72
+ field: string;
73
+ operator?: CMSFilterOperator; // defaults to 'eq'
74
+ value: unknown;
75
+ }
76
+
77
+ /** Sort configuration */
78
+ export interface CMSSortConfig {
79
+ field: string;
80
+ order?: 'asc' | 'desc'; // defaults to 'asc'
81
+ }
82
+
83
+ /** CMSList query configuration */
84
+ export interface CMSListQuery {
85
+ collection: string;
86
+ filter?: CMSFilterCondition | CMSFilterCondition[] | Record<string, unknown>;
87
+ sort?: CMSSortConfig | CMSSortConfig[];
88
+ limit?: number;
89
+ offset?: number;
90
+ }
91
+
92
+ /** CMSList component props (in JSON page) */
93
+ export interface CMSListProps extends CMSListQuery {
94
+ /** Children are repeated for each item with {{item.field}} context */
95
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Color Variables Types
3
+ * Handles color variable definitions and configuration
4
+ */
5
+
6
+ /**
7
+ * Color variables configuration
8
+ * Maps semantic color names to their hex/rgb values
9
+ */
10
+ export interface ColorVariables {
11
+ colors: Record<string, string>;
12
+ }
13
+
14
+ /**
15
+ * Theme configuration with color set and metadata
16
+ */
17
+ export interface Theme {
18
+ label: string;
19
+ colors: Record<string, string>;
20
+ }
21
+
22
+ /**
23
+ * Theme configuration file structure
24
+ * Supports multiple named themes with a default theme
25
+ */
26
+ export interface ThemeConfig {
27
+ default: string;
28
+ themes: Record<string, Theme>;
29
+ }
30
+
31
+ /**
32
+ * Color variable entry for editor display
33
+ */
34
+ export interface ColorVariableEntry {
35
+ name: string;
36
+ value: string;
37
+ }
38
+
39
+ /**
40
+ * Theme entry for theme selector
41
+ */
42
+ export interface ThemeEntry {
43
+ name: string;
44
+ label: string;
45
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Component Definition Types
3
+ * Improved type safety with stricter types
4
+ */
5
+
6
+ import type { ComponentNode } from './nodes';
7
+
8
+ /**
9
+ * Prop type definitions
10
+ */
11
+ export type PropType = 'string' | 'select' | 'boolean' | 'number' | 'link' | 'file';
12
+
13
+ /**
14
+ * Internationalization (i18n) value object
15
+ * Keys are locale codes (e.g., 'en', 'pl', 'de')
16
+ */
17
+ export interface I18nValue {
18
+ _i18n: true;
19
+ [locale: string]: string | true; // true is for the _i18n marker itself
20
+ }
21
+
22
+ /**
23
+ * Locale configuration with metadata
24
+ */
25
+ export interface LocaleConfig {
26
+ code: string; // URL prefix & translation key (e.g., "en", "pl")
27
+ name: string; // English name for admin UI (e.g., "Polish")
28
+ nativeName: string; // Native name for public UI (e.g., "Polski")
29
+ langTag: string; // BCP 47 language tag for SEO (e.g., "pl-PL")
30
+ icon?: string; // Optional flag icon path (e.g., "/icons/flag-en.svg")
31
+ }
32
+
33
+ /**
34
+ * Internationalization configuration
35
+ */
36
+ export interface I18nConfig {
37
+ defaultLocale: string;
38
+ locales: LocaleConfig[];
39
+ }
40
+
41
+ /**
42
+ * Link prop value type
43
+ */
44
+ export interface LinkPropValue {
45
+ href: string;
46
+ target?: '_blank';
47
+ }
48
+
49
+ /**
50
+ * Prop definition with improved type safety
51
+ */
52
+ export interface PropDefinition {
53
+ type: PropType;
54
+ default?: string | number | boolean | I18nValue | LinkPropValue;
55
+ options?: readonly string[]; // Required for "select" type
56
+ accept?: string; // For "file" type: MIME pattern like "image/*", "video/*"
57
+ }
58
+
59
+ /**
60
+ * Structured component definition
61
+ */
62
+ export interface StructuredComponentDefinition {
63
+ interface?: Record<string, PropDefinition>;
64
+ structure?: ComponentNode;
65
+ javascript?: string; // Vanilla JS code to be rendered at end of HTML
66
+ css?: string; // CSS code to be rendered in <style> tag in <head>
67
+ category?: string; // Component category for organization
68
+ }
69
+
70
+ /**
71
+ * Component definition
72
+ * Supports both new format (just component) and legacy format (type/props/children/component)
73
+ */
74
+ export interface ComponentDefinition {
75
+ type?: string; // Legacy format
76
+ props?: Record<string, unknown>; // Legacy format
77
+ children?: unknown[]; // Legacy format
78
+ component: StructuredComponentDefinition;
79
+ }
80
+
81
+ /**
82
+ * Line range for element line number tracking
83
+ */
84
+ export interface LineRange {
85
+ startLine: number;
86
+ endLine: number;
87
+ }
88
+
89
+ /**
90
+ * JSON page structure
91
+ */
92
+ export interface JSONPage {
93
+ meta?: import('./api').PageMetaData;
94
+ components?: Record<string, ComponentDefinition>;
95
+ root?: ComponentNode;
96
+ _lineMap?: Record<string, LineRange>;
97
+ }
98
+
99
+ /**
100
+ * Page data type that can be either a JSONPage or a component definition structure
101
+ */
102
+ export type PageData = JSONPage | {
103
+ component: {
104
+ structure?: ComponentNode;
105
+ interface?: Record<string, PropDefinition>;
106
+ javascript?: string;
107
+ css?: string;
108
+ }
109
+ };
110
+
111
+ /**
112
+ * Page data with component structure (for type narrowing)
113
+ */
114
+ export interface PageDataWithComponent {
115
+ component: {
116
+ structure?: ComponentNode;
117
+ interface?: Record<string, PropDefinition>;
118
+ javascript?: string;
119
+ css?: string;
120
+ };
121
+ }