meno-core 1.0.53 → 1.1.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 (415) hide show
  1. package/.claude/settings.local.json +1 -3
  2. package/bin/cli.ts +66 -261
  3. package/build-astro.ts +150 -128
  4. package/dist/bin/cli.js +42 -185
  5. package/dist/bin/cli.js.map +2 -2
  6. package/dist/chunks/chunk-2AR55GYH.js +42 -0
  7. package/dist/chunks/chunk-2AR55GYH.js.map +7 -0
  8. package/dist/chunks/{chunk-JGWFTO6P.js → chunk-2IIQK7T3.js} +2624 -1389
  9. package/dist/chunks/chunk-2IIQK7T3.js.map +7 -0
  10. package/dist/chunks/chunk-4ZRU52J2.js +169 -0
  11. package/dist/chunks/chunk-4ZRU52J2.js.map +7 -0
  12. package/dist/chunks/{chunk-UB44F4Z2.js → chunk-5X4HCN7J.js} +5 -7
  13. package/dist/chunks/chunk-5X4HCN7J.js.map +7 -0
  14. package/dist/chunks/{chunk-WQFG7PAH.js → chunk-7CCVOL5M.js} +15 -19
  15. package/dist/chunks/chunk-7CCVOL5M.js.map +7 -0
  16. package/dist/chunks/{chunk-YBLHKYFF.js → chunk-AR7LHIFY.js} +8 -2
  17. package/dist/chunks/chunk-AR7LHIFY.js.map +7 -0
  18. package/dist/chunks/{chunk-O3NAGJP4.js → chunk-NUP7H7D3.js} +135 -41
  19. package/dist/chunks/chunk-NUP7H7D3.js.map +7 -0
  20. package/dist/chunks/{chunk-X754AHS5.js → chunk-QWTQZHG3.js} +194 -90
  21. package/dist/chunks/chunk-QWTQZHG3.js.map +7 -0
  22. package/dist/chunks/{chunk-H3GJ4H2U.js → chunk-XTKNX4FW.js} +216 -87
  23. package/dist/chunks/chunk-XTKNX4FW.js.map +7 -0
  24. package/dist/chunks/{fs-JGINUXGL.js → fs-G3ANWEFP.js} +2 -2
  25. package/dist/lib/client/index.js +1003 -475
  26. package/dist/lib/client/index.js.map +3 -3
  27. package/dist/lib/server/index.js +14393 -6371
  28. package/dist/lib/server/index.js.map +4 -4
  29. package/dist/lib/shared/index.js +342 -101
  30. package/dist/lib/shared/index.js.map +4 -4
  31. package/dist/lib/shared/richtext/index.js +1 -1
  32. package/dist/lib/test-utils/index.js +64 -80
  33. package/dist/lib/test-utils/index.js.map +2 -2
  34. package/entries/client-router.tsx +10 -8
  35. package/lib/client/ClientInitializer.ts +8 -8
  36. package/lib/client/ErrorBoundary.test.tsx +246 -186
  37. package/lib/client/ErrorBoundary.tsx +183 -132
  38. package/lib/client/componentRegistry.test.ts +96 -108
  39. package/lib/client/componentRegistry.ts +1 -3
  40. package/lib/client/contexts/ThemeContext.tsx +3 -2
  41. package/lib/client/core/ComponentBuilder.test.ts +663 -560
  42. package/lib/client/core/ComponentBuilder.ts +427 -155
  43. package/lib/client/core/ComponentRenderer.test.tsx +2 -4
  44. package/lib/client/core/ComponentRenderer.tsx +46 -33
  45. package/lib/client/core/builders/embedBuilder.ts +242 -54
  46. package/lib/client/core/builders/linkBuilder.ts +72 -45
  47. package/lib/client/core/builders/linkNodeBuilder.ts +79 -54
  48. package/lib/client/core/builders/listBuilder.ts +155 -89
  49. package/lib/client/core/builders/localeListBuilder.ts +97 -62
  50. package/lib/client/core/builders/types.ts +5 -5
  51. package/lib/client/core/cmsTemplateProcessor.ts +8 -9
  52. package/lib/client/elementRegistry.ts +3 -3
  53. package/lib/client/fontFamiliesService.test.ts +2 -10
  54. package/lib/client/fontFamiliesService.ts +3 -3
  55. package/lib/client/hmr/HMRManager.tsx +47 -30
  56. package/lib/client/hmrCssReload.ts +49 -14
  57. package/lib/client/hmrWebSocket.ts +14 -19
  58. package/lib/client/hooks/useColorVariables.test.ts +21 -21
  59. package/lib/client/hooks/useColorVariables.ts +23 -18
  60. package/lib/client/hooks/usePropertyAutocomplete.ts +4 -6
  61. package/lib/client/hooks/useVariables.ts +6 -4
  62. package/lib/client/hydration/HydrationUtils.test.ts +24 -25
  63. package/lib/client/hydration/HydrationUtils.ts +3 -4
  64. package/lib/client/i18nConfigService.test.ts +2 -7
  65. package/lib/client/i18nConfigService.ts +2 -2
  66. package/lib/client/meno-filter/MenoFilter.test.ts +47 -49
  67. package/lib/client/meno-filter/MenoFilter.ts +14 -15
  68. package/lib/client/meno-filter/bindings.ts +18 -43
  69. package/lib/client/meno-filter/init.ts +4 -4
  70. package/lib/client/meno-filter/renderer.ts +30 -32
  71. package/lib/client/meno-filter/script.generated.ts +1 -3
  72. package/lib/client/meno-filter/ui.ts +4 -6
  73. package/lib/client/meno-filter/updates.ts +18 -23
  74. package/lib/client/meno-filter/utils.ts +1 -1
  75. package/lib/client/navigation.test.ts +174 -178
  76. package/lib/client/navigation.ts +0 -1
  77. package/lib/client/responsiveStyleResolver.knownTokens.test.ts +71 -0
  78. package/lib/client/responsiveStyleResolver.test.ts +234 -232
  79. package/lib/client/responsiveStyleResolver.ts +74 -20
  80. package/lib/client/routing/RouteLoader.test.ts +24 -25
  81. package/lib/client/routing/RouteLoader.ts +27 -35
  82. package/lib/client/routing/Router.tsx +210 -153
  83. package/lib/client/scripts/ScriptExecutor.test.ts +157 -158
  84. package/lib/client/scripts/ScriptExecutor.ts +18 -36
  85. package/lib/client/services/PrefetchService.test.ts +3 -3
  86. package/lib/client/services/PrefetchService.ts +23 -29
  87. package/lib/client/styleProcessor.test.ts +9 -9
  88. package/lib/client/styleProcessor.ts +24 -15
  89. package/lib/client/styles/StyleInjector.test.ts +122 -115
  90. package/lib/client/styles/StyleInjector.ts +10 -7
  91. package/lib/client/styles/UtilityClassCollector.ts +62 -28
  92. package/lib/client/styles/cspNonce.test.ts +2 -5
  93. package/lib/client/templateEngine.test.ts +554 -488
  94. package/lib/client/templateEngine.ts +203 -73
  95. package/lib/client/theme.test.ts +1 -1
  96. package/lib/client/theme.ts +0 -1
  97. package/lib/client/utils/toast.ts +0 -1
  98. package/lib/server/__integration__/api-routes.test.ts +8 -4
  99. package/lib/server/__integration__/cms-integration.test.ts +1 -4
  100. package/lib/server/__integration__/server-lifecycle.test.ts +4 -5
  101. package/lib/server/__integration__/ssr-rendering.test.ts +47 -37
  102. package/lib/server/__integration__/static-assets.test.ts +3 -3
  103. package/lib/server/__integration__/test-helpers.ts +87 -74
  104. package/lib/server/ab/generateFunctions.ts +12 -10
  105. package/lib/server/astro/cmsPageEmitter.ts +26 -42
  106. package/lib/server/astro/componentEmitter.ts +35 -43
  107. package/lib/server/astro/cssCollector.ts +10 -26
  108. package/lib/server/astro/nodeToAstro.test.ts +1754 -34
  109. package/lib/server/astro/nodeToAstro.ts +232 -216
  110. package/lib/server/astro/normalizeOrphanTemplateProps.test.ts +115 -119
  111. package/lib/server/astro/normalizeOrphanTemplateProps.ts +11 -18
  112. package/lib/server/astro/pageEmitter.ts +9 -13
  113. package/lib/server/astro/tailwindMapper.test.ts +10 -37
  114. package/lib/server/astro/tailwindMapper.ts +33 -40
  115. package/lib/server/astro/templateTransformer.ts +17 -20
  116. package/lib/server/createServer.ts +7 -19
  117. package/lib/server/cssGenerator.test.ts +35 -44
  118. package/lib/server/cssGenerator.ts +8 -19
  119. package/lib/server/fileWatcher.test.ts +164 -13
  120. package/lib/server/fileWatcher.ts +209 -124
  121. package/lib/server/index.ts +14 -17
  122. package/lib/server/jsonLoader.test.ts +39 -2
  123. package/lib/server/jsonLoader.ts +39 -34
  124. package/lib/server/middleware/cors.test.ts +21 -21
  125. package/lib/server/middleware/cors.ts +28 -4
  126. package/lib/server/middleware/errorHandler.test.ts +7 -5
  127. package/lib/server/middleware/errorHandler.ts +3 -8
  128. package/lib/server/middleware/index.ts +0 -1
  129. package/lib/server/middleware/logger.test.ts +10 -8
  130. package/lib/server/middleware/logger.ts +17 -29
  131. package/lib/server/migrateTemplates.ts +3 -3
  132. package/lib/server/pageCache.test.ts +76 -77
  133. package/lib/server/pageCache.ts +0 -1
  134. package/lib/server/projectContext.ts +5 -4
  135. package/lib/server/providers/fileSystemCMSProvider.test.ts +142 -99
  136. package/lib/server/providers/fileSystemCMSProvider.ts +62 -38
  137. package/lib/server/routes/api/cms.test.ts +26 -14
  138. package/lib/server/routes/api/cms.ts +9 -14
  139. package/lib/server/routes/api/colors.test.ts +2 -2
  140. package/lib/server/routes/api/components.ts +31 -35
  141. package/lib/server/routes/api/config.ts +3 -4
  142. package/lib/server/routes/api/core-routes.ts +49 -76
  143. package/lib/server/routes/api/enums.test.ts +2 -2
  144. package/lib/server/routes/api/functions.ts +9 -17
  145. package/lib/server/routes/api/index.ts +3 -6
  146. package/lib/server/routes/api/pages.ts +8 -11
  147. package/lib/server/routes/api/shared.test.ts +1 -1
  148. package/lib/server/routes/api/shared.ts +12 -9
  149. package/lib/server/routes/api/variables.test.ts +3 -5
  150. package/lib/server/routes/api/variables.ts +1 -1
  151. package/lib/server/routes/index.ts +25 -28
  152. package/lib/server/routes/pages.ts +35 -31
  153. package/lib/server/routes/static.ts +17 -6
  154. package/lib/server/runtime/bundler.ts +98 -61
  155. package/lib/server/runtime/fs.ts +16 -26
  156. package/lib/server/runtime/httpServer.ts +8 -12
  157. package/lib/server/services/ColorService.test.ts +3 -3
  158. package/lib/server/services/ColorService.ts +37 -29
  159. package/lib/server/services/EnumService.test.ts +4 -8
  160. package/lib/server/services/EnumService.ts +11 -3
  161. package/lib/server/services/VariableService.test.ts +8 -12
  162. package/lib/server/services/VariableService.ts +11 -3
  163. package/lib/server/services/cmsService.test.ts +158 -120
  164. package/lib/server/services/cmsService.ts +36 -146
  165. package/lib/server/services/componentService.test.ts +125 -45
  166. package/lib/server/services/componentService.ts +70 -46
  167. package/lib/server/services/configService.test.ts +22 -45
  168. package/lib/server/services/configService.ts +50 -27
  169. package/lib/server/services/fileWatcherService.ts +42 -8
  170. package/lib/server/services/index.ts +0 -1
  171. package/lib/server/services/pageService.test.ts +8 -10
  172. package/lib/server/services/pageService.ts +32 -18
  173. package/lib/server/ssr/attributeBuilder.ts +18 -8
  174. package/lib/server/ssr/buildErrorOverlay.ts +11 -12
  175. package/lib/server/ssr/clientDataInjector.ts +7 -21
  176. package/lib/server/ssr/cmsSSRProcessor.ts +4 -8
  177. package/lib/server/ssr/cssCollector.ts +1 -1
  178. package/lib/server/ssr/errorOverlay.test.ts +1 -1
  179. package/lib/server/ssr/errorOverlay.ts +3 -9
  180. package/lib/server/ssr/htmlGenerator.nonce.test.ts +9 -14
  181. package/lib/server/ssr/htmlGenerator.test.ts +120 -44
  182. package/lib/server/ssr/htmlGenerator.ts +126 -91
  183. package/lib/server/ssr/imageMetadata.test.ts +3 -1
  184. package/lib/server/ssr/imageMetadata.ts +27 -21
  185. package/lib/server/ssr/jsCollector.test.ts +8 -18
  186. package/lib/server/ssr/jsCollector.ts +3 -8
  187. package/lib/server/ssr/liveReloadIntegration.test.ts +76 -36
  188. package/lib/server/ssr/metaTagGenerator.ts +2 -2
  189. package/lib/server/ssr/ssrRenderer.branches.test.ts +1103 -0
  190. package/lib/server/ssr/ssrRenderer.test.ts +316 -256
  191. package/lib/server/ssr/ssrRenderer.ts +795 -243
  192. package/lib/server/ssrRenderer.test.ts +1048 -954
  193. package/lib/server/utils/jsonLineMapper.test.ts +28 -28
  194. package/lib/server/utils/jsonLineMapper.ts +7 -7
  195. package/lib/server/validateStyleCoverage.ts +18 -21
  196. package/lib/server/websocketManager.ts +28 -19
  197. package/lib/shared/attributeNodeUtils.test.ts +15 -15
  198. package/lib/shared/attributeNodeUtils.ts +5 -12
  199. package/lib/shared/breakpoints.test.ts +5 -5
  200. package/lib/shared/breakpoints.ts +4 -11
  201. package/lib/shared/cmsQuery.test.ts +81 -0
  202. package/lib/shared/cmsQuery.ts +92 -0
  203. package/lib/shared/cmsQueryParser.test.ts +50 -42
  204. package/lib/shared/cmsQueryParser.ts +60 -38
  205. package/lib/shared/colorConversions.ts +9 -9
  206. package/lib/shared/colorVariableUtils.test.ts +27 -5
  207. package/lib/shared/colorVariableUtils.ts +21 -12
  208. package/lib/shared/componentRefs.ts +1 -5
  209. package/lib/shared/constants.test.ts +3 -3
  210. package/lib/shared/constants.ts +8 -8
  211. package/lib/shared/cssGeneration.test.ts +285 -144
  212. package/lib/shared/cssGeneration.ts +217 -531
  213. package/lib/shared/cssNamedColors.ts +152 -30
  214. package/lib/shared/cssProperties.test.ts +69 -70
  215. package/lib/shared/cssProperties.ts +298 -114
  216. package/lib/shared/elementClassName.test.ts +109 -109
  217. package/lib/shared/elementClassName.ts +3 -3
  218. package/lib/shared/elementUtils.ts +12 -16
  219. package/lib/shared/errorLogger.test.ts +12 -12
  220. package/lib/shared/errorLogger.ts +2 -10
  221. package/lib/shared/errors.test.ts +2 -13
  222. package/lib/shared/errors.ts +2 -8
  223. package/lib/shared/expressionEvaluator.test.ts +145 -0
  224. package/lib/shared/expressionEvaluator.ts +52 -24
  225. package/lib/shared/fontLoader.test.ts +32 -18
  226. package/lib/shared/fontLoader.ts +2 -2
  227. package/lib/shared/friendlyError.ts +2 -3
  228. package/lib/shared/gradientUtils.test.ts +3 -7
  229. package/lib/shared/gradientUtils.ts +17 -14
  230. package/lib/shared/hrefRefs.test.ts +2 -2
  231. package/lib/shared/hrefRefs.ts +2 -10
  232. package/lib/shared/i18n.test.ts +4 -4
  233. package/lib/shared/i18n.ts +18 -38
  234. package/lib/shared/index.ts +27 -0
  235. package/lib/shared/inlineSvgStyleRules.ts +14 -13
  236. package/lib/shared/interactiveStyleMappings.test.ts +32 -54
  237. package/lib/shared/interactiveStyleMappings.ts +11 -24
  238. package/lib/shared/interactiveStyles.test.ts +165 -188
  239. package/lib/shared/interfaces/contentProvider.ts +1 -1
  240. package/lib/shared/itemTemplateUtils.test.ts +11 -17
  241. package/lib/shared/itemTemplateUtils.ts +27 -39
  242. package/lib/shared/jsonRepair.ts +16 -10
  243. package/lib/shared/libraryLoader.test.ts +21 -55
  244. package/lib/shared/libraryLoader.ts +7 -22
  245. package/lib/shared/linkUtils.ts +1 -1
  246. package/lib/shared/logger.test.ts +66 -0
  247. package/lib/shared/logger.ts +94 -0
  248. package/lib/shared/markdown.ts +25 -0
  249. package/lib/shared/netlifyLocale404.test.ts +179 -0
  250. package/lib/shared/netlifyLocale404.ts +110 -0
  251. package/lib/shared/nodeUtils.test.ts +28 -16
  252. package/lib/shared/nodeUtils.ts +76 -23
  253. package/lib/shared/pathArrayUtils.test.ts +1 -2
  254. package/lib/shared/pathArrayUtils.ts +1 -1
  255. package/lib/shared/pathSecurity.ts +2 -2
  256. package/lib/shared/pathUtils.test.ts +4 -6
  257. package/lib/shared/pathUtils.ts +42 -49
  258. package/lib/shared/paths/Path.test.ts +2 -2
  259. package/lib/shared/paths/Path.ts +0 -1
  260. package/lib/shared/paths/PathConverter.test.ts +1 -1
  261. package/lib/shared/paths/PathConverter.ts +17 -20
  262. package/lib/shared/paths/PathUtils.ts +13 -13
  263. package/lib/shared/paths/PathValidator.test.ts +2 -15
  264. package/lib/shared/paths/PathValidator.ts +12 -10
  265. package/lib/shared/paths/index.ts +1 -2
  266. package/lib/shared/permissions.test.ts +168 -0
  267. package/lib/shared/permissions.ts +162 -0
  268. package/lib/shared/propResolver.test.ts +240 -244
  269. package/lib/shared/propResolver.ts +15 -26
  270. package/lib/shared/pxToRem.test.ts +7 -6
  271. package/lib/shared/pxToRem.ts +2 -5
  272. package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +12 -6
  273. package/lib/shared/registry/ClientRegistry.ts +0 -2
  274. package/lib/shared/registry/ComponentRegistry.test.ts +46 -32
  275. package/lib/shared/registry/ComponentRegistry.ts +12 -9
  276. package/lib/shared/registry/NodeTypeDefinition.ts +16 -9
  277. package/lib/shared/registry/RegistryManager.ts +1 -2
  278. package/lib/shared/registry/SSRRegistry.ts +0 -2
  279. package/lib/shared/registry/createNodeType.ts +7 -9
  280. package/lib/shared/registry/defineNodeType.ts +2 -6
  281. package/lib/shared/registry/fieldPresets.ts +3 -1
  282. package/lib/shared/registry/index.ts +0 -1
  283. package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +14 -15
  284. package/lib/shared/registry/nodeTypes/CustomNodeType.ts +103 -0
  285. package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +18 -11
  286. package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +48 -19
  287. package/lib/shared/registry/nodeTypes/IslandNodeType.ts +129 -0
  288. package/lib/shared/registry/nodeTypes/LinkNodeType.ts +21 -19
  289. package/lib/shared/registry/nodeTypes/ListNodeType.ts +117 -77
  290. package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +27 -21
  291. package/lib/shared/registry/nodeTypes/MarkdownNodeType.ts +78 -0
  292. package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +12 -8
  293. package/lib/shared/registry/nodeTypes/index.ts +26 -3
  294. package/lib/shared/responsiveScaling.test.ts +16 -34
  295. package/lib/shared/responsiveScaling.ts +61 -42
  296. package/lib/shared/responsiveStyleUtils.test.ts +0 -1
  297. package/lib/shared/responsiveStyleUtils.ts +11 -13
  298. package/lib/shared/richtext/htmlToTiptap.test.ts +139 -130
  299. package/lib/shared/richtext/htmlToTiptap.ts +2 -4
  300. package/lib/shared/richtext/tiptapToHtml.test.ts +5 -6
  301. package/lib/shared/richtext/types.ts +1 -8
  302. package/lib/shared/slugTranslator.test.ts +13 -13
  303. package/lib/shared/slugTranslator.ts +12 -16
  304. package/lib/shared/slugify.ts +9 -15
  305. package/lib/shared/styleNodeUtils.test.ts +8 -8
  306. package/lib/shared/styleNodeUtils.ts +18 -20
  307. package/lib/shared/styleUtils.test.ts +87 -61
  308. package/lib/shared/styleUtils.ts +5 -6
  309. package/lib/shared/themeDefaults.test.ts +11 -14
  310. package/lib/shared/themeDefaults.ts +3 -4
  311. package/lib/shared/tree/PathBuilder.test.ts +63 -145
  312. package/lib/shared/tree/PathBuilder.ts +59 -47
  313. package/lib/shared/treePathUtils.test.ts +51 -12
  314. package/lib/shared/treePathUtils.ts +91 -60
  315. package/lib/shared/types/api.ts +105 -8
  316. package/lib/shared/types/cms.test.ts +34 -0
  317. package/lib/shared/types/cms.ts +90 -34
  318. package/lib/shared/types/comment.ts +49 -16
  319. package/lib/shared/types/components.ts +51 -25
  320. package/lib/shared/types/errors.test.ts +1 -6
  321. package/lib/shared/types/errors.ts +3 -7
  322. package/lib/shared/types/experiments.ts +28 -28
  323. package/lib/shared/types/index.ts +25 -2
  324. package/lib/shared/types/permissions.ts +139 -0
  325. package/lib/shared/types/styles.ts +0 -1
  326. package/lib/shared/types/variables.test.ts +4 -13
  327. package/lib/shared/types/variables.ts +51 -30
  328. package/lib/shared/types.ts +1 -2
  329. package/lib/shared/utilityClassConfig.ts +751 -321
  330. package/lib/shared/utilityClassMapper.durability.test.ts +117 -0
  331. package/lib/shared/utilityClassMapper.test.ts +433 -95
  332. package/lib/shared/utilityClassMapper.ts +322 -249
  333. package/lib/shared/utilityClassNames.test.ts +38 -0
  334. package/lib/shared/utilityClassNames.ts +458 -0
  335. package/lib/shared/utils.test.ts +2 -10
  336. package/lib/shared/utils.ts +19 -10
  337. package/lib/shared/validation/cmsValidators.ts +2 -1
  338. package/lib/shared/validation/commentValidators.test.ts +53 -0
  339. package/lib/shared/validation/commentValidators.ts +12 -1
  340. package/lib/shared/validation/index.ts +1 -0
  341. package/lib/shared/validation/permissionsValidators.test.ts +49 -0
  342. package/lib/shared/validation/permissionsValidators.ts +49 -0
  343. package/lib/shared/validation/propValidator.test.ts +19 -21
  344. package/lib/shared/validation/propValidator.ts +13 -18
  345. package/lib/shared/validation/schemas.test.ts +64 -32
  346. package/lib/shared/validation/schemas.ts +616 -345
  347. package/lib/shared/validation/validators.test.ts +3 -8
  348. package/lib/shared/validation/validators.ts +89 -68
  349. package/lib/shared/viewportUnits.integration.test.ts +14 -10
  350. package/lib/shared/viewportUnits.test.ts +24 -23
  351. package/lib/shared/viewportUnits.ts +12 -5
  352. package/lib/test-utils/dom-setup.ts +1 -1
  353. package/lib/test-utils/factories/ConsoleMockFactory.ts +10 -14
  354. package/lib/test-utils/factories/DomMockFactory.ts +17 -23
  355. package/lib/test-utils/factories/EventMockFactory.ts +7 -13
  356. package/lib/test-utils/factories/FetchMockFactory.ts +39 -57
  357. package/lib/test-utils/factories/ServerMockFactory.ts +6 -10
  358. package/lib/test-utils/factories/StoreMockFactory.ts +18 -29
  359. package/lib/test-utils/fixtures.ts +45 -45
  360. package/lib/test-utils/helpers/asyncHelpers.test.ts +15 -18
  361. package/lib/test-utils/helpers/asyncHelpers.ts +11 -20
  362. package/lib/test-utils/helpers.ts +1 -5
  363. package/lib/test-utils/index.ts +0 -4
  364. package/lib/test-utils/mockFactories.ts +13 -19
  365. package/lib/test-utils/mocks.ts +6 -4
  366. package/package.json +3 -5
  367. package/scripts/build-for-publish.mjs +2 -4
  368. package/scripts/build-meno-filter.ts +4 -7
  369. package/tsconfig.json +1 -0
  370. package/vite.config.ts +4 -4
  371. package/build-next.ts +0 -1361
  372. package/build-static.test.ts +0 -424
  373. package/build-static.ts +0 -1074
  374. package/dist/build-static.js +0 -36
  375. package/dist/chunks/chunk-2MHDV5BF.js +0 -251
  376. package/dist/chunks/chunk-2MHDV5BF.js.map +0 -7
  377. package/dist/chunks/chunk-AZQYF6KE.js +0 -559
  378. package/dist/chunks/chunk-AZQYF6KE.js.map +0 -7
  379. package/dist/chunks/chunk-GZHGVVW3.js +0 -322
  380. package/dist/chunks/chunk-GZHGVVW3.js.map +0 -7
  381. package/dist/chunks/chunk-H3GJ4H2U.js.map +0 -7
  382. package/dist/chunks/chunk-I7YIGZXT.js +0 -467
  383. package/dist/chunks/chunk-I7YIGZXT.js.map +0 -7
  384. package/dist/chunks/chunk-IGYR22T6.js +0 -6192
  385. package/dist/chunks/chunk-IGYR22T6.js.map +0 -7
  386. package/dist/chunks/chunk-JGP5A3Y5.js +0 -1136
  387. package/dist/chunks/chunk-JGP5A3Y5.js.map +0 -7
  388. package/dist/chunks/chunk-JGWFTO6P.js.map +0 -7
  389. package/dist/chunks/chunk-O3NAGJP4.js.map +0 -7
  390. package/dist/chunks/chunk-QB2LNO4W.js +0 -77
  391. package/dist/chunks/chunk-QB2LNO4W.js.map +0 -7
  392. package/dist/chunks/chunk-R6XHAFBF.js +0 -3821
  393. package/dist/chunks/chunk-R6XHAFBF.js.map +0 -7
  394. package/dist/chunks/chunk-UB44F4Z2.js.map +0 -7
  395. package/dist/chunks/chunk-WQFG7PAH.js.map +0 -7
  396. package/dist/chunks/chunk-X754AHS5.js.map +0 -7
  397. package/dist/chunks/chunk-YBLHKYFF.js.map +0 -7
  398. package/dist/chunks/configService-R3OGU2UD.js +0 -13
  399. package/dist/chunks/configService-R3OGU2UD.js.map +0 -7
  400. package/dist/chunks/constants-STK2YBIW.js +0 -46
  401. package/dist/chunks/constants-STK2YBIW.js.map +0 -7
  402. package/dist/chunks/fs-JGINUXGL.js.map +0 -7
  403. package/dist/entries/server-router.js +0 -67
  404. package/dist/entries/server-router.js.map +0 -7
  405. package/entries/server-router.tsx +0 -81
  406. package/lib/server/providers/fileSystemPageProvider.ts +0 -160
  407. package/lib/server/webflow/buildWebflow.ts +0 -635
  408. package/lib/server/webflow/index.ts +0 -23
  409. package/lib/server/webflow/nodeToWebflow.test.ts +0 -3170
  410. package/lib/server/webflow/nodeToWebflow.ts +0 -2435
  411. package/lib/server/webflow/styleMapper.test.ts +0 -389
  412. package/lib/server/webflow/styleMapper.ts +0 -695
  413. package/lib/server/webflow/templateWrapper.ts +0 -49
  414. package/lib/server/webflow/types.ts +0 -396
  415. /package/dist/{build-static.js.map → chunks/fs-G3ANWEFP.js.map} +0 -0
