@redocly/revel 0.138.0-next.16 → 0.138.0-next.18
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 +25 -0
- package/dist/api-schemas/mcp-fetch-count.d.ts +24 -0
- package/dist/api-schemas/mcp-fetch-count.js +1 -0
- package/dist/cli/develop.js +1 -1
- package/dist/cli/eject/ejectTheme.js +1 -1
- package/dist/cli/translations/collect-config-translations.js +1 -1
- package/dist/client/polyfills.js +1 -1
- package/dist/client/utils/catalog/parse-filter-query.js +1 -1
- package/dist/constants/api.d.ts +1 -0
- package/dist/constants/api.js +1 -1
- package/dist/constants/common.d.ts +6 -0
- package/dist/constants/common.js +1 -1
- package/dist/server/constants/entitlements.js +1 -1
- package/dist/server/entitlements/entitlements-provider.js +1 -1
- package/dist/server/esbuild/esbuild.js +3 -3
- package/dist/server/esbuild/plugins/on-rebuild.js +1 -1
- package/dist/server/esbuild/plugins/styled-components-ssr.js +1 -1
- package/dist/server/esbuild/plugins/yaml-loader.js +1 -1
- package/dist/server/plugins/analytics/amplitude/index.js +1 -1
- package/dist/server/plugins/analytics/heap/browser-hooks.js +1 -1
- package/dist/server/plugins/api-docs/loader.js +3 -3
- package/dist/server/plugins/api-docs/utils/apply-nav-item-to-root.js +1 -1
- package/dist/server/plugins/arazzo-docs/arazzo-doc-loader.js +2 -2
- package/dist/server/plugins/config-parser/format-custom-tags.js +1 -1
- package/dist/server/plugins/config-parser/loaders/content-slugs-loader.js +1 -1
- package/dist/server/plugins/config-parser/loaders/versions-config-loader.js +1 -1
- package/dist/server/plugins/config-parser/resolve-mutual-exclusion.js +1 -1
- package/dist/server/plugins/default-theme/resolve-logo.js +1 -1
- package/dist/server/plugins/dev-onboarding/api/adapters/apigee/adapter.js +2 -2
- package/dist/server/plugins/dev-onboarding/api/adapters/gravitee/client.js +2 -2
- package/dist/server/plugins/dev-onboarding/api/combine.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/AppOverview.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/useSortableData.js +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-svg-content.js +1 -1
- package/dist/server/plugins/markdown/markdoc/markdoc-options.js +1 -1
- package/dist/server/plugins/markdown/markdown-static-data-loader.js +1 -1
- package/dist/server/plugins/markdown/search/get-ai-search-documents.js +1 -1
- package/dist/server/plugins/markdown/utils/sanitize-malformed-md-content.js +2 -2
- package/dist/server/plugins/mcp/docs-mcp/tools/core/utils.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-bridge.d.ts +2 -1
- package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-bridge.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/fetch-capability.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/fetch-limiter/fetch-count-client.d.ts +4 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/fetch-limiter/fetch-count-client.js +1 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/fetch-limiter/fetch-count-secret.d.ts +4 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/fetch-limiter/fetch-count-secret.js +1 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/fetch-limiter/fetch-count.d.ts +11 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/fetch-limiter/fetch-count.js +1 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/fetch-limiter/fetch-counter-repository.d.ts +13 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/fetch-limiter/fetch-counter-repository.js +13 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/{limits.d.ts → fetch-limiter/fetch-limits.d.ts} +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/fetch-limiter/fetch-limits.js +1 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/{apply-edge-rate-limit.d.ts → request-limiter/apply-edge-rate-limit.d.ts} +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/request-limiter/apply-edge-rate-limit.js +1 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/{edge-limiter.d.ts → request-limiter/edge-limiter.d.ts} +3 -3
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/request-limiter/edge-limiter.js +1 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/request-limiter/rate-limit-telemetry.js +1 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/request-limiter/request-limits.d.ts +6 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/request-limiter/request-limits.js +1 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/utils/ip-key.js +1 -0
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/{window-counter.d.ts → utils/window-counter.d.ts} +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/utils/window-counter.js +1 -0
- package/dist/server/plugins/mcp/handlers/docs-mcp-handler.js +1 -1
- package/dist/server/plugins/mcp/handlers/handle-mcp-request.js +1 -1
- package/dist/server/plugins/mcp/index.js +1 -1
- package/dist/server/plugins/mcp/servers/mcp-server.js +2 -2
- package/dist/server/plugins/mcp/types.d.ts +9 -0
- package/dist/server/plugins/nav-utils.js +1 -1
- package/dist/server/plugins/scorecard-classic/loaders/scorecard.js +1 -1
- package/dist/server/plugins/scorecard-classic/template/useData.js +1 -1
- package/dist/server/plugins/search/engines/flexsearch/index.js +2 -2
- package/dist/server/plugins/sidebars/index.js +2 -2
- package/dist/server/plugins/sidebars/utils.js +1 -1
- package/dist/server/tools/notifiers/formatter.js +1 -1
- package/dist/server/tools/notifiers/helpers/privacy/mask-email.js +1 -1
- package/dist/server/tools/notifiers/logger.js +1 -1
- package/dist/server/types/web-server.d.ts +1 -0
- package/dist/server/utils/codeframes/codeframes.js +2 -2
- package/dist/server/utils/entitlements/read-entitlement-number.d.ts +3 -0
- package/dist/server/utils/entitlements/read-entitlement-number.js +1 -0
- package/dist/server/utils/fs.js +1 -1
- package/dist/server/utils/rbac.js +1 -1
- package/dist/server/web-server/dev-server.js +1 -1
- package/dist/server/web-server/http.d.ts +2 -1
- package/dist/server/web-server/http.js +2 -2
- package/dist/server/web-server/middleware/catalogAuthMiddleware.js +1 -1
- package/dist/server/web-server/middleware/dynamic-middleware/hono-compose.js +1 -1
- package/dist/server/web-server/middleware/responseHeadersMiddleware.js +1 -1
- package/dist/server/web-server/routes/auth.js +1 -1
- package/dist/server/web-server/routes/mcp-routes/mcp-fetch-count.d.ts +8 -0
- package/dist/server/web-server/routes/mcp-routes/mcp-fetch-count.js +1 -0
- package/dist/server/web-server/routes/mcp-routes/mcp-routes.js +1 -1
- package/dist/server/web-server/routes/resolve-route.js +1 -1
- package/dist/types/entitlements.d.ts +2 -0
- package/dist/utils/time/debounce.js +1 -1
- package/package.json +5 -5
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/apply-edge-rate-limit.js +0 -1
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/edge-limiter.js +0 -1
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/ip-key.js +0 -1
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/limits.js +0 -1
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/rate-limit-telemetry.js +0 -1
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/window-counter.js +0 -1
- /package/dist/server/plugins/mcp/gateway-mcp/rate-limit/{rate-limit-telemetry.d.ts → request-limiter/rate-limit-telemetry.d.ts} +0 -0
- /package/dist/server/plugins/mcp/gateway-mcp/rate-limit/{ip-key.d.ts → utils/ip-key.d.ts} +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{readdir as S,appendFile as A}from"node:fs/promises";import l from"node:path";import{createReadStream as v,existsSync as w}from"node:fs";import{createInterface as O}from"node:readline";import{SEARCH_GROUP_FACET_FIELD as C,SEARCH_PRODUCT_FIELD as N,SEARCH_VERSION_FIELD as j}from"../../../../../constants/common.js";import{SEARCH_INDEX_FIELDS as H,SEARCH_DATA_EXPORT_FOLDER as
|
|
2
|
-
`}async function*P(
|
|
1
|
+
import{readdir as S,appendFile as A}from"node:fs/promises";import l from"node:path";import{createReadStream as v,existsSync as w}from"node:fs";import{createInterface as O}from"node:readline";import{SEARCH_GROUP_FACET_FIELD as C,SEARCH_PRODUCT_FIELD as N,SEARCH_VERSION_FIELD as j}from"../../../../../constants/common.js";import{SEARCH_INDEX_FIELDS as H,SEARCH_DATA_EXPORT_FOLDER as _,SEARCH_MAX_INMEMORY_DOCUMENTS_COUNT as L}from"../../../../constants/plugins/search.js";import{telemetryTraceStep as T}from"../../../../telemetry/helpers/trace-step.js";import{envConfig as M}from"../../../../config/env-config.js";import{ensureDir as u}from"../../../../utils/index.js";import{FlexSearchIndex as b}from"./search-index.js";const F=".jsonl";function R(h){return`${JSON.stringify(h)}
|
|
2
|
+
`}async function*P(h){const t=O({input:v(h,{encoding:"utf8"}),crlfDelay:1/0});for await(const s of t)s&&(yield JSON.parse(s))}class K{#e=new Map;#s;#t=0;async initIndexSchema(t){this.#s={document:{id:"id",tag:"tags",index:H},worker:!1,tokenize:"forward",context:{depth:2,resolution:9}}}#n(t,s){const i=this.#e.get(t);if(i)return i.find(n=>n.id===s)}#i(t,s){let i=this.#n(t,s);return i||(i=new b(s,this.#s),this.#e.set(t,[...this.#e.get(t)??[],i])),i}async addDocuments(t,s){if(t.length){const{group:i,locale:n,outDir:c}=s,o=this.#i(n,i);for(const r of t)o.add(r),M.isBuildMode&&(this.#t++,this.#t>=L&&await this.exportDocuments(c))}}getIndexesSize(){return this.#e.size}async search(t,s,i){return await T("com.redocly.search.performed",async n=>{n?.setAttribute("id","searchQueryId"),n?.setAttribute("object","searchQuery"),n?.setAttribute("uri","urn:redocly:realm:api:searchQuery:searchQueryId");const{query:c,locale:o,filter:r,loadMore:a}=t,d=this.#e.get(o)??[],m=[];let g="";const D=["v:default"];if(n?.setAttribute("locale",o),r)for(const e of r)e.field===C?m.push(...e.values):e.field===N?g=`p:${e.values[0]}`:e.field===j&&e.values.length&&e.values.length===2&&D.push(`v:${e.values[0]}:${e.values[1]}`);const y={auth:s,query:c,offset:0,SEARCH_GROUP_FACET_FIELD:C,product:g,versions:D},x=[],f={facets:{},documents:{}};if(a){const e=this.#n(o,a.groupKey);e&&x.push(e.search({...y,offset:a.offset}))}else for(const e of d)m&&m.length?m.includes(e.id)&&x.push(e.search(y)):x.push(e.search(y));const E=await Promise.all(x),$=E.some(e=>e.hasCoveringHit);for(const e of E)if(!($&&!e.hasCoveringHit)){f.documents={...f.documents,...e.documents};for(const[p,I]of Object.entries(e.facets))f.facets[p]?f.facets[p]=[...f.facets[p],...I]:f.facets[p]=I}return f})}async exportDocuments(t){const s=u(l.join(t,_));for(const[i,n]of this.#e){const c=u(l.join(s,i));for(const o of n){const r=Array.from(o.documents,d=>R({document:d}));if(r.length===0)continue;const a=u(l.join(c,`${o.id}${F}`));await A(a,r.join(""),{encoding:"utf8"}),o.clearDocuments()}}this.#t=0}async exportIndexes(t){const s=u(l.join(t,_));for(const[i,n]of this.#e){const c=u(l.join(s,i));for(const o of n){const r=u(l.join(c,`${o.id}${F}`));await o.export((a,d)=>A(r,R({index:[String(a),d]}),{encoding:"utf8"}))}}this.#e.clear()}async import(t){const s=`${t}/${_}`;if(!w(s))return;const i=await S(s);for(const n of i){const c=await S(`${s}/${n}`);for(const o of c){const r=`${s}/${n}/${o}`,a=l.parse(o).name;await this.#i(n,a).import(P(r))}}}countFacets(t,s,i){const n={};for(const[c,o]of i)n[c]=o.values.map(r=>({value:r,count:0,isCounterVisible:!1}));return n}cleanupFacetValues(t){}}export{K as FlexSearch};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{writeFileSync as at}from"node:fs";import*as g from"path";import{removeMarkdocTags as nt}from"../../../markdoc/helpers/remove-markdoc-tags.js";import{withoutHash as it}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as E,SIDEBAR_PREFIX as st,CONFIG_FILE_NAME as Y}from"../../../constants/common.js";import{CATALOG_OUTPUT_FILE_NAME as lt}from"../../constants/common.js";import{findDeepFirst as B}from"../../../utils/tree/find-deep-first.js";import{isDefined as M}from"../../../utils/guards/is-defined.js";import{partition as ct}from"../../../utils/array/partition.js";import{collectPropValueDeep as N}from"../../../utils/tree/collect-prop-value-deep.js";import{envConfig as ft}from"../../config/env-config.js";import{isLocalLink as ut}from"../../../utils/path/is-local-link.js";import{normalizeRouteSlug as J}from"../../../utils/path/normalize-route-slug.js";import{slash as dt}from"../../../utils/path/slash.js";import{parsePathVersions as mt}from"../../../utils/path/parse-path-versions.js";import{reporter as I}from"../../tools/notifiers/reporter.js";import{logger as $}from"../../tools/notifiers/logger.js";import{sha1 as gt}from"../../utils/crypto/sha1.js";import{getCatalogSharedDataId as pt}from"../../utils/catalog-classic/get-catalog-shared-data-id.js";import{collectItemsLinkedToSidebars as ht,resolveItems as q}from"../nav-utils.js";import{getExcludedFromLinkCheckerPatterns as yt,getSidebarReferences as St,hasCircularDependency as bt}from"./utils.js";import{collectTabRoutes as vt,findDuplicateRoutes as It,hasTabs as Dt,resolveTabs as Ft,tabsAsGroups as Ct,validateTabs as wt}from"./tabs.js";import{getLocaleFromRelativePath as Pt}from"../../fs/utils/get-locale-from-relative-path.js";import{isSystemRouteSlug as Et}from"../../utils/system-routes.js";import{ENTITIES_MAP_GLOBAL_DATA_KEY as Rt}from"../../constants/plugins/catalog-entities.js";import{telemetryTraceStep as At}from"../../../cli/telemetry/helpers/trace-step.js";import{isPathIgnored as kt}from"../../utils/paths.js";const Lt=180,Tt=170,_t=10;async function ce({contentDir:u}){return{id:"sidebars",async afterRoutesCreated(a,c){await At("com.redocly.build.plugin.sidebars",async()=>{const{cache:h,fs:d}=c,D=a.getConfig(),F=new Map,C=new Set,G=[E,...d.localeFolders],x=a.getGlobalData()[Rt]||{},j=new Map
|
|
1
|
+
import{writeFileSync as at}from"node:fs";import*as g from"path";import{removeMarkdocTags as nt}from"../../../markdoc/helpers/remove-markdoc-tags.js";import{withoutHash as it}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as E,SIDEBAR_PREFIX as st,CONFIG_FILE_NAME as Y}from"../../../constants/common.js";import{CATALOG_OUTPUT_FILE_NAME as lt}from"../../constants/common.js";import{findDeepFirst as B}from"../../../utils/tree/find-deep-first.js";import{isDefined as M}from"../../../utils/guards/is-defined.js";import{partition as ct}from"../../../utils/array/partition.js";import{collectPropValueDeep as N}from"../../../utils/tree/collect-prop-value-deep.js";import{envConfig as ft}from"../../config/env-config.js";import{isLocalLink as ut}from"../../../utils/path/is-local-link.js";import{normalizeRouteSlug as J}from"../../../utils/path/normalize-route-slug.js";import{slash as dt}from"../../../utils/path/slash.js";import{parsePathVersions as mt}from"../../../utils/path/parse-path-versions.js";import{reporter as I}from"../../tools/notifiers/reporter.js";import{logger as $}from"../../tools/notifiers/logger.js";import{sha1 as gt}from"../../utils/crypto/sha1.js";import{getCatalogSharedDataId as pt}from"../../utils/catalog-classic/get-catalog-shared-data-id.js";import{collectItemsLinkedToSidebars as ht,resolveItems as q}from"../nav-utils.js";import{getExcludedFromLinkCheckerPatterns as yt,getSidebarReferences as St,hasCircularDependency as bt}from"./utils.js";import{collectTabRoutes as vt,findDuplicateRoutes as It,hasTabs as Dt,resolveTabs as Ft,tabsAsGroups as Ct,validateTabs as wt}from"./tabs.js";import{getLocaleFromRelativePath as Pt}from"../../fs/utils/get-locale-from-relative-path.js";import{isSystemRouteSlug as Et}from"../../utils/system-routes.js";import{ENTITIES_MAP_GLOBAL_DATA_KEY as Rt}from"../../constants/plugins/catalog-entities.js";import{telemetryTraceStep as At}from"../../../cli/telemetry/helpers/trace-step.js";import{isPathIgnored as kt}from"../../utils/paths.js";const Lt=180,Tt=170,_t=10;async function ce({contentDir:u}){return{id:"sidebars",async afterRoutesCreated(a,c){await At("com.redocly.build.plugin.sidebars",async()=>{const{cache:h,fs:d}=c,D=a.getConfig(),F=new Map,C=new Set,G=[E,...d.localeFolders],x=a.getGlobalData()[Rt]||{},j=new Map,L=d.scan(/sidebars.yaml$/).map(({relativePath:t})=>t).filter(t=>!kt(t,D.ignore??[])&&t),H=await St(c,u,L),Z=ct(L.filter(t=>!H.has(t)),t=>{const i=mt(t);return i?`@versioned/${i.versionFolderPath}\0${i.filePathInVersion}`:t}),T=yt(D),tt=await ht(D?.navbar,a,c,{navFile:Y,excludedFromLinkCheckerPatterns:T});let w;const _=bt(H);_&&Array.isArray(_)&&await I.panicOnBuildContentError(`Sidebar references have circular dependency. Please check your sidebar files.
|
|
2
2
|
Circular dependency chain: ${_.reverse().join(" -> ")}
|
|
3
3
|
`);for(const t of Z){const i=(await Promise.all(t.map(async r=>{const e=(await h.load(r,"yaml")).data;if(!Array.isArray(e)){await I.panicOnBuildContentErrorForRealFile('Invalid sidebar contents at %rp, items should be an array, got "%s"',r,d,r,typeof e);return}return{items:e,sidebarRelativePath:r,locale:Pt(r)}}))).filter(M),o=(await A(i))?.firstLink;w||(w=o)}const U=Object.entries(D?.catalogClassic??{});for(const t of G){for(const[e,s]of U)await K(e,s,t);const i={},o=new Set,r=a.getAllRoutesForLocale(t);for(const e of r)if(z(e)&&e.fsPath&&!o.has(e.fsPath)){o.add(e.fsPath);let l=g.posix.dirname(e.fsPath);const f=g.parse(l).root;do i[l]=(i[l]||0)+1,l=g.dirname(l);while(l&&l!="."&&f!=l)}for(const e of r){if(F.has(e.slug))continue;const s=z(e),l=g.dirname(e.fsPath),f=i[l]===1,m=Mt(e.baseSlug||e.slug);s&&f?await A([{items:[{directory:l}],sidebarRelativePath:"sidebar.yaml_"+m,locale:t}]):e.getSidebar!==void 0&&await A([{items:[{page:e.fsPath}],sidebarRelativePath:"sidebar.yaml_"+m,locale:t}])}for(const[e,s]of U)await K(e,s,t,!0)}if(L.length===0)for(const t of G){$.verbose("Creating default sidebar");const i=t===E,o=`sidebars.yaml${i?"":"_"+t}`,r=i?"":`${d.localizationFolder}/${t}`,e=(await A([{items:[{directory:`./${r}`}],sidebarRelativePath:o,locale:t,ignoredRoutes:C}]))?.firstLink;w||(w=e)}if(!a.getRouteBySlug("/")&&!a.getConfig().redirects?.["/"]){const i=a.getAllRoutes().find(r=>!Et(r.slug)),o=w?w.link:i?.baseSlug??null;if(o){const r=it(o);a.addRedirect("/",{to:r,type:302}),$.info("Creating default redirect for index page => %s",r)}}const W=ft.REDOCLY_METADATA_OUTPUT_FOLDER;W&&($.info("Writing catalog data..."),at(g.join(W,lt),JSON.stringify(Object.fromEntries(j.entries()))));function z(t){return C.has(t.slug)||x[t.fsPath]}async function A(t){if(t.length===0)return;const i=t.map(({items:e})=>Dt(e)),o=i.some(Boolean);if(o&&!i.every(Boolean)){await I.panicOnBuildContentError("Versioned copies of %rp must either all use navigation tabs or none of them",t[0].sidebarRelativePath);return}const r=o?await ot(t):await et(t);if(r)return rt(t[0].sidebarRelativePath,r)}async function et(t){const o=(await Promise.all(t.map(async({items:r,locale:e,sidebarRelativePath:s,ignoredRoutes:l})=>{const{fromDir:f,resolveOptions:m}=V(s,e,l),p=await q(r,f,a,c,m);if(!p){await I.panicOnBuild("Failed to resolve sidebar configuration. Make sure %rp is valid",s);return}return p}))).flat().filter(M);return{routeOwners:N(o,"routeSlug").map(r=>({routeSlug:r,items:o})),sharedData:{items:o},navItems:o}}async function ot(t){const i=t[0].sidebarRelativePath,r=(await Promise.all(t.map(async({items:l,locale:f,sidebarRelativePath:m,ignoredRoutes:p})=>{const y=wt(l);if(y.length){await I.panicOnBuildContentError(`Invalid navigation tabs in %rp:
|
|
4
4
|
- ${y.join(`
|
|
5
5
|
- `)}`,m);return}const{fromDir:k,resolveOptions:n}=V(m,f,p);return Ft(l,k,a,c,n)}))).flat().filter(M),e=vt(r),s=It(e);if(s.length){await I.panicOnBuildContentError(`Navigation tabs conflict in %rp. A route can belong to one tab only:
|
|
6
6
|
- ${s.map(({routeSlug:l,owners:f})=>`${l} is claimed by ${f.map(m=>`"${m}"`).join(" and ")}`).join(`
|
|
7
|
-
- `)}`,i);return}return{routeOwners:e,sharedData:{items:[],tabs:r},navItems:Ct(r)}}function V(t,i,o){return{fromDir:g.dirname(g.join(u,t)),resolveOptions:{locale:i,ignoredRoutes:o,navFile:t,excludedFromLinkCheckerPatterns:T}}}async function rt(t,{routeOwners:i,sharedData:o,navItems:r}){const e=Q(t),s=new Set;for(const{routeSlug:f,items:m}of i){const p=a.getRouteBySlug(f)?.fsPath??"",y=x[p];y&&(Array.from(s).find(n=>n.key===y.key&&n.version===y.version)||s.add(y)),a.addRouteSharedData(f,"sidebar",e),F.set(J(f),m)}const l=s.size===1?Array.from(s)[0]:void 0;return await a.createSharedData(e,{relatedNavbarItem:tt?.get(t),...o,catalogEntity:l?{key:l.key,version:l.version}:void 0}),{firstLink:B(r,f=>!!f.link),resolved:r}}async function K(t,i,o,r=!1){const e=structuredClone(i);o&&o!==E&&e.items.forEach(n=>{n.directory=g.posix.join(d.localizationFolder||"",o,n.directory||"")});
|
|
7
|
+
- `)}`,i);return}return{routeOwners:e,sharedData:{items:[],tabs:r},navItems:Ct(r)}}function V(t,i,o){return{fromDir:g.dirname(g.join(u,t)),resolveOptions:{locale:i,ignoredRoutes:o,navFile:t,excludedFromLinkCheckerPatterns:T}}}async function rt(t,{routeOwners:i,sharedData:o,navItems:r}){const e=Q(t),s=new Set;for(const{routeSlug:f,items:m}of i){const p=a.getRouteBySlug(f)?.fsPath??"",y=x[p];y&&(Array.from(s).find(n=>n.key===y.key&&n.version===y.version)||s.add(y)),a.addRouteSharedData(f,"sidebar",e),F.set(J(f),m)}const l=s.size===1?Array.from(s)[0]:void 0;return await a.createSharedData(e,{relatedNavbarItem:tt?.get(t),...o,catalogEntity:l?{key:l.key,version:l.version}:void 0}),{firstLink:B(r,f=>!!f.link),resolved:r}}async function K(t,i,o,r=!1){const e=structuredClone(i);o&&o!==E&&e.items.forEach(n=>{n.directory=g.posix.join(d.localizationFolder||"",o,n.directory||"")});const s=await q(e.items,u,a,c,{groupCustomSidebars:!0,locale:o,navFile:Y,excludedFromLinkCheckerPatterns:T});if(!s){await I.panicOnBuild(`Failed to resolve catalog configuration. Make sure catalog ${t} has valid config`);return}Bt(s);const l=o===E?"":"/"+o.toLowerCase(),f=o===E?"":`-${o}`,m=J(g.posix.join(l,e.slug));if(r)for(const n of s){const S=B(n.items||[],v=>!!v.routeSlug);if(!S?.routeSlug)continue;const b=F.get(S.routeSlug);if(!b)continue;const O=N(b,"routeSlug"),R="current-catalog-info-"+n.routeSlug+f,P={catalog:{label:e.title,titleTranslationKey:e.titleTranslationKey,link:m,icon:e?.icon},item:{label:n.metadata?.title,link:n.link,icon:n.icon}};await a.createSharedData(R,P);for(const v of O)a.addRouteSharedData(v,"current-catalog-info",R),C.add(v);const X=B(b,v=>!!v.link&&!v.external&&ut(v.link)&&(!n.version||v.version===n.version));X&&(n.sidebar=[{...X,items:void 0}])}const p={},y=new Set;for(const n of s){if(!(n.routeSlug||n.sidebar?.[0]?.routeSlug)||!n.fsPath||y.has(n.fsPath))continue;y.add(n.fsPath);const b=g.dirname(n.fsPath);p[b]=(p[b]||0)+1}if(r){const n=await a.createSharedData(pt(t,f),s.flatMap(S=>{const b=g.dirname(S.fsPath??""),O=p[b]===1,R=P=>({...P,fsPath:O?b:P.fsPath??""});return S.type==="group"&&S.items?.every(P=>P.type==="group")?(S.items||[]).map(R):R(S)}));a.addRouteSharedData(m,"catalog",n),Ot(o,s,j,p)}const k=N(s,"routeSlug");for(const n of k)C.add(n)}})}}}function Ot(u,a,c,h){if(u===E)for(const d of a){const D=d.routeSlug||d.sidebar?.[0]?.routeSlug;if(!D||!d.fsPath)continue;const F=g.dirname(d.fsPath),C=h[F]===1;c.set(D,{rootFileFsPath:d.fsPath,fsPath:C?F:d.fsPath,metadata:d.metadata||{},title:d.metadata?.title||d.label||"Untitled",version:d.version||"latest"})}}function Bt(u){const a=c=>{typeof c.metadata?.description=="string"&&(c.metadata={...c.metadata,description:nt(c.metadata.description)})};for(const c of u)if(c.routeSlug)a(c);else for(const h of c.items||[])a(h)}function Q(u){return st+dt(u)}async function fe(u,a,c){const h=g.posix.join(g.dirname(u),a);return await c.exists(h)?Q(h):(await I.panicOnBuildContentErrorForRealFile("File %rp: Failed to create relative path for sidebars.yaml using %s",u,c,u,a),null)}function Mt(u){const a=u.replaceAll("/","_");if(a.length>Lt){const c=a.slice(0,Tt),h=gt(u).slice(0,_t).replaceAll("/","_");return c+h}else return a}export{fe as resolveSidebarId,ce as sidebarsPlugin,Bt as stripMarkdocFromDescriptions};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import s from"path";import{collectPropValueDeep as g}from"../../../utils/tree/collect-prop-value-deep.js";import{removeTrailingSlash as f}from"../../../utils/url/remove-trailing-slash.js";import{slash as C}from"../../../utils/path/slash.js";import{removeLeadingSlash as u}from"../../../utils/url/remove-leading-slash.js";import{reporter as p}from"../../tools/notifiers/reporter.js";import{getCompleteCatalogConfig as h}from"../catalog-entities/get-complete-catalog-config.js";async function k({fs:r,cache:i},e,n){
|
|
1
|
+
import s from"path";import{collectPropValueDeep as g}from"../../../utils/tree/collect-prop-value-deep.js";import{removeTrailingSlash as f}from"../../../utils/url/remove-trailing-slash.js";import{slash as C}from"../../../utils/path/slash.js";import{removeLeadingSlash as u}from"../../../utils/url/remove-leading-slash.js";import{reporter as p}from"../../tools/notifiers/reporter.js";import{getCompleteCatalogConfig as h}from"../catalog-entities/get-complete-catalog-config.js";async function k({fs:r,cache:i},e,n){const a=new Map;for(const t of n){if(!await r.exists(t))continue;const o=s.dirname(s.join(e,t)),m=(await i.load(t,"yaml")).data;try{const c=g(m,"$ref");for(const d of c){const l=C(s.relative(e,s.join(o,d)));a.has(l)&&await p.panicOnBuildContentError(`Sidebar reference '${l}' was used multiple times.`),a.set(l,t)}}catch(c){if(c.message.includes("not iterable")){await p.panicOnBuildContentErrorForRealFile('Invalid sidebar format at %rp, "items" should be an array',t,r,t);continue}throw c}}return a}function v(r){function i(e,n){if(n.includes(e))return[...n,e];const a=r.get(e);if(a){const t=i(a,[...n,e]);if(t)return t}return!1}for(const e of r.keys()){const n=i(e,[]);if(n)return n}return!1}function P(r){const i=r.apiFunctions?.folders?.map(o=>new RegExp(`^/?(${u(f(o))})(/|$)`))||[],e=[new RegExp("^/?(api)(/|$)")],n=h(r.entitiesCatalog),t=Object.values(n?.catalogs||{}).map(o=>o?.slug?new RegExp(`^/?(catalogs\\/${u(f(o?.slug||""))})(/|$)`):null).filter(o=>o!==null);return{apiFunctions:[...i,...e],catalog:t}}export{P as getExcludedFromLinkCheckerPatterns,k as getSidebarReferences,v as hasCircularDependency};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import*as E from"node:path";import{envConfig as R}from"../../config/env-config.js";import{maskEmail as T}from"./helpers/privacy/mask-email.js";import{maskSubject as C}from"./helpers/privacy/mask-subject.js";import{removePii as w}from"./helpers/privacy/remove-pii.js";import{removePiiFromUrl as $}from"./helpers/privacy/remove-pii-from-url.js";import{red as x,green as A,bold as F,blue as l,gray as i,yellow as b,cyan as v}from"./helpers/colors.js";function P(e,r){return r?e:w(e)}var n;(function(e){e.INFO="INFO",e.WARN="WARN",e.ERROR="ERROR",e.SUCCESS="SUCCESS",e.VERBOSE="VERBOSE",e.HTTP="HTTP"})(n||(n={}));const I={[n.VERBOSE]:0,[n.HTTP]:1,[n.INFO]:2,[n.SUCCESS]:2,[n.WARN]:3,[n.ERROR]:4};function L(e,r){return I[e]>=I[r]}function G(e){if(!e)return;const r=e.toUpperCase();if(r in n)return r}const H={[n.INFO]:l,[n.SUCCESS]:A,[n.WARN]:b,[n.ERROR]:x,[n.HTTP]:v,[n.VERBOSE]:i},N={"%ap":e=>l(E.resolve(e)),"%rp":e=>l(E.relative(process.cwd(),e)),"%s":e=>e?.toString(),"%c":e=>l(e)},M={format(e){return R.REDOCLY_LOG_FORMAT==="JSON"?y(e):U(e)},interpolate(e,...r){const u=Object.keys(N).map(a=>`(${a})`).join("|"),o=new RegExp(u,"g");let s,m=e;for(;(s=o.exec(e))!==null;){s.index===o.lastIndex&&o.lastIndex++;const a=r.shift();if(a===void 0)break;const t=s[0];m=m.replace(t,N[t](a))}return m}};var Y=M;const U=e=>{const{level:r,message:u,duration:o,context:s,preSanitized:m}=e,a=R.isRuntimeMode,t=[H[r](F(`[${r.toLowerCase()}]`))];if(a&&t.push(i(`time="${j()}"`)),s){const{email:c,ipAddress:f,subject:d,method:S,pathname:p,statusCode:h,teams:g,apiFunction:O}=s;O&&t.push(l(`fn="${O}"`)),h&&t.push(D(h)),e.level===n.HTTP&&(S&&t.push(i(`method="${S}"`)),p&&t.push(i(`path="${$(p)}"`))),f&&t.push(i(`ip="${f}"`)),c&&t.push(i(`email="${T(c)}"`)),d&&t.push(i(`sub="${C(d)}"`)),g?.length&&t.push(i(`teams="${g.join(", ")}"`))}if(u){const c=a?`msg="${String(P(u,m)).replaceAll?.('"','\\"')}"`:String(u);t.push(c)}return e.level===n.HTTP&&s?.userAgent&&t.push(i(`agent="${s.userAgent}"`)),o&&t.push(V(o)),t.join(" ")+`
|
|
2
2
|
`},y=({context:e,message:r,preSanitized:u,...o})=>{const{method:s,pathname:m,statusCode:a,userAgent:t,subject:c,email:f,teams:d,apiFunction:S}=e||{},p={...o,email:f&&T(f),subject:c&&C(c),teams:d,apiFunction:S,...o.level===n.HTTP?{method:s,pathname:String($(m)),statusCode:a,userAgent:t}:{message:String(P(String(r??""),u))}};return R.isRuntimeMode&&(p.time=j()),JSON.stringify(p,["time","level","scope","message","duration","method","pathname","statusCode","userAgent","subject","ipAddress","email","apiFunction"])+`
|
|
3
|
-
`},D=e=>(e%500<100?x:e%400<100?b:A)(`status="${e}"`),j=()=>{
|
|
3
|
+
`},D=e=>(e%500<100?x:e%400<100?b:A)(`status="${e}"`),j=()=>{const e=new Date().getTimezoneOffset()*6e4;return new Date(Date.now()-e).toISOString().slice(0,-1)},V=e=>i(`dur="${Math.round(e)}ms"`);export{n as LogLevel,Y as default,G as parseLogLevel,L as shouldLog};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const r=e=>{if(!e||typeof e!="string"||!e.includes("@"))return e;
|
|
1
|
+
const r=e=>{if(!e||typeof e!="string"||!e.includes("@"))return e;const[n,t]=e.split("@");return`${n.length>2?n.slice(0,2)+"*".repeat(n.length-2):"*".repeat(n.length)}@${t}`};export{r as maskEmail};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import{telemetry as u}from"../../telemetry/index.js";import{envConfig as h}from"../../config/env-config.js";import m,{LogLevel as r,shouldLog as a,parseLogLevel as f}from"./formatter.js";import{isVirtualFile as g}from"../../fs/utils/isVirtualFile.js";import{TerminalManager as d}from"./terminal-manager.js";class p{#e;#l;#i;#n;#r=new Map;#o=new Map;constructor({context:t,allowInteractive:e,minLogLevel:i}={}){this.#e=t,this.#l=h.isProductionEnv,this.#i=new d(e),this.#n=i??f(h.REDOCLY_LOG_LEVEL)??(h.isDevelopMode?r.INFO:r.HTTP)}shouldLog(t){return a(t,this.#n)}info(t,...e){this.#t({level:r.INFO,message:t,args:e})}infoPreSanitized(t){this.#t({level:r.INFO,message:t,args:[],preSanitized:!0})}infoTime(t,e,...i){return this.#s(r.INFO,e,t,...i)}success(t,...e){this.#t({level:r.SUCCESS,message:t,args:e})}logInFooter(t,e,...i){const o=m.interpolate(e,...i)+`
|
|
2
|
-
`;this.isInteractive()||this.#i.isFooterChanged(t,o)&&this.#t({level:r.INFO,message:e,args:i}),this.#i.updateFooter(t,o)}successTime(t,e,...i){return this.#s(r.SUCCESS,e,t,...i)}warn(t,...e){this.#t({level:r.WARN,message:t,args:e})}warnProd(t,...e){this.#l?this.warn(t,...e):this.verbose(t,...e)}error(t,...e){this.#t({level:r.ERROR,message:t,args:e})}contentError(t,...e){this.#t({level:r.ERROR,message:t,scope:"content",args:e})}verbose(t,...e){this.#t({level:r.VERBOSE,message:t,args:e})}verboseTime(t,e,...i){return this.#s(r.VERBOSE,e,t,...i)}httpTime(t){return this.#s(r.HTTP,"",t)}startTiming(t){const e=t||Symbol();this.#r.set(e,performance.now());const i=setTimeout(()=>{this.#r.delete(e),this.#o.delete(e)},500*1e3);return this.#o.set(e,i),e}updateContext(t){this.#e={...this.#e,...t}}clearAllTimeouts(){for(const t of this.#o.values())clearTimeout(t);this.#o.clear()}isInteractive(){return this.#i.isInteractive()}warnForRealFile(t,e,i,...o){g(e,i)||this.warn(t,e,...o)}#s(t,e,i,...o){const s=this.#r.get(i);if(!s)return;const n=Math.round(performance.now()-s);return this.#r.delete(i),this.#t({level:t,message:e,duration:n,args:o}),{message:e,timeMs:n}}#t({level:t,message:e,duration:i,scope:o,args:s,preSanitized:n}){if(!a(t,this.#n))return;
|
|
2
|
+
`;this.isInteractive()||this.#i.isFooterChanged(t,o)&&this.#t({level:r.INFO,message:e,args:i}),this.#i.updateFooter(t,o)}successTime(t,e,...i){return this.#s(r.SUCCESS,e,t,...i)}warn(t,...e){this.#t({level:r.WARN,message:t,args:e})}warnProd(t,...e){this.#l?this.warn(t,...e):this.verbose(t,...e)}error(t,...e){this.#t({level:r.ERROR,message:t,args:e})}contentError(t,...e){this.#t({level:r.ERROR,message:t,scope:"content",args:e})}verbose(t,...e){this.#t({level:r.VERBOSE,message:t,args:e})}verboseTime(t,e,...i){return this.#s(r.VERBOSE,e,t,...i)}httpTime(t){return this.#s(r.HTTP,"",t)}startTiming(t){const e=t||Symbol();this.#r.set(e,performance.now());const i=setTimeout(()=>{this.#r.delete(e),this.#o.delete(e)},500*1e3);return this.#o.set(e,i),e}updateContext(t){this.#e={...this.#e,...t}}clearAllTimeouts(){for(const t of this.#o.values())clearTimeout(t);this.#o.clear()}isInteractive(){return this.#i.isInteractive()}warnForRealFile(t,e,i,...o){g(e,i)||this.warn(t,e,...o)}#s(t,e,i,...o){const s=this.#r.get(i);if(!s)return;const n=Math.round(performance.now()-s);return this.#r.delete(i),this.#t({level:t,message:e,duration:n,args:o}),{message:e,timeMs:n}}#t({level:t,message:e,duration:i,scope:o,args:s,preSanitized:n}){if(!a(t,this.#n))return;const l=e&&m.interpolate(e,...s);l&&i!=null&&t!==r.VERBOSE&&u.sendTimingPerformedMessage([{id:"timingId",object:"timing",uri:"urn:redocly:realm:api:timing:timingId",timeMs:i,message:l}]);const c={level:t,message:l,duration:i,scope:o,context:this.#e,preSanitized:n};process.stderr.write(m.format(c))}}const O=new p({allowInteractive:!0});export{p as Logger,O as logger};
|
|
@@ -3,6 +3,7 @@ import type { Adapter, UserClaims } from '../plugins/dev-onboarding/api/types.js
|
|
|
3
3
|
import type { PageRouteDetails } from './plugins/common.js';
|
|
4
4
|
export type ServerOptions = {
|
|
5
5
|
port?: number;
|
|
6
|
+
maxPortRetries?: number;
|
|
6
7
|
};
|
|
7
8
|
export type RequestContext = {
|
|
8
9
|
remoteAddr: {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{createColors as d}from"colorette";import*as _ from"../../tools/notifiers/helpers/colors.js";let{gray:g,red:M,colorsAreEnabled:y}=_;const A=120,k=3;function X(
|
|
2
|
-
`)}function w(
|
|
1
|
+
import{createColors as d}from"colorette";import*as _ from"../../tools/notifiers/helpers/colors.js";let{gray:g,red:M,colorsAreEnabled:y}=_;const A=120,k=3;function X(n,t,i=""){const c=!t||!y;t||({gray:g,red:M}=d({useColor:!1}));const{start:f,end:o,lines:e}=n;if(f.line===0)return{frame:"",start:{line:1,character:1}};const u=f.line,s=Math.max(Math.min(o.line??1,e.length),f.line);let l=Math.max(s-u-k+1,0);l<2&&(l=0);const m=[];let b=0;for(let r=u;r<=s&&!(l>0&&r>=s-l);r++){const a=e[r-1]||"";a!==""&&(b=$(a));const h=r===u?f.character-1:b,L=r===s?o.character-1:a.length;m.push([`${r}`,p(a,h,L,M)]),c&&m.push(["",C(a,h,L)])}l>0&&(m.push(["\u2026",`${E(b)}${g(`< ${l} more lines >`)}`]),m.push([`${s}`,p(e[s-1],-1,o.character-1,M)]),c&&m.push(["",C(e[s-1],-1,o.character-1)]));const N=P([[`${u-2}`,p(e[u-1-2])],[`${u-1}`,p(e[u-1-1])],...m,[`${s+1}`,p(e[s-1+1])],[`${s+2}`,p(e[s-1+2])]],i);return{gray:g,red:M}=_,{frame:N,start:f};function p(r,a=-1,h=1/0,L=g){return c||!r?r:(a===-1&&(a=$(r)),h=Math.min(h,r.length),r.substr(0,a)+L(r.substring(a,h))+r.substr(h))}}function P(n,t=""){const i=n.filter(([o,e])=>e!==void 0),c=Math.max(...i.map(([o])=>o.length)),f=Math.min(...i.map(([o,e])=>e===""?1/0:$(e)));return i.map(([o,e])=>t+g(w(c,o)+" |")+(e?" "+S(e.substring(f)):"")).join(`
|
|
2
|
+
`)}function w(n,t){return E(n-t.length)+t}function $(n){for(let t=0;t<n.length;t++)if(n[t]!==" ")return t;return n.length}function E(n){return" ".repeat(n)}function S(n,t=A){const i=n.length-t;if(i>0){const c=g(`...<${i} chars>`);return n.substring(0,t-c.length)+c}else return n}function C(n,t=-1,i=1/0){return t===-1&&(t=$(n)),i=Math.min(i,n.length),E(t)+"^".repeat(Math.max(i-t,1))}export{X as getCodeframe};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{EntitlementsProvider as r}from"../../entitlements/entitlements-provider.js";function m(t,n){const e=r.instance().entitlements?.[t];return typeof e=="number"&&Number.isFinite(e)&&e>0?e:n}export{m as readEntitlementNumber};
|
package/dist/server/utils/fs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as c,readdirSync as l,lstatSync as f,mkdirSync as u,copyFileSync as p,lstat as x}from"fs";import{join as s,extname as S,basename as F,dirname as h,resolve as E}from"path";import{withPathPrefix as w}from"@redocly/theme/core/utils";import{PUBLIC_ASSETS_FOLDER as a}from"../constants/common.js";import{reporter as P}from"../tools/notifiers/reporter.js";import{logger as D}from"../tools/notifiers/logger.js";import{shaDirPathShort as b}from"../utils/crypto/sha-dir-path-short.js";import{shaFileHex as N}from"../utils/crypto/sha-file-hex.js";import{slug as $}from"../../utils/slugger.js";function I(r){if(!c(r))return[];const e=[];return o(r),e;function o(t){try{l(t).forEach(n=>{
|
|
1
|
+
import{existsSync as c,readdirSync as l,lstatSync as f,mkdirSync as u,copyFileSync as p,lstat as x}from"fs";import{join as s,extname as S,basename as F,dirname as h,resolve as E}from"path";import{withPathPrefix as w}from"@redocly/theme/core/utils";import{PUBLIC_ASSETS_FOLDER as a}from"../constants/common.js";import{reporter as P}from"../tools/notifiers/reporter.js";import{logger as D}from"../tools/notifiers/logger.js";import{shaDirPathShort as b}from"../utils/crypto/sha-dir-path-short.js";import{shaFileHex as N}from"../utils/crypto/sha-file-hex.js";import{slug as $}from"../../utils/slugger.js";function I(r){if(!c(r))return[];const e=[];return o(r),e;function o(t){try{l(t).forEach(n=>{const i=s(t,n);f(i).isDirectory()?o(i):e.push(i)})}catch(n){D.error(`Error reading all file names in ${t}: `+n.message)}}}async function T(r,e,o){const t=E(r,e);if(!c(t))throw new v(`File not found at ${t}`,e);c(s(o,a))||u(s(o,a),{recursive:!0});const n=S(t),i=[$(F(t,n)),N(t),b(h(e))].join(".").concat(n),m=s(o,a,i),y=s(a,i);try{if(p(t,m),!c(m))throw new Error(`Cannot copy file ${e}`);return w(y)}catch(d){await P.panicOnBuild(d.message)}}function g(r,e){let o=[];if(c(r)){if(c(e)||u(e,{recursive:!0}),!f(r).isDirectory())throw new Error(`Source path must be a directory, but ${r} provided`);o=l(r),o.forEach(t=>{const n=s(r,t);if(f(n).isDirectory())g(n,s(e,t));else{const i=s(e,t);p(n,i)}})}}function U(r){return u(h(r),{recursive:!0}),r}async function q(r){return new Promise(e=>{x(r,(o,t)=>{e(o==null&&t.isFile())})})}class v extends Error{filePath;constructor(e,o){super(e),this.name="FileNotFoundError",this.filePath=o}}export{v as FileNotFoundError,g as copyFolderRecursiveSync,T as copyStaticFile,U as ensureDir,q as fileExistsAsync,I as readFileNames};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import R from"path";import P from"picomatch";import"../node-crypto-polyfill.js";import{REDOCLY_TEAMS_RBAC as
|
|
1
|
+
import R from"path";import P from"picomatch";import"../node-crypto-polyfill.js";import{REDOCLY_TEAMS_RBAC as x,REDOCLY_ROUTE_RBAC as E}from"@redocly/config";import{MCP_ROUTE as U,PUBLIC_MCP_ROUTE as k}from"@redocly/theme/core/constants/mcp";import{ServerRoutes as w}from"../../constants/api.js";import{DEFAULT_ANONYMOUS_VISITOR_TEAM as D,PUBLIC_RBAC_SCOPE_ITEM as y,RBAC_ALL_OTHER_TEAMS as l,DEFAULT_RBAC_SCOPE as L,GRAPHQL_SPEC_SLUG as M}from"../../constants/common.js";import{DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER as g,PUBLIC_API_DEFINITIONS_FOLDER as C,PUBLIC_ASSETS_FOLDER as B}from"../constants/common.js";import{MCP_CREDENTIALS_ROUTE as b}from"../constants/mcp.js";import{ANONYMOUS_AUTH_DETAILS as W}from"../constants/rbac.js";import{removeTrailingSlash as T}from"../../utils/url/remove-trailing-slash.js";import{removeLeadingSlash as $}from"../../utils/url/remove-leading-slash.js";import{parsePathVersions as v}from"../../utils/path/parse-path-versions.js";import{reporter as Y}from"../tools/notifiers/reporter.js";import{bold as G}from"../tools/notifiers/helpers/colors.js";import{shaDirPathShort as H}from"../utils/crypto/sha-dir-path-short.js";import{isTruthy as z}from"../../utils/guards/is-truthy.js";import{canExpandConfig as K,expandRbacConfig as V,getTeamFolderDefaults as J,parseTeamFoldersTemplate as Q,parseTeamNameTemplate as X}from"./rbac-expand.js";import{getUserParamsFromCookies as Z}from"../web-server/auth.js";import{getDeeperGlobPattern as q}from"./globs.js";import{EntitlementsProvider as tt}from"../entitlements/entitlements-provider.js";const j=["NONE","READ","TRIAGE","WRITE","MAINTAIN","ADMIN"],et=new Set(["x-parsed-md-description","x-parsed-md-summary"]),rt=new Set(Object.values(w).map(t=>T(t.split(":")[0])));function It(t,e){const r=j.indexOf(t.toUpperCase()),n=j.indexOf(e.toUpperCase());return r>n?t:e}const A={};function I(t,e){if(!t?.content)return y;const r=t.content,{slug:n,fsPath:o}=e;if(!n&&!o)return y;const s=a=>{const d=`slug:${a}`,p=A[d]??P(a);A[d]=p;const _=`fsPath:${a}`,S=A[_]??P($(a));return A[_]=S,!!(n&&p(n))||!!(o&&S(o))};if(st(n||o||"")&&Object.keys(r).filter(p=>s(p)).length===0)return r[L]||y;const f=Object.keys(r).filter(a=>s(a));if(f.length==0)return y;const u=f.map(a=>P.scan(a,{tokens:!0,parts:!0}));let h=u[0];for(let a=1;a<u.length;a++)h=q(h,u[a]);return r[h.input]}function F(t,e,r){const n=r?[...e,r]:e,o=[];for(const s of n)t[s]?o.push(t[s]):t[l]&&s!==r&&o.push(t[l]);return o.some(s=>s.toLowerCase()!=="none")}function Ft(t,e,r={},n=!1){if(n&&Object.keys(r).length===0)return e.isAuthenticated;const o=r.features?.[t];return o?F(o,e.teams):!0}function Nt(t,e){return O(t,W,e.access?.rbac||{},e.access?.requiresLogin||!1)}function O(t,e={},r={},n=!1){if(t.slug&&typeof t.slug=="string"&&rt.has(T(t.slug))||(t.slug===U||t.slug===k||t.slug===b)&&tt.instance().canAccessFeature("mcp"))return!0;if(n&&Object.keys(r).length===0)return!!e.isAuthenticated;const o=V(r,e.teams||[]),s=t[x]||I(o,t[E]||{});return Object.keys(s||{}).length===0?!1:Object.keys(s).length===1&&s[l]&&s[l].toLowerCase()!=="none"?!0:F(s,e?.teams||[],e?.email)}function Ut(t,e,r,n){if(!t.startsWith(C)&&!t.startsWith(g))return!0;const o=t.replace(new RegExp(`^${C}/`),"").replace(new RegExp(`^${g}/`),"").replace(new RegExp(`^${M}/`),""),c=o==="."?"":o,i={[E]:{slug:t,fsPath:c},slug:t};return O(i,n,e,r)}function kt(t,e,r,n,o){if(!t.startsWith(B))return!0;const s=t.match(/\..{64}\.([A-Fa-f0-9]{8})\.[^.]+$/)?.[1];if(!s)return!0;const c=n[s];if(!c)return!0;const{base:i,ext:f}=R.parse(t),u=i.split(".")[0],h=f.split(".").join(""),d=c==="."?"":c,p={[E]:{slug:t,fsPath:R.posix.join(d,`${u}.${h}`)},slug:t};return O(p,o,e,r)}async function wt(t,e){const{isAuthenticated:r=!1,idpAccessToken:n,federatedAccessToken:o,federatedIdToken:s,...c}=await Z(t,e),{teams:i=[]}=c;let f;return r?f=i.filter(u=>u!==D):f=[D],{isAuthenticated:r,idpAccessToken:n,teams:f,claims:c}}function N(t,e,r={},n=!1){if(!t)return t;if(Array.isArray(t)){const o=[];for(const s of t){const c=N(s,e,r,n);c!==void 0&&o.push(c)}return o}if(typeof t=="object"){if(!O(t,e,r,n))return;let o=!1;const s={};for(const c in t){if(c===x||c===E)continue;if(et.has(c)){s[c]=t[c];continue}const i=N(t[c],e,r,n);if(!t.link&&(c==="items"||c==="menu"&&t.type==="tab")&&Array.isArray(i)&&i.length===0&&t[c].length!==0){o=!0;continue}i!==void 0&&(s[c]=i),c==="paths"&&nt(i)&&ot(i)}return o?void 0:s}return t}function Mt(t){return typeof t=="string"?t.split(" ").filter(Boolean):Array.isArray(t)?t.map(e=>e.toString()):[]}function Bt(t,e){if(!e)return;const r=e.content;if(!r)return e;const n=Object.entries(r).flatMap(([s,c])=>s===L?[[s,c]]:[[s,c]].concat(t.localeFolders.map(i=>[s.startsWith("/")?`/${i.toLocaleLowerCase()}${s}`:R.posix.join(t.localizationFolder,i,s),c]))),o=Object.fromEntries(n);return{...e,content:o}}async function bt(t,e){if(!e)return{};const r={},n=new Set((await t.scan()).flatMap(({relativePath:o})=>{const{versionFolderPath:s}=v(o)||{},c=R.dirname(o);return s?[s,c]:c}));for(const o of n)r[H(o)]=o;return r}const m=t=>typeof t=="object"&&t!==null&&!Array.isArray(t);function nt(t){return m(t)&&Object.keys(t).length>0}function ot(t){for(const e of Object.keys(t)){const r=t[e];m(r)&&Object.keys(r).length===0&&delete t[e]}}function st(t){return t?t.split("/").filter(Boolean).some(r=>r.startsWith(".")):!1}const ct=t=>{if(t&&m(t)&&("content"in t&&m(t.content)||"reunite"in t&&m(t.reunite)||"features"in t&&m(t.features)||t.teamFolders&&t.teamNamePatterns)){const e=Object.values(t.content||{});if(e.length===0)return!0;if(e.every(m))return e.every(r=>Object.values(r).every(n=>typeof n=="string"))}return!1},Wt=async t=>{if(t){if(Object.keys(t).length===0)return{};if(ct(t))return it(t);await Y.panicOnContentError(`You are using an incorrect format of ${G("rbac:")} configuration. See: https://redocly.com/docs/realm/access`)}},it=t=>{const e={...t};if(e.content){const r={};for(const n in e.content)if(e.content[n]!==void 0){const o=T(n);r[o]=e.content[n]}e.content=r}return e};function $t(t,e){const r=t.fsPath,n=t.slug,o=[],s=K(e)&&(r||n)?Q(e,[r,n].filter(z)):null;if(s){const i=e?.teamNamePatterns?.map(f=>f.replace("{teamPathSegment}",s.teamPathSegment).replace("{projectRole}","read"))??[];o.push(...i)}const c=I(s?{content:{...J(e),...e.content}}:e,t);return o.push(...at(c)),ft(e,o)}function at(t){if(!t)return[];const e=[],r=l in t?{authenticated:t[l],anonymous:t[l]}:{};for(const[n,o]of Object.entries({...r,...t}))o.toLowerCase()!=="none"&&n!==l&&e.push(n);return e}function ft(t,e){return e.map(n=>X(t,n)??{teamName:n}).map(n=>n.projectRole&&n.projectRole!=="READ"?n.teamName?.toLowerCase().replace(n.projectRole?.toLowerCase?.()??"","read")??"":n.teamName?.toLowerCase()??"")}export{j as PROJECT_ROLES_ORDERED_BY_ACCESS_LEVEL,Bt as applyL10nToRbacConfig,kt as canAccessAsset,Ft as canAccessFeature,O as canAccessResource,Ut as canDownloadApiDefinition,ft as expandTeamsForRead,at as extractTeamsFromScopeItems,N as filterDataByAccessDeep,wt as getAuthDetailsFromCookies,It as getHigherRole,$t as getRbacTeamsListForResource,I as getScopeItemsForResource,F as hasScopeAccess,ct as isRbacConfigValid,Nt as isResourcePubliclyAccessible,it as normalizeRbacConfig,Wt as parseRbacConfig,Mt as parseTeamClaimToArray,bt as resolveDirectoryHashes};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ServerRoutes as
|
|
1
|
+
import{ServerRoutes as n}from"../../constants/api.js";import{reporter as s}from"../tools/notifiers/reporter.js";import{logger as p}from"../tools/notifiers/logger.js";import{telemetry as f}from"../telemetry/index.js";import{installDevRoutes as l,installProdRoutes as v}from"./routes/index.js";import{createRouter as u}from"./router.js";import{readStaticAsset as d}from"./node-asset-reader.js";import{createLlmsTxtGenerator as S}from"../plugins/search/llmstxt/create-llms-txt-generator.js";import{startHttpServer as D}from"./http.js";import{listenStore as R}from"./store-ws.js";import{ejectComponentDataHandler as w}from"./routes/eject.js";import{attachWsServer as g}from"./ws.js";import{DatabasePreconnectService as O}from"../providers/database/database-preconnect-service.js";import{KvService as x}from"../persistence/kv/services/kv-service.js";import{runScorecardsWorker as E}from"../plugins/scorecards/workers/run-scorecards-worker.js";import{isScorecardsEnabled as b}from"../utils/is-scorecards-enabled.js";import{isRealmOrReef as y}from"../utils/is-realm-or-reef.js";async function M(r,i,a){f.initialize(!0);const{port:m=4e3,maxPortRetries:c}=a,t=u();if(t.get(n.EJECT_COMPONENT,w(r)),l(t,r),v(t,r,{readStaticAsset:d,resolveRouteData:e=>r.resolveRouteStaticData(e,i),llmsTxtGenerator:S(r,i)}),y()){await O.init(r.serverOutDir);const e=await x.getInstance({baseDbDir:r.serverOutDir});setInterval(()=>{e.clearExpired().catch(o=>{p.error("Failed to clear expired KV entries",o)})},300*1e3)}try{await r.userCodeReady;const e=await D(t,m,{maxPortRetries:c}),o=g(e);R(r,o),b(r.config)&&await E(r.serverOutDir,r.config.scorecards)}catch(e){await s.panic(e)}}export{M as startDevServer};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as http from 'node:http';
|
|
2
2
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
3
3
|
import type { Hono } from 'hono';
|
|
4
|
+
import type { ServerOptions } from '../types';
|
|
4
5
|
declare global {
|
|
5
6
|
var redoclyCookieStorage: AsyncLocalStorage<string> | undefined;
|
|
6
7
|
}
|
|
7
|
-
export declare function startHttpServer(router: Hono, port?: number | string): Promise<http.Server>;
|
|
8
|
+
export declare function startHttpServer(router: Hono, port?: number | string, { maxPortRetries }?: Pick<ServerOptions, 'maxPortRetries'>): Promise<http.Server>;
|
|
8
9
|
//# sourceMappingURL=http.d.ts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import*as
|
|
2
|
-
%s`,e.message,e.stack))}const
|
|
1
|
+
import*as L from"node:http";import*as H from"node:net";import{AsyncLocalStorage as I}from"node:async_hooks";import{Readable as y,Stream as C}from"node:stream";import{pipeline as P}from"node:stream/promises";import{ReadableStream as $}from"node:stream/web";import{getPathPrefix as R}from"@redocly/theme/core/utils";import{ALLOWED_CORS_ORIGINS as k}from"../constants/common.js";import{logger as l}from"../tools/notifiers/logger.js";import{shutdowner as D}from"../tools/shutdowner.js";import{envConfig as O}from"../config/env-config.js";import{getClientIp as T}from"./utils/get-client-ip.js";import{normalizeIpAddress as x}from"./utils.js";import{CORS_PROXY_STREAM_HEADER as U}from"./routes/cors-proxy.js";const E=new I;globalThis.redoclyCookieStorage=E;const S=65535;async function Q(t,a=4e3,{maxPortRetries:d=0}={}){const s=parseInt(String(a),10);if(!Number.isInteger(s)||s<0||s>S)throw new RangeError(`Port must be an integer from 0 to ${S}. Received: ${a}.`);const i=L.createServer(async(r,o)=>{const b=r.headers.cookie||"";E.run(b,async()=>{let c;try{const e=new URL(r.url||"",`http://${r.headers.host}`),f=[],m=r.rawHeaders.length;for(let w=0;w<m;w+=2)f.push([r.rawHeaders[w],r.rawHeaders[w+1]]);const h={headers:f,method:r.method};r.method==="GET"||r.method==="HEAD"||(h.body=y.toWeb(r),h.duplex="half");const p=new Request(e.toString(),h),A=T(p)||"127.0.0.1",v=p?.headers?.get("x-forwarded-host")||x(r.socket.remoteAddress);p.context={remoteAddr:{hostname:v,port:r.socket.localPort,ipAddress:A},url:e},c=await t.fetch(p)}catch(e){c=new Response(null,{status:500}),e instanceof Error&&(e.name==="TimeoutError"||e.constructor.name==="TimeoutError"?c=new Response(null,{status:504}):e.code==="ERR_INVALID_URL"&&(c=new Response("Invalid URL",{status:404})),l.error(`Error while handling request: %s
|
|
2
|
+
%s`,e.message,e.stack))}const u=r.headers.origin;u&&k.some(e=>u===e)&&(o.setHeader("Access-Control-Allow-Origin",u),o.setHeader("Access-Control-Allow-Credentials","true"));for(const[e,f]of c.headers||[])try{e==="set-cookie"?o.setHeader(e,c.headers.getSetCookie(e)):o.setHeader(e,f)}catch(m){l.error(`Error while setting header: ${m?.message}`)}if(o.setHeader("Vary","Cookie"),o.statusCode=c.status||500,c.body)try{if(M(c)){const e=new C.Transform({transform(f,m,h){if(o.closed||o.destroyed)return h();o.write(f,m)?process.nextTick(h):o.once("drain",h)},flush(f){if(o.closed||o.destroyed)return f();o.end(),f()}});await P(c.body instanceof $?y.fromWeb(c.body):c.body,e)}else{const e=await c.text();o.setHeader("Content-Length",Buffer.byteLength(e)),o.end(e)}}catch(e){console.error(e);const f=e instanceof Error?e:new Error("unknown error",{cause:e});o.destroy(f)}else o.end()})});D.registerShutdownCallback(()=>new Promise(r=>{l.verbose("Shutting down http server"),i.close(()=>{l.verbose("Http server shut down"),r()})}));const n=await g(i,s,d);return i.on("error",r=>{l.error("Http server error: %s",r.message)}),n!==s&&l.warn(`Port ${s} is already in use. Using port ${n} instead.`),O.isDevelopMode?l.logInFooter("server",` \u{1F310} Preview URL: http://127.0.0.1:${n}${R()}`):l.logInFooter("server",`Server started at: http://127.0.0.1:${n}${R()}`),i}async function g(t,a,d){const s=d>0,i=s?Math.min(a+d,S):a;for(let n=a;n<=i;n++)if(!(s&&!await N(n)))try{return await _(t,n),n}catch(r){if(!s||r.code!=="EADDRINUSE")throw r}throw Object.assign(new Error(`Ports ${a}-${i} are already in use.`),{code:"EADDRINUSE"})}function _(t,a){return new Promise((d,s)=>{const i=r=>{t.removeListener("listening",n),s(r)},n=()=>{t.removeListener("error",i),d()};t.once("error",i),t.once("listening",n),t.listen(a)})}function N(t){return new Promise(a=>{const d=H.createServer();d.once("error",s=>a(s.code!=="EADDRINUSE")),d.listen(t,"127.0.0.1",()=>d.close(()=>a(!0)))})}function M(t){const a=t.headers.get("content-type")||"",d=t.headers.get("x-accel-buffering")||"",s=t.headers.get("content-encoding"),i=t.headers.get("content-length"),n=t.headers.get("transfer-encoding");return t.headers.get(U)==="1"||s||n||i||/^no$/i.test(d)||!/^(text\/(?!event-stream\b))/i.test(a)}export{Q as startHttpServer};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{KvService as
|
|
1
|
+
import{KvService as h}from"../../persistence/kv/services/kv-service.js";import{DEFAULT_AUTHENTICATED_TEAM as l}from"../../../constants/common.js";import{envConfig as s}from"../../config/env-config.js";import{fetchWithClient as T}from"../../utils/fetch-with-client.js";import{CACHE_CONTROL_NO_STORE_HEADER_VALUE as y,JWT_SECRET_KEY as p}from"../../constants/common.js";import*as m from"../jwt/jwt.js";import{AlgorithmTypes as I}from"../jwt/types.js";const f=60,E=e=>["POST","PUT","DELETE","PATCH"].includes(e),_=e=>["GET"].includes(e);function S({serverOutDir:e,protectReadMethods:t=!0}){return async(a,r)=>await w(a,r,e,t)}const w=async(e,t,a,r=!0)=>{e.header("Cache-Control",y);const n=e.req.method,i=E(n)||_(n)&&r,o=e.req.header("apiKey");if(o)return await C(e,t,o,a);const u=e.req.header("authorization")?.replace("Bearer ","");return u?await g(e,t,u):i?e.json({message:"API key is required"},401):await t()},C=async(e,t,a,r)=>{if(!s.BH_API_URL||!s.ORGANIZATION_ID)return e.json({message:"API key validation service not configured"},500);try{const n=await h.getInstance({baseDbDir:r}),i=await v(n,a);if(i)return e.set("apiKeyTeams",i.teams),await t();const o=new URL(`/api/orgs/${s.ORGANIZATION_ID}/session`,s.BH_API_URL).toString(),c=await T(o,{method:"GET",headers:{Authorization:`Bearer ${a}`}});if(!c.ok)return e.json({message:"Invalid API key"},401);const A=(await c.json())?.user?.teams?.[s.ORGANIZATION_ID]??[],d=[l,...A];return e.set("apiKeyTeams",d),await n.set(["api-keys","reunite",a],{valid:!0,teams:d},{ttlInSeconds:f}),await t()}catch{return e.json({message:"API key validation failed"},400)}},g=async(e,t,a)=>{try{const r=await m.verify(a,p,I.HS256),n=m.decode(a).payload.isInternalConnection;return!r||!n?e.json({message:"API key is required"},401):await t()}catch{return e.json({message:"API key validation failed"},400)}},v=async(e,t)=>{try{return await e.get(["api-keys","reunite",t])}catch{return null}};export{S as catalogAuthMiddleware};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const i=s=>(r,u,f)=>{let
|
|
1
|
+
const i=s=>(r,u,f)=>{let l=-1;return t(0);async function t(e){if(e<=l)throw new Error("next() called multiple times");l=e;let n;s[e]?(n=s[e],r.req.routeIndex=e):n=u;const a=await n(r,()=>t(e+1),f);return a&&r.finalized===!1&&(r.res=a),r}};export{i as compose};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{minimatch as
|
|
1
|
+
import{minimatch as u}from"minimatch";import{withoutPathPrefix as a}from"@redocly/theme/core/utils";import{removeTrailingSlash as C}from"../../../utils/url/remove-trailing-slash.js";import{SERVER_EDITOR_APP_URL as O}from"../../constants/common.js";import{MCP_CREDENTIALS_ROUTE as P}from"../../constants/mcp.js";import{ServerRoutes as f}from"../../../constants/api.js";const _=[P,f.MCP_CALLBACK];function m(r,e){return r===e||r.startsWith(`${e}/`)}function A(r){return async(e,c)=>{try{await c()}finally{const s=r.getConfig(),p=a(new URL(e.req.url).pathname),n=a(e.req.path),d=m(n,f.CORS_PROXY);if(s.responseHeaders&&!d){const R=_.some(o=>m(n,o))?new Headers(e.res.headers):void 0,h=Object.keys(s.responseHeaders);for(const o of h)if(u(p,o))for(const{name:i,value:l}of s.responseHeaders[o])R?.has(i)||e.header(i,l)}const t=e.req.header("referer");t&&C(t)===O&&e.header("Cross-Origin-Resource-Policy","cross-origin")}}}export{A as responseHeadersMiddleware};
|
|
@@ -1 +1 @@
|
|
|
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};
|
|
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,W=H(new URL(V||"/",e.req.url).pathname),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};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Env, Handler, MiddlewareHandler } from 'hono';
|
|
2
|
+
import type { z } from 'zod';
|
|
3
|
+
import type { Store } from '../../../store';
|
|
4
|
+
import type { ValidatedJsonInput } from '../../../types';
|
|
5
|
+
import type { mcpFetchCountRequestSchema } from '../../../../api-schemas/mcp-fetch-count.js';
|
|
6
|
+
export declare function mcpFetchCountAuth(): MiddlewareHandler;
|
|
7
|
+
export declare function mcpFetchCountHandler(store: Store): Handler<Env, string, ValidatedJsonInput<z.infer<typeof mcpFetchCountRequestSchema>>>;
|
|
8
|
+
//# sourceMappingURL=mcp-fetch-count.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{envConfig as o}from"../../../config/env-config.js";import{FetchCounterRepository as n}from"../../../plugins/mcp/gateway-mcp/rate-limit/fetch-limiter/fetch-counter-repository.js";import{isFetchCountSecret as i}from"../../../plugins/mcp/gateway-mcp/rate-limit/fetch-limiter/fetch-count-secret.js";import{applyFetchCount as u}from"../../../plugins/mcp/gateway-mcp/rate-limit/fetch-limiter/fetch-count.js";import{resolveGatewayFetchLimits as m}from"../../../plugins/mcp/gateway-mcp/rate-limit/fetch-limiter/fetch-limits.js";import{extractTokenFromAuthHeader as c}from"../../../plugins/mcp/utils/jwt.js";function D(){return async(e,t)=>{if(!i(c(e.req.header("authorization"))))return e.body(null,403);await t()}}function d(e){return async t=>{const a=await n.create({baseDbDir:e.serverOutDir,sqldRemoteDatabaseUrl:o.SQLD_REMOTE_DATABASE_URL,sqldRemoteDatabaseAuthToken:o.SQLD_REMOTE_DATABASE_AUTH_TOKEN}),r=await u(a,t.req.valid("json").caller,m(),Date.now());return r?t.text(r,429):t.body(null,204)}}export{D as mcpFetchCountAuth,d as mcpFetchCountHandler};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{corsMiddleware as
|
|
1
|
+
import{corsMiddleware as o}from"../../middleware/corsMiddleware.js";import{ServerRoutes as t}from"../../../../constants/api.js";import{EntitlementsProvider as A}from"../../../entitlements/entitlements-provider.js";import{withPathPrefix as e}from"@redocly/theme/core/utils";import{AGENT_CARD_SLUG as d,A2A_SLUG as i,MCP_SERVER_CARD_SLUG as l}from"../../../../constants/common.js";import{mcpOAuthProtectedResourceHandler as _,mcpOAuthAuthorizationServerHandler as r,mcpDynamicClientRegistrationHandler as M,mcpAuthorizationHandler as P,mcpTokenPortalHandler as R,mcpCallbackHandler as p,mcpConsentHandler as c}from"./mcp-oauth.js";import{mcpServerCardHandler as s}from"./mcp-server-card.js";import{mcpFetchCountAuth as T,mcpFetchCountHandler as f}from"./mcp-fetch-count.js";import{jsonValidatorMiddleware as E}from"../../middleware/jsonValidatorMiddleware.js";import{mcpFetchCountRequestSchema as H}from"../../../../api-schemas/mcp-fetch-count.js";import{agentCardHandler as h}from"./agent-card.js";import{a2aHandler as O}from"./a2a.js";function $(m,n){const a=n.getConfig().mcp,C=A.instance().canAccessFeature("mcp");!a?.hide&&!a?.docs?.hide&&C&&(m.get(e(l),s(n)),m.get(e(d),h(n)),m.use(e(i),o({allowMethods:["POST"],exposeHeaders:["Link"]})),m.post(e(i),O(n)),m.get(`${t.MCP_OAUTH_PROTECTED_RESOURCE}${e("/mcp")}`,_(n)),m.get(t.MCP_OAUTH_AUTHORIZATION_SERVER,r(n)),m.post(e(t.MCP_DYNAMIC_CLIENT_REGISTRATION),M(n)),m.get(e(t.MCP_AUTHORIZATION),P(n)),m.post(e(t.MCP_TOKEN_PORTAL),R(n)),m.get(e(t.MCP_CALLBACK),p(n)),m.get(e(`${t.MCP_CALLBACK}/*`),p(n)),m.post(e(t.MCP_CALLBACK),c(n)),m.post(e(`${t.MCP_CALLBACK}/*`),c(n)),m.post(e(t.MCP_FETCH_COUNT),T(),E(H),f(n)))}export{$ as installMcpRoutes};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Source as P}from"@redocly/openapi-core";import y from"node:fs/promises";import S from"node:path";import{withPathPrefix as j,getPathPrefix as h,combineUrls as w}from"@redocly/theme/core/utils";import{API_DOCS_SHARED_DATA_PREFIX as d}from"../../constants/plugins/api-docs.js";import{CATALOG_BASE_SLUG as b,CATALOG_SLUGS as $}from"../../../constants/catalog-entities.js";import{CATALOG_ENTITIES_FILES_REGEX as I}from"../../constants/plugins/catalog-entities.js";import{removeTrailingSlash as E}from"../../../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as N}from"../../../utils/path/normalize-route-slug.js";import{promiseMapLimit as T}from"../../utils/async/promise-map-limit.js";import{getRoutesByLines as L,getPointerPosition as U}from"../../plugins/api-docs/utils/ast-utils.js";import{readSharedData as m}from"../../utils/index.js";const q=new Set(["openapi","asyncapi"]);function K(n){return async t=>{const i=await t.req.json(),r=decodeURI(E(i.split("?")[0])),[o]=r.split("#"),g=h(),f=N(o.slice(g.length)),u=f.startsWith(b)?b:f,e=n.getRouteBySlug(u);if(e){const p=n.getRouteByFsPath(e.fsPath);if(p?.metadata?.type&&q.has(p.metadata.type)){const a=await m(`${d}${e.fsPath}`,n.outdir);if(!a?.routesMapping)return t.json({route:e},200);const s=Object.keys(a.routesMapping).find(c=>w(e.baseSlug,a.routesMapping[c])===r);if(!a.sourcePath)return t.json({route:e},200);let l;if(s){const c=S.join(n.contentDir,e.fsPath),R=await y.readFile(c,"utf-8"),_=new P(c,R);l=U(s,_)}return t.json({route:{fsPath:e.fsPath,pointer:s,position:l}},200)}if(p?.metadata?.type==="graphql"){const a=await m(`${d}${e.fsPath}`,n.outdir);if(!a?.graphqlPositions)return t.json({route:e},200);const s=Object.keys(a.graphqlPositions).find(c=>w(e.baseSlug,c)===r),l=s?a.graphqlPositions[s].position:void 0;return t.json({route:{fsPath:e.fsPath,position:l}},200)}}return t.json({route:e},200)}}function X(n){return async t=>{const i=await t.req.json(),r=await T(i,5,async o=>A(o,n));return t.json({routes:r},200)}}function Y(n){return async t=>{const i=await t.req.json(),r=await A(i,n);return t.json(r,200)}}function J(n){return async t=>{const i=(t.req.query("lines")||"").split(",").map(f=>parseInt(f)),r=t.req.query("file");if(!r)return t.json("No data",400);const o=await m(`${d}${r}`,n.outdir),g=S.join(n.contentDir,r);try{const f=await y.readFile(g,"utf-8");if(!o)return t.json("No data",400);const u=new P(g,f),e=L(i,u,o.routesMapping,o.baseSlug);return t.json(e,200)}catch{return t.json("No data",400)}}}async function A(n,t){const[i,r]=n.split(":");if(I.test(i))return{route:{slug:`${b}/${$.ALL}`}};
|
|
1
|
+
import{Source as P}from"@redocly/openapi-core";import y from"node:fs/promises";import S from"node:path";import{withPathPrefix as j,getPathPrefix as h,combineUrls as w}from"@redocly/theme/core/utils";import{API_DOCS_SHARED_DATA_PREFIX as d}from"../../constants/plugins/api-docs.js";import{CATALOG_BASE_SLUG as b,CATALOG_SLUGS as $}from"../../../constants/catalog-entities.js";import{CATALOG_ENTITIES_FILES_REGEX as I}from"../../constants/plugins/catalog-entities.js";import{removeTrailingSlash as E}from"../../../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as N}from"../../../utils/path/normalize-route-slug.js";import{promiseMapLimit as T}from"../../utils/async/promise-map-limit.js";import{getRoutesByLines as L,getPointerPosition as U}from"../../plugins/api-docs/utils/ast-utils.js";import{readSharedData as m}from"../../utils/index.js";const q=new Set(["openapi","asyncapi"]);function K(n){return async t=>{const i=await t.req.json(),r=decodeURI(E(i.split("?")[0])),[o]=r.split("#"),g=h(),f=N(o.slice(g.length)),u=f.startsWith(b)?b:f,e=n.getRouteBySlug(u);if(e){const p=n.getRouteByFsPath(e.fsPath);if(p?.metadata?.type&&q.has(p.metadata.type)){const a=await m(`${d}${e.fsPath}`,n.outdir);if(!a?.routesMapping)return t.json({route:e},200);const s=Object.keys(a.routesMapping).find(c=>w(e.baseSlug,a.routesMapping[c])===r);if(!a.sourcePath)return t.json({route:e},200);let l;if(s){const c=S.join(n.contentDir,e.fsPath),R=await y.readFile(c,"utf-8"),_=new P(c,R);l=U(s,_)}return t.json({route:{fsPath:e.fsPath,pointer:s,position:l}},200)}if(p?.metadata?.type==="graphql"){const a=await m(`${d}${e.fsPath}`,n.outdir);if(!a?.graphqlPositions)return t.json({route:e},200);const s=Object.keys(a.graphqlPositions).find(c=>w(e.baseSlug,c)===r),l=s?a.graphqlPositions[s].position:void 0;return t.json({route:{fsPath:e.fsPath,position:l}},200)}}return t.json({route:e},200)}}function X(n){return async t=>{const i=await t.req.json(),r=await T(i,5,async o=>A(o,n));return t.json({routes:r},200)}}function Y(n){return async t=>{const i=await t.req.json(),r=await A(i,n);return t.json(r,200)}}function J(n){return async t=>{const i=(t.req.query("lines")||"").split(",").map(f=>parseInt(f)),r=t.req.query("file");if(!r)return t.json("No data",400);const o=await m(`${d}${r}`,n.outdir),g=S.join(n.contentDir,r);try{const f=await y.readFile(g,"utf-8");if(!o)return t.json("No data",400);const u=new P(g,f),e=L(i,u,o.routesMapping,o.baseSlug);return t.json(e,200)}catch{return t.json("No data",400)}}}async function A(n,t){const[i,r]=n.split(":");if(I.test(i))return{route:{slug:`${b}/${$.ALL}`}};const o=t.getRouteByFsPath(i);if(o?.metadata?.type&&q.has(o.metadata.type)){const u=await m(`${d}${i}`,t.outdir);if(u?.sourcePath){const e=S.join(t.contentDir,o.fsPath),p=await y.readFile(e,"utf-8"),a=new P(e,p),s=L([parseInt(r,10)],a,u.routesMapping,o.baseSlug);if(s.length)return{route:{slug:h()?j(s[0]):s[0],filePath:n}}}}if(o?.metadata?.type==="graphql"){const u=await m(`${d}${i}`,t.outdir),e=parseInt(r,10);if(u?.sourcePath&&u.graphqlPositions&&!Number.isNaN(e)){let p,a=1/0;for(const[s,{startLine:l,endLine:c}]of Object.entries(u.graphqlPositions))l<=e&&e<=c&&c-l<a&&(a=c-l,p=s);if(p){const s=w(o.baseSlug,p);return{route:{slug:h()?j(s):s,filePath:n}}}}}const g=o?.slug?h()?j(o?.slug):o?.slug:"";return{route:{...o,pageName:o?await o?.getNavText?.():"",slug:g,filePath:n}}}export{J as getRoutesByLineHandler,Y as resolvePathHandler,X as resolvePathsHandler,K as resolveSlugHandler};
|
|
@@ -40,6 +40,8 @@ export type PlanGates = {
|
|
|
40
40
|
[FEATURE.gatewayFetchRatePerMin]: number;
|
|
41
41
|
[FEATURE.gatewayFetchQuotaPerDay]: number;
|
|
42
42
|
[FEATURE.gatewayFetchBudgetPerExec]: number;
|
|
43
|
+
[FEATURE.mcpRequestRatePerMin]: number;
|
|
44
|
+
[FEATURE.mcpRequestQuotaPerDay]: number;
|
|
43
45
|
[FEATURE.cookieConsent]: boolean;
|
|
44
46
|
};
|
|
45
47
|
export type Entitlements = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function c(e,i=50,
|
|
1
|
+
function c(e,i=50,o=!1){let t;return function(...n){const l=()=>{t=void 0,o||e.apply(this,n)},u=o&&!t;clearTimeout(t),t=setTimeout(l,i),u&&e.apply(this,n)}}export{c as debounce};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/revel",
|
|
3
|
-
"version": "0.138.0-next.
|
|
3
|
+
"version": "0.138.0-next.18",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -33,11 +33,11 @@
|
|
|
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.
|
|
36
|
+
"@redocly/api-docs": "0.3.0-next.15",
|
|
37
37
|
"@redocly/config": "0.65.0",
|
|
38
|
-
"@redocly/openapi-core": "2.
|
|
38
|
+
"@redocly/openapi-core": "2.62.1",
|
|
39
39
|
"@redocly/portal-legacy-ui": "0.19.0",
|
|
40
|
-
"@redocly/portal-plugin-mock-server": "0.23.0-next.
|
|
40
|
+
"@redocly/portal-plugin-mock-server": "0.23.0-next.3",
|
|
41
41
|
"@redocly/realm-asyncapi-sdk": "0.16.0-next.5",
|
|
42
42
|
"@redocly/theme": "0.70.0-next.11",
|
|
43
43
|
"@shikijs/transformers": "3.21.0",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"reactjs-popup": "2.0.6",
|
|
85
85
|
"semver": "7.7.3",
|
|
86
86
|
"shiki": "3.21.0",
|
|
87
|
-
"simple-git": "
|
|
87
|
+
"simple-git": "4.0.2",
|
|
88
88
|
"sitemap": "7.1.1",
|
|
89
89
|
"stream-http": "3.2.0",
|
|
90
90
|
"styled-components": "^6.4.2",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{envConfig as o}from"../../../../config/env-config.js";import{isRealmOrReef as s}from"../../../../utils/is-realm-or-reef.js";import{KvRepository as m}from"../../../../persistence/kv/repositories/kv-repository.js";import{logger as f}from"../../../../tools/notifiers/logger.js";import{ipRateLimitKey as p}from"./ip-key.js";import{checkEdgeRateLimit as c}from"./edge-limiter.js";import{resolveGatewayFetchLimits as d}from"./limits.js";import{reportMcpRateLimited as l}from"./rate-limit-telemetry.js";async function E(n,a){if(!s())return;const i=p(n);if(!i)return;let e,r;try{const t=await m.create({baseDbDir:a.serverOutDir,sqldRemoteDatabaseUrl:o.SQLD_REMOTE_DATABASE_URL,sqldRemoteDatabaseAuthToken:o.SQLD_REMOTE_DATABASE_AUTH_TOKEN});r=d(),e=await c({kv:t,ip:i,limits:r,now:Date.now()})}catch(t){f.warn(`gateway rate-limit init error, failing open: ${String(t)}`);return}if(!e.allowed)return l({scope:e.scope,retryAfterSeconds:e.retryAfterSeconds,limit:e.scope==="rate"?r.ratePerMin:r.quotaPerDay}),new Response(JSON.stringify({jsonrpc:"2.0",error:{code:-32029,message:`Rate limit exceeded (${e.scope}); retry after ${e.retryAfterSeconds}s`},id:null}),{status:429,headers:{"Content-Type":"application/json","Retry-After":String(e.retryAfterSeconds)}})}export{E as applyEdgeRateLimit};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{checkAndIncrement as c}from"./window-counter.js";const s=60,d=86400;async function w(i){const{kv:e,ip:t,limits:o,now:r}=i;await e.sync();const n=await c({kv:e,ip:t,windowSeconds:s,limit:o.ratePerMin,now:r});if(!n.allowed)return{allowed:!1,retryAfterSeconds:n.retryAfterSeconds,scope:"rate"};const a=await c({kv:e,ip:t,windowSeconds:d,limit:o.quotaPerDay,now:r});return a.allowed?{allowed:!0}:{allowed:!1,retryAfterSeconds:a.retryAfterSeconds,scope:"quota"}}export{w as checkEdgeRateLimit};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{isIP as l}from"node:net";import{getFeedbackClientIp as a}from"../../../../web-server/utils/feedback-ip.js";import{extractMappedIpv4 as c}from"../../../../web-server/utils/ssrf-blocklist.js";function I(t){const n=a(t);if(n)return f(n)}function f(t){if(l(t)===6){const n=c(t);return n||`${u(t)}::/64`}return t}function u(t){const n=t.split("%")[0],[e,i=""]=n.split("::"),r=e?e.split(":"):[],o=i?i.split(":"):[],s=8-r.length-o.length;return[...r,...Array(Math.max(0,s)).fill("0"),...o].slice(0,4).map(p=>parseInt(p||"0",16).toString(16)).join(":")}export{I as ipRateLimitKey};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{EntitlementsProvider as u}from"../../../../entitlements/entitlements-provider.js";import{FEATURE as r,GATEWAY_FETCH_LIMIT_DEFAULTS as a}from"../../../../../constants/common.js";function n(e,i,o){const t=e.entitlements?.[i];return typeof t=="number"&&Number.isFinite(t)?t:o}function P(){const e=u.instance();return{ratePerMin:n(e,r.gatewayFetchRatePerMin,a.ratePerMin),quotaPerDay:n(e,r.gatewayFetchQuotaPerDay,a.quotaPerDay),budgetPerExec:n(e,r.gatewayFetchBudgetPerExec,a.budgetPerExec)}}export{P as resolveGatewayFetchLimits};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{telemetry as o}from"../../../../telemetry/index.js";import{logger as i}from"../../../../tools/notifiers/logger.js";import{McpServerType as m}from"../../constants.js";import{serverAttributes as p}from"../../utils/telemetry-attributes.js";function d({scope:e,retryAfterSeconds:r,limit:t}){try{o.sendMcpRateLimitedMessage([{...p(m.Docs),scope:e,retry_after_seconds:r,limit:t}])}catch{}i.warn(`Gateway MCP rate limited [scope=${e}] retryAfterSeconds=${r} limit=${t}`)}export{d as reportMcpRateLimited};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{logger as g}from"../../../../tools/notifiers/logger.js";function u(e,t,o){return["mcp","gateway-fetch-rl",e,String(t),String(o)]}async function S(e){const{kv:t,ip:o,windowSeconds:n,limit:w,now:l}=e,r=Math.floor(l/1e3),c=Math.floor(r/n),i=u(o,n,c);try{const s=(await t.get(i))?.count??0;if(s>=w){const d=(c+1)*n-r;return{allowed:!1,retryAfterSeconds:Math.max(1,d)}}return await t.set(i,{count:s+1},{ttlInSeconds:n}),{allowed:!0}}catch(a){return g.warn(`gateway rate-limit store error, failing open: ${String(a)}`),{allowed:!0}}}export{S as checkAndIncrement};
|
|
File without changes
|
|
File without changes
|