meno-core 1.0.52 → 1.0.54

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 (399) hide show
  1. package/.claude/settings.local.json +1 -3
  2. package/bin/cli.ts +48 -57
  3. package/build-astro.ts +296 -108
  4. package/build-next.ts +1374 -0
  5. package/build-static.test.ts +39 -10
  6. package/build-static.ts +127 -127
  7. package/dist/bin/cli.js +34 -38
  8. package/dist/bin/cli.js.map +2 -2
  9. package/dist/build-static.js +12 -11
  10. package/dist/chunks/chunk-2AR55GYH.js +42 -0
  11. package/dist/chunks/chunk-2AR55GYH.js.map +7 -0
  12. package/dist/chunks/{chunk-CXCBV2M7.js → chunk-2FN4UOVO.js} +581 -502
  13. package/dist/chunks/chunk-2FN4UOVO.js.map +7 -0
  14. package/dist/chunks/chunk-3XER4E5W.js +168 -0
  15. package/dist/chunks/chunk-3XER4E5W.js.map +7 -0
  16. package/dist/chunks/chunk-5ETZFREW.js +514 -0
  17. package/dist/chunks/chunk-5ETZFREW.js.map +7 -0
  18. package/dist/chunks/{chunk-2MHDV5BF.js → chunk-7E4IF5L7.js} +15 -21
  19. package/dist/chunks/chunk-7E4IF5L7.js.map +7 -0
  20. package/dist/chunks/{chunk-7NIC4I3V.js → chunk-7HWQUVTU.js} +1691 -1316
  21. package/dist/chunks/chunk-7HWQUVTU.js.map +7 -0
  22. package/dist/chunks/{chunk-EDQSMAMP.js → chunk-AE3QK5QW.js} +189 -19
  23. package/dist/chunks/chunk-AE3QK5QW.js.map +7 -0
  24. package/dist/chunks/{chunk-HNLUO36W.js → chunk-F6KTJYGV.js} +8 -8
  25. package/dist/chunks/chunk-F6KTJYGV.js.map +7 -0
  26. package/dist/chunks/{chunk-WQFG7PAH.js → chunk-FZITJSSS.js} +2 -6
  27. package/dist/chunks/chunk-FZITJSSS.js.map +7 -0
  28. package/dist/chunks/{chunk-H4JSCDNW.js → chunk-GSYYA5GX.js} +25 -2
  29. package/dist/chunks/chunk-GSYYA5GX.js.map +7 -0
  30. package/dist/chunks/{chunk-2QK6U5UK.js → chunk-HIZMY3EP.js} +12 -2
  31. package/dist/chunks/chunk-HIZMY3EP.js.map +7 -0
  32. package/dist/chunks/{chunk-AZQYF6KE.js → chunk-I2WEGYA7.js} +41 -176
  33. package/dist/chunks/chunk-I2WEGYA7.js.map +7 -0
  34. package/dist/chunks/{chunk-I7YIGZXT.js → chunk-JNO3CNLJ.js} +6 -9
  35. package/dist/chunks/chunk-JNO3CNLJ.js.map +7 -0
  36. package/dist/chunks/{chunk-UB44F4Z2.js → chunk-NVRBTSQG.js} +2 -4
  37. package/dist/chunks/chunk-NVRBTSQG.js.map +7 -0
  38. package/dist/chunks/{chunk-LHLHPYSP.js → chunk-Q4OBWKXG.js} +48 -40
  39. package/dist/chunks/chunk-Q4OBWKXG.js.map +7 -0
  40. package/dist/chunks/{chunk-A725KYFK.js → chunk-QTE32Y53.js} +780 -323
  41. package/dist/chunks/chunk-QTE32Y53.js.map +7 -0
  42. package/dist/chunks/{chunk-LPVETICS.js → chunk-STDY3OVM.js} +397 -84
  43. package/dist/chunks/chunk-STDY3OVM.js.map +7 -0
  44. package/dist/chunks/configService-PRJZF7Y6.js +14 -0
  45. package/dist/chunks/{constants-GWBAD66U.js → constants-KIQEYMAM.js} +2 -2
  46. package/dist/chunks/{fs-JGINUXGL.js → fs-ZI5JEU7V.js} +2 -2
  47. package/dist/entries/server-router.js +14 -19
  48. package/dist/entries/server-router.js.map +2 -2
  49. package/dist/lib/client/index.js +957 -356
  50. package/dist/lib/client/index.js.map +4 -4
  51. package/dist/lib/server/index.js +1538 -328
  52. package/dist/lib/server/index.js.map +4 -4
  53. package/dist/lib/shared/index.js +277 -73
  54. package/dist/lib/shared/index.js.map +4 -4
  55. package/dist/lib/shared/richtext/index.js +1 -1
  56. package/dist/lib/test-utils/index.js +38 -60
  57. package/dist/lib/test-utils/index.js.map +2 -2
  58. package/entries/client-router.tsx +14 -172
  59. package/entries/server-router.tsx +1 -7
  60. package/lib/client/ClientInitializer.ts +8 -8
  61. package/lib/client/ErrorBoundary.test.tsx +156 -151
  62. package/lib/client/ErrorBoundary.tsx +184 -121
  63. package/lib/client/componentRegistry.test.ts +96 -108
  64. package/lib/client/componentRegistry.ts +1 -2
  65. package/lib/client/contexts/ThemeContext.tsx +3 -2
  66. package/lib/client/core/ComponentBuilder.test.ts +513 -560
  67. package/lib/client/core/ComponentBuilder.ts +335 -146
  68. package/lib/client/core/ComponentRenderer.test.tsx +1 -2
  69. package/lib/client/core/ComponentRenderer.tsx +46 -33
  70. package/lib/client/core/builders/embedBuilder.ts +246 -54
  71. package/lib/client/core/builders/linkBuilder.ts +71 -44
  72. package/lib/client/core/builders/linkNodeBuilder.ts +78 -53
  73. package/lib/client/core/builders/listBuilder.ts +137 -89
  74. package/lib/client/core/builders/localeListBuilder.ts +95 -60
  75. package/lib/client/core/builders/types.ts +5 -5
  76. package/lib/client/core/cmsTemplateProcessor.ts +7 -7
  77. package/lib/client/elementRegistry.ts +3 -3
  78. package/lib/client/fontFamiliesService.test.ts +68 -0
  79. package/lib/client/fontFamiliesService.ts +69 -0
  80. package/lib/client/hmr/HMRManager.tsx +8 -0
  81. package/lib/client/hmrCssReload.ts +166 -0
  82. package/lib/client/hmrWebSocket.ts +9 -14
  83. package/lib/client/hooks/useColorVariables.test.ts +21 -21
  84. package/lib/client/hooks/useColorVariables.ts +14 -10
  85. package/lib/client/hooks/usePropertyAutocomplete.ts +3 -5
  86. package/lib/client/hooks/useVariables.ts +4 -4
  87. package/lib/client/hydration/HydrationUtils.test.ts +24 -25
  88. package/lib/client/hydration/HydrationUtils.ts +3 -4
  89. package/lib/client/i18nConfigService.test.ts +2 -7
  90. package/lib/client/i18nConfigService.ts +2 -2
  91. package/lib/client/index.ts +4 -0
  92. package/lib/client/meno-filter/MenoFilter.test.ts +19 -21
  93. package/lib/client/meno-filter/MenoFilter.ts +5 -9
  94. package/lib/client/meno-filter/bindings.ts +15 -40
  95. package/lib/client/meno-filter/init.ts +1 -1
  96. package/lib/client/meno-filter/renderer.ts +23 -29
  97. package/lib/client/meno-filter/script.generated.ts +1 -3
  98. package/lib/client/meno-filter/ui.ts +5 -5
  99. package/lib/client/meno-filter/updates.ts +15 -21
  100. package/lib/client/navigation.test.ts +159 -159
  101. package/lib/client/navigation.ts +0 -1
  102. package/lib/client/responsiveStyleResolver.test.ts +230 -228
  103. package/lib/client/responsiveStyleResolver.ts +13 -16
  104. package/lib/client/routing/RouteLoader.test.ts +25 -26
  105. package/lib/client/routing/RouteLoader.ts +30 -37
  106. package/lib/client/routing/Router.tsx +112 -18
  107. package/lib/client/scripts/ScriptExecutor.test.ts +270 -128
  108. package/lib/client/scripts/ScriptExecutor.ts +69 -33
  109. package/lib/client/services/PrefetchService.test.ts +2 -2
  110. package/lib/client/services/PrefetchService.ts +10 -24
  111. package/lib/client/styleProcessor.test.ts +9 -9
  112. package/lib/client/styleProcessor.ts +18 -15
  113. package/lib/client/styles/StyleInjector.test.ts +122 -115
  114. package/lib/client/styles/StyleInjector.ts +25 -7
  115. package/lib/client/styles/UtilityClassCollector.ts +26 -27
  116. package/lib/client/styles/cspNonce.test.ts +64 -0
  117. package/lib/client/styles/cspNonce.ts +63 -0
  118. package/lib/client/templateEngine.test.ts +600 -448
  119. package/lib/client/templateEngine.ts +205 -64
  120. package/lib/client/theme.ts +0 -1
  121. package/lib/client/utils/toast.ts +0 -1
  122. package/lib/server/__integration__/api-routes.test.ts +8 -4
  123. package/lib/server/__integration__/cms-integration.test.ts +1 -4
  124. package/lib/server/__integration__/server-lifecycle.test.ts +2 -5
  125. package/lib/server/__integration__/ssr-rendering.test.ts +47 -37
  126. package/lib/server/__integration__/static-assets.test.ts +1 -1
  127. package/lib/server/__integration__/test-helpers.ts +84 -70
  128. package/lib/server/ab/generateFunctions.ts +12 -10
  129. package/lib/server/astro/cmsPageEmitter.ts +47 -32
  130. package/lib/server/astro/componentEmitter.ts +82 -37
  131. package/lib/server/astro/cssCollector.ts +10 -26
  132. package/lib/server/astro/nodeToAstro.test.ts +1750 -30
  133. package/lib/server/astro/nodeToAstro.ts +327 -178
  134. package/lib/server/astro/normalizeOrphanTemplateProps.test.ts +260 -0
  135. package/lib/server/astro/normalizeOrphanTemplateProps.ts +176 -0
  136. package/lib/server/astro/pageEmitter.ts +9 -13
  137. package/lib/server/astro/tailwindMapper.test.ts +10 -37
  138. package/lib/server/astro/tailwindMapper.ts +33 -40
  139. package/lib/server/astro/templateTransformer.ts +14 -17
  140. package/lib/server/createServer.ts +16 -17
  141. package/lib/server/cssGenerator.test.ts +35 -44
  142. package/lib/server/cssGenerator.ts +6 -17
  143. package/lib/server/draftPageStore.ts +49 -0
  144. package/lib/server/fileWatcher.test.ts +124 -10
  145. package/lib/server/fileWatcher.ts +164 -98
  146. package/lib/server/index.ts +20 -2
  147. package/lib/server/jsonLoader.test.ts +39 -2
  148. package/lib/server/jsonLoader.ts +33 -31
  149. package/lib/server/middleware/cors.test.ts +20 -20
  150. package/lib/server/middleware/cors.ts +28 -4
  151. package/lib/server/middleware/errorHandler.test.ts +5 -3
  152. package/lib/server/middleware/errorHandler.ts +3 -8
  153. package/lib/server/middleware/index.ts +0 -1
  154. package/lib/server/middleware/logger.test.ts +7 -5
  155. package/lib/server/middleware/logger.ts +10 -22
  156. package/lib/server/pageCache.test.ts +76 -77
  157. package/lib/server/pageCache.ts +0 -1
  158. package/lib/server/projectContext.ts +4 -3
  159. package/lib/server/providers/fileSystemCMSProvider.test.ts +124 -95
  160. package/lib/server/providers/fileSystemCMSProvider.ts +35 -20
  161. package/lib/server/providers/fileSystemPageProvider.test.ts +84 -0
  162. package/lib/server/providers/fileSystemPageProvider.ts +39 -12
  163. package/lib/server/routes/api/cms.test.ts +26 -14
  164. package/lib/server/routes/api/cms.ts +9 -14
  165. package/lib/server/routes/api/components.ts +35 -34
  166. package/lib/server/routes/api/config.ts +0 -1
  167. package/lib/server/routes/api/core-routes.ts +49 -76
  168. package/lib/server/routes/api/functions.ts +8 -16
  169. package/lib/server/routes/api/index.ts +3 -6
  170. package/lib/server/routes/api/pages.ts +21 -32
  171. package/lib/server/routes/api/shared.test.ts +1 -1
  172. package/lib/server/routes/api/shared.ts +65 -6
  173. package/lib/server/routes/api/variables.test.ts +1 -3
  174. package/lib/server/routes/api/variables.ts +1 -1
  175. package/lib/server/routes/index.ts +106 -19
  176. package/lib/server/routes/pages.ts +47 -35
  177. package/lib/server/routes/static.ts +16 -4
  178. package/lib/server/runtime/bundler.ts +47 -32
  179. package/lib/server/runtime/fs.ts +3 -13
  180. package/lib/server/runtime/httpServer.ts +5 -9
  181. package/lib/server/services/ColorService.ts +32 -27
  182. package/lib/server/services/EnumService.test.ts +2 -6
  183. package/lib/server/services/EnumService.ts +7 -2
  184. package/lib/server/services/VariableService.test.ts +1 -5
  185. package/lib/server/services/VariableService.ts +6 -1
  186. package/lib/server/services/cmsService.test.ts +116 -78
  187. package/lib/server/services/cmsService.ts +24 -54
  188. package/lib/server/services/componentService.test.ts +303 -20
  189. package/lib/server/services/componentService.ts +397 -76
  190. package/lib/server/services/configService.test.ts +9 -31
  191. package/lib/server/services/configService.ts +20 -27
  192. package/lib/server/services/fileWatcherService.ts +44 -30
  193. package/lib/server/services/index.ts +0 -1
  194. package/lib/server/services/pageService.test.ts +24 -3
  195. package/lib/server/services/pageService.ts +135 -16
  196. package/lib/server/ssr/attributeBuilder.ts +24 -8
  197. package/lib/server/ssr/buildErrorOverlay.ts +12 -13
  198. package/lib/server/ssr/clientDataInjector.ts +7 -21
  199. package/lib/server/ssr/cmsSSRProcessor.ts +3 -6
  200. package/lib/server/ssr/cssCollector.ts +1 -1
  201. package/lib/server/ssr/errorOverlay.test.ts +21 -2
  202. package/lib/server/ssr/errorOverlay.ts +39 -18
  203. package/lib/server/ssr/htmlGenerator.nonce.test.ts +3 -9
  204. package/lib/server/ssr/htmlGenerator.test.ts +173 -56
  205. package/lib/server/ssr/htmlGenerator.ts +191 -112
  206. package/lib/server/ssr/imageMetadata.test.ts +3 -1
  207. package/lib/server/ssr/imageMetadata.ts +25 -19
  208. package/lib/server/ssr/jsCollector.test.ts +3 -13
  209. package/lib/server/ssr/jsCollector.ts +3 -8
  210. package/lib/server/ssr/liveReloadIntegration.test.ts +184 -22
  211. package/lib/server/ssr/metaTagGenerator.ts +2 -2
  212. package/lib/server/ssr/ssrRenderer.branches.test.ts +1103 -0
  213. package/lib/server/ssr/ssrRenderer.test.ts +263 -246
  214. package/lib/server/ssr/ssrRenderer.ts +700 -231
  215. package/lib/server/ssrRenderer.test.ts +1044 -950
  216. package/lib/server/utils/jsonLineMapper.test.ts +28 -28
  217. package/lib/server/utils/jsonLineMapper.ts +1 -1
  218. package/lib/server/validateStyleCoverage.ts +18 -20
  219. package/lib/server/webflow/buildWebflow.ts +41 -53
  220. package/lib/server/webflow/nodeToWebflow.test.ts +150 -218
  221. package/lib/server/webflow/nodeToWebflow.ts +195 -258
  222. package/lib/server/webflow/styleMapper.test.ts +15 -56
  223. package/lib/server/webflow/styleMapper.ts +33 -41
  224. package/lib/server/webflow/types.ts +1 -8
  225. package/lib/server/websocketManager.ts +16 -20
  226. package/lib/shared/attributeNodeUtils.test.ts +15 -15
  227. package/lib/shared/attributeNodeUtils.ts +5 -12
  228. package/lib/shared/breakpoints.ts +4 -11
  229. package/lib/shared/cmsQueryParser.test.ts +50 -42
  230. package/lib/shared/cmsQueryParser.ts +49 -31
  231. package/lib/shared/colorVariableUtils.test.ts +5 -5
  232. package/lib/shared/colorVariableUtils.ts +3 -8
  233. package/lib/shared/componentRefs.ts +41 -0
  234. package/lib/shared/constants.test.ts +3 -3
  235. package/lib/shared/constants.ts +12 -8
  236. package/lib/shared/cssGeneration.test.ts +262 -144
  237. package/lib/shared/cssGeneration.ts +189 -514
  238. package/lib/shared/cssNamedColors.ts +152 -30
  239. package/lib/shared/cssProperties.test.ts +5 -6
  240. package/lib/shared/cssProperties.ts +479 -111
  241. package/lib/shared/elementClassName.test.ts +109 -109
  242. package/lib/shared/elementClassName.ts +1 -1
  243. package/lib/shared/elementUtils.ts +12 -16
  244. package/lib/shared/errorLogger.ts +2 -10
  245. package/lib/shared/errors.test.ts +2 -13
  246. package/lib/shared/errors.ts +2 -8
  247. package/lib/shared/expressionEvaluator.test.ts +119 -0
  248. package/lib/shared/expressionEvaluator.ts +95 -22
  249. package/lib/shared/fontCss.ts +101 -0
  250. package/lib/shared/fontLoader.test.ts +19 -5
  251. package/lib/shared/fontLoader.ts +8 -86
  252. package/lib/shared/friendlyError.test.ts +87 -0
  253. package/lib/shared/friendlyError.ts +120 -0
  254. package/lib/shared/gradientUtils.test.ts +1 -5
  255. package/lib/shared/gradientUtils.ts +2 -6
  256. package/lib/shared/hrefRefs.test.ts +130 -0
  257. package/lib/shared/hrefRefs.ts +92 -0
  258. package/lib/shared/i18n.test.ts +1 -1
  259. package/lib/shared/i18n.ts +13 -34
  260. package/lib/shared/index.ts +56 -0
  261. package/lib/shared/inlineSvgStyleRules.test.ts +108 -0
  262. package/lib/shared/inlineSvgStyleRules.ts +132 -0
  263. package/lib/shared/interactiveStyleMappings.test.ts +11 -33
  264. package/lib/shared/interactiveStyleMappings.ts +9 -16
  265. package/lib/shared/interactiveStyles.test.ts +165 -188
  266. package/lib/shared/interfaces/contentProvider.ts +14 -1
  267. package/lib/shared/itemTemplateUtils.test.ts +20 -12
  268. package/lib/shared/itemTemplateUtils.ts +23 -36
  269. package/lib/shared/jsonRepair.ts +8 -2
  270. package/lib/shared/libraryLoader.test.ts +15 -49
  271. package/lib/shared/libraryLoader.ts +7 -22
  272. package/lib/shared/netlifyLocale404.test.ts +179 -0
  273. package/lib/shared/netlifyLocale404.ts +110 -0
  274. package/lib/shared/nodeUtils.test.ts +24 -16
  275. package/lib/shared/nodeUtils.ts +49 -19
  276. package/lib/shared/pathArrayUtils.test.ts +1 -2
  277. package/lib/shared/pathArrayUtils.ts +1 -1
  278. package/lib/shared/pathSecurity.ts +1 -1
  279. package/lib/shared/pathUtils.test.ts +4 -6
  280. package/lib/shared/pathUtils.ts +42 -48
  281. package/lib/shared/paths/Path.test.ts +2 -2
  282. package/lib/shared/paths/Path.ts +0 -1
  283. package/lib/shared/paths/PathConverter.test.ts +1 -1
  284. package/lib/shared/paths/PathConverter.ts +14 -17
  285. package/lib/shared/paths/PathUtils.ts +9 -10
  286. package/lib/shared/paths/PathValidator.test.ts +2 -15
  287. package/lib/shared/paths/PathValidator.ts +11 -9
  288. package/lib/shared/paths/index.ts +1 -2
  289. package/lib/shared/propResolver.test.ts +240 -244
  290. package/lib/shared/propResolver.ts +14 -25
  291. package/lib/shared/pxToRem.test.ts +7 -6
  292. package/lib/shared/pxToRem.ts +2 -5
  293. package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +9 -5
  294. package/lib/shared/registry/ClientRegistry.ts +0 -1
  295. package/lib/shared/registry/ComponentRegistry.test.ts +43 -29
  296. package/lib/shared/registry/ComponentRegistry.ts +9 -11
  297. package/lib/shared/registry/NodeTypeDefinition.ts +15 -8
  298. package/lib/shared/registry/RegistryManager.ts +1 -2
  299. package/lib/shared/registry/SSRRegistry.ts +0 -1
  300. package/lib/shared/registry/createNodeType.ts +7 -9
  301. package/lib/shared/registry/defineNodeType.ts +2 -6
  302. package/lib/shared/registry/index.ts +0 -1
  303. package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +14 -15
  304. package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +18 -11
  305. package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +47 -18
  306. package/lib/shared/registry/nodeTypes/LinkNodeType.ts +22 -20
  307. package/lib/shared/registry/nodeTypes/ListNodeType.ts +78 -74
  308. package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +27 -21
  309. package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +6 -7
  310. package/lib/shared/registry/nodeTypes/index.ts +10 -2
  311. package/lib/shared/responsiveScaling.test.ts +15 -31
  312. package/lib/shared/responsiveScaling.ts +55 -37
  313. package/lib/shared/responsiveStyleUtils.ts +11 -13
  314. package/lib/shared/richtext/htmlToTiptap.test.ts +23 -14
  315. package/lib/shared/richtext/htmlToTiptap.ts +1 -3
  316. package/lib/shared/richtext/tiptapToHtml.test.ts +5 -6
  317. package/lib/shared/richtext/types.ts +1 -8
  318. package/lib/shared/slugTranslator.test.ts +37 -13
  319. package/lib/shared/slugTranslator.ts +31 -11
  320. package/lib/shared/slugify.ts +9 -15
  321. package/lib/shared/styleNodeUtils.test.ts +8 -8
  322. package/lib/shared/styleNodeUtils.ts +9 -11
  323. package/lib/shared/styleUtils.test.ts +87 -61
  324. package/lib/shared/styleUtils.ts +5 -6
  325. package/lib/shared/themeDefaults.test.ts +11 -11
  326. package/lib/shared/themeDefaults.ts +3 -4
  327. package/lib/shared/tree/PathBuilder.test.ts +160 -109
  328. package/lib/shared/tree/PathBuilder.ts +121 -59
  329. package/lib/shared/treePathUtils.test.ts +2 -10
  330. package/lib/shared/treePathUtils.ts +54 -59
  331. package/lib/shared/types/api.ts +1 -2
  332. package/lib/shared/types/cms.ts +25 -21
  333. package/lib/shared/types/comment.ts +132 -0
  334. package/lib/shared/types/components.ts +27 -25
  335. package/lib/shared/types/errors.test.ts +1 -6
  336. package/lib/shared/types/errors.ts +3 -7
  337. package/lib/shared/types/experiments.ts +28 -28
  338. package/lib/shared/types/index.ts +14 -2
  339. package/lib/shared/types/rendering.ts +8 -0
  340. package/lib/shared/types/styles.ts +0 -1
  341. package/lib/shared/types/variables.test.ts +4 -13
  342. package/lib/shared/types/variables.ts +48 -27
  343. package/lib/shared/types.ts +1 -2
  344. package/lib/shared/utilityClassConfig.ts +648 -319
  345. package/lib/shared/utilityClassMapper.test.ts +213 -78
  346. package/lib/shared/utilityClassMapper.ts +188 -246
  347. package/lib/shared/utilityClassNames.ts +326 -0
  348. package/lib/shared/utils.test.ts +2 -10
  349. package/lib/shared/utils.ts +19 -10
  350. package/lib/shared/validation/cmsValidators.ts +2 -1
  351. package/lib/shared/validation/commentValidators.test.ts +53 -0
  352. package/lib/shared/validation/commentValidators.ts +80 -0
  353. package/lib/shared/validation/index.ts +1 -0
  354. package/lib/shared/validation/propValidator.test.ts +18 -20
  355. package/lib/shared/validation/propValidator.ts +12 -17
  356. package/lib/shared/validation/schemas.test.ts +24 -33
  357. package/lib/shared/validation/schemas.ts +469 -344
  358. package/lib/shared/validation/validators.test.ts +1 -6
  359. package/lib/shared/validation/validators.ts +89 -68
  360. package/lib/shared/viewportUnits.integration.test.ts +46 -0
  361. package/lib/shared/viewportUnits.test.ts +91 -0
  362. package/lib/shared/viewportUnits.ts +63 -0
  363. package/lib/test-utils/dom-setup.ts +7 -1
  364. package/lib/test-utils/factories/ConsoleMockFactory.ts +3 -7
  365. package/lib/test-utils/factories/DomMockFactory.ts +7 -19
  366. package/lib/test-utils/factories/EventMockFactory.ts +7 -13
  367. package/lib/test-utils/factories/FetchMockFactory.ts +39 -57
  368. package/lib/test-utils/factories/ServerMockFactory.ts +5 -9
  369. package/lib/test-utils/factories/StoreMockFactory.ts +14 -25
  370. package/lib/test-utils/fixtures.ts +45 -45
  371. package/lib/test-utils/helpers/asyncHelpers.test.ts +15 -18
  372. package/lib/test-utils/helpers/asyncHelpers.ts +11 -20
  373. package/lib/test-utils/helpers.ts +1 -5
  374. package/lib/test-utils/index.ts +0 -4
  375. package/lib/test-utils/mockFactories.ts +12 -18
  376. package/lib/test-utils/mocks.ts +4 -2
  377. package/package.json +1 -1
  378. package/scripts/build-meno-filter.ts +1 -4
  379. package/vite.config.ts +4 -4
  380. package/dist/chunks/chunk-2MHDV5BF.js.map +0 -7
  381. package/dist/chunks/chunk-2QK6U5UK.js.map +0 -7
  382. package/dist/chunks/chunk-7NIC4I3V.js.map +0 -7
  383. package/dist/chunks/chunk-A725KYFK.js.map +0 -7
  384. package/dist/chunks/chunk-AZQYF6KE.js.map +0 -7
  385. package/dist/chunks/chunk-CXCBV2M7.js.map +0 -7
  386. package/dist/chunks/chunk-EDQSMAMP.js.map +0 -7
  387. package/dist/chunks/chunk-H4JSCDNW.js.map +0 -7
  388. package/dist/chunks/chunk-HNLUO36W.js.map +0 -7
  389. package/dist/chunks/chunk-I7YIGZXT.js.map +0 -7
  390. package/dist/chunks/chunk-J23ZX5AP.js +0 -241
  391. package/dist/chunks/chunk-J23ZX5AP.js.map +0 -7
  392. package/dist/chunks/chunk-LHLHPYSP.js.map +0 -7
  393. package/dist/chunks/chunk-LPVETICS.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/configService-R3OGU2UD.js +0 -13
  397. /package/dist/chunks/{configService-R3OGU2UD.js.map → configService-PRJZF7Y6.js.map} +0 -0
  398. /package/dist/chunks/{constants-GWBAD66U.js.map → constants-KIQEYMAM.js.map} +0 -0
  399. /package/dist/chunks/{fs-JGINUXGL.js.map → fs-ZI5JEU7V.js.map} +0 -0