@@ -15,12 +15,31 @@ import { NODE_TYPE } from '../constants';
15
15
  /**
16
16
  * Prop type schema - validates PropType values (excluding list for base definition)
17
17
  */
18
- export const BasePropTypeSchema = z.enum(['string', 'select', 'boolean', 'number', 'link', 'file', 'rich-text', 'embed']);
18
+ export const BasePropTypeSchema = z.enum([
19
+ 'string',
20
+ 'select',
21
+ 'boolean',
22
+ 'number',
23
+ 'link',
24
+ 'file',
25
+ 'rich-text',
26
+ 'embed',
27
+ ]);
19
28
 
20
29
  /**
21
30
  * Full prop type schema including list
22
31
  */
23
- export const PropTypeSchema = z.enum(['string', 'select', 'boolean', 'number', 'link', 'file', 'rich-text', 'embed', 'list']);
32
+ export const PropTypeSchema = z.enum([
33
+ 'string',
34
+ 'select',
35
+ 'boolean',
36
+ 'number',
37
+ 'link',
38
+ 'file',
39
+ 'rich-text',
40
+ 'embed',
41
+ 'list',
42
+ ]);
24
43
 
25
44
  /**
26
45
  * `_i18n` value object schema. Used wherever a localizable string can appear:
@@ -34,9 +53,11 @@ export const PropTypeSchema = z.enum(['string', 'select', 'boolean', 'number', '
34
53
  * client builder (`ComponentBuilder.buildComponent`). The schema accepts the
35
54
  * shape so the editor's load path doesn't reject files that contain it.
36
55
  */
