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,20 +3,24 @@
3
3
  * Utilities for loading and parsing JSON files
4
4
  */
5
5
 
6
- import { existsSync, readdirSync } from 'fs';
6
+ import { existsSync, readdirSync } from 'node:fs';
7
7
  import type { z } from 'zod';
8
8
  import type { ComponentDefinition } from '../shared/types';
9
9
  import type { BreakpointConfig, BreakpointConfigInput, BreakpointEntry } from '../shared/breakpoints';
10
10
  import { readTextFile, fileExists } from './runtime';
11
11
  import { DEFAULT_BREAKPOINTS, normalizeBreakpointConfig } from '../shared/breakpoints';
12
12
  import type { I18nConfig } from '../shared/types/components';
13
+ import { createLogger } from '../shared/logger';
13
14
  import { DEFAULT_I18N_CONFIG, migrateI18nConfig } from '../shared/i18n';
14
15
  import type { PrefetchConfig } from '../shared/types/prefetch';
15
16
  import { DEFAULT_PREFETCH_CONFIG } from '../shared/types/prefetch';
16
17
  import { validateComponentDefinition } from '../shared/validation/validators';
17
18
  import { attemptJsonRepair } from '../shared/jsonRepair';
19
+ import { logRuntimeError } from '../shared/errorLogger';
18
20
  import { projectPaths } from './projectContext';
19
21
 
22
+ const log = createLogger('jsonLoader');
23
+
20
24
  /**
21
25
  * Load JSON file from a file path
22
26
  */
@@ -27,6 +31,7 @@ export async function loadJSONFile(filePath: string): Promise<string | null> {
27
31
  }
28
32
  return null;
29
33
  } catch (error) {
34
+ logRuntimeError('jsonLoader.loadJSONFile', error, { filePath });
30
35
  return null;
31
36
  }
32
37
  }