@@ -83,7 +83,7 @@ describe('MenoFilter', () => {
83
83
  Promise.resolve({
84
84
  ok: true,
85
85
  json: () => Promise.resolve(sampleItems),
86
- })
86
+ }),
87
87
  );
88
88
  const originalFetch = (globalThis as any).fetch;
89
89
  (globalThis as any).fetch = mockFetch;
@@ -127,7 +127,7 @@ describe('MenoFilter', () => {
127
127
  Promise.resolve({
128
128
  ok: false,
129
129
  status: 404,
130
- })
130
+ }),
131
131
  );
132
132
  const originalFetch = (globalThis as any).fetch;
133
133
  (globalThis as any).fetch = mockFetch;
@@ -157,7 +157,7 @@ describe('MenoFilter', () => {
157
157
  Promise.resolve({
158
158
  ok: true,
159
159
  json: () => Promise.resolve([{ _id: 'fetch', title: 'Fetch Data' }]),
160
- })
160
+ }),
161
161
  );
162
162
  const originalFetch = (globalThis as any).fetch;
163
163
  (globalThis as any).fetch = mockFetch;
@@ -193,7 +193,7 @@ describe('MenoFilter', () => {
193
193
  const result = filter.filter({ category: 'tech' });
194
194
 
195
195
  expect(result).toHaveLength(3);
196
- expect(result.every(item => item.category === 'tech')).toBe(true);
196
+ expect(result.every((item) => item.category === 'tech')).toBe(true);
197
197
  });
