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.
- package/.claude/settings.local.json +1 -3
- package/bin/cli.ts +66 -261
- package/build-astro.ts +150 -128
- package/dist/bin/cli.js +42 -185
- package/dist/bin/cli.js.map +2 -2
- package/dist/chunks/chunk-2AR55GYH.js +42 -0
- package/dist/chunks/chunk-2AR55GYH.js.map +7 -0
- package/dist/chunks/{chunk-JGWFTO6P.js → chunk-2IIQK7T3.js} +2624 -1389
- package/dist/chunks/chunk-2IIQK7T3.js.map +7 -0
- package/dist/chunks/chunk-4ZRU52J2.js +169 -0
- package/dist/chunks/chunk-4ZRU52J2.js.map +7 -0
- package/dist/chunks/{chunk-UB44F4Z2.js → chunk-5X4HCN7J.js} +5 -7
- package/dist/chunks/chunk-5X4HCN7J.js.map +7 -0
- package/dist/chunks/{chunk-WQFG7PAH.js → chunk-7CCVOL5M.js} +15 -19
- package/dist/chunks/chunk-7CCVOL5M.js.map +7 -0
- package/dist/chunks/{chunk-YBLHKYFF.js → chunk-AR7LHIFY.js} +8 -2
- package/dist/chunks/chunk-AR7LHIFY.js.map +7 -0
- package/dist/chunks/{chunk-O3NAGJP4.js → chunk-NUP7H7D3.js} +135 -41
- package/dist/chunks/chunk-NUP7H7D3.js.map +7 -0
- package/dist/chunks/{chunk-X754AHS5.js → chunk-QWTQZHG3.js} +194 -90
- package/dist/chunks/chunk-QWTQZHG3.js.map +7 -0
- package/dist/chunks/{chunk-H3GJ4H2U.js → chunk-XTKNX4FW.js} +216 -87
- package/dist/chunks/chunk-XTKNX4FW.js.map +7 -0
- package/dist/chunks/{fs-JGINUXGL.js → fs-G3ANWEFP.js} +2 -2
- package/dist/lib/client/index.js +1003 -475
- package/dist/lib/client/index.js.map +3 -3
- package/dist/lib/server/index.js +14393 -6371
- package/dist/lib/server/index.js.map +4 -4
- package/dist/lib/shared/index.js +342 -101
- package/dist/lib/shared/index.js.map +4 -4
- package/dist/lib/shared/richtext/index.js +1 -1
- package/dist/lib/test-utils/index.js +64 -80
- package/dist/lib/test-utils/index.js.map +2 -2
- package/entries/client-router.tsx +10 -8
- package/lib/client/ClientInitializer.ts +8 -8
- package/lib/client/ErrorBoundary.test.tsx +246 -186
- package/lib/client/ErrorBoundary.tsx +183 -132
- package/lib/client/componentRegistry.test.ts +96 -108
- package/lib/client/componentRegistry.ts +1 -3
- package/lib/client/contexts/ThemeContext.tsx +3 -2
- package/lib/client/core/ComponentBuilder.test.ts +663 -560
- package/lib/client/core/ComponentBuilder.ts +427 -155
- package/lib/client/core/ComponentRenderer.test.tsx +2 -4
- package/lib/client/core/ComponentRenderer.tsx +46 -33
- package/lib/client/core/builders/embedBuilder.ts +242 -54
- package/lib/client/core/builders/linkBuilder.ts +72 -45
- package/lib/client/core/builders/linkNodeBuilder.ts +79 -54
- package/lib/client/core/builders/listBuilder.ts +155 -89
- package/lib/client/core/builders/localeListBuilder.ts +97 -62
- package/lib/client/core/builders/types.ts +5 -5
- package/lib/client/core/cmsTemplateProcessor.ts +8 -9
- package/lib/client/elementRegistry.ts +3 -3
- package/lib/client/fontFamiliesService.test.ts +2 -10
- package/lib/client/fontFamiliesService.ts +3 -3
- package/lib/client/hmr/HMRManager.tsx +47 -30
- package/lib/client/hmrCssReload.ts +49 -14
- package/lib/client/hmrWebSocket.ts +14 -19
- package/lib/client/hooks/useColorVariables.test.ts +21 -21
- package/lib/client/hooks/useColorVariables.ts +23 -18
- package/lib/client/hooks/usePropertyAutocomplete.ts +4 -6
- package/lib/client/hooks/useVariables.ts +6 -4
- package/lib/client/hydration/HydrationUtils.test.ts +24 -25
- package/lib/client/hydration/HydrationUtils.ts +3 -4
- package/lib/client/i18nConfigService.test.ts +2 -7
- package/lib/client/i18nConfigService.ts +2 -2
- package/lib/client/meno-filter/MenoFilter.test.ts +47 -49
- package/lib/client/meno-filter/MenoFilter.ts +14 -15
- package/lib/client/meno-filter/bindings.ts +18 -43
- package/lib/client/meno-filter/init.ts +4 -4
- package/lib/client/meno-filter/renderer.ts +30 -32
- package/lib/client/meno-filter/script.generated.ts +1 -3
- package/lib/client/meno-filter/ui.ts +4 -6
- package/lib/client/meno-filter/updates.ts +18 -23
- package/lib/client/meno-filter/utils.ts +1 -1
- package/lib/client/navigation.test.ts +174 -178
- package/lib/client/navigation.ts +0 -1
- package/lib/client/responsiveStyleResolver.knownTokens.test.ts +71 -0
- package/lib/client/responsiveStyleResolver.test.ts +234 -232
- package/lib/client/responsiveStyleResolver.ts +74 -20
- package/lib/client/routing/RouteLoader.test.ts +24 -25
- package/lib/client/routing/RouteLoader.ts +27 -35
- package/lib/client/routing/Router.tsx +210 -153
- package/lib/client/scripts/ScriptExecutor.test.ts +157 -158
- package/lib/client/scripts/ScriptExecutor.ts +18 -36
- package/lib/client/services/PrefetchService.test.ts +3 -3
- package/lib/client/services/PrefetchService.ts +23 -29
- package/lib/client/styleProcessor.test.ts +9 -9
- package/lib/client/styleProcessor.ts +24 -15
- package/lib/client/styles/StyleInjector.test.ts +122 -115
- package/lib/client/styles/StyleInjector.ts +10 -7
- package/lib/client/styles/UtilityClassCollector.ts +62 -28
- package/lib/client/styles/cspNonce.test.ts +2 -5
- package/lib/client/templateEngine.test.ts +554 -488
- package/lib/client/templateEngine.ts +203 -73
- package/lib/client/theme.test.ts +1 -1
- package/lib/client/theme.ts +0 -1
- package/lib/client/utils/toast.ts +0 -1
- package/lib/server/__integration__/api-routes.test.ts +8 -4
- package/lib/server/__integration__/cms-integration.test.ts +1 -4
- package/lib/server/__integration__/server-lifecycle.test.ts +4 -5
- package/lib/server/__integration__/ssr-rendering.test.ts +47 -37
- package/lib/server/__integration__/static-assets.test.ts +3 -3
- package/lib/server/__integration__/test-helpers.ts +87 -74
- package/lib/server/ab/generateFunctions.ts +12 -10
- package/lib/server/astro/cmsPageEmitter.ts +26 -42
- package/lib/server/astro/componentEmitter.ts +35 -43
- package/lib/server/astro/cssCollector.ts +10 -26
- package/lib/server/astro/nodeToAstro.test.ts +1754 -34
- package/lib/server/astro/nodeToAstro.ts +232 -216
- package/lib/server/astro/normalizeOrphanTemplateProps.test.ts +115 -119
- package/lib/server/astro/normalizeOrphanTemplateProps.ts +11 -18
- package/lib/server/astro/pageEmitter.ts +9 -13
- package/lib/server/astro/tailwindMapper.test.ts +10 -37
- package/lib/server/astro/tailwindMapper.ts +33 -40
- package/lib/server/astro/templateTransformer.ts +17 -20
- package/lib/server/createServer.ts +7 -19
- package/lib/server/cssGenerator.test.ts +35 -44
- package/lib/server/cssGenerator.ts +8 -19
- package/lib/server/fileWatcher.test.ts +164 -13
- package/lib/server/fileWatcher.ts +209 -124
- package/lib/server/index.ts +14 -17
- package/lib/server/jsonLoader.test.ts +39 -2
- package/lib/server/jsonLoader.ts +39 -34
- package/lib/server/middleware/cors.test.ts +21 -21
- package/lib/server/middleware/cors.ts +28 -4
- package/lib/server/middleware/errorHandler.test.ts +7 -5
- package/lib/server/middleware/errorHandler.ts +3 -8
- package/lib/server/middleware/index.ts +0 -1
- package/lib/server/middleware/logger.test.ts +10 -8
- package/lib/server/middleware/logger.ts +17 -29
- package/lib/server/migrateTemplates.ts +3 -3
- package/lib/server/pageCache.test.ts +76 -77
- package/lib/server/pageCache.ts +0 -1
- package/lib/server/projectContext.ts +5 -4
- package/lib/server/providers/fileSystemCMSProvider.test.ts +142 -99
- package/lib/server/providers/fileSystemCMSProvider.ts +62 -38
- package/lib/server/routes/api/cms.test.ts +26 -14
- package/lib/server/routes/api/cms.ts +9 -14
- package/lib/server/routes/api/colors.test.ts +2 -2
- package/lib/server/routes/api/components.ts +31 -35
- package/lib/server/routes/api/config.ts +3 -4
- package/lib/server/routes/api/core-routes.ts +49 -76
- package/lib/server/routes/api/enums.test.ts +2 -2
- package/lib/server/routes/api/functions.ts +9 -17
- package/lib/server/routes/api/index.ts +3 -6
- package/lib/server/routes/api/pages.ts +8 -11
- package/lib/server/routes/api/shared.test.ts +1 -1
- package/lib/server/routes/api/shared.ts +12 -9
- package/lib/server/routes/api/variables.test.ts +3 -5
- package/lib/server/routes/api/variables.ts +1 -1
- package/lib/server/routes/index.ts +25 -28
- package/lib/server/routes/pages.ts +35 -31
- package/lib/server/routes/static.ts +17 -6
- package/lib/server/runtime/bundler.ts +98 -61
- package/lib/server/runtime/fs.ts +16 -26
- package/lib/server/runtime/httpServer.ts +8 -12
- package/lib/server/services/ColorService.test.ts +3 -3
- package/lib/server/services/ColorService.ts +37 -29
- package/lib/server/services/EnumService.test.ts +4 -8
- package/lib/server/services/EnumService.ts +11 -3
- package/lib/server/services/VariableService.test.ts +8 -12
- package/lib/server/services/VariableService.ts +11 -3
- package/lib/server/services/cmsService.test.ts +158 -120
- package/lib/server/services/cmsService.ts +36 -146
- package/lib/server/services/componentService.test.ts +125 -45
- package/lib/server/services/componentService.ts +70 -46
- package/lib/server/services/configService.test.ts +22 -45
- package/lib/server/services/configService.ts +50 -27
- package/lib/server/services/fileWatcherService.ts +42 -8
- package/lib/server/services/index.ts +0 -1
- package/lib/server/services/pageService.test.ts +8 -10
- package/lib/server/services/pageService.ts +32 -18
- package/lib/server/ssr/attributeBuilder.ts +18 -8
- package/lib/server/ssr/buildErrorOverlay.ts +11 -12
- package/lib/server/ssr/clientDataInjector.ts +7 -21
- package/lib/server/ssr/cmsSSRProcessor.ts +4 -8
- package/lib/server/ssr/cssCollector.ts +1 -1
- package/lib/server/ssr/errorOverlay.test.ts +1 -1
- package/lib/server/ssr/errorOverlay.ts +3 -9
- package/lib/server/ssr/htmlGenerator.nonce.test.ts +9 -14
- package/lib/server/ssr/htmlGenerator.test.ts +120 -44
- package/lib/server/ssr/htmlGenerator.ts +126 -91
- package/lib/server/ssr/imageMetadata.test.ts +3 -1
- package/lib/server/ssr/imageMetadata.ts +27 -21
- package/lib/server/ssr/jsCollector.test.ts +8 -18
- package/lib/server/ssr/jsCollector.ts +3 -8
- package/lib/server/ssr/liveReloadIntegration.test.ts +76 -36
- package/lib/server/ssr/metaTagGenerator.ts +2 -2
- package/lib/server/ssr/ssrRenderer.branches.test.ts +1103 -0
- package/lib/server/ssr/ssrRenderer.test.ts +316 -256
- package/lib/server/ssr/ssrRenderer.ts +795 -243
- package/lib/server/ssrRenderer.test.ts +1048 -954
- package/lib/server/utils/jsonLineMapper.test.ts +28 -28
- package/lib/server/utils/jsonLineMapper.ts +7 -7
- package/lib/server/validateStyleCoverage.ts +18 -21
- package/lib/server/websocketManager.ts +28 -19
- package/lib/shared/attributeNodeUtils.test.ts +15 -15
- package/lib/shared/attributeNodeUtils.ts +5 -12
- package/lib/shared/breakpoints.test.ts +5 -5
- package/lib/shared/breakpoints.ts +4 -11
- package/lib/shared/cmsQuery.test.ts +81 -0
- package/lib/shared/cmsQuery.ts +92 -0
- package/lib/shared/cmsQueryParser.test.ts +50 -42
- package/lib/shared/cmsQueryParser.ts +60 -38
- package/lib/shared/colorConversions.ts +9 -9
- package/lib/shared/colorVariableUtils.test.ts +27 -5
- package/lib/shared/colorVariableUtils.ts +21 -12
- package/lib/shared/componentRefs.ts +1 -5
- package/lib/shared/constants.test.ts +3 -3
- package/lib/shared/constants.ts +8 -8
- package/lib/shared/cssGeneration.test.ts +285 -144
- package/lib/shared/cssGeneration.ts +217 -531
- package/lib/shared/cssNamedColors.ts +152 -30
- package/lib/shared/cssProperties.test.ts +69 -70
- package/lib/shared/cssProperties.ts +298 -114
- package/lib/shared/elementClassName.test.ts +109 -109
- package/lib/shared/elementClassName.ts +3 -3
- package/lib/shared/elementUtils.ts +12 -16
- package/lib/shared/errorLogger.test.ts +12 -12
- package/lib/shared/errorLogger.ts +2 -10
- package/lib/shared/errors.test.ts +2 -13
- package/lib/shared/errors.ts +2 -8
- package/lib/shared/expressionEvaluator.test.ts +145 -0
- package/lib/shared/expressionEvaluator.ts +52 -24
- package/lib/shared/fontLoader.test.ts +32 -18
- package/lib/shared/fontLoader.ts +2 -2
- package/lib/shared/friendlyError.ts +2 -3
- package/lib/shared/gradientUtils.test.ts +3 -7
- package/lib/shared/gradientUtils.ts +17 -14
- package/lib/shared/hrefRefs.test.ts +2 -2
- package/lib/shared/hrefRefs.ts +2 -10
- package/lib/shared/i18n.test.ts +4 -4
- package/lib/shared/i18n.ts +18 -38
- package/lib/shared/index.ts +27 -0
- package/lib/shared/inlineSvgStyleRules.ts +14 -13
- package/lib/shared/interactiveStyleMappings.test.ts +32 -54
- package/lib/shared/interactiveStyleMappings.ts +11 -24
- package/lib/shared/interactiveStyles.test.ts +165 -188
- package/lib/shared/interfaces/contentProvider.ts +1 -1
- package/lib/shared/itemTemplateUtils.test.ts +11 -17
- package/lib/shared/itemTemplateUtils.ts +27 -39
- package/lib/shared/jsonRepair.ts +16 -10
- package/lib/shared/libraryLoader.test.ts +21 -55
- package/lib/shared/libraryLoader.ts +7 -22
- package/lib/shared/linkUtils.ts +1 -1
- package/lib/shared/logger.test.ts +66 -0
- package/lib/shared/logger.ts +94 -0
- package/lib/shared/markdown.ts +25 -0
- package/lib/shared/netlifyLocale404.test.ts +179 -0
- package/lib/shared/netlifyLocale404.ts +110 -0
- package/lib/shared/nodeUtils.test.ts +28 -16
- package/lib/shared/nodeUtils.ts +76 -23
- package/lib/shared/pathArrayUtils.test.ts +1 -2
- package/lib/shared/pathArrayUtils.ts +1 -1
- package/lib/shared/pathSecurity.ts +2 -2
- package/lib/shared/pathUtils.test.ts +4 -6
- package/lib/shared/pathUtils.ts +42 -49
- package/lib/shared/paths/Path.test.ts +2 -2
- package/lib/shared/paths/Path.ts +0 -1
- package/lib/shared/paths/PathConverter.test.ts +1 -1
- package/lib/shared/paths/PathConverter.ts +17 -20
- package/lib/shared/paths/PathUtils.ts +13 -13
- package/lib/shared/paths/PathValidator.test.ts +2 -15
- package/lib/shared/paths/PathValidator.ts +12 -10
- package/lib/shared/paths/index.ts +1 -2
- package/lib/shared/permissions.test.ts +168 -0
- package/lib/shared/permissions.ts +162 -0
- package/lib/shared/propResolver.test.ts +240 -244
- package/lib/shared/propResolver.ts +15 -26
- package/lib/shared/pxToRem.test.ts +7 -6
- package/lib/shared/pxToRem.ts +2 -5
- package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +12 -6
- package/lib/shared/registry/ClientRegistry.ts +0 -2
- package/lib/shared/registry/ComponentRegistry.test.ts +46 -32
- package/lib/shared/registry/ComponentRegistry.ts +12 -9
- package/lib/shared/registry/NodeTypeDefinition.ts +16 -9
- package/lib/shared/registry/RegistryManager.ts +1 -2
- package/lib/shared/registry/SSRRegistry.ts +0 -2
- package/lib/shared/registry/createNodeType.ts +7 -9
- package/lib/shared/registry/defineNodeType.ts +2 -6
- package/lib/shared/registry/fieldPresets.ts +3 -1
- package/lib/shared/registry/index.ts +0 -1
- package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +14 -15
- package/lib/shared/registry/nodeTypes/CustomNodeType.ts +103 -0
- package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +18 -11
- package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +48 -19
- package/lib/shared/registry/nodeTypes/IslandNodeType.ts +129 -0
- package/lib/shared/registry/nodeTypes/LinkNodeType.ts +21 -19
- package/lib/shared/registry/nodeTypes/ListNodeType.ts +117 -77
- package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +27 -21
- package/lib/shared/registry/nodeTypes/MarkdownNodeType.ts +78 -0
- package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +12 -8
- package/lib/shared/registry/nodeTypes/index.ts +26 -3
- package/lib/shared/responsiveScaling.test.ts +16 -34
- package/lib/shared/responsiveScaling.ts +61 -42
- package/lib/shared/responsiveStyleUtils.test.ts +0 -1
- package/lib/shared/responsiveStyleUtils.ts +11 -13
- package/lib/shared/richtext/htmlToTiptap.test.ts +139 -130
- package/lib/shared/richtext/htmlToTiptap.ts +2 -4
- package/lib/shared/richtext/tiptapToHtml.test.ts +5 -6
- package/lib/shared/richtext/types.ts +1 -8
- package/lib/shared/slugTranslator.test.ts +13 -13
- package/lib/shared/slugTranslator.ts +12 -16
- package/lib/shared/slugify.ts +9 -15
- package/lib/shared/styleNodeUtils.test.ts +8 -8
- package/lib/shared/styleNodeUtils.ts +18 -20
- package/lib/shared/styleUtils.test.ts +87 -61
- package/lib/shared/styleUtils.ts +5 -6
- package/lib/shared/themeDefaults.test.ts +11 -14
- package/lib/shared/themeDefaults.ts +3 -4
- package/lib/shared/tree/PathBuilder.test.ts +63 -145
- package/lib/shared/tree/PathBuilder.ts +59 -47
- package/lib/shared/treePathUtils.test.ts +51 -12
- package/lib/shared/treePathUtils.ts +91 -60
- package/lib/shared/types/api.ts +105 -8
- package/lib/shared/types/cms.test.ts +34 -0
- package/lib/shared/types/cms.ts +90 -34
- package/lib/shared/types/comment.ts +49 -16
- package/lib/shared/types/components.ts +51 -25
- package/lib/shared/types/errors.test.ts +1 -6
- package/lib/shared/types/errors.ts +3 -7
- package/lib/shared/types/experiments.ts +28 -28
- package/lib/shared/types/index.ts +25 -2
- package/lib/shared/types/permissions.ts +139 -0
- package/lib/shared/types/styles.ts +0 -1
- package/lib/shared/types/variables.test.ts +4 -13
- package/lib/shared/types/variables.ts +51 -30
- package/lib/shared/types.ts +1 -2
- package/lib/shared/utilityClassConfig.ts +751 -321
- package/lib/shared/utilityClassMapper.durability.test.ts +117 -0
- package/lib/shared/utilityClassMapper.test.ts +433 -95
- package/lib/shared/utilityClassMapper.ts +322 -249
- package/lib/shared/utilityClassNames.test.ts +38 -0
- package/lib/shared/utilityClassNames.ts +458 -0
- package/lib/shared/utils.test.ts +2 -10
- package/lib/shared/utils.ts +19 -10
- package/lib/shared/validation/cmsValidators.ts +2 -1
- package/lib/shared/validation/commentValidators.test.ts +53 -0
- package/lib/shared/validation/commentValidators.ts +12 -1
- package/lib/shared/validation/index.ts +1 -0
- package/lib/shared/validation/permissionsValidators.test.ts +49 -0
- package/lib/shared/validation/permissionsValidators.ts +49 -0
- package/lib/shared/validation/propValidator.test.ts +19 -21
- package/lib/shared/validation/propValidator.ts +13 -18
- package/lib/shared/validation/schemas.test.ts +64 -32
- package/lib/shared/validation/schemas.ts +616 -345
- package/lib/shared/validation/validators.test.ts +3 -8
- package/lib/shared/validation/validators.ts +89 -68
- package/lib/shared/viewportUnits.integration.test.ts +14 -10
- package/lib/shared/viewportUnits.test.ts +24 -23
- package/lib/shared/viewportUnits.ts +12 -5
- package/lib/test-utils/dom-setup.ts +1 -1
- package/lib/test-utils/factories/ConsoleMockFactory.ts +10 -14
- package/lib/test-utils/factories/DomMockFactory.ts +17 -23
- package/lib/test-utils/factories/EventMockFactory.ts +7 -13
- package/lib/test-utils/factories/FetchMockFactory.ts +39 -57
- package/lib/test-utils/factories/ServerMockFactory.ts +6 -10
- package/lib/test-utils/factories/StoreMockFactory.ts +18 -29
- package/lib/test-utils/fixtures.ts +45 -45
- package/lib/test-utils/helpers/asyncHelpers.test.ts +15 -18
- package/lib/test-utils/helpers/asyncHelpers.ts +11 -20
- package/lib/test-utils/helpers.ts +1 -5
- package/lib/test-utils/index.ts +0 -4
- package/lib/test-utils/mockFactories.ts +13 -19
- package/lib/test-utils/mocks.ts +6 -4
- package/package.json +3 -5
- package/scripts/build-for-publish.mjs +2 -4
- package/scripts/build-meno-filter.ts +4 -7
- package/tsconfig.json +1 -0
- package/vite.config.ts +4 -4
- package/build-next.ts +0 -1361
- package/build-static.test.ts +0 -424
- package/build-static.ts +0 -1074
- package/dist/build-static.js +0 -36
- package/dist/chunks/chunk-2MHDV5BF.js +0 -251
- package/dist/chunks/chunk-2MHDV5BF.js.map +0 -7
- package/dist/chunks/chunk-AZQYF6KE.js +0 -559
- package/dist/chunks/chunk-AZQYF6KE.js.map +0 -7
- package/dist/chunks/chunk-GZHGVVW3.js +0 -322
- package/dist/chunks/chunk-GZHGVVW3.js.map +0 -7
- package/dist/chunks/chunk-H3GJ4H2U.js.map +0 -7
- package/dist/chunks/chunk-I7YIGZXT.js +0 -467
- package/dist/chunks/chunk-I7YIGZXT.js.map +0 -7
- package/dist/chunks/chunk-IGYR22T6.js +0 -6192
- package/dist/chunks/chunk-IGYR22T6.js.map +0 -7
- package/dist/chunks/chunk-JGP5A3Y5.js +0 -1136
- package/dist/chunks/chunk-JGP5A3Y5.js.map +0 -7
- package/dist/chunks/chunk-JGWFTO6P.js.map +0 -7
- package/dist/chunks/chunk-O3NAGJP4.js.map +0 -7
- package/dist/chunks/chunk-QB2LNO4W.js +0 -77
- package/dist/chunks/chunk-QB2LNO4W.js.map +0 -7
- package/dist/chunks/chunk-R6XHAFBF.js +0 -3821
- package/dist/chunks/chunk-R6XHAFBF.js.map +0 -7
- package/dist/chunks/chunk-UB44F4Z2.js.map +0 -7
- package/dist/chunks/chunk-WQFG7PAH.js.map +0 -7
- package/dist/chunks/chunk-X754AHS5.js.map +0 -7
- package/dist/chunks/chunk-YBLHKYFF.js.map +0 -7
- package/dist/chunks/configService-R3OGU2UD.js +0 -13
- package/dist/chunks/configService-R3OGU2UD.js.map +0 -7
- package/dist/chunks/constants-STK2YBIW.js +0 -46
- package/dist/chunks/constants-STK2YBIW.js.map +0 -7
- package/dist/chunks/fs-JGINUXGL.js.map +0 -7
- package/dist/entries/server-router.js +0 -67
- package/dist/entries/server-router.js.map +0 -7
- package/entries/server-router.tsx +0 -81
- package/lib/server/providers/fileSystemPageProvider.ts +0 -160
- package/lib/server/webflow/buildWebflow.ts +0 -635
- package/lib/server/webflow/index.ts +0 -23
- package/lib/server/webflow/nodeToWebflow.test.ts +0 -3170
- package/lib/server/webflow/nodeToWebflow.ts +0 -2435
- package/lib/server/webflow/styleMapper.test.ts +0 -389
- package/lib/server/webflow/styleMapper.ts +0 -695
- package/lib/server/webflow/templateWrapper.ts +0 -49
- package/lib/server/webflow/types.ts +0 -396
- /package/dist/{build-static.js.map → chunks/fs-G3ANWEFP.js.map} +0 -0
package/lib/server/jsonLoader.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
189
|
-
|
|
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
|
-
|
|
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:
|
|
350
|
-
|
|
351
|
-
|
|
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
|
|
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
|
-
|
|
31
|
-
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
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 () => {
|
|
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
|
-
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, test, expect
|
|
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 (
|
|
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 (
|
|
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> => {
|
|
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);
|