@redocly/realm 0.138.0-next.11 → 0.138.0-next.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/client/app/pages/Invite/Invite.js +1 -1
  3. package/dist/client/runtime/loader.js +1 -1
  4. package/dist/constants/l10n/langs/ar.js +1 -1
  5. package/dist/constants/l10n/langs/de.js +1 -1
  6. package/dist/constants/l10n/langs/en.js +1 -1
  7. package/dist/constants/l10n/langs/es.js +1 -1
  8. package/dist/constants/l10n/langs/fr.js +1 -1
  9. package/dist/constants/l10n/langs/hi.js +1 -1
  10. package/dist/constants/l10n/langs/it.js +1 -1
  11. package/dist/constants/l10n/langs/ja.js +1 -1
  12. package/dist/constants/l10n/langs/ko.js +1 -1
  13. package/dist/constants/l10n/langs/pl.js +1 -1
  14. package/dist/constants/l10n/langs/pt-BR.js +1 -1
  15. package/dist/constants/l10n/langs/pt.js +1 -1
  16. package/dist/constants/l10n/langs/ru.js +1 -1
  17. package/dist/constants/l10n/langs/uk.js +1 -1
  18. package/dist/constants/l10n/langs/zh.js +1 -1
  19. package/dist/server/plugins/api-docs/index.js +1 -1
  20. package/dist/server/plugins/api-docs/loader.js +3 -3
  21. package/dist/server/plugins/api-docs/utils/decorators.d.ts +11 -0
  22. package/dist/server/plugins/api-docs/utils/decorators.js +1 -1
  23. package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/openapi-entities-extractor.js +1 -1
  24. package/dist/server/plugins/catalog-entities/utils/extract-parts-from-channels-message-ref.js +1 -1
  25. package/dist/server/plugins/markdown/attribute-resolvers/code-walkthrough/file-chunk-parser.d.ts +1 -1
  26. package/dist/server/plugins/markdown/attribute-resolvers/resolve-link.js +1 -1
  27. package/dist/server/plugins/mcp/docs-mcp/utils/resolve-refs.d.ts +2 -2
  28. package/dist/server/plugins/mcp/docs-mcp/utils/resolve-refs.js +1 -1
  29. package/dist/server/plugins/mcp/gateway-mcp/elicitation.d.ts +1 -0
  30. package/dist/server/plugins/mcp/gateway-mcp/elicitation.js +5 -5
  31. package/dist/server/plugins/mcp/gateway-mcp/rate-limit/apply-edge-rate-limit.js +1 -1
  32. package/dist/server/plugins/mcp/gateway-mcp/secret-store.d.ts +9 -3
  33. package/dist/server/plugins/mcp/gateway-mcp/secret-store.js +1 -1
  34. package/dist/server/plugins/mcp/gateway-mcp/subject.js +1 -1
  35. package/dist/server/plugins/mcp/handlers/credentials-page/constants.d.ts +7 -0
  36. package/dist/server/plugins/mcp/handlers/credentials-page/constants.js +1 -0
  37. package/dist/server/plugins/mcp/handlers/credentials-page/form-contract.d.ts +5 -0
  38. package/dist/server/plugins/mcp/handlers/credentials-page/form-contract.js +1 -0
  39. package/dist/server/plugins/mcp/handlers/credentials-page/form-page.d.ts +14 -0
  40. package/dist/server/plugins/mcp/handlers/credentials-page/form-page.js +62 -0
  41. package/dist/server/plugins/mcp/handlers/credentials-page/layout.d.ts +17 -0
  42. package/dist/server/plugins/mcp/handlers/credentials-page/layout.js +11 -0
  43. package/dist/server/plugins/mcp/handlers/credentials-page/outcome-pages.d.ts +10 -0
  44. package/dist/server/plugins/mcp/handlers/credentials-page/outcome-pages.js +11 -0
  45. package/dist/server/plugins/mcp/handlers/credentials-page/site-branding.d.ts +10 -0
  46. package/dist/server/plugins/mcp/handlers/credentials-page/site-branding.js +1 -0
  47. package/dist/server/plugins/mcp/handlers/credentials-page/styles.d.ts +4 -0
  48. package/dist/server/plugins/mcp/handlers/credentials-page/styles.js +186 -0
  49. package/dist/server/plugins/mcp/handlers/credentials-page/types.d.ts +32 -0
  50. package/dist/server/plugins/mcp/handlers/credentials-page/types.js +0 -0
  51. package/dist/server/plugins/mcp/handlers/mcp-credentials-handler.d.ts +0 -1
  52. package/dist/server/plugins/mcp/handlers/mcp-credentials-handler.js +1 -1
  53. package/dist/server/plugins/mcp/index.js +1 -1
  54. package/dist/server/plugins/mcp/servers/mcp-server.js +2 -2
  55. package/dist/server/plugins/mcp/types.d.ts +6 -0
  56. package/dist/server/plugins/mcp/utils/mode.d.ts +4 -1
  57. package/dist/server/plugins/mcp/utils/mode.js +1 -1
  58. package/dist/server/plugins/scorecard-classic/template/components.js +1 -1
  59. package/dist/server/providers/database/client.d.ts +5 -0
  60. package/dist/server/providers/database/client.js +1 -1
  61. package/dist/server/providers/database/copy-migrations.js +1 -1
  62. package/dist/server/providers/database/database-connections-manager.js +1 -1
  63. package/dist/server/providers/database/has-project-database.d.ts +3 -0
  64. package/dist/server/providers/database/has-project-database.js +1 -0
  65. package/dist/server/web-server/auth.d.ts +0 -1
  66. package/dist/server/web-server/auth.js +4 -4
  67. package/dist/server/web-server/jwt/token-types.d.ts +1 -0
  68. package/dist/server/web-server/jwt/token-types.js +1 -1
  69. package/dist/server/web-server/routes/auth.js +1 -1
  70. package/dist/server/web-server/routes/catalog/helpers/create-entity-schema.d.ts +1756 -1756
  71. package/dist/server/web-server/routes/catalog/helpers/create-entity-update-schema.d.ts +295 -295
  72. package/dist/server/web-server/routes/mcp-routes/mcp-consent-page.d.ts +24 -0
  73. package/dist/server/web-server/routes/mcp-routes/mcp-consent-page.js +35 -0
  74. package/dist/server/web-server/routes/mcp-routes/mcp-oauth-store.d.ts +15 -0
  75. package/dist/server/web-server/routes/mcp-routes/mcp-oauth-store.js +1 -0
  76. package/dist/server/web-server/routes/mcp-routes/mcp-oauth.d.ts +3 -1
  77. package/dist/server/web-server/routes/mcp-routes/mcp-oauth.js +1 -1
  78. package/dist/server/web-server/routes/mcp-routes/mcp-routes.js +1 -1
  79. package/package.json +5 -5
  80. package/dist/server/plugins/mcp/handlers/mcp-credentials-page.d.ts +0 -12
  81. package/dist/server/plugins/mcp/handlers/mcp-credentials-page.js +0 -225
