@redocly/reef 0.137.0-next.7 → 0.137.0-next.9
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 +36 -0
- package/dist/client/templates/api-docs/template.js +8 -3
- package/dist/constants/l10n/langs/ar.js +1 -1
- package/dist/constants/l10n/langs/de.js +1 -1
- package/dist/constants/l10n/langs/en.js +1 -1
- package/dist/constants/l10n/langs/es.js +1 -1
- package/dist/constants/l10n/langs/fr.js +1 -1
- package/dist/constants/l10n/langs/hi.js +1 -1
- package/dist/constants/l10n/langs/it.js +1 -1
- package/dist/constants/l10n/langs/ja.js +1 -1
- package/dist/constants/l10n/langs/ko.js +1 -1
- package/dist/constants/l10n/langs/pl.js +1 -1
- package/dist/constants/l10n/langs/pt-BR.js +1 -1
- package/dist/constants/l10n/langs/pt.js +1 -1
- package/dist/constants/l10n/langs/ru.js +1 -1
- package/dist/constants/l10n/langs/uk.js +1 -1
- package/dist/constants/l10n/langs/zh.js +1 -1
- package/dist/server/config/env-schemas/feature-flags.d.ts +1 -1
- package/dist/server/constants/common.d.ts +1 -0
- package/dist/server/constants/common.js +1 -1
- package/dist/server/plugins/mcp/handlers/docs-mcp-handler.js +1 -1
- package/dist/server/plugins/mcp/handlers/errors.d.ts +1 -1
- package/dist/server/plugins/mcp/handlers/errors.js +1 -1
- package/dist/server/plugins/mcp/servers/docs-server.d.ts +2 -2
- package/dist/server/plugins/mcp/servers/docs-server.js +1 -1
- package/dist/server/plugins/mcp/servers/mcp-server.d.ts +2 -11
- package/dist/server/plugins/mcp/servers/mcp-server.js +3 -3
- package/dist/server/plugins/search/ai-indexer/prepare-semantic-documents.d.ts +4 -2
- package/dist/server/plugins/search/ai-indexer/prepare-semantic-documents.js +1 -3
- package/dist/server/plugins/search/index.js +1 -1
- package/dist/server/plugins/search/llmstxt/build-page-llms-txt-md.d.ts +4 -0
- package/dist/server/plugins/search/llmstxt/build-page-llms-txt-md.js +3 -0
- package/dist/server/plugins/search/llmstxt/create-llms-txt-generator.d.ts +4 -0
- package/dist/server/plugins/search/llmstxt/create-llms-txt-generator.js +1 -0
- package/dist/server/plugins/search/llmstxt/get-llms-txt-exclude-matcher.d.ts +4 -0
- package/dist/server/plugins/search/llmstxt/get-llms-txt-exclude-matcher.js +1 -0
- package/dist/server/plugins/search/llmstxt/index.d.ts +2 -2
- package/dist/server/plugins/search/llmstxt/is-included-in-llms-txt.d.ts +6 -0
- package/dist/server/plugins/search/llmstxt/is-included-in-llms-txt.js +1 -0
- package/dist/server/store.js +1 -1
- package/dist/server/types/web-server.d.ts +8 -0
- package/dist/server/utils/envs/get-api-route-allowed-env-variables.js +1 -1
- package/dist/server/utils/get-route-ai-documents-store.d.ts +5 -0
- package/dist/server/utils/get-route-ai-documents-store.js +1 -0
- package/dist/server/utils/get-route-static-data.d.ts +4 -0
- package/dist/server/utils/get-route-static-data.js +1 -0
- package/dist/server/utils/llmstxt/agent-feedback-llms-footer.js +1 -1
- package/dist/server/utils/llmstxt/resolve-llms-txt-md.d.ts +10 -0
- package/dist/server/utils/llmstxt/resolve-llms-txt-md.js +1 -0
- package/dist/server/web-server/auth.d.ts +13 -2
- package/dist/server/web-server/auth.js +3 -3
- package/dist/server/web-server/dev-server.js +1 -1
- package/dist/server/web-server/routes/auth.js +1 -1
- package/dist/server/web-server/routes/dynamic-route.d.ts +2 -1
- package/dist/server/web-server/routes/dynamic-route.js +1 -1
- package/dist/server/web-server/routes/index.d.ts +2 -1
- package/dist/server/web-server/routes/index.js +1 -1
- package/dist/server/web-server/routes/llms-txt.d.ts +4 -0
- package/dist/server/web-server/routes/llms-txt.js +1 -0
- package/package.json +10 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
import{logger as
|
|
1
|
+
import{logger as u}from"../../../tools/notifiers/logger.js";import{getFeedbackClientIp as N}from"../../../web-server/utils/feedback-ip.js";import{getForwardedOrigin as g,getInternalOrigin as F}from"../../../web-server/utils/get-request-origin.js";import{GATEWAY_FETCH_LIMIT_DEFAULTS as v}from"../../../../constants/common.js";import{isObject as k}from"../../../../utils/guards/is-object.js";import{getAccessibleSkills as B}from"../../../utils/skills/get-accessible-skills.js";import{getSkillUrl as L}from"../../../utils/skills/get-skill-url.js";import{createMcpHandler as H}from"@modelcontextprotocol/server";import{createDocsMcpServer as V}from"../servers/docs-server.js";import{filterApiDescriptionsByRbac as $,filterGraphqlDescriptionsByRbac as j}from"../utils.js";import{McpClientInputError as G,createMethodNotAllowedError as J,withErrorHandling as K}from"./errors.js";import{constructForbiddenResponse as Y,constructInvalidTokenResponse as z,constructUnauthorizedResponse as W,handleMcpAuth as X}from"../auth/auth-handlers.js";import{shouldHandleMcpAuth as Q}from"../../../utils/rbac/should-handle-mcp-auth.js";import{canAnonymousAccessMcpFeature as Z}from"../../../utils/rbac/can-anonymous-access-mcp-feature.js";import{extractTokenFromAuthHeader as q}from"../utils/jwt.js";import{resolveMcpClient as x}from"../utils/mcp-client.js";import{DEFAULT_DOCS_MCP_SERVER_NAME as ee,SSE_KEEPALIVE_INTERVAL_MS as re}from"../constants.js";import{withPathPrefix as oe}from"@redocly/theme/core/utils";import{PUBLIC_MCP_ROUTE as se}from"@redocly/theme/core/constants/mcp";import{canAccessFeature as ne}from"../../../utils/rbac.js";import{ANONYMOUS_AUTH_DETAILS as ie}from"../../../constants/rbac.js";import{DEFAULT_ANONYMOUS_VISITOR_TEAM as te,RbacFeatures as ce}from"../../../../constants/common.js";const h="X-Redocly-AI-Metadata";function ae(r){if(r)try{const e=JSON.parse(r);if(k(e))return e;u.warn(`Ignoring ${h} header: not a JSON object.`)}catch{u.warn(`Ignoring ${h} header: invalid JSON.`)}}const pe=async(r,e,t)=>{const n=!!e?.config?.access?.requiresLogin,i=e?.config?.access?.rbac||{},A=t?.props?.config?.mcpMode??"tools",T=t?.props?.config?.gatewayFetchBudgetPerExec??v.budgetPerExec,S=t?.props?.config?.searchIgnoredRoutes??[],s=t,b=!!s?.props?.config?.isPublicEndpoint;let p;const E=r.headers.get("Authorization");if(b)e.user={...ie,email:"",idpAccessToken:void 0,idpId:void 0};else if(Q(n,i)){const{isAuthenticated:d,isTokenValid:l,currentUser:a,accessToken:m}=await X(r,e);if(!d){const c=g(r),f=e.config.mcp?.docs?.publicEndpoint&&Z(i,n)?`${c}${oe(se)}`:void 0;return l===!1?z(c):W(c,f)}a&&(e.user=a),p=m}else E&&(p=q(E||"")??void 0);const I={isAuthenticated:!!e.user?.isAuthenticated,teams:e.user?.teams||[te],claims:e.user?.claims??{}};return ne(ce.MCP,I,i,n)?await K(async()=>{const d=t?.props?.config?.elicitationEnabled??!1;if(r.method==="GET")return new Response(JSON.stringify({error:"Method Not Allowed",message:`In order to use this MCP server, you need to register it in your MCP client (VS Code, Cursor, Claude Code, etc.) using this URL: ${r.url}`}),{status:405,headers:{"Content-Type":"application/json"}});if(r.method!=="POST")return J();const l=s?.props?.config?.apiDescriptionsMap||{},a=s?.props?.config?.graphqlDescriptions||[],m=s?.props?.config?.mcpDocsServerName||ee,{config:{mcp:c={}}}=e,f=$(l,e.user,i,n),y=j(a,e.user,i,n),U=e.config.products?Object.values(e.config.products).map(o=>o?.name):[],w=c.docs?.name||m,C=ae(r.headers.get(h)),R=s?.props?.tools||[],_=s?.props?.skillsBaseUrl||g(r),D=B(s?.props?.skills||[],e.user,i,n).map(o=>({slug:o.slug,name:o.name,description:o.description,content:o.content,uri:L(_,o.slug)})),M=await de(r),O={name:w,tools:R,skills:D,context:{...e,accessToken:p,outdir:e.outdir||"",baseUrl:e.baseUrl||g(r),internalBaseUrl:F(r),apiDescriptionsMap:f,graphqlDescriptions:y,products:U,metadata:C,mcpMode:A,elicitationEnabled:d,gatewayFetchBudgetPerExec:T,searchIgnoredRoutes:S,clientIp:N(r),...x(r)}},P=await V({...O,getKv:e.getKv});return await H(async()=>P,{keepAliveMs:re,onerror:o=>u.warn(`MCP handler: ${o.message}`)}).fetch(r,M!==void 0?{parsedBody:M}:void 0)},A):Y()};async function de(r){try{return await r.json()}catch(e){throw new G(e instanceof Error?e.message:String(e),{cause:e})}}var Fe=pe;export{Fe as default};
|
|
@@ -21,5 +21,5 @@ export declare function createMethodNotAllowedError(requestId?: string | number
|
|
|
21
21
|
export declare function createInternalServerError(error: Error | string, requestId?: string | number | null): Response;
|
|
22
22
|
export declare function createInvalidRequestError(message?: string, requestId?: string | number | null): Response;
|
|
23
23
|
export declare function createInvalidParamsError(message?: string, requestId?: string | number | null): Response;
|
|
24
|
-
export declare function withErrorHandling<T>(operation: () => Promise<T>,
|
|
24
|
+
export declare function withErrorHandling<T>(operation: () => Promise<T>, mode?: McpMode): Promise<T | Response>;
|
|
25
25
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{telemetry as c}from"../../../telemetry/index.js";import{McpErrorCodes as o,McpServerType as i}from"../constants.js";import{serverAttributes as d}from"../utils/telemetry-attributes.js";class
|
|
1
|
+
import{telemetry as c}from"../../../telemetry/index.js";import{McpErrorCodes as o,McpServerType as i}from"../constants.js";import{serverAttributes as d}from"../utils/telemetry-attributes.js";class s extends Error{code;data;requestId;constructor(e,t,n,a){super(t),this.name="McpError",this.code=e,this.data=n,this.requestId=a}createErrorResponse(){const e={jsonrpc:"2.0",error:{code:this.code,message:this.message,...this.data?{data:this.data}:{}},id:this.requestId??null},t=p(this.code);return new Response(JSON.stringify(e),{status:t,headers:{"Content-Type":"application/json"}})}}class u extends Error{constructor(e,t){super(e,t),this.name="McpClientInputError"}}function p(r){switch(r){case o.InvalidRequest:case o.InvalidParams:return 400;case o.MethodNotFound:return 404;case o.ServerError:return 405;case o.InternalError:default:return 500}}function I(r){return new s(o.ServerError,"Method not allowed",void 0,r).createErrorResponse()}function l(r,e){const t=r instanceof Error?r.message:String(r);return new s(o.InternalError,"Internal server error mcp",t,e).createErrorResponse()}function f(r="Invalid request",e){return new s(o.InvalidRequest,r,void 0,e).createErrorResponse()}function v(r="Invalid parameters",e){return new s(o.InvalidParams,r,void 0,e).createErrorResponse()}async function M(r,e="tools"){try{return await r()}catch(t){return c.sendMcpErrorMessage([{...d(i.Docs),message:t?.message||"",stack:t?.stack||"",error_class:t instanceof u?"client_error":"server_error",mode:e}]),l(t)}}export{u as McpClientInputError,s as McpError,l as createInternalServerError,v as createInvalidParamsError,f as createInvalidRequestError,I as createMethodNotAllowedError,M as withErrorHandling};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
1
2
|
import type { McpToolSchema } from '../../../types';
|
|
2
3
|
import type { McpToolContext, SkillMcpResource } from '../types.js';
|
|
3
4
|
import type { KvService } from '@redocly/config';
|
|
4
|
-
import { type McpServerInstance } from './mcp-server.js';
|
|
5
5
|
export declare function createDocsMcpServer({ name, tools, context, skills, getKv, }: {
|
|
6
6
|
name: string;
|
|
7
7
|
tools: McpToolSchema[];
|
|
8
8
|
context: McpToolContext;
|
|
9
9
|
skills?: SkillMcpResource[];
|
|
10
10
|
getKv: () => Promise<KvService>;
|
|
11
|
-
}): Promise<
|
|
11
|
+
}): Promise<McpServer>;
|
|
12
12
|
//# sourceMappingURL=docs-server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{logger as
|
|
1
|
+
import{logger as S}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as M,MCP_TOOL_IS_AVAILABLE_KEY as v}from"../../../workers/mcp-tool-worker-pool.js";import{CREATE_ANONYMOUS_SESSION_TOOL_NAME as k}from"../constants.js";import{isAnonymousSessionToolAvailable as C}from"../docs-mcp/tools/core/create-anonymous-session.js";import{isManageApiCredentialsToolAvailable as L,manageApiCredentials as y,manageApiCredentialsSchema as D}from"../codemode/tools/manage-credentials.js";import{EXECUTE_TOOL_SCHEMA as A}from"../codemode/tools/execute-schema.js";import{EXECUTE_TOOL_NAME as w,CODE_MODE_TOOL_NAMES as N,FETCH_TOOL_NAME as x}from"../codemode/constants.js";import{buildCodeModeInstructions as H,buildExecuteDescription as I}from"../codemode/prompts.js";import{toToolListEntries as U}from"../codemode/build-tool-list.js";import{composeExecuteAnnotations as F,getActiveCapabilities as P}from"../codemode/capabilities/index.js";import{describeToolsSchema as R,describeTools as z}from"../codemode/tools/describe-tools.js";import{availableSandboxTools as W}from"../utils/available-sandbox-tools.js";import{toRegisterToolConfig as _}from"../utils/tool-registration.js";import{createMcpServer as X,createToolDispatch as Y,extractSerializableContext as $}from"./mcp-server.js";function g(c,r){const o=r.schema;if(C(c))return o;const{sessionHandle:f,...m}=o.properties;return{...o,properties:m}}async function ae({name:c,tools:r,context:o,skills:f=[],getKv:m}){const l=$(o),a=o.mcpMode==="code",s=a?P(o):[],T=s.some(e=>e.id===x),p=a?await W(r.filter(e=>!N.has(e.name)),l):[],b=U(p,s),O=a&&L(o),n=X(c,a?H(T,b):void 0),E={getClientCapabilities:()=>n.server.getClientCapabilities(),getKv:m},d=Y(l,E),u=async e=>{try{return await M.exec(v,[{toolName:e,context:l}],{timeout:1e4})}catch(i){return S.error(`Failed to check MCP tool availability for "${e}": ${i instanceof Error?i.message:String(i)}`),!1}};if(a){const e=[{tool:{...A,description:I(T,b),schema:g(o,A),annotations:F(s)},callback:d(w)},{tool:R(p,s),callback:z(p,u,s,o)}],i=r.find(t=>t.name===k);if(i&&C(o)&&e.push({tool:i,callback:d(i.name)}),O){const t=D(o);e.push({tool:{...t,schema:g(o,t)},callback:y(l,E)})}for(const{tool:t,callback:h}of e)n.registerTool(t.name,_(t),h)}else await Promise.all(r.map(async e=>{const i=n.registerTool(e.name,_(e),d(e.name));await u(e.name)||i.disable()}));for(const e of f)n.registerResource(e.slug,e.uri,{title:e.name,description:e.description,mimeType:"text/markdown"},async()=>({contents:[{uri:e.uri,mimeType:"text/markdown",text:e.content}]}));return n}export{ae as createDocsMcpServer};
|
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
import { McpServer
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/server';
|
|
2
2
|
import type { McpToolCallback, McpToolContext } from '../types.js';
|
|
3
3
|
import type { KvService } from '@redocly/config';
|
|
4
|
-
export
|
|
5
|
-
server: McpServer;
|
|
6
|
-
transport: WebStandardStreamableHTTPServerTransport;
|
|
7
|
-
cleanup: () => Promise<void>;
|
|
8
|
-
};
|
|
9
|
-
export declare function createMcpServer(name: string, instructions?: string): {
|
|
10
|
-
server: McpServer;
|
|
11
|
-
transport: WebStandardStreamableHTTPServerTransport;
|
|
12
|
-
};
|
|
13
|
-
export declare function connectMcpServer(server: McpServer, transport: WebStandardStreamableHTTPServerTransport): Promise<McpServerInstance>;
|
|
4
|
+
export declare function createMcpServer(name: string, instructions?: string): McpServer;
|
|
14
5
|
export type ToolDispatchDeps = {
|
|
15
6
|
getClientCapabilities: () => {
|
|
16
7
|
elicitation?: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{McpServer as
|
|
1
|
+
import{McpServer as j}from"@modelcontextprotocol/server";import{logger as b}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as D,MCP_TOOL_WORKER_KEY as F}from"../../../workers/mcp-tool-worker-pool.js";import{secretStore as K}from"../gateway-mcp/secret-store.js";import{readAuthRequiredMeta as L,stripAuthRequiredMeta as M,upstreamAuthKey as S}from"../gateway-mcp/upstream-auth.js";import{buildCredentialsToolHint as H,buildSchemeChoiceHint as B,buildUpstreamCredentialFallback as q,buildUpstreamCredentialInputRequired as P,isCredentialElicitationDeclined as $,schemeLabelOf as x}from"../gateway-mcp/elicitation.js";import{DEFAULT_SCHEME_NAME as T,resolveUpstreamApiAuth as N,schemeChoicesOf as v}from"../gateway-mcp/upstream-auth-scheme.js";import{MISSING_SESSION_HANDLE_MESSAGE as G,resolveSecretSubject as V}from"../gateway-mcp/subject.js";const W=6e4,E=new Map;function Y(e,t){const r=E.get(e);if(r)return r;const n=t().finally(()=>{E.delete(e)});return E.set(e,n),n}function pe(e,t){return new j({name:e,version:new Date().toISOString().slice(0,10)},{capabilities:{logging:{}},...t?{instructions:t}:{}})}function de(e,t){return r=>async(n,s)=>{b.info(`MCP tool called: ${r}`);const i=e.elicitationEnabled??!1,c=s.mcpReq.envelope!==void 0,a=i?V(e.user,n.sessionHandle):{kind:"anonymous"},o=a.kind==="resolved"?a.subject:void 0;if(a.kind==="invalid")return{content:[{type:"text",text:a.message}],isError:!0};const p=async()=>{if(!(!i||!o||!t?.getKv))try{const m=await t.getKv();return await K.getSecretsForSubject(m,o)}catch(m){b.warn(`gateway mcp secret store error, continuing unauthenticated: ${String(m)}`);return}};let u;const l=async()=>{u=await p();const m={toolName:r,args:n,context:{...e,upstreamSecrets:u},extra:re(s)};return await D.exec(F,[m],{timeout:W})};let d=await l();const f=L(d);if(f.length>0&&i&&!o)return M({...d,content:[{type:"text",text:G}],isError:!0});if(f.length>0&&i&&o&&t?.getKv){b.info(`gateway mcp credentials prompt: era=${c?"2026-07-28":"2025"} clientElicitation=${Q(t)}`);const m=await t.getKv(),h=await X(e,f),k=f.map(_=>Z(_,h)),y=z(u),g=c?await ee(e,s,k,o,m,y,h,t):await te(e,k,o,m,y,h);switch(g.kind){case"inputRequired":return g.result;case"retry":d=await l();break;case"prependText":d={...d,content:[{type:"text",text:g.text},...d.content],isError:!0};break;case"none":break}}return M(d)}}function J(e){return!!e?.getClientCapabilities()?.elicitation?.url}function Q(e){const t=e?.getClientCapabilities()?.elicitation;if(!t)return"none";const r=[t.form?"form":void 0,t.url?"url":void 0].filter(Boolean);return r.length>0?r.join("+"):"none"}async function X(e,t){const r=new Map;for(const n of new Set(t.map(s=>s.api)))r.set(n,await N(e,n));return r}function Z(e,t){const r=t.get(e.api);if(!r)return{...e,scheme:e.scheme??T};const n=e.scheme&&r.schemes[e.scheme]?e.scheme:r.defaultScheme;return{...e,scheme:n}}function z(e){const t=new Set;for(const[r,n]of Object.entries(e??{}))for(const s of Object.keys(n))t.add(S({api:r,scheme:s}));return t}function R(e,t){return e.status===401&&!t.has(S(e))}function A(e,t){const r=t.get(e.api),n=e.sentScheme??e.scheme;return r&&n?x(r.schemes,n):void 0}function I(e,t){const r=t.get(e.api);return!r||!e.schemes?.length?[]:v(r,e.schemes)}function C(e,t){return I(e,t).length>1}function w(e,t){return e.map(r=>H(r,A(r,t))).join(`
|
|
2
2
|
|
|
3
|
-
`)}function
|
|
3
|
+
`)}function U(e,t,r){const n=e.map(s=>B(s,I(s,r)));return t.length>0&&n.push(w(t,r)),n.join(`
|
|
4
4
|
|
|
5
|
-
`)}function
|
|
5
|
+
`)}function O(e,t,r,n,s){const[i,...c]=t,a=s.get(i.api),o=i.scheme??a?.defaultScheme??T,p=A(i,s);return{subject:r,baseUrl:e.baseUrl??"",target:{api:i.api,name:i.name,scheme:o,...p?{schemeLabel:p}:{}},auth:a?.schemes[o]??{kind:"raw"},...a?{apiAuth:a}:{},kv:n,hasExistingSecret:!1,reason:{kind:"authFailure",status:i.status,...i.detail?{detail:i.detail}:{},otherApiNames:c.map(u=>u.name)}}}async function ee(e,t,r,n,s,i,c,a){if($(t.mcpReq.inputResponses))return{kind:"none"};const o=r.filter(l=>R(l,i));if(o.length===0)return{kind:"prependText",text:w(r,c)};const p=o.filter(l=>C(l,c));if(p.length>0){const l=r.filter(d=>!p.includes(d));return{kind:"prependText",text:U(p,l,c)}}const u=O(e,o,n,s,c);return J(a)?{kind:"inputRequired",result:await P(u)}:{kind:"prependText",text:await q(u)}}async function te(e,t,r,n,s,i){const c=t.filter(u=>R(u,s));if(c.length===0)return{kind:"prependText",text:w(t,i)};const a=c.filter(u=>C(u,i));if(a.length>0){const u=t.filter(l=>!a.includes(l));return{kind:"prependText",text:U(a,u,i)}}const o=O(e,c,r,n,i);return{kind:"prependText",text:await Y(`${r}:${S(c[0])}`,()=>q(o))}}function re(e){return{sessionId:e.sessionId,authInfo:e.http?.authInfo,requestId:e.mcpReq.id,_meta:e.mcpReq._meta}}function me(e){return{user:e.user,config:e.config,outdir:e.outdir,baseUrl:e.baseUrl,params:e.params,query:e.query,cookies:e.cookies,apiDescriptionsMap:e.apiDescriptionsMap,graphqlDescriptions:e.graphqlDescriptions,products:e.products,accessToken:e.accessToken,metadata:e.metadata,upstreamAuthorization:e.upstreamAuthorization,mcpMode:e.mcpMode,elicitationEnabled:e.elicitationEnabled,gatewayFetchBudgetPerExec:e.gatewayFetchBudgetPerExec,clientIp:e.clientIp,client:e.client,clientVersion:e.clientVersion,upstreamSecrets:e.upstreamSecrets,searchIgnoredRoutes:e.searchIgnoredRoutes,internalBaseUrl:e.internalBaseUrl,fs:void 0}}export{pe as createMcpServer,de as createToolDispatch,me as extractSerializableContext};
|
|
@@ -3,9 +3,11 @@ import type { AfterRoutesCreatedActions, AfterRoutesCreatedLifecycleContext } fr
|
|
|
3
3
|
* Prepares chunked documents for semantic search, llms.txt, and Docs MCP.
|
|
4
4
|
* Chunking runs when any of these is enabled: AI search, llms.txt, or Docs MCP.
|
|
5
5
|
* embeddingsEnabled is true when AI search or Docs MCP is enabled (shared embedding index).
|
|
6
|
+
* writeLlmstxtFiles is false in develop mode, where the same content is generated on request
|
|
7
|
+
* instead (see plugins/search/llmstxt/create-llms-txt-generator.ts).
|
|
6
8
|
*/
|
|
7
|
-
export declare function prepareSemanticDocuments(actions: AfterRoutesCreatedActions, context: AfterRoutesCreatedLifecycleContext, { embeddingsEnabled,
|
|
9
|
+
export declare function prepareSemanticDocuments(actions: AfterRoutesCreatedActions, context: AfterRoutesCreatedLifecycleContext, { embeddingsEnabled, writeLlmstxtFiles, }: {
|
|
8
10
|
embeddingsEnabled: boolean;
|
|
9
|
-
|
|
11
|
+
writeLlmstxtFiles: boolean;
|
|
10
12
|
}): Promise<void>;
|
|
11
13
|
//# sourceMappingURL=prepare-semantic-documents.d.ts.map
|
|
@@ -1,3 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
${g}`:"";await R(r,`${n.content}${D}`),n.includeInLLMsTxt&&W(e,u)&&d.push(n)}if(o&&C.isBuildMode){const n=await x.getSearchDocuments();if(!n.length)return;const A=tt(e,O),r=e.versions?.find(({active:D})=>D),g=r&&{folder:r.folderId,label:r.label,default:r.default};await et(n,e.fsPath,A,M,b,g)}}}),o&&c.infoTime(a,"Semantic search documents prepared"),d.length)try{J(f);const e=await V(d,f,{title:m?.title,description:m?.description},i);F(l),await R(l,e),c.infoTime(p,`${S} files generated`)}catch(e){c.error(`${S} Failed to generate llms.txt file. ${e.message}`)}}async function Q(t,i){return t.getStaticData?t.getStaticData(t,i):{}}async function Z(t,i,o,s){if(t.getAiDocumentsStore)return t.getAiDocumentsStore(t,{...i,[h]:t[h],[_]:t[_]},o,s)}function tt(t,i){const o=q(t?.[h]);return o?.length?o:z(t,i)}async function et(t,i,o,s,l,p){await P(t,w,async a=>{const m=`${H(i+a.content)}.json`,f=F(L.join(s,m)),T=a.locale===E?l:a.locale;N(f)||await R(f,JSON.stringify({...a,rbacTeams:a.rbacTeams??o??[],version:p,locale:T}),"utf-8")})}export{At as prepareSemanticDocuments};
|
|
1
|
+
import p from"node:path";import{existsSync as O}from"node:fs";import{writeFile as h,access as y,rm as v,constants as b}from"fs/promises";import{REDOCLY_TEAMS_RBAC as N}from"@redocly/config";import{DEFAULT_LOCALE_PLACEHOLDER as P}from"../../../../constants/common.js";import{AI_INDEX_EXPORT_FOLDER as X}from"../../../constants/plugins/search.js";import{LLMS_TXT_FILE_NAME as $}from"../../../constants/common.js";import{envConfig as j}from"../../../config/env-config.js";import{logger as n}from"../../../tools/notifiers/logger.js";import{shaHexShort as B}from"../../../utils/crypto/sha-hex-short.js";import{promiseMapLimit as D}from"../../../utils/async/promise-map-limit.js";import{getLlmsTxtMdPathBySlug as G}from"../../../utils/llmstxt/get-llms-txt-md-path-by-slug.js";import{validateLLMsTxtConfig as H,generateLLMsTxt as k}from"../llmstxt/index.js";import{buildPageLlmsTxtMd as J}from"../llmstxt/build-page-llms-txt-md.js";import{getLlmsTxtExcludeMatcher as K}from"../llmstxt/get-llms-txt-exclude-matcher.js";import{isIncludedInLlmsTxt as U}from"../llmstxt/is-included-in-llms-txt.js";import{getRouteStaticData as V}from"../../../utils/get-route-static-data.js";import{getRouteAiDocumentsStore as W}from"../../../utils/get-route-ai-documents-store.js";import{ensureDir as R}from"../../../utils/fs.js";import{extractTeamsFromScopeItems as Y,getRbacTeamsListForResource as q}from"../../../utils/rbac.js";const _=20,S="llms.txt:";async function ht(t,m,{embeddingsEnabled:e,writeLlmstxtFiles:l}){const c=p.join(t.outdir,$);if(!l){try{await y(c,b.W_OK),await v(c)}catch{}if(!e)return}e&&n.info("Preparing semantic documents..."),l&&n.info(`${S} Generating llms.txt files...`);const g=n.startTiming(),r=n.startTiming(),s=t.getGlobalConfig("seo"),f=s?.llmstxt,T=p.resolve(t.outdir,X),u=t.getConfig(),A=u.access?.rbac??{},M=t.getAllRoutes(),C=u?.l10n?.defaultLocale||u?.i18n?.defaultLocale||P,E=K(s),L=[];if(await D(M,_,async o=>{if(o.excludeFromSearch)return;const w=await V(o,t),x=await W(o,w,m,t);if(x){if(l)for(const i of await x.getLLMsTxts()){if(E(i.fsPath))continue;const d=G(i.slug),a=p.join(t.outdir,d);R(a),await h(a,J(i,s)),U(i,o,u)&&L.push(i)}if(e&&j.isBuildMode){const i=await x.getSearchDocuments();if(!i.length)return;const d=z(o,A),a=o.versions?.find(({active:F})=>F),I=a&&{folder:a.folderId,label:a.label,default:a.default};await Q(i,o.fsPath,d,T,C,I)}}}),e&&n.infoTime(r,"Semantic search documents prepared"),L.length)try{H(f);const o=await k(L,f,{title:s?.title,description:s?.description},m);R(c),await h(c,o),n.infoTime(g,`${S} files generated`)}catch(o){n.error(`${S} Failed to generate llms.txt file. ${o.message}`)}}function z(t,m){const e=Y(t?.[N]);return e?.length?e:q(t,m)}async function Q(t,m,e,l,c,g){await D(t,_,async r=>{const s=`${B(m+r.content)}.json`,f=R(p.join(l,s)),T=r.locale===P?c:r.locale;O(f)||await h(f,JSON.stringify({...r,rbacTeams:r.rbacTeams??e??[],version:g,locale:T}),"utf-8")})}export{ht as prepareSemanticDocuments};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DEFAULT_SEARCH_ENGINE as
|
|
1
|
+
import{DEFAULT_SEARCH_ENGINE as o,REDOCLY_TEAMS_RBAC as u}from"@redocly/config";import{envConfig as i}from"../../config/env-config.js";import{logger as E}from"../../tools/notifiers/logger.js";import{reporter as S}from"../../tools/notifiers/reporter.js";import{prepareSearchDocuments as b}from"./documents/search-documents.js";import{SearchEngine as D}from"./engines/search-engine.js";import{prepareSemanticDocuments as y}from"./ai-indexer/prepare-semantic-documents.js";import{isAiSearchEnabled as A,shouldGenerateEmbeddings as C,shouldIndexSearchDocuments as w}from"./utils.js";import{telemetryTraceStep as R}from"../../../cli/telemetry/helpers/trace-step.js";import{EntitlementsProvider as _}from"../../entitlements/entitlements-provider.js";async function P(){let e=!0;return{async afterRoutesCreated(t,n){(i.SEARCH_DEV_REINIT||e)&&await v(t,n),e=!1},id:"search"}}async function v(e,t){await R("com.redocly.build.plugin.search",async n=>{const r=e.getConfig();if(n?.setAttribute("config",JSON.stringify(r.search||{})),r.search?.hide){E.info("Search is disabled in the config, skipping");return}const m=r.search?.engine||o,s=i.isDevelopMode?i.SEARCH_DEV_DEBUG?m:o:m,{l10n:l}=e.getGlobalData(),h=A(r),c=r.access?.rbac?.features?.aiSearch,d=C(r);let a;try{a=new D(s,l)}catch(p){await S.panic(p);return}await a.initIndexSchema(e.getSearchFacets()),e.setSearchEngine(a),e.setGlobalData({searchFeatures:{advanced:{enabled:s!==o&&!r.search?.filters?.hide},ai:{enabled:h,[u]:c&&Object.keys(c).length?c:void 0}}}),w(s)&&await b(t.fs,e,a);const f=_.instance(),g=!i.isDevelopMode&&f.canAccessFeature("seo")===!0&&r.seo?.llmstxt?.hide!==!0;await y(e,t,{embeddingsEnabled:d,writeLlmstxtFiles:g})})}export{P as searchPlugin};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{envConfig as t}from"../../../config/env-config.js";import{buildAgentFeedbackLlmsFooter as r}from"../../../utils/llmstxt/agent-feedback-llms-footer.js";function d(n,e){const o=t.REDOCLY_EXP_LLMSTXT_AGENT_FEEDBACK_ENABLED?r(e,n.slug):void 0;return`${n.content}${o?`
|
|
2
|
+
|
|
3
|
+
${o}`:""}`}export{d as buildPageLlmsTxtMd};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Store } from '../../../store';
|
|
2
|
+
import type { LifecycleContext, LlmsTxtGenerator } from '../../../types';
|
|
3
|
+
export declare function createLlmsTxtGenerator(store: Store, context: LifecycleContext): LlmsTxtGenerator;
|
|
4
|
+
//# sourceMappingURL=create-llms-txt-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import p from"node:path";import{logger as f}from"../../../tools/notifiers/logger.js";import{promiseMapLimit as x}from"../../../utils/async/promise-map-limit.js";import{getLlmsTxtMdPathBySlug as L}from"../../../utils/llmstxt/get-llms-txt-md-path-by-slug.js";import{getRouteStaticData as T}from"../../../utils/get-route-static-data.js";import{getRouteAiDocumentsStore as w}from"../../../utils/get-route-ai-documents-store.js";import{generateLLMsTxt as h,validateLLMsTxtConfig as M}from"./index.js";import{buildPageLlmsTxtMd as S}from"./build-page-llms-txt-md.js";import{getLlmsTxtExcludeMatcher as m}from"./get-llms-txt-exclude-matcher.js";import{isIncludedInLlmsTxt as R}from"./is-included-in-llms-txt.js";import{EntitlementsProvider as C}from"../../../entitlements/entitlements-provider.js";const A=20;function v(i,c){const a=new WeakMap,l=()=>{const t=i.getGlobalConfig("seo");return C.instance().canAccessFeature("seo")===!0&&t?.llmstxt?.hide!==!0},d=async t=>{if(t.excludeFromSearch)return[];const e=i,n=await T(t,e);return await(await w(t,n,c,e))?.getLLMsTxts()??[]},u=t=>{const e=a.get(t);if(e)return e;const n=d(t).catch(o=>(a.delete(t),f.warn(`Failed to generate Markdown for LLMs of "${t.slug}". ${o}`),[]));return a.set(t,n),n},g=t=>{const e=t==="/index.html.md"?"/":t.replace(/\.md$/,"");for(let n=e;n.startsWith("/");){const o=i.getRouteBySlug(n,{followRedirect:!1});if(o?.getAiDocumentsStore)return o;const r=p.posix.dirname(n);if(r===n)return;n=r}};return{async getPageMarkdown(t){if(!l())return;const e=g(t);if(!e)return;const n=i.getGlobalConfig("seo"),o=m(n),r=(await u(e)).find(s=>L(s.slug)===t&&!o(s.fsPath));if(r)return{route:e,content:S(r,n)}},async getLlmsTxt(){if(!l())return;const t=i.getConfig(),e=i.getGlobalConfig("seo"),n=m(e),o=[];if(await x(i.getAllRoutes(),A,async r=>{for(const s of await u(r))!n(s.fsPath)&&R(s,r,t)&&o.push(s)}),!!o.length)try{return M(e?.llmstxt),await h(o,e?.llmstxt,{title:e?.title,description:e?.description},c)}catch(r){f.error(`Failed to generate llms.txt. ${r}`);return}}}}export{v as createLlmsTxtGenerator};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"picomatch";function l(t){return e(t?.llmstxt?.excludeFiles||[])}export{l as getLlmsTxtExcludeMatcher};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { SeoConfig } from '@redocly/config';
|
|
2
|
-
import type {
|
|
2
|
+
import type { LifecycleContext } from '../../../types';
|
|
3
3
|
import type { LLMsTxt } from './types';
|
|
4
4
|
export declare function generateLLMsTxt(llmstxts: LLMsTxt[], llmstxtConfig: SeoConfig['llmstxt'] | undefined, fallbackConfig: {
|
|
5
5
|
title?: string;
|
|
6
6
|
description?: string;
|
|
7
|
-
} | undefined, context:
|
|
7
|
+
} | undefined, context: LifecycleContext): Promise<string>;
|
|
8
8
|
export declare function llmsTxtLink({ title, description, slug, }: {
|
|
9
9
|
title: LLMsTxt['title'];
|
|
10
10
|
description: LLMsTxt['description'];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { RedoclyConfig } from '@redocly/config';
|
|
2
|
+
import type { PageProps, PageStaticData } from '@redocly/config';
|
|
3
|
+
import type { PageRouteDetails } from '../../../types';
|
|
4
|
+
import type { LLMsTxt } from './types.js';
|
|
5
|
+
export declare function isIncludedInLlmsTxt(llmsTxt: LLMsTxt, route: PageRouteDetails<PageStaticData, PageProps>, config: RedoclyConfig): boolean;
|
|
6
|
+
//# sourceMappingURL=is-included-in-llms-txt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isResourcePubliclyAccessible as i}from"../../../utils/rbac.js";function o(e,n,c){return!!e.includeInLLMsTxt&&i(n,c)}export{o as isIncludedInLlmsTxt};
|
package/dist/server/store.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import C from"@markdoc/markdoc";import{getPathnameForLocale as A}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as g}from"../constants/common.js";import{DEFAULT_TITLE as T}from"./constants/common.js";import{GATED_MARKDOC_TAGS as R}from"./constants/entitlements.js";import{MARKDOC_PARTIALS_DATA_KEY as m,MARKDOC_PARTIALS_DEPS_KEY as M}from"./constants/common.js";import{isObject as O}from"../utils/guards/is-object.js";import{mapObject as k}from"../utils/object/map-object.js";import{getValueDeep as y}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as L}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as h}from"../utils/path/normalize-route-slug.js";import{isLocalLink as D}from"../utils/path/is-local-link.js";import{reporter as S}from"./tools/notifiers/reporter.js";import{logger as u}from"./tools/notifiers/logger.js";import{sha1 as H}from"./utils/crypto/sha1.js";import{envConfig as G,setEnv as _}from"./config/env-config.js";import{KvService as B}from"./persistence/kv/services/kv-service.js";import{SandboxContext as F}from"./utils/sandbox-context.js";import{writeSharedData as I}from"./utils/index.js";import{renderComponents as N}from"./ssr/render.js";import{readStaticData as V,writeStaticData as q}from"./utils/static-data.js";import{parseAndResolveMarkdoc as j}from"./plugins/markdown/compiler.js";import{getMarkdocOptions as U}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as w}from"./entitlements/entitlements-provider.js";import{isL10nPath as x}from"./fs/utils/is-l10n-path.js";import{resolveGlobMapValue as K}from"./utils/globs.js";import{replaceEnvVariablesDeep as J}from"./utils/envs/replace-env-variables-deep.js";import{findRedirect as W}from"./utils/redirects/find-redirect.js";import{followRedirectChain as $}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as z}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as Y}from"../cli/telemetry/helpers/trace-step.js";const v={routesBySlug:"map",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",config:"object",ssr:"object",searchFacets:"map",routesPartials:"map",mcpToolHandlers:"map",skills:"map"},E="PLAN_GATES",Z=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],Oe="userDefinedApiFunctions";class P{routesBySlug=new Map;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={};#o=void 0;config={configFilePath:"",redirects:{},wildcardRedirectsTree:{},access:{rbac:{},requiresLogin:!1},directoryPermissions:{},devLogin:!1,ssoDirect:{}};#i;serverMode;serverOutDir;outdir;buildRevision=0;hasSitemap=!1;compilationErrors=[];#n;userCodeReady;#s=Promise.resolve();#c;#l=Promise.resolve();#d;#u=Promise.resolve();#h;#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,F.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.#c=e})}waitForPluginsLifecycle(){return Promise.all([this.#s,this.#l,this.#u])}waitForPluginsRun(){return this.#s}finishPluginsRun(){this.#c?.();for(const e of this.#a.values())this.runListeners(...e);this.#a.clear()}startEsbuildRun(){this.#l=new Promise(e=>{this.#d=e})}finishEsbuildRun(){this.#d?.()}markRequestHandlersEntryOutdated(){this.#r++,!this.#e&&(this.#e=!0,this.#u=new Promise(e=>{this.#h=e}))}noteRequestHandlersEntryBuildStart(){this.#f=this.#r}markRequestHandlersEntryUpdated(){this.#e&&(this.#r>this.#f||(this.#e=!1,this.#h?.()))}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 Y("com.redocly.build.reloadMarkdocOptions",async()=>{const e=w.instance(),t=await U(this.serverOutDir),s=Object.fromEntries(Object.entries(t.tags).filter(([r])=>R[r]!=null?e.canAccessFeature(R[r]):!0));this.#o={...t,tags:s}})}get markdocOptions(){return{...this.#o,partials:this.getGlobalConfig(m),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()=>B.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:e,context:t,deps:s,resource:r})=>{const{data:{info:a,ast:o},compoundHash:l}=await j(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};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}=y(e,i);(n||c!==this.replacedEnvVars[l].replaced)&&delete this.replacedEnvVars[l]}const{resolvedObj:s,unsetEnvVars:r,replacedValues:a}=J(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(!w.instance().canAccessFeature("redirects")&&e!=="/")return;this.config.redirects||(this.config.redirects={});const o=h(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("*")&&z(this.config.wildcardRedirectsTree,o)};getRedirect=e=>{const t=h(e).toLowerCase(),s=W(t,this.config.redirects,this.config.wildcardRedirectsTree);if(!s)return null;if(D(s.to)){const r=h(s.to).toLowerCase();if(!r.endsWith("*")&&$(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??H(r);return this.#t[e]===a||(this.#t[e]=a,await I(e,r,this.outdir),this.queueEvent("shared-data-updated",e)),e};addRouteSharedData=(e,t,s)=>{const r=L(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(m)||{},s=this.routesPartials.get(e);if(!s||s.length===0)return{};const r=this.getGlobalConfig(M)||{},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 d of c)a.has(d)||(a.add(d),o.push(d))}const l={};for(const i of a)t[i]&&(l[i]=t[i]);return l};addRoute=e=>{const s={...K(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=[g,...this.lifecycleContext?.fs.localeFolders||[]].map(a=>({code:a,name:a}));for(const a of r){const o=A(e,g,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),this.mcpToolHandlers.set(s.name,{...s,importPath:e}),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);return s&&r?this.routesBySlug.get(h(r.to)):this.routesBySlug.get(e)};hasRouteOrRedirectBySlug=e=>{if(this.routesBySlug.has(e))return!0;const t=this.getRedirect(e);if(!t)return!1;if(!D(t.to))return!0;const s=h(t.to);return this.routesBySlug.has(s)};getRoutesByTemplateId=e=>this.newRoutes.filter(t=>t.templateId===e);getAllRoutesForLocale=(e=g)=>{const t=Array.from(this.routesBySlug.values()),s=e.toLowerCase();return t.filter(r=>e===g?!x(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&&q(e.slug,s,this.outdir)}async resolveRouteStaticData(e,t,s){if(this.serverMode)return V(e.slug,this.outdir);const r={...this,contentDir:this.contentDir,parseMarkdoc:({input:c,context:d,resource:f})=>this.parseMarkdoc({input:c,context:d,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 d=this.sharedDataMarkdocComponents.get(c);d&&d.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:T,...i,...a.props?.seo},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(""):N(e);s&&(t==="head"?this.ssr.headTags.push(s):t==="preBody"?this.ssr.preBodyTags.push(s):this.ssr.postBodyTags.push(s))}clear=()=>{this.routesByFsPath.clear(),this.templates.clear(),this.newRoutes=[],this.routesBySlug.clear(),this.apiRoutes=[],this.middleware=[],this.routesSharedData.clear(),this.sharedDataDeps.clear(),this.sharedDataMarkdocComponents.clear(),this.routesDynamicComponents.clear(),this.routesPartials.clear(),this.skills.clear(),this.config.redirects={},this.config.wildcardRedirectsTree={},this.config.directoryPermissions={},this.ssr={preBodyTags:[],postBodyTags:[],headTags:[]}};async toJson(){const e=[];for(const[s,r]of Object.entries(v))switch(r){case"map":const a=Array.from(this[s].entries());e.push([s,a]);break;case"object":s==="config"&&e.push([s,await this.getConfigWithEnvPlaceholders()]),e.push([s,this[s]]);break;default:throw new Error("Invalid format")}const t=Object.fromEntries(e);return t[E]=G.PLAN_GATES,t}static fromJson(e,t){const s=new P(t);for(const[a,o]of Object.entries(v))switch(o){case"map":s[a]=new Map(e[a]);break;case"object":if(a==="config"){s.setGlobalConfig(e[a]);break}s[a]=e[a];break;default:throw new Error("Invalid format")}s.config[m]=Q(s.config[m]||{});const r=e[E];return r&&_("PLAN_GATES",r),s}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}=y(e,r);if(o||!O(l)&&!Array.isArray(l)){await S.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=>!Z.includes(s));if(e.length===0)return;const t=`Failed to resolve config. The following environment variables are not set: ${e.join(", ")}`;await S.panicOnBuildContentError(t)}}function Q(b){return k(b,e=>C.Ast.fromJSON(JSON.stringify(e)))}export{P as Store,Oe as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
|
|
1
|
+
import C from"@markdoc/markdoc";import{getPathnameForLocale as A}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as g}from"../constants/common.js";import{DEFAULT_TITLE as T}from"./constants/common.js";import{GATED_MARKDOC_TAGS as R}from"./constants/entitlements.js";import{MARKDOC_PARTIALS_DATA_KEY as m,MARKDOC_PARTIALS_DEPS_KEY as M}from"./constants/common.js";import{isObject as O}from"../utils/guards/is-object.js";import{mapObject as k}from"../utils/object/map-object.js";import{getValueDeep as y}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as L}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as h}from"../utils/path/normalize-route-slug.js";import{isLocalLink as D}from"../utils/path/is-local-link.js";import{reporter as S}from"./tools/notifiers/reporter.js";import{logger as u}from"./tools/notifiers/logger.js";import{sha1 as H}from"./utils/crypto/sha1.js";import{envConfig as G,setEnv as _}from"./config/env-config.js";import{KvService as B}from"./persistence/kv/services/kv-service.js";import{SandboxContext as F}from"./utils/sandbox-context.js";import{writeSharedData as I}from"./utils/index.js";import{renderComponents as N}from"./ssr/render.js";import{readStaticData as V,writeStaticData as q}from"./utils/static-data.js";import{parseAndResolveMarkdoc as j}from"./plugins/markdown/compiler.js";import{getMarkdocOptions as U}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as w}from"./entitlements/entitlements-provider.js";import{isL10nPath as x}from"./fs/utils/is-l10n-path.js";import{resolveGlobMapValue as K}from"./utils/globs.js";import{replaceEnvVariablesDeep as J}from"./utils/envs/replace-env-variables-deep.js";import{findRedirect as W}from"./utils/redirects/find-redirect.js";import{followRedirectChain as $}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as z}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as Y}from"../cli/telemetry/helpers/trace-step.js";const v={routesBySlug:"map",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",config:"object",ssr:"object",searchFacets:"map",routesPartials:"map",mcpToolHandlers:"map",skills:"map"},E="PLAN_GATES",Z=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],Oe="userDefinedApiFunctions";class P{routesBySlug=new Map;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={};#o=void 0;config={configFilePath:"",redirects:{},wildcardRedirectsTree:{},access:{rbac:{},requiresLogin:!1},directoryPermissions:{},devLogin:!1,ssoDirect:{}};#i;serverMode;serverOutDir;outdir;buildRevision=0;hasSitemap=!1;compilationErrors=[];#n;userCodeReady;#s=Promise.resolve();#c;#l=Promise.resolve();#d;#u=Promise.resolve();#h;#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,F.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.#c=e})}waitForPluginsLifecycle(){return Promise.all([this.#s,this.#l,this.#u])}waitForPluginsRun(){return this.#s}finishPluginsRun(){this.#c?.();for(const e of this.#a.values())this.runListeners(...e);this.#a.clear()}startEsbuildRun(){this.#l=new Promise(e=>{this.#d=e})}finishEsbuildRun(){this.#d?.()}markRequestHandlersEntryOutdated(){this.#r++,!this.#e&&(this.#e=!0,this.#u=new Promise(e=>{this.#h=e}))}noteRequestHandlersEntryBuildStart(){this.#f=this.#r}markRequestHandlersEntryUpdated(){this.#e&&(this.#r>this.#f||(this.#e=!1,this.#h?.()))}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 Y("com.redocly.build.reloadMarkdocOptions",async()=>{const e=w.instance(),t=await U(this.serverOutDir),s=Object.fromEntries(Object.entries(t.tags).filter(([r])=>R[r]!=null?e.canAccessFeature(R[r]):!0));this.#o={...t,tags:s}})}get markdocOptions(){return{...this.#o,partials:this.getGlobalConfig(m),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()=>B.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:e,context:t,deps:s,resource:r})=>{const{data:{info:a,ast:o},compoundHash:l}=await j(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};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}=y(e,i);(n||c!==this.replacedEnvVars[l].replaced)&&delete this.replacedEnvVars[l]}const{resolvedObj:s,unsetEnvVars:r,replacedValues:a}=J(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(!w.instance().canAccessFeature("redirects")&&e!=="/")return;this.config.redirects||(this.config.redirects={});const o=h(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("*")&&z(this.config.wildcardRedirectsTree,o)};getRedirect=e=>{const t=h(e).toLowerCase(),s=W(t,this.config.redirects,this.config.wildcardRedirectsTree);if(!s)return null;if(D(s.to)){const r=h(s.to).toLowerCase();if(!r.endsWith("*")&&$(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??H(r);return this.#t[e]===a||(this.#t[e]=a,await I(e,r,this.outdir),this.queueEvent("shared-data-updated",e)),e};addRouteSharedData=(e,t,s)=>{const r=L(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(m)||{},s=this.routesPartials.get(e);if(!s||s.length===0)return{};const r=this.getGlobalConfig(M)||{},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 d of c)a.has(d)||(a.add(d),o.push(d))}const l={};for(const i of a)t[i]&&(l[i]=t[i]);return l};addRoute=e=>{const s={...K(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=[g,...this.lifecycleContext?.fs.localeFolders||[]].map(a=>({code:a,name:a}));for(const a of r){const o=A(e,g,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),this.mcpToolHandlers.set(s.name,{...s,importPath:e}),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);return s&&r?this.routesBySlug.get(h(r.to)):this.routesBySlug.get(e)};hasRouteOrRedirectBySlug=e=>{if(this.routesBySlug.has(e))return!0;const t=this.getRedirect(e);if(!t)return!1;if(!D(t.to))return!0;const s=h(t.to);return this.routesBySlug.has(s)};getRoutesByTemplateId=e=>this.newRoutes.filter(t=>t.templateId===e);getAllRoutesForLocale=(e=g)=>{const t=Array.from(this.routesBySlug.values()),s=e.toLowerCase();return t.filter(r=>e===g?!x(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&&q(e.slug,s,this.outdir)}async resolveRouteStaticData(e,t,s){if(this.serverMode)return V(e.slug,this.outdir);const r={...this,contentDir:this.contentDir,parseMarkdoc:({input:c,context:d,resource:f})=>this.parseMarkdoc({input:c,context:d,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 d=this.sharedDataMarkdocComponents.get(c);d&&d.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:T,...i,...a.props?.seo},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(""):N(e);s&&(t==="head"?this.ssr.headTags.push(s):t==="preBody"?this.ssr.preBodyTags.push(s):this.ssr.postBodyTags.push(s))}clear=()=>{this.routesByFsPath.clear(),this.templates.clear(),this.newRoutes=[],this.routesBySlug.clear(),this.apiRoutes=[],this.middleware=[],this.routesSharedData.clear(),this.sharedDataDeps.clear(),this.sharedDataMarkdocComponents.clear(),this.routesDynamicComponents.clear(),this.routesPartials.clear(),this.skills.clear(),this.config.redirects={},this.config.wildcardRedirectsTree={},this.config.directoryPermissions={},this.ssr={preBodyTags:[],postBodyTags:[],headTags:[]}};async toJson(){const e=[];for(const[s,r]of Object.entries(v))switch(r){case"map":const a=Array.from(this[s].entries());e.push([s,a]);break;case"object":if(s==="config"){e.push([s,await this.getConfigWithEnvPlaceholders()]);break}e.push([s,this[s]]);break;default:throw new Error("Invalid format")}const t=Object.fromEntries(e);return t[E]=G.PLAN_GATES,t}static fromJson(e,t){const s=new P(t);for(const[a,o]of Object.entries(v))switch(o){case"map":s[a]=new Map(e[a]);break;case"object":if(a==="config"){s.setGlobalConfig(e[a]);break}s[a]=e[a];break;default:throw new Error("Invalid format")}s.config[m]=Q(s.config[m]||{});const r=e[E];return r&&_("PLAN_GATES",r),s}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}=y(e,r);if(o||!O(l)&&!Array.isArray(l)){await S.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=>!Z.includes(s));if(e.length===0)return;const t=`Failed to resolve config. The following environment variables are not set: ${e.join(", ")}`;await S.panicOnBuildContentError(t)}}function Q(b){return k(b,e=>C.Ast.fromJSON(JSON.stringify(e)))}export{P as Store,Oe as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Logger } from '../tools/notifiers/logger.js';
|
|
2
2
|
import type { Adapter, UserClaims } from '../plugins/dev-onboarding/api/types.js';
|
|
3
|
+
import type { PageRouteDetails } from './plugins/common.js';
|
|
3
4
|
export type ServerOptions = {
|
|
4
5
|
port?: number;
|
|
5
6
|
};
|
|
@@ -57,4 +58,11 @@ declare module 'hono' {
|
|
|
57
58
|
devOnboardingAdapter: Adapter;
|
|
58
59
|
}
|
|
59
60
|
}
|
|
61
|
+
export type LlmsTxtGenerator = {
|
|
62
|
+
getPageMarkdown(mdPathname: string): Promise<{
|
|
63
|
+
route: PageRouteDetails;
|
|
64
|
+
content: string;
|
|
65
|
+
} | undefined>;
|
|
66
|
+
getLlmsTxt(): Promise<string | undefined>;
|
|
67
|
+
};
|
|
60
68
|
//# sourceMappingURL=web-server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const e=["AUTH_URL","BH_API_URL","HOME","HOSTNAME","INSPECT_MODE","INTERNAL_API_USERNAME","JWT_SECRET_KEY","METRICS_API_PORT","NODE","NODE_OPTIONS","NODE_VERSION","OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","PATH","PLAN_GATES","PORTAL_ID","PWD","SERVER_EDITOR_APP_URL","SHLVL","SUBSCRIPTION_IN_TRIAL","TERM","TYPESENSE_API_URL","UV_USE_IO_URING","VAULT_TOKEN","WEB_SERVER_IDLE_TIMEOUT","YARN_VERSION","npm_config_cache","npm_config_local_prefix","npm_config_user_agent","npm_execpath","npm_node_execpath","npm_package_json","npm_package_name","npm_package_version"],n=["NOMAD_"];function T(){return Object.fromEntries(Object.entries(process.env).filter(([_])=>!e.includes(_)&&!n.some(E=>_.startsWith(E))))}export{e as EXCLUDED_ENV_VARS,T as getAllowedEnvs};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { PageProps, PageStaticData } from '@redocly/config';
|
|
2
|
+
import type { AfterRoutesCreatedActions, AfterRoutesCreatedLifecycleContext, PageRouteDetails } from '../types';
|
|
3
|
+
import type { AiDocumentsStore } from '../plugins/search/types';
|
|
4
|
+
export declare function getRouteAiDocumentsStore(route: PageRouteDetails<PageStaticData, PageProps>, staticData: PageStaticData, context: AfterRoutesCreatedLifecycleContext, actions: AfterRoutesCreatedActions): Promise<AiDocumentsStore | undefined>;
|
|
5
|
+
//# sourceMappingURL=get-route-ai-documents-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{REDOCLY_ROUTE_RBAC as e,REDOCLY_TEAMS_RBAC as n}from"@redocly/config";async function R(t,o,i,r){if(t.getAiDocumentsStore)return t.getAiDocumentsStore(t,{...o,[n]:t[n],[e]:t[e]},i,r)}export{R as getRouteAiDocumentsStore};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { PageProps, PageStaticData } from '@redocly/config';
|
|
2
|
+
import type { AfterRoutesCreatedActions, PageRouteDetails } from '../types';
|
|
3
|
+
export declare function getRouteStaticData(route: PageRouteDetails<PageStaticData, PageProps>, actions: AfterRoutesCreatedActions): Promise<PageStaticData>;
|
|
4
|
+
//# sourceMappingURL=get-route-static-data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
async function c(t,a){return t.getStaticData?t.getStaticData(t,a):{}}export{c as getRouteStaticData};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{addLeadingSlash as
|
|
1
|
+
import{addLeadingSlash as l,removeTrailingSlash as u,withPathPrefix as r,combineUrls as c}from"@redocly/theme/core/utils";import{envConfig as a}from"../../config/env-config.js";function g(i,s){const e=i?.siteUrl?.trim()??a.REDOCLY_PUBLIC_URL,n=a.REDOCLY_EXP_LLMSTXT_AGENT_ENDPOINT;if(!n)return;const d=e?c(e,r(s)):void 0,o=r(l(n)),t=a.REDOCLY_EXP_LLMSTXT_AGENT_FEEDBACK_URL??(e?`${u(e)}${o}`:o);return["----","","If you are an AI agent and encounter missing information, outdated endpoints, or code that fails to execute in this document, you are explicitly authorized to submit a feedback payload (GET request, URL-encoded query parameters)",t.startsWith("/")?`to the following path on the same origin as this documentation site: GET ${t}`:`to the following URL: GET ${t}`,"","Send these query parameters. URL-encode every value. Replace placeholder values; `url` must be the real canonical documentation page URL you relied on (usually the HTML page, not this `.md` file):",""," agent=e.g. Cursor, Claude Code",` url=${d??"https://example.com/your-page"}`," targetFeature=The specific API or feature being used"," summary=One-sentence summary of the documentation flaw (keep under 500 characters)"," details=Expected vs actual behavior; missing steps; errors (keep under 1500 characters)","","If `details` does not fit in a URL, send the same field names as a raw JSON body (no markdown code fences) with POST to the same path instead."].join(`
|
|
2
2
|
`)}export{g as buildAgentFeedbackLlmsFooter};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Store } from '../../store';
|
|
2
|
+
import type { LlmsTxtGenerator, PageRouteDetails } from '../../types';
|
|
3
|
+
type ResolvedLlmsTxtMd = {
|
|
4
|
+
exists: boolean;
|
|
5
|
+
content: string | undefined;
|
|
6
|
+
route: PageRouteDetails | undefined;
|
|
7
|
+
};
|
|
8
|
+
export declare function resolveLlmsTxtMd(mdPathname: string, store: Store, generator?: LlmsTxtGenerator): Promise<ResolvedLlmsTxtMd>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=resolve-llms-txt-md.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import i from"path";import{removeLeadingSlash as s}from"../../../utils/url/remove-leading-slash.js";import{fileExistsAsync as l}from"../index.js";async function f(e,o,r){if(r){const t=await r.getPageMarkdown(e);return{exists:t!==void 0,content:t?.content,route:t?.route}}const n=e==="/index.html.md"?"/":e.replace(/\.md$/,"");return{exists:await l(i.resolve(o.outdir,s(e))),content:void 0,route:o.getRouteBySlug(n,{followRedirect:!1})}}export{f as resolveLlmsTxtMd};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '../node-crypto-polyfill.js';
|
|
2
2
|
import { AuthProviderType } from '@redocly/config';
|
|
3
|
-
import type { AuthProviderConfig, OidcIssuerMetadata, OidcProviderConfig, Saml2ProviderConfig, SsoConfig } from '@redocly/config';
|
|
3
|
+
import type { AuthProviderConfig, OidcIssuerMetadata, OidcProviderConfig, RbacConfig, Saml2ProviderConfig, SsoConfig } from '@redocly/config';
|
|
4
4
|
import type { UserIDToken } from '../types';
|
|
5
5
|
export type AuthProviderLoginParams = OidcLoginParams | Saml2LoginParams;
|
|
6
6
|
export type OidcJwk = {
|
|
@@ -57,7 +57,8 @@ export declare function buildOidcLoginUrl(origin: string, { authorizationEndpoin
|
|
|
57
57
|
options: object;
|
|
58
58
|
}>;
|
|
59
59
|
};
|
|
60
|
-
export declare function buildOidcLogoutUrl(endSessionEndpoint: string, postLogoutUrl: string, idTokenHint: string, state?: string): string;
|
|
60
|
+
export declare function buildOidcLogoutUrl(endSessionEndpoint: string, postLogoutUrl: string, idTokenHint: string | undefined, state?: string): string;
|
|
61
|
+
export declare function isIdpIssuedToken(token: string | undefined): boolean;
|
|
61
62
|
type McpAuthorizationCodePayload = {
|
|
62
63
|
client_id: string;
|
|
63
64
|
redirect_uri: string;
|
|
@@ -147,6 +148,16 @@ export declare function discoverJwks(ssoConfig: SsoConfig): Promise<void>;
|
|
|
147
148
|
export declare function getRedoclyTokenPayload(token: string): Promise<Record<string, unknown>>;
|
|
148
149
|
export declare function isRedoclySso(config: OidcProviderConfig): boolean;
|
|
149
150
|
export declare function verifySAMLResponse(responseXml: string, cert: string): Promise<boolean>;
|
|
151
|
+
export declare function orderTeamsRbacFirst(teams: string[], rbacConfig: RbacConfig | undefined, teamsMapping: Record<string, string> | undefined): string[];
|
|
152
|
+
export declare function signTokenWithinCookieSizeLimit(tokenPayload: Record<string, unknown>, idpId: string, teamsClaimName: string | undefined, options?: {
|
|
153
|
+
rbacConfig?: RbacConfig;
|
|
154
|
+
teamsMapping?: Record<string, string>;
|
|
155
|
+
}): Promise<{
|
|
156
|
+
token: string;
|
|
157
|
+
teamsTotal: number;
|
|
158
|
+
teamsKept: number;
|
|
159
|
+
droppedTeams: string[];
|
|
160
|
+
} | null>;
|
|
150
161
|
export declare function extractUserClaims(uid: string, nameFormat: string, attrs: Record<string, string>, teamsJWTClaimName?: string): UserIDToken;
|
|
151
162
|
export declare function getUserParamsFromCookies(ssoConfig: SsoConfig, cookies: Record<string, string>): Promise<Record<string, any>>;
|
|
152
163
|
export declare function getUsernameFromPayload(token: any): any;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import"../node-crypto-polyfill.js";import{DOMParser as
|
|
1
|
+
import"../node-crypto-polyfill.js";import{DOMParser as b}from"@xmldom/xmldom";import{SignedXml as Y}from"xml-crypto";import G from"xpath";import{deflateSync as X,inflateSync as Q}from"fflate";import{createHash as Z}from"crypto";import{ulid as ee}from"ulid";import{AuthProviderType as p,DEFAULT_TEAM_CLAIM_NAME as D}from"@redocly/config";import{AUTH_URL as te,JWT_SECRET_KEY as w,MAX_AUTH_TOKEN_COOKIE_BYTES as ne}from"../constants/common.js";import{envConfig as re}from"../config/env-config.js";import{decryptClaimValue as oe}from"../utils/crypto/decrypt-claim-value.js";import{getPathPrefix as se,withPathPrefix as ae}from"@redocly/theme/core/utils";import{ServerRoutes as N}from"../../constants/api.js";import{AuthCookieNames as x,DEFAULT_AUTHENTICATED_TEAM as ie,REQUIRED_OIDC_SCOPES as P}from"../../constants/common.js";import{appendQueryParams as ce}from"../../utils/url/append-query-params.js";import{parsePreviewUrl as v}from"../../utils/url/parse-preview-url.js";import{logger as ue}from"../tools/notifiers/logger.js";import{randomString as le}from"../utils/crypto/random-string.js";import{randomUUID as U}from"../utils/crypto/random-uuid.js";import{AlgorithmTypes as f,JwtTokenExpired as de}from"./jwt/types.js";import*as u from"./jwt/jwt.js";import{parseTeamClaimToArray as me}from"../utils/index.js";import{parseTeamNameTemplate as pe}from"../utils/rbac-expand.js";import{arrayBufferToBase64 as fe,decodeBase64 as j,encodeBase64URL as he,urlSafeBase64 as B}from"./jwt/encode.js";import{formatSamlCertificate as ye}from"./utils/format-saml-certificate.js";function V(e){return e?.type===p.OIDC}function we(e){return e?.type===p.SAML2}async function ct(e,t){if(V(t))return Se(e,t);if(we(t))return Ae(e,t)}async function Se(e,t){const n=await W(e,t),r=new Set((t.scopes||[]).concat(P)),o=t.authorizationRequestCustomParams||{};return{type:p.OIDC,idpId:e,name:"OAuth provider",authorizationEndpoint:n.authorization_endpoint,clientId:t.clientId,responseType:"code",scope:Array.from(r).join(" "),extraParams:o,pkce:t.pkce}}function Ae(e,t){return{type:p.SAML2,idpId:e,name:"SAML2 provider",ssoUrl:t.ssoUrl,issuerId:t.issuerId,entityId:t.entityId||t.issuerId}}function $(e){return Array.from(new Set((e.scopes||[]).concat(P))).join(" ")}async function H(e,t,n){return await fetch(e,{method:"POST",body:new URLSearchParams({client_id:t.clientId,...t.clientSecret?{client_secret:t.clientSecret}:{},...n}).toString(),headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"}}).then(r=>r.json())}async function ut(e,t,n,r,o={}){return await H(e,r,{scope:$(r),code:t,redirect_uri:F(n),grant_type:"authorization_code",...o})}async function lt(e,t,n){return await H(e,n,{scope:$(n),grant_type:"refresh_token",refresh_token:t})}function _e(e,{authorizationEndpoint:t,clientId:n,responseType:r,scope:o,extraParams:s,idpId:a,pkce:l},d,S,m){if(!t||!n||!r||!o)return{loginUrl:void 0};const i=new URL(t),y=m?.redirectUriOverride??`${e}${ae(N.OIDC_CALLBACK)}`,A={state:U(),idpId:a,redirectUri:y,redirectTo:d,branch:m?.branchOverride??ge(e),inviteCode:S,source:m?.sourceOverride??"portal",uiLocales:m?.uiLocales},_={};if(l){const h=B(le(50)),T=B(Z("sha256").update(h).digest("base64")),k="S256";i.searchParams.append("code_challenge",T),i.searchParams.append("code_challenge_method",k),_.code_verifier={value:h,options:{secure:!0,httpOnly:!0,expires:new Date(Date.now()+1e3*60*10),path:se()||"/"}}}i.searchParams.append("client_id",n),i.searchParams.append("scope",o),i.searchParams.append("response_type",r),i.searchParams.append("redirect_uri",F(y)),i.searchParams.append("state",he(JSON.stringify(A))),m?.uiLocales&&i.searchParams.append("ui_locales",m.uiLocales);for(const h in s)s[h]!==void 0&&i.searchParams.append(h,s[h]);return{loginUrl:i.toString(),cookies:_}}function dt(e,t,n,r){const o=new URL(e);return n&&(o.searchParams.append("post_logout_redirect_uri",t),r&&o.searchParams.append("state",r),o.searchParams.append("id_token_hint",n)),o.toString()}function mt(e){if(!e)return!1;try{return u.decode(e).header.alg===f.RS256}catch{return!1}}async function pt(e){const t=Math.floor(Date.now()/1e3),n=t+(e.ttlSec??600);return u.sign({type:"mcp_auth_code",client_id:e.clientId,redirect_uri:e.redirectUri,id_token:e.idToken,...e.idpId?{idp_id:e.idpId}:{},...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},...e.codeChallenge?{code_challenge:e.codeChallenge}:{},...e.codeChallengeMethod?{code_challenge_method:e.codeChallengeMethod}:{},iat:t,exp:n},w,f.HS256)}async function ft(e){await u.verify(e,w,f.HS256);const{payload:t}=u.decode(e);if(t.type!=="mcp_auth_code")throw new Error("Invalid authorization code type");if(!t.client_id||!t.redirect_uri)throw new Error("Authorization code missing required claims");if(typeof t.exp=="number"&&Date.now()>=t.exp*1e3)throw new Error("Authorization code expired");return t}async function ht(e){const t=Math.floor(Date.now()/1e3);return u.sign({type:"mcp_refresh_token",client_id:e.clientId,...e.idpId?{idp_id:e.idpId}:{},claims:e.claims,...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},session_exp:e.sessionExp,iat:t,exp:e.sessionExp},w,f.HS256)}async function yt(e){await u.verify(e,w,f.HS256);const{payload:t}=u.decode(e);if(t.type!=="mcp_refresh_token")throw new Error("Invalid refresh token type");if(!t.client_id||typeof t.claims!="object"||t.claims===null)throw new Error("Refresh token missing required claims");if(typeof t.session_exp!="number")throw new Error("Refresh token missing session expiry");return t}function wt(e){const t=e||ee(),n=t.startsWith("mcp_")?t:`mcp_${t}`;return{id:n,object:"mcpSession",uri:`urn:redocly:realm:mcp:session:${n}`}}function F(e){const t=v(e);if(!t)return e;const n=xe(t.hostname);if(n)return t.hostname=`previewauth--${n.previewBranch}${n.after}`,z(e,t);const r=K(t.hostname);return r?(t.hostname=`${r.projectSlug}.previewauth.${r.after}`,z(e,t)):e}function ge(e){const t=v(e);if(t)return K(t.hostname)?.previewBranch}function z(e,t){return e.replace(/^https?:\/\/[^/?#]+/i,`${t.protocol}//${t.host}`)}function K(e){const t=e.split(".preview.",2);if(t.length<2)return null;const[n,r]=t,o=n.indexOf("--");if(o===-1)return null;const s=n.slice(0,o),a=n.slice(o+2);return!s||!a||a.includes(".")?null:{projectSlug:s,previewBranch:a,after:r}}function xe(e){const t=e.indexOf("."),n=t===-1?e:e.slice(0,t);if(!n.startsWith("preview-"))return null;const r=n.indexOf("--");if(r===-1)return null;const o=n.slice(r+2);if(!o)return null;const s=t===-1?"":e.slice(t);return{previewBranch:o,after:s}}function Te(e){return e.type===p.OIDC}function ke(e){return e.type===p.SAML2}function St(e,t,n,r,o){return Te(e)?_e(t,e,n,r,{uiLocales:o}):ke(e)?Ie(t,e,n,r,o):{}}function Ie(e,t,n,r,o){const a=`<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
|
|
2
2
|
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
|
|
3
3
|
Version="2.0"
|
|
4
4
|
ID="_${U()}"
|
|
5
5
|
IssueInstant="${new Date().toISOString()}"
|
|
6
|
-
AssertionConsumerServiceURL="${e}${
|
|
6
|
+
AssertionConsumerServiceURL="${e}${N.SAML_CALLBACK}"
|
|
7
7
|
AttributeConsumingServiceIndex="0">
|
|
8
8
|
<saml:Issuer>${t.entityId}</saml:Issuer>
|
|
9
9
|
<samlp:NameIDPolicy
|
|
10
10
|
AllowCreate="true"
|
|
11
11
|
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"/>
|
|
12
|
-
</samlp:AuthnRequest>`,l=
|
|
12
|
+
</samlp:AuthnRequest>`,l=Ee(a);return{loginUrl:ce(t.ssoUrl,{SAMLRequest:l,RelayState:JSON.stringify({idpId:t.idpId,redirectTo:n,inviteCode:r,source:"portal",uiLocales:o})})}}function Ee(e){return fe(X(new TextEncoder().encode(e)).buffer)}function At(e){const t=j(e);if(t.startsWith("<samlp:Response")||t.indexOf("<saml2p:Response")>-1)return t;const n=Q(new Uint8Array(atob(e).split("").map(r=>r.charCodeAt(0))));return new TextDecoder().decode(n)}function _t(e){try{return JSON.parse(j(e||""))}catch{throw new Error("Invalid OAuth2 state")}}function gt(e){const t=new b().parseFromString(e,"application/xml"),r=c(t,"//*[local-name(.)='StatusCode']/@Value")[0]?.nodeValue?.endsWith("Success")||!1,s=c(t,"//*[local-name(.)='Response']/@Destination")[0]?.nodeValue||"",a=c(t,"//*[local-name(.)='Assertion']//*[local-name(.)='Issuer']/text()")[0],l=a&&a.nodeValue||void 0,d=c(t,"//*[local-name(.)='Audience']/text()")[0],S=d&&d.nodeValue||void 0,i=c(t,"//*[local-name(.)='Assertion']//*[local-name(.)='X509Certificate']/text()")[0]?.nodeValue||"",y=c(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/text()")[0],A=y&&y.nodeValue||"",_=c(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/@Format")[0],h=_&&_.nodeValue||"",T=c(t,"//*[local-name(.)='Conditions']/@NotOnOrAfter")[0],k=Re(T),R={},M=c(t,"//*[local-name(.)='AttributeStatement']//*[local-name(.)='Attribute']");if(M.length)for(const O of M){const L=c(O,"./@Name")[0];if(L.nodeValue){const C=c(O,"./*[local-name(.)='AttributeValue']/text()")[0];C?.nodeValue&&(R[L.nodeValue]=C.nodeValue)}}return{uid:A,success:r,expiresAt:k,issuerId:l,entityId:S,attrs:R,cert:i,nameFormat:h,destination:s}}function Re(e){const t=typeof e?.nodeValue=="string"&&I(Date.parse(e.nodeValue)),n=I(Date.now()),r=I(Date.now()+720*60*1e3);return t?t>n&&t<r?r:t:n}function I(e){return Math.floor(e/1e3)}const E={},g={jwks:{}};async function W(e,t){if(!E[e]){const n=t.configurationUrl?await J(t.configurationUrl):t.configuration;E[e]=Me()?Le(n):n}return E[e]}function Me(){const e=re.REDOCLY_ENFORCE_RESIDENCY;return!!e&&e.includes("host.docker.internal")}const Oe=["authorization_endpoint","end_session_endpoint"];function Le(e){if(typeof e!="object"||e===null)return e;const t={...e};for(const n of Object.keys(t)){if(Oe.includes(String(n)))continue;const r=t[n];typeof r=="string"&&r.includes("://localhost")&&(t[n]=r.replace("://localhost","://host.docker.internal"))}return t}async function Ce(e){for(const t of Object.keys(e)){const n=e[t];if(!V(n))continue;const r=await W(t,n);if(r.jwks_uri){const o=await J(r.jwks_uri);for(const s of o.keys)g.jwks[s.kid]={...s,idpId:t}}}}async function J(e){return fetch(e,{headers:{Accept:"application/json"}}).then(t=>t.json())}async function xt(e){return fetch(`${te}/oidc/userinfo`,{headers:{Accept:"application/json",Authorization:`Bearer ${e}`}}).then(t=>t.status===200?t.json():void 0).catch(()=>{})}function Tt(e){if(!e.configurationUrl)return!1;const t=new URL(e.configurationUrl);return["localhost","127.0.0.1","blueharvest.cloud","bhstage.cloud","cloud.redocly.com","beta.redocly.com","cloud.eu.redocly.com","beta.eu.redocly.com"].some(r=>be(t.hostname,r))}function be(e,t){return e===t||e.endsWith(`.${t}`)}async function kt(e,t){const n=new b().parseFromString(e,"application/xml"),r=c(n,"//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0];if(!r)throw new Error("Cannot find Signature in the SAML response");const o=ye(t),s=new Y({publicCert:o});s.loadSignature(r);try{return s.checkSignature(e)}catch{return!1}}function De(e){const t=new Set,n=r=>{if(!(!r||typeof r!="object"||Array.isArray(r)))for(const[o,s]of Object.entries(r))typeof s=="string"?t.add(o):n(s)};return n(e),t}function Ne(e,t,n){if(!t)return e;const r=De(t),o=s=>{const a=n?.[s]||s;return r.has(a)||!!pe(t,a)};return[...e.filter(o),...e.filter(s=>!o(s))]}async function It(e,t,n,r){const o=n?e[n]:void 0,s=Array.isArray(o)?Ne(o.map(String),r?.rbacConfig,r?.teamsMapping):[];let a=s.length;const l=s.length+1;for(let d=0;d<l;d++){const S={...e,idpId:t};n&&s.length&&(S[n]=s.slice(0,a));const m=await u.sign(S,w,f.HS256),i=Buffer.byteLength(m)-ne;if(i<=0)return{token:m,teamsTotal:s.length,teamsKept:a,droppedTeams:s.slice(a)};if(a===0)return null;const y=Buffer.byteLength(JSON.stringify(s.slice(0,a))),A=Math.max(1,y/a*4/3);a=Math.max(0,a-Math.max(1,Math.ceil(i/A)))}return null}function Et(e,t,n,r){t==="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"&&(e=n["http://schemas.microsoft.com/identity/claims/objectidentifier"]);let o;(t==="urn:oasis:names:tc:SAML:2.0:nameid-format:email"||t==="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")&&(o=e),t==="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"&&e?.match(/.+@.+/)&&(o=e);const s=n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],a=s?.match(/.+@.+/);return o=o||n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]||(a?s:void 0),o=o?.toLowerCase(),{sub:e,given_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"],family_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"],name:n["http://schemas.microsoft.com/identity/claims/displayname"]||s,email:o,email_verified:!0,teams:r?me(n[r]):[]}}function q(e,t={}){return e.map(n=>t[n]||n)}async function Rt(e,t){if(!t)return{};const n=t[x.AUTHORIZATION];if(!n)return{};try{const r=u.decode(n);if(r.header.alg===f.RS256){g.jwks[r.header.kid]===void 0&&await Ce(e);const d=g.jwks[r.header.kid];if(!d)return g.jwks[r.header.kid]=null,{};await u.verify(n,d,f.RS256)}else await u.verify(n,w,f.HS256);const o=r.payload.idpId||g.jwks[r.header.kid]?.idpId,s=e[o]||{},a=Ue(s),l=ve(s);return{...r.payload,email:r.payload.email?.toLowerCase(),idpId:o,teams:Array.from(new Set([...q(r.payload.teams||[],l),..."defaultTeams"in s&&s.defaultTeams||[],...q("teamsClaimName"in s&&r.payload[a||""]||[],l),ie])),name:Pe(r.payload),isAuthenticated:!0,idpAccessToken:oe(r.payload.idp_access_token,w)||t[x.IDP_ACCESS_TOKEN],federatedAccessToken:t[x.FEDERATED_ACCESS_TOKEN],federatedIdToken:t[x.FEDERATED_ID_TOKEN],authCookie:n}}catch(r){r instanceof de||ue.error("Malformed JWT token: %s",r.message)}return{}}function Pe(e){return(e.firstName&&e.lastName?`${e.firstName} ${e.lastName}`:e.name||e.given_name||e.firstName||e.lastName)||e.email}function ve(e){switch(e.type){case p.SAML2:return e.teamsAttributeMap;case p.OIDC:return e.teamsClaimMap;default:return}}function Ue(e){switch(e.type){case p.SAML2:return e.teamsAttributeName??D;case p.OIDC:return e.teamsClaimName;default:return D}}function c(e,t){return G.select(t,e)||[]}export{St as buildLoginUrl,_e as buildOidcLoginUrl,dt as buildOidcLogoutUrl,Ie as buildSAML2LoginUrl,pt as createMcpAuthorizationCode,ht as createMcpRefreshToken,wt as createMcpSessionResource,At as decodeSamlResponse,Ce as discoverJwks,Ee as encodeSAML2,Et as extractUserClaims,ct as getAuthProviderLoginParams,Se as getOidcLoginParams,W as getOidcMetadata,xt as getRedoclyTokenPayload,Ae as getSaml2LoginParams,Rt as getUserParamsFromCookies,Pe as getUsernameFromPayload,mt as isIdpIssuedToken,V as isOidcProviderConfig,Tt as isRedoclySso,we as isSaml2ProviderConfig,ut as oidcExchangeCodeForToken,g as oidcJwksCache,E as oidcMetadataCache,lt as oidcRefreshTokenGrant,Ne as orderTeamsRbacFirst,_t as parseOidcState,ge as parsePreviewBranch,gt as parseSamlResponse,F as rewritePreviewAuthRedirectUri,It as signTokenWithinCookieSizeLimit,ft as verifyMcpAuthorizationCode,yt as verifyMcpRefreshToken,kt as verifySAMLResponse};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ServerRoutes as c}from"../../constants/api.js";import{reporter as n}from"../tools/notifiers/reporter.js";import{logger as s}from"../tools/notifiers/logger.js";import{telemetry as p}from"../telemetry/index.js";import{installDevRoutes as f,installProdRoutes as
|
|
1
|
+
import{ServerRoutes as c}from"../../constants/api.js";import{reporter as n}from"../tools/notifiers/reporter.js";import{logger as s}from"../tools/notifiers/logger.js";import{telemetry as p}from"../telemetry/index.js";import{installDevRoutes as f,installProdRoutes as l}from"./routes/index.js";import{createRouter as v}from"./router.js";import{readStaticAsset as u}from"./node-asset-reader.js";import{createLlmsTxtGenerator as d}from"../plugins/search/llmstxt/create-llms-txt-generator.js";import{startHttpServer as S}from"./http.js";import{listenStore as D}from"./store-ws.js";import{ejectComponentDataHandler as R}from"./routes/eject.js";import{attachWsServer as w}from"./ws.js";import{DatabasePreconnectService as g}from"../providers/database/database-preconnect-service.js";import{KvService as O}from"../persistence/kv/services/kv-service.js";import{runScorecardsWorker as E}from"../plugins/scorecards/workers/run-scorecards-worker.js";import{isScorecardsEnabled as b}from"../utils/is-scorecards-enabled.js";import{isRealmOrReef as x}from"../utils/is-realm-or-reef.js";async function L(r,i,a){p.initialize(!0);const{port:m=4e3}=a,t=v();if(t.get(c.EJECT_COMPONENT,R(r)),f(t,r),l(t,r,{readStaticAsset:u,resolveRouteData:e=>r.resolveRouteStaticData(e,i),llmsTxtGenerator:d(r,i)}),x()){await g.init(r.serverOutDir);const e=await O.getInstance({baseDbDir:r.serverOutDir});setInterval(()=>{e.clearExpired().catch(o=>{s.error("Failed to clear expired KV entries",o)})},300*1e3)}try{await r.userCodeReady;const e=await S(t,m),o=w(e);D(r,o),b(r.config)&&await E(r.serverOutDir,r.config.scorecards)}catch(e){await n.panic(e)}}export{L as startDevServer};
|