@redocly/realm 0.138.0-next.10 → 0.138.0-next.12

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 CHANGED
@@ -1,5 +1,26 @@
1
1
  # @redocly/realm
2
2
 
3
+ ## 0.138.0-next.12
4
+
5
+ ### Patch Changes
6
+
7
+ - 09404c61096: Fixed SSO login for branch previews of projects configured to use path prefixes.
8
+ - 07fca3fc6a0: Improved rendering of AsyncAPI 2.0–2.6 channels, operations, messages, servers, and traits to match AsyncAPI 3.0.
9
+ - Updated dependencies [192409c012f]
10
+ - Updated dependencies [07fca3fc6a0]
11
+ - @redocly/realm-asyncapi-sdk@0.16.0-next.3
12
+ - @redocly/api-docs@0.3.0-next.9
13
+ - @redocly/theme@0.70.0-next.7
14
+
15
+ ## 0.138.0-next.11
16
+
17
+ ### Patch Changes
18
+
19
+ - 2cd84f3f268: Updated `@redocly/openapi-core` to version `2.58.1`.
20
+ - Updated dependencies [2cd84f3f268]
21
+ - @redocly/api-docs@0.3.0-next.8
22
+ - @redocly/portal-plugin-mock-server@0.23.0-next.1
23
+
3
24
  ## 0.138.0-next.10
4
25
 
5
26
  ### Patch Changes
