@redocly/redoc 0.137.0-next.3 → 0.137.0-next.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -0
- package/dist/cli/stats/collectors/openapi/index.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/json-schema.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/openapi-code-sample.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/openapi-example.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/openapi-response-sample.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/use-markdown-adapter.d.ts +45 -0
- package/dist/client/app/markdoc/custom-components/openapi/use-markdown-adapter.js +1 -0
- package/dist/client/utils/catalog/get-next-page-param.d.ts +1 -1
- package/dist/client/utils/catalog/get-next-page-param.js +1 -1
- package/dist/compiled/svgo/svgo-node.js +32 -32
- package/dist/server/config/env-config.d.ts +2 -0
- package/dist/server/config/env-schema.d.ts +5 -0
- package/dist/server/config/env-schemas/search.d.ts +3 -0
- package/dist/server/config/env-schemas/search.js +1 -1
- package/dist/server/constants/plugins/search.d.ts +0 -2
- package/dist/server/constants/plugins/search.js +1 -1
- package/dist/server/plugins/markdown/utils/sanitize-malformed-md-content.js +2 -2
- package/dist/server/plugins/mcp/codemode/tools/manage-credentials.js +2 -2
- package/dist/server/plugins/mcp/gateway-mcp/elicitation.d.ts +2 -1
- package/dist/server/plugins/mcp/gateway-mcp/elicitation.js +4 -4
- package/dist/server/plugins/mcp/gateway-mcp/secret-store.d.ts +8 -0
- package/dist/server/plugins/mcp/gateway-mcp/upstream-auth-scheme.d.ts +2 -0
- package/dist/server/plugins/mcp/gateway-mcp/upstream-auth-scheme.js +2 -2
- package/dist/server/plugins/mcp/handlers/mcp-credentials-handler.js +1 -144
- package/dist/server/plugins/mcp/handlers/mcp-credentials-page.d.ts +6 -0
- package/dist/server/plugins/mcp/handlers/mcp-credentials-page.js +217 -0
- package/dist/server/plugins/mcp/index.js +1 -1
- package/dist/server/plugins/mcp/servers/mcp-server.js +3 -3
- package/dist/server/plugins/mcp/utils/find-gated-tool-names.d.ts +13 -0
- package/dist/server/plugins/mcp/utils/find-gated-tool-names.js +1 -0
- package/dist/server/plugins/search/engines/typesense/index.js +1 -1
- package/dist/server/plugins/search/index.js +1 -1
- package/dist/server/plugins/search/utils.d.ts +2 -1
- package/dist/server/plugins/search/utils.js +2 -2
- package/dist/server/store.d.ts +1 -0
- package/dist/server/store.js +1 -1
- package/dist/server/types/plugins/common.d.ts +3 -0
- package/dist/server/web-server/routes/mcp-routes/mcp-server-card.js +1 -1
- package/package.json +8 -8
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import{ELICITATION_TTL_MS as p}from"../gateway-mcp/secret-store.js";const h=6,f=`<svg class="logo" width="193" height="56" viewBox="0 0 193 56" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Redocly">
|
|
2
|
+
<g clip-path="url(#redocly-logo-clip)">
|
|
3
|
+
<path d="M84.3634 40.7533H80.0357L73.199 30.9849H69.8119V40.8155H66.1738V14.5592H76.3353C78.9067 14.5592 80.9139 15.368 82.2308 16.9857C83.5483 18.5412 84.1756 20.5322 84.1756 22.8343C84.1756 25.0119 83.6107 26.7541 82.4193 28.3095C81.2902 29.865 79.5339 30.7361 77.2134 30.8605L84.3634 40.7533ZM69.8743 17.8568V27.6251H76.2098C79.22 27.6251 80.5999 25.4475 80.5999 22.7098C80.5999 20.0344 79.22 17.7945 76.3353 17.7945H69.8743V17.8568Z" fill="currentColor"/>
|
|
4
|
+
<path d="M103.117 33.225H88.5654C89.0042 36.1492 91.2623 38.078 94.3355 38.078C96.5312 38.078 98.5384 37.0203 99.4789 35.3403C99.8553 35.4648 100.357 35.7137 101.047 35.9626L102.553 36.4603C101.047 39.5091 97.9111 41.1889 94.2731 41.1889C91.701 41.1889 89.506 40.3179 87.6873 38.638C85.931 36.8959 85.1152 34.7182 85.1152 31.9184C85.1152 29.1185 85.9934 26.9409 87.6873 25.2609C89.4436 23.5188 91.6386 22.71 94.2731 22.71C96.8445 22.71 98.9771 23.581 100.608 25.2609C102.302 26.9409 103.18 29.1807 103.18 31.9184C103.117 32.5405 103.117 32.9139 103.117 33.225ZM99.6674 30.4873C99.228 27.563 97.0954 25.7587 94.21 25.7587C91.1999 25.7587 88.9418 27.6252 88.5654 30.4873H99.6674Z" fill="currentColor"/>
|
|
5
|
+
<path d="M120.994 40.7532V38.0155C119.802 40.0066 117.356 41.1265 114.659 41.1265C112.087 41.1265 110.018 40.2555 108.198 38.5755C106.442 36.8334 105.627 34.6557 105.627 31.9181C105.627 29.1183 106.505 26.9406 108.198 25.2607C109.892 23.5808 112.087 22.7098 114.596 22.7098C117.293 22.7098 119.74 23.7675 120.931 25.7585V13.377H124.444V40.8154L120.994 40.7532ZM110.833 27.6873C109.704 28.745 109.202 30.176 109.202 31.9181C109.202 33.6603 109.704 35.0913 110.833 36.2112C111.962 37.3312 113.342 37.8911 114.973 37.8911C116.604 37.8911 117.983 37.3934 119.175 36.3357C120.367 35.2157 120.931 33.7847 120.931 31.9803C120.931 30.1138 120.304 28.745 119.113 27.6873C117.92 26.6295 116.54 26.1318 114.973 26.1318C113.342 26.0696 111.962 26.5673 110.833 27.6873Z" fill="currentColor"/>
|
|
6
|
+
<path d="M129.461 25.1985C131.279 23.4564 133.475 22.6476 136.172 22.6476C138.806 22.6476 141.064 23.5186 142.883 25.1985C144.702 26.8784 145.581 29.1183 145.581 31.8559C145.581 34.5935 144.702 36.8334 142.821 38.5756C141.002 40.3177 138.806 41.1266 136.172 41.1266C133.6 41.1266 131.342 40.2555 129.523 38.5756C127.704 36.8957 126.826 34.6558 126.826 31.8559C126.826 29.0561 127.704 26.8784 129.461 25.1985ZM140.375 27.6251C139.246 26.5673 137.803 26.0696 136.172 26.0696C134.541 26.0696 133.099 26.5673 131.907 27.6251C130.778 28.6828 130.213 30.1138 130.213 31.8559C130.213 33.6603 130.778 35.0913 131.907 36.2112C133.099 37.3312 134.479 37.8289 136.172 37.8289C137.803 37.8289 139.246 37.3312 140.375 36.2112C141.566 35.0913 142.13 33.6603 142.13 31.8559C142.13 30.1138 141.503 28.6828 140.375 27.6251Z" fill="currentColor"/>
|
|
7
|
+
<path d="M163.896 25.4474L161.262 27.6873C160.133 26.5674 158.69 26.0074 156.997 26.0074C155.366 26.0074 153.923 26.5051 152.732 27.6251C151.54 28.6828 150.976 30.1138 150.976 31.856C150.976 33.5981 151.54 35.0913 152.732 36.149C153.923 37.269 155.303 37.829 156.997 37.829C158.753 37.829 160.133 37.269 161.262 36.0869L163.959 38.3267C162.14 40.2555 159.882 41.251 156.997 41.251C154.362 41.251 152.105 40.3799 150.285 38.7C148.404 36.9579 147.525 34.718 147.525 31.9804C147.525 29.2428 148.404 27.0651 150.285 25.323C152.167 23.5808 154.362 22.772 156.997 22.772C159.819 22.6476 162.14 23.5808 163.896 25.4474Z" fill="currentColor"/>
|
|
8
|
+
<path d="M171.987 13.377H168.475V40.8154H171.987V13.377Z" fill="currentColor"/>
|
|
9
|
+
<path d="M181.898 49.4639H178.26L182.024 40.3177L174.873 23.0209H178.574L183.968 35.9624L189.3 23.0209H193L181.898 49.4639Z" fill="currentColor"/>
|
|
10
|
+
<path d="M47.2308 43.491C45.8508 42.5576 44.3455 41.6866 42.8401 41.0644C45.4745 39.9445 47.9207 38.389 49.8651 36.398C53.754 32.5404 56.2002 27.1896 56.2002 21.341C56.2002 15.4303 53.8167 10.1417 49.8651 6.28409C45.9763 2.36431 40.6448 0 34.6861 0H1.63081C1.19174 0 0.752681 0.186656 0.501787 0.497751C0.18817 0.746628 0 1.18215 0 1.61769C0 2.05322 0.18817 2.48875 0.501787 2.73763C0.752681 3.04872 1.19174 3.23538 1.63081 3.23538C6.64866 3.23538 11.2275 5.2886 14.5518 8.52397C17.8134 11.8216 19.8833 16.3635 19.8833 21.341C19.8833 26.3185 17.8134 30.8605 14.5518 34.1581C11.2275 37.3935 6.64866 39.4467 1.63081 39.4467C1.44264 39.4467 1.37992 39.5089 1.19174 39.5089C0.501787 39.6956 0 40.2556 0 41.0022V41.1888C0 42.0599 0.689956 42.6821 1.50536 42.6821H2.57166C6.20962 42.8687 9.59667 44.0509 12.3565 46.0419C15.3672 48.2195 17.688 51.2061 18.9425 54.7526C19.1307 55.3747 19.8206 55.8102 20.4478 55.8102H53.5658C54.0676 55.8102 54.5694 55.5614 54.8203 55.1258C55.1339 54.6903 55.1966 54.1926 55.0085 53.6948C53.5031 49.5884 50.806 46.0419 47.2308 43.491ZM16.8098 36.398C20.6987 32.5404 23.1449 27.1896 23.1449 21.341C23.1449 15.4303 20.7615 10.1417 16.8098 6.28409C15.6181 5.10194 14.3009 4.04422 12.8583 3.17316H34.6861C39.704 3.17316 44.2828 5.22638 47.6071 8.46173C50.8687 11.7594 52.9385 16.3013 52.9385 21.2788C52.9385 26.2563 50.8687 30.7983 47.6071 34.0959C44.2828 37.3313 39.704 39.3845 34.6861 39.3845C34.5606 39.3845 34.4979 39.3845 34.3725 39.4467H12.7956C14.2382 38.7001 15.6181 37.6424 16.8098 36.398ZM14.1755 43.491C13.7992 43.242 13.3601 42.9932 12.9838 42.6821H35.6896C39.3276 42.8687 42.7146 44.0509 45.4745 46.0419C47.858 47.7218 49.8024 49.9617 51.1196 52.6371H21.4514C19.946 48.9662 17.3744 45.8552 14.1755 43.491Z" fill="currentColor"/>
|
|
11
|
+
<path d="M29.668 21.3412C29.668 22.2122 30.3579 22.8966 31.2361 22.8966H47.1679C48.0459 22.8966 48.7359 22.2122 48.7359 21.3412C48.7359 20.4701 48.0459 19.7856 47.1679 19.7856H31.2988C30.3579 19.7856 29.668 20.5323 29.668 21.3412Z" fill="currentColor"/>
|
|
12
|
+
<path d="M28.5385 29.2427H44.4703C45.3484 29.2427 46.0383 28.5583 46.0383 27.6873C46.0383 26.8162 45.3484 26.1318 44.4703 26.1318H28.5385C27.6604 26.1318 26.9705 26.8162 26.9705 27.6873C26.9077 28.5583 27.6604 29.2427 28.5385 29.2427Z" fill="currentColor"/>
|
|
13
|
+
<path d="M28.5385 16.6127H44.4703C45.3484 16.6127 46.0383 15.9282 46.0383 15.0572C46.0383 14.1861 45.3484 13.5017 44.4703 13.5017H28.5385C27.6604 13.5017 26.9705 14.1861 26.9705 15.0572C26.9077 15.8661 27.6604 16.6127 28.5385 16.6127Z" fill="currentColor"/>
|
|
14
|
+
</g>
|
|
15
|
+
<defs><clipPath id="redocly-logo-clip"><rect width="193" height="56" fill="white"/></clipPath></defs>
|
|
16
|
+
</svg>`;function n(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""")}function s(e){return`<!doctype html>
|
|
17
|
+
<html lang="en">
|
|
18
|
+
<head>
|
|
19
|
+
<meta charset="utf-8">
|
|
20
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
21
|
+
<meta name="robots" content="noindex">
|
|
22
|
+
<title>API credentials</title>
|
|
23
|
+
<style>
|
|
24
|
+
:root{
|
|
25
|
+
color-scheme:light dark;
|
|
26
|
+
--font:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
|
27
|
+
--font-mono:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;
|
|
28
|
+
--page-bg:#ededf2;
|
|
29
|
+
--card-bg:#fbfbfc;
|
|
30
|
+
--border:#dcdde5;
|
|
31
|
+
--border-strong:#c4c6d1;
|
|
32
|
+
--text:#1a1c21;
|
|
33
|
+
--text-muted:#6e6f7a;
|
|
34
|
+
--brand:#2467f2;
|
|
35
|
+
--action:#1f7cff;
|
|
36
|
+
--action-hover:#3072db;
|
|
37
|
+
--action-ring:rgba(31,124,255,.25);
|
|
38
|
+
--code-bg:#ededf2;
|
|
39
|
+
--danger:#c74b55;
|
|
40
|
+
--danger-hover:#a9434c;
|
|
41
|
+
--error-bg:#fef2f2;
|
|
42
|
+
--error-border:#f5a3aa;
|
|
43
|
+
--error-text:#8b3c42;
|
|
44
|
+
}
|
|
45
|
+
@media (prefers-color-scheme:dark){:root{
|
|
46
|
+
--page-bg:#12141a;
|
|
47
|
+
--card-bg:#22242b;
|
|
48
|
+
--border:#2a2b33;
|
|
49
|
+
--border-strong:#3b3c45;
|
|
50
|
+
--text:#fbfbfc;
|
|
51
|
+
--text-muted:#9b9ca8;
|
|
52
|
+
--brand:#2467f2;
|
|
53
|
+
--action:#1f7cff;
|
|
54
|
+
--action-hover:#3072db;
|
|
55
|
+
--action-ring:rgba(31,124,255,.35);
|
|
56
|
+
--code-bg:#2a2b33;
|
|
57
|
+
--danger:#e5535f;
|
|
58
|
+
--danger-hover:#ef858d;
|
|
59
|
+
--error-bg:#2a1517;
|
|
60
|
+
--error-border:#8b3c42;
|
|
61
|
+
--error-text:#f5a3aa;
|
|
62
|
+
}}
|
|
63
|
+
*{box-sizing:border-box}
|
|
64
|
+
body{font-family:var(--font);margin:0;min-height:100vh;display:flex;align-items:center;justify-content:center;background:var(--page-bg);color:var(--text);padding:1.5rem;font-size:14px;line-height:1.5}
|
|
65
|
+
.card{width:100%;max-width:26rem;background:var(--card-bg);border:1px solid var(--border);border-radius:8px;box-shadow:0 1px 2px rgba(26,28,33,.04),0 8px 24px -12px rgba(26,28,33,.18);padding:2rem}
|
|
66
|
+
.logo{color:var(--brand);height:28px;width:auto;display:block;margin:0 0 1.5rem}
|
|
67
|
+
h1{font-size:1.25rem;line-height:1.3;font-weight:600;margin:0 0 .5rem;letter-spacing:-.01em}
|
|
68
|
+
.section{margin-top:1.5rem;padding-top:1.5rem;border-top:1px solid var(--border)}
|
|
69
|
+
.section h2{font-size:.9375rem;font-weight:600;margin:0 0 .5rem}
|
|
70
|
+
p{margin:.75rem 0 0}
|
|
71
|
+
p.hint{color:var(--text-muted);font-size:.8125rem;line-height:1.45}
|
|
72
|
+
p.error{margin:1rem 0 0;padding:.75rem;border:1px solid var(--error-border);border-radius:6px;background:var(--error-bg);color:var(--error-text);font-size:.8125rem;line-height:1.45}
|
|
73
|
+
label{display:block;margin:1rem 0 .25rem;font-weight:600;font-size:.8125rem}
|
|
74
|
+
input[type=password],input[type=text]{width:100%;padding:.625rem .75rem;font-size:.9375rem;font-family:var(--font);border:1px solid var(--border-strong);border-radius:6px;background:var(--card-bg);color:var(--text)}
|
|
75
|
+
input::placeholder{color:var(--text-muted)}
|
|
76
|
+
input:focus{outline:none;border-color:var(--action);box-shadow:0 0 0 3px var(--action-ring)}
|
|
77
|
+
button{margin-top:1.25rem;padding:.625rem 1.25rem;font-size:.9375rem;font-weight:600;font-family:var(--font);color:#fff;background:var(--action);border:1px solid transparent;border-radius:6px;cursor:pointer}
|
|
78
|
+
button:hover{background:var(--action-hover)}
|
|
79
|
+
button:focus-visible{outline:2px solid var(--action);outline-offset:2px}
|
|
80
|
+
button.danger{background:transparent;color:var(--danger);border-color:var(--border-strong)}
|
|
81
|
+
button.danger:hover{background:var(--danger);color:#fff;border-color:var(--danger)}
|
|
82
|
+
button.danger:focus-visible{outline-color:var(--danger)}
|
|
83
|
+
code{font-family:var(--font-mono);background:var(--code-bg);padding:.125rem .375rem;border-radius:4px;font-size:.8125em}
|
|
84
|
+
label.check{display:flex;align-items:center;gap:.5rem;margin-top:.75rem;font-weight:400;font-size:.8125rem;color:var(--text-muted)}
|
|
85
|
+
label.check input{width:auto;margin:0}
|
|
86
|
+
footer{margin-top:1.5rem;padding-top:1rem;border-top:1px solid var(--border);font-size:.75rem;color:var(--text-muted);text-align:center;line-height:1.45}
|
|
87
|
+
</style>
|
|
88
|
+
</head>
|
|
89
|
+
<body><main class="card">${f}${e}</main></body>
|
|
90
|
+
</html>`}function l(e){return`<h1>\u{1F512} ${n(e)}</h1>`}function d(){return"<footer>Secured by Redocly · Credentials never enter the AI conversation</footer>"}function c(){return`<p class="hint">This link expires ${Math.round(p/6e4)} minutes after it was issued. If it stops working, request a new one in your AI client.</p>`}function u({name:e,schemeLabel:t,schemes:o}){return o&&o.length>1?e:t?`${e} (${t})`:e}function b(e){return`
|
|
91
|
+
<div class="section">
|
|
92
|
+
<h2>Remove stored credentials</h2>
|
|
93
|
+
<p class="hint">This API already has credentials stored. Enter new credentials to replace them, or remove them to make calls anonymous again.</p>
|
|
94
|
+
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
95
|
+
<input type="hidden" name="token" value="${n(e)}">
|
|
96
|
+
<input type="hidden" name="mode" value="clear">
|
|
97
|
+
<button class="danger" type="submit">Remove credentials</button>
|
|
98
|
+
</form>
|
|
99
|
+
</div>`}function H(e,t,o){const r=o?`<p class="error">${n(o)}</p>`:"";if(e.schemes&&e.schemes.length>1)return x(e,e.schemes,t,r);const a=e.hasExistingSecret?b(t):"",i=u(e);switch(e.auth.kind){case"bearer":return C(i,t,a,r);case"apiKey":return g(i,t,e.auth.header,a,r);case"cookie":return v(i,t,e.auth.name,a,r);case"oauth2ClientCredentials":return y(i,t,e.auth.scopes,a,r);default:return $(i,t,a,r)}}function C(e,t,o,r){return s(`
|
|
100
|
+
${l(`Credentials for ${e}`)}
|
|
101
|
+
${r}
|
|
102
|
+
<p class="hint">Encrypted and visible only to you for 24 hours (anonymous) or 30 days (signed in).<br />Sent to the API as the <code>Authorization header</code>. It never passes through the AI conversation.</p>
|
|
103
|
+
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
104
|
+
<input type="hidden" name="token" value="${n(t)}">
|
|
105
|
+
<input type="hidden" name="mode" value="token">
|
|
106
|
+
<label for="secret">API token</label>
|
|
107
|
+
<input type="password" id="secret" name="secret" placeholder="your API token" required autofocus>
|
|
108
|
+
<p class="hint">Paste your token with or without the <code>Bearer prefix</code>. The prefix is added automatically, if missing.</p>
|
|
109
|
+
<button type="submit">Save credentials</button>
|
|
110
|
+
</form>
|
|
111
|
+
${o}
|
|
112
|
+
${c()}
|
|
113
|
+
${d()}`)}function g(e,t,o,r,a){return s(`
|
|
114
|
+
${l(`Credentials for ${e}`)}
|
|
115
|
+
${a}
|
|
116
|
+
<p class="hint">Encrypted and visible only to you for 24 hours (anonymous) or 30 days (signed in).<br />Sent to the API as the <code>${n(o)}</code> header. It never passes through the AI conversation.</p>
|
|
117
|
+
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
118
|
+
<input type="hidden" name="token" value="${n(t)}">
|
|
119
|
+
<input type="hidden" name="mode" value="token">
|
|
120
|
+
<label for="secret">${n(o)} value</label>
|
|
121
|
+
<input type="password" id="secret" name="secret" placeholder="value" required autofocus>
|
|
122
|
+
<button type="submit">Save credentials</button>
|
|
123
|
+
</form>
|
|
124
|
+
${r}
|
|
125
|
+
${c()}
|
|
126
|
+
${d()}`)}function v(e,t,o,r,a){return s(`
|
|
127
|
+
${l(`Credentials for ${e}`)}
|
|
128
|
+
${a}
|
|
129
|
+
<p class="hint">Encrypted and visible only to you for 24 hours (anonymous) or 30 days (signed in).<br />Sent to the API as the <code>${n(o)}</code> cookie. It never passes through the AI conversation.</p>
|
|
130
|
+
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
131
|
+
<input type="hidden" name="token" value="${n(t)}">
|
|
132
|
+
<input type="hidden" name="mode" value="token">
|
|
133
|
+
<label for="secret">${n(o)} cookie value</label>
|
|
134
|
+
<input type="password" id="secret" name="secret" placeholder="value" required autofocus>
|
|
135
|
+
<p class="hint">Paste only the value. It's sent as <code>${n(o)}=<value></code>.</p>
|
|
136
|
+
<button type="submit">Save credentials</button>
|
|
137
|
+
</form>
|
|
138
|
+
${r}
|
|
139
|
+
${c()}
|
|
140
|
+
${d()}`)}function y(e,t,o,r,a){const i=o.length>0?o.map(n).join(", "):"default scopes";return s(`
|
|
141
|
+
${l(`Credentials for ${e}`)}
|
|
142
|
+
${a}
|
|
143
|
+
<p class="hint">Paste your token, or provide your OAuth2 client credentials.</p>
|
|
144
|
+
<div class="section">
|
|
145
|
+
<h2>I have a token</h2>
|
|
146
|
+
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
147
|
+
<input type="hidden" name="token" value="${n(t)}">
|
|
148
|
+
<input type="hidden" name="mode" value="token">
|
|
149
|
+
<label for="secret">API token</label>
|
|
150
|
+
<input type="password" id="secret" name="secret" placeholder="your API token" required autofocus>
|
|
151
|
+
<p class="hint"><code>Bearer</code> prefix is added automatically, if missing.</p>
|
|
152
|
+
<button type="submit">Save token</button>
|
|
153
|
+
</form>
|
|
154
|
+
</div>
|
|
155
|
+
<div class="section">
|
|
156
|
+
<h2>Exchange client credentials</h2>
|
|
157
|
+
<p class="hint">We request a token from the API on your behalf. Scopes: ${i}.</p>
|
|
158
|
+
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
159
|
+
<input type="hidden" name="token" value="${n(t)}">
|
|
160
|
+
<input type="hidden" name="mode" value="client-credentials">
|
|
161
|
+
<label for="client_id">Client ID</label>
|
|
162
|
+
<input type="text" id="client_id" name="client_id" required>
|
|
163
|
+
<label for="client_secret">Client secret</label>
|
|
164
|
+
<input type="password" id="client_secret" name="client_secret" required>
|
|
165
|
+
<button type="submit">Exchange & save</button>
|
|
166
|
+
</form>
|
|
167
|
+
</div>
|
|
168
|
+
${r}
|
|
169
|
+
${c()}
|
|
170
|
+
${d()}`)}function $(e,t,o,r){return s(`
|
|
171
|
+
${l(`Credentials for ${e}`)}
|
|
172
|
+
${r}
|
|
173
|
+
<p class="hint">The value is stored and encrypted only for you for 24 hours (anonymous) or 30 days (signed in).<br />It is sent to the API as the <code>Authorization</code> header. It never passes through the AI conversation.</p>
|
|
174
|
+
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
175
|
+
<input type="hidden" name="token" value="${n(t)}">
|
|
176
|
+
<input type="hidden" name="mode" value="token">
|
|
177
|
+
<label for="secret">Authorization header value</label>
|
|
178
|
+
<input type="password" id="secret" name="secret" placeholder="Bearer <your token>" required autofocus>
|
|
179
|
+
<button type="submit">Save credentials</button>
|
|
180
|
+
</form>
|
|
181
|
+
${o}
|
|
182
|
+
${c()}
|
|
183
|
+
${d()}`)}function x(e,t,o,r){const a=t.map((i,m)=>k(i,m)).join(`
|
|
184
|
+
`);return s(`
|
|
185
|
+
${l(`Credentials for ${e.name}`)}
|
|
186
|
+
${r}
|
|
187
|
+
<p class="hint">This API accepts more than one kind of credential. Fill in the one(s) you have and leave the rest blank - each value is stored encrypted for you only (24 hours for anonymous callers, 30 days when signed in) and sent straight to the API. It never passes through the AI conversation.</p>
|
|
188
|
+
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
189
|
+
<input type="hidden" name="token" value="${n(o)}">
|
|
190
|
+
<input type="hidden" name="mode" value="multi">
|
|
191
|
+
${a}
|
|
192
|
+
<button type="submit">Save credentials</button>
|
|
193
|
+
</form>
|
|
194
|
+
${c()}
|
|
195
|
+
${d()}`)}function k(e,t){const o=A(e.auth),r=e.label?`<p class="hint">${n(e.label)}</p>`:"",a=_(e.operations),i=e.hasExistingSecret?`<label class="check"><input type="checkbox" name="remove__${t}" value="1"> Remove the stored credential</label>`:"";return`
|
|
196
|
+
<div class="section">
|
|
197
|
+
<h2>${n(e.name)}</h2>
|
|
198
|
+
<p class="hint">Sent to the API as the <code>${n(o.code)}</code> ${o.suffix}.</p>
|
|
199
|
+
${r}
|
|
200
|
+
${a}
|
|
201
|
+
${w(e.auth,t)}
|
|
202
|
+
${i}
|
|
203
|
+
</div>`}function w(e,t){const o=`secret__${t}`;if(e.kind==="oauth2ClientCredentials"){const a=e.scopes.length>0?e.scopes.map(n).join(", "):"default scopes";return`<label for="${o}">API token</label>
|
|
204
|
+
<input type="password" id="${o}" name="${o}" placeholder="paste a token you already have">
|
|
205
|
+
<p class="hint">Or exchange client credentials for one (scopes: ${a}). We add <code>Bearer</code> automatically.</p>
|
|
206
|
+
<label for="client_id__${t}">Client ID</label>
|
|
207
|
+
<input type="text" id="client_id__${t}" name="client_id__${t}">
|
|
208
|
+
<label for="client_secret__${t}">Client secret</label>
|
|
209
|
+
<input type="password" id="client_secret__${t}" name="client_secret__${t}">`}const r=I(e);return`<label for="${o}">${n(r)}</label>
|
|
210
|
+
<input type="password" id="${o}" name="${o}" placeholder="value">`}function I(e){switch(e.kind){case"apiKey":return`${e.header} value`;case"cookie":return`${e.name} cookie value`;case"bearer":return"API token";default:return"Authorization header value"}}function A(e){switch(e.kind){case"apiKey":return{code:e.header,suffix:"header"};case"cookie":return{code:e.name,suffix:"cookie"};case"bearer":return{code:"Authorization",suffix:"header (bearer token)"};case"oauth2ClientCredentials":return{code:"Authorization",suffix:"header (OAuth2 bearer token)"};default:return{code:"Authorization",suffix:"header"}}}function _(e){if(!e||e.length===0)return"";const t=e.slice(0,h).map(n).join(", "),o=e.length-h,r=o>0?`, and ${o} more`:"";return`<p class="hint">Used by ${t}${r}.</p>`}function M(e){return s(`
|
|
211
|
+
${l("Credentials removed")}
|
|
212
|
+
<p>Credentials for ${n(u(e))} are removed. Calls to the API are anonymous again until you add new credentials.</p>
|
|
213
|
+
${d()}`)}function S(e){return s(`
|
|
214
|
+
${l("Credentials saved")}
|
|
215
|
+
<p>Credentials for ${n(u(e))} are saved. Retry in your AI client, or confirm any prompt it left open. You can close this tab.</p>
|
|
216
|
+
<p class="hint">This link is single-use and is now spent. To change anything, request a new one in your AI client.</p>
|
|
217
|
+
${d()}`)}function z(){return s(`${l("Link expired")}<p>This credentials link is invalid, already used, or older than ${Math.round(p/6e4)} minutes. Retry in your AI client to get a fresh link.</p>${d()}`)}export{M as clearedPage,H as formPage,z as notFoundPage,S as successPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{fileURLToPath as
|
|
1
|
+
import{fileURLToPath as G}from"node:url";import{dirname as q,join as p}from"node:path";import{logger as l}from"../../tools/notifiers/logger.js";import{reporter as I}from"../../tools/notifiers/reporter.js";import{isDefined as N}from"../../../utils/guards/is-defined.js";import{bundledOpenApiDocumentsToMcpOpenApi as H,bundledGraphqlDocumentsToMcpOpenApi as U,buildGraphqlDescriptions as j,collectIgnoredRoutes as x,filterIgnoredApiDescriptions as _,getCleanedUpApiDescriptions as F,isMcpInRedirects as B}from"./utils.js";import{getMcpMode as V,isMcpElicitationEnabled as k}from"./utils/mode.js";import{findGatedToolNames as J}from"./utils/find-gated-tool-names.js";import{getDefaultToolSchemas as W}from"./docs-mcp/tool-schemas.js";import{EXECUTE_TOOL_SCHEMA as X}from"./codemode/tools/execute-schema.js";import{isGatewayCapabilityEnabled as $,isGatewayEligible as z}from"./gateway-mcp/eligibility.js";import{resolveGatewayFetchLimits as K}from"./gateway-mcp/rate-limit/limits.js";import{telemetryTraceStep as Q}from"../../../cli/telemetry/helpers/trace-step.js";import{DEFAULT_DOCS_MCP_SERVER_NAME as v,MCP_NOT_ALLOWED_TEMPLATE_ID as Y}from"./constants.js";const y="mcp-docs-server-handler",Z="/mcp",A="mcp-credentials-handler",ee="/mcp/credentials",m=q(G(import.meta.url));async function te(){let o=!1;return{id:"mcp",async processContent(t,r){o||await Q("com.redocly.build.plugin.mcp",async a=>{const n=H(await t.loadApiDocuments(r)),f=U(await t.loadApiDocuments(r)),s=await r.getConfig(),{mcp:i,redirects:d}=s;if(a?.setAttribute("config",JSON.stringify(i||{})),!(i?.hide||i?.docs?.hide)&&(B(d)&&await I.panicOnBuildContentError('The reserved route "/mcp" cannot be used in redirect configuration. Please remove any redirects involving "/mcp" from your redocly.yaml config.'),t.createTemplate(Y,"@redocly/theme/layouts/McpNotAllowed"),!i?.docs?.hide)){l.info("Configuring MCP servers...");const g=p(m,"./handlers/docs-mcp-handler.js"),c=i?.docs?.ignore||[],h=F(_(n,c)),u=!!s?.access?.requiresLogin,D=s?.access?.rbac||{},R=$(i),M=new Map;for(const e of h){const L=e.definition.info?.title||"",w=e.definition.info?.version||"";M.set(e.relativePath,{name:L,description:typeof e.definition.info?.description=="string"?e.definition.info.description:"",version:w,servers:e.definition.servers||[],relativePath:e.relativePath,...R&&z(e.definition.info,e.relativePath,D)?{fetchEligible:!0}:{}})}const E=j(_(f,c)),T=Object.values(s.products||{}).map(e=>e?.name).filter(N),b=s.l10n?.locales?.map(e=>e.code)??r.fs.localeFolders,C=V(),P=k(),{budgetPerExec:S}=K(),O=W({products:T,locales:b,requiresLogin:u,rbac:D,hasGraphqlApis:E.length>0,elicitationEnabled:P});t.addMcpTools(p(m,"./docs-mcp/tools/index.js"),O),C==="code"&&t.addMcpTools(p(m,"./codemode/tools/execute.js"),[X]),await oe({actions:t,handlerPath:g,apiDescriptionsMap:Object.fromEntries(M),graphqlDescriptions:E,mcpConfig:i,mcpMode:C,elicitationEnabled:P,gatewayFetchBudgetPerExec:S}),o=!0}})},async afterRoutesCreated(t,r){const a=await r.getConfig(),{mcp:n}=a;t.markGatedMcpTools(await J(t.getMcpTools())),t.setGlobalData({mcpData:{docs:{enabled:!n?.hide&&!n?.docs?.hide,name:n?.docs?.name||v}}})}}}var De=te;async function oe({actions:o,handlerPath:t,apiDescriptionsMap:r,graphqlDescriptions:a,mcpConfig:n,mcpMode:f,elicitationEnabled:s,gatewayFetchBudgetPerExec:i}){if(o.createRequestHandler(y,t),o.addApiRoute({slug:Z,requestHandlerId:y,httpMethod:"all",getStaticData:async(d,g)=>{const c=x(g.getAllRoutes(),n?.docs?.ignore||[]);return c.length>0&&l.verbose(`mcp.docs.ignore: hiding ${c.length} page routes from MCP search`),{props:{config:{apiDescriptionsMap:r,graphqlDescriptions:a,mcpDocsServerName:n?.docs?.name||v,mcpMode:f,elicitationEnabled:s,gatewayFetchBudgetPerExec:i,searchIgnoredRoutes:c},serverOutDir:o.serverOutDir,tools:o.getMcpTools().map(({importPath:h,...u})=>u)}}}}),l.info("Registered Docs MCP Server endpoint at /mcp"),s){const d=p(m,"./handlers/mcp-credentials-handler.js");o.createRequestHandler(A,d),o.addApiRoute({slug:ee,requestHandlerId:A,httpMethod:"all",getStaticData:async()=>({props:{}})}),l.info("Registered MCP credentials endpoint at /mcp/credentials")}}export{A as MCP_CREDENTIALS_HANDLER_ID,y as MCP_DOCS_SERVER_HANDLER_ID,De as default,te as mcpServerPlugin};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{McpServer as L,WebStandardStreamableHTTPServerTransport as j}from"@modelcontextprotocol/server";import{logger as S}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as D,MCP_TOOL_WORKER_KEY as K}from"../../../workers/mcp-tool-worker-pool.js";import{SSE_KEEPALIVE_INTERVAL_MS as x}from"../constants.js";import{secretStore as F}from"../gateway-mcp/secret-store.js";import{readAuthRequiredMeta as H,stripAuthRequiredMeta as M,upstreamAuthKey as b}from"../gateway-mcp/upstream-auth.js";import{buildCredentialsToolHint as P,buildSchemeChoiceHint as v,buildUpstreamCredentialFallback as T,buildUpstreamCredentialInputRequired as $,isCredentialElicitationDeclined as N,schemeLabelOf as B}from"../gateway-mcp/elicitation.js";import{DEFAULT_SCHEME_NAME as q,resolveUpstreamApiAuth as G,schemeChoicesOf as W}from"../gateway-mcp/upstream-auth-scheme.js";import{MISSING_SESSION_HANDLE_MESSAGE as V,resolveSecretSubject as Y}from"../gateway-mcp/subject.js";const J=6e4,E=new Map;function Q(e,t){const r=E.get(e);if(r)return r;const n=t().finally(()=>{E.delete(e)});return E.set(e,n),n}function fe(e,t){const r=new L({name:e,version:new Date().toISOString().slice(0,10)},{capabilities:{logging:{}},...t?{instructions:t}:{}}),n=new j({keepAliveMs:x,sessionIdGenerator:void 0});return{server:r,transport:n}}async function he(e,t){return await e.connect(t),{server:e,transport:t,cleanup:async()=>{t.close()}}}function ge(e,t){return r=>async(n,s)=>{S.info(`MCP tool called: ${r}`);const i=e.elicitationEnabled??!1,
|
|
1
|
+
import{McpServer as L,WebStandardStreamableHTTPServerTransport as j}from"@modelcontextprotocol/server";import{logger as S}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as D,MCP_TOOL_WORKER_KEY as K}from"../../../workers/mcp-tool-worker-pool.js";import{SSE_KEEPALIVE_INTERVAL_MS as x}from"../constants.js";import{secretStore as F}from"../gateway-mcp/secret-store.js";import{readAuthRequiredMeta as H,stripAuthRequiredMeta as M,upstreamAuthKey as b}from"../gateway-mcp/upstream-auth.js";import{buildCredentialsToolHint as P,buildSchemeChoiceHint as v,buildUpstreamCredentialFallback as T,buildUpstreamCredentialInputRequired as $,isCredentialElicitationDeclined as N,schemeLabelOf as B}from"../gateway-mcp/elicitation.js";import{DEFAULT_SCHEME_NAME as q,resolveUpstreamApiAuth as G,schemeChoicesOf as W}from"../gateway-mcp/upstream-auth-scheme.js";import{MISSING_SESSION_HANDLE_MESSAGE as V,resolveSecretSubject as Y}from"../gateway-mcp/subject.js";const J=6e4,E=new Map;function Q(e,t){const r=E.get(e);if(r)return r;const n=t().finally(()=>{E.delete(e)});return E.set(e,n),n}function fe(e,t){const r=new L({name:e,version:new Date().toISOString().slice(0,10)},{capabilities:{logging:{}},...t?{instructions:t}:{}}),n=new j({keepAliveMs:x,sessionIdGenerator:void 0});return{server:r,transport:n}}async function he(e,t){return await e.connect(t),{server:e,transport:t,cleanup:async()=>{t.close()}}}function ge(e,t){return r=>async(n,s)=>{S.info(`MCP tool called: ${r}`);const i=e.elicitationEnabled??!1,c=s.mcpReq.envelope!==void 0,o=i?Y(e.user,n.sessionHandle):{kind:"anonymous"},a=o.kind==="resolved"?o.subject:void 0;if(o.kind==="invalid")return{content:[{type:"text",text:o.message}],isError:!0};const p=async()=>{if(!(!i||!a||!t?.getKv))try{const m=await t.getKv();return await F.getSecretsForSubject(m,a)}catch(m){S.warn(`gateway mcp secret store error, continuing unauthenticated: ${String(m)}`);return}};let u;const l=async()=>{u=await p();const m={toolName:r,args:n,context:{...e,upstreamSecrets:u},extra:ie(s)};return await D.exec(K,[m],{timeout:J})};let d=await l();const f=H(d);if(f.length>0&&i&&!a)return M({...d,content:[{type:"text",text:V}],isError:!0});if(f.length>0&&i&&a&&t?.getKv){S.info(`gateway mcp credentials prompt: era=${c?"2026-07-28":"2025"} clientElicitation=${Z(t)}`);const m=await t.getKv(),h=await z(e,f),k=f.map(U=>ee(U,h)),y=te(u),g=c?await re(e,s,k,a,m,y,h,t):await ne(e,k,a,m,y,h);switch(g.kind){case"inputRequired":return g.result;case"retry":d=await l();break;case"prependText":d={...d,content:[{type:"text",text:g.text},...d.content],isError:!0};break;case"none":break}}return M(d)}}function X(e){return!!e?.getClientCapabilities()?.elicitation?.url}function Z(e){const t=e?.getClientCapabilities()?.elicitation;if(!t)return"none";const r=[t.form?"form":void 0,t.url?"url":void 0].filter(Boolean);return r.length>0?r.join("+"):"none"}async function z(e,t){const r=new Map;for(const n of new Set(t.map(s=>s.api)))r.set(n,await G(e,n));return r}function ee(e,t){const r=t.get(e.api);if(!r)return{...e,scheme:e.scheme??q};const n=e.scheme&&r.schemes[e.scheme]?e.scheme:r.defaultScheme;return{...e,scheme:n}}function te(e){const t=new Set;for(const[r,n]of Object.entries(e??{}))for(const s of Object.keys(n))t.add(b({api:r,scheme:s}));return t}function A(e,t){return e.status===401&&!t.has(b(e))}function I(e,t){const r=t.get(e.api);return r&&e.scheme?B(r.schemes,e.scheme):void 0}function R(e,t){const r=t.get(e.api);return!r||!e.schemes?.length?[]:W(r,e.schemes)}function _(e,t){return R(e,t).length>1}function w(e,t){return e.map(r=>P(r,I(r,t))).join(`
|
|
2
2
|
|
|
3
|
-
`)}function C(e,t,r){const n=e.map(s=>v(s,
|
|
3
|
+
`)}function C(e,t,r){const n=e.map(s=>v(s,R(s,r)));return t.length>0&&n.push(w(t,r)),n.join(`
|
|
4
4
|
|
|
5
|
-
`)}function O(e,t,r,n,s){const[i,...
|
|
5
|
+
`)}function O(e,t,r,n,s){const[i,...c]=t,o=s.get(i.api),a=i.scheme??o?.defaultScheme??q,p=I(i,s);return{subject:r,baseUrl:e.baseUrl??"",target:{api:i.api,name:i.name,scheme:a,...p?{schemeLabel:p}:{}},auth:o?.schemes[a]??{kind:"raw"},...o?{apiAuth:o}:{},kv:n,hasExistingSecret:!1,reason:{kind:"authFailure",status:i.status,otherApiNames:c.map(u=>u.name)}}}async function re(e,t,r,n,s,i,c,o){if(N(t.mcpReq.inputResponses))return{kind:"none"};const a=r.filter(l=>A(l,i));if(a.length===0)return{kind:"prependText",text:w(r,c)};const p=a.filter(l=>_(l,c));if(p.length>0){const l=r.filter(d=>!p.includes(d));return{kind:"prependText",text:C(p,l,c)}}const u=O(e,a,n,s,c);return X(o)?{kind:"inputRequired",result:await $(u)}:{kind:"prependText",text:await T(u)}}async function ne(e,t,r,n,s,i){const c=t.filter(u=>A(u,s));if(c.length===0)return{kind:"prependText",text:w(t,i)};const o=c.filter(u=>_(u,i));if(o.length>0){const u=t.filter(l=>!o.includes(l));return{kind:"prependText",text:C(o,u,i)}}const a=O(e,c,r,n,i);return{kind:"prependText",text:await Q(`${r}:${b(c[0])}`,()=>T(a))}}function ie(e){return{sessionId:e.sessionId,authInfo:e.http?.authInfo,requestId:e.mcpReq.id,_meta:e.mcpReq._meta}}function Se(e){return{user:e.user,config:e.config,outdir:e.outdir,baseUrl:e.baseUrl,params:e.params,query:e.query,cookies:e.cookies,apiDescriptionsMap:e.apiDescriptionsMap,graphqlDescriptions:e.graphqlDescriptions,products:e.products,accessToken:e.accessToken,metadata:e.metadata,upstreamAuthorization:e.upstreamAuthorization,mcpMode:e.mcpMode,elicitationEnabled:e.elicitationEnabled,gatewayFetchBudgetPerExec:e.gatewayFetchBudgetPerExec,clientIp:e.clientIp,upstreamSecrets:e.upstreamSecrets,searchIgnoredRoutes:e.searchIgnoredRoutes}}export{he as connectMcpServer,fe as createMcpServer,ge as createToolDispatch,Se as extractSerializableContext};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { McpToolRegistration } from '../../../types';
|
|
2
|
+
import type { McpToolHandler, McpToolHandlerExtended } from '../types.js';
|
|
3
|
+
type ToolHandlers = Record<string, McpToolHandler | McpToolHandlerExtended>;
|
|
4
|
+
type ToolModuleLoader = (importPath: string) => Promise<{
|
|
5
|
+
default: ToolHandlers;
|
|
6
|
+
}>;
|
|
7
|
+
/**
|
|
8
|
+
* Names of the tools whose handler declares an `isAvailable` predicate — the ones the live
|
|
9
|
+
* server enables per caller. Includes every tool of a handler module that fails to load.
|
|
10
|
+
*/
|
|
11
|
+
export declare function findGatedToolNames(registrations: McpToolRegistration[], load?: ToolModuleLoader): Promise<string[]>;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=find-gated-tool-names.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const r=t=>import(t);async function f(t,o=r){const n=[];for(const{name:a,importPath:i}of t){const e=await c(i,o);(!e||s(e[a]))&&n.push(a)}return n}async function c(t,o){try{const{default:n}=await o(t);return n}catch{return}}function s(t){return typeof t=="object"&&typeof t.isAvailable=="function"}export{f as findGatedToolNames};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{appendFile as D}from"node:fs/promises";import{existsSync as
|
|
1
|
+
import{appendFile as D}from"node:fs/promises";import{existsSync as v}from"node:fs";import b from"node:path";import{Client as N}from"typesense";import{SEARCH_GROUP_FACET_FIELD as A,SEARCH_PRODUCT_FIELD as q,SEARCH_RBAC_FIELD as T,SEARCH_VERSION_FIELD as h}from"../../../../../constants/common.js";import{DISABLE_DEEP_LINK_IF_FIELDS_EXIST as L,HIGHLIGHTED_TEXT_MAX_LENGTH as R,SEARCH_DATA_EXPORT_FOLDER as C,SEARCH_DOCUMENT_METADATA_KEY as j,SEARCH_GROUP_LIMIT as O,SEARCH_MAX_FACET_VALUES as y,SEARCH_MAX_INMEMORY_DOCUMENTS_COUNT as k}from"../../../../constants/plugins/search.js";import{telemetryTraceStep as P}from"../../../../telemetry/helpers/trace-step.js";import{envConfig as _}from"../../../../config/env-config.js";import{logger as F}from"../../../../tools/notifiers/logger.js";import{ensureDir as E}from"../../../../utils/index.js";class X{#e=null;#o="";#t=[{name:"title",type:"string",facet:!1,optional:!0},{name:"text",type:"string",facet:!1,optional:!0},{name:"path",type:"string[]",facet:!1,optional:!0},{name:"isAdditionalOperation",type:"bool",facet:!1,optional:!0,queryable:!1},{name:"deprecated",type:"bool",facet:!1,optional:!0,queryable:!1},{name:"parameters",type:"object[]",facet:!1,optional:!0},{name:"parameters.name",type:"string[]",facet:!1,optional:!0,queryable:!1},{name:"parameters.description",type:"string[]",facet:!1,optional:!0,queryable:!1},{name:"parameters.place",type:"string[]",facet:!1,optional:!0,queryable:!1},{name:"parameters.path",type:"string[]",facet:!1,optional:!0,queryable:!1},{name:q,type:"string",facet:!0,optional:!0},{name:h,type:"object",facet:!0,optional:!0},{name:T,type:"string[]",facet:!0},{name:"metadata_curated",type:"bool",facet:!1,optional:!0,queryable:!1},{name:"metadata_keywords_excludes",type:"string[]",facet:!1,optional:!0,queryable:!1},{name:"metadata_keywords_includes",type:"string[]",facet:!1,optional:!0,queryable:!1}];#s=new Map;#r={ORGANIZATION_ID:_.ORGANIZATION_ID??"",PROJECT_ID:_.PROJECT_ID??"",TYPESENSE_API_URL:_.TYPESENSE_API_URL??"",TYPESENSE_API_KEY:_.TYPESENSE_API_KEY??""};constructor(){if(!_.isBuildMode){for(const[e,t]of Object.entries(this.#r))if(!t)throw new Error(`Cannot initialize search index. ${e} is not set`);this.#o=`${this.#r.ORGANIZATION_ID}_${this.#r.PROJECT_ID}_`,this.#e=new N({nodes:[{url:this.#r.TYPESENSE_API_URL}],apiKey:this.#r.TYPESENSE_API_KEY,connectionTimeoutSeconds:5})}}async initIndexSchema(e){this.#_(e)}async addDocuments(e,t){const{locale:s,outDir:a}=t,r=e.map(n=>{const o={...n},c=n[j];return c&&(c.curated&&(o.metadata_curated=c.curated),c.excludes&&(o.metadata_keywords_excludes=c.excludes),c.includes&&(o.metadata_keywords_includes=c.includes)),o}),i=this.#s.get(s)??[];this.#s.set(s,i.concat(r)),i.length+e.length>=k&&await this.exportDocuments(a)}async search(e,t){return await P("com.redocly.search.performed",async s=>{s?.setAttribute("id","searchQueryId"),s?.setAttribute("object","searchQuery"),s?.setAttribute("uri","urn:redocly:realm:api:searchQuery:searchQueryId");const{query:a,locale:r,filter:i,loadMore:n}=e,o=this.#u(r),c={collection:o,q:a||"*",query_by:this.#f(),max_facet_values:y};if(s?.setAttribute("collectionName",o),s?.setAttribute("locale",r),this.#e)if(n){const{groupKey:l,offset:u}=n,f={offset:u,filter_by:this.#a(t,i),facet_by:"*",per_page:O};return s?.setAttribute("isLoadMore",!0),s?.setAttribute("filter",f.filter_by),this.#p(await this.#e.collections(o).documents().search({...c,...f}),l)}else{const l=this.#a(t,i);s?.setAttribute("filter",l);const u=await this.#e.multiSearch.perform({searches:this.#b(t,i)},c),f=this.#i(u,this.#t.filter(p=>p.facet).map(p=>p.name));f&&F.warn("Search facet requests failed: %s",f);const m=this.#m(u),d=(m[A]??[]).map(p=>p.value);s?.setAttribute("groups",d.join(",")),d.length===y&&F.warn("Search category facet list reached the limit of %s; results for some categories may be missing",y);const I=[...new Set(this.#h(i))].slice(0,y),g=I.length?I:d;if(!g.length)return{facets:m,documents:{},groupCounts:{}};const $=await this.#e.multiSearch.perform({searches:g.map(p=>({filter_by:`${A}:=${this.#n(p)} && ${l}`,per_page:O}))},c),S=this.#i($,g);return S&&F.warn("Search category requests failed: %s",S),this.#d($,g,m,i)}else return{facets:{},documents:{}}})}#p(e,t){const s={facets:{},documents:{[t]:[]},groupCounts:typeof e.found=="number"?{[t]:e.found}:{}};if(e.hits&&e.hits.length)for(const a of e.hits)s.documents[t].push({document:this.#c(a),highlight:this.#l(a)});return s}#m(e){const t={};if("results"in e){for(const s of e.results)if(s.facet_counts&&s.facet_counts.length)for(const a of s.facet_counts)t[a.field_name]=a.counts.map(r=>({value:r.value,count:r.count}))}return t}#i(e,t){return"results"in e?e.results.map((s,a)=>({result:s,label:t[a]})).filter(({result:s})=>"error"in s).map(({result:s,label:a})=>`${a}: ${s.error}`).join("; "):""}#d(e,t,s,a){const r={},i={facets:s,documents:{},groupCounts:r};for(const n of this.#h(a))i.documents[n]=[];return"results"in e&&e.results.forEach((n,o)=>{const c=t[o];typeof n.found=="number"&&(r[c]=n.found);const l=n.hits??[];l.length&&(i.documents[c]=l.map(u=>({document:this.#c(u),highlight:this.#l(u)})))}),i}#c(e){return this.#y(e)}#l(e){const t={},s=[];for(const[a,r]of Object.entries(e.highlight??{}))if(Array.isArray(r)){if(a==="path"){const i=r,n=[];for(const o of i)n.push(o.snippet);t.path=n}else if(a==="parameters"){const i=r;for(const n of i){let o=!1;for(const[c,l]of Object.entries(n))if(c!=="deepLink")if(Array.isArray(l))for(const u of l)u.matched_tokens?.length&&(o=!0);else l.matched_tokens?.length&&(o=!0);if(o){const c={name:n.name?.snippet||"",description:n.description?.snippet||"",place:n.place?.snippet||"",path:n.path?.map(l=>l?.snippet)||[]};t.parameters=[c];break}}}}else t[a]=r.snippet,s.push(a);for(const[a,r]of Object.entries(e.document))!s.includes(a)&&typeof r=="string"&&(t[a]=r.length>R?`${r.substring(0,R)}...`:r);return t.parameters||(t.parameters=[]),t}#y(e){let t;const s=Object.keys(e.highlight??{});for(const r of L)if(s.some(i=>i===r))return e.document;const a=e.highlight?.parameters;if(a){for(const r of a)for(const[i,n]of Object.entries(r))if(i!=="deepLink"){if(Array.isArray(n)){for(const o of n)if(o.matched_tokens?.length){t=r.deepLink?.snippet??t;break}}else if(n.matched_tokens?.length){t=r.deepLink?.snippet??t;break}}}if(t){const r=t.split("#")[1];return{...e.document,url:`${e.document.url}#${r}`}}return e.document}async exportDocuments(e){const t='{"documents":[',s=E(b.join(e,C));for(const[a,r]of this.#s){if(r.length===0)continue;const i=E(b.join(s,`${a}.json`)),n=!v(i),o=JSON.stringify(r).substring(1).slice(0,-1),c=n?t+o:","+o;await D(i,c,{encoding:"utf8"}),this.#s.set(a,[])}}async exportIndexes(e){const t=E(b.join(e,C));for(const s of this.#s.keys()){const a=E(b.join(t,`${s}.json`)),r=`],"schemaFields":${JSON.stringify(this.#t)}}`;await D(a,r,{encoding:"utf8"})}}async import(e){}async countFacets(e,t){return await P("com.redocly.search.facets",async s=>{s?.setAttribute("id","searchFacetsId"),s?.setAttribute("object","searchFacets"),s?.setAttribute("uri","urn:redocly:realm:api:searchFacets:searchFacetsId");const{locale:a,query:r,facetQuery:i,filter:n,field:o}=e,c=this.#u(a),l={q:r||"*",query_by:this.#f(),facet_by:"*",facet_query:this.#g(i,o),filter_by:this.#a(t,n,o),max_facet_values:y};if(s?.setAttribute("collectionName",c),s?.setAttribute("query",l.q),s?.setAttribute("facetQuery",l.facet_query),s?.setAttribute("filter",l.filter_by),this.#e){const u={},f=await this.#e.collections(c).documents().search(l);if(f.facet_counts&&f.facet_counts.length)for(const m of f.facet_counts)u[m.field_name]=m.counts.map(d=>({value:d.value,count:d.count,isCounterVisible:!!r||(n?.length||0)>0}));return u}else return{}})}#u(e){return`${this.#o}${e}`}#_(e){for(const[t,{queryable:s}]of e)this.#t.find(a=>a.name===t)||this.#t.push({queryable:s,name:t,type:"string",facet:!0,optional:!0})}#f(){return this.#t.filter(e=>e.facet?e.queryable:e.queryable!==!1).map(e=>e.name).join(",")}#n(e){return`\`${e.replaceAll("`","")}\``}#a(e,t,s){const a=`${T}:[${e.teams.map(i=>`'${i}'`).join(", ")}]`;let r=`${h}.isDefault:true`;if(t&&t.length){const i=t.filter(o=>o.field!==s&&o.field!==h).map(o=>{const c=o.values.map(l=>this.#n(l));return c.length?`${o.field}:=[${c.join(", ")}]`:""}).join(" && "),n=t.find(o=>o.field===h);if(n&&n.values.length&&n.values.length===2){const o=n.values[0],c=n.values[1],l=`(${h}.folderId:! ${o} && ${h}.isDefault:true)`,u=`(${h}.folderId: ${o} && ${h}.version:=${this.#n(c)})`;r=`(${l} || ${u})`}return i?`${a} && ${i} && ${r}`:`${a} && ${r}`}else return`${a} && ${r}`}#g(e,t){return e&&t?`${t}:${e}`:""}#h(e){const t=[];if(e&&e.length)for(const s of e)s.field===A&&t.push(...s.values);return t}#b(e,t){const s=[],a=this.#t.filter(r=>r.facet);for(const r of a){const i={facet_by:r.name,filter_by:this.#a(e,t,r.name)};s.push(i)}return s}cleanupFacetValues(e){const t=e.getSearchFacets();t.forEach(s=>{s.values=[]}),e.setSearchFacets(t)}}export{X as Typesense};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DEFAULT_SEARCH_ENGINE as s,REDOCLY_TEAMS_RBAC as
|
|
1
|
+
import{DEFAULT_SEARCH_ENGINE as s,REDOCLY_TEAMS_RBAC as p}from"@redocly/config";import{envConfig as o}from"../../config/env-config.js";import{logger as E}from"../../tools/notifiers/logger.js";import{reporter as u}from"../../tools/notifiers/reporter.js";import{prepareSearchDocuments as S}from"./documents/search-documents.js";import{SearchEngine as b}from"./engines/search-engine.js";import{prepareSemanticDocuments as D}from"./ai-indexer/prepare-semantic-documents.js";import{isAiSearchEnabled as y,shouldGenerateEmbeddings as A,shouldIndexSearchDocuments as C}from"./utils.js";import{telemetryTraceStep as R}from"../../../cli/telemetry/helpers/trace-step.js";import{EntitlementsProvider as _}from"../../entitlements/entitlements-provider.js";async function B(){let e=!0;return{async afterRoutesCreated(t,n){(o.SEARCH_DEV_REINIT||e)&&await w(t,n),e=!1},id:"search"}}async function w(e,t){await R("com.redocly.build.plugin.search",async n=>{const r=e.getConfig();if(n?.setAttribute("config",JSON.stringify(r.search||{})),r.search?.hide){E.info("Search is disabled in the config, skipping");return}const m=r.search?.engine||s,i=o.isDevelopMode?o.SEARCH_DEV_DEBUG?m:s:m,{l10n:l}=e.getGlobalData(),d=y(r),c=r.access?.rbac?.features?.aiSearch,h=A(r);let a;try{a=new b(i,l)}catch(g){await u.panic(g);return}await a.initIndexSchema(e.getSearchFacets()),e.setSearchEngine(a),e.setGlobalData({searchFeatures:{advanced:{enabled:i!==s&&!r.search?.filters?.hide},ai:{enabled:d,[p]:c&&Object.keys(c).length?c:void 0}}}),C(i)&&await S(t.fs,e,a);const f=_.instance().canAccessFeature("seo")===!0&&r.seo?.llmstxt?.hide!==!0;await D(e,t,{embeddingsEnabled:h,llmstxtEnabled:f})})}export{B as searchPlugin};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { RedoclyConfig } from '@redocly/config';
|
|
1
|
+
import type { RedoclyConfig, SearchEngineType } from '@redocly/config';
|
|
2
2
|
export declare function isAiSearchEnabled(config: RedoclyConfig): boolean;
|
|
3
3
|
export declare function isDocsMcpEnabled(config: RedoclyConfig): boolean;
|
|
4
4
|
export declare function shouldGenerateEmbeddings(config: RedoclyConfig): boolean;
|
|
5
|
+
export declare function shouldIndexSearchDocuments(engine: SearchEngineType): boolean;
|
|
5
6
|
export declare function getSearchDocumentGroup(documentFacets?: Record<string, string>): string | undefined;
|
|
6
7
|
export declare function formatDocumentMetadata(metadata?: Record<string, unknown>): string;
|
|
7
8
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{SEARCH_GROUP_FACET_FIELD as o}from"../../../constants/common.js";import{DEFAULT_AI_SEARCH_ENABLED as s}from"../../constants/plugins/search.js";import{isPrimitive as c}from"../../../utils/guards/is-primitive.js";import{envConfig as
|
|
2
|
-
`)}export{
|
|
1
|
+
import{SEARCH_GROUP_FACET_FIELD as o}from"../../../constants/common.js";import{DEFAULT_AI_SEARCH_ENABLED as s}from"../../constants/plugins/search.js";import{isPrimitive as c}from"../../../utils/guards/is-primitive.js";import{envConfig as n}from"../../config/env-config.js";import{EntitlementsProvider as i}from"../../entitlements/entitlements-provider.js";function a(e){if(n.isDevelopMode)return n.SEARCH_DEV_DEBUG;if(!i.instance().canAccessFeature("aiSearchLimit"))return!1;const t=e.aiAssistant;return typeof t?.hide=="boolean"?!t?.hide:s}function u(e){return i.instance().canAccessFeature("mcp")?!e.mcp?.hide&&!e.mcp?.docs?.hide:!1}function S(e){const r=a(e),t=u(e);return(r||t)&&n.isProductionEnv}function l(e){return e==="typesense"?!n.REDOCLY_SKIP_TYPESENSE_INDEXING:!0}function A(e){let r;return e&&Object.keys(e).forEach(t=>{t===o&&(r=e[t])}),r}function D(e){return Object.entries(e||{}).map(([r,t])=>` - ${r}: ${c(t)?t:JSON.stringify(t)}`).join(`
|
|
2
|
+
`)}export{D as formatDocumentMetadata,A as getSearchDocumentGroup,a as isAiSearchEnabled,u as isDocsMcpEnabled,S as shouldGenerateEmbeddings,l as shouldIndexSearchDocuments};
|
package/dist/server/store.d.ts
CHANGED
|
@@ -125,6 +125,7 @@ export declare class Store {
|
|
|
125
125
|
addApiRoute: (route: ApiRoute) => void;
|
|
126
126
|
addMcpTools: (importPath: string, tools: McpToolSchema[]) => void;
|
|
127
127
|
getMcpTools: () => McpToolRegistration[];
|
|
128
|
+
markGatedMcpTools: (toolNames: string[]) => void;
|
|
128
129
|
addSkills: (skills: Skill[]) => void;
|
|
129
130
|
getSkills: () => Skill[];
|
|
130
131
|
addMiddleware: (middleware: MiddlewareDetails) => void;
|
package/dist/server/store.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import C from"@markdoc/markdoc";import{getPathnameForLocale as A}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as g}from"../constants/common.js";import{DEFAULT_TITLE as T}from"./constants/common.js";import{GATED_MARKDOC_TAGS as R}from"./constants/entitlements.js";import{MARKDOC_PARTIALS_DATA_KEY as m,MARKDOC_PARTIALS_DEPS_KEY as O}from"./constants/common.js";import{isObject as M}from"../utils/guards/is-object.js";import{mapObject as k}from"../utils/object/map-object.js";import{getValueDeep as D}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as L}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as h}from"../utils/path/normalize-route-slug.js";import{isLocalLink as y}from"../utils/path/is-local-link.js";import{reporter as S}from"./tools/notifiers/reporter.js";import{logger as u}from"./tools/notifiers/logger.js";import{sha1 as H}from"./utils/crypto/sha1.js";import{envConfig as _,setEnv as B}from"./config/env-config.js";import{KvService as F}from"./persistence/kv/services/kv-service.js";import{SandboxContext as G}from"./utils/sandbox-context.js";import{writeSharedData as I}from"./utils/index.js";import{renderComponents as N}from"./ssr/render.js";import{readStaticData as V,writeStaticData as q}from"./utils/static-data.js";import{parseAndResolveMarkdoc as j}from"./plugins/markdown/compiler.js";import{getMarkdocOptions as U}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as w}from"./entitlements/entitlements-provider.js";import{isL10nPath as K}from"./fs/utils/is-l10n-path.js";import{resolveGlobMapValue as x}from"./utils/globs.js";import{replaceEnvVariablesDeep as J}from"./utils/envs/replace-env-variables-deep.js";import{findRedirect as W}from"./utils/redirects/find-redirect.js";import{followRedirectChain as $}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as z}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as Y}from"../cli/telemetry/helpers/trace-step.js";const v={routesBySlug:"map",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",config:"object",ssr:"object",searchFacets:"map",routesPartials:"map",mcpToolHandlers:"map",skills:"map"},E="PLAN_GATES",Z=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],Me="userDefinedApiFunctions";class P{routesBySlug=new Map;replacedEnvVars={};unsetEnvVars=new Set;lifecycleContext;newRoutes=[];#t={};routesByFsPath=new Map;apiRoutes=[];middleware=[];routesSharedData=new Map;sharedDataDeps=new Map;sharedDataMarkdocComponents=new Map;routesDynamicComponents=new Map;routesPartials=new Map;ssr={preBodyTags:[],postBodyTags:[],headTags:[]};searchFacets=new Map;searchEngine;templates=new Map;browserPlugins=new Set;apiRoutesRequestHandlers=new Map;mcpToolHandlers=new Map;skills=new Map;serverPropsGetters=new Map;pagePropsGetters=new Map;listeners=new Map;globalData={};#o=void 0;config={configFilePath:"",redirects:{},wildcardRedirectsTree:{},access:{rbac:{},requiresLogin:!1},directoryPermissions:{},devLogin:!1,ssoDirect:{}};#i;serverMode;serverOutDir;outdir;buildRevision=0;hasSitemap=!1;compilationErrors=[];#n;userCodeReady;#s=Promise.resolve();#c;#l=Promise.resolve();#d;#u=Promise.resolve();#h;#e=!1;#r=0;#f=0;#a=new Map;constructor({outdir:e,contentDir:t,serverMode:s=!1,serverOutDir:a}){this.#i=t,this.outdir=e,this.serverMode=s,this.serverOutDir=a,G.register({serverOutDir:a,readRoots:[e,process.cwd()]}),this.userCodeReady=new Promise(r=>{this.#n=r})}on(e,t){const s=this.listeners.get(e);s?s.add(t):this.listeners.set(e,new Set([t]))}queueEvent=(e,t,...s)=>{this.#a.set(e+String(t),[e,t,...s])};runListeners=(e,t,...s)=>{for(const a of this.listeners.get(e)||new Set)t?a(t,...s):a(...s)};startPluginsRun(){this.clear(),this.#s=new Promise(e=>{this.#c=e})}waitForPluginsLifecycle(){return Promise.all([this.#s,this.#l,this.#u])}waitForPluginsRun(){return this.#s}finishPluginsRun(){this.#c?.();for(const e of this.#a.values())this.runListeners(...e);this.#a.clear()}startEsbuildRun(){this.#l=new Promise(e=>{this.#d=e})}finishEsbuildRun(){this.#d?.()}markRequestHandlersEntryOutdated(){this.#r++,!this.#e&&(this.#e=!0,this.#u=new Promise(e=>{this.#h=e}))}noteRequestHandlersEntryBuildStart(){this.#f=this.#r}markRequestHandlersEntryUpdated(){this.#e&&(this.#r>this.#f||(this.#e=!1,this.#h?.()))}get contentDir(){if(this.serverMode)throw new Error("contentDir should not be used in server mode");return this.#i}markUserCodeReady(){this.#n?.(!0)}async reloadMarkdocOptions(){await Y("com.redocly.build.reloadMarkdocOptions",async()=>{const e=w.instance(),t=await U(this.serverOutDir),s=Object.fromEntries(Object.entries(t.tags).filter(([a])=>R[a]!=null?e.canAccessFeature(R[a]):!0));this.#o={...t,tags:s}})}get markdocOptions(){return{...this.#o,partials:this.getGlobalConfig(m),themeConfig:this.config.markdown}}setGlobalData=e=>{const t=this.globalData,s={...this.globalData,...e};this.globalData=s,JSON.stringify(s)!==JSON.stringify(t)&&this.queueEvent("global-data-updated",void 0,s)};getGlobalData=()=>this.globalData;getKv=async()=>F.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:e,context:t,deps:s,resource:a})=>{const{data:{info:r,ast:o},compoundHash:l}=await j(e,this.markdocOptions,{actions:this,context:t},a);for(const i of r.sharedDataDeps||[]){for(const n of s?.routeSlugs||[])this.addRouteSharedData(n,i,i);for(const n of s?.sharedDataIds||[]){const c=this.sharedDataDeps.get(n)||new Set;c.add(i),this.sharedDataDeps.set(n,c)}}for(const i of r.dynamicMarkdocComponents||[]){for(const n of s?.routeSlugs||[]){const c=this.routesDynamicComponents.get(n)||new Set;c.add(i),this.routesDynamicComponents.set(n,c)}for(const n of s?.sharedDataIds||[]){const c=this.sharedDataMarkdocComponents.get(n)||new Set;c.add(i),this.sharedDataMarkdocComponents.set(n,c)}}if(s?.routeSlugs&&r.partials?.length)for(const i of s.routeSlugs){const n=this.routesPartials.get(i)||[];for(const c of r.partials)n.includes(c)||n.push(c);this.routesPartials.set(i,n)}return{info:r,ast:o,compoundHash:l}};async loadApiDocuments(e){return(await e.cache.load(".","api-docs")).data}setSearchEngine(e){this.searchEngine=e}setSearchFacets=e=>{this.searchFacets=e};setGlobalConfig=e=>{const t=Object.keys(e);for(const o of t)for(const l in this.replacedEnvVars)if(l===o||l.startsWith(`${o}:`)){const i=l.split(":"),{error:n,value:c}=D(e,i);(n||c!==this.replacedEnvVars[l].replaced)&&delete this.replacedEnvVars[l]}const{resolvedObj:s,unsetEnvVars:a,replacedValues:r}=J(e);for(const o of a)this.unsetEnvVars.add(o);Object.assign(this.replacedEnvVars,r),Object.assign(this.config,s)};getConfig=()=>this.config;getGlobalConfig=e=>this.config[e];getSearchFacets=()=>this.searchFacets;addRedirect=(e,t,s={})=>{if(!w.instance().canAccessFeature("redirects")&&e!=="/")return;this.config.redirects||(this.config.redirects={});const o=h(e).toLowerCase();this.config.redirects[o]=t;const{trackOriginalSource:l=!0}=s,i=this.getGlobalConfig("originalRedirectSources");l&&Array.isArray(i)&&!i.includes(o)&&i.push(o),o.endsWith("*")&&z(this.config.wildcardRedirectsTree,o)};getRedirect=e=>{const t=h(e).toLowerCase(),s=W(t,this.config.redirects,this.config.wildcardRedirectsTree);if(!s)return null;if(y(s.to)){const a=h(s.to).toLowerCase();if(!a.endsWith("*")&&$(a,[t],this.config.redirects,this.config.wildcardRedirectsTree).type==="cycle")return null}return{to:s.to,type:s.type}};createSharedData=async(e,t,s)=>{if(s&&this.#t[e]===s)return e;const a=JSON.stringify(t),r=s??H(a);return this.#t[e]===r||(this.#t[e]=r,await I(e,a,this.outdir),this.queueEvent("shared-data-updated",e)),e};addRouteSharedData=(e,t,s)=>{const a=L(e),r=this.routesSharedData.get(a)||{};r[t]=s,this.routesSharedData.set(a,r),u.verbose(`Adding shared data to ${e}, ${t}, ${s}`)};getRouteSharedDataByFsPath=e=>{const t=this.routesByFsPath.get(e);return t?this.routesSharedData.get(t)||{}:{}};getPartialsForRoute=e=>{const t=this.getGlobalConfig(m)||{},s=this.routesPartials.get(e);if(!s||s.length===0)return{};const a=this.getGlobalConfig(O)||{},r=new Set(s),o=Array.from(s);for(let i=0;i<o.length;i++){const n=o[i],c=a[n]?.partials??[];for(const d of c)r.has(d)||(r.add(d),o.push(d))}const l={};for(const i of r)t[i]&&(l[i]=t[i]);return l};addRoute=e=>{const s={...x(e.fsPath,this.config.metadataGlobs),...e.metadata||{}};this.newRoutes.push({...e,metadata:s}),u.verbose("Created route %s",e.slug)};addRouteSharedDataToAllLocales=(e,t,s)=>{const a=[g,...this.lifecycleContext?.fs.localeFolders||[]].map(r=>({code:r,name:r}));for(const r of a){const o=A(e,g,r.code,a);this.addRouteSharedData(o,t,s)}};addApiRoute=e=>{this.apiRoutes.push(e),u.verbose("Created API route %s",e.slug)};addMcpTools=(e,t)=>{for(const s of t)this.mcpToolHandlers.set(s.name,{...s,importPath:e}),u.verbose("Created MCP tool %s",s.name)};getMcpTools=()=>Array.from(this.mcpToolHandlers.values());addSkills=e=>{for(const t of e)this.skills.set(t.slug,t),u.verbose("Discovered skill %s",t.slug)};getSkills=()=>Array.from(this.skills.values());addMiddleware=e=>{this.middleware.push(e),u.verbose("Created middleware %s",e.id)};setResourceResponseHeaders=(e,t)=>{this.config.responseHeaders||(this.config.responseHeaders={});const s=new Set(t.map(o=>o.name.toLowerCase())),r=[...(this.config.responseHeaders[e]??[]).filter(o=>!s.has(o.name.toLowerCase())),...t];this.config.responseHeaders[e]=r};getRouteByFsPath=e=>{const t=this.routesByFsPath.get(e);return t?this.getRouteBySlug(t):void 0};getRouteBySlug=(e,t={})=>{const{followRedirect:s=!0}=t,a=this.getRedirect(e);return s&&a?this.routesBySlug.get(h(a.to)):this.routesBySlug.get(e)};hasRouteOrRedirectBySlug=e=>{if(this.routesBySlug.has(e))return!0;const t=this.getRedirect(e);if(!t)return!1;if(!y(t.to))return!0;const s=h(t.to);return this.routesBySlug.has(s)};getRoutesByTemplateId=e=>this.newRoutes.filter(t=>t.templateId===e);getAllRoutesForLocale=(e=g)=>{const t=Array.from(this.routesBySlug.values()),s=e.toLowerCase();return t.filter(a=>e===g?!K(a.fsPath):a.slug.startsWith(`/${s}`))};getAllRoutes=()=>Array.from(this.routesBySlug.values());getAllApiRoutes=()=>this.apiRoutes;getAllMiddleware=()=>this.middleware;getTemplate=e=>this.templates.get(e);getRequestHandler=e=>this.apiRoutesRequestHandlers.get(e);createTemplate=(e,t)=>(this.templates.set(e,t),e);addBrowserPlugin=e=>{this.browserPlugins.add(e)};createRequestHandler=(e,t)=>(this.apiRoutesRequestHandlers.set(e,t),e);clearRequestHandlersByPrefix=e=>{for(const t of this.apiRoutesRequestHandlers.keys())t.startsWith(e)&&this.apiRoutesRequestHandlers.delete(t)};registerServerPropsGetter=(e,t)=>(this.serverPropsGetters.set(e,t),e);registerPagePropsGetter=(e,t)=>{this.pagePropsGetters.set(e,t)};async writeRouteStaticData(e,t){const s=await this.resolveRouteStaticData(e,t,!1);s&&q(e.slug,s,this.outdir)}async resolveRouteStaticData(e,t,s){if(this.serverMode)return V(e.slug,this.outdir);const a={...this,contentDir:this.contentDir,parseMarkdoc:({input:c,context:d,resource:f})=>this.parseMarkdoc({input:c,context:d,deps:{routeSlugs:[e.slug]},resource:f})},r=await e.getStaticData?.(e,a)||{},o=new Set(this.routesDynamicComponents.get(e.slug)),l=this.routesSharedData.get(e.slug)||{};for(const c of Object.values(l)){const d=this.sharedDataMarkdocComponents.get(c);d&&d.forEach(p=>o.add(p));const f=this.sharedDataDeps.get(c);f&&f.forEach(p=>this.addRouteSharedData(e.slug,p,p))}const i=this.getGlobalConfig("seo"),n=r?.frontmatter||{};return{...r,frontmatter:{...n,seo:{...n?.seo,title:n?.seo?.title||await e.getNavText?.()}},props:{...r.props,dynamicMarkdocComponents:Array.from(o),metadata:{...r?.props?.metadata,...e.metadata},seo:{title:T,...i,...r.props?.seo},compilationErrors:this.compilationErrors},lastModified:s||!e.fsPath?null:await this.lifecycleContext?.fs.getLastModified(e.fsPath)}}addSsrComponents(e,t){if(!e?.length)return;const s=typeof e[0]=="string"?e.join(""):N(e);s&&(t==="head"?this.ssr.headTags.push(s):t==="preBody"?this.ssr.preBodyTags.push(s):this.ssr.postBodyTags.push(s))}clear=()=>{this.routesByFsPath.clear(),this.templates.clear(),this.newRoutes=[],this.routesBySlug.clear(),this.apiRoutes=[],this.middleware=[],this.routesSharedData.clear(),this.sharedDataDeps.clear(),this.sharedDataMarkdocComponents.clear(),this.routesDynamicComponents.clear(),this.routesPartials.clear(),this.skills.clear(),this.config.redirects={},this.config.wildcardRedirectsTree={},this.config.directoryPermissions={},this.ssr={preBodyTags:[],postBodyTags:[],headTags:[]}};async toJson(){const e=[];for(const[s,a]of Object.entries(v))switch(a){case"map":const r=Array.from(this[s].entries());e.push([s,r]);break;case"object":s==="config"&&e.push([s,await this.getConfigWithEnvPlaceholders()]),e.push([s,this[s]]);break;default:throw new Error("Invalid format")}const t=Object.fromEntries(e);return t[E]=_.PLAN_GATES,t}static fromJson(e,t){const s=new P(t);for(const[r,o]of Object.entries(v))switch(o){case"map":s[r]=new Map(e[r]);break;case"object":if(r==="config"){s.setGlobalConfig(e[r]);break}s[r]=e[r];break;default:throw new Error("Invalid format")}s.config[m]=Q(s.config[m]||{});const a=e[E];return a&&B("PLAN_GATES",a),s}async getConfigWithEnvPlaceholders(){const e=JSON.parse(JSON.stringify(this.config));for(const t in this.replacedEnvVars){const{original:s}=this.replacedEnvVars[t],a=t.split(":"),r=a.pop(),{error:o,value:l}=D(e,a);if(o||!M(l)&&!Array.isArray(l)){await S.panicOnBuild(`Failed to replace env var with env name for ${t}`);continue}l[r]=s}return e}async reportUnsetEnvVars(){if(this.unsetEnvVars.size===0)return;const e=Array.from(this.unsetEnvVars).filter(s=>!Z.includes(s));if(e.length===0)return;const t=`Failed to resolve config. The following environment variables are not set: ${e.join(", ")}`;await S.panicOnBuildContentError(t)}}function Q(b){return k(b,e=>C.Ast.fromJSON(JSON.stringify(e)))}export{P as Store,Me as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
|
|
1
|
+
import C from"@markdoc/markdoc";import{getPathnameForLocale as A}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as g}from"../constants/common.js";import{DEFAULT_TITLE as T}from"./constants/common.js";import{GATED_MARKDOC_TAGS as R}from"./constants/entitlements.js";import{MARKDOC_PARTIALS_DATA_KEY as m,MARKDOC_PARTIALS_DEPS_KEY as O}from"./constants/common.js";import{isObject as M}from"../utils/guards/is-object.js";import{mapObject as k}from"../utils/object/map-object.js";import{getValueDeep as y}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as L}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as h}from"../utils/path/normalize-route-slug.js";import{isLocalLink as D}from"../utils/path/is-local-link.js";import{reporter as S}from"./tools/notifiers/reporter.js";import{logger as u}from"./tools/notifiers/logger.js";import{sha1 as H}from"./utils/crypto/sha1.js";import{envConfig as G,setEnv as _}from"./config/env-config.js";import{KvService as B}from"./persistence/kv/services/kv-service.js";import{SandboxContext as F}from"./utils/sandbox-context.js";import{writeSharedData as I}from"./utils/index.js";import{renderComponents as N}from"./ssr/render.js";import{readStaticData as V,writeStaticData as q}from"./utils/static-data.js";import{parseAndResolveMarkdoc as j}from"./plugins/markdown/compiler.js";import{getMarkdocOptions as U}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as w}from"./entitlements/entitlements-provider.js";import{isL10nPath as K}from"./fs/utils/is-l10n-path.js";import{resolveGlobMapValue as x}from"./utils/globs.js";import{replaceEnvVariablesDeep as J}from"./utils/envs/replace-env-variables-deep.js";import{findRedirect as W}from"./utils/redirects/find-redirect.js";import{followRedirectChain as $}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as z}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as Y}from"../cli/telemetry/helpers/trace-step.js";const v={routesBySlug:"map",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",config:"object",ssr:"object",searchFacets:"map",routesPartials:"map",mcpToolHandlers:"map",skills:"map"},E="PLAN_GATES",Z=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],Me="userDefinedApiFunctions";class P{routesBySlug=new Map;replacedEnvVars={};unsetEnvVars=new Set;lifecycleContext;newRoutes=[];#t={};routesByFsPath=new Map;apiRoutes=[];middleware=[];routesSharedData=new Map;sharedDataDeps=new Map;sharedDataMarkdocComponents=new Map;routesDynamicComponents=new Map;routesPartials=new Map;ssr={preBodyTags:[],postBodyTags:[],headTags:[]};searchFacets=new Map;searchEngine;templates=new Map;browserPlugins=new Set;apiRoutesRequestHandlers=new Map;mcpToolHandlers=new Map;skills=new Map;serverPropsGetters=new Map;pagePropsGetters=new Map;listeners=new Map;globalData={};#o=void 0;config={configFilePath:"",redirects:{},wildcardRedirectsTree:{},access:{rbac:{},requiresLogin:!1},directoryPermissions:{},devLogin:!1,ssoDirect:{}};#i;serverMode;serverOutDir;outdir;buildRevision=0;hasSitemap=!1;compilationErrors=[];#n;userCodeReady;#s=Promise.resolve();#c;#l=Promise.resolve();#d;#u=Promise.resolve();#h;#e=!1;#r=0;#f=0;#a=new Map;constructor({outdir:e,contentDir:t,serverMode:s=!1,serverOutDir:a}){this.#i=t,this.outdir=e,this.serverMode=s,this.serverOutDir=a,F.register({serverOutDir:a,readRoots:[e,process.cwd()]}),this.userCodeReady=new Promise(r=>{this.#n=r})}on(e,t){const s=this.listeners.get(e);s?s.add(t):this.listeners.set(e,new Set([t]))}queueEvent=(e,t,...s)=>{this.#a.set(e+String(t),[e,t,...s])};runListeners=(e,t,...s)=>{for(const a of this.listeners.get(e)||new Set)t?a(t,...s):a(...s)};startPluginsRun(){this.clear(),this.#s=new Promise(e=>{this.#c=e})}waitForPluginsLifecycle(){return Promise.all([this.#s,this.#l,this.#u])}waitForPluginsRun(){return this.#s}finishPluginsRun(){this.#c?.();for(const e of this.#a.values())this.runListeners(...e);this.#a.clear()}startEsbuildRun(){this.#l=new Promise(e=>{this.#d=e})}finishEsbuildRun(){this.#d?.()}markRequestHandlersEntryOutdated(){this.#r++,!this.#e&&(this.#e=!0,this.#u=new Promise(e=>{this.#h=e}))}noteRequestHandlersEntryBuildStart(){this.#f=this.#r}markRequestHandlersEntryUpdated(){this.#e&&(this.#r>this.#f||(this.#e=!1,this.#h?.()))}get contentDir(){if(this.serverMode)throw new Error("contentDir should not be used in server mode");return this.#i}markUserCodeReady(){this.#n?.(!0)}async reloadMarkdocOptions(){await Y("com.redocly.build.reloadMarkdocOptions",async()=>{const e=w.instance(),t=await U(this.serverOutDir),s=Object.fromEntries(Object.entries(t.tags).filter(([a])=>R[a]!=null?e.canAccessFeature(R[a]):!0));this.#o={...t,tags:s}})}get markdocOptions(){return{...this.#o,partials:this.getGlobalConfig(m),themeConfig:this.config.markdown}}setGlobalData=e=>{const t=this.globalData,s={...this.globalData,...e};this.globalData=s,JSON.stringify(s)!==JSON.stringify(t)&&this.queueEvent("global-data-updated",void 0,s)};getGlobalData=()=>this.globalData;getKv=async()=>B.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:e,context:t,deps:s,resource:a})=>{const{data:{info:r,ast:o},compoundHash:l}=await j(e,this.markdocOptions,{actions:this,context:t},a);for(const i of r.sharedDataDeps||[]){for(const n of s?.routeSlugs||[])this.addRouteSharedData(n,i,i);for(const n of s?.sharedDataIds||[]){const c=this.sharedDataDeps.get(n)||new Set;c.add(i),this.sharedDataDeps.set(n,c)}}for(const i of r.dynamicMarkdocComponents||[]){for(const n of s?.routeSlugs||[]){const c=this.routesDynamicComponents.get(n)||new Set;c.add(i),this.routesDynamicComponents.set(n,c)}for(const n of s?.sharedDataIds||[]){const c=this.sharedDataMarkdocComponents.get(n)||new Set;c.add(i),this.sharedDataMarkdocComponents.set(n,c)}}if(s?.routeSlugs&&r.partials?.length)for(const i of s.routeSlugs){const n=this.routesPartials.get(i)||[];for(const c of r.partials)n.includes(c)||n.push(c);this.routesPartials.set(i,n)}return{info:r,ast:o,compoundHash:l}};async loadApiDocuments(e){return(await e.cache.load(".","api-docs")).data}setSearchEngine(e){this.searchEngine=e}setSearchFacets=e=>{this.searchFacets=e};setGlobalConfig=e=>{const t=Object.keys(e);for(const o of t)for(const l in this.replacedEnvVars)if(l===o||l.startsWith(`${o}:`)){const i=l.split(":"),{error:n,value:c}=y(e,i);(n||c!==this.replacedEnvVars[l].replaced)&&delete this.replacedEnvVars[l]}const{resolvedObj:s,unsetEnvVars:a,replacedValues:r}=J(e);for(const o of a)this.unsetEnvVars.add(o);Object.assign(this.replacedEnvVars,r),Object.assign(this.config,s)};getConfig=()=>this.config;getGlobalConfig=e=>this.config[e];getSearchFacets=()=>this.searchFacets;addRedirect=(e,t,s={})=>{if(!w.instance().canAccessFeature("redirects")&&e!=="/")return;this.config.redirects||(this.config.redirects={});const o=h(e).toLowerCase();this.config.redirects[o]=t;const{trackOriginalSource:l=!0}=s,i=this.getGlobalConfig("originalRedirectSources");l&&Array.isArray(i)&&!i.includes(o)&&i.push(o),o.endsWith("*")&&z(this.config.wildcardRedirectsTree,o)};getRedirect=e=>{const t=h(e).toLowerCase(),s=W(t,this.config.redirects,this.config.wildcardRedirectsTree);if(!s)return null;if(D(s.to)){const a=h(s.to).toLowerCase();if(!a.endsWith("*")&&$(a,[t],this.config.redirects,this.config.wildcardRedirectsTree).type==="cycle")return null}return{to:s.to,type:s.type}};createSharedData=async(e,t,s)=>{if(s&&this.#t[e]===s)return e;const a=JSON.stringify(t),r=s??H(a);return this.#t[e]===r||(this.#t[e]=r,await I(e,a,this.outdir),this.queueEvent("shared-data-updated",e)),e};addRouteSharedData=(e,t,s)=>{const a=L(e),r=this.routesSharedData.get(a)||{};r[t]=s,this.routesSharedData.set(a,r),u.verbose(`Adding shared data to ${e}, ${t}, ${s}`)};getRouteSharedDataByFsPath=e=>{const t=this.routesByFsPath.get(e);return t?this.routesSharedData.get(t)||{}:{}};getPartialsForRoute=e=>{const t=this.getGlobalConfig(m)||{},s=this.routesPartials.get(e);if(!s||s.length===0)return{};const a=this.getGlobalConfig(O)||{},r=new Set(s),o=Array.from(s);for(let i=0;i<o.length;i++){const n=o[i],c=a[n]?.partials??[];for(const d of c)r.has(d)||(r.add(d),o.push(d))}const l={};for(const i of r)t[i]&&(l[i]=t[i]);return l};addRoute=e=>{const s={...x(e.fsPath,this.config.metadataGlobs),...e.metadata||{}};this.newRoutes.push({...e,metadata:s}),u.verbose("Created route %s",e.slug)};addRouteSharedDataToAllLocales=(e,t,s)=>{const a=[g,...this.lifecycleContext?.fs.localeFolders||[]].map(r=>({code:r,name:r}));for(const r of a){const o=A(e,g,r.code,a);this.addRouteSharedData(o,t,s)}};addApiRoute=e=>{this.apiRoutes.push(e),u.verbose("Created API route %s",e.slug)};addMcpTools=(e,t)=>{for(const s of t)this.mcpToolHandlers.set(s.name,{...s,importPath:e}),u.verbose("Created MCP tool %s",s.name)};getMcpTools=()=>Array.from(this.mcpToolHandlers.values());markGatedMcpTools=e=>{for(const t of e){const s=this.mcpToolHandlers.get(t);s&&this.mcpToolHandlers.set(t,{...s,hasAvailabilityGate:!0})}};addSkills=e=>{for(const t of e)this.skills.set(t.slug,t),u.verbose("Discovered skill %s",t.slug)};getSkills=()=>Array.from(this.skills.values());addMiddleware=e=>{this.middleware.push(e),u.verbose("Created middleware %s",e.id)};setResourceResponseHeaders=(e,t)=>{this.config.responseHeaders||(this.config.responseHeaders={});const s=new Set(t.map(o=>o.name.toLowerCase())),r=[...(this.config.responseHeaders[e]??[]).filter(o=>!s.has(o.name.toLowerCase())),...t];this.config.responseHeaders[e]=r};getRouteByFsPath=e=>{const t=this.routesByFsPath.get(e);return t?this.getRouteBySlug(t):void 0};getRouteBySlug=(e,t={})=>{const{followRedirect:s=!0}=t,a=this.getRedirect(e);return s&&a?this.routesBySlug.get(h(a.to)):this.routesBySlug.get(e)};hasRouteOrRedirectBySlug=e=>{if(this.routesBySlug.has(e))return!0;const t=this.getRedirect(e);if(!t)return!1;if(!D(t.to))return!0;const s=h(t.to);return this.routesBySlug.has(s)};getRoutesByTemplateId=e=>this.newRoutes.filter(t=>t.templateId===e);getAllRoutesForLocale=(e=g)=>{const t=Array.from(this.routesBySlug.values()),s=e.toLowerCase();return t.filter(a=>e===g?!K(a.fsPath):a.slug.startsWith(`/${s}`))};getAllRoutes=()=>Array.from(this.routesBySlug.values());getAllApiRoutes=()=>this.apiRoutes;getAllMiddleware=()=>this.middleware;getTemplate=e=>this.templates.get(e);getRequestHandler=e=>this.apiRoutesRequestHandlers.get(e);createTemplate=(e,t)=>(this.templates.set(e,t),e);addBrowserPlugin=e=>{this.browserPlugins.add(e)};createRequestHandler=(e,t)=>(this.apiRoutesRequestHandlers.set(e,t),e);clearRequestHandlersByPrefix=e=>{for(const t of this.apiRoutesRequestHandlers.keys())t.startsWith(e)&&this.apiRoutesRequestHandlers.delete(t)};registerServerPropsGetter=(e,t)=>(this.serverPropsGetters.set(e,t),e);registerPagePropsGetter=(e,t)=>{this.pagePropsGetters.set(e,t)};async writeRouteStaticData(e,t){const s=await this.resolveRouteStaticData(e,t,!1);s&&q(e.slug,s,this.outdir)}async resolveRouteStaticData(e,t,s){if(this.serverMode)return V(e.slug,this.outdir);const a={...this,contentDir:this.contentDir,parseMarkdoc:({input:c,context:d,resource:f})=>this.parseMarkdoc({input:c,context:d,deps:{routeSlugs:[e.slug]},resource:f})},r=await e.getStaticData?.(e,a)||{},o=new Set(this.routesDynamicComponents.get(e.slug)),l=this.routesSharedData.get(e.slug)||{};for(const c of Object.values(l)){const d=this.sharedDataMarkdocComponents.get(c);d&&d.forEach(p=>o.add(p));const f=this.sharedDataDeps.get(c);f&&f.forEach(p=>this.addRouteSharedData(e.slug,p,p))}const i=this.getGlobalConfig("seo"),n=r?.frontmatter||{};return{...r,frontmatter:{...n,seo:{...n?.seo,title:n?.seo?.title||await e.getNavText?.()}},props:{...r.props,dynamicMarkdocComponents:Array.from(o),metadata:{...r?.props?.metadata,...e.metadata},seo:{title:T,...i,...r.props?.seo},compilationErrors:this.compilationErrors},lastModified:s||!e.fsPath?null:await this.lifecycleContext?.fs.getLastModified(e.fsPath)}}addSsrComponents(e,t){if(!e?.length)return;const s=typeof e[0]=="string"?e.join(""):N(e);s&&(t==="head"?this.ssr.headTags.push(s):t==="preBody"?this.ssr.preBodyTags.push(s):this.ssr.postBodyTags.push(s))}clear=()=>{this.routesByFsPath.clear(),this.templates.clear(),this.newRoutes=[],this.routesBySlug.clear(),this.apiRoutes=[],this.middleware=[],this.routesSharedData.clear(),this.sharedDataDeps.clear(),this.sharedDataMarkdocComponents.clear(),this.routesDynamicComponents.clear(),this.routesPartials.clear(),this.skills.clear(),this.config.redirects={},this.config.wildcardRedirectsTree={},this.config.directoryPermissions={},this.ssr={preBodyTags:[],postBodyTags:[],headTags:[]}};async toJson(){const e=[];for(const[s,a]of Object.entries(v))switch(a){case"map":const r=Array.from(this[s].entries());e.push([s,r]);break;case"object":s==="config"&&e.push([s,await this.getConfigWithEnvPlaceholders()]),e.push([s,this[s]]);break;default:throw new Error("Invalid format")}const t=Object.fromEntries(e);return t[E]=G.PLAN_GATES,t}static fromJson(e,t){const s=new P(t);for(const[r,o]of Object.entries(v))switch(o){case"map":s[r]=new Map(e[r]);break;case"object":if(r==="config"){s.setGlobalConfig(e[r]);break}s[r]=e[r];break;default:throw new Error("Invalid format")}s.config[m]=Q(s.config[m]||{});const a=e[E];return a&&_("PLAN_GATES",a),s}async getConfigWithEnvPlaceholders(){const e=JSON.parse(JSON.stringify(this.config));for(const t in this.replacedEnvVars){const{original:s}=this.replacedEnvVars[t],a=t.split(":"),r=a.pop(),{error:o,value:l}=y(e,a);if(o||!M(l)&&!Array.isArray(l)){await S.panicOnBuild(`Failed to replace env var with env name for ${t}`);continue}l[r]=s}return e}async reportUnsetEnvVars(){if(this.unsetEnvVars.size===0)return;const e=Array.from(this.unsetEnvVars).filter(s=>!Z.includes(s));if(e.length===0)return;const t=`Failed to resolve config. The following environment variables are not set: ${e.join(", ")}`;await S.panicOnBuildContentError(t)}}function Q(b){return k(b,e=>C.Ast.fromJSON(JSON.stringify(e)))}export{P as Store,Me as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
|
|
@@ -130,6 +130,7 @@ export type McpToolSchema = {
|
|
|
130
130
|
};
|
|
131
131
|
export type McpToolRegistration = McpToolSchema & {
|
|
132
132
|
importPath: string;
|
|
133
|
+
hasAvailabilityGate?: boolean;
|
|
133
134
|
};
|
|
134
135
|
export type MiddlewareDetails = {
|
|
135
136
|
id: string;
|
|
@@ -208,6 +209,8 @@ export type AfterRoutesCreatedActions = {
|
|
|
208
209
|
addApiRoute: (route: ApiRoute) => void;
|
|
209
210
|
addSkills: (skills: Skill[]) => void;
|
|
210
211
|
getSkills: () => Skill[];
|
|
212
|
+
getMcpTools: () => McpToolRegistration[];
|
|
213
|
+
markGatedMcpTools: (toolNames: string[]) => void;
|
|
211
214
|
loadApiDocuments(context: LifecycleContext): Promise<BundledApiDefinition[]>;
|
|
212
215
|
getRouteSharedDataByFsPath: (routeFsPath: string) => Record<string, string> | undefined;
|
|
213
216
|
getKv: () => Promise<KvService>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{withPathPrefix as
|
|
1
|
+
import{withPathPrefix as S}from"@redocly/theme/core/utils";import{DEFAULT_DOCS_MCP_SERVER_NAME as f,MCP_PROTOCOL_VERSION as A}from"../../../plugins/mcp/constants.js";import{RESERVED_ROUTES as O}from"../../../plugins/get-reserved-routes.js";import{isMcpCodeModeEnabled as T}from"../../../plugins/mcp/utils/mode.js";import{CODE_MODE_TOOL_NAMES as b,DESCRIBE_TOOLS_TOOL_NAME as M,FETCH_TOOL_NAME as R}from"../../../plugins/mcp/codemode/constants.js";import{EXECUTE_TOOL_SCHEMA as g}from"../../../plugins/mcp/codemode/tools/execute-schema.js";import{describeToolsSchema as v}from"../../../plugins/mcp/codemode/tools/describe-tools.js";import{buildCodeModeInstructions as L,buildExecuteDescription as I,DESCRIBE_TOOLS_DESCRIPTION as D}from"../../../plugins/mcp/codemode/prompts.js";import{toToolListEntries as P}from"../../../plugins/mcp/codemode/build-tool-list.js";import{composeExecuteAnnotations as y}from"../../../plugins/mcp/codemode/capabilities/index.js";import{ALL_CAPABILITIES as V}from"../../../plugins/mcp/codemode/capabilities/registry.js";import{isGatewayCapabilityEnabled as H}from"../../../plugins/mcp/gateway-mcp/eligibility.js";import{shouldHandleMcpAuth as N}from"../../../plugins/mcp/auth/auth-handlers.js";import{resolvePublicBaseUrl as U}from"../../../web-server/utils/resolve-public-base-url.js";import{telemetryTraceStep as x}from"../../../telemetry/helpers/trace-step.js";import{MCP_AUTH_SCHEMES as w}from"./constants.js";const B="https://static.modelcontextprotocol.io/schemas/mcp-server-card/v1.json",j="1.0";function ie(r){const s=T(),E=new Date().toISOString().slice(0,10);return async m=>await x("com.redocly.mcpServerCard.served",async e=>{e?.setAttribute("id","mcpServerCardId"),e?.setAttribute("object","mcpServerCard"),e?.setAttribute("uri","urn:redocly:realm:api:mcpServerCard:mcpServerCardId");const o=r.getConfig(),a=o.mcp?.docs?.name||f,n=r.getMcpTools().filter(t=>!b.has(t.name)&&!t.hasAvailabilityGate),i=H(o.mcp)?[...V]:[],l=i.some(t=>t.id===R),d=P(n,i),c=(s?[{...g,description:I(l,d),annotations:y(i)},{name:M,description:D,schema:v(n,i)}]:n).map(({name:t,description:_,schema:h,annotations:u})=>({name:t,description:_,inputSchema:h,...u?{annotations:u}:{}})),C=o.skills?.hide===!0?[]:r.getSkills(),p={$schema:B,version:j,protocolVersion:A,serverInfo:{name:a,title:a,version:E},description:o.seo?.description,documentationUrl:U(r,m),transport:{type:"streamable-http",endpoint:S(O.mcp)},capabilities:{logging:{},tools:{listChanged:!0},...C.length>0?{resources:{listChanged:!0},completions:{}}:{}},authentication:N(!!o.access?.requiresLogin,o.access?.rbac)?{required:!0,schemes:w}:void 0,instructions:s?L(l,d):void 0,tools:c.length>0?c:["dynamic"]};return e?.setAttribute("mode",s?"code":"tools"),e?.setAttribute("toolsCount",c.length),e?.setAttribute("authRequired",p.authentication!==void 0),m.json(p,200,{"Access-Control-Allow-Origin":"*","Cache-Control":"public, max-age=3600"})})}export{ie as mcpServerCardHandler};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/redoc",
|
|
3
|
-
"version": "0.137.0-next.
|
|
3
|
+
"version": "0.137.0-next.5",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@opentelemetry/sdk-trace-web": "2.8.0",
|
|
34
34
|
"@opentelemetry/semantic-conventions": "1.40.0",
|
|
35
35
|
"@redocly/ajv": "8.18.0",
|
|
36
|
-
"@redocly/openapi-core": "2.
|
|
36
|
+
"@redocly/openapi-core": "2.47.0",
|
|
37
37
|
"@shikijs/transformers": "3.21.0",
|
|
38
38
|
"@tanstack/react-query": "5.62.3",
|
|
39
39
|
"@tanstack/react-table": "8.21.3",
|
|
@@ -93,15 +93,15 @@
|
|
|
93
93
|
"xpath": "0.0.34",
|
|
94
94
|
"yaml-ast-parser": "0.0.43",
|
|
95
95
|
"zod": "^3.25.76",
|
|
96
|
-
"@redocly/api-docs": "0.2.0-next.
|
|
97
|
-
"@redocly/asyncapi-docs": "1.14.0-next.
|
|
96
|
+
"@redocly/api-docs": "0.2.0-next.5",
|
|
97
|
+
"@redocly/asyncapi-docs": "1.14.0-next.5",
|
|
98
98
|
"@redocly/config": "0.53.1",
|
|
99
|
-
"@redocly/graphql-docs": "1.14.0-next.
|
|
100
|
-
"@redocly/openapi-docs": "3.25.0-next.
|
|
99
|
+
"@redocly/graphql-docs": "1.14.0-next.5",
|
|
100
|
+
"@redocly/openapi-docs": "3.25.0-next.5",
|
|
101
101
|
"@redocly/portal-legacy-ui": "0.19.0",
|
|
102
|
-
"@redocly/portal-plugin-mock-server": "0.22.0-next.
|
|
102
|
+
"@redocly/portal-plugin-mock-server": "0.22.0-next.5",
|
|
103
103
|
"@redocly/realm-asyncapi-sdk": "0.15.0-next.2",
|
|
104
|
-
"@redocly/theme": "0.69.0-next.
|
|
104
|
+
"@redocly/theme": "0.69.0-next.5"
|
|
105
105
|
},
|
|
106
106
|
"peerDependencies": {
|
|
107
107
|
"react": "^19.2.7",
|