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
@@ -3,32 +3,33 @@
3
3
  * Handles rendering of link nodes (links rendered as div in editor)
4
4
  */
5
5
 
6
- import { createElement as h } from "react";
7
- import type { ReactElement } from "react";
8
- import type { LinkNode, StyleObject, ResponsiveStyleObject, InteractiveStyles } from "../../../shared/types";
9
- import { extractAttributesFromNode } from "../../../shared/attributeNodeUtils";
10
- import { responsiveStylesToClasses } from "../../../shared/utilityClassMapper";
11
- import { getCachedResponsiveScalesConfig } from "../../responsiveStyleResolver";
12
- import { pathToString } from "../../../shared/pathArrayUtils";
13
- import { generateElementClassName, type ElementClassContext } from "../../../shared/elementClassName";
14
- import { InteractiveStylesRegistry } from "../../InteractiveStylesRegistry";
15
- import { hasInteractiveStyleMappings, extractInteractiveStyleMappings, resolveExtractedMappings } from "../../../shared/interactiveStyleMappings";
16
- import { processItemPropsTemplate, type ValueResolver } from "../../../shared/itemTemplateUtils";
17
- import { UtilityClassCollector } from "../../styles/UtilityClassCollector";
18
- import { resolveI18nValue, DEFAULT_I18N_CONFIG } from "../../../shared/i18n";
19
- import { isCurrentLink } from "../../../shared/linkUtils";
20
- import type { ElementRegistry } from "../../elementRegistry";
21
- import type { BuilderContext, BuildResult, BuildChildrenContext } from "./types";
6
+ import { createElement as h } from 'react';
7
+ import type { ReactElement } from 'react';
8
+ import type { LinkNode, StyleObject, ResponsiveStyleObject, InteractiveStyles } from '../../../shared/types';
9
+ import { extractAttributesFromNode } from '../../../shared/attributeNodeUtils';
10
+ import { responsiveStylesToClasses } from '../../../shared/utilityClassMapper';
11
+ import { getCachedResponsiveScalesConfig } from '../../responsiveStyleResolver';
12
+ import { pathToString } from '../../../shared/pathArrayUtils';
13
+ import { generateElementClassName, type ElementClassContext } from '../../../shared/elementClassName';
14
+ import { InteractiveStylesRegistry } from '../../InteractiveStylesRegistry';
15
+ import {
16
+ hasInteractiveStyleMappings,
17
+ extractInteractiveStyleMappings,
18
+ resolveExtractedMappings,
19
+ } from '../../../shared/interactiveStyleMappings';
20
+ import { processItemPropsTemplate, type ValueResolver } from '../../../shared/itemTemplateUtils';
21
+ import { UtilityClassCollector } from '../../styles/UtilityClassCollector';
22
+ import { resolveI18nValue, DEFAULT_I18N_CONFIG } from '../../../shared/i18n';
23
+ import { isCurrentLink } from '../../../shared/linkUtils';
24
+ import type { ElementRegistry } from '../../elementRegistry';
25
+ import type { BuilderContext, BuildResult, BuildChildrenContext } from './types';
22
26
 