37
- export const I18nValueObjectSchema = z.object({
38
- _i18n: z.literal(true),
39
- }).passthrough();
56
+ export const I18nValueObjectSchema = z
57
+ .object({
58
+ _i18n: z.literal(true),
59
+ })
60
+ .passthrough();
40
61
 
41
62
  /**
42
63
  * Base prop definition schema (for non-list props).
@@ -47,20 +68,24 @@ export const I18nValueObjectSchema = z.object({
47
68
  * in this file; we inline the matching shape here to keep the validator
48
69
  * tolerant of localized defaults.
49
70
  */
50
- export const BasePropDefinitionSchema = z.object({
51
- type: BasePropTypeSchema,
52
- default: z.union([
53
- z.string(),
54
- z.number(),
55
- z.boolean(),
56
- z.object({ href: z.string(), target: z.string().optional() }),
57
- I18nValueObjectSchema,
58
- ]).optional(),
59
- options: z.array(z.string()).readonly().optional(),
60
- enumName: z.string().optional(), // For 'select' type: reference to project-level enum
61
- accept: z.string().optional(), // For 'file' type: MIME pattern like "image/*", "video/*"
62
- editor: z.enum(['basic', 'extended']).optional(), // For 'rich-text' type: which editor to use
63
- }).passthrough();
71
+ export const BasePropDefinitionSchema = z
72
+ .object({
73
+ type: BasePropTypeSchema,
74
+ default: z
75
+ .union([
76
+ z.string(),
77
+ z.number(),
78
+ z.boolean(),
79
+ z.object({ href: z.string(), target: z.string().optional() }),
80
+ I18nValueObjectSchema,
81
+ ])
82
+ .optional(),
83
+ options: z.array(z.string()).readonly().optional(),
84
+ enumName: z.string().optional(), // For 'select' type: reference to project-level enum
85
+ accept: z.string().optional(), // For 'file' type: MIME pattern like "image/*", "video/*"
86
+ editor: z.enum(['basic', 'extended']).optional(), // For 'rich-text' type: which editor to use
87
+ })
88
+ .passthrough();
64
89
 
