@redocly/redoc-reef 0.139.2 → 0.139.4
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 +17 -0
- package/dist/cli/build/index.js +1 -1
- package/dist/server/api-routes/execute-api-route.js +1 -1
- package/dist/server/api-routes/run-api-routes-worker.js +1 -1
- package/dist/server/node-bundle-entry.js +1 -1
- package/dist/server/plugins/mcp/codemode/tools/describe-tools.d.ts +1 -1
- package/dist/server/plugins/mcp/codemode/tools/describe-tools.js +1 -3
- package/dist/server/plugins/mcp/handlers/docs-mcp-handler.js +1 -1
- package/dist/server/plugins/mcp/handlers/handle-mcp-request.js +1 -1
- package/dist/server/plugins/mcp/servers/docs-server.js +1 -1
- package/dist/server/plugins/mcp/servers/mcp-server.js +2 -2
- package/dist/server/plugins/mcp/types.d.ts +10 -0
- package/dist/server/plugins/mcp/utils/available-tools.d.ts +5 -0
- package/dist/server/plugins/mcp/utils/available-tools.js +1 -0
- package/dist/server/plugins/mcp/workers/worker-context.d.ts +4 -0
- package/dist/server/plugins/mcp/workers/worker-context.js +1 -0
- package/dist/server/plugins/search/engines/typesense/index.js +1 -1
- package/dist/server/store-config.d.ts +33 -0
- package/dist/server/store-config.js +1 -0
- package/dist/server/store.d.ts +10 -15
- package/dist/server/store.js +1 -1
- package/dist/server/types/plugins/api-routes.d.ts +4 -1
- package/dist/server/utils/envs/get-api-route-allowed-env-variables.d.ts +2 -0
- package/dist/server/utils/envs/get-api-route-allowed-env-variables.js +1 -1
- package/dist/server/utils/envs/interpolate-env-variables.d.ts +5 -1
- package/dist/server/utils/envs/interpolate-env-variables.js +1 -1
- package/dist/server/utils/envs/replace-env-variables-deep.d.ts +17 -9
- package/dist/server/utils/envs/replace-env-variables-deep.js +1 -1
- package/dist/server/workers/mcp-tool-worker-methods.d.ts +7 -0
- package/dist/server/workers/mcp-tool-worker-methods.js +1 -0
- package/dist/server/workers/mcp-tool-worker-pool.d.ts +0 -1
- package/dist/server/workers/mcp-tool-worker-pool.js +1 -1
- package/dist/server/workers/mcp-tool-worker.js +1 -1
- package/dist/server/workers/types.d.ts +4 -8
- package/dist/server/workers/worker-config.d.ts +33 -0
- package/dist/server/workers/worker-config.js +1 -0
- package/package.json +2 -2
- package/dist/server/plugins/mcp/utils/available-sandbox-tools.d.ts +0 -4
- package/dist/server/plugins/mcp/utils/available-sandbox-tools.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @redocly/redoc-reef
|
|
2
2
|
|
|
3
|
+
## 0.139.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 3a1bc71aaf5: Fixed an issue in OpenAPI docs where the page of a schema that other schemas extend through `allOf` never finished rendering when its properties reached schemas that refer back to those extensions.
|
|
8
|
+
- 3a1bc71aaf5: Fixed an issue in OpenAPI docs where a page never finished rendering when two discriminator schemas listed the same variants and both declared the properties the variants inherit.
|
|
9
|
+
- 460fdd452f2: Fixed an issue where the Docs MCP server in projects with large configurations used too much memory and responded slowly.
|
|
10
|
+
- Updated dependencies [3a1bc71aaf5]
|
|
11
|
+
- Updated dependencies [3a1bc71aaf5]
|
|
12
|
+
- @redocly/api-docs@0.4.2
|
|
13
|
+
|
|
14
|
+
## 0.139.3
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- ff9ea69c647: Fixed an issue where search with the Typesense engine failed or took several seconds on projects with large API operations.
|
|
19
|
+
|
|
3
20
|
## 0.139.2
|
|
4
21
|
|
|
5
22
|
## 0.139.1
|
package/dist/cli/build/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import v from"node:path";import{unlink as C}from"node:fs/promises";import{PORTAL_VERSION as P}from"../../server/version.js";import{logger as e}from"../../server/tools/notifiers/logger.js";import{reporter as w}from"../../server/tools/notifiers/reporter.js";import{shutdowner as R}from"../../server/tools/shutdowner.js";import{promiseMapLimit as h}from"../../server/utils/async/promise-map-limit.js";import{validateReactPages as A}from"../../server/plugins/pages/validators/validate-react-pages.js";import{initPlugins as E,runPlugins as M,writeClientEntries as D}from"../../server/plugins/lifecycle.js";import{createClientCompiler as j,createNodeBundleCompiler as x}from"../../server/esbuild/esbuild.js";import{validateAllMarkdowns as L}from"../../server/plugins/markdown/compiler.js";import{copyEnvFiles as O}from"./copy-env-files.js";import{EsbuildError as c,esbuildLogger as d}from"../../server/esbuild/esbuild-logger.js";import{reportAllErrors as k}from"../../server/utils/report-all-errors.js";import{writeProblemsReport as z}from"../../server/utils/write-problems-report.js";import{collectAnalytics as J}from"./analytics/collect-analytics.js";import{copyMigrationsFolder as F}from"../../server/providers/database/copy-migrations.js";import{telemetry as s}from"../telemetry/index.js";import{telemetryTraceStep as m}from"../telemetry/helpers/trace-step.js";async function ri(b,i){s.initialize(P),e.info("Starting project build."),i.devLogin=b.devLogin===!0;const y=e.startTiming();await m("com.redocly.build.start",async T=>{D(i);const p=await j(i,i.outdir,"production"),u=await x(i,i.serverOutDir,"production");let o,g;try{await m("com.redocly.build.clientCompilation",async()=>{const l=e.startTiming(),n=await p.rebuild().catch(a=>{throw new c(a)});Array.isArray(n.warnings)&&d(new c({warnings:n.warnings}));const r=e.infoTime(l,"Client JavaScript compiled");r&&s.sendTimingPerformedMessage([{id:"buildStep",object:"timing",uri:"urn:redocly:realm:cli:timing:buildStep",...r}])}),await m("com.redocly.build.serverCompilation",async()=>{const l=e.startTiming(),n=await u.rebuild().catch(a=>{throw new c(a)});Array.isArray(n.warnings)&&d(new c({warnings:n.warnings}));const r=e.infoTime(l,"Server JavaScript compiled");r&&s.sendTimingPerformedMessage([{id:"buildStep",object:"timing",uri:"urn:redocly:realm:cli:timing:buildStep",message:r.message,timeMs:r.timeMs}])}),await i.reloadMarkdocOptions();let t;({pluginInstances:t,lifecycleContext:o}=await E({outdir:i.outdir,serverOutDir:i.serverOutDir,contentDir:i.contentDir,devLogin:i.devLogin,setGlobalConfig:i.setGlobalConfig})),o.fs.dispose(),i.lifecycleContext=o,F(i),await M(t,i,o),g=J(i,o),await m("com.redocly.build.clientServerRecompile",async()=>{const l=e.startTiming();e.info("Re-compiling JavaScript with user code"),(await Promise.all([p.rebuild(),u.rebuild()]).catch(a=>{throw new c(a)})).forEach(({warnings:a})=>{Array.isArray(a)&&d(new c({warnings:a}))});const r=e.infoTime(l,"JavaScript re-compiled");r&&s.sendTimingPerformedMessage([{id:"buildStep",object:"timing",uri:"urn:redocly:realm:cli:timing:buildStep",...r}])}),await m("com.redocly.build.pagesValidation",async()=>{await A(i,o),await L(i,o)}),await k(o),i.markUserCodeReady()}catch(t){T?.error(t),t instanceof c&&await d(t),await w.panic(t instanceof Error?t:String(t))}finally{g&&await g.catch(()=>{}),await p.dispose(),await u.dispose()}w.printErrors(),e.info("Rendering pages..."),await m("com.redocly.build.prepareStaticData",async t=>{const l=e.startTiming(),n=[...i.getAllRoutes(),...i.getAllApiRoutes()];t?.setAttribute("totalRoutes",n.length.toString()),await h(n,15,a=>i.writeRouteStaticData(a,o));const r=e.successTime(l,"Page static data prepared.");r&&s.sendTimingPerformedMessage([{id:"buildStep",object:"timing",uri:"urn:redocly:realm:cli:timing:buildStep",...r}])}),await m("com.redocly.build.storeSerialization",async()=>{const t=e.startTiming();await i.writeServerFiles(),e.successTime(t,"Store serialized and written."),O(i)});const S=e.startTiming();z()&&e.successTime(S,"Broken links written."),await C(v.join(i.serverOutDir,"index.css"));const f=e.successTime(y,"All done");f&&s.sendTimingPerformedMessage([{id:"buildStep",object:"timing",uri:"urn:redocly:realm:cli:timing:buildStep",...f}])}),await R.exitWithCode(0)}export{ri as build};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Hono as l}from"hono";import{TrieRouter as f}from"hono/router/trie-router";import{Logger as d}from"../tools/notifiers/logger.js";import{envConfig as a}from"../config/env-config.js";import{importApiRoutesHandler as
|
|
1
|
+
import{Hono as l}from"hono";import{TrieRouter as f}from"hono/router/trie-router";import{Logger as d}from"../tools/notifiers/logger.js";import{envConfig as a}from"../config/env-config.js";import{importApiRoutesHandler as g}from"./import-api-routes-handlers.js";import{getBodyWithSizeCheck as m}from"./helpers/get-body-with-size-check.js";import{setupLogger as D}from"./helpers/setup-logger.js";import{telemetry as c}from"../telemetry/index.js";import{KvService as w}from"../persistence/kv/services/kv-service.js";import{enhanceContext as R,enhanceMockServerContext as p}from"./helpers/enhance-context.js";import{isMockServerRoute as q}from"./helpers/is-mock-server.js";import{readWorkerConfig as h}from"../workers/worker-config.js";const u=new d,v=8e3,A=100;async function W(e){c.initialize(),D(u,e.requestHandlerId);const n=await H(e),r=await x(e,n).request(e.req.url,e.req),t=await m(r,e.maxResponseSizeMB);return{status:r.status,headers:Object.fromEntries(r.headers.entries()),body:t}}async function H(e){return(await(await b(e))()).default}function x(e,n){const o=new l({router:new f}),r=y(e.requestHandlerId);return o.all(e.slug,async t=>{if(typeof n!="function")return M(e,t);const s=r({honoCtx:t,ctx:{...e.ctxData,config:h(e.ctxData.config)},telemetry:c,getKv:()=>E(e),slug:e.slug}),i=await n(t.req.raw,s,e.staticData);return i instanceof Response?i:typeof i=="string"?t.text(i):t.json(i)}),o.onError((t,s)=>{if(u.error(`[${e.requestHandlerId}] ${t.message} ${t.stack}`),t instanceof SyntaxError)return s.text(t.message,500);throw t}),o}function y(e){return q(e)?p:R}function E(e){return w.getInstance({baseDbDir:e.serverOutDir,sqldRemoteDatabaseUrl:e.sqldRemoteDatabaseUrl,sqldRemoteDatabaseAuthToken:e.sqldRemoteDatabaseAuthToken})}function M(e,n){const o=`API function "${e.slug}" does not export a default function. Add 'export default function (request, context) { ... }' to the file.`;return u.error(`[${e.requestHandlerId}] ${o}`),a.isDevelopMode?n.text(o,500):n.newResponse(null,404)}async function b(e){const n=Date.now()+(a.isDevelopMode?v:0);let o;for(;;){try{const{requestHandlers:r}=await g(e.serverOutDir),t=r?.[e.requestHandlerId];if(typeof t=="function")return t;o=new Error(`Request handler "${e.requestHandlerId}" is not available yet.`)}catch(r){o=r}if(Date.now()>=n)throw o;await new Promise(r=>setTimeout(r,A))}}export{W as executeApiRoute};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as p from"workerpool";import{withPathPrefix as
|
|
1
|
+
import*as p from"workerpool";import{withPathPrefix as f}from"@redocly/theme/core/utils";import{envConfig as u}from"../config/env-config.js";import{telemetry as a}from"../telemetry/index.js";import{TimeoutExceededError as g}from"./errors/timeout-exceeded.js";import{serializeRequest as E}from"./helpers/serialize-request.js";import{apiRoutesWorkers as A,API_ROUTES_WORKER_KEY as T}from"../workers/api-routes-worker-pool.js";import{API_FUNCTIONS_REQUEST_HANDLER_ID as m}from"../plugins/api-functions/index.js";import{toWorkerConfig as h}from"../workers/worker-config.js";const S=1e4,R=15;async function U(e,r,i){a.initialize();const o=r.get("auth"),s=await i.resolveRouteStaticData(e)||{},n=f(e.slug);try{const{status:t,headers:l,body:c}=await A.exec(T,[{serverOutDir:i.serverOutDir,slug:n,requestHandlerId:e.requestHandlerId,maxResponseSizeMB:R,ctxData:{user:{teams:o.teams,email:o.claims.email,claims:o.claims,idpAccessToken:o.idpAccessToken,idpId:o.claims.idpId,isAuthenticated:o.isAuthenticated},config:h(i.config,i.configFile),outdir:i.outdir,baseUrl:new URL(r.req.url).origin},staticData:{...s,props:{...s.props,routeSlug:n,outdir:i.outdir}},req:await E(r.req.raw),sqldRemoteDatabaseUrl:u.SQLD_REMOTE_DATABASE_URL,sqldRemoteDatabaseAuthToken:u.SQLD_REMOTE_DATABASE_AUTH_TOKEN}],{timeout:S});_({requestHandlerId:e.requestHandlerId,status:t,ctx:r});const d=c.data?Buffer.from(c.data):null;return new Response(d,{status:t,headers:l})}catch(t){throw M({requestHandlerId:e.requestHandlerId,error:t}),t instanceof p.Promise.TimeoutError?new g("Timeout exceeded"):t}}function _({requestHandlerId:e,status:r,ctx:i}){e.startsWith(m)?a.sendApiFunctionCalledMessage([{id:"apiFunctionId",object:"apiFunction",uri:"urn:redocly:realm:api:apiFunction:apiFunctionId",handler:e,status:r,message:null}]):a.sendMockServerCalledMessage([{id:e,object:"mockServer",uri:`urn:redocly:realm:api:mockServer:${e}`,error:null,handler:e,headers:{"user-agent":i.req.header("user-agent")??null},status:r}])}function M({requestHandlerId:e,error:r}){e.startsWith(m)?a.sendApiFunctionErrorMessage([{id:"apiFunctionId",object:"apiFunction",uri:"urn:redocly:realm:api:apiFunction:apiFunctionId",status:500,handler:e,message:r.message}]):a.sendMockServerErrorMessage([{id:e,object:"mockServer",uri:`urn:redocly:realm:api:mockServer:${e}`,handler:e,message:r.message}])}export{U as runApiRoutesWorker,M as sendTelemetryErrorMessage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolve as
|
|
1
|
+
import{resolve as c,dirname as p}from"node:path";import{fileURLToPath as f}from"node:url";import{createReadStream as l}from"fs";import{envConfig as u}from"./config/env-config.js";import{telemetry as d}from"./telemetry/index.js";import{createRouter as v}from"./web-server/router.js";import{installProdRoutes as g}from"./web-server/routes/index.js";import{Store as S}from"./store.js";import{loadEnvVariables as R}from"./utils/envs/load-env-variables.js";import{startHttpServer as x}from"./web-server/http.js";import{readStaticData as D}from"./utils/static-data.js";import{startIdleTimeout as P}from"./web-server/middleware/idleTimeoutMiddleware.js";import{reporter as w}from"./tools/notifiers/reporter.js";import{logger as E}from"./tools/notifiers/logger.js";import{EntitlementsProvider as y}from"./entitlements/entitlements-provider.js";import{DatabasePreconnectService as b}from"./providers/database/database-preconnect-service.js";import{KvService as h}from"./persistence/kv/services/kv-service.js";import{runScorecardsWorker as I}from"./plugins/scorecards/workers/run-scorecards-worker.js";import{isScorecardsEnabled as O}from"./utils/is-scorecards-enabled.js";import{isRealmOrReef as T}from"./utils/is-realm-or-reef.js";import{renderPage as rr}from"./ssr/index.js";const k=new URL(import.meta.url),A=k.searchParams.get("only-exports")==="true";if(!A){let s=function(){const r=process.argv.findIndex(i=>i==="--port"||i==="-p");if(r===-1)return null;const t=process.argv[r+1];if(t.startsWith("-"))return null;const o=parseInt(t,10);return isNaN(o)?null:o};const e=p(f(import.meta.url));await R(e);const n=await S.fromServerFiles({outdir:c(e,"../client"),serverOutDir:e,serverMode:!0,contentDir:""});if(await y.instance().init({ignoreTokenExpiration:!0}),T()){await b.init(e);const r=await h.getInstance({baseDbDir:e});setInterval(()=>{r.clearExpired().catch(t=>{E.error("Failed to clear expired KV entries",t)})},300*1e3)}m(n).catch(r=>{console.error(r)});async function m(r){d.initialize();const t=v();g(t,r,{readStaticAsset:async a=>l(a),resolveRouteData:async a=>D(a.slug,r.outdir)}),w.printErrors();const o=s(),i=u.PORT;return await x(t,o??i??4e3),P(),O(r.config)&&I(r.serverOutDir,r.config.scorecards),t}}export{rr as renderPage};
|
|
@@ -2,5 +2,5 @@ import type { McpToolSchema } from '../../../../types';
|
|
|
2
2
|
import type { McpToolCallback, McpToolContext } from '../../types.js';
|
|
3
3
|
import { type SandboxCapability } from '../capabilities/index.js';
|
|
4
4
|
export declare function describeToolsSchema(tools: McpToolSchema[], capabilities: SandboxCapability[]): McpToolSchema;
|
|
5
|
-
export declare function describeTools(tools: McpToolSchema[],
|
|
5
|
+
export declare function describeTools(tools: McpToolSchema[], capabilities: SandboxCapability[], context: McpToolContext): McpToolCallback;
|
|
6
6
|
//# sourceMappingURL=describe-tools.d.ts.map
|
|
@@ -1,3 +1 @@
|
|
|
1
|
-
import{renderDescribeToolsOutput as
|
|
2
|
-
|
|
3
|
-
`);return E({tool:l,mode:m,serverType:u.Docs,args:t,result:h,...d}),k({tool:l,mode:m,serverType:u.Docs,durationMs:Math.round(performance.now()-b),outputLength:h.length,...d}),{content:[{type:"text",text:h}]}}catch(e){const i=e instanceof O,a=i?"tool_error":"server_error";throw E({tool:l,mode:m,serverType:u.Docs,args:t,errorClass:a,...d}),C({tool:l,mode:m,serverType:u.Docs,durationMs:Math.round(performance.now()-b),message:e instanceof Error?e.message:String(e),stack:!i&&e instanceof Error&&e.stack||"",errorClass:a,...d}),e}}}export{z as describeTools,B as describeToolsSchema};
|
|
1
|
+
import{renderDescribeToolsOutput as E}from"../capabilities/index.js";import{McpServerType as u,READ_ONLY_ANNOTATIONS as O}from"../../constants.js";import{logMcpToolCallAudit as h,reportMcpToolCalled as S,reportMcpToolError as b}from"../../workers/mcp-tool-telemetry.js";import{DESCRIBE_TOOLS_TOOL_NAME as l}from"../constants.js";import{DESCRIBE_TOOLS_DESCRIPTION as v}from"../prompts.js";import{sanitizeToolName as s}from"../build-tool-type-declarations.js";import{UnknownToolError as w}from"./tool-errors.js";import{callAttribution as D}from"../../utils/telemetry-attributes.js";function R(p,i){const n=[...new Set([...p.map(r=>s(r.name)),...i.map(r=>s(r.id))])];return{name:l,title:"Get docs function signatures",description:v,annotations:O,schema:{type:"object",required:[],additionalProperties:!1,properties:{tools:{type:"array",description:`Tool names to describe: ${n.join(", ")}. Omit to describe everything.`,items:n.length>0?{type:"string",enum:n}:{type:"string"},nullable:!0}}}}}function U(p,i,n){const r=new Map(p.map(t=>[s(t.name),t])),T=new Map(i.map(t=>[s(t.id),t]));return async t=>{const y=performance.now(),m=n.mcpMode??"code",d=D(n);try{const o=[...new Set((t.tools??[]).map(s))],a=o.length===0,c=o.filter(e=>!r.has(e)&&!T.has(e));if(c.length>0){const e=[...r.keys(),...T.keys()];throw new w(`Unknown tool(s): ${c.join(", ")}. Valid tools: ${e.join(", ")}.`)}const M=a?i:i.filter(e=>o.includes(s(e.id))),g=a?p:o.filter(e=>r.has(e)).map(e=>r.get(e)),f=E(g,M,n);return h({tool:l,mode:m,serverType:u.Docs,args:t,result:f,...d}),S({tool:l,mode:m,serverType:u.Docs,durationMs:Math.round(performance.now()-y),outputLength:f.length,...d}),{content:[{type:"text",text:f}]}}catch(o){const a=o instanceof w,c=a?"tool_error":"server_error";throw h({tool:l,mode:m,serverType:u.Docs,args:t,errorClass:c,...d}),b({tool:l,mode:m,serverType:u.Docs,durationMs:Math.round(performance.now()-y),message:o instanceof Error?o.message:String(o),stack:!a&&o instanceof Error&&o.stack||"",errorClass:c,...d}),o}}}export{U as describeTools,R as describeToolsSchema};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{logger as A}from"../../../tools/notifiers/logger.js";import{getFeedbackClientIp as N}from"../../../web-server/utils/feedback-ip.js";import{getForwardedOrigin as M,getInternalOrigin as L}from"../../../web-server/utils/get-request-origin.js";import{GATEWAY_FETCH_LIMIT_DEFAULTS as B,DEFAULT_ANONYMOUS_VISITOR_TEAM as H,RbacFeatures as j}from"../../../../constants/common.js";import{isObject as V}from"../../../../utils/guards/is-object.js";import{getAccessibleSkills as $}from"../../../utils/skills/get-accessible-skills.js";import{getSkillUrl as G}from"../../../utils/skills/get-skill-url.js";import{createMcpHandler as J}from"@modelcontextprotocol/server";import{createDocsMcpServer as K}from"../servers/docs-server.js";import{filterApiDescriptionsByRbac as Y,filterGraphqlDescriptionsByRbac as z}from"../utils.js";import{userSubject as W}from"../gateway-mcp/subject.js";import{ipRateLimitKey as X}from"../gateway-mcp/rate-limit/utils/ip-key.js";import{McpClientInputError as Q,createMethodNotAllowedError as Z,withErrorHandling as q}from"./errors.js";import{constructForbiddenResponse as x,constructInvalidTokenResponse as ee,constructUnauthorizedResponse as re,handleMcpAuth as oe}from"../auth/auth-handlers.js";import{isAssistantAnonymousToken as se}from"../auth/assistant-anonymous-token.js";import{shouldHandleMcpAuth as ne}from"../../../utils/rbac/should-handle-mcp-auth.js";import{extractTokenFromAuthHeader as ie}from"../utils/jwt.js";import{resolveMcpClient as te}from"../utils/mcp-client.js";import{randomUUID as ce}from"../../../utils/crypto/random-uuid.js";import{getPublicMcpEndpointStatus as ae}from"../utils/public-endpoint.js";import{narrowMcpConfigForCaller as pe}from"../gateway-mcp/caller-api-calls.js";import{DEFAULT_DOCS_MCP_SERVER_NAME as de,SSE_KEEPALIVE_INTERVAL_MS as
|
|
1
|
+
import{logger as A}from"../../../tools/notifiers/logger.js";import{getFeedbackClientIp as N}from"../../../web-server/utils/feedback-ip.js";import{getForwardedOrigin as M,getInternalOrigin as L}from"../../../web-server/utils/get-request-origin.js";import{GATEWAY_FETCH_LIMIT_DEFAULTS as B,DEFAULT_ANONYMOUS_VISITOR_TEAM as H,RbacFeatures as j}from"../../../../constants/common.js";import{isObject as V}from"../../../../utils/guards/is-object.js";import{getAccessibleSkills as $}from"../../../utils/skills/get-accessible-skills.js";import{getSkillUrl as G}from"../../../utils/skills/get-skill-url.js";import{createMcpHandler as J}from"@modelcontextprotocol/server";import{createDocsMcpServer as K}from"../servers/docs-server.js";import{filterApiDescriptionsByRbac as Y,filterGraphqlDescriptionsByRbac as z}from"../utils.js";import{userSubject as W}from"../gateway-mcp/subject.js";import{ipRateLimitKey as X}from"../gateway-mcp/rate-limit/utils/ip-key.js";import{McpClientInputError as Q,createMethodNotAllowedError as Z,withErrorHandling as q}from"./errors.js";import{constructForbiddenResponse as x,constructInvalidTokenResponse as ee,constructUnauthorizedResponse as re,handleMcpAuth as oe}from"../auth/auth-handlers.js";import{isAssistantAnonymousToken as se}from"../auth/assistant-anonymous-token.js";import{shouldHandleMcpAuth as ne}from"../../../utils/rbac/should-handle-mcp-auth.js";import{extractTokenFromAuthHeader as ie}from"../utils/jwt.js";import{resolveMcpClient as te}from"../utils/mcp-client.js";import{randomUUID as ce}from"../../../utils/crypto/random-uuid.js";import{getPublicMcpEndpointStatus as ae}from"../utils/public-endpoint.js";import{narrowMcpConfigForCaller as pe}from"../gateway-mcp/caller-api-calls.js";import{DEFAULT_DOCS_MCP_SERVER_NAME as de,SSE_KEEPALIVE_INTERVAL_MS as le}from"../constants.js";import{withPathPrefix as me}from"@redocly/theme/core/utils";import{PUBLIC_MCP_ROUTE as fe}from"@redocly/theme/core/constants/mcp";import{canAccessFeature as ue}from"../../../utils/rbac.js";import{ANONYMOUS_AUTH_DETAILS as ge}from"../../../constants/rbac.js";const E="X-Redocly-AI-Metadata";function he(r){if(r)try{const e=JSON.parse(r);if(V(e))return e;A.warn(`Ignoring ${E} header: not a JSON object.`)}catch{A.warn(`Ignoring ${E} header: invalid JSON.`)}}function Ae(r,e){return new URL(r).searchParams.get("mode")==="tools"?"tools":e}function I(){return{...ge,email:"",idpAccessToken:void 0,idpId:void 0}}const Me=async(r,e,i)=>{const t=!!e?.config?.access?.requiresLogin,c=e?.config?.access?.rbac||{},y=i?.props?.config?.mcpMode??"tools",p=Ae(r.url,y),C=i?.props?.config?.gatewayFetchBudgetPerExec??B.budgetPerExec,U=i?.props?.config?.searchIgnoredRoutes??[],o=i,R=!!o?.props?.config?.isPublicEndpoint;let u;const T=r.headers.get("Authorization");if(R)e.user=I();else if(ne(t,c)){const{isAuthenticated:n,isTokenValid:d,isFirstPartyAnonymous:g,currentUser:l,accessToken:h}=await oe(r,e);if(g)e.user=I();else if(n)l&&(e.user=l),u=h;else{const a=M(r),m=ae(e.config)==="served"?`${a}${me(fe)}`:void 0;return d===!1?ee(a,m):re(a,m)}}else if(T){const n=ie(T)??void 0;u=(n?await se(n):!1)?void 0:n}const w={isAuthenticated:!!e.user?.isAuthenticated,teams:e.user?.teams||[H],claims:e.user?.claims??{}};return ue(j.MCP,w,c,t)?await q(async()=>{const n=p==="code"&&(i?.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 Z();const d=o?.props?.config?.apiDescriptionsMap||{},g=o?.props?.config?.graphqlDescriptions||[],l=o?.props?.config?.mcpDocsServerName||de,{config:{mcp:h={}}}=e,a=Y(d,e.user,c,t),m=z(g,e.user,c,t),P=e.config.products?Object.values(e.config.products).map(s=>s?.name):[],D=h.docs?.name||l,f=he(r.headers.get(E)),_=o?.props?.tools||[],O=o?.props?.skillsBaseUrl||M(r),F=$(o?.props?.skills||[],e.user,c,t).map(s=>({slug:s.slug,name:s.name,description:s.description,content:s.content,uri:G(O,s.slug)})),S=await Ee(r),b=o?.props?.fetchCountSecret,k={name:D,tools:_,skills:F,context:{...e,config:{...e.config,mcp:pe(e.config.mcp,f)},configFile:o?.props?.configFile,accessToken:u,outdir:e.outdir||"",baseUrl:e.baseUrl||M(r),internalBaseUrl:L(r),apiDescriptionsMap:a,graphqlDescriptions:m,products:P,metadata:f,mcpMode:p,elicitationEnabled:n,gatewayFetchBudgetPerExec:C,fetchCountRequest:p==="code"&&b?{caller:{subject:W(e.user),ip:X(r)},secret:b}:void 0,searchIgnoredRoutes:U,clientIp:N(r),...te(r),requestId:ce(),messageId:typeof f?.messageId=="string"?f.messageId:void 0}},v=await K({...k,getKv:e.getKv});return await J(async()=>v,{keepAliveMs:le,onerror:s=>A.warn(`MCP handler: ${s.message}`)}).fetch(r,S!==void 0?{parsedBody:S}:void 0)},p):x()};async function Ee(r){try{return await r.json()}catch(e){throw new Q(e instanceof Error?e.message:String(e),{cause:e})}}var ze=Me;export{ze as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{withPathPrefix as g}from"@redocly/theme/core/utils";import{importApiRoutesHandler as h}from"../../../api-routes/import-api-routes-handlers.js";import{enhanceContext as D}from"../../../api-routes/helpers/enhance-context.js";import{telemetry as A}from"../../../telemetry/index.js";import{KvService as E}from"../../../persistence/kv/services/kv-service.js";import{envConfig as l}from"../../../config/env-config.js";import{acceptsHtml as R}from"../../../utils/ai-agent-detection.js";import{handleErrorPageRender as T}from"../../../web-server/utils.js";import{MCP_DOCS_SERVER_HANDLER_ID as _,MCP_NOT_ALLOWED_TEMPLATE_ID as w}from"../../../constants/mcp.js";import{resolvePublicBaseUrl as S}from"../../../web-server/utils/resolve-public-base-url.js";import{getForwardedOrigin as q}from"../../../web-server/utils/get-request-origin.js";import{applyEdgeRateLimit as C}from"../gateway-mcp/rate-limit/request-limiter/apply-edge-rate-limit.js";import{fetchCountSecret as O}from"../gateway-mcp/rate-limit/fetch-limiter/fetch-count-secret.js";async function
|
|
1
|
+
import{withPathPrefix as g}from"@redocly/theme/core/utils";import{importApiRoutesHandler as h}from"../../../api-routes/import-api-routes-handlers.js";import{enhanceContext as D}from"../../../api-routes/helpers/enhance-context.js";import{telemetry as A}from"../../../telemetry/index.js";import{KvService as E}from"../../../persistence/kv/services/kv-service.js";import{envConfig as l}from"../../../config/env-config.js";import{acceptsHtml as R}from"../../../utils/ai-agent-detection.js";import{handleErrorPageRender as T}from"../../../web-server/utils.js";import{MCP_DOCS_SERVER_HANDLER_ID as _,MCP_NOT_ALLOWED_TEMPLATE_ID as w}from"../../../constants/mcp.js";import{resolvePublicBaseUrl as S}from"../../../web-server/utils/resolve-public-base-url.js";import{getForwardedOrigin as q}from"../../../web-server/utils/get-request-origin.js";import{applyEdgeRateLimit as C}from"../gateway-mcp/rate-limit/request-limiter/apply-edge-rate-limit.js";import{fetchCountSecret as O}from"../gateway-mcp/rate-limit/fetch-limiter/fetch-count-secret.js";async function K(i,t,e){const n=t.req.raw;if(i.requestHandlerId===_&&n.method==="GET"&&R(n.headers.get("accept"))&&e.globalData.mcpData?.docs?.enabled)return T(t,e,{slug:i.slug},405,w);const r=t.get("auth"),o=await C(n,e);if(o)return o;const s=await e.resolveRouteStaticData(i)||{},c=g(i.slug),{requestHandlers:m}=await h(e.serverOutDir),p=m[i.requestHandlerId],d=(await p()).default,u=async()=>await E.getInstance({baseDbDir:e.serverOutDir,sqldRemoteDatabaseUrl:l.SQLD_REMOTE_DATABASE_URL,sqldRemoteDatabaseAuthToken:l.SQLD_REMOTE_DATABASE_AUTH_TOKEN}),f=D({honoCtx:t,ctx:{user:{teams:r.teams,email:r.claims.email,claims:r.claims,idpAccessToken:r.idpAccessToken,idpId:r.claims.idpId,isAuthenticated:r.isAuthenticated},config:e.config,outdir:e.outdir,baseUrl:q(t.req.raw)},telemetry:A,getKv:u}),a=await d(t.req.raw,f,{...s,props:{...s.props,routeSlug:c,outdir:e.outdir,fetchCountSecret:O(),skills:e.config.skills?.hide===!0?[]:e.getSkills(),skillsBaseUrl:S(e,t),configFile:e.configFile}});return a instanceof Response?a:typeof a=="string"?new Response(a,{headers:{"Content-Type":"text/plain"}}):new Response(JSON.stringify(a),{headers:{"Content-Type":"application/json"}})}export{K as handleMcpRequest};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{CREATE_ANONYMOUS_SESSION_TOOL_NAME as C,MANAGE_API_CREDENTIALS_TOOL_NAME as k,SUBMIT_FEEDBACK_TOOL_NAME as S}from"../constants.js";import{isAnonymousSessionToolAvailable as U}from"../docs-mcp/tools/core/create-anonymous-session.js";import{isManageApiCredentialsToolAvailable as w,manageApiCredentials as y,manageApiCredentialsSchema as H}from"../codemode/tools/manage-credentials.js";import{EXECUTE_TOOL_SCHEMA as h,executeToolTitle as I}from"../codemode/tools/execute-schema.js";import{EXECUTE_TOOL_NAME as R,CODE_MODE_TOOL_NAMES as M,FETCH_TOOL_NAME as B}from"../codemode/constants.js";import{buildCodeModeInstructions as F,buildExecuteDescription as x}from"../codemode/prompts.js";import{toToolListEntries as z}from"../codemode/build-tool-list.js";import{composeExecuteAnnotations as X,getActiveCapabilities as Y}from"../codemode/capabilities/index.js";import{describeToolsSchema as j,describeTools as G}from"../codemode/tools/describe-tools.js";import{availableTools as g}from"../utils/available-tools.js";import{resolveDocsSiteUrl as K}from"../utils/docs-site-url.js";import{isToolDisabledByCaller as b}from"../utils/disabled-tools.js";import{toRegisterToolConfig as N}from"../utils/tool-registration.js";import{createMcpServer as P,createToolDispatch as W,extractSerializableContext as q}from"./mcp-server.js";const J=new Set([...M].filter(s=>s!==S));function D(s,t){const e=t.schema;if(s)return e;const{sessionHandle:u,...T}=e.properties;return{...e,properties:T}}function Q(s){return Object.fromEntries(s.map(t=>[t.name,t.schema]))}async function pe({name:s,tools:t,context:e,skills:u=[],getKv:T}){const l=q(e),n=e.mcpMode==="code",c=n?Y(e):[],O=c.some(o=>o.id===B),m=n?await g(t.filter(o=>!M.has(o.name)),l):[],_=z(m,c),E=U(e)&&!b(C,e.metadata),v=n&&w(e)&&!b(k,e.metadata),p=P(s,n?F(O,_):void 0),A={getClientCapabilities:()=>p.server.getClientCapabilities(),getKv:T},f=W(n?{...l,sandboxToolSchemas:Q(m)}:l,A);if(n){const o=X(m,c,e.config?.mcp),r=[{tool:{...h,title:I(o),description:x(O,_,K(e.config?.seo?.siteUrl,e.baseUrl)),schema:D(E,h),annotations:o},callback:f(R)},{tool:j(m,c),callback:G(m,c,e)}],i=t.find(a=>a.name===S);i&&!b(i.name,e.metadata)&&r.push({tool:i,callback:f(i.name)});const d=t.find(a=>a.name===C);if(d&&E&&r.push({tool:d,callback:f(d.name)}),v){const a=H(e);r.push({tool:{...a,schema:D(E,a)},callback:y(l,A)})}for(const{tool:a,callback:L}of r)p.registerTool(a.name,N(a),L)}else{const o=t.filter(i=>!J.has(i.name)),r=new Set((await g(o,l)).map(i=>i.name));for(const i of o){const d=p.registerTool(i.name,N({...i,outputSchema:void 0}),f(i.name));r.has(i.name)||d.disable()}}for(const o of u)p.registerResource(o.slug,o.uri,{title:o.name,description:o.description,mimeType:"text/markdown"},async()=>({contents:[{uri:o.uri,mimeType:"text/markdown",text:o.content}]}));return p}export{pe as createDocsMcpServer};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{McpServer as U}from"@modelcontextprotocol/server";import{logger as S}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as _,MCP_TOOL_WORKER_KEY as O}from"../../../workers/mcp-tool-worker-pool.js";import{secretStore as
|
|
1
|
+
import{McpServer as U}from"@modelcontextprotocol/server";import{logger as S}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as _,MCP_TOOL_WORKER_KEY as O}from"../../../workers/mcp-tool-worker-pool.js";import{secretStore as F}from"../gateway-mcp/secret-store.js";import{readAuthRequiredMeta as L,stripAuthRequiredMeta as E,upstreamAuthKey as b}from"../gateway-mcp/upstream-auth.js";import{acceptedCredentialsLink as D,buildCredentialsAcceptedHint as K,buildCredentialsToolHint as j,buildUpstreamCredentialFallback as q,buildUpstreamCredentialInputRequired as v,credentialElicitationAnswer as H,dedupeElicitation as N,isStatelessRequest as P,schemeLabelOf as $,supportsUrlElicitation as B}from"../gateway-mcp/elicitation.js";import{DEFAULT_SCHEME_NAME as y,resolveUpstreamApiAuth as W}from"../gateway-mcp/upstream-auth-scheme.js";import{MISSING_SESSION_HANDLE_MESSAGE as G,resolveSecretSubject as V}from"../gateway-mcp/subject.js";import{toWorkerContext as Y}from"../workers/worker-context.js";const J=6e4;function de(e,t){return new U({name:e,version:new Date().toISOString().slice(0,10)},{capabilities:{logging:{}},...t?{instructions:t}:{}})}function pe(e,t){return r=>async(n,s)=>{S.info(`MCP tool called: ${r}`);const i=e.elicitationEnabled??!1,u=P(s.mcpReq),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 c=async()=>{if(!(!i||!o||!t?.getKv))try{const m=await t.getKv();return await F.getSecretsForSubject(m,o)}catch(m){S.warn(`gateway mcp secret store error, continuing unauthenticated: ${String(m)}`);return}};let l;const d=async()=>{l=await c();const m={toolName:r,args:n,context:Y({...e,upstreamSecrets:l}),extra:te(s)};return await _.exec(O,[m],{timeout:J})};let p=await d();const f=L(p);if(f.length>0&&i&&!o)return E(R(p,[{type:"text",text:G}]));if(f.length>0&&i&&o&&t?.getKv){S.info(`gateway mcp credentials prompt: era=${u?"2026-07-28":"2025"} clientElicitation=${Q(t)}`);const m=await t.getKv(),h=await X(e,f),w=f.map(C=>Z(C,h)),k=z(l),g=u?await x(e,s,w,o,m,k,h,t):await ee(e,w,o,m,k,h);switch(g.kind){case"inputRequired":return g.result;case"retry":p=await d();break;case"prependText":p=R(p,[{type:"text",text:g.text},...p.content]);break;case"none":break}}return E(p)}}function R(e,t){const{structuredContent:r,...n}=e;return{...n,content:t,isError:!0}}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 W(e,n));return r}function Z(e,t){const r=t.get(e.api);if(!r)return{...e,scheme:e.scheme??y};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(b({api:r,scheme:s}));return t}function I(e,t){return e.status===401&&!t.has(b(e))}function T(e,t){const r=t.get(e.api),n=e.sentScheme??e.scheme;return r&&n?$(r.schemes,n):void 0}function M(e,t){return e.map(r=>j(r,T(r,t))).join(`
|
|
2
2
|
|
|
3
|
-
`)}function A(e,t,r,n,
|
|
3
|
+
`)}function A(e,t,r,n,s){const[i,...u]=t,a=s.get(i.api),o=i.scheme??a?.defaultScheme??y,c=i.schemes&&i.schemes.length>1?i.schemes:void 0,l=c?void 0:T(i,s);return{subject:r,baseUrl:e.baseUrl??"",target:{api:i.api,name:i.name,scheme:o,...l?{schemeLabel:l}:{},...c?{acceptedSchemes:c}:{}},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:u.map(d=>d.name)}}}async function x(e,t,r,n,s,i,u,a){const o=H(t.mcpReq.inputResponses);if(o==="declined")return{kind:"none"};const c=r.filter(d=>I(d,i));if(c.length===0)return{kind:"prependText",text:M(r,u)};if(o==="accepted"){const d=await D(t.mcpReq.requestState(),{kv:s,subject:n,baseUrl:e.baseUrl??"",apis:c.map(f=>f.api)}),p=d?.apiName??c[0].name;return{kind:"prependText",text:K(p,d?.url)}}const l=A(e,c,n,s,u);return B(a?.getClientCapabilities())?{kind:"inputRequired",result:await v(l)}:{kind:"prependText",text:await q(l)}}async function ee(e,t,r,n,s,i){const u=t.filter(c=>I(c,s));if(u.length===0)return{kind:"prependText",text:M(t,i)};const a=A(e,u,r,n,i);return{kind:"prependText",text:await N(`${r}:${b(u[0])}`,()=>q(a))}}function te(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,fetchCountRequest:e.fetchCountRequest,clientIp:e.clientIp,client:e.client,clientVersion:e.clientVersion,requestId:e.requestId,messageId:e.messageId,upstreamSecrets:e.upstreamSecrets,searchIgnoredRoutes:e.searchIgnoredRoutes,internalBaseUrl:e.internalBaseUrl,sandboxToolSchemas:e.sandboxToolSchemas,configFile:e.configFile,fs:void 0}}export{de as createMcpServer,pe as createToolDispatch,me as extractSerializableContext};
|
|
@@ -6,6 +6,7 @@ import type { McpToolSchema } from '../../types';
|
|
|
6
6
|
import type { Skill } from '../../types/plugins/skills.js';
|
|
7
7
|
import type { UpstreamSecrets } from './gateway-mcp/secret-store.js';
|
|
8
8
|
import type { SiteBranding } from './handlers/credentials-page/types.js';
|
|
9
|
+
import type { StoreConfigFile, WorkerConfig } from '../../workers/worker-config.js';
|
|
9
10
|
type ObjectValues<T> = T[keyof T];
|
|
10
11
|
export type McpServerType = ObjectValues<typeof McpServerType>;
|
|
11
12
|
export type McpErrorCodeTypes = ObjectValues<typeof McpErrorCodes>;
|
|
@@ -40,6 +41,7 @@ export type McpDocsStaticData = PageStaticData & {
|
|
|
40
41
|
tools?: McpToolSchema[];
|
|
41
42
|
skills?: Skill[];
|
|
42
43
|
skillsBaseUrl?: string;
|
|
44
|
+
configFile?: StoreConfigFile;
|
|
43
45
|
};
|
|
44
46
|
};
|
|
45
47
|
export type CredentialsPageStaticData = PageStaticData & {
|
|
@@ -117,6 +119,11 @@ export type McpToolContext = Omit<ApiFunctionsContext, 'telemetry' | 'getKv' | k
|
|
|
117
119
|
internalBaseUrl?: string;
|
|
118
120
|
sandboxToolSchemas?: Record<string, Record<string, unknown>>;
|
|
119
121
|
fs?: McpToolFileSystem;
|
|
122
|
+
configFile?: StoreConfigFile;
|
|
123
|
+
};
|
|
124
|
+
/** `McpToolContext` as it crosses to a worker. */
|
|
125
|
+
export type McpToolWorkerContext = Omit<McpToolContext, 'config' | 'fs' | 'configFile'> & {
|
|
126
|
+
config: WorkerConfig;
|
|
120
127
|
};
|
|
121
128
|
export type McpToolHandler<TArgs extends ToolArgs = ToolArgs> = (args: TArgs, context: McpToolContext, extra: McpToolExtra) => Promise<McpToolWorkerResponse | McpToolJsonValue>;
|
|
122
129
|
export type McpToolIsAvailable = (context: McpToolContext) => boolean | Promise<boolean>;
|
|
@@ -130,6 +137,9 @@ export type McpToolExecutionParams = {
|
|
|
130
137
|
context: McpToolContext;
|
|
131
138
|
extra: McpToolExtra;
|
|
132
139
|
};
|
|
140
|
+
export type McpToolWorkerParams = Omit<McpToolExecutionParams, 'context'> & {
|
|
141
|
+
context: McpToolWorkerContext;
|
|
142
|
+
};
|
|
133
143
|
export type CustomMcpToolsState = {
|
|
134
144
|
registeredToolNames: string[];
|
|
135
145
|
warnedNoOutputSchemaTools: Set<string>;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { McpToolSchema } from '../../../types';
|
|
2
|
+
import type { McpToolContext } from '../types.js';
|
|
3
|
+
/** The candidates this request may use. Asks the worker pool only when a candidate is gated. */
|
|
4
|
+
export declare function availableTools(candidates: McpToolSchema[], context: McpToolContext): Promise<McpToolSchema[]>;
|
|
5
|
+
//# sourceMappingURL=available-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{logger as n}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as m,MCP_TOOL_AVAILABILITY_KEY as s}from"../../../workers/mcp-tool-worker-pool.js";import{toWorkerContext as f}from"../workers/worker-context.js";import{isToolDisabledByCaller as p}from"./disabled-tools.js";async function d(o,r){const a=o.filter(t=>!p(t.name,r.metadata)),e=a.filter(t=>t.hasAvailabilityGate).map(t=>t.name);if(e.length===0)return a;let i;try{i=await m.exec(s,[{toolNames:e,context:f(r)}],{timeout:1e4})}catch(t){return l(a,t instanceof Error?t.message:String(t))}return!i||typeof i!="object"?l(a,"the worker returned no availability map"):a.filter(t=>i[t.name]!==!1)}function l(o,r){const a=o.filter(e=>e.hasAvailabilityGate);return n.warn(`MCP tool availability is unknown (${r}); the tool list drops the gated tools: ${a.map(e=>e.name).join(", ")}.`),o.filter(e=>!e.hasAvailabilityGate)}export{d as availableTools};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpToolContext, McpToolWorkerContext } from '../types.js';
|
|
2
|
+
export declare function toWorkerContext({ config, configFile, fs: _fs, ...context }: McpToolContext): McpToolWorkerContext;
|
|
3
|
+
export declare function fromWorkerContext(context: McpToolWorkerContext): McpToolContext;
|
|
4
|
+
//# sourceMappingURL=worker-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readWorkerConfig as f,toWorkerConfig as t}from"../../../workers/worker-config.js";function g({config:o,configFile:r,fs:e,...n}){return{...n,config:t(o,r)}}function c(o){return{...o,config:f(o.config)}}export{c as fromWorkerContext,g as toWorkerContext};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{appendFile as D}from"node:fs/promises";import{existsSync as v}from"node:fs";import b from"node:path";import{Client as N}from"typesense";import{SEARCH_GROUP_FACET_FIELD as A,SEARCH_PRODUCT_FIELD as q,SEARCH_RBAC_FIELD as T,SEARCH_VERSION_FIELD as h}from"../../../../../constants/common.js";import{DISABLE_DEEP_LINK_IF_FIELDS_EXIST as L,HIGHLIGHTED_TEXT_MAX_LENGTH as R,SEARCH_DATA_EXPORT_FOLDER as C,SEARCH_DOCUMENT_METADATA_KEY as j,SEARCH_GROUP_LIMIT as O,SEARCH_MAX_FACET_VALUES as y,SEARCH_MAX_INMEMORY_DOCUMENTS_COUNT as k}from"../../../../constants/plugins/search.js";import{telemetryTraceStep as P}from"../../../../telemetry/helpers/trace-step.js";import{envConfig as _}from"../../../../config/env-config.js";import{logger as F}from"../../../../tools/notifiers/logger.js";import{ensureDir as E}from"../../../../utils/index.js";class X{#e=null;#o="";#t=[{name:"title",type:"string",facet:!1,optional:!0},{name:"text",type:"string",facet:!1,optional:!0},{name:"path",type:"string[]",facet:!1,optional:!0},{name:"isAdditionalOperation",type:"bool",facet:!1,optional:!0,queryable:!1},{name:"deprecated",type:"bool",facet:!1,optional:!0,queryable:!1},{name:"parameters",type:"object[]",facet:!1,optional:!0},{name:"parameters.name",type:"string[]",facet:!1,optional:!0,queryable:!1},{name:"parameters.description",type:"string[]",facet:!1,optional:!0,queryable:!1},{name:"parameters.place",type:"string[]",facet:!1,optional:!0,queryable:!1},{name:"parameters.path",type:"string[]",facet:!1,optional:!0,queryable:!1},{name:q,type:"string",facet:!0,optional:!0},{name:h,type:"object",facet:!0,optional:!0},{name:T,type:"string[]",facet:!0},{name:"metadata_curated",type:"bool",facet:!1,optional:!0,queryable:!1},{name:"metadata_keywords_excludes",type:"string[]",facet:!1,optional:!0,queryable:!1},{name:"metadata_keywords_includes",type:"string[]",facet:!1,optional:!0,queryable:!1}];#s=new Map;#r={ORGANIZATION_ID:_.ORGANIZATION_ID??"",PROJECT_ID:_.PROJECT_ID??"",TYPESENSE_API_URL:_.TYPESENSE_API_URL??"",TYPESENSE_API_KEY:_.TYPESENSE_API_KEY??""};constructor(){if(!_.isBuildMode){for(const[e,t]of Object.entries(this.#r))if(!t)throw new Error(`Cannot initialize search index. ${e} is not set`);this.#o=`${this.#r.ORGANIZATION_ID}_${this.#r.PROJECT_ID}_`,this.#e=new N({nodes:[{url:this.#r.TYPESENSE_API_URL}],apiKey:this.#r.TYPESENSE_API_KEY,connectionTimeoutSeconds:5})}}async initIndexSchema(e){this.#_(e)}async addDocuments(e,t){const{locale:s,outDir:a}=t,r=e.map(n=>{const o={...n},c=n[j];return c&&(c.curated&&(o.metadata_curated=c.curated),c.excludes&&(o.metadata_keywords_excludes=c.excludes),c.includes&&(o.metadata_keywords_includes=c.includes)),o}),i=this.#s.get(s)??[];this.#s.set(s,i.concat(r)),i.length+e.length>=k&&await this.exportDocuments(a)}async search(e,t){return await P("com.redocly.search.performed",async s=>{s?.setAttribute("id","searchQueryId"),s?.setAttribute("object","searchQuery"),s?.setAttribute("uri","urn:redocly:realm:api:searchQuery:searchQueryId");const{query:a,locale:r,filter:i,loadMore:n}=e,o=this.#u(r),c={collection:o,q:a||"*",query_by:this.#f(),max_facet_values:y};if(s?.setAttribute("collectionName",o),s?.setAttribute("locale",r),this.#e)if(n){const{groupKey:l,offset:u}=n,f={offset:u,filter_by:this.#a(t,i),facet_by:"*",per_page:O};return s?.setAttribute("isLoadMore",!0),s?.setAttribute("filter",f.filter_by),this.#p(await this.#e.collections(o).documents().search({...c,...f}),l)}else{const l=this.#a(t,i);s?.setAttribute("filter",l);const u=await this.#e.multiSearch.perform({searches:this.#b(t,i)},c),f=this.#i(u,this.#t.filter(p=>p.facet).map(p=>p.name));f&&F.warn("Search facet requests failed: %s",f);const m=this.#m(u),d=(m[A]??[]).map(p=>p.value);s?.setAttribute("groups",d.join(",")),d.length===y&&F.warn("Search category facet list reached the limit of %s; results for some categories may be missing",y);const I=[...new Set(this.#h(i))].slice(0,y),g=I.length?I:d;if(!g.length)return{facets:m,documents:{},groupCounts:{}};const $=await this.#e.multiSearch.perform({searches:g.map(p=>({filter_by:`${A}:=${this.#n(p)} && ${l}`,per_page:O}))},c),S=this.#i($,g);return S&&F.warn("Search category requests failed: %s",S),this.#d($,g,m,i)}else return{facets:{},documents:{}}})}#p(e,t){const s={facets:{},documents:{[t]:[]},groupCounts:typeof e.found=="number"?{[t]:e.found}:{}};if(e.hits&&e.hits.length)for(const a of e.hits)s.documents[t].push({document:this.#c(a),highlight:this.#l(a)});return s}#m(e){const t={};if("results"in e){for(const s of e.results)if(s.facet_counts&&s.facet_counts.length)for(const a of s.facet_counts)t[a.field_name]=a.counts.map(r=>({value:r.value,count:r.count}))}return t}#i(e,t){return"results"in e?e.results.map((s,a)=>({result:s,label:t[a]})).filter(({result:s})=>"error"in s).map(({result:s,label:a})=>`${a}: ${s.error}`).join("; "):""}#d(e,t,s,a){const r={},i={facets:s,documents:{},groupCounts:r};for(const n of this.#h(a))i.documents[n]=[];return"results"in e&&e.results.forEach((n,o)=>{const c=t[o];typeof n.found=="number"&&(r[c]=n.found);const l=n.hits??[];l.length&&(i.documents[c]=l.map(u=>({document:this.#c(u),highlight:this.#l(u)})))}),i}#c(e){return this.#y(e)}#l(e){const t={},s=[];for(const[a,r]of Object.entries(e.highlight??{}))if(Array.isArray(r)){if(a==="path"){const i=r,n=[];for(const o of i)n.push(o.snippet);t.path=n}else if(a==="parameters"){const i=r;for(const n of i){let o=!1;for(const[c,l]of Object.entries(n))if(c!=="deepLink")if(Array.isArray(l))for(const u of l)u.matched_tokens?.length&&(o=!0);else l.matched_tokens?.length&&(o=!0);if(o){const c={name:n.name?.snippet||"",description:n.description?.snippet||"",place:n.place?.snippet||"",path:n.path?.map(l=>l?.snippet)||[]};t.parameters=[c];break}}}}else t[a]=r.snippet,s.push(a);for(const[a,r]of Object.entries(e.document))!s.includes(a)&&typeof r=="string"&&(t[a]=r.length>R?`${r.substring(0,R)}...`:r);return t.parameters||(t.parameters=[]),t}#y(e){let t;const s=Object.keys(e.highlight??{});for(const r of L)if(s.some(i=>i===r))return e.document;const a=e.highlight?.parameters;if(a){for(const r of a)for(const[i,n]of Object.entries(r))if(i!=="deepLink"){if(Array.isArray(n)){for(const o of n)if(o.matched_tokens?.length){t=r.deepLink?.snippet??t;break}}else if(n.matched_tokens?.length){t=r.deepLink?.snippet??t;break}}}if(t){const r=t.split("#")[1];return{...e.document,url:`${e.document.url}#${r}`}}return e.document}async exportDocuments(e){const t='{"documents":[',s=E(b.join(e,C));for(const[a,r]of this.#s){if(r.length===0)continue;const i=E(b.join(s,`${a}.json`)),n=!v(i),o=JSON.stringify(r).substring(1).slice(0,-1),c=n?t+o:","+o;await D(i,c,{encoding:"utf8"}),this.#s.set(a,[])}}async exportIndexes(e){const t=E(b.join(e,C));for(const s of this.#s.keys()){const a=E(b.join(t,`${s}.json`)),r=`],"schemaFields":${JSON.stringify(this.#t)}}`;await D(a,r,{encoding:"utf8"})}}async import(e){}async countFacets(e,t){return await P("com.redocly.search.facets",async s=>{s?.setAttribute("id","searchFacetsId"),s?.setAttribute("object","searchFacets"),s?.setAttribute("uri","urn:redocly:realm:api:searchFacets:searchFacetsId");const{locale:a,query:r,facetQuery:i,filter:n,field:o}=e,c=this.#u(a),l={q:r||"*",query_by:this.#f(),facet_by:"*",facet_query:this.#g(i,o),filter_by:this.#a(t,n,o),max_facet_values:y};if(s?.setAttribute("collectionName",c),s?.setAttribute("query",l.q),s?.setAttribute("facetQuery",l.facet_query),s?.setAttribute("filter",l.filter_by),this.#e){const u={},f=await this.#e.collections(c).documents().search(l);if(f.facet_counts&&f.facet_counts.length)for(const m of f.facet_counts)u[m.field_name]=m.counts.map(d=>({value:d.value,count:d.count,isCounterVisible:!!r||(n?.length||0)>0}));return u}else return{}})}#u(e){return`${this.#o}${e}`}#_(e){for(const[t,{queryable:s}]of e)this.#t.find(a=>a.name===t)||this.#t.push({queryable:s,name:t,type:"string",facet:!0,optional:!0})}#f(){return this.#t.filter(e=>e.facet?e.queryable:e.queryable!==!1).map(e=>e.name).join(",")}#n(e){return`\`${e.replaceAll("`","")}\``}#a(e,t,s){const a=`${T}:[${e.teams.map(i=>`'${i}'`).join(", ")}]`;let r=`${h}.isDefault:true`;if(t&&t.length){const i=t.filter(o=>o.field!==s&&o.field!==h).map(o=>{const c=o.values.map(l=>this.#n(l));return c.length?`${o.field}:=[${c.join(", ")}]`:""}).join(" && "),n=t.find(o=>o.field===h);if(n&&n.values.length&&n.values.length===2){const o=n.values[0],c=n.values[1],l=`(${h}.folderId:! ${o} && ${h}.isDefault:true)`,u=`(${h}.folderId: ${o} && ${h}.version:=${this.#n(c)})`;r=`(${l} || ${u})`}return i?`${a} && ${i} && ${r}`:`${a} && ${r}`}else return`${a} && ${r}`}#g(e,t){return e&&t?`${t}:${e}`:""}#h(e){const t=[];if(e&&e.length)for(const s of e)s.field===A&&t.push(...s.values);return t}#b(e,t){const s=[],a=this.#t.filter(r=>r.facet);for(const r of a){const i={facet_by:r.name,filter_by:this.#a(e,t,r.name)};s.push(i)}return s}cleanupFacetValues(e){const t=e.getSearchFacets();t.forEach(s=>{s.values=[]}),e.setSearchFacets(t)}}export{X as Typesense};
|
|
1
|
+
import{appendFile as D}from"node:fs/promises";import{existsSync as v}from"node:fs";import b from"node:path";import{Client as N}from"typesense";import{SEARCH_GROUP_FACET_FIELD as A,SEARCH_PRODUCT_FIELD as q,SEARCH_RBAC_FIELD as T,SEARCH_VERSION_FIELD as h}from"../../../../../constants/common.js";import{DISABLE_DEEP_LINK_IF_FIELDS_EXIST as L,HIGHLIGHTED_TEXT_MAX_LENGTH as R,SEARCH_DATA_EXPORT_FOLDER as C,SEARCH_DOCUMENT_METADATA_KEY as j,SEARCH_GROUP_LIMIT as O,SEARCH_MAX_FACET_VALUES as y,SEARCH_MAX_INMEMORY_DOCUMENTS_COUNT as k}from"../../../../constants/plugins/search.js";import{telemetryTraceStep as P}from"../../../../telemetry/helpers/trace-step.js";import{envConfig as _}from"../../../../config/env-config.js";import{logger as F}from"../../../../tools/notifiers/logger.js";import{ensureDir as E}from"../../../../utils/index.js";class X{#e=null;#o="";#t=[{name:"title",type:"string",facet:!1,optional:!0},{name:"text",type:"string",facet:!1,optional:!0},{name:"path",type:"string[]",facet:!1,optional:!0},{name:"isAdditionalOperation",type:"bool",facet:!1,optional:!0,queryable:!1},{name:"deprecated",type:"bool",facet:!1,optional:!0,queryable:!1},{name:"parameters",type:"object[]",facet:!1,optional:!0},{name:"parameters.name",type:"string[]",facet:!1,optional:!0,queryable:!1},{name:"parameters.description",type:"string[]",facet:!1,optional:!0,queryable:!1},{name:"parameters.place",type:"string[]",facet:!1,optional:!0,queryable:!1},{name:"parameters.path",type:"string[]",facet:!1,optional:!0,queryable:!1},{name:q,type:"string",facet:!0,optional:!0},{name:h,type:"object",facet:!0,optional:!0},{name:T,type:"string[]",facet:!0},{name:"metadata_curated",type:"bool",facet:!1,optional:!0,queryable:!1},{name:"metadata_keywords_excludes",type:"string[]",facet:!1,optional:!0,queryable:!1},{name:"metadata_keywords_includes",type:"string[]",facet:!1,optional:!0,queryable:!1}];#s=new Map;#r={ORGANIZATION_ID:_.ORGANIZATION_ID??"",PROJECT_ID:_.PROJECT_ID??"",TYPESENSE_API_URL:_.TYPESENSE_API_URL??"",TYPESENSE_API_KEY:_.TYPESENSE_API_KEY??""};constructor(){if(!_.isBuildMode){for(const[e,t]of Object.entries(this.#r))if(!t)throw new Error(`Cannot initialize search index. ${e} is not set`);this.#o=`${this.#r.ORGANIZATION_ID}_${this.#r.PROJECT_ID}_`,this.#e=new N({nodes:[{url:this.#r.TYPESENSE_API_URL}],apiKey:this.#r.TYPESENSE_API_KEY,connectionTimeoutSeconds:5})}}async initIndexSchema(e){this.#_(e)}async addDocuments(e,t){const{locale:s,outDir:a}=t,r=e.map(n=>{const o={...n},c=n[j];return c&&(c.curated&&(o.metadata_curated=c.curated),c.excludes&&(o.metadata_keywords_excludes=c.excludes),c.includes&&(o.metadata_keywords_includes=c.includes)),o}),i=this.#s.get(s)??[];this.#s.set(s,i.concat(r)),i.length+e.length>=k&&await this.exportDocuments(a)}async search(e,t){return await P("com.redocly.search.performed",async s=>{s?.setAttribute("id","searchQueryId"),s?.setAttribute("object","searchQuery"),s?.setAttribute("uri","urn:redocly:realm:api:searchQuery:searchQueryId");const{query:a,locale:r,filter:i,loadMore:n}=e,o=this.#u(r),c={collection:o,q:a||"*",query_by:this.#f(),max_facet_values:y};if(s?.setAttribute("collectionName",o),s?.setAttribute("locale",r),this.#e)if(n){const{groupKey:l,offset:u}=n,f={offset:u,filter_by:this.#a(t,i),facet_by:"*",per_page:O};return s?.setAttribute("isLoadMore",!0),s?.setAttribute("filter",f.filter_by),this.#p(await this.#e.collections(o).documents().search({...c,...f}),l)}else{const l=this.#a(t,i);s?.setAttribute("filter",l);const u=await this.#e.multiSearch.perform({searches:this.#b(t,i)},c),f=this.#i(u,this.#t.filter(p=>p.facet).map(p=>p.name));f&&F.warn("Search facet requests failed: %s",f);const m=this.#m(u),d=(m[A]??[]).map(p=>p.value);s?.setAttribute("groups",d.join(",")),d.length===y&&F.warn("Search category facet list reached the limit of %s; results for some categories may be missing",y);const I=[...new Set(this.#h(i))].slice(0,y),g=I.length?I:d;if(!g.length)return{facets:m,documents:{},groupCounts:{}};const $=await this.#e.multiSearch.perform({searches:g.map(p=>({filter_by:`${A}:=${this.#n(p)} && ${l}`,per_page:O}))},c),S=this.#i($,g);return S&&F.warn("Search category requests failed: %s",S),this.#d($,g,m,i)}else return{facets:{},documents:{}}})}#p(e,t){const s={facets:{},documents:{[t]:[]},groupCounts:typeof e.found=="number"?{[t]:e.found}:{}};if(e.hits&&e.hits.length)for(const a of e.hits)s.documents[t].push({document:this.#c(a),highlight:this.#l(a)});return s}#m(e){const t={};if("results"in e){for(const s of e.results)if(s.facet_counts&&s.facet_counts.length)for(const a of s.facet_counts)t[a.field_name]=a.counts.map(r=>({value:r.value,count:r.count}))}return t}#i(e,t){return"results"in e?e.results.map((s,a)=>({result:s,label:t[a]})).filter(({result:s})=>"error"in s).map(({result:s,label:a})=>`${a}: ${s.error}`).join("; "):""}#d(e,t,s,a){const r={},i={facets:s,documents:{},groupCounts:r};for(const n of this.#h(a))i.documents[n]=[];return"results"in e&&e.results.forEach((n,o)=>{const c=t[o];typeof n.found=="number"&&(r[c]=n.found);const l=n.hits??[];l.length&&(i.documents[c]=l.map(u=>({document:this.#c(u),highlight:this.#l(u)})))}),i}#c(e){return this.#y(e)}#l(e){const t={},s=[];for(const[a,r]of Object.entries(e.highlight??{}))if(Array.isArray(r)){if(a==="path"){const i=r,n=[];for(const o of i)n.push(o.snippet);t.path=n}else if(a==="parameters"){const i=r;for(const n of i){let o=!1;for(const[c,l]of Object.entries(n))if(c!=="deepLink")if(Array.isArray(l))for(const u of l)u.matched_tokens?.length&&(o=!0);else l.matched_tokens?.length&&(o=!0);if(o){const c={name:n.name?.snippet||"",description:n.description?.snippet||"",place:n.place?.snippet||"",path:n.path?.map(l=>l?.snippet)||[]};t.parameters=[c];break}}}}else t[a]=r.snippet,s.push(a);for(const[a,r]of Object.entries(e.document))!s.includes(a)&&typeof r=="string"&&(t[a]=r.length>R?`${r.substring(0,R)}...`:r);return t.parameters||(t.parameters=[]),t}#y(e){let t;const s=Object.keys(e.highlight??{});for(const r of L)if(s.some(i=>i===r))return e.document;const a=e.highlight?.parameters;if(a){for(const r of a)for(const[i,n]of Object.entries(r))if(i!=="deepLink"){if(Array.isArray(n)){for(const o of n)if(o.matched_tokens?.length){t=r.deepLink?.snippet??t;break}}else if(n.matched_tokens?.length){t=r.deepLink?.snippet??t;break}}}if(t){const r=t.split("#")[1];return{...e.document,url:`${e.document.url}#${r}`}}return e.document}async exportDocuments(e){const t='{"documents":[',s=E(b.join(e,C));for(const[a,r]of this.#s){if(r.length===0)continue;const i=E(b.join(s,`${a}.json`)),n=!v(i),o=JSON.stringify(r).substring(1).slice(0,-1),c=n?t+o:","+o;await D(i,c,{encoding:"utf8"}),this.#s.set(a,[])}}async exportIndexes(e){const t=E(b.join(e,C));for(const s of this.#s.keys()){const a=E(b.join(t,`${s}.json`)),r=`],"schemaFields":${JSON.stringify(this.#t)}}`;await D(a,r,{encoding:"utf8"})}}async import(e){}async countFacets(e,t){return await P("com.redocly.search.facets",async s=>{s?.setAttribute("id","searchFacetsId"),s?.setAttribute("object","searchFacets"),s?.setAttribute("uri","urn:redocly:realm:api:searchFacets:searchFacetsId");const{locale:a,query:r,facetQuery:i,filter:n,field:o}=e,c=this.#u(a),l={q:r||"*",query_by:this.#f(),facet_by:"*",facet_query:this.#g(i,o),filter_by:this.#a(t,n,o),max_facet_values:y,per_page:0};if(s?.setAttribute("collectionName",c),s?.setAttribute("query",l.q),s?.setAttribute("facetQuery",l.facet_query),s?.setAttribute("filter",l.filter_by),this.#e){const u={},f=await this.#e.collections(c).documents().search(l);if(f.facet_counts&&f.facet_counts.length)for(const m of f.facet_counts)u[m.field_name]=m.counts.map(d=>({value:d.value,count:d.count,isCounterVisible:!!r||(n?.length||0)>0}));return u}else return{}})}#u(e){return`${this.#o}${e}`}#_(e){for(const[t,{queryable:s}]of e)this.#t.find(a=>a.name===t)||this.#t.push({queryable:s,name:t,type:"string",facet:!0,optional:!0})}#f(){return this.#t.filter(e=>e.facet?e.queryable:e.queryable!==!1).map(e=>e.name).join(",")}#n(e){return`\`${e.replaceAll("`","")}\``}#a(e,t,s){const a=`${T}:[${e.teams.map(i=>`'${i}'`).join(", ")}]`;let r=`${h}.isDefault:true`;if(t&&t.length){const i=t.filter(o=>o.field!==s&&o.field!==h).map(o=>{const c=o.values.map(l=>this.#n(l));return c.length?`${o.field}:=[${c.join(", ")}]`:""}).join(" && "),n=t.find(o=>o.field===h);if(n&&n.values.length&&n.values.length===2){const o=n.values[0],c=n.values[1],l=`(${h}.folderId:! ${o} && ${h}.isDefault:true)`,u=`(${h}.folderId: ${o} && ${h}.version:=${this.#n(c)})`;r=`(${l} || ${u})`}return i?`${a} && ${i} && ${r}`:`${a} && ${r}`}else return`${a} && ${r}`}#g(e,t){return e&&t?`${t}:${e}`:""}#h(e){const t=[];if(e&&e.length)for(const s of e)s.field===A&&t.push(...s.values);return t}#b(e,t){const s=[],a=this.#t.filter(r=>r.facet);for(const r of a){const i={facet_by:r.name,filter_by:this.#a(e,t,r.name),per_page:0};s.push(i)}return s}cleanupFacetValues(e){const t=e.getSearchFacets();t.forEach(s=>{s.values=[]}),e.setSearchFacets(t)}}export{X as Typesense};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { RedoclyConfig } from '@redocly/config';
|
|
2
|
+
import type { WildcardRedirectsTree } from './types';
|
|
3
|
+
import { readEnvVariable } from './utils/envs/read-env-variable.js';
|
|
4
|
+
export type ComputedRedoclyConfig = {
|
|
5
|
+
configFilePath: string;
|
|
6
|
+
linkTags?: string;
|
|
7
|
+
headScriptTags?: string;
|
|
8
|
+
postBodyScriptTags?: string;
|
|
9
|
+
preBodyScriptTags?: string;
|
|
10
|
+
wildcardRedirectsTree: WildcardRedirectsTree;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
};
|
|
13
|
+
export type StoreConfig = WithRequired<RedoclyConfig, 'redirects'> & ComputedRedoclyConfig;
|
|
14
|
+
export declare function createDefaultStoreConfig(): StoreConfig;
|
|
15
|
+
/** Resolves the env placeholders in a copy of `entries` and normalizes `seo.siteUrl`. */
|
|
16
|
+
export declare function resolveConfigEntries(entries: Partial<RedoclyConfig>): {
|
|
17
|
+
config: Partial<RedoclyConfig>;
|
|
18
|
+
unsetEnvVars: string[];
|
|
19
|
+
replacedValues: Record<string, {
|
|
20
|
+
original: string;
|
|
21
|
+
replaced: string;
|
|
22
|
+
}>;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* The store config of a build output: the defaults, overridden by the parsed JSON of its config
|
|
26
|
+
* file, which this changes in place. `env` holds each value that `readEnv` returned: a reader of
|
|
27
|
+
* only these values gives the same config. The partials stay JSON, never `undefined`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveStoreConfig(fileEntries: Partial<RedoclyConfig>, readEnv?: typeof readEnvVariable): {
|
|
30
|
+
config: StoreConfig;
|
|
31
|
+
env: Record<string, string>;
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=store-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{removeTrailingSlash as a}from"../utils/url/remove-trailing-slash.js";import{MARKDOC_PARTIALS_DATA_KEY as c}from"./constants/common.js";import{readEnvVariable as l}from"./utils/envs/read-env-variable.js";import{replaceEnvVariablesDeep as f,replaceEnvVariablesInPlace as u}from"./utils/envs/replace-env-variables-deep.js";function d(){return{configFilePath:"",redirects:{},wildcardRedirectsTree:{},access:{rbac:{},requiresLogin:!1},directoryPermissions:{},devLogin:!1,ssoDirect:{}}}function S(e){const{resolvedObj:r,unsetEnvVars:o,replacedValues:i}=f(e);return s(r),{config:r,unsetEnvVars:o,replacedValues:i}}function U(e,r=l){const o={};u(e,t=>{const n=r(t);return n!==void 0&&(o[t]=n),n}),s(e);const i={...d(),...e};return i[c]||={},{config:i,env:o}}function s(e){const r=e.seo?.siteUrl?.trim();r&&(e.seo={...e.seo,siteUrl:URL.canParse(r)?new URL(r).origin:a(r)})}export{d as createDefaultStoreConfig,S as resolveConfigEntries,U as resolveStoreConfig};
|
package/dist/server/store.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { type Node } from '@markdoc/markdoc';
|
|
2
2
|
import type { JSX } from 'react';
|
|
3
3
|
import type { CommonError, GlobalData } from '../types/index.js';
|
|
4
|
-
import type { MiddlewareDetails, PageRouteInit, PageRouteDetails, RouteDetails, LifecycleContext, ApiRoute, ParseMarkdocOpts,
|
|
4
|
+
import type { MiddlewareDetails, PageRouteInit, PageRouteDetails, RouteDetails, LifecycleContext, ApiRoute, ParseMarkdocOpts, StaticFilePaths, RedirectOptions, McpToolRegistration, McpToolSchema, MarkdocOptions } from './types';
|
|
5
5
|
import type { Skill } from './types/plugins/skills.js';
|
|
6
6
|
import type { RedoclyConfig, RedirectConfig, CompilationError, PageStaticData } from '@redocly/config';
|
|
7
7
|
import type { SearchFacet } from '@redocly/theme/core/types';
|
|
8
8
|
import type { BundledApiDefinition } from './plugins/api-docs/loader.js';
|
|
9
9
|
import type { SearchEngine } from './plugins/search/engines/search-engine.js';
|
|
10
|
+
import type { StoreConfig } from './store-config.js';
|
|
11
|
+
import type { StoreConfigFile } from './workers/worker-config.js';
|
|
10
12
|
import { KvService } from './persistence/kv/services/kv-service.js';
|
|
11
13
|
export declare const USER_DEFINED_API_FUNCTIONS_COUNTER_KEY = "userDefinedApiFunctions";
|
|
12
14
|
type LifecycleEventType = 'global-data-updated' | 'shared-data-updated' | 'routes-updated' | 'build-updated' | 'build-started' | 'errors-updated' | 'pages-updated';
|
|
@@ -16,15 +18,6 @@ export type StoreOptions = {
|
|
|
16
18
|
serverOutDir: string;
|
|
17
19
|
serverMode?: boolean;
|
|
18
20
|
};
|
|
19
|
-
export type ComputedRedoclyConfig = {
|
|
20
|
-
configFilePath: string;
|
|
21
|
-
linkTags?: string;
|
|
22
|
-
headScriptTags?: string;
|
|
23
|
-
postBodyScriptTags?: string;
|
|
24
|
-
preBodyScriptTags?: string;
|
|
25
|
-
wildcardRedirectsTree: WildcardRedirectsTree;
|
|
26
|
-
[key: string]: unknown;
|
|
27
|
-
};
|
|
28
21
|
export declare class Store {
|
|
29
22
|
#private;
|
|
30
23
|
routesBySlug: Map<string, PageRouteDetails>;
|
|
@@ -62,7 +55,9 @@ export declare class Store {
|
|
|
62
55
|
globalData: GlobalData;
|
|
63
56
|
/** Whether `POST /authorize` mints dev sessions: set by `realm develop` (unless `--no-dev-login`) and `realm build --devLogin`. */
|
|
64
57
|
devLogin: boolean;
|
|
65
|
-
config:
|
|
58
|
+
config: StoreConfig;
|
|
59
|
+
/** Set when the store loads from a build output: the workers then read the config from this file. */
|
|
60
|
+
configFile?: StoreConfigFile;
|
|
66
61
|
serverMode: boolean;
|
|
67
62
|
serverOutDir: string;
|
|
68
63
|
outdir: string;
|
|
@@ -157,10 +152,10 @@ export declare class Store {
|
|
|
157
152
|
resolveRouteStaticData(route: RouteDetails, _context?: LifecycleContext, skipLastModified?: boolean): Promise<PageStaticData>;
|
|
158
153
|
addSsrComponents(components: JSX.Element[] | string[], position: 'head' | 'preBody' | 'postBody'): void;
|
|
159
154
|
clear: () => void;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
static
|
|
155
|
+
/** Writes the files that `fromServerFiles` reads. The config file keeps the env placeholders. */
|
|
156
|
+
writeServerFiles(): Promise<void>;
|
|
157
|
+
/** Loads the store from the files of a build output. The config resolves from the current env. */
|
|
158
|
+
static fromServerFiles(options: StoreOptions): Promise<Store>;
|
|
164
159
|
getConfigWithEnvPlaceholders(): Promise<RedoclyConfig>;
|
|
165
160
|
reportUnsetEnvVars(): Promise<void>;
|
|
166
161
|
}
|
package/dist/server/store.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import A from"@markdoc/markdoc";import{parseAndResolveMarkdoc as T}from"./plugins/markdown/compiler.js";import{getPathnameForLocale as M}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as g}from"../constants/common.js";import{DEFAULT_TITLE as O,MARKDOC_PARTIALS_DATA_KEY as m,MARKDOC_PARTIALS_DEPS_KEY as k}from"./constants/common.js";import{GATED_MARKDOC_TAGS as R}from"./constants/entitlements.js";import{isObject as L}from"../utils/guards/is-object.js";import{mapObject as H}from"../utils/object/map-object.js";import{getValueDeep as y}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as D}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as u}from"../utils/path/normalize-route-slug.js";import{isLocalLink as S}from"../utils/path/is-local-link.js";import{reporter as w}from"./tools/notifiers/reporter.js";import{logger as h}from"./tools/notifiers/logger.js";import{sha1 as F}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 I}from"./utils/sandbox-context.js";import{writeSharedData as N}from"./utils/index.js";import{renderComponents as V}from"./ssr/render.js";import{readStaticData as q,writeStaticData as j}from"./utils/static-data.js";import{getMarkdocOptions as U}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as v}from"./entitlements/entitlements-provider.js";import{isRouteReserved as x}from"./plugins/get-reserved-routes.js";import{isL10nPath as K}from"./fs/utils/is-l10n-path.js";import{resolveGlobMapValue as J}from"./utils/globs.js";import{replaceEnvVariablesDeep as W}from"./utils/envs/replace-env-variables-deep.js";import{findRedirect as $}from"./utils/redirects/find-redirect.js";import{followRedirectChain as z}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as Y}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as Z}from"../cli/telemetry/helpers/trace-step.js";const E={routesBySlug:"map",staticFilePaths:"object",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",config:"object",ssr:"object",searchFacets:"map",routesPartials:"map",mcpToolHandlers:"map",skills:"map",devLogin:"object"},P="PLAN_GATES",Q=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],ke="userDefinedApiFunctions";class b{routesBySlug=new Map;staticFilePaths={};replacedEnvVars={};unsetEnvVars=new Set;lifecycleContext;newRoutes=[];#t={};routesByFsPath=new Map;apiRoutes=[];middleware=[];routesSharedData=new Map;sharedDataDeps=new Map;sharedDataMarkdocComponents=new Map;routesDynamicComponents=new Map;routesPartials=new Map;ssr={preBodyTags:[],postBodyTags:[],headTags:[]};searchFacets=new Map;searchEngine;templates=new Map;browserPlugins=new Set;apiRoutesRequestHandlers=new Map;mcpToolHandlers=new Map;skills=new Map;serverPropsGetters=new Map;pagePropsGetters=new Map;listeners=new Map;globalData={};devLogin=!1;#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;#h=Promise.resolve();#u;#e=!1;#r=0;#f=0;#a=new Map;constructor({outdir:e,contentDir:s,serverMode:t=!1,serverOutDir:r}){this.#i=s,this.outdir=e,this.serverMode=t,this.serverOutDir=r,I.register({serverOutDir:r,readRoots:[e,process.cwd()]}),this.userCodeReady=new Promise(a=>{this.#n=a})}on(e,s){const t=this.listeners.get(e);t?t.add(s):this.listeners.set(e,new Set([s]))}queueEvent=(e,s,...t)=>{this.#a.set(e+String(s),[e,s,...t])};runListeners=(e,s,...t)=>{for(const r of this.listeners.get(e)||new Set)s?r(s,...t):r(...t)};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.#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.#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 Z("com.redocly.build.reloadMarkdocOptions",async()=>{const e=v.instance(),s=await U(this.serverOutDir),t=Object.fromEntries(Object.entries(s.tags).filter(([r])=>R[r]!=null?e.canAccessFeature(R[r]):!0));this.#o={...s,tags:t}})}get markdocOptions(){return{...this.#o,partials:this.getGlobalConfig(m),themeConfig:this.config.markdown}}setGlobalData=e=>{const s=this.globalData,t={...this.globalData,...e};this.globalData=t,JSON.stringify(t)!==JSON.stringify(s)&&this.queueEvent("global-data-updated",void 0,t)};getGlobalData=()=>this.globalData;getKv=async()=>B.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:e,context:s,deps:t,resource:r})=>{const{data:{info:a,ast:i},compoundHash:l}=await T(e,this.markdocOptions,{actions:this,context:s},r);for(const o of a.sharedDataDeps||[]){for(const n of t?.routeSlugs||[])this.addRouteSharedData(n,o,o);for(const n of t?.sharedDataIds||[]){const c=this.sharedDataDeps.get(n)||new Set;c.add(o),this.sharedDataDeps.set(n,c)}}for(const o of a.dynamicMarkdocComponents||[]){for(const n of t?.routeSlugs||[]){const c=this.routesDynamicComponents.get(n)||new Set;c.add(o),this.routesDynamicComponents.set(n,c)}for(const n of t?.sharedDataIds||[]){const c=this.sharedDataMarkdocComponents.get(n)||new Set;c.add(o),this.sharedDataMarkdocComponents.set(n,c)}}if(t?.routeSlugs&&a.partials?.length)for(const o of t.routeSlugs){const n=this.routesPartials.get(o)||[];for(const c of a.partials)n.includes(c)||n.push(c);this.routesPartials.set(o,n)}return{info:a,ast:i,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 s=Object.keys(e);for(const l of s)for(const o in this.replacedEnvVars)if(o===l||o.startsWith(`${l}:`)){const n=o.split(":"),{error:c,value:d}=y(e,n);(c||d!==this.replacedEnvVars[o].replaced)&&delete this.replacedEnvVars[o]}const{resolvedObj:t,unsetEnvVars:r,replacedValues:a}=W(e);for(const l of r)this.unsetEnvVars.add(l);Object.assign(this.replacedEnvVars,a);const i=t.seo?.siteUrl?.trim();i&&(t.seo={...t.seo,siteUrl:URL.canParse(i)?new URL(i).origin:D(i)}),Object.assign(this.config,t)};getConfig=()=>this.config;getGlobalConfig=e=>this.config[e];getSearchFacets=()=>this.searchFacets;addRedirect=(e,s,t={})=>{if(!v.instance().canAccessFeature("redirects")&&e!=="/")return;this.config.redirects||(this.config.redirects={});const i=u(e).toLowerCase();this.config.redirects[i]=s;const{trackOriginalSource:l=!0}=t,o=this.getGlobalConfig("originalRedirectSources");l&&Array.isArray(o)&&!o.includes(i)&&o.push(i),i.endsWith("*")&&Y(this.config.wildcardRedirectsTree,i)};getRedirect=e=>{const s=u(e).toLowerCase(),t=$(s,this.config.redirects,this.config.wildcardRedirectsTree,this.staticFilePaths);if(!t)return null;if(S(t.to)){const r=u(t.to).toLowerCase();if(!r.endsWith("*")&&z(r,[s],this.config.redirects,this.config.wildcardRedirectsTree).type==="cycle")return null}return{to:t.to,type:t.type}};createSharedData=async(e,s,t)=>{if(t&&this.#t[e]===t)return e;const r=JSON.stringify(s),a=t??F(r);return this.#t[e]===a||(this.#t[e]=a,await N(e,r,this.outdir),this.queueEvent("shared-data-updated",e)),e};addRouteSharedData=(e,s,t)=>{const r=D(e),a=this.routesSharedData.get(r)||{};a[s]=t,this.routesSharedData.set(r,a),h.verbose(`Adding shared data to ${e}, ${s}, ${t}`)};getRouteSharedDataByFsPath=e=>{const s=this.routesByFsPath.get(e);return s?this.routesSharedData.get(s)||{}:{}};getPartialsForRoute=e=>{const s=this.getGlobalConfig(m)||{},t=this.routesPartials.get(e);if(!t||t.length===0)return{};const r=this.getGlobalConfig(k)||{},a=new Set(t),i=Array.from(t);for(let o=0;o<i.length;o++){const n=i[o],c=r[n]?.partials??[];for(const d of c)a.has(d)||(a.add(d),i.push(d))}const l={};for(const o of a)s[o]&&(l[o]=s[o]);return l};addRoute=e=>{const t={...J(e.fsPath,this.config.metadataGlobs),...e.metadata||{}};this.newRoutes.push({...e,metadata:t}),h.verbose("Created route %s",e.slug)};addRouteSharedDataToAllLocales=(e,s,t)=>{const r=[g,...this.lifecycleContext?.fs.localeFolders||[]].map(a=>({code:a,name:a}));for(const a of r){const i=M(e,g,a.code,r);this.addRouteSharedData(i,s,t)}};addApiRoute=e=>{this.apiRoutes.push(e),h.verbose("Created API route %s",e.slug)};addMcpTools=(e,s)=>{for(const t of s){const r=this.mcpToolHandlers.get(t.name);r&&r.importPath!==e&&h.warn('MCP tool "%s" from %s overwrites the tool registered from %s',t.name,e,r.importPath),this.mcpToolHandlers.set(t.name,{...t,importPath:e}),h.verbose("Created MCP tool %s",t.name)}};getMcpTools=()=>Array.from(this.mcpToolHandlers.values());markGatedMcpTools=e=>{for(const s of e){const t=this.mcpToolHandlers.get(s);t&&this.mcpToolHandlers.set(s,{...t,hasAvailabilityGate:!0})}};addSkills=e=>{for(const s of e)this.skills.set(s.slug,s),h.verbose("Discovered skill %s",s.slug)};getSkills=()=>Array.from(this.skills.values());removeMcpTools=e=>{for(const s of e)this.mcpToolHandlers.delete(s)};addMiddleware=e=>{this.middleware.push(e),h.verbose("Created middleware %s",e.id)};setResourceResponseHeaders=(e,s)=>{this.config.responseHeaders||(this.config.responseHeaders={});const t=new Set(s.map(i=>i.name.toLowerCase())),a=[...(this.config.responseHeaders[e]??[]).filter(i=>!t.has(i.name.toLowerCase())),...s];this.config.responseHeaders[e]=a};getRouteByFsPath=e=>{const s=this.routesByFsPath.get(e);return s?this.getRouteBySlug(s):void 0};getRouteBySlug=(e,s={})=>{const{followRedirect:t=!0}=s,r=this.getRedirect(e);return t&&r?this.routesBySlug.get(u(r.to)):this.routesBySlug.get(e)};hasRouteOrRedirectBySlug=e=>{if(this.routesBySlug.has(e)||x(e,this.getConfig()))return!0;const s=this.getRedirect(e);if(!s)return!1;if(!S(s.to))return!0;const t=u(s.to);return this.routesBySlug.has(t)};getRoutesByTemplateId=e=>this.newRoutes.filter(s=>s.templateId===e);getAllRoutesForLocale=(e=g)=>{const s=Array.from(this.routesBySlug.values()),t=e.toLowerCase();return s.filter(r=>e===g?!K(r.fsPath):r.slug.startsWith(`/${t}`))};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,s)=>(this.templates.set(e,s),e);addBrowserPlugin=e=>{this.browserPlugins.add(e)};createRequestHandler=(e,s)=>(this.apiRoutesRequestHandlers.set(e,s),e);clearRequestHandlersByPrefix=e=>{for(const s of this.apiRoutesRequestHandlers.keys())s.startsWith(e)&&this.apiRoutesRequestHandlers.delete(s)};registerServerPropsGetter=(e,s)=>(this.serverPropsGetters.set(e,s),e);registerPagePropsGetter=(e,s)=>{this.pagePropsGetters.set(e,s)};async writeRouteStaticData(e,s){const t=await this.resolveRouteStaticData(e,s,!1);t&&j(e.slug,t,this.outdir)}async resolveRouteStaticData(e,s,t){if(this.serverMode)return q(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)||{},i=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=>i.add(p));const f=this.sharedDataDeps.get(c);f&&f.forEach(p=>this.addRouteSharedData(e.slug,p,p))}const o=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(i),metadata:{...a?.props?.metadata,...e.metadata},seo:{title:O,...o,...a.props?.seo,siteUrl:o?.siteUrl},compilationErrors:this.compilationErrors},lastModified:t||!e.fsPath?null:await this.lifecycleContext?.fs.getLastModified(e.fsPath)}}addSsrComponents(e,s){if(!e?.length)return;const t=typeof e[0]=="string"?e.join(""):V(e);t&&(s==="head"?this.ssr.headTags.push(t):s==="preBody"?this.ssr.preBodyTags.push(t):this.ssr.postBodyTags.push(t))}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[t,r]of Object.entries(E))switch(r){case"map":{const a=Array.from(this[t].entries());e.push([t,a]);break}case"object":if(t==="config"){e.push([t,await this.getConfigWithEnvPlaceholders()]);break}e.push([t,this[t]]);break;default:throw new Error("Invalid format")}const s=Object.fromEntries(e);return s[P]=G.PLAN_GATES,s}static fromJson(e,s){const t=new b(s);for(const[a,i]of Object.entries(E))switch(i){case"map":t[a]=new Map(e[a]);break;case"object":if(a==="config"){t.setGlobalConfig(e[a]);break}t[a]=e[a];break;default:throw new Error("Invalid format")}t.config[m]=X(t.config[m]||{});const r=e[P];return r&&_("PLAN_GATES",r),t}async getConfigWithEnvPlaceholders(){const e=JSON.parse(JSON.stringify(this.config));for(const s in this.replacedEnvVars){const{original:t}=this.replacedEnvVars[s],r=s.split(":"),a=r.pop(),{error:i,value:l}=y(e,r);if(i||!L(l)&&!Array.isArray(l)){await w.panicOnBuild(`Failed to replace env var with env name for ${s}`);continue}l[a]=t}return e}async reportUnsetEnvVars(){if(this.unsetEnvVars.size===0)return;const e=Array.from(this.unsetEnvVars).filter(t=>!Q.includes(t));if(e.length===0)return;const s=`Failed to resolve config. The following environment variables are not set: ${e.join(", ")}`;await w.panicOnBuildContentError(s)}}function X(C){return H(C,e=>A.Ast.fromJSON(JSON.stringify(e)))}export{b as Store,ke as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
|
|
1
|
+
import k from"@markdoc/markdoc";import{writeFileSync as D}from"node:fs";import{readFile as R}from"node:fs/promises";import{join as g}from"node:path";import{parseAndResolveMarkdoc as F}from"./plugins/markdown/compiler.js";import{getPathnameForLocale as L}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as m}from"../constants/common.js";import{DEFAULT_TITLE as H,MARKDOC_PARTIALS_DATA_KEY as y,MARKDOC_PARTIALS_DEPS_KEY as G}from"./constants/common.js";import{GATED_MARKDOC_TAGS as v}from"./constants/entitlements.js";import{isObject as _}from"../utils/guards/is-object.js";import{mapObject as N}from"../utils/object/map-object.js";import{getValueDeep as w}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as B}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as u}from"../utils/path/normalize-route-slug.js";import{isLocalLink as E}from"../utils/path/is-local-link.js";import{reporter as P}from"./tools/notifiers/reporter.js";import{logger as h}from"./tools/notifiers/logger.js";import{sha1 as I}from"./utils/crypto/sha1.js";import{envConfig as j,setEnv as V}from"./config/env-config.js";import{KvService as q}from"./persistence/kv/services/kv-service.js";import{SandboxContext as J}from"./utils/sandbox-context.js";import{isAllowedEnv as U}from"./utils/envs/get-api-route-allowed-env-variables.js";import{ensureDir as x,writeSharedData as K}from"./utils/index.js";import{renderComponents as W}from"./ssr/render.js";import{readStaticData as $,writeStaticData as z}from"./utils/static-data.js";import{getMarkdocOptions as Y}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as C}from"./entitlements/entitlements-provider.js";import{isRouteReserved as Z}from"./plugins/get-reserved-routes.js";import{isL10nPath as Q}from"./fs/utils/is-l10n-path.js";import{resolveGlobMapValue as X}from"./utils/globs.js";import{createDefaultStoreConfig as ee,resolveConfigEntries as te,resolveStoreConfig as se}from"./store-config.js";import{findRedirect as re}from"./utils/redirects/find-redirect.js";import{followRedirectChain as oe}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as ae}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as ie}from"../cli/telemetry/helpers/trace-step.js";const b={routesBySlug:"map",staticFilePaths:"object",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",ssr:"object",searchFacets:"map",routesPartials:"map",mcpToolHandlers:"map",skills:"map",devLogin:"object"},O="store.json",A="store-config.json",T="PLAN_GATES",ne=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],Ue="userDefinedApiFunctions";class S{routesBySlug=new Map;staticFilePaths={};replacedEnvVars={};unsetEnvVars=new Set;lifecycleContext;newRoutes=[];#t={};routesByFsPath=new Map;apiRoutes=[];middleware=[];routesSharedData=new Map;sharedDataDeps=new Map;sharedDataMarkdocComponents=new Map;routesDynamicComponents=new Map;routesPartials=new Map;ssr={preBodyTags:[],postBodyTags:[],headTags:[]};searchFacets=new Map;searchEngine;templates=new Map;browserPlugins=new Set;apiRoutesRequestHandlers=new Map;mcpToolHandlers=new Map;skills=new Map;serverPropsGetters=new Map;pagePropsGetters=new Map;listeners=new Map;globalData={};devLogin=!1;#a=void 0;config=ee();configFile;#i;serverMode;serverOutDir;outdir;buildRevision=0;hasSitemap=!1;compilationErrors=[];#n;userCodeReady;#s=Promise.resolve();#c;#l=Promise.resolve();#d;#h=Promise.resolve();#u;#e=!1;#r=0;#f=0;#o=new Map;constructor({outdir:e,contentDir:t,serverMode:s=!1,serverOutDir:r}){this.#i=t,this.outdir=e,this.serverMode=s,this.serverOutDir=r,J.register({serverOutDir:r,readRoots:[e,process.cwd()]}),this.userCodeReady=new Promise(o=>{this.#n=o})}on(e,t){const s=this.listeners.get(e);s?s.add(t):this.listeners.set(e,new Set([t]))}queueEvent=(e,t,...s)=>{this.#o.set(e+String(t),[e,t,...s])};runListeners=(e,t,...s)=>{for(const r of this.listeners.get(e)||new Set)t?r(t,...s):r(...s)};startPluginsRun(){this.clear(),this.#s=new Promise(e=>{this.#c=e})}waitForPluginsLifecycle(){return Promise.all([this.#s,this.#l,this.#h])}waitForPluginsRun(){return this.#s}finishPluginsRun(){this.#c?.();for(const e of this.#o.values())this.runListeners(...e);this.#o.clear()}startEsbuildRun(){this.#l=new Promise(e=>{this.#d=e})}finishEsbuildRun(){this.#d?.()}markRequestHandlersEntryOutdated(){this.#r++,!this.#e&&(this.#e=!0,this.#h=new Promise(e=>{this.#u=e}))}noteRequestHandlersEntryBuildStart(){this.#f=this.#r}markRequestHandlersEntryUpdated(){this.#e&&(this.#r>this.#f||(this.#e=!1,this.#u?.()))}get contentDir(){if(this.serverMode)throw new Error("contentDir should not be used in server mode");return this.#i}markUserCodeReady(){this.#n?.(!0)}async reloadMarkdocOptions(){await ie("com.redocly.build.reloadMarkdocOptions",async()=>{const e=C.instance(),t=await Y(this.serverOutDir),s=Object.fromEntries(Object.entries(t.tags).filter(([r])=>v[r]!=null?e.canAccessFeature(v[r]):!0));this.#a={...t,tags:s}})}get markdocOptions(){return{...this.#a,partials:this.getGlobalConfig(y),themeConfig:this.config.markdown}}setGlobalData=e=>{const t=this.globalData,s={...this.globalData,...e};this.globalData=s,JSON.stringify(s)!==JSON.stringify(t)&&this.queueEvent("global-data-updated",void 0,s)};getGlobalData=()=>this.globalData;getKv=async()=>q.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:e,context:t,deps:s,resource:r})=>{const{data:{info:o,ast:a},compoundHash:c}=await F(e,this.markdocOptions,{actions:this,context:t},r);for(const i of o.sharedDataDeps||[]){for(const n of s?.routeSlugs||[])this.addRouteSharedData(n,i,i);for(const n of s?.sharedDataIds||[]){const l=this.sharedDataDeps.get(n)||new Set;l.add(i),this.sharedDataDeps.set(n,l)}}for(const i of o.dynamicMarkdocComponents||[]){for(const n of s?.routeSlugs||[]){const l=this.routesDynamicComponents.get(n)||new Set;l.add(i),this.routesDynamicComponents.set(n,l)}for(const n of s?.sharedDataIds||[]){const l=this.sharedDataMarkdocComponents.get(n)||new Set;l.add(i),this.sharedDataMarkdocComponents.set(n,l)}}if(s?.routeSlugs&&o.partials?.length)for(const i of s.routeSlugs){const n=this.routesPartials.get(i)||[];for(const l of o.partials)n.includes(l)||n.push(l);this.routesPartials.set(i,n)}return{info:o,ast:a,compoundHash:c}};async loadApiDocuments(e){return(await e.cache.load(".","api-docs")).data}setSearchEngine(e){this.searchEngine=e}setSearchFacets=e=>{this.searchFacets=e};setStaticFilePaths=e=>{this.staticFilePaths=e};setGlobalConfig=e=>{const t=Object.keys(e);for(const a of t)for(const c in this.replacedEnvVars)if(c===a||c.startsWith(`${a}:`)){const i=c.split(":"),{error:n,value:l}=w(e,i);(n||l!==this.replacedEnvVars[c].replaced)&&delete this.replacedEnvVars[c]}const{config:s,unsetEnvVars:r,replacedValues:o}=te(e);for(const a of r)this.unsetEnvVars.add(a);Object.assign(this.replacedEnvVars,o),Object.assign(this.config,s)};getConfig=()=>this.config;getGlobalConfig=e=>this.config[e];getSearchFacets=()=>this.searchFacets;addRedirect=(e,t,s={})=>{if(!C.instance().canAccessFeature("redirects")&&e!=="/")return;this.config.redirects||(this.config.redirects={});const a=u(e).toLowerCase();this.config.redirects[a]=t;const{trackOriginalSource:c=!0}=s,i=this.getGlobalConfig("originalRedirectSources");c&&Array.isArray(i)&&!i.includes(a)&&i.push(a),a.endsWith("*")&&ae(this.config.wildcardRedirectsTree,a)};getRedirect=e=>{const t=u(e).toLowerCase(),s=re(t,this.config.redirects,this.config.wildcardRedirectsTree,this.staticFilePaths);if(!s)return null;if(E(s.to)){const r=u(s.to).toLowerCase();if(!r.endsWith("*")&&oe(r,[t],this.config.redirects,this.config.wildcardRedirectsTree).type==="cycle")return null}return{to:s.to,type:s.type}};createSharedData=async(e,t,s)=>{if(s&&this.#t[e]===s)return e;const r=JSON.stringify(t),o=s??I(r);return this.#t[e]===o||(this.#t[e]=o,await K(e,r,this.outdir),this.queueEvent("shared-data-updated",e)),e};addRouteSharedData=(e,t,s)=>{const r=B(e),o=this.routesSharedData.get(r)||{};o[t]=s,this.routesSharedData.set(r,o),h.verbose(`Adding shared data to ${e}, ${t}, ${s}`)};getRouteSharedDataByFsPath=e=>{const t=this.routesByFsPath.get(e);return t?this.routesSharedData.get(t)||{}:{}};getPartialsForRoute=e=>{const t=this.getGlobalConfig(y)||{},s=this.routesPartials.get(e);if(!s||s.length===0)return{};const r=this.getGlobalConfig(G)||{},o=new Set(s),a=Array.from(s);for(let i=0;i<a.length;i++){const n=a[i],l=r[n]?.partials??[];for(const d of l)o.has(d)||(o.add(d),a.push(d))}const c={};for(const i of o)t[i]&&(c[i]=t[i]);return c};addRoute=e=>{const s={...X(e.fsPath,this.config.metadataGlobs),...e.metadata||{}};this.newRoutes.push({...e,metadata:s}),h.verbose("Created route %s",e.slug)};addRouteSharedDataToAllLocales=(e,t,s)=>{const r=[m,...this.lifecycleContext?.fs.localeFolders||[]].map(o=>({code:o,name:o}));for(const o of r){const a=L(e,m,o.code,r);this.addRouteSharedData(a,t,s)}};addApiRoute=e=>{this.apiRoutes.push(e),h.verbose("Created API route %s",e.slug)};addMcpTools=(e,t)=>{for(const s of t){const r=this.mcpToolHandlers.get(s.name);r&&r.importPath!==e&&h.warn('MCP tool "%s" from %s overwrites the tool registered from %s',s.name,e,r.importPath);const o=s.hasAvailabilityGate??(r?.importPath===e?r.hasAvailabilityGate:void 0);this.mcpToolHandlers.set(s.name,{...s,importPath:e,hasAvailabilityGate:o}),h.verbose("Created MCP tool %s",s.name)}};getMcpTools=()=>Array.from(this.mcpToolHandlers.values());markGatedMcpTools=e=>{for(const t of e){const s=this.mcpToolHandlers.get(t);s&&this.mcpToolHandlers.set(t,{...s,hasAvailabilityGate:!0})}};addSkills=e=>{for(const t of e)this.skills.set(t.slug,t),h.verbose("Discovered skill %s",t.slug)};getSkills=()=>Array.from(this.skills.values());removeMcpTools=e=>{for(const t of e)this.mcpToolHandlers.delete(t)};addMiddleware=e=>{this.middleware.push(e),h.verbose("Created middleware %s",e.id)};setResourceResponseHeaders=(e,t)=>{this.config.responseHeaders||(this.config.responseHeaders={});const s=new Set(t.map(a=>a.name.toLowerCase())),o=[...(this.config.responseHeaders[e]??[]).filter(a=>!s.has(a.name.toLowerCase())),...t];this.config.responseHeaders[e]=o};getRouteByFsPath=e=>{const t=this.routesByFsPath.get(e);return t?this.getRouteBySlug(t):void 0};getRouteBySlug=(e,t={})=>{const{followRedirect:s=!0}=t,r=this.getRedirect(e);return s&&r?this.routesBySlug.get(u(r.to)):this.routesBySlug.get(e)};hasRouteOrRedirectBySlug=e=>{if(this.routesBySlug.has(e)||Z(e,this.getConfig()))return!0;const t=this.getRedirect(e);if(!t)return!1;if(!E(t.to))return!0;const s=u(t.to);return this.routesBySlug.has(s)};getRoutesByTemplateId=e=>this.newRoutes.filter(t=>t.templateId===e);getAllRoutesForLocale=(e=m)=>{const t=Array.from(this.routesBySlug.values()),s=e.toLowerCase();return t.filter(r=>e===m?!Q(r.fsPath):r.slug.startsWith(`/${s}`))};getAllRoutes=()=>Array.from(this.routesBySlug.values());getAllApiRoutes=()=>this.apiRoutes;getAllMiddleware=()=>this.middleware;getTemplate=e=>this.templates.get(e);getRequestHandler=e=>this.apiRoutesRequestHandlers.get(e);createTemplate=(e,t)=>(this.templates.set(e,t),e);addBrowserPlugin=e=>{this.browserPlugins.add(e)};createRequestHandler=(e,t)=>(this.apiRoutesRequestHandlers.set(e,t),e);clearRequestHandlersByPrefix=e=>{for(const t of this.apiRoutesRequestHandlers.keys())t.startsWith(e)&&this.apiRoutesRequestHandlers.delete(t)};registerServerPropsGetter=(e,t)=>(this.serverPropsGetters.set(e,t),e);registerPagePropsGetter=(e,t)=>{this.pagePropsGetters.set(e,t)};async writeRouteStaticData(e,t){const s=await this.resolveRouteStaticData(e,t,!1);s&&z(e.slug,s,this.outdir)}async resolveRouteStaticData(e,t,s){if(this.serverMode)return $(e.slug,this.outdir);const r={...this,contentDir:this.contentDir,parseMarkdoc:({input:l,context:d,resource:f})=>this.parseMarkdoc({input:l,context:d,deps:{routeSlugs:[e.slug]},resource:f})},o=await e.getStaticData?.(e,r)||{},a=new Set(this.routesDynamicComponents.get(e.slug)),c=this.routesSharedData.get(e.slug)||{};for(const l of Object.values(c)){const d=this.sharedDataMarkdocComponents.get(l);d&&d.forEach(p=>a.add(p));const f=this.sharedDataDeps.get(l);f&&f.forEach(p=>this.addRouteSharedData(e.slug,p,p))}const i=this.getGlobalConfig("seo"),n=o?.frontmatter||{};return{...o,frontmatter:{...n,seo:{...n?.seo,title:n?.seo?.title||await e.getNavText?.()}},props:{...o.props,dynamicMarkdocComponents:Array.from(a),metadata:{...o?.props?.metadata,...e.metadata},seo:{title:H,...i,...o.props?.seo,siteUrl:i?.siteUrl},compilationErrors:this.compilationErrors},lastModified:s||!e.fsPath?null:await this.lifecycleContext?.fs.getLastModified(e.fsPath)}}addSsrComponents(e,t){if(!e?.length)return;const s=typeof e[0]=="string"?e.join(""):W(e);s&&(t==="head"?this.ssr.headTags.push(s):t==="preBody"?this.ssr.preBodyTags.push(s):this.ssr.postBodyTags.push(s))}clear=()=>{this.routesByFsPath.clear(),this.templates.clear(),this.newRoutes=[],this.routesBySlug.clear(),this.apiRoutes=[],this.middleware=[],this.routesSharedData.clear(),this.sharedDataDeps.clear(),this.sharedDataMarkdocComponents.clear(),this.routesDynamicComponents.clear(),this.routesPartials.clear(),this.skills.clear(),this.config.redirects={},this.config.wildcardRedirectsTree={},this.config.directoryPermissions={},this.ssr={preBodyTags:[],postBodyTags:[],headTags:[]}};#p(){const e=[];for(const[s,r]of Object.entries(b))switch(r){case"map":{const o=Array.from(this[s].entries());e.push([s,o]);break}case"object":e.push([s,this[s]]);break;default:throw new Error("Invalid format")}const t=Object.fromEntries(e);return t[T]=j.PLAN_GATES,t}static#g(e,t){const s=new S(t);for(const[o,a]of Object.entries(b))switch(a){case"map":s[o]=new Map(e[o]);break;case"object":s[o]=e[o];break;default:throw new Error("Invalid format")}const r=e[T];return r&&V("PLAN_GATES",r),s}async writeServerFiles(){x(this.serverOutDir),D(g(this.serverOutDir,O),JSON.stringify(this.#p())),D(g(this.serverOutDir,A),JSON.stringify(await this.getConfigWithEnvPlaceholders()))}static async fromServerFiles(e){const t=g(e.serverOutDir,A),{config:s,env:r}=se(JSON.parse(await R(t,"utf8")));s[y]=ce(s[y]);const o=JSON.parse(await R(g(e.serverOutDir,O),"utf8")),a=S.#g(o,e);a.config=s;const c=Object.fromEntries(Object.entries(r).filter(([i])=>U(i)));return a.configFile={path:t,env:c,config:{...s}},a}async getConfigWithEnvPlaceholders(){const e=JSON.parse(JSON.stringify(this.config));for(const t in this.replacedEnvVars){const{original:s}=this.replacedEnvVars[t],r=t.split(":"),o=r.pop(),{error:a,value:c}=w(e,r);if(a||!_(c)&&!Array.isArray(c)){await P.panicOnBuild(`Failed to replace env var with env name for ${t}`);continue}c[o]=s}return e}async reportUnsetEnvVars(){if(this.unsetEnvVars.size===0)return;const e=Array.from(this.unsetEnvVars).filter(s=>!ne.includes(s));if(e.length===0)return;const t=`Failed to resolve config. The following environment variables are not set: ${e.join(", ")}`;await P.panicOnBuildContentError(t)}}function ce(M){return N(M,e=>k.Ast.fromJSON(JSON.stringify(e)))}export{S as Store,Ue as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { ApiFunctionsBasicContext } from '../../types';
|
|
2
|
+
import type { WorkerConfig } from '../../workers/worker-config.js';
|
|
2
3
|
export type ApiRoutesWorkerParams = {
|
|
3
4
|
maxResponseSizeMB?: number;
|
|
4
5
|
slug: string;
|
|
5
6
|
requestHandlerId: string;
|
|
6
|
-
ctxData: ApiFunctionsBasicContext
|
|
7
|
+
ctxData: Omit<ApiFunctionsBasicContext, 'config'> & {
|
|
8
|
+
config: WorkerConfig;
|
|
9
|
+
};
|
|
7
10
|
staticData: Record<string, unknown>;
|
|
8
11
|
serverOutDir: string;
|
|
9
12
|
req: {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export declare const EXCLUDED_ENV_VARS: string[];
|
|
2
|
+
/** Whether a worker may see the env variable `name`. */
|
|
3
|
+
export declare function isAllowedEnv(name: string): boolean;
|
|
2
4
|
export declare function getAllowedEnvs(): Record<string, string | undefined>;
|
|
3
5
|
//# sourceMappingURL=get-api-route-allowed-env-variables.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
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(([_])
|
|
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!e.includes(_)&&!n.some(E=>_.startsWith(E))}function R(){return Object.fromEntries(Object.entries(process.env).filter(([_])=>T(_)))}export{e as EXCLUDED_ENV_VARS,R as getAllowedEnvs,T as isAllowedEnv};
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
+
import { readEnvVariable } from './read-env-variable.js';
|
|
2
|
+
/** False when `value` has no env variable template. Cheaper than `interpolateEnvVariables`. */
|
|
3
|
+
export declare function mayHaveEnvVariables(value: string): boolean;
|
|
1
4
|
/**
|
|
2
5
|
* Interpolates environment variables in a string using the format {{ process.env.VARIABLE_NAME }}.
|
|
3
6
|
* Supports optional quoted default values applied when the variable is not set,
|
|
4
7
|
* e.g. {{ process.env.VARIABLE_NAME || "default" }} (both `|` and `||` separators are accepted).
|
|
5
8
|
*
|
|
6
9
|
* @param value - The string containing environment variable templates to interpolate
|
|
10
|
+
* @param readEnv - Returns the value of an environment variable; reads `process.env` by default
|
|
7
11
|
* @returns An object containing:
|
|
8
12
|
* - interpolatedValue: The string with all environment variables replaced with their values
|
|
9
13
|
* - unsetVars: Array of environment variable names that were not found and had no default
|
|
10
14
|
* - interpolatedVars: Array of environment variable names that were successfully resolved
|
|
11
15
|
*/
|
|
12
|
-
export declare function interpolateEnvVariables(value: string): {
|
|
16
|
+
export declare function interpolateEnvVariables(value: string, readEnv?: typeof readEnvVariable): {
|
|
13
17
|
interpolatedValue: string;
|
|
14
18
|
unsetVars: string[];
|
|
15
19
|
interpolatedVars: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readEnvVariable as d}from"./read-env-variable.js";const
|
|
1
|
+
import{readEnvVariable as d}from"./read-env-variable.js";const f=/{{\s*process\.env\.([A-Z0-9_]+)\s*(?:\|\|?\s*(?:"([^"]*)"|'([^']*)')\s*)?}}/gi;function p(a){return a.includes("{{")}function u(a,c=d){let e=a;const s=new Set,t=new Set,i=[...a.matchAll(f)];for(const n of i){const r=n[1].toUpperCase(),o=n[2]??n[3],l=c(r);l!==void 0?(e=e.replace(n[0],l),t.add(r)):o!==void 0?(e=e.replace(n[0],o),t.add(r)):s.add(r)}return{interpolatedValue:e,unsetVars:Array.from(s),interpolatedVars:Array.from(t)}}export{u as interpolateEnvVariables,p as mayHaveEnvVariables};
|
|
@@ -1,12 +1,20 @@
|
|
|
1
|
+
import { readEnvVariable } from './read-env-variable.js';
|
|
2
|
+
type EnvReplacement = {
|
|
3
|
+
unsetEnvVars: string[];
|
|
4
|
+
interpolatedEnvVars: string[];
|
|
5
|
+
replacedValues: Record<string, {
|
|
6
|
+
original: string;
|
|
7
|
+
replaced: string;
|
|
8
|
+
}>;
|
|
9
|
+
};
|
|
1
10
|
/**
|
|
2
11
|
* Recursively traverses an object or array and interpolates environment variables in all string values.
|
|
3
12
|
* Keeps track of the path to each modified value and maintains a record of original and replaced values.
|
|
4
13
|
*
|
|
5
14
|
* @param obj - The object or array to process
|
|
6
|
-
* @param currentPath - Array of keys representing current position in object tree
|
|
7
15
|
*
|
|
8
16
|
* @returns An object containing:
|
|
9
|
-
* - resolvedObj:
|
|
17
|
+
* - resolvedObj: A copy of the plain objects and arrays, with all environment variables replaced
|
|
10
18
|
* - unsetEnvVars: Array of environment variable names that were not found
|
|
11
19
|
* - interpolatedEnvVars: Array of environment variable names that were successfully resolved
|
|
12
20
|
* - replacedValues: Record of paths where values were replaced, with original and new values
|
|
@@ -14,13 +22,13 @@
|
|
|
14
22
|
* For usage examples and test cases, see:
|
|
15
23
|
* [unit tests](./__tests__/env.test.ts)
|
|
16
24
|
*/
|
|
17
|
-
export declare function replaceEnvVariablesDeep<T extends Record<string, unknown> | Array<unknown>>(obj: T
|
|
25
|
+
export declare function replaceEnvVariablesDeep<T extends Record<string, unknown> | Array<unknown>>(obj: T): EnvReplacement & {
|
|
18
26
|
resolvedObj: T;
|
|
19
|
-
unsetEnvVars: string[];
|
|
20
|
-
interpolatedEnvVars: string[];
|
|
21
|
-
replacedValues: Record<string, {
|
|
22
|
-
original: string;
|
|
23
|
-
replaced: string;
|
|
24
|
-
}>;
|
|
25
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* `replaceEnvVariablesDeep` without the copy: changes the strings of `container` in place.
|
|
30
|
+
* `readEnv` returns the value of an environment variable; it reads `process.env` by default.
|
|
31
|
+
*/
|
|
32
|
+
export declare function replaceEnvVariablesInPlace(container: object, readEnv?: typeof readEnvVariable): EnvReplacement;
|
|
33
|
+
export {};
|
|
26
34
|
//# sourceMappingURL=replace-env-variables-deep.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isObject as
|
|
1
|
+
import{isObject as c}from"../../../utils/guards/is-object.js";import{interpolateEnvVariables as E,mayHaveEnvVariables as b}from"./interpolate-env-variables.js";import{readEnvVariable as v}from"./read-env-variable.js";function g(r){const n=p(r);return{resolvedObj:n,...A(n)}}function A(r,n=v){const t=new Set,f=new Set,l={},i=[],V=u=>{const o=u;for(const a in o){const e=o[a];if(typeof e=="string"){if(!b(e))continue;const{interpolatedValue:d,unsetVars:y,interpolatedVars:m}=E(e,n);y.forEach(s=>t.add(s)),m.forEach(s=>f.add(s)),m.length>0&&(o[a]=d,l[[...i,a].join(":")]={original:e,replaced:d})}else(c(e)||Array.isArray(e))&&(i.push(a),V(e),i.pop())}};return(c(r)||Array.isArray(r))&&V(r),{unsetEnvVars:Array.from(t),interpolatedEnvVars:Array.from(f),replacedValues:l}}function p(r){return Array.isArray(r)?r.map(p):c(r)?Object.fromEntries(Object.entries(r).map(([n,t])=>[n,p(t)])):r}export{g as replaceEnvVariablesDeep,A as replaceEnvVariablesInPlace};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { WorkerParams } from './types.js';
|
|
2
|
+
import { MCP_TOOL_AVAILABILITY_KEY, MCP_TOOL_WORKER_KEY } from './mcp-tool-worker-pool.js';
|
|
3
|
+
export declare const mcpToolWorkerMethods: {
|
|
4
|
+
executeMcpTool: (params: WorkerParams<typeof MCP_TOOL_WORKER_KEY>[0]) => Promise<import("../plugins/mcp/types.js").McpToolWorkerResponse>;
|
|
5
|
+
mcpToolAvailability: (params: WorkerParams<typeof MCP_TOOL_AVAILABILITY_KEY>[0]) => Promise<Record<string, boolean>>;
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=mcp-tool-worker-methods.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{executeMcpTool as e,mcpToolAvailability as r}from"../plugins/mcp/workers/execute-mcp-tool.js";import{fromWorkerContext as t}from"../plugins/mcp/workers/worker-context.js";import{MCP_TOOL_AVAILABILITY_KEY as c,MCP_TOOL_WORKER_KEY as m}from"./mcp-tool-worker-pool.js";const T={[m]:o=>e({...o,context:t(o.context)}),[c]:o=>r({...o,context:t(o.context)})};export{T as mcpToolWorkerMethods};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { WorkerPool } from './worker-pool.js';
|
|
2
2
|
export declare const MCP_TOOL_WORKER_KEY = "executeMcpTool";
|
|
3
|
-
export declare const MCP_TOOL_IS_AVAILABLE_KEY = "isMcpToolAvailable";
|
|
4
3
|
export declare const MCP_TOOL_AVAILABILITY_KEY = "mcpToolAvailability";
|
|
5
4
|
export declare const mcpToolWorkers: WorkerPool;
|
|
6
5
|
//# sourceMappingURL=mcp-tool-worker-pool.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getAllowedEnvs as o}from"../utils/envs/get-api-route-allowed-env-variables.js";import{WorkerPool as r}from"./worker-pool.js";const p="executeMcpTool",
|
|
1
|
+
import{getAllowedEnvs as o}from"../utils/envs/get-api-route-allowed-env-variables.js";import{WorkerPool as r}from"./worker-pool.js";const p="executeMcpTool",c="mcpToolAvailability",e=5,l=new r({workerScript:"./mcp-tool-worker",minWorkers:1,maxWorkers:e,workerType:"process",forkOpts:{stdio:"inherit"},resolveForkEnv:o,lazy:!0,sandbox:!0});export{c as MCP_TOOL_AVAILABILITY_KEY,p as MCP_TOOL_WORKER_KEY,l as mcpToolWorkers};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import o from"workerpool";import{
|
|
1
|
+
import o from"workerpool";import{mcpToolWorkerMethods as r}from"./mcp-tool-worker-methods.js";o.worker(r);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { ApiRoutesWorkerParams, ApiRoutesWorkerResponse } from '../types/plugins/api-routes.js';
|
|
2
2
|
import type { RenderPayload } from '../../types/ssr.js';
|
|
3
3
|
import type { ScorecardsWorkerParams, ScorecardsWorkerResponse } from '../types/plugins/scorecards.js';
|
|
4
|
-
import type {
|
|
4
|
+
import type { McpToolWorkerContext, McpToolWorkerParams, McpToolWorkerResponse } from '../plugins/mcp/types.js';
|
|
5
5
|
import type { SCORECARDS_WORKER_KEY } from './scorecards-worker-pool.js';
|
|
6
6
|
import type { API_ROUTES_WORKER_KEY } from './api-routes-worker-pool.js';
|
|
7
7
|
import type { SSR_WORKER_KEY } from './ssr-worker-pool.js';
|
|
8
|
-
import type { MCP_TOOL_AVAILABILITY_KEY,
|
|
8
|
+
import type { MCP_TOOL_AVAILABILITY_KEY, MCP_TOOL_WORKER_KEY } from './mcp-tool-worker-pool.js';
|
|
9
9
|
export type SsrWorkerResponse = {
|
|
10
10
|
html: string;
|
|
11
11
|
statusCode: 200 | 500;
|
|
@@ -25,17 +25,13 @@ export type WorkerTypeMapping = {
|
|
|
25
25
|
response: ScorecardsWorkerResponse;
|
|
26
26
|
};
|
|
27
27
|
[MCP_TOOL_WORKER_KEY]: {
|
|
28
|
-
params: [
|
|
28
|
+
params: [McpToolWorkerParams];
|
|
29
29
|
response: McpToolWorkerResponse;
|
|
30
30
|
};
|
|
31
|
-
[MCP_TOOL_IS_AVAILABLE_KEY]: {
|
|
32
|
-
params: [Omit<McpToolExecutionParams, 'args' | 'extra'>];
|
|
33
|
-
response: boolean;
|
|
34
|
-
};
|
|
35
31
|
[MCP_TOOL_AVAILABILITY_KEY]: {
|
|
36
32
|
params: [{
|
|
37
33
|
toolNames: string[];
|
|
38
|
-
context:
|
|
34
|
+
context: McpToolWorkerContext;
|
|
39
35
|
}];
|
|
40
36
|
response: Record<string, boolean>;
|
|
41
37
|
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { RedoclyConfig } from '@redocly/config';
|
|
2
|
+
type ConfigFileRef = {
|
|
3
|
+
path: string;
|
|
4
|
+
env: Record<string, string>;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* The store config file of a build output, and the env values that resolve its placeholders in a
|
|
8
|
+
* worker. `env` has no variable that the worker env excludes: those placeholders stay unresolved.
|
|
9
|
+
* Main process only: `config` is the store config that was loaded from the file.
|
|
10
|
+
*/
|
|
11
|
+
export type StoreConfigFile = ConfigFileRef & {
|
|
12
|
+
config: RedoclyConfig;
|
|
13
|
+
};
|
|
14
|
+
/** The config as it crosses to a worker: the config file entries, overridden by `entries`. */
|
|
15
|
+
export type WorkerConfig = {
|
|
16
|
+
file: ConfigFileRef;
|
|
17
|
+
entries: Partial<RedoclyConfig>;
|
|
18
|
+
} | {
|
|
19
|
+
file?: undefined;
|
|
20
|
+
entries: RedoclyConfig;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Sends only the top-level entries of `config` that are not the same values as in the config file.
|
|
24
|
+
* Without a config file (`develop`), sends all of `config`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function toWorkerConfig(config: RedoclyConfig, configFile: StoreConfigFile | undefined): WorkerConfig;
|
|
27
|
+
/**
|
|
28
|
+
* Worker side. Returns a config that the call owns: a change to it, at any depth, is not seen by
|
|
29
|
+
* other calls. Throws when the config file cannot be read.
|
|
30
|
+
*/
|
|
31
|
+
export declare function readWorkerConfig(workerConfig: WorkerConfig): RedoclyConfig;
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=worker-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFileSync as a}from"node:fs";import{MARKDOC_PARTIALS_DATA_KEY as f}from"../constants/common.js";import{resolveStoreConfig as l}from"../store-config.js";let s;function m(n,e){if(!e)return{entries:n};const o=e.config;return{file:{path:e.path,env:e.env},entries:Object.fromEntries(Object.entries(n).filter(([t,r])=>o[t]!==r))}}function j(n){const{file:e,entries:o}=n;if(!e)return o;const t=d(e),r=Object.fromEntries(Object.keys(t).map(i=>[i,()=>structuredClone(t[i])]));return r[f]=()=>u(e)[f],O(r,o)}function d(n){const e=JSON.stringify([n.path,n.env]);if(s?.key!==e){const{[f]:o,...t}=u(n);s={key:e,config:t}}return s.config}function u({path:n,env:e}){let o;try{o=JSON.parse(a(n,"utf8"))}catch(t){throw new Error(`Could not read the store config file "${n}": ${String(t)}`)}return l(o,t=>e[t]).config}function O(n,e){const o={},t=(r,i)=>{Object.defineProperty(o,r,{value:i,writable:!0,enumerable:!0,configurable:!0})};for(const[r,i]of Object.entries(n)){if(Object.hasOwn(e,r)){t(r,e[r]);continue}Object.defineProperty(o,r,{enumerable:!0,configurable:!0,get:()=>{const c=i();return t(r,c),c},set:c=>t(r,c)})}for(const[r,i]of Object.entries(e))Object.hasOwn(o,r)||t(r,i);return o}export{j as readWorkerConfig,m as toWorkerConfig};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/redoc-reef",
|
|
3
|
-
"version": "0.139.
|
|
3
|
+
"version": "0.139.4",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -33,7 +33,7 @@
|
|
|
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.4.
|
|
36
|
+
"@redocly/api-docs": "0.4.2",
|
|
37
37
|
"@redocly/config": "0.67.0",
|
|
38
38
|
"@redocly/openapi-core": "2.66.0",
|
|
39
39
|
"@redocly/portal-legacy-ui": "0.19.0",
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { McpToolSchema } from '../../../types';
|
|
2
|
-
import type { McpToolContext } from '../types.js';
|
|
3
|
-
export declare function availableSandboxTools(candidates: McpToolSchema[], context: McpToolContext): Promise<McpToolSchema[]>;
|
|
4
|
-
//# sourceMappingURL=available-sandbox-tools.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{logger as a}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as n,MCP_TOOL_AVAILABILITY_KEY as m}from"../../../workers/mcp-tool-worker-pool.js";async function p(t,l){if(t.length===0)return t;const e=t.map(o=>o.name);let r;try{r=await n.exec(m,[{toolNames:e,context:l}],{timeout:1e4})}catch(o){return i(t,o instanceof Error?o.message:String(o))}return!r||typeof r!="object"?i(t,"the worker returned no availability map"):t.filter(o=>r[o.name]!==!1)}function i(t,l){const e=t.filter(r=>r.hasAvailabilityGate);return e.length===0?t:(a.warn(`MCP tool availability is unknown (${l}); the sandbox tool list drops the gated tools: ${e.map(r=>r.name).join(", ")}.`),t.filter(r=>!r.hasAvailabilityGate))}export{p as availableSandboxTools};
|