@@ -1 +1 @@
1
- import{REDOCLY_ROUTE_RBAC as w,REDOCLY_TEAMS_RBAC as v}from"@redocly/config";import{mcpTagSchemas as Z,schemaDefinitionTag as ee,markdocParser as U}from"@redocly/api-docs";import{withPathPrefix as q}from"@redocly/theme/core/utils";import{API_DOCS_TEMPLATE_ID as V,GRAPHQL_SPEC_SLUG as te,PUBLIC_RBAC_SCOPE_ITEM as x}from"../../../constants/common.js";import{DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER as oe,PUBLIC_API_DEFINITIONS_FOLDER as re}from"../../constants/common.js";import{API_DOCS_SHARED_DATA_PREFIX as C}from"../../constants/plugins/api-docs.js";import{fromCurrentDir as ae}from"../../utils/paths.js";import{normalizeRouteSlug as se}from"../../../utils/path/normalize-route-slug.js";import{createSlugify as ie}from"../create-slugify.js";import{apiDocLoader as ne,apiDocsLoader as ce}from"./loader.js";import{getTemplatePath as j}from"./get-template-path.js";import{storeDefinitionBundles as pe}from"./store-definition-bundles.js";import{telemetryTraceStep as ue}from"../../../cli/telemetry/helpers/trace-step.js";import{searchResolver as le}from"./search/search-resolver.js";import{getAiDocumentsStore as de}from"./search/get-ai-search-documents.js";import{getApiConfigByPath as me}from"../get-api-config.js";import{getRouteSlugsForPath as fe}from"../utils.js";import{collectItemRoutes as ge}from"./utils/collect-item-routes.js";import{applyNavItemToRoot as he}from"./utils/apply-nav-item-to-root.js";import{buildRoutesMapping as De}from"./utils/build-routes-mapping.js";import{buildItemPositions as Se}from"./utils/build-item-positions.js";import{resolveApiDocTitle as Pe}from"./utils/resolve-api-doc-title.js";import{resolveConfigRbac as ye}from"./utils/resolve-config-rbac.js";import{relativePathToBaseSlug as Ie}from"./utils/relative-path-to-base-slug.js";import{normalizeApiVersion as Ae}from"./utils/normalize-api-version.js";import{importApiDocsBuilder as W}from"./import-api-docs-builder.js";import{envConfig as $}from"../../config/env-config.js";import{logger as ve}from"../../tools/notifiers/logger.js";const T="openapi-spec-download",Re={asyncapi:"AsyncAPI Docs",graphql:"GraphQL Docs",openapi:"OpenAPI Docs"};async function st(){let t=[],r=new Map;const s=new WeakMap;let n=new Set,m={};function k(o,g,A){const P=s.get(o)??new Map;s.set(o,P);const h=g??"";return P.get(h)===A?!1:(P.set(h,A),!0)}return{id:"api-docs",requiredEntitlements:["openapi","asyncapi","graphql"],loaders:{"api-doc":ne,"api-docs":ce},processContent:async(o,g)=>{await ue("com.redocly.build.plugin.apiDocs",async()=>{o.createRequestHandler(T,ae(import.meta.url,"./spec-download.api.js")),o.addApiRoute({slug:re+"/*",requestHandlerId:T,httpMethod:"all",[v]:x,getStaticData:async()=>({props:{}})}),o.addApiRoute({slug:oe+"/*",requestHandlerId:T,httpMethod:"all",[v]:x,getStaticData:async()=>({props:{}})}),o.addApiRoute({slug:te+"/*",requestHandlerId:T,httpMethod:"all",[v]:x,getStaticData:async()=>({props:{}})});const A=o.createTemplate(V,j("../../../client/templates/api-docs/template.js")),P=o.registerServerPropsGetter(V,j("./get-server-props.js")),h=await g.getConfig();r=new Map,m={};const y=await o.loadApiDocuments(g);pe(y,o.outdir);const E=!!h.plugins?.some(e=>typeof e=="string"&&e.startsWith("@redocly/portal-plugin-async-api/"));E&&y.some(e=>e.type==="asyncapi")&&ve.warn("The plugin '@redocly/portal-plugin-async-api' is deprecated. Please remove it from your config to use built-in AsyncAPI docs.");const c=E?y.filter(e=>e.type!=="asyncapi"):y;t=c.map(({markdocChunks:e,relativePath:I,isVirtual:p,customOutputRelativeFile:a,realRelativePath:f})=>({chunks:e??[],relativePath:I,realRelativePath:f,isVirtual:a!=null||p}));const{buildItems:i}=await W(o.serverOutDir),D=(await g.cache.load("content-slugs","content-slugs")).data,S=ie(D),O=e=>se(S(e));for(const e of c){const I=e.customOutputRelativeFile||e.relativePath,p=O(I);Ee(e,p);const{items:a,store:f}=await i({type:e.type,document:e.document,options:e.options,basePath:p,markdownParser:U}),R=`${C}${e.relativePath}`,M=$.isDevelopMode?e.relativePath:void 0,u=Ce(e.document),b=u?.["x-metadata"],_=e.isVirtual?void 0:b?.apiId,N={document:e,basePath:p,sourcePath:M,apiId:_};await o.createSharedData(R,Q(N,a,f));const K=ye(e.config),F=!!e.options.excludeFromSearch,Y=e.type==="openapi",d=ge(a,K).map(l=>{const B=l.metadata?.seo,H={...we(l.link,p),fsPath:I,templateId:A,sharedData:[{key:"apiDocsStore",id:R}],serverPropsGetterIds:[P],excludeFromSearch:F,getStaticData:_e(l.label,B),[v]:l[v]};return Y?{...H,...l.httpVerb?{httpVerb:l.httpVerb,metadata:{subType:"openapi-operation"}}:{}}:H});if(!d.length)continue;const G=Pe(u,e.type,a),X=Ae(u?.version),L={type:e.type,title:G,version:X,description:typeof u?.description=="string"?u.description:void 0,summary:typeof u?.summary=="string"?u.summary:void 0,...u?.["x-metadata"]&&typeof u["x-metadata"]=="object"?u["x-metadata"]:{}};d[0]={...d[0],metadata:L,hasClientRoutes:!0,getSidebar:(l,B)=>he(z(a),p,B),getNavText:()=>G??Re[e.type]??"API Docs",getSearchDocuments:le(a,L,o.getSearchFacets,o.setSearchFacets,F,f,e.document),getAiDocumentsStore:de({items:a,metadata:L,store:f,document:e.document,specType:e.type,excludeFromSearch:F,basePath:p,getSearchFacets:o.getSearchFacets,markdocTags:{...Z,schemaDefinition:ee}})};const J=d[0];d[0]=d[d.length-1],d[d.length-1]=J;for(const l of d)o.addRoute(l);if(_){const l=me(e.config?.apis,e.realRelativePath);m[_]={[v]:l.rbac,[w]:{fsPath:I}}}r.set(R,N)}})},afterRoutesCreated:async(o,g)=>{const{buildItems:A,simplifyAstStructure:P}=await W(o.serverOutDir),h=new Set,y=new Set;for(const{chunks:c,relativePath:i,isVirtual:D}of t){const S=`${C}${i}`,O=fe(o.getAllRoutes(),i);for(const{node:e,markdown:I,pointer:p,key:a,relativePath:f}of c){const{ast:R,compoundHash:M}=await o.parseMarkdoc({input:{content:I,relativePath:f,isVirtual:D},resource:p,context:g,deps:{sharedDataIds:[`${C}${f}`],routeSlugs:O}});if(h.add(p),!(a==="graphql-docs"?e?.description?.value!==void 0:!!(a&&e[a]))||!k(e,a,M))continue;y.add(S);const b=P(R);if(a==="graphql-docs"){e.description&&(e.description.value=b);const _=`${C}${f}`;be(r.get(_),b)}else a&&(e[a]=b)}}const E=n.difference(h);for(const c of E)g.cache.delete(c);n=h,Object.entries(m).forEach(([c,i])=>{const D=i[w]?.fsPath;if(!D)return;const S=o.getRouteByFsPath(D)?.baseSlug;m[c]={...i,[w]:{...i[w],slug:S},slug:S}}),o.setGlobalData({apiProducts:m});for(const[c,i]of r){if(!y.has(c))continue;const{items:D,store:S}=await A({type:i.document.type,document:i.document.document,options:i.document.options,basePath:i.basePath,markdownParser:U});await o.createSharedData(c,Q(i,D,S))}}}}function Q({document:t,basePath:r,sourcePath:s,apiId:n},m,k){return{items:m,store:k,options:t.options,baseSlug:r,routesMapping:$.isDevelopMode?De(m,r):{},graphqlPositions:$.isDevelopMode&&t.type==="graphql"?Se(m,r):void 0,sourcePath:s,apiId:n}}function be(t,r){t?.document.type!=="graphql"||!t.document.options||(t.document.options.info={...t.document.options.info||{},description:r})}function _e(t,r){return async function(s){return{props:{settings:{baseUrlPath:q(s.baseSlug)},disableAutoScroll:!0,seo:{title:t,...r}}}}}function Ee(t,r){const s=t.options?.mockServer,n=q(`/_mock${Ie(t.relativePath)}`);s?.url===n&&(t.options={...t.options,mockServer:{...s,url:q(`/_mock${r}`)}})}function we(t,r){if(t===r)return{};const s=r==="/"?"/":`${r}/`;if(t.startsWith(s)){const n=t.slice(s.length);return n?{slugSuffix:n}:{}}return{slug:t}}function Ce(t){if(t&&typeof t=="object"&&"info"in t)return t.info}function z(t){return t.map(({content:r,items:s,...n})=>({...n,...s&&{items:z(s)}}))}export{st as apiDocsPlugin,we as toRouteSlugFields};
1
+ import{REDOCLY_ROUTE_RBAC as E,REDOCLY_TEAMS_RBAC as v}from"@redocly/config";import{mcpTagSchemas as Z,schemaDefinitionTag as ee,markdocParser as U}from"@redocly/api-docs";import{withPathPrefix as q}from"@redocly/theme/core/utils";import{API_DOCS_TEMPLATE_ID as V,GRAPHQL_SPEC_SLUG as te,PUBLIC_RBAC_SCOPE_ITEM as x}from"../../../constants/common.js";import{DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER as oe,PUBLIC_API_DEFINITIONS_FOLDER as re}from"../../constants/common.js";import{API_DOCS_SHARED_DATA_PREFIX as w}from"../../constants/plugins/api-docs.js";import{fromCurrentDir as ae}from"../../utils/paths.js";import{normalizeRouteSlug as se}from"../../../utils/path/normalize-route-slug.js";import{createSlugify as ie}from"../create-slugify.js";import{apiDocLoader as ne,apiDocsLoader as ce}from"./loader.js";import{getTemplatePath as j}from"./get-template-path.js";import{storeDefinitionBundles as pe}from"./store-definition-bundles.js";import{telemetryTraceStep as ue}from"../../../cli/telemetry/helpers/trace-step.js";import{searchResolver as de}from"./search/search-resolver.js";import{getAiDocumentsStore as le}from"./search/get-ai-search-documents.js";import{getApiConfigByPath as me}from"../get-api-config.js";import{getRouteSlugsForPath as fe}from"../utils.js";import{collectItemRoutes as he}from"./utils/collect-item-routes.js";import{isChunkCurrent as ge}from"./utils/decorators.js";import{applyNavItemToRoot as De}from"./utils/apply-nav-item-to-root.js";import{buildRoutesMapping as Se}from"./utils/build-routes-mapping.js";import{buildItemPositions as Pe}from"./utils/build-item-positions.js";import{resolveApiDocTitle as ye}from"./utils/resolve-api-doc-title.js";import{resolveConfigRbac as Ie}from"./utils/resolve-config-rbac.js";import{relativePathToBaseSlug as Ae}from"./utils/relative-path-to-base-slug.js";import{normalizeApiVersion as ve}from"./utils/normalize-api-version.js";import{importApiDocsBuilder as W}from"./import-api-docs-builder.js";import{envConfig as $}from"../../config/env-config.js";import{logger as Re}from"../../tools/notifiers/logger.js";const T="openapi-spec-download",be={asyncapi:"AsyncAPI Docs",graphql:"GraphQL Docs",openapi:"OpenAPI Docs"};async function nt(){let t=[],r=new Map;const s=new WeakMap;let n=new Set,m={};function k(o,h,A){const y=s.get(o)??new Map;s.set(o,y);const g=h??"";return y.get(g)===A?!1:(y.set(g,A),!0)}return{id:"api-docs",requiredEntitlements:["openapi","asyncapi","graphql"],loaders:{"api-doc":ne,"api-docs":ce},processContent:async(o,h)=>{await ue("com.redocly.build.plugin.apiDocs",async()=>{o.createRequestHandler(T,ae(import.meta.url,"./spec-download.api.js")),o.addApiRoute({slug:re+"/*",requestHandlerId:T,httpMethod:"all",[v]:x,getStaticData:async()=>({props:{}})}),o.addApiRoute({slug:oe+"/*",requestHandlerId:T,httpMethod:"all",[v]:x,getStaticData:async()=>({props:{}})}),o.addApiRoute({slug:te+"/*",requestHandlerId:T,httpMethod:"all",[v]:x,getStaticData:async()=>({props:{}})});const A=o.createTemplate(V,j("../../../client/templates/api-docs/template.js")),y=o.registerServerPropsGetter(V,j("./get-server-props.js")),g=await h.getConfig();r=new Map,m={};const I=await o.loadApiDocuments(h);pe(I,o.outdir);const C=!!g.plugins?.some(e=>typeof e=="string"&&e.startsWith("@redocly/portal-plugin-async-api/"));C&&I.some(e=>e.type==="asyncapi")&&Re.warn("The plugin '@redocly/portal-plugin-async-api' is deprecated. Please remove it from your config to use built-in AsyncAPI docs.");const c=C?I.filter(e=>e.type!=="asyncapi"):I;t=c.map(({markdocChunks:e,relativePath:P,isVirtual:p,customOutputRelativeFile:a,realRelativePath:f})=>({chunks:e??[],relativePath:P,realRelativePath:f,isVirtual:a!=null||p}));const{buildItems:i}=await W(o.serverOutDir),D=(await h.cache.load("content-slugs","content-slugs")).data,S=ie(D),O=e=>se(S(e));for(const e of c){const P=e.customOutputRelativeFile||e.relativePath,p=O(P);Ee(e,p);const{items:a,store:f}=await i({type:e.type,document:e.document,options:e.options,basePath:p,markdownParser:U}),R=`${w}${e.relativePath}`,M=$.isDevelopMode?e.relativePath:void 0,u=Te(e.document),b=u?.["x-metadata"],_=e.isVirtual?void 0:b?.apiId,N={document:e,basePath:p,sourcePath:M,apiId:_};await o.createSharedData(R,Q(N,a,f));const K=Ie(e.config),F=!!e.options.excludeFromSearch,Y=e.type==="openapi",l=he(a,K).map(d=>{const B=d.metadata?.seo,H={...we(d.link,p),fsPath:P,templateId:A,sharedData:[{key:"apiDocsStore",id:R}],serverPropsGetterIds:[y],excludeFromSearch:F,getStaticData:Ce(d.label,B),[v]:d[v]};return Y?{...H,...d.httpVerb?{httpVerb:d.httpVerb,metadata:{subType:"openapi-operation"}}:{}}:H});if(!l.length)continue;const G=ye(u,e.type,a),X=ve(u?.version),L={type:e.type,title:G,version:X,description:typeof u?.description=="string"?u.description:void 0,summary:typeof u?.summary=="string"?u.summary:void 0,...u?.["x-metadata"]&&typeof u["x-metadata"]=="object"?u["x-metadata"]:{}};l[0]={...l[0],metadata:L,hasClientRoutes:!0,getSidebar:(d,B)=>De(z(a),p,B),getNavText:()=>G??be[e.type]??"API Docs",getSearchDocuments:de(a,L,o.getSearchFacets,o.setSearchFacets,F,f,e.document),getAiDocumentsStore:le({items:a,metadata:L,store:f,document:e.document,specType:e.type,excludeFromSearch:F,basePath:p,getSearchFacets:o.getSearchFacets,markdocTags:{...Z,schemaDefinition:ee}})};const J=l[0];l[0]=l[l.length-1],l[l.length-1]=J;for(const d of l)o.addRoute(d);if(_){const d=me(e.config?.apis,e.realRelativePath);m[_]={[v]:d.rbac,[E]:{fsPath:P}}}r.set(R,N)}})},afterRoutesCreated:async(o,h)=>{const{buildItems:A,simplifyAstStructure:y}=await W(o.serverOutDir),g=new Set,I=new Set;for(const{chunks:c,relativePath:i,isVirtual:D}of t){const S=`${w}${i}`,O=fe(o.getAllRoutes(),i);for(const{node:e,markdown:P,pointer:p,key:a,relativePath:f}of c){const{ast:R,compoundHash:M}=await o.parseMarkdoc({input:{content:P,relativePath:f,isVirtual:D},resource:p,context:h,deps:{sharedDataIds:[`${w}${f}`],routeSlugs:O}});if(g.add(p),!(a==="graphql-docs"?e?.description?.value!==void 0:!!a&&ge(e,a,P))||!k(e,a,M))continue;I.add(S);const b=y(R);if(a==="graphql-docs"){e.description&&(e.description.value=b);const _=`${w}${f}`;_e(r.get(_),b)}else a&&(e[a]=b)}}const C=n.difference(g);for(const c of C)h.cache.delete(c);n=g,Object.entries(m).forEach(([c,i])=>{const D=i[E]?.fsPath;if(!D)return;const S=o.getRouteByFsPath(D)?.baseSlug;m[c]={...i,[E]:{...i[E],slug:S},slug:S}}),o.setGlobalData({apiProducts:m});for(const[c,i]of r){if(!I.has(c))continue;const{items:D,store:S}=await A({type:i.document.type,document:i.document.document,options:i.document.options,basePath:i.basePath,markdownParser:U});await o.createSharedData(c,Q(i,D,S))}}}}function Q({document:t,basePath:r,sourcePath:s,apiId:n},m,k){return{items:m,store:k,options:t.options,baseSlug:r,routesMapping:$.isDevelopMode?Se(m,r):{},graphqlPositions:$.isDevelopMode&&t.type==="graphql"?Pe(m,r):void 0,sourcePath:s,apiId:n}}function _e(t,r){t?.document.type!=="graphql"||!t.document.options||(t.document.options.info={...t.document.options.info||{},description:r})}function Ce(t,r){return async function(s){return{props:{settings:{baseUrlPath:q(s.baseSlug)},disableAutoScroll:!0,seo:{title:t,...r}}}}}function Ee(t,r){const s=t.options?.mockServer,n=q(`/_mock${Ae(t.relativePath)}`);s?.url===n&&(t.options={...t.options,mockServer:{...s,url:q(`/_mock${r}`)}})}function we(t,r){if(t===r)return{};const s=r==="/"?"/":`${r}/`;if(t.startsWith(s)){const n=t.slice(s.length);return n?{slugSuffix:n}:{}}return{slug:t}}function Te(t){if(t&&typeof t=="object"&&"info"in t)return t.info}function z(t){return t.map(({content:r,items:s,...n})=>({...n,...s&&{items:z(s)}}))}export{nt as apiDocsPlugin,we as toRouteSlugFields};
@@ -1,3 +1,3 @@
1
- import*as p from"node:path";import{parse as B,visit as x}from"graphql";import{Source as N,bundle as _,createConfig as D,getTotals as M,resolvePlugins as V}from"@redocly/openapi-core";import{convertSwagger2OpenAPI as j}from"@redocly/api-docs/lib/adapters/utils/convertSwagger2OpenAPI.js";import{CONFIG_FILE_NAME as k}from"../../../constants/common.js";import{MAX_BUNDLING_ERRORS_TO_PRINT as S}from"../../constants/plugins/api-docs.js";import{unique as F}from"../../../utils/array/unique.js";import{deepMerge as G}from"../../../utils/object/deep-merge.js";import{pluralize as C}from"../../../utils/string/pluralize.js";import{replaceEnvVariablesDeep as L}from"../../utils/envs/replace-env-variables-deep.js";import{logger as q}from"../../tools/notifiers/logger.js";import{reporter as h}from"../../tools/notifiers/reporter.js";import{ExternalResolver as b}from"../../fs/utils/external-ref-resolver.js";import{injectDecoratorIntoConfig as z,isPlainNonMarkdownText as H}from"./utils/decorators.js";import{getAllApiConfigsByPath as J,getApiConfigByPath as Q}from"../get-api-config.js";import{formatBundleError as U}from"./utils/format-bundle-error.js";import{createOptions as O}from"./options.js";const X=/(\.ya?ml|\.json|\.gql|\.graphql)$/,P=/(\.gql|\.graphql)$/;async function kn(n,r){if(p.posix.basename(n)===k)return[];if(P.test(n)){if(await r.isPathIgnored(n))return[];const u=await tn(n,r);return u?[u]:[]}let o;try{o=(await r.cache.load(n,"yaml")).data}catch{return[]}const t=Y(n,o);if(!t||!o||t==="graphql")return t===null&&q.verbose(`${n} file is not a recognized API spec. Skipping`),[];const{fs:c,getConfig:a,isPathIgnored:s}=r,e=t==="openapi"?await a("."):null,i=c.getFileInfo(n),f=await a(p.posix.dirname(n)),l=J(f?.apis,n,f.configPath),w=await s(n),d=[];for(const u of l){if(!u.output&&w)continue;const y=f.configPath?p.posix.dirname(f.configPath):".",g=u.output?p.posix.join(y,u.output):n;if(!await s(g)){i&&u.output&&!c.exists(g)&&c.addVirtualFile(g,i);try{const m=await on({relativePath:n,outputRelativePath:g,loaded:o,specType:t,rootConfig:e,config:f,apiConfig:u,fileRecord:i,fs:c});m&&d.push(m)}catch(m){await h.panicOnBuildContentError(m)}}}return d}async function qn(n,r){const{fs:o,cache:t}=r,c=[];for(const a of o.scan(X)){let s;try{s=await t.load(a.relativePath,"api-doc")}catch{continue}if(s.data?.length)for(const e of s.data)c.push({...e,isVirtual:a.isVirtual||e.isVirtual,hash:s.compoundHash})}return c}function K(n){const r=[];return x(n,{leave:o=>{!("description"in o)||!o.description?.value||!("name"in o)||H(o.description.value)||r.push({node:o,pointer:`#/types/${o?.name?.value}`,markdown:o.description.value,relativePath:"",key:"graphql-docs"})}}),r}async function W(n){try{const r=B(n);return{document:r,markdocChunks:K(r)}}catch(r){await h.panicOnBuildContentError(r);return}}function Y(n,r){return P.test(n)?"graphql":r?.openapi||r?.swagger?"openapi":r?.asyncapi?"asyncapi":null}function $(n,r){return r?{...n,info:{...n.info,"x-metadata":{...n.info?.["x-metadata"],...r}}}:n}async function Z(n,r,o){if(n==="openapi"){const t=r.swagger!==void 0?await j(r):r;return $(t,o)}return $(r,o)}async function nn(n){const{outputRelativePath:r,rootConfig:o,config:t,apiConfig:c,fs:a}=n,s=G({decorators:o?.decorators},t,{rbac:void 0},c),{resolvedObj:e}=L(s);if(e.decorators&&typeof e.decorators!="object"){await h.panicOnBuildContentError(`'decorators' must be an object at redocly.yaml, got '${typeof e.decorators}'`);return}if(e.plugins&&!Array.isArray(e.plugins)){await h.panicOnBuildContentError(`'plugins' must be an array at redocly.yaml, got '${typeof e.plugins}'`);return}const i=p.resolve(a.cwd,t.realConfigPath||k),f=new b(a),l=await D(z(e,r,a.cwd),{configPath:i,externalRefResolver:f});return o?.plugins&&o.configPath!==t.configPath&&(l.plugins=F([...l.plugins||[],...await V(o.plugins,a.cwd)],w=>w.id)),{coreConfig:l,mergedConfig:e}}async function rn(n,r,o){const t=M(n),c=[t.errors&&`${t.errors} ${C(t.errors,"error","errors")}`,t.warnings&&`${t.warnings} ${C(t.warnings,"warning","warnings")}`,t.ignored&&`${t.ignored} ignored`].filter(Boolean).join(", ");if(t.errors){const a=n.filter(i=>i.severity==="error");let s=`${c} while bundling ${r} definition:
2
- `;const e=Math.min(a.length,S);for(let i=0;i<e;i++){const f=await U(a[i],o);s+=`${f}
3
- `}if(a.length>e){const i=a.length-e;s+=`... and ${i} more ${C(i,"error","errors")}`}return await h.panicOnBuild(s),!0}return t.warnings&&q.warn(`${c} while bundling %rp definition`,r),!1}async function on(n){const{relativePath:r,outputRelativePath:o,loaded:t,specType:c,rootConfig:a,config:s,apiConfig:e,fileRecord:i,fs:f}=n,l=await nn(n);if(!l)return;const{coreConfig:w,mergedConfig:d}=l,u=p.resolve(f.cwd,r),y=new b(f),g={config:w,base:p.dirname(u),doc:{source:new N(u,await f.read(r)),parsed:JSON.parse(JSON.stringify(t))},externalRefResolver:y},{bundle:{parsed:R},problems:m,visitorsData:v}=await _(g);if(await rn(m,o,f.cwd))return;const I=v["markdown/markdown"]?.markdocChunks??[],A=d.metadata,E=await Z(c,R,A),T=O(c,E,{rootConfig:a,config:s,apiConfig:e,mergedConfig:d,relativePath:o});return{type:c,document:E,config:d,relativePath:o,realRelativePath:i?.realRelativePath||o,customOutputRelativeFile:e.output?o:void 0,markdocChunks:I,isVirtual:i?.isVirtual,options:T}}async function tn(n,r){const{fs:o,getConfig:t}=r,c=await o.read(n),a=o.getFileInfo(n),{document:s,markdocChunks:e}=await W(c)||{};if(!s){await h.panicOnBuildContentError(new Error("Failed to build GraphQL document"));return}const i=await t(),f=Q(i.apis,n);i.graphql?.info?.description&&e?.unshift({node:{description:{value:i.graphql?.info?.description}},pointer:"#/info",markdown:i.graphql?.info?.description,relativePath:"",key:"graphql-docs"});const l=O("graphql",s,{rootConfig:null,config:i,apiConfig:f??{},mergedConfig:{graphql:{...i.graphql??{},...f?.graphql??{}}},relativePath:n});return{type:"graphql",document:s,config:{...i,...f},relativePath:n,realRelativePath:a?.realRelativePath||n,markdocChunks:e,isVirtual:a?.isVirtual,options:l}}export{kn as apiDocLoader,qn as apiDocsLoader,K as collectGraphqlMarkdocChunks};
1
+ import*as l from"node:path";import{parse as B,visit as x}from"graphql";import{Source as D,bundle as N,createConfig as _,getTotals as M,resolvePlugins as V}from"@redocly/openapi-core";import{convertSwagger2OpenAPI as j}from"@redocly/api-docs/lib/adapters/utils/convertSwagger2OpenAPI.js";import{normalizeAsyncApiDefinition as S}from"@redocly/api-docs/lib/adapters/utils/normalizeAsyncApiDefinition.js";import{CONFIG_FILE_NAME as q}from"../../../constants/common.js";import{MAX_BUNDLING_ERRORS_TO_PRINT as F}from"../../constants/plugins/api-docs.js";import{unique as G}from"../../../utils/array/unique.js";import{deepMerge as L}from"../../../utils/object/deep-merge.js";import{pluralize as k}from"../../../utils/string/pluralize.js";import{replaceEnvVariablesDeep as z}from"../../utils/envs/replace-env-variables-deep.js";import{logger as b}from"../../tools/notifiers/logger.js";import{reporter as h}from"../../tools/notifiers/reporter.js";import{ExternalResolver as O}from"../../fs/utils/external-ref-resolver.js";import{collectCopiedDescriptionChunks as H,injectDecoratorIntoConfig as J,isPlainNonMarkdownText as Q}from"./utils/decorators.js";import{getAllApiConfigsByPath as U,getApiConfigByPath as X}from"../get-api-config.js";import{formatBundleError as K}from"./utils/format-bundle-error.js";import{createOptions as P}from"./options.js";const W=/(\.ya?ml|\.json|\.gql|\.graphql)$/,$=/(\.gql|\.graphql)$/;async function On(n,o){if(l.posix.basename(n)===q)return[];if($.test(n)){if(await o.isPathIgnored(n))return[];const u=await an(n,o);return u?[u]:[]}let r;try{r=(await o.cache.load(n,"yaml")).data}catch{return[]}const t=nn(n,r);if(!t||!r||t==="graphql")return t===null&&b.verbose(`${n} file is not a recognized API spec. Skipping`),[];const{fs:s,getConfig:a,isPathIgnored:c}=o,e=t==="openapi"?await a("."):null,i=s.getFileInfo(n),f=await a(l.posix.dirname(n)),p=U(f?.apis,n,f.configPath),w=await c(n),d=[];for(const u of p){if(!u.output&&w)continue;const y=f.configPath?l.posix.dirname(f.configPath):".",g=u.output?l.posix.join(y,u.output):n;if(!await c(g)){i&&u.output&&!s.exists(g)&&s.addVirtualFile(g,i);try{const m=await en({relativePath:n,outputRelativePath:g,loaded:r,specType:t,rootConfig:e,config:f,apiConfig:u,fileRecord:i,fs:s});m&&d.push(m)}catch(m){await h.panicOnBuildContentError(m)}}}return d}async function Pn(n,o){const{fs:r,cache:t}=o,s=[];for(const a of r.scan(W)){let c;try{c=await t.load(a.relativePath,"api-doc")}catch{continue}if(c.data?.length)for(const e of c.data)s.push({...e,isVirtual:a.isVirtual||e.isVirtual,hash:c.compoundHash})}return s}function Y(n){const o=[];return x(n,{leave:r=>{!("description"in r)||!r.description?.value||!("name"in r)||Q(r.description.value)||o.push({node:r,pointer:`#/types/${r?.name?.value}`,markdown:r.description.value,relativePath:"",key:"graphql-docs"})}}),o}async function Z(n){try{const o=B(n);return{document:o,markdocChunks:Y(o)}}catch(o){await h.panicOnBuildContentError(o);return}}function nn(n,o){return $.test(n)?"graphql":o?.openapi||o?.swagger?"openapi":o?.asyncapi?"asyncapi":null}function v(n,o){return o?{...n,info:{...n.info,"x-metadata":{...n.info?.["x-metadata"],...o}}}:n}async function on(n,o,r){if(n==="openapi"){const t=o.swagger!==void 0?await j(o):o;return v(t,r)}return v(S(o),r)}async function rn(n){const{outputRelativePath:o,rootConfig:r,config:t,apiConfig:s,fs:a}=n,c=L({decorators:r?.decorators},t,{rbac:void 0},s),{resolvedObj:e}=z(c);if(e.decorators&&typeof e.decorators!="object"){await h.panicOnBuildContentError(`'decorators' must be an object at redocly.yaml, got '${typeof e.decorators}'`);return}if(e.plugins&&!Array.isArray(e.plugins)){await h.panicOnBuildContentError(`'plugins' must be an array at redocly.yaml, got '${typeof e.plugins}'`);return}const i=l.resolve(a.cwd,t.realConfigPath||q),f=new O(a),p=await _(J(e,o,a.cwd),{configPath:i,externalRefResolver:f});return r?.plugins&&r.configPath!==t.configPath&&(p.plugins=G([...p.plugins||[],...await V(r.plugins,a.cwd)],w=>w.id)),{coreConfig:p,mergedConfig:e}}async function tn(n,o,r){const t=M(n),s=[t.errors&&`${t.errors} ${k(t.errors,"error","errors")}`,t.warnings&&`${t.warnings} ${k(t.warnings,"warning","warnings")}`,t.ignored&&`${t.ignored} ignored`].filter(Boolean).join(", ");if(t.errors){const a=n.filter(i=>i.severity==="error");let c=`${s} while bundling ${o} definition:
2
+ `;const e=Math.min(a.length,F);for(let i=0;i<e;i++){const f=await K(a[i],r);c+=`${f}
3
+ `}if(a.length>e){const i=a.length-e;c+=`... and ${i} more ${k(i,"error","errors")}`}return await h.panicOnBuild(c),!0}return t.warnings&&b.warn(`${s} while bundling %rp definition`,o),!1}async function en(n){const{relativePath:o,outputRelativePath:r,loaded:t,specType:s,rootConfig:a,config:c,apiConfig:e,fileRecord:i,fs:f}=n,p=await rn(n);if(!p)return;const{coreConfig:w,mergedConfig:d}=p,u=l.resolve(f.cwd,o),y=new O(f),g={config:w,base:l.dirname(u),doc:{source:new D(u,await f.read(o)),parsed:JSON.parse(JSON.stringify(t))},externalRefResolver:y},{bundle:{parsed:E},problems:m,visitorsData:A}=await N(g);if(await tn(m,r,f.cwd))return;const C=A["markdown/markdown"]?.markdocChunks??[],I=d.metadata,R=await on(s,E,I);s==="asyncapi"&&C.push(...H(R,C));const T=P(s,R,{rootConfig:a,config:c,apiConfig:e,mergedConfig:d,relativePath:r});return{type:s,document:R,config:d,relativePath:r,realRelativePath:i?.realRelativePath||r,customOutputRelativeFile:e.output?r:void 0,markdocChunks:C,isVirtual:i?.isVirtual,options:T}}async function an(n,o){const{fs:r,getConfig:t}=o,s=await r.read(n),a=r.getFileInfo(n),{document:c,markdocChunks:e}=await Z(s)||{};if(!c){await h.panicOnBuildContentError(new Error("Failed to build GraphQL document"));return}const i=await t(),f=X(i.apis,n);i.graphql?.info?.description&&e?.unshift({node:{description:{value:i.graphql?.info?.description}},pointer:"#/info",markdown:i.graphql?.info?.description,relativePath:"",key:"graphql-docs"});const p=P("graphql",c,{rootConfig:null,config:i,apiConfig:f??{},mergedConfig:{graphql:{...i.graphql??{},...f?.graphql??{}}},relativePath:n});return{type:"graphql",document:c,config:{...i,...f},relativePath:n,realRelativePath:a?.realRelativePath||n,markdocChunks:e,isVirtual:a?.isVirtual,options:p}}export{On as apiDocLoader,Pn as apiDocsLoader,Y as collectGraphqlMarkdocChunks};
@@ -1,5 +1,16 @@
1
1
  import type { RawUniversalConfig } from '@redocly/openapi-core';