65
90
  /**
66
91
  * Project-level enums configuration schema
@@ -76,109 +101,129 @@ export const ListItemSchemaSchema = z.record(z.string(), BasePropDefinitionSchem
76
101
  /**
77
102
  * List prop definition schema
78
103
  */
79
- export const ListPropDefinitionSchema = z.object({
80
- type: z.literal('list'),
81
- itemSchema: ListItemSchemaSchema,
82
- // List-item field values can be any primitive, a link object, an `_i18n`
83
- // object (resolved by the SSR pipeline), or null.
84
- default: z.array(z.record(z.string(), z.union([
85
- z.string(),
86
- z.number(),
87
- z.boolean(),
88
- z.object({ href: z.string(), target: z.string().optional() }),
89
- I18nValueObjectSchema,
90
- z.null(),
91
- ]))).optional(),
92
- }).passthrough();
104
+ export const ListPropDefinitionSchema = z
105
+ .object({
106
+ type: z.literal('list'),
107
+ itemSchema: ListItemSchemaSchema,
108
+ // List-item field values can be any primitive, a link object, an `_i18n`
109
+ // object (resolved by the SSR pipeline), or null.
110
+ default: z
111
+ .array(
112
+ z.record(
113
+ z.string(),
114
+ z.union([
115
+ z.string(),
116
+ z.number(),
117
+ z.boolean(),
118
+ z.object({ href: z.string(), target: z.string().optional() }),
119
+ I18nValueObjectSchema,
120
+ z.null(),
121
+ ]),
122
+ ),
123
+ )
124
+ .optional(),
125
+ })
126
+ .passthrough();
93
127
 
94
128
  /**
95
129
  * Prop definition schema (union of base and list)
96
130
  * Validates prop definitions from component interfaces
97
131
  */
98
- export const PropDefinitionSchema = z.union([
99
- ListPropDefinitionSchema,
100
- BasePropDefinitionSchema,
101
- ]);
132
+ export const PropDefinitionSchema = z.union([ListPropDefinitionSchema, BasePropDefinitionSchema]);
102
133
 
103
134
  /**
104
135
  * Style mapping schema
105
136
  */
106
- export const StyleMappingSchema: z.ZodType<StyleMapping> = z.object({
107
- _mapping: z.literal(true),
108
- prop: z.string(),
109
- values: z.record(z.string(), z.union([z.string(), z.number()])),
110
- }).passthrough();
137
+ export const StyleMappingSchema: z.ZodType<StyleMapping> = z
138
+ .object({
139
+ _mapping: z.literal(true),
140
+ prop: z.string(),
141
+ values: z.record(z.string(), z.union([z.string(), z.number()])),
142
+ })
143
+ .passthrough();
111
144
 
112
145
  /**
113
146
  * Boolean mapping schema - for conditional rendering (if property)
114
147
  * Maps component prop values to boolean results
115
148
  */
116
- export const BooleanMappingSchema = z.object({
117
- _mapping: z.literal(true),
118
- prop: z.string(),
119
- values: z.record(z.string(), z.boolean()),
120
- }).passthrough();
149
+ export const BooleanMappingSchema = z
150
+ .object({
151
+ _mapping: z.literal(true),
152
+ prop: z.string(),
153
+ values: z.record(z.string(), z.boolean()),
154
+ })
155
+ .passthrough();
121
156
 
122
157
  /**
123
- * If condition schema - can be boolean, string template, or mapping
158
+ * Verbatim-code marker (`{ _code: true, expr }`) arbitrary JS the meno-astro dialect can't
159
+ * model as a binding (a function/method call, optional chaining, `??`, …). It is a legitimate
160
+ * value anywhere a child node, an `if` condition, or an attribute value can appear: it renders
161
+ * natively in the Astro build, and the editor preview renders nothing for it (see
162
+ * ComponentBuilder's `_code` guard). The editor is meno-astro-only, so its model routinely
163
+ * carries these — they must validate or a component with any verbatim expression won't open.
124
164
  */
125
- export const IfConditionSchema = z.union([
126
- z.boolean(),
127
- z.string(),
128
- BooleanMappingSchema,
129
- ]);
165
+ export const VerbatimCodeSchema = z.object({ _code: z.literal(true), expr: z.string() }).passthrough();
166
+
167
+ /**
168
+ * If condition schema - can be boolean, string template, mapping, or a verbatim-code marker
169
+ */
170
+ export const IfConditionSchema = z.union([z.boolean(), z.string(), BooleanMappingSchema, VerbatimCodeSchema]);
130
171
 
131
172
  /**
132
173
  * Link mapping schema - for link node href mappings
133
174
  * values is optional - when missing, acts as passthrough for link-type props
134
175
  */
135
- export const LinkMappingSchema: z.ZodType<LinkMapping> = z.object({
136
- _mapping: z.literal(true),
137
- prop: z.string(),
138
- values: z.record(z.string(), z.object({
139
- href: z.string(),
140
- target: z.string().optional(),
141
- })).optional(),
142
- }).passthrough();
176
+ export const LinkMappingSchema: z.ZodType<LinkMapping> = z
177
+ .object({
178
+ _mapping: z.literal(true),
179
+ prop: z.string(),
180
+ values: z
181
+ .record(
182
+ z.string(),
183
+ z.object({
184
+ href: z.string(),
185
+ target: z.string().optional(),
186
+ }),
187
+ )
188
+ .optional(),
189
+ })
190
+ .passthrough();
143
191
 
144
192
  /**
145
193
  * HTML mapping schema - for embed node html content mappings
146
194
  * values is optional - when missing, acts as passthrough for string-type props
147
195
  */
148
- export const HtmlMappingSchema: z.ZodType<HtmlMapping> = z.object({
149
- _mapping: z.literal(true),
150
- prop: z.string(),
151
- values: z.record(z.string(), z.string()).optional(),
152
- }).passthrough();
196
+ export const HtmlMappingSchema: z.ZodType<HtmlMapping> = z
197
+ .object({
198
+ _mapping: z.literal(true),
199
+ prop: z.string(),
200
+ values: z.record(z.string(), z.string()).optional(),
201
+ })
202
+ .passthrough();
153
203
 
154
204
  /**
155
205
  * Style object schema - flat style object
156
206
  */
157
207
  export const StyleObjectSchema: z.ZodType<StyleObject> = z.record(
158
208
  z.string(),
159
- z.union([
160
- z.string(),
161
- z.number(),
162
- StyleMappingSchema,
163
- ])
209
+ z.union([z.string(), z.number(), StyleMappingSchema]),
164
210
  );
165
211
 
166
212
  /**
167
213
  * Responsive style object schema
168
214
  */
169
- export const ResponsiveStyleObjectSchema = z.object({
170
- base: StyleObjectSchema.optional(),
171
- tablet: StyleObjectSchema.optional(),
172
- mobile: StyleObjectSchema.optional(),
173
- }).passthrough();
215
+ export const ResponsiveStyleObjectSchema = z
216
+ .object({
217
+ base: StyleObjectSchema.optional(),
218
+ tablet: StyleObjectSchema.optional(),
219
+ mobile: StyleObjectSchema.optional(),
220
+ })
221
+ .passthrough();
174
222
 
175
223
  /**
176
224
  * Style value schema - can be either flat or responsive
177
225
  */
178
- export const StyleValueSchema = z.union([
179
- StyleObjectSchema,
180
- ResponsiveStyleObjectSchema,
181
- ]);
226
+ export const StyleValueSchema = z.union([StyleObjectSchema, ResponsiveStyleObjectSchema]);
182
227
 
183
228
  /**
184
229
  * Interactive style rule schema
@@ -209,14 +254,20 @@ export const InteractiveStylesSchema = z.array(InteractiveStyleRuleSchema);
209
254
  * default: Optional default content when instance has no children
210
255
  * Can be an array of ComponentNodes or a string
211
256
  */
212
- export const SlotMarkerSchema: z.ZodType<any> = z.lazy(() => z.object({
213
- type: z.literal(NODE_TYPE.SLOT),
214
- default: z.union([
215
- z.array(z.union([ComponentNodeSchema, z.string(), I18nValueObjectSchema])),
216
- z.string(),
217
- I18nValueObjectSchema,
218
- ]).optional(),
219
- }).passthrough());
257
+ export const SlotMarkerSchema: z.ZodType<any> = z.lazy(() =>
258
+ z
259
+ .object({
260
+ type: z.literal(NODE_TYPE.SLOT),
261
+ default: z
262
+ .union([
263
+ z.array(z.union([ComponentNodeSchema, z.string(), I18nValueObjectSchema])),
264
+ z.string(),
265
+ I18nValueObjectSchema,
266
+ ])
267
+ .optional(),
268
+ })
269
+ .passthrough(),
270
+ );
220
271
 
