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,91 @@
3
3
  * Watches for changes in JSON files and triggers callbacks
4
4
  */
5
5
 
6
- import { watch, existsSync } from 'fs';
7
- import { basename, dirname, join } from 'path';
8
- import type { FSWatcher } from 'fs';
6
+ import { watch, existsSync, statSync, readdirSync } from 'node:fs';
7
+ import { basename, dirname, join, relative } from 'node:path';
8
+ import type { FSWatcher } from 'node:fs';
9
9
  import { mapPageNameToPath } from './jsonLoader';
10
10
  import { projectPaths, getProjectRoot } from './projectContext';
11
11
 
12
+ /**
13
+ * Recursive directory watcher that also covers subdirectories created AFTER the
14
+ * watch began. Node's `fs.watch({recursive:true})` does this natively, but Bun's
15
+ * does NOT — it reports the new subdir's creation event yet never the files
16
+ * created inside it (verified on Bun 1.3). Since the studio server can run under
17
+ * Bun, relying on the native behavior silently breaks live-reload / cache
18
+ * invalidation for any collection or page added at runtime (e.g. a new
19
+ * `src/pages/<col>/[slug].astro` or `src/content/<col>/*.json`).
20
+ *
21
+ * We attach the native recursive watcher (covering the depth present at start),
22
+ * then, whenever a directory appears, attach a nested watcher to it AND replay
23
+ * its current contents as synthetic `rename` events — closing the
24
+ * mkdir-then-write race where a file lands before the nested watcher is live.
25
+ * The listener receives paths relative to `root` (matching `fs.watch`'s relative
26
+ * `filename`). Idempotent callbacks tolerate the occasional duplicate event this
27
+ * can produce under Node (where the native watcher already recurses).
28
+ */
29
+ export function watchRecursive(root: string, listener: (event: string, relPath: string) => void): FSWatcher {
30
+ const nested: FSWatcher[] = [];
31
+ const watchedDirs = new Set<string>();
32
+ let rootWatcher: FSWatcher | null = null;
33
+
34
+ const coverNewDir = (abs: string): void => {
35
+ let isDir = false;
36
+ try {
37
+ isDir = statSync(abs).isDirectory();
38
+ } catch {
39
+ return; // vanished (or unreadable) between event and stat — nothing to do
40
+ }
41
+ if (!isDir || watchedDirs.has(abs)) return;
42
+ attachDir(abs);
43
+ // Replay what's already inside (created before the nested watcher went live).
44
+ try {
45
+ for (const e of readdirSync(abs, { withFileTypes: true })) {
46
+ const child = join(abs, e.name);
47
+ if (e.isDirectory()) coverNewDir(child);
48
+ else listener('rename', relative(root, child));
49
+ }
50
+ } catch {
51
+ /* dir vanished / unreadable — nothing to replay */
52
+ }
53
+ };
54
+
55
+ function attachDir(absDir: string): void {
56
+ if (watchedDirs.has(absDir)) return;
57
+ watchedDirs.add(absDir);
58
+ let w: FSWatcher;
59
+ try {
60
+ w = watch(absDir, { recursive: true }, (event, filename) => {
61
+ if (!filename) return;
62
+ const abs = join(absDir, filename);
63
+ listener(event, relative(root, abs));
64
+ coverNewDir(abs); // a newly-created subdir — Bun won't recurse into it on its own
65
+ });
66
+ } catch {
67
+ return; // dir vanished mid-attach
68
+ }
69
+ if (absDir === root) rootWatcher = w;
70
+ else nested.push(w);
71
+ }
72
+
73
+ attachDir(root);
74
+
75
+ // Composite handle: closing it tears down the root + every nested watcher.
76
+ const handle = (rootWatcher ?? { close() {} }) as FSWatcher;
77
+ const baseClose = handle.close.bind(handle);
78
+ handle.close = () => {
79
+ baseClose();
80
+ for (const w of nested) {
81
+ try {
82
+ w.close();
83
+ } catch {
84
+ /* already closed */
85
+ }
86
+ }
87
+ };
88
+ return handle;
89
+ }
90
+
12
91
  /**
13
92
  * Attach a recursive watcher to `dirPath`. If the directory doesn't exist
14
93
  * yet, watch the parent directory and defer the attach until the target is
@@ -21,7 +100,7 @@ import { projectPaths, getProjectRoot } from './projectContext';
21
100
  function attachWhenDirExists(
22
101
  dirPath: string,
23
102
  attach: () => FSWatcher | null,
24
- setWatcher: (w: FSWatcher | null) => void
103
+ setWatcher: (w: FSWatcher | null) => void,
25
104
  ): void {
26
105
  if (existsSync(dirPath)) {
27
106
  setWatcher(attach());
@@ -50,6 +129,13 @@ function attachWhenDirExists(
50
129
  export interface FileWatchCallbacks {
51
130
  onComponentChange?: () => Promise<void>;
52
131
  onPageChange?: (pagePath: string) => Promise<void>;
132
+ /**
133
+ * Fires when an astro CMS template page (`src/pages/<col>/[slug].astro`) is
134
+ * added, edited, or removed. Distinct from onPageChange because this changes
135
+ * the set of collections (not just one page's content), so the handler must
136
+ * refresh the CMS schema cache + the `/templates/<id>` page-cache entries.
137
+ */
138
+ onCMSTemplateChange?: () => Promise<void>;
53
139
  onColorsChange?: () => Promise<void>;
