@redocly/reef 0.137.0-next.1 → 0.137.0-next.3

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 (59) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/constants/l10n/langs/ar.js +1 -1
  3. package/dist/constants/l10n/langs/de.js +1 -1
  4. package/dist/constants/l10n/langs/en.js +1 -1
  5. package/dist/constants/l10n/langs/es.js +1 -1
  6. package/dist/constants/l10n/langs/fr.js +1 -1
  7. package/dist/constants/l10n/langs/hi.js +1 -1
  8. package/dist/constants/l10n/langs/it.js +1 -1
  9. package/dist/constants/l10n/langs/ja.js +1 -1
  10. package/dist/constants/l10n/langs/ko.js +1 -1
  11. package/dist/constants/l10n/langs/pl.js +1 -1
  12. package/dist/constants/l10n/langs/pt-BR.js +1 -1
  13. package/dist/constants/l10n/langs/pt.js +1 -1
  14. package/dist/constants/l10n/langs/ru.js +1 -1
  15. package/dist/constants/l10n/langs/uk.js +1 -1
  16. package/dist/constants/l10n/langs/zh.js +1 -1
  17. package/dist/server/config/env-config.d.ts +2 -0
  18. package/dist/server/config/env-schema.d.ts +5 -0
  19. package/dist/server/config/env-schemas/feature-flags.d.ts +4 -0
  20. package/dist/server/config/env-schemas/feature-flags.js +1 -1
  21. package/dist/server/plugins/api-docs/search/search-indexer.js +1 -1
  22. package/dist/server/plugins/markdown/html/html-jsx-attribute-mapping.js +1 -1
  23. package/dist/server/plugins/mcp/auth/auth-handlers.js +1 -1
  24. package/dist/server/plugins/mcp/auth/token-audience-telemetry.d.ts +7 -0
  25. package/dist/server/plugins/mcp/auth/token-audience-telemetry.js +1 -0
  26. package/dist/server/plugins/mcp/auth/token-audience.d.ts +6 -0
  27. package/dist/server/plugins/mcp/auth/token-audience.js +1 -0
  28. package/dist/server/plugins/mcp/codemode/tools/execute.js +1 -1
  29. package/dist/server/plugins/mcp/codemode/tools/manage-credentials.d.ts +1 -0
  30. package/dist/server/plugins/mcp/codemode/tools/manage-credentials.js +2 -2
  31. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/utils.js +1 -1
  32. package/dist/server/plugins/mcp/gateway-mcp/credential-validation.d.ts +4 -0
  33. package/dist/server/plugins/mcp/gateway-mcp/credential-validation.js +1 -0
  34. package/dist/server/plugins/mcp/gateway-mcp/elicitation.d.ts +11 -3
  35. package/dist/server/plugins/mcp/gateway-mcp/elicitation.js +4 -3
  36. package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-bridge.d.ts +10 -1
  37. package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-bridge.js +1 -1
  38. package/dist/server/plugins/mcp/gateway-mcp/fetch-capability.d.ts +1 -1
  39. package/dist/server/plugins/mcp/gateway-mcp/fetch-capability.js +1 -1
  40. package/dist/server/plugins/mcp/gateway-mcp/secret-store.d.ts +8 -4
  41. package/dist/server/plugins/mcp/gateway-mcp/secret-store.js +1 -1
  42. package/dist/server/plugins/mcp/gateway-mcp/upstream-auth-scheme.d.ts +24 -1
  43. package/dist/server/plugins/mcp/gateway-mcp/upstream-auth-scheme.js +2 -1
  44. package/dist/server/plugins/mcp/gateway-mcp/upstream-auth.d.ts +6 -0
  45. package/dist/server/plugins/mcp/gateway-mcp/upstream-auth.js +1 -1
  46. package/dist/server/plugins/mcp/handlers/docs-mcp-handler.js +1 -1
  47. package/dist/server/plugins/mcp/handlers/mcp-credentials-handler.js +53 -33
  48. package/dist/server/plugins/mcp/servers/mcp-server.js +4 -2
  49. package/dist/server/plugins/mcp/types.d.ts +2 -2
  50. package/dist/server/sandbox/constants/sandbox.d.ts +4 -1
  51. package/dist/server/sandbox/constants/sandbox.js +1 -1
  52. package/dist/server/sandbox/sandbox.js +2 -2
  53. package/dist/server/web-server/routes/ask-ai.d.ts +1 -0
  54. package/dist/server/web-server/routes/ask-ai.js +1 -1
  55. package/dist/server/web-server/routes/auth.js +1 -1
  56. package/dist/server/web-server/routes/index.js +1 -1
  57. package/dist/server/web-server/utils/prepare-list-response.d.ts +2 -0
  58. package/dist/server/web-server/utils/prepare-list-response.js +1 -1
  59. package/package.json +8 -8