221
272
  /**
222
273
  * Component node schema - discriminated union (forward declaration)
@@ -227,95 +278,131 @@ let ComponentNodeSchema: z.ZodType<any>;
227
278
  /**
228
279
  * HTML node schema - recursive definition
229
280
  */
230
- export const HtmlNodeSchema: z.ZodType<any> = z.lazy(() => z.object({
231
- type: z.literal(NODE_TYPE.NODE),
232
- tag: z.string(),
233
- label: z.string().optional(),
234
- if: IfConditionSchema.optional(),
235
- style: StyleValueSchema.optional(),
236
- interactiveStyles: InteractiveStylesSchema.optional(),
237
- generateElementClass: z.boolean().optional(),
238
- attributes: z.record(z.string(), z.union([z.string(), z.number(), z.boolean(), I18nValueObjectSchema])).optional(),
239
- props: z.record(z.string(), z.any()).optional(), // Allow props for backward compatibility
240
- children: z.union([
241
- z.array(z.union([ComponentNodeSchema, z.string(), I18nValueObjectSchema])),
242
- z.string(),
243
- I18nValueObjectSchema,
244
- ]).optional(),
245
- }).passthrough());
281
+ export const HtmlNodeSchema: z.ZodType<any> = z.lazy(() =>
282
+ z
283
+ .object({
284
+ type: z.literal(NODE_TYPE.NODE),
285
+ tag: z.string(),
286
+ label: z.string().optional(),
287
+ if: IfConditionSchema.optional(),
288
+ style: StyleValueSchema.optional(),
289
+ interactiveStyles: InteractiveStylesSchema.optional(),
290
+ generateElementClass: z.boolean().optional(),
291
+ attributes: z
292
+ .record(z.string(), z.union([z.string(), z.number(), z.boolean(), I18nValueObjectSchema, VerbatimCodeSchema]))
293
+ .optional(),
294
+ props: z.record(z.string(), z.any()).optional(), // Allow props for backward compatibility
295
+ children: z
296
+ .union([
297
+ z.array(z.union([ComponentNodeSchema, z.string(), I18nValueObjectSchema])),
298
+ z.string(),
299
+ I18nValueObjectSchema,
300
+ ])
301
+ .optional(),
302
+ })
303
+ .passthrough(),
304
+ );
246
305
 
247
306
  /**
248
307
  * Component instance node schema - recursive definition
249
308
  */
250
- export const ComponentInstanceNodeSchema: z.ZodType<any> = z.lazy(() => z.object({
251
- type: z.literal(NODE_TYPE.COMPONENT),
252
- component: z.string(),
253
- label: z.string().optional(),
254
- if: IfConditionSchema.optional(),
255
- props: z.record(z.string(), z.any()).optional(),
256
- style: StyleValueSchema.optional(),
257
- interactiveStyles: InteractiveStylesSchema.optional(),
258
- generateElementClass: z.boolean().optional(),
259
- attributes: z.record(z.string(), z.union([z.string(), z.number(), z.boolean(), I18nValueObjectSchema])).optional(),
260
- children: z.union([
261
- z.array(z.union([ComponentNodeSchema, z.string(), I18nValueObjectSchema])),
262
- z.string(),
263
- I18nValueObjectSchema,
264
- ]).optional(),
265
- }).passthrough());
309
+ export const ComponentInstanceNodeSchema: z.ZodType<any> = z.lazy(() =>
310
+ z
311
+ .object({
312
+ type: z.literal(NODE_TYPE.COMPONENT),
313
+ component: z.string(),
314
+ label: z.string().optional(),
315
+ if: IfConditionSchema.optional(),
316
+ props: z.record(z.string(), z.any()).optional(),
317
+ style: StyleValueSchema.optional(),
318
+ interactiveStyles: InteractiveStylesSchema.optional(),
319
+ generateElementClass: z.boolean().optional(),
320
+ attributes: z
321
+ .record(z.string(), z.union([z.string(), z.number(), z.boolean(), I18nValueObjectSchema, VerbatimCodeSchema]))
322
+ .optional(),
323
+ children: z
324
+ .union([
325
+ z.array(z.union([ComponentNodeSchema, z.string(), I18nValueObjectSchema])),
326
+ z.string(),
327
+ I18nValueObjectSchema,
328
+ ])
329
+ .optional(),
330
+ })
331
+ .passthrough(),
332
+ );
266
333
 
267
334
  /**
268
335
  * Embed node schema - for rendering custom HTML/SVG content
269
336
  */
270
- export const EmbedNodeSchema: z.ZodType<any> = z.lazy(() => z.object({
271
- type: z.literal(NODE_TYPE.EMBED),
272
- html: z.union([z.string(), HtmlMappingSchema, I18nValueObjectSchema]),
273
- label: z.string().optional(),
274
- if: IfConditionSchema.optional(),
275
- style: StyleValueSchema.optional(),
276
- interactiveStyles: InteractiveStylesSchema.optional(),
277
- generateElementClass: z.boolean().optional(),
278
- attributes: z.record(z.string(), z.union([z.string(), z.number(), z.boolean(), I18nValueObjectSchema])).optional(),
279
- }).passthrough());
337
+ export const EmbedNodeSchema: z.ZodType<any> = z.lazy(() =>
338
+ z
339
+ .object({
340
+ type: z.literal(NODE_TYPE.EMBED),
341
+ html: z.union([z.string(), HtmlMappingSchema, I18nValueObjectSchema]),
342
+ label: z.string().optional(),
343
+ if: IfConditionSchema.optional(),
344
+ style: StyleValueSchema.optional(),
345
+ interactiveStyles: InteractiveStylesSchema.optional(),
346
+ generateElementClass: z.boolean().optional(),
347
+ attributes: z
348
+ .record(z.string(), z.union([z.string(), z.number(), z.boolean(), I18nValueObjectSchema, VerbatimCodeSchema]))
349
+ .optional(),
350
+ })
351
+ .passthrough(),
352
+ );
280
353
 
281
354
  /**
282
355
  * Link node schema - renders as div in editor, <a> in SSR
283
356
  */
284
- export const LinkNodeSchema: z.ZodType<any> = z.lazy(() => z.object({
285
- type: z.literal(NODE_TYPE.LINK),
286
- href: z.union([z.string(), LinkMappingSchema, I18nValueObjectSchema]),
287
- label: z.string().optional(),
288
- if: IfConditionSchema.optional(),
289
- style: StyleValueSchema.optional(),
290
- interactiveStyles: InteractiveStylesSchema.optional(),
291
- generateElementClass: z.boolean().optional(),
292
- attributes: z.record(z.string(), z.union([z.string(), z.number(), z.boolean(), I18nValueObjectSchema])).optional(),
293
- children: z.union([
294
- z.array(z.union([ComponentNodeSchema, z.string(), I18nValueObjectSchema])),
295
- z.string(),
296
- I18nValueObjectSchema,
297
- ]).optional(),
298
- }).passthrough());
357
+ export const LinkNodeSchema: z.ZodType<any> = z.lazy(() =>
358
+ z
359
+ .object({
360
+ type: z.literal(NODE_TYPE.LINK),
361
+ href: z.union([z.string(), LinkMappingSchema, I18nValueObjectSchema]),
362
+ label: z.string().optional(),
363
+ if: IfConditionSchema.optional(),
364
+ style: StyleValueSchema.optional(),
365
+ interactiveStyles: InteractiveStylesSchema.optional(),
366
+ generateElementClass: z.boolean().optional(),
367
+ attributes: z
368
+ .record(z.string(), z.union([z.string(), z.number(), z.boolean(), I18nValueObjectSchema, VerbatimCodeSchema]))
369
+ .optional(),
370
+ children: z
371
+ .union([
372
+ z.array(z.union([ComponentNodeSchema, z.string(), I18nValueObjectSchema])),
373
+ z.string(),
374
+ I18nValueObjectSchema,
375
+ ])
376
+ .optional(),
377
+ })
378
+ .passthrough(),
379
+ );
299
380
 
300
381
  /**
301
382
  * Locale list node schema - renders locale switcher links
302
383
  */
303
- export const LocaleListNodeSchema: z.ZodType<any> = z.lazy(() => z.object({
304
- type: z.literal(NODE_TYPE.LOCALE_LIST),
305
- label: z.string().optional(),
306
- if: IfConditionSchema.optional(),
307
- style: StyleValueSchema.optional(),
308
- interactiveStyles: InteractiveStylesSchema.optional(),
309
- generateElementClass: z.boolean().optional(),
310
- itemStyle: StyleValueSchema.optional(),
311
- activeItemStyle: StyleValueSchema.optional(),
312
- separatorStyle: StyleValueSchema.optional(),
313
- showCurrent: z.boolean().optional(),
314
- showSeparator: z.boolean().optional(),
315
- showFlag: z.boolean().optional(),
316
- flagStyle: StyleValueSchema.optional(),
317
- attributes: z.record(z.string(), z.union([z.string(), z.number(), z.boolean(), I18nValueObjectSchema])).optional(),
318
- }).passthrough());
384
+ export const LocaleListNodeSchema: z.ZodType<any> = z.lazy(() =>
385
+ z
386
+ .object({
387
+ type: z.literal(NODE_TYPE.LOCALE_LIST),
388
+ label: z.string().optional(),
389
+ if: IfConditionSchema.optional(),
390
+ style: StyleValueSchema.optional(),
391
+ interactiveStyles: InteractiveStylesSchema.optional(),
392
+ generateElementClass: z.boolean().optional(),
393
+ itemStyle: StyleValueSchema.optional(),
394
+ activeItemStyle: StyleValueSchema.optional(),
395
+ separatorStyle: StyleValueSchema.optional(),
396
+ showCurrent: z.boolean().optional(),
397
+ showSeparator: z.boolean().optional(),
398
+ showFlag: z.boolean().optional(),
399
+ flagStyle: StyleValueSchema.optional(),
400
+ attributes: z
401
+ .record(z.string(), z.union([z.string(), z.number(), z.boolean(), I18nValueObjectSchema, VerbatimCodeSchema]))
402
+ .optional(),
403
+ })
404
+ .passthrough(),
405
+ );
319
406
 