@@ -42,10 +47,7 @@ export function parseJSON<T = unknown>(content: string): T {
42
47
  * Parse and validate JSON content against a zod schema
43
48
  * Returns null on validation failure (for backward compatibility)
44
49
  */
45
- export function parseAndValidateJSON<T>(
46
- content: string,
47
- schema: z.ZodType<T>
48
- ): T | null {
50
+ export function parseAndValidateJSON<T>(content: string, schema: z.ZodType<T>): T | null {
49
51
  try {
50
52
  const parsed = JSON.parse(content);
51
53
  const result = schema.safeParse(parsed);
@@ -55,6 +57,7 @@ export function parseAndValidateJSON<T>(
55
57
  // Return parsed value anyway for backward compatibility
56
58
  return parsed as T;
57
59
  } catch (error) {
60
+ logRuntimeError('jsonLoader.parseAndValidateJSON', error);
58
61
  return null;
59
62
  }
60
63
  }
@@ -65,10 +68,11 @@ export function parseAndValidateJSON<T>(
65
68
  export async function loadAndParseJSON<T = any>(filePath: string): Promise<T | null> {
66
69
  const content = await loadJSONFile(filePath);
67
70
  if (!content) return null;
68
-
71
+
69
72
  try {
70
73
  return parseJSON<T>(content);
71
74
  } catch (error) {
75
+ logRuntimeError('jsonLoader.loadAndParseJSON', error, { filePath });
72
76
  return null;
73
77
  }
74
78
  }
@@ -76,29 +80,27 @@ export async function loadAndParseJSON<T = any>(filePath: string): Promise<T | n
76
80
  /**
77
81
  * Load all JSON files from a directory
78
82
  */
79
- export async function loadJSONDirectory(
80
- dirPath: string
81
- ): Promise<Map<string, string>> {
83
+ export async function loadJSONDirectory(dirPath: string): Promise<Map<string, string>> {
82
84
  const files = new Map<string, string>();
83
-
85
+
84
86
  if (!existsSync(dirPath)) {
85
87
  return files;
86
88
  }
87
-
89
+
88
90
  const fileList = readdirSync(dirPath);
89
-
90
- const jsonFiles = fileList.filter(f => f.endsWith('.json'));
91
+
92
+ const jsonFiles = fileList.filter((f) => f.endsWith('.json'));
91
93
  const results = await Promise.all(
92
- jsonFiles.map(async file => {
94
+ jsonFiles.map(async (file) => {
93
95
  const name = file.replace('.json', '');
94
96
  const content = await loadJSONFile(`${dirPath}/${file}`);
95
97
  return content ? { name, content } : null;
96
- })
98
+ }),
97
99
  );
98
100
  for (const r of results) {
99
101
  if (r) files.set(r.name, r.content);
100
102
  }
101
-
103
+
102
104
  return files;
103
105
  }
104
106
 
@@ -112,6 +114,7 @@ async function loadFileAsText(filePath: string): Promise<string | null> {
112
114
  }
113
115
  return null;
114
116
  } catch (error) {
117
+ logRuntimeError('jsonLoader.loadFileAsText', error, { filePath });
115
118
  return null;
116
119
  }
117
120
  }
@@ -150,12 +153,12 @@ export interface ComponentDirectoryResult {
150
153
  async function loadSingleComponent(
151
154
  filePath: string,
152
155
  componentName: string,
153
- category?: string
156
+ category?: string,
154
157
  ): Promise<ComponentLoadResult> {
155
158
  const content = await loadJSONFile(filePath);
156
159
  if (!content) return { component: null };
157
160
 
158
- const fileName = `${componentName}.json`;
161
+ const _fileName = `${componentName}.json`;
159
162
  const repairResult = attemptJsonRepair(content);
160
163
 
161
164
  if (!repairResult.success) {
@@ -185,8 +188,10 @@ async function loadSingleComponent(
185
188
  // Validate component definition (logs warnings but doesn't fail - graceful degradation)
186
189
  const validationResult = validateComponentDefinition(parsed);
187
190
  if (!validationResult.valid) {
188
- console.warn(`[jsonLoader] Component validation failed for ${componentName}:`,
189
- validationResult.errors.map(e => `${e.path}: ${e.message}`).join('; '));
191
+ log.warn(
192
+ `Component validation failed for ${componentName}:`,
193
+ validationResult.errors.map((e) => `${e.path}: ${e.message}`).join('; '),
194
+ );
190
195
  }
191
196
  const componentDef: ComponentWithCategory = validationResult.valid ? validationResult.data : parsed;
192
197
 
@@ -196,10 +201,7 @@ async function loadSingleComponent(
196
201
  // Load .js and .css files in parallel
197
202
  const jsFilePath = `${dirPath}/${componentName}.js`;
198
203
  const cssFilePath = `${dirPath}/${componentName}.css`;
199
- const [jsContent, cssContent] = await Promise.all([
200
- loadFileAsText(jsFilePath),
201
- loadFileAsText(cssFilePath),
202
- ]);
204
+ const [jsContent, cssContent] = await Promise.all([loadFileAsText(jsFilePath), loadFileAsText(cssFilePath)]);
203
205
 
204
206
  if (!componentDef.component) {
205
207
  componentDef.component = {};
@@ -230,6 +232,7 @@ async function loadSingleComponent(
230
232
 
231
233
  return { component: componentDef, warning };
232
234
  } catch (error) {
235
+ logRuntimeError('jsonLoader.loadSingleComponent', error, { filePath, componentName });
233
236
  return { component: null };
234
237
  }
235
238
  }
@@ -245,9 +248,7 @@ async function loadSingleComponent(
245
248
  * @param dirPath - Root components directory path
246
249
  * @returns Map of component names to ComponentDefinition with category metadata
247
250
  */
248
- export async function loadComponentDirectory(
249
- dirPath: string = './components'
250
- ): Promise<ComponentDirectoryResult> {
251
+ export async function loadComponentDirectory(dirPath: string = './components'): Promise<ComponentDirectoryResult> {
251
252
  const components = new Map<string, ComponentWithCategory>();
252
253
  const warnings: ComponentLoadDiagnostic[] = [];
253
254
  const errors: ComponentLoadDiagnostic[] = [];
@@ -286,10 +287,10 @@ export async function loadComponentDirectory(
286
287
 
287
288
  // Load all components in parallel
288
289
  const results = await Promise.all(
289
- loadEntries.map(async e => ({
290
+ loadEntries.map(async (e) => ({
290
291
  ...e,
291
292
  result: await loadSingleComponent(e.filePath, e.componentName, e.category),
292
- }))
293
+ })),
293
294
  );
294
295
 
295
296
  // Insert into map preserving listing order, with duplicate detection
@@ -299,7 +300,7 @@ export async function loadComponentDirectory(
299
300
  if (!result.component) continue;
300
301
  if (components.has(componentName)) {
301
302
  const location = category ? `category "${category}"` : 'root level';
302
- console.warn(`[jsonLoader] Duplicate component name "${componentName}" found at ${location}. Skipping.`);
303
+ log.warn(`Duplicate component name "${componentName}" found at ${location}. Skipping.`);
303
304
  continue;
304
305
  }
305
306
  components.set(componentName, result.component);
@@ -346,9 +347,10 @@ export async function loadBreakpointConfig(): Promise<BreakpointConfig> {
346
347
  if (typeof entry.breakpoint === 'number' && entry.breakpoint > 0) {
347
348
  validInput[key] = {
348
349
  breakpoint: entry.breakpoint,
349
- previewPoint: typeof entry.previewPoint === 'number' && entry.previewPoint > 0
350
- ? entry.previewPoint
351
- : entry.breakpoint,
350
+ previewPoint:
351
+ typeof entry.previewPoint === 'number' && entry.previewPoint > 0
352
+ ? entry.previewPoint
353
+ : entry.breakpoint,
352
354
  };
353
355
  }
354
356
  }
@@ -361,6 +363,7 @@ export async function loadBreakpointConfig(): Promise<BreakpointConfig> {
361
363
  }
362
364
  }
363
365
  } catch (error) {
366
+ logRuntimeError('jsonLoader.loadBreakpointConfig', error, { filePath: projectPaths.config() });
364
367
  }
365
368
 
366
369
  return { ...DEFAULT_BREAKPOINTS };
@@ -396,6 +399,7 @@ export async function loadI18nConfig(): Promise<I18nConfig> {
396
399
  }
397
400
  } catch (error) {
398
401
  // Fall through to default
402
+ logRuntimeError('jsonLoader.loadI18nConfig', error, { filePath: projectPaths.config() });
399
403
  }
400
404
 
401
405
  return { ...DEFAULT_I18N_CONFIG };
@@ -441,6 +445,7 @@ export async function loadIconsConfig(): Promise<IconsConfig> {
441
445
  }
442
446
  } catch (error) {
443
447
  // Fall through to default
448
+ logRuntimeError('jsonLoader.loadIconsConfig', error, { filePath: projectPaths.config() });
444
449
  }
445
450
 
446
451
  return {};
@@ -465,8 +470,8 @@ export async function loadPrefetchConfig(): Promise<PrefetchConfig> {
465
470
  }
466
471
  } catch (error) {
467
472
  // Fall through to default
473
+ logRuntimeError('jsonLoader.loadPrefetchConfig', error, { filePath: projectPaths.config() });
468
474
  }
469
475
 
470
476
  return { ...DEFAULT_PREFETCH_CONFIG };
471
477
  }
472
-
@@ -1,5 +1,5 @@
1
1
  import { describe, test, expect } from 'bun:test';
2
- import { createCorsHeaders, handleCorsPreflight, type CorsOptions } from './cors';
2
+ import { createCorsHeaders, handleCorsPreflight } from './cors';
3
3
 
4
4
  describe('cors middleware', () => {
5
5
  describe('createCorsHeaders', () => {
@@ -17,14 +17,14 @@ describe('cors middleware', () => {
17
17
 
18
18
  test('handles array origin', () => {
19
19
  const headers = createCorsHeaders({
20
- origin: ['https://example.com', 'https://test.com']
20
+ origin: ['https://example.com', 'https://test.com'],
21
21
  });
22
22
  expect(headers['Access-Control-Allow-Origin']).toBe('https://example.com, https://test.com');
23
23
  });
24
24
 
25
25
  test('handles function origin', () => {
26
26
  const headers = createCorsHeaders({
27
- origin: (origin: string) => origin === 'https://example.com'
27
+ origin: (origin: string) => origin === 'https://example.com',
28
28
  });
29
29
  // Function origin falls back to '*' since we don't have request origin
30
30
  expect(headers['Access-Control-Allow-Origin']).toBe('*');
@@ -32,42 +32,42 @@ describe('cors middleware', () => {
32
32
 
33
33
  test('handles custom methods', () => {
34
34
  const headers = createCorsHeaders({
35
- methods: ['GET', 'POST']
35
+ methods: ['GET', 'POST'],
36
36
  });
37
37
  expect(headers['Access-Control-Allow-Methods']).toBe('GET, POST');
38
38
  });
39
39
 
40
40
  test('handles empty methods array', () => {
41
41
  const headers = createCorsHeaders({
42
- methods: []
42
+ methods: [],
43
43
  });
44
44
  expect(headers['Access-Control-Allow-Methods']).toBeUndefined();
45
45
  });
46
46
 
47
47
  test('handles custom allowed headers', () => {
48
48
  const headers = createCorsHeaders({
49
- allowedHeaders: ['X-Custom-Header', 'Content-Type']
49
+ allowedHeaders: ['X-Custom-Header', 'Content-Type'],
50
50
  });
51
51
  expect(headers['Access-Control-Allow-Headers']).toBe('X-Custom-Header, Content-Type');
52
52
  });
53
53
 
54
54
  test('handles empty allowed headers array', () => {
55
55
  const headers = createCorsHeaders({
56
- allowedHeaders: []
56
+ allowedHeaders: [],
57
57
  });
58
58
  expect(headers['Access-Control-Allow-Headers']).toBeUndefined();
59
59
  });
60
60
 
61
61
  test('sets credentials header when enabled', () => {
62
62
  const headers = createCorsHeaders({
63
- credentials: true
63
+ credentials: true,
64
64
  });
65
65
  expect(headers['Access-Control-Allow-Credentials']).toBe('true');
66
66
  });
67
67
 
68
68
  test('does not set credentials header when disabled', () => {
69
69
  const headers = createCorsHeaders({
70
- credentials: false
70
+ credentials: false,
71
71
  });
72
72
  expect(headers['Access-Control-Allow-Credentials']).toBeUndefined();
73
73
  });
@@ -77,7 +77,7 @@ describe('cors middleware', () => {
77
77
  origin: 'https://example.com',
78
78
  methods: ['GET', 'POST'],
79
79
  allowedHeaders: ['X-Token'],
80
- credentials: true
80
+ credentials: true,
81
81
  });
82
82
  expect(headers['Access-Control-Allow-Origin']).toBe('https://example.com');
83
83
  expect(headers['Access-Control-Allow-Methods']).toBe('GET, POST');
@@ -87,7 +87,7 @@ describe('cors middleware', () => {
87
87
 
88
88
  test('overrides defaults with provided options', () => {
89
89
  const headers = createCorsHeaders({
90
- methods: ['GET']
90
+ methods: ['GET'],
91
91
  });
92
92
  expect(headers['Access-Control-Allow-Methods']).toBe('GET');
93
93
  expect(headers['Access-Control-Allow-Origin']).toBe('*'); // Still defaults
@@ -97,7 +97,7 @@ describe('cors middleware', () => {
97
97
  describe('handleCorsPreflight', () => {
98
98
  test('returns null for non-OPTIONS request', () => {
99
99
  const req = new Request('http://localhost:3000/api/test', {
100
- method: 'GET'
100
+ method: 'GET',
101
101
  });
102
102
  const response = handleCorsPreflight(req);
103
103
  expect(response).toBeNull();
@@ -105,7 +105,7 @@ describe('cors middleware', () => {
105
105
 
106
106
  test('returns response for OPTIONS request', () => {
107
107
  const req = new Request('http://localhost:3000/api/test', {
108
- method: 'OPTIONS'
108
+ method: 'OPTIONS',
109
109
  });
110
110
  const response = handleCorsPreflight(req);
111
111
  expect(response).not.toBeNull();
@@ -114,7 +114,7 @@ describe('cors middleware', () => {
114
114
 
115
115
  test('OPTIONS response includes default CORS headers', () => {
116
116
  const req = new Request('http://localhost:3000/api/test', {
117
- method: 'OPTIONS'
117
+ method: 'OPTIONS',
118
118
  });
119
119
  const response = handleCorsPreflight(req);
120
120
  expect(response?.headers.get('Access-Control-Allow-Origin')).toBe('*');
@@ -123,11 +123,11 @@ describe('cors middleware', () => {
123
123
 
124
124
  test('OPTIONS response includes custom CORS headers', () => {
125
125
  const req = new Request('http://localhost:3000/api/test', {
126
- method: 'OPTIONS'
126
+ method: 'OPTIONS',
127
127
  });
128
128
  const response = handleCorsPreflight(req, {
129
129
  origin: 'https://example.com',
130
- methods: ['GET', 'POST']
130
+ methods: ['GET', 'POST'],
131
131
  });
132
132
  expect(response?.headers.get('Access-Control-Allow-Origin')).toBe('https://example.com');
133
133
  expect(response?.headers.get('Access-Control-Allow-Methods')).toBe('GET, POST');
@@ -135,7 +135,7 @@ describe('cors middleware', () => {
135
135
 
136
136
  test('OPTIONS response has no body', async () => {
137
137
  const req = new Request('http://localhost:3000/api/test', {
138
- method: 'OPTIONS'
138
+ method: 'OPTIONS',
139
139
  });
140
140
  const response = handleCorsPreflight(req);
141
141
  const body = await response?.text();
@@ -144,7 +144,7 @@ describe('cors middleware', () => {
144
144
 
145
145
  test('handles POST request', () => {
146
146
  const req = new Request('http://localhost:3000/api/test', {
147
- method: 'POST'
147
+ method: 'POST',
148
148
  });
149
149
  const response = handleCorsPreflight(req);
150
150
  expect(response).toBeNull();
@@ -152,7 +152,7 @@ describe('cors middleware', () => {
152
152
 
153
153
  test('handles PUT request', () => {
154
154
  const req = new Request('http://localhost:3000/api/test', {
155
- method: 'PUT'
155
+ method: 'PUT',
156
156
  });
157
157
  const response = handleCorsPreflight(req);
158
158
  expect(response).toBeNull();
@@ -160,7 +160,7 @@ describe('cors middleware', () => {
160
160
 
161
161
  test('handles DELETE request', () => {
162
162
  const req = new Request('http://localhost:3000/api/test', {
163
- method: 'DELETE'
163
+ method: 'DELETE',
164
164
  });
165
165
  const response = handleCorsPreflight(req);
166
166
  expect(response).toBeNull();
@@ -168,7 +168,7 @@ describe('cors middleware', () => {
168
168
 
169
169
  test('OPTIONS with credentials option', () => {
170
170
  const req = new Request('http://localhost:3000/api/test', {
171
- method: 'OPTIONS'
171
+ method: 'OPTIONS',
172
172
  });
173
173
  const response = handleCorsPreflight(req, { credentials: true });
174
174
  expect(response?.headers.get('Access-Control-Allow-Credentials')).toBe('true');
@@ -8,6 +8,14 @@ export interface CorsOptions {
8
8
  methods?: string[];
9
9
  allowedHeaders?: string[];
10
10
  credentials?: boolean;
11
+ /**
12
+ * The incoming request's `Origin` header. When provided alongside a function
13
+ * or array `origin`, the matching request origin is REFLECTED (and `Vary: Origin`
14
+ * set) so an allow-list can be enforced per-request; a non-matching origin gets
15
+ * no `Access-Control-Allow-Origin` header at all (browser blocks it). When omitted,
16
+ * function/array origins keep their legacy permissive/joined behavior.
17
+ */
18
+ requestOrigin?: string;
11
19
  }
12
20
 
13
21
  const DEFAULT_OPTIONS: CorsOptions = {
@@ -27,10 +35,27 @@ export function createCorsHeaders(options: CorsOptions = {}): Record<string, str
27
35
  // Handle origin
28
36
  if (opts.origin) {
29
37
  if (typeof opts.origin === 'function') {
30
- // Dynamic origin - would need request origin to evaluate
31
- headers['Access-Control-Allow-Origin'] = '*';
38
+ if (opts.requestOrigin !== undefined) {
39
+ // Allow-list mode: reflect the request Origin only when the predicate
40
+ // accepts it; otherwise emit no ACAO header so the browser blocks it.
41
+ if (opts.origin(opts.requestOrigin)) {
42
+ headers['Access-Control-Allow-Origin'] = opts.requestOrigin;
43
+ headers.Vary = 'Origin';
44
+ }
45
+ } else {
46
+ // No request Origin to evaluate against — legacy permissive fallback.
47
+ headers['Access-Control-Allow-Origin'] = '*';
48
+ }
32
49
  } else if (Array.isArray(opts.origin)) {
33
- headers['Access-Control-Allow-Origin'] = opts.origin.join(', ');
50
+ if (opts.requestOrigin !== undefined) {
51
+ // Allow-list mode: reflect the request Origin only when it's listed.
52
+ if (opts.origin.includes(opts.requestOrigin)) {
53
+ headers['Access-Control-Allow-Origin'] = opts.requestOrigin;
54
+ headers.Vary = 'Origin';
55
+ }
56
+ } else {
57
+ headers['Access-Control-Allow-Origin'] = opts.origin.join(', ');
58
+ }
34
59
  } else {
35
60
  headers['Access-Control-Allow-Origin'] = opts.origin;
36
61
  }
@@ -66,4 +91,3 @@ export function handleCorsPreflight(req: Request, options: CorsOptions = {}): Re
66
91
  }
67
92
  return null;
68
93
  }
69
-
@@ -109,7 +109,7 @@ describe('errorHandler middleware', () => {
109
109
 
110
110
  test('handles async handler with delay', async () => {
111
111
  const handler = async () => {
112
- await new Promise(resolve => setTimeout(resolve, 10));
112
+ await new Promise((resolve) => setTimeout(resolve, 10));
113
113
  return new Response('OK');
114
114
  };
115
115
  const response = await handleRouteError(handler);
@@ -156,7 +156,7 @@ describe('errorHandler middleware', () => {
156
156
  });
157
157
 
158
158
  test('wrapped handler passes arguments correctly', async () => {
159
- const handler = async (req: Request, id: string) => {
159
+ const handler = async (_req: Request, id: string) => {
160
160
  return new Response(`Hello ${id}`);
161
161
  };
162
162
  const wrapped = withErrorHandling(handler);
@@ -166,7 +166,7 @@ describe('errorHandler middleware', () => {
166
166
  });
167
167
 
168
168
  test('wrapped handler catches errors with arguments', async () => {
169
- const handler = async (req: Request, shouldFail: boolean) => {
169
+ const handler = async (_req: Request, shouldFail: boolean) => {
170
170
  if (shouldFail) throw new Error('Failed');
171
171
  return new Response('OK');
172
172
  };
@@ -183,7 +183,7 @@ describe('errorHandler middleware', () => {
183
183
  test('wrapped handler preserves response headers', async () => {
184
184
  const handler = async () => {
185
185
  return new Response('OK', {
186
- headers: { 'X-Custom': 'value' }
186
+ headers: { 'X-Custom': 'value' },
187
187
  });
188
188
  };
189
189
  const wrapped = withErrorHandling(handler);
@@ -193,7 +193,9 @@ describe('errorHandler middleware', () => {
193
193
 
194
194
  test('multiple wrapped handlers work independently', async () => {
195
195
  const handler1 = async () => new Response('One');
196
- const handler2 = async () => { throw new Error('Two'); };
196
+ const handler2 = async () => {
197
+ throw new Error('Two');
198
+ };
197
199
 
198
200
  const wrapped1 = withErrorHandling(handler1);
199
201
  const wrapped2 = withErrorHandling(handler2);
@@ -12,11 +12,7 @@ export interface ErrorResponse {
12
12
  /**
13
13
  * Create an error response
14
14
  */
15
- export function createErrorResponse(
16
- error: unknown,
17
- status: number = 500,
18
- message?: string
19
- ): Response {
15
+ export function createErrorResponse(error: unknown, status: number = 500, message?: string): Response {
20
16
  const errorMessage = error instanceof Error ? error.message : String(error);
21
17
  const response: ErrorResponse = {
22
18
  error: errorMessage,
@@ -40,7 +36,7 @@ export function createErrorResponse(
40
36
  */
41
37
  export async function handleRouteError(
42
38
  handler: () => Promise<Response>,
43
- errorMessage: string = 'Internal server error'
39
+ errorMessage: string = 'Internal server error',
44
40
  ): Promise<Response> {
45
41
  try {
46
42
  return await handler();
@@ -54,10 +50,9 @@ export async function handleRouteError(
54
50
  */
55
51
  export function withErrorHandling<T extends unknown[]>(
56
52
  handler: (...args: T) => Promise<Response>,
57
- errorMessage?: string
53
+ errorMessage?: string,
58
54
  ): (...args: T) => Promise<Response> {
59
55
  return async (...args: T) => {
60
56
  return handleRouteError(() => handler(...args), errorMessage);
61
57
  };
62
58
  }
63
-
@@ -6,4 +6,3 @@
6
6
  export * from './cors';
7
7
  export * from './errorHandler';
8
8
  export * from './logger';
9
-
@@ -1,4 +1,4 @@
1
- import { describe, test, expect, beforeEach, mock } from 'bun:test';
1
+ import { describe, test, expect } from 'bun:test';
2
2
  import { logRequest, logError, logResponseTime, withLogging, type LogOptions } from './logger';
3
3
 
4
4
  describe('logger middleware', () => {
@@ -8,7 +8,7 @@ describe('logger middleware', () => {
8
8
  describe('logRequest', () => {
9
9
  test('executes without error when enabled', () => {
10
10
  const req = new Request('http://localhost:3000/api/test', {
11
- method: 'GET'
11
+ method: 'GET',
12
12
  });
13
13
  expect(() => logRequest(req)).not.toThrow();
14
14
  });
@@ -144,7 +144,7 @@ describe('logger middleware', () => {
144
144
  });
145
145
 
146
146
  test('wrapped handler passes arguments correctly', async () => {
147
- const handler = async (req: Request, id: string) => {
147
+ const handler = async (_req: Request, id: string) => {
148
148
  return new Response(`ID: ${id}`);
149
149
  };
150
150
  const wrapped = withLogging(handler);
@@ -158,7 +158,7 @@ describe('logger middleware', () => {
158
158
  const options: LogOptions = {
159
159
  logRequests: false,
160
160
  logErrors: false,
161
- logResponseTime: false
161
+ logResponseTime: false,
162
162
  };
163
163
  const wrapped = withLogging(handler, options);
164
164
  const req = new Request('http://localhost:3000/api/test');
@@ -169,7 +169,7 @@ describe('logger middleware', () => {
169
169
  test('wrapped handler preserves response headers', async () => {
170
170
  const handler = async (_req: Request) => {
171
171
  return new Response('OK', {
172
- headers: { 'X-Custom': 'value' }
172
+ headers: { 'X-Custom': 'value' },
173
173
  });
174
174
  };
175
175
  const wrapped = withLogging(handler);
@@ -179,7 +179,7 @@ describe('logger middleware', () => {
179
179
  });
180
180
 
181
181
  test('wrapped handler with multiple arguments', async () => {
182
- const handler = async (req: Request, a: number, b: number) => {
182
+ const handler = async (_req: Request, a: number, b: number) => {
183
183
  return new Response(`Sum: ${a + b}`);
184
184
  };
185
185
  const wrapped = withLogging(handler);
@@ -190,7 +190,9 @@ describe('logger middleware', () => {
190
190
 
191
191
  test('wrapped handler logs on success and error', async () => {
192
192
  const successHandler = async (_req: Request) => new Response('OK');
193
- const errorHandler = async (_req: Request): Promise<Response> => { throw new Error('Failed'); };
193
+ const errorHandler = async (_req: Request): Promise<Response> => {
194
+ throw new Error('Failed');
195
+ };
194
196
 
195
197
  const wrappedSuccess = withLogging(successHandler);
196
198
  const wrappedError = withLogging(errorHandler);
@@ -205,7 +207,7 @@ describe('logger middleware', () => {
205
207
 
206
208
  test('wrapped handler handles async operations', async () => {
207
209
  const handler = async (_req: Request) => {
208
- await new Promise(resolve => setTimeout(resolve, 10));
210
+ await new Promise((resolve) => setTimeout(resolve, 10));
209
211
  return new Response('Delayed OK');
210
212
  };
211
213
  const wrapped = withLogging(handler);