23
27
  export interface LinkNodeBuilderDeps {
24
28
  elementRegistry: ElementRegistry;
25
- buildChildren: (
26
- children: unknown,
27
- ctx: BuildChildrenContext
28
- ) => BuildResult;
29
+ buildChildren: (children: unknown, ctx: BuildChildrenContext) => BuildResult;
29
30
  getEffectiveParentComponentName: (
30
31
  componentContext: string | null,
31
- parentComponentName: string | null
32
+ parentComponentName: string | null,
32
33
  ) => string | null;
33
34
  /** Optional getter for current page name (for interactive styles class generation) */
34
35
  getCurrentPageName?: () => string;
@@ -45,12 +46,25 @@ export function buildLinkNode(
45
46
  node: LinkNode,
46
47
  children: unknown,
47
48
  ctx: BuilderContext,
48
- deps: LinkNodeBuilderDeps
49
+ deps: LinkNodeBuilderDeps,
49
50
  ): ReactElement {
50
51
  const {
51
- key, elementPath, parentComponentName, viewportWidth, componentContext, componentRootPath,
52
- locale, i18nConfig, cmsContext, cmsLocale, collectionItemsMap,
53
- itemContext, cmsItemIndexPath, cmsListPaths, templateContext, componentResolvedProps
52
+ key,
53
+ elementPath,
54
+ parentComponentName,
55
+ viewportWidth,
56
+ componentContext,
57
+ componentRootPath,
58
+ locale,
59
+ i18nConfig,
60
+ cmsContext,
61
+ cmsLocale,
62
+ collectionItemsMap,
63
+ itemContext,
64
+ cmsItemIndexPath,
65
+ cmsListPaths,
66
+ templateContext,
67
+ componentResolvedProps,
54
68
  } = ctx;
55
69
 
56
70
  const effectiveParentComponentName = deps.getEffectiveParentComponentName(componentContext, parentComponentName);
@@ -59,7 +73,7 @@ export function buildLinkNode(
59
73
  const extractedAttributes = extractAttributesFromNode(node as any);
60
74
 
61
75
  // Build link node props (renders as div in editor)
62
- const linkNodeProps: Record<string, any> = {
76
+ const linkNodeProps: Record<string, unknown> = {
63
77
  key,
64
78
  'data-element-path': pathToString(elementPath),
65
79
  'data-link-node': 'true',
@@ -74,7 +88,7 @@ export function buildLinkNode(
74
88
  }
75
89
  }
76
90
  }
77
- }
91
+ },
78
92
  };
79
93
 
80
94
  // Add CMS item index path for elements inside CMS lists
@@ -83,7 +97,7 @@ export function buildLinkNode(
83
97
  }
84
98
 
85
99
  // Start building className
86
- let classNames: string[] = [];
100
+ const classNames: string[] = [];
87
101
 
88
102
  // Convert link node styles to utility classes
89
103
  if (node.style) {
@@ -99,16 +113,15 @@ export function buildLinkNode(
99
113
  processedStyle = processItemPropsTemplate(
100
114
  node.style as Record<string, unknown>,
101
115
  ctx.templateContext,
102
- i18nResolver
116
+ i18nResolver,
103
117
  ) as StyleObject | ResponsiveStyleObject;
104
118
  }
105
119
  const scales = getCachedResponsiveScalesConfig();
106
- const fluidActive =
107
- scales?.enabled === true && scales?.mode === 'fluid';
108
- const utilityClasses = responsiveStylesToClasses(
109
- processedStyle as StyleObject | ResponsiveStyleObject,
110
- { fluidActive, responsiveScales: scales ?? undefined }
111
- );
120
+ const fluidActive = scales?.enabled === true && scales?.mode === 'fluid';
121
+ const utilityClasses = responsiveStylesToClasses(processedStyle as StyleObject | ResponsiveStyleObject, {
122
+ fluidActive,
123
+ responsiveScales: scales ?? undefined,
124
+ });
112
125
  UtilityClassCollector.collect(utilityClasses);
113
126
  classNames.push(...utilityClasses);
114
127
  }