@@ -1 +1 @@
1
- import*as g from"react";import{withPathPrefix as x}from"@redocly/theme/core/utils";import{appendSearchParams as z}from"../utils";import{mapObject as I}from"../../utils/object/map-object.js";import{mapObjectAsync as $}from"../../utils/object/map-object-async.js";import{isFunction as A}from"../../utils/guards/is-function.js";import{isBrowser as G}from"../../utils/env/is-browser.js";import{GLOBAL_DATA_URL as N}from"../../constants/common.js";import{getClientPageDataUrl as V}from"../../utils/url/get-client-page-data-url.js";import{getSharedDataFetchUrl as B}from"../../utils/url/get-shared-data-fetch-url.js";import{normalizeRouteSlug as d}from"../../utils/path/normalize-route-slug.js";import{templates as b}from"./generated/templates.js";import{AsyncCache as S}from"./cache.js";import{fetch as y}from"./fetch.js";import{updateTranslations as F}from"../app/l10n";import{PageMissingDefaultExportError as v}from"./errors/page-missing-default-export-error.js";const i=new S,c=new S;let D=null,m,K;const u=new Set;async function W(){const o=await(await y(x(N))).json();return m=o,o}async function C(o){const t=B(o,process.env.NODE_ENV==="development");return await(await y(t)).json()}async function q(o){return c.has(o)||c.set(o,C(o)),c.get(o)}function dt(){const[,o]=g.useReducer(t=>t+1,0);g.useEffect(()=>{process.env.NODE_ENV!=="production"&&(window.__redoclyRefresh=o)},[o])}function _(o,t){const e=t?new URLSearchParams(t).get("revision"):null,a=t?new URLSearchParams(t).get("version"):null;return e||a?`${o}?${e?`revision=${encodeURIComponent(e)}`:""}${a?`&version=${encodeURIComponent(a)}`:""}`.trim():o}class p{static markdocComponents={};static async updateTemplate(t,e,a){const n=d(t);b[a]=()=>e;const r=d(window.location.pathname);n===r&&(await this.load(t,!0),window.__redoclyRefresh())}static async update(t){const e=d(t),a=d(window.location.pathname);if(e!==a){i.delete(e);return}await this.load(t,!0),window.__redoclyRefresh()}static async updateSharedData(t){await c.set(t,C(t)),window.__redoclyRefresh()}static addGlobalDataListener(t){u.add(t)}static removeGlobalDataListener(t){u.delete(t)}static updateGlobalData(t){u.forEach(e=>e(t)),m=t,F(m.l10n)}static async tryLoad(t,e,a){try{return await this.load(t,e,a)}catch{return null}}static async loadDynamicMarkdocComponents(t){const e=this.markdocComponents?.[t];e&&A(e)&&Object.assign(this.markdocComponents,await e())}static async load(t,e,a){const n=d(t),r=_(n,a);if(!m&&!D&&(D=W()),!e&&i.has(r)){const f=await i.get(r);if(f)return f}const[s]=await Promise.all([i.set(r,l(a)),D]);return s;async function l(f){const E=V(n),T=z(E,f),k=await y(T),{templateId:P,sharedDataIds:w,props:R,userData:L,versions:M,redirectTo:O,isPublic:U}=await k.json(),[j]=await Promise.all([(async()=>p.loadTemplate(P))(),Promise.all((R.dynamicMarkdocComponents||[]).map(h=>p.loadDynamicMarkdocComponents(h))),w?$(w,h=>q(h)):Promise.resolve({})]);return{templateId:P,slug:n,sharedDataIds:w,redirectTo:O,props:R,Template:j,userData:L,versions:M,isPublic:U}}}static loadSync(t){const e=d(t),a=_(e,typeof window<"u"?window.location.search:void 0),n=i.tryGetSync(a);if(!n)return;const r=I(n.sharedDataIds,s=>c.getSync(s,"Data dependency not cached ahead of time: "+s));return{...n,sharedData:r}}static async prepare(t,e,a,n){const r=t.templateId;if(await Promise.all((t.props.dynamicMarkdocComponents||[]).map(l=>this.loadDynamicMarkdocComponents(l))),e)for(const l of Object.keys(e))c.setSync(l,e[l]);const s=await p.loadTemplate(r);s&&i.setSync(t.slug,{templateId:r,slug:t.slug,props:t.props,sharedDataIds:t.sharedDataIds||{},Template:s,userData:t.userInfo,versions:t.versions,isPublic:t.isPublic}),m=a,K=n}static async loadTemplate(t){try{const e=await b[t]();if(t.endsWith("page.tsx")&&!e.default)throw new v(t);const a=e.default;return H(a)?a:a?.default}catch(e){if(!(process.env.NODE_ENV==="development")||!G())throw e;return e instanceof v?()=>(window.location.reload(),null):(window.location.reload(),null)}}static clear(){u.clear(),c.clear(),i.clear()}}(typeof window<"u"?window:global).__LOADER=p;function H(o){return typeof o=="function"||typeof o=="object"&&o!==null&&"$$typeof"in o}export{p as Loader,q as getSharedData,m as globalData,K as initLocale,dt as useRefresh};
1
+ import*as g from"react";import{withPathPrefix as x}from"@redocly/theme/core/utils";import{appendSearchParams as z}from"../utils";import{mapObject as I}from"../../utils/object/map-object.js";import{mapObjectAsync as $}from"../../utils/object/map-object-async.js";import{isFunction as A}from"../../utils/guards/is-function.js";import{isBrowser as G}from"../../utils/env/is-browser.js";import{GLOBAL_DATA_URL as N}from"../../constants/common.js";import{getClientPageDataUrl as V}from"../../utils/url/get-client-page-data-url.js";import{getSharedDataFetchUrl as B}from"../../utils/url/get-shared-data-fetch-url.js";import{normalizeRouteSlug as c}from"../../utils/path/normalize-route-slug.js";import{templates as b}from"./generated/templates.js";import{AsyncCache as S}from"./cache.js";import{fetch as y}from"./fetch.js";import{updateTranslations as F}from"../app/l10n";import{PageMissingDefaultExportError as v}from"./errors/page-missing-default-export-error.js";const l=new S,d=new S;let D=null,m,K;const u=new Set;async function W(){const o=await(await y(x(N))).json();return m=o,o}async function C(o){const t=B(o,process.env.NODE_ENV==="development");return await(await y(t)).json()}async function q(o){return d.has(o)||d.set(o,C(o)),d.get(o)}function dt(){const[,o]=g.useReducer(t=>t+1,0);g.useEffect(()=>{process.env.NODE_ENV!=="production"&&(window.__redoclyRefresh=o)},[o])}function _(o,t){const e=t?new URLSearchParams(t).get("revision"):null,a=t?new URLSearchParams(t).get("version"):null;return e||a?`${o}?${e?`revision=${encodeURIComponent(e)}`:""}${a?`&version=${encodeURIComponent(a)}`:""}`.trim():o}class p{static markdocComponents={};static async updateTemplate(t,e,a){const n=c(t);b[a]=()=>e;const r=c(window.location.pathname);n===r&&(await this.load(t,!0),window.__redoclyRefresh())}static async update(t){const e=c(t),a=c(window.location.pathname);if(e!==a){l.delete(e);return}await this.load(t,!0),window.__redoclyRefresh()}static async updateSharedData(t){await d.set(t,C(t)),window.__redoclyRefresh()}static addGlobalDataListener(t){u.add(t)}static removeGlobalDataListener(t){u.delete(t)}static updateGlobalData(t){u.forEach(e=>e(t)),m=t,F(m.l10n)}static async tryLoad(t,e,a){try{return await this.load(t,e,a)}catch{return null}}static async loadDynamicMarkdocComponents(t){const e=this.markdocComponents?.[t];e&&A(e)&&Object.assign(this.markdocComponents,await e())}static async load(t,e,a){const n=c(t),r=_(n,a);if(!m&&!D&&(D=W()),!e&&l.has(r)){const f=await l.get(r);if(f)return f}const[i]=await Promise.all([l.set(r,s(a)),D]);return i;async function s(f){const E=V(n),T=z(E,f),k=await y(T),{templateId:P,sharedDataIds:w,props:R,userData:L,versions:M,redirectTo:O,isPublic:U}=await k.json(),[j]=await Promise.all([(async()=>p.loadTemplate(P))(),Promise.all((R.dynamicMarkdocComponents||[]).map(h=>p.loadDynamicMarkdocComponents(h))),w?$(w,h=>q(h)):Promise.resolve({})]);return{templateId:P,slug:n,sharedDataIds:w,redirectTo:O,props:R,Template:j,userData:L,versions:M,isPublic:U}}}static loadSync(t){const e=c(t),a=_(e,typeof window<"u"?window.location.search:void 0),n=l.tryGetSync(a);if(!n)return;const r=I(n.sharedDataIds,i=>d.getSync(i,"Data dependency not cached ahead of time: "+i));return{...n,sharedData:r}}static async prepare(t,e,a,n){const r=t.templateId;if(await Promise.all((t.props.dynamicMarkdocComponents||[]).map(s=>this.loadDynamicMarkdocComponents(s))),e)for(const s of Object.keys(e))d.setSync(s,e[s]);const i=await p.loadTemplate(r);if(i){const s=c(t.slug);l.setSync(s,{templateId:r,slug:s,props:t.props,sharedDataIds:t.sharedDataIds||{},Template:i,userData:t.userInfo,versions:t.versions,isPublic:t.isPublic})}m=a,K=n}static async loadTemplate(t){try{const e=await b[t]();if(t.endsWith("page.tsx")&&!e.default)throw new v(t);const a=e.default;return H(a)?a:a?.default}catch(e){if(!(process.env.NODE_ENV==="development")||!G())throw e;return e instanceof v?()=>(window.location.reload(),null):(window.location.reload(),null)}}static clear(){u.clear(),d.clear(),l.clear()}}(typeof window<"u"?window:global).__LOADER=p;function H(o){return typeof o=="function"||typeof o=="object"&&o!==null&&"$$typeof"in o}export{p as Loader,q as getSharedData,m as globalData,K as initLocale,dt as useRefresh};
@@ -1 +1 @@
1
- import{REDOCLY_ROUTE_RBAC as w,REDOCLY_TEAMS_RBAC as v}from"@redocly/config";import{mcpTagSchemas as Z,schemaDefinitionTag as ee,markdocParser as U}from"@redocly/api-docs";import{withPathPrefix as q}from"@redocly/theme/core/utils";import{API_DOCS_TEMPLATE_ID as V,GRAPHQL_SPEC_SLUG as te,PUBLIC_RBAC_SCOPE_ITEM as x}from"../../../constants/common.js";import{DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER as oe,PUBLIC_API_DEFINITIONS_FOLDER as re}from"../../constants/common.js";import{API_DOCS_SHARED_DATA_PREFIX as C}from"../../constants/plugins/api-docs.js";import{fromCurrentDir as ae}from"../../utils/paths.js";import{normalizeRouteSlug as se}from"../../../utils/path/normalize-route-slug.js";import{createSlugify as ie}from"../create-slugify.js";import{apiDocLoader as ne,apiDocsLoader as ce}from"./loader.js";import{getTemplatePath as j}from"./get-template-path.js";import{storeDefinitionBundles as pe}from"./store-definition-bundles.js";import{telemetryTraceStep as ue}from"../../../cli/telemetry/helpers/trace-step.js";import{searchResolver as le}from"./search/search-resolver.js";import{getAiDocumentsStore as de}from"./search/get-ai-search-documents.js";import{getApiConfigByPath as me}from"../get-api-config.js";import{getRouteSlugsForPath as fe}from"../utils.js";import{collectItemRoutes as ge}from"./utils/collect-item-routes.js";import{applyNavItemToRoot as he}from"./utils/apply-nav-item-to-root.js";import{buildRoutesMapping as De}from"./utils/build-routes-mapping.js";import{buildItemPositions as Se}from"./utils/build-item-positions.js";import{resolveApiDocTitle as Pe}from"./utils/resolve-api-doc-title.js";import{resolveConfigRbac as ye}from"./utils/resolve-config-rbac.js";import{relativePathToBaseSlug as Ie}from"./utils/relative-path-to-base-slug.js";import{normalizeApiVersion as Ae}from"./utils/normalize-api-version.js";import{importApiDocsBuilder as W}from"./import-api-docs-builder.js";import{envConfig as $}from"../../config/env-config.js";import{logger as ve}from"../../tools/notifiers/logger.js";const T="openapi-spec-download",Re={asyncapi:"AsyncAPI Docs",graphql:"GraphQL Docs",openapi:"OpenAPI Docs"};async function st(){let t=[],r=new Map;const s=new WeakMap;let n=new Set,m={};function k(o,g,A){const P=s.get(o)??new Map;s.set(o,P);const h=g??"";return P.get(h)===A?!1:(P.set(h,A),!0)}return{id:"api-docs",requiredEntitlements:["openapi","asyncapi","graphql"],loaders:{"api-doc":ne,"api-docs":ce},processContent:async(o,g)=>{await ue("com.redocly.build.plugin.apiDocs",async()=>{o.createRequestHandler(T,ae(import.meta.url,"./spec-download.api.js")),o.addApiRoute({slug:re+"/*",requestHandlerId:T,httpMethod:"all",[v]:x,getStaticData:async()=>({props:{}})}),o.addApiRoute({slug:oe+"/*",requestHandlerId:T,httpMethod:"all",[v]:x,getStaticData:async()=>({props:{}})}),o.addApiRoute({slug:te+"/*",requestHandlerId:T,httpMethod:"all",[v]:x,getStaticData:async()=>({props:{}})});const A=o.createTemplate(V,j("../../../client/templates/api-docs/template.js")),P=o.registerServerPropsGetter(V,j("./get-server-props.js")),h=await g.getConfig();r=new Map,m={};const y=await o.loadApiDocuments(g);pe(y,o.outdir);const E=!!h.plugins?.some(e=>typeof e=="string"&&e.startsWith("@redocly/portal-plugin-async-api/"));E&&y.some(e=>e.type==="asyncapi")&&ve.warn("The plugin '@redocly/portal-plugin-async-api' is deprecated. Please remove it from your config to use built-in AsyncAPI docs.");const c=E?y.filter(e=>e.type!=="asyncapi"):y;t=c.map(({markdocChunks:e,relativePath:I,isVirtual:p,customOutputRelativeFile:a,realRelativePath:f})=>({chunks:e??[],relativePath:I,realRelativePath:f,isVirtual:a!=null||p}));const{buildItems:i}=await W(o.serverOutDir),D=(await g.cache.load("content-slugs","content-slugs")).data,S=ie(D),O=e=>se(S(e));for(const e of c){const I=e.customOutputRelativeFile||e.relativePath,p=O(I);Ee(e,p);const{items:a,store:f}=await i({type:e.type,document:e.document,options:e.options,basePath:p,markdownParser:U}),R=`${C}${e.relativePath}`,M=$.isDevelopMode?e.relativePath:void 0,u=Ce(e.document),b=u?.["x-metadata"],_=e.isVirtual?void 0:b?.apiId,N={document:e,basePath:p,sourcePath:M,apiId:_};await o.createSharedData(R,Q(N,a,f));const K=ye(e.config),F=!!e.options.excludeFromSearch,Y=e.type==="openapi",d=ge(a,K).map(l=>{const B=l.metadata?.seo,H={...we(l.link,p),fsPath:I,templateId:A,sharedData:[{key:"apiDocsStore",id:R}],serverPropsGetterIds:[P],excludeFromSearch:F,getStaticData:_e(l.label,B),[v]:l[v]};return Y?{...H,...l.httpVerb?{httpVerb:l.httpVerb,metadata:{subType:"openapi-operation"}}:{}}:H});if(!d.length)continue;const G=Pe(u,e.type,a),X=Ae(u?.version),L={type:e.type,title:G,version:X,description:typeof u?.description=="string"?u.description:void 0,summary:typeof u?.summary=="string"?u.summary:void 0,...u?.["x-metadata"]&&typeof u["x-metadata"]=="object"?u["x-metadata"]:{}};d[0]={...d[0],metadata:L,hasClientRoutes:!0,getSidebar:(l,B)=>he(z(a),p,B),getNavText:()=>G??Re[e.type]??"API Docs",getSearchDocuments:le(a,L,o.getSearchFacets,o.setSearchFacets,F,f,e.document),getAiDocumentsStore:de({items:a,metadata:L,store:f,document:e.document,specType:e.type,excludeFromSearch:F,basePath:p,getSearchFacets:o.getSearchFacets,markdocTags:{...Z,schemaDefinition:ee}})};const J=d[0];d[0]=d[d.length-1],d[d.length-1]=J;for(const l of d)o.addRoute(l);if(_){const l=me(e.config?.apis,e.realRelativePath);m[_]={[v]:l.rbac,[w]:{fsPath:I}}}r.set(R,N)}})},afterRoutesCreated:async(o,g)=>{const{buildItems:A,simplifyAstStructure:P}=await W(o.serverOutDir),h=new Set,y=new Set;for(const{chunks:c,relativePath:i,isVirtual:D}of t){const S=`${C}${i}`,O=fe(o.getAllRoutes(),i);for(const{node:e,markdown:I,pointer:p,key:a,relativePath:f}of c){const{ast:R,compoundHash:M}=await o.parseMarkdoc({input:{content:I,relativePath:f,isVirtual:D},resource:p,context:g,deps:{sharedDataIds:[`${C}${f}`],routeSlugs:O}});if(h.add(p),!(a==="graphql-docs"?e?.description?.value!==void 0:!!(a&&e[a]))||!k(e,a,M))continue;y.add(S);const b=P(R);if(a==="graphql-docs"){e.description&&(e.description.value=b);const _=`${C}${f}`;be(r.get(_),b)}else a&&(e[a]=b)}}const E=n.difference(h);for(const c of E)g.cache.delete(c);n=h,Object.entries(m).forEach(([c,i])=>{const D=i[w]?.fsPath;if(!D)return;const S=o.getRouteByFsPath(D)?.baseSlug;m[c]={...i,[w]:{...i[w],slug:S},slug:S}}),o.setGlobalData({apiProducts:m});for(const[c,i]of r){if(!y.has(c))continue;const{items:D,store:S}=await A({type:i.document.type,document:i.document.document,options:i.document.options,basePath:i.basePath,markdownParser:U});await o.createSharedData(c,Q(i,D,S))}}}}function Q({document:t,basePath:r,sourcePath:s,apiId:n},m,k){return{items:m,store:k,options:t.options,baseSlug:r,routesMapping:$.isDevelopMode?De(m,r):{},graphqlPositions:$.isDevelopMode&&t.type==="graphql"?Se(m,r):void 0,sourcePath:s,apiId:n}}function be(t,r){t?.document.type!=="graphql"||!t.document.options||(t.document.options.info={...t.document.options.info||{},description:r})}function _e(t,r){return async function(s){return{props:{settings:{baseUrlPath:q(s.baseSlug)},disableAutoScroll:!0,seo:{title:t,...r}}}}}function Ee(t,r){const s=t.options?.mockServer,n=q(`/_mock${Ie(t.relativePath)}`);s?.url===n&&(t.options={...t.options,mockServer:{...s,url:q(`/_mock${r}`)}})}function we(t,r){if(t===r)return{};const s=r==="/"?"/":`${r}/`;if(t.startsWith(s)){const n=t.slice(s.length);return n?{slugSuffix:n}:{}}return{slug:t}}function Ce(t){if(t&&typeof t=="object"&&"info"in t)return t.info}function z(t){return t.map(({content:r,items:s,...n})=>({...n,...s&&{items:z(s)}}))}export{st as apiDocsPlugin,we as toRouteSlugFields};
1
+ import{REDOCLY_ROUTE_RBAC as E,REDOCLY_TEAMS_RBAC as v}from"@redocly/config";import{mcpTagSchemas as Z,schemaDefinitionTag as ee,markdocParser as U}from"@redocly/api-docs";import{withPathPrefix as q}from"@redocly/theme/core/utils";import{API_DOCS_TEMPLATE_ID as V,GRAPHQL_SPEC_SLUG as te,PUBLIC_RBAC_SCOPE_ITEM as x}from"../../../constants/common.js";import{DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER as oe,PUBLIC_API_DEFINITIONS_FOLDER as re}from"../../constants/common.js";import{API_DOCS_SHARED_DATA_PREFIX as w}from"../../constants/plugins/api-docs.js";import{fromCurrentDir as ae}from"../../utils/paths.js";import{normalizeRouteSlug as se}from"../../../utils/path/normalize-route-slug.js";import{createSlugify as ie}from"../create-slugify.js";import{apiDocLoader as ne,apiDocsLoader as ce}from"./loader.js";import{getTemplatePath as j}from"./get-template-path.js";import{storeDefinitionBundles as pe}from"./store-definition-bundles.js";import{telemetryTraceStep as ue}from"../../../cli/telemetry/helpers/trace-step.js";import{searchResolver as de}from"./search/search-resolver.js";import{getAiDocumentsStore as le}from"./search/get-ai-search-documents.js";import{getApiConfigByPath as me}from"../get-api-config.js";import{getRouteSlugsForPath as fe}from"../utils.js";import{collectItemRoutes as he}from"./utils/collect-item-routes.js";import{isChunkCurrent as ge}from"./utils/decorators.js";import{applyNavItemToRoot as De}from"./utils/apply-nav-item-to-root.js";import{buildRoutesMapping as Se}from"./utils/build-routes-mapping.js";import{buildItemPositions as Pe}from"./utils/build-item-positions.js";import{resolveApiDocTitle as ye}from"./utils/resolve-api-doc-title.js";import{resolveConfigRbac as Ie}from"./utils/resolve-config-rbac.js";import{relativePathToBaseSlug as Ae}from"./utils/relative-path-to-base-slug.js";import{normalizeApiVersion as ve}from"./utils/normalize-api-version.js";import{importApiDocsBuilder as W}from"./import-api-docs-builder.js";import{envConfig as $}from"../../config/env-config.js";import{logger as Re}from"../../tools/notifiers/logger.js";const T="openapi-spec-download",be={asyncapi:"AsyncAPI Docs",graphql:"GraphQL Docs",openapi:"OpenAPI Docs"};async function nt(){let t=[],r=new Map;const s=new WeakMap;let n=new Set,m={};function k(o,h,A){const y=s.get(o)??new Map;s.set(o,y);const g=h??"";return y.get(g)===A?!1:(y.set(g,A),!0)}return{id:"api-docs",requiredEntitlements:["openapi","asyncapi","graphql"],loaders:{"api-doc":ne,"api-docs":ce},processContent:async(o,h)=>{await ue("com.redocly.build.plugin.apiDocs",async()=>{o.createRequestHandler(T,ae(import.meta.url,"./spec-download.api.js")),o.addApiRoute({slug:re+"/*",requestHandlerId:T,httpMethod:"all",[v]:x,getStaticData:async()=>({props:{}})}),o.addApiRoute({slug:oe+"/*",requestHandlerId:T,httpMethod:"all",[v]:x,getStaticData:async()=>({props:{}})}),o.addApiRoute({slug:te+"/*",requestHandlerId:T,httpMethod:"all",[v]:x,getStaticData:async()=>({props:{}})});const A=o.createTemplate(V,j("../../../client/templates/api-docs/template.js")),y=o.registerServerPropsGetter(V,j("./get-server-props.js")),g=await h.getConfig();r=new Map,m={};const I=await o.loadApiDocuments(h);pe(I,o.outdir);const C=!!g.plugins?.some(e=>typeof e=="string"&&e.startsWith("@redocly/portal-plugin-async-api/"));C&&I.some(e=>e.type==="asyncapi")&&Re.warn("The plugin '@redocly/portal-plugin-async-api' is deprecated. Please remove it from your config to use built-in AsyncAPI docs.");const c=C?I.filter(e=>e.type!=="asyncapi"):I;t=c.map(({markdocChunks:e,relativePath:P,isVirtual:p,customOutputRelativeFile:a,realRelativePath:f})=>({chunks:e??[],relativePath:P,realRelativePath:f,isVirtual:a!=null||p}));const{buildItems:i}=await W(o.serverOutDir),D=(await h.cache.load("content-slugs","content-slugs")).data,S=ie(D),O=e=>se(S(e));for(const e of c){const P=e.customOutputRelativeFile||e.relativePath,p=O(P);Ee(e,p);const{items:a,store:f}=await i({type:e.type,document:e.document,options:e.options,basePath:p,markdownParser:U}),R=`${w}${e.relativePath}`,M=$.isDevelopMode?e.relativePath:void 0,u=Te(e.document),b=u?.["x-metadata"],_=e.isVirtual?void 0:b?.apiId,N={document:e,basePath:p,sourcePath:M,apiId:_};await o.createSharedData(R,Q(N,a,f));const K=Ie(e.config),F=!!e.options.excludeFromSearch,Y=e.type==="openapi",l=he(a,K).map(d=>{const B=d.metadata?.seo,H={...we(d.link,p),fsPath:P,templateId:A,sharedData:[{key:"apiDocsStore",id:R}],serverPropsGetterIds:[y],excludeFromSearch:F,getStaticData:Ce(d.label,B),[v]:d[v]};return Y?{...H,...d.httpVerb?{httpVerb:d.httpVerb,metadata:{subType:"openapi-operation"}}:{}}:H});if(!l.length)continue;const G=ye(u,e.type,a),X=ve(u?.version),L={type:e.type,title:G,version:X,description:typeof u?.description=="string"?u.description:void 0,summary:typeof u?.summary=="string"?u.summary:void 0,...u?.["x-metadata"]&&typeof u["x-metadata"]=="object"?u["x-metadata"]:{}};l[0]={...l[0],metadata:L,hasClientRoutes:!0,getSidebar:(d,B)=>De(z(a),p,B),getNavText:()=>G??be[e.type]??"API Docs",getSearchDocuments:de(a,L,o.getSearchFacets,o.setSearchFacets,F,f,e.document),getAiDocumentsStore:le({items:a,metadata:L,store:f,document:e.document,specType:e.type,excludeFromSearch:F,basePath:p,getSearchFacets:o.getSearchFacets,markdocTags:{...Z,schemaDefinition:ee}})};const J=l[0];l[0]=l[l.length-1],l[l.length-1]=J;for(const d of l)o.addRoute(d);if(_){const d=me(e.config?.apis,e.realRelativePath);m[_]={[v]:d.rbac,[E]:{fsPath:P}}}r.set(R,N)}})},afterRoutesCreated:async(o,h)=>{const{buildItems:A,simplifyAstStructure:y}=await W(o.serverOutDir),g=new Set,I=new Set;for(const{chunks:c,relativePath:i,isVirtual:D}of t){const S=`${w}${i}`,O=fe(o.getAllRoutes(),i);for(const{node:e,markdown:P,pointer:p,key:a,relativePath:f}of c){const{ast:R,compoundHash:M}=await o.parseMarkdoc({input:{content:P,relativePath:f,isVirtual:D},resource:p,context:h,deps:{sharedDataIds:[`${w}${f}`],routeSlugs:O}});if(g.add(p),!(a==="graphql-docs"?e?.description?.value!==void 0:!!a&&ge(e,a,P))||!k(e,a,M))continue;I.add(S);const b=y(R);if(a==="graphql-docs"){e.description&&(e.description.value=b);const _=`${w}${f}`;_e(r.get(_),b)}else a&&(e[a]=b)}}const C=n.difference(g);for(const c of C)h.cache.delete(c);n=g,Object.entries(m).forEach(([c,i])=>{const D=i[E]?.fsPath;if(!D)return;const S=o.getRouteByFsPath(D)?.baseSlug;m[c]={...i,[E]:{...i[E],slug:S},slug:S}}),o.setGlobalData({apiProducts:m});for(const[c,i]of r){if(!I.has(c))continue;const{items:D,store:S}=await A({type:i.document.type,document:i.document.document,options:i.document.options,basePath:i.basePath,markdownParser:U});await o.createSharedData(c,Q(i,D,S))}}}}function Q({document:t,basePath:r,sourcePath:s,apiId:n},m,k){return{items:m,store:k,options:t.options,baseSlug:r,routesMapping:$.isDevelopMode?Se(m,r):{},graphqlPositions:$.isDevelopMode&&t.type==="graphql"?Pe(m,r):void 0,sourcePath:s,apiId:n}}function _e(t,r){t?.document.type!=="graphql"||!t.document.options||(t.document.options.info={...t.document.options.info||{},description:r})}function Ce(t,r){return async function(s){return{props:{settings:{baseUrlPath:q(s.baseSlug)},disableAutoScroll:!0,seo:{title:t,...r}}}}}function Ee(t,r){const s=t.options?.mockServer,n=q(`/_mock${Ae(t.relativePath)}`);s?.url===n&&(t.options={...t.options,mockServer:{...s,url:q(`/_mock${r}`)}})}function we(t,r){if(t===r)return{};const s=r==="/"?"/":`${r}/`;if(t.startsWith(s)){const n=t.slice(s.length);return n?{slugSuffix:n}:{}}return{slug:t}}function Te(t){if(t&&typeof t=="object"&&"info"in t)return t.info}function z(t){return t.map(({content:r,items:s,...n})=>({...n,...s&&{items:z(s)}}))}export{nt as apiDocsPlugin,we as toRouteSlugFields};
@@ -1,3 +1,3 @@
1
- import*as p from"node:path";import{parse as B,visit as x}from"graphql";import{Source as N,bundle as _,createConfig as D,getTotals as M,resolvePlugins as V}from"@redocly/openapi-core";import{convertSwagger2OpenAPI as j}from"@redocly/api-docs/lib/adapters/utils/convertSwagger2OpenAPI.js";import{CONFIG_FILE_NAME as k}from"../../../constants/common.js";import{MAX_BUNDLING_ERRORS_TO_PRINT as S}from"../../constants/plugins/api-docs.js";import{unique as F}from"../../../utils/array/unique.js";import{deepMerge as G}from"../../../utils/object/deep-merge.js";import{pluralize as C}from"../../../utils/string/pluralize.js";import{replaceEnvVariablesDeep as L}from"../../utils/envs/replace-env-variables-deep.js";import{logger as q}from"../../tools/notifiers/logger.js";import{reporter as h}from"../../tools/notifiers/reporter.js";import{ExternalResolver as b}from"../../fs/utils/external-ref-resolver.js";import{injectDecoratorIntoConfig as z,isPlainNonMarkdownText as H}from"./utils/decorators.js";import{getAllApiConfigsByPath as J,getApiConfigByPath as Q}from"../get-api-config.js";import{formatBundleError as U}from"./utils/format-bundle-error.js";import{createOptions as O}from"./options.js";const X=/(\.ya?ml|\.json|\.gql|\.graphql)$/,P=/(\.gql|\.graphql)$/;async function kn(n,r){if(p.posix.basename(n)===k)return[];if(P.test(n)){if(await r.isPathIgnored(n))return[];const u=await tn(n,r);return u?[u]:[]}let o;try{o=(await r.cache.load(n,"yaml")).data}catch{return[]}const t=Y(n,o);if(!t||!o||t==="graphql")return t===null&&q.verbose(`${n} file is not a recognized API spec. Skipping`),[];const{fs:c,getConfig:a,isPathIgnored:s}=r,e=t==="openapi"?await a("."):null,i=c.getFileInfo(n),f=await a(p.posix.dirname(n)),l=J(f?.apis,n,f.configPath),w=await s(n),d=[];for(const u of l){if(!u.output&&w)continue;const y=f.configPath?p.posix.dirname(f.configPath):".",g=u.output?p.posix.join(y,u.output):n;if(!await s(g)){i&&u.output&&!c.exists(g)&&c.addVirtualFile(g,i);try{const m=await on({relativePath:n,outputRelativePath:g,loaded:o,specType:t,rootConfig:e,config:f,apiConfig:u,fileRecord:i,fs:c});m&&d.push(m)}catch(m){await h.panicOnBuildContentError(m)}}}return d}async function qn(n,r){const{fs:o,cache:t}=r,c=[];for(const a of o.scan(X)){let s;try{s=await t.load(a.relativePath,"api-doc")}catch{continue}if(s.data?.length)for(const e of s.data)c.push({...e,isVirtual:a.isVirtual||e.isVirtual,hash:s.compoundHash})}return c}function K(n){const r=[];return x(n,{leave:o=>{!("description"in o)||!o.description?.value||!("name"in o)||H(o.description.value)||r.push({node:o,pointer:`#/types/${o?.name?.value}`,markdown:o.description.value,relativePath:"",key:"graphql-docs"})}}),r}async function W(n){try{const r=B(n);return{document:r,markdocChunks:K(r)}}catch(r){await h.panicOnBuildContentError(r);return}}function Y(n,r){return P.test(n)?"graphql":r?.openapi||r?.swagger?"openapi":r?.asyncapi?"asyncapi":null}function $(n,r){return r?{...n,info:{...n.info,"x-metadata":{...n.info?.["x-metadata"],...r}}}:n}async function Z(n,r,o){if(n==="openapi"){const t=r.swagger!==void 0?await j(r):r;return $(t,o)}return $(r,o)}async function nn(n){const{outputRelativePath:r,rootConfig:o,config:t,apiConfig:c,fs:a}=n,s=G({decorators:o?.decorators},t,{rbac:void 0},c),{resolvedObj:e}=L(s);if(e.decorators&&typeof e.decorators!="object"){await h.panicOnBuildContentError(`'decorators' must be an object at redocly.yaml, got '${typeof e.decorators}'`);return}if(e.plugins&&!Array.isArray(e.plugins)){await h.panicOnBuildContentError(`'plugins' must be an array at redocly.yaml, got '${typeof e.plugins}'`);return}const i=p.resolve(a.cwd,t.realConfigPath||k),f=new b(a),l=await D(z(e,r,a.cwd),{configPath:i,externalRefResolver:f});return o?.plugins&&o.configPath!==t.configPath&&(l.plugins=F([...l.plugins||[],...await V(o.plugins,a.cwd)],w=>w.id)),{coreConfig:l,mergedConfig:e}}async function rn(n,r,o){const t=M(n),c=[t.errors&&`${t.errors} ${C(t.errors,"error","errors")}`,t.warnings&&`${t.warnings} ${C(t.warnings,"warning","warnings")}`,t.ignored&&`${t.ignored} ignored`].filter(Boolean).join(", ");if(t.errors){const a=n.filter(i=>i.severity==="error");let s=`${c} while bundling ${r} definition:
2
- `;const e=Math.min(a.length,S);for(let i=0;i<e;i++){const f=await U(a[i],o);s+=`${f}
3
- `}if(a.length>e){const i=a.length-e;s+=`... and ${i} more ${C(i,"error","errors")}`}return await h.panicOnBuild(s),!0}return t.warnings&&q.warn(`${c} while bundling %rp definition`,r),!1}async function on(n){const{relativePath:r,outputRelativePath:o,loaded:t,specType:c,rootConfig:a,config:s,apiConfig:e,fileRecord:i,fs:f}=n,l=await nn(n);if(!l)return;const{coreConfig:w,mergedConfig:d}=l,u=p.resolve(f.cwd,r),y=new b(f),g={config:w,base:p.dirname(u),doc:{source:new N(u,await f.read(r)),parsed:JSON.parse(JSON.stringify(t))},externalRefResolver:y},{bundle:{parsed:R},problems:m,visitorsData:v}=await _(g);if(await rn(m,o,f.cwd))return;const I=v["markdown/markdown"]?.markdocChunks??[],A=d.metadata,E=await Z(c,R,A),T=O(c,E,{rootConfig:a,config:s,apiConfig:e,mergedConfig:d,relativePath:o});return{type:c,document:E,config:d,relativePath:o,realRelativePath:i?.realRelativePath||o,customOutputRelativeFile:e.output?o:void 0,markdocChunks:I,isVirtual:i?.isVirtual,options:T}}async function tn(n,r){const{fs:o,getConfig:t}=r,c=await o.read(n),a=o.getFileInfo(n),{document:s,markdocChunks:e}=await W(c)||{};if(!s){await h.panicOnBuildContentError(new Error("Failed to build GraphQL document"));return}const i=await t(),f=Q(i.apis,n);i.graphql?.info?.description&&e?.unshift({node:{description:{value:i.graphql?.info?.description}},pointer:"#/info",markdown:i.graphql?.info?.description,relativePath:"",key:"graphql-docs"});const l=O("graphql",s,{rootConfig:null,config:i,apiConfig:f??{},mergedConfig:{graphql:{...i.graphql??{},...f?.graphql??{}}},relativePath:n});return{type:"graphql",document:s,config:{...i,...f},relativePath:n,realRelativePath:a?.realRelativePath||n,markdocChunks:e,isVirtual:a?.isVirtual,options:l}}export{kn as apiDocLoader,qn as apiDocsLoader,K as collectGraphqlMarkdocChunks};
1
+ import*as l from"node:path";import{parse as B,visit as x}from"graphql";import{Source as D,bundle as N,createConfig as _,getTotals as M,resolvePlugins as V}from"@redocly/openapi-core";import{convertSwagger2OpenAPI as j}from"@redocly/api-docs/lib/adapters/utils/convertSwagger2OpenAPI.js";import{normalizeAsyncApiDefinition as S}from"@redocly/api-docs/lib/adapters/utils/normalizeAsyncApiDefinition.js";import{CONFIG_FILE_NAME as q}from"../../../constants/common.js";import{MAX_BUNDLING_ERRORS_TO_PRINT as F}from"../../constants/plugins/api-docs.js";import{unique as G}from"../../../utils/array/unique.js";import{deepMerge as L}from"../../../utils/object/deep-merge.js";import{pluralize as k}from"../../../utils/string/pluralize.js";import{replaceEnvVariablesDeep as z}from"../../utils/envs/replace-env-variables-deep.js";import{logger as b}from"../../tools/notifiers/logger.js";import{reporter as h}from"../../tools/notifiers/reporter.js";import{ExternalResolver as O}from"../../fs/utils/external-ref-resolver.js";import{collectCopiedDescriptionChunks as H,injectDecoratorIntoConfig as J,isPlainNonMarkdownText as Q}from"./utils/decorators.js";import{getAllApiConfigsByPath as U,getApiConfigByPath as X}from"../get-api-config.js";import{formatBundleError as K}from"./utils/format-bundle-error.js";import{createOptions as P}from"./options.js";const W=/(\.ya?ml|\.json|\.gql|\.graphql)$/,$=/(\.gql|\.graphql)$/;async function On(n,o){if(l.posix.basename(n)===q)return[];if($.test(n)){if(await o.isPathIgnored(n))return[];const u=await an(n,o);return u?[u]:[]}let r;try{r=(await o.cache.load(n,"yaml")).data}catch{return[]}const t=nn(n,r);if(!t||!r||t==="graphql")return t===null&&b.verbose(`${n} file is not a recognized API spec. Skipping`),[];const{fs:s,getConfig:a,isPathIgnored:c}=o,e=t==="openapi"?await a("."):null,i=s.getFileInfo(n),f=await a(l.posix.dirname(n)),p=U(f?.apis,n,f.configPath),w=await c(n),d=[];for(const u of p){if(!u.output&&w)continue;const y=f.configPath?l.posix.dirname(f.configPath):".",g=u.output?l.posix.join(y,u.output):n;if(!await c(g)){i&&u.output&&!s.exists(g)&&s.addVirtualFile(g,i);try{const m=await en({relativePath:n,outputRelativePath:g,loaded:r,specType:t,rootConfig:e,config:f,apiConfig:u,fileRecord:i,fs:s});m&&d.push(m)}catch(m){await h.panicOnBuildContentError(m)}}}return d}async function Pn(n,o){const{fs:r,cache:t}=o,s=[];for(const a of r.scan(W)){let c;try{c=await t.load(a.relativePath,"api-doc")}catch{continue}if(c.data?.length)for(const e of c.data)s.push({...e,isVirtual:a.isVirtual||e.isVirtual,hash:c.compoundHash})}return s}function Y(n){const o=[];return x(n,{leave:r=>{!("description"in r)||!r.description?.value||!("name"in r)||Q(r.description.value)||o.push({node:r,pointer:`#/types/${r?.name?.value}`,markdown:r.description.value,relativePath:"",key:"graphql-docs"})}}),o}async function Z(n){try{const o=B(n);return{document:o,markdocChunks:Y(o)}}catch(o){await h.panicOnBuildContentError(o);return}}function nn(n,o){return $.test(n)?"graphql":o?.openapi||o?.swagger?"openapi":o?.asyncapi?"asyncapi":null}function v(n,o){return o?{...n,info:{...n.info,"x-metadata":{...n.info?.["x-metadata"],...o}}}:n}async function on(n,o,r){if(n==="openapi"){const t=o.swagger!==void 0?await j(o):o;return v(t,r)}return v(S(o),r)}async function rn(n){const{outputRelativePath:o,rootConfig:r,config:t,apiConfig:s,fs:a}=n,c=L({decorators:r?.decorators},t,{rbac:void 0},s),{resolvedObj:e}=z(c);if(e.decorators&&typeof e.decorators!="object"){await h.panicOnBuildContentError(`'decorators' must be an object at redocly.yaml, got '${typeof e.decorators}'`);return}if(e.plugins&&!Array.isArray(e.plugins)){await h.panicOnBuildContentError(`'plugins' must be an array at redocly.yaml, got '${typeof e.plugins}'`);return}const i=l.resolve(a.cwd,t.realConfigPath||q),f=new O(a),p=await _(J(e,o,a.cwd),{configPath:i,externalRefResolver:f});return r?.plugins&&r.configPath!==t.configPath&&(p.plugins=G([...p.plugins||[],...await V(r.plugins,a.cwd)],w=>w.id)),{coreConfig:p,mergedConfig:e}}async function tn(n,o,r){const t=M(n),s=[t.errors&&`${t.errors} ${k(t.errors,"error","errors")}`,t.warnings&&`${t.warnings} ${k(t.warnings,"warning","warnings")}`,t.ignored&&`${t.ignored} ignored`].filter(Boolean).join(", ");if(t.errors){const a=n.filter(i=>i.severity==="error");let c=`${s} while bundling ${o} definition:
2
+ `;const e=Math.min(a.length,F);for(let i=0;i<e;i++){const f=await K(a[i],r);c+=`${f}
3
+ `}if(a.length>e){const i=a.length-e;c+=`... and ${i} more ${k(i,"error","errors")}`}return await h.panicOnBuild(c),!0}return t.warnings&&b.warn(`${s} while bundling %rp definition`,o),!1}async function en(n){const{relativePath:o,outputRelativePath:r,loaded:t,specType:s,rootConfig:a,config:c,apiConfig:e,fileRecord:i,fs:f}=n,p=await rn(n);if(!p)return;const{coreConfig:w,mergedConfig:d}=p,u=l.resolve(f.cwd,o),y=new O(f),g={config:w,base:l.dirname(u),doc:{source:new D(u,await f.read(o)),parsed:JSON.parse(JSON.stringify(t))},externalRefResolver:y},{bundle:{parsed:E},problems:m,visitorsData:A}=await N(g);if(await tn(m,r,f.cwd))return;const C=A["markdown/markdown"]?.markdocChunks??[],I=d.metadata,R=await on(s,E,I);s==="asyncapi"&&C.push(...H(R,C));const T=P(s,R,{rootConfig:a,config:c,apiConfig:e,mergedConfig:d,relativePath:r});return{type:s,document:R,config:d,relativePath:r,realRelativePath:i?.realRelativePath||r,customOutputRelativeFile:e.output?r:void 0,markdocChunks:C,isVirtual:i?.isVirtual,options:T}}async function an(n,o){const{fs:r,getConfig:t}=o,s=await r.read(n),a=r.getFileInfo(n),{document:c,markdocChunks:e}=await Z(s)||{};if(!c){await h.panicOnBuildContentError(new Error("Failed to build GraphQL document"));return}const i=await t(),f=X(i.apis,n);i.graphql?.info?.description&&e?.unshift({node:{description:{value:i.graphql?.info?.description}},pointer:"#/info",markdown:i.graphql?.info?.description,relativePath:"",key:"graphql-docs"});const p=P("graphql",c,{rootConfig:null,config:i,apiConfig:f??{},mergedConfig:{graphql:{...i.graphql??{},...f?.graphql??{}}},relativePath:n});return{type:"graphql",document:c,config:{...i,...f},relativePath:n,realRelativePath:a?.realRelativePath||n,markdocChunks:e,isVirtual:a?.isVirtual,options:p}}export{On as apiDocLoader,Pn as apiDocsLoader,Y as collectGraphqlMarkdocChunks};
@@ -1,5 +1,16 @@
1
1
  import type { RawUniversalConfig } from '@redocly/openapi-core';