@@ -1,4 +1,4 @@
1
- import{secretStore as s,ELICITATION_TTL_MS as x}from"../gateway-mcp/secret-store.js";import{buildAllowedHostMatchers as P}from"../gateway-mcp/fetch/allowed-hosts.js";import{assertUrlAllowed as _}from"../gateway-mcp/fetch/ssrf-guard.js";import{readCappedBody as E,getDefaultAgent as T}from"../gateway-mcp/fetch/fetch-bridge.js";const p={"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store","X-Frame-Options":"DENY","Referrer-Policy":"no-referrer","Content-Security-Policy":"default-src 'none'; style-src 'unsafe-inline'; form-action 'self'"},R=3e4,z=64*1024;let $=fetch;function Q(e){$=e}const F=async(e,t)=>{const n=await t.getKv();if(e.method==="GET"){const o=new URL(e.url).searchParams.get("token")??"",r=await s.peekElicitation(n,o);return r?new Response(j(r,o),{status:200,headers:p}):u()}if(e.method==="POST"){const o=await e.formData(),r=String(o.get("token")??""),m=String(o.get("mode")??"token");return m==="client-credentials"?M(n,o,r):m==="clear"?B(n,r):O(n,o,r)}return new Response("Method Not Allowed",{status:405,headers:{Allow:"GET, POST"}})};async function O(e,t,n){const o=String(t.get("secret")??"");if(!o)return u();const r=await s.consumeElicitation(e,n);return r?(await s.setSecret(e,r.subject,r.api,H(r.auth,o)),new Response(A(r.name),{status:200,headers:p})):u()}async function B(e,t){const n=await s.consumeElicitation(e,t);return n?(await s.deleteSecret(e,n.subject,n.api),new Response(D(n.name),{status:200,headers:p})):u()}async function M(e,t,n){const o=String(t.get("client_id")??"").trim(),r=String(t.get("client_secret")??"").trim();if(!o||!r)return u();const m=await s.peekElicitation(e,n);if(!m||m.auth.kind!=="oauth2ClientCredentials")return u();const i=await s.consumeElicitation(e,n);if(!i||i.auth.kind!=="oauth2ClientCredentials")return u();const b=i.auth;try{const S=P([{url:b.tokenUrl}]),C=_(b.tokenUrl,S),k=new AbortController,I=setTimeout(()=>k.abort(),R);let g;try{g=await $(C,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"client_credentials",client_id:o,client_secret:r,...b.scopes.length>0?{scope:b.scopes.join(" ")}:{}}),redirect:"manual",signal:k.signal,dispatcher:T()})}finally{clearTimeout(I)}if(!g.ok)return f(i.name);let v;try{v=await E(g,z)}catch{return f(i.name)}let h;try{h=JSON.parse(v)}catch{return f(i.name)}const w=typeof h=="object"&&h!==null&&"access_token"in h?h.access_token:void 0;return typeof w!="string"||!w?f(i.name):(await s.setSecret(e,i.subject,i.api,{header:"Authorization",value:`Bearer ${w}`}),new Response(A(i.name),{status:200,headers:p}))}catch{return f(i.name)}}function H(e,t){return e.kind==="apiKey"?{header:e.header,value:t}:e.kind==="bearer"||e.kind==="oauth2ClientCredentials"?{header:"Authorization",value:N(t)}:{header:"Authorization",value:t}}function N(e){const t=e.trim();return t.toLowerCase().startsWith("bearer")?t:`Bearer ${t}`}function a(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;")}function c(e){return`<!doctype html>
1
+ import{secretStore as c,ELICITATION_TTL_MS as C}from"../gateway-mcp/secret-store.js";import{rejectCredential as z}from"../gateway-mcp/credential-validation.js";import{buildAllowedHostMatchers as F}from"../gateway-mcp/fetch/allowed-hosts.js";import{assertUrlAllowed as O}from"../gateway-mcp/fetch/ssrf-guard.js";import{readCappedBody as j,getDefaultAgent as B}from"../gateway-mcp/fetch/fetch-bridge.js";const p={"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store","X-Frame-Options":"DENY","Referrer-Policy":"no-referrer","Content-Security-Policy":"default-src 'none'; style-src 'unsafe-inline'; form-action 'self'"},M=3e4,q=64*1024;let I=fetch;function re(e){I=e}const H=async(e,t)=>{const n=await t.getKv();if(e.method==="GET"){const o=new URL(e.url).searchParams.get("token")??"",r=await c.peekElicitation(n,o);return r?new Response(S(r,o),{status:200,headers:p}):m()}if(e.method==="POST"){const o=await e.formData(),r=f(o,"token"),s=f(o,"mode")||"token";return s==="client-credentials"?G(n,o,r):s==="clear"?D(n,r):U(n,o,r)}return new Response("Method Not Allowed",{status:405,headers:{Allow:"GET, POST"}})};async function U(e,t,n){const o=f(t,"secret").trim(),r=await c.peekElicitation(e,n);if(!r)return m();const s=z(r.auth,o);if(s)return P(r,n,s);const a=await c.consumeElicitation(e,n);return a?(await c.setSecret(e,a.subject,a.api,a.scheme,K(a.auth,o)),new Response(E(a),{status:200,headers:p})):m()}async function D(e,t){const n=await c.consumeElicitation(e,t);return n?(await c.deleteSecret(e,n.subject,n.api,n.scheme),new Response(V(n),{status:200,headers:p})):m()}async function G(e,t,n){const o=f(t,"client_id").trim(),r=f(t,"client_secret").trim(),s=await c.peekElicitation(e,n);if(!s||s.auth.kind!=="oauth2ClientCredentials")return m();if(!o||!r)return P(s,n,"Enter both the client ID and the client secret before saving.");const a=await c.consumeElicitation(e,n);if(!a||a.auth.kind!=="oauth2ClientCredentials")return m();const g=a.auth;try{const T=F([{url:g.tokenUrl}]),_=O(g.tokenUrl,T),w=new AbortController,R=setTimeout(()=>w.abort(),M);let v;try{v=await I(_,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"client_credentials",client_id:o,client_secret:r,...g.scopes.length>0?{scope:g.scopes.join(" ")}:{}}),redirect:"manual",signal:w.signal,dispatcher:B()})}finally{clearTimeout(R)}if(!v.ok)return y(a.name);let $;try{$=await j(v,q)}catch{return y(a.name)}let h;try{h=JSON.parse($)}catch{return y(a.name)}const x=typeof h=="object"&&h!==null&&"access_token"in h?h.access_token:void 0,A=typeof x=="string"?x.trim():"";return A?(await c.setSecret(e,a.subject,a.api,a.scheme,{header:"Authorization",value:`Bearer ${A}`}),new Response(E(a),{status:200,headers:p})):y(a.name)}catch{return y(a.name)}}function f(e,t){const n=e.get(t);return typeof n=="string"?n:""}function P(e,t,n){return new Response(S(e,t,n),{status:400,headers:p})}function K(e,t){return e.kind==="apiKey"?{header:e.header,value:t}:e.kind==="cookie"?{header:"Cookie",value:`${e.name}=${t}`}:e.kind==="bearer"||e.kind==="oauth2ClientCredentials"?{header:"Authorization",value:L(t)}:{header:"Authorization",value:t}}function L(e){const t=e.trim();return t.toLowerCase().startsWith("bearer")?t:`Bearer ${t}`}function i(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;")}function d(e){return`<!doctype html>
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8">
@@ -14,6 +14,7 @@ h1{font-size:1.25rem;margin:0 0 .5rem}
14
14
  .section{margin-top:1.5rem;padding-top:1.5rem;border-top:1px solid #e5e7eb}
15
15
  .section h2{font-size:1rem;margin:0 0 .5rem}
16
16
  p.hint{color:#6b7280;font-size:.875rem;line-height:1.4}
17
+ p.error{margin:1rem 0 0;padding:.75rem;border:1px solid #fecaca;border-radius:.5rem;background:#fef2f2;color:#991b1b;font-size:.875rem;line-height:1.4}
17
18
  label{display:block;margin:1rem 0 .25rem;font-weight:600;font-size:.875rem}
18
19
  input[type=password],input[type=text]{width:100%;padding:.625rem .75rem;font-size:1rem;border:1px solid #d1d5db;border-radius:.5rem;background:#fff;color:#111827}
19
20
  input:focus{outline:none;border-color:#4f46e5;box-shadow:0 0 0 3px rgba(79,70,229,.2)}
@@ -30,6 +31,7 @@ body{background:#0b0f19;color:#e5e7eb}
30
31
  .card{background:#111827;box-shadow:0 10px 25px -5px rgba(0,0,0,.4)}
31
32
  .section{border-top-color:#1f2937}
32
33
  p.hint{color:#9ca3af}
34
+ p.error{border-color:#7f1d1d;background:#2a1215;color:#fca5a5}
33
35
  input[type=password],input[type=text]{background:#0b0f19;border-color:#374151;color:#e5e7eb}
34
36
  code{background:#1f2937}
35
37
  footer{border-top-color:#1f2937;color:#6b7280}
@@ -37,20 +39,21 @@ footer{border-top-color:#1f2937;color:#6b7280}
37
39
  </style>
38
40
  </head>
39
41
  <body><div class="card">${e}</div></body>
40
- </html>`}function d(e){return`<h1>\u{1F512} ${a(e)}</h1>`}function l(){return"<footer>Secured by Redocly &middot; credentials never enter the AI conversation</footer>"}function y(){return`<p class="hint">This link expires ${Math.round(x/6e4)} minutes after it was issued. If it stops working, ask your AI client for a new one.</p>`}function j(e,t){const n=e.hasExistingSecret?q(t):"";switch(e.auth.kind){case"bearer":return L(e.name,t,n);case"apiKey":return U(e.name,t,e.auth.header,n);case"oauth2ClientCredentials":return G(e.name,t,e.auth.scopes,n);default:return K(e.name,t,n)}}function q(e){return`
42
+ </html>`}function l(e){return`<h1>\u{1F512} ${i(e)}</h1>`}function u(){return"<footer>Secured by Redocly &middot; credentials never enter the AI conversation</footer>"}function b(){return`<p class="hint">This link expires ${Math.round(C/6e4)} minutes after it was issued. If it stops working, ask your AI client for a new one.</p>`}function S(e,t,n){const o=e.hasExistingSecret?N(t):"",r=n?`<p class="error">${i(n)}</p>`:"",s=k(e);switch(e.auth.kind){case"bearer":return W(s,t,o,r);case"apiKey":return X(s,t,e.auth.header,o,r);case"cookie":return Y(s,t,e.auth.name,o,r);case"oauth2ClientCredentials":return J(s,t,e.auth.scopes,o,r);default:return Q(s,t,o,r)}}function k({name:e,schemeLabel:t}){return t?`${e} (${t})`:e}function N(e){return`
41
43
  <div class="section">
42
44
  <h2>Remove stored credentials</h2>
43
45
  <p class="hint">This API already has credentials stored. Save above to replace them, or remove them to make calls anonymous again.</p>
44
46
  <form method="post" action="/mcp/credentials" autocomplete="off">
45
- <input type="hidden" name="token" value="${a(e)}">
47
+ <input type="hidden" name="token" value="${i(e)}">
46
48
  <input type="hidden" name="mode" value="clear">
47
49
  <button class="danger" type="submit">Remove credentials</button>
48
50
  </form>
49
- </div>`}function L(e,t,n){return c(`
50
- ${d(`Credentials for ${e}`)}
51
+ </div>`}function W(e,t,n,o){return d(`
52
+ ${l(`Credentials for ${e}`)}
53
+ ${o}
51
54
  <p class="hint">Stored encrypted for you only (24 hours for anonymous callers, 30 days when signed in) and sent to the API as the <code>Authorization</code> header. It never passes through the AI conversation.</p>
52
55
  <form method="post" action="/mcp/credentials" autocomplete="off">
53
- <input type="hidden" name="token" value="${a(t)}">
56
+ <input type="hidden" name="token" value="${i(t)}">
54
57
  <input type="hidden" name="mode" value="token">
55
58
  <label for="secret">API token</label>
56
59
  <input type="password" id="secret" name="secret" placeholder="your API token" required autofocus>
@@ -58,26 +61,42 @@ ${d(`Credentials for ${e}`)}
58
61
  <button type="submit">Save credentials</button>
59
62
  </form>
60
63
  ${n}
61
- ${y()}
62
- ${l()}`)}function U(e,t,n,o){return c(`
63
- ${d(`Credentials for ${e}`)}
64
- <p class="hint">Stored encrypted for you only (24 hours for anonymous callers, 30 days when signed in) and sent to the API as the <code>${a(n)}</code> header. It never passes through the AI conversation.</p>
64
+ ${b()}
65
+ ${u()}`)}function X(e,t,n,o,r){return d(`
66
+ ${l(`Credentials for ${e}`)}
67
+ ${r}
68
+ <p class="hint">Stored encrypted for you only (24 hours for anonymous callers, 30 days when signed in) and sent to the API as the <code>${i(n)}</code> header. It never passes through the AI conversation.</p>
69
+ <form method="post" action="/mcp/credentials" autocomplete="off">
70
+ <input type="hidden" name="token" value="${i(t)}">
71
+ <input type="hidden" name="mode" value="token">
72
+ <label for="secret">${i(n)} value</label>
73
+ <input type="password" id="secret" name="secret" placeholder="value" required autofocus>
74
+ <button type="submit">Save credentials</button>
75
+ </form>
76
+ ${o}
77
+ ${b()}
78
+ ${u()}`)}function Y(e,t,n,o,r){return d(`
79
+ ${l(`Credentials for ${e}`)}
80
+ ${r}
81
+ <p class="hint">Stored encrypted for you only (24 hours for anonymous callers, 30 days when signed in) and sent to the API as the <code>${i(n)}</code> cookie. It never passes through the AI conversation.</p>
65
82
  <form method="post" action="/mcp/credentials" autocomplete="off">
66
- <input type="hidden" name="token" value="${a(t)}">
83
+ <input type="hidden" name="token" value="${i(t)}">
67
84
  <input type="hidden" name="mode" value="token">
68
- <label for="secret">${a(n)} value</label>
85
+ <label for="secret">${i(n)} cookie value</label>
69
86
  <input type="password" id="secret" name="secret" placeholder="value" required autofocus>
87
+ <p class="hint">Paste only the value &mdash; we send it as <code>${i(n)}=&lt;value&gt;</code>.</p>
70
88
  <button type="submit">Save credentials</button>
71
89
  </form>
72
90
  ${o}
73
- ${y()}
74
- ${l()}`)}function G(e,t,n,o){const r=n.length>0?n.map(a).join(", "):"default scopes";return c(`
75
- ${d(`Credentials for ${e}`)}
91
+ ${b()}
92
+ ${u()}`)}function J(e,t,n,o,r){const s=n.length>0?n.map(i).join(", "):"default scopes";return d(`
93
+ ${l(`Credentials for ${e}`)}
94
+ ${r}
76
95
  <p class="hint">Paste a token you already have, or let us exchange client credentials for one via OAuth2 client_credentials.</p>
77
96
  <div class="section">
78
97
  <h2>I have a token</h2>
79
98
  <form method="post" action="/mcp/credentials" autocomplete="off">
80
- <input type="hidden" name="token" value="${a(t)}">
99
+ <input type="hidden" name="token" value="${i(t)}">
81
100
  <input type="hidden" name="mode" value="token">
82
101
  <label for="secret">API token</label>
83
102
  <input type="password" id="secret" name="secret" placeholder="your API token" required autofocus>
@@ -87,9 +106,9 @@ ${d(`Credentials for ${e}`)}
87
106
  </div>
88
107
  <div class="section">
89
108
  <h2>Exchange client credentials</h2>
90
- <p class="hint">We request a token from the API on your behalf. Scopes: ${r}.</p>
109
+ <p class="hint">We request a token from the API on your behalf. Scopes: ${s}.</p>
91
110
  <form method="post" action="/mcp/credentials" autocomplete="off">
92
- <input type="hidden" name="token" value="${a(t)}">
111
+ <input type="hidden" name="token" value="${i(t)}">
93
112
  <input type="hidden" name="mode" value="client-credentials">
94
113
  <label for="client_id">Client ID</label>
95
114
  <input type="text" id="client_id" name="client_id" required>
@@ -99,26 +118,27 @@ ${d(`Credentials for ${e}`)}
99
118
  </form>
100
119
  </div>
101
120
  ${o}
102
- ${y()}
103
- ${l()}`)}function K(e,t,n){return c(`
104
- ${d(`Credentials for ${e}`)}
121
+ ${b()}
122
+ ${u()}`)}function Q(e,t,n,o){return d(`
123
+ ${l(`Credentials for ${e}`)}
124
+ ${o}
105
125
  <p class="hint">The value is stored encrypted for you only (24 hours for anonymous callers, 30 days when signed in) and is sent to the API as the <code>Authorization</code> header. It never passes through the AI conversation.</p>
106
126
  <form method="post" action="/mcp/credentials" autocomplete="off">
107
- <input type="hidden" name="token" value="${a(t)}">
127
+ <input type="hidden" name="token" value="${i(t)}">
108
128
  <input type="hidden" name="mode" value="token">
109
129
  <label for="secret">Authorization header value</label>
110
130
  <input type="password" id="secret" name="secret" placeholder="Bearer &lt;your token&gt;" required autofocus>
111
131
  <button type="submit">Save credentials</button>
112
132
  </form>
113
133
  ${n}
114
- ${y()}
115
- ${l()}`)}function D(e){return c(`
116
- ${d("Credentials removed")}
117
- <p>The stored credentials for ${a(e)} are gone. Calls to that API are anonymous again until you add new ones.</p>
118
- ${l()}`)}function A(e){return c(`
119
- ${d("Credentials saved")}
120
- <p>Credentials for ${a(e)} are saved. Go back to your AI client and ask it to retry - or confirm the prompt it left open, if there is one. You can close this tab.</p>
121
- ${l()}`)}function f(e){return new Response(c(`
122
- ${d("Exchange failed")}
123
- <p>We could not exchange your client credentials for a token for ${a(e)}. This link has been used up - go back to your AI client and request a new link, then try again.</p>
124
- ${l()}`),{status:502,headers:p})}function u(){return new Response(c(`${d("Link expired")}<p>This credentials link is invalid, already used, or older than ${Math.round(x/6e4)} minutes. Retry the request in your AI client to get a fresh link.</p>${l()}`),{status:404,headers:p})}var V=F;export{V as default,Q as setCredentialsFetchImpl};
134
+ ${b()}
135
+ ${u()}`)}function V(e){return d(`
136
+ ${l("Credentials removed")}
137
+ <p>The stored credentials for ${i(k(e))} are gone. Calls to that API are anonymous again until you add new ones.</p>
138
+ ${u()}`)}function E(e){return d(`
139
+ ${l("Credentials saved")}
140
+ <p>Credentials for ${i(k(e))} are saved. Go back to your AI client and ask it to retry - or confirm the prompt it left open, if there is one. You can close this tab.</p>
141
+ ${u()}`)}function y(e){return new Response(d(`
142
+ ${l("Exchange failed")}
143
+ <p>We could not exchange your client credentials for a token for ${i(e)}. This link has been used up - go back to your AI client and request a new link, then try again.</p>
144
+ ${u()}`),{status:502,headers:p})}function m(){return new Response(d(`${l("Link expired")}<p>This credentials link is invalid, already used, or older than ${Math.round(C/6e4)} minutes. Retry the request in your AI client to get a fresh link.</p>${u()}`),{status:404,headers:p})}var ae=H;export{ae as default,re as setCredentialsFetchImpl};
@@ -1,3 +1,5 @@
1
- import{McpServer as T,WebStandardStreamableHTTPServerTransport as q}from"@modelcontextprotocol/server";import{logger as g}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as I,MCP_TOOL_WORKER_KEY as v}from"../../../workers/mcp-tool-worker-pool.js";import{SSE_KEEPALIVE_INTERVAL_MS as R}from"../constants.js";import{secretStore as _}from"../gateway-mcp/secret-store.js";import{readAuthRequiredMeta as A,stripAuthRequiredMeta as b}from"../gateway-mcp/upstream-auth.js";import{buildCredentialsToolHint as O,buildUpstreamCredentialFallback as w,buildUpstreamCredentialInputRequired as U,isCredentialElicitationDeclined as C,targetOf as D}from"../gateway-mcp/elicitation.js";import{resolveUpstreamAuthScheme as j}from"../gateway-mcp/upstream-auth-scheme.js";import{MISSING_SESSION_HANDLE_MESSAGE as K,resolveSecretSubject as P}from"../gateway-mcp/subject.js";const L=6e4,S=new Map;function $(e,t){const r=S.get(e);if(r)return r;const n=t().finally(()=>{S.delete(e)});return S.set(e,n),n}function ee(e,t){const r=new T({name:e,version:new Date().toISOString().slice(0,10)},{capabilities:{logging:{}},...t?{instructions:t}:{}}),n=new q({keepAliveMs:R,sessionIdGenerator:void 0});return{server:r,transport:n}}async function te(e,t){return await e.connect(t),{server:e,transport:t,cleanup:async()=>{t.close()}}}function re(e,t){return r=>async(n,a)=>{g.info(`MCP tool called: ${r}`);const i=e.elicitationEnabled??!1,s=a.mcpReq.envelope!==void 0,c=i?P(e.user,n.sessionHandle):{kind:"anonymous"},o=c.kind==="resolved"?c.subject:void 0;if(c.kind==="invalid")return{content:[{type:"text",text:c.message}],isError:!0};const d=async()=>{if(!(!i||!o||!t?.getKv))try{const u=await t.getKv();return await _.getSecretsForSubject(u,o)}catch(u){g.warn(`gateway mcp secret store error, continuing unauthenticated: ${String(u)}`);return}};let m;const E=async()=>{m=await d();const u={toolName:r,args:n,context:{...e,upstreamSecrets:m},extra:N(a)};return await I.exec(v,[u],{timeout:L})};let p=await E();const l=A(p);if(l.length>0&&i&&!o)return b({...p,content:[{type:"text",text:K}],isError:!0});if(l.length>0&&i&&o&&t?.getKv){g.info(`gateway mcp credentials prompt: era=${s?"2026-07-28":"2025"} clientElicitation=${H(t)}`);const u=await t.getKv(),h=new Set(Object.keys(m??{})),f=s?await x(e,a,l,o,u,h,t):await B(e,l,o,u,h);switch(f.kind){case"inputRequired":return f.result;case"retry":p=await E();break;case"prependText":p={...p,content:[{type:"text",text:f.text},...p.content],isError:!0};break;case"none":break}}return b(p)}}function F(e){return!!e?.getClientCapabilities()?.elicitation?.url}function H(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"}function k(e,t){return e.status===401&&!t.has(e.api)}function y(e){return e.map(O).join(`
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,a=s.mcpReq.envelope!==void 0,u=i?Y(e.user,n.sessionHandle):{kind:"anonymous"},o=u.kind==="resolved"?u.subject:void 0;if(u.kind==="invalid")return{content:[{type:"text",text:u.message}],isError:!0};const p=async()=>{if(!(!i||!o||!t?.getKv))try{const m=await t.getKv();return await F.getSecretsForSubject(m,o)}catch(m){S.warn(`gateway mcp secret store error, continuing unauthenticated: ${String(m)}`);return}};let c;const l=async()=>{c=await p();const m={toolName:r,args:n,context:{...e,upstreamSecrets:c},extra:ie(s)};return await D.exec(K,[m],{timeout:J})};let d=await l();const f=H(d);if(f.length>0&&i&&!o)return M({...d,content:[{type:"text",text:V}],isError:!0});if(f.length>0&&i&&o&&t?.getKv){S.info(`gateway mcp credentials prompt: era=${a?"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(c),g=a?await re(e,s,k,o,m,y,h,t):await ne(e,k,o,m,y,h);switch(g.kind){case"inputRequired":return g.result;case"retry":d=await l();break;case"prependText":d={...d,content:[{type:"text",text:g.text},...d.content],isError:!0};break;case"none":break}}return M(d)}}function 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 I(e,t){return e.status===401&&!t.has(b(e))}function R(e,t){const r=t.get(e.api);return r&&e.scheme?B(r.schemes,e.scheme):void 0}function A(e,t){const r=t.get(e.api);return!r||!e.schemes?.length?[]:W(r,e.schemes)}function _(e,t){return A(e,t).length>1}function w(e,t){return e.map(r=>P(r,R(r,t))).join(`
2
2
 
3
- `)}async function M(e,t,r,n){const[a,...i]=t;return{subject:r,baseUrl:e.baseUrl??"",target:D(a),auth:await j(e,a.api),kv:n,hasExistingSecret:!1,reason:{kind:"authFailure",status:a.status,otherApiNames:i.map(s=>s.name)}}}async function x(e,t,r,n,a,i,s){if(C(t.mcpReq.inputResponses))return{kind:"none"};const c=r.filter(d=>k(d,i));if(c.length===0)return{kind:"prependText",text:y(r)};const o=await M(e,c,n,a);return F(s)?{kind:"inputRequired",result:await U(o)}:{kind:"prependText",text:await w(o)}}async function B(e,t,r,n,a){const i=t.filter(o=>k(o,a));if(i.length===0)return{kind:"prependText",text:y(t)};const s=await M(e,i,r,n);return{kind:"prependText",text:await $(`${r}:${i[0].api}`,()=>w(s))}}function N(e){return{sessionId:e.sessionId,authInfo:e.http?.authInfo,requestId:e.mcpReq.id,_meta:e.mcpReq._meta}}function ne(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{te as connectMcpServer,ee as createMcpServer,re as createToolDispatch,ne as extractSerializableContext};
3
+ `)}function C(e,t,r){const n=e.map(s=>v(s,A(s,r)));return t.length>0&&n.push(w(t,r)),n.join(`
4
+
5
+ `)}function O(e,t,r,n,s){const[i,...a]=t,u=s.get(i.api),o=i.scheme??u?.defaultScheme??q,p=R(i,s);return{subject:r,baseUrl:e.baseUrl??"",target:{api:i.api,name:i.name,scheme:o,...p?{schemeLabel:p}:{}},auth:u?.schemes[o]??{kind:"raw"},kv:n,hasExistingSecret:!1,reason:{kind:"authFailure",status:i.status,otherApiNames:a.map(c=>c.name)}}}async function re(e,t,r,n,s,i,a,u){if(N(t.mcpReq.inputResponses))return{kind:"none"};const o=r.filter(l=>I(l,i));if(o.length===0)return{kind:"prependText",text:w(r,a)};const p=o.filter(l=>_(l,a));if(p.length>0){const l=r.filter(d=>!p.includes(d));return{kind:"prependText",text:C(p,l,a)}}const c=O(e,o,n,s,a);return X(u)?{kind:"inputRequired",result:await $(c)}:{kind:"prependText",text:await T(c)}}async function ne(e,t,r,n,s,i){const a=t.filter(c=>I(c,s));if(a.length===0)return{kind:"prependText",text:w(t,i)};const u=a.filter(c=>_(c,i));if(u.length>0){const c=t.filter(l=>!u.includes(l));return{kind:"prependText",text:C(u,c,i)}}const o=O(e,a,r,n,i);return{kind:"prependText",text:await Q(`${r}:${b(a[0])}`,()=>T(o))}}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};
@@ -4,7 +4,7 @@ import type { OpenAPIPath, OpenAPIServer, OpenAPIOperation } from '@redocly/api-
4
4
  import type { McpErrorCodes, McpServerType } from './constants';
5
5
  import type { McpToolSchema } from '../../types';
6
6
  import type { Skill } from '../../types/plugins/skills.js';
7
- import type { StoredSecret } from './gateway-mcp/secret-store.js';
7
+ import type { UpstreamSecrets } from './gateway-mcp/secret-store.js';
8
8
  type ObjectValues<T> = T[keyof T];
9
9
  export type McpServerType = ObjectValues<typeof McpServerType>;
10
10
  export type McpErrorCodeTypes = ObjectValues<typeof McpErrorCodes>;
@@ -98,7 +98,7 @@ export type McpToolContext = Omit<ApiFunctionsContext, 'telemetry' | 'getKv' | k
98
98
  elicitationEnabled?: boolean;
99
99
  gatewayFetchBudgetPerExec?: number;
100
100
  clientIp?: string;
101
- upstreamSecrets?: Record<string, StoredSecret>;
101
+ upstreamSecrets?: UpstreamSecrets;
102
102
  searchIgnoredRoutes?: string[];
103
103
  };
104
104
  export type McpToolHandler<TArgs extends ToolArgs = ToolArgs> = (args: TArgs, context: McpToolContext, extra: McpToolExtra) => Promise<McpToolWorkerResponse>;
@@ -1,6 +1,9 @@
1
1
  export declare const DEFAULT_TIMEOUT_MS = 10000;
2
- export declare const MEMORY_LIMIT_BYTES: number;
3
2
  export declare const DEFAULT_MAX_LIFETIME_MS: number;
4
3
  export declare const MAX_STACK_SIZE_BYTES: number;
5
4
  export declare const MAX_RESULT_BYTES: number;
5
+ export declare const MAX_ALLOCATION_BYTES: number;
6
+ export declare const MAX_TOTAL_MEMORY_BYTES: number;
7
+ export declare const INITIAL_TOTAL_MEMORY_BYTES: number;
8
+ export declare const WASM_PAGE_BYTES: number;
6
9
  //# sourceMappingURL=sandbox.d.ts.map
@@ -1 +1 @@
1
- const _=1e4,E=67108864,T=6e5,o=262144,t=1048576;export{T as DEFAULT_MAX_LIFETIME_MS,_ as DEFAULT_TIMEOUT_MS,t as MAX_RESULT_BYTES,o as MAX_STACK_SIZE_BYTES,E as MEMORY_LIMIT_BYTES};
1
+ const _=1e4,T=6e5,o=262144,t=1048576,E=67108864,A=134217728,M=16777216,S=65536;export{T as DEFAULT_MAX_LIFETIME_MS,_ as DEFAULT_TIMEOUT_MS,M as INITIAL_TOTAL_MEMORY_BYTES,E as MAX_ALLOCATION_BYTES,t as MAX_RESULT_BYTES,o as MAX_STACK_SIZE_BYTES,A as MAX_TOTAL_MEMORY_BYTES,S as WASM_PAGE_BYTES};
@@ -1,3 +1,3 @@
1
- import p from"@jitl/quickjs-singlefile-mjs-release-sync";import{newQuickJSWASMModuleFromVariant as S}from"quickjs-emscripten-core";import{createExecutionBudget as E}from"./utils/execution-budget.js";import{DEFAULT_MAX_LIFETIME_MS as g,DEFAULT_TIMEOUT_MS as T,MAX_RESULT_BYTES as _,MAX_STACK_SIZE_BYTES as y,MEMORY_LIMIT_BYTES as h}from"./constants/sandbox.js";let d;function x(){return d??=S(p)}async function R(t,c={}){const{timeoutMs:l=T,maxLifetimeMs:m=g,setupContext:f}=c,e=(await x()).newContext();let n,s,r;try{e.runtime.setMemoryLimit(h),e.runtime.setMaxStackSize(y),n=E(e,l,m),s=f?.(e,n)??void 0,r=e.unwrapResult(e.evalCode(`(async () => {
1
+ import _ from"@jitl/quickjs-singlefile-mjs-release-sync";import{newQuickJSWASMModuleFromVariant as E,newVariant as S}from"quickjs-emscripten-core";import{createExecutionBudget as A}from"./utils/execution-budget.js";import{DEFAULT_MAX_LIFETIME_MS as p,DEFAULT_TIMEOUT_MS as y,INITIAL_TOTAL_MEMORY_BYTES as g,MAX_ALLOCATION_BYTES as w,MAX_RESULT_BYTES as x,MAX_STACK_SIZE_BYTES as I,MAX_TOTAL_MEMORY_BYTES as L,WASM_PAGE_BYTES as d}from"./constants/sandbox.js";let l;function h(){return l??=E(S(_,{wasmMemory:new WebAssembly.Memory({initial:g/d,maximum:L/d})}))}async function k(t,c={}){const{timeoutMs:m=y,maxLifetimeMs:M=p,setupContext:f}=c,e=(await h()).newContext();let n,s,r;try{e.runtime.setMemoryLimit(w),e.runtime.setMaxStackSize(I),n=A(e,m,M),s=f?.(e,n)??void 0,r=e.unwrapResult(e.evalCode(`(async () => {
2
2
  ${t}
3
- })()`));const i=e.resolvePromise(r);e.runtime.executePendingJobs();const M=await Promise.race([i,n.aborted]),u=e.unwrapResult(M),o=e.dump(u);if(u.dispose(),o===void 0)return{error:"The code returned undefined. Check that a top-level `return` yields a defined value: return the value directly (not `return;` or `return undefined`), read an existing field (a wrong field name is undefined), and do not wrap the logic in a function or IIFE whose return value is discarded."};const a=JSON.stringify(o??null).length;return a>_?{error:`Result is too large (${a} bytes); return less data`}:{result:o}}catch(i){return{error:v(i)}}finally{n?.dispose(),r?.dispose();try{s?.()}catch{}try{e.dispose()}catch{d=void 0}}}function v(t){return t instanceof Error?t.message:String(t)}export{R as runInSandbox};
3
+ })()`));const i=e.resolvePromise(r);e.runtime.executePendingJobs();const T=await Promise.race([i,n.aborted]),u=e.unwrapResult(T),o=e.dump(u);if(u.dispose(),o===void 0)return{error:"The code returned undefined. Check that a top-level `return` yields a defined value: return the value directly (not `return;` or `return undefined`), read an existing field (a wrong field name is undefined), and do not wrap the logic in a function or IIFE whose return value is discarded."};const a=JSON.stringify(o??null).length;return a>x?{error:`Result is too large (${a} bytes); return less data`}:{result:o}}catch(i){return{error:b(i)}}finally{n?.dispose(),r?.dispose();try{s?.()}catch{}try{e.dispose()}catch{l=void 0}}}function b(t){return t instanceof Error?t.message:String(t)}export{k as runInSandbox};
@@ -1,5 +1,6 @@
1
1
  import type { Handler } from 'hono';
2
2
  import type { Store } from '../../store';
3
+ export declare const AI_ASSISTANT_IDENTITY_STATUS_HEADER = "x-redocly-ai-assistant-identity-status";
3
4
  export declare function askAiHandler(store: Store): Handler;
4
5
  /**
5
6
  * Ticket submissions from the embedded assistant. Proxied for the portal's own org/project so the
@@ -1 +1 @@
1
- import{RbacFeatures as c}from"../../../constants/common.js";import{ASK_AI_API_URL as u,AI_ASSISTANT_ESCALATIONS_API_URL as A,AI_ASSISTANT_FEEDBACK_API_URL as p}from"../../constants/common.js";import{fetchWithClient as f}from"../../utils/fetch-with-client.js";import{canAccessFeature as h}from"../../utils/rbac.js";import{handleUnauthorizedApiRequest as d}from"../utils.js";import{isAiSearchEnabled as l}from"../../plugins/search/utils.js";function m(e,n){const r=n.getConfig();return l(r)?h(c.AI_SEARCH,e.get("auth"),r.access?.rbac,r.access?.requiresLogin)?null:d(e):e.newResponse(null,403,{})}function S(e){const n=e.get("auth"),r={"Content-Type":"application/json"},t=[];n.claims?.authCookie&&t.push(`authorization=${n.claims.authCookie}`),n.idpAccessToken&&t.push(`accessToken=${n.idpAccessToken}`),t.length>0&&(r.Cookie=t.join("; "));const i=e.req.header("Authorization");return i&&(r.Authorization=i),r}const g="x-redocly-ai-assistant-origin";function _(e){const n=e.req.header("Origin");if(n)return n;const r=e.req.header("Referer");if(r)try{return new URL(r).origin}catch{}return new URL(e.req.url).origin}function s({store:e,apiUrl:n,successContentType:r="application/json"}){return async t=>{if(!n)return t.newResponse(null,404);const i=m(t,e);if(i)return i;const a=S(t);a[g]=_(t);const o=await f(n,{method:"POST",body:JSON.stringify(await t.req.json()),headers:a});return t.newResponse(o.body,o.status,{"Content-Type":o.ok?r:"application/json"})}}function E(e){return s({store:e,apiUrl:u,successContentType:"text/event-stream"})}function b(e){return s({store:e,apiUrl:A})}function w(e){return s({store:e,apiUrl:p})}export{b as aiAssistantEscalationsHandler,w as aiAssistantFeedbackHandler,E as askAiHandler};
1
+ import{RbacFeatures as d}from"../../../constants/common.js";import{ASK_AI_API_URL as h,AI_ASSISTANT_ESCALATIONS_API_URL as p,AI_ASSISTANT_FEEDBACK_API_URL as f}from"../../constants/common.js";import{fetchWithClient as l}from"../../utils/fetch-with-client.js";import{canAccessFeature as S}from"../../utils/rbac.js";import{handleUnauthorizedApiRequest as T}from"../utils.js";import{isAiSearchEnabled as I}from"../../plugins/search/utils.js";function _(e,t){const n=t.getConfig();return I(n)?S(d.AI_SEARCH,e.get("auth"),n.access?.rbac,n.access?.requiresLogin)?null:T(e):e.newResponse(null,403,{})}function g(e){const t=e.get("auth"),n={"Content-Type":"application/json"},r=[];t.claims?.authCookie&&r.push(`authorization=${t.claims.authCookie}`),t.idpAccessToken&&r.push(`accessToken=${t.idpAccessToken}`),r.length>0&&(n.Cookie=r.join("; "));const i=e.req.header("Authorization");i&&(n.Authorization=i);const s=e.req.header(u);return s&&(n[u]=s),n}const m="x-redocly-ai-assistant-origin",u="x-redocly-ai-assistant-identity",A="x-redocly-ai-assistant-identity-status";function y(e){const t=e.req.header("Origin");if(t)return t;const n=e.req.header("Referer");if(n)try{return new URL(n).origin}catch{}return new URL(e.req.url).origin}function a({store:e,apiUrl:t,successContentType:n="application/json"}){return async r=>{if(!t)return r.newResponse(null,404);const i=_(r,e);if(i)return i;const s=g(r);s[m]=y(r);const o=await l(t,{method:"POST",body:JSON.stringify(await r.req.json()),headers:s}),c=o.headers.get(A);return r.newResponse(o.body,o.status,{"Content-Type":o.ok?n:"application/json",...c?{[A]:c}:{}})}}function U(e){return a({store:e,apiUrl:h,successContentType:"text/event-stream"})}function b(e){return a({store:e,apiUrl:p})}function q(e){return a({store:e,apiUrl:f})}export{A as AI_ASSISTANT_IDENTITY_STATUS_HEADER,b as aiAssistantEscalationsHandler,q as aiAssistantFeedbackHandler,U as askAiHandler};
@@ -1 +1 @@
1
- import{setCookie as L,deleteCookie as U}from"hono/cookie";import{AuthProviderType as Q,DEFAULT_TEAM_CLAIM_NAME as x}from"@redocly/config";import{withPathPrefix as O,getPathPrefix as C}from"@redocly/theme/core/utils";import{compareURIs as ee}from"../../../utils/url/compare-uris.js";import{ensureArray as N}from"../../../utils/array/ensure-array.js";import{ALTERNATIVE_AUD_CLAIM_NAME as H,JWT_SECRET_KEY as v,ORG_SLUG as re,ORG_ID as oe}from"../../constants/common.js";import{AUTH_SEGMENT as ne,ServerRoutes as P}from"../../../constants/api.js";import{AuthCookieNames as _,DEFAULT_COOKIE_EXPIRATION as B}from"../../../constants/common.js";import{sanitizeRedirectPathname as K}from"../../../utils/url/sanitize-redirect-pathname.js";import{telemetry as T}from"../../telemetry/index.js";import{envConfig as j}from"../../config/env-config.js";import{fetchWithClient as te}from"../../utils/fetch-with-client.js";import{clearAuthCookies as z,clearPostLogoutCookie as $,hasPostLogoutCookie as se,setPostLogoutCookie as G}from"../../utils/cookie.js";import{getAuthProviderLoginParams as ie,isOidcProviderConfig as b,isSaml2ProviderConfig as ae,oidcExchangeCodeForToken as de,buildLoginUrl as ce,decodeSamlResponse as le,extractUserClaims as ue,parseSamlResponse as pe,parseOidcState as ge,verifySAMLResponse as fe,getUsernameFromPayload as me,buildOidcLogoutUrl as he,getOidcMetadata as J,getRedoclyTokenPayload as Re,isRedoclySso as _e,rewritePreviewAuthRedirectUri as Ie,parsePreviewBranch as Z,buildOidcLoginUrl as Ce,createMcpSessionResource as D}from"../auth.js";import*as k from"../jwt/jwt.js";import{AlgorithmTypes as E}from"../jwt/types.js";import{handleErrorPageRender as Ae}from"../utils.js";import{encodeBase64URL as we}from"../jwt/encode.js";import{resolveUiLocalesForIdpLogin as Le}from"./helpers/resolve-ui-locales-for-idp-login.js";async function Ke(s){if(j.isProductionEnv)return s.newResponse(null,404,{});const{password:e,...r}=await s.req.json(),a=await k.sign({...r,name:r.username||r.email||"Unknown"},v,E.HS256);return L(s,_.AUTHORIZATION,a,{path:C()||"/",httpOnly:!0,secure:!0,sameSite:"none"}),$(s),s.newResponse(null,200,{})}function je(){return async s=>{const e=s.get("logger"),r=encodeURIComponent(s.req.query("message")||"");e.error(`Login error: ${r}`);const a=`${P.LOGIN}/?error=${encodeURIComponent(r)}`;return s.newResponse(null,301,{Location:a})}}function V(s){if(!s||!s.includes(P.MCP_CALLBACK))return null;try{const e=s.split("/"),r=e[e.length-1];if(r){const a=Buffer.from(r,"base64url").toString("utf-8");return JSON.parse(a).mcpSessionId||null}}catch{}return null}function ze(s){return async e=>{const r=e.get("logger"),a=s.getConfig().ssoDirect,o=ge(e.req.query("state")),m=o.idpId,t=o.source==="mcp"||o.redirectTo&&typeof o.redirectTo=="string"&&o.redirectTo.includes(P.MCP_CALLBACK),l=t?V(typeof o.redirectTo=="string"?o.redirectTo:void 0):null,i=a?.[m];if(!b(i))return r.error("OIDC login error: missing OIDC provider config"),e.text("Forbidden",403);const d=await J(m,i);if(a&&!d.token_endpoint){const p="Invalid OIDC configuration: token_endpoint is required";return r.error(`OIDC login error: ${p}`),e.text(p,500)}try{const p=d.token_endpoint,u=e.req.query("code"),h=e.req.query("error");if(h)return t&&T.sendMcpAuthorizationFailedMessage([{...D(l),error:`OIDC error: ${h}`,error_details:e.req.query("error_description")||null}]),Ae(e,s,{slug:"/"},403,"403OIDC");if(!u){const I="Code is expected but not present";return r.error(`OIDC login error: ${I}`),t&&T.sendMcpAuthorizationFailedMessage([{...D(l),error:I,error_details:null}]),new Response(`Forbidden: ${I}`,{status:403})}const w=typeof o.redirectUri=="string"?o.redirectUri:new URL(O(P.OIDC_CALLBACK),e.req.url).toString(),R=e.get("cookies")?.code_verifier,c=await de(p,u,w,i,{...i.tokenRequestCustomParams,...R?{code_verifier:R}:{}});if(c.error)return r.error(`Error from OIDC provider: "${c.error}"`),t&&T.sendMcpAuthorizationFailedMessage([{...D(l),error:`Token exchange error: ${c.error}`,error_details:c.error_description||null}]),e.text(`Forbidden: ${c.error_description||c.error}`,403);if(!c?.id_token){const I="No id_token, please, add openid to scopes";return r.error(`OIDC login error: ${I}`),t&&T.sendMcpAuthorizationFailedMessage([{...D(l),error:I,error_details:null}]),new Response(`Forbidden: ${I}`,{status:403})}const{payload:f,header:M}=k.decode(c.id_token),n=M.alg===E.RS256;if(i.audience?.length&&![...N(f.aud||[]),...N(f[H]||[])].some(y=>i.audience?.includes(y))){const y="No valid audience found in id_token";return r.error(`OIDC login error: ${y}`),t&&T.sendMcpAuthorizationFailedMessage([{...D(l),error:y,error_details:null}]),new Response(`Forbidden: ${y}`)}const g=n?c.id_token:await k.sign({...f,idpId:m},v,E.HS256);me(f)||r.warn("To display your username, the required 'email' or 'full_profile' scope must be added to the identity provider configuration");const S=i?.tokenExpirationTime?Date.now()+i.tokenExpirationTime*1e3:f.exp*1e3||Date.now()+B*1e3;if(i.introspectEndpoint){const I=await fetch(i.introspectEndpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({access_token:c.access_token})});if(I.ok){const q=(await I.json()).ext?.federatedIdentity;q&&(L(e,_.FEDERATED_ACCESS_TOKEN,q.access_token||"",{path:C()||"/",httpOnly:!1,expires:new Date(S)}),L(e,_.FEDERATED_ID_TOKEN,q.id_token||"",{path:C()||"/",httpOnly:!1,expires:new Date(S)}))}else r.warn(`OIDC introspect error: ${I.statusText}`)}if(L(e,_.AUTHORIZATION,g,{path:C()||"/",httpOnly:!0,expires:new Date(S)}),g!==c.id_token&&L(e,_.IDP_ID_TOKEN,c.id_token||"",{path:C()||"/",httpOnly:!0,expires:new Date(S)}),L(e,_.IDP_ACCESS_TOKEN,c.access_token||"",{path:C()||"/",httpOnly:!0,expires:new Date(S)}),c.refresh_token?L(e,_.IDP_REFRESH_TOKEN,c.refresh_token,{path:C()||"/",httpOnly:!0,expires:new Date(S)}):U(e,_.IDP_REFRESH_TOKEN,{path:C()||"/"}),U(e,"code_verifier",{path:C()||"/"}),$(e),t&&o.redirectTo&&typeof o.redirectTo=="string"&&o.redirectTo.includes(P.MCP_CALLBACK)){const y=`${e.req.url.split("?")[0].replace(P.OIDC_CALLBACK,"")}${o.redirectTo}`;return e.newResponse(null,302,{Location:y})}const W=typeof o.redirectTo=="string"?o.redirectTo:void 0;let X=K(new URL(W||"/",e.req.url).pathname);const Y=e.newResponse(null,302,{Location:X});return r.updateContext({email:f.email,subject:f.sub}),r.info("OIDC login successful"),Y}catch(p){const u=p instanceof Error?p.message:String(p),h=p instanceof Error?p.stack:String(p);if(r.error(`OIDC login error: ${u}`),t&&T.sendMcpAuthorizationFailedMessage([{...D(l),error:u,error_details:h}]),p.error==="access_denied")return r.info("Access denied"),e.text("Forbidden",403)}const A="Something went wrong";return r.error(`OIDC login error: ${A}`),t&&T.sendMcpAuthorizationFailedMessage([{...D(l),error:A,error_details:null}]),e.text(A,500)}}function Ge(s){return async e=>{const r=e.get("logger"),o=e.get("auth").claims?.idpId,t=s.getConfig().ssoDirect?.[o];if(e.req.method==="POST")return b(t)||(z(e),G(e)),r.info("Logout successful"),e.newResponse(null,200,{});let l;if(b(t)){const i=(await J(o,t)).end_session_endpoint;if(i){const d=new URL(e.req.url),A=e.req.header("x-forwarded-proto")||d.protocol.slice(0,-1)||"https",p=e.req.header("x-forwarded-host")||d.host,u=`${A}://${p}`,h=Z(u),w=h?we(JSON.stringify({branch:Z(u)})):void 0,R=h?`${Ie(u)}/${ne}/logout`:`${u}${O(P.POST_LOGOUT)}`;l=he(i,R,e.get("cookies")?.[_.IDP_ID_TOKEN]||e.get("cookies")?.[_.AUTHORIZATION]||"",w)}}return r.info("Logout successful"),z(e),G(e),e.newResponse(null,302,{Location:l||O("/")})}}function Je(s){return async e=>{const r=s.getConfig().access?.logoutReturnUrl,a=r||O("/");return e.newResponse(null,302,{Location:a})}}function Ze(s){return async e=>{const r=e.get("logger"),a=e.req.param("code"),o=j.BH_API_URL,m=(t,l,i)=>t&&l?`${t} ${l.charAt(0)}`:i;try{if(!o)throw new Error("BH_API_URL is not set");const t=s.getConfig().ssoDirect;if(!t||!Object.keys(t).length)return r.warn("Invite no sso configured to handle"),e.redirect(O("/"));const l=await te(`${o}/public-user-invites/${a}`);if(!l.ok)return l.status===404?(r.warn(`Invite ${a} not found redirect to homepage`),e.redirect(O("/"))):(r.error("Invite error",await l.text()),e.redirect(O("/")));const i=await l.json(),d=new URL(O("/invite"),e.req.url);return d.searchParams.set("code",a),d.searchParams.set("org",i.organization.name),d.searchParams.set("invitedBy",m(i.invitedBy.firstName,i.invitedBy.lastName,i.invitedBy.name)),e.newResponse(null,302,{Location:d.toString()})}catch(t){return r.error("Error processing invite",{error:t,inviteCode:a}),e.text(t.message||"Failed to process invite",400)}}}function Ve(s){return async e=>{const r=e.get("logger"),a=s.getConfig().ssoDirect,o=new URL(e.req.url),m=e.req.query("inviteCode"),t=e.req.header("x-forwarded-proto")||o.protocol.slice(0,-1)||"https",l=e.req.header("x-forwarded-host")||o.host,i=`${t}://${l}`;let d=o.searchParams.get("idpId");const A=o.searchParams.get("redirectTo"),p=Object.keys(a||{})[0];d=d||p;const u=o.searchParams.get("mcp_redirect_uri"),h=!!u;if(!a?.[d]){const g="Invalid idpId";if(r.error(`IdP login error: ${g}`),h){const F=V(A||void 0);T.sendMcpAuthorizationFailedMessage([{...D(F),error:g,error_details:null}])}return e.text(`Forbidden: ${g}`,403)}const R=Le({localePrefixParam:o.searchParams.get("localePrefix"),l10n:s.getGlobalData()?.l10n}),c=d&&a?await ie(d,a[d]):void 0,f={};for(const g of Object.keys(c?.extraParams||{}))f[g]=o.searchParams.get(g)||c?.extraParams?.[g]||void 0;se(e)&&(f.prompt="login",f.post_logout="1");let M,n={};if(h&&u&&c&&c.type===Q.OIDC){r.info(`Building MCP OAuth login URL with redirect_uri: ${u}`);const g=Ce("",{...c,extraParams:f},A,m,{redirectUriOverride:u,sourceOverride:"mcp",branchOverride:void 0,uiLocales:R});M=g.loginUrl,n=g.cookies||{}}else if(c){const g=ce({...c,extraParams:f},i,A,m,R);M=g.loginUrl,n=g.cookies||{}}return Object.keys(n).forEach(g=>{L(e,g,n[g].value,n[g].options)}),r.info(`IdP login initiated for ID '${d}'`),e.newResponse(null,302,{Location:M||new URL(e.req.url).pathname})}}function We(s){return async e=>{const r=e.get("logger"),a=await e.req.formData(),o=a.get("SAMLResponse"),m=a.get("RelayState");if(typeof o!="string"||typeof m!="string"){const n="SAMLResponse is required";return r.error(`SAML2 login error: ${n}`),e.text(`Bad request: ${n}`,400)}const t=le(o),{success:l,uid:i,nameFormat:d,attrs:A,issuerId:p,expiresAt:u}=pe(t),{idpId:h,redirectTo:w}=JSON.parse(m);if(!l){const n="SAML2 assertion is not successful";return r.error(`SAML2 login error: ${n}`),e.text(`Permission denied: ${n}`,401)}if(!u||Math.ceil(Date.now()/1e3)>=u){const n="SAML2 Token Expired";return r.error(`SAML2 login error: ${n}`),e.text(n,401)}const R=s.getConfig().ssoDirect?.[h];if(!R||!ae(R)){const n="Cannot find valid IdP";return r.error(`SAML2 login error: ${n}`),e.text(`Permission denied: ${n}`,401)}if(!(R.issuerId&&p&&ee(R.issuerId,p))){const n="IssuerID is misconfigured or untrusted assertions issuer received";return r.error(`SAML2 login error: ${n}`),e.text(`Permission denied: ${n}`,401)}if(!await fe(t,R.x509PublicCert)){const n="SAMLResponse signature invalid";return r.error(`SAML2 login error: ${n}`),e.text(n,401)}const f=ue(i,d,A,R.teamsAttributeName??x);if(!f.sub){const n="The provider did not return a valid user identity.";return r.error(`SAML2 login error: ${n}`),e.text(n,400)}if(!f.email){const n="The provider did not return a valid user email.";return r.error(`SAML2 login error: ${n}`),e.text(n,400)}const M=await k.sign({...f,idpId:h},v,E.HS256);return L(e,_.AUTHORIZATION,M,{path:C()||"/",httpOnly:!0,expires:new Date(u*1e3)}),U(e,_.IDP_REFRESH_TOKEN,{path:C()||"/"}),$(e),r.updateContext({email:f.email,subject:f.sub}),r.info("SAML2 login successful"),e.newResponse(null,302,{Location:w||"/"})}}function Xe(s){return async e=>{const r=e.get("logger"),a=new URL(e.req.query("redirectTo")||"/",e.req.url),o=O(K(a.pathname)),m=s.getConfig().ssoDirect,t=Object.entries(m||{}).find(([,w])=>b(w)&&_e(w));if(!(m&&t))return e.newResponse(null,302,{Location:o});const i=e.req.query("token"),d=i&&await Re(i);if(!d)return e.newResponse(null,302,{Location:o});if(!N(d[H]||[]).some(w=>w===re||w===oe))return e.newResponse(null,302,{Location:o});const u=await k.sign({...d,idpId:t?.at(0)},v,E.HS256),h=Date.now()+B*1e3;return L(e,_.AUTHORIZATION,u,{path:C()||"/",httpOnly:!0,expires:new Date(h),sameSite:"None",secure:!0}),U(e,_.IDP_REFRESH_TOKEN,{path:C()||"/"}),$(e),r.info("Token login successful"),e.newResponse(null,302,{Location:o})}}export{Ke as authorizeHandler,Ve as idpLoginHandler,Ze as inviteHandler,Ge as logoutHandler,ze as oidcCallbackHandler,Je as postLogoutHandler,je as redoclyLoginCallbackHandler,Xe as redoclyTokenLoginHandler,We as samlCallbackHandler};
1
+ import{setCookie as O,deleteCookie as U}from"hono/cookie";import{AuthProviderType as Q,DEFAULT_TEAM_CLAIM_NAME as x}from"@redocly/config";import{withPathPrefix as L,getPathPrefix as C}from"@redocly/theme/core/utils";import{compareURIs as ee}from"../../../utils/url/compare-uris.js";import{ensureArray as N}from"../../../utils/array/ensure-array.js";import{ALTERNATIVE_AUD_CLAIM_NAME as H,JWT_SECRET_KEY as v,ORG_SLUG as re,ORG_ID as oe}from"../../constants/common.js";import{AUTH_SEGMENT as ne,ServerRoutes as P}from"../../../constants/api.js";import{AuthCookieNames as R,DEFAULT_COOKIE_EXPIRATION as B}from"../../../constants/common.js";import{sanitizeRedirectPathname as K}from"../../../utils/url/sanitize-redirect-pathname.js";import{telemetry as D}from"../../telemetry/index.js";import{envConfig as j}from"../../config/env-config.js";import{fetchWithClient as te}from"../../utils/fetch-with-client.js";import{clearAuthCookies as z,clearPostLogoutCookie as $,hasPostLogoutCookie as se,setPostLogoutCookie as G}from"../../utils/cookie.js";import{getAuthProviderLoginParams as ie,isOidcProviderConfig as q,isSaml2ProviderConfig as ae,oidcExchangeCodeForToken as de,buildLoginUrl as ce,decodeSamlResponse as le,extractUserClaims as ue,parseSamlResponse as pe,parseOidcState as ge,verifySAMLResponse as fe,getUsernameFromPayload as me,buildOidcLogoutUrl as he,getOidcMetadata as J,getRedoclyTokenPayload as Ie,isRedoclySso as Re,rewritePreviewAuthRedirectUri as _e,parsePreviewBranch as Z,buildOidcLoginUrl as Ce,createMcpSessionResource as T}from"../auth.js";import*as k from"../jwt/jwt.js";import{AlgorithmTypes as E}from"../jwt/types.js";import{handleErrorPageRender as Ae}from"../utils.js";import{encodeBase64URL as we}from"../jwt/encode.js";import{resolveUiLocalesForIdpLogin as Oe}from"./helpers/resolve-ui-locales-for-idp-login.js";async function Ke(s){if(j.isProductionEnv)return s.newResponse(null,404,{});const{password:e,...r}=await s.req.json(),a=await k.sign({...r,name:r.username||r.email||"Unknown"},v,E.HS256);return O(s,R.AUTHORIZATION,a,{path:C()||"/",httpOnly:!0,secure:!0,sameSite:"none"}),$(s),s.newResponse(null,200,{})}function je(){return async s=>{const e=s.get("logger"),r=encodeURIComponent(s.req.query("message")||"");e.error(`Login error: ${r}`);const a=`${P.LOGIN}/?error=${encodeURIComponent(r)}`;return s.newResponse(null,301,{Location:a})}}function V(s){if(!s||!s.includes(P.MCP_CALLBACK))return null;try{const e=s.split("/"),r=e[e.length-1];if(r){const a=Buffer.from(r,"base64url").toString("utf-8");return JSON.parse(a).mcpSessionId||null}}catch{}return null}function ze(s){return async e=>{const r=e.get("logger"),a=s.getConfig().ssoDirect;let o;try{o=ge(e.req.query("state"))}catch{return r.warn("OIDC login error: Invalid OAuth2 state"),e.text("Bad request: Invalid OAuth2 state",400)}const m=o.idpId,t=o.source==="mcp"||o.redirectTo&&typeof o.redirectTo=="string"&&o.redirectTo.includes(P.MCP_CALLBACK),l=t?V(typeof o.redirectTo=="string"?o.redirectTo:void 0):null,i=a?.[m];if(!q(i))return r.error("OIDC login error: missing OIDC provider config"),e.text("Forbidden",403);const d=await J(m,i);if(a&&!d.token_endpoint){const p="Invalid OIDC configuration: token_endpoint is required";return r.error(`OIDC login error: ${p}`),e.text(p,500)}try{const p=d.token_endpoint,u=e.req.query("code"),h=e.req.query("error");if(h)return t&&D.sendMcpAuthorizationFailedMessage([{...T(l),error:`OIDC error: ${h}`,error_details:e.req.query("error_description")||null}]),Ae(e,s,{slug:"/"},403,"403OIDC");if(!u){const _="Code is expected but not present";return r.error(`OIDC login error: ${_}`),t&&D.sendMcpAuthorizationFailedMessage([{...T(l),error:_,error_details:null}]),new Response(`Forbidden: ${_}`,{status:403})}const w=typeof o.redirectUri=="string"?o.redirectUri:new URL(L(P.OIDC_CALLBACK),e.req.url).toString(),I=e.get("cookies")?.code_verifier,c=await de(p,u,w,i,{...i.tokenRequestCustomParams,...I?{code_verifier:I}:{}});if(c.error)return r.error(`Error from OIDC provider: "${c.error}"`),t&&D.sendMcpAuthorizationFailedMessage([{...T(l),error:`Token exchange error: ${c.error}`,error_details:c.error_description||null}]),e.text(`Forbidden: ${c.error_description||c.error}`,403);if(!c?.id_token){const _="No id_token, please, add openid to scopes";return r.error(`OIDC login error: ${_}`),t&&D.sendMcpAuthorizationFailedMessage([{...T(l),error:_,error_details:null}]),new Response(`Forbidden: ${_}`,{status:403})}const{payload:f,header:M}=k.decode(c.id_token),n=M.alg===E.RS256;if(i.audience?.length&&![...N(f.aud||[]),...N(f[H]||[])].some(y=>i.audience?.includes(y))){const y="No valid audience found in id_token";return r.error(`OIDC login error: ${y}`),t&&D.sendMcpAuthorizationFailedMessage([{...T(l),error:y,error_details:null}]),new Response(`Forbidden: ${y}`)}const g=n?c.id_token:await k.sign({...f,idpId:m},v,E.HS256);me(f)||r.warn("To display your username, the required 'email' or 'full_profile' scope must be added to the identity provider configuration");const S=i?.tokenExpirationTime?Date.now()+i.tokenExpirationTime*1e3:f.exp*1e3||Date.now()+B*1e3;if(i.introspectEndpoint){const _=await fetch(i.introspectEndpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({access_token:c.access_token})});if(_.ok){const b=(await _.json()).ext?.federatedIdentity;b&&(O(e,R.FEDERATED_ACCESS_TOKEN,b.access_token||"",{path:C()||"/",httpOnly:!1,expires:new Date(S)}),O(e,R.FEDERATED_ID_TOKEN,b.id_token||"",{path:C()||"/",httpOnly:!1,expires:new Date(S)}))}else r.warn(`OIDC introspect error: ${_.statusText}`)}if(O(e,R.AUTHORIZATION,g,{path:C()||"/",httpOnly:!0,expires:new Date(S)}),g!==c.id_token&&O(e,R.IDP_ID_TOKEN,c.id_token||"",{path:C()||"/",httpOnly:!0,expires:new Date(S)}),O(e,R.IDP_ACCESS_TOKEN,c.access_token||"",{path:C()||"/",httpOnly:!0,expires:new Date(S)}),c.refresh_token?O(e,R.IDP_REFRESH_TOKEN,c.refresh_token,{path:C()||"/",httpOnly:!0,expires:new Date(S)}):U(e,R.IDP_REFRESH_TOKEN,{path:C()||"/"}),U(e,"code_verifier",{path:C()||"/"}),$(e),t&&o.redirectTo&&typeof o.redirectTo=="string"&&o.redirectTo.includes(P.MCP_CALLBACK)){const y=`${e.req.url.split("?")[0].replace(P.OIDC_CALLBACK,"")}${o.redirectTo}`;return e.newResponse(null,302,{Location:y})}const W=typeof o.redirectTo=="string"?o.redirectTo:void 0;let X=K(new URL(W||"/",e.req.url).pathname);const Y=e.newResponse(null,302,{Location:X});return r.updateContext({email:f.email,subject:f.sub}),r.info("OIDC login successful"),Y}catch(p){const u=p instanceof Error?p.message:String(p),h=p instanceof Error?p.stack:String(p);if(r.error(`OIDC login error: ${u}`),t&&D.sendMcpAuthorizationFailedMessage([{...T(l),error:u,error_details:h}]),p.error==="access_denied")return r.info("Access denied"),e.text("Forbidden",403)}const A="Something went wrong";return r.error(`OIDC login error: ${A}`),t&&D.sendMcpAuthorizationFailedMessage([{...T(l),error:A,error_details:null}]),e.text(A,500)}}function Ge(s){return async e=>{const r=e.get("logger"),o=e.get("auth").claims?.idpId,t=s.getConfig().ssoDirect?.[o];if(e.req.method==="POST")return q(t)||(z(e),G(e)),r.info("Logout successful"),e.newResponse(null,200,{});let l;if(q(t)){const i=(await J(o,t)).end_session_endpoint;if(i){const d=new URL(e.req.url),A=e.req.header("x-forwarded-proto")||d.protocol.slice(0,-1)||"https",p=e.req.header("x-forwarded-host")||d.host,u=`${A}://${p}`,h=Z(u),w=h?we(JSON.stringify({branch:Z(u)})):void 0,I=h?`${_e(u)}/${ne}/logout`:`${u}${L(P.POST_LOGOUT)}`;l=he(i,I,e.get("cookies")?.[R.IDP_ID_TOKEN]||e.get("cookies")?.[R.AUTHORIZATION]||"",w)}}return r.info("Logout successful"),z(e),G(e),e.newResponse(null,302,{Location:l||L("/")})}}function Je(s){return async e=>{const r=s.getConfig().access?.logoutReturnUrl,a=r||L("/");return e.newResponse(null,302,{Location:a})}}function Ze(s){return async e=>{const r=e.get("logger"),a=e.req.param("code"),o=j.BH_API_URL,m=(t,l,i)=>t&&l?`${t} ${l.charAt(0)}`:i;try{if(!o)throw new Error("BH_API_URL is not set");const t=s.getConfig().ssoDirect;if(!t||!Object.keys(t).length)return r.warn("Invite no sso configured to handle"),e.redirect(L("/"));const l=await te(`${o}/public-user-invites/${a}`);if(!l.ok)return l.status===404?(r.warn(`Invite ${a} not found redirect to homepage`),e.redirect(L("/"))):(r.error("Invite error",await l.text()),e.redirect(L("/")));const i=await l.json(),d=new URL(L("/invite"),e.req.url);return d.searchParams.set("code",a),d.searchParams.set("org",i.organization.name),d.searchParams.set("invitedBy",m(i.invitedBy.firstName,i.invitedBy.lastName,i.invitedBy.name)),e.newResponse(null,302,{Location:d.toString()})}catch(t){return r.error("Error processing invite",{error:t,inviteCode:a}),e.text(t.message||"Failed to process invite",400)}}}function Ve(s){return async e=>{const r=e.get("logger"),a=s.getConfig().ssoDirect,o=new URL(e.req.url),m=e.req.query("inviteCode"),t=e.req.header("x-forwarded-proto")||o.protocol.slice(0,-1)||"https",l=e.req.header("x-forwarded-host")||o.host,i=`${t}://${l}`;let d=o.searchParams.get("idpId");const A=o.searchParams.get("redirectTo"),p=Object.keys(a||{})[0];d=d||p;const u=o.searchParams.get("mcp_redirect_uri"),h=!!u;if(!a?.[d]){const g="Invalid idpId";if(r.error(`IdP login error: ${g}`),h){const F=V(A||void 0);D.sendMcpAuthorizationFailedMessage([{...T(F),error:g,error_details:null}])}return e.text(`Forbidden: ${g}`,403)}const I=Oe({localePrefixParam:o.searchParams.get("localePrefix"),l10n:s.getGlobalData()?.l10n}),c=d&&a?await ie(d,a[d]):void 0,f={};for(const g of Object.keys(c?.extraParams||{}))f[g]=o.searchParams.get(g)||c?.extraParams?.[g]||void 0;se(e)&&(f.prompt="login",f.post_logout="1");let M,n={};if(h&&u&&c&&c.type===Q.OIDC){r.info(`Building MCP OAuth login URL with redirect_uri: ${u}`);const g=Ce("",{...c,extraParams:f},A,m,{redirectUriOverride:u,sourceOverride:"mcp",branchOverride:void 0,uiLocales:I});M=g.loginUrl,n=g.cookies||{}}else if(c){const g=ce({...c,extraParams:f},i,A,m,I);M=g.loginUrl,n=g.cookies||{}}return Object.keys(n).forEach(g=>{O(e,g,n[g].value,n[g].options)}),r.info(`IdP login initiated for ID '${d}'`),e.newResponse(null,302,{Location:M||new URL(e.req.url).pathname})}}function We(s){return async e=>{const r=e.get("logger"),a=await e.req.formData(),o=a.get("SAMLResponse"),m=a.get("RelayState");if(typeof o!="string"||typeof m!="string"){const n="SAMLResponse is required";return r.error(`SAML2 login error: ${n}`),e.text(`Bad request: ${n}`,400)}const t=le(o),{success:l,uid:i,nameFormat:d,attrs:A,issuerId:p,expiresAt:u}=pe(t),{idpId:h,redirectTo:w}=JSON.parse(m);if(!l){const n="SAML2 assertion is not successful";return r.error(`SAML2 login error: ${n}`),e.text(`Permission denied: ${n}`,401)}if(!u||Math.ceil(Date.now()/1e3)>=u){const n="SAML2 Token Expired";return r.error(`SAML2 login error: ${n}`),e.text(n,401)}const I=s.getConfig().ssoDirect?.[h];if(!I||!ae(I)){const n="Cannot find valid IdP";return r.error(`SAML2 login error: ${n}`),e.text(`Permission denied: ${n}`,401)}if(!(I.issuerId&&p&&ee(I.issuerId,p))){const n="IssuerID is misconfigured or untrusted assertions issuer received";return r.error(`SAML2 login error: ${n}`),e.text(`Permission denied: ${n}`,401)}if(!await fe(t,I.x509PublicCert)){const n="SAMLResponse signature invalid";return r.error(`SAML2 login error: ${n}`),e.text(n,401)}const f=ue(i,d,A,I.teamsAttributeName??x);if(!f.sub){const n="The provider did not return a valid user identity.";return r.error(`SAML2 login error: ${n}`),e.text(n,400)}if(!f.email){const n="The provider did not return a valid user email.";return r.error(`SAML2 login error: ${n}`),e.text(n,400)}const M=await k.sign({...f,idpId:h},v,E.HS256);return O(e,R.AUTHORIZATION,M,{path:C()||"/",httpOnly:!0,expires:new Date(u*1e3)}),U(e,R.IDP_REFRESH_TOKEN,{path:C()||"/"}),$(e),r.updateContext({email:f.email,subject:f.sub}),r.info("SAML2 login successful"),e.newResponse(null,302,{Location:w||"/"})}}function Xe(s){return async e=>{const r=e.get("logger"),a=new URL(e.req.query("redirectTo")||"/",e.req.url),o=L(K(a.pathname)),m=s.getConfig().ssoDirect,t=Object.entries(m||{}).find(([,w])=>q(w)&&Re(w));if(!(m&&t))return e.newResponse(null,302,{Location:o});const i=e.req.query("token"),d=i&&await Ie(i);if(!d)return e.newResponse(null,302,{Location:o});if(!N(d[H]||[]).some(w=>w===re||w===oe))return e.newResponse(null,302,{Location:o});const u=await k.sign({...d,idpId:t?.at(0)},v,E.HS256),h=Date.now()+B*1e3;return O(e,R.AUTHORIZATION,u,{path:C()||"/",httpOnly:!0,expires:new Date(h),sameSite:"None",secure:!0}),U(e,R.IDP_REFRESH_TOKEN,{path:C()||"/"}),$(e),r.info("Token login successful"),e.newResponse(null,302,{Location:o})}}export{Ke as authorizeHandler,Ve as idpLoginHandler,Ze as inviteHandler,Ge as logoutHandler,ze as oidcCallbackHandler,Je as postLogoutHandler,je as redoclyLoginCallbackHandler,Xe as redoclyTokenLoginHandler,We as samlCallbackHandler};
@@ -1 +1 @@
1
- import{serveStatic as R}from"hono/serve-static";import{withPathPrefix as i,withoutPathPrefix as c}from"@redocly/theme/core/utils";import{ServerRoutes as l}from"../../../constants/api.js";import{PUBLIC_STATIC_FOLDER as H}from"../../constants/common.js";import{envConfig as L}from"../../config/env-config.js";import{authMiddleware as C}from"../middleware/authMiddleware.js";import{ensureSearchData as g}from"../middleware/ensureSearchData.js";import{dynamicMiddleware as I}from"../middleware/dynamic-middleware/dynamic-middleware.js";import{installRoutes as h}from"../../plugins/dev-onboarding/api/routes/index.js";import{installSkillsRoutes as D}from"./skills/skills-routes.js";import{authorizeHandler as P,oidcCallbackHandler as N,logoutHandler as s,postLogoutHandler as w,idpLoginHandler as M,redoclyLoginCallbackHandler as B,samlCallbackHandler as v,redoclyTokenLoginHandler as F,inviteHandler as G}from"./auth.js";import{appDataHandler as U}from"./app-data.js";import{searchFacetsHandler as y,searchHandler as K}from"./search.js";import{dynamicRouteHandler as Y}from"./dynamic-route.js";import{pageDataHandler as k,sharedPageDataHandler as b}from"./page-data.js";import{pathPrefixRedirectHandler as V}from"./path-prefix-redirect.js";import{getRoutesByLineHandler as E,resolvePathHandler as T,resolvePathsHandler as x,resolveSlugHandler as _}from"./resolve-route.js";import{feedbackHandler as q}from"./feedback.js";import{loggerMiddleware as X}from"../middleware/loggerMiddleware.js";import{responseHeadersMiddleware as $}from"../middleware/responseHeadersMiddleware.js";import{idleTimeoutMiddleware as j}from"../middleware/idleTimeoutMiddleware.js";import{otelTracesHandler as z}from"./otel/otel.js";import{healthCheckHandler as W}from"./health.js";import{askAiHandler as Z,aiAssistantEscalationsHandler as u,aiAssistantFeedbackHandler as J}from"./ask-ai.js";import{semanticSearchHandler as Q}from"./semantic-search.js";import{replayOauth2RedirectCallbackHandler as aa}from"./replay-oauth2-redirect.js";import{corsProxyHandler as f}from"./cors-proxy.js";import{corsMiddleware as t}from"../middleware/corsMiddleware.js";import{installApiRoutes as ea}from"./api-routes/api-routes.js";import{cookieMiddleware as ia}from"../middleware/cookieMiddleware.js";import{staticContentHandler as la}from"../routes/static-content.js";import{infoHandler as S}from"./info.js";import{catalogHandler as ta}from"./catalog/catalog.js";import{catalogRelationsHandler as ma}from"./catalog/catalog-relations.js";import{bffCatalogHandler as da}from"./catalog/bff-catalog.js";import{bffCatalogRevisionsHandler as oa}from"./catalog/bff-catalog-revisions.js";import{bffCatalogRelatedEntitiesHandler as na}from"./catalog/bff-catalog-related-entities.js";import{catalogAuthMiddleware as m}from"../middleware/catalogAuthMiddleware.js";import{telemetryMiddleware as Aa}from"../middleware/telemetry-middleware.js";import{errorHandler as pa}from"./error.js";import{installMcpRoutes as sa}from"./mcp-routes/mcp-routes.js";import{jsonValidatorMiddleware as O}from"../middleware/jsonValidatorMiddleware.js";import{searchFacetsRequestSchema as Ea}from"../../../api-schemas/search-facets.js";import{searchRequestSchema as Ta}from"../../../api-schemas/search.js";function me(a,e,d){const{resolveRouteData:n,readStaticAsset:A}=d;a.use("*",j()),a.use("*",ia()),a.use("*",I(e)),a.use("*",C(e)),a.use("*",X()),a.use("*",$(e)),a.use("*",Aa()),a.use(i("*"),R({root:`./${H}`,getContent:(o,r)=>la(o,r,e,A),rewriteRequestPath:o=>c(o)})),a.use(i(l.FEEDBACK),t({allowMethods:["POST"]})),a.use(i(l.ASK_AI),t({allowMethods:["POST"]})),a.use(i(l.AI_ASSISTANT_ESCALATIONS),t({allowMethods:["POST"]})),a.use(i(l.AI_ASSISTANT_FEEDBACK),t({allowMethods:["POST"]})),a.use(i(l.SEMANTIC_SEARCH),t({allowMethods:["POST"]})),a.use("*",_a(e));const p=g(e);a.use(i(l.INFO),S()),L.NEW_CATALOG_ENABLED&&(a.use(i(l.CATALOG_ENTITIES),m({serverOutDir:e.serverOutDir}),ta(e)),a.use(i(l.CATALOG_ENTITIES_RELATIONS),m({serverOutDir:e.serverOutDir}),ma(e)),a.get(i(l.BFF_CATALOG_ENTITIES),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),da(e)),a.get(i(l.BFF_CATALOG_RELATED_ENTITIES),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),na(e)),a.get(i(l.BFF_CATALOG_REVISIONS),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),oa(e))),a.get(i(l.SHARED_PAGE_DATA),b(e)),a.get(i(l.PAGE_DATA),k(e,n)),a.get(i(l.APP_DATA),U(e)),a.post(i(l.SEARCH),p,O(Ta),K(e)),a.post(i(l.SEARCH_FACETS),p,O(Ea),y(e)),a.post(i(l.AUTHORIZATION),P),a.post(i(l.LOGOUT),s(e)),a.get(i(l.LOGOUT),s(e)),a.get(i(l.POST_LOGOUT),w(e)),a.get(i(l.OIDC_CALLBACK),N(e)),a.get(i(l.REDOCLY_TOKEN_LOGIN),F(e)),a.get(i(l.REDOCLY_LOGIN_CALLBACK),B()),a.get(i(l.IDP_LOGIN),M(e)),a.post(i(l.SAML_CALLBACK),v(e)),a.get(i(l.INVITE),G(e)),a.get(i(l.HEALTH),W),sa(a,e),h(a,e),D(a,e),a.all(i(l.CORS_PROXY),f(e)),a.all(i(`${l.CORS_PROXY}/*`),f(e)),ea(a,e),a.post(i(l.FEEDBACK),q(e)),a.post(i(l.RESOLVE_ROUTE_BY_PATH),T(e)),a.post(i(l.RESOLVE_ROUTES_BY_PATHS),x(e)),a.post(i(l.RESOLVE_ROUTE_BY_SLUG),_(e)),a.post(i(l.ASK_AI),Z(e)),a.post(i(l.AI_ASSISTANT_ESCALATIONS),u(e)),a.post(i(l.AI_ASSISTANT_FEEDBACK),J(e)),a.post(i(l.SEMANTIC_SEARCH),Q(e)),a.get(i(l.GET_ROUTES_BY_LINE),E(e)),a.post(i(l.OTEL_TRACES),z),a.get(i(l.REPLAY_OAUTH2_CALLBACK),aa),a.all(i("/*"),Y(e,n,A)),a.get("*",V),a.onError(pa)}function _a(a){return async(e,d)=>{await a.waitForPluginsLifecycle(),await d()}}function de(a,e){a.get(i(l.INFO),S()),a.post(i(l.RESOLVE_ROUTE_BY_PATH),T(e)),a.post(i(l.RESOLVE_ROUTE_BY_SLUG),_(e)),a.get(i(l.GET_ROUTES_BY_LINE),E(e))}export{de as installDevRoutes,me as installProdRoutes,_a as waitForPluginsLifecycle};
1
+ import{serveStatic as R}from"hono/serve-static";import{withPathPrefix as i,withoutPathPrefix as H}from"@redocly/theme/core/utils";import{ServerRoutes as l}from"../../../constants/api.js";import{PUBLIC_STATIC_FOLDER as c}from"../../constants/common.js";import{envConfig as L}from"../../config/env-config.js";import{authMiddleware as C}from"../middleware/authMiddleware.js";import{ensureSearchData as I}from"../middleware/ensureSearchData.js";import{dynamicMiddleware as g}from"../middleware/dynamic-middleware/dynamic-middleware.js";import{installRoutes as D}from"../../plugins/dev-onboarding/api/routes/index.js";import{installSkillsRoutes as h}from"./skills/skills-routes.js";import{authorizeHandler as P,oidcCallbackHandler as N,logoutHandler as s,postLogoutHandler as w,idpLoginHandler as M,redoclyLoginCallbackHandler as B,samlCallbackHandler as v,redoclyTokenLoginHandler as F,inviteHandler as G}from"./auth.js";import{appDataHandler as U}from"./app-data.js";import{searchFacetsHandler as Y,searchHandler as y}from"./search.js";import{dynamicRouteHandler as K}from"./dynamic-route.js";import{pageDataHandler as k,sharedPageDataHandler as b}from"./page-data.js";import{pathPrefixRedirectHandler as x}from"./path-prefix-redirect.js";import{getRoutesByLineHandler as T,resolvePathHandler as E,resolvePathsHandler as V,resolveSlugHandler as _}from"./resolve-route.js";import{feedbackHandler as q}from"./feedback.js";import{loggerMiddleware as X}from"../middleware/loggerMiddleware.js";import{responseHeadersMiddleware as $}from"../middleware/responseHeadersMiddleware.js";import{idleTimeoutMiddleware as j}from"../middleware/idleTimeoutMiddleware.js";import{otelTracesHandler as z}from"./otel/otel.js";import{healthCheckHandler as W}from"./health.js";import{askAiHandler as Z,aiAssistantEscalationsHandler as u,aiAssistantFeedbackHandler as J,AI_ASSISTANT_IDENTITY_STATUS_HEADER as Q}from"./ask-ai.js";import{semanticSearchHandler as aa}from"./semantic-search.js";import{replayOauth2RedirectCallbackHandler as ea}from"./replay-oauth2-redirect.js";import{corsProxyHandler as S}from"./cors-proxy.js";import{corsMiddleware as t}from"../middleware/corsMiddleware.js";import{installApiRoutes as ia}from"./api-routes/api-routes.js";import{cookieMiddleware as la}from"../middleware/cookieMiddleware.js";import{staticContentHandler as ta}from"../routes/static-content.js";import{infoHandler as f}from"./info.js";import{catalogHandler as ma}from"./catalog/catalog.js";import{catalogRelationsHandler as da}from"./catalog/catalog-relations.js";import{bffCatalogHandler as Aa}from"./catalog/bff-catalog.js";import{bffCatalogRevisionsHandler as oa}from"./catalog/bff-catalog-revisions.js";import{bffCatalogRelatedEntitiesHandler as na}from"./catalog/bff-catalog-related-entities.js";import{catalogAuthMiddleware as m}from"../middleware/catalogAuthMiddleware.js";import{telemetryMiddleware as pa}from"../middleware/telemetry-middleware.js";import{errorHandler as sa}from"./error.js";import{installMcpRoutes as Ta}from"./mcp-routes/mcp-routes.js";import{jsonValidatorMiddleware as O}from"../middleware/jsonValidatorMiddleware.js";import{searchFacetsRequestSchema as Ea}from"../../../api-schemas/search-facets.js";import{searchRequestSchema as _a}from"../../../api-schemas/search.js";function de(a,e,d){const{resolveRouteData:o,readStaticAsset:n}=d;a.use("*",j()),a.use("*",la()),a.use("*",g(e)),a.use("*",C(e)),a.use("*",X()),a.use("*",$(e)),a.use("*",pa()),a.use(i("*"),R({root:`./${c}`,getContent:(A,r)=>ta(A,r,e,n),rewriteRequestPath:A=>H(A)})),a.use(i(l.FEEDBACK),t({allowMethods:["POST"]})),a.use(i(l.ASK_AI),t({allowMethods:["POST"],exposeHeaders:[Q]})),a.use(i(l.AI_ASSISTANT_ESCALATIONS),t({allowMethods:["POST"]})),a.use(i(l.AI_ASSISTANT_FEEDBACK),t({allowMethods:["POST"]})),a.use(i(l.SEMANTIC_SEARCH),t({allowMethods:["POST"]})),a.use("*",Sa(e));const p=I(e);a.use(i(l.INFO),f()),L.NEW_CATALOG_ENABLED&&(a.use(i(l.CATALOG_ENTITIES),m({serverOutDir:e.serverOutDir}),ma(e)),a.use(i(l.CATALOG_ENTITIES_RELATIONS),m({serverOutDir:e.serverOutDir}),da(e)),a.get(i(l.BFF_CATALOG_ENTITIES),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),Aa(e)),a.get(i(l.BFF_CATALOG_RELATED_ENTITIES),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),na(e)),a.get(i(l.BFF_CATALOG_REVISIONS),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),oa(e))),a.get(i(l.SHARED_PAGE_DATA),b(e)),a.get(i(l.PAGE_DATA),k(e,o)),a.get(i(l.APP_DATA),U(e)),a.post(i(l.SEARCH),p,O(_a),y(e)),a.post(i(l.SEARCH_FACETS),p,O(Ea),Y(e)),a.post(i(l.AUTHORIZATION),P),a.post(i(l.LOGOUT),s(e)),a.get(i(l.LOGOUT),s(e)),a.get(i(l.POST_LOGOUT),w(e)),a.get(i(l.OIDC_CALLBACK),N(e)),a.get(i(l.REDOCLY_TOKEN_LOGIN),F(e)),a.get(i(l.REDOCLY_LOGIN_CALLBACK),B()),a.get(i(l.IDP_LOGIN),M(e)),a.post(i(l.SAML_CALLBACK),v(e)),a.get(i(l.INVITE),G(e)),a.get(i(l.HEALTH),W),Ta(a,e),D(a,e),h(a,e),a.all(i(l.CORS_PROXY),S(e)),a.all(i(`${l.CORS_PROXY}/*`),S(e)),ia(a,e),a.post(i(l.FEEDBACK),q(e)),a.post(i(l.RESOLVE_ROUTE_BY_PATH),E(e)),a.post(i(l.RESOLVE_ROUTES_BY_PATHS),V(e)),a.post(i(l.RESOLVE_ROUTE_BY_SLUG),_(e)),a.post(i(l.ASK_AI),Z(e)),a.post(i(l.AI_ASSISTANT_ESCALATIONS),u(e)),a.post(i(l.AI_ASSISTANT_FEEDBACK),J(e)),a.post(i(l.SEMANTIC_SEARCH),aa(e)),a.get(i(l.GET_ROUTES_BY_LINE),T(e)),a.post(i(l.OTEL_TRACES),z),a.get(i(l.REPLAY_OAUTH2_CALLBACK),ea),a.all(i("/*"),K(e,o,n)),a.get("*",x),a.onError(sa)}function Sa(a){return async(e,d)=>{await a.waitForPluginsLifecycle(),await d()}}function Ae(a,e){a.get(i(l.INFO),f()),a.post(i(l.RESOLVE_ROUTE_BY_PATH),E(e)),a.post(i(l.RESOLVE_ROUTE_BY_SLUG),_(e)),a.get(i(l.GET_ROUTES_BY_LINE),T(e))}export{Ae as installDevRoutes,de as installProdRoutes,Sa as waitForPluginsLifecycle};
@@ -11,6 +11,8 @@ export type ListResponseResult<T extends {
11
11
  hasPrevPage: boolean;
12
12
  startCursor: string | null;
13
13
  endCursor: string | null;
14
+ nextPage: string | null;
15
+ previousPage: string | null;
14
16
  };
15
17
  };
16
18
  export declare function prepareListResponse<T extends {
@@ -1 +1 @@
1
- import{createCursor as l}from"../../providers/database/pagination/utils/create-cursor.js";import{DEFAULT_LIMIT as o}from"../../providers/database/pagination/limit.js";function c(e){return!!(e.after||e.before)}function g(e,i,r){const n=c(i),t=i.limit??o;if(n){if(i.before)return!0;if(i.after)return e.length===t}return r!==void 0&&i.skip!==void 0?i.skip+t<r:r!==void 0?t<r:e.length===t}function h(e){return!!(e.after||e.before||e.skip!==void 0&&e.skip>0)}function d({data:e,params:i,totalCount:r,nameTransformationsFromDatabase:n={},hasMore:t}){if(!e.length)return{object:"list",items:[],page:{total:0,limit:i.limit??o,hasNextPage:!1,hasPrevPage:!1,startCursor:null,endCursor:null}};const f=i.sort?.length?i.sort:[{field:"id",order:"ASC"}],s=t??g(e,i,r),u=h(i);return{object:"list",items:e,page:{total:r??0,limit:i.limit??o,hasNextPage:s,hasPrevPage:u,startCursor:l(e[0],f,n),endCursor:l(e[e.length-1],f,n)}}}export{d as prepareListResponse};
1
+ import{createCursor as c}from"../../providers/database/pagination/utils/create-cursor.js";import{DEFAULT_LIMIT as o}from"../../providers/database/pagination/limit.js";function h(e){return!!(e.after||e.before)}function b(e,i,n){const r=h(i),t=i.limit??o;if(r){if(i.before)return!0;if(i.after)return e.length===t}return n!==void 0&&i.skip!==void 0?i.skip+t<n:n!==void 0?t<n:e.length===t}function d(e){return!!(e.after||e.before||e.skip!==void 0&&e.skip>0)}function x({data:e,params:i,totalCount:n,nameTransformationsFromDatabase:r={},hasMore:t}){if(!e.length)return{object:"list",items:[],page:{total:0,limit:i.limit??o,hasNextPage:!1,hasPrevPage:!1,startCursor:null,endCursor:null,nextPage:null,previousPage:null}};const l=i.sort?.length?i.sort:[{field:"id",order:"ASC"}],u=t??b(e,i,n),g=d(i),P=!!(i.after||i.before&&t||i.skip!==void 0&&i.skip>0),f=c(e[0],l,r),s=c(e[e.length-1],l,r);return{object:"list",items:e,page:{total:n??0,limit:i.limit??o,hasNextPage:u,hasPrevPage:g,startCursor:f,endCursor:s,nextPage:u?s:null,previousPage:P?f:null}}}export{x as prepareListResponse};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/reef",
3
- "version": "0.137.0-next.1",
3
+ "version": "0.137.0-next.3",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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.1",
97
- "@redocly/asyncapi-docs": "1.14.0-next.1",
96
+ "@redocly/api-docs": "0.2.0-next.3",
97
+ "@redocly/asyncapi-docs": "1.14.0-next.3",
98
98
  "@redocly/config": "0.53.1",
99
- "@redocly/graphql-docs": "1.14.0-next.1",
100
- "@redocly/openapi-docs": "3.25.0-next.1",
99
+ "@redocly/graphql-docs": "1.14.0-next.3",
100
+ "@redocly/openapi-docs": "3.25.0-next.3",
101
101
  "@redocly/portal-legacy-ui": "0.19.0",
102
- "@redocly/portal-plugin-mock-server": "0.22.0-next.1",
103
- "@redocly/realm-asyncapi-sdk": "0.15.0-next.0",
104
- "@redocly/theme": "0.69.0-next.1"
102
+ "@redocly/portal-plugin-mock-server": "0.22.0-next.3",
103
+ "@redocly/realm-asyncapi-sdk": "0.15.0-next.2",
104
+ "@redocly/theme": "0.69.0-next.3"
105
105
  },
106
106
  "peerDependencies": {
107
107
  "react": "^19.2.7",