@redocly/reef 0.140.0 → 0.141.0-next.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/client/app/search/useFacets.d.ts +1 -1
  3. package/dist/client/app/search/useFacets.js +1 -1
  4. package/dist/client/app/search/useSearch.js +1 -1
  5. package/dist/constants/l10n/langs/ar.js +1 -1
  6. package/dist/constants/l10n/langs/de.js +1 -1
  7. package/dist/constants/l10n/langs/en.js +1 -1
  8. package/dist/constants/l10n/langs/es.js +1 -1
  9. package/dist/constants/l10n/langs/fr.js +1 -1
  10. package/dist/constants/l10n/langs/hi.js +1 -1
  11. package/dist/constants/l10n/langs/it.js +1 -1
  12. package/dist/constants/l10n/langs/ja.js +1 -1
  13. package/dist/constants/l10n/langs/ko.js +1 -1
  14. package/dist/constants/l10n/langs/pl.js +1 -1
  15. package/dist/constants/l10n/langs/pt-BR.js +1 -1
  16. package/dist/constants/l10n/langs/pt.js +1 -1
  17. package/dist/constants/l10n/langs/ru.js +1 -1
  18. package/dist/constants/l10n/langs/uk.js +1 -1
  19. package/dist/constants/l10n/langs/zh.js +1 -1
  20. package/dist/markdoc/nodes/table.js +1 -1
  21. package/dist/server/plugins/api-docs/index.js +1 -1
  22. package/dist/server/plugins/api-docs/search/get-ai-search-documents.js +65 -65
  23. package/dist/server/plugins/markdown/attribute-resolvers/resolve-code-snippet-from-file.js +7 -6
  24. package/dist/server/plugins/markdown/index.js +1 -1
  25. package/dist/server/plugins/markdown/search/create-render-tag-fn.d.ts +1 -1
  26. package/dist/server/plugins/markdown/search/create-render-tag-fn.js +1 -1
  27. package/dist/server/plugins/markdown/search/get-ai-search-documents.js +8 -8
  28. package/dist/server/plugins/pages/index.js +1 -1
  29. package/dist/server/store.d.ts +1 -0
  30. package/dist/server/store.js +1 -1
  31. package/dist/server/types/plugins/common.d.ts +9 -1
  32. package/dist/server/web-server/routes/helpers/process-redirects.d.ts +6 -4
  33. package/dist/server/web-server/routes/helpers/process-redirects.js +1 -1
  34. package/dist/server/web-server/routes/page-data.js +1 -1
  35. package/package.json +4 -4