2
+ import type { MarkdocChunk } from '../loader.js';
2
3
  export declare function isPlainNonMarkdownText(markdown: string): boolean;
4
+ /**
5
+ * False once `node[key]` holds a string other than the chunk's markdown: the value was replaced
6
+ * after bundling (e.g. by an AsyncAPI 2.x trait), so writing the chunk back would restore it.
7
+ */
8
+ export declare function isChunkCurrent(node: Record<string, unknown>, key: string, markdown: string): boolean;
9
+ /**
10
+ * Chunks for `description` strings that were copied onto other objects after bundling (AsyncAPI
11
+ * traits), each taking the parse source of the chunk whose markdown it copies.
12
+ */
13
+ export declare function collectCopiedDescriptionChunks(document: unknown, chunks: MarkdocChunk[]): MarkdocChunk[];
3
14
  export declare const MarkdownParserDecorator: ({ outputRelativePath, cwd, }: {
4
15
  outputRelativePath: string;
5
16
  cwd: string;
@@ -1 +1 @@
1
- import b from"node:path";import{REDOCLY_TEAMS_RBAC as p}from"@redocly/config";import{slash as k}from"../../../../utils/path/slash.js";function f(t,r,a){if(!r)throw new Error("Visitor required");if(t){t.description&&r(t,"description",a.location.absolutePointer+"/description",a);for(const o in t["x-enumDescriptions"])r(t["x-enumDescriptions"],o,a.location.absolutePointer+"/x-enumDescriptions/"+o,a)}}const x=/^[a-zA-Z0-9\s\-,.!?:;'"()/]+$/,d=/^[^\S\r\n\u2028\u2029]*(?:[-+*]\s|\d+[.)]\s)/m;function g(t){return x.test(t)&&!d.test(t)}const s=({outputRelativePath:t,cwd:r})=>{const a=function(e,u,l,m){const{node:n}=m.resolve(e[u]);if(!n||typeof n!="string"||g(n))return;const i=m.getVisitorData();i.markdocChunks=i.markdocChunks||[],i.markdocChunks.push({node:e,pointer:k(b.relative(r,l)),markdown:n,relativePath:t,key:u})};return{any:{leave(o,e){f(o,a,e)}},ref:{leave(o,e){f(o,a,e)}}}},c=()=>{const t={};return{Tag:{enter(r){r?.["x-rbac"]&&(t[String(r.name)]=r["x-rbac"])}},any:{leave(r,a){if((a.type.name==="Operation"||a.type.name==="Webhook")&&"tags"in r&&Array.isArray(r.tags))for(const o of r.tags)t[o]&&(r["x-rbac"]=r["x-rbac"]||t[o]);r?.["x-rbac"]&&(r[p]=r["x-rbac"],delete r["x-rbac"])}},ref:{leave(r){r?.["x-rbac"]&&(r[p]=r["x-rbac"],delete r["x-rbac"])}}}};function D(t,r,a){const o={id:"markdown",decorators:{oas2:{markdown:s,rbac:c},oas3:{markdown:s,rbac:c},async3:{markdown:s,rbac:c}}};return{...t,decorators:{"markdown/rbac":"on","markdown/markdown":{outputRelativePath:r,cwd:a},...t.decorators||{}},plugins:[...t.plugins||[],o]}}export{s as MarkdownParserDecorator,c as RbacDecorator,D as injectDecoratorIntoConfig,g as isPlainNonMarkdownText};
1
+ import k from"node:path";import{REDOCLY_TEAMS_RBAC as m}from"@redocly/config";import{slash as w}from"../../../../utils/path/slash.js";function d(t,r,n){if(!r)throw new Error("Visitor required");if(t){t.description&&r(t,"description",n.location.absolutePointer+"/description",n);for(const o in t["x-enumDescriptions"])r(t["x-enumDescriptions"],o,n.location.absolutePointer+"/x-enumDescriptions/"+o,n)}}const b=/^[a-zA-Z0-9\s\-,.!?:;'"()/]+$/,l=/^[^\S\r\n\u2028\u2029]*(?:[-+*]\s|\d+[.)]\s)/m;function g(t){return b.test(t)&&!l.test(t)}function C(t,r,n){const o=t[r];return!!o&&(typeof o!="string"||o===n)}function M(t,r){const n=new Map,o=new Map;for(const e of r)e.key==="description"&&(n.has(e.markdown)||n.set(e.markdown,e),o.set(e.node,e.markdown));const a=[],c=new Set,f=e=>{if(typeof e!="object"||e===null||c.has(e))return;c.add(e);const{description:s}=e;if(typeof s=="string"&&o.get(e)!==s){const i=n.get(s);i&&a.push({...i,node:e})}for(const i of Object.values(e))f(i)};return f(t),a}const p=({outputRelativePath:t,cwd:r})=>{const n=function(a,c,f,e){const{node:s}=e.resolve(a[c]);if(!s||typeof s!="string"||g(s))return;const i=e.getVisitorData();i.markdocChunks=i.markdocChunks||[],i.markdocChunks.push({node:a,pointer:w(k.relative(r,f)),markdown:s,relativePath:t,key:c})};return{any:{leave(o,a){d(o,n,a)}},ref:{leave(o,a){d(o,n,a)}}}},u=()=>{const t={};return{Tag:{enter(r){r?.["x-rbac"]&&(t[String(r.name)]=r["x-rbac"])}},any:{leave(r,n){if((n.type.name==="Operation"||n.type.name==="Webhook")&&"tags"in r&&Array.isArray(r.tags))for(const o of r.tags)t[o]&&(r["x-rbac"]=r["x-rbac"]||t[o]);r?.["x-rbac"]&&(r[m]=r["x-rbac"],delete r["x-rbac"])}},ref:{leave(r){r?.["x-rbac"]&&(r[m]=r["x-rbac"],delete r["x-rbac"])}}}};function D(t,r,n){const o={id:"markdown",decorators:{oas2:{markdown:p,rbac:u},oas3:{markdown:p,rbac:u},async2:{markdown:p,rbac:u},async3:{markdown:p,rbac:u}}};return{...t,decorators:{"markdown/rbac":"on","markdown/markdown":{outputRelativePath:r,cwd:n},...t.decorators||{}},plugins:[...t.plugins||[],o]}}export{p as MarkdownParserDecorator,u as RbacDecorator,M as collectCopiedDescriptionChunks,D as injectDecoratorIntoConfig,C as isChunkCurrent,g as isPlainNonMarkdownText};
@@ -1 +1 @@
1
- import{REDOCLY_TEAMS_RBAC as $}from"@redocly/config";import{removeLeadingSlash as T}from"@redocly/theme/core/utils";import{toKebabCase as y}from"../../../../../../utils/string/to-kebab-case.js";import{promiseMapLimit as E}from"../../../../../utils/async/promise-map-limit.js";import{promiseMapLimitWithStatus as x}from"../../../../../utils/async/promise-map-limit-with-status.js";import{extractTeamsFromScopeItems as R}from"../../../../../utils/rbac.js";import{removeMarkdocTags as w}from"../../../../../../markdoc/helpers/remove-markdoc-tags.js";import{BaseApiEntitiesExtractor as b}from"./base.js";import{OpenapiSchemaResolver as S}from"../../../utils/openapi-schema-resolver.js";import{extractPartsFromComponentsRef as A}from"../../../utils/extract-parts-from-components-ref.js";import{isValidTagName as v}from"../../../utils/is-valid-tag-name.js";const N=["get","post","put","delete","patch"],d=15,O=15;class V extends b{#e;constructor(e){super("openapi",e),this.#e=e.context.logger}async loadApiDescriptions(){return(await this.actions.loadApiDocuments(this.context)).filter(o=>o.type==="openapi").map(o=>({...o,isVirtual:o.isVirtual??!1}))}mapApiDescriptionToEntity(e,o){const s=T(e.realRelativePath),r=e.document.info.title,t=this.resolveEntityKey({realRelativePath:e.realRelativePath,customKey:e.document["x-redocly-catalog-key"]});return{type:this.type,key:t,title:r,summary:w(typeof e.document.info.description=="string"?e.document.info.description:void 0),tags:e.document.tags?.filter(a=>a.name&&v(a.name)).map(a=>a.name),metadata:{specType:this.specType,descriptionFile:s},version:o}}async processApiDescription(e,o,s,r){if(S.clearSchemaCache(),!e?.document)throw new Error("OpenAPI definition is missing a bundled document");const t=this.getRbacTeamsForDefinition(e.relativePath),a=this.mapApiDescriptionToEntity(e,s);await this.catalogEntitiesService.createEntity({entity:a,sourceFile:e.realRelativePath,fileHash:e.hash,source:"file",isRootEntity:!0,rbacTeams:t,revision:o}),r.delete(`${a.key}:${s}`),await this.#r(e,a.key,a.version,s,o,r,t);const n=this.#s(e.document.paths);if(n.length!==0){const i=(await x(n,d,async({operation:m,path:h,method:f})=>{if(!m.operationId)return;const l=await this.#n(m,h,f,e,a.key,a.version,o,t);r.delete(`${l}:${s}`)},this.#e)).count.failed;i>0&&this.#e.warn(`Extraction completed with ${i} failures out of ${n.length} operations for ${a.key}`)}}#s(e){return Object.entries(e??{}).flatMap(([o,s])=>N.filter(r=>s[r]).map(r=>{const t=s[r];if(!t)throw new Error(`Operation not found for method ${r} on path ${o}`);return{operation:t,path:o,method:r.toUpperCase()}}))}async#r(e,o,s,r,t,a,n){const c=e.document.components?.schemas;if(!c)return;const i=Object.entries(c);i.length!==0&&await E(i,O,async([m,h])=>{const f=`${o}-${y(m)}`,l=await this.#o({schemaName:m,schema:h,description:e,parentKey:o,parentVersion:s,parentRevision:t,rbacTeams:n});a.delete(`${f}:${r}`),l&&await this.catalogEntitiesService.createEntityRelation({sourceKey:o,sourceVersion:s,sourceRevision:t,type:"uses",targetKey:l.key,targetVersion:s,targetRevision:t,sourceFile:e.realRelativePath,fileHash:e.hash})})}async#o({schemaName:e,schema:o,description:s,parentKey:r,parentVersion:t,parentRevision:a,rbacTeams:n}){const c=S.resolveSchemaRefs(o,s.document),i=JSON.stringify(c),m=c[$]?R(c[$]):n,h=R(c["x-rbac"]),f={type:"data-schema",key:`${r}-${y(e)}`,title:e,summary:(typeof c.description=="string"?c.description:c.title)??null,tags:[],metadata:{specType:this.specType,schema:i},version:t};try{return await this.catalogEntitiesService.createEntity({entity:f,sourceFile:s.realRelativePath,fileHash:s.hash,rbacTeams:h.length?h:m,source:"file",revision:a})}catch(l){return this.#e.error(`Failed to create data schema entity for ${e}: ${l instanceof Error?l.message:"Unknown error"}`),null}}async#n(e,o,s,r,t,a,n,c){const i=`${t}-${y(`${e.operationId}`)}`,m=await Promise.all([this.#m(e.requestBody??null,r),this.#h(e.responses??{},r)]),h=m[0].schemaNames,f=m[1].schemaNames;m[0].errors.length>0&&this.#e.warn(`Schema extraction errors for operation ${e.operationId}: ${m[0].errors.join(", ")}`),m[1].errors.length>0&&this.#e.warn(`Response schema extraction errors for operation ${e.operationId}: ${m[1].errors.join(", ")}`);const l=h.map(p=>`${t}-${y(p)}`),u=f.map(p=>`${t}-${y(p)}`),g=await this.#f({path:o,method:s,operation:e,requestBodySchemasKeys:l,responseSchemasKeys:u,description:r,parentKey:t,parentVersion:a,parentRevision:n,rbacTeams:c});return g&&await Promise.all([this.#c(h,e.operationId??"",r.realRelativePath,r.hash,t,a,n),this.#i(f,e.operationId??"",r.realRelativePath,r.hash,t,a,n),this.#p({apiOperationKey:g.key,operationRelations:e["x-catalogRelations"],descriptionUniqueKey:t,description:r,parentVersion:a,parentRevision:n})]),i}async#c(e,o,s,r,t,a,n){return e.length===0||!o?[]:await this.#a(e,o,s,r,t,a,n)}async#i(e,o,s,r,t,a,n){return e.length===0||!o?[]:await this.#a(e,o,s,r,t,a,n)}#m(e,o){const s=[],r=[];try{if(e&&"content"in e&&e.content)for(const t of Object.values(e.content)){if(!t.schema?.$ref)continue;const a=this.#t(t.schema.$ref);a?.componentType==="schemas"&&a.componentName&&s.push(a.componentName)}if(e&&e.$ref){const t=this.#t(e.$ref);if(t?.componentType==="requestBodies"&&t.componentName){const a=o.document.components?.requestBodies?.[t.componentName];if(a&&"content"in a)for(const n of Object.values(a.content??{})){if(!n.schema?.$ref)continue;const c=this.#t(n.schema.$ref);c?.componentType==="schemas"&&c.componentName&&s.push(c.componentName)}}}}catch(t){r.push(`Failed to extract request body schemas: ${t instanceof Error?t.message:"Unknown error"}`)}return{schemaNames:[...new Set(s)],errors:r}}#h(e,o){const s=[],r=[];try{for(const t of Object.values(e)){if(t&&"content"in t&&t.content)for(const a of Object.values(t.content)){if(!a.schema?.$ref)continue;const n=this.#t(a.schema.$ref);n?.componentType==="schemas"&&n.componentName&&s.push(n.componentName)}if(t&&t.$ref){const a=this.#t(t.$ref);if(a?.componentType==="responses"&&a.componentName){const n=o.document.components?.responses?.[a.componentName];if(n&&"content"in n)for(const c of Object.values(n.content??{})){if(!c.schema?.$ref)continue;const i=this.#t(c.schema.$ref);i?.componentType==="schemas"&&i.componentName&&s.push(i.componentName)}}}}}catch(t){r.push(`Failed to extract response schemas: ${t instanceof Error?t.message:"Unknown error"}`)}return{schemaNames:[...new Set(s)],errors:r}}#t(e){try{return A(e)}catch{return{componentType:"",componentName:null}}}async#a(e,o,s,r,t,a,n){return e.length===0?[]:await E(e,O,async c=>await this.#l({operationId:o,schemaName:c,descriptionSourceFile:s,descriptionHash:r,parentKey:t,parentVersion:a,parentRevision:n}))}async#l({operationId:e,schemaName:o,descriptionSourceFile:s,descriptionHash:r,parentKey:t,parentVersion:a,parentRevision:n}){const c=`${t}-${y(o)}`;try{return await this.catalogEntitiesService.createEntityRelation({sourceKey:`${t}-${y(e)}`,type:"uses",targetKey:c,sourceFile:s,fileHash:r,sourceVersion:a,targetVersion:a,sourceRevision:n,targetRevision:n}),c}catch(i){throw this.#e.error(`Failed to create relation between operation ${e} and schema ${o}: ${i instanceof Error?i.message:"Unknown error"}`),i}}async#f({path:e,method:o,operation:s,requestBodySchemasKeys:r,responseSchemasKeys:t,description:a,parentKey:n,parentVersion:c,parentRevision:i,rbacTeams:m}){const h=`${s.operationId}`,f=`${n}-${y(h)}`,l=s[$]?R(s[$]):m,u=R(s["x-rbac"]),g={type:"api-operation",key:f,title:h,summary:w(s.summary),tags:s.tags?.filter(p=>v(p)),metadata:{path:e,method:o,payload:r,responses:t},version:c};try{return await this.catalogEntitiesService.createEntity({entity:g,sourceFile:a.realRelativePath,fileHash:a.hash,rbacTeams:u.length>0?u:l,source:"file",revision:i})}catch(p){return this.#e.error(`Failed to create API operation entity for ${h}: ${p instanceof Error?p.message:"Unknown error"}`),null}}async#p({apiOperationKey:e,operationRelations:o,descriptionUniqueKey:s,description:r,parentVersion:t,parentRevision:a}){try{await this.catalogEntitiesService.createEntityRelation({sourceKey:e,type:"partOf",targetKey:s,sourceFile:r.realRelativePath,fileHash:r.hash,sourceVersion:t,targetVersion:t,sourceRevision:a,targetRevision:a}),await this.#y(e,r,o,t,a)}catch(n){this.#e.error(`Failed to create API operation relations for ${e}: ${n instanceof Error?n.message:"Unknown error"}`)}}async#y(e,o,s,r,t){s?.length&&await E(s,d,async a=>{try{this.validateEntityRelationFileSchema(a),await this.catalogEntitiesService.createEntityRelation({sourceKey:e,type:a.type,targetKey:a.key,sourceFile:o.realRelativePath,fileHash:o.hash,sourceVersion:r,targetVersion:"",sourceRevision:t,targetRevision:""})}catch(n){this.context.logger.warn(`Error creating entity relation for operation ${e} based on custom property: ${n instanceof Error?n.message:"Unknown error"}`)}})}}export{V as OpenApiEntitiesExtractor};
1
+ import{REDOCLY_TEAMS_RBAC as R}from"@redocly/config";import{removeLeadingSlash as O}from"@redocly/theme/core/utils";import{toKebabCase as y}from"../../../../../../utils/string/to-kebab-case.js";import{promiseMapLimit as E}from"../../../../../utils/async/promise-map-limit.js";import{promiseMapLimitWithStatus as x}from"../../../../../utils/async/promise-map-limit-with-status.js";import{extractTeamsFromScopeItems as $}from"../../../../../utils/rbac.js";import{removeMarkdocTags as S}from"../../../../../../markdoc/helpers/remove-markdoc-tags.js";import{BaseApiEntitiesExtractor as b}from"./base.js";import{OpenapiSchemaResolver as w}from"../../../utils/openapi-schema-resolver.js";import{extractPartsFromComponentsRef as N}from"../../../utils/extract-parts-from-components-ref.js";import{isValidTagName as d}from"../../../utils/is-valid-tag-name.js";const A=["get","post","put","delete","patch"],T=15,v=15;class V extends b{#e;constructor(e){super("openapi",e),this.#e=e.context.logger}async loadApiDescriptions(){return(await this.actions.loadApiDocuments(this.context)).filter(o=>o.type==="openapi").map(o=>({...o,isVirtual:o.isVirtual??!1}))}mapApiDescriptionToEntity(e,o){const n=O(e.realRelativePath),s=e.document.info.title,t=this.resolveEntityKey({realRelativePath:e.realRelativePath,customKey:e.document["x-redocly-catalog-key"]});return{type:this.type,key:t,title:s,summary:S(typeof e.document.info.description=="string"?e.document.info.description:void 0),tags:e.document.tags?.filter(a=>a.name&&d(a.name)).map(a=>a.name),metadata:{specType:this.specType,descriptionFile:n},version:o}}async processApiDescription(e,o,n,s){if(w.clearSchemaCache(),!e?.document)throw new Error("OpenAPI definition is missing a bundled document");const t=this.getRbacTeamsForDefinition(e.relativePath),a=this.mapApiDescriptionToEntity(e,n);await this.catalogEntitiesService.createEntity({entity:a,sourceFile:e.realRelativePath,fileHash:e.hash,source:"file",isRootEntity:!0,rbacTeams:t,revision:o}),s.delete(`${a.key}:${n}`),await this.#o(e,a.key,a.version,n,o,s,t);const r=this.#n(e.document.paths);if(r.length!==0){const i=(await x(r,T,async({operation:m,path:h,method:f})=>{if(!m.operationId)return;const l=await this.#c(m,h,f,e,a.key,a.version,o,t);s.delete(`${l}:${n}`)},this.#e)).count.failed;i>0&&this.#e.warn(`Extraction completed with ${i} failures out of ${r.length} operations for ${a.key}`)}}#n(e){return Object.entries(e??{}).flatMap(([o,n])=>A.filter(s=>n[s]).map(s=>{const t=n[s];if(!t)throw new Error(`Operation not found for method ${s} on path ${o}`);return{operation:t,path:o,method:s.toUpperCase()}}))}async#o(e,o,n,s,t,a,r){const c=e.document.components?.schemas;if(!c)return;const i=Object.entries(c);i.length!==0&&await E(i,v,async([m,h])=>{const f=`${o}-${y(m)}`,l=await this.#r({schemaName:m,schema:h,description:e,parentKey:o,parentVersion:n,parentRevision:t,rbacTeams:r});a.delete(`${f}:${s}`),l&&await this.catalogEntitiesService.createEntityRelation({sourceKey:o,sourceVersion:n,sourceRevision:t,type:"uses",targetKey:l.key,targetVersion:n,targetRevision:t,sourceFile:e.realRelativePath,fileHash:e.hash})})}async#r({schemaName:e,schema:o,description:n,parentKey:s,parentVersion:t,parentRevision:a,rbacTeams:r}){const c=w.resolveSchemaRefs(o,n.document),i=JSON.stringify(c),m=c[R]?$(c[R]):r,h=$(c["x-rbac"]),f={type:"data-schema",key:`${s}-${y(e)}`,title:e,summary:(typeof c.description=="string"?c.description:c.title)??null,tags:[],metadata:{specType:this.specType,schema:i},version:t};try{return await this.catalogEntitiesService.createEntity({entity:f,sourceFile:n.realRelativePath,fileHash:n.hash,rbacTeams:h.length?h:m,source:"file",revision:a})}catch(l){return this.#e.error(`Failed to create data schema entity for ${e}: ${l instanceof Error?l.message:"Unknown error"}`),null}}async#c(e,o,n,s,t,a,r,c){const i=`${t}-${y(`${e.operationId}`)}`,m=await Promise.all([this.#h(e.requestBody??null,s),this.#l(e.responses??{},s)]),h=m[0].schemaNames,f=m[1].schemaNames;m[0].errors.length>0&&this.#e.warn(`Schema extraction errors for operation ${e.operationId}: ${m[0].errors.join(", ")}`),m[1].errors.length>0&&this.#e.warn(`Response schema extraction errors for operation ${e.operationId}: ${m[1].errors.join(", ")}`);const l=h.map(p=>`${t}-${y(p)}`),u=f.map(p=>`${t}-${y(p)}`),g=await this.#p({path:o,method:n,operation:e,requestBodySchemasKeys:l,responseSchemasKeys:u,description:s,parentKey:t,parentVersion:a,parentRevision:r,rbacTeams:c});return g&&await Promise.all([this.#i(h,e.operationId??"",s.realRelativePath,s.hash,t,a,r),this.#m(f,e.operationId??"",s.realRelativePath,s.hash,t,a,r),this.#y({apiOperationKey:g.key,operationRelations:e["x-catalogRelations"],descriptionUniqueKey:t,description:s,parentVersion:a,parentRevision:r})]),i}async#i(e,o,n,s,t,a,r){return e.length===0||!o?[]:await this.#s(e,o,n,s,t,a,r)}async#m(e,o,n,s,t,a,r){return e.length===0||!o?[]:await this.#s(e,o,n,s,t,a,r)}#h(e,o){const n=[],s=[];try{if(e&&"content"in e&&e.content)for(const t of Object.values(e.content)){const a=this.#a(t,o);if(!a)continue;const r=this.#t(a);r?.componentType==="schemas"&&r.componentName&&n.push(r.componentName)}if(e&&e.$ref){const t=this.#t(e.$ref);if(t?.componentType==="requestBodies"&&t.componentName){const a=o.document.components?.requestBodies?.[t.componentName];if(a&&"content"in a)for(const r of Object.values(a.content??{})){const c=this.#a(r,o);if(!c)continue;const i=this.#t(c);i?.componentType==="schemas"&&i.componentName&&n.push(i.componentName)}}}}catch(t){s.push(`Failed to extract request body schemas: ${t instanceof Error?t.message:"Unknown error"}`)}return{schemaNames:[...new Set(n)],errors:s}}#l(e,o){const n=[],s=[];try{for(const t of Object.values(e)){if(t&&"content"in t&&t.content)for(const a of Object.values(t.content)){const r=this.#a(a,o);if(!r)continue;const c=this.#t(r);c?.componentType==="schemas"&&c.componentName&&n.push(c.componentName)}if(t&&t.$ref){const a=this.#t(t.$ref);if(a?.componentType==="responses"&&a.componentName){const r=o.document.components?.responses?.[a.componentName];if(r&&"content"in r)for(const c of Object.values(r.content??{})){const i=this.#a(c,o);if(!i)continue;const m=this.#t(i);m?.componentType==="schemas"&&m.componentName&&n.push(m.componentName)}}}}}catch(t){s.push(`Failed to extract response schemas: ${t instanceof Error?t.message:"Unknown error"}`)}return{schemaNames:[...new Set(n)],errors:s}}#a(e,o){const n=new Set;let s=e;for(;s&&"$ref"in s;){const t=this.#t(s.$ref),a=t?.componentName;if(t?.componentType!=="mediaTypes"||!a||n.has(a))return;n.add(a),s=o.document.components?.mediaTypes?.[a]}return s?.schema?.$ref}#t(e){try{return N(e)}catch{return{componentType:"",componentName:null}}}async#s(e,o,n,s,t,a,r){return e.length===0?[]:await E(e,v,async c=>await this.#f({operationId:o,schemaName:c,descriptionSourceFile:n,descriptionHash:s,parentKey:t,parentVersion:a,parentRevision:r}))}async#f({operationId:e,schemaName:o,descriptionSourceFile:n,descriptionHash:s,parentKey:t,parentVersion:a,parentRevision:r}){const c=`${t}-${y(o)}`;try{return await this.catalogEntitiesService.createEntityRelation({sourceKey:`${t}-${y(e)}`,type:"uses",targetKey:c,sourceFile:n,fileHash:s,sourceVersion:a,targetVersion:a,sourceRevision:r,targetRevision:r}),c}catch(i){throw this.#e.error(`Failed to create relation between operation ${e} and schema ${o}: ${i instanceof Error?i.message:"Unknown error"}`),i}}async#p({path:e,method:o,operation:n,requestBodySchemasKeys:s,responseSchemasKeys:t,description:a,parentKey:r,parentVersion:c,parentRevision:i,rbacTeams:m}){const h=`${n.operationId}`,f=`${r}-${y(h)}`,l=n[R]?$(n[R]):m,u=$(n["x-rbac"]),g={type:"api-operation",key:f,title:h,summary:S(n.summary),tags:n.tags?.filter(p=>d(p)),metadata:{path:e,method:o,payload:s,responses:t},version:c};try{return await this.catalogEntitiesService.createEntity({entity:g,sourceFile:a.realRelativePath,fileHash:a.hash,rbacTeams:u.length>0?u:l,source:"file",revision:i})}catch(p){return this.#e.error(`Failed to create API operation entity for ${h}: ${p instanceof Error?p.message:"Unknown error"}`),null}}async#y({apiOperationKey:e,operationRelations:o,descriptionUniqueKey:n,description:s,parentVersion:t,parentRevision:a}){try{await this.catalogEntitiesService.createEntityRelation({sourceKey:e,type:"partOf",targetKey:n,sourceFile:s.realRelativePath,fileHash:s.hash,sourceVersion:t,targetVersion:t,sourceRevision:a,targetRevision:a}),await this.#u(e,s,o,t,a)}catch(r){this.#e.error(`Failed to create API operation relations for ${e}: ${r instanceof Error?r.message:"Unknown error"}`)}}async#u(e,o,n,s,t){n?.length&&await E(n,T,async a=>{try{this.validateEntityRelationFileSchema(a),await this.catalogEntitiesService.createEntityRelation({sourceKey:e,type:a.type,targetKey:a.key,sourceFile:o.realRelativePath,fileHash:o.hash,sourceVersion:s,targetVersion:"",sourceRevision:t,targetRevision:""})}catch(r){this.context.logger.warn(`Error creating entity relation for operation ${e} based on custom property: ${r instanceof Error?r.message:"Unknown error"}`)}})}}export{V as OpenApiEntitiesExtractor};
@@ -1 +1 @@
1
- function s(a){const n=/^#\/channels\/(?<channelName>.+)\/messages\/(?<messageName>.+)$/,e=a.match(n);return!e||!e.groups?null:{channelName:e?.groups?.channelName??null,messageName:e?.groups?.messageName??null}}export{s as extractPartsFromChannelsMessageRef};
1
+ import{unescapePointerFragment as a}from"@redocly/openapi-core";function m(n){const s=/^#\/channels\/(?<channelName>.+)\/messages\/(?<messageName>.+)$/,e=n.match(s);return!e||!e.groups?null:{channelName:a(e.groups.channelName),messageName:a(e.groups.messageName)}}export{m as extractPartsFromChannelsMessageRef};
@@ -1,5 +1,7 @@
1
1
  import type { OpenAPIParameter, Referenced, OpenAPIDefinition } from '@redocly/api-docs';
2
2
  import type { McpEndpoint } from '../../types.js';
3
+ import { resolveRef } from '@redocly/api-docs/lib/utils/inline-openapi-refs.js';
4
+ export { resolveRef };
3
5
  type JsonValue = string | number | boolean | null | JsonValue[] | {
4
6
  [key: string]: JsonValue;
5
7
  };
@@ -10,7 +12,6 @@ export type ResolvedOperation = {
10
12
  operation: JsonObject;
11
13
  components?: Record<string, JsonObject>;
12
14
  };
13
- export declare function resolveRef<T>(ref: string, def?: OpenAPIDefinition): T | undefined;
14
15
  type ResolveOperationParams = {
15
16
  operation: McpEndpoint;
16
17
  pathParams: Referenced<OpenAPIParameter>[];
@@ -22,5 +23,4 @@ export declare function resolveOperation({ operation, pathParams, definition, }:
22
23
  * `maxBytes` or the inliner cannot read the operation.
23
24
  */
24
25
  export declare function inlineOperation({ operation, pathParams, definition }: ResolveOperationParams, maxBytes: number): ResolvedOperation | undefined;
25
- export {};
26
26
  //# sourceMappingURL=resolve-refs.d.ts.map
@@ -1 +1 @@
1
- const S=/^#\/components\/([^/]+)\/([^/]+)(\/.*)?$/;function w(e){return e.replace(/~1/g,"/").replace(/~0/g,"~")}function d(e,s){if(!s||!e?.startsWith("#/"))return;const n=e.slice(2).split("/").map(w);let t=s;for(const r of n)if(typeof t=="object"&&t!==null&&r in t)t=t[r];else return;return t}function h(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function O(e,s){const n={},t=new Set,r=new Set,o=[e];for(;o.length>0;){const c=o.pop();if(!(c===null||typeof c!="object")){if(h(c)&&typeof c.$ref=="string"&&!t.has(c.$ref)){t.add(c.$ref);const[,i,f]=S.exec(c.$ref)??[],u=f&&w(f);if(i&&u&&!r.has(`${i}/${u}`)){const l=d(`#/components/${i}/${f}`,s);l!==void 0&&(r.add(`${i}/${u}`),(n[i]??={})[u]=l,o.push(l))}}for(const i of Object.values(c))o.push(i)}}return Object.keys(n).length>0?n:void 0}function R(e,s){const n=h(e)&&typeof e.$ref=="string"?d(e.$ref,s)??e:e,{name:t,in:r}=h(n)?n:{};return typeof t=="string"&&typeof r=="string"?`${r}:${t}`:JSON.stringify(e)}function g(e,s,n){const t=new Map;for(const r of[...e,...s])r&&t.set(R(r,n),r);return[...t.values()]}function C({operation:e,pathParams:s,definition:n}){const t={...e,parameters:g(s,e.parameters??[],n)};return{operation:t,components:O(t,n)}}class E extends Error{}function j(e,s){if(e.remainingBytes-=s,e.remainingBytes<0)throw new E}function $(e,s){return j(e,JSON.stringify(s).length),s}function m(e,s,n){const t=d(e,s);if(t===void 0)return;const r=n.bytesByRef.get(e)??JSON.stringify(t).length;return n.bytesByRef.set(e,r),j(n,r),t}function y(e,s,n){const t=$(n,Object.fromEntries(Object.entries(e).filter(([r,o])=>r!=="$ref"&&o!==void 0)));return{...s,...t}}function a({schema:e,definition:s,budget:n,visitedRefs:t=new Set,maxDepth:r=50,currentDepth:o=0}){if(!e)return;if(o>=r)return $(n,{type:"object",description:`Maximum resolution depth exceeded (${r})`});if("$ref"in e&&e.$ref){if(t.has(e.$ref))return $(n,{type:"object",description:`Circular reference detected: ${e.$ref}`,"x-circular-ref":e.$ref});const f=new Set(t);f.add(e.$ref);const u=m(e.$ref,s,n);if(!u)return $(n,{type:"object",description:`Could not resolve reference: ${e.$ref}`,"x-unresolved-ref":e.$ref});const l=a({schema:u,definition:s,budget:n,visitedRefs:f,maxDepth:r,currentDepth:o+1});return l&&y(e,l,n)}const c={...e};if(c.properties){const f={};for(const[u,l]of Object.entries(c.properties)){const p=a({schema:l,definition:s,budget:n,visitedRefs:new Set(t),maxDepth:r,currentDepth:o+1});f[u]=p||l}c.properties=f}if(c.items&&typeof c.items=="object")if(Array.isArray(c.items))c.items=c.items.map(f=>a({schema:f,definition:s,budget:n,visitedRefs:new Set(t),maxDepth:r,currentDepth:o+1})||f);else{const f=a({schema:c.items,definition:s,budget:n,visitedRefs:new Set(t),maxDepth:r,currentDepth:o+1});f&&(c.items=f)}return["allOf","anyOf","oneOf"].forEach(f=>{const u=c[f];Array.isArray(u)&&(c[f]=u.reduce((l,p)=>{const v=a({schema:p,definition:s,budget:n,visitedRefs:new Set(t),maxDepth:r,currentDepth:o+1});return v&&l.push(v),l},[]))}),c}function P({pathParams:e,opParams:s,definition:n,budget:t}){return[...e,...s].reduce((r,o)=>{if(!o)return r;if("$ref"in o&&o.$ref){const c=m(o.$ref,n,t);if(!c)return r;const i=y(o,c,t);return r.push({...i,schema:"schema"in i&&i.schema?a({schema:i.schema,definition:n,budget:t}):void 0}),r}return r.push({...o,schema:"schema"in o&&o.schema?a({schema:o.schema,definition:n,budget:t}):void 0}),r},[])}function x(e,s,n){const t={...e};if(e.example!==void 0&&(t.example=e.example),!e.examples)return t;const r={};for(const[o,c]of Object.entries(e.examples))if("$ref"in c){const i=m(c.$ref,s,n);i&&(r[o]=i)}else r[o]=c;return t.examples=r,t}function A(e,s,n){if(!e)return;const t="$ref"in e&&e.$ref?m(e.$ref,s,n):e;if(!t)return;const r=y(e,t,n),o={...r};if("content"in r&&r.content){const c={};for(const[i,f]of Object.entries(r.content))c[i]={...x(f,s,n),schema:f.schema?a({schema:f.schema,definition:s,budget:n}):void 0};o.content=c}return o}function B(e,s,n){if(e)return Object.entries(e).reduce((t,[r,o])=>{const c="$ref"in o&&o.$ref?m(o.$ref,s,n):o;if(!c)return t;const i=y(o,c,n),f={...i};if("content"in i&&i.content){const u={};for(const[l,p]of Object.entries(i.content))u[l]={...x(p,s,n),schema:p.schema?a({schema:p.schema,definition:s,budget:n}):void 0};f.content=u}return t[r]=f,t},{})}function N({operation:e,pathParams:s,definition:n},t){const r={remainingBytes:t,bytesByRef:new Map};try{const o={...e,parameters:P({pathParams:s,opParams:e.parameters||[],definition:n,budget:r}),requestBody:A(e.requestBody,n,r),responses:B(e.responses,n,r)};return{operation:o,components:O(o,n)}}catch{return}}export{N as inlineOperation,C as resolveOperation,d as resolveRef};
1
+ import{resolveParameters as y,resolveRef as p,resolveRequestBody as g,resolveResponses as m,unescapeJsonPointerToken as $}from"@redocly/api-docs/lib/utils/inline-openapi-refs.js";const h=/^#\/components\/([^/]+)\/([^/]+)(\/.*)?$/;function d(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function l(e,o){const n={},s=new Set,t=new Set,r=[e];for(;r.length>0;){const c=r.pop();if(!(c===null||typeof c!="object")){if(d(c)&&typeof c.$ref=="string"&&!s.has(c.$ref)){s.add(c.$ref);const[,f,i]=h.exec(c.$ref)??[],u=i&&$(i);if(f&&u&&!t.has(`${f}/${u}`)){const a=p(`#/components/${f}/${i}`,o);a!==void 0&&(t.add(`${f}/${u}`),(n[f]??={})[u]=a,r.push(a))}}for(const f of Object.values(c))r.push(f)}}return Object.keys(n).length>0?n:void 0}function b(e,o){const n=d(e)&&typeof e.$ref=="string"?p(e.$ref,o)??e:e,{name:s,in:t}=d(n)?n:{};return typeof s=="string"&&typeof t=="string"?`${t}:${s}`:JSON.stringify(e)}function O(e,o,n){const s=new Map;for(const t of[...e,...o])t&&s.set(b(t,n),t);return[...s.values()]}function x({operation:e,pathParams:o,definition:n}){const s={...e,parameters:O(o,e.parameters??[],n)};return{operation:s,components:l(s,n)}}class w extends Error{}function B(e){let o=e;const n=new Map;return(s,t)=>{let r=t===void 0?void 0:n.get(t);if(r===void 0&&(r=JSON.stringify(s).length,t!==void 0&&n.set(t,r)),o-=r,o<0)throw new w}}function R({operation:e,pathParams:o,definition:n},s){const t=B(s);try{const r={...e,parameters:y({pathParams:o,opParams:e.parameters||[],definition:n,charge:t}),requestBody:g(e.requestBody,n,t),responses:e.responses&&m(e.responses,n,t)};return{operation:r,components:l(r,n)}}catch{return}}export{R as inlineOperation,x as resolveOperation,p as resolveRef};
@@ -1,7 +1,7 @@
1
- import"../node-crypto-polyfill.js";import{DOMParser as v}from"@xmldom/xmldom";import{SignedXml as Q}from"xml-crypto";import ee from"xpath";import{deflateSync as te,inflateSync as ne}from"fflate";import{createHash as re}from"crypto";import{ulid as oe}from"ulid";import{AuthProviderType as f,DEFAULT_TEAM_CLAIM_NAME as j}from"@redocly/config";import{ALTERNATIVE_AUD_CLAIM_NAME as se,AUTH_URL as ie,JWT_SECRET_KEY as S,MAX_AUTH_TOKEN_COOKIE_BYTES as ae}from"../constants/common.js";import{ensureArray as R}from"../../utils/array/ensure-array.js";import{envConfig as ce}from"../config/env-config.js";import{decryptClaimValue as ue}from"../utils/crypto/decrypt-claim-value.js";import{getPathPrefix as de,withPathPrefix as le}from"@redocly/theme/core/utils";import{ServerRoutes as U}from"../../constants/api.js";import{AuthCookieNames as g,DEFAULT_AUTHENTICATED_TEAM as me,REQUIRED_OIDC_SCOPES as V}from"../../constants/common.js";import{appendQueryParams as pe}from"../../utils/url/append-query-params.js";import{parsePreviewUrl as B}from"../../utils/url/parse-preview-url.js";import{logger as O}from"../tools/notifiers/logger.js";import{randomString as fe}from"../utils/crypto/random-string.js";import{randomUUID as F}from"../utils/crypto/random-uuid.js";import{AlgorithmTypes as d,JwtTokenExpired as he}from"./jwt/types.js";import*as m from"./jwt/jwt.js";import{InternalTokenTypes as T}from"./jwt/token-types.js";import{parseTeamClaimToArray as ye}from"../utils/index.js";import{parseTeamNameTemplate as Ae}from"../utils/rbac-expand.js";import{arrayBufferToBase64 as Se,decodeBase64 as H,encodeBase64URL as we,urlSafeBase64 as $}from"./jwt/encode.js";import{formatSamlCertificate as _e}from"./utils/format-saml-certificate.js";const Te=new Set(Object.values(T));function x(e){return e?.type===f.OIDC}function xe(e){return e?.type===f.SAML2}async function xt(e,t){if(x(t))return Ie(e,t);if(xe(t))return ge(e,t)}async function Ie(e,t){const n=await G(e,t),r=new Set((t.scopes||[]).concat(V)),o=t.authorizationRequestCustomParams||{};return{type:f.OIDC,idpId:e,name:"OAuth provider",authorizationEndpoint:n.authorization_endpoint,clientId:t.clientId,responseType:"code",scope:Array.from(r).join(" "),extraParams:o,pkce:t.pkce}}function ge(e,t){return{type:f.SAML2,idpId:e,name:"SAML2 provider",ssoUrl:t.ssoUrl,issuerId:t.issuerId,entityId:t.entityId||t.issuerId}}function K(e){return Array.from(new Set((e.scopes||[]).concat(V))).join(" ")}async function z(e,t,n){return await fetch(e,{method:"POST",body:new URLSearchParams({client_id:t.clientId,...t.clientSecret?{client_secret:t.clientSecret}:{},...n}).toString(),headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"}}).then(r=>r.json())}async function It(e,t,n,r,o={}){return await z(e,r,{scope:K(r),code:t,redirect_uri:J(n),grant_type:"authorization_code",...o})}async function gt(e,t,n){return await z(e,n,{scope:K(n),grant_type:"refresh_token",refresh_token:t})}function Ee(e,{authorizationEndpoint:t,clientId:n,responseType:r,scope:o,extraParams:s,idpId:i,pkce:c},u,h,p){if(!t||!n||!r||!o)return{loginUrl:void 0};const a=new URL(t),A=p?.redirectUriOverride??`${e}${le(U.OIDC_CALLBACK)}`,w={state:F(),idpId:i,redirectUri:A,redirectTo:u,branch:p?.branchOverride??Me(e),inviteCode:h,source:p?.sourceOverride??"portal",uiLocales:p?.uiLocales},_={};if(c){const y=$(fe(50)),E=$(re("sha256").update(y).digest("base64")),k="S256";a.searchParams.append("code_challenge",E),a.searchParams.append("code_challenge_method",k),_.code_verifier={value:y,options:{secure:!0,httpOnly:!0,expires:new Date(Date.now()+1e3*60*10),path:de()||"/"}}}a.searchParams.append("client_id",n),a.searchParams.append("scope",o),a.searchParams.append("response_type",r),a.searchParams.append("redirect_uri",J(A)),a.searchParams.append("state",we(JSON.stringify(w))),p?.uiLocales&&a.searchParams.append("ui_locales",p.uiLocales);for(const y in s)s[y]!==void 0&&a.searchParams.append(y,s[y]);return{loginUrl:a.toString(),cookies:_}}function Et(e,t,n,r){const o=new URL(e);return n&&(o.searchParams.append("post_logout_redirect_uri",t),r&&o.searchParams.append("state",r),o.searchParams.append("id_token_hint",n)),o.toString()}function ke(e,t){const n=R(t?.audience??[]);return!n.length||W(e,n)}function W(e,t){const n=[e.aud,e[se]].flat();return t.some(r=>r!==""&&n.includes(r))}function Re(e,t){return!t||!x(t)?!0:ke(e,t)&&Oe(e,t)}function Oe(e,t){return!x(t)||!$e(t)||!t.clientId?!0:R(e.aud||[]).includes(t.clientId)}function kt(e){if(!e)return!1;try{return m.decode(e).header.alg===d.RS256}catch{return!1}}async function Rt(e){const t=Math.floor(Date.now()/1e3),n=t+(e.ttlSec??600);return m.sign({type:T.MCP_AUTHORIZATION_CODE,client_id:e.clientId,redirect_uri:e.redirectUri,id_token:e.idToken,...e.idpId?{idp_id:e.idpId}:{},...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},...e.codeChallenge?{code_challenge:e.codeChallenge}:{},...e.codeChallengeMethod?{code_challenge_method:e.codeChallengeMethod}:{},iat:t,exp:n},S,d.HS256)}async function Ot(e){await m.verify(e,S,d.HS256);const{payload:t}=m.decode(e);if(t.type!==T.MCP_AUTHORIZATION_CODE)throw new Error("Invalid authorization code type");if(!t.client_id||!t.redirect_uri)throw new Error("Authorization code missing required claims");if(typeof t.exp=="number"&&Date.now()>=t.exp*1e3)throw new Error("Authorization code expired");return t}async function Mt(e){const t=Math.floor(Date.now()/1e3);return m.sign({type:T.MCP_REFRESH_TOKEN,client_id:e.clientId,...e.idpId?{idp_id:e.idpId}:{},claims:e.claims,...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},session_exp:e.sessionExp,iat:t,exp:e.sessionExp},S,d.HS256)}async function Lt(e){await m.verify(e,S,d.HS256);const{payload:t}=m.decode(e);if(t.type!==T.MCP_REFRESH_TOKEN)throw new Error("Invalid refresh token type");if(!t.client_id||typeof t.claims!="object"||t.claims===null)throw new Error("Refresh token missing required claims");if(typeof t.session_exp!="number")throw new Error("Refresh token missing session expiry");return t}function Nt(e){const t=e||oe(),n=t.startsWith("mcp_")?t:`mcp_${t}`;return{id:n,object:"mcpSession",uri:`urn:redocly:realm:mcp:session:${n}`}}function J(e){const t=B(e);if(!t)return e;const n=Le(t.hostname);if(n)return t.hostname=`previewauth--${n.previewBranch}${n.after}`,q(e,t);const r=Y(t.hostname);return r?(t.hostname=`${r.projectSlug}.previewauth.${r.after}`,q(e,t)):e}function Me(e){const t=B(e);if(t)return Y(t.hostname)?.previewBranch}function q(e,t){return e.replace(/^https?:\/\/[^/?#]+/i,`${t.protocol}//${t.host}`)}function Y(e){const t=e.split(".preview.",2);if(t.length<2)return null;const[n,r]=t,o=n.indexOf("--");if(o===-1)return null;const s=n.slice(0,o),i=n.slice(o+2);return!s||!i||i.includes(".")?null:{projectSlug:s,previewBranch:i,after:r}}function Le(e){const t=e.indexOf("."),n=t===-1?e:e.slice(0,t);if(!n.startsWith("preview-"))return null;const r=n.indexOf("--");if(r===-1)return null;const o=n.slice(r+2);if(!o)return null;const s=t===-1?"":e.slice(t);return{previewBranch:o,after:s}}function Ne(e){return e.type===f.OIDC}function Ce(e){return e.type===f.SAML2}function Ct(e,t,n,r,o){return Ne(e)?Ee(t,e,n,r,{uiLocales:o}):Ce(e)?De(t,e,n,r,o):{}}function De(e,t,n,r,o){const i=`<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
1
+ import"../node-crypto-polyfill.js";import{DOMParser as v}from"@xmldom/xmldom";import{SignedXml as Z}from"xml-crypto";import ee from"xpath";import{deflateSync as te,inflateSync as ne}from"fflate";import{createHash as re}from"crypto";import{ulid as oe}from"ulid";import{AuthProviderType as f,DEFAULT_TEAM_CLAIM_NAME as j}from"@redocly/config";import{ALTERNATIVE_AUD_CLAIM_NAME as se,AUTH_URL as ie,JWT_SECRET_KEY as w,MAX_AUTH_TOKEN_COOKIE_BYTES as ae}from"../constants/common.js";import{ensureArray as R}from"../../utils/array/ensure-array.js";import{envConfig as ce}from"../config/env-config.js";import{decryptClaimValue as ue}from"../utils/crypto/decrypt-claim-value.js";import{getPathPrefix as de,withPathPrefix as le,withoutPathPrefix as me}from"@redocly/theme/core/utils";import{ServerRoutes as U}from"../../constants/api.js";import{AuthCookieNames as g,DEFAULT_AUTHENTICATED_TEAM as pe,REQUIRED_OIDC_SCOPES as V}from"../../constants/common.js";import{appendQueryParams as fe}from"../../utils/url/append-query-params.js";import{parsePreviewUrl as B}from"../../utils/url/parse-preview-url.js";import{logger as O}from"../tools/notifiers/logger.js";import{randomString as he}from"../utils/crypto/random-string.js";import{randomUUID as $}from"../utils/crypto/random-uuid.js";import{AlgorithmTypes as d,JwtTokenExpired as ye}from"./jwt/types.js";import*as m from"./jwt/jwt.js";import{InternalTokenTypes as T}from"./jwt/token-types.js";import{parseTeamClaimToArray as Ae}from"../utils/index.js";import{parseTeamNameTemplate as we}from"../utils/rbac-expand.js";import{arrayBufferToBase64 as Se,decodeBase64 as F,encodeBase64URL as _e,urlSafeBase64 as H}from"./jwt/encode.js";import{formatSamlCertificate as Te}from"./utils/format-saml-certificate.js";const xe=new Set(Object.values(T));function x(e){return e?.type===f.OIDC}function Ie(e){return e?.type===f.SAML2}async function It(e,t){if(x(t))return ge(e,t);if(Ie(t))return Ee(e,t)}async function ge(e,t){const n=await G(e,t),r=new Set((t.scopes||[]).concat(V)),o=t.authorizationRequestCustomParams||{};return{type:f.OIDC,idpId:e,name:"OAuth provider",authorizationEndpoint:n.authorization_endpoint,clientId:t.clientId,responseType:"code",scope:Array.from(r).join(" "),extraParams:o,pkce:t.pkce}}function Ee(e,t){return{type:f.SAML2,idpId:e,name:"SAML2 provider",ssoUrl:t.ssoUrl,issuerId:t.issuerId,entityId:t.entityId||t.issuerId}}function K(e){return Array.from(new Set((e.scopes||[]).concat(V))).join(" ")}async function W(e,t,n){return await fetch(e,{method:"POST",body:new URLSearchParams({client_id:t.clientId,...t.clientSecret?{client_secret:t.clientSecret}:{},...n}).toString(),headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"}}).then(r=>r.json())}async function gt(e,t,n,r,o={}){return await W(e,r,{scope:K(r),code:t,redirect_uri:J(n),grant_type:"authorization_code",...o})}async function Et(e,t,n){return await W(e,n,{scope:K(n),grant_type:"refresh_token",refresh_token:t})}function ke(e,{authorizationEndpoint:t,clientId:n,responseType:r,scope:o,extraParams:s,idpId:i,pkce:c},u,h,p){if(!t||!n||!r||!o)return{loginUrl:void 0};const a=new URL(t),A=p?.redirectUriOverride??`${e}${le(U.OIDC_CALLBACK)}`,S={state:$(),idpId:i,redirectUri:A,redirectTo:u,branch:p?.branchOverride??Le(e),inviteCode:h,source:p?.sourceOverride??"portal",uiLocales:p?.uiLocales},_={};if(c){const y=H(he(50)),E=H(re("sha256").update(y).digest("base64")),k="S256";a.searchParams.append("code_challenge",E),a.searchParams.append("code_challenge_method",k),_.code_verifier={value:y,options:{secure:!0,httpOnly:!0,expires:new Date(Date.now()+1e3*60*10),path:de()||"/"}}}a.searchParams.append("client_id",n),a.searchParams.append("scope",o),a.searchParams.append("response_type",r),a.searchParams.append("redirect_uri",J(A)),a.searchParams.append("state",_e(JSON.stringify(S))),p?.uiLocales&&a.searchParams.append("ui_locales",p.uiLocales);for(const y in s)s[y]!==void 0&&a.searchParams.append(y,s[y]);return{loginUrl:a.toString(),cookies:_}}function kt(e,t,n,r){const o=new URL(e);return n&&(o.searchParams.append("post_logout_redirect_uri",t),r&&o.searchParams.append("state",r),o.searchParams.append("id_token_hint",n)),o.toString()}function Re(e,t){const n=R(t?.audience??[]);return!n.length||z(e,n)}function z(e,t){const n=[e.aud,e[se]].flat();return t.some(r=>r!==""&&n.includes(r))}function Oe(e,t){return!t||!x(t)?!0:Re(e,t)&&Me(e,t)}function Me(e,t){return!x(t)||!Ke(t)||!t.clientId?!0:R(e.aud||[]).includes(t.clientId)}function Rt(e){if(!e)return!1;try{return m.decode(e).header.alg===d.RS256}catch{return!1}}async function Ot(e){const t=Math.floor(Date.now()/1e3),n=t+(e.ttlSec??600);return m.sign({type:T.MCP_AUTHORIZATION_CODE,client_id:e.clientId,redirect_uri:e.redirectUri,id_token:e.idToken,...e.idpId?{idp_id:e.idpId}:{},...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},...e.codeChallenge?{code_challenge:e.codeChallenge}:{},...e.codeChallengeMethod?{code_challenge_method:e.codeChallengeMethod}:{},iat:t,exp:n},w,d.HS256)}async function Mt(e){await m.verify(e,w,d.HS256);const{payload:t}=m.decode(e);if(t.type!==T.MCP_AUTHORIZATION_CODE)throw new Error("Invalid authorization code type");if(!t.client_id||!t.redirect_uri)throw new Error("Authorization code missing required claims");if(typeof t.exp=="number"&&Date.now()>=t.exp*1e3)throw new Error("Authorization code expired");return t}async function Lt(e){const t=Math.floor(Date.now()/1e3);return m.sign({type:T.MCP_REFRESH_TOKEN,client_id:e.clientId,...e.idpId?{idp_id:e.idpId}:{},claims:e.claims,...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},session_exp:e.sessionExp,iat:t,exp:e.sessionExp},w,d.HS256)}async function Nt(e){await m.verify(e,w,d.HS256);const{payload:t}=m.decode(e);if(t.type!==T.MCP_REFRESH_TOKEN)throw new Error("Invalid refresh token type");if(!t.client_id||typeof t.claims!="object"||t.claims===null)throw new Error("Refresh token missing required claims");if(typeof t.session_exp!="number")throw new Error("Refresh token missing session expiry");return t}function Ct(e){const t=e||oe(),n=t.startsWith("mcp_")?t:`mcp_${t}`;return{id:n,object:"mcpSession",uri:`urn:redocly:realm:mcp:session:${n}`}}function J(e){const t=B(e);if(!t)return e;const n=Ne(t.hostname);if(n)return t.hostname=`previewauth--${n.previewBranch}${n.after}`,q(e,t);const r=Y(t.hostname);return r?(t.hostname=`${r.projectSlug}.previewauth.${r.after}`,q(e,t)):e}function Le(e){const t=B(e);if(t)return Y(t.hostname)?.previewBranch}function q(e,t){const n=e.replace(/^https?:\/\/[^/?#]+/i,"");return`${t.protocol}//${t.host}${me(n)}`}function Y(e){const t=e.split(".preview.",2);if(t.length<2)return null;const[n,r]=t,o=n.indexOf("--");if(o===-1)return null;const s=n.slice(0,o),i=n.slice(o+2);return!s||!i||i.includes(".")?null:{projectSlug:s,previewBranch:i,after:r}}function Ne(e){const t=e.indexOf("."),n=t===-1?e:e.slice(0,t);if(!n.startsWith("preview-"))return null;const r=n.indexOf("--");if(r===-1)return null;const o=n.slice(r+2);if(!o)return null;const s=t===-1?"":e.slice(t);return{previewBranch:o,after:s}}function Ce(e){return e.type===f.OIDC}function De(e){return e.type===f.SAML2}function Dt(e,t,n,r,o){return Ce(e)?ke(t,e,n,r,{uiLocales:o}):De(e)?be(t,e,n,r,o):{}}function be(e,t,n,r,o){const i=`<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
2
2
  xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
3
3
  Version="2.0"
4
- ID="_${F()}"
4
+ ID="_${$()}"
5
5
  IssueInstant="${new Date().toISOString()}"
6
6
  AssertionConsumerServiceURL="${e}${U.SAML_CALLBACK}"
7
7
  AttributeConsumingServiceIndex="0">
@@ -9,4 +9,4 @@ import"../node-crypto-polyfill.js";import{DOMParser as v}from"@xmldom/xmldom";im
9
9
  <samlp:NameIDPolicy
10
10
  AllowCreate="true"
11
11
  Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"/>
12
- </samlp:AuthnRequest>`,c=be(i);return{loginUrl:pe(t.ssoUrl,{SAMLRequest:c,RelayState:JSON.stringify({idpId:t.idpId,redirectTo:n,inviteCode:r,source:"portal",uiLocales:o})})}}function be(e){return Se(te(new TextEncoder().encode(e)).buffer)}function Dt(e){const t=H(e);if(t.startsWith("<samlp:Response")||t.indexOf("<saml2p:Response")>-1)return t;const n=ne(new Uint8Array(atob(e).split("").map(r=>r.charCodeAt(0))));return new TextDecoder().decode(n)}function bt(e){try{return JSON.parse(H(e||""))}catch{throw new Error("Invalid OAuth2 state")}}function Pt(e){const t=new v().parseFromString(e,"application/xml"),r=l(t,"//*[local-name(.)='StatusCode']/@Value")[0]?.nodeValue?.endsWith("Success")||!1,s=l(t,"//*[local-name(.)='Response']/@Destination")[0]?.nodeValue||"",i=l(t,"//*[local-name(.)='Assertion']//*[local-name(.)='Issuer']/text()")[0],c=i&&i.nodeValue||void 0,u=l(t,"//*[local-name(.)='Audience']/text()")[0],h=u&&u.nodeValue||void 0,a=l(t,"//*[local-name(.)='Assertion']//*[local-name(.)='X509Certificate']/text()")[0]?.nodeValue||"",A=l(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/text()")[0],w=A&&A.nodeValue||"",_=l(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/@Format")[0],y=_&&_.nodeValue||"",E=l(t,"//*[local-name(.)='Conditions']/@NotOnOrAfter")[0],k=Pe(E),N={},C=l(t,"//*[local-name(.)='AttributeStatement']//*[local-name(.)='Attribute']");if(C.length)for(const D of C){const b=l(D,"./@Name")[0];if(b.nodeValue){const P=l(D,"./*[local-name(.)='AttributeValue']/text()")[0];P?.nodeValue&&(N[b.nodeValue]=P.nodeValue)}}return{uid:w,success:r,expiresAt:k,issuerId:c,entityId:h,attrs:N,cert:a,nameFormat:y,destination:s}}function Pe(e){const t=typeof e?.nodeValue=="string"&&M(Date.parse(e.nodeValue)),n=M(Date.now()),r=M(Date.now()+720*60*1e3);return t?t>n&&t<r?r:t:n}function M(e){return Math.floor(e/1e3)}const L={},I={jwks:{}};async function G(e,t){if(!L[e]){const n=t.configurationUrl?await X(t.configurationUrl):t.configuration;L[e]=ve()?Ue(n):n}return L[e]}function ve(){const e=ce.REDOCLY_ENFORCE_RESIDENCY;return!!e&&e.includes("host.docker.internal")}const je=["authorization_endpoint","end_session_endpoint"];function Ue(e){if(typeof e!="object"||e===null)return e;const t={...e};for(const n of Object.keys(t)){if(je.includes(String(n)))continue;const r=t[n];typeof r=="string"&&r.includes("://localhost")&&(t[n]=r.replace("://localhost","://host.docker.internal"))}return t}const Ve=["kty","n","e","crv","x","y"];function Be(e,t){return Ve.every(n=>e[n]===t[n])}async function Fe(e){const t={};for(const n of Object.keys(e)){const r=e[n];if(!x(r))continue;const o=await G(n,r);if(o.jwks_uri){const s=await X(o.jwks_uri);for(const i of s.keys){const c=t[i.kid],u=c&&Be(c,i)?[...c.idpIds,n]:[n];t[i.kid]={...i,idpIds:u},I.jwks[i.kid]=t[i.kid]}}}}function He(e,t,n){const r=I.jwks[e]?.idpIds??[];return r.find(o=>{const s=n[o];if(!x(s))return!1;const i=R(s.audience??[]);return W(t,i.length?i:[s.clientId])})??r.at(-1)}async function X(e){return fetch(e,{headers:{Accept:"application/json"}}).then(t=>t.json())}async function vt(e){return fetch(`${ie}/oidc/userinfo`,{headers:{Accept:"application/json",Authorization:`Bearer ${e}`}}).then(t=>t.status===200?t.json():void 0).catch(()=>{})}function $e(e){if(!e.configurationUrl)return!1;const t=new URL(e.configurationUrl);return["localhost","127.0.0.1","blueharvest.cloud","bhstage.cloud","cloud.redocly.com","beta.redocly.com","cloud.eu.redocly.com","beta.eu.redocly.com"].some(r=>Ke(t.hostname,r))}function Ke(e,t){return e===t||e.endsWith(`.${t}`)}async function jt(e,t){const n=new v().parseFromString(e,"application/xml"),r=l(n,"//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0];if(!r)throw new Error("Cannot find Signature in the SAML response");const o=_e(t),s=new Q({publicCert:o});s.loadSignature(r);try{return s.checkSignature(e)}catch{return!1}}function ze(e){const t=new Set,n=r=>{if(!(!r||typeof r!="object"||Array.isArray(r)))for(const[o,s]of Object.entries(r))typeof s=="string"?t.add(o):n(s)};return n(e),t}function We(e,t,n){if(!t)return e;const r=ze(t),o=s=>{const i=n?.[s]||s;return r.has(i)||!!Ae(t,i)};return[...e.filter(o),...e.filter(s=>!o(s))]}async function Ut(e,t,n,r){const o=n?e[n]:void 0,s=Array.isArray(o)?We(o.map(String),r?.rbacConfig,r?.teamsMapping):[];let i=s.length;const c=s.length+1;for(let u=0;u<c;u++){const h={...e,idpId:t};n&&s.length&&(h[n]=s.slice(0,i));const p=await m.sign(h,S,d.HS256),a=Buffer.byteLength(p)-ae;if(a<=0)return{token:p,teamsTotal:s.length,teamsKept:i,droppedTeams:s.slice(i)};if(i===0)return null;const A=Buffer.byteLength(JSON.stringify(s.slice(0,i))),w=Math.max(1,A/i*4/3);i=Math.max(0,i-Math.max(1,Math.ceil(a/w)))}return null}function Vt(e,t,n,r){t==="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"&&(e=n["http://schemas.microsoft.com/identity/claims/objectidentifier"]);let o;(t==="urn:oasis:names:tc:SAML:2.0:nameid-format:email"||t==="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")&&(o=e),t==="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"&&e?.match(/^.+@.+/m)&&(o=e);const s=n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],i=s?.match(/^.+@.+/m);return o=o||n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]||(i?s:void 0),o=o?.toLowerCase(),{sub:e,given_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"],family_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"],name:n["http://schemas.microsoft.com/identity/claims/displayname"]||s,email:o,email_verified:!0,teams:r?ye(n[r]):[]}}function Z(e,t={}){return e.map(n=>t[n]||n)}async function Bt(e,t){if(!t)return{};const n=t[g.AUTHORIZATION];if(!n)return{};try{const r=m.decode(n);if(typeof r.payload.type=="string"&&Te.has(r.payload.type))return{};if(r.header.alg===d.RS256){I.jwks[r.header.kid]===void 0&&await Fe(e);const h=I.jwks[r.header.kid];if(!h)return I.jwks[r.header.kid]=null,{};await m.verify(n,h,d.RS256)}else await m.verify(n,S,d.HS256);const s=r.header.alg===d.RS256?He(r.header.kid,r.payload,e):r.payload.idpId,i=e[s]||{};if(r.header.alg===d.RS256&&typeof r.payload.exp!="number")return O.warn("Rejected id_token: it has no exp claim"),{};if(r.header.alg===d.RS256&&!Re(r.payload,i))return O.warn("Rejected id_token: it was issued for another project"),{};const c=Ye(i),u=qe(i);return{...r.payload,email:r.payload.email?.toLowerCase(),idpId:s,teams:Array.from(new Set([...Z(r.payload.teams||[],u),..."defaultTeams"in i&&i.defaultTeams||[],...Z("teamsClaimName"in i&&r.payload[c||""]||[],u),me])),name:Je(r.payload),isAuthenticated:!0,idpAccessToken:ue(r.payload.idp_access_token,S)||t[g.IDP_ACCESS_TOKEN],federatedAccessToken:t[g.FEDERATED_ACCESS_TOKEN],federatedIdToken:t[g.FEDERATED_ID_TOKEN],authCookie:n}}catch(r){r instanceof he||O.error("Malformed JWT token: %s",r.message)}return{}}function Je(e){return(e.firstName&&e.lastName?`${e.firstName} ${e.lastName}`:e.name||e.given_name||e.firstName||e.lastName)||e.email}function qe(e){switch(e.type){case f.SAML2:return e.teamsAttributeMap;case f.OIDC:return e.teamsClaimMap;default:return}}function Ye(e){switch(e.type){case f.SAML2:return e.teamsAttributeName??j;case f.OIDC:return e.teamsClaimName;default:return j}}function l(e,t){return ee.select(t,e)||[]}export{Ct as buildLoginUrl,Ee as buildOidcLoginUrl,Et as buildOidcLogoutUrl,De as buildSAML2LoginUrl,Rt as createMcpAuthorizationCode,Mt as createMcpRefreshToken,Nt as createMcpSessionResource,Dt as decodeSamlResponse,Fe as discoverJwks,be as encodeSAML2,Vt as extractUserClaims,xt as getAuthProviderLoginParams,Ie as getOidcLoginParams,G as getOidcMetadata,vt as getRedoclyTokenPayload,ge as getSaml2LoginParams,Bt as getUserParamsFromCookies,Je as getUsernameFromPayload,ke as idTokenAudienceIsAllowed,Re as idTokenIsForThisProject,kt as isIdpIssuedToken,x as isOidcProviderConfig,$e as isRedoclySso,xe as isSaml2ProviderConfig,It as oidcExchangeCodeForToken,I as oidcJwksCache,L as oidcMetadataCache,gt as oidcRefreshTokenGrant,We as orderTeamsRbacFirst,bt as parseOidcState,Me as parsePreviewBranch,Pt as parseSamlResponse,J as rewritePreviewAuthRedirectUri,Ut as signTokenWithinCookieSizeLimit,Ot as verifyMcpAuthorizationCode,Lt as verifyMcpRefreshToken,jt as verifySAMLResponse};
12
+ </samlp:AuthnRequest>`,c=Pe(i);return{loginUrl:fe(t.ssoUrl,{SAMLRequest:c,RelayState:JSON.stringify({idpId:t.idpId,redirectTo:n,inviteCode:r,source:"portal",uiLocales:o})})}}function Pe(e){return Se(te(new TextEncoder().encode(e)).buffer)}function bt(e){const t=F(e);if(t.startsWith("<samlp:Response")||t.indexOf("<saml2p:Response")>-1)return t;const n=ne(new Uint8Array(atob(e).split("").map(r=>r.charCodeAt(0))));return new TextDecoder().decode(n)}function Pt(e){try{return JSON.parse(F(e||""))}catch{throw new Error("Invalid OAuth2 state")}}function vt(e){const t=new v().parseFromString(e,"application/xml"),r=l(t,"//*[local-name(.)='StatusCode']/@Value")[0]?.nodeValue?.endsWith("Success")||!1,s=l(t,"//*[local-name(.)='Response']/@Destination")[0]?.nodeValue||"",i=l(t,"//*[local-name(.)='Assertion']//*[local-name(.)='Issuer']/text()")[0],c=i&&i.nodeValue||void 0,u=l(t,"//*[local-name(.)='Audience']/text()")[0],h=u&&u.nodeValue||void 0,a=l(t,"//*[local-name(.)='Assertion']//*[local-name(.)='X509Certificate']/text()")[0]?.nodeValue||"",A=l(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/text()")[0],S=A&&A.nodeValue||"",_=l(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/@Format")[0],y=_&&_.nodeValue||"",E=l(t,"//*[local-name(.)='Conditions']/@NotOnOrAfter")[0],k=ve(E),N={},C=l(t,"//*[local-name(.)='AttributeStatement']//*[local-name(.)='Attribute']");if(C.length)for(const D of C){const b=l(D,"./@Name")[0];if(b.nodeValue){const P=l(D,"./*[local-name(.)='AttributeValue']/text()")[0];P?.nodeValue&&(N[b.nodeValue]=P.nodeValue)}}return{uid:S,success:r,expiresAt:k,issuerId:c,entityId:h,attrs:N,cert:a,nameFormat:y,destination:s}}function ve(e){const t=typeof e?.nodeValue=="string"&&M(Date.parse(e.nodeValue)),n=M(Date.now()),r=M(Date.now()+720*60*1e3);return t?t>n&&t<r?r:t:n}function M(e){return Math.floor(e/1e3)}const L={},I={jwks:{}};async function G(e,t){if(!L[e]){const n=t.configurationUrl?await Q(t.configurationUrl):t.configuration;L[e]=je()?Ve(n):n}return L[e]}function je(){const e=ce.REDOCLY_ENFORCE_RESIDENCY;return!!e&&e.includes("host.docker.internal")}const Ue=["authorization_endpoint","end_session_endpoint"];function Ve(e){if(typeof e!="object"||e===null)return e;const t={...e};for(const n of Object.keys(t)){if(Ue.includes(String(n)))continue;const r=t[n];typeof r=="string"&&r.includes("://localhost")&&(t[n]=r.replace("://localhost","://host.docker.internal"))}return t}const Be=["kty","n","e","crv","x","y"];function $e(e,t){return Be.every(n=>e[n]===t[n])}async function Fe(e){const t={};for(const n of Object.keys(e)){const r=e[n];if(!x(r))continue;const o=await G(n,r);if(o.jwks_uri){const s=await Q(o.jwks_uri);for(const i of s.keys){const c=t[i.kid],u=c&&$e(c,i)?[...c.idpIds,n]:[n];t[i.kid]={...i,idpIds:u},I.jwks[i.kid]=t[i.kid]}}}}function He(e,t,n){const r=I.jwks[e]?.idpIds??[];return r.find(o=>{const s=n[o];if(!x(s))return!1;const i=R(s.audience??[]);return z(t,i.length?i:[s.clientId])})??r.at(-1)}async function Q(e){return fetch(e,{headers:{Accept:"application/json"}}).then(t=>t.json())}async function jt(e){return fetch(`${ie}/oidc/userinfo`,{headers:{Accept:"application/json",Authorization:`Bearer ${e}`}}).then(t=>t.status===200?t.json():void 0).catch(()=>{})}function Ke(e){if(!e.configurationUrl)return!1;const t=new URL(e.configurationUrl);return["localhost","127.0.0.1","blueharvest.cloud","bhstage.cloud","cloud.redocly.com","beta.redocly.com","cloud.eu.redocly.com","beta.eu.redocly.com"].some(r=>We(t.hostname,r))}function We(e,t){return e===t||e.endsWith(`.${t}`)}async function Ut(e,t){const n=new v().parseFromString(e,"application/xml"),r=l(n,"//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0];if(!r)throw new Error("Cannot find Signature in the SAML response");const o=Te(t),s=new Z({publicCert:o});s.loadSignature(r);try{return s.checkSignature(e)}catch{return!1}}function ze(e){const t=new Set,n=r=>{if(!(!r||typeof r!="object"||Array.isArray(r)))for(const[o,s]of Object.entries(r))typeof s=="string"?t.add(o):n(s)};return n(e),t}function Je(e,t,n){if(!t)return e;const r=ze(t),o=s=>{const i=n?.[s]||s;return r.has(i)||!!we(t,i)};return[...e.filter(o),...e.filter(s=>!o(s))]}async function Vt(e,t,n,r){const o=n?e[n]:void 0,s=Array.isArray(o)?Je(o.map(String),r?.rbacConfig,r?.teamsMapping):[];let i=s.length;const c=s.length+1;for(let u=0;u<c;u++){const h={...e,idpId:t};n&&s.length&&(h[n]=s.slice(0,i));const p=await m.sign(h,w,d.HS256),a=Buffer.byteLength(p)-ae;if(a<=0)return{token:p,teamsTotal:s.length,teamsKept:i,droppedTeams:s.slice(i)};if(i===0)return null;const A=Buffer.byteLength(JSON.stringify(s.slice(0,i))),S=Math.max(1,A/i*4/3);i=Math.max(0,i-Math.max(1,Math.ceil(a/S)))}return null}function Bt(e,t,n,r){t==="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"&&(e=n["http://schemas.microsoft.com/identity/claims/objectidentifier"]);let o;(t==="urn:oasis:names:tc:SAML:2.0:nameid-format:email"||t==="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")&&(o=e),t==="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"&&e?.match(/^.+@.+/m)&&(o=e);const s=n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],i=s?.match(/^.+@.+/m);return o=o||n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]||(i?s:void 0),o=o?.toLowerCase(),{sub:e,given_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"],family_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"],name:n["http://schemas.microsoft.com/identity/claims/displayname"]||s,email:o,email_verified:!0,teams:r?Ae(n[r]):[]}}function X(e,t={}){return e.map(n=>t[n]||n)}async function $t(e,t){if(!t)return{};const n=t[g.AUTHORIZATION];if(!n)return{};try{const r=m.decode(n);if(typeof r.payload.type=="string"&&xe.has(r.payload.type))return{};if(r.header.alg===d.RS256){I.jwks[r.header.kid]===void 0&&await Fe(e);const h=I.jwks[r.header.kid];if(!h)return I.jwks[r.header.kid]=null,{};await m.verify(n,h,d.RS256)}else await m.verify(n,w,d.HS256);const s=r.header.alg===d.RS256?He(r.header.kid,r.payload,e):r.payload.idpId,i=e[s]||{};if(r.header.alg===d.RS256&&typeof r.payload.exp!="number")return O.warn("Rejected id_token: it has no exp claim"),{};if(r.header.alg===d.RS256&&!Oe(r.payload,i))return O.warn("Rejected id_token: it was issued for another project"),{};const c=Ge(i),u=Ye(i);return{...r.payload,email:r.payload.email?.toLowerCase(),idpId:s,teams:Array.from(new Set([...X(r.payload.teams||[],u),..."defaultTeams"in i&&i.defaultTeams||[],...X("teamsClaimName"in i&&r.payload[c||""]||[],u),pe])),name:qe(r.payload),isAuthenticated:!0,idpAccessToken:ue(r.payload.idp_access_token,w)||t[g.IDP_ACCESS_TOKEN],federatedAccessToken:t[g.FEDERATED_ACCESS_TOKEN],federatedIdToken:t[g.FEDERATED_ID_TOKEN],authCookie:n}}catch(r){r instanceof ye||O.error("Malformed JWT token: %s",r.message)}return{}}function qe(e){return(e.firstName&&e.lastName?`${e.firstName} ${e.lastName}`:e.name||e.given_name||e.firstName||e.lastName)||e.email}function Ye(e){switch(e.type){case f.SAML2:return e.teamsAttributeMap;case f.OIDC:return e.teamsClaimMap;default:return}}function Ge(e){switch(e.type){case f.SAML2:return e.teamsAttributeName??j;case f.OIDC:return e.teamsClaimName;default:return j}}function l(e,t){return ee.select(t,e)||[]}export{Dt as buildLoginUrl,ke as buildOidcLoginUrl,kt as buildOidcLogoutUrl,be as buildSAML2LoginUrl,Ot as createMcpAuthorizationCode,Lt as createMcpRefreshToken,Ct as createMcpSessionResource,bt as decodeSamlResponse,Fe as discoverJwks,Pe as encodeSAML2,Bt as extractUserClaims,It as getAuthProviderLoginParams,ge as getOidcLoginParams,G as getOidcMetadata,jt as getRedoclyTokenPayload,Ee as getSaml2LoginParams,$t as getUserParamsFromCookies,qe as getUsernameFromPayload,Re as idTokenAudienceIsAllowed,Oe as idTokenIsForThisProject,Rt as isIdpIssuedToken,x as isOidcProviderConfig,Ke as isRedoclySso,Ie as isSaml2ProviderConfig,gt as oidcExchangeCodeForToken,I as oidcJwksCache,L as oidcMetadataCache,Et as oidcRefreshTokenGrant,Je as orderTeamsRbacFirst,Pt as parseOidcState,Le as parsePreviewBranch,vt as parseSamlResponse,J as rewritePreviewAuthRedirectUri,Vt as signTokenWithinCookieSizeLimit,Mt as verifyMcpAuthorizationCode,Nt as verifyMcpRefreshToken,Ut as verifySAMLResponse};
@@ -1 +1 @@
1
- import{setCookie as L,deleteCookie as P}from"hono/cookie";import{AuthProviderType as Q,DEFAULT_TEAM_CLAIM_NAME as x}from"@redocly/config";import{withPathPrefix as w,getPathPrefix as A}from"@redocly/theme/core/utils";import{compareURIs as ee}from"../../../utils/url/compare-uris.js";import{ensureArray as re}from"../../../utils/array/ensure-array.js";import{ALTERNATIVE_AUD_CLAIM_NAME as oe,JWT_SECRET_KEY as v,MAX_AUTH_TOKEN_COOKIE_BYTES as N,ORG_SLUG as te,ORG_ID as ne}from"../../constants/common.js";import{AUTH_SEGMENT as ie,ServerRoutes as k}from"../../../constants/api.js";import{AuthCookieNames as _,DEFAULT_COOKIE_EXPIRATION as B}from"../../../constants/common.js";import{sanitizeRedirectPathname as H}from"../../../utils/url/sanitize-redirect-pathname.js";import{telemetry as y}from"../../telemetry/index.js";import{envConfig as K}from"../../config/env-config.js";import{fetchWithClient as se}from"../../utils/fetch-with-client.js";import{clearAuthCookies as z,clearPostLogoutCookie as U,hasPostLogoutCookie as ae,setPostLogoutCookie as j}from"../../utils/cookie.js";import{getAuthProviderLoginParams as de,isOidcProviderConfig as b,isSaml2ProviderConfig as le,oidcExchangeCodeForToken as ce,buildLoginUrl as ue,decodeSamlResponse as pe,extractUserClaims as ge,parseSamlResponse as fe,parseOidcState as me,verifySAMLResponse as he,getUsernameFromPayload as Ce,buildOidcLogoutUrl as Ie,isIdpIssuedToken as _e,getOidcMetadata as J,getRedoclyTokenPayload as Ae,idTokenAudienceIsAllowed as Re,isRedoclySso as Oe,rewritePreviewAuthRedirectUri as Te,parsePreviewBranch as G,buildOidcLoginUrl as Le,createMcpSessionResource as M,signTokenWithinCookieSizeLimit as Z}from"../auth.js";import*as E from"../jwt/jwt.js";import{AlgorithmTypes as $}from"../jwt/types.js";import{handleErrorPageRender as we}from"../utils.js";import{encodeBase64URL as ye}from"../jwt/encode.js";import{resolveUiLocalesForIdpLogin as Me}from"./helpers/resolve-ui-locales-for-idp-login.js";async function Ge(n,e){if(!e.devLogin||K.isProductionEnv)return n.newResponse(null,404,{});const{password:r,...a}=await n.req.json(),o=await E.sign({...a,name:a.username||a.email||"Unknown"},v,$.HS256);return L(n,_.AUTHORIZATION,o,{path:A()||"/",httpOnly:!0,secure:!0,sameSite:"none"}),U(n),n.newResponse(null,200,{})}function Ze(){return async n=>{const e=n.get("logger"),r=encodeURIComponent(n.req.query("message")||"");e.error(`Login error: ${r}`);const a=`${k.LOGIN}/?error=${encodeURIComponent(r)}`;return n.newResponse(null,301,{Location:a})}}function V(n){if(!n||!n.includes(k.MCP_CALLBACK))return null;try{const e=n.split("/"),r=e[e.length-1];if(r){const a=Buffer.from(r,"base64url").toString("utf-8");return JSON.parse(a).mcpSessionId||null}}catch{}return null}function Ve(n){return async e=>{const r=e.get("logger"),a=n.getConfig().ssoDirect;let o;try{o=me(e.req.query("state"))}catch{return r.warn("OIDC login error: Invalid OAuth2 state"),e.text("Bad request: Invalid OAuth2 state",400)}const h=o.idpId,i=o.source==="mcp"||o.redirectTo&&typeof o.redirectTo=="string"&&o.redirectTo.includes(k.MCP_CALLBACK),u=i?V(typeof o.redirectTo=="string"?o.redirectTo:void 0):null,s=a?.[h];if(!b(s))return r.error("OIDC login error: missing OIDC provider config"),e.text("Forbidden",403);const p=await J(h,s);if(a&&!p.token_endpoint){const m="Invalid OIDC configuration: token_endpoint is required";return r.error(`OIDC login error: ${m}`),e.text(m,500)}try{const m=p.token_endpoint,f=e.req.query("code"),C=e.req.query("error");if(C)return i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:`OIDC error: ${C}`,error_details:e.req.query("error_description")||null}]),we(e,n,{slug:"/"},403,"403OIDC");if(!f){const g="Code is expected but not present";return r.error(`OIDC login error: ${g}`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:g,error_details:null}]),new Response(`Forbidden: ${g}`,{status:403})}const O=typeof o.redirectUri=="string"?o.redirectUri:new URL(w(k.OIDC_CALLBACK),e.req.url).toString(),I=e.get("cookies")?.code_verifier,d=await ce(m,f,O,s,{...s.tokenRequestCustomParams,...I?{code_verifier:I}:{}});if(d.error)return r.error(`Error from OIDC provider: "${d.error}"`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:`Token exchange error: ${d.error}`,error_details:d.error_description||null}]),e.text(`Forbidden: ${d.error_description||d.error}`,403);if(!d?.id_token){const g="No id_token, please, add openid to scopes";return r.error(`OIDC login error: ${g}`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:g,error_details:null}]),new Response(`Forbidden: ${g}`,{status:403})}const{payload:l,header:T}=E.decode(d.id_token),t=T.alg===$.RS256;if(!Re(l,s)){const g="No valid audience found in id_token";return r.error(`OIDC login error: ${g}`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:g,error_details:null}]),new Response(`Forbidden: ${g}`,{status:403})}let c=t?d.id_token:await E.sign({...l,idpId:h},v,$.HS256);if(Buffer.byteLength(c)>N){const g=await Z(l,h,s.teamsClaimName,{rbacConfig:n.getConfig().access?.rbac,teamsMapping:s.teamsClaimMap});if(!g){const S=`The id_token claims are too large for the session cookie: ${Buffer.byteLength(JSON.stringify(l))} bytes of claims`;return r.error(`OIDC login error: ${S}`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:S,error_details:null}]),e.text(`Forbidden: ${S}`,403)}g.teamsKept<g.teamsTotal&&r.warn(`OIDC login: the "${s.teamsClaimName}" claim is too large for the session cookie size limit; kept ${g.teamsKept} of ${g.teamsTotal} values, with teams from the RBAC configuration first; RBAC ignores the dropped teams: ${JSON.stringify(g.droppedTeams)}`),c=g.token}Ce(l)||r.warn("To display your username, the required 'email' or 'full_profile' scope must be added to the identity provider configuration");const D=s?.tokenExpirationTime?Date.now()+s.tokenExpirationTime*1e3:l.exp*1e3||Date.now()+B*1e3;if(s.introspectEndpoint){const g=await fetch(s.introspectEndpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({access_token:d.access_token})});if(g.ok){const q=(await g.json()).ext?.federatedIdentity;q&&(L(e,_.FEDERATED_ACCESS_TOKEN,q.access_token||"",{path:A()||"/",httpOnly:!1,expires:new Date(D)}),L(e,_.FEDERATED_ID_TOKEN,q.id_token||"",{path:A()||"/",httpOnly:!1,expires:new Date(D)}))}else r.warn(`OIDC introspect error: ${g.statusText}`)}if(L(e,_.AUTHORIZATION,c,{path:A()||"/",httpOnly:!0,expires:new Date(D)}),c!==d.id_token&&Buffer.byteLength(d.id_token)<=N?L(e,_.IDP_ID_TOKEN,d.id_token||"",{path:A()||"/",httpOnly:!0,expires:new Date(D)}):P(e,_.IDP_ID_TOKEN,{path:A()||"/"}),L(e,_.IDP_ACCESS_TOKEN,d.access_token||"",{path:A()||"/",httpOnly:!0,expires:new Date(D)}),d.refresh_token?L(e,_.IDP_REFRESH_TOKEN,d.refresh_token,{path:A()||"/",httpOnly:!0,expires:new Date(D)}):P(e,_.IDP_REFRESH_TOKEN,{path:A()||"/"}),P(e,"code_verifier",{path:A()||"/"}),U(e),i&&o.redirectTo&&typeof o.redirectTo=="string"&&o.redirectTo.includes(k.MCP_CALLBACK)){const S=`${e.req.url.split("?")[0].replace(k.OIDC_CALLBACK,"")}${o.redirectTo}`;return e.newResponse(null,302,{Location:S})}const W=typeof o.redirectTo=="string"?o.redirectTo:void 0;let X=H(new URL(W||"/",e.req.url).pathname);const Y=e.newResponse(null,302,{Location:X});return r.updateContext({email:l.email,subject:l.sub}),r.info("OIDC login successful"),Y}catch(m){const f=m instanceof Error?m.message:String(m),C=m instanceof Error?m.stack:String(m);if(r.error(`OIDC login error: ${f}`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:f,error_details:C}]),m.error==="access_denied")return r.info("Access denied"),e.text("Forbidden",403)}const R="Something went wrong";return r.error(`OIDC login error: ${R}`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:R,error_details:null}]),e.text(R,500)}}function We(n){return async e=>{const r=e.get("logger"),o=e.get("auth").claims?.idpId,i=n.getConfig().ssoDirect?.[o];if(e.req.method==="POST")return b(i)||(z(e),j(e)),r.info("Logout successful"),e.newResponse(null,200,{});let u;if(b(i)){const s=(await J(o,i)).end_session_endpoint;if(s){const p=new URL(e.req.url),R=e.req.header("x-forwarded-proto")||p.protocol.slice(0,-1)||"https",m=e.req.header("x-forwarded-host")||p.host,f=`${R}://${m}`,C=G(f),O=C?ye(JSON.stringify({branch:G(f)})):void 0,I=C?`${Te(f)}/${ie}/logout`:`${f}${w(k.POST_LOGOUT)}`,d=e.get("cookies"),l=d?.[_.AUTHORIZATION],T=d?.[_.IDP_ID_TOKEN]||(_e(l)?l:void 0);u=Ie(s,I,T,O)}}return r.info("Logout successful"),z(e),j(e),e.newResponse(null,302,{Location:u||w("/")})}}function Xe(n){return async e=>{const r=n.getConfig().access?.logoutReturnUrl,a=r||w("/");return e.newResponse(null,302,{Location:a})}}function Ye(n){return async e=>{const r=e.get("logger"),a=e.req.param("code"),o=K.BH_API_URL,h=(i,u,s)=>i&&u?`${i} ${u.charAt(0)}`:s;try{if(!o)throw new Error("BH_API_URL is not set");const i=n.getConfig().ssoDirect;if(!i||!Object.keys(i).length)return r.warn("Invite no sso configured to handle"),e.redirect(w("/"));const u=await se(`${o}/public-user-invites/${a}`);if(!u.ok)return u.status===404?(r.warn(`Invite ${a} not found redirect to homepage`),e.redirect(w("/"))):(r.error("Invite error",await u.text()),e.redirect(w("/")));const s=await u.json(),p=new URL(w("/invite"),e.req.url);return p.searchParams.set("code",a),p.searchParams.set("org",s.organization.name),p.searchParams.set("invitedBy",h(s.invitedBy.firstName,s.invitedBy.lastName,s.invitedBy.name)),e.newResponse(null,302,{Location:p.toString()})}catch(i){return r.error("Error processing invite",{error:i,inviteCode:a}),e.text(i.message||"Failed to process invite",400)}}}function Qe(n){return async e=>{const r=e.get("logger"),a=n.getConfig().ssoDirect,o=new URL(e.req.url),h=e.req.query("inviteCode"),i=e.req.header("x-forwarded-proto")||o.protocol.slice(0,-1)||"https",u=e.req.header("x-forwarded-host")||o.host,s=`${i}://${u}`;let p=o.searchParams.get("idpId");const R=o.searchParams.get("redirectTo"),m=Object.keys(a||{})[0];p=p||m;const f=o.searchParams.get("mcp_redirect_uri"),C=!!f;if(!a?.[p]){const c="Invalid idpId";if(r.error(`IdP login error: ${c}`),C){const F=V(R||void 0);y.sendMcpAuthorizationFailedMessage([{...M(F),error:c,error_details:null}])}return e.text(`Forbidden: ${c}`,403)}const I=Me({localePrefixParam:o.searchParams.get("localePrefix"),l10n:n.getGlobalData()?.l10n}),d=p&&a?await de(p,a[p]):void 0,l={};for(const c of Object.keys(d?.extraParams||{}))l[c]=o.searchParams.get(c)||d?.extraParams?.[c]||void 0;ae(e)&&(l.prompt="login",l.post_logout="1");let T,t={};if(C&&f&&d&&d.type===Q.OIDC){r.info(`Building MCP OAuth login URL with redirect_uri: ${f}`);const c=Le("",{...d,extraParams:l},R,h,{redirectUriOverride:f,sourceOverride:"mcp",branchOverride:void 0,uiLocales:I});T=c.loginUrl,t=c.cookies||{}}else if(d){const c=ue({...d,extraParams:l},s,R,h,I);T=c.loginUrl,t=c.cookies||{}}return Object.keys(t).forEach(c=>{L(e,c,t[c].value,t[c].options)}),r.info(`IdP login initiated for ID '${p}'`),e.newResponse(null,302,{Location:T||new URL(e.req.url).pathname})}}function xe(n){return async e=>{const r=e.get("logger"),a=await e.req.formData(),o=a.get("SAMLResponse"),h=a.get("RelayState");if(typeof o!="string"||typeof h!="string"){const t="SAMLResponse is required";return r.error(`SAML2 login error: ${t}`),e.text(`Bad request: ${t}`,400)}const i=pe(o),{success:u,uid:s,nameFormat:p,attrs:R,issuerId:m,expiresAt:f}=fe(i),{idpId:C,redirectTo:O}=JSON.parse(h);if(!u){const t="SAML2 assertion is not successful";return r.error(`SAML2 login error: ${t}`),e.text(`Permission denied: ${t}`,401)}if(!f||Math.ceil(Date.now()/1e3)>=f){const t="SAML2 Token Expired";return r.error(`SAML2 login error: ${t}`),e.text(t,401)}const I=n.getConfig().ssoDirect?.[C];if(!I||!le(I)){const t="Cannot find valid IdP";return r.error(`SAML2 login error: ${t}`),e.text(`Permission denied: ${t}`,401)}if(!(I.issuerId&&m&&ee(I.issuerId,m))){const t="IssuerID is misconfigured or untrusted assertions issuer received";return r.error(`SAML2 login error: ${t}`),e.text(`Permission denied: ${t}`,401)}if(!await he(i,I.x509PublicCert)){const t="SAMLResponse signature invalid";return r.error(`SAML2 login error: ${t}`),e.text(t,401)}const l=ge(s,p,R,I.teamsAttributeName??x);if(!l.sub){const t="The provider did not return a valid user identity.";return r.error(`SAML2 login error: ${t}`),e.text(t,400)}if(!l.email){const t="The provider did not return a valid user email.";return r.error(`SAML2 login error: ${t}`),e.text(t,400)}let T=await E.sign({...l,idpId:C},v,$.HS256);if(Buffer.byteLength(T)>N){const t=await Z({...l},C,"teams",{rbacConfig:n.getConfig().access?.rbac,teamsMapping:I.teamsAttributeMap});if(!t){const c=`The SAML2 claims are too large for the session cookie: ${Buffer.byteLength(JSON.stringify(l))} bytes of claims`;return r.error(`SAML2 login error: ${c}`),e.text(`Forbidden: ${c}`,403)}t.teamsKept<t.teamsTotal&&r.warn(`SAML2 login: the "teams" claim is too large for the session cookie size limit; kept ${t.teamsKept} of ${t.teamsTotal} values, with teams from the RBAC configuration first; RBAC ignores the dropped teams: ${JSON.stringify(t.droppedTeams)}`),T=t.token}return L(e,_.AUTHORIZATION,T,{path:A()||"/",httpOnly:!0,expires:new Date(f*1e3)}),P(e,_.IDP_REFRESH_TOKEN,{path:A()||"/"}),U(e),r.updateContext({email:l.email,subject:l.sub}),r.info("SAML2 login successful"),e.newResponse(null,302,{Location:O||"/"})}}function er(n){return async e=>{const r=e.get("logger"),a=new URL(e.req.query("redirectTo")||"/",e.req.url),o=w(H(a.pathname)),h=n.getConfig().ssoDirect,i=Object.entries(h||{}).find(([,O])=>b(O)&&Oe(O));if(!(h&&i))return e.newResponse(null,302,{Location:o});const s=e.req.query("token"),p=s&&await Ae(s);if(!p)return e.newResponse(null,302,{Location:o});if(!re(p[oe]||[]).some(O=>O===te||O===ne))return e.newResponse(null,302,{Location:o});const f=await E.sign({...p,idpId:i?.at(0)},v,$.HS256),C=Date.now()+B*1e3;return L(e,_.AUTHORIZATION,f,{path:A()||"/",httpOnly:!0,expires:new Date(C),sameSite:"None",secure:!0}),P(e,_.IDP_REFRESH_TOKEN,{path:A()||"/"}),U(e),r.info("Token login successful"),e.newResponse(null,302,{Location:o})}}export{Ge as authorizeHandler,Qe as idpLoginHandler,Ye as inviteHandler,We as logoutHandler,Ve as oidcCallbackHandler,Xe as postLogoutHandler,Ze as redoclyLoginCallbackHandler,er as redoclyTokenLoginHandler,xe as samlCallbackHandler};
1
+ import{setCookie as L,deleteCookie as P}from"hono/cookie";import{AuthProviderType as Y,DEFAULT_TEAM_CLAIM_NAME as Q}from"@redocly/config";import{withPathPrefix as w,getPathPrefix as A}from"@redocly/theme/core/utils";import{compareURIs as x}from"../../../utils/url/compare-uris.js";import{ensureArray as ee}from"../../../utils/array/ensure-array.js";import{ALTERNATIVE_AUD_CLAIM_NAME as re,JWT_SECRET_KEY as U,MAX_AUTH_TOKEN_COOKIE_BYTES as N,ORG_SLUG as oe,ORG_ID as te}from"../../constants/common.js";import{AUTH_SEGMENT as ne,ServerRoutes as k}from"../../../constants/api.js";import{AuthCookieNames as _,DEFAULT_COOKIE_EXPIRATION as B}from"../../../constants/common.js";import{sanitizeRedirectPathname as H}from"../../../utils/url/sanitize-redirect-pathname.js";import{telemetry as y}from"../../telemetry/index.js";import{envConfig as K}from"../../config/env-config.js";import{fetchWithClient as ie}from"../../utils/fetch-with-client.js";import{clearAuthCookies as z,clearPostLogoutCookie as v,hasPostLogoutCookie as se,setPostLogoutCookie as j}from"../../utils/cookie.js";import{getAuthProviderLoginParams as ae,isOidcProviderConfig as b,isSaml2ProviderConfig as de,oidcExchangeCodeForToken as le,buildLoginUrl as ce,decodeSamlResponse as ue,extractUserClaims as pe,parseSamlResponse as ge,parseOidcState as fe,verifySAMLResponse as me,getUsernameFromPayload as he,buildOidcLogoutUrl as Ce,isIdpIssuedToken as Ie,getOidcMetadata as J,getRedoclyTokenPayload as _e,idTokenAudienceIsAllowed as Ae,isRedoclySso as Oe,rewritePreviewAuthRedirectUri as Re,parsePreviewBranch as Te,buildOidcLoginUrl as Le,createMcpSessionResource as M,signTokenWithinCookieSizeLimit as G}from"../auth.js";import*as $ from"../jwt/jwt.js";import{AlgorithmTypes as E}from"../jwt/types.js";import{handleErrorPageRender as we}from"../utils.js";import{encodeBase64URL as ye}from"../jwt/encode.js";import{resolveUiLocalesForIdpLogin as Me}from"./helpers/resolve-ui-locales-for-idp-login.js";async function Ge(n,e){if(!e.devLogin||K.isProductionEnv)return n.newResponse(null,404,{});const{password:r,...a}=await n.req.json(),o=await $.sign({...a,name:a.username||a.email||"Unknown"},U,E.HS256);return L(n,_.AUTHORIZATION,o,{path:A()||"/",httpOnly:!0,secure:!0,sameSite:"none"}),v(n),n.newResponse(null,200,{})}function Ze(){return async n=>{const e=n.get("logger"),r=encodeURIComponent(n.req.query("message")||"");e.error(`Login error: ${r}`);const a=`${k.LOGIN}/?error=${encodeURIComponent(r)}`;return n.newResponse(null,301,{Location:a})}}function Z(n){if(!n||!n.includes(k.MCP_CALLBACK))return null;try{const e=n.split("/"),r=e[e.length-1];if(r){const a=Buffer.from(r,"base64url").toString("utf-8");return JSON.parse(a).mcpSessionId||null}}catch{}return null}function Ve(n){return async e=>{const r=e.get("logger"),a=n.getConfig().ssoDirect;let o;try{o=fe(e.req.query("state"))}catch{return r.warn("OIDC login error: Invalid OAuth2 state"),e.text("Bad request: Invalid OAuth2 state",400)}const C=o.idpId,i=o.source==="mcp"||o.redirectTo&&typeof o.redirectTo=="string"&&o.redirectTo.includes(k.MCP_CALLBACK),u=i?Z(typeof o.redirectTo=="string"?o.redirectTo:void 0):null,s=a?.[C];if(!b(s))return r.error("OIDC login error: missing OIDC provider config"),e.text("Forbidden",403);const p=await J(C,s);if(a&&!p.token_endpoint){const m="Invalid OIDC configuration: token_endpoint is required";return r.error(`OIDC login error: ${m}`),e.text(m,500)}try{const m=p.token_endpoint,f=e.req.query("code"),h=e.req.query("error");if(h)return i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:`OIDC error: ${h}`,error_details:e.req.query("error_description")||null}]),we(e,n,{slug:"/"},403,"403OIDC");if(!f){const g="Code is expected but not present";return r.error(`OIDC login error: ${g}`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:g,error_details:null}]),new Response(`Forbidden: ${g}`,{status:403})}const R=typeof o.redirectUri=="string"?o.redirectUri:new URL(w(k.OIDC_CALLBACK),e.req.url).toString(),I=e.get("cookies")?.code_verifier,d=await le(m,f,R,s,{...s.tokenRequestCustomParams,...I?{code_verifier:I}:{}});if(d.error)return r.error(`Error from OIDC provider: "${d.error}"`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:`Token exchange error: ${d.error}`,error_details:d.error_description||null}]),e.text(`Forbidden: ${d.error_description||d.error}`,403);if(!d?.id_token){const g="No id_token, please, add openid to scopes";return r.error(`OIDC login error: ${g}`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:g,error_details:null}]),new Response(`Forbidden: ${g}`,{status:403})}const{payload:l,header:T}=$.decode(d.id_token),t=T.alg===E.RS256;if(!Ae(l,s)){const g="No valid audience found in id_token";return r.error(`OIDC login error: ${g}`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:g,error_details:null}]),new Response(`Forbidden: ${g}`,{status:403})}let c=t?d.id_token:await $.sign({...l,idpId:C},U,E.HS256);if(Buffer.byteLength(c)>N){const g=await G(l,C,s.teamsClaimName,{rbacConfig:n.getConfig().access?.rbac,teamsMapping:s.teamsClaimMap});if(!g){const S=`The id_token claims are too large for the session cookie: ${Buffer.byteLength(JSON.stringify(l))} bytes of claims`;return r.error(`OIDC login error: ${S}`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:S,error_details:null}]),e.text(`Forbidden: ${S}`,403)}g.teamsKept<g.teamsTotal&&r.warn(`OIDC login: the "${s.teamsClaimName}" claim is too large for the session cookie size limit; kept ${g.teamsKept} of ${g.teamsTotal} values, with teams from the RBAC configuration first; RBAC ignores the dropped teams: ${JSON.stringify(g.droppedTeams)}`),c=g.token}he(l)||r.warn("To display your username, the required 'email' or 'full_profile' scope must be added to the identity provider configuration");const D=s?.tokenExpirationTime?Date.now()+s.tokenExpirationTime*1e3:l.exp*1e3||Date.now()+B*1e3;if(s.introspectEndpoint){const g=await fetch(s.introspectEndpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({access_token:d.access_token})});if(g.ok){const q=(await g.json()).ext?.federatedIdentity;q&&(L(e,_.FEDERATED_ACCESS_TOKEN,q.access_token||"",{path:A()||"/",httpOnly:!1,expires:new Date(D)}),L(e,_.FEDERATED_ID_TOKEN,q.id_token||"",{path:A()||"/",httpOnly:!1,expires:new Date(D)}))}else r.warn(`OIDC introspect error: ${g.statusText}`)}if(L(e,_.AUTHORIZATION,c,{path:A()||"/",httpOnly:!0,expires:new Date(D)}),c!==d.id_token&&Buffer.byteLength(d.id_token)<=N?L(e,_.IDP_ID_TOKEN,d.id_token||"",{path:A()||"/",httpOnly:!0,expires:new Date(D)}):P(e,_.IDP_ID_TOKEN,{path:A()||"/"}),L(e,_.IDP_ACCESS_TOKEN,d.access_token||"",{path:A()||"/",httpOnly:!0,expires:new Date(D)}),d.refresh_token?L(e,_.IDP_REFRESH_TOKEN,d.refresh_token,{path:A()||"/",httpOnly:!0,expires:new Date(D)}):P(e,_.IDP_REFRESH_TOKEN,{path:A()||"/"}),P(e,"code_verifier",{path:A()||"/"}),v(e),i&&o.redirectTo&&typeof o.redirectTo=="string"&&o.redirectTo.includes(k.MCP_CALLBACK)){const S=`${e.req.url.split("?")[0].replace(k.OIDC_CALLBACK,"")}${o.redirectTo}`;return e.newResponse(null,302,{Location:S})}const V=typeof o.redirectTo=="string"?o.redirectTo:void 0;let W=H(new URL(V||"/",e.req.url).pathname);const X=e.newResponse(null,302,{Location:W});return r.updateContext({email:l.email,subject:l.sub}),r.info("OIDC login successful"),X}catch(m){const f=m instanceof Error?m.message:String(m),h=m instanceof Error?m.stack:String(m);if(r.error(`OIDC login error: ${f}`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:f,error_details:h}]),m.error==="access_denied")return r.info("Access denied"),e.text("Forbidden",403)}const O="Something went wrong";return r.error(`OIDC login error: ${O}`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:O,error_details:null}]),e.text(O,500)}}function We(n){return async e=>{const r=e.get("logger"),o=e.get("auth").claims?.idpId,i=n.getConfig().ssoDirect?.[o];if(e.req.method==="POST")return b(i)||(z(e),j(e)),r.info("Logout successful"),e.newResponse(null,200,{});let u;if(b(i)){const s=(await J(o,i)).end_session_endpoint;if(s){const p=new URL(e.req.url),O=e.req.header("x-forwarded-proto")||p.protocol.slice(0,-1)||"https",m=e.req.header("x-forwarded-host")||p.host,f=`${O}://${m}`,h=Te(f),R=h?ye(JSON.stringify({branch:h,postLogoutRedirectUri:`${f}${w(k.POST_LOGOUT)}`})):void 0,I=h?`${Re(f)}/${ne}/logout`:`${f}${w(k.POST_LOGOUT)}`,d=e.get("cookies"),l=d?.[_.AUTHORIZATION],T=d?.[_.IDP_ID_TOKEN]||(Ie(l)?l:void 0);u=Ce(s,I,T,R)}}return r.info("Logout successful"),z(e),j(e),e.newResponse(null,302,{Location:u||w("/")})}}function Xe(n){return async e=>{const r=n.getConfig().access?.logoutReturnUrl,a=r||w("/");return e.newResponse(null,302,{Location:a})}}function Ye(n){return async e=>{const r=e.get("logger"),a=e.req.param("code"),o=K.BH_API_URL,C=(i,u,s)=>i&&u?`${i} ${u.charAt(0)}`:s;try{if(!o)throw new Error("BH_API_URL is not set");const i=n.getConfig().ssoDirect;if(!i||!Object.keys(i).length)return r.warn("Invite no sso configured to handle"),e.redirect(w("/"));const u=await ie(`${o}/public-user-invites/${a}`);if(!u.ok)return u.status===404?(r.warn(`Invite ${a} not found redirect to homepage`),e.redirect(w("/"))):(r.error("Invite error",await u.text()),e.redirect(w("/")));const s=await u.json(),p=new URL(w("/invite"),e.req.url);return p.searchParams.set("code",a),p.searchParams.set("org",s.organization.name),p.searchParams.set("invitedBy",C(s.invitedBy.firstName,s.invitedBy.lastName,s.invitedBy.name)),e.newResponse(null,302,{Location:p.toString()})}catch(i){return r.error("Error processing invite",{error:i,inviteCode:a}),e.text(i.message||"Failed to process invite",400)}}}function Qe(n){return async e=>{const r=e.get("logger"),a=n.getConfig().ssoDirect,o=new URL(e.req.url),C=e.req.query("inviteCode"),i=e.req.header("x-forwarded-proto")||o.protocol.slice(0,-1)||"https",u=e.req.header("x-forwarded-host")||o.host,s=`${i}://${u}`;let p=o.searchParams.get("idpId");const O=o.searchParams.get("redirectTo"),m=Object.keys(a||{})[0];p=p||m;const f=o.searchParams.get("mcp_redirect_uri"),h=!!f;if(!a?.[p]){const c="Invalid idpId";if(r.error(`IdP login error: ${c}`),h){const F=Z(O||void 0);y.sendMcpAuthorizationFailedMessage([{...M(F),error:c,error_details:null}])}return e.text(`Forbidden: ${c}`,403)}const I=Me({localePrefixParam:o.searchParams.get("localePrefix"),l10n:n.getGlobalData()?.l10n}),d=p&&a?await ae(p,a[p]):void 0,l={};for(const c of Object.keys(d?.extraParams||{}))l[c]=o.searchParams.get(c)||d?.extraParams?.[c]||void 0;se(e)&&(l.prompt="login",l.post_logout="1");let T,t={};if(h&&f&&d&&d.type===Y.OIDC){r.info(`Building MCP OAuth login URL with redirect_uri: ${f}`);const c=Le("",{...d,extraParams:l},O,C,{redirectUriOverride:f,sourceOverride:"mcp",branchOverride:void 0,uiLocales:I});T=c.loginUrl,t=c.cookies||{}}else if(d){const c=ce({...d,extraParams:l},s,O,C,I);T=c.loginUrl,t=c.cookies||{}}return Object.keys(t).forEach(c=>{L(e,c,t[c].value,t[c].options)}),r.info(`IdP login initiated for ID '${p}'`),e.newResponse(null,302,{Location:T||new URL(e.req.url).pathname})}}function xe(n){return async e=>{const r=e.get("logger"),a=await e.req.formData(),o=a.get("SAMLResponse"),C=a.get("RelayState");if(typeof o!="string"||typeof C!="string"){const t="SAMLResponse is required";return r.error(`SAML2 login error: ${t}`),e.text(`Bad request: ${t}`,400)}const i=ue(o),{success:u,uid:s,nameFormat:p,attrs:O,issuerId:m,expiresAt:f}=ge(i),{idpId:h,redirectTo:R}=JSON.parse(C);if(!u){const t="SAML2 assertion is not successful";return r.error(`SAML2 login error: ${t}`),e.text(`Permission denied: ${t}`,401)}if(!f||Math.ceil(Date.now()/1e3)>=f){const t="SAML2 Token Expired";return r.error(`SAML2 login error: ${t}`),e.text(t,401)}const I=n.getConfig().ssoDirect?.[h];if(!I||!de(I)){const t="Cannot find valid IdP";return r.error(`SAML2 login error: ${t}`),e.text(`Permission denied: ${t}`,401)}if(!(I.issuerId&&m&&x(I.issuerId,m))){const t="IssuerID is misconfigured or untrusted assertions issuer received";return r.error(`SAML2 login error: ${t}`),e.text(`Permission denied: ${t}`,401)}if(!await me(i,I.x509PublicCert)){const t="SAMLResponse signature invalid";return r.error(`SAML2 login error: ${t}`),e.text(t,401)}const l=pe(s,p,O,I.teamsAttributeName??Q);if(!l.sub){const t="The provider did not return a valid user identity.";return r.error(`SAML2 login error: ${t}`),e.text(t,400)}if(!l.email){const t="The provider did not return a valid user email.";return r.error(`SAML2 login error: ${t}`),e.text(t,400)}let T=await $.sign({...l,idpId:h},U,E.HS256);if(Buffer.byteLength(T)>N){const t=await G({...l},h,"teams",{rbacConfig:n.getConfig().access?.rbac,teamsMapping:I.teamsAttributeMap});if(!t){const c=`The SAML2 claims are too large for the session cookie: ${Buffer.byteLength(JSON.stringify(l))} bytes of claims`;return r.error(`SAML2 login error: ${c}`),e.text(`Forbidden: ${c}`,403)}t.teamsKept<t.teamsTotal&&r.warn(`SAML2 login: the "teams" claim is too large for the session cookie size limit; kept ${t.teamsKept} of ${t.teamsTotal} values, with teams from the RBAC configuration first; RBAC ignores the dropped teams: ${JSON.stringify(t.droppedTeams)}`),T=t.token}return L(e,_.AUTHORIZATION,T,{path:A()||"/",httpOnly:!0,expires:new Date(f*1e3)}),P(e,_.IDP_REFRESH_TOKEN,{path:A()||"/"}),v(e),r.updateContext({email:l.email,subject:l.sub}),r.info("SAML2 login successful"),e.newResponse(null,302,{Location:R||"/"})}}function er(n){return async e=>{const r=e.get("logger"),a=new URL(e.req.query("redirectTo")||"/",e.req.url),o=w(H(a.pathname)),C=n.getConfig().ssoDirect,i=Object.entries(C||{}).find(([,R])=>b(R)&&Oe(R));if(!(C&&i))return e.newResponse(null,302,{Location:o});const s=e.req.query("token"),p=s&&await _e(s);if(!p)return e.newResponse(null,302,{Location:o});if(!ee(p[re]||[]).some(R=>R===oe||R===te))return e.newResponse(null,302,{Location:o});const f=await $.sign({...p,idpId:i?.at(0)},U,E.HS256),h=Date.now()+B*1e3;return L(e,_.AUTHORIZATION,f,{path:A()||"/",httpOnly:!0,expires:new Date(h),sameSite:"None",secure:!0}),P(e,_.IDP_REFRESH_TOKEN,{path:A()||"/"}),v(e),r.info("Token login successful"),e.newResponse(null,302,{Location:o})}}export{Ge as authorizeHandler,Qe as idpLoginHandler,Ye as inviteHandler,We as logoutHandler,Ve as oidcCallbackHandler,Xe as postLogoutHandler,Ze as redoclyLoginCallbackHandler,er as redoclyTokenLoginHandler,xe as samlCallbackHandler};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/realm",
3
- "version": "0.138.0-next.10",
3
+ "version": "0.138.0-next.12",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,12 +33,12 @@
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.7",
37
- "@redocly/config": "0.62.0",
38
- "@redocly/openapi-core": "2.55.0",
36
+ "@redocly/api-docs": "0.3.0-next.9",
37
+ "@redocly/config": "0.64.0",
38
+ "@redocly/openapi-core": "2.58.1",
39
39
  "@redocly/portal-legacy-ui": "0.19.0",
40
- "@redocly/portal-plugin-mock-server": "0.23.0-next.0",
41
- "@redocly/realm-asyncapi-sdk": "0.16.0-next.2",
40
+ "@redocly/portal-plugin-mock-server": "0.23.0-next.1",
41
+ "@redocly/realm-asyncapi-sdk": "0.16.0-next.3",
42
42
  "@redocly/theme": "0.70.0-next.7",
43
43
  "@shikijs/transformers": "3.21.0",
44
44
  "@tanstack/react-query": "5.62.3",