54
140
  onVariablesChange?: () => Promise<void>;
55
141
  onEnumsChange?: () => Promise<void>;
@@ -76,7 +162,7 @@ export class FileWatcher {
76
162
  private astroContentWatcher: FSWatcher | null = null;
77
163
 
78
164
  constructor(private callbacks: FileWatchCallbacks) {}
79
-
165
+
80
166
  /**
81
167
  * Start watching components directory
82
168
  * Watches both .json and .js files to detect component definition and JavaScript changes
@@ -85,24 +171,19 @@ export class FileWatcher {
85
171
  if (!existsSync(dirPath)) {
86
172
  return;
87
173
  }
88
-
89
- this.componentsWatcher = watch(
90
- dirPath,
91
- { recursive: true },
92
- async (event, filename) => {
93
- // Watch .json (component definitions), .js (component JavaScript), and .css (component CSS) files
94
- if (filename && (filename.endsWith('.json') || filename.endsWith('.js') || filename.endsWith('.css'))) {
95
- const fileType = filename.endsWith('.js') ? 'JavaScript' : filename.endsWith('.css') ? 'CSS' : 'component';
96
-
97
- if (this.callbacks.onComponentChange) {
98
- await this.callbacks.onComponentChange();
99
- }
174
+
175
+ this.componentsWatcher = watchRecursive(dirPath, async (_event, filename) => {
176
+ // Watch .json (component definitions), .js (component JavaScript), and .css (component CSS) files
177
+ if (filename && (filename.endsWith('.json') || filename.endsWith('.js') || filename.endsWith('.css'))) {
178
+ const _fileType = filename.endsWith('.js') ? 'JavaScript' : filename.endsWith('.css') ? 'CSS' : 'component';
179
+
180
+ if (this.callbacks.onComponentChange) {
181
+ await this.callbacks.onComponentChange();
100
182
  }
101
183
  }
102
- );
103
-
184
+ });
104
185
  }
105
-
186
+
106
187
  /**
107
188
  * Start watching pages directory
108
189
  */
@@ -111,21 +192,16 @@ export class FileWatcher {
111
192
  return;
112
193
  }
113
194
 
114
- this.pagesWatcher = watch(
115
- dirPath,
116
- { recursive: true },
117
- async (event, filename) => {
118
- if (filename && filename.endsWith('.json')) {
119
- const pageName = filename.replace('.json', '');
120
- const pagePath = mapPageNameToPath(pageName);
121
-
122
- if (this.callbacks.onPageChange) {
123
- await this.callbacks.onPageChange(pagePath);
124
- }
195
+ this.pagesWatcher = watchRecursive(dirPath, async (_event, filename) => {
196
+ if (filename?.endsWith('.json')) {
197
+ const pageName = filename.replace('.json', '');
198
+ const pagePath = mapPageNameToPath(pageName);
199
+
200
+ if (this.callbacks.onPageChange) {
201
+ await this.callbacks.onPageChange(pagePath);
125
202
  }
126
203
  }
127
- );
128
-
204
+ });
129
205
  }
130
206
 
131
207
  /**
@@ -137,11 +213,9 @@ export class FileWatcher {
137
213
  watchTemplates(dirPath: string = projectPaths.templates()): void {
138
214
  attachWhenDirExists(
139
215
  dirPath,
140
- () => watch(
141
- dirPath,
142
- { recursive: true },
143
- async (event, filename) => {
144
- if (filename && filename.endsWith('.json')) {
216
+ () =>
217
+ watchRecursive(dirPath, async (_event, filename) => {
218
+ if (filename?.endsWith('.json')) {
145
219
  // Prefix with templates/ so the path is /templates/...
146
220
  const pageName = `templates/${filename.replace('.json', '')}`;
147
221
  const pagePath = mapPageNameToPath(pageName);
@@ -150,12 +224,13 @@ export class FileWatcher {
150
224
  await this.callbacks.onPageChange(pagePath);
151
225
  }
152
226
  }
153
- }
154
- ),
155
- (w) => { this.templatesWatcher = w; }
227
+ }),
228
+ (w) => {
229
+ this.templatesWatcher = w;
230
+ },
156
231
  );
157
232
  }
158
-
233
+
159
234
  /**
160
235
  * Start watching colors.json file
161
236
  */
@@ -166,15 +241,11 @@ export class FileWatcher {
166
241
 
167
242
  // Watch the file's directory for changes
168
243
  const dirPath = getProjectRoot();
169
- this.colorsWatcher = watch(
170
- dirPath,
171
- { recursive: false },
172
- async (event, filename) => {
173
- if (filename === 'colors.json' && this.callbacks.onColorsChange) {
174
- await this.callbacks.onColorsChange();
175
- }
244
+ this.colorsWatcher = watch(dirPath, { recursive: false }, async (_event, filename) => {
245
+ if (filename === 'colors.json' && this.callbacks.onColorsChange) {
246
+ await this.callbacks.onColorsChange();
176
247
  }
177
- );
248
+ });
178
249
  }
179
250
 
180
251
  /**
@@ -182,15 +253,11 @@ export class FileWatcher {
182
253
  */
183
254
  watchVariables(): void {
184
255
  const dirPath = getProjectRoot();
185
- this.variablesWatcher = watch(
186
- dirPath,
187
- { recursive: false },
188
- async (event, filename) => {
189
- if (filename === 'variables.json' && this.callbacks.onVariablesChange) {
190
- await this.callbacks.onVariablesChange();
191
- }
256
+ this.variablesWatcher = watch(dirPath, { recursive: false }, async (_event, filename) => {
257
+ if (filename === 'variables.json' && this.callbacks.onVariablesChange) {
258
+ await this.callbacks.onVariablesChange();
192
259
  }
193
- );
260
+ });
194
261
  }
195
262
 
196
263
  /**
@@ -198,15 +265,11 @@ export class FileWatcher {
198
265
  */
199
266
  watchEnums(): void {
200
267
  const dirPath = getProjectRoot();
201
- this.enumsWatcher = watch(
202
- dirPath,
203
- { recursive: false },
204
- async (event, filename) => {
205
- if (filename === 'enums.json' && this.callbacks.onEnumsChange) {
206
- await this.callbacks.onEnumsChange();
207
- }
268
+ this.enumsWatcher = watch(dirPath, { recursive: false }, async (_event, filename) => {
269
+ if (filename === 'enums.json' && this.callbacks.onEnumsChange) {
270
+ await this.callbacks.onEnumsChange();
208
271
  }
209
- );
272
+ });
210
273
  }
211
274
 
212
275
  /**
@@ -217,20 +280,19 @@ export class FileWatcher {
217
280
  watchCMS(dirPath: string = projectPaths.cms()): void {
218
281
  attachWhenDirExists(
219
282
  dirPath,
220
- () => watch(
221
- dirPath,
222
- { recursive: true },
223
- async (event, filename) => {
224
- if (filename && filename.endsWith('.json')) {
283
+ () =>
284
+ watchRecursive(dirPath, async (_event, filename) => {
285
+ if (filename?.endsWith('.json')) {
225
286
  // Extract collection from path: "blog/my-post.json" -> "blog"
226
- const collection = filename.split('/')[0];
287
+ const collection = filename.split('/')[0] ?? filename;
227
288
  if (this.callbacks.onCMSChange) {
228
289
  await this.callbacks.onCMSChange(collection);
229
290
  }
230
291
  }
231
- }
232
- ),
233
- (w) => { this.cmsWatcher = w; }
292
+ }),
293
+ (w) => {
294
+ this.cmsWatcher = w;
295
+ },
234
296
  );
235
297
  }
236
298
 
@@ -242,15 +304,11 @@ export class FileWatcher {
242
304
  return;
243
305
  }
244
306
 
245
- this.imagesWatcher = watch(
246
- dirPath,
247
- { recursive: false },
248
- async (event, filename) => {
249
- if (event === 'rename' && filename && this.callbacks.onImageAdded) {
250
- await this.callbacks.onImageAdded(filename);
251
- }
307
+ this.imagesWatcher = watch(dirPath, { recursive: false }, async (event, filename) => {
308
+ if (event === 'rename' && filename && this.callbacks.onImageAdded) {
309
+ await this.callbacks.onImageAdded(filename);
252
310
  }
253
- );
311
+ });
254
312
  }
255
313
 
256
314
  /**
@@ -261,15 +319,11 @@ export class FileWatcher {
261
319
  */
262
320
  watchProjectConfig(): void {
263
321
  const dirPath = getProjectRoot();
264
- this.projectConfigWatcher = watch(
265
- dirPath,
266
- { recursive: false },
267
- async (_event, filename) => {
268
- if (filename === 'project.config.json' && this.callbacks.onProjectConfigChange) {
269
- await this.callbacks.onProjectConfigChange();
270
- }
322
+ this.projectConfigWatcher = watch(dirPath, { recursive: false }, async (_event, filename) => {
323
+ if (filename === 'project.config.json' && this.callbacks.onProjectConfigChange) {
324
+ await this.callbacks.onProjectConfigChange();
271
325
  }
272
- );
326
+ });
273
327
  }
274
328
 
275
329
  /**
@@ -280,17 +334,13 @@ export class FileWatcher {
280
334
  return;
281
335
  }
282
336
 
283
- this.librariesWatcher = watch(
284
- dirPath,
285
- { recursive: true },
286
- async (event, filename) => {
287
- if (filename && (filename.endsWith('.css') || filename.endsWith('.js'))) {
288
- if (this.callbacks.onLibraryChange) {
289
- await this.callbacks.onLibraryChange();
290
- }
337
+ this.librariesWatcher = watchRecursive(dirPath, async (_event, filename) => {
338
+ if (filename && (filename.endsWith('.css') || filename.endsWith('.js'))) {
339
+ if (this.callbacks.onLibraryChange) {
340
+ await this.callbacks.onLibraryChange();
291
341
  }
292
342
  }
293
- );
343
+ });
294
344
  }
295
345
 
296
346
  /**
@@ -299,35 +349,66 @@ export class FileWatcher {
299
349
  * (reload goes through the active provider), so file→editor live reload works
300
350
  * for `.astro` the same way it does for JSON. These are no-ops in JSON projects
301
351
  * (the src/ dirs don't exist).
352
+ *
353
+ * All three use deferred attach (like watchTemplates/watchCMS): `src/content`
354
+ * (and occasionally `src/components`) may not exist at startup — e.g. a project
355
+ * with no CMS collections yet. Without deferral the watcher would silently
356
+ * early-out and the first collection's item edits wouldn't live-reload until a
357
+ * dev-server restart.
302
358
  */
303
359
  watchAstroPages(dirPath: string = join(getProjectRoot(), 'src', 'pages')): void {
304
- if (!existsSync(dirPath)) return;
305
- this.astroPagesWatcher = watch(dirPath, { recursive: true }, async (_event, filename) => {
306
- // Skip dynamic CMS routes ([slug].astro) — those are CMS templates, not pages.
307
- if (filename && filename.endsWith('.astro') && !filename.includes('[')) {
308
- const pagePath = mapPageNameToPath(filename.replace(/\.astro$/, ''));
309
- if (this.callbacks.onPageChange) await this.callbacks.onPageChange(pagePath);
310
- }
311
- });
360
+ attachWhenDirExists(
361
+ dirPath,
362
+ () =>
363
+ watchRecursive(dirPath, async (_event, filename) => {
364
+ if (!filename?.endsWith('.astro')) return;
365
+ // Dynamic CMS routes ([slug].astro) are CMS template pages, not regular pages.
366
+ // A change there means a collection was added/edited/removed — route it to the
367
+ // CMS-template refresh (schema cache + /templates/<id> page cache). Mapping its
368
+ // on-disk name ([slug]) as a page path would be wrong, and skipping it (as before)
369
+ // left new collections + their template pages invisible until a dev-server restart.
370
+ if (filename.includes('[')) {
371
+ if (this.callbacks.onCMSTemplateChange) await this.callbacks.onCMSTemplateChange();
372
+ return;
373
+ }
374
+ const pagePath = mapPageNameToPath(filename.replace(/\.astro$/, ''));
375
+ if (this.callbacks.onPageChange) await this.callbacks.onPageChange(pagePath);
376
+ }),
377
+ (w) => {
378
+ this.astroPagesWatcher = w;
379
+ },
380
+ );
312
381
  }
313
382
 
314
383
  watchAstroComponents(dirPath: string = join(getProjectRoot(), 'src', 'components')): void {
315
- if (!existsSync(dirPath)) return;
316
- this.astroComponentsWatcher = watch(dirPath, { recursive: true }, async (_event, filename) => {
317
- if (filename && filename.endsWith('.astro') && this.callbacks.onComponentChange) {
318
- await this.callbacks.onComponentChange();
319
- }
320
- });
384
+ attachWhenDirExists(
385
+ dirPath,
386
+ () =>
387
+ watchRecursive(dirPath, async (_event, filename) => {
388
+ if (filename?.endsWith('.astro') && this.callbacks.onComponentChange) {
389
+ await this.callbacks.onComponentChange();
390
+ }
391
+ }),
392
+ (w) => {
393
+ this.astroComponentsWatcher = w;
394
+ },
395
+ );
321
396
  }
322
397
 
323
398
  watchAstroContent(dirPath: string = join(getProjectRoot(), 'src', 'content')): void {
324
- if (!existsSync(dirPath)) return;
325
- this.astroContentWatcher = watch(dirPath, { recursive: true }, async (_event, filename) => {
326
- if (filename && filename.endsWith('.json') && this.callbacks.onCMSChange) {
327
- const collection = filename.split('/')[0];
328
- await this.callbacks.onCMSChange(collection);
329
- }
330
- });
399
+ attachWhenDirExists(
400
+ dirPath,
401
+ () =>
402
+ watchRecursive(dirPath, async (_event, filename) => {
403
+ if (filename?.endsWith('.json') && this.callbacks.onCMSChange) {
404
+ const collection = filename.split('/')[0] ?? filename;
405
+ await this.callbacks.onCMSChange(collection);
406
+ }
407
+ }),
408
+ (w) => {
409
+ this.astroContentWatcher = w;
410
+ },
411
+ );
331
412
  }
332
413
 
333
414
  /**
@@ -349,7 +430,7 @@ export class FileWatcher {
349
430
  this.watchAstroComponents();
350
431
  this.watchAstroContent();
351
432
  }
352
-
433
+
353
434
  /**
354
435
  * Stop watching all directories
355
436
  */
@@ -424,7 +505,11 @@ export class FileWatcher {
424
505
  * Check if watchers are active
425
506
  */
426
507
  isWatching(): boolean {
427
- return this.componentsWatcher !== null || this.pagesWatcher !== null || this.cmsWatcher !== null || this.imagesWatcher !== null;
508
+ return (
509
+ this.componentsWatcher !== null ||
510
+ this.pagesWatcher !== null ||
511
+ this.cmsWatcher !== null ||
512
+ this.imagesWatcher !== null
513
+ );
428
514
  }
429
515
  }
430
-
@@ -29,7 +29,9 @@ export { VariableService, variableService } from './services/VariableService';
29
29
  export { EnumService, enumService } from './services/EnumService';
30
30
 
31
31
  // Providers
32
- export { FileSystemPageProvider } from './providers/fileSystemPageProvider';
32
+ // FileSystemPageProvider was retired with the bespoke JSON renderer (the editor is
33
+ // meno-astro-only; AstroPageProvider is the live page provider). FileSystemCMSProvider
34
+ // remains: the JSON→astro export (build-astro.ts) still reads JSON CMS items through it.
33
35
  export { FileSystemCMSProvider } from './providers/fileSystemCMSProvider';
34
36
 
35
37
  // CMS interfaces
@@ -39,10 +41,16 @@ export type { CMSProvider, CMSSchemaInfo } from '../shared/interfaces/contentPro
39
41
  export { handleCoreApiRoutes, type CoreRouteContext } from './routes/api/core-routes';
40
42
 
41
43
  // CMS read routes (GET handlers)
42
- export { handleCollectionsRoute, handleCollectionSchemaRoute, handleItemsRoute, handleItemRoute } from './routes/api/cms';
44
+ export {
45
+ handleCollectionsRoute,
46
+ handleCollectionSchemaRoute,
47
+ handleItemsRoute,
48
+ handleItemRoute,
49
+ } from './routes/api/cms';
43
50
 
44
51
  // Route utilities
45
52
  export { jsonResponse, errorResponse } from './routes/api/shared';
53
+ export { createCorsHeaders, handleCorsPreflight } from './middleware/cors';
46
54
 
47
55
  // Note: Write route handlers moved to @meno/studio:
48
56
  // - handleSavePageRoute
@@ -64,28 +72,17 @@ export * from './projectContext';
64
72
 
65
73
  // File watcher
66
74
  export { FileWatcherService } from './services/fileWatcherService';
75
+ // Recursive watch shim that also covers subdirs created AFTER the watch began (Bun's native
76
+ // `fs.watch({recursive:true})` does not — see fileWatcher.ts). Reused by the studio Astro
77
+ // dev-server's workdir mirror so a new collection/page dir live-syncs under Bun.
78
+ export { watchRecursive } from './fileWatcher';
67
79
 
68
80
  // Migration
69
81
  export { migrateTemplatesDirectory } from './migrateTemplates';
70
82
 
71
- // Static build
72
- export { buildStaticPages } from '../../build-static';
73
-
74
83
  // Astro export
75
84
  export { buildAstroProject } from '../../build-astro';
76
85
 
77
- // Next.js export
78
- export { buildNextProject } from '../../build-next';
79
-
80
- // Webflow export
81
- export { buildWebflowPayload, wrapInWebflowTemplate } from './webflow';
82
- export type {
83
- WebflowExportPayload,
84
- WebflowPage,
85
- WebflowElement,
86
- WebflowStyleClass,
87
- } from './webflow';
88
-
89
86
  // Utilities
90
87
  export * from './utils';
91
88
 
@@ -1,6 +1,11 @@
1
- import { describe, test, expect } from 'bun:test';
1
+ import { describe, test, expect, afterEach } from 'bun:test';
2
+ import { writeFileSync, mkdtempSync, rmSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { tmpdir } from 'node:os';
2
5
  import {
3
6
  parseJSON,
7
+ loadAndParseJSON,
8
+ loadJSONFile,
4
9
  mapPageNameToPath,
5
10
  mapPathToPageName,
6
11
  getBreakpointConfig,
@@ -8,6 +13,7 @@ import {
8
13
  getI18nConfig,
9
14
  setI18nConfig,
10
15
  } from './jsonLoader';
16
+ import { setErrorHandler } from '../shared/errorLogger';
11
17
  import { DEFAULT_BREAKPOINTS } from '../shared/breakpoints';
12
18
  import { DEFAULT_I18N_CONFIG } from '../shared/i18n';
13
19
 
@@ -56,6 +62,37 @@ describe('jsonLoader', () => {
56
62
  });
57
63
  });
58
64
 
65
+ describe('file-load error observability', () => {
66
+ afterEach(() => setErrorHandler(null));
67
+
68
+ test('loadAndParseJSON surfaces corrupt JSON via the error handler but still returns null', async () => {
69
+ const dir = mkdtempSync(join(tmpdir(), 'jsonloader-'));
70
+ const filePath = join(dir, 'broken.json');
71
+ writeFileSync(filePath, '{ not valid json ', 'utf8');
72
+
73
+ const entries: string[] = [];
74
+ setErrorHandler((entry) => entries.push(entry.location));
75
+
76
+ try {
77
+ const result = await loadAndParseJSON(filePath);
78
+ expect(result).toBeNull();
79
+ expect(entries).toContain('jsonLoader.loadAndParseJSON');
80
+ } finally {
81
+ rmSync(dir, { recursive: true, force: true });
82
+ }
83
+ });
84
+
85
+ test('loadJSONFile returns null without logging for a missing file', async () => {
86
+ const entries: string[] = [];
87
+ setErrorHandler((entry) => entries.push(entry.location));
88
+
89
+ const result = await loadJSONFile(join(tmpdir(), 'definitely-does-not-exist-xyz.json'));
90
+ expect(result).toBeNull();
91
+ // Missing file is gated by fileExists() — no error should be logged.
92
+ expect(entries).toHaveLength(0);
93
+ });
94
+ });
95
+
59
96
  describe('breakpoint config cache', () => {
60
97
  test('returns default config initially', () => {
61
98
  const config = getBreakpointConfig();
@@ -65,7 +102,7 @@ describe('jsonLoader', () => {
65
102
  test('returns cached config after set', () => {
66
103
  const customConfig = {
67
104
  mobile: { breakpoint: 500, previewPoint: 375 },
68
- tablet: { breakpoint: 900, previewPoint: 768 }
105
+ tablet: { breakpoint: 900, previewPoint: 768 },
69
106
  };
70
107
  setBreakpointConfig(customConfig);
71
108
  const config = getBreakpointConfig();