198
198
 
199
199
  test('should filter by multiple criteria (AND logic)', () => {
@@ -217,7 +217,7 @@ describe('MenoFilter', () => {
217
217
 
218
218
  test('should emit to subscribers on filter', () => {
219
219
  let emittedItems: CMSItemClient[] = [];
220
- filter.subscribe(items => {
220
+ filter.subscribe((items) => {
221
221
  emittedItems = items;
222
222
  });
223
223
 
@@ -245,8 +245,8 @@ describe('MenoFilter', () => {
245
245
  const result = filter.search('world');
246
246
 
247
247
  expect(result).toHaveLength(2);
248
- expect(result.map(i => i.title)).toContain('Hello World');
249
- expect(result.map(i => i.title)).toContain('Goodbye World');
248
+ expect(result.map((i) => i.title)).toContain('Hello World');
249
+ expect(result.map((i) => i.title)).toContain('Goodbye World');
250
250
  });
251
251
 
252
252
  test('should search specified fields only', () => {
@@ -337,9 +337,7 @@ describe('MenoFilter', () => {
337
337
  });
338
338
 
339
339
  test('should find item by _filename', () => {
340
- const itemsWithFilename = [
341
- { _id: '1', _filename: 'hello-world', title: 'Hello' },
342
- ];
340
+ const itemsWithFilename = [{ _id: '1', _filename: 'hello-world', title: 'Hello' }];
343
341
  const mockElement = {
344
342
  textContent: JSON.stringify(itemsWithFilename),
345
343
  };
@@ -424,7 +422,7 @@ describe('MenoFilter', () => {
424
422
  await filter.init();
425
423
 
426
424
  let receivedItems: CMSItemClient[] = [];
427
- filter.subscribe(items => {
425
+ filter.subscribe((items) => {
428
426
  receivedItems = items;
429
427
  });
430
428
 
@@ -648,7 +646,7 @@ describe('MenoFilter', () => {
648
646
  await filter.init();
649
647
 
650
648
  let receivedItems: CMSItemClient[] = [];
651
- filter.subscribe(items => {
649
+ filter.subscribe((items) => {
652
650
  receivedItems = items;
653
651
  });
654
652
 
@@ -701,7 +699,7 @@ describe('MenoFilter', () => {
701
699
  const result = filter.filter({ price: { $gt: 100 } });
702
700
 
703
701
  expect(result).toHaveLength(2);
704
- expect(result.map(i => i.price)).toEqual([200, 150]);
702
+ expect(result.map((i) => i.price)).toEqual([200, 150]);
705
703
  });
706
704
 
707
705
  test('should filter numbers from string criteria', async () => {
@@ -718,7 +716,7 @@ describe('MenoFilter', () => {
718
716
  const result = filter.filter({ price: { $gte: '100', $lte: '150' } });
719
717
 
720
718
  expect(result).toHaveLength(2);
721
- expect(result.map(i => i.price)).toEqual([100, 150]);
719
+ expect(result.map((i) => i.price)).toEqual([100, 150]);
722
720
  });
723
721
 
724
722
  test('should filter dates with type coercion', async () => {
@@ -735,7 +733,7 @@ describe('MenoFilter', () => {
735
733
  const result = filter.filter({ date: { $gt: '2024-01-31' } });
736
734
 
737
735
  expect(result).toHaveLength(2);
738
- expect(result.map(i => i.date)).toEqual(['2024-02-15', '2024-03-15']);
736
+ expect(result.map((i) => i.date)).toEqual(['2024-02-15', '2024-03-15']);
739
737
  });
740
738
 
741
739
  test('should filter booleans with type coercion', async () => {
@@ -752,7 +750,7 @@ describe('MenoFilter', () => {
752
750
  const result = filter.filter({ featured: 'true' });
753
751
 
754
752
  expect(result).toHaveLength(2);
755
- expect(result.every(i => i.featured === true)).toBe(true);
753
+ expect(result.every((i) => i.featured === true)).toBe(true);
756
754
  });
757
755
 
758
756
  test('setFieldType() should update field types', async () => {
@@ -773,7 +771,7 @@ describe('MenoFilter', () => {
773
771
  result = filter.filter({ price: { $gt: 100 } });
774
772
 
775
773
  expect(result).toHaveLength(2);
776
- expect(result.map(i => i.price)).toEqual([200, 150]);
774
+ expect(result.map((i) => i.price)).toEqual([200, 150]);
777
775
  });
778
776
 
779
777
  test('setFieldTypes() should set multiple types', async () => {
@@ -820,7 +818,7 @@ describe('MenoFilter', () => {
820
818
  const result = filter.filterRange('price', { min: 100, max: 200 });
821
819
 
822
820
  expect(result).toHaveLength(3);
823
- expect(result.map(i => i.price).sort((a, b) => (a as number) - (b as number))).toEqual([100, 150, 200]);
821
+ expect(result.map((i) => i.price).sort((a, b) => (a as number) - (b as number))).toEqual([100, 150, 200]);
824
822
  });
825
823
 
826
824
  test('should filter with only min', async () => {
@@ -836,7 +834,7 @@ describe('MenoFilter', () => {
836
834
  const result = filter.filterRange('price', { min: 150 });
837
835
 
838
836
  expect(result).toHaveLength(3);
839
- expect(result.every(i => (i.price as number) >= 150)).toBe(true);
837
+ expect(result.every((i) => (i.price as number) >= 150)).toBe(true);
840
838
  });
841
839
 
842
840
  test('should filter with only max', async () => {
@@ -852,7 +850,7 @@ describe('MenoFilter', () => {
852
850
  const result = filter.filterRange('price', { max: 100 });
853
851
 
854
852
  expect(result).toHaveLength(2);
855
- expect(result.every(i => (i.price as number) <= 100)).toBe(true);
853
+ expect(result.every((i) => (i.price as number) <= 100)).toBe(true);
856
854
  });
857
855
 
858
856
  test('should clear filter when both min and max are empty', async () => {
@@ -956,7 +954,7 @@ describe('MenoFilter', () => {
956
954
  const result = filter.search('recat', ['title']);
957
955
 
958
956
  expect(result.length).toBeGreaterThanOrEqual(1);
959
- expect(result.some(i => i.title === 'React')).toBe(true);
957
+ expect(result.some((i) => i.title === 'React')).toBe(true);
960
958
  });
961
959
 
962
960
  test('should use exact match for short queries (< 3 chars)', async () => {
@@ -797,9 +797,7 @@ export class MenoFilter {
797
797
  }
798
798
 
799
799
  const queryString = params.toString();
800
- const url = queryString
801
- ? `${window.location.pathname}?${queryString}`
802
- : window.location.pathname;
800
+ const url = queryString ? `${window.location.pathname}?${queryString}` : window.location.pathname;
803
801
 
804
802
  if (this.urlMode === 'push') {
805
803
  history.pushState(null, '', url);
@@ -1082,17 +1080,14 @@ export class MenoFilter {
1082
1080
 
1083
1081
  // Apply OR criteria
1084
1082
  if (this.orCriteria.length > 0) {
1085
- result = result.filter((item) =>
1086
- this.orCriteria.some((criteria) => this.matchesCriteria(item, criteria))
1087
- );
1083
+ result = result.filter((item) => this.orCriteria.some((criteria) => this.matchesCriteria(item, criteria)));
1088
1084
  }
1089
1085
 
1090
1086
  // Apply search
1091
1087
  if (this.searchQuery) {
1092
1088
  const query = this.searchQuery;
1093
1089
  result = result.filter((item) => {
1094
- const fieldsToSearch =
1095
- this.searchFields.length > 0 ? this.searchFields : Object.keys(item);
1090
+ const fieldsToSearch = this.searchFields.length > 0 ? this.searchFields : Object.keys(item);
1096
1091
  return fieldsToSearch.some((field) => {
1097
1092
  const value = item[field];
1098
1093
  if (typeof value !== 'string') return false;
@@ -1259,13 +1254,14 @@ export class MenoFilter {
1259
1254
  case '$nin':
1260
1255
  return Array.isArray(opValue) ? !opValue.includes(itemValue) : true;
1261
1256
 
1262
- case '$empty':
1257
+ case '$empty': {
1263
1258
  const isEmpty =
1264
1259
  itemValue === undefined ||
1265
1260
  itemValue === null ||
1266
1261
  itemValue === '' ||
1267
1262
  (Array.isArray(itemValue) && itemValue.length === 0);
1268
1263
  return opValue ? isEmpty : !isEmpty;
1264
+ }
1269
1265
 
1270
1266
  default:
1271
1267
  console.warn(`MenoFilter: Unknown operator "${op}"`);
@@ -5,7 +5,7 @@
5
5
  * to MenoFilter instances via data attributes.
6
6
  */
7
7
 
8
- import { MenoFilter } from './MenoFilter';
8
+ import type { MenoFilter } from './MenoFilter';
9
9
  import { ATTR } from './constants';
10
10
  import type { FilterInstance, FilterMode } from './types';
11
11
 
@@ -42,12 +42,7 @@ function getFilterMode(element: HTMLElement): FilterMode | null {
42
42
  * Handle button click in multi-select mode
43
43
  * Toggles value in $in array
44
44
  */
45
- function handleMultiButtonClick(
46
- filter: MenoFilter,
47
- field: string,
48
- value: string,
49
- currentValue: unknown
50
- ): void {
45
+ function handleMultiButtonClick(filter: MenoFilter, field: string, value: string, currentValue: unknown): void {
51
46
  // Get current values as array
52
47
  let values: string[] = [];
53
48
  if (currentValue !== undefined) {
@@ -87,9 +82,7 @@ export function bindFilterControls(instance: FilterInstance): void {
87
82
  const { wrapper, filter, cleanup } = instance;
88
83
 
89
84
  // Filter buttons
90
- const filterButtons = wrapper.querySelectorAll<HTMLElement>(
91
- `[${ATTR.FILTER_FIELD}][${ATTR.FILTER_VALUE}]`
92
- );
85
+ const filterButtons = wrapper.querySelectorAll<HTMLElement>(`[${ATTR.FILTER_FIELD}][${ATTR.FILTER_VALUE}]`);
93
86
 
94
87
  for (const btn of filterButtons) {
95
88
  if (btn.tagName === 'INPUT') continue; // Handle inputs separately
@@ -118,7 +111,6 @@ export function bindFilterControls(instance: FilterInstance): void {
118
111
  filter.addFilter(field, value);
119
112
  }
120
113
  }
121
-
122
114
  };
123
115
 
124
116
  btn.addEventListener('click', handler);
@@ -126,9 +118,7 @@ export function bindFilterControls(instance: FilterInstance): void {
126
118
  }
127
119
 
128
120
  // Filter checkboxes (allow multiple selections with OR logic)
129
- const filterCheckboxes = wrapper.querySelectorAll<HTMLInputElement>(
130
- `input[type="checkbox"][${ATTR.FILTER_FIELD}]`
131
- );
121
+ const filterCheckboxes = wrapper.querySelectorAll<HTMLInputElement>(`input[type="checkbox"][${ATTR.FILTER_FIELD}]`);
132
122
 
133
123
  for (const checkbox of filterCheckboxes) {
134
124
  const field = checkbox.getAttribute(ATTR.FILTER_FIELD)!;
@@ -139,7 +129,7 @@ export function bindFilterControls(instance: FilterInstance): void {
139
129
  // Single mode: uncheck other checkboxes in the same field
140
130
  if (mode === 'single' && checkbox.checked) {
141
131
  const siblings = wrapper.querySelectorAll<HTMLInputElement>(
142
- `input[type="checkbox"][${ATTR.FILTER_FIELD}="${field}"]`
132
+ `input[type="checkbox"][${ATTR.FILTER_FIELD}="${field}"]`,
143
133
  );
144
134
  for (const sibling of siblings) {
145
135
  if (sibling !== checkbox) {
@@ -156,9 +146,7 @@ export function bindFilterControls(instance: FilterInstance): void {
156
146
  }
157
147
 
158
148
  // Filter selects
159
- const filterSelects = wrapper.querySelectorAll<HTMLSelectElement>(
160
- `select[${ATTR.FILTER_FIELD}]`
161
- );
149
+ const filterSelects = wrapper.querySelectorAll<HTMLSelectElement>(`select[${ATTR.FILTER_FIELD}]`);
162
150
 
163
151
  for (const select of filterSelects) {
164
152
  const field = select.getAttribute(ATTR.FILTER_FIELD)!;
@@ -177,9 +165,7 @@ export function bindFilterControls(instance: FilterInstance): void {
177
165
  }
178
166
 
179
167
  // Filter radio buttons
180
- const filterRadios = wrapper.querySelectorAll<HTMLInputElement>(
181
- `input[type="radio"][${ATTR.FILTER_FIELD}]`
182
- );
168
+ const filterRadios = wrapper.querySelectorAll<HTMLInputElement>(`input[type="radio"][${ATTR.FILTER_FIELD}]`);
183
169
 
184
170
  for (const radio of filterRadios) {
185
171
  const field = radio.getAttribute(ATTR.FILTER_FIELD)!;
@@ -203,18 +189,12 @@ export function bindFilterControls(instance: FilterInstance): void {
203
189
  /**
204
190
  * Update filters based on checked checkboxes for a field
205
191
  */
206
- function updateCheckboxFilters(
207
- wrapper: HTMLElement,
208
- filter: MenoFilter,
209
- field: string
210
- ): void {
192
+ function updateCheckboxFilters(wrapper: HTMLElement, filter: MenoFilter, field: string): void {
211
193
  const checkboxes = wrapper.querySelectorAll<HTMLInputElement>(
212
- `input[type="checkbox"][${ATTR.FILTER_FIELD}="${field}"]:checked`
194
+ `input[type="checkbox"][${ATTR.FILTER_FIELD}="${field}"]:checked`,
213
195
  );
214
196
 
215
- const values = Array.from(checkboxes).map(
216
- (cb) => cb.getAttribute(ATTR.FILTER_VALUE) || cb.value
217
- );
197
+ const values = Array.from(checkboxes).map((cb) => cb.getAttribute(ATTR.FILTER_VALUE) || cb.value);
218
198
 
219
199
  if (values.length === 0) {
220
200
  filter.removeFilter(field);
@@ -363,17 +343,13 @@ export function bindActionControls(instance: FilterInstance): void {
363
343
  */
364
344
  export function resetFormControls(wrapper: HTMLElement): void {
365
345
  // Reset checkboxes
366
- const checkboxes = wrapper.querySelectorAll<HTMLInputElement>(
367
- `input[type="checkbox"][${ATTR.FILTER_FIELD}]`
368
- );
346
+ const checkboxes = wrapper.querySelectorAll<HTMLInputElement>(`input[type="checkbox"][${ATTR.FILTER_FIELD}]`);
369
347
  for (const cb of checkboxes) {
370
348
  cb.checked = false;
371
349
  }
372
350
 
373
351
  // Reset selects
374
- const selects = wrapper.querySelectorAll<HTMLSelectElement>(
375
- `select[${ATTR.FILTER_FIELD}]`
376
- );
352
+ const selects = wrapper.querySelectorAll<HTMLSelectElement>(`select[${ATTR.FILTER_FIELD}]`);
377
353
  for (const select of selects) {
378
354
  select.selectedIndex = 0;
379
355
  }
@@ -421,12 +397,13 @@ export function bindPaginationControls(instance: FilterInstance): void {
421
397
  case 'last':
422
398
  filter.setPage(filter.getPageInfo().total);
423
399
  break;
424
- default:
400
+ default: {
425
401
  // Numeric page
426
402
  const page = parseInt(action || '1', 10);
427
403
  if (!isNaN(page)) {
428
404
  filter.setPage(page);
429
405
  }
406
+ }
430
407
  }
431
408
  };
432
409
 
@@ -435,9 +412,7 @@ export function bindPaginationControls(instance: FilterInstance): void {
435
412
  }
436
413
 
437
414
  // Per-page select
438
- const perPageSelects = wrapper.querySelectorAll<HTMLSelectElement>(
439
- `[${ATTR.PER_PAGE_SELECT}]`
440
- );
415
+ const perPageSelects = wrapper.querySelectorAll<HTMLSelectElement>(`[${ATTR.PER_PAGE_SELECT}]`);
441
416
 
442
417
  for (const select of perPageSelects) {
443
418
  const handler = () => {
@@ -136,7 +136,7 @@ async function initFilterInstance(wrapper: HTMLElement, collection: string): Pro
136
136
  cleanup.push(
137
137
  filter.watch(() => {
138
138
  updateUI(instance);
139
- })
139
+ }),
140
140
  );
141
141
  }
142
142
 
@@ -20,7 +20,7 @@ export function renderItemFromTemplate(
20
20
  item: Record<string, unknown>,
21
21
  itemAs: string,
22
22
  index: number,
23
- total: number
23
+ total: number,
24
24
  ): HTMLElement {
25
25
  let html = template;
26
26
 
@@ -69,7 +69,7 @@ export function renderNestedItemFromTemplate(
69
69
  index: number,
70
70
  total: number,
71
71
  parentItem: Record<string, unknown>,
72
- parentItemAs: string
72
+ parentItemAs: string,
73
73
  ): HTMLElement {
74
74
  let html = template;
75
75
 
@@ -146,11 +146,9 @@ export function renderNestedItemFromTemplate(
146
146
  export function hydrateNestedCMSLists(
147
147
  element: HTMLElement,
148
148
  parentItem: Record<string, unknown>,
149
- parentItemAs: string
149
+ parentItemAs: string,
150
150
  ): void {
151
- const placeholders = element.querySelectorAll<HTMLElement>(
152
- '[data-cms-list-nested="true"]'
153
- );
151
+ const placeholders = element.querySelectorAll<HTMLElement>('[data-cms-list-nested="true"]');
154
152
 
155
153
  for (const placeholder of placeholders) {
156
154
  hydrateNestedCMSList(placeholder, parentItem, parentItemAs);
@@ -163,7 +161,7 @@ export function hydrateNestedCMSLists(
163
161
  function hydrateNestedCMSList(
164
162
  placeholder: HTMLElement,
165
163
  parentItem: Record<string, unknown>,
166
- parentItemAs: string
164
+ parentItemAs: string,
167
165
  ): void {
168
166
  const configStr = placeholder.getAttribute('data-cms-config');
169
167
  if (!configStr) return;
@@ -176,9 +174,7 @@ function hydrateNestedCMSList(
176
174
  }
177
175
 
178
176
  const collection = config.collection;
179
- const templateEl = placeholder.querySelector<HTMLTemplateElement>(
180
- 'template[data-nested-template]'
181
- );
177
+ const templateEl = placeholder.querySelector<HTMLTemplateElement>('template[data-nested-template]');
182
178
  if (!templateEl) return;
183
179
 
184
180
  const childTemplate = templateEl.innerHTML.trim();
@@ -187,7 +183,9 @@ function hydrateNestedCMSList(
187
183
  // Get collection data from inline JSON
188
184
  const collectionData = getCollectionData(collection);
189
185
  if (!collectionData) {
190
- console.warn(`MenoFilter: No data for nested collection "${collection}". Ensure clientData.enabled is true in schema.`);
186
+ console.warn(
187
+ `MenoFilter: No data for nested collection "${collection}". Ensure clientData.enabled is true in schema.`,
188
+ );
191
189
  return;
192
190
  }
193
191
 
@@ -198,7 +196,7 @@ function hydrateNestedCMSList(
198
196
  // Reference relationship: resolve IDs from parent
199
197
  const itemIds = resolveItemsFromParent(config.items, parentItem, parentItemAs);
200
198
  items = itemIds
201
- .map(id => collectionData.find(item => item._id === id || item._filename === id))
199
+ .map((id) => collectionData.find((item) => item._id === id || item._filename === id))
202
200
  .filter((item): item is Record<string, unknown> => item !== undefined);
203
201
  } else {
204
202
  // Filter relationship: apply filter with resolved parent values
@@ -224,15 +222,14 @@ function hydrateNestedCMSList(
224
222
  i,
225
223
  items.length,
226
224
  parentItem,
227
- parentItemAs
225
+ parentItemAs,
228
226
  );
229
227
  renderedItems.push(childElement);
230
228
  }
231
229
 
232
230
  // Replace placeholder content (keep template for potential re-renders)
233
- const existingChildren = Array.from(placeholder.children)
234
- .filter(el => el.tagName !== 'TEMPLATE');
235
- existingChildren.forEach(el => el.remove());
231
+ const existingChildren = Array.from(placeholder.children).filter((el) => el.tagName !== 'TEMPLATE');
232
+ existingChildren.forEach((el) => el.remove());
236
233
 
237
234
  for (const item of renderedItems) {
238
235
  placeholder.appendChild(item);
@@ -260,7 +257,7 @@ function getCollectionData(collection: string): Record<string, unknown>[] | null
260
257
  function resolveItemsFromParent(
261
258
  template: string | string[],
262
259
  parentItem: Record<string, unknown>,
263
- parentItemAs: string
260
+ parentItemAs: string,
264
261
  ): string[] {
265
262
  if (Array.isArray(template)) return template;
266
263
  if (typeof template !== 'string' || !template.startsWith('{{')) return [template];
@@ -284,12 +281,12 @@ function applyNestedFilter(
284
281
  items: Record<string, unknown>[],
285
282
  filter: unknown,
286
283
  parentItem: Record<string, unknown>,
287
- parentItemAs: string
284
+ parentItemAs: string,
288
285
  ): Record<string, unknown>[] {
289
286
  if (!filter) return items;
290
287
 
291
288
  const resolved = resolveNestedFilterTemplates(filter, parentItem, parentItemAs);
292
- return items.filter(item => matchesNestedFilter(item, resolved));
289
+ return items.filter((item) => matchesNestedFilter(item, resolved));
293
290
  }
294
291
 
295
292
  /**
@@ -298,12 +295,12 @@ function applyNestedFilter(
298
295
  function resolveNestedFilterTemplates(
299
296
  filter: unknown,
300
297
  parentItem: Record<string, unknown>,
301
- parentItemAs: string
298
+ parentItemAs: string,
302
299
  ): unknown {
303
300
  if (Array.isArray(filter)) {
304
- return filter.map(cond => ({
301
+ return filter.map((cond) => ({
305
302
  ...cond,
306
- value: resolveNestedTemplateValue(cond.value, parentItem, parentItemAs)
303
+ value: resolveNestedTemplateValue(cond.value, parentItem, parentItemAs),
307
304
  }));
308
305
  }
309
306
 
@@ -311,7 +308,7 @@ function resolveNestedFilterTemplates(
311
308
  if ('field' in filter && 'value' in filter) {
312
309
  return {
313
310
  ...filter,
314
- value: resolveNestedTemplateValue((filter as { value: unknown }).value, parentItem, parentItemAs)
311
+ value: resolveNestedTemplateValue((filter as { value: unknown }).value, parentItem, parentItemAs),
315
312
  };
316
313
  }
317
314
 
@@ -332,7 +329,7 @@ function resolveNestedFilterTemplates(
332
329
  function resolveNestedTemplateValue(
333
330
  value: unknown,
334
331
  parentItem: Record<string, unknown>,
335
- parentItemAs: string
332
+ parentItemAs: string,
336
333
  ): unknown {
337
334
  if (typeof value !== 'string' || !value.startsWith('{{')) return value;
338
335
 
@@ -352,7 +349,7 @@ function matchesNestedFilter(item: Record<string, unknown>, filter: unknown): bo
352
349
  if (!filter || typeof filter !== 'object') return true;
353
350
 
354
351
  if (Array.isArray(filter)) {
355
- return filter.every(cond => matchesNestedCondition(item, cond));
352
+ return filter.every((cond) => matchesNestedCondition(item, cond));
356
353
  }
357
354
 
358
355
  if ('field' in filter && 'value' in filter) {
@@ -383,10 +380,7 @@ function matchesNestedCondition(item: Record<string, unknown>, cond: { field: st
383
380
  /**
384
381
  * Apply sorting to items
385
382
  */
386
- function applyNestedSorting(
387
- items: Record<string, unknown>[],
388
- sort: unknown
389
- ): Record<string, unknown>[] {
383
+ function applyNestedSorting(items: Record<string, unknown>[], sort: unknown): Record<string, unknown>[] {
390
384
  if (!sort) return items;
391
385
 
392
386
  const sortConfig = Array.isArray(sort) ? sort[0] : sort;
@@ -47,9 +47,7 @@ export function needsMenoFilter(html: string): boolean {
47
47
  /**
48
48
  * Check if any schemas have clientData enabled
49
49
  */
50
- export function schemasNeedMenoFilter(
51
- schemas: Array<{ clientData?: { enabled: boolean } }>
52
- ): boolean {
50
+ export function schemasNeedMenoFilter(schemas: Array<{ clientData?: { enabled: boolean } }>): boolean {
53
51
  return schemas.some((schema) => schema.clientData?.enabled === true);
54
52
  }
55
53
 
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import { MenoFilter, type CMSItem } from './MenoFilter';
11
+ import { applyNonce } from '../styles/cspNonce';
11
12
 
12
13
  // ============================================================================
13
14
  // Types
@@ -56,10 +57,7 @@ const DEFAULT_OPTIONS: Required<UIOptions> = {
56
57
  /**
57
58
  * Bind UI rendering to a MenoFilter instance
58
59
  */
59
- export function bindFilterUI(
60
- collection: string,
61
- options: UIOptions = {}
62
- ): (() => void) | null {
60
+ export function bindFilterUI(collection: string, options: UIOptions = {}): (() => void) | null {
63
61
  const filter = MenoFilter.get(collection);
64
62
  if (!filter) {
65
63
  console.warn(`MenoFilter: No instance found for "${collection}"`);
@@ -170,7 +168,7 @@ function showElement(element: HTMLElement, options: Required<UIOptions>): void {
170
168
  }, duration);
171
169
  break;
172
170
 
173
- case 'slide':
171
+ case 'slide': {
174
172
  element.style.maxHeight = '0';
175
173
  element.style.overflow = 'hidden';
176
174
  element.classList.remove(hiddenClass);
@@ -189,6 +187,7 @@ function showElement(element: HTMLElement, options: Required<UIOptions>): void {
189
187
  element.style.overflow = '';
190
188
  }, duration);
191
189
  break;
190
+ }
192
191
 
193
192
  default:
194
193
  element.classList.remove(hiddenClass);
@@ -304,6 +303,7 @@ function injectCSS(options: Required<UIOptions>): void {
304
303
  }
305
304
  `;
306
305
 
306
+ applyNonce(style);
307
307
  document.head.appendChild(style);
308
308
  cssInjected = true;
309
309
  }
@@ -149,7 +149,7 @@ function filterDOMOnly(instance: FilterInstance, listEl: HTMLElement): void {
149
149
  const currentPage = filter.getPageInfo().current;
150
150
 
151
151
  // Build filtered list
152
- let filteredItems = items.filter(item => {
152
+ const filteredItems = items.filter((item) => {
153
153
  // Check filters
154
154
  for (const [field, value] of Object.entries(filters)) {
155
155
  const itemValue = getItemFieldValue(item, field);
@@ -234,7 +234,7 @@ function getItemFieldValue(item: HTMLElement, field: string): string | null {
234
234
  */
235
235
  function getSearchableText(item: HTMLElement, fields: string[]): string {
236
236
  if (fields.length > 0) {
237
- return fields.map(f => getItemFieldValue(item, f) || '').join(' ');
237
+ return fields.map((f) => getItemFieldValue(item, f) || '').join(' ');
238
238
  }
239
239
  return item.textContent || '';
240
240
  }
@@ -345,17 +345,11 @@ export function updateFacets(wrapper: HTMLElement, filter: MenoFilter): void {
345
345
  /**
346
346
  * Update active filters display
347
347
  */
348
- export function updateActiveFilters(
349
- wrapper: HTMLElement,
350
- filter: MenoFilter,
351
- instance: FilterInstance
352
- ): void {
348
+ export function updateActiveFilters(wrapper: HTMLElement, filter: MenoFilter, instance: FilterInstance): void {
353
349
  const container = wrapper.querySelector<HTMLElement>(`[${ATTR.ACTIVE_FILTERS}]`);
354
350
  if (!container) return;
355
351
 
356
- const template = wrapper.querySelector<HTMLTemplateElement>(
357
- `template[${ATTR.ACTIVE_FILTER_TEMPLATE}]`
358
- );
352
+ const template = wrapper.querySelector<HTMLTemplateElement>(`template[${ATTR.ACTIVE_FILTER_TEMPLATE}]`);
359
353
 
360
354
  const filters = filter.getFilters();
361
355
  const entries = Object.entries(filters);
@@ -478,7 +472,7 @@ export function updatePagination(wrapper: HTMLElement, state: FilterState): void
478
472
  export function generatePageButtons(
479
473
  container: HTMLElement,
480
474
  page: { current: number; total: number },
481
- onClick: (page: number) => void
475
+ onClick: (page: number) => void,
482
476
  ): void {
483
477
  container.innerHTML = '';
484
478
 
@@ -566,9 +560,7 @@ export function updateActiveStates(wrapper: HTMLElement, filter: MenoFilter): vo
566
560
  const activeClass = wrapper.getAttribute(ATTR.ACTIVE_CLASS) || 'active';
567
561
 
568
562
  // Update filter buttons
569
- const filterButtons = wrapper.querySelectorAll<HTMLElement>(
570
- `[${ATTR.FILTER_FIELD}][${ATTR.FILTER_VALUE}]`
571
- );
563
+ const filterButtons = wrapper.querySelectorAll<HTMLElement>(`[${ATTR.FILTER_FIELD}][${ATTR.FILTER_VALUE}]`);
572
564
 
573
565
  for (const btn of filterButtons) {
574
566
  if (btn.tagName === 'INPUT') continue;
@@ -582,13 +574,15 @@ export function updateActiveStates(wrapper: HTMLElement, filter: MenoFilter): vo
582
574
  const currentValue = filters[field];
583
575
 
584
576
  // Only active if filter is explicitly set AND matches this value
585
- const isActive = currentValue !== undefined && (
586
- currentValue === value ||
587
- (Array.isArray(currentValue) && currentValue.includes(value)) ||
588
- (typeof currentValue === 'object' && currentValue !== null && '$in' in currentValue &&
589
- Array.isArray((currentValue as { $in: unknown[] }).$in) &&
590
- (currentValue as { $in: unknown[] }).$in.includes(value))
591
- );
577
+ const isActive =
578
+ currentValue !== undefined &&
579
+ (currentValue === value ||
580
+ (Array.isArray(currentValue) && currentValue.includes(value)) ||
581
+ (typeof currentValue === 'object' &&
582
+ currentValue !== null &&
583
+ '$in' in currentValue &&
584
+ Array.isArray((currentValue as { $in: unknown[] }).$in) &&
585
+ (currentValue as { $in: unknown[] }).$in.includes(value)));
592
586
 
593
587
  btn.classList.toggle(activeClass, isActive);
594
588
  }