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,12 +3,12 @@
3
3
  * Manages file watching for pages and components
4
4
  */
5
5
 
6
- import { FileWatcher, type FileWatchCallbacks } from '../fileWatcher';
7
- import { WebSocketManager } from '../websocketManager';
8
- import { ComponentService } from './componentService';
9
- import { PageService } from './pageService';
6
+ import { FileWatcher } from '../fileWatcher';
7
+ import type { WebSocketManager } from '../websocketManager';
8
+ import type { ComponentService } from './componentService';
9
+ import type { PageService } from './pageService';
10
10
  import type { CMSService } from './cmsService';
11
- import { PageCache } from '../pageCache';
11
+ import type { PageCache } from '../pageCache';
12
12
  import { colorService } from './ColorService';
13
13
  import { variableService } from './VariableService';
14
14
  import { enumService } from './EnumService';
@@ -22,10 +22,10 @@ export class FileWatcherService {
22
22
  constructor(
23
23
  private componentService: ComponentService,
24
24
  private pageService: PageService,
25
- private pageCache: PageCache,
25
+ _pageCache: PageCache,
26
26
  private wsManager: WebSocketManager,
27
27
  private cmsService?: CMSService,
28
- private onImageAdded?: (filename: string) => Promise<void>
28
+ private onImageAdded?: (filename: string) => Promise<void>,
29
29
  ) {}
30
30
 
31
31
  /**
@@ -63,6 +63,41 @@ export class FileWatcherService {
63
63
  }, 50);
64
64
  }
65
65
  },
66
+ onCMSTemplateChange: async () => {
67
+ // An astro CMS template page (src/pages/<col>/[slug].astro) was added,
68
+ // edited, or removed. Unlike a regular page edit this changes the set of
69
+ // collections, so refresh the CMS schema cache AND reconcile the page
70
+ // cache's /templates/<id> entries — otherwise the new collection and its
71
+ // template page only appear after a dev-server restart. Debounced (shares
72
+ // refreshSchemasTimer) to coalesce burst fs.watch events from one write.
73
+ if (!this.cmsService) return;
74
+ if (this.refreshSchemasTimer !== null) {
75
+ clearTimeout(this.refreshSchemasTimer);
76
+ }
77
+ const cmsService = this.cmsService;
78
+ const pageService = this.pageService;
79
+ const wsManager = this.wsManager;
80
+ this.refreshSchemasTimer = setTimeout(async () => {
81
+ this.refreshSchemasTimer = null;
82
+ await cmsService.refreshSchemas();
83
+ // In astro projects every /templates/<id> page-cache entry is a CMS
84
+ // template (regular pages keep their real path), so reconcile them 1:1
85
+ // with the fresh schema set: drop entries for removed collections,
86
+ // (re)load entries for current ones.
87
+ const ids = new Set(cmsService.getAllSchemas().keys());
88
+ for (const path of pageService.getAllPagePaths()) {
89
+ if (path.startsWith('/templates/') && !ids.has(path.slice('/templates/'.length))) {
90
+ await pageService.deletePage(path);
91
+ }
92
+ }
93
+ for (const id of ids) {
94
+ await pageService.reloadPageFromDisk(`/templates/${id}`);
95
+ }
96
+ // Refresh the studio's collections list (CMS panel) and page list (dropdown).
97
+ wsManager.broadcastCollectionsUpdate();
98
+ wsManager.broadcastUpdate();
99
+ }, 50);
100
+ },
66
101
  onColorsChange: async () => {
67
102
  // Clear the color service cache so it reloads on next request
68
103
  colorService.clearCache();
@@ -119,4 +154,3 @@ export class FileWatcherService {
119
154
  }
120
155
  }
121
156
  }
122
-
@@ -7,4 +7,3 @@ export { PageService } from './pageService';
7
7
  export { ComponentService, type ComponentInfo } from './componentService';
8
8
  export { FileWatcherService } from './fileWatcherService';
9
9
  export { CMSService, type ReferenceLocation } from './cmsService';
10
-
@@ -1,4 +1,4 @@
1
- import { test, expect, describe, beforeEach, afterEach, mock } from 'bun:test';
1
+ import { test, expect, describe, beforeEach, afterEach } from 'bun:test';
2
2
  import { PageService } from './pageService';
3
3
  import { PageCache } from '../pageCache';
4
4
  import { createMockPageData } from '../../test-utils';
@@ -122,9 +122,7 @@ describe('PageService', () => {
122
122
  const serviceWithoutProvider = new PageService(pageCache);
123
123
  const testData = createMockPageData();
124
124
 
125
- await expect(serviceWithoutProvider.savePage('/', testData)).rejects.toThrow(
126
- 'PageProvider not set'
127
- );
125
+ await expect(serviceWithoutProvider.savePage('/', testData)).rejects.toThrow('PageProvider not set');
128
126
  });
129
127
 
130
128
  test('should strip _lineMap from saved data', async () => {
@@ -229,8 +227,8 @@ describe('PageService', () => {
229
227
  const mappings = pageService.getSlugMappings();
230
228
 
231
229
  expect(mappings).toHaveLength(1);
232
- expect(mappings[0].pageId).toBe('about');
233
- expect(mappings[0].slugs).toEqual({ en: 'about', pl: 'o-nas' });
230
+ expect(mappings[0]!.pageId).toBe('about');
231
+ expect(mappings[0]!.slugs).toEqual({ en: 'about', pl: 'o-nas' });
234
232
  });
235
233
 
236
234
  test('should create default slug for pages without explicit slugs', () => {
@@ -240,8 +238,8 @@ describe('PageService', () => {
240
238
  const mappings = pageService.getSlugMappings();
241
239
 
242
240
  expect(mappings).toHaveLength(1);
243
- expect(mappings[0].pageId).toBe('about');
244
- expect(mappings[0].slugs).toEqual({ _default: 'about' });
241
+ expect(mappings[0]!.pageId).toBe('about');
242
+ expect(mappings[0]!.slugs).toEqual({ _default: 'about' });
245
243
  });
246
244
 
247
245
  test('should handle index page correctly', () => {
@@ -251,8 +249,8 @@ describe('PageService', () => {
251
249
  const mappings = pageService.getSlugMappings();
252
250
 
253
251
  expect(mappings).toHaveLength(1);
254
- expect(mappings[0].pageId).toBe('index');
255
- expect(mappings[0].slugs).toEqual({ _default: '' });
252
+ expect(mappings[0]!.pageId).toBe('index');
253
+ expect(mappings[0]!.slugs).toEqual({ _default: '' });
256
254
  });
257
255
  });
258
256
 
@@ -6,12 +6,14 @@
6
6
  * and managing page data including saving and deletion.
7
7
  */
8
8
 
9
- import { existsSync, readdirSync, mkdirSync, rmdirSync } from 'fs';
10
- import { join } from 'path';
11
- import { PageCache } from '../pageCache';
9
+ import { existsSync, readdirSync, mkdirSync, rmdirSync } from 'node:fs';
10
+ import { join } from 'node:path';
11
+ import type { PageCache } from '../pageCache';
12
12
  import { parseJSON } from '../jsonLoader';
13
13
  import { buildLineMap } from '../utils/jsonLineMapper';
14
14
  import { projectPaths } from '../projectContext';
15
+ import { logRuntimeError } from '../../shared/errorLogger';
16
+ import { isPathWithinRoot } from '../../shared/pathSecurity';
15
17
  import type { JSONPage } from '../../shared/types';
16
18
  import type { SlugMap } from '../../shared/slugTranslator';
17
19
  import type { PageProvider } from '../../shared/interfaces/contentProvider';
@@ -25,7 +27,7 @@ export class PageService {
25
27
  */
26
28
  constructor(
27
29
  private pageCache: PageCache,
28
- private provider?: PageProvider
30
+ private provider?: PageProvider,
29
31
  ) {}
30
32
 
31
33
  /**
@@ -165,8 +167,10 @@ export class PageService {
165
167
  let content: string | null = null;
166
168
  try {
167
169
  content = await this.provider.get(path);
168
- } catch {
170
+ } catch (error) {
169
171
  // Transient (mid-write / partial .astro parse error) — keep the cache as-is.
172
+ // Logged (dev-gated) so a persistent read/parse failure isn't fully invisible.
173
+ logRuntimeError('pageService.reloadPageFromDisk', error, { path });
170
174
  return false;
171
175
  }
172
176
 
@@ -241,19 +245,24 @@ export class PageService {
241
245
  * // [{ path: "/", isDraft: false }, { path: "/about", isDraft: true }]
242
246
  * ```
243
247
  */
244
- getAllPagesWithInfo(): Array<{ path: string; isDraft: boolean; folder?: string }> {
245
- const paths = this.pageCache.keys().filter(p => !p.startsWith('/_sketch/'));
246
- return paths.map(path => {
248
+ getAllPagesWithInfo(): Array<{ path: string; isDraft: boolean; folder?: string; source?: 'cms' | 'ssr' }> {
249
+ const paths = this.pageCache.keys().filter((p) => !p.startsWith('/_sketch/'));
250
+ return paths.map((path) => {
247
251
  const pageData = this.getPageData(path);
248
252
  // Extract folder from path: /blog/post -> blog, /templates/blog-post -> templates
249
253
  // Root-level pages (e.g., /, /about) have no folder
250
254
  const pathWithoutSlash = path === '/' ? 'index' : path.substring(1);
251
255
  const slashIndex = pathWithoutSlash.lastIndexOf('/');
252
256
  const folder = slashIndex > 0 ? pathWithoutSlash.substring(0, slashIndex) : undefined;
257
+ // Surface a non-static page source (`cms` template / `ssr` live page) so the editor's
258
+ // page picker can group them apart from plain static pages. Static/undefined omitted.
259
+ const metaSource = pageData?.meta?.source;
260
+ const source = metaSource === 'cms' || metaSource === 'ssr' ? metaSource : undefined;
253
261
  return {
254
262
  path,
255
263
  isDraft: pageData?.meta?.draft === true,
256
- ...(folder ? { folder } : {})
264
+ ...(folder ? { folder } : {}),
265
+ ...(source ? { source } : {}),
257
266
  };
258
267
  });
259
268
  }
@@ -309,7 +318,9 @@ export class PageService {
309
318
  result.push(...collectFolders(join(dir, entry.name), relativePath));
310
319
  }
311
320
  return result;
312
- } catch { return []; }
321
+ } catch {
322
+ return [];
323
+ }
313
324
  };
314
325
 
315
326
  return collectFolders(pagesDir, '').sort();
@@ -359,7 +370,7 @@ export class PageService {
359
370
  * @param newFolder - Target folder name (empty string for root)
360
371
  */
361
372
  async movePage(pagePath: string, newFolder: string): Promise<void> {
362
- const { rename } = await import('fs/promises');
373
+ const { rename } = await import('node:fs/promises');
363
374
  const pagesDir = this.pagesBaseDir();
364
375
  const ext = this.pageExt();
365
376
 
@@ -370,9 +381,14 @@ export class PageService {
370
381
  const baseName = slashIndex >= 0 ? pageName.substring(slashIndex + 1) : pageName;
371
382
 
372
383
  const sourceFile = join(pagesDir, `${pageName}${ext}`);
373
- const targetFile = newFolder
374
- ? join(pagesDir, newFolder, `${baseName}${ext}`)
375
- : join(pagesDir, `${baseName}${ext}`);
384
+ const targetFile = newFolder ? join(pagesDir, newFolder, `${baseName}${ext}`) : join(pagesDir, `${baseName}${ext}`);
385
+
386
+ // Defense-in-depth: a crafted pagePath / newFolder must not move a file out of
387
+ // (or in from) outside the pages dir. Siblings createFolder/renamePage validate;
388
+ // movePage previously did not.
389
+ if (!isPathWithinRoot(sourceFile, pagesDir) || !isPathWithinRoot(targetFile, pagesDir)) {
390
+ throw new Error(`movePage path escapes pages dir: ${pagePath} -> ${newFolder}`);
391
+ }
376
392
 
377
393
  // Create target directory if needed
378
394
  if (newFolder) {
@@ -399,9 +415,7 @@ export class PageService {
399
415
  }
400
416
 
401
417
  // Clean up empty source directory
402
- const sourceDir = slashIndex >= 0
403
- ? join(pagesDir, pageName.substring(0, slashIndex))
404
- : null;
418
+ const sourceDir = slashIndex >= 0 ? join(pagesDir, pageName.substring(0, slashIndex)) : null;
405
419
 
406
420
  if (sourceDir && sourceDir !== pagesDir) {
407
421
  try {
@@ -454,7 +468,7 @@ export class PageService {
454
468
  throw new Error('Page slug can only contain lowercase letters, numbers, dashes, and underscores');
455
469
  }
456
470
 
457
- const { rename } = await import('fs/promises');
471
+ const { rename } = await import('node:fs/promises');
458
472
  const pagesDir = this.pagesBaseDir();
459
473
  const ext = this.pageExt();
460
474
  const sourceFile = join(pagesDir, `${oldName}${ext}`);
@@ -27,9 +27,21 @@ export function escapeHtml(unsafe: string): string {
27
27
 
28
28
  /** HTML attributes that must NOT be kebab-cased (single-word or special casing) */
29
29
  const NO_KEBAB_ATTRS = new Set([
30
- 'autoplay', 'playsinline', 'crossorigin', 'novalidate', 'readonly',
31
- 'nomodule', 'formnovalidate', 'ismap', 'usemap', 'srcset', 'inputmode',
32
- 'enterkeyhint', 'autocomplete', 'autofocus', 'contenteditable',
30
+ 'autoplay',
31
+ 'playsinline',
32
+ 'crossorigin',
33
+ 'novalidate',
34
+ 'readonly',
35
+ 'nomodule',
36
+ 'formnovalidate',
37
+ 'ismap',
38
+ 'usemap',
39
+ 'srcset',
40
+ 'inputmode',
41
+ 'enterkeyhint',
42
+ 'autocomplete',
43
+ 'autofocus',
44
+ 'contenteditable',
33
45
  ]);
34
46
 
35
47
  /**
@@ -61,9 +73,7 @@ export function buildAttributes(props: Record<string, unknown>, exclude: string[
61
73
  const attrName = NO_KEBAB_ATTRS.has(key.toLowerCase())
62
74
  ? key.toLowerCase()
63
75
  : key.replace(/([A-Z])/g, '-$1').toLowerCase();
64
- const attrValue = typeof value === 'boolean'
65
- ? (value ? '' : undefined)
66
- : escapeHtml(String(value));
76
+ const attrValue = typeof value === 'boolean' ? (value ? '' : undefined) : escapeHtml(String(value));
67
77
 
68
78
  if (attrValue !== undefined) {
69
79
  if (typeof value === 'boolean' && value) {
@@ -74,7 +84,7 @@ export function buildAttributes(props: Record<string, unknown>, exclude: string[
74
84
  }
75
85
  }
76
86
 
77
- return attrs.length > 0 ? ' ' + attrs.join(' ') : '';
87
+ return attrs.length > 0 ? ` ${attrs.join(' ')}` : '';
78
88
  }
79
89
 
80
90
  /**
@@ -115,7 +125,7 @@ export function buildEditorAttrs(opts: {
115
125
  parts.push(`data-component-context="${escapeHtml(opts.componentContext)}"`);
116
126
  }
117
127
 
118
- return ' ' + parts.join(' ');
128
+ return ` ${parts.join(' ')}`;
119
129
  }
120
130
 
121
131
  /**
@@ -4,9 +4,9 @@
4
4
  */
5
5
 
6
6
  export interface BuildError {
7
- file: string; // e.g., "pages/posts.json"
8
- message: string; // Error message
9
- type: string; // 'minify' | 'render' | 'parse' | 'cms'
7
+ file: string; // e.g., "pages/posts.json"
8
+ message: string; // Error message
9
+ type: string; // 'minify' | 'render' | 'parse' | 'cms'
10
10
  }
11
11
 
12
12
  export interface BuildErrorsData {
@@ -30,10 +30,7 @@ function escapeHtml(str: string): string {
30
30
  * Safely encode data for embedding in a script tag
31
31
  */
32
32
  function safeJsonForScript(data: unknown): string {
33
- return JSON.stringify(data)
34
- .replace(/</g, '\\u003c')
35
- .replace(/>/g, '\\u003e')
36
- .replace(/&/g, '\\u0026');
33
+ return JSON.stringify(data).replace(/</g, '\\u003c').replace(/>/g, '\\u003e').replace(/&/g, '\\u0026');
37
34
  }
38
35
 
39
36
  /**
@@ -52,12 +49,12 @@ export function generateBuildErrorPage(data: BuildErrorsData, cspNonce?: string)
52
49
  const nonceAttr = cspNonce ? ` nonce="${cspNonce}"` : '';
53
50
 
54
51
  // Generate plain text for copying to AI
55
- const plainTextErrors = errors.map(err =>
56
- `[${err.type.toUpperCase()}] ${err.file}\n${err.message}`
57
- ).join('\n\n');
52
+ const plainTextErrors = errors.map((err) => `[${err.type.toUpperCase()}] ${err.file}\n${err.message}`).join('\n\n');
58
53
  const copyText = `Build failed with ${errors.length} error(s):\n\n${plainTextErrors}`;
59
54
 
60
- const errorListHtml = errors.map((err) => `
55
+ const errorListHtml = errors
56
+ .map(
57
+ (err) => `
61
58
  <div class="error-item">
62
59
  <div class="error-item-header">
63
60
  <div class="error-type">${escapeHtml(err.type)}</div>
@@ -65,7 +62,9 @@ export function generateBuildErrorPage(data: BuildErrorsData, cspNonce?: string)
65
62
  </div>
66
63
  <div class="error-message">${escapeHtml(err.message)}</div>
67
64
  </div>
68
- `).join('');
65
+ `,
66
+ )
67
+ .join('');
69
68
 
70
69
  return `<!DOCTYPE html>
71
70
  <html lang="en">
@@ -18,10 +18,7 @@ const SYSTEM_FIELDS = ['_id', '_url', '_filename', '_slug'];
18
18
  * @param fields Fields to include (undefined or empty = all fields)
19
19
  * @returns Filtered items with only specified fields
20
20
  */
21
- export function filterItemFields(
22
- items: CMSItem[],
23
- fields: string[] | undefined
24
- ): CMSItem[] {
21
+ export function filterItemFields(items: CMSItem[], fields: string[] | undefined): CMSItem[] {
25
22
  // If no field restriction, return all items as-is
26
23
  if (!fields || fields.length === 0) {
27
24
  return items;
@@ -31,7 +28,7 @@ export function filterItemFields(
31
28
  const allowedFields = new Set([...SYSTEM_FIELDS, ...fields]);
32
29
 
33
30
  // Filter each item to only include allowed fields
34
- return items.map(item => {
31
+ return items.map((item) => {
35
32
  const filtered: CMSItem = { _id: item._id };
36
33
 
37
34
  for (const [key, value] of Object.entries(item)) {
@@ -52,15 +49,9 @@ export function filterItemFields(
52
49
  * @param items Items to embed
53
50
  * @returns HTML script tag string
54
51
  */
55
- export function generateInlineDataScript(
56
- collection: string,
57
- items: CMSItem[]
58
- ): string {
52
+ export function generateInlineDataScript(collection: string, items: CMSItem[]): string {
59
53
  // Escape HTML entities in JSON to prevent XSS
60
- const jsonString = JSON.stringify(items)
61
- .replace(/</g, '\\u003c')
62
- .replace(/>/g, '\\u003e')
63
- .replace(/&/g, '\\u0026');
54
+ const jsonString = JSON.stringify(items).replace(/</g, '\\u003c').replace(/>/g, '\\u003e').replace(/&/g, '\\u0026');
64
55
 
65
56
  return `<script type="application/json" id="meno-cms-${escapeAttr(collection)}">${jsonString}</script>`;
66
57
  }
@@ -84,10 +75,7 @@ function escapeAttr(str: string): string {
84
75
  * @param itemCount Number of items in collection
85
76
  * @returns Effective strategy ('inline' or 'static')
86
77
  */
87
- export function determineStrategy(
88
- config: CMSClientDataConfig,
89
- itemCount: number
90
- ): 'inline' | 'static' {
78
+ export function determineStrategy(config: CMSClientDataConfig, itemCount: number): 'inline' | 'static' {
91
79
  if (config.strategy === 'inline') return 'inline';
92
80
  if (config.strategy === 'static') return 'static';
93
81
 
@@ -122,7 +110,7 @@ export interface ClientDataCollection {
122
110
  export function prepareClientData(
123
111
  collection: string,
124
112
  items: CMSItem[],
125
- config: CMSClientDataConfig | undefined
113
+ config: CMSClientDataConfig | undefined,
126
114
  ): ClientDataCollection | null {
127
115
  // Skip if not enabled
128
116
  if (!config?.enabled) {
@@ -146,9 +134,7 @@ export function prepareClientData(
146
134
  * @param collections Map of collection ID to prepared client data
147
135
  * @returns Combined HTML string with all data scripts
148
136
  */
149
- export function generateAllInlineDataScripts(
150
- collections: Map<string, ClientDataCollection>
151
- ): string {
137
+ export function generateAllInlineDataScripts(collections: Map<string, ClientDataCollection>): string {
152
138
  const scripts: string[] = [];
153
139
 
154
140
  for (const [collectionId, data] of collections) {
@@ -4,7 +4,6 @@
4
4
  */
5
5
 
6
6
  import type { I18nConfig } from '../../shared/types/components';
7
- import type { TemplateContext } from '../../shared/types';
8
7
  import { DEFAULT_I18N_CONFIG, resolveI18nValue, isI18nValue } from '../../shared/i18n';
9
8
  import type { ValueResolver } from '../../shared/itemTemplateUtils';
10
9
  import { tiptapToHtml } from '../../shared/richtext/tiptapToHtml';
@@ -24,10 +23,7 @@ export interface CMSContext {
24
23
  /**
25
24
  * Get i18n resolver function for value resolution
26
25
  */
27
- export function createI18nResolver(
28
- locale?: string,
29
- i18nConfig?: I18nConfig
30
- ): ValueResolver | undefined {
26
+ export function createI18nResolver(locale?: string, i18nConfig?: I18nConfig): ValueResolver | undefined {
31
27
  if (!locale || !i18nConfig) return undefined;
32
28
  return (value: unknown) => resolveI18nValue(value, locale, i18nConfig);
33
29
  }
@@ -41,12 +37,12 @@ export function processCMSTemplate(
41
37
  template: string,
42
38
  cmsItem: Record<string, unknown>,
43
39
  locale?: string,
44
- i18nConfig?: I18nConfig
40
+ i18nConfig?: I18nConfig,
45
41
  ): string {
46
42
  const config = i18nConfig || DEFAULT_I18N_CONFIG;
47
43
  const effectiveLocale = locale || config.defaultLocale;
48
44
 
49
- return template.replace(/\{\{cms\.([^}]+)\}\}/g, (match, fieldPath) => {
45
+ return template.replace(/\{\{cms\.([^}]+)\}\}/g, (_match, fieldPath) => {
50
46
  // Support nested paths like cms.author.name
51
47
  const parts = fieldPath.trim().split('.');
52
48
  let value: unknown = cmsItem;
@@ -94,7 +90,7 @@ export function processCMSPropsTemplate(
94
90
  props: Record<string, unknown>,
95
91
  cmsItem: Record<string, unknown>,
96
92
  locale?: string,
97
- i18nConfig?: I18nConfig
93
+ i18nConfig?: I18nConfig,
98
94
  ): Record<string, unknown> {
99
95
  const result: Record<string, unknown> = {};
100
96
 
@@ -10,7 +10,7 @@ import type { ComponentDefinition } from '../../shared/types';
10
10
  */
11
11
  export function collectComponentCSS(
12
12
  globalComponents: Record<string, ComponentDefinition> = {},
13
- pageComponents: Record<string, ComponentDefinition> = {}
13
+ pageComponents: Record<string, ComponentDefinition> = {},
14
14
  ): string {
15
15
  const cssBlocks: string[] = [];
16
16
 
@@ -64,7 +64,7 @@ describe('generateErrorPage', () => {
64
64
  expect(html).toContain("A section couldn't load its content");
65
65
  expect(html).toContain('error-friendly');
66
66
  // ...while the raw message is still present (under technical details / copy).
67
- expect(html).toContain("Cannot read properties of null (reading &#039;length&#039;)");
67
+ expect(html).toContain('Cannot read properties of null (reading &#039;length&#039;)');
68
68
  });
69
69
 
70
70
  it('sends friendly + raw fields in the PREVIEW_ERROR payload', () => {
@@ -44,10 +44,7 @@ function escapeHtml(str: string): string {
44
44
  * Escapes </script> sequences and other problematic characters
45
45
  */
46
46
  function safeJsonForScript(data: unknown): string {
47
- return JSON.stringify(data)
48
- .replace(/</g, '\\u003c')
49
- .replace(/>/g, '\\u003e')
50
- .replace(/&/g, '\\u0026');
47
+ return JSON.stringify(data).replace(/</g, '\\u003c').replace(/>/g, '\\u003e').replace(/&/g, '\\u0026');
51
48
  }
52
49
 
53
50
  /**
@@ -66,8 +63,7 @@ export function generateErrorPage(error: unknown, context?: string, cspNonce?: s
66
63
  const errorMessage = escapeHtml(errorInfo.message);
67
64
  const errorStack = errorInfo.stack ? escapeHtml(errorInfo.stack) : '';
68
65
  // Escape first (XSS-safe), then turn `backtick` spans into <code> for display.
69
- const renderInline = (s: string) =>
70
- escapeHtml(s).replace(/`([^`]+)`/g, '<code>$1</code>');
66
+ const renderInline = (s: string) => escapeHtml(s).replace(/`([^`]+)`/g, '<code>$1</code>');
71
67
  const friendlyTitle = escapeHtml(friendly.title);
72
68
  const friendlyMessage = renderInline(friendly.friendlyMessage);
73
69
  const friendlyHint = friendly.hint ? renderInline(friendly.hint) : '';
@@ -89,9 +85,7 @@ export function generateErrorPage(error: unknown, context?: string, cspNonce?: s
89
85
  });
90
86
 
91
87
  // Full error text for copying
92
- const fullErrorText = safeJsonForScript(
93
- errorInfo.message + (errorInfo.stack ? '\n\n' + errorInfo.stack : '')
94
- );
88
+ const fullErrorText = safeJsonForScript(errorInfo.message + (errorInfo.stack ? `\n\n${errorInfo.stack}` : ''));
95
89
 
96
90
  return `<!DOCTYPE html>
97
91
  <html lang="en">
@@ -18,8 +18,8 @@
18
18
  */
19
19
 
20
20
  import { test, expect, describe } from 'bun:test';
21
- import * as fs from 'fs';
22
- import * as path from 'path';
21
+ import * as fs from 'node:fs';
22
+ import * as path from 'node:path';
23
23
 
24
24
  const CORE = path.resolve(import.meta.dir, '../..', '..');
25
25
  const SSR_DIR = path.join(CORE, 'lib/server/ssr');
@@ -55,21 +55,18 @@ const SCRIPT_OPEN_TAG = /<script\b([^>]*)>/g;
55
55
  * newlines for the comment content.
56
56
  */
57
57
  function stripComments(src: string): string {
58
- const blockStripped = src.replace(/\/\*[\s\S]*?\*\//g, (m) =>
59
- m.replace(/[^\n]/g, ' ')
60
- );
61
- return blockStripped.replace(/(^|[^:])\/\/[^\n]*/g, (m, prefix) =>
62
- prefix + ' '.repeat(m.length - prefix.length)
63
- );
58
+ const blockStripped = src.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, ' '));
59
+ return blockStripped.replace(/(^|[^:])\/\/[^\n]*/g, (m, prefix) => prefix + ' '.repeat(m.length - prefix.length));
64
60
  }
65
61
 
66
62
  function findInlineScripts(src: string): { attrs: string; index: number }[] {
67
63
  const clean = stripComments(src);
68
64
  const out: { attrs: string; index: number }[] = [];
69
- let match: RegExpExecArray | null;
70
65
  SCRIPT_OPEN_TAG.lastIndex = 0;
71
- while ((match = SCRIPT_OPEN_TAG.exec(clean))) {
72
- out.push({ attrs: match[1], index: match.index });
66
+ let match: RegExpExecArray | null = SCRIPT_OPEN_TAG.exec(clean);
67
+ while (match) {
68
+ out.push({ attrs: match[1]!, index: match.index });
69
+ match = SCRIPT_OPEN_TAG.exec(clean);
73
70
  }
74
71
  return out;
75
72
  }
@@ -108,9 +105,7 @@ describe('SSR inline script nonce contract', () => {
108
105
  }
109
106
  }
110
107
  if (offenders.length > 0) {
111
- throw new Error(
112
- `Inline <script> tags missing \${nonceAttr}:\n ${offenders.join('\n ')}`,
113
- );
108
+ throw new Error(`Inline <script> tags missing \${nonceAttr}:\n ${offenders.join('\n ')}`);
114
109
  }
115
110
  });
116
111
  });