@@ -1,9 +1,9 @@
1
- import M from"@redocly/markdoc";import D from"node:path";import{AI_SEARCH_CHUNK_SIZE as R,AI_SEARCH_DOCUMENT_CHUNK_SIZE as v}from"../../../constants/plugins/search.js";import{slugger as I}from"../../../../utils/slugger.js";import{joinSectionContent as N}from"./join-section-content.js";import{toMarkdown as P}from"./to-markdown.js";import{AstToSearchNodeTransformer as w}from"./walk-sections.js";import{createRenderTagFn as x}from"./create-render-tag-fn.js";import{getBuildTranslateContext as K}from"../../../utils/l10n/get-build-translate-context.js";import{getLocaleFromRelativePath as L}from"../../../fs/utils/get-locale-from-relative-path.js";import{HeadingNode as b}from"./nodes/heading-node.js";import{extractDocumentSearchFacets as U}from"./search-facets.js";import{formatDocumentMetadata as y}from"../../search/utils.js";import{TagNode as H}from"./nodes/tag-node.js";import{MARKDOC_PARTIALS_DATA_KEY as O}from"../../../constants/common.js";const j=Symbol(),at=(n,o)=>async(e,r,i,c)=>{if(o?.excludeFromSearch)return;I.reset();const l=await e.getNavText?.()||D.basename(e.fsPath),f=M.Ast.fromJSON(r.ast),{locale:g,translate:A}=await K(e.slug,c.getGlobalData?.().l10n),s=new w({partials:c.getGlobalConfig(O)||{},renderTag:x(c.markdocOptions.tags,g,A),getInnerContent:P,ast:f,skipConditionals:!0}),d=Array.from(s.transform());return{async getLLMsTxts(){const u=o?.seo,t=u&&typeof u=="object"&&"description"in u?String(u.description):void 0,a=N(d);return[{title:l,description:t,slug:e.slug,fsPath:e.fsPath,content:a,includeInLLMsTxt:!0}]},async getSearchDocuments(){return F(e,d,n,l)}}};function F(n,o,e,r){const i=n.slug,c=new Map,l=n.metadata||{},f=U(l,e),g=y(l),A=g?`Metadata:
1
+ import v from"@redocly/markdoc";import M from"node:path";import{AI_SEARCH_CHUNK_SIZE as R,AI_SEARCH_DOCUMENT_CHUNK_SIZE as D}from"../../../constants/plugins/search.js";import{slugger as I}from"../../../../utils/slugger.js";import{joinSectionContent as N}from"./join-section-content.js";import{toMarkdown as P}from"./to-markdown.js";import{AstToSearchNodeTransformer as b}from"./walk-sections.js";import{createRenderTagFn as w}from"./create-render-tag-fn.js";import{getPublicEnvVariables as x}from"../../../utils/envs/get-public-env-variables.js";import{getBuildTranslateContext as K}from"../../../utils/l10n/get-build-translate-context.js";import{getLocaleFromRelativePath as L}from"../../../fs/utils/get-locale-from-relative-path.js";import{HeadingNode as U}from"./nodes/heading-node.js";import{extractDocumentSearchFacets as y}from"./search-facets.js";import{formatDocumentMetadata as H}from"../../search/utils.js";import{TagNode as O}from"./nodes/tag-node.js";import{MARKDOC_PARTIALS_DATA_KEY as j}from"../../../constants/common.js";const F=Symbol(),mt=(n,r)=>async(e,o,i,c)=>{if(r?.excludeFromSearch)return;I.reset();const l=await e.getNavText?.()||M.basename(e.fsPath),f=v.Ast.fromJSON(o.ast),{locale:g,translate:A}=await K(e.slug,c.getGlobalData?.().l10n),s=new b({partials:c.getGlobalConfig(j)||{},renderTag:w(c.markdocOptions.tags,g,A,{frontmatter:o.frontmatter,env:x()}),getInnerContent:P,ast:f,skipConditionals:!0}),d=Array.from(s.transform());return{async getLLMsTxts(){const u=r?.seo,t=u&&typeof u=="object"&&"description"in u?String(u.description):void 0,a=N(d);return[{title:l,description:t,slug:e.slug,fsPath:e.fsPath,content:a,includeInLLMsTxt:!0}]},async getSearchDocuments(){return $(e,d,n,l)}}};function $(n,r,e,o){const i=n.slug,c=new Map,l=n.metadata||{},f=y(l,e),g=H(l),A=g?`Metadata:
2
2
  ${g}
3
- `:"",s=[];for(const t of o)if(t instanceof b){s.splice(t.attributes.level-1,6);const a=t.attributes.level>2,m=s[s.length-1]?.title,T=s[s.length-1]?.url,C=a&&m?m:W(r,t),S=a&&T?T:t.getUrl(i),p={title:C,content:"",url:S,level:t.attributes.level,toc:s.map(E=>h(E.section)).join(`
4
- `)};(t.attributes.level>1||h(t)!==r)&&(p.content+=p.content?`
5
- `:"",p.content+=t.content.trimEnd()),c.set(t.id,p),s.push({section:t,title:C,url:S})}else if(t instanceof H)c.set(t.id,{title:t.content,content:t.content,url:t.getUrl(i),level:t.attributes.level+1,toc:s.map(a=>h(a.section)).concat(t.content).join(`
6
- `)});else{const a=t.parentNode?.id??j,m=c.get(a)??{title:r,content:"",url:i,level:1/0,toc:""};m.content+=m.content?`
7
- `:"",m.content+=t.content.trimEnd(),c.set(a,m)}const d=$(Array.from(c.values())),u=L(n.fsPath);return d.filter(t=>t.content).map(t=>({title:t.title,url:t.url,content:[A,t.toc||`# ${r}`,t.content].filter(Boolean).join(`
8
- `),fsPath:n.fsPath,locale:u,product:n.product?.name,facets:f}))}function _(n){return n.length/4}function $(n){if(n.length===0)return[];const o=[];let e=n[0];for(let r=1;r<n.length;r++){const i=n[r],c=_(e.content),l=_(i.content),f=r===n.length-1;l<R&&(c<v||f)&&i.level>e.level?e.content+=`
9
- `+i.content:(o.push(e),e=i)}return o.push(e),o}const k=String.raw`\((?:[^()]|\([^()]*\))*\)`,G=new RegExp(String.raw`!\[[^\]]*\]`+k,"g"),z=new RegExp(String.raw`\[(?<!\[[^\]()]*?\[)([^\]]*)\]`+k,"g");function W(n,o){const e=h(o);return o.content===n||!e?n:`${n} \u2192 ${e}`}function h(n){return B(n.content.replace(/^#+/,""))}function B(n){return n.replace(G,"").replace(z,"$1").trim()}export{at as getAiDocumentsStore};
3
+ `:"",s=[];for(const t of r)if(t instanceof U){s.splice(t.attributes.level-1,6);const a=t.attributes.level>2,m=s[s.length-1]?.title,T=s[s.length-1]?.url,C=a&&m?m:B(o,t),S=a&&T?T:t.getUrl(i),p={title:C,content:"",url:S,level:t.attributes.level,toc:s.map(k=>h(k.section)).join(`
4
+ `)};(t.attributes.level>1||h(t)!==o)&&(p.content+=p.content?`
5
+ `:"",p.content+=t.content.trimEnd()),c.set(t.id,p),s.push({section:t,title:C,url:S})}else if(t instanceof O)c.set(t.id,{title:t.content,content:t.content,url:t.getUrl(i),level:t.attributes.level+1,toc:s.map(a=>h(a.section)).concat(t.content).join(`
6
+ `)});else{const a=t.parentNode?.id??F,m=c.get(a)??{title:o,content:"",url:i,level:1/0,toc:""};m.content+=m.content?`
7
+ `:"",m.content+=t.content.trimEnd(),c.set(a,m)}const d=G(Array.from(c.values())),u=L(n.fsPath);return d.filter(t=>t.content).map(t=>({title:t.title,url:t.url,content:[A,t.toc||`# ${o}`,t.content].filter(Boolean).join(`
8
+ `),fsPath:n.fsPath,locale:u,product:n.product?.name,facets:f}))}function _(n){return n.length/4}function G(n){if(n.length===0)return[];const r=[];let e=n[0];for(let o=1;o<n.length;o++){const i=n[o],c=_(e.content),l=_(i.content),f=o===n.length-1;l<R&&(c<D||f)&&i.level>e.level?e.content+=`
9
+ `+i.content:(r.push(e),e=i)}return r.push(e),r}const E=String.raw`\((?:[^()]|\([^()]*\))*\)`,z=new RegExp(String.raw`!\[[^\]]*\]`+E,"g"),W=new RegExp(String.raw`\[(?<!\[[^\]()]*?\[)([^\]]*)\]`+E,"g");function B(n,r){const e=h(r);return r.content===n||!e?n:`${n} \u2192 ${e}`}function h(n){return Y(n.content.replace(/^#+/,""))}function Y(n){return n.replace(z,"").replace(W,"$1").trim()}export{mt as getAiDocumentsStore};
@@ -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 y}from"../../utils/entitlements/can-access-feature.js";import{staticDataLoader as b}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 p=r.createTemplate(S(e),g.resolve(o.cwd,l));D(n,o,r);const{data:t}=await f.load(e,"react-frontmatter"),u=await F(e,"react-frontmatter",t,a),s=v(u,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:p,fsPath:e,sharedData:c?[{id:c,key:"sidebar"}]:void 0,[h]:y("rbac")?t?.rbac:void 0,getNavText:async()=>t?.seo?.title,getStaticData:async(m,d)=>({props:await b({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||301})}})}}}export{x as customPagesPlugin};
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};
@@ -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;
@@ -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 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 S}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 v}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 C}from"./tools/notifiers/reporter.js";import{logger as u}from"./tools/notifiers/logger.js";import{sha1 as I}from"./utils/crypto/sha1.js";import{FileNotFoundError as j}from"./utils/fs.js";import{resolveAssetPath as V}from"./utils/resolve-asset-path.js";import{envConfig as q,setEnv as J}from"./config/env-config.js";import{KvService as U}from"./persistence/kv/services/kv-service.js";import{SandboxContext as x}from"./utils/sandbox-context.js";import{isAllowedEnv as K}from"./utils/envs/get-api-route-allowed-env-variables.js";import{ensureDir as $,writeSharedData as W}from"./utils/index.js";import{renderComponents as z}from"./ssr/render.js";import{readStaticData as Y,writeStaticData as Z}from"./utils/static-data.js";import{getMarkdocOptions as Q}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as P}from"./entitlements/entitlements-provider.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 ce}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",le=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],We="userDefinedApiFunctions";class w{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;config=se();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,x.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 ce("com.redocly.build.reloadMarkdocOptions",async()=>{const e=P.instance(),t=await Q(this.serverOutDir),s=Object.fromEntries(Object.entries(t.tags).filter(([r])=>S[r]!=null?e.canAccessFeature(S[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()=>U.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}=v(e,i);(n||l!==this.replacedEnvVars[c].replaced)&&delete this.replacedEnvVars[c]}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(!P.instance().canAccessFeature("redirects")&&e!=="/")return;this.config.redirects||(this.config.redirects={});const a=d(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("*")&&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(E(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??I(r);return this.#t[e]===o||(this.#t[e]=o,await W(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),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)||{},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 h of l)o.has(h)||(o.add(h),a.push(h))}const c={};for(const i of o)t[i]&&(c[i]=t[i]);return c};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=L(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(!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?!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 V(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 j)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 Y(e.slug,this.outdir);const r={...this,contentDir:this.contentDir,parseMarkdoc:({input:l,context:h,resource:f})=>this.parseMarkdoc({input:l,context:h,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 h=this.sharedDataMarkdocComponents.get(l);h&&h.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(""):z(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 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]=q.PLAN_GATES,t}static#g(e,t){const s=new w(t);for(const[o,a]of Object.entries(A))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&&J("PLAN_GATES",r),s}async writeServerFiles(){$(this.serverOutDir),D(g(this.serverOutDir,b),JSON.stringify(this.#p())),D(g(this.serverOutDir,O),JSON.stringify(await this.getConfigWithEnvPlaceholders()))}static async fromServerFiles(e){const t=g(e.serverOutDir,O),{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,b),"utf8")),a=w.#g(o,e);a.config=s;const c=Object.fromEntries(Object.entries(r).filter(([i])=>K(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}=v(e,r);if(a||!_(c)&&!Array.isArray(c)){await C.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=>!le.includes(s));if(e.length===0)return;const t=`Failed to resolve config. The following environment variables are not set: ${e.join(", ")}`;await C.panicOnBuildContentError(t)}}function de(M){return N(M,e=>k.Ast.fromJSON(JSON.stringify(e)))}export{w as Store,We as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
@@ -111,7 +111,7 @@ export type PageRouteInit = Omit<PageRouteDetails<PageStaticData, PageProps>, 'v
111
111
  key: string;
112
112
  }>;
113
113
  redirectFrom?: Array<{
114
- type: number;
114
+ type?: number;
115
115
  from: string;
116
116
  }>;
117
117
  };
@@ -169,6 +169,14 @@ export type ProcessContentActions = {
169
169
  addRedirect: (from: string, to: RedirectConfig, options?: RedirectOptions) => void;
170
170
  createTemplate: (id: string, importPath: string) => string;
171
171
  addBrowserPlugin: (importPath: string) => void;
172
+ /**
173
+ * Resolves an asset `src` written in the page at `fromFileRelativePath` the way Markdown
174
+ * images resolve: a `static/` file keeps its path, a content file is published under a
175
+ * host-owned asset name, an l10n page falls back to the default-locale file, and an
176
+ * external URL is returned as is. The URL includes the path prefix. Resolves to `null`
177
+ * when the file does not exist; rejects on any other failure.
178
+ */
179
+ copyAsset: (src: string, fromFileRelativePath: string) => Promise<string | null>;
172
180
  registerServerPropsGetter: (id: string, importPath: string) => string;
173
181
  registerPagePropsGetter: (id: string, importPath: string) => void;
174
182
  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 { StatusCode } from 'hono/utils/http-status';
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: StatusCode;
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 e}from"../../../../utils/path/is-local-link.js";import{withPathPrefix as r}from"@redocly/theme/core/utils";function m({redirect:o,reqUrlSearch:t=""}){const n=e(o.to)?r(o.to):o.to;return{location:a(i(n))+t,type:o?.type||301}}function a(o){return encodeURI(o).replace(/%25([0-9A-Fa-f]{2})/g,"%$1")}export{m as processRedirects};
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 y,PUBLIC_MCP_ROUTE as $}from"@redocly/theme/core/constants/mcp";import{ServerRoutes as w}from"../../../constants/api.js";import{DEV_LOGIN_SLUG as M}from"../../../constants/common.js";import{CACHE_CONTROL_NO_STORE_HEADER_VALUE as a,DEFAULT_TITLE as F}from"../../constants/common.js";import{findInIterable as N}from"../../../utils/collection/find-in-iterable.js";import{removeTrailingSlash as B}from"../../../utils/url/remove-trailing-slash.js";import{envConfig as b}from"../../config/env-config.js";import{canAccessResource as q,filterDataByAccessDeep as T,isResourcePubliclyAccessible as H}from"../../utils/rbac.js";import{getServerProps as G}from"../../ssr/index.js";import{readSharedData as V}from"../../utils/index.js";import{getRedirectLoginUrl as W}from"../utils/get-redirect-login-url.js";import{processRedirects as k}from"./helpers/process-redirects.js";import{removeErrorDetails as K}from"../utils/remove-error-details.js";import{telemetry as z}from"../../telemetry/index.js";import{MCP_NOT_ALLOWED_TEMPLATE_ID as J}from"../../constants/mcp.js";function pe(e,n){return async(t,g)=>{const p=t.get("logger"),{req:m}=t,{pathname:d}=new URL(m.url),{seo:f,ssoDirect:h}=e.getConfig(),i=f?.title||F;if(e?.compilationErrors?.length&&b.isDevelopMode)return t.json({templateId:"compilation-error",props:{compilationErrors:e?.compilationErrors},sharedDataIds:{}},500,{"Cache-Control":a});const u=d.match(/page-data(.*)data.json$/);if(!u)return g();const c=decodeURI(u[1]),r=c==="/index/"?"/":B(c),o=e.getRouteBySlug(r,{followRedirect:!1})||N(e.routesBySlug.values(),s=>s.hasClientRoutes&&c.startsWith(s.slug+"/"));if(c===w.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===y||r===$&&!!e.globalData.mcpData?.docs?.publicEndpoint)&&e.globalData.mcpData?.docs?.enabled)return t.json({templateId:J,sharedDataIds:{},props:{},userData:l},405,{"Cache-Control":a});const E=e.getRedirect(r);if(E){const s=k({redirect:E}).location;return z.sendRedirectMessage([{id:r,object:"redirect",uri:`urn:redocly:realm:api:redirect:${r}`,from:r,templateId:"404"}]),t.json({templateId:"404",redirectTo:s,sharedDataIds:{},props:{}},301,{"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}`),!q(o,R,e.config.access?.rbac,e.config.access?.requiresLogin)&&o.slug!==M){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:W(e,o.slug)}:{}},s?401:404,{"Cache-Control":a})}const j=T(o.versions,R,e.config.access?.rbac,e.config.access?.requiresLogin),A=e.routesSharedData.get(o.slug)||{},S=await n(o),v=await G(o,t,S,e),{sharedDataIds:O,...L}=v,U={templateId:o.templateId,versions:j,sharedDataIds:{...A,...O||{}},props:b.isProductionEnv?K(L):L,slug:o.slug,userData:l,isPublic:H(o,e.config)};return t.json(U,200,{"Cache-Control":a})}}function me(e){return async(n,t)=>{const g=n.get("logger"),{req:p}=n,{pathname:m}=new URL(p.url),d=m.match(/\/page-data\/shared\/(.*)\.json/);if(!d)return t();const f=decodeURIComponent(d[1]),h=await V(f,e.outdir),{isAuthenticated:i,teams:u,claims:{email:c}}=n.get("auth"),r=T(h,{isAuthenticated:i,email:c,teams:u},e.config.access?.rbac,e.config.access?.requiresLogin);return r?n.json(r,200,{"Cache-Control":a}):(g.error(`Shared data not found: ${m}`),n.text("Not Found",404,{"Cache-Control":a}))}}export{pe as pageDataHandler,me as sharedPageDataHandler};
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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/reef",
3
- "version": "0.140.0",
3
+ "version": "0.141.0-next.1",
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.5.0",
37
- "@redocly/config": "0.72.0",
36
+ "@redocly/api-docs": "0.6.0-next.1",
37
+ "@redocly/config": "0.72.1",
38
38
  "@redocly/markdoc": "0.7.0-beta.2",
39
39
  "@redocly/openapi-core": "2.73.0",
40
40
  "@redocly/portal-legacy-ui": "0.19.0",
41
41
  "@redocly/portal-plugin-mock-server": "0.25.0",
42
42
  "@redocly/realm-asyncapi-sdk": "0.17.0",
43
- "@redocly/theme": "0.72.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",