@@ -121,11 +134,10 @@ export function buildLinkNode(
121
134
  if ((nodeInteractiveStyles && nodeInteractiveStyles.length > 0) || nodeGenerateElementClass) {
122
135
  // Determine file type and name for element class generation
123
136
  const useComponentContext = Boolean(componentContext);
124
- const fileType = useComponentContext ? 'component' : (deps.getCurrentFileType?.() || 'page');
125
- const fileName = useComponentContext ? componentContext! : (deps.getCurrentPageName?.() || 'page');
126
- const pathForClass = useComponentContext && componentRootPath
127
- ? elementPath.slice(componentRootPath.length)
128
- : elementPath;
137
+ const fileType = useComponentContext ? 'component' : deps.getCurrentFileType?.() || 'page';
138
+ const fileName = useComponentContext ? componentContext! : deps.getCurrentPageName?.() || 'page';
139
+ const pathForClass =
140
+ useComponentContext && componentRootPath ? elementPath.slice(componentRootPath.length) : elementPath;
129
141
 
130
142
  const elementClassCtx: ElementClassContext = {
131
143
  fileType,
@@ -157,12 +169,11 @@ export function buildLinkNode(
157
169
  for (const rule of nodeInteractiveStyles) {
158
170
  if (rule.previewProp && ctx.componentResolvedProps[rule.previewProp] === true) {
159
171
  const previewScales = getCachedResponsiveScalesConfig();
160
- const previewFluidActive =
161
- previewScales?.enabled === true && previewScales?.mode === 'fluid';
162
- const styleClasses = responsiveStylesToClasses(
163
- rule.style as StyleObject | ResponsiveStyleObject,
164
- { fluidActive: previewFluidActive, responsiveScales: previewScales ?? undefined }
165
- );
172
+ const previewFluidActive = previewScales?.enabled === true && previewScales?.mode === 'fluid';
173
+ const styleClasses = responsiveStylesToClasses(rule.style as StyleObject | ResponsiveStyleObject, {
174
+ fluidActive: previewFluidActive,
175
+ responsiveScales: previewScales ?? undefined,
176
+ });
166
177
  UtilityClassCollector.collect(styleClasses);
167
178
  previewClasses.push(...styleClasses);
168
179
  }
@@ -179,9 +190,9 @@ export function buildLinkNode(
179
190
  // would be spread onto the React element below and override the className that
180
191
  // holds the utility classes derived from node.style.
181
192
  if ('class' in extractedAttributes || 'className' in extractedAttributes) {
182
- const attrClass = ((extractedAttributes as Record<string, unknown>).class
183
- ?? (extractedAttributes as Record<string, unknown>).className
184
- ?? '') as string;
193
+ const attrClass = ((extractedAttributes as Record<string, unknown>).class ??
194
+ (extractedAttributes as Record<string, unknown>).className ??
195
+ '') as string;
185
196
  if (attrClass) {
186
197
  classNames.push(...attrClass.split(/\s+/));
187
198
  }
@@ -209,9 +220,10 @@ export function buildLinkNode(
209
220
  const i18nResolver: ValueResolver | undefined = effectiveLocale
210
221
  ? (value: unknown) => resolveI18nValue(value, effectiveLocale, config)
211
222
  : undefined;
212
- processedAttributes = processItemPropsTemplate(
213
- extractedAttributes, ctx.templateContext, i18nResolver
214
- ) as Record<string, string | number | boolean>;
223
+ processedAttributes = processItemPropsTemplate(extractedAttributes, ctx.templateContext, i18nResolver) as Record<
224
+ string,
225
+ string | number | boolean
226
+ >;
215
227
  }
216
228
 
217
229
  // Add extracted attributes (like class, id, data-*, aria-*, etc.)
@@ -229,8 +241,21 @@ export function buildLinkNode(
229
241
 
230
242
  // Build children recursively
231
243
  const linkNodeChildren = deps.buildChildren(children, {
232
- elementPath, parentComponentName: effectiveParentComponentName, viewportWidth, componentContext, componentRootPath,
233
- locale, i18nConfig, cmsContext, cmsLocale, collectionItemsMap, itemContext, cmsItemIndexPath, cmsListPaths, templateContext, componentResolvedProps
244
+ elementPath,
245
+ parentComponentName: effectiveParentComponentName,
246
+ viewportWidth,
247
+ componentContext,
248
+ componentRootPath,
249
+ locale,
250
+ i18nConfig,
251
+ cmsContext,
252
+ cmsLocale,
253
+ collectionItemsMap,
254
+ itemContext,
255
+ cmsItemIndexPath,
256
+ cmsListPaths,
257
+ templateContext,
258
+ componentResolvedProps,
234
259
  });
235
260
 
236
261
  return h('div', linkNodeProps, linkNodeChildren);
@@ -5,56 +5,76 @@
5
5
  * - CMS collections (sourceType: 'collection')
6
6
  */
7
7
 
8
- import { createElement as h, Fragment } from "react";
9
- import type { ReactElement } from "react";
10
- import type { ListNode } from "../../../shared/registry/nodeTypes/ListNodeType";
11
- import type { CMSItem, CMSFilterCondition, CMSSortConfig, CMSFilterOperator } from "../../../shared/types/cms";
12
- import { singularize, isItemDraftForLocale } from "../../../shared/types/cms";
13
- import { buildTemplateContext, resolveItemsTemplate, getNestedValue } from "../../../shared/itemTemplateUtils";
14
- import type { TemplateContext } from "../../../shared/types/cms";
15
- import { pathToString } from "../../../shared/pathArrayUtils";
16
- import { extractAttributesFromNode } from "../../../shared/attributeNodeUtils";
17
- import type { ElementRegistry } from "../../elementRegistry";
18
- import type { BuilderContext, BuildResult, BuildChildrenContext } from "./types";
8
+ import { createElement as h, Fragment } from 'react';
9
+ import type { ReactElement } from 'react';
10
+ import type { ListNode } from '../../../shared/registry/nodeTypes/ListNodeType';
11
+ import type { CMSItem, CMSFilterCondition, CMSSortConfig, CMSFilterOperator } from '../../../shared/types/cms';
12
+ import { singularize, isItemDraftForLocale } from '../../../shared/types/cms';
13
+ import { buildTemplateContext, resolveItemsTemplate, getNestedValue } from '../../../shared/itemTemplateUtils';
14
+ import type { TemplateContext } from '../../../shared/types/cms';
15
+ import { pathToString } from '../../../shared/pathArrayUtils';
16
+ import { extractAttributesFromNode } from '../../../shared/attributeNodeUtils';
17
+ import type { ElementRegistry } from '../../elementRegistry';
18
+ import type { BuilderContext, BuildResult, BuildChildrenContext } from './types';
19
19
 
20
20
  export interface ListBuilderDeps {
21
21
  elementRegistry: ElementRegistry;
22
- buildChildren: (
23
- children: unknown,
24
- ctx: BuildChildrenContext
25
- ) => BuildResult;
22
+ buildChildren: (children: unknown, ctx: BuildChildrenContext) => BuildResult;
26
23
  getEffectiveParentComponentName: (
27
24
  componentContext: string | null,
28
- parentComponentName: string | null
25
+ parentComponentName: string | null,
29
26
  ) => string | null;
30
27
  }
31
28
 
32
29
  /**
33
30
  * Build a unified list node - handles both prop and collection source types
34
31
  */
35
- export function buildList(
36
- node: ListNode,
37
- children: unknown,
38
- ctx: BuilderContext,
39
- deps: ListBuilderDeps
40
- ): ReactElement {
32
+ export function buildList(node: ListNode, children: unknown, ctx: BuilderContext, deps: ListBuilderDeps): ReactElement {
41
33
  const {
42
- key, elementPath, parentComponentName, viewportWidth, componentContext, componentRootPath,
43
- locale, i18nConfig, cmsContext, cmsLocale, collectionItemsMap = {},
44
- cmsItemIndexPath, cmsListPaths, templateContext, componentResolvedProps
34
+ key,
35
+ elementPath,
36
+ parentComponentName,
37
+ viewportWidth,
38
+ componentContext,
39
+ componentRootPath,
40
+ locale,
41
+ i18nConfig,
42
+ cmsContext,
43
+ cmsLocale,
44
+ collectionItemsMap = {},
45
+ cmsItemIndexPath,
46
+ cmsListPaths,
47
+ templateContext,
48
+ componentResolvedProps,
45
49
  } = ctx;
46
50
 
47
51
  // Determine source type (default to 'prop', but handle legacy 'cms-list' type)
48
52
  const nodeType = (node as any).type;
49
53
  const isLegacyCMSList = nodeType === 'cms-list';
50
- const sourceType = isLegacyCMSList ? 'collection' : (node.sourceType || 'prop');
54
+ const sourceType = isLegacyCMSList ? 'collection' : node.sourceType || 'prop';
51
55
  const isCollectionMode = sourceType === 'collection';
56
+ // Remote lists (sourceType: 'remote') are fetched by the meno-astro runtime (getRemoteData)
57
+ // at build/SSR. In the editor canvas there's no Astro runtime, so the studio pre-fetches the
58
+ // endpoint's items and injects them into `collectionItemsMap` keyed by the endpoint URL — the
59
+ // same channel CMS collections use. We then reuse the collection query path (filter/sort/
60
+ // offset/limit) so the canvas matches what the real build renders.
61
+ const isRemoteMode = sourceType === 'remote';
62
+ const remoteUrl = isRemoteMode && typeof node.url === 'string' ? node.url : '';
63
+ // Sanity lists (sourceType: 'sanity') are fetched by the meno-astro runtime (getSanityData)
64
+ // at build/SSR. Like remote lists, the editor canvas has no Astro runtime, so the studio
65
+ // pre-fetches sample items (via /api/sanity-sample) and injects them into `collectionItemsMap`
66
+ // keyed by the document type — the same channel CMS/remote lists use.
67
+ const isSanityMode = sourceType === 'sanity';
68
+ const sanityKey = isSanityMode && typeof node.documentType === 'string' ? node.documentType : '';
52
69
 
53
70
  // Get source - handle both new 'source' and legacy 'collection' property
54
71
  // Source can be a string (prop name) or array (pre-resolved by processStructure)
55
72
  const rawSource = node.source || (node as any).collection;
56
73
  const source = typeof rawSource === 'string' ? rawSource : '';
57
74
  const sourceIsResolved = Array.isArray(rawSource);
75
+ // Unified "do we have something to render from?" flag (remote keys off its URL, sanity off its type).
76
+ const hasSource =
77
+ sourceIsResolved || Boolean(source) || (isRemoteMode && Boolean(remoteUrl)) || (isSanityMode && Boolean(sanityKey));
58
78
 
59
79
  const effectiveParentComponentName = deps.getEffectiveParentComponentName(componentContext, parentComponentName);
60
80
 
@@ -62,12 +82,12 @@ export function buildList(
62
82
  const extractedAttributes = extractAttributesFromNode(node as any);
63
83
 
64
84
  // Build container props with appropriate data attributes
65
- const containerProps: Record<string, any> = {
85
+ const containerProps: Record<string, unknown> = {
66
86
  key,
67
87
  'data-element-path': pathToString(elementPath),
68
88
  ref: (el: HTMLElement | null) => {
69
89
  deps.elementRegistry.register(elementPath, el, effectiveParentComponentName, false);
70
- }
90
+ },
71
91
  };
72
92
 
73
93
  // Set data attributes based on source type
@@ -75,6 +95,14 @@ export function buildList(
75
95
  containerProps['data-cms-list'] = 'true';
76
96
  if (isCollectionMode) {
77
97
  containerProps['data-collection'] = source || '';
98
+ } else if (isRemoteMode) {
99
+ containerProps['data-list'] = 'true';
100
+ containerProps['data-source-type'] = 'remote';
101
+ containerProps['data-source'] = remoteUrl;
102
+ } else if (isSanityMode) {
103
+ containerProps['data-list'] = 'true';
104
+ containerProps['data-source-type'] = 'sanity';
105
+ containerProps['data-source'] = sanityKey;
78
106
  } else {
79
107
  containerProps['data-list'] = 'true';
80
108
  containerProps['data-source'] = source || (sourceIsResolved ? 'resolved' : '');
@@ -112,6 +140,19 @@ export function buildList(
112
140
 
113
141
  if (isCollectionMode) {
114
142
  itemsToRender = getCollectionItems(node, source, ctx);
143
+ } else if (isRemoteMode) {
144
+ // Remote items are injected into collectionItemsMap keyed by URL; reuse the collection
145
+ // query path so filter/sort/offset/limit behave identically to the Astro runtime.
146
+ itemsToRender = getCollectionItems(node, remoteUrl, ctx);
147
+ } else if (isSanityMode) {
148
+ // Sanity sample items are injected into collectionItemsMap keyed by document type; reuse
149
+ // the collection query path so filter/sort/offset/limit match the Astro runtime.
150
+ itemsToRender = getCollectionItems(node, sanityKey, ctx);
151
+ } else if (sourceType === 'expression') {
152
+ // Expression mode: the source is a verbatim JS expression (an Astro Action / supabase result,
153
+ // …) with no data in the editor — it resolves only in the real Astro build/play. Render empty
154
+ // here; the item template is still editable. (Mirrors ssrRenderer's expression branch.)
155
+ itemsToRender = [];
115
156
  } else {
116
157
  // Prop mode: Read from component props or template context
117
158
  // If source was already resolved to an array by processStructure (e.g., from {{features}}),
@@ -139,26 +180,30 @@ export function buildList(
139
180
  const bgColor = isCollectionMode ? 'rgba(139, 92, 246, 0.05)' : 'rgba(59, 130, 246, 0.05)';
140
181
  const borderColor = isCollectionMode ? 'rgba(139, 92, 246, 0.3)' : 'rgba(59, 130, 246, 0.3)';
141
182
  const textColor = isCollectionMode ? '#8b5cf6' : '#3b82f6';
142
- const label = isCollectionMode ? 'CMS List' : 'List';
183
+ const label = isCollectionMode ? 'CMS List' : isRemoteMode ? 'Remote List' : isSanityMode ? 'Sanity List' : 'List';
143
184
 
144
185
  // Use configurable tag (defaults to 'div') - null means fragment mode (no wrapper)
145
186
  const tag = typeof node.tag === 'string' ? node.tag : null;
146
187
 
147
- if (!source && !sourceIsResolved) {
188
+ if (!hasSource) {
148
189
  // No source - render empty container with placeholder
149
- const emptyState = h('div', {
150
- key: 'list-empty',
151
- style: {
152
- padding: '12px',
153
- background: bgColor,
154
- border: `1px dashed ${borderColor}`,
155
- borderRadius: '4px',
156
- color: textColor,
157
- fontSize: '12px',
158
- fontFamily: 'system-ui, sans-serif',
159
- textAlign: 'center'
160
- }
161
- }, `${label}: No source - No items`);
190
+ const emptyState = h(
191
+ 'div',
192
+ {
193
+ key: 'list-empty',
194
+ style: {
195
+ padding: '12px',
196
+ background: bgColor,
197
+ border: `1px dashed ${borderColor}`,
198
+ borderRadius: '4px',
199
+ color: textColor,
200
+ fontSize: '12px',
201
+ fontFamily: 'system-ui, sans-serif',
202
+ textAlign: 'center',
203
+ },
204
+ },
205
+ `${label}: No source - No items`,
206
+ );
162
207
  if (tag === null) {
163
208
  return emptyState;
164
209
  }
@@ -167,19 +212,23 @@ export function buildList(
167
212
 
168
213
  if (itemsToRender.length === 0) {
169
214
  // No items - render empty container with placeholder
170
- const emptyState = h('div', {
171
- key: 'list-empty',
172
- style: {
173
- padding: '12px',
174
- background: bgColor,
175
- border: `1px dashed ${borderColor}`,
176
- borderRadius: '4px',
177
- color: textColor,
178
- fontSize: '12px',
179
- fontFamily: 'system-ui, sans-serif',
180
- textAlign: 'center'
181
- }
182
- }, `${label}: ${source || 'resolved'} - No items`);
215
+ const emptyState = h(
216
+ 'div',
217
+ {
218
+ key: 'list-empty',
219
+ style: {
220
+ padding: '12px',
221
+ background: bgColor,
222
+ border: `1px dashed ${borderColor}`,
223
+ borderRadius: '4px',
224
+ color: textColor,
225
+ fontSize: '12px',
226
+ fontFamily: 'system-ui, sans-serif',
227
+ textAlign: 'center',
228
+ },
229
+ },
230
+ `${label}: ${source || remoteUrl || sanityKey || 'resolved'} - No items`,
231
+ );
183
232
  if (tag === null) {
184
233
  return emptyState;
185
234
  }
@@ -197,7 +246,7 @@ export function buildList(
197
246
  item as CMSItem,
198
247
  index,
199
248
  itemsToRender.length,
200
- templateContext || undefined
249
+ templateContext || undefined,
201
250
  );
202
251
 
203
252
  // Build children with item context
@@ -220,7 +269,7 @@ export function buildList(
220
269
  cmsItemIndexPath: newIndexPath,
221
270
  cmsListPaths: newListPaths,
222
271
  templateContext: itemTemplateContext,
223
- componentResolvedProps
272
+ componentResolvedProps,
224
273
  });
225
274
 
226
275
  // Add children directly (no wrapper div).
@@ -241,7 +290,10 @@ export function buildList(
241
290
  }
242
291
  } else if (typeof itemChildren === 'object' && itemChildren !== null && 'key' in itemChildren) {
243
292
  // Single child - ensure unique key
244
- renderedItems.push({ ...itemChildren, key: `${(itemChildren as ReactElement).key}-item-${itemKey}` } as ReactElement);
293
+ renderedItems.push({
294
+ ...itemChildren,
295
+ key: `${(itemChildren as ReactElement).key}-item-${itemKey}`,
296
+ } as ReactElement);
245
297
  } else {
246
298
  renderedItems.push(itemChildren);
247
299
  }
@@ -262,15 +314,15 @@ export function buildList(
262
314
  */
263
315
  function getCollectionItems(node: ListNode, source: string, ctx: BuilderContext): CMSItem[] {
264
316
  const { collectionItemsMap = {}, cmsContext, templateContext } = ctx;
265
- const collectionItems = source ? (collectionItemsMap[source] || []) : [];
317
+ const collectionItems = source ? collectionItemsMap[source] || [] : [];
266
318
 
267
319
  // Helper to lookup items by ID or filename
268
320
  const lookupItemsByIds = (ids: string[], items: CMSItem[]): CMSItem[] => {
269
- const itemMap = new Map(items.map(item => [item._id, item]));
270
- const filenameMap = new Map(items.map(item => [item._filename, item]));
321
+ const itemMap = new Map(items.map((item) => [item._id, item]));
322
+ const filenameMap = new Map(items.map((item) => [item._filename, item]));
271
323
  return ids
272
324
  .filter(Boolean)
273
- .map(id => itemMap.get(id) || filenameMap.get(id))
325
+ .map((id) => itemMap.get(id) || filenameMap.get(id))
274
326
  .filter((item): item is CMSItem => item !== undefined);
275
327
  };
276
328
 
@@ -295,7 +347,7 @@ function getCollectionItems(node: ListNode, source: string, ctx: BuilderContext)
295
347
  }
296
348
  }
297
349
  if (value !== null && value !== undefined) {
298
- resolvedIds = Array.isArray(value) ? value.map(v => String(v)) : String(value);
350
+ resolvedIds = Array.isArray(value) ? value.map((v) => String(v)) : String(value);
299
351
  }
300
352
  } else {
301
353
  // Otherwise resolve from template context (for nested lists)
@@ -315,7 +367,12 @@ function getCollectionItems(node: ListNode, source: string, ctx: BuilderContext)
315
367
 
316
368
  // Apply filters and sorting to referenced items
317
369
  if (node.filter) {
318
- itemsToRender = applyClientFilters(itemsToRender, node.filter as CMSFilterCondition | CMSFilterCondition[] | Record<string, unknown>, cmsContext ?? undefined, templateContext ?? undefined);
370
+ itemsToRender = applyClientFilters(
371
+ itemsToRender,
372
+ node.filter as CMSFilterCondition | CMSFilterCondition[] | Record<string, unknown>,
373
+ cmsContext ?? undefined,
374
+ templateContext ?? undefined,
375
+ );
319
376
  }
320
377
  if (node.sort) {
321
378
  itemsToRender = applyClientSorting(itemsToRender, node.sort);
@@ -326,7 +383,12 @@ function getCollectionItems(node: ListNode, source: string, ctx: BuilderContext)
326
383
 
327
384
  // Apply filters (BEFORE offset/limit)
328
385
  if (node.filter) {
329
- itemsToRender = applyClientFilters(itemsToRender, node.filter as CMSFilterCondition | CMSFilterCondition[] | Record<string, unknown>, cmsContext ?? undefined, templateContext ?? undefined);
386
+ itemsToRender = applyClientFilters(
387
+ itemsToRender,
388
+ node.filter as CMSFilterCondition | CMSFilterCondition[] | Record<string, unknown>,
389
+ cmsContext ?? undefined,
390
+ templateContext ?? undefined,
391
+ );
330
392
  }
331
393
 
332
394
  // Apply sorting (BEFORE offset/limit)
@@ -346,13 +408,13 @@ function getCollectionItems(node: ListNode, source: string, ctx: BuilderContext)
346
408
  // Filter draft items for the current locale
347
409
  const locale = ctx.locale || ctx.cmsLocale;
348
410
  if (locale) {
349
- itemsToRender = itemsToRender.filter(item => !isItemDraftForLocale(item, locale));
411
+ itemsToRender = itemsToRender.filter((item) => !isItemDraftForLocale(item, locale));
350
412
  }
351
413
 
352
414
  // Exclude current item if option is set and we have a current CMS context
353
415
  if (node.excludeCurrentItem && cmsContext && (cmsContext as any)._id) {
354
416
  const currentId = (cmsContext as any)._id as string;
355
- itemsToRender = itemsToRender.filter(item => item._id !== currentId);
417
+ itemsToRender = itemsToRender.filter((item) => item._id !== currentId);
356
418
  }
357
419
 
358
420
  return itemsToRender;
@@ -408,23 +470,21 @@ function applyClientFilters(
408
470
  items: CMSItem[],
409
471
  filter: CMSFilterCondition | CMSFilterCondition[] | Record<string, unknown>,
410
472
  cmsContext?: Record<string, unknown>,
411
- templateContext?: TemplateContext
473
+ templateContext?: TemplateContext,
412
474
  ): CMSItem[] {
413
475
  // Handle simple object filter: { featured: true }
414
476
  if (!Array.isArray(filter) && !isFilterCondition(filter)) {
415
- return items.filter(item =>
477
+ return items.filter((item) =>
416
478
  Object.entries(filter).every(([key, value]) => {
417
479
  const resolvedValue = resolveTemplateValue(value, cmsContext, templateContext);
418
480
  return item[key] === resolvedValue;
419
- })
481
+ }),
420
482
  );
421
483
  }
422
484
 
423
485
  // Handle array of conditions or single condition
424
486
  const conditions = Array.isArray(filter) ? filter : [filter as CMSFilterCondition];
425
- return items.filter(item =>
426
- conditions.every(cond => matchCondition(item, cond, cmsContext, templateContext))
427
- );
487
+ return items.filter((item) => conditions.every((cond) => matchCondition(item, cond, cmsContext, templateContext)));
428
488
  }
429
489
 
430
490
  function isFilterCondition(obj: unknown): obj is CMSFilterCondition {
@@ -437,7 +497,7 @@ function isFilterCondition(obj: unknown): obj is CMSFilterCondition {
437
497
  function resolveTemplateValue(
438
498
  value: unknown,
439
499
  cmsContext?: Record<string, unknown>,
440
- templateContext?: TemplateContext
500
+ templateContext?: TemplateContext,
441
501
  ): unknown {
442
502
  if (typeof value !== 'string' || !value.startsWith('{{') || !value.endsWith('}}')) {
443
503
  return value;
@@ -474,32 +534,38 @@ function matchCondition(
474
534
  item: CMSItem,
475
535
  condition: CMSFilterCondition,
476
536
  cmsContext?: Record<string, unknown>,
477
- templateContext?: TemplateContext
537
+ templateContext?: TemplateContext,
478
538
  ): boolean {
479
539
  const value = item[condition.field];
480
540
  const target = resolveTemplateValue(condition.value, cmsContext, templateContext);
481
541
  const op: CMSFilterOperator = condition.operator || 'eq';
482
542
 
483
543
  switch (op) {
484
- case 'eq': return value === target;
485
- case 'neq': return value !== target;
486
- case 'gt': return typeof value === 'number' && typeof target === 'number' && value > target;
487
- case 'gte': return typeof value === 'number' && typeof target === 'number' && value >= target;
488
- case 'lt': return typeof value === 'number' && typeof target === 'number' && value < target;
489
- case 'lte': return typeof value === 'number' && typeof target === 'number' && value <= target;
490
- case 'contains': return typeof value === 'string' && typeof target === 'string' && value.includes(target);
491
- case 'in': return Array.isArray(target) && target.includes(value);
492
- default: return false;
544
+ case 'eq':
545
+ return value === target;
546
+ case 'neq':
547
+ return value !== target;
548
+ case 'gt':
549
+ return typeof value === 'number' && typeof target === 'number' && value > target;
550
+ case 'gte':
551
+ return typeof value === 'number' && typeof target === 'number' && value >= target;
552
+ case 'lt':
553
+ return typeof value === 'number' && typeof target === 'number' && value < target;
554
+ case 'lte':
555
+ return typeof value === 'number' && typeof target === 'number' && value <= target;
556
+ case 'contains':
557
+ return typeof value === 'string' && typeof target === 'string' && value.includes(target);
558
+ case 'in':
559
+ return Array.isArray(target) && target.includes(value);
560
+ default:
561
+ return false;
493
562
  }
494
563
  }
495
564
 
496
565
  /**
497
566
  * Apply sorting to items (client-side)
498
567
  */
499
- function applyClientSorting(
500
- items: CMSItem[],
501
- sort: CMSSortConfig | CMSSortConfig[]
502
- ): CMSItem[] {
568
+ function applyClientSorting(items: CMSItem[], sort: CMSSortConfig | CMSSortConfig[]): CMSItem[] {
503
569
  const sorts = Array.isArray(sort) ? sort : [sort];
504
570
 
505
571
  return [...items].sort((a, b) => {