@redocly/reef 0.140.0-next.9 → 0.141.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +167 -0
- package/dist/client/app/search/useFacets.d.ts +1 -1
- package/dist/client/app/search/useFacets.js +1 -1
- package/dist/client/app/search/useSearch.js +1 -1
- package/dist/client/templates/api-docs/helpers.js +3 -3
- package/dist/compiled/svgo/svgo-node.js +1 -1
- package/dist/constants/l10n/langs/ar.js +1 -1
- package/dist/constants/l10n/langs/de.js +1 -1
- package/dist/constants/l10n/langs/en.js +1 -1
- package/dist/constants/l10n/langs/es.js +1 -1
- package/dist/constants/l10n/langs/fr.js +1 -1
- package/dist/constants/l10n/langs/hi.js +1 -1
- package/dist/constants/l10n/langs/it.js +1 -1
- package/dist/constants/l10n/langs/ja.js +1 -1
- package/dist/constants/l10n/langs/ko.js +1 -1
- package/dist/constants/l10n/langs/pl.js +1 -1
- package/dist/constants/l10n/langs/pt-BR.js +1 -1
- package/dist/constants/l10n/langs/pt.js +1 -1
- package/dist/constants/l10n/langs/ru.js +1 -1
- package/dist/constants/l10n/langs/uk.js +1 -1
- package/dist/constants/l10n/langs/zh.js +1 -1
- package/dist/server/esbuild/plugins/styled-components-ssr.js +1 -1
- package/dist/server/plugins/analytics/amplitude/index.js +1 -1
- package/dist/server/plugins/analytics/ga/build-scripts.js +6 -6
- package/dist/server/plugins/api-docs/index.js +1 -1
- package/dist/server/plugins/api-docs/search/get-ai-search-documents.js +24 -24
- package/dist/server/plugins/config-parser/loaders/utils/read-and-validate-config.js +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-openapi-code-samples.js +1 -1
- package/dist/server/plugins/markdown/search/create-render-tag-fn.d.ts +1 -1
- package/dist/server/plugins/markdown/search/create-render-tag-fn.js +1 -1
- package/dist/server/plugins/markdown/search/get-ai-search-documents.js +8 -8
- package/dist/server/store.d.ts +3 -0
- package/dist/server/store.js +1 -1
- package/dist/server/tools/notifiers/logger.d.ts +4 -0
- package/dist/server/tools/notifiers/reporter.js +1 -1
- package/dist/server/types/plugins/common.d.ts +1 -0
- package/dist/server/utils/errors.d.ts +3 -0
- package/dist/server/utils/errors.js +1 -1
- package/dist/server/utils/globs.d.ts +12 -0
- package/dist/server/utils/globs.js +1 -1
- package/dist/server/web-server/auth.d.ts +1 -0
- package/dist/server/web-server/auth.js +3 -3
- package/dist/server/web-server/routes/catalog/helpers/create-entity-schema.d.ts +1756 -1756
- package/dist/server/web-server/routes/catalog/helpers/create-entity-update-schema.d.ts +295 -295
- package/dist/server/web-server/routes/error.js +1 -1
- package/package.json +8 -8
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import v from"@redocly/markdoc";import M from"node:path";import{AI_SEARCH_CHUNK_SIZE as R,AI_SEARCH_DOCUMENT_CHUNK_SIZE as D}from"../../../constants/plugins/search.js";import{slugger as I}from"../../../../utils/slugger.js";import{joinSectionContent as N}from"./join-section-content.js";import{toMarkdown as P}from"./to-markdown.js";import{AstToSearchNodeTransformer as b}from"./walk-sections.js";import{createRenderTagFn as w}from"./create-render-tag-fn.js";import{getPublicEnvVariables as x}from"../../../utils/envs/get-public-env-variables.js";import{getBuildTranslateContext as K}from"../../../utils/l10n/get-build-translate-context.js";import{getLocaleFromRelativePath as L}from"../../../fs/utils/get-locale-from-relative-path.js";import{HeadingNode as U}from"./nodes/heading-node.js";import{extractDocumentSearchFacets as y}from"./search-facets.js";import{formatDocumentMetadata as H}from"../../search/utils.js";import{TagNode as O}from"./nodes/tag-node.js";import{MARKDOC_PARTIALS_DATA_KEY as j}from"../../../constants/common.js";const F=Symbol(),mt=(n,r)=>async(e,o,i,c)=>{if(r?.excludeFromSearch)return;I.reset();const l=await e.getNavText?.()||M.basename(e.fsPath),f=v.Ast.fromJSON(o.ast),{locale:g,translate:A}=await K(e.slug,c.getGlobalData?.().l10n),s=new b({partials:c.getGlobalConfig(j)||{},renderTag:w(c.markdocOptions.tags,g,A,{frontmatter:o.frontmatter,env:x()}),getInnerContent:P,ast:f,skipConditionals:!0}),d=Array.from(s.transform());return{async getLLMsTxts(){const u=r?.seo,t=u&&typeof u=="object"&&"description"in u?String(u.description):void 0,a=N(d);return[{title:l,description:t,slug:e.slug,fsPath:e.fsPath,content:a,includeInLLMsTxt:!0}]},async getSearchDocuments(){return $(e,d,n,l)}}};function $(n,r,e,o){const i=n.slug,c=new Map,l=n.metadata||{},f=y(l,e),g=H(l),A=g?`Metadata:
|
|
2
2
|
${g}
|
|
3
|
-
`:"",s=[];for(const t of
|
|
4
|
-
`)};(t.attributes.level>1||h(t)!==
|
|
5
|
-
`:"",p.content+=t.content.trimEnd()),c.set(t.id,p),s.push({section:t,title:C,url:S})}else if(t instanceof
|
|
6
|
-
`)});else{const a=t.parentNode?.id??
|
|
7
|
-
`:"",m.content+=t.content.trimEnd(),c.set(a,m)}const d
|
|
8
|
-
`),fsPath:n.fsPath,locale:u,product:n.product?.name,facets:f}))}function _(n){return n.length/4}function
|
|
9
|
-
`+i.content:(
|
|
3
|
+
`:"",s=[];for(const t of r)if(t instanceof U){s.splice(t.attributes.level-1,6);const a=t.attributes.level>2,m=s[s.length-1]?.title,T=s[s.length-1]?.url,C=a&&m?m:B(o,t),S=a&&T?T:t.getUrl(i),p={title:C,content:"",url:S,level:t.attributes.level,toc:s.map(k=>h(k.section)).join(`
|
|
4
|
+
`)};(t.attributes.level>1||h(t)!==o)&&(p.content+=p.content?`
|
|
5
|
+
`:"",p.content+=t.content.trimEnd()),c.set(t.id,p),s.push({section:t,title:C,url:S})}else if(t instanceof O)c.set(t.id,{title:t.content,content:t.content,url:t.getUrl(i),level:t.attributes.level+1,toc:s.map(a=>h(a.section)).concat(t.content).join(`
|
|
6
|
+
`)});else{const a=t.parentNode?.id??F,m=c.get(a)??{title:o,content:"",url:i,level:1/0,toc:""};m.content+=m.content?`
|
|
7
|
+
`:"",m.content+=t.content.trimEnd(),c.set(a,m)}const d=G(Array.from(c.values())),u=L(n.fsPath);return d.filter(t=>t.content).map(t=>({title:t.title,url:t.url,content:[A,t.toc||`# ${o}`,t.content].filter(Boolean).join(`
|
|
8
|
+
`),fsPath:n.fsPath,locale:u,product:n.product?.name,facets:f}))}function _(n){return n.length/4}function G(n){if(n.length===0)return[];const r=[];let e=n[0];for(let o=1;o<n.length;o++){const i=n[o],c=_(e.content),l=_(i.content),f=o===n.length-1;l<R&&(c<D||f)&&i.level>e.level?e.content+=`
|
|
9
|
+
`+i.content:(r.push(e),e=i)}return r.push(e),r}const E=String.raw`\((?:[^()]|\([^()]*\))*\)`,z=new RegExp(String.raw`!\[[^\]]*\]`+E,"g"),W=new RegExp(String.raw`\[(?<!\[[^\]()]*?\[)([^\]]*)\]`+E,"g");function B(n,r){const e=h(r);return r.content===n||!e?n:`${n} \u2192 ${e}`}function h(n){return Y(n.content.replace(/^#+/,""))}function Y(n){return n.replace(z,"").replace(W,"$1").trim()}export{mt as getAiDocumentsStore};
|
package/dist/server/store.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export type StoreOptions = {
|
|
|
21
21
|
export declare class Store {
|
|
22
22
|
#private;
|
|
23
23
|
routesBySlug: Map<string, PageRouteDetails>;
|
|
24
|
+
routeAliases: Map<string, string>;
|
|
24
25
|
staticFilePaths: StaticFilePaths;
|
|
25
26
|
replacedEnvVars: Record<string, {
|
|
26
27
|
original: string;
|
|
@@ -134,6 +135,8 @@ export declare class Store {
|
|
|
134
135
|
getRouteBySlug: (slug: string, opts?: {
|
|
135
136
|
followRedirect?: boolean;
|
|
136
137
|
}) => PageRouteDetails | undefined;
|
|
138
|
+
/** Makes `from` resolve to the route at `to` wherever a redirect would be followed (link resolution, the link checker), without an HTTP redirect; unlike `addRedirect` it needs no entitlement. */
|
|
139
|
+
addRouteAlias: (from: string, to: string) => void;
|
|
137
140
|
hasRouteOrRedirectBySlug: (slug: string) => boolean;
|
|
138
141
|
getRoutesByTemplateId: (templateId: string) => PageRouteInit[];
|
|
139
142
|
getAllRoutesForLocale: (locale?: string) => PageRouteDetails[];
|
package/dist/server/store.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import k from"@redocly/markdoc";import{writeFileSync as D}from"node:fs";import{readFile as R}from"node:fs/promises";import{join as g}from"node:path";import{parseAndResolveMarkdoc as F}from"./plugins/markdown/compiler.js";import{getPathnameForLocale as L}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as m}from"../constants/common.js";import{DEFAULT_TITLE as H,MARKDOC_PARTIALS_DATA_KEY as y,MARKDOC_PARTIALS_DEPS_KEY as G}from"./constants/common.js";import{GATED_MARKDOC_TAGS as v}from"./constants/entitlements.js";import{isObject as _}from"../utils/guards/is-object.js";import{mapObject as N}from"../utils/object/map-object.js";import{getValueDeep as w}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as B}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as u}from"../utils/path/normalize-route-slug.js";import{isLocalLink as E}from"../utils/path/is-local-link.js";import{reporter as P}from"./tools/notifiers/reporter.js";import{logger as h}from"./tools/notifiers/logger.js";import{sha1 as I}from"./utils/crypto/sha1.js";import{envConfig as j,setEnv as V}from"./config/env-config.js";import{KvService as q}from"./persistence/kv/services/kv-service.js";import{SandboxContext as J}from"./utils/sandbox-context.js";import{isAllowedEnv as U}from"./utils/envs/get-api-route-allowed-env-variables.js";import{ensureDir as x,writeSharedData as K}from"./utils/index.js";import{renderComponents as W}from"./ssr/render.js";import{readStaticData as $,writeStaticData as z}from"./utils/static-data.js";import{getMarkdocOptions as Y}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as C}from"./entitlements/entitlements-provider.js";import{isRouteReserved as Z}from"./plugins/get-reserved-routes.js";import{isL10nPath as Q}from"./fs/utils/is-l10n-path.js";import{resolveGlobMapValue as X}from"./utils/globs.js";import{createDefaultStoreConfig as ee,resolveConfigEntries as te,resolveStoreConfig as se}from"./store-config.js";import{findRedirect as re}from"./utils/redirects/find-redirect.js";import{followRedirectChain as oe}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as ae}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as ie}from"../cli/telemetry/helpers/trace-step.js";const b={routesBySlug:"map",staticFilePaths:"object",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",ssr:"object",searchFacets:"map",routesPartials:"map",mcpToolHandlers:"map",skills:"map",devLogin:"object"},O="store.json",A="store-config.json",T="PLAN_GATES",ne=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],Ue="userDefinedApiFunctions";class S{routesBySlug=new Map;staticFilePaths={};replacedEnvVars={};unsetEnvVars=new Set;lifecycleContext;newRoutes=[];#t={};routesByFsPath=new Map;apiRoutes=[];middleware=[];routesSharedData=new Map;sharedDataDeps=new Map;sharedDataMarkdocComponents=new Map;routesDynamicComponents=new Map;routesPartials=new Map;ssr={preBodyTags:[],postBodyTags:[],headTags:[]};searchFacets=new Map;searchEngine;templates=new Map;browserPlugins=new Set;apiRoutesRequestHandlers=new Map;mcpToolHandlers=new Map;skills=new Map;serverPropsGetters=new Map;pagePropsGetters=new Map;listeners=new Map;globalData={};devLogin=!1;#a=void 0;config=ee();configFile;#i;serverMode;serverOutDir;outdir;buildRevision=0;hasSitemap=!1;compilationErrors=[];#n;userCodeReady;#s=Promise.resolve();#c;#l=Promise.resolve();#d;#h=Promise.resolve();#u;#e=!1;#r=0;#f=0;#o=new Map;constructor({outdir:e,contentDir:t,serverMode:s=!1,serverOutDir:r}){this.#i=t,this.outdir=e,this.serverMode=s,this.serverOutDir=r,J.register({serverOutDir:r,readRoots:[e,process.cwd()]}),this.userCodeReady=new Promise(o=>{this.#n=o})}on(e,t){const s=this.listeners.get(e);s?s.add(t):this.listeners.set(e,new Set([t]))}queueEvent=(e,t,...s)=>{this.#o.set(e+String(t),[e,t,...s])};runListeners=(e,t,...s)=>{for(const r of this.listeners.get(e)||new Set)t?r(t,...s):r(...s)};startPluginsRun(){this.clear(),this.#s=new Promise(e=>{this.#c=e})}waitForPluginsLifecycle(){return Promise.all([this.#s,this.#l,this.#h])}waitForPluginsRun(){return this.#s}finishPluginsRun(){this.#c?.();for(const e of this.#o.values())this.runListeners(...e);this.#o.clear()}startEsbuildRun(){this.#l=new Promise(e=>{this.#d=e})}finishEsbuildRun(){this.#d?.()}markRequestHandlersEntryOutdated(){this.#r++,!this.#e&&(this.#e=!0,this.#h=new Promise(e=>{this.#u=e}))}noteRequestHandlersEntryBuildStart(){this.#f=this.#r}markRequestHandlersEntryUpdated(){this.#e&&(this.#r>this.#f||(this.#e=!1,this.#u?.()))}get contentDir(){if(this.serverMode)throw new Error("contentDir should not be used in server mode");return this.#i}markUserCodeReady(){this.#n?.(!0)}async reloadMarkdocOptions(){await ie("com.redocly.build.reloadMarkdocOptions",async()=>{const e=C.instance(),t=await Y(this.serverOutDir),s=Object.fromEntries(Object.entries(t.tags).filter(([r])=>v[r]!=null?e.canAccessFeature(v[r]):!0));this.#a={...t,tags:s}})}get markdocOptions(){return{...this.#a,partials:this.getGlobalConfig(y),themeConfig:this.config.markdown}}setGlobalData=e=>{const t=this.globalData,s={...this.globalData,...e};this.globalData=s,JSON.stringify(s)!==JSON.stringify(t)&&this.queueEvent("global-data-updated",void 0,s)};getGlobalData=()=>this.globalData;getKv=async()=>q.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:e,context:t,deps:s,resource:r})=>{const{data:{info:o,ast:a},compoundHash:c}=await F(e,this.markdocOptions,{actions:this,context:t},r);for(const i of o.sharedDataDeps||[]){for(const n of s?.routeSlugs||[])this.addRouteSharedData(n,i,i);for(const n of s?.sharedDataIds||[]){const l=this.sharedDataDeps.get(n)||new Set;l.add(i),this.sharedDataDeps.set(n,l)}}for(const i of o.dynamicMarkdocComponents||[]){for(const n of s?.routeSlugs||[]){const l=this.routesDynamicComponents.get(n)||new Set;l.add(i),this.routesDynamicComponents.set(n,l)}for(const n of s?.sharedDataIds||[]){const l=this.sharedDataMarkdocComponents.get(n)||new Set;l.add(i),this.sharedDataMarkdocComponents.set(n,l)}}if(s?.routeSlugs&&o.partials?.length)for(const i of s.routeSlugs){const n=this.routesPartials.get(i)||[];for(const l of o.partials)n.includes(l)||n.push(l);this.routesPartials.set(i,n)}return{info:o,ast:a,compoundHash:c}};async loadApiDocuments(e){return(await e.cache.load(".","api-docs")).data}setSearchEngine(e){this.searchEngine=e}setSearchFacets=e=>{this.searchFacets=e};setStaticFilePaths=e=>{this.staticFilePaths=e};setGlobalConfig=e=>{const t=Object.keys(e);for(const a of t)for(const c in this.replacedEnvVars)if(c===a||c.startsWith(`${a}:`)){const i=c.split(":"),{error:n,value:l}=w(e,i);(n||l!==this.replacedEnvVars[c].replaced)&&delete this.replacedEnvVars[c]}const{config:s,unsetEnvVars:r,replacedValues:o}=te(e);for(const a of r)this.unsetEnvVars.add(a);Object.assign(this.replacedEnvVars,o),Object.assign(this.config,s)};getConfig=()=>this.config;getGlobalConfig=e=>this.config[e];getSearchFacets=()=>this.searchFacets;addRedirect=(e,t,s={})=>{if(!C.instance().canAccessFeature("redirects")&&e!=="/")return;this.config.redirects||(this.config.redirects={});const a=u(e).toLowerCase();this.config.redirects[a]=t;const{trackOriginalSource:c=!0}=s,i=this.getGlobalConfig("originalRedirectSources");c&&Array.isArray(i)&&!i.includes(a)&&i.push(a),a.endsWith("*")&&ae(this.config.wildcardRedirectsTree,a)};getRedirect=e=>{const t=u(e).toLowerCase(),s=re(t,this.config.redirects,this.config.wildcardRedirectsTree,this.staticFilePaths);if(!s)return null;if(E(s.to)){const r=u(s.to).toLowerCase();if(!r.endsWith("*")&&oe(r,[t],this.config.redirects,this.config.wildcardRedirectsTree).type==="cycle")return null}return{to:s.to,type:s.type}};createSharedData=async(e,t,s)=>{if(s&&this.#t[e]===s)return e;const r=JSON.stringify(t),o=s??I(r);return this.#t[e]===o||(this.#t[e]=o,await K(e,r,this.outdir),this.queueEvent("shared-data-updated",e)),e};addRouteSharedData=(e,t,s)=>{const r=B(e),o=this.routesSharedData.get(r)||{};o[t]=s,this.routesSharedData.set(r,o),h.verbose(`Adding shared data to ${e}, ${t}, ${s}`)};getRouteSharedDataByFsPath=e=>{const t=this.routesByFsPath.get(e);return t?this.routesSharedData.get(t)||{}:{}};getPartialsForRoute=e=>{const t=this.getGlobalConfig(y)||{},s=this.routesPartials.get(e);if(!s||s.length===0)return{};const r=this.getGlobalConfig(G)||{},o=new Set(s),a=Array.from(s);for(let i=0;i<a.length;i++){const n=a[i],l=r[n]?.partials??[];for(const d of l)o.has(d)||(o.add(d),a.push(d))}const c={};for(const i of o)t[i]&&(c[i]=t[i]);return c};addRoute=e=>{const s={...X(e.fsPath,this.config.metadataGlobs),...e.metadata||{}};this.newRoutes.push({...e,metadata:s}),h.verbose("Created route %s",e.slug)};addRouteSharedDataToAllLocales=(e,t,s)=>{const r=[m,...this.lifecycleContext?.fs.localeFolders||[]].map(o=>({code:o,name:o}));for(const o of r){const a=L(e,m,o.code,r);this.addRouteSharedData(a,t,s)}};addApiRoute=e=>{this.apiRoutes.push(e),h.verbose("Created API route %s",e.slug)};addMcpTools=(e,t)=>{for(const s of t){const r=this.mcpToolHandlers.get(s.name);r&&r.importPath!==e&&h.warn('MCP tool "%s" from %s overwrites the tool registered from %s',s.name,e,r.importPath);const o=s.hasAvailabilityGate??(r?.importPath===e?r.hasAvailabilityGate:void 0);this.mcpToolHandlers.set(s.name,{...s,importPath:e,hasAvailabilityGate:o}),h.verbose("Created MCP tool %s",s.name)}};getMcpTools=()=>Array.from(this.mcpToolHandlers.values());markGatedMcpTools=e=>{for(const t of e){const s=this.mcpToolHandlers.get(t);s&&this.mcpToolHandlers.set(t,{...s,hasAvailabilityGate:!0})}};addSkills=e=>{for(const t of e)this.skills.set(t.slug,t),h.verbose("Discovered skill %s",t.slug)};getSkills=()=>Array.from(this.skills.values());removeMcpTools=e=>{for(const t of e)this.mcpToolHandlers.delete(t)};addMiddleware=e=>{this.middleware.push(e),h.verbose("Created middleware %s",e.id)};setResourceResponseHeaders=(e,t)=>{this.config.responseHeaders||(this.config.responseHeaders={});const s=new Set(t.map(a=>a.name.toLowerCase())),o=[...(this.config.responseHeaders[e]??[]).filter(a=>!s.has(a.name.toLowerCase())),...t];this.config.responseHeaders[e]=o};getRouteByFsPath=e=>{const t=this.routesByFsPath.get(e);return t?this.getRouteBySlug(t):void 0};getRouteBySlug=(e,t={})=>{const{followRedirect:s=!0}=t,r=this.getRedirect(e);return s&&r?this.routesBySlug.get(u(r.to)):this.routesBySlug.get(e)};hasRouteOrRedirectBySlug=e=>{if(this.routesBySlug.has(e)||Z(e,this.getConfig()))return!0;const t=this.getRedirect(e);if(!t)return!1;if(!E(t.to))return!0;const s=u(t.to);return this.routesBySlug.has(s)};getRoutesByTemplateId=e=>this.newRoutes.filter(t=>t.templateId===e);getAllRoutesForLocale=(e=m)=>{const t=Array.from(this.routesBySlug.values()),s=e.toLowerCase();return t.filter(r=>e===m?!Q(r.fsPath):r.slug.startsWith(`/${s}`))};getAllRoutes=()=>Array.from(this.routesBySlug.values());getAllApiRoutes=()=>this.apiRoutes;getAllMiddleware=()=>this.middleware;getTemplate=e=>this.templates.get(e);getRequestHandler=e=>this.apiRoutesRequestHandlers.get(e);createTemplate=(e,t)=>(this.templates.set(e,t),e);addBrowserPlugin=e=>{this.browserPlugins.add(e)};createRequestHandler=(e,t)=>(this.apiRoutesRequestHandlers.set(e,t),e);clearRequestHandlersByPrefix=e=>{for(const t of this.apiRoutesRequestHandlers.keys())t.startsWith(e)&&this.apiRoutesRequestHandlers.delete(t)};registerServerPropsGetter=(e,t)=>(this.serverPropsGetters.set(e,t),e);registerPagePropsGetter=(e,t)=>{this.pagePropsGetters.set(e,t)};async writeRouteStaticData(e,t){const s=await this.resolveRouteStaticData(e,t,!1);s&&z(e.slug,s,this.outdir)}async resolveRouteStaticData(e,t,s){if(this.serverMode)return $(e.slug,this.outdir);const r={...this,contentDir:this.contentDir,parseMarkdoc:({input:l,context:d,resource:f})=>this.parseMarkdoc({input:l,context:d,deps:{routeSlugs:[e.slug]},resource:f})},o=await e.getStaticData?.(e,r)||{},a=new Set(this.routesDynamicComponents.get(e.slug)),c=this.routesSharedData.get(e.slug)||{};for(const l of Object.values(c)){const d=this.sharedDataMarkdocComponents.get(l);d&&d.forEach(p=>a.add(p));const f=this.sharedDataDeps.get(l);f&&f.forEach(p=>this.addRouteSharedData(e.slug,p,p))}const i=this.getGlobalConfig("seo"),n=o?.frontmatter||{};return{...o,frontmatter:{...n,seo:{...n?.seo,title:n?.seo?.title||await e.getNavText?.()}},props:{...o.props,dynamicMarkdocComponents:Array.from(a),metadata:{...o?.props?.metadata,...e.metadata},seo:{title:H,...i,...o.props?.seo,siteUrl:i?.siteUrl},compilationErrors:this.compilationErrors},lastModified:s||!e.fsPath?null:await this.lifecycleContext?.fs.getLastModified(e.fsPath)}}addSsrComponents(e,t){if(!e?.length)return;const s=typeof e[0]=="string"?e.join(""):W(e);s&&(t==="head"?this.ssr.headTags.push(s):t==="preBody"?this.ssr.preBodyTags.push(s):this.ssr.postBodyTags.push(s))}clear=()=>{this.routesByFsPath.clear(),this.templates.clear(),this.newRoutes=[],this.routesBySlug.clear(),this.apiRoutes=[],this.middleware=[],this.routesSharedData.clear(),this.sharedDataDeps.clear(),this.sharedDataMarkdocComponents.clear(),this.routesDynamicComponents.clear(),this.routesPartials.clear(),this.skills.clear(),this.config.redirects={},this.config.wildcardRedirectsTree={},this.config.directoryPermissions={},this.ssr={preBodyTags:[],postBodyTags:[],headTags:[]}};#p(){const e=[];for(const[s,r]of Object.entries(b))switch(r){case"map":{const o=Array.from(this[s].entries());e.push([s,o]);break}case"object":e.push([s,this[s]]);break;default:throw new Error("Invalid format")}const t=Object.fromEntries(e);return t[T]=j.PLAN_GATES,t}static#g(e,t){const s=new S(t);for(const[o,a]of Object.entries(b))switch(a){case"map":s[o]=new Map(e[o]);break;case"object":s[o]=e[o];break;default:throw new Error("Invalid format")}const r=e[T];return r&&V("PLAN_GATES",r),s}async writeServerFiles(){x(this.serverOutDir),D(g(this.serverOutDir,O),JSON.stringify(this.#p())),D(g(this.serverOutDir,A),JSON.stringify(await this.getConfigWithEnvPlaceholders()))}static async fromServerFiles(e){const t=g(e.serverOutDir,A),{config:s,env:r}=se(JSON.parse(await R(t,"utf8")));s[y]=ce(s[y]);const o=JSON.parse(await R(g(e.serverOutDir,O),"utf8")),a=S.#g(o,e);a.config=s;const c=Object.fromEntries(Object.entries(r).filter(([i])=>U(i)));return a.configFile={path:t,env:c,config:{...s}},a}async getConfigWithEnvPlaceholders(){const e=JSON.parse(JSON.stringify(this.config));for(const t in this.replacedEnvVars){const{original:s}=this.replacedEnvVars[t],r=t.split(":"),o=r.pop(),{error:a,value:c}=w(e,r);if(a||!_(c)&&!Array.isArray(c)){await P.panicOnBuild(`Failed to replace env var with env name for ${t}`);continue}c[o]=s}return e}async reportUnsetEnvVars(){if(this.unsetEnvVars.size===0)return;const e=Array.from(this.unsetEnvVars).filter(s=>!ne.includes(s));if(e.length===0)return;const t=`Failed to resolve config. The following environment variables are not set: ${e.join(", ")}`;await P.panicOnBuildContentError(t)}}function ce(M){return N(M,e=>k.Ast.fromJSON(JSON.stringify(e)))}export{S as Store,Ue as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
|
|
1
|
+
import k from"@redocly/markdoc";import{writeFileSync as S}from"node:fs";import{readFile as D}from"node:fs/promises";import{join as g}from"node:path";import{parseAndResolveMarkdoc as F}from"./plugins/markdown/compiler.js";import{getPathnameForLocale as L}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as m}from"../constants/common.js";import{DEFAULT_TITLE as H,MARKDOC_PARTIALS_DATA_KEY as y,MARKDOC_PARTIALS_DEPS_KEY as G}from"./constants/common.js";import{GATED_MARKDOC_TAGS as v}from"./constants/entitlements.js";import{isObject as _}from"../utils/guards/is-object.js";import{mapObject as N}from"../utils/object/map-object.js";import{getValueDeep as w}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as B}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as d}from"../utils/path/normalize-route-slug.js";import{isLocalLink as E}from"../utils/path/is-local-link.js";import{reporter as P}from"./tools/notifiers/reporter.js";import{logger as u}from"./tools/notifiers/logger.js";import{sha1 as I}from"./utils/crypto/sha1.js";import{envConfig as j,setEnv as V}from"./config/env-config.js";import{KvService as q}from"./persistence/kv/services/kv-service.js";import{SandboxContext as J}from"./utils/sandbox-context.js";import{isAllowedEnv as U}from"./utils/envs/get-api-route-allowed-env-variables.js";import{ensureDir as x,writeSharedData as K}from"./utils/index.js";import{renderComponents as W}from"./ssr/render.js";import{readStaticData as $,writeStaticData as z}from"./utils/static-data.js";import{getMarkdocOptions as Y}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as C}from"./entitlements/entitlements-provider.js";import{isRouteReserved as Z}from"./plugins/get-reserved-routes.js";import{isL10nPath as Q}from"./fs/utils/is-l10n-path.js";import{resolveGlobMapValue as X}from"./utils/globs.js";import{createDefaultStoreConfig as ee,resolveConfigEntries as te,resolveStoreConfig as se}from"./store-config.js";import{findRedirect as re}from"./utils/redirects/find-redirect.js";import{followRedirectChain as ae}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as oe}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as ie}from"../cli/telemetry/helpers/trace-step.js";const A={routesBySlug:"map",staticFilePaths:"object",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",ssr:"object",searchFacets:"map",routesPartials:"map",mcpToolHandlers:"map",skills:"map",devLogin:"object"},b="store.json",O="store-config.json",T="PLAN_GATES",ne=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],Ue="userDefinedApiFunctions";class R{routesBySlug=new Map;routeAliases=new Map;staticFilePaths={};replacedEnvVars={};unsetEnvVars=new Set;lifecycleContext;newRoutes=[];#t={};routesByFsPath=new Map;apiRoutes=[];middleware=[];routesSharedData=new Map;sharedDataDeps=new Map;sharedDataMarkdocComponents=new Map;routesDynamicComponents=new Map;routesPartials=new Map;ssr={preBodyTags:[],postBodyTags:[],headTags:[]};searchFacets=new Map;searchEngine;templates=new Map;browserPlugins=new Set;apiRoutesRequestHandlers=new Map;mcpToolHandlers=new Map;skills=new Map;serverPropsGetters=new Map;pagePropsGetters=new Map;listeners=new Map;globalData={};devLogin=!1;#o=void 0;config=ee();configFile;#i;serverMode;serverOutDir;outdir;buildRevision=0;hasSitemap=!1;compilationErrors=[];#n;userCodeReady;#s=Promise.resolve();#l;#c=Promise.resolve();#d;#h=Promise.resolve();#u;#e=!1;#r=0;#f=0;#a=new Map;constructor({outdir:e,contentDir:t,serverMode:s=!1,serverOutDir:r}){this.#i=t,this.outdir=e,this.serverMode=s,this.serverOutDir=r,J.register({serverOutDir:r,readRoots:[e,process.cwd()]}),this.userCodeReady=new Promise(a=>{this.#n=a})}on(e,t){const s=this.listeners.get(e);s?s.add(t):this.listeners.set(e,new Set([t]))}queueEvent=(e,t,...s)=>{this.#a.set(e+String(t),[e,t,...s])};runListeners=(e,t,...s)=>{for(const r of this.listeners.get(e)||new Set)t?r(t,...s):r(...s)};startPluginsRun(){this.clear(),this.#s=new Promise(e=>{this.#l=e})}waitForPluginsLifecycle(){return Promise.all([this.#s,this.#c,this.#h])}waitForPluginsRun(){return this.#s}finishPluginsRun(){this.#l?.();for(const e of this.#a.values())this.runListeners(...e);this.#a.clear()}startEsbuildRun(){this.#c=new Promise(e=>{this.#d=e})}finishEsbuildRun(){this.#d?.()}markRequestHandlersEntryOutdated(){this.#r++,!this.#e&&(this.#e=!0,this.#h=new Promise(e=>{this.#u=e}))}noteRequestHandlersEntryBuildStart(){this.#f=this.#r}markRequestHandlersEntryUpdated(){this.#e&&(this.#r>this.#f||(this.#e=!1,this.#u?.()))}get contentDir(){if(this.serverMode)throw new Error("contentDir should not be used in server mode");return this.#i}markUserCodeReady(){this.#n?.(!0)}async reloadMarkdocOptions(){await ie("com.redocly.build.reloadMarkdocOptions",async()=>{const e=C.instance(),t=await Y(this.serverOutDir),s=Object.fromEntries(Object.entries(t.tags).filter(([r])=>v[r]!=null?e.canAccessFeature(v[r]):!0));this.#o={...t,tags:s}})}get markdocOptions(){return{...this.#o,partials:this.getGlobalConfig(y),themeConfig:this.config.markdown}}setGlobalData=e=>{const t=this.globalData,s={...this.globalData,...e};this.globalData=s,JSON.stringify(s)!==JSON.stringify(t)&&this.queueEvent("global-data-updated",void 0,s)};getGlobalData=()=>this.globalData;getKv=async()=>q.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:e,context:t,deps:s,resource:r})=>{const{data:{info:a,ast:o},compoundHash:l}=await F(e,this.markdocOptions,{actions:this,context:t},r);for(const i of a.sharedDataDeps||[]){for(const n of s?.routeSlugs||[])this.addRouteSharedData(n,i,i);for(const n of s?.sharedDataIds||[]){const c=this.sharedDataDeps.get(n)||new Set;c.add(i),this.sharedDataDeps.set(n,c)}}for(const i of a.dynamicMarkdocComponents||[]){for(const n of s?.routeSlugs||[]){const c=this.routesDynamicComponents.get(n)||new Set;c.add(i),this.routesDynamicComponents.set(n,c)}for(const n of s?.sharedDataIds||[]){const c=this.sharedDataMarkdocComponents.get(n)||new Set;c.add(i),this.sharedDataMarkdocComponents.set(n,c)}}if(s?.routeSlugs&&a.partials?.length)for(const i of s.routeSlugs){const n=this.routesPartials.get(i)||[];for(const c of a.partials)n.includes(c)||n.push(c);this.routesPartials.set(i,n)}return{info:a,ast:o,compoundHash:l}};async loadApiDocuments(e){return(await e.cache.load(".","api-docs")).data}setSearchEngine(e){this.searchEngine=e}setSearchFacets=e=>{this.searchFacets=e};setStaticFilePaths=e=>{this.staticFilePaths=e};setGlobalConfig=e=>{const t=Object.keys(e);for(const o of t)for(const l in this.replacedEnvVars)if(l===o||l.startsWith(`${o}:`)){const i=l.split(":"),{error:n,value:c}=w(e,i);(n||c!==this.replacedEnvVars[l].replaced)&&delete this.replacedEnvVars[l]}const{config:s,unsetEnvVars:r,replacedValues:a}=te(e);for(const o of r)this.unsetEnvVars.add(o);Object.assign(this.replacedEnvVars,a),Object.assign(this.config,s)};getConfig=()=>this.config;getGlobalConfig=e=>this.config[e];getSearchFacets=()=>this.searchFacets;addRedirect=(e,t,s={})=>{if(!C.instance().canAccessFeature("redirects")&&e!=="/")return;this.config.redirects||(this.config.redirects={});const o=d(e).toLowerCase();this.config.redirects[o]=t;const{trackOriginalSource:l=!0}=s,i=this.getGlobalConfig("originalRedirectSources");l&&Array.isArray(i)&&!i.includes(o)&&i.push(o),o.endsWith("*")&&oe(this.config.wildcardRedirectsTree,o)};getRedirect=e=>{const t=d(e).toLowerCase(),s=re(t,this.config.redirects,this.config.wildcardRedirectsTree,this.staticFilePaths);if(!s)return null;if(E(s.to)){const r=d(s.to).toLowerCase();if(!r.endsWith("*")&&ae(r,[t],this.config.redirects,this.config.wildcardRedirectsTree).type==="cycle")return null}return{to:s.to,type:s.type}};createSharedData=async(e,t,s)=>{if(s&&this.#t[e]===s)return e;const r=JSON.stringify(t),a=s??I(r);return this.#t[e]===a||(this.#t[e]=a,await K(e,r,this.outdir),this.queueEvent("shared-data-updated",e)),e};addRouteSharedData=(e,t,s)=>{const r=B(e),a=this.routesSharedData.get(r)||{};a[t]=s,this.routesSharedData.set(r,a),u.verbose(`Adding shared data to ${e}, ${t}, ${s}`)};getRouteSharedDataByFsPath=e=>{const t=this.routesByFsPath.get(e);return t?this.routesSharedData.get(t)||{}:{}};getPartialsForRoute=e=>{const t=this.getGlobalConfig(y)||{},s=this.routesPartials.get(e);if(!s||s.length===0)return{};const r=this.getGlobalConfig(G)||{},a=new Set(s),o=Array.from(s);for(let i=0;i<o.length;i++){const n=o[i],c=r[n]?.partials??[];for(const h of c)a.has(h)||(a.add(h),o.push(h))}const l={};for(const i of a)t[i]&&(l[i]=t[i]);return l};addRoute=e=>{const s={...X(e.fsPath,this.config.metadataGlobs),...e.metadata||{}};this.newRoutes.push({...e,metadata:s}),u.verbose("Created route %s",e.slug)};addRouteSharedDataToAllLocales=(e,t,s)=>{const r=[m,...this.lifecycleContext?.fs.localeFolders||[]].map(a=>({code:a,name:a}));for(const a of r){const o=L(e,m,a.code,r);this.addRouteSharedData(o,t,s)}};addApiRoute=e=>{this.apiRoutes.push(e),u.verbose("Created API route %s",e.slug)};addMcpTools=(e,t)=>{for(const s of t){const r=this.mcpToolHandlers.get(s.name);r&&r.importPath!==e&&u.warn('MCP tool "%s" from %s overwrites the tool registered from %s',s.name,e,r.importPath);const a=s.hasAvailabilityGate??(r?.importPath===e?r.hasAvailabilityGate:void 0);this.mcpToolHandlers.set(s.name,{...s,importPath:e,hasAvailabilityGate:a}),u.verbose("Created MCP tool %s",s.name)}};getMcpTools=()=>Array.from(this.mcpToolHandlers.values());markGatedMcpTools=e=>{for(const t of e){const s=this.mcpToolHandlers.get(t);s&&this.mcpToolHandlers.set(t,{...s,hasAvailabilityGate:!0})}};addSkills=e=>{for(const t of e)this.skills.set(t.slug,t),u.verbose("Discovered skill %s",t.slug)};getSkills=()=>Array.from(this.skills.values());removeMcpTools=e=>{for(const t of e)this.mcpToolHandlers.delete(t)};addMiddleware=e=>{this.middleware.push(e),u.verbose("Created middleware %s",e.id)};setResourceResponseHeaders=(e,t)=>{this.config.responseHeaders||(this.config.responseHeaders={});const s=new Set(t.map(o=>o.name.toLowerCase())),a=[...(this.config.responseHeaders[e]??[]).filter(o=>!s.has(o.name.toLowerCase())),...t];this.config.responseHeaders[e]=a};getRouteByFsPath=e=>{const t=this.routesByFsPath.get(e);return t?this.getRouteBySlug(t):void 0};getRouteBySlug=(e,t={})=>{const{followRedirect:s=!0}=t,r=this.getRedirect(e);if(s&&r)return this.routesBySlug.get(d(r.to));const a=s?this.routeAliases.get(d(e).toLowerCase()):void 0;return this.routesBySlug.get(a??e)};addRouteAlias=(e,t)=>{this.routeAliases.set(d(e).toLowerCase(),d(t))};hasRouteOrRedirectBySlug=e=>{if(this.routesBySlug.has(e)||this.routeAliases.has(d(e).toLowerCase())||Z(e,this.getConfig()))return!0;const t=this.getRedirect(e);if(!t)return!1;if(!E(t.to))return!0;const s=d(t.to);return this.routesBySlug.has(s)};getRoutesByTemplateId=e=>this.newRoutes.filter(t=>t.templateId===e);getAllRoutesForLocale=(e=m)=>{const t=Array.from(this.routesBySlug.values()),s=e.toLowerCase();return t.filter(r=>e===m?!Q(r.fsPath):r.slug.startsWith(`/${s}`))};getAllRoutes=()=>Array.from(this.routesBySlug.values());getAllApiRoutes=()=>this.apiRoutes;getAllMiddleware=()=>this.middleware;getTemplate=e=>this.templates.get(e);getRequestHandler=e=>this.apiRoutesRequestHandlers.get(e);createTemplate=(e,t)=>(this.templates.set(e,t),e);addBrowserPlugin=e=>{this.browserPlugins.add(e)};createRequestHandler=(e,t)=>(this.apiRoutesRequestHandlers.set(e,t),e);clearRequestHandlersByPrefix=e=>{for(const t of this.apiRoutesRequestHandlers.keys())t.startsWith(e)&&this.apiRoutesRequestHandlers.delete(t)};registerServerPropsGetter=(e,t)=>(this.serverPropsGetters.set(e,t),e);registerPagePropsGetter=(e,t)=>{this.pagePropsGetters.set(e,t)};async writeRouteStaticData(e,t){const s=await this.resolveRouteStaticData(e,t,!1);s&&z(e.slug,s,this.outdir)}async resolveRouteStaticData(e,t,s){if(this.serverMode)return $(e.slug,this.outdir);const r={...this,contentDir:this.contentDir,parseMarkdoc:({input:c,context:h,resource:f})=>this.parseMarkdoc({input:c,context:h,deps:{routeSlugs:[e.slug]},resource:f})},a=await e.getStaticData?.(e,r)||{},o=new Set(this.routesDynamicComponents.get(e.slug)),l=this.routesSharedData.get(e.slug)||{};for(const c of Object.values(l)){const h=this.sharedDataMarkdocComponents.get(c);h&&h.forEach(p=>o.add(p));const f=this.sharedDataDeps.get(c);f&&f.forEach(p=>this.addRouteSharedData(e.slug,p,p))}const i=this.getGlobalConfig("seo"),n=a?.frontmatter||{};return{...a,frontmatter:{...n,seo:{...n?.seo,title:n?.seo?.title||await e.getNavText?.()}},props:{...a.props,dynamicMarkdocComponents:Array.from(o),metadata:{...a?.props?.metadata,...e.metadata},seo:{title:H,...i,...a.props?.seo,siteUrl:i?.siteUrl},compilationErrors:this.compilationErrors},lastModified:s||!e.fsPath?null:await this.lifecycleContext?.fs.getLastModified(e.fsPath)}}addSsrComponents(e,t){if(!e?.length)return;const s=typeof e[0]=="string"?e.join(""):W(e);s&&(t==="head"?this.ssr.headTags.push(s):t==="preBody"?this.ssr.preBodyTags.push(s):this.ssr.postBodyTags.push(s))}clear=()=>{this.routesByFsPath.clear(),this.templates.clear(),this.newRoutes=[],this.routesBySlug.clear(),this.routeAliases.clear(),this.apiRoutes=[],this.middleware=[],this.routesSharedData.clear(),this.sharedDataDeps.clear(),this.sharedDataMarkdocComponents.clear(),this.routesDynamicComponents.clear(),this.routesPartials.clear(),this.skills.clear(),this.config.redirects={},this.config.wildcardRedirectsTree={},this.config.directoryPermissions={},this.ssr={preBodyTags:[],postBodyTags:[],headTags:[]}};#p(){const e=[];for(const[s,r]of Object.entries(A))switch(r){case"map":{const a=Array.from(this[s].entries());e.push([s,a]);break}case"object":e.push([s,this[s]]);break;default:throw new Error("Invalid format")}const t=Object.fromEntries(e);return t[T]=j.PLAN_GATES,t}static#g(e,t){const s=new R(t);for(const[a,o]of Object.entries(A))switch(o){case"map":s[a]=new Map(e[a]);break;case"object":s[a]=e[a];break;default:throw new Error("Invalid format")}const r=e[T];return r&&V("PLAN_GATES",r),s}async writeServerFiles(){x(this.serverOutDir),S(g(this.serverOutDir,b),JSON.stringify(this.#p())),S(g(this.serverOutDir,O),JSON.stringify(await this.getConfigWithEnvPlaceholders()))}static async fromServerFiles(e){const t=g(e.serverOutDir,O),{config:s,env:r}=se(JSON.parse(await D(t,"utf8")));s[y]=le(s[y]);const a=JSON.parse(await D(g(e.serverOutDir,b),"utf8")),o=R.#g(a,e);o.config=s;const l=Object.fromEntries(Object.entries(r).filter(([i])=>U(i)));return o.configFile={path:t,env:l,config:{...s}},o}async getConfigWithEnvPlaceholders(){const e=JSON.parse(JSON.stringify(this.config));for(const t in this.replacedEnvVars){const{original:s}=this.replacedEnvVars[t],r=t.split(":"),a=r.pop(),{error:o,value:l}=w(e,r);if(o||!_(l)&&!Array.isArray(l)){await P.panicOnBuild(`Failed to replace env var with env name for ${t}`);continue}l[a]=s}return e}async reportUnsetEnvVars(){if(this.unsetEnvVars.size===0)return;const e=Array.from(this.unsetEnvVars).filter(s=>!ne.includes(s));if(e.length===0)return;const t=`Failed to resolve config. The following environment variables are not set: ${e.join(", ")}`;await P.panicOnBuildContentError(t)}}function le(M){return N(M,e=>k.Ast.fromJSON(JSON.stringify(e)))}export{R as Store,Ue as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
|
|
@@ -28,6 +28,10 @@ export declare class Logger {
|
|
|
28
28
|
timeMs: number;
|
|
29
29
|
} | undefined;
|
|
30
30
|
success(message: string, ...args: unknown[]): void;
|
|
31
|
+
/**
|
|
32
|
+
* Status line: describes the current state (error count, preview URL, ...).
|
|
33
|
+
* In non-interactive mode it is printed only when its text changes, so repeated rebuilds don't flood the log.
|
|
34
|
+
*/
|
|
31
35
|
logInFooter(id: string, message: string, ...args: unknown[]): void;
|
|
32
36
|
successTime(timingId: symbol | string, message: string, ...args: unknown[]): {
|
|
33
37
|
message: string;
|
|
@@ -2,7 +2,7 @@ import{envConfig as h}from"../../config/env-config.js";import{logger as e}from".
|
|
|
2
2
|
`+r.stack),h.isBuildMode?await this.panic(r,...t):this.pushError(r,"PANIC",...t)}async panicOnBuildContentErrorForRealFile(r,t,o,...i){C(t,o)||await this.panicOnBuildContentError(r,...i)}async panicOnBuildContentError(r,...t){r instanceof Error&&(r=r.message+`
|
|
3
3
|
`+r.stack),h.isBuildMode?await this.panicOnContentError(r,...t):this.pushError(r,"PANIC",...t)}async panic(r,...t){let o;r instanceof Error?(o=r,r=r.message+`
|
|
4
4
|
`+r.stack):o=new Error(r),e.error(r,...t),u.sendCliErrorCaughtMessage([{id:"cliId",object:"cli",uri:"urn:redocly:realm:api:cli:cliId",message:r}]),await d.exitWithCode(1,o)}async panicOnContentError(r,...t){let o;r instanceof Error?(o=r,r=r.message+`
|
|
5
|
-
`+r.stack):o=new Error(r),e.contentError(r,...t),u.sendCliErrorCaughtMessage([{id:"cliId",object:"cli",uri:"urn:redocly:realm:api:cli:cliId",message:r,scope:"content"}]),await d.exitWithCode(1,o)}reportMarkdocProblem(r){this.#o.push(r)}clearErrors(){this.#r=[]}clearMarkdocProblems(){this.#o=[]}clearEsbuildProblems(){this.#e.clear()}clearPageRenderProblems(){this.#t=[]}getCompilationProblem(r){return this.#e.get(r)}getCompilationProblems(){return[...this.#e.values()]}getPageRenderProblems(){return this.#t}getPageRenderProblem(r){return this.#t.find(t=>t.sourceFileRelativePath===r)}getProblems(){return[...this.#r,...this.getCompilationProblems(),...this.#o,...this.#t]}summary(r,t=0){const o=this.getProblems(),i=this.#o.filter(s=>s.type==="BROKEN_LINK").length,l=this.#r.filter(s=>s.type==="BROKEN_LINK").length,
|
|
5
|
+
`+r.stack):o=new Error(r),e.contentError(r,...t),u.sendCliErrorCaughtMessage([{id:"cliId",object:"cli",uri:"urn:redocly:realm:api:cli:cliId",message:r,scope:"content"}]),await d.exitWithCode(1,o)}reportMarkdocProblem(r){this.#o.push(r)}clearErrors(){this.#r=[]}clearMarkdocProblems(){this.#o=[]}clearEsbuildProblems(){this.#e.clear()}clearPageRenderProblems(){this.#t=[]}getCompilationProblem(r){return this.#e.get(r)}getCompilationProblems(){return[...this.#e.values()]}getPageRenderProblems(){return this.#t}getPageRenderProblem(r){return this.#t.find(t=>t.sourceFileRelativePath===r)}getProblems(){return[...this.#r,...this.getCompilationProblems(),...this.#o,...this.#t]}summary(r,t=0){const o=this.getProblems(),i=this.#o.filter(s=>s.type==="BROKEN_LINK").length,l=this.#r.filter(s=>s.type==="BROKEN_LINK").length,m=this.#o.length-i,E=this.#r.length-l+this.getCompilationProblems().length+this.#t.length;return e.logInFooter("validate",o.length?a(" \u274C Status: %s markdoc errors, %s broken links, %s other errors"):" \u2705 Status: No errors found",m,i+l,E),t!==0&&e.logInFooter("pages",` \u{1F4C4} Total pages: ${t}`),e.isInteractive()?e.logInFooter("timing",r):e.info(r),e.logInFooter("validate-sep",""),e.isInteractive()&&(o.length?e.logInFooter("actions","Press (e) to print all errors, (q) to quit"):e.logInFooter("actions",p("Press (q) to quit"))),o}printErrors(r=[]){const t=this.#r.length,o=r.length?r:this.getProblems();let i=0;for(const l of o.slice(0,c))i>=t?e.contentError(a(`[${++i}] `)+f(l)):e.error(a(`[${++i}] `)+f(l));o.length>c&&e.error(`... and ${o.length-c} more errors`)}}const $=new b;function f(n){const r=n.sourceFileLocation,t=r?`:${r.line}:${r.character??1}`:"";return n.message+(n.codeframe?`
|
|
6
6
|
|
|
7
7
|
`+n.codeframe+`
|
|
8
8
|
|
|
@@ -201,6 +201,7 @@ export type AfterRoutesCreatedActions = {
|
|
|
201
201
|
getSearchFacets: () => Map<string, SearchFacet>;
|
|
202
202
|
setSearchEngine: (engine: SearchEngine) => void;
|
|
203
203
|
addRedirect: (from: string, to: RedirectConfig, options?: RedirectOptions) => void;
|
|
204
|
+
addRouteAlias: (from: string, to: string) => void;
|
|
204
205
|
contentDir: string;
|
|
205
206
|
outdir: string;
|
|
206
207
|
serverOutDir: string;
|
|
@@ -2,4 +2,7 @@ export declare class HttpError extends Error {
|
|
|
2
2
|
status: number;
|
|
3
3
|
constructor(status: number, message: string);
|
|
4
4
|
}
|
|
5
|
+
export declare class OidcDiscoveryError extends Error {
|
|
6
|
+
constructor(url: string, status: number, contentType: string | null);
|
|
7
|
+
}
|
|
5
8
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
class
|
|
1
|
+
class r extends Error{status;constructor(t,e){super(e),this.status=t,Object.setPrototypeOf(this,r.prototype)}}class s extends Error{constructor(t,e,o){super(`OIDC discovery failed: ${new URL(t).host} answered HTTP ${e} (${o??"no content type"})`),this.name="OidcDiscoveryError",Object.setPrototypeOf(this,s.prototype)}}export{r as HttpError,s as OidcDiscoveryError};
|
|
@@ -7,5 +7,17 @@ export declare function compileGlobMatcher(patterns: string[]): (relativePath: s
|
|
|
7
7
|
export declare function pathMatchesGlobPattern(relativePath: string, pattern: string): boolean;
|
|
8
8
|
export declare function getMostSpecificMatchingGlobKey(relativePath: string, globMap?: Record<string, unknown>): string | undefined;
|
|
9
9
|
export declare function resolveGlobMapValue<T>(relativePath: string, globMap?: Record<string, T>): T | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* Converts a glob to a regular expression that is safe to inline in an HTML script: its source
|
|
12
|
+
* never contains `<`.
|
|
13
|
+
*
|
|
14
|
+
* @param glob - The glob, for example `/docs/**`
|
|
15
|
+
* @returns The regular expression, or undefined when the glob has none
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* convertGlobToRegex('/docs/**')?.test('/docs/a') // Returns true
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
10
22
|
export declare function convertGlobToRegex(glob: string): RegExp | undefined;
|
|
11
23
|
//# sourceMappingURL=globs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import s from"picomatch";import{minimatch as m,Minimatch as p}from"minimatch";import
|
|
1
|
+
import s from"picomatch";import{minimatch as m,Minimatch as p}from"minimatch";import l from"is-glob";import c from"path";import{removeTrailingSlash as u}from"../../utils/url/remove-trailing-slash.js";import{removeLeadingSlash as f}from"../../utils/url/remove-leading-slash.js";const a=(n,e)=>{const t=n.tokens??[],o=e.tokens??[];if(n.isGlob!==e.isGlob)return n.isGlob?e:n;if(t.length!==o.length)return t.length>o.length?n:e;for(let r=0;r<t.length;r++){if(t[r].isGlob!==o[r].isGlob)return t[r].isGlob?e:n;if(t[r].value!==o[r].value)return t[r].value.length>o[r].value.length?n:e}return n};function v(n){const e=n.map(t=>{if(l(t)){const r=new p(t);return i=>r.match(i)}const o=u(f(c.posix.normalize(t)));return r=>r===o||r.startsWith(o+"/")});return t=>e.some(o=>o(t))}function h(n,e){if(l(e)){if(m(n,e))return!0}else{const t=u(f(c.posix.normalize(e))),o=n.startsWith(t+"/");if(n===t||o)return!0}return!1}function d(n,e){if(!e)return;const t=[];for(const i of Object.keys(e))h(n,i)&&t.push(i);if(!t.length)return;const o=t.map(i=>s.scan(i,{tokens:!0,parts:!0}));let r=o[0];for(let i=1;i<o.length;i++)r=a(r,o[i]);return r.input}function P(n,e){const t=d(n,e);return t!=null&&e?e[t]:void 0}function T(n){const e=s.makeRe(n);return e?new RegExp(e.source.replaceAll("<","\\u003c"),e.flags):void 0}export{v as compileGlobMatcher,T as convertGlobToRegex,a as getDeeperGlobPattern,d as getMostSpecificMatchingGlobKey,h as pathMatchesGlobPattern,P as resolveGlobMapValue};
|
|
@@ -160,6 +160,7 @@ export declare const oidcJwksCache: {
|
|
|
160
160
|
export declare const UNKNOWN_KID_RETRY_MS = 60000;
|
|
161
161
|
export declare function getOidcMetadata(idpId: string, providerConfig: OidcProviderConfig): Promise<OidcIssuerMetadata>;
|
|
162
162
|
export declare function discoverJwks(ssoConfig: SsoConfig): Promise<void>;
|
|
163
|
+
export declare const OIDC_DISCOVERY_RETRY_MS = 60000;
|
|
163
164
|
export declare function getRedoclyTokenPayload(token: string): Promise<Record<string, unknown>>;
|
|
164
165
|
export declare function exchangeTokenLoginCode(code: string): Promise<string | undefined>;
|
|
165
166
|
export declare function isRedoclySso(config: OidcProviderConfig): boolean;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import"../node-crypto-polyfill.js";import{DOMParser as j}from"@xmldom/xmldom";import{SignedXml as
|
|
1
|
+
import"../node-crypto-polyfill.js";import{DOMParser as j}from"@xmldom/xmldom";import{SignedXml as ne}from"xml-crypto";import re from"xpath";import{deflateSync as oe,inflateSync as se}from"fflate";import{createHash as ie}from"crypto";import{ulid as ae}from"ulid";import{AuthProviderType as p,DEFAULT_TEAM_CLAIM_NAME as U}from"@redocly/config";import{ALTERNATIVE_AUD_CLAIM_NAME as ce,AUTH_URL as V,JWT_SECRET_KEY as y,MAX_AUTH_TOKEN_COOKIE_BYTES as ue}from"../constants/common.js";import{ensureArray as O}from"../../utils/array/ensure-array.js";import{envConfig as de}from"../config/env-config.js";import{decryptClaimValue as le}from"../utils/crypto/decrypt-claim-value.js";import{decryptAesGcm as fe}from"../utils/crypto/decrypt-aes-gcm.js";import{encryptAesGcm as me}from"../utils/crypto/encrypt-aes-gcm.js";import{getPathPrefix as pe,withPathPrefix as he,withoutPathPrefix as ye}from"@redocly/theme/core/utils";import{ServerRoutes as K}from"../../constants/api.js";import{AuthCookieNames as I,DEFAULT_AUTHENTICATED_TEAM as Ae,REQUIRED_OIDC_SCOPES as $}from"../../constants/common.js";import{appendQueryParams as we}from"../../utils/url/append-query-params.js";import{parsePreviewUrl as B}from"../../utils/url/parse-preview-url.js";import{logger as E}from"../tools/notifiers/logger.js";import{OidcDiscoveryError as F}from"../utils/errors.js";import{randomString as _e}from"../utils/crypto/random-string.js";import{randomUUID as M}from"../utils/crypto/random-uuid.js";import{AlgorithmTypes as l,JwtTokenExpired as Se}from"./jwt/types.js";import*as m from"./jwt/jwt.js";import{InternalTokenTypes as g}from"./jwt/token-types.js";import{parseTeamClaimToArray as Te}from"../utils/index.js";import{parseTeamNameTemplate as ge}from"../utils/rbac-expand.js";import{arrayBufferToBase64 as xe,decodeBase64 as H,encodeBase64URL as Ie,urlSafeBase64 as z}from"./jwt/encode.js";import{formatSamlCertificate as Ee}from"./utils/format-saml-certificate.js";const ke=new Set(Object.values(g));function x(e){return e?.type===p.OIDC}function Re(e){return e?.type===p.SAML2}async function Ct(e,t){if(x(t))return Oe(e,t);if(Re(t))return Me(e,t)}async function Oe(e,t){const n=await X(e,t),r=new Set((t.scopes||[]).concat($)),o=t.authorizationRequestCustomParams||{};return{type:p.OIDC,idpId:e,name:"OAuth provider",authorizationEndpoint:n.authorization_endpoint,clientId:t.clientId,responseType:"code",scope:Array.from(r).join(" "),extraParams:o,pkce:t.pkce}}function Me(e,t){return{type:p.SAML2,idpId:e,name:"SAML2 provider",ssoUrl:t.ssoUrl,issuerId:t.issuerId,entityId:t.entityId||t.issuerId}}function W(e){return Array.from(new Set((e.scopes||[]).concat($))).join(" ")}async function J(e,t,n){return await fetch(e,{method:"POST",body:new URLSearchParams({client_id:t.clientId,...t.clientSecret?{client_secret:t.clientSecret}:{},...n}).toString(),headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"}}).then(r=>r.json())}async function bt(e,t,n,r,o={}){return await J(e,r,{scope:W(r),code:t,redirect_uri:q(n),grant_type:"authorization_code",...o})}async function vt(e,t,n){return await J(e,n,{scope:W(n),grant_type:"refresh_token",refresh_token:t})}function De(e,{authorizationEndpoint:t,clientId:n,responseType:r,scope:o,extraParams:s,idpId:i,pkce:c},d,h,u){if(!t||!n||!r||!o)return{loginUrl:void 0};const a=new URL(t),w=u?.redirectUriOverride??`${e}${he(K.OIDC_CALLBACK)}`,S={state:M(),idpId:i,redirectUri:w,redirectTo:d,branch:u?.branchOverride??be(e),inviteCode:h,source:u?.sourceOverride??"portal",uiLocales:u?.uiLocales},T={};if(c){const A=z(_e(50)),k=z(ie("sha256").update(A).digest("base64")),R="S256";a.searchParams.append("code_challenge",k),a.searchParams.append("code_challenge_method",R),T.code_verifier={value:A,options:{secure:!0,httpOnly:!0,expires:new Date(Date.now()+1e3*60*10),path:pe()||"/"}}}a.searchParams.append("client_id",n),a.searchParams.append("scope",o),a.searchParams.append("response_type",r),a.searchParams.append("redirect_uri",q(w)),a.searchParams.append("state",Ie(JSON.stringify(S))),u?.uiLocales&&a.searchParams.append("ui_locales",u.uiLocales);for(const A in s)s[A]!==void 0&&a.searchParams.append(A,s[A]);return{loginUrl:a.toString(),cookies:T}}function Pt(e,t,n,r){const o=new URL(e);return n&&(o.searchParams.append("post_logout_redirect_uri",t),r&&o.searchParams.append("state",r),o.searchParams.append("id_token_hint",n)),o.toString()}function Le(e,t){const n=O(t?.audience??[]);return!n.length||Y(e,n)}function Y(e,t){const n=[e.aud,e[ce]].flat();return t.some(r=>r!==""&&n.includes(r))}function Ne(e,t){return!t||!x(t)?!0:Le(e,t)&&Ce(e,t)}function Ce(e,t){return!x(t)||!Qe(t)||!t.clientId?!0:O(e.aud||[]).includes(t.clientId)}function jt(e){if(!e)return!1;try{return m.decode(e).header.alg===l.RS256}catch{return!1}}async function Ut(e){const t=Math.floor(Date.now()/1e3),n=t+(e.ttlSec??600);return m.sign({type:g.MCP_AUTHORIZATION_CODE,client_id:e.clientId,redirect_uri:e.redirectUri,id_token_enc:me(e.idToken,y),...e.idpId?{idp_id:e.idpId}:{},...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},...e.codeChallenge?{code_challenge:e.codeChallenge}:{},...e.codeChallengeMethod?{code_challenge_method:e.codeChallengeMethod}:{},iat:t,exp:n},y,l.HS256)}async function Vt(e){await m.verify(e,y,l.HS256);const{payload:t}=m.decode(e);if(t.type!==g.MCP_AUTHORIZATION_CODE)throw new Error("Invalid authorization code type");if(!t.client_id||!t.redirect_uri)throw new Error("Authorization code missing required claims");if(typeof t.exp=="number"&&Date.now()>=t.exp*1e3)throw new Error("Authorization code expired");if(typeof t.id_token_enc!="string")throw new Error("Authorization code missing session");return{...t,id_token:fe(t.id_token_enc,y)}}async function Kt(e){const t=Math.floor(Date.now()/1e3);return m.sign({type:g.MCP_REFRESH_TOKEN,client_id:e.clientId,...e.idpId?{idp_id:e.idpId}:{},claims:e.claims,...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},session_exp:e.sessionExp,iat:t,exp:e.sessionExp,jti:M()},y,l.HS256)}async function $t(e){await m.verify(e,y,l.HS256);const{payload:t}=m.decode(e);if(t.type!==g.MCP_REFRESH_TOKEN)throw new Error("Invalid refresh token type");if(!t.client_id||typeof t.claims!="object"||t.claims===null)throw new Error("Refresh token missing required claims");if(typeof t.session_exp!="number")throw new Error("Refresh token missing session expiry");return t}function Bt(e){const t=e||ae(),n=t.startsWith("mcp_")?t:`mcp_${t}`;return{id:n,object:"mcpSession",uri:`urn:redocly:realm:mcp:session:${n}`}}function q(e){const t=B(e);if(!t)return e;const n=ve(t.hostname);if(n)return t.hostname=`previewauth--${n.previewBranch}${n.after}`,G(e,t);const r=Q(t.hostname);return r?(t.hostname=`${r.projectSlug}.previewauth.${r.after}`,G(e,t)):e}function be(e){const t=B(e);if(t)return Q(t.hostname)?.previewBranch}function G(e,t){const n=e.replace(/^https?:\/\/[^/?#]+/i,"");return`${t.protocol}//${t.host}${ye(n)}`}function Q(e){const t=e.split(".preview.",2);if(t.length<2)return null;const[n,r]=t,o=n.indexOf("--");if(o===-1)return null;const s=n.slice(0,o),i=n.slice(o+2);return!s||!i||i.includes(".")?null:{projectSlug:s,previewBranch:i,after:r}}function ve(e){const t=e.indexOf("."),n=t===-1?e:e.slice(0,t);if(!n.startsWith("preview-"))return null;const r=n.indexOf("--");if(r===-1)return null;const o=n.slice(r+2);if(!o)return null;const s=t===-1?"":e.slice(t);return{previewBranch:o,after:s}}function Pe(e){return e.type===p.OIDC}function je(e){return e.type===p.SAML2}function Ft(e,t,n,r,o){return Pe(e)?De(t,e,n,r,{uiLocales:o}):je(e)?Ue(t,e,n,r,o):{}}function Ue(e,t,n,r,o){const i=`<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
|
|
2
2
|
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
|
|
3
3
|
Version="2.0"
|
|
4
4
|
ID="_${M()}"
|
|
5
5
|
IssueInstant="${new Date().toISOString()}"
|
|
6
|
-
AssertionConsumerServiceURL="${e}${
|
|
6
|
+
AssertionConsumerServiceURL="${e}${K.SAML_CALLBACK}"
|
|
7
7
|
AttributeConsumingServiceIndex="0">
|
|
8
8
|
<saml:Issuer>${t.entityId}</saml:Issuer>
|
|
9
9
|
<samlp:NameIDPolicy
|
|
10
10
|
AllowCreate="true"
|
|
11
11
|
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"/>
|
|
12
|
-
</samlp:AuthnRequest>`,c=
|
|
12
|
+
</samlp:AuthnRequest>`,c=Ve(i);return{loginUrl:we(t.ssoUrl,{SAMLRequest:c,RelayState:JSON.stringify({idpId:t.idpId,redirectTo:n,inviteCode:r,source:"portal",uiLocales:o})})}}function Ve(e){return xe(oe(new TextEncoder().encode(e)).buffer)}function Ht(e){const t=H(e);if(t.startsWith("<samlp:Response")||t.indexOf("<saml2p:Response")>-1)return t;const n=se(new Uint8Array(atob(e).split("").map(r=>r.charCodeAt(0))));return new TextDecoder().decode(n)}function zt(e){try{return JSON.parse(H(e||""))}catch{throw new Error("Invalid OAuth2 state")}}function Wt(e){const t=new j().parseFromString(e,"application/xml"),r=f(t,"//*[local-name(.)='StatusCode']/@Value")[0]?.nodeValue?.endsWith("Success")||!1,s=f(t,"//*[local-name(.)='Response']/@Destination")[0]?.nodeValue||"",i=f(t,"//*[local-name(.)='Assertion']//*[local-name(.)='Issuer']/text()")[0],c=i&&i.nodeValue||void 0,d=f(t,"//*[local-name(.)='Audience']/text()")[0],h=d&&d.nodeValue||void 0,a=f(t,"//*[local-name(.)='Assertion']//*[local-name(.)='X509Certificate']/text()")[0]?.nodeValue||"",w=f(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/text()")[0],S=w&&w.nodeValue||"",T=f(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/@Format")[0],A=T&&T.nodeValue||"",k=f(t,"//*[local-name(.)='Conditions']/@NotOnOrAfter")[0],R=Ke(k),N={},C=f(t,"//*[local-name(.)='AttributeStatement']//*[local-name(.)='Attribute']");if(C.length)for(const b of C){const v=f(b,"./@Name")[0];if(v.nodeValue){const P=f(b,"./*[local-name(.)='AttributeValue']/text()")[0];P?.nodeValue&&(N[v.nodeValue]=P.nodeValue)}}return{uid:S,success:r,expiresAt:R,issuerId:c,entityId:h,attrs:N,cert:a,nameFormat:A,destination:s}}function Ke(e){const t=typeof e?.nodeValue=="string"&&D(Date.parse(e.nodeValue)),n=D(Date.now()),r=D(Date.now()+720*60*1e3);return t?t>n&&t<r?r:t:n}function D(e){return Math.floor(e/1e3)}const L={},_={jwks:{},missedAt:{}},$e=6e4;function Be(e){const t=_.jwks[e];return t===void 0?!0:t===null&&Date.now()-(_.missedAt[e]??0)>=$e}async function X(e,t){if(!L[e]){const n=t.configurationUrl?await ee(t.configurationUrl):t.configuration;L[e]=Fe()?ze(n):n}return L[e]}function Fe(){const e=de.REDOCLY_ENFORCE_RESIDENCY;return!!e&&e.includes("host.docker.internal")}const He=["authorization_endpoint","end_session_endpoint"];function ze(e){if(typeof e!="object"||e===null)return e;const t={...e};for(const n of Object.keys(t)){if(He.includes(String(n)))continue;const r=t[n];typeof r=="string"&&r.includes("://localhost")&&(t[n]=r.replace("://localhost","://host.docker.internal"))}return t}const We=["kty","n","e","crv","x","y"];function Je(e,t){return We.every(n=>e[n]===t[n])}async function Ye(e){const t={};for(const n of Object.keys(e)){const r=e[n];if(!x(r))continue;const o=await X(n,r);if(o.jwks_uri){const s=await ee(o.jwks_uri);for(const i of s.keys){const c=t[i.kid],d=c&&Je(c,i)?[...c.idpIds,n]:[n];t[i.kid]={...i,idpIds:d},_.jwks[i.kid]=t[i.kid]}}}}function qe(e,t,n){const r=_.jwks[e]?.idpIds??[];return r.find(o=>{const s=n[o];if(!x(s))return!1;const i=O(s.audience??[]);return Y(t,i.length?i:[s.clientId])})??r.at(-1)}const Ge=6e4,Z={};async function ee(e){const t=Z[e];if(t&&Date.now()-t.failedAt<Ge)throw t.error;const n=await fetch(e,{headers:{Accept:"application/json"}}),r=n.ok?await n.json().catch(()=>{}):void 0;if(r===void 0){const o=new F(e,n.status,n.headers.get("content-type"));throw Z[e]={error:o,failedAt:Date.now()},o}return r}async function Jt(e){return fetch(`${V}/oidc/userinfo`,{headers:{Accept:"application/json",Authorization:`Bearer ${e}`}}).then(t=>t.status===200?t.json():void 0).catch(()=>{})}async function Yt(e){return fetch(`${V}/api/auth/organization-session/login-code/exchange`,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify({code:e})}).then(t=>t.status===200?t.json():void 0).then(t=>typeof t?.token=="string"?t.token:void 0).catch(()=>{})}function Qe(e){if(!e.configurationUrl)return!1;const t=new URL(e.configurationUrl);return["localhost","127.0.0.1","blueharvest.cloud","bhstage.cloud","cloud.redocly.com","beta.redocly.com","cloud.eu.redocly.com","beta.eu.redocly.com"].some(r=>Xe(t.hostname,r))}function Xe(e,t){return e===t||e.endsWith(`.${t}`)}async function qt(e,t){const n=new j().parseFromString(e,"application/xml"),r=f(n,"//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0];if(!r)throw new Error("Cannot find Signature in the SAML response");const o=Ee(t),s=new ne({publicCert:o});s.loadSignature(r);try{return s.checkSignature(e)}catch{return!1}}function Ze(e){const t=new Set,n=r=>{if(!(!r||typeof r!="object"||Array.isArray(r)))for(const[o,s]of Object.entries(r))typeof s=="string"?t.add(o):n(s)};return n(e),t}function et(e,t,n){if(!t)return e;const r=Ze(t),o=s=>{const i=n?.[s]||s;return r.has(i)||!!ge(t,i)};return[...e.filter(o),...e.filter(s=>!o(s))]}async function Gt(e,t,n,r){const o=n?e[n]:void 0,s=Array.isArray(o)?et(o.map(String),r?.rbacConfig,r?.teamsMapping):[];let i=s.length;const c=s.length+1;for(let d=0;d<c;d++){const h={...e,idpId:t};n&&s.length&&(h[n]=s.slice(0,i));const u=await m.sign(h,y,l.HS256),a=Buffer.byteLength(u)-ue;if(a<=0)return{token:u,teamsTotal:s.length,teamsKept:i,droppedTeams:s.slice(i)};if(i===0)return null;const w=Buffer.byteLength(JSON.stringify(s.slice(0,i))),S=Math.max(1,w/i*4/3);i=Math.max(0,i-Math.max(1,Math.ceil(a/S)))}return null}function Qt(e,t,n,r){t==="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"&&(e=n["http://schemas.microsoft.com/identity/claims/objectidentifier"]);let o;(t==="urn:oasis:names:tc:SAML:2.0:nameid-format:email"||t==="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")&&(o=e),t==="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"&&e?.match(/^.+@.+/m)&&(o=e);const s=n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],i=s?.match(/^.+@.+/m);return o=o||n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]||(i?s:void 0),o=o?.toLowerCase(),{sub:e,given_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"],family_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"],name:n["http://schemas.microsoft.com/identity/claims/displayname"]||s,email:o,email_verified:!0,teams:r?Te(n[r]):[]}}function te(e,t={}){return e.map(n=>t[n]||n)}async function Xt(e,t){if(!t)return{};const n=t[I.AUTHORIZATION];if(!n)return{};try{const r=m.decode(n);if(typeof r.payload.type=="string"&&ke.has(r.payload.type))return{};if(r.header.alg===l.RS256){const h=Be(r.header.kid);h&&await Ye(e);const u=_.jwks[r.header.kid];if(!u)return h&&(_.missedAt[r.header.kid]=Date.now()),_.jwks[r.header.kid]=null,{};await m.verify(n,u,l.RS256)}else await m.verify(n,y,l.HS256);const s=r.header.alg===l.RS256?qe(r.header.kid,r.payload,e):r.payload.idpId,i=e[s]||{};if(r.header.alg===l.RS256&&typeof r.payload.exp!="number")return E.warn("Rejected id_token: it has no exp claim"),{};if(r.header.alg===l.RS256&&!Ne(r.payload,i))return E.warn("Rejected id_token: it was issued for another project"),{};const c=rt(i),d=nt(i);return{...r.payload,email:r.payload.email?.toLowerCase(),idpId:s,teams:Array.from(new Set([...te(r.payload.teams||[],d),..."defaultTeams"in i&&i.defaultTeams||[],...te("teamsClaimName"in i&&r.payload[c||""]||[],d),Ae])),name:tt(r.payload),isAuthenticated:!0,idpAccessToken:le(r.payload.idp_access_token,y)||t[I.IDP_ACCESS_TOKEN],federatedAccessToken:t[I.FEDERATED_ACCESS_TOKEN],federatedIdToken:t[I.FEDERATED_ID_TOKEN],authCookie:n}}catch(r){r instanceof Se||(r instanceof F?E.error(r.message):E.error("Malformed JWT token: %s",r.message))}return{}}function tt(e){return(e.firstName&&e.lastName?`${e.firstName} ${e.lastName}`:e.name||e.given_name||e.firstName||e.lastName)||e.email}function nt(e){switch(e.type){case p.SAML2:return e.teamsAttributeMap;case p.OIDC:return e.teamsClaimMap;default:return}}function rt(e){switch(e.type){case p.SAML2:return e.teamsAttributeName??U;case p.OIDC:return e.teamsClaimName;default:return U}}function f(e,t){return re.select(t,e)||[]}export{Ge as OIDC_DISCOVERY_RETRY_MS,$e as UNKNOWN_KID_RETRY_MS,Ft as buildLoginUrl,De as buildOidcLoginUrl,Pt as buildOidcLogoutUrl,Ue as buildSAML2LoginUrl,Ut as createMcpAuthorizationCode,Kt as createMcpRefreshToken,Bt as createMcpSessionResource,Ht as decodeSamlResponse,Ye as discoverJwks,Ve as encodeSAML2,Yt as exchangeTokenLoginCode,Qt as extractUserClaims,Ct as getAuthProviderLoginParams,Oe as getOidcLoginParams,X as getOidcMetadata,Jt as getRedoclyTokenPayload,Me as getSaml2LoginParams,Xt as getUserParamsFromCookies,tt as getUsernameFromPayload,Le as idTokenAudienceIsAllowed,Ne as idTokenIsForThisProject,jt as isIdpIssuedToken,x as isOidcProviderConfig,Qe as isRedoclySso,Re as isSaml2ProviderConfig,bt as oidcExchangeCodeForToken,_ as oidcJwksCache,L as oidcMetadataCache,vt as oidcRefreshTokenGrant,et as orderTeamsRbacFirst,zt as parseOidcState,be as parsePreviewBranch,Wt as parseSamlResponse,q as rewritePreviewAuthRedirectUri,Gt as signTokenWithinCookieSizeLimit,Vt as verifyMcpAuthorizationCode,$t as verifyMcpRefreshToken,qt as verifySAMLResponse};
|