2
+ import type { MarkdocChunk } from '../loader.js';
2
3
  export declare function isPlainNonMarkdownText(markdown: string): boolean;
4
+ /**
5
+ * False once `node[key]` holds a string other than the chunk's markdown: the value was replaced
6
+ * after bundling (e.g. by an AsyncAPI 2.x trait), so writing the chunk back would restore it.
7
+ */
8
+ export declare function isChunkCurrent(node: Record<string, unknown>, key: string, markdown: string): boolean;
9
+ /**
10
+ * Chunks for `description` strings that were copied onto other objects after bundling (AsyncAPI
11
+ * traits), each taking the parse source of the chunk whose markdown it copies.
12
+ */
13
+ export declare function collectCopiedDescriptionChunks(document: unknown, chunks: MarkdocChunk[]): MarkdocChunk[];
3
14
  export declare const MarkdownParserDecorator: ({ outputRelativePath, cwd, }: {
4
15
  outputRelativePath: string;
5
16
  cwd: string;
@@ -1 +1 @@
1
- import b from"node:path";import{REDOCLY_TEAMS_RBAC as p}from"@redocly/config";import{slash as k}from"../../../../utils/path/slash.js";function f(t,r,a){if(!r)throw new Error("Visitor required");if(t){t.description&&r(t,"description",a.location.absolutePointer+"/description",a);for(const o in t["x-enumDescriptions"])r(t["x-enumDescriptions"],o,a.location.absolutePointer+"/x-enumDescriptions/"+o,a)}}const x=/^[a-zA-Z0-9\s\-,.!?:;'"()/]+$/,d=/^[^\S\r\n\u2028\u2029]*(?:[-+*]\s|\d+[.)]\s)/m;function g(t){return x.test(t)&&!d.test(t)}const s=({outputRelativePath:t,cwd:r})=>{const a=function(e,u,l,m){const{node:n}=m.resolve(e[u]);if(!n||typeof n!="string"||g(n))return;const i=m.getVisitorData();i.markdocChunks=i.markdocChunks||[],i.markdocChunks.push({node:e,pointer:k(b.relative(r,l)),markdown:n,relativePath:t,key:u})};return{any:{leave(o,e){f(o,a,e)}},ref:{leave(o,e){f(o,a,e)}}}},c=()=>{const t={};return{Tag:{enter(r){r?.["x-rbac"]&&(t[String(r.name)]=r["x-rbac"])}},any:{leave(r,a){if((a.type.name==="Operation"||a.type.name==="Webhook")&&"tags"in r&&Array.isArray(r.tags))for(const o of r.tags)t[o]&&(r["x-rbac"]=r["x-rbac"]||t[o]);r?.["x-rbac"]&&(r[p]=r["x-rbac"],delete r["x-rbac"])}},ref:{leave(r){r?.["x-rbac"]&&(r[p]=r["x-rbac"],delete r["x-rbac"])}}}};function D(t,r,a){const o={id:"markdown",decorators:{oas2:{markdown:s,rbac:c},oas3:{markdown:s,rbac:c},async3:{markdown:s,rbac:c}}};return{...t,decorators:{"markdown/rbac":"on","markdown/markdown":{outputRelativePath:r,cwd:a},...t.decorators||{}},plugins:[...t.plugins||[],o]}}export{s as MarkdownParserDecorator,c as RbacDecorator,D as injectDecoratorIntoConfig,g as isPlainNonMarkdownText};
1
+ import k from"node:path";import{REDOCLY_TEAMS_RBAC as m}from"@redocly/config";import{slash as w}from"../../../../utils/path/slash.js";function d(t,r,n){if(!r)throw new Error("Visitor required");if(t){t.description&&r(t,"description",n.location.absolutePointer+"/description",n);for(const o in t["x-enumDescriptions"])r(t["x-enumDescriptions"],o,n.location.absolutePointer+"/x-enumDescriptions/"+o,n)}}const b=/^[a-zA-Z0-9\s\-,.!?:;'"()/]+$/,l=/^[^\S\r\n\u2028\u2029]*(?:[-+*]\s|\d+[.)]\s)/m;function g(t){return b.test(t)&&!l.test(t)}function C(t,r,n){const o=t[r];return!!o&&(typeof o!="string"||o===n)}function M(t,r){const n=new Map,o=new Map;for(const e of r)e.key==="description"&&(n.has(e.markdown)||n.set(e.markdown,e),o.set(e.node,e.markdown));const a=[],c=new Set,f=e=>{if(typeof e!="object"||e===null||c.has(e))return;c.add(e);const{description:s}=e;if(typeof s=="string"&&o.get(e)!==s){const i=n.get(s);i&&a.push({...i,node:e})}for(const i of Object.values(e))f(i)};return f(t),a}const p=({outputRelativePath:t,cwd:r})=>{const n=function(a,c,f,e){const{node:s}=e.resolve(a[c]);if(!s||typeof s!="string"||g(s))return;const i=e.getVisitorData();i.markdocChunks=i.markdocChunks||[],i.markdocChunks.push({node:a,pointer:w(k.relative(r,f)),markdown:s,relativePath:t,key:c})};return{any:{leave(o,a){d(o,n,a)}},ref:{leave(o,a){d(o,n,a)}}}},u=()=>{const t={};return{Tag:{enter(r){r?.["x-rbac"]&&(t[String(r.name)]=r["x-rbac"])}},any:{leave(r,n){if((n.type.name==="Operation"||n.type.name==="Webhook")&&"tags"in r&&Array.isArray(r.tags))for(const o of r.tags)t[o]&&(r["x-rbac"]=r["x-rbac"]||t[o]);r?.["x-rbac"]&&(r[m]=r["x-rbac"],delete r["x-rbac"])}},ref:{leave(r){r?.["x-rbac"]&&(r[m]=r["x-rbac"],delete r["x-rbac"])}}}};function D(t,r,n){const o={id:"markdown",decorators:{oas2:{markdown:p,rbac:u},oas3:{markdown:p,rbac:u},async2:{markdown:p,rbac:u},async3:{markdown:p,rbac:u}}};return{...t,decorators:{"markdown/rbac":"on","markdown/markdown":{outputRelativePath:r,cwd:n},...t.decorators||{}},plugins:[...t.plugins||[],o]}}export{p as MarkdownParserDecorator,u as RbacDecorator,M as collectCopiedDescriptionChunks,D as injectDecoratorIntoConfig,C as isChunkCurrent,g as isPlainNonMarkdownText};
@@ -1 +1 @@
1
- import{REDOCLY_TEAMS_RBAC as $}from"@redocly/config";import{removeLeadingSlash as T}from"@redocly/theme/core/utils";import{toKebabCase as y}from"../../../../../../utils/string/to-kebab-case.js";import{promiseMapLimit as E}from"../../../../../utils/async/promise-map-limit.js";import{promiseMapLimitWithStatus as x}from"../../../../../utils/async/promise-map-limit-with-status.js";import{extractTeamsFromScopeItems as R}from"../../../../../utils/rbac.js";import{removeMarkdocTags as w}from"../../../../../../markdoc/helpers/remove-markdoc-tags.js";import{BaseApiEntitiesExtractor as b}from"./base.js";import{OpenapiSchemaResolver as S}from"../../../utils/openapi-schema-resolver.js";import{extractPartsFromComponentsRef as A}from"../../../utils/extract-parts-from-components-ref.js";import{isValidTagName as v}from"../../../utils/is-valid-tag-name.js";const N=["get","post","put","delete","patch"],d=15,O=15;class V extends b{#e;constructor(e){super("openapi",e),this.#e=e.context.logger}async loadApiDescriptions(){return(await this.actions.loadApiDocuments(this.context)).filter(o=>o.type==="openapi").map(o=>({...o,isVirtual:o.isVirtual??!1}))}mapApiDescriptionToEntity(e,o){const s=T(e.realRelativePath),r=e.document.info.title,t=this.resolveEntityKey({realRelativePath:e.realRelativePath,customKey:e.document["x-redocly-catalog-key"]});return{type:this.type,key:t,title:r,summary:w(typeof e.document.info.description=="string"?e.document.info.description:void 0),tags:e.document.tags?.filter(a=>a.name&&v(a.name)).map(a=>a.name),metadata:{specType:this.specType,descriptionFile:s},version:o}}async processApiDescription(e,o,s,r){if(S.clearSchemaCache(),!e?.document)throw new Error("OpenAPI definition is missing a bundled document");const t=this.getRbacTeamsForDefinition(e.relativePath),a=this.mapApiDescriptionToEntity(e,s);await this.catalogEntitiesService.createEntity({entity:a,sourceFile:e.realRelativePath,fileHash:e.hash,source:"file",isRootEntity:!0,rbacTeams:t,revision:o}),r.delete(`${a.key}:${s}`),await this.#r(e,a.key,a.version,s,o,r,t);const n=this.#s(e.document.paths);if(n.length!==0){const i=(await x(n,d,async({operation:m,path:h,method:f})=>{if(!m.operationId)return;const l=await this.#n(m,h,f,e,a.key,a.version,o,t);r.delete(`${l}:${s}`)},this.#e)).count.failed;i>0&&this.#e.warn(`Extraction completed with ${i} failures out of ${n.length} operations for ${a.key}`)}}#s(e){return Object.entries(e??{}).flatMap(([o,s])=>N.filter(r=>s[r]).map(r=>{const t=s[r];if(!t)throw new Error(`Operation not found for method ${r} on path ${o}`);return{operation:t,path:o,method:r.toUpperCase()}}))}async#r(e,o,s,r,t,a,n){const c=e.document.components?.schemas;if(!c)return;const i=Object.entries(c);i.length!==0&&await E(i,O,async([m,h])=>{const f=`${o}-${y(m)}`,l=await this.#o({schemaName:m,schema:h,description:e,parentKey:o,parentVersion:s,parentRevision:t,rbacTeams:n});a.delete(`${f}:${r}`),l&&await this.catalogEntitiesService.createEntityRelation({sourceKey:o,sourceVersion:s,sourceRevision:t,type:"uses",targetKey:l.key,targetVersion:s,targetRevision:t,sourceFile:e.realRelativePath,fileHash:e.hash})})}async#o({schemaName:e,schema:o,description:s,parentKey:r,parentVersion:t,parentRevision:a,rbacTeams:n}){const c=S.resolveSchemaRefs(o,s.document),i=JSON.stringify(c),m=c[$]?R(c[$]):n,h=R(c["x-rbac"]),f={type:"data-schema",key:`${r}-${y(e)}`,title:e,summary:(typeof c.description=="string"?c.description:c.title)??null,tags:[],metadata:{specType:this.specType,schema:i},version:t};try{return await this.catalogEntitiesService.createEntity({entity:f,sourceFile:s.realRelativePath,fileHash:s.hash,rbacTeams:h.length?h:m,source:"file",revision:a})}catch(l){return this.#e.error(`Failed to create data schema entity for ${e}: ${l instanceof Error?l.message:"Unknown error"}`),null}}async#n(e,o,s,r,t,a,n,c){const i=`${t}-${y(`${e.operationId}`)}`,m=await Promise.all([this.#m(e.requestBody??null,r),this.#h(e.responses??{},r)]),h=m[0].schemaNames,f=m[1].schemaNames;m[0].errors.length>0&&this.#e.warn(`Schema extraction errors for operation ${e.operationId}: ${m[0].errors.join(", ")}`),m[1].errors.length>0&&this.#e.warn(`Response schema extraction errors for operation ${e.operationId}: ${m[1].errors.join(", ")}`);const l=h.map(p=>`${t}-${y(p)}`),u=f.map(p=>`${t}-${y(p)}`),g=await this.#f({path:o,method:s,operation:e,requestBodySchemasKeys:l,responseSchemasKeys:u,description:r,parentKey:t,parentVersion:a,parentRevision:n,rbacTeams:c});return g&&await Promise.all([this.#c(h,e.operationId??"",r.realRelativePath,r.hash,t,a,n),this.#i(f,e.operationId??"",r.realRelativePath,r.hash,t,a,n),this.#p({apiOperationKey:g.key,operationRelations:e["x-catalogRelations"],descriptionUniqueKey:t,description:r,parentVersion:a,parentRevision:n})]),i}async#c(e,o,s,r,t,a,n){return e.length===0||!o?[]:await this.#a(e,o,s,r,t,a,n)}async#i(e,o,s,r,t,a,n){return e.length===0||!o?[]:await this.#a(e,o,s,r,t,a,n)}#m(e,o){const s=[],r=[];try{if(e&&"content"in e&&e.content)for(const t of Object.values(e.content)){if(!t.schema?.$ref)continue;const a=this.#t(t.schema.$ref);a?.componentType==="schemas"&&a.componentName&&s.push(a.componentName)}if(e&&e.$ref){const t=this.#t(e.$ref);if(t?.componentType==="requestBodies"&&t.componentName){const a=o.document.components?.requestBodies?.[t.componentName];if(a&&"content"in a)for(const n of Object.values(a.content??{})){if(!n.schema?.$ref)continue;const c=this.#t(n.schema.$ref);c?.componentType==="schemas"&&c.componentName&&s.push(c.componentName)}}}}catch(t){r.push(`Failed to extract request body schemas: ${t instanceof Error?t.message:"Unknown error"}`)}return{schemaNames:[...new Set(s)],errors:r}}#h(e,o){const s=[],r=[];try{for(const t of Object.values(e)){if(t&&"content"in t&&t.content)for(const a of Object.values(t.content)){if(!a.schema?.$ref)continue;const n=this.#t(a.schema.$ref);n?.componentType==="schemas"&&n.componentName&&s.push(n.componentName)}if(t&&t.$ref){const a=this.#t(t.$ref);if(a?.componentType==="responses"&&a.componentName){const n=o.document.components?.responses?.[a.componentName];if(n&&"content"in n)for(const c of Object.values(n.content??{})){if(!c.schema?.$ref)continue;const i=this.#t(c.schema.$ref);i?.componentType==="schemas"&&i.componentName&&s.push(i.componentName)}}}}}catch(t){r.push(`Failed to extract response schemas: ${t instanceof Error?t.message:"Unknown error"}`)}return{schemaNames:[...new Set(s)],errors:r}}#t(e){try{return A(e)}catch{return{componentType:"",componentName:null}}}async#a(e,o,s,r,t,a,n){return e.length===0?[]:await E(e,O,async c=>await this.#l({operationId:o,schemaName:c,descriptionSourceFile:s,descriptionHash:r,parentKey:t,parentVersion:a,parentRevision:n}))}async#l({operationId:e,schemaName:o,descriptionSourceFile:s,descriptionHash:r,parentKey:t,parentVersion:a,parentRevision:n}){const c=`${t}-${y(o)}`;try{return await this.catalogEntitiesService.createEntityRelation({sourceKey:`${t}-${y(e)}`,type:"uses",targetKey:c,sourceFile:s,fileHash:r,sourceVersion:a,targetVersion:a,sourceRevision:n,targetRevision:n}),c}catch(i){throw this.#e.error(`Failed to create relation between operation ${e} and schema ${o}: ${i instanceof Error?i.message:"Unknown error"}`),i}}async#f({path:e,method:o,operation:s,requestBodySchemasKeys:r,responseSchemasKeys:t,description:a,parentKey:n,parentVersion:c,parentRevision:i,rbacTeams:m}){const h=`${s.operationId}`,f=`${n}-${y(h)}`,l=s[$]?R(s[$]):m,u=R(s["x-rbac"]),g={type:"api-operation",key:f,title:h,summary:w(s.summary),tags:s.tags?.filter(p=>v(p)),metadata:{path:e,method:o,payload:r,responses:t},version:c};try{return await this.catalogEntitiesService.createEntity({entity:g,sourceFile:a.realRelativePath,fileHash:a.hash,rbacTeams:u.length>0?u:l,source:"file",revision:i})}catch(p){return this.#e.error(`Failed to create API operation entity for ${h}: ${p instanceof Error?p.message:"Unknown error"}`),null}}async#p({apiOperationKey:e,operationRelations:o,descriptionUniqueKey:s,description:r,parentVersion:t,parentRevision:a}){try{await this.catalogEntitiesService.createEntityRelation({sourceKey:e,type:"partOf",targetKey:s,sourceFile:r.realRelativePath,fileHash:r.hash,sourceVersion:t,targetVersion:t,sourceRevision:a,targetRevision:a}),await this.#y(e,r,o,t,a)}catch(n){this.#e.error(`Failed to create API operation relations for ${e}: ${n instanceof Error?n.message:"Unknown error"}`)}}async#y(e,o,s,r,t){s?.length&&await E(s,d,async a=>{try{this.validateEntityRelationFileSchema(a),await this.catalogEntitiesService.createEntityRelation({sourceKey:e,type:a.type,targetKey:a.key,sourceFile:o.realRelativePath,fileHash:o.hash,sourceVersion:r,targetVersion:"",sourceRevision:t,targetRevision:""})}catch(n){this.context.logger.warn(`Error creating entity relation for operation ${e} based on custom property: ${n instanceof Error?n.message:"Unknown error"}`)}})}}export{V as OpenApiEntitiesExtractor};
1
+ import{REDOCLY_TEAMS_RBAC as R}from"@redocly/config";import{removeLeadingSlash as O}from"@redocly/theme/core/utils";import{toKebabCase as y}from"../../../../../../utils/string/to-kebab-case.js";import{promiseMapLimit as E}from"../../../../../utils/async/promise-map-limit.js";import{promiseMapLimitWithStatus as x}from"../../../../../utils/async/promise-map-limit-with-status.js";import{extractTeamsFromScopeItems as $}from"../../../../../utils/rbac.js";import{removeMarkdocTags as S}from"../../../../../../markdoc/helpers/remove-markdoc-tags.js";import{BaseApiEntitiesExtractor as b}from"./base.js";import{OpenapiSchemaResolver as w}from"../../../utils/openapi-schema-resolver.js";import{extractPartsFromComponentsRef as N}from"../../../utils/extract-parts-from-components-ref.js";import{isValidTagName as d}from"../../../utils/is-valid-tag-name.js";const A=["get","post","put","delete","patch"],T=15,v=15;class V extends b{#e;constructor(e){super("openapi",e),this.#e=e.context.logger}async loadApiDescriptions(){return(await this.actions.loadApiDocuments(this.context)).filter(o=>o.type==="openapi").map(o=>({...o,isVirtual:o.isVirtual??!1}))}mapApiDescriptionToEntity(e,o){const n=O(e.realRelativePath),s=e.document.info.title,t=this.resolveEntityKey({realRelativePath:e.realRelativePath,customKey:e.document["x-redocly-catalog-key"]});return{type:this.type,key:t,title:s,summary:S(typeof e.document.info.description=="string"?e.document.info.description:void 0),tags:e.document.tags?.filter(a=>a.name&&d(a.name)).map(a=>a.name),metadata:{specType:this.specType,descriptionFile:n},version:o}}async processApiDescription(e,o,n,s){if(w.clearSchemaCache(),!e?.document)throw new Error("OpenAPI definition is missing a bundled document");const t=this.getRbacTeamsForDefinition(e.relativePath),a=this.mapApiDescriptionToEntity(e,n);await this.catalogEntitiesService.createEntity({entity:a,sourceFile:e.realRelativePath,fileHash:e.hash,source:"file",isRootEntity:!0,rbacTeams:t,revision:o}),s.delete(`${a.key}:${n}`),await this.#o(e,a.key,a.version,n,o,s,t);const r=this.#n(e.document.paths);if(r.length!==0){const i=(await x(r,T,async({operation:m,path:h,method:f})=>{if(!m.operationId)return;const l=await this.#c(m,h,f,e,a.key,a.version,o,t);s.delete(`${l}:${n}`)},this.#e)).count.failed;i>0&&this.#e.warn(`Extraction completed with ${i} failures out of ${r.length} operations for ${a.key}`)}}#n(e){return Object.entries(e??{}).flatMap(([o,n])=>A.filter(s=>n[s]).map(s=>{const t=n[s];if(!t)throw new Error(`Operation not found for method ${s} on path ${o}`);return{operation:t,path:o,method:s.toUpperCase()}}))}async#o(e,o,n,s,t,a,r){const c=e.document.components?.schemas;if(!c)return;const i=Object.entries(c);i.length!==0&&await E(i,v,async([m,h])=>{const f=`${o}-${y(m)}`,l=await this.#r({schemaName:m,schema:h,description:e,parentKey:o,parentVersion:n,parentRevision:t,rbacTeams:r});a.delete(`${f}:${s}`),l&&await this.catalogEntitiesService.createEntityRelation({sourceKey:o,sourceVersion:n,sourceRevision:t,type:"uses",targetKey:l.key,targetVersion:n,targetRevision:t,sourceFile:e.realRelativePath,fileHash:e.hash})})}async#r({schemaName:e,schema:o,description:n,parentKey:s,parentVersion:t,parentRevision:a,rbacTeams:r}){const c=w.resolveSchemaRefs(o,n.document),i=JSON.stringify(c),m=c[R]?$(c[R]):r,h=$(c["x-rbac"]),f={type:"data-schema",key:`${s}-${y(e)}`,title:e,summary:(typeof c.description=="string"?c.description:c.title)??null,tags:[],metadata:{specType:this.specType,schema:i},version:t};try{return await this.catalogEntitiesService.createEntity({entity:f,sourceFile:n.realRelativePath,fileHash:n.hash,rbacTeams:h.length?h:m,source:"file",revision:a})}catch(l){return this.#e.error(`Failed to create data schema entity for ${e}: ${l instanceof Error?l.message:"Unknown error"}`),null}}async#c(e,o,n,s,t,a,r,c){const i=`${t}-${y(`${e.operationId}`)}`,m=await Promise.all([this.#h(e.requestBody??null,s),this.#l(e.responses??{},s)]),h=m[0].schemaNames,f=m[1].schemaNames;m[0].errors.length>0&&this.#e.warn(`Schema extraction errors for operation ${e.operationId}: ${m[0].errors.join(", ")}`),m[1].errors.length>0&&this.#e.warn(`Response schema extraction errors for operation ${e.operationId}: ${m[1].errors.join(", ")}`);const l=h.map(p=>`${t}-${y(p)}`),u=f.map(p=>`${t}-${y(p)}`),g=await this.#p({path:o,method:n,operation:e,requestBodySchemasKeys:l,responseSchemasKeys:u,description:s,parentKey:t,parentVersion:a,parentRevision:r,rbacTeams:c});return g&&await Promise.all([this.#i(h,e.operationId??"",s.realRelativePath,s.hash,t,a,r),this.#m(f,e.operationId??"",s.realRelativePath,s.hash,t,a,r),this.#y({apiOperationKey:g.key,operationRelations:e["x-catalogRelations"],descriptionUniqueKey:t,description:s,parentVersion:a,parentRevision:r})]),i}async#i(e,o,n,s,t,a,r){return e.length===0||!o?[]:await this.#s(e,o,n,s,t,a,r)}async#m(e,o,n,s,t,a,r){return e.length===0||!o?[]:await this.#s(e,o,n,s,t,a,r)}#h(e,o){const n=[],s=[];try{if(e&&"content"in e&&e.content)for(const t of Object.values(e.content)){const a=this.#a(t,o);if(!a)continue;const r=this.#t(a);r?.componentType==="schemas"&&r.componentName&&n.push(r.componentName)}if(e&&e.$ref){const t=this.#t(e.$ref);if(t?.componentType==="requestBodies"&&t.componentName){const a=o.document.components?.requestBodies?.[t.componentName];if(a&&"content"in a)for(const r of Object.values(a.content??{})){const c=this.#a(r,o);if(!c)continue;const i=this.#t(c);i?.componentType==="schemas"&&i.componentName&&n.push(i.componentName)}}}}catch(t){s.push(`Failed to extract request body schemas: ${t instanceof Error?t.message:"Unknown error"}`)}return{schemaNames:[...new Set(n)],errors:s}}#l(e,o){const n=[],s=[];try{for(const t of Object.values(e)){if(t&&"content"in t&&t.content)for(const a of Object.values(t.content)){const r=this.#a(a,o);if(!r)continue;const c=this.#t(r);c?.componentType==="schemas"&&c.componentName&&n.push(c.componentName)}if(t&&t.$ref){const a=this.#t(t.$ref);if(a?.componentType==="responses"&&a.componentName){const r=o.document.components?.responses?.[a.componentName];if(r&&"content"in r)for(const c of Object.values(r.content??{})){const i=this.#a(c,o);if(!i)continue;const m=this.#t(i);m?.componentType==="schemas"&&m.componentName&&n.push(m.componentName)}}}}}catch(t){s.push(`Failed to extract response schemas: ${t instanceof Error?t.message:"Unknown error"}`)}return{schemaNames:[...new Set(n)],errors:s}}#a(e,o){const n=new Set;let s=e;for(;s&&"$ref"in s;){const t=this.#t(s.$ref),a=t?.componentName;if(t?.componentType!=="mediaTypes"||!a||n.has(a))return;n.add(a),s=o.document.components?.mediaTypes?.[a]}return s?.schema?.$ref}#t(e){try{return N(e)}catch{return{componentType:"",componentName:null}}}async#s(e,o,n,s,t,a,r){return e.length===0?[]:await E(e,v,async c=>await this.#f({operationId:o,schemaName:c,descriptionSourceFile:n,descriptionHash:s,parentKey:t,parentVersion:a,parentRevision:r}))}async#f({operationId:e,schemaName:o,descriptionSourceFile:n,descriptionHash:s,parentKey:t,parentVersion:a,parentRevision:r}){const c=`${t}-${y(o)}`;try{return await this.catalogEntitiesService.createEntityRelation({sourceKey:`${t}-${y(e)}`,type:"uses",targetKey:c,sourceFile:n,fileHash:s,sourceVersion:a,targetVersion:a,sourceRevision:r,targetRevision:r}),c}catch(i){throw this.#e.error(`Failed to create relation between operation ${e} and schema ${o}: ${i instanceof Error?i.message:"Unknown error"}`),i}}async#p({path:e,method:o,operation:n,requestBodySchemasKeys:s,responseSchemasKeys:t,description:a,parentKey:r,parentVersion:c,parentRevision:i,rbacTeams:m}){const h=`${n.operationId}`,f=`${r}-${y(h)}`,l=n[R]?$(n[R]):m,u=$(n["x-rbac"]),g={type:"api-operation",key:f,title:h,summary:S(n.summary),tags:n.tags?.filter(p=>d(p)),metadata:{path:e,method:o,payload:s,responses:t},version:c};try{return await this.catalogEntitiesService.createEntity({entity:g,sourceFile:a.realRelativePath,fileHash:a.hash,rbacTeams:u.length>0?u:l,source:"file",revision:i})}catch(p){return this.#e.error(`Failed to create API operation entity for ${h}: ${p instanceof Error?p.message:"Unknown error"}`),null}}async#y({apiOperationKey:e,operationRelations:o,descriptionUniqueKey:n,description:s,parentVersion:t,parentRevision:a}){try{await this.catalogEntitiesService.createEntityRelation({sourceKey:e,type:"partOf",targetKey:n,sourceFile:s.realRelativePath,fileHash:s.hash,sourceVersion:t,targetVersion:t,sourceRevision:a,targetRevision:a}),await this.#u(e,s,o,t,a)}catch(r){this.#e.error(`Failed to create API operation relations for ${e}: ${r instanceof Error?r.message:"Unknown error"}`)}}async#u(e,o,n,s,t){n?.length&&await E(n,T,async a=>{try{this.validateEntityRelationFileSchema(a),await this.catalogEntitiesService.createEntityRelation({sourceKey:e,type:a.type,targetKey:a.key,sourceFile:o.realRelativePath,fileHash:o.hash,sourceVersion:s,targetVersion:"",sourceRevision:t,targetRevision:""})}catch(r){this.context.logger.warn(`Error creating entity relation for operation ${e} based on custom property: ${r instanceof Error?r.message:"Unknown error"}`)}})}}export{V as OpenApiEntitiesExtractor};
@@ -1 +1 @@
1
- function s(a){const n=/^#\/channels\/(?<channelName>.+)\/messages\/(?<messageName>.+)$/,e=a.match(n);return!e||!e.groups?null:{channelName:e?.groups?.channelName??null,messageName:e?.groups?.messageName??null}}export{s as extractPartsFromChannelsMessageRef};
1
+ import{unescapePointerFragment as a}from"@redocly/openapi-core";function m(n){const s=/^#\/channels\/(?<channelName>.+)\/messages\/(?<messageName>.+)$/,e=n.match(s);return!e||!e.groups?null:{channelName:a(e.groups.channelName),messageName:a(e.groups.messageName)}}export{m as extractPartsFromChannelsMessageRef};
@@ -1,4 +1,4 @@
1
- import type { CodeWalkthroughNode, CodeWalkthroughFileMetadata } from '@redocly/config';
1
+ import type { CodeWalkthroughNode, CodeWalkthroughFileMetadata } from '@redocly/theme/core/types';
2
2
  export declare function parseFileChunks(text: string, filePath: string): Promise<{
3
3
  root: CodeWalkthroughNode[];
4
4
  metadata: CodeWalkthroughFileMetadata;
@@ -1 +1 @@
1
- import o from"node:path";import{ASYNC_API_DOCS_TEMPLATE_ID as T,GRAPHQL_TEMPLATE_ID as v,OPENAPI_DOCS_TEMPLATE_ID as y}from"../../../../constants/common.js";import{isLocalLink as D}from"../../../../utils/path/is-local-link.js";import{normalizeRouteSlug as h}from"../../../../utils/path/normalize-route-slug.js";import{removeFragment as F}from"../../../../utils/path/remove-fragment.js";import{getInnerText as M}from"../../../../markdoc/helpers/get-inner-text.js";import{getNodeAttribute as U}from"../../../../markdoc/helpers/get-node-attribute.js";import{setNodeAttributeValue as p}from"../../../../markdoc/helpers/set-node-attribute-value.js";import{isAuthRoutePath as W}from"../../../../utils/auth/is-auth-route-path.js";import{isApiDownloadLink as j}from"../../../utils/is-api-download-link.js";import{MdResolveError as g}from"./md-resolve-error.js";import{getLinkOriginalAttrName as B}from"./utils.js";import{isOpenApiURL as $}from"../../api-docs/utils/is-api-doc.js";import{parseBaseName as C}from"../../utils.js";import{isMarkdownPage as V}from"../is-markdown-page.js";import{copyStaticFile as b}from"../../../utils/fs.js";async function at(i,s,E,{actions:k,context:f}){const{contentDir:d,outdir:S,getRouteByFsPath:w,hasRouteOrRedirectBySlug:A}=k,L=B(s),e=i[L]||U(i,s);if(i[L]=e,!D(e)||j(e)||W(e))return;const[z,t="",R,P]=/^([^?#]+)?([^#]+)?(.+)?/.exec(e)||[],c=i.type==="image"?"IMAGE":"LINK",u=String(i.attributes.title||i.attributes.alt||M([i])||""),n=F(E);if(i.type!=="image"){const{isOpenapiDetected:r,isOpenapiValid:m}=await $(t,n,k,f);if(r&&!m)throw new g(`OpenAPI route ${e} does not exist`,{rawLink:e,link:t,title:u,brokenLinkType:c})}if(o.extname(t)===""){if(t.startsWith("/")){if(!A(h(t)))throw new g(`Route ${t} does not exist`,{rawLink:e,link:t,title:u,brokenLinkType:c});p(i,s,t+(R||"")+(P||""))}if(!e.startsWith("#")&&!t.startsWith("/")&&V(n)){const r=w(n)?.slug;if(!r)return;const{isIndexFile:m}=C(n),x=m?h(o.posix.join(r,t)):h(o.posix.join(r,"../",t));if(!A(h(x)))throw new g(`Route ${t} does not exist`,{rawLink:e,link:x,title:u,brokenLinkType:c});p(i,s,x+(R||"")+(P||""))}return}const a=t.startsWith("/")?t.substring(1):o.posix.join(o.posix.dirname(n),decodeURI(t)),_=t.startsWith("/")?o.posix.join("static",t.substring(1)):"",I=f.fs.exists(a),O=f.fs.exists(_),l=w(a);if(!I&&!l&&!O)throw p(i,s,"#"),new g(`File ${a} does not exist`,{rawLink:e,link:a,title:u,brokenLinkType:c});if(l){const r=[y,v,T].includes(l.templateId);p(i,s,(r?l.baseSlug:l.slug)+(P||""))}else if(I){const r=f.fs.getFileInfo(a);if(!r||r.isVirtual)return;const m=await b(d,r.realRelativePath,S);p(i,s,m)}}export{at as resolveLink};
1
+ import n from"node:path";import{API_DOCS_TEMPLATE_ID as y,ASYNC_API_DOCS_TEMPLATE_ID as F,GRAPHQL_TEMPLATE_ID as M,OPENAPI_DOCS_TEMPLATE_ID as U}from"../../../../constants/common.js";import{isLocalLink as C}from"../../../../utils/path/is-local-link.js";import{normalizeRouteSlug as P}from"../../../../utils/path/normalize-route-slug.js";import{removeFragment as B}from"../../../../utils/path/remove-fragment.js";import{getInnerText as W}from"../../../../markdoc/helpers/get-inner-text.js";import{getNodeAttribute as j}from"../../../../markdoc/helpers/get-node-attribute.js";import{setNodeAttributeValue as c}from"../../../../markdoc/helpers/set-node-attribute-value.js";import{isAuthRoutePath as $}from"../../../../utils/auth/is-auth-route-path.js";import{isApiDownloadLink as V}from"../../../utils/is-api-download-link.js";import{MdResolveError as A}from"./md-resolve-error.js";import{getLinkOriginalAttrName as b}from"./utils.js";import{isOpenApiURL as z}from"../../api-docs/utils/is-api-doc.js";import{parseBaseName as G}from"../../utils.js";import{isMarkdownPage as N}from"../is-markdown-page.js";import{copyStaticFile as q}from"../../../utils/fs.js";async function pt(i,r,p,{actions:w,context:u}){const{contentDir:D,outdir:T,getRouteByFsPath:x,getRouteBySlug:I,hasRouteOrRedirectBySlug:d}=w,k=b(r),o=i[k]||j(i,r);if(i[k]=o,!C(o)||V(o)||$(o))return;const[H,t="",R,L]=/^([^?#]+)?([^#]+)?(.+)?/.exec(o)||[],f=i.type==="image"?"IMAGE":"LINK",g=String(i.attributes.title||i.attributes.alt||W([i])||""),a=B(p);if(i.type!=="image"){const{isOpenapiDetected:e,isOpenapiValid:s}=await z(t,a,w,u);if(e&&!s)throw new A(`OpenAPI route ${o} does not exist`,{rawLink:o,link:t,title:g,brokenLinkType:f})}if(n.extname(t)===""){if(t.startsWith("/")){const e=P(t),s=d(e)?t:_(e,I);if(!s)throw new A(`Route ${t} does not exist`,{rawLink:o,link:t,title:g,brokenLinkType:f});c(i,r,s+(R||"")+(L||""))}if(!o.startsWith("#")&&!t.startsWith("/")&&N(a)){const e=x(a)?.slug;if(!e)return;const{isIndexFile:s}=G(a),h=s?P(n.posix.join(e,t)):P(n.posix.join(e,"../",t)),E=d(P(h))?h:_(h,I);if(!E)throw new A(`Route ${t} does not exist`,{rawLink:o,link:h,title:g,brokenLinkType:f});c(i,r,E+(R||"")+(L||""))}return}const l=t.startsWith("/")?t.substring(1):n.posix.join(n.posix.dirname(a),decodeURI(t)),v=t.startsWith("/")?n.posix.join("static",t.substring(1)):"",S=u.fs.exists(l),O=u.fs.exists(v),m=x(l);if(!S&&!m&&!O)throw c(i,r,"#"),new A(`File ${l} does not exist`,{rawLink:o,link:l,title:g,brokenLinkType:f});if(m){const e=[U,M,F].includes(m.templateId);c(i,r,(e?m.baseSlug:m.slug)+(L||""))}else if(S){const e=u.fs.getFileInfo(l);if(!e||e.isVirtual)return;const s=await q(D,e.realRelativePath,T);c(i,r,s)}}function _(i,r){const p=r(i.toLowerCase());return p?.templateId===y?p.slug:void 0}export{pt as resolveLink};
@@ -1,5 +1,7 @@
1
1
  import type { OpenAPIParameter, Referenced, OpenAPIDefinition } from '@redocly/api-docs';
2
2
  import type { McpEndpoint } from '../../types.js';
3
+ import { resolveRef } from '@redocly/api-docs/lib/utils/inline-openapi-refs.js';
4
+ export { resolveRef };
3
5
  type JsonValue = string | number | boolean | null | JsonValue[] | {
4
6
  [key: string]: JsonValue;
5
7
  };
@@ -10,7 +12,6 @@ export type ResolvedOperation = {
10
12
  operation: JsonObject;
11
13
  components?: Record<string, JsonObject>;
12
14
  };
13
- export declare function resolveRef<T>(ref: string, def?: OpenAPIDefinition): T | undefined;
14
15
  type ResolveOperationParams = {
15
16
  operation: McpEndpoint;
16
17
  pathParams: Referenced<OpenAPIParameter>[];
@@ -22,5 +23,4 @@ export declare function resolveOperation({ operation, pathParams, definition, }:
22
23
  * `maxBytes` or the inliner cannot read the operation.
23
24
  */
24
25
  export declare function inlineOperation({ operation, pathParams, definition }: ResolveOperationParams, maxBytes: number): ResolvedOperation | undefined;
25
- export {};
26
26
  //# sourceMappingURL=resolve-refs.d.ts.map
@@ -1 +1 @@
1
- const S=/^#\/components\/([^/]+)\/([^/]+)(\/.*)?$/;function w(e){return e.replace(/~1/g,"/").replace(/~0/g,"~")}function d(e,s){if(!s||!e?.startsWith("#/"))return;const n=e.slice(2).split("/").map(w);let t=s;for(const r of n)if(typeof t=="object"&&t!==null&&r in t)t=t[r];else return;return t}function h(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function O(e,s){const n={},t=new Set,r=new Set,o=[e];for(;o.length>0;){const c=o.pop();if(!(c===null||typeof c!="object")){if(h(c)&&typeof c.$ref=="string"&&!t.has(c.$ref)){t.add(c.$ref);const[,i,f]=S.exec(c.$ref)??[],u=f&&w(f);if(i&&u&&!r.has(`${i}/${u}`)){const l=d(`#/components/${i}/${f}`,s);l!==void 0&&(r.add(`${i}/${u}`),(n[i]??={})[u]=l,o.push(l))}}for(const i of Object.values(c))o.push(i)}}return Object.keys(n).length>0?n:void 0}function R(e,s){const n=h(e)&&typeof e.$ref=="string"?d(e.$ref,s)??e:e,{name:t,in:r}=h(n)?n:{};return typeof t=="string"&&typeof r=="string"?`${r}:${t}`:JSON.stringify(e)}function g(e,s,n){const t=new Map;for(const r of[...e,...s])r&&t.set(R(r,n),r);return[...t.values()]}function C({operation:e,pathParams:s,definition:n}){const t={...e,parameters:g(s,e.parameters??[],n)};return{operation:t,components:O(t,n)}}class E extends Error{}function j(e,s){if(e.remainingBytes-=s,e.remainingBytes<0)throw new E}function $(e,s){return j(e,JSON.stringify(s).length),s}function m(e,s,n){const t=d(e,s);if(t===void 0)return;const r=n.bytesByRef.get(e)??JSON.stringify(t).length;return n.bytesByRef.set(e,r),j(n,r),t}function y(e,s,n){const t=$(n,Object.fromEntries(Object.entries(e).filter(([r,o])=>r!=="$ref"&&o!==void 0)));return{...s,...t}}function a({schema:e,definition:s,budget:n,visitedRefs:t=new Set,maxDepth:r=50,currentDepth:o=0}){if(!e)return;if(o>=r)return $(n,{type:"object",description:`Maximum resolution depth exceeded (${r})`});if("$ref"in e&&e.$ref){if(t.has(e.$ref))return $(n,{type:"object",description:`Circular reference detected: ${e.$ref}`,"x-circular-ref":e.$ref});const f=new Set(t);f.add(e.$ref);const u=m(e.$ref,s,n);if(!u)return $(n,{type:"object",description:`Could not resolve reference: ${e.$ref}`,"x-unresolved-ref":e.$ref});const l=a({schema:u,definition:s,budget:n,visitedRefs:f,maxDepth:r,currentDepth:o+1});return l&&y(e,l,n)}const c={...e};if(c.properties){const f={};for(const[u,l]of Object.entries(c.properties)){const p=a({schema:l,definition:s,budget:n,visitedRefs:new Set(t),maxDepth:r,currentDepth:o+1});f[u]=p||l}c.properties=f}if(c.items&&typeof c.items=="object")if(Array.isArray(c.items))c.items=c.items.map(f=>a({schema:f,definition:s,budget:n,visitedRefs:new Set(t),maxDepth:r,currentDepth:o+1})||f);else{const f=a({schema:c.items,definition:s,budget:n,visitedRefs:new Set(t),maxDepth:r,currentDepth:o+1});f&&(c.items=f)}return["allOf","anyOf","oneOf"].forEach(f=>{const u=c[f];Array.isArray(u)&&(c[f]=u.reduce((l,p)=>{const v=a({schema:p,definition:s,budget:n,visitedRefs:new Set(t),maxDepth:r,currentDepth:o+1});return v&&l.push(v),l},[]))}),c}function P({pathParams:e,opParams:s,definition:n,budget:t}){return[...e,...s].reduce((r,o)=>{if(!o)return r;if("$ref"in o&&o.$ref){const c=m(o.$ref,n,t);if(!c)return r;const i=y(o,c,t);return r.push({...i,schema:"schema"in i&&i.schema?a({schema:i.schema,definition:n,budget:t}):void 0}),r}return r.push({...o,schema:"schema"in o&&o.schema?a({schema:o.schema,definition:n,budget:t}):void 0}),r},[])}function x(e,s,n){const t={...e};if(e.example!==void 0&&(t.example=e.example),!e.examples)return t;const r={};for(const[o,c]of Object.entries(e.examples))if("$ref"in c){const i=m(c.$ref,s,n);i&&(r[o]=i)}else r[o]=c;return t.examples=r,t}function A(e,s,n){if(!e)return;const t="$ref"in e&&e.$ref?m(e.$ref,s,n):e;if(!t)return;const r=y(e,t,n),o={...r};if("content"in r&&r.content){const c={};for(const[i,f]of Object.entries(r.content))c[i]={...x(f,s,n),schema:f.schema?a({schema:f.schema,definition:s,budget:n}):void 0};o.content=c}return o}function B(e,s,n){if(e)return Object.entries(e).reduce((t,[r,o])=>{const c="$ref"in o&&o.$ref?m(o.$ref,s,n):o;if(!c)return t;const i=y(o,c,n),f={...i};if("content"in i&&i.content){const u={};for(const[l,p]of Object.entries(i.content))u[l]={...x(p,s,n),schema:p.schema?a({schema:p.schema,definition:s,budget:n}):void 0};f.content=u}return t[r]=f,t},{})}function N({operation:e,pathParams:s,definition:n},t){const r={remainingBytes:t,bytesByRef:new Map};try{const o={...e,parameters:P({pathParams:s,opParams:e.parameters||[],definition:n,budget:r}),requestBody:A(e.requestBody,n,r),responses:B(e.responses,n,r)};return{operation:o,components:O(o,n)}}catch{return}}export{N as inlineOperation,C as resolveOperation,d as resolveRef};
1
+ import{resolveParameters as y,resolveRef as p,resolveRequestBody as g,resolveResponses as m,unescapeJsonPointerToken as $}from"@redocly/api-docs/lib/utils/inline-openapi-refs.js";const h=/^#\/components\/([^/]+)\/([^/]+)(\/.*)?$/;function d(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function l(e,o){const n={},s=new Set,t=new Set,r=[e];for(;r.length>0;){const c=r.pop();if(!(c===null||typeof c!="object")){if(d(c)&&typeof c.$ref=="string"&&!s.has(c.$ref)){s.add(c.$ref);const[,f,i]=h.exec(c.$ref)??[],u=i&&$(i);if(f&&u&&!t.has(`${f}/${u}`)){const a=p(`#/components/${f}/${i}`,o);a!==void 0&&(t.add(`${f}/${u}`),(n[f]??={})[u]=a,r.push(a))}}for(const f of Object.values(c))r.push(f)}}return Object.keys(n).length>0?n:void 0}function b(e,o){const n=d(e)&&typeof e.$ref=="string"?p(e.$ref,o)??e:e,{name:s,in:t}=d(n)?n:{};return typeof s=="string"&&typeof t=="string"?`${t}:${s}`:JSON.stringify(e)}function O(e,o,n){const s=new Map;for(const t of[...e,...o])t&&s.set(b(t,n),t);return[...s.values()]}function x({operation:e,pathParams:o,definition:n}){const s={...e,parameters:O(o,e.parameters??[],n)};return{operation:s,components:l(s,n)}}class w extends Error{}function B(e){let o=e;const n=new Map;return(s,t)=>{let r=t===void 0?void 0:n.get(t);if(r===void 0&&(r=JSON.stringify(s).length,t!==void 0&&n.set(t,r)),o-=r,o<0)throw new w}}function R({operation:e,pathParams:o,definition:n},s){const t=B(s);try{const r={...e,parameters:y({pathParams:o,opParams:e.parameters||[],definition:n,charge:t}),requestBody:g(e.requestBody,n,t),responses:e.responses&&m(e.responses,n,t)};return{operation:r,components:l(r,n)}}catch{return}}export{R as inlineOperation,x as resolveOperation,p as resolveRef};
@@ -8,6 +8,7 @@ export type CredentialTarget = {
8
8
  name: string;
9
9
  scheme: string;
10
10
  schemeLabel?: string;
11
+ acceptedSchemes?: string[];
11
12
  };
12
13
  /**
13
14
  * Why the prompt is being raised. `authFailure` is the automatic path behind an upstream 401;
@@ -1,5 +1,5 @@
1
- import{inputRequired as h,inputResponse as y}from"@modelcontextprotocol/server";import{withPathPrefix as I}from"@redocly/theme/core/utils";import{MCP_CREDENTIALS_ROUTE as A}from"../../../constants/mcp.js";import{MANAGE_API_CREDENTIALS_TOOL_NAME as u}from"../constants.js";import{secretStore as l,ELICITATION_TTL_MS as P}from"./secret-store.js";import{operationsForScheme as E,orderedSchemesOf as g}from"./upstream-auth-scheme.js";import{ANONYMOUS_SUBJECT_PREFIX as p}from"./subject.js";const m="credentials",d=new Map;function F(e,t){const r=d.get(e);if(r)return r;const n=t().finally(()=>{d.delete(e)});return d.set(e,n),n}function D(e){return!!e?.elicitation?.url}function H(e){return e.envelope!==void 0}function q(e){const t=y(e,m);if(t.kind==="elicit")return t.action==="accept"?"accepted":"declined"}function B(e,t){return[`The user accepted the prompt to add credentials for the "${e}" API.`,...t?["The credentials page:",t]:[],"",`Wait until the user says they saved the credentials, then retry the call that needed them. Do not call \`${u}\` again unless the user asks for a new link.`].join(`
2
- `)}async function Y(e,{kv:t,subject:r,baseUrl:n,apis:o}){if(typeof e!="string")return;const i=await l.peekElicitation(t,e);if(!(i?.subject!==r||!o.includes(i.api)))return{apiName:i.name,url:T(n,e)}}function W(e,t){return Object.keys(e).length>1?t:void 0}async function z(e){const{token:t,url:r}=await f(e);return h({inputRequests:{[m]:h.elicitUrl({message:C(e),url:r})},requestState:t})}async function G(e){const{url:t}=await f(e);return _(e,t,S(e.subject))}function J(e,t){const r=b(e);if(e.status===401&&!e.sentScheme){const[i,s]=e.callerHeader?[`Your code set the "${e.callerHeader}" header itself, so the stored credential was not sent.`,"Remove that header from the request and retry. Do not replace the stored credentials."]:["No stored credential was sent with this request, so this result says nothing about the stored credentials.","Do not replace them. Check whether this operation needs a different kind of credential or a different route."];return[`The "${e.name}" API returned ${r}. ${i}`,"",s].join(`
3
- `)}const n=e.status===401?`The "${e.name}" API rejected the stored ${t?`"${t}" `:""}credential (${r}).`:`The "${e.name}" API rejected the request with ${r}.`,o=t?`api: "${e.api}" and scheme: "${t}"`:`api: "${e.api}"`;return[n,"",`Do not retry the same call. Call \`${u}\` with ${o} to get a link the user has to open in a browser to replace or remove the stored credentials, then retry once they confirm they saved them.`].join(`
4
- `)}function b({status:e,detail:t}){return t?`HTTP ${e}: ${t}`:`HTTP ${e}`}async function f({kv:e,subject:t,baseUrl:r,target:n,auth:o,apiAuth:i,hasExistingSecret:s}){const a=i?await k(e,t,n.api,i):[],c=await l.createElicitation(e,{subject:t,api:n.api,name:n.name,scheme:n.scheme,...n.schemeLabel?{schemeLabel:n.schemeLabel}:{},auth:o,hasExistingSecret:s,...a.length>1?{schemes:a}:{}});return{token:c,url:T(r,c)}}async function k(e,t,r,n){const i=(await l.getSecretsForSubject(e,t))[r]??{};return g(n.schemes).map(s=>{const a=E(n,s),c=n.descriptions?.[s];return{name:s,...c?{label:c}:{},auth:n.schemes[s],hasExistingSecret:!!i[s],...a.length>0?{operations:a}:{}}})}function S(e){return e.startsWith(p)?e.slice(p.length):void 0}function C({target:e,hasExistingSecret:t,reason:r}){const n=`The link works for ${$()} minutes.`,o=x(e),i=r.kind==="authFailure"&&r.detail?` The API answered: ${r.detail}.`:"";return t?`Open this page in a browser to replace or remove the stored credentials for ${o}. They go straight to the API and never enter the chat. ${n}`:`${O(o)} needs credentials.${i} Open this page in a browser to paste them - they go straight to the API and never enter the chat. ${n}`}function x({name:e,schemeLabel:t}){return t?`the "${t}" scheme of the "${e}" API`:`the "${e}" API`}function O(e){return e.charAt(0).toUpperCase()+e.slice(1)}function $(){return Math.round(P/6e4)}function T(e,t){const r=I(A);if(!e)return`${r}?token=${t}`;try{const n=new URL(r,e);return n.searchParams.set("token",t),n.toString()}catch{return`${e}${r}?token=${t}`}}function _({target:e,reason:t,hasExistingSecret:r},n,o){const s=[`**Stop and show the user this link. They have to open it in a browser to ${r?`replace or remove the stored credentials for the "${e.name}" API`:`add credentials for the "${e.name}" API`} - nothing continues until they do.**`,"",n,"",`The link is valid ${$()} minutes and opens a form on this site. Credentials go straight to the API from there: never ask for them in the conversation and never pass them as a tool argument.`,"",t.kind==="authFailure"?`The upstream API "${e.name}" returned ${t.status}${t.detail?` (${t.detail})`:""} and needs credentials.`:r?`Credentials are already stored for the "${e.name}" API; the link lets the user replace or remove them.`:`The "${e.name}" API needs credentials.`,...e.schemeLabel?[`The operation authenticates with its "${e.schemeLabel}" security scheme.`]:[],o?`Once the user confirms they saved them, call this tool again with sessionHandle: "${o}" - the credentials are stored against that handle, applied server-side, and never appear in the conversation.`:"Once the user confirms they saved them, call this tool again - the credentials are applied server-side and never appear in the conversation."],a=t.kind==="authFailure"?t.otherApiNames??[]:[];if(a.length>0){const c=a.map(w=>`"${w}"`).join(", ");s.push("",`The remaining APIs (${c}) will prompt for credentials on subsequent calls.`)}return s.join(`
5
- `)}export{m as CREDENTIALS_INPUT_REQUEST_KEY,Y as acceptedCredentialsLink,B as buildCredentialsAcceptedHint,J as buildCredentialsToolHint,G as buildUpstreamCredentialFallback,z as buildUpstreamCredentialInputRequired,q as credentialElicitationAnswer,F as dedupeElicitation,H as isStatelessRequest,W as schemeLabelOf,D as supportsUrlElicitation};
1
+ import{inputRequired as d,inputResponse as w}from"@modelcontextprotocol/server";import{withPathPrefix as I}from"@redocly/theme/core/utils";import{MCP_CREDENTIALS_ROUTE as E}from"../../../constants/mcp.js";import{MANAGE_API_CREDENTIALS_TOOL_NAME as h}from"../constants.js";import{secretStore as l,ELICITATION_TTL_MINUTES as u}from"./secret-store.js";import{operationsForScheme as A,orderedSchemesOf as P}from"./upstream-auth-scheme.js";import{ANONYMOUS_SUBJECT_PREFIX as p}from"./subject.js";const m="credentials",c=new Map;function D(e,t){const n=c.get(e);if(n)return n;const r=t().finally(()=>{c.delete(e)});return c.set(e,r),r}function F(e){return!!e?.elicitation?.url}function H(e){return e.envelope!==void 0}function M(e){const t=w(e,m);if(t.kind==="elicit")return t.action==="accept"?"accepted":"declined"}function q(e,t){return[`The user accepted the prompt to add credentials for the "${e}" API.`,...t?["The credentials page:",t]:[],"",`Wait until the user says they saved the credentials, then retry the call that needed them. Do not call \`${h}\` again unless the user asks for a new link.`].join(`
2
+ `)}async function Y(e,{kv:t,subject:n,baseUrl:r,apis:i}){if(typeof e!="string")return;const s=await l.peekElicitation(t,e);if(!(s?.subject!==n||!i.includes(s.api)))return{apiName:s.name,url:$(r,e)}}function B(e,t){return Object.keys(e).length>1?t:void 0}async function W(e){const{token:t,url:n}=await f(e);return d({inputRequests:{[m]:d.elicitUrl({message:C(e),url:n})},requestState:t})}async function z(e){const{url:t}=await f(e);return g(e,t,S(e.subject))}function G(e,t){const n=b(e);if(e.status===401&&!e.sentScheme){const[s,a]=e.callerHeader?[`Your code set the "${e.callerHeader}" header itself, so the stored credential was not sent.`,"Remove that header from the request and retry. Do not replace the stored credentials."]:["No stored credential was sent with this request, so this result says nothing about the stored credentials.","Do not replace them. Check whether this operation needs a different kind of credential or a different route."];return[`The "${e.name}" API returned ${n}. ${s}`,"",a].join(`
3
+ `)}const r=e.status===401?`The "${e.name}" API rejected the stored ${t?`"${t}" `:""}credential (${n}).`:`The "${e.name}" API rejected the request with ${n}.`,i=t?`api: "${e.api}" and scheme: "${t}"`:`api: "${e.api}"`;return[r,"",`Do not retry the same call. Call \`${h}\` with ${i} to get a link the user has to open in a browser to replace or remove the stored credentials, then retry once they confirm they saved them.`].join(`
4
+ `)}function b({status:e,detail:t}){return t?`HTTP ${e}: ${t}`:`HTTP ${e}`}async function f({kv:e,subject:t,baseUrl:n,target:r,auth:i,apiAuth:s}){const a=s?k(s):[],o=await l.createElicitation(e,{subject:t,api:r.api,name:r.name,scheme:r.scheme,...r.schemeLabel?{schemeLabel:r.schemeLabel}:{},auth:i,...a.length>1?{schemes:a}:{},...a.length>1&&r.acceptedSchemes?{acceptedSchemes:r.acceptedSchemes}:{}});return{token:o,url:$(n,o)}}function k(e){return P(e.schemes).map(t=>{const n=A(e,t),r=e.descriptions?.[t];return{name:t,...r?{label:r}:{},auth:e.schemes[t],...n.length>0?{operations:n}:{}}})}function S(e){return e.startsWith(p)?e.slice(p.length):void 0}function C({target:e,hasExistingSecret:t,reason:n}){const r=`The link works for ${u} minutes.`,i=x(e),s=n.kind==="authFailure"&&n.detail?` The API answered: ${n.detail}.`:"";return t?`Open this page in a browser to replace or remove the stored credentials for ${i}. They stay out of the chat. ${r}`:`${O(i)} needs credentials.${s} Open this page in a browser to paste them - they stay out of the chat. ${r}`}function x({name:e,schemeLabel:t}){return t?`the "${t}" scheme of the "${e}" API`:`the "${e}" API`}function O(e){return e.charAt(0).toUpperCase()+e.slice(1)}function $(e,t){const n=I(E);if(!e)return`${n}?token=${t}`;try{const r=new URL(n,e);return r.searchParams.set("token",t),r.toString()}catch{return`${e}${n}?token=${t}`}}function g({target:e,reason:t,hasExistingSecret:n},r,i){const a=[`**Stop and show the user this link. They have to open it in a browser to ${n?`replace or remove the stored credentials for the "${e.name}" API`:`add credentials for the "${e.name}" API`} - nothing continues until they do.**`,"",r,"",`The link is valid ${u} minutes and opens a form on this site. The site stores the credentials and adds them to API calls: never ask for them in the conversation and never pass them as a tool argument.`,"",t.kind==="authFailure"?`The upstream API "${e.name}" returned ${t.status}${t.detail?` (${t.detail})`:""} and needs credentials.`:n?`Credentials are already stored for the "${e.name}" API; the link lets the user replace or remove them.`:`The "${e.name}" API needs credentials.`,...e.schemeLabel?[`The operation authenticates with its "${e.schemeLabel}" security scheme.`]:[],i?`Once the user confirms they saved them, retry the call that needed them with sessionHandle: "${i}" - the credentials are stored against that handle, applied server-side, and never appear in the conversation.`:"Once the user confirms they saved them, retry the call that needed them - the credentials are applied server-side and never appear in the conversation."],o=t.kind==="authFailure"?t.otherApiNames??[]:[];if(o.length>0){const T=o.map(y=>`"${y}"`).join(", ");a.push("",`The remaining APIs (${T}) will prompt for credentials on subsequent calls.`)}return a.join(`
5
+ `)}export{m as CREDENTIALS_INPUT_REQUEST_KEY,Y as acceptedCredentialsLink,q as buildCredentialsAcceptedHint,G as buildCredentialsToolHint,z as buildUpstreamCredentialFallback,W as buildUpstreamCredentialInputRequired,M as credentialElicitationAnswer,D as dedupeElicitation,H as isStatelessRequest,B as schemeLabelOf,F as supportsUrlElicitation};
@@ -1 +1 @@
1
- import{envConfig as o}from"../../../../config/env-config.js";import{KvRepository as s}from"../../../../persistence/kv/repositories/kv-repository.js";import{logger as m}from"../../../../tools/notifiers/logger.js";import{ipRateLimitKey as p}from"./ip-key.js";import{checkEdgeRateLimit as c}from"./edge-limiter.js";import{resolveGatewayFetchLimits as d}from"./limits.js";import{reportMcpRateLimited as f}from"./rate-limit-telemetry.js";async function S(n,a){const i=p(n);if(!i)return;let e,t;try{const r=await s.create({baseDbDir:a.serverOutDir,sqldRemoteDatabaseUrl:o.SQLD_REMOTE_DATABASE_URL,sqldRemoteDatabaseAuthToken:o.SQLD_REMOTE_DATABASE_AUTH_TOKEN});t=d(),e=await c({kv:r,ip:i,limits:t,now:Date.now()})}catch(r){m.warn(`gateway rate-limit init error, failing open: ${String(r)}`);return}if(!e.allowed)return f({scope:e.scope,retryAfterSeconds:e.retryAfterSeconds,limit:e.scope==="rate"?t.ratePerMin:t.quotaPerDay}),new Response(JSON.stringify({jsonrpc:"2.0",error:{code:-32029,message:`Rate limit exceeded (${e.scope}); retry after ${e.retryAfterSeconds}s`},id:null}),{status:429,headers:{"Content-Type":"application/json","Retry-After":String(e.retryAfterSeconds)}})}export{S as applyEdgeRateLimit};
1
+ import{envConfig as o}from"../../../../config/env-config.js";import{isRealmOrReef as s}from"../../../../utils/is-realm-or-reef.js";import{KvRepository as m}from"../../../../persistence/kv/repositories/kv-repository.js";import{logger as f}from"../../../../tools/notifiers/logger.js";import{ipRateLimitKey as p}from"./ip-key.js";import{checkEdgeRateLimit as c}from"./edge-limiter.js";import{resolveGatewayFetchLimits as d}from"./limits.js";import{reportMcpRateLimited as l}from"./rate-limit-telemetry.js";async function E(n,a){if(!s())return;const i=p(n);if(!i)return;let e,r;try{const t=await m.create({baseDbDir:a.serverOutDir,sqldRemoteDatabaseUrl:o.SQLD_REMOTE_DATABASE_URL,sqldRemoteDatabaseAuthToken:o.SQLD_REMOTE_DATABASE_AUTH_TOKEN});r=d(),e=await c({kv:t,ip:i,limits:r,now:Date.now()})}catch(t){f.warn(`gateway rate-limit init error, failing open: ${String(t)}`);return}if(!e.allowed)return l({scope:e.scope,retryAfterSeconds:e.retryAfterSeconds,limit:e.scope==="rate"?r.ratePerMin:r.quotaPerDay}),new Response(JSON.stringify({jsonrpc:"2.0",error:{code:-32029,message:`Rate limit exceeded (${e.scope}); retry after ${e.retryAfterSeconds}s`},id:null}),{status:429,headers:{"Content-Type":"application/json","Retry-After":String(e.retryAfterSeconds)}})}export{E as applyEdgeRateLimit};
@@ -1,5 +1,6 @@
1
1
  import type { KvService } from '@redocly/config';
2
2
  import type { UpstreamAuthScheme } from './upstream-auth-scheme.js';
3
+ export declare const ELICITATION_TTL_MINUTES = 15;
3
4
  export declare const ELICITATION_TTL_MS: number;
4
5
  export type StoredSecret = {
5
6
  header: string;
@@ -11,7 +12,6 @@ export type ElicitationScheme = {
11
12
  name: string;
12
13
  label?: string;
13
14
  auth: UpstreamAuthScheme;
14
- hasExistingSecret: boolean;
15
15
  operations?: string[];
16
16
  };
17
17
  export type ElicitationTarget = {
@@ -21,17 +21,23 @@ export type ElicitationTarget = {
21
21
  scheme: string;
22
22
  schemeLabel?: string;
23
23
  auth: UpstreamAuthScheme;
24
- hasExistingSecret?: boolean;
25
24
  schemes?: ElicitationScheme[];
25
+ /** The schemes the failing operation accepts, when it accepts more than one. */
26
+ acceptedSchemes?: string[];
27
+ expiresAt: number;
26
28
  };
29
+ export type StoredSchemes = Record<string, {
30
+ savedAt?: number;
31
+ }>;
27
32
  export declare class SecretStore {
28
33
  #private;
29
34
  setSecret(kv: KvService, subject: string, api: string, scheme: string, secret: StoredSecret): Promise<void>;
30
35
  getSecretsForSubject(kv: KvService, subject: string): Promise<UpstreamSecrets>;
36
+ getStoredSchemes(kv: KvService, subject: string, api: string): Promise<StoredSchemes>;
31
37
  hasSecret(kv: KvService, subject: string, api: string, scheme: string): Promise<boolean>;
32
38
  deleteSecret(kv: KvService, subject: string, api: string, scheme: string): Promise<void>;
33
39
  deleteSubject(kv: KvService, subject: string): Promise<void>;
34
- createElicitation(kv: KvService, target: ElicitationTarget): Promise<string>;
40
+ createElicitation(kv: KvService, target: Omit<ElicitationTarget, 'expiresAt'>): Promise<string>;
35
41
  peekElicitation(kv: KvService, token: string): Promise<ElicitationTarget | undefined>;
36
42
  consumeElicitation(kv: KvService, token: string): Promise<ElicitationTarget | undefined>;
37
43
  }
@@ -1 +1 @@
1
- import{createCipheriv as h,createDecipheriv as m,createHash as d,hkdfSync as o,randomBytes as l,randomUUID as p}from"node:crypto";import{JWT_SECRET_KEY as f}from"../../../constants/common.js";import{assertHeaderSafe as y}from"./credential-validation.js";import{secretTtlSeconds as S}from"./subject.js";const g=900*1e3,I="mcp-upstream-secrets",T="mcp-elicitation",w="mcp-elicitation";class A{async setSecret(t,e,r,s,i){y(i.header,i.value);const n={...i,savedAt:Date.now()},a={api:r,scheme:s,...this.#o(this.#c(e),n,this.#n(r,s))};await t.set(this.#t(e,r,s),a,{ttlInSeconds:S(e)})}async getSecretsForSubject(t,e){const r=this.#c(e),{items:s}=await t.list({prefix:this.#i(e)}),i={};for(const n of s){if(!n.value?.scheme)continue;const{api:a,scheme:c}=n.value;try{const u=this.#s(r,n.value,this.#n(a,c));i[a]={...i[a],[c]:u}}catch{continue}}return i}async hasSecret(t,e,r,s){return!!await t.get(this.#t(e,r,s))}async deleteSecret(t,e,r,s){await t.delete(this.#t(e,r,s))}async deleteSubject(t,e){const{items:r}=await t.list({prefix:this.#i(e)}),s=r.flatMap(i=>i.value?[i.key]:[]);s.length!==0&&await t.transaction(async i=>{for(const n of s)await i.delete(n)})}async createElicitation(t,e){const r=p();return await t.set(this.#e(r),this.#o(this.#r(),e),{ttlInSeconds:g/1e3}),r}async peekElicitation(t,e){const r=await t.get(this.#e(e));if(r)try{return this.#s(this.#r(),r)}catch{return}}async consumeElicitation(t,e){const r=this.#e(e),s=await t.transaction(async i=>{const n=await i.get(r);return n&&await i.delete(r),n});if(s)try{return this.#s(this.#r(),s)}catch{return}}#i(t){return["mcp","secret",this.#a(t)]}#t(t,e,r){return["mcp","secret",this.#a(t),e,r]}#n(t,e){return`${t}\0${e}`}#e(t){return["mcp","elicitation",t]}#a(t){return d("sha256").update(t).digest("hex")}#c(t){return Buffer.from(o("sha256",f,t,I,32))}#r(){return Buffer.from(o("sha256",f,w,T,32))}#o(t,e,r){const s=l(12),i=h("aes-256-gcm",t,s);r&&i.setAAD(Buffer.from(r,"utf8"));const n=Buffer.concat([i.update(JSON.stringify(e),"utf8"),i.final()]);return{iv:s.toString("base64"),tag:i.getAuthTag().toString("base64"),data:n.toString("base64")}}#s(t,e,r){const s=m("aes-256-gcm",t,Buffer.from(e.iv,"base64"));r&&s.setAAD(Buffer.from(r,"utf8")),s.setAuthTag(Buffer.from(e.tag,"base64"));const i=Buffer.concat([s.update(Buffer.from(e.data,"base64")),s.final()]).toString("utf8");return JSON.parse(i)}}const N=new A;export{g as ELICITATION_TTL_MS,A as SecretStore,N as secretStore};
1
+ import{createCipheriv as d,createDecipheriv as l,createHash as p,hkdfSync as f,randomBytes as y,randomUUID as S}from"node:crypto";import{JWT_SECRET_KEY as u}from"../../../constants/common.js";import{assertHeaderSafe as I}from"./credential-validation.js";import{secretTtlSeconds as T}from"./subject.js";const g=15,h=g*60*1e3,w="mcp-upstream-secrets",A="mcp-elicitation",E="mcp-elicitation";class v{async setSecret(t,e,r,s,n){I(n.header,n.value);const i={...n,savedAt:Date.now()},a={api:r,scheme:s,...this.#f(this.#o(e),i,this.#a(r,s))};await t.set(this.#e(e,r,s),a,{ttlInSeconds:T(e)})}async getSecretsForSubject(t,e){return this.#i(t,e,this.#t(e))}async getStoredSchemes(t,e,r){const s=[...this.#t(e),r],n=(await this.#i(t,e,s))[r]??{};return Object.fromEntries(Object.entries(n).map(([i,{savedAt:a}])=>[i,{savedAt:a}]))}async#i(t,e,r){const s=this.#o(e),{items:n}=await t.list({prefix:r}),i={};for(const a of n){if(!a.value?.scheme)continue;const{api:c,scheme:o}=a.value;try{const m=this.#n(s,a.value,this.#a(c,o));i[c]={...i[c],[o]:m}}catch{continue}}return i}async hasSecret(t,e,r,s){return!!await t.get(this.#e(e,r,s))}async deleteSecret(t,e,r,s){await t.delete(this.#e(e,r,s))}async deleteSubject(t,e){const{items:r}=await t.list({prefix:this.#t(e)}),s=r.flatMap(n=>n.value?[n.key]:[]);s.length!==0&&await t.transaction(async n=>{for(const i of s)await n.delete(i)})}async createElicitation(t,e){const r=S(),s={...e,expiresAt:Date.now()+h};return await t.set(this.#r(r),this.#f(this.#s(),s),{ttlInSeconds:h/1e3}),r}async peekElicitation(t,e){const r=await t.get(this.#r(e));if(r)try{return this.#n(this.#s(),r)}catch{return}}async consumeElicitation(t,e){const r=this.#r(e),s=await t.transaction(async n=>{const i=await n.get(r);return i&&await n.delete(r),i});if(s)try{return this.#n(this.#s(),s)}catch{return}}#t(t){return["mcp","secret",this.#c(t)]}#e(t,e,r){return["mcp","secret",this.#c(t),e,r]}#a(t,e){return`${t}\0${e}`}#r(t){return["mcp","elicitation",t]}#c(t){return p("sha256").update(t).digest("hex")}#o(t){return Buffer.from(f("sha256",u,t,w,32))}#s(){return Buffer.from(f("sha256",u,E,A,32))}#f(t,e,r){const s=y(12),n=d("aes-256-gcm",t,s);r&&n.setAAD(Buffer.from(r,"utf8"));const i=Buffer.concat([n.update(JSON.stringify(e),"utf8"),n.final()]);return{iv:s.toString("base64"),tag:n.getAuthTag().toString("base64"),data:i.toString("base64")}}#n(t,e,r){const s=l("aes-256-gcm",t,Buffer.from(e.iv,"base64"));r&&s.setAAD(Buffer.from(r,"utf8")),s.setAuthTag(Buffer.from(e.tag,"base64"));const n=Buffer.concat([s.update(Buffer.from(e.data,"base64")),s.final()]).toString("utf8");return JSON.parse(n)}}const K=new v;export{g as ELICITATION_TTL_MINUTES,h as ELICITATION_TTL_MS,v as SecretStore,K as secretStore};
@@ -1 +1 @@
1
- import{randomBytes as r}from"node:crypto";const i=32,S=/^[A-Za-z0-9_-]{43}$/,c=1440*60,u=720*60*60,s="anon:",d="user:",f="This API requires credentials and the caller is anonymous; call createAnonymousSession first, then retry with sessionHandle",a="The sessionHandle is not a valid handle; call createAnonymousSession to obtain one";function N(){return r(i).toString("base64url")}function E(e){return S.test(e)}function l(e){if(!e?.isAuthenticated)return;const n=e.claims,o=(typeof n?.sub=="string"&&n.sub?n.sub:void 0)??(e.email||void 0);if(o)return`${d}${e.idpId??"local"}:${o}`}function A(e,n){const t=l(e);return t?{kind:"resolved",subject:t}:n===void 0?{kind:"anonymous"}:E(n)?{kind:"resolved",subject:`${s}${n}`}:{kind:"invalid",message:a}}function T(e){return e.startsWith(s)?c:u}export{s as ANONYMOUS_SUBJECT_PREFIX,a as MALFORMED_SESSION_HANDLE_MESSAGE,f as MISSING_SESSION_HANDLE_MESSAGE,d as USER_SUBJECT_PREFIX,N as createSessionHandle,E as isValidSessionHandle,A as resolveSecretSubject,T as secretTtlSeconds,l as userSubject};
1
+ import{randomBytes as r}from"node:crypto";const i=32,S=/^[A-Za-z0-9_-]{43}$/,c=1440*60,u=10080*60,s="anon:",d="user:",f="This API requires credentials and the caller is anonymous; call createAnonymousSession first, then retry with sessionHandle",a="The sessionHandle is not a valid handle; call createAnonymousSession to obtain one";function N(){return r(i).toString("base64url")}function E(e){return S.test(e)}function l(e){if(!e?.isAuthenticated)return;const n=e.claims,o=(typeof n?.sub=="string"&&n.sub?n.sub:void 0)??(e.email||void 0);if(o)return`${d}${e.idpId??"local"}:${o}`}function A(e,n){const t=l(e);return t?{kind:"resolved",subject:t}:n===void 0?{kind:"anonymous"}:E(n)?{kind:"resolved",subject:`${s}${n}`}:{kind:"invalid",message:a}}function T(e){return e.startsWith(s)?c:u}export{s as ANONYMOUS_SUBJECT_PREFIX,a as MALFORMED_SESSION_HANDLE_MESSAGE,f as MISSING_SESSION_HANDLE_MESSAGE,d as USER_SUBJECT_PREFIX,N as createSessionHandle,E as isValidSessionHandle,A as resolveSecretSubject,T as secretTtlSeconds,l as userSubject};
@@ -0,0 +1,7 @@
1
+ export declare const FORM_ACTIONS: {
2
+ readonly save: 'save';
3
+ readonly remove: 'remove';
4
+ readonly removeAll: 'remove-all';
5
+ readonly decline: 'decline';
6
+ };
7
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ const e={save:"save",remove:"remove",removeAll:"remove-all",decline:"decline"};export{e as FORM_ACTIONS};
@@ -0,0 +1,5 @@
1
+ import type { ElicitationScheme, ElicitationTarget } from '../../gateway-mcp/secret-store.js';
2
+ import type { SchemeField } from './types.js';
3
+ export declare function schemeFieldName(field: SchemeField, schemeIndex: number): string;
4
+ export declare function credentialSchemesOf(target: ElicitationTarget): ElicitationScheme[];
5
+ //# sourceMappingURL=form-contract.d.ts.map
@@ -0,0 +1 @@
1
+ function c(e,n){return`${e}__${n}`}function h(e){return e.schemes??[{name:e.scheme,auth:e.auth}]}export{h as credentialSchemesOf,c as schemeFieldName};
@@ -0,0 +1,14 @@
1
+ import type { ElicitationTarget, StoredSchemes } from '../../gateway-mcp/secret-store.js';
2
+ import type { FormError, PageContent, PageSite } from './types.js';
3
+ type FormPageInput = {
4
+ site: PageSite;
5
+ target: ElicitationTarget;
6
+ token: string;
7
+ stored: StoredSchemes;
8
+ error?: FormError;
9
+ /** Client IDs to put back into the OAuth2 fields after a rejected submission, by scheme index. */
10
+ clientIds?: readonly string[];
11
+ };
12
+ export declare function formPage(input: FormPageInput): PageContent;
13
+ export {};
14
+ //# sourceMappingURL=form-page.d.ts.map
@@ -0,0 +1,62 @@
1
+ import{withPathPrefix as D}from"@redocly/theme/core/utils";import{MCP_CREDENTIALS_ROUTE as R}from"../../../../constants/mcp.js";import{ELICITATION_TTL_MS as _}from"../../gateway-mcp/secret-store.js";import{secretTtlSeconds as I}from"../../gateway-mcp/subject.js";import{FORM_ACTIONS as $}from"./constants.js";import{credentialSchemesOf as F,schemeFieldName as M}from"./form-contract.js";import{ICONS as v,code as h,escapeHtml as r,siteHeader as N,targetTitle as A}from"./layout.js";const S=10,g="form-error",T=new Intl.DateTimeFormat("en-GB",{day:"numeric",month:"short",year:"numeric",timeZone:"UTC"}),P={bearer:{title:"API token",noun:"API token",placeholder:"Paste your API token"},apiKey:{title:"API key",noun:"API key",placeholder:"Paste your API key"},cookie:{title:"Cookie",noun:"cookie value",placeholder:"Paste the cookie value"},oauth2ClientCredentials:{title:"OAuth2",noun:"access token",placeholder:"Paste an access token"},raw:{title:"Authorization header",noun:"header value",placeholder:"Bearer <token>"}};function te(e){const{site:t,target:n,token:o,error:s}=e,a=D(R),u=F(n),i=u.length>1,c=L(e,u),l=c.some(f=>f.isStored),d=i&&l,m=c.map(f=>i?U(f):B(f)).join(""),p=d?'<button class="text-danger" type="submit" form="remove-all">Remove all credentials</button>':"",O=s?`<div class="banner" role="alert" id="${g}">${v.alert}<p>${r(s.message)}</p></div>`:"";return{title:`Credentials for ${A(n)}`,body:`<div class="page">
2
+ <main class="card">
3
+ ${N(t)}
4
+ <h1>${l?"Update":"Add"} credentials for ${r(A(n))}</h1>
5
+ <p class="lede">${i?"This API accepts more than one kind of credential. Fill in the one that you have.":"This API needs one credential to authenticate your calls."}</p>
6
+ ${O}
7
+ <form class="credentials" method="post" action="${a}" autocomplete="off">
8
+ ${y(o,$.save)}
9
+ <div class="schemes">${m}</div>
10
+ <div class="actions">
11
+ <div class="actions-row">${p}<button class="primary" type="submit">Save credentials</button></div>
12
+ <p class="decline small">Did not ask for this link? <button class="link-button" type="submit" form="decline">Decline it</button></p>
13
+ </div>
14
+ </form>
15
+ ${H(a,o,c,d)}
16
+ </main>
17
+ ${W(n,t.host)}
18
+ </div>`}}function L(e,t){const{target:n,stored:o,error:s,clientIds:a}=e,u=n.acceptedSchemes??[n.scheme],i=s?.schemeIndex??Math.max(0,t.findIndex(({name:l})=>u.includes(l))),c=t.length>1;return t.map((l,d)=>{const m=o[l.name],p=s?.schemeIndex===d?s:void 0;return{scheme:l,index:d,isMulti:c,title:c?l.name:P[l.auth.kind].title,savedAt:m?.savedAt,deletedAt:m?.savedAt===void 0?void 0:m.savedAt+I(n.subject)*1e3,isStored:m!==void 0,isOpen:d===i||u.includes(l.name)||m!==void 0,error:p,clientId:a?.[d]??"",autofocusField:d===i?p?.field??"secret":void 0}})}function B(e){return`<section class="scheme" aria-labelledby="scheme-${e.index}-title">
19
+ <div class="scheme-body">
20
+ <div class="scheme-head">
21
+ <h2 id="scheme-${e.index}-title">${r(e.title)}</h2>
22
+ <p>${E(e.scheme.auth)}</p>
23
+ </div>
24
+ ${C(e)}
25
+ </div>
26
+ </section>`}function U(e){const t=e.isStored?'<span class="tag stored">Stored</span>':'<span class="tag">Not set</span>';return`<details class="scheme" aria-labelledby="scheme-${e.index}-title"${e.isOpen?" open":""}>
27
+ <summary><span class="summary-title" id="scheme-${e.index}-title">${r(e.title)}</span><span class="summary-tag">${t}</span><span class="summary-line">${E(e.scheme.auth)}</span>${e.scheme.label?`<span class="summary-line">${r(e.scheme.label)}</span>`:""}</summary>
28
+ <div class="scheme-body">
29
+ ${C(e)}
30
+ </div>
31
+ </details>`}function C(e){const t=e.isStored?j(e):"",n=e.isMulti?K(e.scheme.operations):"";return e.scheme.auth.kind==="oauth2ClientCredentials"?`${t}${x(e,!1)}${n}${z(e)}`:`${t}${x(e,!e.isMulti&&!e.isStored)}${n}`}function j({savedAt:e,deletedAt:t,index:n,title:o}){return`<div class="stored-box">
32
+ <div>${e===void 0||t===void 0?"<p>A credential is stored for this scheme.</p>":`<p>Saved on ${T.format(e)}</p><p class="muted small">Kept until ${T.format(t)} at ${b(t)} UTC</p>`}</div>
33
+ <button class="text-danger small" type="submit" form="remove-${n}" aria-label="Remove the stored ${r(o)} credential">Remove</button>
34
+ </div>`}function x(e,t){const{auth:n}=e.scheme,{noun:o,placeholder:s}=P[n.kind];return k({view:e,field:"secret",type:"password",label:e.isStored?`Replace the ${o}`:Y(o),placeholder:e.isStored&&e.isMulti?"Leave empty to keep the stored value":s,hint:q(n),isRequired:t})}function z(e){return`<hr class="divider">
35
+ <div class="scheme-head">
36
+ <p class="strong">No token yet?</p>
37
+ <p>We exchange your client ID and secret for a token and store only the token.</p>
38
+ </div>
39
+ <div class="field-row">
40
+ ${k({view:e,field:"client_id",type:"text",label:"Client ID",placeholder:"Enter the client ID",value:e.clientId})}
41
+ ${k({view:e,field:"client_secret",type:"password",label:"Client secret",placeholder:"Enter the client secret"})}
42
+ </div>`}function k({view:e,field:t,type:n,label:o,placeholder:s,hint:a,value:u,isRequired:i}){const c=M(t,e.index),l=e.error!==void 0&&e.error.field===t,d=`${c}-hint`,m=[a?d:"",l?g:""].filter(Boolean).join(" "),p=[`type="${n}"`,`id="${c}"`,`name="${c}"`,`placeholder="${r(s)}"`,'autocomplete="off"','autocapitalize="off"','spellcheck="false"',u?`value="${r(u)}"`:"",m?`aria-describedby="${m}"`:"",l?'aria-invalid="true"':"",i?"required":"",e.autofocusField===t?"autofocus":""].filter(Boolean);return`<div class="field">
43
+ <label for="${c}">${r(o)}</label>
44
+ <input ${p.join(" ")}>
45
+ ${a?`<p class="muted small" id="${d}">${a}</p>`:""}
46
+ </div>`}function K(e){if(!e||e.length===0)return"";const t=e.slice(0,S).map(s=>`<li>${h(s)}</li>`).join(""),n=e.length-S,o=n>0?`<li class="muted">and ${n} more</li>`:"";return`<details class="operations"><summary>Which operations use it</summary><ul>${t}${o}</ul></details>`}function W({name:e,subject:t,expiresAt:n},o){const s=`${v.shield}What happens to your credentials`,a=`<ol class="steps">
47
+ <li>Sent straight to ${r(o)}, never through your AI client</li>
48
+ <li>Stored encrypted and never shown again</li>
49
+ <li>Added only to calls to ${r(e)} from the AI client that asked for this link</li>
50
+ <li>You can delete them anytime</li>
51
+ </ol>`;return`<aside class="panel" aria-labelledby="panel-title">
52
+ <div class="panel-steps"><h2 id="panel-title">${s}</h2>${a}</div>
53
+ <details class="panel-steps-toggle"><summary><h2>${s}</h2></summary>${a}</details>
54
+ <div class="link-facts">
55
+ <p>Link requested at ${b(n-_)} UTC</p>
56
+ <span class="pill">Open until ${b(n)} UTC</span>
57
+ <p>This link works one time.</p>
58
+ <p>Kept for ${V(I(t))} after you save them.</p>
59
+ <p>To delete them, ask your AI client for a new link.</p>
60
+ </div>
61
+ </aside>`}function H(e,t,n,o){const s=n.filter(i=>i.isStored).map(i=>`<form id="remove-${i.index}" method="post" action="${e}">${y(t,$.remove)}<input type="hidden" name="scheme" value="${i.index}"></form>`),a=o?`<form id="remove-all" method="post" action="${e}">${y(t,$.removeAll)}</form>`:"",u=`<form id="decline" method="post" action="${e}">${y(t,$.decline)}</form>`;return[...s,a,u].join(`
62
+ `)}function y(e,t){return`<input type="hidden" name="token" value="${r(e)}"><input type="hidden" name="action" value="${t}">`}function E(e){return e.kind==="apiKey"?`Sent to the API as the ${h(e.header)} header.`:e.kind==="cookie"?`Sent to the API as the ${h(e.name)} cookie.`:`Sent to the API as the ${h("Authorization")} header.`}function q(e){if(e.kind==="cookie")return`Paste only the value. We send it as ${h(`${e.name}=<value>`)}.`;if(e.kind==="raw")return`Paste the full header value, for example ${h("Bearer <token>")}.`}function b(e){return new Date(e).toISOString().slice(11,16)}function V(e){const t=e/86400;return Number.isInteger(t)&&t>1?`${t} days`:`${Math.round(e/3600)} hours`}function Y(e){return e.charAt(0).toUpperCase()+e.slice(1)}export{te as formPage};
@@ -0,0 +1,17 @@
1
+ import type { ElicitationTarget } from '../../gateway-mcp/secret-store.js';
2
+ import type { PageColorScheme, PageContent, PageSite } from './types.js';
3
+ export declare function escapeHtml(value: string): string;
4
+ /** The API name, plus the scheme when the link names one scheme of a multi-scheme API. */
5
+ export declare function targetTitle({ name, schemeLabel, schemes }: ElicitationTarget): string;
6
+ export declare function code(value: string): string;
7
+ export declare function htmlDocument({ title, body }: PageContent, colorScheme: PageColorScheme | undefined): string;
8
+ export declare function siteHeader({ name, logo, host, colorScheme }: PageSite): string;
9
+ export declare const ICONS: {
10
+ readonly shield: '<svg width="20" height="20" viewBox="0 0 20 20" fill="none" aria-hidden="true"><path d="M10 2.5 3.75 4.8v4.7c0 3.9 2.6 6.9 6.25 8 3.65-1.1 6.25-4.1 6.25-8V4.8L10 2.5Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="m7.3 10 1.9 1.9 3.6-3.8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>';
11
+ readonly alert: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><circle cx="8" cy="8" r="6.25" stroke="currentColor" stroke-width="1.5"/><path d="m3.8 12.2 8.4-8.4" stroke="currentColor" stroke-width="1.5"/></svg>';
12
+ readonly warning: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M8 2.2 1.9 13h12.2L8 2.2Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M8 6.5v3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><circle cx="8" cy="11.3" r=".8" fill="currentColor"/></svg>';
13
+ readonly check: '<svg width="18" height="18" viewBox="0 0 18 18" fill="none" aria-hidden="true"><path d="m4.5 9.2 3 3 6-6.4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>';
14
+ readonly cross: '<svg width="18" height="18" viewBox="0 0 18 18" fill="none" aria-hidden="true"><path d="m5.5 5.5 7 7m0-7-7 7" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>';
15
+ readonly clock: '<svg width="18" height="18" viewBox="0 0 18 18" fill="none" aria-hidden="true"><circle cx="9" cy="9" r="5.75" stroke="currentColor" stroke-width="1.6"/><path d="M9 6v3.2l2 1.3" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>';
16
+ };
17
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1,11 @@
1
+ import{pageStyles as l}from"./styles.js";function o(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}function a({name:e,schemeLabel:r,schemes:t}){return t&&t.length>1?e:r?`${e} (${r})`:e}function h(e){return`<code>${o(e)}</code>`}function u({title:e,body:r},t){return`<!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta name="robots" content="noindex">
7
+ <title>${o(e)}</title>
8
+ <style>${l(t)}</style>
9
+ </head>
10
+ <body>${r}</body>
11
+ </html>`}function k({name:e,logo:r,host:t,colorScheme:i}){const n=r?d(r,e??t,i):`<span class="site-name">${o(e??t)}</span>`,s=r||e?`<span class="site-host">${o(t)}</span>`:"";return`<header class="site">${n}${s}</header>`}function d(e,r,t){const i=n=>`<img class="site-logo" src="${o(n)}" alt="${o(r)}">`;return!e.dark||t==="light"?i(e.light):t==="dark"?i(e.dark):`<picture><source srcset="${o(e.dark)}" media="(prefers-color-scheme: dark)">${i(e.light)}</picture>`}const p={shield:'<svg width="20" height="20" viewBox="0 0 20 20" fill="none" aria-hidden="true"><path d="M10 2.5 3.75 4.8v4.7c0 3.9 2.6 6.9 6.25 8 3.65-1.1 6.25-4.1 6.25-8V4.8L10 2.5Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="m7.3 10 1.9 1.9 3.6-3.8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>',alert:'<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><circle cx="8" cy="8" r="6.25" stroke="currentColor" stroke-width="1.5"/><path d="m3.8 12.2 8.4-8.4" stroke="currentColor" stroke-width="1.5"/></svg>',warning:'<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M8 2.2 1.9 13h12.2L8 2.2Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M8 6.5v3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><circle cx="8" cy="11.3" r=".8" fill="currentColor"/></svg>',check:'<svg width="18" height="18" viewBox="0 0 18 18" fill="none" aria-hidden="true"><path d="m4.5 9.2 3 3 6-6.4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>',cross:'<svg width="18" height="18" viewBox="0 0 18 18" fill="none" aria-hidden="true"><path d="m5.5 5.5 7 7m0-7-7 7" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>',clock:'<svg width="18" height="18" viewBox="0 0 18 18" fill="none" aria-hidden="true"><circle cx="9" cy="9" r="5.75" stroke="currentColor" stroke-width="1.6"/><path d="M9 6v3.2l2 1.3" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>'};export{p as ICONS,h as code,o as escapeHtml,u as htmlDocument,k as siteHeader,a as targetTitle};
@@ -0,0 +1,10 @@
1
+ import type { ElicitationTarget } from '../../gateway-mcp/secret-store.js';
2
+ import type { CredentialsOutcome, PageContent } from './types.js';
3
+ type Tone = 'success' | 'danger' | 'neutral' | 'warning';
4
+ export declare function outcomePage(target: ElicitationTarget, { action, schemes, stored }: CredentialsOutcome): PageContent;
5
+ export declare function declinedPage(): PageContent;
6
+ export declare function expiredPage(): PageContent;
7
+ /** A centered status card; `lines` are trusted HTML. */
8
+ export declare function statusPage(tone: Tone, title: string, lines: readonly string[]): PageContent;
9
+ export {};
10
+ //# sourceMappingURL=outcome-pages.d.ts.map