@redocly/reef 0.141.0-next.0 → 0.141.0-next.2
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 +33 -0
- 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/markdoc/nodes/table.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/fs/fast-mtime.js +3 -3
- package/dist/server/plugins/api-docs/index.js +1 -1
- package/dist/server/plugins/api-docs/search/get-ai-search-documents.js +64 -64
- package/dist/server/plugins/config-parser/format-error.js +2 -2
- package/dist/server/plugins/config-parser/loaders/utils/read-and-validate-config.js +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-code-snippet-from-file.js +7 -6
- package/dist/server/plugins/markdown/compiler.d.ts +3 -3
- package/dist/server/plugins/markdown/compiler.js +1 -1
- package/dist/server/plugins/markdown/index.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/plugins/pages/index.js +1 -1
- package/dist/server/store.d.ts +1 -0
- package/dist/server/store.js +1 -1
- package/dist/server/types/plugins/common.d.ts +11 -1
- package/dist/server/web-server/routes/helpers/process-redirects.d.ts +6 -4
- package/dist/server/web-server/routes/helpers/process-redirects.js +1 -1
- package/dist/server/web-server/routes/page-data.js +1 -1
- package/dist/types/entitlements.d.ts +4 -0
- package/package.json +6 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{getLineColLocation as u,isAbsoluteUrl as g,getCodeframe as l}from"@redocly/openapi-core";import m from"node:path";import{gray as i}from"../../tools/notifiers/helpers/colors.js";function
|
|
1
|
+
import{getLineColLocation as u,isAbsoluteUrl as g,getCodeframe as l}from"@redocly/openapi-core";import m from"node:path";import{gray as i}from"../../tools/notifiers/helpers/colors.js";function v(a,s){return`Config validation ${a.severity==="warn"?"warning":"error"}:
|
|
2
2
|
|
|
3
3
|
`+c(a);function c(o){const t=o.location[0];if(!t)return o.message;if(!t.source){const n=t.pointer??"";return`${o.message}${n?` at ${n}`:""}`}const e=t.source?.absoluteRef,r=e&&g(e)?e:e?m.relative(s,e):"config";try{if(typeof t.source.getAst!="function"){const $=t.pointer?i(`at ${t.pointer}`):"";return`${r} ${$}
|
|
4
4
|
|
|
@@ -14,4 +14,4 @@ ${o.message}
|
|
|
14
14
|
|
|
15
15
|
${o.message}
|
|
16
16
|
|
|
17
|
-
`}}}export{
|
|
17
|
+
`}}}export{v as formatConfigProblem};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import C from"path";import{lintConfig as E,loadConfig as L,createConfigTypes as S}from"@redocly/openapi-core";import{findEmptyAgentSection as q,rbacConfigSchema as x,rootRedoclyConfigSchema as B,withPlatformSection as k}from"@redocly/config";import{deepMerge as I}from"../../../../../utils/object/deep-merge.js";import{isPlainObject as v}from"../../../../../utils/guards/is-plain-object.js";import{logger as d}from"../../../../tools/notifiers/logger.js";import{BRANCH_ENV_PREFIX as M}from"../../../../../constants/common.js";import{sanitizeBranchName as N}from"../../../../utils/envs/sanitize-branch-name.js";import{envConfig as A}from"../../../../config/env-config.js";import{ajv as _,safeParsePartial as D}from"../../safe-parse.js";import{formatConfigProblem as F}from"../../format-error.js";import{ExternalResolver as G}from"../../../../fs/utils/external-ref-resolver.js";import{resolveMutuallyExclusiveProps as z}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 V(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 Y(e){return _.compile(B.properties.graphql.properties.servers.items.properties.url)(e)}function $(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"&&!Y(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 H(e,s){if($(e.graphql,"graphql",s),!!v(e.apis))for(const[t,n]of Object.entries(e.apis))v(n)&&$(n.graphql,`apis.${t}.graphql`,s)}function K(e,s){const t=[["",e],...Object.entries(e.env??{}).map(([n,r])=>[`env.${n}.`,r])];for(const[n,r]of t){const a=q(r);a&&s(new Error(`Invalid ${n}${a}.agent: it must set at least one option.`))}}async function he(e,s,t,n){const a=s.getFileInfo(e)?.realRelativePath||e;async function c(){const f=new G(s),m=C.join(s.cwd,a),l=await L({configPath:m,externalRefResolver:f}),h=await n(l.resolvedConfig);if(h===void 0)return l.resolvedConfig;const g=l.document?.source,j=[...g?await E({config:l,externalConfigTypes:S(h,l)}):[],...g?z(l.resolvedConfig,g):[]];for(const P of j){const R=F(P,s.cwd);P.severity==="warn"?d.warn(R):t(new Error(R))}return l.resolvedConfig}const o=s.exists(e)?await c():{},u=Z(o),w=await n(o);y(o,"residency","Residency",t),y(o,"logoutReturnUrl","Logout return URL",t),V(o,t),H(o,t),K(o,t);let p=w?D(w,o):o;p=X(p),p=Q(p,t);const{env:b}=p;if(b){const f=A.REDOCLY_ENV,m=b[f]||{},l=A.PUBLIC_REDOCLY_BRANCH_NAME||"",h=l?N(l):"",g=h&&b[`${M}${h}`]||{};p=I(p,g,m)}const i=ee(p,u);if(i.imports&&i.imports.length>0){d.warn("The 'imports' property is deprecated. Please use 'plugins' property instead.");const f=new Set([...i.plugins||[],...i.imports.map(m=>C.posix.join(m,"plugin.js"))]);i.plugins=Array.from(f),delete i.imports}if(i.catalog&&(d.warn("The 'catalog' property is deprecated. Please use 'catalogClassic' property instead."),i.catalogClassic={...i.catalog},delete i.catalog),i.scorecard&&(d.warn("The 'scorecard' property is deprecated. Please use 'scorecardClassic' property instead."),i.scorecardClassic=i.scorecard,delete i.scorecard),i.search?.ai){d.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 W(i)}function W(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&&d.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 X(e){const s=Object.entries(e.env??{});[e,...s.map(([,r])=>r)].some(r=>r?.mcp?.docs)&&d.warn("The 'mcp.docs' property is deprecated. Please use the 'mcp' properties instead.");const t=e.mcp?{...e,mcp:O(e.mcp)}:e;if(!e.env)return t;const n=s.map(([r,a])=>[r,a?.mcp?{...a,mcp:O(a.mcp)}:a]);return{...t,env:Object.fromEntries(n)}}function O(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 J(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(x.properties,t))}function Q(e,s){const t=Object.entries(e.env??{}),n=new Set;let r=!1;for(const o of[e,...t.map(([,u])=>u)])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(J)&&(r=!0);for(const o of n)d.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,u])=>[o,u&&T(u)]);return{...a,env:Object.fromEntries(c)}}function T(e){const s=k(e),t=e.rbac?{...s,rbac:U(e.rbac)}:s;return e.access?.rbac?{...t,access:{...e.access,rbac:U(e.access.rbac)}}:t}function U(e){const{reunite:s,...t}=e;return s===void 0?t:{...t,platform:e.platform??s}}function Z(e){if(!e.theme)return[];d.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):d.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 ee(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{W as ensureAccessBackwardsCompatibility,he as readAndValidateConfig};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
`&&t>0;)t--;typeof
|
|
3
|
-
`).length),e=e.slice(t+1)}if(typeof f=="string"){let t=e.indexOf(f);if(t===-1)throw new
|
|
4
|
-
`&&t
|
|
5
|
-
|
|
6
|
-
`)
|
|
1
|
+
import m from"node:path";import{slash as O}from"../../../../utils/path/slash.js";import{reporter as F}from"../../../tools/notifiers/reporter.js";import{getNodeAttribute as i}from"../../../../markdoc/helpers/get-node-attribute.js";import{MdResolveError as w}from"./md-resolve-error.js";async function j(n,b,y,{actions:{contentDir:d},context:u}){const o=i(n,b);if(!o)return;const r=o.startsWith("/")?o.slice(1):O(m.relative(d,m.resolve(d,m.dirname(y),o)));if(!u.fs.exists(r)){await F.panicOnBuildContentError(`Code snippet file ${r} does not exist`);return}let e=await u.fs.read(r);const x=i(n,"after"),p=i(n,"before"),C=i(n,"from"),g=i(n,"to"),v=i(n,"prefix")||"",s=C??x,f=g??p,E=x!==null,a=p!==null,L=(typeof s=="number"?Math.max(s||0,1):1)+(E?1:0);let c=(typeof f=="number"?f:1/0)-(a?1:0);if(typeof s=="string"){let t=e.indexOf(s);if(t===-1)throw new w(`Cannot find start text "${s}" in snippet file`,{rawLink:r});for(;e[t]!==`
|
|
2
|
+
`&&t>0;)t--;typeof c=="number"&&(c-=e.slice(0,t-1).split(`
|
|
3
|
+
`).length),e=e.slice(t+1)}if(typeof f=="string"){let t=e.indexOf(f);if(t===-1)throw new w(`Cannot find end text "${p}" in snippet file`,{rawLink:r});if(a)e[t-1]===`
|
|
4
|
+
`&&t--;else{const h=e.indexOf(`
|
|
5
|
+
`,t);t=h===-1?e.length:h}e=e.slice(0,t)}const l=e.split(`
|
|
6
|
+
`);if(a)for(;l[l.length-1]==="";)l.pop();n.children=[],n.attributes.rawContent=v+l.slice(L-1,c).join(`
|
|
7
|
+
`)}export{j as resolveCodeSnippetFromFile};
|
|
@@ -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};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{REDOCLY_TEAMS_RBAC as
|
|
1
|
+
import{REDOCLY_TEAMS_RBAC as y}from"@redocly/config";import{MARKDOC_PARTIALS_DATA_KEY as D}from"../../constants/common.js";import{reporter as R}from"../../tools/notifiers/reporter.js";import{canAccessFeature as g}from"../../utils/entitlements/can-access-feature.js";import{extractMdFirstHeading as A,getAst as T}from"./compiler.js";import{getTemplatePath as w}from"./get-template-path.js";import{prepareMarkdocPartials as M}from"./markdoc/partials.js";import{searchResolver as C}from"./search/search-resolver.js";import{markdownStaticDataLoader as E}from"./markdown-static-data-loader.js";import{makeErrorRoute as I}from"../error-route.js";import{validateRbacConfig as O}from"../validate-rbac-config.js";import{findFrontmatterSlugs as _,getSidebarSharedDataId as G,resolveFrontmatterSlugs as L}from"../utils.js";import{isPartial as j}from"./is-partial.js";import{markdownFrontmatterLoader as B}from"./markdown-frontmatter-loader.js";import{resolveRawPartials as K}from"./markdoc/resolve-raw-partials.js";import{getAiDocumentsStore as N}from"./search/get-ai-search-documents.js";import{registerPageProps as Y}from"../register-page-props.js";import{resolveMarkdownTemplate as q}from"./utils/resolve-markdown-template.js";import{sanitizeMalformedMdContent as z}from"./utils/sanitize-malformed-md-content.js";import{telemetryTraceStep as H}from"../../../cli/telemetry/helpers/trace-step.js";async function ce(h){return{id:"markdoc",requiredEntitlements:["markdown"],loaders:{"markdown-frontmatter":B,"markdown-ast":async(e,t)=>{const i=await t.fs.read(e),m=await t.getConfig(),s=await K(i,e,m?.markdown?.partialsFolders,t),n=z(s);return T(e,n)}},processContent:async(e,t)=>{await H("com.redocly.build.plugin.markdown",async i=>{const{markdown:m}=await t.getConfig();i?.setAttribute("config",JSON.stringify(m||{}));const s=m?.partialsFolders??[],n=e.createTemplate("markdown","@redocly/theme/core/templates/Markdown"),S=e.registerServerPropsGetter("markdown",w("./get-server-props.js"));e.createTemplate("error",w("../../../client/app/Error/ErrorDetails.js"));for(const o of t.fs.scan(/\.md$/))if(!await t.isPathIgnored(o.relativePath)&&!j(o.relativePath,s))try{await k(o)}catch(a){e.addRoute(I(o.relativePath,a)),i?.error(a),await R.panicOnBuild("Failed to create route for markdown file: %s",a.message)}async function k(o){const{relativePath:a,realRelativePath:b}=o,{data:{frontmatter:r}}=await t.cache.load(a,"markdown-frontmatter"),P=r?.metadata||{},F=await _(a,"markdown-frontmatter",r,t),p=L(F,a);let d=null;r?.sidebar&&(d=await G(r.sidebar,a,t.fs));let u=n;const l=q({realRelativePath:b,frontmatter:r,markdown:m,contentDir:h.contentDir});l&&(u=e.createTemplate(l.templateRef,l.templatePath)),r?.rbac&&(g("rbac")?O({content:{[a]:r.rbac}}):r.rbac=void 0),g("banner")||(r.banner=void 0),Y(o,t.fs,e),(p.length?p.reverse():[void 0]).forEach(v=>{e.addRoute({excludeFromSearch:r?.excludeFromSearch||!1,slug:v,fsPath:a,templateId:u,sharedData:d?[{id:d,key:"sidebar"}]:void 0,redirectFrom:Object.entries(r.redirects||{}).map(([c,{type:f}])=>({type:f,from:c})),[y]:r?.rbac,getNavText:async()=>(r?.seo?.title||await A(a,t)||"").toString(),metadata:{type:"markdown",...P},async getStaticData(c,f){return E(o,c,t,r,f)},getSearchDocuments:C(r,a,e.getSearchFacets,e.setSearchFacets),getAiDocumentsStore:N(e.getSearchFacets,r),serverPropsGetterIds:[S]})})}})},afterRoutesCreated:async(e,t)=>{const i=await M(t,e);e.setGlobalConfig({[D]:i})}}}export{ce as markdownPlugin};
|
|
@@ -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;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import g from"node:path";import{REDOCLY_TEAMS_RBAC as h}from"@redocly/config";import{slash as S}from"../../../utils/path/slash.js";import{canAccessFeature as
|
|
1
|
+
import g from"node:path";import{REDOCLY_TEAMS_RBAC as h}from"@redocly/config";import{slash as S}from"../../../utils/path/slash.js";import{canAccessFeature as b}from"../../utils/entitlements/can-access-feature.js";import{staticDataLoader as y}from"./loaders/static-data-loader.js";import{findFrontmatterSlugs as F,getSidebarSharedDataId as P,resolveFrontmatterSlugs as v}from"../utils.js";import{reactFrontmatterLoader as w}from"./loaders/react-frontmatter-loader.js";import{registerPageProps as D}from"../register-page-props.js";import{telemetryTraceStep as R}from"../../../cli/telemetry/helpers/trace-step.js";import{searchResolver as E}from"./search/search-resolver.js";async function x(){return{loaders:{"react-frontmatter":w},id:"react-pages",requiredEntitlements:["reactPages"],processContent:async(r,a)=>{await R("com.redocly.build.plugin.pages",async()=>{const{fs:o,cache:f}=a;for(const n of o.scan(/\.page\.tsx?$/)){const{relativePath:e,realRelativePath:l}=n;if(await a.isPathIgnored(e))continue;const u=r.createTemplate(S(e),g.resolve(o.cwd,l));D(n,o,r);const{data:t}=await f.load(e,"react-frontmatter"),p=await F(e,"react-frontmatter",t,a),s=v(p,e);let c=null;t?.sidebar&&(c=await P(t.sidebar,e,a.fs)),(s.length?s.reverse():[void 0]).forEach(i=>{r.addRoute({excludeFromSearch:t?.excludeFromSearch||!1,slug:i,templateId:u,fsPath:e,sharedData:c?[{id:c,key:"sidebar"}]:void 0,[h]:b("rbac")?t?.rbac:void 0,getNavText:async()=>t?.seo?.title,getStaticData:async(m,d)=>({props:await y({frontmatter:t,relativePath:e},a,d)}),getSearchDocuments:E(t,e,r.getSearchFacets,r.setSearchFacets)})});for(const[i,{to:m,type:d}]of Object.entries(t?.redirects||{}))r.addRedirect(i,{to:m||s[0],type:d})}})}}}export{x as customPagesPlugin};
|
package/dist/server/store.d.ts
CHANGED
|
@@ -147,6 +147,7 @@ export declare class Store {
|
|
|
147
147
|
getRequestHandler: (id: string) => string | undefined;
|
|
148
148
|
createTemplate: (id: string, importPath: string) => string;
|
|
149
149
|
addBrowserPlugin: (importPath: string) => void;
|
|
150
|
+
copyAsset: (src: string, fromFileRelativePath: string) => Promise<string | null>;
|
|
150
151
|
createRequestHandler: (id: string, importPath: string) => string;
|
|
151
152
|
clearRequestHandlersByPrefix: (prefix: string) => void;
|
|
152
153
|
registerServerPropsGetter: (id: string, importPath: string) => string;
|
package/dist/server/store.js
CHANGED
|
@@ -1 +1 @@
|
|
|
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};
|
|
1
|
+
import T from"@redocly/markdoc";import{writeFileSync as w}from"node:fs";import{readFile as R}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 F,MARKDOC_PARTIALS_DATA_KEY as y,MARKDOC_PARTIALS_DEPS_KEY as L}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 S}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{FileNotFoundError as B}from"./utils/fs.js";import{resolveAssetPath as I}from"./utils/resolve-asset-path.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 $}from"./ssr/render.js";import{readStaticData as W,writeStaticData as z}from"./utils/static-data.js";import{getMarkdocOptions as Y}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as Z}from"./entitlements/entitlements-provider.js";import{getPlanDeniedMarkdocTags as Q}from"./entitlements/plan-denied-markdoc-tags.js";import{isRouteReserved as X}from"./plugins/get-reserved-routes.js";import{isL10nPath as ee}from"./fs/utils/is-l10n-path.js";import{resolveGlobMapValue as te}from"./utils/globs.js";import{createDefaultStoreConfig as se,resolveConfigEntries as re,resolveStoreConfig as oe}from"./store-config.js";import{findRedirect as ae}from"./utils/redirects/find-redirect.js";import{followRedirectChain as ie}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as ne}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as le}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",ce=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],We="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;#a=void 0;#i={};config=se();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;#o=new Map;constructor({outdir:e,contentDir:t,serverMode:s=!1,serverOutDir:r}){this.#n=t,this.outdir=e,this.serverMode=s,this.serverOutDir=r,J.register({serverOutDir:r,readRoots:[e,process.cwd()]}),this.userCodeReady=new Promise(o=>{this.#l=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.#d,this.#u])}waitForPluginsRun(){return this.#s}finishPluginsRun(){this.#c?.();for(const e of this.#o.values())this.runListeners(...e);this.#o.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 le("com.redocly.build.reloadMarkdocOptions",async()=>{const{userTagNames:e,...t}=await Y(this.serverOutDir),s=Q(e),r=Object.fromEntries(Object.entries(t.tags).filter(([o])=>!(o in s)));this.#a={...t,tags:r},this.#i=s})}get markdocOptions(){return{...this.#a,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()=>q.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:e,context:t,deps:s,resource:r})=>{const{data:{info:o,ast:a},compoundHash:l}=await M(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 c=this.sharedDataDeps.get(n)||new Set;c.add(i),this.sharedDataDeps.set(n,c)}}for(const i of o.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&&o.partials?.length)for(const i of s.routeSlugs){const n=this.routesPartials.get(i)||[];for(const c of o.partials)n.includes(c)||n.push(c);this.routesPartials.set(i,n)}return{info:o,ast:a,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 a of t)for(const l in this.replacedEnvVars)if(l===a||l.startsWith(`${a}:`)){const i=l.split(":"),{error:n,value:c}=S(e,i);(n||c!==this.replacedEnvVars[l].replaced)&&delete this.replacedEnvVars[l]}const{config:s,unsetEnvVars:r,replacedValues:o}=re(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(!Z.instance().canAccessFeature("redirects")&&e!=="/")return;this.config.redirects||(this.config.redirects={});const a=d(e).toLowerCase();this.config.redirects[a]=t;const{trackOriginalSource:l=!0}=s,i=this.getGlobalConfig("originalRedirectSources");l&&Array.isArray(i)&&!i.includes(a)&&i.push(a),a.endsWith("*")&&ne(this.config.wildcardRedirectsTree,a)};getRedirect=e=>{const t=d(e).toLowerCase(),s=ae(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("*")&&ie(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??_(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=G(e),o=this.routesSharedData.get(r)||{};o[t]=s,this.routesSharedData.set(r,o),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(L)||{},o=new Set(s),a=Array.from(s);for(let i=0;i<a.length;i++){const n=a[i],c=r[n]?.partials??[];for(const h of c)o.has(h)||(o.add(h),a.push(h))}const l={};for(const i of o)t[i]&&(l[i]=t[i]);return l};addRoute=e=>{const s={...te(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(o=>({code:o,name:o}));for(const o of r){const a=k(e,m,o.code,r);this.addRouteSharedData(a,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 o=s.hasAvailabilityGate??(r?.importPath===e?r.hasAvailabilityGate:void 0);this.mcpToolHandlers.set(s.name,{...s,importPath:e,hasAvailabilityGate:o}),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(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);if(s&&r)return this.routesBySlug.get(d(r.to));const o=s?this.routeAliases.get(d(e).toLowerCase()):void 0;return this.routesBySlug.get(o??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())||X(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?!ee(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)};copyAsset=async(e,t)=>{const s=this.lifecycleContext?.fs;if(!s)throw new Error(`Cannot copy asset ${e}: the content file system is not ready`);try{const r=await I(e,s,{fromFileRelativePath:t,contentDir:this.contentDir,outdir:this.outdir});if(!r)throw new Error(`Cannot copy asset ${e}`);return r}catch(r){if(r instanceof B)return null;throw r}};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 W(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})},o=await e.getStaticData?.(e,r)||{},a=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=>a.add(p));const f=this.sharedDataDeps.get(c);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:F,...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(""):$(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 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[b]=j.PLAN_GATES,t}static#m(e,t){const s=new D(t);for(const[o,a]of Object.entries(P))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[b];return r&&V("PLAN_GATES",r),s}async writeServerFiles(){x(this.serverOutDir),w(g(this.serverOutDir,C),JSON.stringify(this.#g())),w(g(this.serverOutDir,A),JSON.stringify(await this.getConfigWithEnvPlaceholders()))}static async fromServerFiles(e){const t=g(e.serverOutDir,A),{config:s,env:r}=oe(JSON.parse(await R(t,"utf8")));s[y]=de(s[y]);const o=JSON.parse(await R(g(e.serverOutDir,C),"utf8")),a=D.#m(o,e);a.config=s;const l=Object.fromEntries(Object.entries(r).filter(([i])=>U(i)));return a.configFile={path:t,env:l,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:l}=S(e,r);if(a||!H(l)&&!Array.isArray(l)){await E.panicOnBuild(`Failed to replace env var with env name for ${t}`);continue}l[o]=s}return e}async reportUnsetEnvVars(){if(this.unsetEnvVars.size===0)return;const e=Array.from(this.unsetEnvVars).filter(s=>!ce.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 de(O){return N(O,e=>T.Ast.fromJSON(JSON.stringify(e)))}export{D as Store,We as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
|
|
@@ -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;
|
|
@@ -111,7 +113,7 @@ export type PageRouteInit = Omit<PageRouteDetails<PageStaticData, PageProps>, 'v
|
|
|
111
113
|
key: string;
|
|
112
114
|
}>;
|
|
113
115
|
redirectFrom?: Array<{
|
|
114
|
-
type
|
|
116
|
+
type?: number;
|
|
115
117
|
from: string;
|
|
116
118
|
}>;
|
|
117
119
|
};
|
|
@@ -169,6 +171,14 @@ export type ProcessContentActions = {
|
|
|
169
171
|
addRedirect: (from: string, to: RedirectConfig, options?: RedirectOptions) => void;
|
|
170
172
|
createTemplate: (id: string, importPath: string) => string;
|
|
171
173
|
addBrowserPlugin: (importPath: string) => void;
|
|
174
|
+
/**
|
|
175
|
+
* Resolves an asset `src` written in the page at `fromFileRelativePath` the way Markdown
|
|
176
|
+
* images resolve: a `static/` file keeps its path, a content file is published under a
|
|
177
|
+
* host-owned asset name, an l10n page falls back to the default-locale file, and an
|
|
178
|
+
* external URL is returned as is. The URL includes the path prefix. Resolves to `null`
|
|
179
|
+
* when the file does not exist; rejects on any other failure.
|
|
180
|
+
*/
|
|
181
|
+
copyAsset: (src: string, fromFileRelativePath: string) => Promise<string | null>;
|
|
172
182
|
registerServerPropsGetter: (id: string, importPath: string) => string;
|
|
173
183
|
registerPagePropsGetter: (id: string, importPath: string) => void;
|
|
174
184
|
addSsrComponents: (components: JSX.Element[] | string[], position: 'head' | 'preBody' | 'postBody') => void;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { RedirectConfig } from '@redocly/config';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ContentfulStatusCode } from 'hono/utils/http-status';
|
|
3
3
|
/**
|
|
4
4
|
*
|
|
5
5
|
* Processes a redirect by normalizing the location and adding search parameters.
|
|
6
6
|
* Does not handle wildcard redirects. Only direct redirects are supported.
|
|
7
|
+
* Without an explicit `type`, the redirect is permanent (301) only in production;
|
|
8
|
+
* elsewhere it is temporary (302) so browsers do not cache an edited redirect.
|
|
7
9
|
*
|
|
8
10
|
* @param options - The options for the redirect.
|
|
9
11
|
* @param options.redirect - The redirect configuration.
|
|
@@ -12,11 +14,11 @@ import type { StatusCode } from 'hono/utils/http-status';
|
|
|
12
14
|
*
|
|
13
15
|
* @example
|
|
14
16
|
* processRedirects({ redirect: { to: '/new-page' } });
|
|
15
|
-
* // Returns: { location: '/new-page', type: 301 }
|
|
17
|
+
* // Returns: { location: '/new-page', type: 301 } in production, 302 otherwise
|
|
16
18
|
*
|
|
17
19
|
* @example
|
|
18
20
|
* processRedirects({ redirect: { to: '/new-page' }, reqUrlSearch: '?param=value' });
|
|
19
|
-
* // Returns: { location: '/new-page?param=value', type: 301 }
|
|
21
|
+
* // Returns: { location: '/new-page?param=value', type: 301 } in production, 302 otherwise
|
|
20
22
|
*
|
|
21
23
|
* @example
|
|
22
24
|
* processRedirects({ redirect: { to: 'https://example.com', type: 302 } });
|
|
@@ -27,6 +29,6 @@ export declare function processRedirects({ redirect, reqUrlSearch, }: {
|
|
|
27
29
|
reqUrlSearch?: string;
|
|
28
30
|
}): {
|
|
29
31
|
location: string;
|
|
30
|
-
type:
|
|
32
|
+
type: ContentfulStatusCode;
|
|
31
33
|
};
|
|
32
34
|
//# sourceMappingURL=process-redirects.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{removeTrailingSlash as i}from"../../../../utils/url/remove-trailing-slash.js";import{isLocalLink as
|
|
1
|
+
import{removeTrailingSlash as i}from"../../../../utils/url/remove-trailing-slash.js";import{isLocalLink as r}from"../../../../utils/path/is-local-link.js";import{withPathPrefix as e}from"@redocly/theme/core/utils";import{envConfig as c}from"../../../config/env-config.js";function l({redirect:o,reqUrlSearch:t=""}){const n=r(o.to)?e(o.to):o.to;return{location:a(i(n))+t,type:o?.type||(c.isProductionEnv?301:302)}}function a(o){return encodeURI(o).replace(/%25([0-9A-Fa-f]{2})/g,"%$1")}export{l as processRedirects};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{MCP_ROUTE as
|
|
1
|
+
import{MCP_ROUTE as $,PUBLIC_MCP_ROUTE as w}from"@redocly/theme/core/constants/mcp";import{ServerRoutes as M}from"../../../constants/api.js";import{DEV_LOGIN_SLUG as F}from"../../../constants/common.js";import{CACHE_CONTROL_NO_STORE_HEADER_VALUE as a,DEFAULT_TITLE as N}from"../../constants/common.js";import{findInIterable as B}from"../../../utils/collection/find-in-iterable.js";import{removeTrailingSlash as q}from"../../../utils/url/remove-trailing-slash.js";import{envConfig as b}from"../../config/env-config.js";import{canAccessResource as H,filterDataByAccessDeep as T,isResourcePubliclyAccessible as G}from"../../utils/rbac.js";import{getServerProps as V}from"../../ssr/index.js";import{readSharedData as W}from"../../utils/index.js";import{getRedirectLoginUrl as k}from"../utils/get-redirect-login-url.js";import{processRedirects as K}from"./helpers/process-redirects.js";import{removeErrorDetails as z}from"../utils/remove-error-details.js";import{telemetry as J}from"../../telemetry/index.js";import{MCP_NOT_ALLOWED_TEMPLATE_ID as Q}from"../../constants/mcp.js";function ue(e,n){return async(t,g)=>{const p=t.get("logger"),{req:u}=t,{pathname:d}=new URL(u.url),{seo:f,ssoDirect:h}=e.getConfig(),i=f?.title||N;if(e?.compilationErrors?.length&&b.isDevelopMode)return t.json({templateId:"compilation-error",props:{compilationErrors:e?.compilationErrors},sharedDataIds:{}},500,{"Cache-Control":a});const m=d.match(/page-data(.*)data.json$/);if(!m)return g();const c=decodeURI(m[1]),r=c==="/index/"?"/":q(c),o=e.getRouteBySlug(r,{followRedirect:!1})||B(e.routesBySlug.values(),s=>s.hasClientRoutes&&c.startsWith(s.slug+"/"));if(c===M.OIDC_CALLBACK+"/")return t.json({templateId:"403OIDC",sharedDataIds:{},props:{seo:{title:`${i} - Forbidden`}}},200,{"Cache-Control":a});const{isAuthenticated:C,teams:D,claims:{name:_,picture:P,email:I}}=t.get("auth"),R={isAuthenticated:C,email:I,teams:D},l={isAuthenticated:C,name:_,picture:P,email:I,teams:D};if((r===$||r===w&&!!e.globalData.mcpData?.docs?.publicEndpoint)&&e.globalData.mcpData?.docs?.enabled)return t.json({templateId:Q,sharedDataIds:{},props:{},userData:l},405,{"Cache-Control":a});const E=e.getRedirect(r);if(E){const{location:s,type:U}=K({redirect:E});return J.sendRedirectMessage([{id:r,object:"redirect",uri:`urn:redocly:realm:api:redirect:${r}`,from:r,templateId:"404"}]),t.json({templateId:"404",redirectTo:s,sharedDataIds:{},props:{}},U,{"Cache-Control":a})}if(!o){const s=e.getRouteBySlug(r,{followRedirect:!0});return p.error(`Page not found: ${d}`),t.json({templateId:"404",redirectTo:s?.slug,sharedDataIds:{},props:{seo:{title:`${i} - Not Found`}},userData:l},404,{"Cache-Control":a})}if(p.verbose(`Page viewed: ${o.slug}`),!H(o,R,e.config.access?.rbac,e.config.access?.requiresLogin)&&o.slug!==F){if(C)return t.json({templateId:"403",sharedDataIds:{},props:{seo:{title:`${i} - Forbidden`}},userData:l},403,{"Cache-Control":a});const s=Object.keys(h||{}).length>0;return t.json({templateId:"404",sharedDataIds:{},props:{seo:{title:`${i} - Not Found`}},userData:l,...s?{redirectTo:k(e,o.slug)}:{}},s?401:404,{"Cache-Control":a})}const S=T(o.versions,R,e.config.access?.rbac,e.config.access?.requiresLogin),j=e.routesSharedData.get(o.slug)||{},A=await n(o),v=await V(o,t,A,e),{sharedDataIds:O,...L}=v,y={templateId:o.templateId,versions:S,sharedDataIds:{...j,...O||{}},props:b.isProductionEnv?z(L):L,slug:o.slug,userData:l,isPublic:G(o,e.config)};return t.json(y,200,{"Cache-Control":a})}}function me(e){return async(n,t)=>{const g=n.get("logger"),{req:p}=n,{pathname:u}=new URL(p.url),d=u.match(/\/page-data\/shared\/(.*)\.json/);if(!d)return t();const f=decodeURIComponent(d[1]),h=await W(f,e.outdir),{isAuthenticated:i,teams:m,claims:{email:c}}=n.get("auth"),r=T(h,{isAuthenticated:i,email:c,teams:m},e.config.access?.rbac,e.config.access?.requiresLogin);return r?n.json(r,200,{"Cache-Control":a}):(g.error(`Shared data not found: ${u}`),n.text("Not Found",404,{"Cache-Control":a}))}}export{ue as pageDataHandler,me as sharedPageDataHandler};
|
|
@@ -46,6 +46,10 @@ export type PlanGates = {
|
|
|
46
46
|
[FEATURE.sdkGeneration]: boolean;
|
|
47
47
|
/** Absent at runtime from legacy tokens, which keep every npm package. */
|
|
48
48
|
[FEATURE.customPackages]: boolean;
|
|
49
|
+
/** Absent at runtime from tokens signed before the claim, which keep the project's Markdoc tags. */
|
|
50
|
+
[FEATURE.customMarkdocTags]: boolean;
|
|
51
|
+
/** Absent at runtime from tokens signed before the claim, which keep the tools in `@mcp` folders. */
|
|
52
|
+
[FEATURE.customMcpTools]: boolean;
|
|
49
53
|
/** The new-pricing tier of the order; `null` or absent for legacy orders. */
|
|
50
54
|
pricingTier?: PricingTier | null;
|
|
51
55
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/reef",
|
|
3
|
-
"version": "0.141.0-next.
|
|
3
|
+
"version": "0.141.0-next.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -33,14 +33,14 @@
|
|
|
33
33
|
"@opentelemetry/sdk-trace-web": "2.8.0",
|
|
34
34
|
"@opentelemetry/semantic-conventions": "1.40.0",
|
|
35
35
|
"@redocly/ajv": "8.18.0",
|
|
36
|
-
"@redocly/api-docs": "0.6.0-next.
|
|
37
|
-
"@redocly/config": "0.72.
|
|
36
|
+
"@redocly/api-docs": "0.6.0-next.2",
|
|
37
|
+
"@redocly/config": "0.72.1",
|
|
38
38
|
"@redocly/markdoc": "0.7.0-beta.2",
|
|
39
|
-
"@redocly/openapi-core": "2.
|
|
39
|
+
"@redocly/openapi-core": "2.75.0",
|
|
40
40
|
"@redocly/portal-legacy-ui": "0.19.0",
|
|
41
|
-
"@redocly/portal-plugin-mock-server": "0.
|
|
41
|
+
"@redocly/portal-plugin-mock-server": "0.26.0-next.0",
|
|
42
42
|
"@redocly/realm-asyncapi-sdk": "0.17.0",
|
|
43
|
-
"@redocly/theme": "0.
|
|
43
|
+
"@redocly/theme": "0.73.0-next.0",
|
|
44
44
|
"@shikijs/transformers": "3.21.0",
|
|
45
45
|
"@tanstack/react-query": "5.62.3",
|
|
46
46
|
"@tanstack/react-table": "8.21.3",
|