@redocly/reef 0.140.0-next.9 → 0.140.1
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 +166 -0
- package/dist/client/templates/api-docs/helpers.js +3 -3
- package/dist/compiled/svgo/svgo-node.js +1 -1
- package/dist/constants/common.d.ts +2 -0
- package/dist/constants/common.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/constants/entitlements.js +1 -1
- package/dist/server/entitlements/entitlements-provider.d.ts +4 -0
- package/dist/server/entitlements/entitlements-provider.js +1 -1
- package/dist/server/entitlements/plan-denied-markdoc-tags.d.ts +6 -0
- package/dist/server/entitlements/plan-denied-markdoc-tags.js +1 -0
- package/dist/server/esbuild/plugins/styled-components-ssr.js +1 -1
- package/dist/server/fs/fast-mtime.js +3 -3
- 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 +23 -23
- 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/compiler.d.ts +3 -3
- package/dist/server/plugins/markdown/compiler.js +1 -1
- package/dist/server/plugins/markdown/markdoc/markdoc-options.d.ts +2 -0
- package/dist/server/plugins/markdown/markdoc/markdoc-options.js +1 -1
- package/dist/server/plugins/mcp/custom-tools/scan-custom-tools.d.ts +4 -1
- package/dist/server/plugins/mcp/custom-tools/scan-custom-tools.js +1 -1
- package/dist/server/plugins/mcp/index.js +1 -1
- package/dist/server/plugins/mcp/types.d.ts +1 -0
- 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 +3 -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/dist/types/entitlements.d.ts +4 -0
- package/package.json +8 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
import R from"path";import{lintConfig as
|
|
1
|
+
import R from"path";import{lintConfig as j,loadConfig as E,createConfigTypes as L}from"@redocly/openapi-core";import{findEmptyAgentSection as S,rbacConfigSchema as q,rootRedoclyConfigSchema as x,withPlatformSection as B}from"@redocly/config";import{deepMerge as k}from"../../../../../utils/object/deep-merge.js";import{isPlainObject as v}from"../../../../../utils/guards/is-plain-object.js";import{logger as p}from"../../../../tools/notifiers/logger.js";import{BRANCH_ENV_PREFIX as I}from"../../../../../constants/common.js";import{sanitizeBranchName as M}from"../../../../utils/envs/sanitize-branch-name.js";import{envConfig as C}from"../../../../config/env-config.js";import{ajv as N,safeParsePartial as _}from"../../safe-parse.js";import{formatConfigProblem as D}from"../../format-error.js";import{ExternalResolver as F}from"../../../../fs/utils/external-ref-resolver.js";import{resolveMutuallyExclusiveProps as G}from"../../resolve-mutual-exclusion.js";function y(e,s,t,n){s in e&&e[s]&&typeof e[s]=="string"&&(/^https?:\/\/.*/.test(e[s])||n(new Error(`Invalid ${t} URL: "${e[s]}". ${t} must start with "http://" or "https://".`)))}function z(e,s){if("access"in e&&e.access&&typeof e.access=="object"){const t=e.access;y(t,"logoutReturnUrl","access.logoutReturnUrl",s),y(t,"residency","access.residency",s);const n=["requiresLogin","logoutReturnUrl","residency","sso","rbac"];for(const r of n)r in t&&t[r]!==void 0&&r in e&&e[r]!==void 0&&s(new Error(`Property '${r}' is defined both at root level and in 'access' object. Please use 'access.${r}' to define this configuration.`))}}function V(e){return N.compile(x.properties.graphql.properties.servers.items.properties.url)(e)}function A(e,s,t){if(v(e)&&Array.isArray(e.servers))for(const n of e.servers){const r=v(n)?n.url:void 0;if(typeof r=="string"&&!V(r)){const a=e.servers.indexOf(n),c=`${s}.servers[${a}].url`;t(new Error(`Invalid ${c}: "${r}". It must be an absolute http:// or https:// URL.`))}}}function Y(e,s){if(A(e.graphql,"graphql",s),!!v(e.apis))for(const[t,n]of Object.entries(e.apis))v(n)&&A(n.graphql,`apis.${t}.graphql`,s)}function H(e,s){const t=[["",e],...Object.entries(e.env??{}).map(([n,r])=>[`env.${n}.`,r])];for(const[n,r]of t){const a=S(r);a&&s(new Error(`Invalid ${n}${a}.agent: it must set at least one option.`))}}async function me(e,s,t,n){const a=s.getFileInfo(e)?.realRelativePath||e;async function c(){const f=new F(s),m=R.join(s.cwd,a),d=await E({configPath:m,externalRefResolver:f}),h=await n(d.resolvedConfig);if(h===void 0)return d.resolvedConfig;const g=d.document?.source,P=[...g?await j({config:d,externalConfigTypes:L(h,d)}):[],...g?G(d.resolvedConfig,g):[]];if(P.length>0)for(const U of P)t(new Error(D(U,s.cwd)));return d.resolvedConfig}const o=s.exists(e)?await c():{},l=Q(o),w=await n(o);y(o,"residency","Residency",t),y(o,"logoutReturnUrl","Logout return URL",t),z(o,t),Y(o,t),H(o,t);let u=w?_(w,o):o;u=W(u),u=J(u,t);const{env:b}=u;if(b){const f=C.REDOCLY_ENV,m=b[f]||{},d=C.PUBLIC_REDOCLY_BRANCH_NAME||"",h=d?M(d):"",g=h&&b[`${I}${h}`]||{};u=k(u,g,m)}const i=Z(u,l);if(i.imports&&i.imports.length>0){p.warn("The 'imports' property is deprecated. Please use 'plugins' property instead.");const f=new Set([...i.plugins||[],...i.imports.map(m=>R.posix.join(m,"plugin.js"))]);i.plugins=Array.from(f),delete i.imports}if(i.catalog&&(p.warn("The 'catalog' property is deprecated. Please use 'catalogClassic' property instead."),i.catalogClassic={...i.catalog},delete i.catalog),i.scorecard&&(p.warn("The 'scorecard' property is deprecated. Please use 'scorecardClassic' property instead."),i.scorecardClassic=i.scorecard,delete i.scorecard),i.search?.ai){p.warn("The 'search.ai' property is deprecated. Please use 'aiAssistant' property instead.");const f={...i.search?.ai,...i.aiAssistant,suggestions:i.aiAssistant?.suggestions?.length?i.aiAssistant.suggestions:i.search?.ai.suggestions||[]};i.aiAssistant=f,delete i.search.ai}return K(i)}function K(e){const s={...e},n={..."access"in e&&e.access&&typeof e.access=="object"?e.access:{}},r=["requiresLogin","logoutReturnUrl","residency","sso","idps","rbac"],a=[];for(const c of r){const o=s[c];o!==void 0&&(a.push(c),c==="requiresLogin"&&n.requiresLogin===void 0&&(n.requiresLogin=o),c==="logoutReturnUrl"&&n.logoutReturnUrl===void 0&&(n.logoutReturnUrl=o),c==="residency"&&n.residency===void 0&&(n.residency=o),c==="sso"&&n.sso===void 0&&(n.sso=o),c==="idps"&&n.idps===void 0&&(n.idps=o),c==="rbac"&&n.rbac===void 0&&(n.rbac=o),delete s[c])}return a.length>0&&p.warn(`The following properties at root level are deprecated: ${a.join(", ")}. Please move them to the 'access' object.`),Object.keys(n).length>0&&(s.access=n),s}function W(e){const s=Object.entries(e.env??{});[e,...s.map(([,r])=>r)].some(r=>r?.mcp?.docs)&&p.warn("The 'mcp.docs' property is deprecated. Please use the 'mcp' properties instead.");const t=e.mcp?{...e,mcp:$(e.mcp)}:e;if(!e.env)return t;const n=s.map(([r,a])=>[r,a?.mcp?{...a,mcp:$(a.mcp)}:a]);return{...t,env:Object.fromEntries(n)}}function $(e){const s=e.docs??{},t={};(e.hide??s.hide)!==void 0&&(t.hide=e.hide===!0||s.hide===!0);const n=e.name??s.name;n!==void 0&&(t.name=n);const r=e.ignore??s.ignore;r!==void 0&&(t.ignore=r);const a=e.publicEndpoint??s.publicEndpoint;return a!==void 0&&(t.publicEndpoint=a),{...e,...t,docs:{...s,...t}}}function X(e){if(!v(e)||e.platform===void 0)return!1;const s=Object.keys(e).filter(t=>t!=="platform");return s.length>0&&s.every(t=>!Object.hasOwn(q.properties,t))}function J(e,s){const t=Object.entries(e.env??{}),n=new Set;let r=!1;for(const o of[e,...t.map(([,l])=>l)])o?.reunite!==void 0&&n.add("The 'reunite' property is deprecated. Please use 'platform' property instead."),o?.reunite!==void 0&&o?.platform!==void 0&&n.add("Both 'platform' and 'reunite' are set. 'reunite' is ignored, so remove it."),[o?.rbac,o?.access?.rbac].some(l=>l?.reunite!==void 0)&&n.add("The 'access.rbac.reunite' property is deprecated. Please use 'access.rbac.platform' property instead."),[o?.rbac,o?.access?.rbac].some(X)&&(r=!0);for(const o of n)p.warn(o);r&&s(new Error("You are using an incorrect format of 'rbac:' configuration. See: https://redocly.com/docs/realm/access"));const a=T(e);if(!e.env)return a;const c=t.map(([o,l])=>[o,l&&T(l)]);return{...a,env:Object.fromEntries(c)}}function T(e){const s=B(e),t=e.rbac?{...s,rbac:O(e.rbac)}:s;return e.access?.rbac?{...t,access:{...e.access,rbac:O(e.access.rbac)}}:t}function O(e){const{reunite:s,...t}=e;return s===void 0?t:{...t,platform:e.platform??s}}function Q(e){if(!e.theme)return[];p.warn("The 'theme' property in redocly.yaml is deprecated. Please move all of the properties from 'theme' to the root of the config.");const s=[];for(const t of Object.keys(e.theme))e[t]==null?s.push(t):p.warn(`Detected both '${t}' and 'theme.${t}' properties in redocly.yaml. The 'theme.${t}' property will be ignored and needs to be removed or merged into the '${t}'.`);return s}function Z(e,s){if(!e.theme||s.length===0)return e;const t={...e};for(const n of s)t[n]=e.theme[n];return delete t.theme,t}export{K as ensureAccessBackwardsCompatibility,me as readAndValidateConfig};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{buildCodeSamples as v,getRequestBodySamples as d,normalizeOptions as f,resolveOperationSampleContext as S}from"@redocly/api-docs";import{setNodeAttributeValue as O}from"../../../../markdoc/helpers/set-node-attribute-value.js";import{loadOpenapiBundle as b}from"./helpers/load-openapi-bundle.js";import{getOptionalAttribute as t}from"./utils.js";const h={curl:"shell",JavaScript:"javascript","Node.js":"javascript",Python:"python",Java:"java","Java8+Apache":"java","C#":"csharp","C#+Newtonsoft":"csharp",PHP:"php",Go:"go",Ruby:"ruby",R:"r"};async function N(e,u,x,{context:g}){const o=await b(e,g);if(!o)return;const i=t(e,"language"),l=t(e,"options"),n={exampleKey:t(e,"exampleKey"),mimeType:t(e,"mimeType"),parameters:t(e,"parameters"),requestBody:t(e,"requestBody"),environments:t(e,"environments"),environment:t(e,"environment")},y={...o.rawOptions,...l,...o.rawOptions.mockServer&&{mockServer:{...o.rawOptions.mockServer,position:"off"}},codeSamples:{...o.rawOptions.codeSamples,...l?.codeSamples,...i&&{languages:[{lang:i}]}}},m=f(y),r=S(o.definition,m,{operationId:t(e,"operationId"),pointer:t(e,"pointer")});if(!r)return;const s=v(r,n).map(a=>({lang:h[a.lang]??a.lang
|
|
1
|
+
import{buildCodeSamples as v,getRequestBodySamples as d,normalizeOptions as f,resolveOperationSampleContext as S}from"@redocly/api-docs";import{setNodeAttributeValue as O}from"../../../../markdoc/helpers/set-node-attribute-value.js";import{loadOpenapiBundle as b}from"./helpers/load-openapi-bundle.js";import{getOptionalAttribute as t}from"./utils.js";const h={curl:"shell",JavaScript:"javascript","Node.js":"javascript",Python:"python",Java:"java","Java8+Apache":"java","C#":"csharp","C#+Newtonsoft":"csharp",PHP:"php",Go:"go",Ruby:"ruby",R:"r"};async function N(e,u,x,{context:g}){const o=await b(e,g);if(!o)return;const i=t(e,"language"),l=t(e,"options"),n={exampleKey:t(e,"exampleKey"),mimeType:t(e,"mimeType"),parameters:t(e,"parameters"),requestBody:t(e,"requestBody"),environments:t(e,"environments"),environment:t(e,"environment")},y={...o.rawOptions,...l,...o.rawOptions.mockServer&&{mockServer:{...o.rawOptions.mockServer,position:"off"}},codeSamples:{...o.rawOptions.codeSamples,...l?.codeSamples,...i&&{languages:[{lang:i}]}}},m=f(y),r=S(o.definition,m,{operationId:t(e,"operationId"),pointer:t(e,"pointer")});if(!r)return;const s=v(r,n).map(a=>({lang:h[a.lang]??a.lang?.toLowerCase(),title:a.label??a.lang,source:a.source})),c=m.codeSamples?.languages??[],p=c.findIndex(({lang:a})=>a==="Payload");if(p!==-1){const a=d(r,{exampleKey:n.exampleKey,mimeType:n.mimeType,titlePrefix:c[p].label});s.splice(p,0,...a)}s.length&&O(e,u,s)}export{N as resolveOpenapiCodeSamples};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Config, Node, ValidationType } from '@redocly/markdoc';
|
|
2
2
|
import type { Store } from '../../store.js';
|
|
3
|
-
import type { LifecycleContext, MarkdownParseInput, MarkdocResolveContext } from '../../types';
|
|
3
|
+
import type { LifecycleContext, MarkdownParseInput, MarkdocOptions, MarkdocResolveContext } from '../../types';
|
|
4
4
|
import type { MarkdocError } from '../../../types/index.js';
|
|
5
5
|
export type ResolvableType = ValidationType & {
|
|
6
6
|
resolver?: string;
|
|
@@ -31,11 +31,11 @@ type MarkdocInfo = {
|
|
|
31
31
|
codeSnippetLanguages?: Record<string, number>;
|
|
32
32
|
codeWalkthroughLanguages?: Record<string, number>;
|
|
33
33
|
};
|
|
34
|
-
export declare function resolveAndValidateMarkdoc(parsedAstChunk: ParsedAstChunk, markdocOpts:
|
|
34
|
+
export declare function resolveAndValidateMarkdoc(parsedAstChunk: ParsedAstChunk, markdocOpts: MarkdocOptions | undefined, context: MarkdocResolveContext): Promise<MarkdocInfo>;
|
|
35
35
|
export declare function getAst(relativePath: string, rawContent: string): ParsedAstChunk;
|
|
36
36
|
export declare function extractMdFirstHeading(relativePath: string, context: LifecycleContext): Promise<string | undefined>;
|
|
37
37
|
export declare function validateAllMarkdowns(store: Store, context?: LifecycleContext): Promise<void>;
|
|
38
|
-
export declare function parseAndResolveMarkdoc(input: MarkdownParseInput, markdocOpts:
|
|
38
|
+
export declare function parseAndResolveMarkdoc(input: MarkdownParseInput, markdocOpts: MarkdocOptions | undefined, rootContext: MarkdocResolveContext, resource?: string): Promise<import("../../types").LoadResult<{
|
|
39
39
|
ast: Node;
|
|
40
40
|
info: MarkdocInfo;
|
|
41
41
|
}>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import N from"@redocly/markdoc";import{htmlTags as
|
|
1
|
+
import N from"@redocly/markdoc";import{htmlTags as $}from"@redocly/markdoc/html";import*as P from"path";import{convertGithubAlertToAdmonition as A}from"@redocly/theme/markdoc/tags/admonition";import{logger as H}from"../../tools/notifiers/logger.js";import{reporter as F}from"../../tools/notifiers/reporter.js";import{sha1 as B}from"../../utils/crypto/sha1.js";import{formatMarkdocError as G}from"./errors.js";import{extractFirstHeading as C,SKIP as j,visit as K}from"./markdoc/plugins/utils.js";import{isPromptNode as U}from"../../../markdoc/helpers/guards/is-prompt-node.js";import{attributeResolvers as L}from"./attribute-resolvers/index.js";import{MdResolveError as V}from"./attribute-resolvers/md-resolve-error.js";import{normalizeHtmlHeadings as q}from"./html/normalize-html-headings.js";import{resolveRawPartials as J}from"./markdoc/resolve-raw-partials.js";import{exampleContentTrees as Q,reparseExampleFences as X}from"./markdoc/reparse-example-fences.js";import{MARKDOC_PARTIALS_DEPS_KEY as Z}from"../../constants/common.js";import{getLinkOriginalAttrName as tt}from"./attribute-resolvers/utils.js";import{createSourceLinesReader as at}from"./create-source-lines-reader.js";const et="EMPTY";async function rt(a,e,r){const{actions:n}=r,{relativePath:s,resolveErrors:D}=a,u={...r,readSourceLines:at(a.rawContent)},k=new Set,d=new Set,w=new Set,c=new Set,g={},m=new Set,b={},M={};function S(t,o){const E=o||et;t[E]=(t[E]||0)+1}const _=[];a.resolveErrors.length=0;const T=Q(a.ast),I=t=>{A(t);const o=E();_.push(o.then(y=>{y?.sharedDataIds&&y.sharedDataIds.forEach(f=>k.add(f))}).catch(Y(t)));async function E(){const y=[];switch(t.type){case"fence":S(b,t.attributes.language);break;case"link":case"image":await L.nativeMdLink(t,t.type==="link"?"href":"src",s,u);break;case"tag":{if(!t.tag)break;const f=e?.tags?.[t.tag];if(!f){const i=e?.planDeniedTags?.[t.tag];i&&(t.addError({level:"warning",id:"invalid-tag",message:i}),t.tag="",t.type="error",t.attributes={});break}const v=t.tag==="html"?t.attributes.name:t.tag;if((t.tag==="html"?m:c).add(v),g[v]=(g[v]||0)+1,v==="code-snippet"&&S(b,t.attributes.language),f.attributes?.__idx&&(t.attributes.__idx=g[t.tag]),f.dynamicComponentLib&&d.add(f.dynamicComponentLib),!f.attributes)break;if(v==="partial"&&t.attributes?.file){const i=tt("file"),l=t[i]||t.attributes.file,h=l.startsWith("/")?l.slice(1):P.posix.normalize(P.posix.join(P.posix.dirname(s),l));w.add(h);const p=(n.getGlobalConfig(Z)||{})[h];if(p?.dynamicComponents)for(const R of p.dynamicComponents)d.add(R);if(p?.sharedDataDeps)for(const R of p.sharedDataDeps)k.add(R)}for(const[i,l]of Object.entries(f.attributes)){const h=l.type;if(h&&(h.resolver||l.resolver)){const O=L[h.resolver||l.resolver],p=await O?.(t,i,s,u);p?.sharedDataId&&y.push(p.sharedDataId)}}if(v==="code-walkthrough")for(const i of t.attributes.resolvedFilesets||[])for(const l of i.files||[])S(M,l.language);const x=t.attributes?.attrs;if(x&&t.tag==="html")for(const i of Object.keys(x))i==="src"||i==="srcSet"?await L.htmlSourceAttribute(t,i,s,u):i==="href"&&await L.htmlHref(t,i,s,u);break}}return{sharedDataIds:y}}};function Y(t){return async o=>{if(!(o instanceof V)){await F.panicOnBuild(o.message);return}D.push(G({type:"resolve_link",meta:{...o.meta,link:o.meta.link??o.meta.rawLink},lines:t.location?[t.location.start.line,t.location.end.line]:[],location:t.location,error:{id:"",level:"error",message:o.message,location:t.location}},a.relativePath,a.rawContent))}}K(a.ast,t=>(I(t),U(t)?j:void 0));for(const t of T)K(t,o=>(A(o),o.type==="tag"&&I(o),U(o)?j:void 0));return await Promise.all(_),a.sharedDataDeps=k,a.dynamicMarkdocComponents=d,a.validatedAtRevision<n.buildRevision&&(a.markdocErrors=[a.ast,...T].flatMap(t=>N.validate(t,e)).map(t=>G(t,s,a.rawContent)),a.validatedAtRevision=n.buildRevision),{sharedDataDeps:a.sharedDataDeps,dynamicMarkdocComponents:Array.from(a.dynamicMarkdocComponents?.values()??[]),partials:Array.from(w),tagList:Array.from(c),htmlTagsList:Array.from(m),tagOccurrence:g,codeSnippetLanguages:b,codeWalkthroughLanguages:M}}const z="unclosed-html",ot={html:$({errorTag:z}),allowIndentation:!0,allowComments:!0};function W(a,e){const r=N.parse(a,{...ot,...e});return it(r),q(r),r}function it(a){for(const e of a.walk())e.type!=="tag"||e.tag!==z||(e.type="error",e.errors.push({id:"parse-error",level:"critical",message:String(e.attributes.message),location:e.location}))}function st(a,e){const r=W(e,{file:a,slots:!0});return X(r,s=>W(s,{file:a})),{ast:r,rawContent:e,resolveErrors:[],markdocErrors:[],relativePath:a,sharedDataDeps:void 0,validatedAtRevision:-1}}async function St(a,e){const{data:r}=await e.cache.load(a,"markdown-ast");return C(r.ast)}async function Rt(a,e){F.clearMarkdocProblems();const r=a.getAllRoutes();let n=0;const s=H.isInteractive();for(const D of r)await a.resolveRouteStaticData(D,e,!0),n++,s&&n%100===0&&H.logInFooter("validate",` \u{1F50D} Status: validating markdoc (${n}/${r.length})`)}async function Pt(a,e,r,n){const s=a.content?B(a.content):"";return await r.context.cache.load(n||a.relativePath,{loader:u,name:"markdown-inline-parser"},[s,String(r.actions.buildRevision)]);async function u(k,d,w){let c;if(a.content){const m=await d.getConfig(),b=await J(a.content,a.relativePath,m.markdown?.partialsFolders,d);c=st(a.relativePath,b)}else c=(await d.cache.load(a.relativePath,"markdown-ast")).data;let g={};if(r.actions.buildRevision!==0&&(g=await rt(c,e,r)),!a.isVirtual){for(const m of c.markdocErrors)w(m);for(const m of c.resolveErrors)w(m)}return{ast:c.ast,info:g}}}export{St as extractMdFirstHeading,st as getAst,Pt as parseAndResolveMarkdoc,rt as resolveAndValidateMarkdoc,Rt as validateAllMarkdowns};
|
|
@@ -50,5 +50,7 @@ export declare function getMarkdocOptions(serverOutDir?: string, _templateId?: s
|
|
|
50
50
|
}): string;
|
|
51
51
|
};
|
|
52
52
|
};
|
|
53
|
+
/** The tags the project schema defines, under the names pages use them by; built-in names included. */
|
|
54
|
+
userTagNames: string[];
|
|
53
55
|
}>;
|
|
54
56
|
//# sourceMappingURL=markdoc-options.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import f from"@redocly/markdoc";import*as
|
|
1
|
+
import f from"@redocly/markdoc";import*as g from"@redocly/theme/markdoc/default";import{reporter as p}from"../../../tools/notifiers/reporter.js";import*as u from"../../../../markdoc/nodes/index.js";import d from"../../../../markdoc/tags/index.js";import{normalizeCustomTags as l}from"../../../../markdoc/helpers/normalize-custom-tags.js";import{includes as h}from"./functions/includes.js";import{importUserTags as O}from"./import-user-tags.js";import{concat as y}from"./functions/concat.js";const T=g.tags;function b(r){const{tags:c,functions:o={},...n}=r,a={...c,...n},i=Object.entries(a),t=[],s=[];for(const[e,m]of i)e in f.nodes?t.push([e,m]):s.push([e,m]);return{nodes:Object.fromEntries(t),tags:Object.fromEntries(s),functions:o}}async function S(r="../../../esbuild/cache/server/",c=""){let o;try{o=(await O(r)).schema}catch(e){throw e.code==="ERR_MODULE_NOT_FOUND"?(p.printErrors(),new Error("Please fix the errors above and try again.")):e}const{tags:n={},nodes:a={},functions:i={}}=b(o),t=l(n),s={tags:{schemaDefinition:{render:"SchemaDefinition",attributes:{schemaRef:{type:String},exampleRef:{type:String},showReadOnly:{type:Boolean},showWriteOnly:{type:Boolean}}}}};return{nodes:{...u,...a},tags:{...d,...T,...t,...s.tags},functions:{...f.functions,...i,includes:h,concat:y},userTagNames:Object.keys(t)}}export{S as getMarkdocOptions};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { LifecycleContext, ProcessContentActions } from '../../../types';
|
|
2
2
|
import type { CustomMcpToolsState } from '../types.js';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Registers every file in an `@mcp` folder as one MCP tool, named after the file. Without custom
|
|
5
|
+
* MCP tools in the plan, each file is a warning and no tool.
|
|
6
|
+
*/
|
|
4
7
|
export declare function scanCustomMcpTools(actions: ProcessContentActions, context: LifecycleContext, state: CustomMcpToolsState): Promise<number>;
|
|
5
8
|
export declare function resetCustomMcpTools(actions: Pick<ProcessContentActions, 'removeMcpTools'>, state: CustomMcpToolsState): void;
|
|
6
9
|
//# sourceMappingURL=scan-custom-tools.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{basename as
|
|
1
|
+
import{basename as p,join as f}from"node:path";import{logger as d}from"../../../tools/notifiers/logger.js";import{reporter as i}from"../../../tools/notifiers/reporter.js";import{EntitlementsProvider as h}from"../../../entitlements/entitlements-provider.js";import{CODE_MODE_TOOL_NAMES as T}from"../codemode/constants.js";import{ALL_DEFAULT_TOOL_NAMES as M}from"../docs-mcp/tool-schemas.js";import{extractToolDefinition as O}from"./extract-tool-definition.js";const E=/(^|\/)@mcp\/.*\.(ts|js)$/,C=/^[a-zA-Z0-9_-]{1,64}$/,P=new Set([...T,...M]);async function b(t,r,n){_(t,n);const s=r.fs.scan(E).filter(e=>!e.isVirtual&&!e.relativePath.endsWith(".d.ts")).map(e=>e.relativePath);if(!h.instance().canUseCustomMcpTools()){for(const e of s)n.warnedPlanIgnoredFiles.has(e)||(n.warnedPlanIgnoredFiles.add(e),d.warn("Custom MCP tools are not in this project's plan, so %rp is ignored.",e));return 0}const u=new Set(t.getMcpTools().map(e=>e.name)),l=new Map;for(const e of s){const o=p(e).replace(/\.(ts|js)$/,"");if(!C.test(o)){await i.panicOnBuildContentError(`Invalid MCP tool file "${e}": the tool name "${o}" is derived from the file name and may only contain letters, numbers, hyphens, and underscores (max 64 characters). Rename the file.`);continue}const c=l.get(o);if(c){await i.panicOnBuildContentError(`Duplicate MCP tool name "${o}": defined by both "${c}" and "${e}". Tool names are file names, so each file name must be unique across @mcp folders.`);continue}if(P.has(o)||u.has(o)){await i.panicOnBuildContentError(`Invalid MCP tool file "${e}": the tool name "${o}" is already used by a built-in MCP tool. Rename the file.`);continue}l.set(o,e);const m=f(t.contentDir,e),a=await O({toolName:o,absoluteSourcePath:m,contentDir:t.contentDir,serverOutDir:t.serverOutDir});if(!a.ok){await i.panicOnBuildContentError(`Invalid MCP tool file "${e}": ${a.errors.join(" ")}`);continue}!a.schema.outputSchema&&!n.warnedNoOutputSchemaTools.has(o)&&(n.warnedNoOutputSchemaTools.add(o),d.warn(`MCP tool "${o}" in %rp declares no \`outputSchema\`. Add one that describes the returned value.`,e)),t.addMcpTools(m,[a.schema]),n.registeredToolNames.push(o)}return n.registeredToolNames.length}function _(t,r){t.removeMcpTools(r.registeredToolNames),r.registeredToolNames=[]}export{_ as resetCustomMcpTools,b as scanCustomMcpTools};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{fileURLToPath as U}from"node:url";import{dirname as
|
|
1
|
+
import{fileURLToPath as U}from"node:url";import{dirname as F,join as D}from"node:path";import{MCP_ROUTE as $,PUBLIC_MCP_ROUTE as M}from"@redocly/theme/core/constants/mcp";import{logger as p}from"../../tools/notifiers/logger.js";import{reporter as k}from"../../tools/notifiers/reporter.js";import{isDefined as B}from"../../../utils/guards/is-defined.js";import{MCP_CREDENTIALS_HANDLER_ID as P,MCP_CREDENTIALS_ROUTE as V,MCP_DOCS_SERVER_HANDLER_ID as T,MCP_NOT_ALLOWED_TEMPLATE_ID as x}from"../../constants/mcp.js";import{bundledOpenApiDocumentsToMcpOpenApi as J,bundledGraphqlDocumentsToMcpOpenApi as W,buildGraphqlDescriptions as X,collectIgnoredRoutes as z,filterIgnoredApiDescriptions as A,getCleanedUpApiDescriptions as K,isMcpInRedirects as Q}from"./utils.js";import{getMcpMode as R,isMcpElicitationEnabled as Y,isMcpGatewayEnabled as Z}from"./utils/mode.js";import{getPublicMcpEndpointStatus as S}from"./utils/public-endpoint.js";import{findGatedToolNames as ee}from"./utils/find-gated-tool-names.js";import{getDefaultToolSchemas as oe}from"./docs-mcp/tool-schemas.js";import{EXECUTE_TOOL_SCHEMA as te}from"./codemode/tools/execute-schema.js";import{isGatewayCapabilityEnabled as ie,isGatewayEligible as re}from"./gateway-mcp/eligibility.js";import{resolveGatewayFetchLimits as se}from"./gateway-mcp/rate-limit/fetch-limiter/fetch-limits.js";import{resetCustomMcpTools as ne,scanCustomMcpTools as ae}from"./custom-tools/scan-custom-tools.js";import{resolveSiteBranding as ce}from"./handlers/credentials-page/site-branding.js";import{telemetryTraceStep as pe}from"../../../cli/telemetry/helpers/trace-step.js";import{DEFAULT_DOCS_MCP_SERVER_NAME as _}from"./constants.js";const b=F(U(import.meta.url));async function de(){let i;const m={registeredToolNames:[],warnedNoOutputSchemaTools:new Set,warnedPlanIgnoredFiles:new Set};let u=0,f=!1;return{id:"mcp",async processContent(e,a){await pe("com.redocly.build.plugin.mcp",async s=>{const t=await a.getConfig(),{mcp:r,redirects:g}=t;if(s?.setAttribute("config",JSON.stringify(r||{})),s?.setAttribute("mcpMode",R()),s?.setAttribute("mcpGateway",String(Z())),r?.hide||r?.docs?.hide){ne(e,m);return}Q(g)&&await k.panicOnBuildContentError('The reserved route "/mcp" cannot be used in redirect configuration. Please remove any redirects involving "/mcp" from your redocly.yaml config.'),e.createTemplate(x,"@redocly/theme/layouts/McpNotAllowed");const l=!!t?.access?.requiresLogin,C=t?.access?.rbac||{},d=R(),c=Y(),{budgetPerExec:h}=se();if(!i){p.info("Configuring MCP servers...");const w=J(await e.loadApiDocuments(a)),O=W(await e.loadApiDocuments(a)),v=r?.docs?.ignore||[],L=K(A(w,v)),q=ie(r),E=new Map;for(const o of L){const H=o.definition.info?.title||"",j=o.definition.info?.version||"";E.set(o.relativePath,{name:H,description:typeof o.definition.info?.description=="string"?o.definition.info.description:"",version:j,servers:o.definition.servers||[],relativePath:o.relativePath,...q&&re(o.definition.info)?{fetchEligible:!0}:{}})}const y=X(A(O,v)),I=Object.values(t.products||{}).map(o=>o?.name).filter(B),G=t.l10n?.locales?.map(o=>o.code)??a.fs.localeFolders,N=oe({products:I,locales:G,requiresLogin:l,rbac:C,hasGraphqlApis:y.length>0,elicitationEnabled:c,hasCallableApis:E.values().some(o=>o.fetchEligible),feedbackHidden:t.feedback?.hide===!0});e.addMcpTools(D(b,"./docs-mcp/tools/index.js"),N),d==="code"&&e.addMcpTools(D(b,"./codemode/tools/execute.js"),[te]),i={apiDescriptionsMap:Object.fromEntries(E),graphqlDescriptions:y}}const n=await ae(e,a,m);s?.setAttribute("customToolsCount",String(n)),n!==u&&(u=n,n>0&&p.info(`Registered ${n} custom MCP tool${n===1?"":"s"} from @mcp folders`)),await le({actions:e,handlerPath:D(b,"./handlers/docs-mcp-handler.js"),apiDescriptionsMap:i.apiDescriptionsMap,graphqlDescriptions:i.graphqlDescriptions,mcpConfig:r,mcpMode:d,elicitationEnabled:c,gatewayFetchBudgetPerExec:h,publicEndpointStatus:S(t)}),f||(p.info("Registered Docs MCP Server endpoint at /mcp"),f=!0)})},async afterRoutesCreated(e,a){const s=await a.getConfig(),{mcp:t}=s,r=!t?.hide&&!t?.docs?.hide;e.markGatedMcpTools(await ee(e.getMcpTools())),e.setGlobalData({mcpData:{docs:{enabled:r,name:t?.docs?.name||_,publicEndpoint:S(s)==="served"}}})}}}var Oe=de;async function le({actions:i,handlerPath:m,apiDescriptionsMap:u,graphqlDescriptions:f,mcpConfig:e,mcpMode:a,elicitationEnabled:s,gatewayFetchBudgetPerExec:t,publicEndpointStatus:r}){i.createRequestHandler(T,m);const g=l=>async(C,d)=>{const c=z(d.getAllRoutes(),e?.docs?.ignore||[]);return c.length>0&&p.verbose(`mcp.docs.ignore: hiding ${c.length} page routes from MCP search`),{props:{config:{apiDescriptionsMap:u,graphqlDescriptions:f,mcpDocsServerName:e?.docs?.name||_,mcpMode:a,elicitationEnabled:s,gatewayFetchBudgetPerExec:t,searchIgnoredRoutes:c,...l?{isPublicEndpoint:l}:{}},serverOutDir:i.serverOutDir,tools:i.getMcpTools().map(({importPath:h,...n})=>n)}}};if(i.addApiRoute({slug:$,requestHandlerId:T,httpMethod:"all",getStaticData:g(!1)}),r==="no-protected-content"?p.info(`mcp.docs.publicEndpoint is enabled, but the project has no protected content - /mcp already serves anonymous users, skipping ${M}.`):r==="anonymous-denied"?p.info(`mcp.docs.publicEndpoint is enabled, but anonymous users are denied the MCP feature (rbac.features.mcp or requiresLogin) - skipping ${M}.`):r==="served"&&(i.addApiRoute({slug:M,requestHandlerId:T,httpMethod:"all",getStaticData:g(!0)}),p.verbose(`Registered public Docs MCP Server endpoint at ${M}`)),s){const l=D(b,"./handlers/mcp-credentials-handler.js");i.createRequestHandler(P,l),i.addApiRoute({slug:V,requestHandlerId:P,httpMethod:"all",getStaticData:async(C,d)=>{const{seo:c,colorMode:h}=d.getConfig(),{logo:n}=d.getGlobalData();return{props:{branding:ce({seo:c,logo:n,colorMode:h})}}}}),p.verbose("Registered MCP credentials endpoint at /mcp/credentials")}}export{Oe as default,de as mcpServerPlugin};
|
|
@@ -143,6 +143,7 @@ export type McpToolWorkerParams = Omit<McpToolExecutionParams, 'context'> & {
|
|
|
143
143
|
export type CustomMcpToolsState = {
|
|
144
144
|
registeredToolNames: string[];
|
|
145
145
|
warnedNoOutputSchemaTools: Set<string>;
|
|
146
|
+
warnedPlanIgnoredFiles: Set<string>;
|
|
146
147
|
};
|
|
147
148
|
export type ToolDefinitionValidationResult = {
|
|
148
149
|
ok: true;
|
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 T from"@redocly/markdoc";import{writeFileSync as R}from"node:fs";import{readFile as S}from"node:fs/promises";import{join as g}from"node:path";import{parseAndResolveMarkdoc as M}from"./plugins/markdown/compiler.js";import{getPathnameForLocale as k}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as m}from"../constants/common.js";import{DEFAULT_TITLE as L,MARKDOC_PARTIALS_DATA_KEY as y,MARKDOC_PARTIALS_DEPS_KEY as F}from"./constants/common.js";import{isObject as H}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 G}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as d}from"../utils/path/normalize-route-slug.js";import{isLocalLink as v}from"../utils/path/is-local-link.js";import{reporter as E}from"./tools/notifiers/reporter.js";import{logger as u}from"./tools/notifiers/logger.js";import{sha1 as _}from"./utils/crypto/sha1.js";import{envConfig as B,setEnv as I}from"./config/env-config.js";import{KvService as j}from"./persistence/kv/services/kv-service.js";import{SandboxContext as V}from"./utils/sandbox-context.js";import{isAllowedEnv as q}from"./utils/envs/get-api-route-allowed-env-variables.js";import{ensureDir as J,writeSharedData as U}from"./utils/index.js";import{renderComponents as x}from"./ssr/render.js";import{readStaticData as K,writeStaticData as W}from"./utils/static-data.js";import{getMarkdocOptions as $}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as z}from"./entitlements/entitlements-provider.js";import{getPlanDeniedMarkdocTags as Y}from"./entitlements/plan-denied-markdoc-tags.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 P={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"},C="store.json",A="store-config.json",b="PLAN_GATES",ne=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],Ue="userDefinedApiFunctions";class D{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;#i={};config=ee();configFile;#n;serverMode;serverOutDir;outdir;buildRevision=0;hasSitemap=!1;compilationErrors=[];#l;userCodeReady;#s=Promise.resolve();#c;#d=Promise.resolve();#h;#u=Promise.resolve();#f;#e=!1;#r=0;#p=0;#a=new Map;constructor({outdir:e,contentDir:t,serverMode:s=!1,serverOutDir:r}){this.#n=t,this.outdir=e,this.serverMode=s,this.serverOutDir=r,V.register({serverOutDir:r,readRoots:[e,process.cwd()]}),this.userCodeReady=new Promise(a=>{this.#l=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.#c=e})}waitForPluginsLifecycle(){return Promise.all([this.#s,this.#d,this.#u])}waitForPluginsRun(){return this.#s}finishPluginsRun(){this.#c?.();for(const e of this.#a.values())this.runListeners(...e);this.#a.clear()}startEsbuildRun(){this.#d=new Promise(e=>{this.#h=e})}finishEsbuildRun(){this.#h?.()}markRequestHandlersEntryOutdated(){this.#r++,!this.#e&&(this.#e=!0,this.#u=new Promise(e=>{this.#f=e}))}noteRequestHandlersEntryBuildStart(){this.#p=this.#r}markRequestHandlersEntryUpdated(){this.#e&&(this.#r>this.#p||(this.#e=!1,this.#f?.()))}get contentDir(){if(this.serverMode)throw new Error("contentDir should not be used in server mode");return this.#n}markUserCodeReady(){this.#l?.(!0)}async reloadMarkdocOptions(){await ie("com.redocly.build.reloadMarkdocOptions",async()=>{const{userTagNames:e,...t}=await $(this.serverOutDir),s=Y(e),r=Object.fromEntries(Object.entries(t.tags).filter(([a])=>!(a in s)));this.#o={...t,tags:r},this.#i=s})}get markdocOptions(){return{...this.#o,partials:this.getGlobalConfig(y),themeConfig:this.config.markdown,planDeniedTags:this.#i}}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()=>j.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:e,context:t,deps:s,resource:r})=>{const{data:{info:a,ast:o},compoundHash:l}=await M(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(!z.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(v(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??_(r);return this.#t[e]===a||(this.#t[e]=a,await U(e,r,this.outdir),this.queueEvent("shared-data-updated",e)),e};addRouteSharedData=(e,t,s)=>{const r=G(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(F)||{},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=k(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(!v(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&&W(e.slug,s,this.outdir)}async resolveRouteStaticData(e,t,s){if(this.serverMode)return K(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:L,...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(""):x(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:[]}};#g(){const e=[];for(const[s,r]of Object.entries(P))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[b]=B.PLAN_GATES,t}static#m(e,t){const s=new D(t);for(const[a,o]of Object.entries(P))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[b];return r&&I("PLAN_GATES",r),s}async writeServerFiles(){J(this.serverOutDir),R(g(this.serverOutDir,C),JSON.stringify(this.#g())),R(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 S(t,"utf8")));s[y]=le(s[y]);const a=JSON.parse(await S(g(e.serverOutDir,C),"utf8")),o=D.#m(a,e);o.config=s;const l=Object.fromEntries(Object.entries(r).filter(([i])=>q(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||!H(l)&&!Array.isArray(l)){await E.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 E.panicOnBuildContentError(t)}}function le(O){return N(O,e=>T.Ast.fromJSON(JSON.stringify(e)))}export{D 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
|
|
|
@@ -31,6 +31,8 @@ export type ResolveItemsOptions = {
|
|
|
31
31
|
};
|
|
32
32
|
export type MarkdocOptions = Partial<Omit<MarkdocConfigProps, 'relativePath'>> & {
|
|
33
33
|
themeConfig?: RedoclyConfig['markdown'];
|
|
34
|
+
/** The tags the project's plan lacks, each with the message a page shows in place of the tag. */
|
|
35
|
+
planDeniedTags?: Record<string, string>;
|
|
34
36
|
};
|
|
35
37
|
export type GetStaticDataContext = {
|
|
36
38
|
getRouteByFsPath: (relativePath: string) => PageRouteDetails<PageStaticData, PageProps> | undefined;
|
|
@@ -201,6 +203,7 @@ export type AfterRoutesCreatedActions = {
|
|
|
201
203
|
getSearchFacets: () => Map<string, SearchFacet>;
|
|
202
204
|
setSearchEngine: (engine: SearchEngine) => void;
|
|
203
205
|
addRedirect: (from: string, to: RedirectConfig, options?: RedirectOptions) => void;
|
|
206
|
+
addRouteAlias: (from: string, to: string) => void;
|
|
204
207
|
contentDir: string;
|
|
205
208
|
outdir: string;
|
|
206
209
|
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};
|