meno-core 1.0.53 → 1.0.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (387) hide show
  1. package/.claude/settings.local.json +1 -3
  2. package/bin/cli.ts +48 -57
  3. package/build-astro.ts +131 -113
  4. package/build-next.ts +109 -96
  5. package/build-static.test.ts +39 -10
  6. package/build-static.ts +120 -122
  7. package/dist/bin/cli.js +34 -38
  8. package/dist/bin/cli.js.map +2 -2
  9. package/dist/build-static.js +12 -11
  10. package/dist/chunks/chunk-2AR55GYH.js +42 -0
  11. package/dist/chunks/chunk-2AR55GYH.js.map +7 -0
  12. package/dist/chunks/{chunk-IGYR22T6.js → chunk-2FN4UOVO.js} +512 -239
  13. package/dist/chunks/chunk-2FN4UOVO.js.map +7 -0
  14. package/dist/chunks/chunk-3XER4E5W.js +168 -0
  15. package/dist/chunks/chunk-3XER4E5W.js.map +7 -0
  16. package/dist/chunks/{chunk-X754AHS5.js → chunk-5ETZFREW.js} +9 -12
  17. package/dist/chunks/chunk-5ETZFREW.js.map +7 -0
  18. package/dist/chunks/{chunk-2MHDV5BF.js → chunk-7E4IF5L7.js} +15 -21
  19. package/dist/chunks/chunk-7E4IF5L7.js.map +7 -0
  20. package/dist/chunks/{chunk-JGWFTO6P.js → chunk-7HWQUVTU.js} +1578 -1349
  21. package/dist/chunks/chunk-7HWQUVTU.js.map +7 -0
  22. package/dist/chunks/{chunk-O3NAGJP4.js → chunk-AE3QK5QW.js} +110 -21
  23. package/dist/chunks/chunk-AE3QK5QW.js.map +7 -0
  24. package/dist/chunks/{chunk-GZHGVVW3.js → chunk-F6KTJYGV.js} +7 -7
  25. package/dist/chunks/chunk-F6KTJYGV.js.map +7 -0
  26. package/dist/chunks/{chunk-WQFG7PAH.js → chunk-FZITJSSS.js} +2 -6
  27. package/dist/chunks/chunk-FZITJSSS.js.map +7 -0
  28. package/dist/chunks/{chunk-QB2LNO4W.js → chunk-GSYYA5GX.js} +2 -2
  29. package/dist/chunks/chunk-GSYYA5GX.js.map +7 -0
  30. package/dist/chunks/{chunk-YBLHKYFF.js → chunk-HIZMY3EP.js} +1 -1
  31. package/dist/chunks/chunk-HIZMY3EP.js.map +7 -0
  32. package/dist/chunks/{chunk-AZQYF6KE.js → chunk-I2WEGYA7.js} +41 -176
  33. package/dist/chunks/chunk-I2WEGYA7.js.map +7 -0
  34. package/dist/chunks/{chunk-I7YIGZXT.js → chunk-JNO3CNLJ.js} +6 -9
  35. package/dist/chunks/chunk-JNO3CNLJ.js.map +7 -0
  36. package/dist/chunks/{chunk-UB44F4Z2.js → chunk-NVRBTSQG.js} +2 -4
  37. package/dist/chunks/chunk-NVRBTSQG.js.map +7 -0
  38. package/dist/chunks/{chunk-JGP5A3Y5.js → chunk-Q4OBWKXG.js} +40 -33
  39. package/dist/chunks/chunk-Q4OBWKXG.js.map +7 -0
  40. package/dist/chunks/{chunk-R6XHAFBF.js → chunk-QTE32Y53.js} +250 -242
  41. package/dist/chunks/chunk-QTE32Y53.js.map +7 -0
  42. package/dist/chunks/{chunk-H3GJ4H2U.js → chunk-STDY3OVM.js} +214 -85
  43. package/dist/chunks/chunk-STDY3OVM.js.map +7 -0
  44. package/dist/chunks/configService-PRJZF7Y6.js +14 -0
  45. package/dist/chunks/{constants-STK2YBIW.js → constants-KIQEYMAM.js} +2 -2
  46. package/dist/chunks/{fs-JGINUXGL.js → fs-ZI5JEU7V.js} +2 -2
  47. package/dist/entries/server-router.js +14 -19
  48. package/dist/entries/server-router.js.map +2 -2
  49. package/dist/lib/client/index.js +638 -332
  50. package/dist/lib/client/index.js.map +2 -2
  51. package/dist/lib/server/index.js +177 -235
  52. package/dist/lib/server/index.js.map +2 -2
  53. package/dist/lib/shared/index.js +80 -44
  54. package/dist/lib/shared/index.js.map +2 -2
  55. package/dist/lib/shared/richtext/index.js +1 -1
  56. package/dist/lib/test-utils/index.js +38 -60
  57. package/dist/lib/test-utils/index.js.map +2 -2
  58. package/entries/client-router.tsx +10 -8
  59. package/entries/server-router.tsx +1 -7
  60. package/lib/client/ClientInitializer.ts +8 -8
  61. package/lib/client/ErrorBoundary.test.tsx +146 -143
  62. package/lib/client/ErrorBoundary.tsx +175 -127
  63. package/lib/client/componentRegistry.test.ts +96 -108
  64. package/lib/client/componentRegistry.ts +1 -2
  65. package/lib/client/contexts/ThemeContext.tsx +3 -2
  66. package/lib/client/core/ComponentBuilder.test.ts +513 -560
  67. package/lib/client/core/ComponentBuilder.ts +318 -146
  68. package/lib/client/core/ComponentRenderer.test.tsx +1 -2
  69. package/lib/client/core/ComponentRenderer.tsx +46 -33
  70. package/lib/client/core/builders/embedBuilder.ts +241 -53
  71. package/lib/client/core/builders/linkBuilder.ts +71 -44
  72. package/lib/client/core/builders/linkNodeBuilder.ts +78 -53
  73. package/lib/client/core/builders/listBuilder.ts +115 -86
  74. package/lib/client/core/builders/localeListBuilder.ts +95 -60
  75. package/lib/client/core/builders/types.ts +5 -5
  76. package/lib/client/core/cmsTemplateProcessor.ts +7 -7
  77. package/lib/client/elementRegistry.ts +3 -3
  78. package/lib/client/fontFamiliesService.test.ts +2 -10
  79. package/lib/client/fontFamiliesService.ts +3 -3
  80. package/lib/client/hmr/HMRManager.tsx +8 -0
  81. package/lib/client/hmrCssReload.ts +14 -8
  82. package/lib/client/hmrWebSocket.ts +9 -14
  83. package/lib/client/hooks/useColorVariables.test.ts +21 -21
  84. package/lib/client/hooks/useColorVariables.ts +12 -10
  85. package/lib/client/hooks/usePropertyAutocomplete.ts +3 -5
  86. package/lib/client/hooks/useVariables.ts +4 -4
  87. package/lib/client/hydration/HydrationUtils.test.ts +24 -25
  88. package/lib/client/hydration/HydrationUtils.ts +3 -4
  89. package/lib/client/i18nConfigService.test.ts +2 -7
  90. package/lib/client/i18nConfigService.ts +2 -2
  91. package/lib/client/meno-filter/MenoFilter.test.ts +19 -21
  92. package/lib/client/meno-filter/MenoFilter.ts +5 -9
  93. package/lib/client/meno-filter/bindings.ts +15 -40
  94. package/lib/client/meno-filter/init.ts +1 -1
  95. package/lib/client/meno-filter/renderer.ts +23 -29
  96. package/lib/client/meno-filter/script.generated.ts +1 -3
  97. package/lib/client/meno-filter/ui.ts +3 -5
  98. package/lib/client/meno-filter/updates.ts +15 -21
  99. package/lib/client/navigation.test.ts +159 -159
  100. package/lib/client/navigation.ts +0 -1
  101. package/lib/client/responsiveStyleResolver.test.ts +230 -228
  102. package/lib/client/responsiveStyleResolver.ts +13 -16
  103. package/lib/client/routing/RouteLoader.test.ts +23 -24
  104. package/lib/client/routing/RouteLoader.ts +22 -35
  105. package/lib/client/routing/Router.tsx +31 -3
  106. package/lib/client/scripts/ScriptExecutor.test.ts +157 -158
  107. package/lib/client/scripts/ScriptExecutor.ts +15 -33
  108. package/lib/client/services/PrefetchService.test.ts +2 -2
  109. package/lib/client/services/PrefetchService.ts +10 -24
  110. package/lib/client/styleProcessor.test.ts +9 -9
  111. package/lib/client/styleProcessor.ts +18 -15
  112. package/lib/client/styles/StyleInjector.test.ts +122 -115
  113. package/lib/client/styles/StyleInjector.ts +9 -6
  114. package/lib/client/styles/UtilityClassCollector.ts +19 -26
  115. package/lib/client/styles/cspNonce.test.ts +2 -5
  116. package/lib/client/templateEngine.test.ts +554 -482
  117. package/lib/client/templateEngine.ts +200 -64
  118. package/lib/client/theme.ts +0 -1
  119. package/lib/client/utils/toast.ts +0 -1
  120. package/lib/server/__integration__/api-routes.test.ts +8 -4
  121. package/lib/server/__integration__/cms-integration.test.ts +1 -4
  122. package/lib/server/__integration__/server-lifecycle.test.ts +2 -5
  123. package/lib/server/__integration__/ssr-rendering.test.ts +47 -37
  124. package/lib/server/__integration__/static-assets.test.ts +1 -1
  125. package/lib/server/__integration__/test-helpers.ts +84 -70
  126. package/lib/server/ab/generateFunctions.ts +12 -10
  127. package/lib/server/astro/cmsPageEmitter.ts +23 -38
  128. package/lib/server/astro/componentEmitter.ts +25 -36
  129. package/lib/server/astro/cssCollector.ts +10 -26
  130. package/lib/server/astro/nodeToAstro.test.ts +1750 -30
  131. package/lib/server/astro/nodeToAstro.ts +198 -187
  132. package/lib/server/astro/normalizeOrphanTemplateProps.test.ts +105 -109
  133. package/lib/server/astro/normalizeOrphanTemplateProps.ts +4 -12
  134. package/lib/server/astro/pageEmitter.ts +9 -13
  135. package/lib/server/astro/tailwindMapper.test.ts +10 -37
  136. package/lib/server/astro/tailwindMapper.ts +33 -40
  137. package/lib/server/astro/templateTransformer.ts +14 -17
  138. package/lib/server/createServer.ts +5 -17
  139. package/lib/server/cssGenerator.test.ts +35 -44
  140. package/lib/server/cssGenerator.ts +6 -17
  141. package/lib/server/fileWatcher.test.ts +124 -10
  142. package/lib/server/fileWatcher.ts +124 -118
  143. package/lib/server/index.ts +7 -1
  144. package/lib/server/jsonLoader.test.ts +39 -2
  145. package/lib/server/jsonLoader.ts +33 -31
  146. package/lib/server/middleware/cors.test.ts +20 -20
  147. package/lib/server/middleware/cors.ts +28 -4
  148. package/lib/server/middleware/errorHandler.test.ts +5 -3
  149. package/lib/server/middleware/errorHandler.ts +3 -8
  150. package/lib/server/middleware/index.ts +0 -1
  151. package/lib/server/middleware/logger.test.ts +7 -5
  152. package/lib/server/middleware/logger.ts +10 -22
  153. package/lib/server/pageCache.test.ts +76 -77
  154. package/lib/server/pageCache.ts +0 -1
  155. package/lib/server/projectContext.ts +4 -3
  156. package/lib/server/providers/fileSystemCMSProvider.test.ts +124 -95
  157. package/lib/server/providers/fileSystemCMSProvider.ts +35 -20
  158. package/lib/server/providers/fileSystemPageProvider.test.ts +84 -0
  159. package/lib/server/providers/fileSystemPageProvider.ts +31 -12
  160. package/lib/server/routes/api/cms.test.ts +26 -14
  161. package/lib/server/routes/api/cms.ts +9 -14
  162. package/lib/server/routes/api/components.ts +30 -34
  163. package/lib/server/routes/api/config.ts +0 -1
  164. package/lib/server/routes/api/core-routes.ts +47 -74
  165. package/lib/server/routes/api/functions.ts +8 -16
  166. package/lib/server/routes/api/index.ts +3 -6
  167. package/lib/server/routes/api/pages.ts +8 -11
  168. package/lib/server/routes/api/shared.test.ts +1 -1
  169. package/lib/server/routes/api/shared.ts +12 -9
  170. package/lib/server/routes/api/variables.test.ts +1 -3
  171. package/lib/server/routes/api/variables.ts +1 -1
  172. package/lib/server/routes/index.ts +23 -26
  173. package/lib/server/routes/pages.ts +34 -29
  174. package/lib/server/routes/static.ts +16 -4
  175. package/lib/server/runtime/bundler.ts +47 -32
  176. package/lib/server/runtime/fs.ts +3 -13
  177. package/lib/server/runtime/httpServer.ts +5 -9
  178. package/lib/server/services/ColorService.ts +32 -27
  179. package/lib/server/services/EnumService.test.ts +2 -6
  180. package/lib/server/services/EnumService.ts +7 -2
  181. package/lib/server/services/VariableService.test.ts +1 -5
  182. package/lib/server/services/VariableService.ts +6 -1
  183. package/lib/server/services/cmsService.test.ts +116 -78
  184. package/lib/server/services/cmsService.ts +24 -54
  185. package/lib/server/services/componentService.test.ts +122 -36
  186. package/lib/server/services/componentService.ts +54 -38
  187. package/lib/server/services/configService.test.ts +9 -31
  188. package/lib/server/services/configService.ts +20 -27
  189. package/lib/server/services/fileWatcherService.ts +40 -6
  190. package/lib/server/services/index.ts +0 -1
  191. package/lib/server/services/pageService.test.ts +1 -3
  192. package/lib/server/services/pageService.ts +14 -13
  193. package/lib/server/ssr/attributeBuilder.ts +16 -6
  194. package/lib/server/ssr/buildErrorOverlay.ts +11 -12
  195. package/lib/server/ssr/clientDataInjector.ts +7 -21
  196. package/lib/server/ssr/cmsSSRProcessor.ts +3 -6
  197. package/lib/server/ssr/cssCollector.ts +1 -1
  198. package/lib/server/ssr/errorOverlay.test.ts +1 -1
  199. package/lib/server/ssr/errorOverlay.ts +3 -9
  200. package/lib/server/ssr/htmlGenerator.nonce.test.ts +3 -9
  201. package/lib/server/ssr/htmlGenerator.test.ts +120 -43
  202. package/lib/server/ssr/htmlGenerator.ts +120 -85
  203. package/lib/server/ssr/imageMetadata.test.ts +3 -1
  204. package/lib/server/ssr/imageMetadata.ts +25 -19
  205. package/lib/server/ssr/jsCollector.test.ts +3 -13
  206. package/lib/server/ssr/jsCollector.ts +3 -8
  207. package/lib/server/ssr/liveReloadIntegration.test.ts +69 -28
  208. package/lib/server/ssr/metaTagGenerator.ts +2 -2
  209. package/lib/server/ssr/ssrRenderer.branches.test.ts +1103 -0
  210. package/lib/server/ssr/ssrRenderer.test.ts +196 -246
  211. package/lib/server/ssr/ssrRenderer.ts +609 -225
  212. package/lib/server/ssrRenderer.test.ts +1044 -950
  213. package/lib/server/utils/jsonLineMapper.test.ts +28 -28
  214. package/lib/server/utils/jsonLineMapper.ts +1 -1
  215. package/lib/server/validateStyleCoverage.ts +18 -20
  216. package/lib/server/webflow/buildWebflow.ts +41 -53
  217. package/lib/server/webflow/nodeToWebflow.test.ts +150 -218
  218. package/lib/server/webflow/nodeToWebflow.ts +195 -258
  219. package/lib/server/webflow/styleMapper.test.ts +15 -56
  220. package/lib/server/webflow/styleMapper.ts +33 -41
  221. package/lib/server/webflow/types.ts +1 -8
  222. package/lib/server/websocketManager.ts +16 -19
  223. package/lib/shared/attributeNodeUtils.test.ts +15 -15
  224. package/lib/shared/attributeNodeUtils.ts +5 -12
  225. package/lib/shared/breakpoints.ts +4 -11
  226. package/lib/shared/cmsQueryParser.test.ts +50 -42
  227. package/lib/shared/cmsQueryParser.ts +49 -31
  228. package/lib/shared/colorVariableUtils.test.ts +5 -5
  229. package/lib/shared/colorVariableUtils.ts +3 -8
  230. package/lib/shared/componentRefs.ts +1 -5
  231. package/lib/shared/constants.test.ts +3 -3
  232. package/lib/shared/constants.ts +4 -8
  233. package/lib/shared/cssGeneration.test.ts +262 -144
  234. package/lib/shared/cssGeneration.ts +189 -516
  235. package/lib/shared/cssNamedColors.ts +152 -30
  236. package/lib/shared/cssProperties.test.ts +5 -6
  237. package/lib/shared/cssProperties.ts +296 -112
  238. package/lib/shared/elementClassName.test.ts +109 -109
  239. package/lib/shared/elementClassName.ts +1 -1
  240. package/lib/shared/elementUtils.ts +12 -16
  241. package/lib/shared/errorLogger.ts +2 -10
  242. package/lib/shared/errors.test.ts +2 -13
  243. package/lib/shared/errors.ts +2 -8
  244. package/lib/shared/expressionEvaluator.test.ts +119 -0
  245. package/lib/shared/expressionEvaluator.ts +43 -24
  246. package/lib/shared/fontLoader.test.ts +19 -5
  247. package/lib/shared/friendlyError.ts +2 -3
  248. package/lib/shared/gradientUtils.test.ts +1 -5
  249. package/lib/shared/gradientUtils.ts +2 -6
  250. package/lib/shared/hrefRefs.ts +2 -10
  251. package/lib/shared/i18n.test.ts +1 -1
  252. package/lib/shared/i18n.ts +13 -34
  253. package/lib/shared/index.ts +4 -0
  254. package/lib/shared/inlineSvgStyleRules.ts +2 -4
  255. package/lib/shared/interactiveStyleMappings.test.ts +11 -33
  256. package/lib/shared/interactiveStyleMappings.ts +9 -16
  257. package/lib/shared/interactiveStyles.test.ts +165 -188
  258. package/lib/shared/interfaces/contentProvider.ts +1 -1
  259. package/lib/shared/itemTemplateUtils.test.ts +6 -12
  260. package/lib/shared/itemTemplateUtils.ts +19 -35
  261. package/lib/shared/jsonRepair.ts +8 -2
  262. package/lib/shared/libraryLoader.test.ts +15 -49
  263. package/lib/shared/libraryLoader.ts +7 -22
  264. package/lib/shared/netlifyLocale404.test.ts +179 -0
  265. package/lib/shared/netlifyLocale404.ts +110 -0
  266. package/lib/shared/nodeUtils.test.ts +24 -16
  267. package/lib/shared/nodeUtils.ts +49 -19
  268. package/lib/shared/pathArrayUtils.test.ts +1 -2
  269. package/lib/shared/pathArrayUtils.ts +1 -1
  270. package/lib/shared/pathSecurity.ts +1 -1
  271. package/lib/shared/pathUtils.test.ts +4 -6
  272. package/lib/shared/pathUtils.ts +42 -48
  273. package/lib/shared/paths/Path.test.ts +2 -2
  274. package/lib/shared/paths/Path.ts +0 -1
  275. package/lib/shared/paths/PathConverter.test.ts +1 -1
  276. package/lib/shared/paths/PathConverter.ts +14 -17
  277. package/lib/shared/paths/PathUtils.ts +9 -10
  278. package/lib/shared/paths/PathValidator.test.ts +2 -15
  279. package/lib/shared/paths/PathValidator.ts +11 -9
  280. package/lib/shared/paths/index.ts +1 -2
  281. package/lib/shared/propResolver.test.ts +240 -244
  282. package/lib/shared/propResolver.ts +14 -25
  283. package/lib/shared/pxToRem.test.ts +7 -6
  284. package/lib/shared/pxToRem.ts +2 -5
  285. package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +9 -5
  286. package/lib/shared/registry/ClientRegistry.ts +0 -1
  287. package/lib/shared/registry/ComponentRegistry.test.ts +43 -29
  288. package/lib/shared/registry/ComponentRegistry.ts +9 -11
  289. package/lib/shared/registry/NodeTypeDefinition.ts +15 -8
  290. package/lib/shared/registry/RegistryManager.ts +1 -2
  291. package/lib/shared/registry/SSRRegistry.ts +0 -1
  292. package/lib/shared/registry/createNodeType.ts +7 -9
  293. package/lib/shared/registry/defineNodeType.ts +2 -6
  294. package/lib/shared/registry/index.ts +0 -1
  295. package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +14 -15
  296. package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +18 -11
  297. package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +47 -18
  298. package/lib/shared/registry/nodeTypes/LinkNodeType.ts +21 -19
  299. package/lib/shared/registry/nodeTypes/ListNodeType.ts +78 -74
  300. package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +27 -21
  301. package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +6 -7
  302. package/lib/shared/registry/nodeTypes/index.ts +10 -2
  303. package/lib/shared/responsiveScaling.test.ts +15 -31
  304. package/lib/shared/responsiveScaling.ts +55 -37
  305. package/lib/shared/responsiveStyleUtils.ts +11 -13
  306. package/lib/shared/richtext/htmlToTiptap.test.ts +23 -14
  307. package/lib/shared/richtext/htmlToTiptap.ts +1 -3
  308. package/lib/shared/richtext/tiptapToHtml.test.ts +5 -6
  309. package/lib/shared/richtext/types.ts +1 -8
  310. package/lib/shared/slugTranslator.test.ts +13 -13
  311. package/lib/shared/slugTranslator.ts +12 -16
  312. package/lib/shared/slugify.ts +9 -15
  313. package/lib/shared/styleNodeUtils.test.ts +8 -8
  314. package/lib/shared/styleNodeUtils.ts +5 -10
  315. package/lib/shared/styleUtils.test.ts +87 -61
  316. package/lib/shared/styleUtils.ts +5 -6
  317. package/lib/shared/themeDefaults.test.ts +11 -11
  318. package/lib/shared/themeDefaults.ts +3 -4
  319. package/lib/shared/tree/PathBuilder.test.ts +62 -138
  320. package/lib/shared/tree/PathBuilder.ts +49 -39
  321. package/lib/shared/treePathUtils.test.ts +2 -10
  322. package/lib/shared/treePathUtils.ts +54 -59
  323. package/lib/shared/types/api.ts +1 -2
  324. package/lib/shared/types/cms.ts +25 -21
  325. package/lib/shared/types/comment.ts +49 -16
  326. package/lib/shared/types/components.ts +27 -25
  327. package/lib/shared/types/errors.test.ts +1 -6
  328. package/lib/shared/types/errors.ts +3 -7
  329. package/lib/shared/types/experiments.ts +28 -28
  330. package/lib/shared/types/index.ts +2 -2
  331. package/lib/shared/types/styles.ts +0 -1
  332. package/lib/shared/types/variables.test.ts +4 -13
  333. package/lib/shared/types/variables.ts +48 -27
  334. package/lib/shared/types.ts +1 -2
  335. package/lib/shared/utilityClassConfig.ts +648 -321
  336. package/lib/shared/utilityClassMapper.test.ts +203 -92
  337. package/lib/shared/utilityClassMapper.ts +188 -246
  338. package/lib/shared/utilityClassNames.ts +326 -0
  339. package/lib/shared/utils.test.ts +2 -10
  340. package/lib/shared/utils.ts +19 -10
  341. package/lib/shared/validation/cmsValidators.ts +2 -1
  342. package/lib/shared/validation/commentValidators.test.ts +53 -0
  343. package/lib/shared/validation/commentValidators.ts +12 -1
  344. package/lib/shared/validation/propValidator.test.ts +18 -20
  345. package/lib/shared/validation/propValidator.ts +12 -17
  346. package/lib/shared/validation/schemas.test.ts +24 -33
  347. package/lib/shared/validation/schemas.ts +469 -344
  348. package/lib/shared/validation/validators.test.ts +1 -6
  349. package/lib/shared/validation/validators.ts +89 -68
  350. package/lib/shared/viewportUnits.integration.test.ts +14 -10
  351. package/lib/shared/viewportUnits.test.ts +11 -23
  352. package/lib/test-utils/dom-setup.ts +1 -1
  353. package/lib/test-utils/factories/ConsoleMockFactory.ts +3 -7
  354. package/lib/test-utils/factories/DomMockFactory.ts +7 -19
  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 +5 -9
  358. package/lib/test-utils/factories/StoreMockFactory.ts +14 -25
  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 +12 -18
  365. package/lib/test-utils/mocks.ts +4 -2
  366. package/package.json +1 -1
  367. package/scripts/build-meno-filter.ts +1 -4
  368. package/vite.config.ts +4 -4
  369. package/dist/chunks/chunk-2MHDV5BF.js.map +0 -7
  370. package/dist/chunks/chunk-AZQYF6KE.js.map +0 -7
  371. package/dist/chunks/chunk-GZHGVVW3.js.map +0 -7
  372. package/dist/chunks/chunk-H3GJ4H2U.js.map +0 -7
  373. package/dist/chunks/chunk-I7YIGZXT.js.map +0 -7
  374. package/dist/chunks/chunk-IGYR22T6.js.map +0 -7
  375. package/dist/chunks/chunk-JGP5A3Y5.js.map +0 -7
  376. package/dist/chunks/chunk-JGWFTO6P.js.map +0 -7
  377. package/dist/chunks/chunk-O3NAGJP4.js.map +0 -7
  378. package/dist/chunks/chunk-QB2LNO4W.js.map +0 -7
  379. package/dist/chunks/chunk-R6XHAFBF.js.map +0 -7
  380. package/dist/chunks/chunk-UB44F4Z2.js.map +0 -7
  381. package/dist/chunks/chunk-WQFG7PAH.js.map +0 -7
  382. package/dist/chunks/chunk-X754AHS5.js.map +0 -7
  383. package/dist/chunks/chunk-YBLHKYFF.js.map +0 -7
  384. package/dist/chunks/configService-R3OGU2UD.js +0 -13
  385. /package/dist/chunks/{configService-R3OGU2UD.js.map → configService-PRJZF7Y6.js.map} +0 -0
  386. /package/dist/chunks/{constants-STK2YBIW.js.map → constants-KIQEYMAM.js.map} +0 -0
  387. /package/dist/chunks/{fs-JGINUXGL.js.map → fs-ZI5JEU7V.js.map} +0 -0
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Locale-scoped Netlify 404 rules + the managed netlify.toml block that carries them.
3
+ *
4
+ * Why: a multi-locale static build emits a localized 404 per non-default locale
5
+ * (`dist/pl/404/index.html`), but a static host serves exactly ONE catch-all 404
6
+ * page unless told otherwise — so `/pl/<missing>` would get the default-locale
7
+ * `404.html`. These rules scope the 404 per locale prefix.
8
+ *
9
+ * Shared by every netlify.toml producer — meno-astro's `convertProject` scaffold,
10
+ * the JSON→Astro export (`build-astro.ts`), the CLI new-project scaffold — and by
11
+ * the studio save-config route, which re-syncs the managed block when the locale
12
+ * set changes AFTER the file was first written (so a project that adds a locale
13
+ * later still gets its rules). It lives in meno-core because the dependency
14
+ * direction is astro→core: meno-core must never import from meno-astro.
15
+ */
16
+
17
+ import type { I18nConfig } from './types';
18
+
19
+ /**
20
+ * First line of the managed block. Everything between the markers (inclusive) is
21
+ * generated and may be rewritten wholesale by `syncNetlifyLocale404Block`; user
22
+ * content outside the markers is never touched.
23
+ */
24
+ export const NETLIFY_LOCALE_404_BEGIN = '# BEGIN meno locale-404 redirects (generated — do not edit between markers)';
25
+
26
+ /** Last line of the managed block. */
27
+ export const NETLIFY_LOCALE_404_END = '# END meno locale-404 redirects';
28
+
29
+ /**
30
+ * Locale-scoped 404 rules for a netlify.toml — one `[[redirects]]` block per
31
+ * non-default locale (`''` for single-locale configs):
32
+ *
33
+ * [[redirects]]
34
+ * from = "/pl/*"
35
+ * to = "/pl/404/index.html"
36
+ * status = 404
37
+ *
38
+ * Netlify semantics (verified against Netlify's "redirect options" docs, the
39
+ * documented "localized custom 404" pattern): a redirect with `status = 404` and no
40
+ * `force` is a NON-SHADOWING rewrite — a request that matches an existing file is
41
+ * served normally (real `/pl/...` pages always win), and only an unmatched `/pl/*`
42
+ * URL serves the `to` file's CONTENT with an HTTP 404 status (the URL stays put; no
43
+ * 3xx). Everything the rules don't cover falls through to Netlify's root convention:
44
+ * a top-level `404.html` (which Astro builds from `src/pages/404.astro`) is served
45
+ * automatically for any other unmatched URL — no explicit catch-all rule needed.
46
+ *
47
+ * Locale codes are sanity-checked (`/^[A-Za-z0-9_-]+$/`): a malformed code would
48
+ * corrupt the TOML / redirect syntax, and such a locale cannot produce a route anyway.
49
+ *
50
+ * Pure (config in, TOML fragment out) so it is directly unit-testable; callers own
51
+ * the existence gates (404 page present, netlify.toml ownership). Most callers
52
+ * should go through `syncNetlifyLocale404Block`, which wraps the rules in the
53
+ * managed marker block so later config saves can update them in place.
54
+ */
55
+ export function netlifyLocale404Redirects(config: I18nConfig): string {
56
+ if (!config.locales || config.locales.length <= 1) return '';
57
+ const codes = config.locales
58
+ .map((l) => l.code)
59
+ .filter((code) => code !== config.defaultLocale && /^[A-Za-z0-9_-]+$/.test(code));
60
+ if (codes.length === 0) return '';
61
+ const header =
62
+ "\n# Locale-scoped 404s — an unmatched /<locale>/* URL serves that locale's own\n" +
63
+ '# prerendered 404 page WITH a 404 status (non-shadowing: real pages still win).\n' +
64
+ '# Unmatched URLs outside these prefixes fall through to the default /404.html.\n';
65
+ const rules = codes
66
+ .map((code) => `[[redirects]]\n from = "/${code}/*"\n to = "/${code}/404/index.html"\n status = 404\n`)
67
+ .join('\n');
68
+ return header + rules;
69
+ }
70
+
71
+ /**
72
+ * Insert / refresh / remove the managed locale-404 block in netlify.toml content.
73
+ * Pure (string in, string out) — callers own reading/writing the file.
74
+ *
75
+ * - Rules applicable (multi-locale config AND `has404`) + no block yet → the block
76
+ * is APPENDED (a trailing newline is added to the existing content first if it
77
+ * lacks one).
78
+ * - Rules applicable + block present → the block's contents are REPLACED in place
79
+ * (idempotent: re-syncing identical state is a no-op).
80
+ * - Rules no longer applicable (single-locale, or no 404 page) + block present →
81
+ * the whole block is REMOVED; everything outside the markers stays byte-identical.
82
+ * - Nothing applicable + no block → the content is returned unchanged.
83
+ *
84
+ * A corrupted block (BEGIN marker without its END) is left untouched — rewriting up
85
+ * to an unknown boundary could eat user content, and a hand-damaged block is the
86
+ * user taking ownership.
87
+ */
88
+ export function syncNetlifyLocale404Block(content: string, config: I18nConfig, has404: boolean): string {
89
+ const rules = has404 ? netlifyLocale404Redirects(config) : '';
90
+ // `rules` starts with '\n' (terminating the BEGIN line) and ends with '\n'
91
+ // (putting END at line start), so the block is exactly BEGIN…rules…END lines.
92
+ const block = rules ? `${NETLIFY_LOCALE_404_BEGIN}${rules}${NETLIFY_LOCALE_404_END}\n` : '';
93
+
94
+ const beginIdx = content.indexOf(NETLIFY_LOCALE_404_BEGIN);
95
+ if (beginIdx === -1) {
96
+ if (!block) return content;
97
+ const base = content === '' || content.endsWith('\n') ? content : `${content}\n`;
98
+ return base + block;
99
+ }
100
+
101
+ const endMarkerIdx = content.indexOf(NETLIFY_LOCALE_404_END, beginIdx);
102
+ if (endMarkerIdx === -1) return content; // corrupted block — refuse to guess its extent
103
+
104
+ let endIdx = endMarkerIdx + NETLIFY_LOCALE_404_END.length;
105
+ if (content[endIdx] === '\n') endIdx += 1; // the END line's own newline belongs to the block
106
+
107
+ const before = content.slice(0, beginIdx);
108
+ const after = content.slice(endIdx);
109
+ return before + block + after;
110
+ }
@@ -24,7 +24,15 @@ import {
24
24
  isEmptyContainer,
25
25
  } from './nodeUtils';
