@redocly/realm 0.138.0-next.6 → 0.138.0-next.9
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/CHANGELOG.md +38 -0
- package/dist/client/App.js +1 -1
- package/dist/client/app/ReviewComments/ReviewComments.js +2 -2
- package/dist/client/app/l10n/hooks/useTranslationKeys.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/openapi-example.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/replay-openapi.js +1 -1
- package/dist/client/app/markdoc/hooks/use-store.d.ts +2 -3
- package/dist/client/app/pages/DevLogin/DevLoginForm.js +1 -1
- package/dist/client/app/pages/Invite/Invite.js +1 -1
- package/dist/client/app/search/useSearch.js +1 -1
- package/dist/client/runtime/loader.js +1 -1
- package/dist/constants/l10n/langs/ar.js +1 -1
- package/dist/constants/l10n/langs/de.js +1 -1
- package/dist/constants/l10n/langs/en.js +1 -1
- package/dist/constants/l10n/langs/es.js +1 -1
- package/dist/constants/l10n/langs/fr.js +1 -1
- package/dist/constants/l10n/langs/hi.js +1 -1
- package/dist/constants/l10n/langs/it.js +1 -1
- package/dist/constants/l10n/langs/ja.js +1 -1
- package/dist/constants/l10n/langs/ko.js +1 -1
- package/dist/constants/l10n/langs/pl.js +1 -1
- package/dist/constants/l10n/langs/pt-BR.js +1 -1
- package/dist/constants/l10n/langs/pt.js +1 -1
- package/dist/constants/l10n/langs/ru.js +1 -1
- package/dist/constants/l10n/langs/uk.js +1 -1
- package/dist/constants/l10n/langs/zh.js +1 -1
- package/dist/server/config/env-config.d.ts +0 -2
- package/dist/server/config/env-schema.d.ts +0 -5
- package/dist/server/config/env-schemas/feature-flags.d.ts +0 -4
- package/dist/server/config/env-schemas/feature-flags.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/App.js +5 -5
- package/dist/server/plugins/dev-onboarding/template/components/ApiKeyOverview.js +3 -3
- package/dist/server/plugins/markdown/search/get-ai-search-documents.js +1 -1
- package/dist/server/plugins/mcp/codemode/capabilities/fetch/index.js +3 -3
- package/dist/server/plugins/mcp/codemode/tools/manage-credentials.js +2 -2
- package/dist/server/plugins/mcp/custom-tools/scan-custom-tools.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tool-schemas.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/core/whoami.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/credential-validation.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/elicitation.d.ts +1 -7
- package/dist/server/plugins/mcp/gateway-mcp/elicitation.js +5 -6
- package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-bridge.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/fetch/ssrf-guard.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/ip-key.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/upstream-auth-scheme.d.ts +0 -6
- package/dist/server/plugins/mcp/gateway-mcp/upstream-auth-scheme.js +2 -2
- package/dist/server/plugins/mcp/handlers/mcp-credentials-handler.js +1 -1
- package/dist/server/plugins/mcp/servers/mcp-server.js +2 -4
- package/dist/server/plugins/scorecard-classic/template/Grid/GridHeader.js +21 -8
- package/dist/server/plugins/scorecard-classic/template/ScorecardDashboard.js +3 -3
- package/dist/server/plugins/scorecard-classic/template/index.styles.d.ts +1 -0
- package/dist/server/plugins/scorecard-classic/template/index.styles.js +15 -5
- package/dist/server/plugins/scorecard-classic/template/views.js +1 -1
- package/dist/server/plugins/sidebars/tabs.js +1 -1
- package/dist/server/web-server/routes/cors-proxy.js +2 -2
- package/dist/server/web-server/routes/mcp-routes/mcp-oauth.js +1 -1
- package/package.json +5 -5
- /package/dist/server/{plugins/mcp/gateway-mcp/fetch → web-server/utils}/ssrf-blocklist.d.ts +0 -0
- /package/dist/server/{plugins/mcp/gateway-mcp/fetch → web-server/utils}/ssrf-blocklist.js +0 -0
- /package/dist/server/{plugins/mcp/gateway-mcp/fetch → web-server/utils}/ssrf-safe-lookup.d.ts +0 -0
- /package/dist/server/{plugins/mcp/gateway-mcp/fetch → web-server/utils}/ssrf-safe-lookup.js +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import r from"react";import t from"styled-components";import{flexRender as g}from"@tanstack/react-table";const w=({headerGroups:e,gridTemplateColumns:i,groupHeaderTitle:l})=>r.createElement(p,null,l&&r.createElement(u,{gridTemplateColumns:i,role:"row"},r.createElement(c,{gridColumn:"1",role:"cell"}),r.createElement(c,{gridColumn:"2 / -1",role:"cell"},r.createElement("span",null,l))),e.map((a,m)=>r.createElement(b,{key:`header-group-${m}`,gridTemplateColumns:i,role:"row"},a.headers.map((o,s)=>{const d=g(o.column.columnDef.header,o.getContext()),n=o.column.getIsSorted();return r.createElement(x,{key:o.id,gridColumn:s+1,$sortable:o.column.getCanSort(),role:"columnheader","aria-sort":n==="asc"?"ascending":n==="desc"?"descending":void 0},o.column.getCanSort()?r.createElement(f,{type:"button",onClick:o.column.getToggleSortingHandler()},d,r.createElement(h,{"aria-hidden":"true"},n?n==="desc"?"\u25BE":"\u25B4":"\u21D5")):d)})))),p=t.div`
|
|
2
2
|
border: 1px solid var(--border-color-primary);
|
|
3
3
|
min-width: 775px;
|
|
4
|
-
`,
|
|
4
|
+
`,u=t.div`
|
|
5
5
|
display: grid;
|
|
6
6
|
grid-template-columns: ${e=>e.gridTemplateColumns};
|
|
7
7
|
width: 100%;
|
|
8
8
|
min-width: 775px;
|
|
9
|
-
`,
|
|
9
|
+
`,c=t.div`
|
|
10
10
|
padding: 5px 15px;
|
|
11
11
|
background-color: var(--bg-color-raised);
|
|
12
12
|
font-weight: bold;
|
|
@@ -21,12 +21,12 @@ import o from"react";import l from"styled-components";import{flexRender as a}fro
|
|
|
21
21
|
&:nth-child(2) {
|
|
22
22
|
border-bottom: 1px solid var(--border-color-primary);
|
|
23
23
|
}
|
|
24
|
-
`,
|
|
24
|
+
`,b=t.div`
|
|
25
25
|
display: grid;
|
|
26
26
|
grid-template-columns: ${e=>e.gridTemplateColumns};
|
|
27
27
|
width: 100%;
|
|
28
|
-
`,
|
|
29
|
-
padding: 5px 15px;
|
|
28
|
+
`,x=t.div`
|
|
29
|
+
padding: ${({$sortable:e})=>e?"0":"5px 15px"};
|
|
30
30
|
background-color: var(--bg-color-raised);
|
|
31
31
|
font-weight: bold;
|
|
32
32
|
color: var(--text-color-secondary);
|
|
@@ -37,6 +37,19 @@ import o from"react";import l from"styled-components";import{flexRender as a}fro
|
|
|
37
37
|
&:first-child {
|
|
38
38
|
justify-content: flex-start;
|
|
39
39
|
}
|
|
40
|
-
`,
|
|
40
|
+
`,f=t.button`
|
|
41
|
+
display: block;
|
|
42
|
+
width: 100%;
|
|
43
|
+
height: 100%;
|
|
44
|
+
box-sizing: border-box;
|
|
45
|
+
margin: 0;
|
|
46
|
+
padding: 5px 15px;
|
|
47
|
+
border: 0;
|
|
48
|
+
background: none;
|
|
49
|
+
font: inherit;
|
|
50
|
+
color: inherit;
|
|
51
|
+
text-align: inherit;
|
|
52
|
+
cursor: pointer;
|
|
53
|
+
`,h=t.span`
|
|
41
54
|
font-family: monospace;
|
|
42
|
-
`;export{
|
|
55
|
+
`;export{w as GridHeader};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import*as e from"react";import{useState as
|
|
1
|
+
import*as e from"react";import{useState as F}from"react";import N from"styled-components";import{HighlightContext as x}from"@redocly/portal-legacy-ui";import{breakpoints as B,getScorecardColorVariable as L}from"@redocly/theme/core/utils";import{FilterContent as O}from"@redocly/theme/components/Filter/FilterContent";import{FilterPopover as D}from"@redocly/theme/components/Filter/FilterPopover";import{CatalogPageWrapper as I,CatalogPageContent as M,CatalogTitle as R,CatalogDescription as W,CatalogPageDescriptionWrapper as j}from"@redocly/theme/components/CatalogClassic/CatalogClassic";import{useModalScrollLock as $}from"@redocly/theme/core/hooks";import{CatalogClassicActions as H}from"@redocly/theme/components/CatalogClassic/CatalogClassicActions";import{Sidebar as _}from"@redocly/theme/components/Sidebar/Sidebar";import{CardValue as p,CardsWrapper as q,ModeSwitcherTab as z,ModeSwitcherTabs as G,CardRow as J,ProblemsByStatusCardBody as K,ScoreCardStatusCardBody as Q,TableWrap as d,ContentWithDotWrap as U,ScorecardCard as h,ScorecardCardTitle as S,LinkButton as X}from"./index.styles";import{NON_CONFORMANT as T}from"./utils";import{ApiLevel as Y,StatusDistribution as Z}from"./components";import{ApiListView as ee,TeamsView as te,RulesView as re}from"./views";import{useData as ae}from"./useData";function Se(y){const{apis:v,summary:r,problemsByRule:A,nonConformantByTeam:P,sortedStatsByTeam:m,filterTerm:a,setFilterTerm:u,filters:l,scorecardInfo:f,hasTeam:i,hasPublishedAt:V,catalogConfig:E,teamLabel:n,teamProperty:w}=ae(y),b={apis:"APIs",rules:"Rules",...i&&{teams:n}},[C,g]=e.useState(!1),[o,s]=F("apis");return $(C),e.createElement(x.Provider,{value:[a]},e.createElement(I,null,e.createElement(le,{menu:e.createElement(O,{setFilterTerm:u,filters:l,filterTerm:a,filterValuesCasing:E.filterValuesCasing})}),C&&e.createElement(D,{onClose:()=>g(!1),setFilterTerm:u,filterTerm:a,filters:l,filterValuesCasing:E.filterValuesCasing}),e.createElement(H,{onOpenFilter:()=>g(!0),filters:l,filterTerm:a}),e.createElement(M,{id:"scorecard-page-content"},e.createElement(j,null,e.createElement(R,null,"API design scorecard"),e.createElement(W,null,"See the health of your API designs.")),e.createElement(q,null,e.createElement(h,null,e.createElement(S,null,"Scorecard status"),e.createElement(Q,null,e.createElement("div",null,e.createElement(p,null,r.total),e.createElement("br",null),"APIs"),e.createElement("div",null,Object.entries(r.levels).map(([t,c],k)=>e.createElement(U,{key:t},e.createElement(p,{style:{width:"40px",textAlign:"right"}},c),e.createElement(Y,{level:t,colorVariable:L(k,Object.values(r.levels).length)}))))),e.createElement(Z,{summary:r})),i&&e.createElement(h,null,e.createElement(S,null,`${T} APIs by ${n}`),e.createElement(K,null,P.slice(0,4).map((t,c)=>e.createElement(J,{key:c},e.createElement("span",{style:{flex:1,textAlign:"left"}},t.team),e.createElement(p,null,t.levels[T].apis))),m.length>4&&e.createElement(X,{onClick:()=>s("teams")},"See all (",m.length,")")))),e.createElement(G,{"data-component-name":"Scorecard/ModeSwitcherTabs"},Object.keys(b).map(t=>t&&e.createElement(z,{"data-component-name":"Scorecard/ModeSwitcherTab",key:t,isActive:o===t,onClick:()=>s(t)},b[t]))),e.createElement(d,{isActive:o==="apis"},e.createElement(ee,{data:v,levelNames:f.levelNames,hasTeam:i,hasPublishedAt:V,teamLabel:n})),e.createElement(d,{isActive:o==="rules"},e.createElement(re,{data:A})),i&&e.createElement(d,{isActive:o==="teams"},e.createElement(te,{data:m,levelNames:f.levelNames,filters:l,setActiveTab:s,teamLabel:n,teamProperty:w})))))}const le=N(_)`
|
|
2
2
|
display: none;
|
|
3
3
|
|
|
4
|
-
@media screen and (min-width: ${
|
|
4
|
+
@media screen and (min-width: ${B.medium}) {
|
|
5
5
|
display: flex;
|
|
6
6
|
}
|
|
7
7
|
--menu-container-padding-top: 0;
|
|
8
|
-
`;export{
|
|
8
|
+
`;export{Se as ScorecardDashboard};
|
|
@@ -64,4 +64,5 @@ export declare const LevelComplianceCardTitle: import("styled-components/dist/ty
|
|
|
64
64
|
}, never>>> & string;
|
|
65
65
|
export declare const ContentWithDotWrap: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never> & Partial<Pick<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>>> & string;
|
|
66
66
|
export declare const EmojiWrap: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, never> & Partial<Pick<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, never>>> & string;
|
|
67
|
+
export declare const LinkButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, keyof import("react").ButtonHTMLAttributes<HTMLButtonElement> | keyof import("react").ClassAttributes<HTMLButtonElement>> & import("react").ClassAttributes<HTMLButtonElement> & import("react").ButtonHTMLAttributes<HTMLButtonElement>, "about" | "accessKey" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-braillelabel" | "aria-brailleroledescription" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colindextext" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-description" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowindextext" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "autoCapitalize" | "autoCorrect" | "autoFocus" | "autoSave" | "children" | "className" | "color" | "content" | "contentEditable" | "contextMenu" | "dangerouslySetInnerHTML" | "datatype" | "defaultChecked" | "defaultValue" | "dir" | "disabled" | "draggable" | "enterKeyHint" | "exportparts" | "form" | "formAction" | "formEncType" | "formMethod" | "formNoValidate" | "formTarget" | "hidden" | "id" | "inert" | "inlist" | "inputMode" | "is" | "itemID" | "itemProp" | "itemRef" | "itemScope" | "itemType" | "key" | "lang" | "name" | "nonce" | "onAbort" | "onAbortCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAuxClick" | "onAuxClickCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onBeforeToggle" | "onBlur" | "onBlurCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onChange" | "onChangeCapture" | "onClick" | "onClickCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onContextMenu" | "onContextMenuCapture" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onError" | "onErrorCapture" | "onFocus" | "onFocusCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onInput" | "onInputCapture" | "onInvalid" | "onInvalidCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onLoad" | "onLoadCapture" | "onLoadStart" | "onLoadStartCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onPaste" | "onPasteCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerEnter" | "onPointerLeave" | "onPointerMove" | "onPointerMoveCapture" | "onPointerOut" | "onPointerOutCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerUp" | "onPointerUpCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onReset" | "onResetCapture" | "onScroll" | "onScrollCapture" | "onScrollEnd" | "onScrollEndCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onSelect" | "onSelectCapture" | "onStalled" | "onStalledCapture" | "onSubmit" | "onSubmitCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onToggle" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onTransitionCancel" | "onTransitionCancelCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "onTransitionRun" | "onTransitionRunCapture" | "onTransitionStart" | "onTransitionStartCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onWheel" | "onWheelCapture" | "part" | "popover" | "popoverTarget" | "popoverTargetAction" | "prefix" | "property" | "radioGroup" | "ref" | "rel" | "resource" | "results" | "rev" | "role" | "security" | "slot" | "spellCheck" | "style" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "tabIndex" | "title" | "translate" | "type" | "typeof" | "unselectable" | "value" | "vocab"> & Partial<Pick<import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, keyof import("react").ButtonHTMLAttributes<HTMLButtonElement> | keyof import("react").ClassAttributes<HTMLButtonElement>> & import("react").ClassAttributes<HTMLButtonElement> & import("react").ButtonHTMLAttributes<HTMLButtonElement>, "about" | "accessKey" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-braillelabel" | "aria-brailleroledescription" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colindextext" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-description" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowindextext" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "autoCapitalize" | "autoCorrect" | "autoFocus" | "autoSave" | "children" | "className" | "color" | "content" | "contentEditable" | "contextMenu" | "dangerouslySetInnerHTML" | "datatype" | "defaultChecked" | "defaultValue" | "dir" | "disabled" | "draggable" | "enterKeyHint" | "exportparts" | "form" | "formAction" | "formEncType" | "formMethod" | "formNoValidate" | "formTarget" | "hidden" | "id" | "inert" | "inlist" | "inputMode" | "is" | "itemID" | "itemProp" | "itemRef" | "itemScope" | "itemType" | "key" | "lang" | "name" | "nonce" | "onAbort" | "onAbortCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAuxClick" | "onAuxClickCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onBeforeToggle" | "onBlur" | "onBlurCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onChange" | "onChangeCapture" | "onClick" | "onClickCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onContextMenu" | "onContextMenuCapture" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onError" | "onErrorCapture" | "onFocus" | "onFocusCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onInput" | "onInputCapture" | "onInvalid" | "onInvalidCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onLoad" | "onLoadCapture" | "onLoadStart" | "onLoadStartCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onPaste" | "onPasteCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerEnter" | "onPointerLeave" | "onPointerMove" | "onPointerMoveCapture" | "onPointerOut" | "onPointerOutCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerUp" | "onPointerUpCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onReset" | "onResetCapture" | "onScroll" | "onScrollCapture" | "onScrollEnd" | "onScrollEndCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onSelect" | "onSelectCapture" | "onStalled" | "onStalledCapture" | "onSubmit" | "onSubmitCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onToggle" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onTransitionCancel" | "onTransitionCancelCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "onTransitionRun" | "onTransitionRunCapture" | "onTransitionStart" | "onTransitionStartCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onWheel" | "onWheelCapture" | "part" | "popover" | "popoverTarget" | "popoverTargetAction" | "prefix" | "property" | "radioGroup" | "ref" | "rel" | "resource" | "results" | "rev" | "role" | "security" | "slot" | "spellCheck" | "style" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "tabIndex" | "title" | "translate" | "type" | "typeof" | "unselectable" | "value" | "vocab">>> & string;
|
|
67
68
|
//# sourceMappingURL=index.styles.d.ts.map
|
|
@@ -55,7 +55,7 @@ import r from"styled-components";import{Popup as t}from"reactjs-popup";import{Bu
|
|
|
55
55
|
border: 1px solid var(--border-color-primary);
|
|
56
56
|
background-color: var(${o=>o.colorVariable});
|
|
57
57
|
margin-top: 1px;
|
|
58
|
-
`,
|
|
58
|
+
`,u=r.p`
|
|
59
59
|
margin: 16px 0 32px 0;
|
|
60
60
|
font-size: 16px;
|
|
61
61
|
color: var(--text-color-secondary);
|
|
@@ -88,7 +88,7 @@ import r from"styled-components";import{Popup as t}from"reactjs-popup";import{Bu
|
|
|
88
88
|
@media screen and (max-width: 767px) {
|
|
89
89
|
flex-direction: column;
|
|
90
90
|
}
|
|
91
|
-
`,
|
|
91
|
+
`,v=r.input`
|
|
92
92
|
border: var(--filter-input-border);
|
|
93
93
|
min-width: var(--filter-input-min-width);
|
|
94
94
|
width: 100%;
|
|
@@ -197,12 +197,12 @@ import r from"styled-components";import{Popup as t}from"reactjs-popup";import{Bu
|
|
|
197
197
|
text-align: center;
|
|
198
198
|
display: block;
|
|
199
199
|
}
|
|
200
|
-
`,
|
|
200
|
+
`,B=r(i)`
|
|
201
201
|
display: flex;
|
|
202
202
|
flex-direction: row;
|
|
203
203
|
justify-content: space-between;
|
|
204
204
|
gap: 10px;
|
|
205
|
-
`,
|
|
205
|
+
`,E=r.div`
|
|
206
206
|
display: flex;
|
|
207
207
|
flex-direction: row;
|
|
208
208
|
align-items: center;
|
|
@@ -217,4 +217,14 @@ import r from"styled-components";import{Popup as t}from"reactjs-popup";import{Bu
|
|
|
217
217
|
'Noto Color Emoji',
|
|
218
218
|
'EmojiOne Color';
|
|
219
219
|
vertical-align: middle;
|
|
220
|
-
|
|
220
|
+
`,D=r.button.attrs({type:"button"})`
|
|
221
|
+
padding: 0;
|
|
222
|
+
margin: 0;
|
|
223
|
+
border: 0;
|
|
224
|
+
background: none;
|
|
225
|
+
font: inherit;
|
|
226
|
+
font-weight: var(--link-font-weight);
|
|
227
|
+
color: var(--link-color-primary);
|
|
228
|
+
text-align: left;
|
|
229
|
+
cursor: pointer;
|
|
230
|
+
`;export{C as AppPageWrapper,f as CardRow,a as CardValue,b as CardsWrapper,u as CatalogDescription,n as CatalogPageWrapper,W as Code,E as ContentWithDotWrap,m as Dot,M as EmojiWrap,I as FilePreview,g as GaugeNumber,P as GroupTh,T as InstanceControlsWrap,$ as InstanceMessage,z as InstanceReference,j as InstanceWrap,B as LevelComplianceCardTitle,D as LinkButton,y as ModeSwitcherTab,h as ModeSwitcherTabs,x as ProblemsByStatusCardBody,s as ScoreCardStatusCardBody,c as ScorecardCard,i as ScorecardCardTitle,S as SortIcon,v as StyledInput,k as StyledPopup,w as TableWrap};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"react";import{useCallback as H,useMemo as b,useState as G,useEffect as _}from"react";import{useLocation as T}from"react-router";import{Flex as U}from"@redocly/portal-legacy-ui";import{Button as x}from"@redocly/theme/components/Button/Button";import{AddIcon as I}from"@redocly/theme/icons/AddIcon/AddIcon";import{SubtractIcon as
|
|
1
|
+
import*as e from"react";import{useCallback as H,useMemo as b,useState as G,useEffect as _}from"react";import{useLocation as T}from"react-router";import{Flex as U}from"@redocly/portal-legacy-ui";import{Button as x}from"@redocly/theme/components/Button/Button";import{AddIcon as I}from"@redocly/theme/icons/AddIcon/AddIcon";import{SubtractIcon as K}from"@redocly/theme/icons/SubtractIcon/SubtractIcon";import{ArrowLeftIcon as D}from"@redocly/theme/icons/ArrowLeftIcon/ArrowLeftIcon";import{getScorecardColorVariable as z,withoutPathPrefix as O}from"@redocly/theme/core/utils";import{Link as w}from"../../../../client/app/Link";import J from"../../../../client/app/CircularProgress";import{isDefined as R}from"../../../../utils/guards/is-defined.js";import{removeLeadingSlash as N}from"../../../../utils/url/remove-leading-slash.js";import{StatusByLevelWidget as Q}from"./StatusByLevelWidget";import{OTHER_TEAMS_LABEL as X,NON_CONFORMANT as Y}from"./utils";import{LevelIndicator as Z}from"./LevelIndicator";import{AppPageWrapper as V,CardValue as v,CardsWrapper as j,CatalogDescription as ee,ModeSwitcherTab as te,ModeSwitcherTabs as re,CardRow as B,ProblemsByStatusCardBody as W,ScoreCardStatusCardBody as F,TableWrap as ne,LevelComplianceCardTitle as le,ContentWithDotWrap as C,ScorecardCard as A,ScorecardCardTitle as L,LinkButton as ae}from"./index.styles";import{ApiLevel as M,InstanceStatus as k,LevelStatus as $,RuleStatus as q}from"./components";import{Grid as S}from"./Grid/Grid";import{useDetailedScorecard as se}from"./useDetailedScorecard";import{Gauge as ce,GaugeValue as oe}from"./Gauge";function Ie(p){const u=T(),{data:d,levelNames:i,hasPublishedAt:n,hasTeam:s,teamLabel:l}=p,f=H((a,o,E)=>{const g=a.getValue(E)?new Date(a.getValue(E)).getTime():0,t=o.getValue(E)?new Date(o.getValue(E)).getTime():0;return g>t?1:-1},[]),m=b(()=>[{id:"title",header:"API",accessorKey:"api.title",size:200,cell:({getValue:a,row:o})=>e.createElement(w,{to:O(u.pathname)+"/apis/"+encodeURIComponent(N(o.original.api.link))},a())},n?{header:"Published at",accessorKey:"api.publishedAt",sortingFn:f,cell:({getValue:a})=>{const o=a();return o?new Date(o).toLocaleDateString():"-"}}:void 0,s?{header:l,accessorKey:"api.team"}:void 0,{id:"level",header:"Level",accessorKey:"scorecard.scorecardLevel",cell:({getValue:a,row:o})=>e.createElement(M,{level:a(),colorVariable:z(o.original?.scorecard.scorecardLevelIdx,i.length+1)})},...i.map(a=>({id:a,header:`${a}`,accessorKey:"scorecard.levels",cell:({getValue:o,row:E})=>{const g=o(),t=E.original.api.link;return g?.[a]?e.createElement(Z,{key:t,uniqueErrors:g[a].uniqueErrors,uniqueWarnings:g[a].uniqueWarnings,id:t}):null}}))].filter(R),[f,i,u.pathname,n,s,l]);return e.createElement(S,{columns:m,data:d,sortBy:[{id:"title",desc:!1}]})}function Ke(p){const u=T(),{data:d}=p,i=b(()=>[{header:"Rules",accessorKey:"ruleId",cell:({getValue:n})=>e.createElement(w,{to:O(u.pathname)+"/rules/"+encodeURIComponent(n())},n())},{id:"status",header:"Status",accessorKey:"status",size:200,cell:({row:n})=>e.createElement(q,{errors:n.original.errorApisCount,warnings:n.original.warningApisCount})},{header:"Problem APIs",size:200,accessorKey:"apis",cell:({getValue:n})=>n().length,sortDescFirst:!0},{header:"Problem count",size:200,accessorKey:"count",sortDescFirst:!0}],[u.pathname]);return e.createElement(S,{columns:i,data:d,sortBy:[{id:"status",desc:!0}]})}function De(p){const{data:u,levelNames:d,filters:i,setActiveTab:n,teamLabel:s,teamProperty:l}=p,f=b(()=>[{id:"team",header:`API ${s}`,accessorKey:"team",size:200,cell:({getValue:m})=>e.createElement(ae,{onClick:()=>{n("apis"),i.find(a=>a.property===l)?.selectOption(m())}},m())},...[Y,...d].map(m=>({id:m,header:`${m}`,accessorFn:a=>a.levels[m]?.apis}))],[i,d,n,s,l]);return e.createElement(S,{columns:f,data:u,sortBy:[{id:"team",desc:!1}],groupHeaderTitle:"APIs by level"})}function ie(p){const{data:u,title:d}=p,i=b(()=>[{id:"expand",maxSize:40,size:40,header:()=>null,enableSorting:!1,cell:({row:n})=>n.getCanExpand()?e.createElement(x,{onClick:n.getToggleExpandedHandler(),variant:"text",size:"medium",icon:n.getIsExpanded()?e.createElement(K,null):e.createElement(I,null)}):null},{header:"Rules",accessorKey:"ruleId",cell:({getValue:n,row:s})=>s.getCanExpand()?e.createElement("span",{onClick:s.getToggleExpandedHandler()},n()):null},{header:"Status",accessorKey:"status",size:200,cell:({getValue:n})=>{const s=n();return e.createElement(q,{errors:s?.errorsCount,warnings:s?.warningsCount})},enableSorting:!1},{header:"Problem count",accessorKey:"problemsCount",size:200,sortDescFirst:!0}],[]);return e.createElement(S,{columns:i,data:u,sortBy:[{id:"ruleId",desc:!1}],empty:e.createElement(e.Fragment,null,"\u2705 This API is compliant with ",e.createElement("strong",null,d)," level rules"),subRowKey:"problems"})}function ze(p){const{catalogSlug:u,slug:d,data:i,hasTeam:n,hasPublishedAt:s,teamLabel:l}=p,{apis:f,apisCount:m,warningApisCount:a,errorApisCount:o,levels:E,count:g}=i,t=f.length===0,h=t?void 0:{id:"expand",maxSize:40,size:40,header:()=>null,enableSorting:!1,cell:({row:r})=>r.getCanExpand()?e.createElement(x,{onClick:r.getToggleExpandedHandler(),variant:"text",size:"medium",icon:r.getIsExpanded()?e.createElement(K,null):e.createElement(I,null)}):null},P=b(()=>[h,{header:"API",accessorKey:"title"},s?{header:"Published at",accessorKey:"publishedAt",cell:({getValue:r})=>{const c=r();return c?new Date(c).toLocaleDateString():"-"}}:void 0,n?{header:l,accessorKey:"team",cell:({getValue:r})=>r()||X}:void 0,{header:"Problem count",size:200,accessorKey:"count",cell:({getValue:r})=>r()||0,sortDescFirst:!0}].filter(R),[n,s,l,t]),y=b(()=>Object.entries(i.teams).sort((r,c)=>c[1]-r[1]),[i.teams]);return e.createElement(V,null,e.createElement(x,{icon:e.createElement(D,null),iconPosition:"left",variant:"ghost",size:"large",to:`${u}scorecard`},"Back to dashboard"),e.createElement("h1",null,"Rule ",d),e.createElement(j,null,e.createElement(A,null,e.createElement(L,null,"Rule status"),e.createElement(F,null,e.createElement("div",null,e.createElement(v,null,m),e.createElement("br",null),"APIs"),e.createElement("div",null,e.createElement(C,null,e.createElement(v,{style:{textAlign:"right"}},m-a-o),e.createElement(k,{severity:"Pass"})),e.createElement(C,null,e.createElement(v,{style:{textAlign:"right"}},a),e.createElement(k,{severity:"Warn"})),e.createElement(C,null,e.createElement(v,{style:{textAlign:"right"}},o),e.createElement(k,{severity:"Error"}))))),e.createElement(A,null,e.createElement(L,null,"Problems by level"),e.createElement(W,null,Object.entries(E).map(([r,c])=>e.createElement(B,{key:r},e.createElement("span",null,r),e.createElement(ce,{chunks:[{share:c.errors/g*100,colorVariable:"--scorecard-color-error"},{share:c.warnings/g*100,colorVariable:"--scorecard-color-warning"}]}),e.createElement(oe,null,c.errors+c.warnings))))),e.createElement(A,null,e.createElement(L,null,`Problems by ${l}`),e.createElement(W,null,y.slice(0,4).map(([r,c])=>e.createElement(B,{key:r},e.createElement("span",{style:{flex:1,textAlign:"left"}},r),e.createElement(v,null,c)))))),e.createElement(S,{columns:P,data:f,sortBy:[{id:"title",desc:!1}],subRowKey:"problems",slug:d}))}function Oe(p){const{slug:u,api:d,teamLabel:i,catalogSlug:n}=p,{api:s}=d||{},{scorecard:l,isLoading:f}=se(`/${u}`),m=l?.levels||{},[a,o]=G(Object.keys(m)[0]),E=b(()=>s?.publishedAt instanceof Date?s?.publishedAt.toLocaleDateString():s?.publishedAt,[s]);_(()=>{if(l){const t=l.levels||{};o(Object.keys(t)[0])}},[l]);const g=b(()=>{if(!l?.levels)return{};const t={};for(const h of Object.keys(l.levels)){const P=l.levels[h].problems.reduce((y,r)=>(y[r.ruleId]||(y[r.ruleId]=[]),y[r.ruleId].push(r),y),{});t[h]=Object.entries(P).map(([y,r])=>({ruleId:y,problems:[...r.filter(c=>c.severity==="error"),...r.filter(c=>c.severity==="warn")],problemsCount:r.length,status:{errorsCount:r.filter(c=>c.severity==="error").length,warningsCount:r.filter(c=>c.severity==="warn").length}}))}return t},[l?.levels]);return f?e.createElement(U,{justifyContent:"center",mt:"2em"},e.createElement(J,null)):l?e.createElement(V,null,e.createElement(x,{icon:e.createElement(D,null),iconPosition:"left",variant:"ghost",size:"large",to:`${n}scorecard`},"Back to dashboard"),e.createElement("h1",null,s?.title||u),e.createElement(ee,null,E&&e.createElement(e.Fragment,null,"Published at: ",E,e.createElement("br",null)),e.createElement(w,{to:u,external:!0},"Documentation")),e.createElement(j,null,e.createElement(A,null,e.createElement(le,null,e.createElement("span",null,"Scorecard level"),e.createElement(M,{level:l.scorecardLevel,colorVariable:z(l.scorecardLevelIdx,Object.keys(m).length)})),e.createElement(F,null,e.createElement("div",null,"Compliance by level"),e.createElement("div",null,Object.entries(l.levels).map(([t,h])=>e.createElement(C,{key:t},e.createElement(v,{style:{width:"100px",textAlign:"right"}},t),e.createElement($,{errors:h.errors,warnings:h.warnings})))))),e.createElement(Q,{title:"Rules compliance",levels:Object.entries(l.levels).map(([t,h])=>({errors:h.uniqueErrors,warnings:h.uniqueWarnings,total:p.rulesPerLevel[t].length,name:t}))}),e.createElement(A,null,e.createElement(L,null,i),e.createElement(v,{style:{fontWeight:"normal"}},s?.team))),e.createElement(re,{style:{justifyContent:"flex-start"},"data-component-name":"Scorecard/ModeSwitcherTabs"},Object.keys(l.levels).map(t=>e.createElement(te,{"data-component-name":"Scorecard/ModeSwitcherTab",key:t,isActive:a===t,onClick:()=>o(t)},e.createElement(C,null,e.createElement("span",null,t)," ",e.createElement($,{errors:l.levels[t].errors,warnings:l.levels[t].warnings}))))),Object.keys(m).map(t=>e.createElement(ne,{key:t,isActive:a===t},e.createElement(ie,{title:t,data:g[t]})))):e.createElement("div",null,"Scorecard not found")}export{Ie as ApiListView,Oe as ApiPage,ie as ApiView,ze as RuleView,Ke as RulesView,De as TeamsView};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{REDOCLY_ROUTE_RBAC as p,REDOCLY_TEAMS_RBAC as c}from"@redocly/config";import{collectPropValueDeep as v}from"../../../utils/tree/collect-prop-value-deep.js";import{isDefined as T}from"../../../utils/guards/is-defined.js";import{resolveItem as $,resolveItems as w}from"../nav-utils.js";function F(n){return Array.isArray(n)&&n.some(b)}function I(n,e){const a=e.normalize("NFKD").replace(/[\u0300-\u036f]/g,"").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(
|
|
1
|
+
import{REDOCLY_ROUTE_RBAC as p,REDOCLY_TEAMS_RBAC as c}from"@redocly/config";import{collectPropValueDeep as v}from"../../../utils/tree/collect-prop-value-deep.js";import{isDefined as T}from"../../../utils/guards/is-defined.js";import{resolveItem as $,resolveItems as w}from"../nav-utils.js";function F(n){return Array.isArray(n)&&n.some(b)}function I(n,e){const a=e.normalize("NFKD").replace(/[\u0300-\u036f]/g,"").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|(?<!-)-+$/g,"");return`${n}-${a||"tab"}`}function x(n){const e=[],a=n.filter(o=>!b(o));a.length&&e.push(`when a sidebar uses tabs, every top-level entry must be a tab; found ${a.length} other entr${a.length===1?"y":"ies"}: ${JSON.stringify(a)}`);for(const{tab:o,page:i,menu:r,items:f,$ref:t,directory:l,group:s,href:m}of n.filter(b)){const u=`tab "${o}"`;if(r!==void 0&&!Array.isArray(r)){e.push(`${u} "menu" must be a list of entries`);continue}i?.includes("#")&&e.push(`${u} page must not contain a heading anchor`),r&&f?e.push(`${u} cannot have both "menu" and "items"`):r&&i?e.push(`${u} has "menu" and cannot also have "page"`):!r&&!i&&e.push(`${u} needs "page" or "menu"`),(t||l||s||m)&&e.push(`${u} accepts only "page", "items", "menu" and "rbac"; move "$ref"/"directory"/"group"/"href" into "items"`),r?.length===0&&e.push(`${u} has an empty "menu"`),r?.forEach((d,y)=>{const h=`menu entry ${y+1} of ${u}`;d?.page||e.push(`${h} needs "page"`),d?.page?.includes("#")&&e.push(`${h} page must not contain a heading anchor`)});const g=[...r??[],...f??[]];v(g,"tab").length&&e.push(`${u} contains a nested "tab"; tabs are only allowed at the top level`),v(g,"menu").length&&e.push(`${u} contains a nested "menu"; "menu" is only allowed directly on a tab`)}return e}async function C(n,e,a,o,i){const r=[];for(const[f,t]of n.filter(b).entries()){const l=I(f,t.tab);if(t.menu){const g=(await Promise.all(t.menu.map((y,h)=>A(y,l,h,e,a,o,i)))).filter(T),d=t.icon?S(await $({group:t.tab,icon:t.icon,items:[]},e,a,o,i)):void 0;r.push({type:"tab",tabId:l,label:t.tab,labelTranslationKey:t.tabTranslationKey,menu:g,icon:d?.icon,srcSet:d?.srcSet,[c]:t.rbac});continue}const s=S(await $({group:t.tab,rbac:t.rbac,page:t.page,icon:t.icon,items:[]},e,a,o,i)),m=s?.routeSlug?a.getRouteBySlug(s.routeSlug):void 0,u=await w(t.items,e,a,o,i)??[];r.push({type:"tab",tabId:l,label:t.tab,labelTranslationKey:t.tabTranslationKey,link:s?.link,routeSlug:s?.routeSlug,fsPath:s?.fsPath,icon:s?.icon,srcSet:s?.srcSet,items:[...s?.items??[],...u],...k(m?.versions,s),[c]:t.rbac??s?.[c],[p]:s?.[p]})}return r}function M(n){return n.flatMap(e=>e.menu?e.menu.flatMap(a=>R(`${e.label} > ${a.label}`,a.routeSlug,a.items)):R(e.label,e.routeSlug,e.items??[]))}function N(n){const e=new Map;for(const{routeSlug:a,owner:o}of n){const i=a.split("#")[0],r=e.get(i)??new Set;r.add(o),e.set(i,r)}return[...e.entries()].filter(([,a])=>a.size>1).map(([a,o])=>({routeSlug:a,owners:[...o]}))}function O(n){return n.map(e=>({type:"group",label:e.label,labelTranslationKey:e.labelTranslationKey,link:e.link,routeSlug:e.routeSlug,fsPath:e.fsPath,version:e.version,isDefault:e.isDefault,versionFolderId:e.versionFolderId,items:e.menu??e.items??[],[c]:e[c],[p]:e[p]}))}function b(n){return typeof n?.tab=="string"}async function A(n,e,a,o,i,r,f){const t=S(await $({...n,group:n.label||n.page,items:[]},o,i,r,f));if(!t?.link)return;const l=t.routeSlug?i.getRouteBySlug(t.routeSlug):void 0,s=n.label||await l?.getNavText?.()||t.link,m=await w(n.items,o,i,r,f)??[],u=[...t.items??[],...m];return{type:"link",tabId:`${e}/${I(a,s)}`,label:s,labelTranslationKey:n.labelTranslationKey,link:t.link,routeSlug:t.routeSlug,fsPath:t.fsPath,metadata:t.metadata,icon:t.icon,srcSet:t.srcSet,badges:t.badges,items:u,...k(l?.versions,t),[c]:n.rbac??l?.[c]??t[c],[p]:t[p]}}function S(n){const e=Array.isArray(n)?n[0]:n;return e?.type==="group"?e:void 0}function k(n,e){const a=n?.find(o=>o.active);return a?{version:a.version,isDefault:a.default,versionFolderId:a.folderId}:e?.version===void 0?{}:{version:e.version,isDefault:e.isDefault,versionFolderId:e.versionFolderId}}function R(n,e,a){return[e,...v(a,"routeSlug")].filter(T).map(o=>({routeSlug:o,owner:n,items:a}))}export{M as collectTabRoutes,N as findDuplicateRoutes,F as hasTabs,I as makeTabId,C as resolveTabs,O as tabsAsGroups,x as validateTabs};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
Usage: ${e}/https://api.example.com/path`);const
|
|
1
|
+
import q from"node:dns";import{isIP as H}from"node:net";import{Agent as L}from"undici";import{withPathPrefix as $}from"@redocly/theme/core/utils";import{ServerRoutes as D}from"../../../constants/api.js";import{envConfig as y}from"../../config/env-config.js";import{getRequestOrigin as M}from"../utils/get-request-origin.js";import{SsrfBlockedError as N}from"../utils/ssrf-blocklist.js";import{createSsrfSafeLookup as b}from"../utils/ssrf-safe-lookup.js";const U=new Set(["connection","keep-alive","proxy-authenticate","proxy-connection","proxy-authorization","te","trailer","transfer-encoding","upgrade","host"]),W=new Set(["cookie","cookie2","accept-encoding"]),z=new Set(["set-cookie","set-cookie2","content-encoding","content-length"]),S="x-redocly-proxy-streaming",x="x-http-method-override",v="x-redocly-cookie",P="Requests to private network addresses are not allowed",Y=new L({connect:{lookup:b()}});function le(t,e=$(D.CORS_PROXY)){return async n=>{const r=new URL(n.req.url).pathname;if(r===e||r===`${e}/`)return n.text(`Realm CORS proxy endpoint.
|
|
2
|
+
Usage: ${e}/https://api.example.com/path`);const s=ne(n.req.url,e);if(!s)return n.text("Invalid proxied URL",400);const a=t?.getConfig().corsProxy?.allowedTargets;if(a&&a.length>0&&!a.some(f=>Z(s,f)))return n.text("Target URL is not in the allowed list for CORS proxy",403);const m=M(n),g=s.origin===m,w=s.pathname===e||s.pathname.startsWith(`${e}/`);if(g&&!w)return new Response("Please use a direct request",{status:308,headers:{Location:s.toString(),Vary:"origin","Cache-Control":"private"}});const p=await j(s.hostname),u=!y.isDevelopMode||y.isReunite;if(u&&p&&F(p))return n.text(P,403);const i=new Headers,A=T(n.req.raw.headers);for(const[o,f]of n.req.raw.headers)A.has(o.toLowerCase())||W.has(o.toLowerCase())||i.append(o,f);const R=i.get(v);if(R){const o=n.req.raw.headers.get("cookie")||"";i.set("cookie",o?`${o}; ${R}`:R),i.delete(v)}const O=n.req.raw.headers.get("origin")||"";X(O)&&i.delete("origin");let l=n.req.method;const E=i.get(x);E&&(l=E.toUpperCase(),i.delete(x));const h={method:l,headers:i,redirect:"manual"};u&&(h.dispatcher=Y),l!=="GET"&&l!=="HEAD"&&n.req.raw.body&&(h.body=n.req.raw.body,h.duplex="half");let c;try{c=await fetch(s,h)}catch(o){if(o instanceof Error&&o.cause instanceof N)return n.text(P,403);const f=o instanceof Error?o.message:"unknown error",k=o instanceof Error&&o.cause instanceof Error?`: ${o.cause.message}`:"";return n.text(`Failed to proxy request: ${f}${k}`,502)}if(n.req.raw.headers.get("sec-fetch-mode")==="navigate"&&ee(c))return n.text("Direct browser navigation to proxied HTML, JavaScript, or redirect responses is not allowed",403);const d=new Headers(c.headers),_=T(c.headers);for(const o of _)d.delete(o);for(const o of z)d.delete(o);return d.set(S,"1"),d.set("x-content-type-options","nosniff"),d.has("content-type")||d.set("content-type","text/plain"),new Response(c.body,{status:c.status,statusText:c.statusText,headers:d})}}function B(t){try{return decodeURIComponent(t)}catch{return t}}function G(t){return t.replace(/^(https?):\/(?!\/)/i,"$1://")}function V(t,e){return e?t.includes("?")?e==="?"?t:`${t.endsWith("?")||t.endsWith("&")?t:`${t}&`}${e.slice(1)}`:`${t}${e}`:t}function T(t){const e=new Set(U),n=t.get("connection");if(!n)return e;for(const r of n.split(",")){const s=r.trim().toLowerCase();s&&e.add(s)}return e}function X(t){const e=t.toLowerCase();return e.includes(".redocly.app")||e.includes("localhost")}async function j(t){const e=C(t);if(H(e))return e;try{const{address:n}=await q.promises.lookup(e);return n}catch{return null}}function F(t){const e=C(t).toLowerCase();if(!H(e))return!0;const n=J(e);return n?I(n):e.includes(":")?K(e):I(e)}function C(t){return t.startsWith("[")&&t.endsWith("]")?t.slice(1,-1):t}function J(t){const e=t.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i);if(e){const r=parseInt(e[1],16),s=parseInt(e[2],16);return[r>>8&255,r&255,s>>8&255,s&255].join(".")}return t.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/i)?.[1]??null}function I(t){const e=t.split(".").map(Number);if(e.length!==4||e.some(s=>isNaN(s)))return!0;const[n,r]=e;return n===0||n===10||n===127||n===172&&r>=16&&r<=31||n===192&&r===168||n===169&&r===254||n===100&&r>=64&&r<=127}function K(t){const e=Q(t);return t==="::1"||t==="::"||e!==null&&(e&65024)===64512||e!==null&&(e&65472)===65152}function Q(t){const[e]=t.split(":");if(!e)return null;const n=parseInt(e,16);return Number.isNaN(n)?null:n}function Z(t,e){let n;try{n=new URL(e)}catch{return!1}if(t.origin!==n.origin)return!1;const r=n.pathname.endsWith("/")?n.pathname:`${n.pathname}/`;return t.pathname.startsWith(r)||t.pathname===n.pathname}function ee(t){return t.status>=300&&t.status<400&&t.headers.has("location")||te(t.headers.get("content-type")||"")}function te(t){const e=t.toLowerCase();return e.includes("text/html")||e.includes("javascript")||e.includes("application/xhtml")||e.includes("application/xml")||e.includes("image/svg")}function ne(t,e){const n=new URL(t),r=n.pathname===e,s=n.pathname.startsWith(`${e}/`);if(!r&&!s)return null;const a=n.pathname.slice(e.length).replace(/^\/+/,"");if(!a)return null;const m=[a,B(a)];for(const g of m){const w=G(g),p=V(w,n.search);try{const u=new URL(p);if(u.protocol==="http:"||u.protocol==="https:")return u}catch{continue}}return null}const he=S;export{he as CORS_PROXY_STREAM_HEADER,le as corsProxyHandler,F as isPrivateIp,ne as resolveCorsProxyTarget};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getCookie as U}from"hono/cookie";import{createHash as V,timingSafeEqual as Z}from"node:crypto";import{ulid as Y}from"ulid";import{isRedoclySso as W,isOidcProviderConfig as w,createMcpAuthorizationCode as X,verifyMcpAuthorizationCode as Q,createMcpRefreshToken as $,verifyMcpRefreshToken as x,createMcpSessionResource as S,oidcRefreshTokenGrant as ee,getOidcMetadata as re,getUserParamsFromCookies as te,idTokenIsForThisProject as ne}from"../../auth.js";import{isMcpContentProtected as P}from"../../../plugins/mcp/auth/auth-handlers.js";import{SUPPORTED_MCP_SCOPES as y}from"../../../plugins/mcp/constants.js";import{AUTH_URL as oe,JWT_SECRET_KEY as m,MCP_ACCESS_TOKEN_TTL_SEC as z,MCP_REFRESH_SESSION_LIFETIME_SEC as ie}from"../../../constants/common.js";import{ServerRoutes as T}from"../../../../constants/api.js";import{AuthCookieNames as v}from"../../../../constants/common.js";import{withPathPrefix as I}from"@redocly/theme/core/utils";import{telemetry as A}from"../../../telemetry/index.js";import{envConfig as D}from"../../../config/env-config.js";import*as M from"../../jwt/jwt.js";import{InternalTokenTypes as F,McpAccessTokenMarker as R}from"../../jwt/token-types.js";import{AlgorithmTypes as L,JwtAlgorithmNotImplemented as se,JwtTokenExpired as ce,JwtTokenInvalid as de,JwtTokenNotBefore as ae,JwtTokenSignatureMismatched as le}from"../../jwt/types.js";import{getRequestOrigin as N}from"../../utils/get-request-origin.js";import{encryptClaimValue as q}from"../../../utils/crypto/encrypt-claim-value.js";import{decryptAesGcm as pe}from"../../../utils/crypto/decrypt-aes-gcm.js";import{encryptAesGcm as G}from"../../../utils/crypto/encrypt-aes-gcm.js";import{fetchClientMetadataDocument as _e,isClientMetadataDocumentUrl as H,isRedirectUriRegistered as ue}from"./mcp-client-metadata.js";const s=(r,e,t=200,o)=>r.json(e,t,{"Content-Type":"application/json",...o??{}}),b=new Set(["S256"]),j=["authorization_code","refresh_token"],fe="https://claude.ai/oauth/claude-code-client-metadata";function he(r,e,t){if(!b.has(e||""))return!1;const o=V("sha256").update(t).digest("base64url"),c=Buffer.from(o),i=Buffer.from(r);return c.length!==i.length?!1:Z(c,i)}async function me(r){const e=Math.floor(Date.now()/1e3);return M.sign({type:F.MCP_CONTEXT,...r,iat:e,exp:e+600},m,L.HS256)}async function ge(r){await M.verify(r,m,L.HS256);const{payload:e}=M.decode(r);if(e.type!==F.MCP_CONTEXT)throw new Error("Invalid context token type");return e}function je(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);if(e.req.method!=="GET")return s(e,{error:"Method not allowed"},405,{Allow:"GET"});const t=N(e);return s(e,{resource:`${t}${I("/mcp")}`,authorization_servers:[t],bearer_methods_supported:["header"],resource_documentation:`${t}${T.MCP_OAUTH_AUTHORIZATION_SERVER}`,scopes_supported:[...y],bearer_token_types_supported:["Bearer"]},200,{"Access-Control-Allow-Origin":"*"})}}function $e(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);const t=N(e);return s(e,{issuer:t,authorization_endpoint:`${t}${I(T.MCP_AUTHORIZATION)}`,token_endpoint:`${t}${I(T.MCP_TOKEN_PORTAL)}`,scopes_supported:[...y],registration_endpoint:`${t}${I(T.MCP_DYNAMIC_CLIENT_REGISTRATION)}`,response_types_supported:["code"],grant_types_supported:[...j],subject_types_supported:["public"],id_token_signing_alg_values_supported:["RS256"],code_challenge_methods_supported:["S256"],token_endpoint_auth_methods_supported:["none"],client_id_metadata_document_supported:!0},200,{"Access-Control-Allow-Origin":"*"})}}function ze(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{return s(e,{client_id:D.OAUTH_CLIENT_ID||"",client_name:"MCP Client",redirect_uris:[],grant_types:[...j],response_types:["code"],scope:y.join(" "),subject_type:"public",token_endpoint_auth_method:"none",created_at:new Date().toISOString(),updated_at:new Date().toISOString()},201,{"Access-Control-Allow-Origin":"*"})}catch(t){return s(e,{error:"invalid_request",error_description:t?.message||"Unable to register client"},500,{"Access-Control-Allow-Origin":"*"})}}}function B(r){const e=r||{},t=Object.keys(e),o=t.find(i=>w(e[i])&&W(e[i])),c=t.find(i=>w(e[i]));return o||c||t[0]}function Fe(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);const t=new URL(e.req.url),{searchParams:o}=t,c=o.get("redirect_uri"),i=o.get("client_id"),p=o.get("code_challenge"),n=o.get("code_challenge_method"),l=Y();if(p&&!b.has(n||""))return s(e,{error:"invalid_request",error_description:`Unsupported code_challenge_method. Supported: ${[...b].join(", ")}`},400);if(H(i)&&!p)return s(e,{error:"invalid_request",error_description:"code_challenge is required for client_id_metadata_document clients"},400);if(H(i)&&c)try{const d=await _e(i),a=i===fe;if(!ue(c,d.redirect_uris,{ignorePort:a}))return s(e,{error:"invalid_request",error_description:"redirect_uri is not registered for this client"},400)}catch(d){const a=d instanceof Error?d.message:String(d);return s(e,{error:"invalid_client",error_description:`Unable to resolve client metadata document: ${a}`},400)}A.sendMcpAuthorizationStartedMessage([{...S(l),redirect_uri:c||null}]);const _=N(e),f={isMcpFlow:!0,originalRedirectUri:c,mcpClientId:i,mcpState:o.get("state"),mcpSessionId:l,mcpCodeChallenge:p,mcpCodeChallengeMethod:p?n:null,timestamp:Date.now()};try{const d=await me(f),a=new URL(I(T.IDP_LOGIN),_);a.searchParams.set("redirectTo",`${T.MCP_CALLBACK}/${d}`);const C=B(r.getConfig().ssoDirect);return C?(a.searchParams.set("idpId",C),e.redirect(a.toString())):s(e,{error:"not_found"},404)}catch(d){const a=d instanceof Error?d.message:String(d),C=d instanceof Error?d.stack:String(d);A.sendMcpAuthorizationFailedMessage([{...S(l),error:a,error_details:C}]);const g=new URL(I(`${oe}/oauth2/auth`));return g.search=o.toString(),e.redirect(g.toString())}}}async function K(r){const e=Math.floor(Date.now()/1e3),t=await M.sign({...r.claims,[R.CLAIM]:R.VALUE,...r.idpId?{idpId:r.idpId}:{},...r.idpAccessTokenEnc?{idp_access_token:r.idpAccessTokenEnc}:{},...r.ttlSec?{iat:e,exp:e+r.ttlSec}:{}},m,L.HS256,r.kid),o=r.ttlSec??(typeof r.claims.exp=="number"?Math.max(r.claims.exp-e,0):z);return{accessToken:t,expiresIn:o}}async function ke(r,e,t){const o=k=>s(r,{error:"invalid_grant",error_description:k},400),c=r.get("logger"),i=t.get("refresh_token"),p=t.get("client_id")||void 0;if(!i)return s(r,{error:"invalid_request",error_description:"refresh_token is required"},400);let n;try{n=await x(i)}catch(k){return c?.warn("MCP refresh token rejected: %s",k instanceof Error?k.message:String(k)),o("Invalid refresh token")}if(p&&p!==n.client_id)return o("Client mismatch");const l=n.idp_id?e.getConfig().ssoDirect?.[n.idp_id]:void 0;let _,f,d=n.idp_access_token_enc,a=n.idp_refresh_token_enc;if(n.idp_refresh_token_enc){let k;try{k=pe(n.idp_refresh_token_enc,m)}catch{return o("Invalid refresh token")}if(!n.idp_id||!w(l))return o("Unknown identity provider");let u;try{const h=await re(n.idp_id,l);if(!h?.token_endpoint)return o("Identity provider has no token endpoint");u=await ee(h.token_endpoint,k,l)}catch(h){return c?.warn("MCP refresh failed to reach the IdP token endpoint: %s",h instanceof Error?h.message:String(h)),s(r,{error:"server_error",error_description:"Failed to refresh session"},500)}if(u.error||!(u.id_token||u.access_token))return c?.warn("MCP refresh rejected by IdP: %s",u.error||"empty token response"),o("Refresh rejected by identity provider");if(u.refresh_token&&(a=G(u.refresh_token,m)),u.access_token&&(d=q(u.access_token,m)),u.id_token)try{const h=M.decode(u.id_token);_=h.payload,f=h.header.kid}catch{return o("Invalid token response from identity provider")}}const C=w(l)&&l.tokenExpirationTime||z,g=_??n.claims;if(!ne(g,l))return c?.warn("MCP refresh rejected: the id_token was issued for another project"),o("Invalid refresh token");const E=await K({claims:g,kid:f,idpId:n.idp_id,idpAccessTokenEnc:d,ttlSec:_?void 0:C}),O=await $({clientId:n.client_id,idpId:n.idp_id,claims:g,idpAccessTokenEnc:d,idpRefreshTokenEnc:a,sessionExp:n.session_exp});return s(r,{access_token:E.accessToken,token_type:"Bearer",expires_in:E.expiresIn,refresh_token:O,scope:y.join(" ")},200,{"Cache-Control":"no-store",Pragma:"no-cache"})}function Ge(r){return async e=>{if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{const t=await e.req.formData(),o=t.get("grant_type"),c=t.get("code"),i=t.get("redirect_uri")||void 0,p=t.get("code_verifier")||void 0;if(!j.includes(o))return s(e,{error:"invalid_request",error_description:"Invalid grant type"},400);if(o==="refresh_token")return await ke(e,r,t);if(!c)return s(e,{error:"invalid_request",error_description:"Missing authorization code"},400);try{const n=await Q(c);if(i&&i!==n.redirect_uri)return s(e,{error:"invalid_grant",error_description:"redirect_uri mismatch"},400);if(n.code_challenge){if(!p)return s(e,{error:"invalid_grant",error_description:"code_verifier required"},400);if(!he(n.code_challenge,n.code_challenge_method,p))return s(e,{error:"invalid_grant",error_description:"code_verifier mismatch"},400)}if(D.OAUTH_CLIENT_ID&&n.client_id&&!H(n.client_id)&&n.client_id!==D.OAUTH_CLIENT_ID)return s(e,{error:"invalid_client",error_description:"Client mismatch"},400);const l=n.id_token;if(typeof l!="string"||l.length===0)return s(e,{error:"invalid_grant",error_description:"Missing id_token in authorization code"},400);const _=await J(l,r.getConfig().ssoDirect);if(!_)return s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400);const f=n.idp_access_token_enc??(n.idp_access_token?q(n.idp_access_token,m):void 0),{accessToken:d,expiresIn:a}=await K({claims:_.payload,kid:_.kid,idpId:n.idp_id,idpAccessTokenEnc:f}),g=Math.floor(Date.now()/1e3)+ie,E=await $({clientId:n.client_id,idpId:n.idp_id,claims:_.payload,idpAccessTokenEnc:f,idpRefreshTokenEnc:n.idp_refresh_token_enc,sessionExp:g}),O={access_token:d,token_type:"Bearer",expires_in:a,refresh_token:E,scope:y.join(" ")};return s(e,O,200,{"Cache-Control":"no-store",Pragma:"no-cache"})}catch(n){return e.get("logger")?.warn("MCP token exchange failed: %s",n instanceof Error?n.message:String(n)),s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400)}}catch(t){const o=t instanceof Error?t.message:String(t);return s(e,{error:"server_error",error_description:"Failed to process token request",error_details:o},500)}}}async function J(r,e){const t=await te(e||{},{[v.AUTHORIZATION]:r});if(t.isAuthenticated!==!0)return;const{payload:o,header:c}=M.decode(r);if(o[R.CLAIM]!==R.VALUE)return{payload:o,kid:c.kid,idpId:typeof t.idpId=="string"?t.idpId:void 0}}function Be(r){return async e=>{const t=new URL(e.req.url);let o=t.searchParams.get("context");if(!o&&t.pathname.startsWith(I(`${T.MCP_CALLBACK}/`))){const i=t.pathname.split("/");o=i[i.length-1]}if(!o)return e.text("Missing context parameter",400);let c=null;try{const i=await ge(o);if(c=i.mcpSessionId||null,!i.isMcpFlow||!i.originalRedirectUri)throw new Error("Invalid MCP context");const p=r.getConfig().ssoDirect,n=U(e,v.AUTHORIZATION),l=n?await J(n,p):void 0,_=U(e,v.IDP_ACCESS_TOKEN),f=U(e,v.IDP_REFRESH_TOKEN);if(!n||!l)return A.sendMcpAuthorizationFailedMessage([{...S(c),error:"Authentication required",error_details:null}]),e.text("Authentication required",401);const d=await X({idToken:n,idpId:l.idpId??B(p),idpAccessTokenEnc:_?q(_,m):void 0,idpRefreshTokenEnc:f?G(f,m):void 0,clientId:i.mcpClientId||"",redirectUri:i.originalRedirectUri,codeChallenge:i.mcpCodeChallenge||void 0,codeChallengeMethod:i.mcpCodeChallengeMethod||void 0,ttlSec:600}),a=new URL(i.originalRedirectUri);return a.searchParams.set("code",d),i.mcpState&&a.searchParams.set("state",i.mcpState),A.sendMcpAuthorizationCompletedMessage([{...S(c),redirect_uri:i.originalRedirectUri||null}]),e.redirect(a.toString())}catch(i){const p=Ce(i);return A.sendMcpAuthorizationFailedMessage([{...S(c),...p}]),e.text(`Invalid MCP callback: ${p.error}`,400)}}}function Ce(r){return r instanceof Error&&[se,ce,de,ae,le].some(e=>r instanceof e)?{error:r.name,error_details:null}:r instanceof Error?{error:r.message,error_details:r.stack??null}:{error:String(r),error_details:null}}export{j as SUPPORTED_MCP_GRANT_TYPES,me as createMcpContextToken,Fe as mcpAuthorizationHandler,Be as mcpCallbackHandler,ze as mcpDynamicClientRegistrationHandler,$e as mcpOAuthAuthorizationServerHandler,je as mcpOAuthProtectedResourceHandler,Ge as mcpTokenPortalHandler,B as resolveMcpIdpId,ge as verifyAndParseMcpContextToken,he as verifyPkce};
|
|
1
|
+
import{getCookie as U}from"hono/cookie";import{createHash as V,timingSafeEqual as Z}from"node:crypto";import{ulid as Y}from"ulid";import{isRedoclySso as W,isOidcProviderConfig as w,createMcpAuthorizationCode as X,verifyMcpAuthorizationCode as Q,createMcpRefreshToken as $,verifyMcpRefreshToken as x,createMcpSessionResource as S,oidcRefreshTokenGrant as ee,getOidcMetadata as re,getUserParamsFromCookies as te,idTokenIsForThisProject as ne}from"../../auth.js";import{isMcpContentProtected as P}from"../../../plugins/mcp/auth/auth-handlers.js";import{SUPPORTED_MCP_SCOPES as y}from"../../../plugins/mcp/constants.js";import{AUTH_URL as oe,JWT_SECRET_KEY as m,MCP_ACCESS_TOKEN_TTL_SEC as z,MCP_REFRESH_SESSION_LIFETIME_SEC as ie}from"../../../constants/common.js";import{ServerRoutes as T}from"../../../../constants/api.js";import{AuthCookieNames as v}from"../../../../constants/common.js";import{withPathPrefix as I}from"@redocly/theme/core/utils";import{telemetry as A}from"../../../telemetry/index.js";import{envConfig as D}from"../../../config/env-config.js";import*as M from"../../jwt/jwt.js";import{InternalTokenTypes as F,McpAccessTokenMarker as R}from"../../jwt/token-types.js";import{AlgorithmTypes as L,JwtAlgorithmNotImplemented as se,JwtTokenExpired as ce,JwtTokenInvalid as de,JwtTokenNotBefore as ae,JwtTokenSignatureMismatched as le}from"../../jwt/types.js";import{getRequestOrigin as N}from"../../utils/get-request-origin.js";import{encryptClaimValue as q}from"../../../utils/crypto/encrypt-claim-value.js";import{decryptAesGcm as pe}from"../../../utils/crypto/decrypt-aes-gcm.js";import{encryptAesGcm as G}from"../../../utils/crypto/encrypt-aes-gcm.js";import{fetchClientMetadataDocument as _e,isClientMetadataDocumentUrl as H,isRedirectUriRegistered as ue}from"./mcp-client-metadata.js";const s=(r,e,n=200,o)=>r.json(e,n,{"Content-Type":"application/json",...o??{}}),b=new Set(["S256"]),j=["authorization_code","refresh_token"],fe="https://claude.ai/oauth/claude-code-client-metadata";function he(r,e,n){if(!b.has(e||""))return!1;const o=V("sha256").update(n).digest("base64url"),c=Buffer.from(o),i=Buffer.from(r);return c.length!==i.length?!1:Z(c,i)}async function me(r){const e=Math.floor(Date.now()/1e3);return M.sign({type:F.MCP_CONTEXT,...r,iat:e,exp:e+600},m,L.HS256)}async function ge(r){await M.verify(r,m,L.HS256);const{payload:e}=M.decode(r);if(e.type!==F.MCP_CONTEXT)throw new Error("Invalid context token type");return e}function je(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);if(e.req.method!=="GET")return s(e,{error:"Method not allowed"},405,{Allow:"GET"});const n=N(e);return s(e,{resource:`${n}${I("/mcp")}`,authorization_servers:[n],bearer_methods_supported:["header"],resource_documentation:`${n}${T.MCP_OAUTH_AUTHORIZATION_SERVER}`,scopes_supported:[...y],bearer_token_types_supported:["Bearer"]},200,{"Access-Control-Allow-Origin":"*"})}}function $e(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);const n=N(e);return s(e,{issuer:n,authorization_endpoint:`${n}${I(T.MCP_AUTHORIZATION)}`,token_endpoint:`${n}${I(T.MCP_TOKEN_PORTAL)}`,scopes_supported:[...y],registration_endpoint:`${n}${I(T.MCP_DYNAMIC_CLIENT_REGISTRATION)}`,response_types_supported:["code"],grant_types_supported:[...j],subject_types_supported:["public"],id_token_signing_alg_values_supported:["RS256"],code_challenge_methods_supported:["S256"],token_endpoint_auth_methods_supported:["none"],client_id_metadata_document_supported:!0},200,{"Access-Control-Allow-Origin":"*"})}}function ze(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{return s(e,{client_id:D.OAUTH_CLIENT_ID||"",client_name:"MCP Client",redirect_uris:[],grant_types:[...j],response_types:["code"],scope:y.join(" "),subject_type:"public",token_endpoint_auth_method:"none",created_at:new Date().toISOString(),updated_at:new Date().toISOString()},201,{"Access-Control-Allow-Origin":"*"})}catch(n){return s(e,{error:"invalid_request",error_description:n?.message||"Unable to register client"},500,{"Access-Control-Allow-Origin":"*"})}}}function B(r){const e=r||{},n=Object.keys(e),o=n.find(i=>w(e[i])&&W(e[i])),c=n.find(i=>w(e[i]));return o||c||n[0]}function Fe(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);const n=new URL(e.req.url),{searchParams:o}=n,c=o.get("redirect_uri"),i=o.get("client_id"),p=o.get("code_challenge"),t=o.get("code_challenge_method"),l=Y();if(p&&!b.has(t||""))return s(e,{error:"invalid_request",error_description:`Unsupported code_challenge_method. Supported: ${[...b].join(", ")}`},400);if(H(i)&&!p)return s(e,{error:"invalid_request",error_description:"code_challenge is required for client_id_metadata_document clients"},400);if(H(i)&&c)try{const d=await _e(i),a=i===fe;if(!ue(c,d.redirect_uris,{ignorePort:a}))return s(e,{error:"invalid_request",error_description:"redirect_uri is not registered for this client"},400)}catch(d){const a=d instanceof Error?d.message:String(d);return s(e,{error:"invalid_client",error_description:`Unable to resolve client metadata document: ${a}`},400)}A.sendMcpAuthorizationStartedMessage([{...S(l),redirect_uri:c||null}]);const _=N(e),f={isMcpFlow:!0,originalRedirectUri:c,mcpClientId:i,mcpState:o.get("state"),mcpSessionId:l,mcpCodeChallenge:p,mcpCodeChallengeMethod:p?t:null,timestamp:Date.now()};try{const d=await me(f),a=new URL(I(T.IDP_LOGIN),_);a.searchParams.set("redirectTo",`${T.MCP_CALLBACK}/${d}`);const C=B(r.getConfig().ssoDirect);return C?(a.searchParams.set("idpId",C),e.redirect(a.toString())):s(e,{error:"not_found"},404)}catch(d){const a=d instanceof Error?d.message:String(d),C=d instanceof Error?d.stack:String(d);A.sendMcpAuthorizationFailedMessage([{...S(l),error:a,error_details:C}]);const g=new URL(I(`${oe}/oauth2/auth`));return g.search=o.toString(),e.redirect(g.toString())}}}async function K(r){const e=Math.floor(Date.now()/1e3),n=await M.sign({...r.claims,[R.CLAIM]:R.VALUE,...r.idpId?{idpId:r.idpId}:{},...r.idpAccessTokenEnc?{idp_access_token:r.idpAccessTokenEnc}:{},...r.ttlSec?{iat:e,exp:e+r.ttlSec}:{}},m,L.HS256,r.kid),o=r.ttlSec??(typeof r.claims.exp=="number"?Math.max(r.claims.exp-e,0):z);return{accessToken:n,expiresIn:o}}async function ke(r,e,n){const o=k=>s(r,{error:"invalid_grant",error_description:k},400),c=r.get("logger"),i=n.get("refresh_token"),p=n.get("client_id")||void 0;if(!i)return s(r,{error:"invalid_request",error_description:"refresh_token is required"},400);let t;try{t=await x(i)}catch(k){return c?.warn("MCP refresh token rejected: %s",k instanceof Error?k.message:String(k)),o("Invalid refresh token")}if(p&&p!==t.client_id)return o("Client mismatch");const l=t.idp_id?e.getConfig().ssoDirect?.[t.idp_id]:void 0;let _,f,d=t.idp_access_token_enc,a=t.idp_refresh_token_enc;if(t.idp_refresh_token_enc){let k;try{k=pe(t.idp_refresh_token_enc,m)}catch{return o("Invalid refresh token")}if(!t.idp_id||!w(l))return o("Unknown identity provider");let u;try{const h=await re(t.idp_id,l);if(!h?.token_endpoint)return o("Identity provider has no token endpoint");u=await ee(h.token_endpoint,k,l)}catch(h){return c?.warn("MCP refresh failed to reach the IdP token endpoint: %s",h instanceof Error?h.message:String(h)),s(r,{error:"server_error",error_description:"Failed to refresh session"},500)}if(u.error||!(u.id_token||u.access_token))return c?.warn("MCP refresh rejected by IdP: %s",u.error||"empty token response"),o("Refresh rejected by identity provider");if(u.refresh_token&&(a=G(u.refresh_token,m)),u.access_token&&(d=q(u.access_token,m)),u.id_token){try{const h=M.decode(u.id_token);_=h.payload,f=h.header.kid}catch{return o("Invalid token response from identity provider")}if(_.iss!==t.claims.iss||_.sub!==t.claims.sub)return c?.warn("MCP refresh rejected: the id_token names another issuer or subject"),o("Invalid refresh token")}}const C=w(l)&&l.tokenExpirationTime||z,g=_??t.claims;if(!ne(g,l))return c?.warn("MCP refresh rejected: the id_token was issued for another project"),o("Invalid refresh token");const E=await K({claims:g,kid:f,idpId:t.idp_id,idpAccessTokenEnc:d,ttlSec:_?void 0:C}),O=await $({clientId:t.client_id,idpId:t.idp_id,claims:g,idpAccessTokenEnc:d,idpRefreshTokenEnc:a,sessionExp:t.session_exp});return s(r,{access_token:E.accessToken,token_type:"Bearer",expires_in:E.expiresIn,refresh_token:O,scope:y.join(" ")},200,{"Cache-Control":"no-store",Pragma:"no-cache"})}function Ge(r){return async e=>{if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{const n=await e.req.formData(),o=n.get("grant_type"),c=n.get("code"),i=n.get("redirect_uri")||void 0,p=n.get("code_verifier")||void 0;if(!j.includes(o))return s(e,{error:"invalid_request",error_description:"Invalid grant type"},400);if(o==="refresh_token")return await ke(e,r,n);if(!c)return s(e,{error:"invalid_request",error_description:"Missing authorization code"},400);try{const t=await Q(c);if(i&&i!==t.redirect_uri)return s(e,{error:"invalid_grant",error_description:"redirect_uri mismatch"},400);if(t.code_challenge){if(!p)return s(e,{error:"invalid_grant",error_description:"code_verifier required"},400);if(!he(t.code_challenge,t.code_challenge_method,p))return s(e,{error:"invalid_grant",error_description:"code_verifier mismatch"},400)}if(D.OAUTH_CLIENT_ID&&t.client_id&&!H(t.client_id)&&t.client_id!==D.OAUTH_CLIENT_ID)return s(e,{error:"invalid_client",error_description:"Client mismatch"},400);const l=t.id_token;if(typeof l!="string"||l.length===0)return s(e,{error:"invalid_grant",error_description:"Missing id_token in authorization code"},400);const _=await J(l,r.getConfig().ssoDirect);if(!_)return s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400);const f=t.idp_access_token_enc??(t.idp_access_token?q(t.idp_access_token,m):void 0),{accessToken:d,expiresIn:a}=await K({claims:_.payload,kid:_.kid,idpId:t.idp_id,idpAccessTokenEnc:f}),g=Math.floor(Date.now()/1e3)+ie,E=await $({clientId:t.client_id,idpId:t.idp_id,claims:_.payload,idpAccessTokenEnc:f,idpRefreshTokenEnc:t.idp_refresh_token_enc,sessionExp:g}),O={access_token:d,token_type:"Bearer",expires_in:a,refresh_token:E,scope:y.join(" ")};return s(e,O,200,{"Cache-Control":"no-store",Pragma:"no-cache"})}catch(t){return e.get("logger")?.warn("MCP token exchange failed: %s",t instanceof Error?t.message:String(t)),s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400)}}catch(n){const o=n instanceof Error?n.message:String(n);return s(e,{error:"server_error",error_description:"Failed to process token request",error_details:o},500)}}}async function J(r,e){const n=await te(e||{},{[v.AUTHORIZATION]:r});if(n.isAuthenticated!==!0)return;const{payload:o,header:c}=M.decode(r);if(o[R.CLAIM]!==R.VALUE)return{payload:o,kid:c.kid,idpId:typeof n.idpId=="string"?n.idpId:void 0}}function Be(r){return async e=>{const n=new URL(e.req.url);let o=n.searchParams.get("context");if(!o&&n.pathname.startsWith(I(`${T.MCP_CALLBACK}/`))){const i=n.pathname.split("/");o=i[i.length-1]}if(!o)return e.text("Missing context parameter",400);let c=null;try{const i=await ge(o);if(c=i.mcpSessionId||null,!i.isMcpFlow||!i.originalRedirectUri)throw new Error("Invalid MCP context");const p=r.getConfig().ssoDirect,t=U(e,v.AUTHORIZATION),l=t?await J(t,p):void 0,_=U(e,v.IDP_ACCESS_TOKEN),f=U(e,v.IDP_REFRESH_TOKEN);if(!t||!l)return A.sendMcpAuthorizationFailedMessage([{...S(c),error:"Authentication required",error_details:null}]),e.text("Authentication required",401);const d=await X({idToken:t,idpId:l.idpId??B(p),idpAccessTokenEnc:_?q(_,m):void 0,idpRefreshTokenEnc:f?G(f,m):void 0,clientId:i.mcpClientId||"",redirectUri:i.originalRedirectUri,codeChallenge:i.mcpCodeChallenge||void 0,codeChallengeMethod:i.mcpCodeChallengeMethod||void 0,ttlSec:600}),a=new URL(i.originalRedirectUri);return a.searchParams.set("code",d),i.mcpState&&a.searchParams.set("state",i.mcpState),A.sendMcpAuthorizationCompletedMessage([{...S(c),redirect_uri:i.originalRedirectUri||null}]),e.redirect(a.toString())}catch(i){const p=Ce(i);return A.sendMcpAuthorizationFailedMessage([{...S(c),...p}]),e.text(`Invalid MCP callback: ${p.error}`,400)}}}function Ce(r){return r instanceof Error&&[se,ce,de,ae,le].some(e=>r instanceof e)?{error:r.name,error_details:null}:r instanceof Error?{error:r.message,error_details:r.stack??null}:{error:String(r),error_details:null}}export{j as SUPPORTED_MCP_GRANT_TYPES,me as createMcpContextToken,Fe as mcpAuthorizationHandler,Be as mcpCallbackHandler,ze as mcpDynamicClientRegistrationHandler,$e as mcpOAuthAuthorizationServerHandler,je as mcpOAuthProtectedResourceHandler,Ge as mcpTokenPortalHandler,B as resolveMcpIdpId,ge as verifyAndParseMcpContextToken,he as verifyPkce};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/realm",
|
|
3
|
-
"version": "0.138.0-next.
|
|
3
|
+
"version": "0.138.0-next.9",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -33,13 +33,13 @@
|
|
|
33
33
|
"@opentelemetry/sdk-trace-web": "2.8.0",
|
|
34
34
|
"@opentelemetry/semantic-conventions": "1.40.0",
|
|
35
35
|
"@redocly/ajv": "8.18.0",
|
|
36
|
-
"@redocly/api-docs": "0.3.0-next.
|
|
37
|
-
"@redocly/config": "0.
|
|
36
|
+
"@redocly/api-docs": "0.3.0-next.6",
|
|
37
|
+
"@redocly/config": "0.62.0",
|
|
38
38
|
"@redocly/openapi-core": "2.55.0",
|
|
39
39
|
"@redocly/portal-legacy-ui": "0.19.0",
|
|
40
40
|
"@redocly/portal-plugin-mock-server": "0.23.0-next.0",
|
|
41
41
|
"@redocly/realm-asyncapi-sdk": "0.16.0-next.2",
|
|
42
|
-
"@redocly/theme": "0.70.0-next.
|
|
42
|
+
"@redocly/theme": "0.70.0-next.6",
|
|
43
43
|
"@shikijs/transformers": "3.21.0",
|
|
44
44
|
"@tanstack/react-query": "5.62.3",
|
|
45
45
|
"@tanstack/react-table": "8.21.3",
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
"styled-components": "^6.4.2",
|
|
92
92
|
"tty-browserify": "0.0.1",
|
|
93
93
|
"typesense": "2.1.0",
|
|
94
|
-
"ulid": "^
|
|
94
|
+
"ulid": "^3.0.2",
|
|
95
95
|
"undici": "^6.28.0",
|
|
96
96
|
"web-vitals": "3.3.1",
|
|
97
97
|
"workerpool": "9.2.0",
|
|
File without changes
|
|
File without changes
|
/package/dist/server/{plugins/mcp/gateway-mcp/fetch → web-server/utils}/ssrf-safe-lookup.d.ts
RENAMED
|
File without changes
|
|
File without changes
|