320
407
  /**
321
408
  * Unified List node schema (basic version for ComponentNodeSchema union)
@@ -323,24 +410,34 @@ export const LocaleListNodeSchema: z.ZodType<any> = z.lazy(() => z.object({
323
410
  * Uses sourceType to distinguish: 'prop' (default) or 'collection'.
324
411
  * Also supports legacy 'cms-list' type for backward compatibility.
325
412
  */
326
- export const ListNodeSchemaBasic: z.ZodType<any> = z.lazy(() => z.object({
327
- type: z.union([z.literal(NODE_TYPE.LIST), z.literal('cms-list')]), // Support both for migration
328
- sourceType: z.enum(['prop', 'collection']).optional(), // defaults to 'prop'
329
- source: z.string().optional(), // Source prop name or collection name
330
- collection: z.string().optional(), // Legacy field for cms-list migration
331
- label: z.string().optional(),
332
- if: IfConditionSchema.optional(),
333
- itemAs: z.string().optional(),
334
- // Collection-specific options
335
- items: z.union([z.string(), z.array(z.string())]).optional(),
336
- filter: z.unknown().optional(),
337
- sort: z.unknown().optional(),
338
- limit: z.number().optional(),
339
- offset: z.number().optional(),
340
- excludeCurrentItem: z.boolean().optional(),
341
- emitTemplate: z.boolean().optional(),
342
- children: z.array(z.union([ComponentNodeSchema, z.string(), I18nValueObjectSchema])).optional(),
343
- }).passthrough());
413
+ export const ListNodeSchemaBasic: z.ZodType<any> = z.lazy(() =>
414
+ z
415
+ .object({
416
+ type: z.union([z.literal(NODE_TYPE.LIST), z.literal('cms-list')]), // Support both for migration
417
+ sourceType: z.enum(['prop', 'collection', 'remote', 'sanity', 'expression']).optional(), // defaults to 'prop'
418
+ source: z.string().optional(), // Source prop name or collection name
419
+ collection: z.string().optional(), // Legacy field for cms-list migration
420
+ // Remote-source options (sourceType: 'remote')
421
+ url: z.string().optional(), // HTTP/JSON endpoint
422
+ path: z.string().optional(), // dot-path into the response to the items array
423
+ // Sanity-source options (sourceType: 'sanity')
424
+ documentType: z.string().optional(), // Sanity document `_type`
425
+ slugField: z.string().optional(), // field used as the URL slug
426
+ label: z.string().optional(),
427
+ if: IfConditionSchema.optional(),
428
+ itemAs: z.string().optional(),
429
+ // Collection-specific options
430
+ items: z.union([z.string(), z.array(z.string())]).optional(),
431
+ filter: z.unknown().optional(),
432
+ sort: z.unknown().optional(),
433
+ limit: z.number().optional(),
434
+ offset: z.number().optional(),
435
+ excludeCurrentItem: z.boolean().optional(),
436
+ emitTemplate: z.boolean().optional(),
437
+ children: z.array(z.union([ComponentNodeSchema, z.string(), I18nValueObjectSchema])).optional(),
438
+ })
439
+ .passthrough(),
440
+ );
344
441
 
345
442
  /**
346
443
  * @deprecated Use ListNodeSchemaBasic instead.
@@ -348,6 +445,79 @@ export const ListNodeSchemaBasic: z.ZodType<any> = z.lazy(() => z.object({
348
445
  */
349
446
  export const CMSListNodeSchemaBasic = ListNodeSchemaBasic;
350
447
 
448
+ /**
449
+ * Island node schema - a BYO framework component rendered as an Astro island.
450
+ * `src` is the file relative to `src/islands/` (extension derives the framework);
451
+ * `client` carries the `client:*` hydration directive (omitted = server-only).
452
+ */
453
+ export const IslandNodeSchema: z.ZodType<any> = z.lazy(() =>
454
+ z
455
+ .object({
456
+ type: z.literal(NODE_TYPE.ISLAND),
457
+ src: z.string(),
458
+ client: z
459
+ .object({
460
+ directive: z.enum(['load', 'idle', 'visible', 'media', 'only']),
461
+ value: z.string().optional(),
462
+ })
463
+ .passthrough()
464
+ .optional(),
465
+ props: z.record(z.string(), z.any()).optional(),
466
+ if: IfConditionSchema.optional(),
467
+ children: z
468
+ .union([
469
+ z.array(z.union([ComponentNodeSchema, z.string(), I18nValueObjectSchema])),
470
+ z.string(),
471
+ I18nValueObjectSchema,
472
+ ])
473
+ .optional(),
474
+ })
475
+ .passthrough(),
476
+ );
477
+
478
+ /**
479
+ * Markdown node schema - renders a verbatim Markdown source string as HTML.
480
+ */
481
+ export const MarkdownNodeSchema: z.ZodType<any> = z.lazy(() =>
482
+ z
483
+ .object({
484
+ type: z.literal(NODE_TYPE.MARKDOWN),
485
+ source: z.string(),
486
+ label: z.string().optional(),
487
+ if: IfConditionSchema.optional(),
488
+ style: StyleValueSchema.optional(),
489
+ interactiveStyles: InteractiveStylesSchema.optional(),
490
+ generateElementClass: z.boolean().optional(),
491
+ attributes: z
492
+ .record(z.string(), z.union([z.string(), z.number(), z.boolean(), I18nValueObjectSchema, VerbatimCodeSchema]))
493
+ .optional(),
494
+ })
495
+ .passthrough(),
496
+ );
497
+
498
+ /**
499
+ * Custom node schema - an opaque, hand-authored `.astro` component under `src/custom/`.
500
+ * `src` is the file relative to `src/custom/`; Meno renders it via Astro but doesn't model
501
+ * its internals (server-only, no client directive — the native-Astro sibling of an island).
502
+ */
503
+ export const CustomNodeSchema: z.ZodType<any> = z.lazy(() =>
504
+ z
505
+ .object({
506
+ type: z.literal(NODE_TYPE.CUSTOM),
507
+ src: z.string(),
508
+ props: z.record(z.string(), z.any()).optional(),
509
+ if: IfConditionSchema.optional(),
510
+ children: z
511
+ .union([
512
+ z.array(z.union([ComponentNodeSchema, z.string(), I18nValueObjectSchema])),
513
+ z.string(),
514
+ I18nValueObjectSchema,
515
+ ])
516
+ .optional(),
517
+ })
518
+ .passthrough(),
519
+ );
520
+
351
521
  /**
352
522
  * Component node schema - discriminated union (now defined after dependencies)
353
523
  */
@@ -359,6 +529,12 @@ ComponentNodeSchema = z.union([
359
529
  LinkNodeSchema,
360
530
  LocaleListNodeSchema,
361
531
  ListNodeSchemaBasic,
532
+ IslandNodeSchema,
533
+ MarkdownNodeSchema,
534
+ CustomNodeSchema,
535
+ // A verbatim-code marker is a valid child node (renders natively at build; nothing in the
536
+ // editor preview) — a meno-astro component with any `{_code}` child must validate to open.
537
+ VerbatimCodeSchema,
362
538
  ]);
363
539
 
364
540
  // Export ComponentNodeSchema
@@ -386,42 +562,50 @@ export const ScriptTypeSchema = z.enum(['classic', 'module']);
386
562
  /**
387
563
  * JavaScript library schema
388
564
  */
389
- export const JSLibraryConfigSchema = z.object({
390
- url: z.string().min(1),
391
- mode: ScriptLoadModeSchema.optional(),
392
- position: LibraryPositionSchema.optional(),
393
- type: ScriptTypeSchema.optional(),
394
- }).passthrough();
565
+ export const JSLibraryConfigSchema = z
566
+ .object({
567
+ url: z.string().min(1),
568
+ mode: ScriptLoadModeSchema.optional(),
569
+ position: LibraryPositionSchema.optional(),
570
+ type: ScriptTypeSchema.optional(),
571
+ })
572
+ .passthrough();
395
573
 
396
574
  /**
397
575
  * CSS library schema
398
576
  */
399
- export const CSSLibraryConfigSchema = z.object({
400
- url: z.string().min(1),
401
- media: z.string().optional(),
402
- inline: z.boolean().optional(),
403
- }).passthrough();
577
+ export const CSSLibraryConfigSchema = z
578
+ .object({
579
+ url: z.string().min(1),
580
+ media: z.string().optional(),
581
+ inline: z.boolean().optional(),
582
+ })
583
+ .passthrough();
404
584
 
405
585
  /**
406
586
  * Libraries config schema
407
587
  */
408
- export const LibrariesConfigSchema = z.object({
409
- js: z.array(JSLibraryConfigSchema).optional(),
410
- css: z.array(CSSLibraryConfigSchema).optional(),
411
- }).passthrough();
588
+ export const LibrariesConfigSchema = z
589
+ .object({
590
+ js: z.array(JSLibraryConfigSchema).optional(),
591
+ css: z.array(CSSLibraryConfigSchema).optional(),
592
+ })
593
+ .passthrough();
412
594
 
413
595
  /**
414
596
  * Structured component definition schema
415
597
  */
416
- export const StructuredComponentDefinitionSchema = z.object({
417
- interface: z.record(z.string(), PropDefinitionSchema).optional(),
418
- structure: ComponentNodeSchema.optional(),
419
- javascript: z.string().optional(),
420
- css: z.string().optional(),
421
- category: z.string().optional(),
422
- libraries: LibrariesConfigSchema.optional(),
423
- acceptsStyles: z.boolean().optional(),
424
- }).passthrough();
598
+ export const StructuredComponentDefinitionSchema = z
599
+ .object({
600
+ interface: z.record(z.string(), PropDefinitionSchema).optional(),
601
+ structure: ComponentNodeSchema.optional(),
602
+ javascript: z.string().optional(),
603
+ css: z.string().optional(),
604
+ category: z.string().optional(),
605
+ libraries: LibrariesConfigSchema.optional(),
606
+ acceptsStyles: z.boolean().optional(),
607
+ })
608
+ .passthrough();
425
609
 
426
610
  /**
427
611
  * Component definition schema
@@ -430,16 +614,20 @@ export const StructuredComponentDefinitionSchema = z.object({
430
614
  */
