zudoku 0.0.0-z179c4f85 → 0.0.0-z2f55b5ae
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/dist/app/main.js +1 -1
- package/dist/app/main.js.map +1 -1
- package/dist/config/create-plugin.d.ts +2 -0
- package/dist/config/create-plugin.js +55 -0
- package/dist/config/create-plugin.js.map +1 -0
- package/dist/config/loader.js +3 -1
- package/dist/config/loader.js.map +1 -1
- package/dist/config/validators/InputNavigationSchema.d.ts +103 -53
- package/dist/config/validators/InputNavigationSchema.js +17 -0
- package/dist/config/validators/InputNavigationSchema.js.map +1 -1
- package/dist/config/validators/NavigationSchema.d.ts +10 -2
- package/dist/config/validators/NavigationSchema.js +7 -0
- package/dist/config/validators/NavigationSchema.js.map +1 -1
- package/dist/config/validators/ProtectedRoutesSchema.d.ts +1 -1
- package/dist/config/validators/validate.d.ts +25 -6
- package/dist/config/validators/validate.js +8 -3
- package/dist/config/validators/validate.js.map +1 -1
- package/dist/flat-config.d.ts +40 -27
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/components/Bootstrap.js +1 -2
- package/dist/lib/components/Bootstrap.js.map +1 -1
- package/dist/lib/components/Heading.d.ts +1 -1
- package/dist/lib/components/MobileTopNavigation.js +2 -1
- package/dist/lib/components/MobileTopNavigation.js.map +1 -1
- package/dist/lib/components/Slot.test.js +1 -1
- package/dist/lib/components/Slot.test.js.map +1 -1
- package/dist/lib/components/TopNavigation.d.ts +7 -1
- package/dist/lib/components/TopNavigation.js +7 -2
- package/dist/lib/components/TopNavigation.js.map +1 -1
- package/dist/lib/components/Zudoku.d.ts +4 -1
- package/dist/lib/components/Zudoku.js +4 -7
- package/dist/lib/components/Zudoku.js.map +1 -1
- package/dist/lib/components/context/ZudokuContext.d.ts +9 -4
- package/dist/lib/components/context/ZudokuContext.js +4 -2
- package/dist/lib/components/context/ZudokuContext.js.map +1 -1
- package/dist/lib/components/context/ZudokuProvider.js +1 -1
- package/dist/lib/components/context/ZudokuProvider.js.map +1 -1
- package/dist/lib/components/context/ZudokuReactContext.d.ts +11 -0
- package/dist/lib/components/context/ZudokuReactContext.js +4 -0
- package/dist/lib/components/context/ZudokuReactContext.js.map +1 -0
- package/dist/lib/components/index.d.ts +18 -74
- package/dist/lib/components/index.js +19 -36
- package/dist/lib/components/index.js.map +1 -1
- package/dist/lib/components/navigation/Navigation.js +4 -3
- package/dist/lib/components/navigation/Navigation.js.map +1 -1
- package/dist/lib/components/navigation/NavigationCategory.js +8 -0
- package/dist/lib/components/navigation/NavigationCategory.js.map +1 -1
- package/dist/lib/components/navigation/NavigationFilterContext.d.ts +8 -0
- package/dist/lib/components/navigation/NavigationFilterContext.js +12 -0
- package/dist/lib/components/navigation/NavigationFilterContext.js.map +1 -0
- package/dist/lib/components/navigation/NavigationFilterInput.d.ts +3 -0
- package/dist/lib/components/navigation/NavigationFilterInput.js +9 -0
- package/dist/lib/components/navigation/NavigationFilterInput.js.map +1 -0
- package/dist/lib/components/navigation/NavigationItem.js +11 -1
- package/dist/lib/components/navigation/NavigationItem.js.map +1 -1
- package/dist/lib/components/navigation/utils.d.ts +2 -1
- package/dist/lib/components/navigation/utils.js +22 -1
- package/dist/lib/components/navigation/utils.js.map +1 -1
- package/dist/lib/core/ZudokuContext.d.ts +2 -1
- package/dist/lib/core/ZudokuContext.js +3 -1
- package/dist/lib/core/ZudokuContext.js.map +1 -1
- package/dist/lib/core/__internal.d.ts +1 -0
- package/dist/lib/core/__internal.js +2 -0
- package/dist/lib/core/__internal.js.map +1 -1
- package/dist/lib/core/plugins.d.ts +15 -1
- package/dist/lib/core/plugins.js +1 -0
- package/dist/lib/core/plugins.js.map +1 -1
- package/dist/lib/core/transform-config.d.ts +4 -0
- package/dist/lib/core/transform-config.js +42 -0
- package/dist/lib/core/transform-config.js.map +1 -0
- package/dist/lib/core/transform-config.test.d.ts +1 -0
- package/dist/lib/core/transform-config.test.js +83 -0
- package/dist/lib/core/transform-config.test.js.map +1 -0
- package/dist/lib/errors/ErrorAlert.js +1 -2
- package/dist/lib/errors/ErrorAlert.js.map +1 -1
- package/dist/lib/hooks/index.d.ts +7 -30
- package/dist/lib/hooks/index.js +7 -15
- package/dist/lib/hooks/index.js.map +1 -1
- package/dist/lib/hooks/useEvent.test.js +1 -1
- package/dist/lib/hooks/useEvent.test.js.map +1 -1
- package/dist/lib/oas/graphql/circular.d.ts +1 -1
- package/dist/lib/oas/graphql/circular.js +18 -35
- package/dist/lib/oas/graphql/circular.js.map +1 -1
- package/dist/lib/oas/graphql/circular.test.js +33 -2
- package/dist/lib/oas/graphql/circular.test.js.map +1 -1
- package/dist/lib/oas/parser/index.js +14 -5
- package/dist/lib/oas/parser/index.js.map +1 -1
- package/dist/lib/plugins/openapi/OasProvider.js +6 -2
- package/dist/lib/plugins/openapi/OasProvider.js.map +1 -1
- package/dist/lib/plugins/openapi/OperationList.js +4 -8
- package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
- package/dist/lib/plugins/openapi/interfaces.d.ts +3 -0
- package/dist/lib/plugins/openapi/playground/fileUtils.d.ts +1 -0
- package/dist/lib/plugins/openapi/playground/fileUtils.js +3 -0
- package/dist/lib/plugins/openapi/playground/fileUtils.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/result-panel/AudioPlayer.d.ts +6 -0
- package/dist/lib/plugins/openapi/playground/result-panel/AudioPlayer.js +20 -0
- package/dist/lib/plugins/openapi/playground/result-panel/AudioPlayer.js.map +1 -0
- package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.js +7 -2
- package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.js.map +1 -1
- package/dist/lib/plugins/openapi/util/getRoutes.d.ts +3 -0
- package/dist/lib/plugins/openapi/util/getRoutes.js +2 -1
- package/dist/lib/plugins/openapi/util/getRoutes.js.map +1 -1
- package/dist/lib/ui/Alert.d.ts +3 -2
- package/dist/lib/ui/Alert.js +9 -5
- package/dist/lib/ui/Alert.js.map +1 -1
- package/dist/lib/ui/Command.d.ts +3 -3
- package/dist/lib/ui/InputGroup.d.ts +16 -0
- package/dist/lib/ui/InputGroup.js +65 -0
- package/dist/lib/ui/InputGroup.js.map +1 -0
- package/dist/lib/ui/Secret.js +2 -2
- package/dist/lib/ui/Secret.js.map +1 -1
- package/dist/lib/util/flattenAllOf.d.ts +0 -2
- package/dist/lib/util/flattenAllOf.js +0 -46
- package/dist/lib/util/flattenAllOf.js.map +1 -1
- package/dist/lib/util/flattenAllOf.test.js +2 -1
- package/dist/lib/util/flattenAllOf.test.js.map +1 -1
- package/dist/lib/util/flattenAllOfProcessor.d.ts +2 -0
- package/dist/lib/util/flattenAllOfProcessor.js +48 -0
- package/dist/lib/util/flattenAllOfProcessor.js.map +1 -0
- package/dist/lib/util/readFrontmatter.js +2 -1
- package/dist/lib/util/readFrontmatter.js.map +1 -1
- package/dist/vite/api/SchemaManager.d.ts +11 -1
- package/dist/vite/api/SchemaManager.js +29 -18
- package/dist/vite/api/SchemaManager.js.map +1 -1
- package/dist/vite/api/SchemaManager.test.js +45 -1
- package/dist/vite/api/SchemaManager.test.js.map +1 -1
- package/dist/vite/build.js +91 -73
- package/dist/vite/build.js.map +1 -1
- package/dist/vite/config.js +5 -2
- package/dist/vite/config.js.map +1 -1
- package/dist/vite/mdx/remark-inject-filepath.js +5 -1
- package/dist/vite/mdx/remark-inject-filepath.js.map +1 -1
- package/dist/vite/mdx/remark-link-rewrite.js +3 -2
- package/dist/vite/mdx/remark-link-rewrite.js.map +1 -1
- package/dist/vite/plugin-api.js +5 -3
- package/dist/vite/plugin-api.js.map +1 -1
- package/dist/vite/plugin-config.js +16 -4
- package/dist/vite/plugin-config.js.map +1 -1
- package/dist/vite/plugin-docs.js +9 -7
- package/dist/vite/plugin-docs.js.map +1 -1
- package/dist/vite/plugin-markdown-export.js +4 -2
- package/dist/vite/plugin-markdown-export.js.map +1 -1
- package/dist/vite/plugin-theme.js +2 -1
- package/dist/vite/plugin-theme.js.map +1 -1
- package/dist/vite/prerender/prerender.js +3 -1
- package/dist/vite/prerender/prerender.js.map +1 -1
- package/dist/vite/prerender/worker.js +3 -1
- package/dist/vite/prerender/worker.js.map +1 -1
- package/lib/{ClaudeLogo-DHxJUhN_.js → ClaudeLogo-Br8C_vTq.js} +26 -22
- package/lib/{ClaudeLogo-DHxJUhN_.js.map → ClaudeLogo-Br8C_vTq.js.map} +1 -1
- package/lib/Drawer-Ch7927PF.js.map +1 -1
- package/lib/{HydrationBoundary-CNF2ZV3E.js → HydrationBoundary-CJu4vUlG.js} +6 -6
- package/lib/{HydrationBoundary-CNF2ZV3E.js.map → HydrationBoundary-CJu4vUlG.js.map} +1 -1
- package/lib/{MdxPage-CD36PJ17.js → MdxPage-C0QFAsgv.js} +8 -8
- package/lib/{MdxPage-CD36PJ17.js.map → MdxPage-C0QFAsgv.js.map} +1 -1
- package/lib/Mermaid-Chx5BPHn.js +104 -0
- package/lib/Mermaid-Chx5BPHn.js.map +1 -0
- package/lib/{OAuthErrorPage-4mN5DA86.js → OAuthErrorPage-CFz_gBFx.js} +22 -19
- package/lib/{OAuthErrorPage-4mN5DA86.js.map → OAuthErrorPage-CFz_gBFx.js.map} +1 -1
- package/lib/OasProvider-BnQ1_ehf.js +48 -0
- package/lib/OasProvider-BnQ1_ehf.js.map +1 -0
- package/lib/OperationList-Bw-3OS_8.js +5907 -0
- package/lib/OperationList-Bw-3OS_8.js.map +1 -0
- package/lib/{RouteGuard--A04ESy8.js → RouteGuard-CVs3yvEs.js} +5 -5
- package/lib/{RouteGuard--A04ESy8.js.map → RouteGuard-CVs3yvEs.js.map} +1 -1
- package/lib/{SchemaList-Dw3-CJPb.js → SchemaList-IehIWcDV.js} +8 -8
- package/lib/{SchemaList-Dw3-CJPb.js.map → SchemaList-IehIWcDV.js.map} +1 -1
- package/lib/{SchemaView-DyJkiQkD.js → SchemaView-BZLyoQRI.js} +3 -3
- package/lib/{SchemaView-DyJkiQkD.js.map → SchemaView-BZLyoQRI.js.map} +1 -1
- package/lib/{Secret-BDBqq4p3.js → Secret-DUpgv4V3.js} +92 -72
- package/lib/Secret-DUpgv4V3.js.map +1 -0
- package/lib/{SignUp-DRvN-8cq.js → SignUp-Dug1jAGC.js} +31 -26
- package/lib/{SignUp-DRvN-8cq.js.map → SignUp-Dug1jAGC.js.map} +1 -1
- package/lib/{SyntaxHighlight-klTH8c6-.js → SyntaxHighlight-BMu0b_hF.js} +12 -11
- package/lib/SyntaxHighlight-BMu0b_hF.js.map +1 -0
- package/lib/{Toc-PbuF-u9x.js → Toc-BiJ2YL0O.js} +2 -2
- package/lib/{Toc-PbuF-u9x.js.map → Toc-BiJ2YL0O.js.map} +1 -1
- package/lib/{index-BDsEwofZ.js → Zudoku-iyiXgWFY.js} +2996 -2859
- package/lib/Zudoku-iyiXgWFY.js.map +1 -0
- package/lib/ZudokuContext-CYyb_PB_.js +175 -0
- package/lib/ZudokuContext-CYyb_PB_.js.map +1 -0
- package/lib/ZudokuReactContext-DGJAP1sN.js +222 -0
- package/lib/ZudokuReactContext-DGJAP1sN.js.map +1 -0
- package/lib/chunk-EPOLDU6W-C6C8jAwd.js.map +1 -1
- package/lib/{circular-DFquXeY2.js → circular-CfBNz-Ot.js} +1327 -1346
- package/lib/{circular-DFquXeY2.js.map → circular-CfBNz-Ot.js.map} +1 -1
- package/lib/createServer-BtZPTSEO.js +13036 -0
- package/lib/createServer-BtZPTSEO.js.map +1 -0
- package/lib/{errors-rWHkzVTd.js → errors-B77S9iOc.js} +3 -3
- package/lib/{errors-rWHkzVTd.js.map → errors-B77S9iOc.js.map} +1 -1
- package/lib/{firebase-BmGU1FuD.js → firebase-C7XKRGLf.js} +26 -25
- package/lib/firebase-C7XKRGLf.js.map +1 -0
- package/lib/{hook-BGlHBdET.js → hook-Dz_n9SoE.js} +16 -15
- package/lib/{hook-BGlHBdET.js.map → hook-Dz_n9SoE.js.map} +1 -1
- package/lib/{index-DRBOFufT.js → index-BDp2MTiq.js} +2 -2
- package/lib/{index-DRBOFufT.js.map → index-BDp2MTiq.js.map} +1 -1
- package/lib/{index-BQB9hb6n.js → index-CQ-p1wyT.js} +588 -547
- package/lib/index-CQ-p1wyT.js.map +1 -0
- package/lib/index-CrcNWbel.js.map +1 -1
- package/lib/index-DAWHN3cH.js +86 -0
- package/lib/index-DAWHN3cH.js.map +1 -0
- package/lib/index.esm-BYObtETB.js.map +1 -1
- package/lib/{index.esm-B_0dvNjB.js → index.esm-Ca5zvoff.js} +20 -20
- package/lib/{index.esm-B_0dvNjB.js.map → index.esm-Ca5zvoff.js.map} +1 -1
- package/lib/{index.esm-Cx8B1YJQ.js → index.esm-Cth49JBv.js} +2 -2
- package/lib/index.esm-Cth49JBv.js.map +1 -0
- package/lib/{invariant-BJAl77rw.js → invariant-B_t_F2s_.js} +3 -3
- package/lib/{invariant-BJAl77rw.js.map → invariant-B_t_F2s_.js.map} +1 -1
- package/lib/jsx-runtime-BzflLqGi.js.map +1 -1
- package/lib/{mutation-BISOc7OM.js → mutation-B7eFBLZY.js} +2 -2
- package/lib/{mutation-BISOc7OM.js.map → mutation-B7eFBLZY.js.map} +1 -1
- package/lib/ui/Alert.js +32 -20
- package/lib/ui/Alert.js.map +1 -1
- package/lib/ui/Carousel.js.map +1 -1
- package/lib/ui/InputGroup.js +155 -0
- package/lib/ui/InputGroup.js.map +1 -0
- package/lib/ui/Secret.js +2 -2
- package/lib/ui/Secret.js.map +1 -1
- package/lib/ui/SyntaxHighlight.js +3 -3
- package/lib/useExposedProps-CzTDfXfq.js +30 -0
- package/lib/useExposedProps-CzTDfXfq.js.map +1 -0
- package/lib/{useMutation-CFMGlAMW.js → useMutation-CErliDZ9.js} +5 -5
- package/lib/{useMutation-CFMGlAMW.js.map → useMutation-CErliDZ9.js.map} +1 -1
- package/lib/{useSuspenseQuery-CSB_rVek.js → useQuery-ht7aWJ3S.js} +432 -446
- package/lib/useQuery-ht7aWJ3S.js.map +1 -0
- package/lib/useSuspenseQuery-DQH4Bmc2.js +18 -0
- package/lib/useSuspenseQuery-DQH4Bmc2.js.map +1 -0
- package/lib/zudoku.__internal.js +1519 -1033
- package/lib/zudoku.__internal.js.map +1 -1
- package/lib/zudoku.auth-auth0.js +6 -5
- package/lib/zudoku.auth-auth0.js.map +1 -1
- package/lib/zudoku.auth-azureb2c.js +14 -13
- package/lib/zudoku.auth-azureb2c.js.map +1 -1
- package/lib/zudoku.auth-clerk.js +2 -2
- package/lib/zudoku.auth-firebase.js +5 -5
- package/lib/zudoku.auth-openid.js +8 -7
- package/lib/zudoku.auth-openid.js.map +1 -1
- package/lib/zudoku.auth-supabase.js +4 -4
- package/lib/zudoku.components.js +31 -29
- package/lib/zudoku.components.js.map +1 -1
- package/lib/zudoku.hooks.js +24 -11
- package/lib/zudoku.hooks.js.map +1 -1
- package/lib/zudoku.mermaid.js +5 -4
- package/lib/zudoku.mermaid.js.map +1 -1
- package/lib/zudoku.plugin-api-catalog.js +41 -36
- package/lib/zudoku.plugin-api-catalog.js.map +1 -1
- package/lib/zudoku.plugin-api-keys.js +156 -153
- package/lib/zudoku.plugin-api-keys.js.map +1 -1
- package/lib/zudoku.plugin-custom-pages.js +1 -1
- package/lib/zudoku.plugin-markdown.js +1 -1
- package/lib/zudoku.plugin-openapi.js +2 -2
- package/lib/zudoku.plugin-search-pagefind.js +19 -18
- package/lib/zudoku.plugin-search-pagefind.js.map +1 -1
- package/lib/zudoku.plugins.js +9 -8
- package/lib/zudoku.plugins.js.map +1 -1
- package/lib/zudoku.react-query.js +26 -25
- package/lib/zudoku.react-query.js.map +1 -1
- package/lib/zudoku.router.js.map +1 -1
- package/package.json +23 -15
- package/src/app/defaultTheme.css +4 -0
- package/src/app/main.css +2 -0
- package/src/app/main.tsx +1 -1
- package/src/lib/components/Bootstrap.tsx +1 -4
- package/src/lib/components/MobileTopNavigation.tsx +13 -8
- package/src/lib/components/Slot.test.tsx +1 -1
- package/src/lib/components/TopNavigation.tsx +25 -7
- package/src/lib/components/Zudoku.tsx +18 -14
- package/src/lib/components/context/ZudokuContext.ts +3 -6
- package/src/lib/components/context/ZudokuProvider.tsx +1 -1
- package/src/lib/components/context/ZudokuReactContext.tsx +17 -0
- package/src/lib/components/index.ts +19 -39
- package/src/lib/components/navigation/Navigation.tsx +4 -3
- package/src/lib/components/navigation/NavigationCategory.tsx +9 -0
- package/src/lib/components/navigation/NavigationFilterContext.tsx +28 -0
- package/src/lib/components/navigation/NavigationFilterInput.tsx +35 -0
- package/src/lib/components/navigation/NavigationItem.tsx +17 -1
- package/src/lib/components/navigation/utils.ts +32 -1
- package/src/lib/core/ZudokuContext.ts +7 -1
- package/src/lib/core/__internal.tsx +2 -0
- package/src/lib/core/plugins.ts +25 -1
- package/src/lib/core/transform-config.test.tsx +99 -0
- package/src/lib/core/transform-config.ts +67 -0
- package/src/lib/errors/ErrorAlert.tsx +1 -6
- package/src/lib/hooks/index.ts +7 -16
- package/src/lib/hooks/useEvent.test.tsx +1 -1
- package/src/lib/oas/graphql/circular.test.ts +37 -2
- package/src/lib/oas/graphql/circular.ts +25 -51
- package/src/lib/oas/parser/index.ts +17 -6
- package/src/lib/plugins/openapi/OasProvider.tsx +10 -2
- package/src/lib/plugins/openapi/OperationList.tsx +4 -9
- package/src/lib/plugins/openapi/interfaces.ts +6 -1
- package/src/lib/plugins/openapi/playground/fileUtils.ts +4 -0
- package/src/lib/plugins/openapi/playground/result-panel/AudioPlayer.tsx +50 -0
- package/src/lib/plugins/openapi/playground/result-panel/ResponseTab.tsx +33 -17
- package/src/lib/plugins/openapi/util/getRoutes.tsx +4 -1
- package/src/lib/ui/Alert.tsx +17 -5
- package/src/lib/ui/InputGroup.tsx +168 -0
- package/src/lib/ui/Secret.tsx +2 -2
- package/src/lib/util/flattenAllOf.test.ts +2 -1
- package/src/lib/util/flattenAllOf.ts +0 -57
- package/src/lib/util/flattenAllOfProcessor.ts +58 -0
- package/src/lib/util/readFrontmatter.ts +2 -1
- package/src/shiki/langs/c3.js +1 -0
- package/src/shiki/langs/gn.js +1 -0
- package/src/shiki/langs/moonbit.js +1 -0
- package/src/zuplo/enrich-with-zuplo-mcp.ts +168 -0
- package/src/zuplo/enrich-with-zuplo.ts +254 -0
- package/src/zuplo/policy-types.ts +46 -0
- package/src/zuplo/with-zuplo-processors.ts +35 -0
- package/src/zuplo/with-zuplo.ts +14 -0
- package/lib/Mermaid-Koc3z8mU.js +0 -102
- package/lib/Mermaid-Koc3z8mU.js.map +0 -1
- package/lib/OasProvider-DSe-hk5Y.js +0 -40
- package/lib/OasProvider-DSe-hk5Y.js.map +0 -1
- package/lib/OperationList-CaknPbvq.js +0 -5823
- package/lib/OperationList-CaknPbvq.js.map +0 -1
- package/lib/Secret-BDBqq4p3.js.map +0 -1
- package/lib/Separator-BXt1LYnm.js +0 -27
- package/lib/Separator-BXt1LYnm.js.map +0 -1
- package/lib/SyntaxHighlight-klTH8c6-.js.map +0 -1
- package/lib/ZudokuContext-BZB1TWdT.js +0 -387
- package/lib/ZudokuContext-BZB1TWdT.js.map +0 -1
- package/lib/___vite-browser-external_commonjs-proxy-BttVsNON.js +0 -9
- package/lib/___vite-browser-external_commonjs-proxy-BttVsNON.js.map +0 -1
- package/lib/createServer-BXZ0CAUn.js +0 -16693
- package/lib/createServer-BXZ0CAUn.js.map +0 -1
- package/lib/firebase-BmGU1FuD.js.map +0 -1
- package/lib/index-BDsEwofZ.js.map +0 -1
- package/lib/index-BQB9hb6n.js.map +0 -1
- package/lib/index-DBjOT2H1.js +0 -133
- package/lib/index-DBjOT2H1.js.map +0 -1
- package/lib/index.esm-Cx8B1YJQ.js.map +0 -1
- package/lib/useSuspenseQuery-CSB_rVek.js.map +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ExternalLinkIcon } from "lucide-react";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import { NavLink, useLocation } from "react-router";
|
|
4
|
+
import { Separator } from "zudoku/ui/Separator.js";
|
|
4
5
|
import { Tooltip, TooltipContent, TooltipTrigger } from "zudoku/ui/Tooltip.js";
|
|
5
6
|
import type { NavigationItem as NavigationItemType } from "../../../config/validators/NavigationSchema.js";
|
|
6
7
|
import { useAuth } from "../../authentication/hook.js";
|
|
@@ -11,6 +12,8 @@ import { useViewportAnchor } from "../context/ViewportAnchorContext.js";
|
|
|
11
12
|
import { useZudoku } from "../context/ZudokuContext.js";
|
|
12
13
|
import { NavigationBadge } from "./NavigationBadge.js";
|
|
13
14
|
import { NavigationCategory } from "./NavigationCategory.js";
|
|
15
|
+
import { useNavigationFilter } from "./NavigationFilterContext.js";
|
|
16
|
+
import { NavigationFilterInput } from "./NavigationFilterInput.js";
|
|
14
17
|
import { navigationListItem, shouldShowItem } from "./utils.js";
|
|
15
18
|
|
|
16
19
|
const TruncatedLabel = ({
|
|
@@ -65,8 +68,9 @@ export const NavigationItem = ({
|
|
|
65
68
|
const { activeAnchor } = useViewportAnchor();
|
|
66
69
|
const auth = useAuth();
|
|
67
70
|
const context = useZudoku();
|
|
71
|
+
const { query } = useNavigationFilter();
|
|
68
72
|
|
|
69
|
-
if (!shouldShowItem(auth, context)(item)) {
|
|
73
|
+
if (!shouldShowItem(auth, context, query)(item)) {
|
|
70
74
|
return null;
|
|
71
75
|
}
|
|
72
76
|
|
|
@@ -75,6 +79,18 @@ export const NavigationItem = ({
|
|
|
75
79
|
return (
|
|
76
80
|
<NavigationCategory category={item} onRequestClose={onRequestClose} />
|
|
77
81
|
);
|
|
82
|
+
case "separator":
|
|
83
|
+
return (
|
|
84
|
+
<Separator className="my-1 mx-auto w-[calc(100%-var(--padding-nav-item)*2)]!" />
|
|
85
|
+
);
|
|
86
|
+
case "section":
|
|
87
|
+
return (
|
|
88
|
+
<div className="mt-4 px-(--padding-nav-item) text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
|
89
|
+
{item.label}
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
case "filter":
|
|
93
|
+
return <NavigationFilterInput placeholder={item.placeholder} />;
|
|
78
94
|
case "doc":
|
|
79
95
|
return (
|
|
80
96
|
<NavLink
|
|
@@ -86,6 +86,13 @@ export const usePrevNext = (): {
|
|
|
86
86
|
let foundCurrent = false;
|
|
87
87
|
|
|
88
88
|
traverseNavigation(navigation, (item) => {
|
|
89
|
+
if (
|
|
90
|
+
item.type === "separator" ||
|
|
91
|
+
item.type === "section" ||
|
|
92
|
+
item.type === "filter"
|
|
93
|
+
)
|
|
94
|
+
return;
|
|
95
|
+
|
|
89
96
|
const itemId =
|
|
90
97
|
item.type === "doc"
|
|
91
98
|
? joinUrl(item.path)
|
|
@@ -133,9 +140,33 @@ export const navigationListItem = cva(
|
|
|
133
140
|
},
|
|
134
141
|
);
|
|
135
142
|
|
|
143
|
+
export const itemMatchesFilter = (
|
|
144
|
+
item: NavigationItem,
|
|
145
|
+
query: string,
|
|
146
|
+
): boolean => {
|
|
147
|
+
if (["separator", "section", "filter"].includes(item.type)) {
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
if (item.label?.toLowerCase().includes(query.toLowerCase())) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (item.type === "category") {
|
|
155
|
+
return item.items.some((child) => itemMatchesFilter(child, query));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return false;
|
|
159
|
+
};
|
|
160
|
+
|
|
136
161
|
export const shouldShowItem =
|
|
137
|
-
(auth: UseAuthReturn, context: ZudokuContext) =>
|
|
162
|
+
(auth: UseAuthReturn, context: ZudokuContext, filterQuery?: string) =>
|
|
138
163
|
(item: NavigationItem): boolean => {
|
|
164
|
+
if (item.type === "filter") return true;
|
|
165
|
+
|
|
166
|
+
if (filterQuery?.trim() && !itemMatchesFilter(item, filterQuery)) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
|
|
139
170
|
if (typeof item.display === "function") {
|
|
140
171
|
return item.display({ context, auth });
|
|
141
172
|
}
|
|
@@ -132,10 +132,15 @@ export class ZudokuContext {
|
|
|
132
132
|
public readonly getAuthState: () => AuthState;
|
|
133
133
|
public readonly queryClient: QueryClient;
|
|
134
134
|
public readonly options: ZudokuContextOptions;
|
|
135
|
+
public readonly env: Record<string, string | undefined>;
|
|
135
136
|
private readonly navigationPlugins: NavigationPlugin[];
|
|
136
137
|
private emitter = createNanoEvents<ZudokuEvents>();
|
|
137
138
|
|
|
138
|
-
constructor(
|
|
139
|
+
constructor(
|
|
140
|
+
options: ZudokuContextOptions,
|
|
141
|
+
queryClient: QueryClient,
|
|
142
|
+
env: Record<string, string | undefined>,
|
|
143
|
+
) {
|
|
139
144
|
const pluginProtectedRoutes = Object.fromEntries(
|
|
140
145
|
(options.plugins ?? []).flatMap((plugin) => {
|
|
141
146
|
if (!isNavigationPlugin(plugin)) return [];
|
|
@@ -152,6 +157,7 @@ export class ZudokuContext {
|
|
|
152
157
|
};
|
|
153
158
|
|
|
154
159
|
this.queryClient = queryClient;
|
|
160
|
+
this.env = env;
|
|
155
161
|
this.options = { ...options, protectedRoutes };
|
|
156
162
|
this.plugins = options.plugins ?? [];
|
|
157
163
|
this.navigation = options.navigation ?? [];
|
|
@@ -17,6 +17,7 @@ import { StatusPage as StatusPageImport } from "../components/StatusPage.js";
|
|
|
17
17
|
import { RouterError as RouterErrorImport } from "../errors/RouterError.js";
|
|
18
18
|
import { ServerError as ServerErrorImport } from "../errors/ServerError.js";
|
|
19
19
|
import { RouteGuard as RouteGuardImport } from "./RouteGuard.js";
|
|
20
|
+
import { runPluginTransformConfig as runPluginTransformConfigImport } from "./transform-config.js";
|
|
20
21
|
|
|
21
22
|
export const Layout = LayoutImport;
|
|
22
23
|
export const RouterError = RouterErrorImport;
|
|
@@ -28,3 +29,4 @@ export const Head = Helmet;
|
|
|
28
29
|
export const StatusPage = StatusPageImport;
|
|
29
30
|
export const BuildCheck = BuildCheckImport;
|
|
30
31
|
export const Meta = MetaImport;
|
|
32
|
+
export const runPluginTransformConfig = runPluginTransformConfigImport;
|
package/src/lib/core/plugins.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { ReactNode } from "react";
|
|
|
3
3
|
import type { Location, RouteObject } from "react-router";
|
|
4
4
|
import type { Navigation } from "../../config/validators/NavigationSchema.js";
|
|
5
5
|
import type { ProtectedRoutesInput } from "../../config/validators/ProtectedRoutesSchema.js";
|
|
6
|
+
import type { ZudokuConfig } from "../../config/validators/validate.js";
|
|
6
7
|
import type { AuthenticationPlugin } from "../authentication/authentication.js";
|
|
7
8
|
import type { MdxComponentsType } from "../util/MdxComponents.js";
|
|
8
9
|
import type {
|
|
@@ -18,7 +19,8 @@ export type ZudokuPlugin =
|
|
|
18
19
|
| ApiIdentityPlugin
|
|
19
20
|
| SearchProviderPlugin
|
|
20
21
|
| EventConsumerPlugin
|
|
21
|
-
| AuthenticationPlugin
|
|
22
|
+
| AuthenticationPlugin
|
|
23
|
+
| TransformConfigPlugin;
|
|
22
24
|
|
|
23
25
|
export type { AuthenticationPlugin, RouteObject };
|
|
24
26
|
|
|
@@ -60,6 +62,23 @@ export type ProfileNavigationItem = {
|
|
|
60
62
|
icon?: LucideIcon;
|
|
61
63
|
};
|
|
62
64
|
|
|
65
|
+
export interface ConfigHookContext {
|
|
66
|
+
mode: typeof process.env.ZUDOKU_ENV;
|
|
67
|
+
rootDir: string;
|
|
68
|
+
configPath: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface TransformConfigContext {
|
|
72
|
+
config: ZudokuConfig;
|
|
73
|
+
merge: <T extends Partial<ZudokuConfig>>(partial: T) => ZudokuConfig & T;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface TransformConfigPlugin {
|
|
77
|
+
transformConfig?: (
|
|
78
|
+
context: TransformConfigContext,
|
|
79
|
+
) => ZudokuConfig | void | Promise<ZudokuConfig | void>;
|
|
80
|
+
}
|
|
81
|
+
|
|
63
82
|
export interface CommonPlugin {
|
|
64
83
|
initialize?: (
|
|
65
84
|
context: ZudokuContext,
|
|
@@ -110,3 +129,8 @@ export const isApiIdentityPlugin = (
|
|
|
110
129
|
obj: ZudokuPlugin,
|
|
111
130
|
): obj is ApiIdentityPlugin =>
|
|
112
131
|
"getIdentities" in obj && typeof obj.getIdentities === "function";
|
|
132
|
+
|
|
133
|
+
export const isTransformConfigPlugin = (
|
|
134
|
+
obj: ZudokuPlugin,
|
|
135
|
+
): obj is TransformConfigPlugin =>
|
|
136
|
+
"transformConfig" in obj && typeof obj.transformConfig === "function";
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { isPlainObject, mergeConfig } from "./transform-config.js";
|
|
3
|
+
|
|
4
|
+
describe("isPlainObject", () => {
|
|
5
|
+
test("returns true for plain objects", () => {
|
|
6
|
+
expect(isPlainObject({})).toBe(true);
|
|
7
|
+
expect(isPlainObject({ a: 1 })).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("returns false for arrays", () => {
|
|
11
|
+
expect(isPlainObject([])).toBe(false);
|
|
12
|
+
expect(isPlainObject([1, 2, 3])).toBe(false);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("returns false for null and undefined", () => {
|
|
16
|
+
expect(isPlainObject(null)).toBe(false);
|
|
17
|
+
expect(isPlainObject(undefined)).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("returns false for class instances", () => {
|
|
21
|
+
expect(isPlainObject(new Date())).toBe(false);
|
|
22
|
+
expect(isPlainObject(new Map())).toBe(false);
|
|
23
|
+
expect(isPlainObject(/regex/)).toBe(false);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe("mergeConfig", () => {
|
|
28
|
+
test("merges flat objects", () => {
|
|
29
|
+
const target = { a: 1, b: 2 };
|
|
30
|
+
const source = { b: 3, c: 4 };
|
|
31
|
+
expect(mergeConfig(target, source)).toEqual({ a: 1, b: 3, c: 4 });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("merges nested objects", () => {
|
|
35
|
+
const target = { nested: { a: 1, b: 2 } } as Record<string, unknown>;
|
|
36
|
+
const source = { nested: { b: 3, c: 4 } };
|
|
37
|
+
expect(mergeConfig(target, source)).toEqual({
|
|
38
|
+
nested: { a: 1, b: 3, c: 4 },
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("replaces arrays instead of merging", () => {
|
|
43
|
+
const target = { arr: [1, 2, 3] };
|
|
44
|
+
const source = { arr: [4, 5] };
|
|
45
|
+
expect(mergeConfig(target, source)).toEqual({ arr: [4, 5] });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("preserves React elements without deep cloning", () => {
|
|
49
|
+
const element = <div className="test">Hello</div>;
|
|
50
|
+
const target = { banner: { message: "old" } };
|
|
51
|
+
const source = { banner: { message: element } };
|
|
52
|
+
|
|
53
|
+
const result = mergeConfig(target, source);
|
|
54
|
+
|
|
55
|
+
// Should be the exact same reference, not a clone
|
|
56
|
+
expect(result.banner.message).toBe(element);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("does not clone React element children", () => {
|
|
60
|
+
const child = <strong>Bold</strong>;
|
|
61
|
+
const element = <div>{child} text</div>;
|
|
62
|
+
const target = { site: { banner: {} } };
|
|
63
|
+
const source = { site: { banner: { message: element } } };
|
|
64
|
+
|
|
65
|
+
const result = mergeConfig(target, source);
|
|
66
|
+
|
|
67
|
+
// The element should be identical (same reference)
|
|
68
|
+
expect(result.site.banner.message).toBe(element);
|
|
69
|
+
// Children should be preserved exactly
|
|
70
|
+
expect(result.site.banner.message.props.children).toBe(
|
|
71
|
+
element.props.children,
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("handles null and undefined values", () => {
|
|
76
|
+
const target = { a: 1, b: 2 };
|
|
77
|
+
const source = { a: null, c: undefined };
|
|
78
|
+
expect(mergeConfig(target, source)).toEqual({
|
|
79
|
+
a: null,
|
|
80
|
+
b: 2,
|
|
81
|
+
c: undefined,
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("replaces non-plain objects", () => {
|
|
86
|
+
const date = new Date("2024-01-01");
|
|
87
|
+
const target = { date: new Date("2020-01-01") };
|
|
88
|
+
const source = { date };
|
|
89
|
+
const result = mergeConfig(target, source);
|
|
90
|
+
expect(result.date).toBe(date);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("does not mutate target", () => {
|
|
94
|
+
const target = { a: 1, nested: { b: 2 } };
|
|
95
|
+
const source = { a: 2, nested: { c: 3 } };
|
|
96
|
+
mergeConfig(target, source);
|
|
97
|
+
expect(target).toEqual({ a: 1, nested: { b: 2 } });
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { isValidElement } from "react";
|
|
2
|
+
import type { ZudokuConfig } from "../../config/validators/validate.js";
|
|
3
|
+
import { isTransformConfigPlugin } from "./plugins.js";
|
|
4
|
+
|
|
5
|
+
export const isPlainObject = (
|
|
6
|
+
value: unknown,
|
|
7
|
+
): value is Record<string, unknown> =>
|
|
8
|
+
typeof value === "object" &&
|
|
9
|
+
value !== null &&
|
|
10
|
+
!Array.isArray(value) &&
|
|
11
|
+
Object.getPrototypeOf(value) === Object.prototype;
|
|
12
|
+
|
|
13
|
+
export const mergeConfig = <
|
|
14
|
+
T extends Record<string, unknown>,
|
|
15
|
+
S extends Record<string, unknown>,
|
|
16
|
+
>(
|
|
17
|
+
target: T,
|
|
18
|
+
source: S,
|
|
19
|
+
): T & S => {
|
|
20
|
+
const result = { ...target } as T & S;
|
|
21
|
+
|
|
22
|
+
for (const key of Object.keys(source) as (keyof S)[]) {
|
|
23
|
+
const sourceValue = source[key];
|
|
24
|
+
const targetValue = target[key as keyof T];
|
|
25
|
+
|
|
26
|
+
// Don't merge React elements, arrays, or non-plain objects - just replace
|
|
27
|
+
if (
|
|
28
|
+
isValidElement(sourceValue) ||
|
|
29
|
+
Array.isArray(sourceValue) ||
|
|
30
|
+
!isPlainObject(sourceValue)
|
|
31
|
+
) {
|
|
32
|
+
(result as Record<string, unknown>)[key as string] = sourceValue;
|
|
33
|
+
} else if (isPlainObject(targetValue)) {
|
|
34
|
+
(result as Record<string, unknown>)[key as string] = mergeConfig(
|
|
35
|
+
targetValue,
|
|
36
|
+
sourceValue,
|
|
37
|
+
);
|
|
38
|
+
} else {
|
|
39
|
+
(result as Record<string, unknown>)[key as string] = sourceValue;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const runPluginTransformConfig = async <T extends ZudokuConfig>(
|
|
47
|
+
config: T,
|
|
48
|
+
): Promise<T> => {
|
|
49
|
+
const plugins = config.plugins ?? [];
|
|
50
|
+
|
|
51
|
+
let result = config;
|
|
52
|
+
|
|
53
|
+
for (const plugin of plugins.filter(isTransformConfigPlugin)) {
|
|
54
|
+
const merge = <T extends Record<string, unknown>>(partial: T) =>
|
|
55
|
+
mergeConfig(result, partial);
|
|
56
|
+
|
|
57
|
+
const transformed = await plugin.transformConfig?.({
|
|
58
|
+
config: result,
|
|
59
|
+
merge,
|
|
60
|
+
});
|
|
61
|
+
if (!transformed) continue;
|
|
62
|
+
|
|
63
|
+
result = transformed as T;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return result;
|
|
67
|
+
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { SyntaxHighlight } from "zudoku/ui/SyntaxHighlight.js";
|
|
2
1
|
import { DeveloperHint } from "../components/DeveloperHint.js";
|
|
3
2
|
import { Heading } from "../components/Heading.js";
|
|
4
3
|
import { Typography } from "../components/Typography.js";
|
|
@@ -22,11 +21,7 @@ export function ErrorAlert({ error }: { error: unknown }) {
|
|
|
22
21
|
Error: {message}
|
|
23
22
|
{hint && <DeveloperHint className="mb-4">{hint}</DeveloperHint>}
|
|
24
23
|
{stringError && (
|
|
25
|
-
<
|
|
26
|
-
className="max-h-[400px] [&>pre]:p-4"
|
|
27
|
-
language="js"
|
|
28
|
-
code={stringError}
|
|
29
|
-
/>
|
|
24
|
+
<pre className="max-h-[400px] [&>pre]:p-4">{stringError}</pre>
|
|
30
25
|
)}
|
|
31
26
|
</Typography>
|
|
32
27
|
);
|
package/src/lib/hooks/index.ts
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export const useEvent = /*@__PURE__*/ useEventImport;
|
|
10
|
-
export const useTheme = /*@__PURE__*/ useThemeImport;
|
|
11
|
-
export const useExposedProps = /*@__PURE__*/ useExposedPropsImport;
|
|
12
|
-
export const useMDXComponents = /*@__PURE__*/ useMDXComponentsImport;
|
|
13
|
-
export const useAuth = /*@__PURE__*/ useAuthImport;
|
|
14
|
-
export const useZudoku = /*@__PURE__*/ useZudokuImport;
|
|
15
|
-
export const useCache = /*@__PURE__*/ useCacheImport;
|
|
16
|
-
export { CACHE_KEYS };
|
|
1
|
+
export { useMDXComponents } from "@mdx-js/react";
|
|
2
|
+
export { useTheme } from "next-themes";
|
|
3
|
+
export { useAuth } from "../authentication/hook.js";
|
|
4
|
+
export { CACHE_KEYS, useCache } from "../components/cache.js";
|
|
5
|
+
export { useZudoku } from "../components/context/ZudokuContext.js";
|
|
6
|
+
export { useExposedProps } from "../util/useExposedProps.js";
|
|
7
|
+
export { useEvent } from "./useEvent.js";
|
|
@@ -13,7 +13,7 @@ import { useEvent } from "./useEvent.js";
|
|
|
13
13
|
|
|
14
14
|
const createTestContext = () => {
|
|
15
15
|
const queryClient = new QueryClient();
|
|
16
|
-
const context = new ZudokuContext({}, queryClient);
|
|
16
|
+
const context = new ZudokuContext({}, queryClient, {});
|
|
17
17
|
const wrapper = ({ children }: PropsWithChildren) => (
|
|
18
18
|
<QueryClientProvider client={queryClient}>
|
|
19
19
|
<ZudokuProvider context={context}>{children}</ZudokuProvider>
|
|
@@ -156,16 +156,20 @@ describe("handleCircularRefs", () => {
|
|
|
156
156
|
});
|
|
157
157
|
});
|
|
158
158
|
|
|
159
|
-
it("should
|
|
159
|
+
it("should handle shared object instances with __$ref without marking circular", () => {
|
|
160
160
|
const shared = { __$ref: "#/components/schemas/Foo", type: "string" };
|
|
161
161
|
const obj = { a: shared, b: shared };
|
|
162
162
|
const result = handleCircularRefs(obj);
|
|
163
163
|
|
|
164
|
+
// Both should return the cached result, not mark as circular
|
|
164
165
|
expect(result.a).toEqual({
|
|
165
166
|
__$ref: "#/components/schemas/Foo",
|
|
166
167
|
type: "string",
|
|
167
168
|
});
|
|
168
|
-
expect(result.b).
|
|
169
|
+
expect(result.b).toEqual({
|
|
170
|
+
__$ref: "#/components/schemas/Foo",
|
|
171
|
+
type: "string",
|
|
172
|
+
});
|
|
169
173
|
});
|
|
170
174
|
|
|
171
175
|
it("should mark circular ref with property name from path", () => {
|
|
@@ -183,4 +187,35 @@ describe("handleCircularRefs", () => {
|
|
|
183
187
|
|
|
184
188
|
expect(result.properties.child.properties.back).toContain(CIRCULAR_REF);
|
|
185
189
|
});
|
|
190
|
+
|
|
191
|
+
// Exact reproduction of #1869 - shared object instances with __$ref
|
|
192
|
+
it("should NOT mark shared object instances with __$ref as circular (issue #1869)", () => {
|
|
193
|
+
// When dereferencing, the SAME object instance is returned for all refs to the same schema
|
|
194
|
+
const timestampSchema = {
|
|
195
|
+
__$ref: "#/components/schemas/timestamp",
|
|
196
|
+
type: "string",
|
|
197
|
+
format: "date-time",
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// Both created_at and updated_at point to the SAME object instance
|
|
201
|
+
const obj = {
|
|
202
|
+
type: "object",
|
|
203
|
+
properties: {
|
|
204
|
+
created_at: timestampSchema,
|
|
205
|
+
updated_at: timestampSchema,
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const result = handleCircularRefs(obj);
|
|
210
|
+
|
|
211
|
+
// The first one should be fully expanded
|
|
212
|
+
expect(result.properties.created_at).toEqual({
|
|
213
|
+
__$ref: "#/components/schemas/timestamp",
|
|
214
|
+
type: "string",
|
|
215
|
+
format: "date-time",
|
|
216
|
+
});
|
|
217
|
+
// The second one should ALSO be fully expanded (not marked as circular)
|
|
218
|
+
expect(typeof result.properties.updated_at).toBe("object");
|
|
219
|
+
expect(result.properties.updated_at).not.toContain(CIRCULAR_REF);
|
|
220
|
+
});
|
|
186
221
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { GraphQLScalarType } from "graphql/index.js";
|
|
2
2
|
import { GraphQLJSON } from "graphql-type-json";
|
|
3
|
-
import type { RecordAny } from "../../util/traverse.js";
|
|
4
3
|
|
|
5
4
|
export const CIRCULAR_REF = "$[Circular Reference]";
|
|
6
5
|
export const SCHEMA_REF_PREFIX = "$ref:";
|
|
@@ -17,73 +16,48 @@ const OPENAPI_PROPS = new Set([
|
|
|
17
16
|
export const handleCircularRefs = (
|
|
18
17
|
// biome-ignore lint/suspicious/noExplicitAny: Allow any type
|
|
19
18
|
obj: any,
|
|
20
|
-
|
|
19
|
+
currentPath = new WeakSet(),
|
|
21
20
|
refs = new WeakMap(),
|
|
22
21
|
path: string[] = [],
|
|
23
|
-
|
|
22
|
+
currentRefPaths = new Set<string>(),
|
|
24
23
|
// biome-ignore lint/suspicious/noExplicitAny: Allow any type
|
|
25
24
|
): any => {
|
|
26
25
|
if (obj === null || typeof obj !== "object") return obj;
|
|
27
26
|
|
|
28
27
|
const refPath = obj.__$ref;
|
|
28
|
+
const isCircular =
|
|
29
|
+
currentPath.has(obj) ||
|
|
30
|
+
(typeof refPath === "string" && currentRefPaths.has(refPath));
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// instead of the full data to avoid JSON.stringify serializing duplicates
|
|
33
|
-
if (typeof refPath === "string" && seenRefPaths.has(refPath)) {
|
|
34
|
-
return SCHEMA_REF_PREFIX + refPath;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (visited.has(obj)) {
|
|
38
|
-
const cached = refs.get(obj);
|
|
39
|
-
if (cached) {
|
|
40
|
-
return typeof refPath === "string"
|
|
41
|
-
? // If already processed, return ref marker to avoid duplicate serialization
|
|
42
|
-
SCHEMA_REF_PREFIX + refPath
|
|
43
|
-
: cached;
|
|
44
|
-
}
|
|
32
|
+
if (isCircular) {
|
|
33
|
+
if (typeof refPath === "string") return SCHEMA_REF_PREFIX + refPath;
|
|
45
34
|
const circularProp = path.find((p) => !OPENAPI_PROPS.has(p)) || path[0];
|
|
46
|
-
|
|
47
35
|
return [CIRCULAR_REF, circularProp].filter(Boolean).join(":");
|
|
48
36
|
}
|
|
49
37
|
|
|
50
|
-
|
|
38
|
+
if (refs.has(obj)) return refs.get(obj);
|
|
51
39
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (typeof refPath === "string") {
|
|
55
|
-
seenRefPaths.add(refPath);
|
|
56
|
-
}
|
|
40
|
+
currentPath.add(obj);
|
|
41
|
+
if (typeof refPath === "string") currentRefPaths.add(refPath);
|
|
57
42
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
[...path, index.toString()],
|
|
66
|
-
seenRefPaths,
|
|
67
|
-
),
|
|
43
|
+
const recurse = (value: unknown, key: string) =>
|
|
44
|
+
handleCircularRefs(
|
|
45
|
+
value,
|
|
46
|
+
currentPath,
|
|
47
|
+
refs,
|
|
48
|
+
[...path, key],
|
|
49
|
+
currentRefPaths,
|
|
68
50
|
);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
visited,
|
|
75
|
-
refs,
|
|
76
|
-
[...path, key],
|
|
77
|
-
seenRefPaths,
|
|
51
|
+
|
|
52
|
+
const result = Array.isArray(obj)
|
|
53
|
+
? obj.map((item, i) => recurse(item, i.toString()))
|
|
54
|
+
: Object.fromEntries(
|
|
55
|
+
Object.entries(obj).map(([k, v]) => [k, recurse(v, k)]),
|
|
78
56
|
);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
refs.set(obj, result);
|
|
82
57
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
58
|
+
refs.set(obj, result);
|
|
59
|
+
currentPath.delete(obj);
|
|
60
|
+
if (typeof refPath === "string") currentRefPaths.delete(refPath);
|
|
87
61
|
|
|
88
62
|
return result;
|
|
89
63
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { GraphQLError } from "graphql/error/index.js";
|
|
2
2
|
import { OpenAPIV3, type OpenAPIV3_1 } from "openapi-types";
|
|
3
|
-
import {
|
|
3
|
+
import { flattenAllOf } from "../../util/flattenAllOf.js";
|
|
4
|
+
import { traverse } from "../../util/traverse.js";
|
|
4
5
|
import { dereference, type JSONSchema } from "./dereference/index.js";
|
|
5
6
|
import { upgradeSchema } from "./upgrade/index.js";
|
|
6
7
|
|
|
@@ -104,11 +105,21 @@ export const validate = async (schemaInput: unknown) => {
|
|
|
104
105
|
const dereferenced = await dereference(schema);
|
|
105
106
|
const upgraded = upgradeSchema(dereferenced);
|
|
106
107
|
|
|
107
|
-
const flattened =
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
const flattened = traverse(upgraded, (spec) => {
|
|
109
|
+
if (!spec || typeof spec !== "object" || Array.isArray(spec)) {
|
|
110
|
+
return spec;
|
|
111
|
+
}
|
|
112
|
+
const isSchemaObject =
|
|
113
|
+
"type" in spec ||
|
|
114
|
+
"properties" in spec ||
|
|
115
|
+
"allOf" in spec ||
|
|
116
|
+
"anyOf" in spec ||
|
|
117
|
+
"oneOf" in spec;
|
|
118
|
+
|
|
119
|
+
if (!isSchemaObject) return spec;
|
|
120
|
+
|
|
121
|
+
return flattenAllOf(spec) as typeof spec;
|
|
122
|
+
}) as OpenAPIDocument;
|
|
112
123
|
|
|
113
124
|
return flattened;
|
|
114
125
|
};
|
|
@@ -19,13 +19,21 @@ export const OasProvider = ({
|
|
|
19
19
|
client: GraphQLClient;
|
|
20
20
|
}) => {
|
|
21
21
|
const value = useMemo(() => {
|
|
22
|
-
const {
|
|
22
|
+
const {
|
|
23
|
+
versions: availableVersions,
|
|
24
|
+
labels,
|
|
25
|
+
downloadUrls,
|
|
26
|
+
} = getVersionMetadata(config);
|
|
23
27
|
const currentVersion = version ?? availableVersions.at(0);
|
|
24
28
|
|
|
25
29
|
const versionLinks = Object.fromEntries(
|
|
26
30
|
availableVersions.map((id) => [
|
|
27
31
|
id,
|
|
28
|
-
{
|
|
32
|
+
{
|
|
33
|
+
path: joinUrl(basePath, id),
|
|
34
|
+
label: labels[id] ?? id,
|
|
35
|
+
downloadUrl: downloadUrls[id],
|
|
36
|
+
},
|
|
29
37
|
]),
|
|
30
38
|
);
|
|
31
39
|
|