26
26
  import { NODE_TYPE } from './constants';
27
- import type { ComponentNode, ComponentInstanceNode, HtmlNode, SlotMarker, EmbedNode, LinkNode, LocaleListNode } from './types';
27
+ import type {
28
+ ComponentNode,
29
+ ComponentInstanceNode,
30
+ HtmlNode,
31
+ SlotMarker,
32
+ EmbedNode,
33
+ LinkNode,
34
+ LocaleListNode,
35
+ } from './types';
28
36
  import { registerBuiltInNodeTypes } from './registry';
29
37
 
30
38
  describe('nodeUtils', () => {
@@ -126,11 +134,11 @@ describe('nodeUtils', () => {
126
134
  const node: ComponentInstanceNode = {
127
135
  type: NODE_TYPE.COMPONENT,
128
136
  component: 'Button',
129
- props: { text: 'Click me' }
137
+ props: { text: 'Click me' },
130
138
  };
131
139
  expect(extractNodeProperties(node)).toEqual({
132
140
  componentName: 'Button',
133
- props: { text: 'Click me' }
141
+ props: { text: 'Click me' },
134
142
  });
135
143
  });
136
144
 
@@ -138,7 +146,7 @@ describe('nodeUtils', () => {
138
146
  const node: HtmlNode = { type: NODE_TYPE.NODE, tag: 'div' };
139
147
  expect(extractNodeProperties(node)).toEqual({
140
148
  tag: 'div',
141
- props: {}
149
+ props: {},
142
150
  });
143
151
  });
144
152
 
@@ -328,7 +336,7 @@ describe('nodeUtils', () => {
328
336
  const mapping = {
329
337
  _mapping: true,
330
338
  prop: 'visible',
331
- values: { 'true': true, 'false': false }
339
+ values: { true: true, false: false },
332
340
  };
333
341
  expect(isBooleanMapping(mapping)).toBe(true);
334
342
  });
@@ -368,8 +376,8 @@ describe('nodeUtils', () => {
368
376
  if: {
369
377
  _mapping: true,
370
378
  prop: 'showBanner',
371
- values: { 'true': true, 'false': false }
372
- }
379
+ values: { true: true, false: false },
380
+ },
373
381
  } as any;
374
382
 
375
383
  expect(evaluateNodeIf(node, { showBanner: 'true' })).toBe(true);
@@ -383,8 +391,8 @@ describe('nodeUtils', () => {
383
391
  if: {
384
392
  _mapping: true,
385
393
  prop: 'status',
386
- values: { 'active': true, 'inactive': false }
387
- }
394
+ values: { active: true, inactive: false },
395
+ },
388
396
  } as any;
389
397
 
390
398
  // 'unknown' is not in the values map
@@ -398,8 +406,8 @@ describe('nodeUtils', () => {
398
406
  if: {
399
407
  _mapping: true,
400
408
  prop: 'visible',
401
- values: { 'true': true, 'false': false }
402
- }
409
+ values: { true: true, false: false },
410
+ },
403
411
  } as any;
404
412
 
405
413
  expect(evaluateNodeIf(node)).toBe(true);
@@ -409,7 +417,7 @@ describe('nodeUtils', () => {
409
417
  const node = {
410
418
  type: NODE_TYPE.NODE,
411
419
  tag: 'div',
412
- if: '{{showBanner}}'
420
+ if: '{{showBanner}}',
413
421
  } as any;
414
422
 
415
423
  // String templates can't be resolved without context, default to true
@@ -420,7 +428,7 @@ describe('nodeUtils', () => {
420
428
  const node: ComponentInstanceNode = {
421
429
  type: NODE_TYPE.COMPONENT,
422
430
  component: 'Banner',
423
- if: false
431
+ if: false,
424
432
  } as any;
425
433
  expect(evaluateNodeIf(node)).toBe(false);
426
434
  });
@@ -429,7 +437,7 @@ describe('nodeUtils', () => {
429
437
  const node: EmbedNode = {
430
438
  type: NODE_TYPE.EMBED,
431
439
  html: '<svg></svg>',
432
- if: false
440
+ if: false,
433
441
  } as any;
434
442
  expect(evaluateNodeIf(node)).toBe(false);
435
443
  });
@@ -438,7 +446,7 @@ describe('nodeUtils', () => {
438
446
  const node: LinkNode = {
439
447
  type: NODE_TYPE.LINK,
440
448
  href: '/page',
441
- if: true
449
+ if: true,
442
450
  } as any;
443
451
  expect(evaluateNodeIf(node)).toBe(true);
444
452
  });
@@ -515,7 +523,7 @@ describe('nodeUtils', () => {
515
523
  const node: HtmlNode = {
516
524
  type: NODE_TYPE.NODE,
517
525
  tag: 'div',
518
- children: [{ type: NODE_TYPE.NODE, tag: 'span' }]
526
+ children: [{ type: NODE_TYPE.NODE, tag: 'span' }],
519
527
  };
520
528
  expect(isEmptyContainer(node)).toBe(false);
521
529
  });
@@ -3,7 +3,17 @@
3
3
  * Provides helper functions for working with ComponentNode types
4
4
  */
5
5
 
6
- import type { ComponentNode, ComponentInstanceNode, HtmlNode, SlotMarker, EmbedNode, LinkNode, LocaleListNode, ComponentDefinition, StructuredComponentDefinition } from './types';
6
+ import type {
7
+ ComponentNode,
8
+ ComponentInstanceNode,
9
+ HtmlNode,
10
+ SlotMarker,
11
+ EmbedNode,
12
+ LinkNode,
13
+ LocaleListNode,
14
+ ComponentDefinition,
15
+ StructuredComponentDefinition,
16
+ } from './types';
7
17
  import type { ListNode } from './registry/nodeTypes/ListNodeType';
8
18
 
9
19
  // For backward compatibility during migration
@@ -15,8 +25,20 @@ import { NODE_TYPE } from './constants';
15
25
  * These are self-closing tags like <img />, <br />, <input />, etc.
16
26
  */
17
27
  export const VOID_ELEMENTS = new Set([
18
- 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
19
- 'link', 'meta', 'param', 'source', 'track', 'wbr'
28
+ 'area',
29
+ 'base',
30
+ 'br',
31
+ 'col',
32
+ 'embed',
33
+ 'hr',
34
+ 'img',
35
+ 'input',
36
+ 'link',
37
+ 'meta',
38
+ 'param',
39
+ 'source',
40
+ 'track',
41
+ 'wbr',
20
42
  ]);
21
43
 
22
44
  /**
@@ -112,13 +134,13 @@ export function extractNodeProperties(node: ComponentNode): {
112
134
  if (isComponentNode(node)) {
113
135
  return {
114
136
  componentName: node.component,
115
- props: node.props || {}
137
+ props: node.props || {},
116
138
  };
117
139
  }
118
140
  if (isHtmlNode(node)) {
119
141
  return {
120
142
  tag: node.tag,
121
- props: {}
143
+ props: {},
122
144
  };
123
145
  }
124
146
  return { props: {} };
@@ -129,7 +151,7 @@ export function extractNodeProperties(node: ComponentNode): {
129
151
  * This is a heuristic based on naming conventions:
130
152
  * - Components typically start with uppercase (e.g., "Card", "Button")
131
153
  * - HTML elements are lowercase (e.g., "div", "span")
132
- *
154
+ *
133
155
  * Note: This is not foolproof, but works for most cases.
134
156
  * For accurate detection, use component registry lookup.
135
157
  */
@@ -220,10 +242,17 @@ export function isListNode(node: unknown): node is ListNode {
220
242
  /**
221
243
  * Validate that a node type is valid
222
244
  */
223
- export function isValidNodeType(type: string): type is typeof NODE_TYPE[keyof typeof NODE_TYPE] {
224
- return type === NODE_TYPE.NODE || type === NODE_TYPE.COMPONENT || type === NODE_TYPE.SLOT ||
225
- type === NODE_TYPE.EMBED || type === NODE_TYPE.LINK || type === NODE_TYPE.LOCALE_LIST ||
226
- type === NODE_TYPE.LIST || type === 'cms-list'; // 'cms-list' supported for migration
245
+ export function isValidNodeType(type: string): type is (typeof NODE_TYPE)[keyof typeof NODE_TYPE] {
246
+ return (
247
+ type === NODE_TYPE.NODE ||
248
+ type === NODE_TYPE.COMPONENT ||
249
+ type === NODE_TYPE.SLOT ||
250
+ type === NODE_TYPE.EMBED ||
251
+ type === NODE_TYPE.LINK ||
252
+ type === NODE_TYPE.LOCALE_LIST ||
253
+ type === NODE_TYPE.LIST ||
254
+ type === 'cms-list'
255
+ ); // 'cms-list' supported for migration
227
256
  }
228
257
 
229
258
  /**
@@ -258,7 +287,12 @@ export function hasIf(node: ComponentNode): node is ComponentNode & { if: IfCond
258
287
  * Check if a node is marked as slot content
259
288
  */
260
289
  export function isSlotContent(node: unknown): boolean {
261
- return node != null && typeof node === 'object' && '_isSlotContent' in node && (node as Record<string, unknown>)._isSlotContent === true;
290
+ return (
291
+ node != null &&
292
+ typeof node === 'object' &&
293
+ '_isSlotContent' in node &&
294
+ (node as Record<string, unknown>)._isSlotContent === true
295
+ );
262
296
  }
263
297
 
264
298
  /**
@@ -389,10 +423,7 @@ export function isBooleanMapping(value: unknown): value is BooleanMapping {
389
423
  * { visible: 'true' }
390
424
  * ) // true
391
425
  */
392
- export function evaluateNodeIf(
393
- node: ComponentNode,
394
- props?: Record<string, unknown>
395
- ): boolean {
426
+ export function evaluateNodeIf(node: ComponentNode, props?: Record<string, unknown>): boolean {
396
427
  const ifValue = hasIf(node) ? node.if : undefined;
397
428
 
398
429
  // No if property = render (default true)
@@ -441,7 +472,7 @@ export function evaluateNodeIf(
441
472
  * @returns The same node with _isSlotContent: true added to all object nodes
442
473
  */
443
474
  export function markAsSlotContent<T extends ComponentNode | ComponentNode[] | string | number | null | undefined>(
444
- node: T
475
+ node: T,
445
476
  ): T {
446
477
  if (!node || typeof node !== 'object') return node;
447
478
 
@@ -455,8 +486,8 @@ export function markAsSlotContent<T extends ComponentNode | ComponentNode[] | st
455
486
  // Recursively mark children
456
487
  if ('children' in node && node.children) {
457
488
  if (Array.isArray(node.children)) {
458
- node.children = node.children.map(child =>
459
- typeof child === 'object' && child !== null ? markAsSlotContent(child) : child
489
+ node.children = node.children.map((child) =>
490
+ typeof child === 'object' && child !== null ? markAsSlotContent(child) : child,
460
491
  ) as typeof node.children;
461
492
  } else if (typeof node.children === 'object') {
462
493
  markAsSlotContent(node.children as ComponentNode);
@@ -465,4 +496,3 @@ export function markAsSlotContent<T extends ComponentNode | ComponentNode[] | st
465
496
 
466
497
  return node;
467
498
  }
468
-
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { describe, it, expect } from 'bun:test';
7
7
  import {
8
- Path,
8
+ type Path,
9
9
  ROOT_STRING,
10
10
  ROOT_0_STRING,
11
11
  CHILDREN_SEPARATOR,
@@ -312,4 +312,3 @@ describe('Path Array Utilities - Phase 1 Foundation', () => {
312
312
  });
313
313
  });
314
314
  });
315
-
@@ -2,7 +2,7 @@
2
2
  * Array-based path utilities
3
3
  * Paths are represented as number arrays internally: [0,0,1] instead of "root_0_children_0_children_1"
4
4
  * Root is represented as [0] (or empty array [] depending on context)
5
- *
5
+ *
6
6
  * This file maintains backward compatibility while using the new modular path system.
7
7
  * All path utilities are re-exported from the organized path modules.
8
8
  */
@@ -11,7 +11,7 @@ import { resolve, sep } from 'path';
11
11
  export class PathTraversalError extends Error {
12
12
  constructor(
13
13
  public readonly requestedPath: string,
14
- public readonly rootPath: string
14
+ public readonly rootPath: string,
15
15
  ) {
16
16
  super(`Path traversal detected: "${requestedPath}" escapes root "${rootPath}"`);
17
17
  this.name = 'PathTraversalError';
@@ -94,7 +94,9 @@ describe('pathUtils', () => {
94
94
  });
95
95
 
96
96
  test('should handle deeply nested paths', () => {
97
- expect(domPathToTreePath('root_children_0_children_1_children_2')).toBe('root_0_children_0_children_1_children_2');
97
+ expect(domPathToTreePath('root_children_0_children_1_children_2')).toBe(
98
+ 'root_0_children_0_children_1_children_2',
99
+ );
98
100
  });
99
101
  });
100
102
 
@@ -115,11 +117,7 @@ describe('pathUtils', () => {
115
117
  });
116
118
 
117
119
  test('should handle deeply nested paths', () => {
118
- expect(buildParentPaths([0, 0, 1, 2])).toEqual([
119
- 'root_0',
120
- 'root_0_children_0',
121
- 'root_0_children_0_children_1',
122
- ]);
120
+ expect(buildParentPaths([0, 0, 1, 2])).toEqual(['root_0', 'root_0_children_0', 'root_0_children_0_children_1']);
123
121
  });
124
122
  });
125
123
 
@@ -4,9 +4,9 @@
4
4
  */
5
5
 
6
6
  import type { Path } from './paths';
7
- import {
8
- stringToPath,
9
- pathToLegacyString,
7
+ import {
8
+ stringToPath,
9
+ pathToLegacyString,
10
10
  getParentPath as getParentPathArray,
11
11
  buildParentPaths as buildParentPathsArray,
12
12
  pathsEqual,
@@ -14,59 +14,59 @@ import {
14
14
  ROOT_STRING,
15
15
  domPathStringToTreePath,
16
16
  treePathToDomPathString,
17
- isAncestorPath
17
+ isAncestorPath,
18
18
  } from './paths';
19
19
 
20
20
  /**
21
21
  * Convert a page path to static HTML file path
22
- *
22
+ *
23
23
  * @example
24
24
  * getStaticFilePath("/") -> "./dist/index.html"
25
25
  * getStaticFilePath("/about") -> "./dist/about.html"
26
26
  * getStaticFilePath("/blog/post-1") -> "./dist/blog-post-1.html"
27
27
  */
28
- export function getStaticFilePath(pagePath: string, distDir: string = "./dist"): string {
29
- if (pagePath === "/") {
28
+ export function getStaticFilePath(pagePath: string, distDir: string = './dist'): string {
29
+ if (pagePath === '/') {
30
30
  return `${distDir}/index.html`;
31
31
  }
32
-
32
+
33
33
  // Convert /about -> about.html
34
34
  // Convert /blog/post-1 -> blog-post-1.html (flat structure)
35
- const pathParts = pagePath.substring(1).split("/");
36
- const fileName = pathParts.join("-") + ".html";
37
-
35
+ const pathParts = pagePath.substring(1).split('/');
36
+ const fileName = pathParts.join('-') + '.html';
37
+
38
38
  return `${distDir}/${fileName}`;
39
39
  }
40
40
 
41
41
  /**
42
42
  * Convert a page path to a page name (for file system)
43
- *
43
+ *
44
44
  * @example
45
45
  * pathToPageName("/") -> "index"
46
46
  * pathToPageName("/about") -> "about"
47
47
  */
48
48
  export function pathToPageName(pagePath: string): string {
49
- return pagePath === "/" ? "index" : pagePath.substring(1);
49
+ return pagePath === '/' ? 'index' : pagePath.substring(1);
50
50
  }
51
51
 
52
52
  /**
53
53
  * Convert a page name to a page path
54
- *
54
+ *
55
55
  * @example
56
56
  * pageNameToPath("index") -> "/"
57
57
  * pageNameToPath("about") -> "/about"
58
58
  */
59
59
  export function pageNameToPath(pageName: string): string {
60
- return pageName === "index" ? "/" : `/${pageName}`;
60
+ return pageName === 'index' ? '/' : `/${pageName}`;
61
61
  }
62
62
 
63
63
  /**
64
64
  * Convert tree path to DOM path
65
65
  * The tree adds a synthetic "root" container, so "root_0" in the tree corresponds to "root" in the DOM
66
66
  * Also accepts array paths: [0] -> "root", [0,0] -> "root_children_0"
67
- *
67
+ *
68
68
  * Uses array operations internally for conversion.
69
- *
69
+ *
70
70
  * @example
71
71
  * treePathToDomPath("root_0") -> "root"
72
72
  * treePathToDomPath("root_0_children_0") -> "root_children_0"
@@ -83,9 +83,9 @@ export function treePathToDomPath(treePath: Path | string): string {
83
83
  * Convert DOM path back to tree path
84
84
  * The tree adds a synthetic "root" container, so "root" in the DOM corresponds to "root_0" in the tree
85
85
  * Returns legacy string format for backward compatibility
86
- *
86
+ *
87
87
  * Uses array operations internally for conversion.
88
- *
88
+ *
89
89
  * @example
90
90
  * domPathToTreePath("root") -> "root_0"
91
91
  * domPathToTreePath("root_children_0") -> "root_0_children_0"
@@ -100,18 +100,18 @@ export function domPathToTreePath(domPath: string): string {
100
100
  * Build parent paths for expanding tree nodes
101
101
  * Returns all parent path segments for a given tree path
102
102
  * Also accepts array paths
103
- *
103
+ *
104
104
  * @example
105
105
  * buildParentPaths("root_0_children_0_children_1") -> ["root_0", "root_0_children_0"]
106
106
  * buildParentPaths([0,0,1]) -> ["root_0", "root_0_children_0"]
107
107
  */
108
108
  export function buildParentPaths(treePath: Path | string): string[] {
109
109
  const pathArray = typeof treePath === 'string' ? stringToPath(treePath) : treePath;
110
-
110
+
111
111
  if (isRootPathArray(pathArray)) {
112
112
  return [];
113
113
  }
114
-
114
+
115
115
  const parentArrays = buildParentPathsArray(pathArray);
116
116
  return parentArrays.map(pathToLegacyString);
117
117
  }
@@ -134,43 +134,40 @@ export function getParentPathFromChildPath(childPath: Path | string): string {
134
134
  * Convert component instance path to component structure path
135
135
  * When editing a component, elements clicked inside the component instance on the page
136
136
  * need to be converted to their paths within the component structure.
137
- *
137
+ *
138
138
  * Uses array operations internally for path manipulation.
139
- *
139
+ *
140
140
  * @example
141
141
  * componentInstancePath: "root_children_0" (component instance on page)
142
142
  * instancePath: "root_children_0_children_1" (element inside instance)
143
143
  * Returns: "root_children_1" (component structure path)
144
- *
144
+ *
145
145
  * @param instancePath - Path of element within component instance on page (DOM path string)
146
146
  * @param componentInstancePath - Path of the component instance root on the page (DOM path string)
147
147
  * @returns Component structure path (always starts with "root")
148
148
  */
149
- export function componentInstancePathToStructurePath(
150
- instancePath: string,
151
- componentInstancePath: string
152
- ): string {
149
+ export function componentInstancePathToStructurePath(instancePath: string, componentInstancePath: string): string {
153
150
  // Convert DOM paths to arrays for manipulation
154
151
  const instancePathArray = domPathStringToTreePath(instancePath);
155
152
  const componentInstancePathArray = domPathStringToTreePath(componentInstancePath);
156
-
153
+
157
154
  // Check if instancePath is inside componentInstancePath using array operations
158
155
  if (!isAncestorPath(componentInstancePathArray, instancePathArray)) {
159
156
  // Not inside component instance - return as-is (shouldn't happen, but handle gracefully)
160
157
  return instancePath;
161
158
  }
162
-
159
+
163
160
  // Extract relative path using array operations
164
161
  // componentInstancePathArray: [0, 0] -> instancePathArray: [0, 0, 1]
165
162
  // relative: [1] -> convert to DOM path: "root_children_1"
166
163
  const relativePathArray = instancePathArray.slice(componentInstancePathArray.length);
167
-
164
+
168
165
  // Convert relative path array back to DOM path string
169
166
  // If relative is empty, return root
170
167
  if (relativePathArray.length === 0) {
171
168
  return ROOT_STRING;
172
169
  }
173
-
170
+
174
171
  // Build component structure path: root + relative path
175
172
  return treePathToDomPathString(relativePathArray);
176
173
  }
@@ -179,34 +176,31 @@ export function componentInstancePathToStructurePath(
179
176
  * Convert component structure path to component instance path
180
177
  * When selecting an element in the component structure tree, convert it to
181
178
  * the path within the component instance on the page.
182
- *
179
+ *
183
180
  * Uses array operations internally for path manipulation.
184
- *
181
+ *
185
182
  * @example
186
183
  * componentInstancePath: "root_children_0" (component instance on page)
187
184
  * structurePath: "root_children_1" (element in component structure)
188
185
  * Returns: "root_children_0_children_1" (path within instance on page)
189
- *
186
+ *
190
187
  * @param structurePath - Path within component structure (starts with "root", DOM path string)
191
188
  * @param componentInstancePath - Path of the component instance root on the page (DOM path string)
192
189
  * @returns Path within component instance on page (DOM path string)
193
190
  */
194
- export function componentStructurePathToInstancePath(
195
- structurePath: string,
196
- componentInstancePath: string
197
- ): string {
191
+ export function componentStructurePathToInstancePath(structurePath: string, componentInstancePath: string): string {
198
192
  // Convert DOM paths to arrays for manipulation
199
193
  const structurePathArray = domPathStringToTreePath(structurePath);
200
194
  const componentInstancePathArray = domPathStringToTreePath(componentInstancePath);
201
-
195
+
202
196
  // Extract relative path from structure path (skip root [0])
203
197
  // structurePathArray: [0, 1] -> relative: [1]
204
198
  const relativePathArray = structurePathArray.slice(1);
205
-
199
+
206
200
  // Combine component instance path with relative path using array operations
207
201
  // componentInstancePathArray: [0, 0] + relativePathArray: [1] -> [0, 0, 1]
208
202
  const instancePathArray: Path = [...componentInstancePathArray, ...relativePathArray];
209
-
203
+
210
204
  // Convert back to DOM path string
211
205
  return treePathToDomPathString(instancePathArray);
212
206
  }
@@ -214,9 +208,9 @@ export function componentStructurePathToInstancePath(
214
208
  /**
215
209
  * Convert path for component editing context
216
210
  * When editing a component, converts component instance paths to component structure paths
217
- *
211
+ *
218
212
  * Uses array operations internally for path manipulation.
219
- *
213
+ *
220
214
  * @param path - Path array from canvas
221
215
  * @param isEditingComponent - Whether we're editing a component
222
216
  * @param componentInstancePath - Path of component instance on page (if editing component, Path array)
@@ -225,7 +219,7 @@ export function componentStructurePathToInstancePath(
225
219
  export function convertPathForComponentContext(
226
220
  path: Path,
227
221
  isEditingComponent: boolean,
228
- componentInstancePath: Path | null
222
+ componentInstancePath: Path | null,
229
223
  ): Path {
230
224
  if (isEditingComponent && componentInstancePath) {
231
225
  // Check if path is inside componentInstancePath using array operations
@@ -233,12 +227,12 @@ export function convertPathForComponentContext(
233
227
  // Extract relative path: componentInstancePath: [0, 0] -> path: [0, 0, 1]
234
228
  // relative: [1] -> component structure path: [0, 1]
235
229
  const relativePath = path.slice(componentInstancePath.length);
236
-
230
+
237
231
  // Build component structure path: [0] + relative path
238
232
  return [0, ...relativePath];
239
233
  }
240
234
  }
241
-
235
+
242
236
  // Regular page editing - return path as-is
243
237
  return path;
244
238
  }