431
615
  export const ComponentDefinitionSchema = z.union([
432
616
  // New format: { component: {...} }
433
- z.object({
434
- component: StructuredComponentDefinitionSchema,
435
- }).passthrough(),
617
+ z
618
+ .object({
619
+ component: StructuredComponentDefinitionSchema,
620
+ })
621
+ .passthrough(),
436
622
  // Legacy format: { type, props?, children?, component }
437
- z.object({
438
- type: z.string(),
439
- props: z.record(z.string(), z.any()).optional(),
440
- children: z.array(z.any()).optional(),
441
- component: StructuredComponentDefinitionSchema,
442
- }).passthrough(),
623
+ z
624
+ .object({
625
+ type: z.string(),
626
+ props: z.record(z.string(), z.any()).optional(),
627
+ children: z.array(z.any()).optional(),
628
+ component: StructuredComponentDefinitionSchema,
629
+ })
630
+ .passthrough(),
443
631
  ]);
444
632
 
445
633
  /**
@@ -447,9 +635,11 @@ export const ComponentDefinitionSchema = z.union([
447
635
  */
448
636
  const I18nOrStringSchema = z.union([
449
637
  z.string(),
450
- z.object({
451
- _i18n: z.literal(true),
452
- }).passthrough(),
638
+ z
639
+ .object({
640
+ _i18n: z.literal(true),
641
+ })
642
+ .passthrough(),
453
643
  ]);
454
644
 
455
645
  /**
@@ -460,35 +650,53 @@ export const LibraryMergeModeSchema = z.enum(['extend', 'replace']);
460
650
  /**
461
651
  * Page libraries config schema (with merge mode)
462
652
  */
463
- export const PageLibrariesConfigSchema = z.object({
464
- js: z.array(JSLibraryConfigSchema).optional(),
465
- css: z.array(CSSLibraryConfigSchema).optional(),
466
- mode: LibraryMergeModeSchema.optional(),
467
- }).passthrough();
653
+ export const PageLibrariesConfigSchema = z
654
+ .object({
655
+ js: z.array(JSLibraryConfigSchema).optional(),
656
+ css: z.array(CSSLibraryConfigSchema).optional(),
657
+ mode: LibraryMergeModeSchema.optional(),
658
+ })
659
+ .passthrough();
468
660
 
469
661
  /**
470
662
  * Page metadata schema (base version without CMS - used internally)
471
663
  * Note: CMS fields added via PageMetaDataWithCMSSchema after CMS schemas are defined
472
664
  */
473
- const CustomCodeConfigSchema = z.object({
474
- head: z.string().optional(),
475
- bodyStart: z.string().optional(),
476
- bodyEnd: z.string().optional(),
477
- }).passthrough();
478
-
479
- const PageMetaDataBaseSchema = z.object({
480
- title: I18nOrStringSchema.optional(),
481
- description: I18nOrStringSchema.optional(),
482
- keywords: I18nOrStringSchema.optional(),
483
- ogTitle: I18nOrStringSchema.optional(),
484
- ogDescription: I18nOrStringSchema.optional(),
485
- ogImage: z.string().optional(),
486
- ogType: z.string().optional(),
487
- slugs: z.record(z.string(), z.string()).optional(),
488
- draft: z.boolean().optional(),
489
- libraries: PageLibrariesConfigSchema.optional(),
490
- customCode: CustomCodeConfigSchema.optional(),
491
- }).passthrough();
665
+ const CustomCodeConfigSchema = z
666
+ .object({
667
+ head: z.string().optional(),
668
+ bodyStart: z.string().optional(),
669
+ bodyEnd: z.string().optional(),
670
+ })
671
+ .passthrough();
672
+
673
+ const SitemapPageConfigSchema = z
674
+ .object({
675
+ priority: z.number().optional(),
676
+ changefreq: z.enum(['always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never']).optional(),
677
+ exclude: z.boolean().optional(),
678
+ })
679
+ .passthrough();
680
+
681
+ const PageMetaDataBaseSchema = z
682
+ .object({
683
+ title: I18nOrStringSchema.optional(),
684
+ description: I18nOrStringSchema.optional(),
685
+ keywords: I18nOrStringSchema.optional(),
686
+ ogTitle: I18nOrStringSchema.optional(),
687
+ ogDescription: I18nOrStringSchema.optional(),
688
+ ogImage: z.string().optional(),
689
+ ogType: z.string().optional(),
690
+ slugs: z.record(z.string(), z.string()).optional(),
691
+ draft: z.boolean().optional(),
692
+ libraries: PageLibrariesConfigSchema.optional(),
693
+ customCode: CustomCodeConfigSchema.optional(),
694
+ viewTransitions: z.boolean().optional(),
695
+ noindex: z.boolean().optional(),
696
+ prerender: z.boolean().optional(),
697
+ sitemap: SitemapPageConfigSchema.optional(),
698
+ })
699
+ .passthrough();
492
700
 
493
701
  // Temporary export for backward compatibility - will be replaced with full schema below
494
702
  export const PageMetaDataSchema = PageMetaDataBaseSchema;
@@ -505,39 +713,49 @@ export const LineRangeSchema = z.object({
505
713
  * JSON page schema
506
714
  * Requires at least one field to prevent empty objects from matching
507
715
  */
508
- export const JSONPageSchema = z.object({
509
- components: z.record(z.string(), ComponentDefinitionSchema).optional(),
510
- root: ComponentNodeSchema.optional(),
511
- meta: PageMetaDataSchema.optional(),
512
- _lineMap: z.record(z.string(), LineRangeSchema).optional(),
513
- }).passthrough().refine((data) => {
514
- // Require at least one field to be present
515
- return data.meta !== undefined || data.components !== undefined || data.root !== undefined;
516
- }, {
517
- message: "JSONPage must have at least one of: meta, components, or root"
518
- });
716
+ export const JSONPageSchema = z
717
+ .object({
718
+ components: z.record(z.string(), ComponentDefinitionSchema).optional(),
719
+ root: ComponentNodeSchema.optional(),
720
+ meta: PageMetaDataSchema.optional(),
721
+ _lineMap: z.record(z.string(), LineRangeSchema).optional(),
722
+ // Marks a page whose source is outside the meno-astro dialect (read-only in the editor).
723
+ // Explicit (not just .passthrough()) so it survives a future tightening of this schema.
724
+ _unsupported: z.object({ reason: z.string() }).passthrough().optional(),
725
+ })
726
+ .passthrough()
727
+ .refine(
728
+ (data) => {
729
+ // Require at least one field to be present
730
+ return data.meta !== undefined || data.components !== undefined || data.root !== undefined;
731
+ },
732
+ {
733
+ message: 'JSONPage must have at least one of: meta, components, or root',
734
+ },
735
+ );
519
736
 
520
737
  /**
521
738
  * Page data with component schema
522
739
  */
523
- export const PageDataWithComponentSchema = z.object({
524
- component: z.object({
525
- structure: ComponentNodeSchema.optional(),
526
- interface: z.record(z.string(), PropDefinitionSchema).optional(),
527
- javascript: z.string().optional(),
528
- css: z.string().optional(),
529
- acceptsStyles: z.boolean().optional(),
530
- }).passthrough(),
531
- }).passthrough();
740
+ export const PageDataWithComponentSchema = z
741
+ .object({
742
+ component: z
743
+ .object({
744
+ structure: ComponentNodeSchema.optional(),
745
+ interface: z.record(z.string(), PropDefinitionSchema).optional(),
746
+ javascript: z.string().optional(),
747
+ css: z.string().optional(),
748
+ acceptsStyles: z.boolean().optional(),
749
+ })
750
+ .passthrough(),
751
+ })
752
+ .passthrough();
532
753
 
533
754
  /**
534
755
  * Page data schema - union of JSONPage and PageDataWithComponent
535
756
  * PageDataWithComponent checked first because it's more specific
536
757
  */
537
- export const PageDataSchema = z.union([
538
- PageDataWithComponentSchema,
539
- JSONPageSchema,
540
- ]);
758
+ export const PageDataSchema = z.union([PageDataWithComponentSchema, JSONPageSchema]);
541
759
 
542
760
  // ============================================================================
543
761
  // CMS Schemas
@@ -547,23 +765,34 @@ export const PageDataSchema = z.union([
547
765
  * CMS field type schema
548
766
  */
549
767
  export const CMSFieldTypeSchema = z.enum([
550
- 'string', 'text', 'rich-text', 'number', 'boolean', 'image', 'file', 'date', 'select', 'reference'
768
+ 'string',
769
+ 'text',
770
+ 'rich-text',
771
+ 'number',
772
+ 'boolean',
773
+ 'image',
774
+ 'file',
775
+ 'date',
776
+ 'select',
777
+ 'reference',
551
778
  ]);
552
779
 
553
780
  /**
554
781
  * CMS field definition schema
555
782
  */
556
- export const CMSFieldDefinitionSchema = z.object({
557
- type: CMSFieldTypeSchema,
558
- required: z.boolean().optional(),
559
- default: z.unknown().optional(),
560
- label: z.string().optional(),
561
- description: z.string().optional(),
562
- options: z.array(z.string()).optional(),
563
- accept: z.string().optional(),
564
- collection: z.string().optional(),
565
- multiple: z.boolean().optional(),
566
- }).passthrough();
783
+ export const CMSFieldDefinitionSchema = z
784
+ .object({
785
+ type: CMSFieldTypeSchema,
786
+ required: z.boolean().optional(),
787
+ default: z.unknown().optional(),
788
+ label: z.string().optional(),
789
+ description: z.string().optional(),
790
+ options: z.array(z.string()).optional(),
791
+ accept: z.string().optional(),
792
+ collection: z.string().optional(),
793
+ multiple: z.boolean().optional(),
794
+ })
795
+ .passthrough();
567
796
 
568
797
  /**
569
798
  * CMS client data strategy schema
@@ -574,34 +803,42 @@ export const CMSClientDataStrategySchema = z.enum(['auto', 'inline', 'static']);
574
803
  * CMS client data config schema
575
804
  * Configures client-side data exposure for dynamic filtering
576
805
  */
577
- export const CMSClientDataConfigSchema = z.object({
578
- enabled: z.boolean(),
579
- strategy: CMSClientDataStrategySchema,
580
- threshold: z.number().positive().optional(),
581
- fields: z.array(z.string()).optional(),
582
- }).passthrough();
806
+ export const CMSClientDataConfigSchema = z
807
+ .object({
808
+ enabled: z.boolean(),
809
+ strategy: CMSClientDataStrategySchema,
810
+ threshold: z.number().positive().optional(),
811
+ fields: z.array(z.string()).optional(),
812
+ })
813
+ .passthrough();
583
814
 
584
815
  /**
585
816
  * CMS schema schema (defines collection structure)
586
817
  */
587
- export const CMSSchemaSchema = z.object({
588
- id: z.string(),
589
- name: z.string(),
590
- slugField: z.string(),
591
- urlPattern: z.string(),
592
- fields: z.record(CMSFieldDefinitionSchema),
593
- clientData: CMSClientDataConfigSchema.optional(),
594
- }).passthrough();
818
+ export const CMSSchemaSchema = z
819
+ .object({
820
+ id: z.string(),
821
+ name: z.string(),
822
+ // Optional: a data-only collection (no per-item route) omits both.
823
+ slugField: z.string().optional(),
824
+ urlPattern: z.string().optional(),
825
+ fields: z.record(CMSFieldDefinitionSchema),
826
+ clientData: CMSClientDataConfigSchema.optional(),
827
+ rss: z.boolean().optional(),
828
+ })
829
+ .passthrough();
595
830
 
596
831
  /**
597
832
  * CMS item schema (content entry, strict — used at publish/save time).
598
833
  */
599
- export const CMSItemSchema = z.object({
600
- _id: z.string(),
601
- _slug: z.string().optional(),
602
- _filename: z.string().optional(),
603
- _createdAt: z.string().optional(),
604
- }).passthrough();
834
+ export const CMSItemSchema = z
835
+ .object({
836
+ _id: z.string(),
837
+ _slug: z.string().optional(),
838
+ _filename: z.string().optional(),
839
+ _createdAt: z.string().optional(),
840
+ })
841
+ .passthrough();
605
842
 
606
843
  /**
607
844
  * CMS draft item schema (loose — used at draft-write time).
@@ -610,12 +847,14 @@ export const CMSItemSchema = z.object({
610
847
  * are optional at this stage. Strict validation runs when promoting the draft
611
848
  * to published (see `publishDraft` in `FileSystemCMSProvider`).
612
849
  */
613
- export const CMSDraftItemSchema = z.object({
614
- _id: z.string().optional(),
615
- _slug: z.string().optional(),
616
- _filename: z.string().optional(),
617
- _createdAt: z.string().optional(),
618
- }).passthrough();
850
+ export const CMSDraftItemSchema = z
851
+ .object({
852
+ _id: z.string().optional(),
853
+ _slug: z.string().optional(),
854
+ _filename: z.string().optional(),
855
+ _createdAt: z.string().optional(),
856
+ })
857
+ .passthrough();
619
858
 
620
859
  /**
621
860
  * CMS filter condition schema
@@ -639,11 +878,7 @@ export const CMSSortConfigSchema = z.object({
639
878
  */
640
879
  export const CMSListQuerySchema = z.object({
641
880
  collection: z.string(),
642
- filter: z.union([
643
- CMSFilterConditionSchema,
644
- z.array(CMSFilterConditionSchema),
645
- z.record(z.unknown()),
646
- ]).optional(),
881
+ filter: z.union([CMSFilterConditionSchema, z.array(CMSFilterConditionSchema), z.record(z.unknown())]).optional(),
647
882
  sort: z.union([CMSSortConfigSchema, z.array(CMSSortConfigSchema)]).optional(),
648
883
  limit: z.number().positive().optional(),
649
884
  offset: z.number().nonnegative().optional(),
@@ -656,51 +891,87 @@ export const CMSListQuerySchema = z.object({
656
891
  * - {{<itemAs>Index}}, {{<itemAs>First}}, {{<itemAs>Last}} - Position info
657
892
  * Legacy {{item.field}}, {{itemIndex}}, etc. still supported
658
893
  */
659
- export const CMSListNodeSchema = z.object({
660
- type: z.literal('cms-list'),
661
- collection: z.string(),
662
- if: IfConditionSchema.optional(),
663
- itemAs: z.string().optional(),
664
- items: z.union([z.string(), z.array(z.string())]).optional(),
665
- filter: z.union([
666
- CMSFilterConditionSchema,
667
- z.array(CMSFilterConditionSchema),
668
- z.record(z.unknown()),
669
- ]).optional(),
670
- sort: z.union([CMSSortConfigSchema, z.array(CMSSortConfigSchema)]).optional(),
671
- limit: z.number().positive().optional(),
672
- offset: z.number().nonnegative().optional(),
673
- excludeCurrentItem: z.boolean().optional(),
674
- children: z.array(z.unknown()).optional(),
675
- }).passthrough();
894
+ export const CMSListNodeSchema = z
895
+ .object({
896
+ type: z.literal('cms-list'),
897
+ collection: z.string(),
898
+ if: IfConditionSchema.optional(),
899
+ itemAs: z.string().optional(),
900
+ items: z.union([z.string(), z.array(z.string())]).optional(),
901
+ filter: z.union([CMSFilterConditionSchema, z.array(CMSFilterConditionSchema), z.record(z.unknown())]).optional(),
902
+ sort: z.union([CMSSortConfigSchema, z.array(CMSSortConfigSchema)]).optional(),
903
+ limit: z.number().positive().optional(),
904
+ offset: z.number().nonnegative().optional(),
905
+ excludeCurrentItem: z.boolean().optional(),
906
+ children: z.array(z.unknown()).optional(),
907
+ })
908
+ .passthrough();
676
909
 
677
910
  /**
678
911
  * Page CMS config schema (embedded in page meta)
679
912
  */
680
- export const PageCmsConfigSchema = z.object({
681
- id: z.string(),
682
- name: z.string(),
683
- slugField: z.string(),
684
- urlPattern: z.string(),
685
- fields: z.record(CMSFieldDefinitionSchema),
686
- }).passthrough();
913
+ export const PageCmsConfigSchema = z
914
+ .object({
915
+ id: z.string(),
916
+ name: z.string(),
917
+ // Optional: a data-only collection (no per-item route) omits both.
918
+ slugField: z.string().optional(),
919
+ urlPattern: z.string().optional(),
920
+ fields: z.record(CMSFieldDefinitionSchema),
921
+ rss: z.boolean().optional(),
922
+ })
923
+ .passthrough();
924
+
925
+ /**
926
+ * SSR page data-source schema (one entry of `meta.data.sources`).
927
+ */
928
+ export const PageDataSourceSchema = z
929
+ .object({
930
+ type: z.literal('fetch'),
931
+ url: z.string(),
932
+ method: z.literal('GET').optional(),
933
+ headers: z.record(z.string(), z.string()).optional(),
934
+ path: z.string().optional(),
935
+ fields: z.record(z.enum(['string', 'number', 'boolean', 'date', 'image', 'object'])).optional(),
936
+ cardinality: z.enum(['object', 'list']).optional(),
937
+ })
938
+ .passthrough();
939
+
940
+ /**
941
+ * SSR page data config schema (embedded in page meta as `meta.data`, required when
942
+ * `meta.source === 'ssr'`).
943
+ */
944
+ export const PageDataConfigSchema = z
945
+ .object({
946
+ sources: z.record(z.string(), PageDataSourceSchema),
947
+ })
948
+ .passthrough();
687
949
 
688
950
  /**
689
951
  * Page metadata schema with CMS support
690
952
  * Extends base schema with source and cms fields
691
953
  */
692
- export const PageMetaDataWithCMSSchema = z.object({
693
- title: z.union([z.string(), z.object({ _i18n: z.literal(true) }).passthrough()]).optional(),
694
- description: z.union([z.string(), z.object({ _i18n: z.literal(true) }).passthrough()]).optional(),
695
- keywords: z.union([z.string(), z.object({ _i18n: z.literal(true) }).passthrough()]).optional(),
696
- ogTitle: z.union([z.string(), z.object({ _i18n: z.literal(true) }).passthrough()]).optional(),
697
- ogDescription: z.union([z.string(), z.object({ _i18n: z.literal(true) }).passthrough()]).optional(),
698
- ogImage: z.string().optional(),
699
- ogType: z.string().optional(),
700
- slugs: z.record(z.string(), z.string()).optional(),
701
- source: z.enum(['static', 'cms']).optional(),
702
- cms: PageCmsConfigSchema.optional(),
703
- draft: z.boolean().optional(),
704
- libraries: PageLibrariesConfigSchema.optional(),
705
- customCode: CustomCodeConfigSchema.optional(),
706
- }).passthrough();
954
+ export const PageMetaDataWithCMSSchema = z
955
+ .object({
956
+ title: z.union([z.string(), z.object({ _i18n: z.literal(true) }).passthrough()]).optional(),
957
+ description: z.union([z.string(), z.object({ _i18n: z.literal(true) }).passthrough()]).optional(),
958
+ keywords: z.union([z.string(), z.object({ _i18n: z.literal(true) }).passthrough()]).optional(),
959
+ ogTitle: z.union([z.string(), z.object({ _i18n: z.literal(true) }).passthrough()]).optional(),
960
+ ogDescription: z.union([z.string(), z.object({ _i18n: z.literal(true) }).passthrough()]).optional(),
961
+ ogImage: z.string().optional(),
962
+ ogType: z.string().optional(),
963
+ slugs: z.record(z.string(), z.string()).optional(),
964
+ source: z.enum(['static', 'cms', 'ssr']).optional(),
965
+ cms: PageCmsConfigSchema.optional(),
966
+ data: PageDataConfigSchema.optional(),
967
+ routeParams: z.array(z.string()).optional(),
968
+ routePreview: z.record(z.string(), z.string()).optional(),
969
+ draft: z.boolean().optional(),
970
+ libraries: PageLibrariesConfigSchema.optional(),
971
+ customCode: CustomCodeConfigSchema.optional(),
972
+ viewTransitions: z.boolean().optional(),
973
+ noindex: z.boolean().optional(),
974
+ prerender: z.boolean().optional(),
975
+ sitemap: SitemapPageConfigSchema.optional(),
976
+ })
977
+ .passthrough();