@redocly/realm 0.137.0-next.9 → 0.137.1

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 (117) hide show
  1. package/CHANGELOG.md +219 -0
  2. package/dist/cli/build/index.js +1 -1
  3. package/dist/client/app/Sidebar/useActions.js +1 -1
  4. package/dist/client/app/Sidebar/useSidebarItems.js +1 -1
  5. package/dist/client/app/hooks/catalog/useCatalogClassic.js +1 -1
  6. package/dist/client/app/hooks/catalog/useCatalogFilter.js +1 -1
  7. package/dist/client/app/search/useFacets.js +1 -1
  8. package/dist/client/app/seo/SeoTags.js +1 -1
  9. package/dist/client/app/utils/loadAndNavigate.js +1 -1
  10. package/dist/server/config/env-config.d.ts +2 -0
  11. package/dist/server/config/env-schema.d.ts +5 -0
  12. package/dist/server/config/env-schemas/feature-flags.d.ts +4 -0
  13. package/dist/server/config/env-schemas/feature-flags.js +1 -1
  14. package/dist/server/constants/common.d.ts +1 -0
  15. package/dist/server/constants/common.js +1 -1
  16. package/dist/server/constants/plugins/search.js +1 -1
  17. package/dist/server/esbuild/esbuild-logger.d.ts +2 -2
  18. package/dist/server/esbuild/esbuild-logger.js +3 -3
  19. package/dist/server/plugins/api-docs/options.js +1 -1
  20. package/dist/server/plugins/api-docs/search/create-indexer.d.ts +5 -0
  21. package/dist/server/plugins/api-docs/search/create-indexer.js +1 -0
  22. package/dist/server/plugins/api-docs/search/get-ai-search-documents.d.ts +1 -1
  23. package/dist/server/plugins/api-docs/search/get-ai-search-documents.js +41 -41
  24. package/dist/server/plugins/api-docs/search/search-resolver.js +1 -1
  25. package/dist/server/plugins/catalog-entities/database/repositories/common/revision-repository.js +1 -1
  26. package/dist/server/plugins/catalog-entities/database/repositories/common/version-repository.js +1 -1
  27. package/dist/server/plugins/catalog-entities/database/repositories/utils/normalize-revision-flags.js +1 -1
  28. package/dist/server/plugins/config-parser/loaders/content-slugs-loader.js +1 -1
  29. package/dist/server/plugins/config-parser/loaders/versions-config-loader.js +1 -1
  30. package/dist/server/plugins/dev-onboarding/template/components/EditAppDescriptionDialog.js +1 -1
  31. package/dist/server/plugins/dev-onboarding/template/components/EditAppDialog.js +1 -1
  32. package/dist/server/plugins/dev-onboarding/template/components/EditCallbackUrlDialog.js +1 -1
  33. package/dist/server/plugins/markdown/is-partial.js +1 -1
  34. package/dist/server/plugins/mcp/codemode/capabilities/fetch/index.js +5 -4
  35. package/dist/server/plugins/mcp/codemode/capabilities/types.d.ts +5 -2
  36. package/dist/server/plugins/mcp/codemode/capabilities/utils.js +4 -4
  37. package/dist/server/plugins/mcp/codemode/code-execution-audit.js +1 -1
  38. package/dist/server/plugins/mcp/codemode/prompts.d.ts +1 -1
  39. package/dist/server/plugins/mcp/codemode/prompts.js +7 -6
  40. package/dist/server/plugins/mcp/codemode/sandbox/create-sandbox-setup.d.ts +2 -2
  41. package/dist/server/plugins/mcp/codemode/sandbox/create-sandbox-setup.js +1 -11
  42. package/dist/server/plugins/mcp/codemode/sandbox/polyfills/console.d.ts +2 -0
  43. package/dist/server/plugins/mcp/codemode/sandbox/polyfills/console.js +7 -0
  44. package/dist/server/plugins/mcp/codemode/sandbox/polyfills/index.d.ts +1 -0
  45. package/dist/server/plugins/mcp/codemode/sandbox/polyfills/index.js +2 -2
  46. package/dist/server/plugins/mcp/codemode/sandbox/tool-input-validator.d.ts +5 -0
  47. package/dist/server/plugins/mcp/codemode/sandbox/tool-input-validator.js +1 -0
  48. package/dist/server/plugins/mcp/codemode/sandbox/unknown-tool-hint.d.ts +8 -0
  49. package/dist/server/plugins/mcp/codemode/sandbox/unknown-tool-hint.js +2 -0
  50. package/dist/server/plugins/mcp/codemode/tools/describe-tools.js +3 -1
  51. package/dist/server/plugins/mcp/codemode/tools/execute.js +1 -1
  52. package/dist/server/plugins/mcp/codemode/tools/manage-credentials.d.ts +2 -2
  53. package/dist/server/plugins/mcp/codemode/tools/manage-credentials.js +2 -2
  54. package/dist/server/plugins/mcp/docs-mcp/tool-schemas.js +1 -1
  55. package/dist/server/plugins/mcp/docs-mcp/tools/core/create-anonymous-session.js +1 -1
  56. package/dist/server/plugins/mcp/docs-mcp/tools/core/search.js +2 -2
  57. package/dist/server/plugins/mcp/docs-mcp/tools/core/types.d.ts +2 -2
  58. package/dist/server/plugins/mcp/docs-mcp/tools/graphql/get-graphql-type.js +5 -5
  59. package/dist/server/plugins/mcp/docs-mcp/tools/graphql/types.d.ts +2 -1
  60. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/list-apis.js +1 -1
  61. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/load-api-description.d.ts +4 -2
  62. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/load-api-description.js +1 -1
  63. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/types.d.ts +6 -2
  64. package/dist/server/plugins/mcp/docs-mcp/utils/api-titles.d.ts +6 -0
  65. package/dist/server/plugins/mcp/docs-mcp/utils/api-titles.js +1 -0
  66. package/dist/server/plugins/mcp/gateway-mcp/elicitation.d.ts +11 -0
  67. package/dist/server/plugins/mcp/gateway-mcp/elicitation.js +5 -5
  68. package/dist/server/plugins/mcp/gateway-mcp/fetch/ssrf-guard.js +1 -1
  69. package/dist/server/plugins/mcp/gateway-mcp/fetch/url-placeholders.d.ts +5 -0
  70. package/dist/server/plugins/mcp/gateway-mcp/fetch/url-placeholders.js +1 -0
  71. package/dist/server/plugins/mcp/servers/docs-server.js +1 -1
  72. package/dist/server/plugins/mcp/servers/mcp-server.js +3 -3
  73. package/dist/server/plugins/mcp/types.d.ts +1 -0
  74. package/dist/server/plugins/mcp/utils/available-sandbox-tools.js +1 -1
  75. package/dist/server/plugins/mcp/workers/execute-mcp-tool.js +1 -1
  76. package/dist/server/plugins/search/ai-indexer/prepare-semantic-documents.js +1 -1
  77. package/dist/server/plugins/search/engines/flexsearch/index.js +1 -1
  78. package/dist/server/plugins/search/engines/flexsearch/search-index.d.ts +8 -4
  79. package/dist/server/plugins/search/engines/flexsearch/search-index.js +1 -1
  80. package/dist/server/plugins/search/llmstxt/create-llms-txt-generator.js +1 -1
  81. package/dist/server/plugins/search/llmstxt/index.d.ts +3 -1
  82. package/dist/server/plugins/search/llmstxt/index.js +9 -9
  83. package/dist/server/sandbox/sandbox.js +2 -2
  84. package/dist/server/sandbox/types.d.ts +9 -0
  85. package/dist/server/sandbox/utils/execution-budget.js +1 -1
  86. package/dist/server/sandbox/utils/return-last-expression.d.ts +6 -0
  87. package/dist/server/sandbox/utils/return-last-expression.js +1 -0
  88. package/dist/server/sandbox/utils/returns-from-top-level.d.ts +1 -2
  89. package/dist/server/sandbox/utils/returns-from-top-level.js +1 -1
  90. package/dist/server/sandbox/utils/setup-host-functions.d.ts +1 -0
  91. package/dist/server/sandbox/utils/setup-host-functions.js +2 -2
  92. package/dist/server/ssr/render.js +1 -1
  93. package/dist/server/store.js +1 -1
  94. package/dist/server/types/plugins/search.d.ts +7 -2
  95. package/dist/server/utils/globs.d.ts +4 -0
  96. package/dist/server/utils/globs.js +1 -1
  97. package/dist/server/utils/search/highlight-text-for-search.js +1 -1
  98. package/dist/server/utils/static-data.js +1 -1
  99. package/dist/server/web-server/auth.d.ts +9 -1
  100. package/dist/server/web-server/auth.js +4 -4
  101. package/dist/server/web-server/jwt/jwt.js +1 -1
  102. package/dist/server/web-server/jwt/token-types.d.ts +11 -0
  103. package/dist/server/web-server/jwt/token-types.js +1 -0
  104. package/dist/server/web-server/middleware/ensureSearchData.js +1 -1
  105. package/dist/server/web-server/routes/auth.js +1 -1
  106. package/dist/server/web-server/routes/dynamic-route.js +1 -1
  107. package/dist/server/web-server/routes/mcp-routes/mcp-oauth.js +1 -1
  108. package/dist/server/web-server/routes/mcp-routes/mcp-server-card.js +1 -1
  109. package/dist/server/web-server/routes/semantic-search.d.ts +1 -1
  110. package/dist/server/web-server/routes/semantic-search.js +1 -1
  111. package/dist/server/web-server/routes/static-content.js +1 -1
  112. package/dist/{server/plugins/catalog-entities/utils/version-compare.d.ts → utils/version/compare-versions.d.ts} +1 -16
  113. package/dist/utils/version/compare-versions.js +1 -0
  114. package/package.json +11 -10
  115. package/dist/server/plugins/api-docs/search/search-indexer.d.ts +0 -9
  116. package/dist/server/plugins/api-docs/search/search-indexer.js +0 -1
  117. package/dist/server/plugins/catalog-entities/utils/version-compare.js +0 -1
@@ -1,13 +1,13 @@
1
- import d from"picomatch";import{dirname as T,resolve as x}from"node:path";import{access as $,readFile as E,constants as F}from"fs/promises";import{removeTrailingSlash as w}from"../../../../utils/url/remove-trailing-slash.js";import{logger as g}from"../../../tools/notifiers/logger.js";import{withPathPrefix as y}from"@redocly/theme/core/utils";import{envConfig as I}from"../../../config/env-config.js";import{getLlmsTxtMdPathBySlug as v}from"../../../utils/llmstxt/get-llms-txt-md-path-by-slug.js";const a="llms.txt",u="Table of contents";async function D(t,e,n={title:a,description:void 0},o){if(!e)return{title:n.title||a,description:n.description,details:void 0,sections:[{title:u,description:void 0,llmstxts:t}]};const s=e.title||n.title||a,r=e.description||n.description,i=await M(e,o),c=e.sections?.map(l=>{const{title:f,description:h,excludeFiles:m,includeFiles:L}=l,P=p(t,{excludeFiles:m,includeFiles:L});return{title:f,description:h,llmstxts:P}});return{title:s,description:r,details:i,sections:c||[{title:u,description:void 0,llmstxts:p(t,{includeFiles:["**/*"],excludeFiles:[]})}]}}async function M(t,e){const n=await e.getConfig();if(t?.details?.path&&n.configPath)try{const o=e.fs.getFileInfo(n.configPath);if(!o)throw new Error(`Config file ${n.configPath} not found`);const s=x(e.fs.cwd,T(o.relativePath),t.details.path);return await $(s,F.R_OK),E(s,"utf-8")}catch{throw new Error(`${t.details.path} is not accessible`)}if(t?.details?.content)return t.details.content}function p(t,e){const{excludeFiles:n,includeFiles:o}=e,s=n?.map(i=>d(i)),r=o?.map(i=>d(i));return t.filter(i=>s?.some(l=>l(i.fsPath))?!1:r?.some(l=>l(i.fsPath)))}async function j(t,e,n={title:a,description:void 0},o){const s=await D(t,e,n,o),r=[`# ${s.title}
1
+ import d from"picomatch";import{dirname as T,resolve as $}from"node:path";import{access as x,readFile as E,constants as F}from"fs/promises";import{removeTrailingSlash as w}from"../../../../utils/url/remove-trailing-slash.js";import{logger as g}from"../../../tools/notifiers/logger.js";import{withPathPrefix as U}from"@redocly/theme/core/utils";import{envConfig as y}from"../../../config/env-config.js";import{getLlmsTxtMdPathBySlug as I}from"../../../utils/llmstxt/get-llms-txt-md-path-by-slug.js";const a="llms.txt",u="Table of contents";async function v(e,i,n={title:a,description:void 0},r){if(!i)return{title:n.title||a,description:n.description,details:void 0,sections:[{title:u,description:void 0,llmstxts:e}]};const s=i.title||n.title||a,l=i.description||n.description,t=await D(i,r),o=i.sections?.map(c=>{const{title:f,description:h,excludeFiles:m,includeFiles:L}=c,P=p(e,{excludeFiles:m,includeFiles:L});return{title:f,description:h,llmstxts:P}});return{title:s,description:l,details:t,sections:o||[{title:u,description:void 0,llmstxts:p(e,{includeFiles:["**/*"],excludeFiles:[]})}]}}async function D(e,i){const n=await i.getConfig();if(e?.details?.path&&n.configPath)try{const r=i.fs.getFileInfo(n.configPath);if(!r)throw new Error(`Config file ${n.configPath} not found`);const s=$(i.fs.cwd,T(r.relativePath),e.details.path);return await x(s,F.R_OK),E(s,"utf-8")}catch{throw new Error(`${e.details.path} is not accessible`)}if(e?.details?.content)return e.details.content}function p(e,i){const{excludeFiles:n,includeFiles:r}=i,s=n?.map(t=>d(t)),l=r?.map(t=>d(t));return e.filter(t=>s?.some(c=>c(t.fsPath))?!1:l?.some(c=>c(t.fsPath)))}async function b(e,i,n={title:a,description:void 0,siteUrl:void 0},r){const s=await v(e,i,n,r),l=n.siteUrl,t=[`# ${s.title}
2
2
 
3
- `];return s.description&&r.push(`> ${s.description}
3
+ `];return s.description&&t.push(`> ${s.description}
4
4
 
5
- `),s.details&&r.push(`${s.details}
5
+ `),s.details&&t.push(`${s.details}
6
6
 
7
- `),s.sections.forEach(i=>{if(!i.llmstxts.length){g.warn(`llms.txt: No links found for section "${i.title}". Please check the includeFiles/excludeFiles patterns.`);return}r.push(`## ${i.title}
8
- `),i.description?r.push(`${i.description.replace(/\n+$/,"")}
7
+ `),s.sections.forEach(o=>{if(!o.llmstxts.length){g.warn(`llms.txt: No links found for section "${o.title}". Please check the includeFiles/excludeFiles patterns.`);return}t.push(`## ${o.title}
8
+ `),o.description?t.push(`${o.description.replace(/\n+$/,"")}
9
9
 
10
- `):r.push(`
11
- `),i.llmstxts.forEach(c=>{r.push(` - ${_({title:c.title,description:c.description,slug:v(c.slug)})}`)}),r.push(`
12
- `)}),r.join("")}function _({title:t,description:e,slug:n}){const o=e?.replace(/\s+/g," ").trim();return`[${t}](${w(I.REDOCLY_PUBLIC_URL||"")}${encodeURI(y(n))})${o?`: ${o}`:""}
13
- `}function K(t){if(t?.details?.path&&t?.details?.content)throw new Error('"details.path" and "details.content" are mutually exclusive. Please use only one of them.')}export{j as generateLLMsTxt,_ as llmsTxtLink,K as validateLLMsTxtConfig};
10
+ `):t.push(`
11
+ `),o.llmstxts.forEach(c=>{t.push(` - ${M({title:c.title,description:c.description,slug:I(c.slug),siteUrl:l})}`)}),t.push(`
12
+ `)}),t.join("")}function M({title:e,description:i,slug:n,siteUrl:r}){const l=`${w(r?.trim()||y.REDOCLY_PUBLIC_URL||"")}${encodeURI(U(n))}`,t=i?.replace(/\s+/g," ").trim();return`[${e}](${l})${t?`: ${t}`:""}
13
+ `}function j(e){if(e?.details?.path&&e?.details?.content)throw new Error('"details.path" and "details.content" are mutually exclusive. Please use only one of them.')}export{b as generateLLMsTxt,M as llmsTxtLink,j as validateLLMsTxtConfig};
@@ -1,3 +1,3 @@
1
- import p from"@jitl/quickjs-singlefile-mjs-release-sync";import{newQuickJSWASMModuleFromVariant as _,newVariant as E}from"quickjs-emscripten-core";import{createExecutionBudget as S}from"./utils/execution-budget.js";import{returnsFromTopLevel as A}from"./utils/returns-from-top-level.js";import{DEFAULT_MAX_LIFETIME_MS as y,DEFAULT_TIMEOUT_MS as b,INITIAL_TOTAL_MEMORY_BYTES as h,MAX_ALLOCATION_BYTES as L,MAX_RESULT_BYTES as g,MAX_STACK_SIZE_BYTES as w,MAX_TOTAL_MEMORY_BYTES as x,WASM_PAGE_BYTES as d}from"./constants/sandbox.js";let c;function I(){return c??=_(E(p,{wasmMemory:new WebAssembly.Memory({initial:h/d,maximum:x/d})}))}async function P(t,l={}){const{timeoutMs:m=b,maxLifetimeMs:f=y,setupContext:M}=l,e=(await I()).newContext();let n,i,r;try{e.runtime.setMemoryLimit(L),e.runtime.setMaxStackSize(w),n=S(e,m,f),i=M?.(e,n)??void 0,r=e.unwrapResult(e.evalCode(`(async () => {
1
+ import p from"@jitl/quickjs-singlefile-mjs-release-sync";import{newQuickJSWASMModuleFromVariant as _,newVariant as S}from"quickjs-emscripten-core";import{createExecutionBudget as A}from"./utils/execution-budget.js";import{returnsFromTopLevel as x}from"./utils/returns-from-top-level.js";import{DEFAULT_MAX_LIFETIME_MS as b,DEFAULT_TIMEOUT_MS as y,INITIAL_TOTAL_MEMORY_BYTES as g,MAX_ALLOCATION_BYTES as L,MAX_RESULT_BYTES as h,MAX_STACK_SIZE_BYTES as w,MAX_TOTAL_MEMORY_BYTES as I,WASM_PAGE_BYTES as f}from"./constants/sandbox.js";let M;function O(){return M??=_(S(p,{wasmMemory:new WebAssembly.Memory({initial:g/f,maximum:I/f})}))}async function X(t,s={}){const{timeoutMs:n=y,maxLifetimeMs:u=b,setupContext:E,authoredCode:T=t}=s,e=(await O()).newContext();let r,l,a;try{e.runtime.setMemoryLimit(L),e.runtime.setMaxStackSize(w),r=A(e,n,u),l=E?.(e,r)??void 0,a=e.unwrapResult(e.evalCode(`(async () => {
2
2
  ${t}
3
- })()`));const o=e.resolvePromise(r);e.runtime.executePendingJobs();const T=await Promise.race([o,n.aborted]),u=e.unwrapResult(T),s=e.dump(u);if(u.dispose(),s===void 0)return{error:A(e,t)?"The code returned undefined: the `return` ran but produced no value. Read fields off the declared return types \u2014 a field name that does not exist reads as undefined \u2014 and do not `return;` or `return undefined`.":"The code returned undefined because nothing was returned. Add a top-level `return`: a value computed inside a function or IIFE is discarded unless you return it, and a promise chain has to be returned to be awaited."};const a=JSON.stringify(s??null).length;return a>g?{error:`Result is too large (${a} bytes); return less data`}:{result:s}}catch(o){return{error:v(o)}}finally{n?.dispose(),r?.dispose();try{i?.()}catch{}try{e.dispose()}catch{c=void 0}}}function v(t){return t instanceof Error?t.message:String(t)}export{P as runInSandbox};
3
+ })()`,k));const i=e.resolvePromise(a);e.runtime.executePendingJobs();const d=await Promise.race([i,r.aborted]),o=e.unwrapResult(d),c=e.dump(o);if(o.dispose(),c===void 0)return{error:x(T)?"The code returned undefined: the `return` ran but produced no value. Read fields off the declared return types \u2014 a field name that does not exist reads as undefined \u2014 and do not `return;` or `return undefined`.":"The code returned undefined because nothing was returned. Add a top-level `return` of the value to send back."};const m=JSON.stringify(c??null).length;return m>h?{error:`Result is too large (${m} bytes); return less data`}:{result:c}}catch(i){const d=r?.expiredMessage();if(d!==void 0)return{error:d};const o=Y(i);return{error:R(i),...o?{errorPosition:o}:{}}}finally{r?.dispose(),a?.dispose();try{l?.()}catch{}try{e.dispose()}catch{M=void 0}}}function R(t){return t instanceof Error?t.message:String(t)}const k="sandbox-code.js",v=/sandbox-code\.js:(\d+):(\d+)/;function Y(t){const s=t.cause?.stack,n=typeof s=="string"?v.exec(s):null;if(!n)return;const u=Number(n[1])-1;return u>=1?{line:u,column:Number(n[2])}:void 0}export{X as runInSandbox};
@@ -1,7 +1,13 @@
1
1
  import type { QuickJSContext } from 'quickjs-emscripten-core';
2
+ /** 1-based position in the submitted code where the guest error was thrown. */
3
+ export type GuestPosition = {
4
+ line: number;
5
+ column: number;
6
+ };
2
7
  export type SandboxResult = {
3
8
  result?: unknown;
4
9
  error?: string;
10
+ errorPosition?: GuestPosition;
5
11
  };
6
12
  export type ExecutionBudget = {
7
13
  /**
@@ -13,6 +19,8 @@ export type ExecutionBudget = {
13
19
  hostCall<T>(work: () => Promise<T>): Promise<T>;
14
20
  /** Rejects when a wall-clock deadline fires */
15
21
  aborted: Promise<never>;
22
+ /** The message of the deadline that fired, or `undefined` while the run is still in budget */
23
+ expiredMessage(): string | undefined;
16
24
  dispose(): void;
17
25
  };
18
26
  export type SetupSandboxContext = (context: QuickJSContext, budget: ExecutionBudget) => void | (() => void);
@@ -20,6 +28,7 @@ export type RunInSandboxOptions = {
20
28
  timeoutMs?: number;
21
29
  maxLifetimeMs?: number;
22
30
  setupContext?: SetupSandboxContext;
31
+ authoredCode?: string;
23
32
  };
24
33
  export type SandboxHostFunction = (input: unknown) => Promise<unknown> | unknown;
25
34
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- function f(c,t,i){const d=Date.now()+i;let l=Date.now()+t,u=t,a=()=>{};const T=new Promise((e,o)=>{a=r=>o(new Error(r))});let n;const m=()=>{clearTimeout(n),n=setTimeout(()=>a(`Execution timed out after ${t}ms`),t)};c.runtime.setInterruptHandler(()=>{const e=Date.now();return e>l||e>d}),m();const w=setTimeout(()=>a(`Exceeded the maximum lifetime of ${i}ms`),i);let s=0;return{hostCall:e=>{u=l-Date.now(),s++,clearTimeout(n);const o=()=>{c.alive&&(l=Date.now()+u,--s===0&&m())},r=e();return r.then(o,o),r},aborted:T,dispose(){clearTimeout(n),clearTimeout(w)}}}export{f as createExecutionBudget};
1
+ function h(c,i,l){const u=`Execution timed out after ${i}ms`,m=`Exceeded the maximum lifetime of ${l}ms`,w=Date.now()+l;let s=Date.now()+i,d=i,e,a=()=>{};const p=new Promise((t,r)=>{a=n=>{e=n,r(new Error(n))}});let o;const f=()=>{clearTimeout(o),o=setTimeout(()=>a(u),i)};c.runtime.setInterruptHandler(()=>{const t=Date.now();return t>s?e=u:t>w&&(e=m),e!==void 0}),f();const D=setTimeout(()=>a(m),l);let T=0;return{hostCall:t=>{d=s-Date.now(),T++,clearTimeout(o);const r=()=>{c.alive&&(s=Date.now()+d,--T===0&&f())},n=t();return n.then(r,r),n},aborted:p,expiredMessage:()=>e,dispose(){clearTimeout(o),clearTimeout(D)}}}export{h as createExecutionBudget};
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Prefixes the last top-level statement with `return ` when it is a bare expression, so sandbox
3
+ * code reads like a REPL. Code that ends in anything else stays byte-identical.
4
+ */
5
+ export declare function returnLastExpression(code: string): string;
6
+ //# sourceMappingURL=return-last-expression.d.ts.map
@@ -0,0 +1 @@
1
+ import{parse as e}from"acorn";function i(t){let r;try{r=e(t,{ecmaVersion:"latest",sourceType:"script",allowAwaitOutsideFunction:!0,allowReturnOutsideFunction:!0}).body.at(-1)}catch{return t}return r?.type!=="ExpressionStatement"||r.directive!==void 0?t:`${t.slice(0,r.start)}return ${t.slice(r.start)}`}export{i as returnLastExpression};
@@ -1,4 +1,3 @@
1
- import type { QuickJSContext } from 'quickjs-emscripten-core';
2
1
  /** Whether `code` returns from the guest wrapper itself rather than from a nested function. */
3
- export declare function returnsFromTopLevel(context: QuickJSContext, code: string): boolean;
2
+ export declare function returnsFromTopLevel(code: string): boolean;
4
3
  //# sourceMappingURL=returns-from-top-level.d.ts.map
@@ -1 +1 @@
1
- const o="return not in a function";function u(r,t){let e;try{e=r.evalCode(t,"return-probe.js",{type:"module",compileOnly:!0})}catch{return!1}if(!e.error)return e.value.dispose(),!1;const n=r.dump(e.error);return e.error.dispose(),String(n?.message??"").includes(o)}export{u as returnsFromTopLevel};
1
+ import{parse as n}from"acorn";const u=new Set(["FunctionDeclaration","FunctionExpression","ArrowFunctionExpression"]);function o(t){try{const r=n(t,{ecmaVersion:"latest",sourceType:"script",allowAwaitOutsideFunction:!0,allowReturnOutsideFunction:!0});return e(r)}catch{return!1}}function e(t){if(Array.isArray(t))return t.some(e);if(t===null||typeof t!="object")return!1;const{type:r}=t;return r==="ReturnStatement"?!0:typeof r=="string"&&u.has(r)?!1:Object.values(t).some(e)}export{o as returnsFromTopLevel};
@@ -10,6 +10,7 @@ export declare function setupHostFunctions(context: QuickJSContext, hostFunction
10
10
  globalName?: string;
11
11
  budget?: ExecutionBudget;
12
12
  maxCalls?: number;
13
+ maxOutputBytes?: number;
13
14
  messages?: {
14
15
  outputTooLarge?: (bytes: number) => string;
15
16
  callLimitExceeded?: (max: number) => string;
@@ -1,5 +1,5 @@
1
- import{DEFAULT_GLOBAL_NAME as v,MAX_HOST_OUTPUT_BYTES as _}from"../constants/host-functions.js";import{evalGuestCode as E}from"./eval-guest-code.js";function w(e,o,s){const l=s?.globalName??v,u=s?.maxCalls,f=s?.messages?.outputTooLarge??(n=>`Host function output is too large (${n} bytes); request narrower data instead`),m=s?.messages?.callLimitExceeded??(n=>`Exceeded the limit of ${n} host calls`),d=s?.budget?.hostCall??(n=>n()),c=new Set;let S=0;return e.newFunction("__invoke",(n,a)=>{const h=e.getString(n),p=JSON.parse(e.getString(a)),r=e.newPromise();return c.add(r),u!==void 0&&++S>u?e.newError(m(u)).consume(r.reject):d(()=>Promise.resolve().then(()=>o[h](p))).then(t=>{if(!e.alive)return;let i;try{i=JSON.stringify(t??null)}catch(g){const b=g instanceof Error?g.message:String(g);e.newError(`Host function output is not serializable: ${b}`).consume(r.reject);return}i.length>_?e.newError(f(i.length)).consume(r.reject):e.newString(i).consume(r.resolve)},t=>{if(!e.alive)return;const i=t instanceof Error?t.message:String(t);e.newError(i).consume(r.reject)}),r.settled.then(()=>{e.alive&&e.runtime.executePendingJobs()}),r.handle}).consume(n=>e.setProp(e.global,"__invoke",n)),E(e,O(Object.keys(o),l)),function(){for(const a of c)a.alive&&a.dispose()}}function O(e,o){const s=e.map(l=>`${JSON.stringify(l)}: (input) => invoke(${JSON.stringify(l)}, JSON.stringify(input ?? {})).then(JSON.parse)`).join(", ");return`(() => {
1
+ import{DEFAULT_GLOBAL_NAME as v,MAX_HOST_OUTPUT_BYTES as _}from"../constants/host-functions.js";import{evalGuestCode as E}from"./eval-guest-code.js";function w(e,o,n){const l=n?.globalName??v,u=n?.maxCalls,m=n?.maxOutputBytes??_,f=n?.messages?.outputTooLarge??(s=>`Host function output is too large (${s} bytes); request narrower data instead`),d=n?.messages?.callLimitExceeded??(s=>`Exceeded the limit of ${s} host calls`),p=n?.budget?.hostCall??(s=>s()),c=new Set;let S=0;return e.newFunction("__invoke",(s,a)=>{const h=e.getString(s),O=JSON.parse(e.getString(a)),r=e.newPromise();return c.add(r),u!==void 0&&++S>u?e.newError(d(u)).consume(r.reject):p(()=>Promise.resolve().then(()=>o[h](O))).then(i=>{if(!e.alive)return;let t;try{t=JSON.stringify(i??null)}catch(g){const b=g instanceof Error?g.message:String(g);e.newError(`Host function output is not serializable: ${b}`).consume(r.reject);return}t.length>m?e.newError(f(t.length)).consume(r.reject):e.newString(t).consume(r.resolve)},i=>{if(!e.alive)return;const t=i instanceof Error?i.message:String(i);e.newError(t).consume(r.reject)}),r.settled.then(()=>{e.alive&&e.runtime.executePendingJobs()}),r.handle}).consume(s=>e.setProp(e.global,"__invoke",s)),E(e,y(Object.keys(o),l)),function(){for(const a of c)a.alive&&a.dispose()}}function y(e,o){const n=e.map(l=>`${JSON.stringify(l)}: (input) => invoke(${JSON.stringify(l)}, JSON.stringify(input ?? {})).then(JSON.parse)`).join(", ");return`(() => {
2
2
  const invoke = globalThis.__invoke;
3
3
  delete globalThis.__invoke;
4
- globalThis[${JSON.stringify(o)}] = Object.freeze({ ${s} });
4
+ globalThis[${JSON.stringify(o)}] = Object.freeze({ ${n} });
5
5
  })();`}export{w as setupHostFunctions};
@@ -1 +1 @@
1
- import{pathToFileURL as z}from"url";import{HelmetProvider as G}from"@dr.pogodin/react-helmet";import{renderToString as l}from"react-dom/server";import{StaticRouterProvider as J,createStaticHandler as K,createStaticRouter as Q}from"react-router";import r from"react";import{readFile as V}from"fs/promises";import{join as W}from"path";import{withPathPrefix as m,getLocaleFromPathname as X,withoutPathPrefix as Y,removeTrailingSlash as Z}from"@redocly/theme/core/utils";import{RUNTIME_RESOURCES_DIR as T}from"../constants/common.js";import{slash as tt}from"../../utils/path/slash.js";import{reporter as et}from"../tools/notifiers/reporter.js";import{envConfig as p}from"../config/env-config.js";import{slug as rt}from"../../utils/slugger.js";import{shouldForwardProp as ot}from"../../utils/should-forward-prop.js";import{htmlTemplate as st}from"./template.js";import{detectColorSchemaScript as nt,generatePreloadPageData as at,generatePreloadSharedData as it}from"./utils.js";import"./hijack-console.js";import{getSitemapLinkTag as ct}from"./get-sitemap-link-tag.js";let g="";async function lt(t){return p.isRuntimeMode?await import("../../client/server-entry.js"):await import(z(`${tt(t)}/server-entry.js`)+"?"+new Date)}async function Bt(t){if(!p.isRuntimeMode){const e=et.getCompilationProblems();if(e.length>0){const{renderCompilationError:n}=await import("./render-compilation-error.js");return n(e)}}const{Loader:u,App:R,routes:w,ServerStyleSheet:E,StyleSheetManager:P,ErrorDetails:C,initL10n:D}=await lt(t.serverOutDir),{page:h,store:{globalData:S,hasSitemap:y},sharedData:v,ssrHref:b}=t,d=S?.l10n,i=X(Y(h.slug),d?.defaultLocale,d?.locales),f=t.page.request?.url||"http://localhost/";await u.prepare({...h,slug:Z(new URL(f).pathname)},v,S,i),await D(i);const c=new E,{query:M,dataRoutes:x}=K([{Component:R,path:m("*"),children:w}]),s=await M(new Request(f));if(s instanceof Response)throw s;const B=Q(x,s);try{globalThis.SSR_HOSTNAME=b,globalThis.SSR_USER_AGENT=t.userAgent,globalThis.SSR_OMIT_SUSPENSE=t.omitSuspense;const e={},n=l(r.createElement(G,{context:e},r.createElement(P,{sheet:c.instance,shouldForwardProp:ot},r.createElement(J,{router:B,context:s})))),L=y?ct():"",a=e.helmet,j=a?.title.toString()||"",k=t.store.globalData?.logo?.favicon,o=t.store.config,_=`<link rel="preload" as="fetch" crossorigin="anonymous" href="${m("/app-data.json")}" />`,F=at(t.page.slug),H=it(t.page.sharedDataIds),U=nt(o),A=t.page.product?.name?`product-${rt(t.page.product.name)}`:"",$=t.store.ssr.headTags.join(""),I=t.store.ssr.postBodyTags.join(""),N=t.store.ssr.preBodyTags.join(""),O=`<link rel="stylesheet" href="${m(T)}/browser-entry.css" />`;!p.isDevelopMode&&!g&&(g=`<style>${await V(W(t.outdir,T,"browser-entry.css"),"utf-8")}</style>`);const q={bodyHtml:n,linkTags:o.linkTags+c.getStyleTags()+(a?.link.toString()||""),title:j,favicon:k,headScriptTags:L+U+o.headScriptTags+$+(a?.script.toString()||"")+(a?.meta.toString()||""),preload:[_,F,...H],postBodyScriptTags:o.postBodyScriptTags+I,preBodyScriptTags:o.preBodyScriptTags||""+N,lang:i,productClass:A,runtimeCss:g||O};return{html:st(q),statusCode:200}}catch(e){return t.telemetry.sendSsrErrorCaughtMessage([{id:"ssr",object:"error",uri:"urn:redocly:realm:cli:error:ssr",message:e.message}]),{html:l(r.createElement(C,{error:e})),statusCode:500,error:e}}finally{c.seal(),u.clear()}}function Lt(t){return l(r.createElement(r.Fragment,null,...t))}export{Bt as render,Lt as renderComponents};
1
+ import{pathToFileURL as z}from"url";import{HelmetProvider as G}from"@dr.pogodin/react-helmet";import{renderToString as l}from"react-dom/server";import{StaticRouterProvider as J,createStaticHandler as K,createStaticRouter as Q}from"react-router";import r from"react";import{readFile as V}from"fs/promises";import{join as W}from"path";import{withPathPrefix as m,getLocaleFromPathname as X,withoutPathPrefix as Y,removeTrailingSlash as Z,getSafeClassName as tt}from"@redocly/theme/core/utils";import{RUNTIME_RESOURCES_DIR as T}from"../constants/common.js";import{slash as et}from"../../utils/path/slash.js";import{reporter as rt}from"../tools/notifiers/reporter.js";import{envConfig as p}from"../config/env-config.js";import{slug as ot}from"../../utils/slugger.js";import{shouldForwardProp as st}from"../../utils/should-forward-prop.js";import{htmlTemplate as nt}from"./template.js";import{detectColorSchemaScript as at,generatePreloadPageData as it,generatePreloadSharedData as ct}from"./utils.js";import"./hijack-console.js";import{getSitemapLinkTag as lt}from"./get-sitemap-link-tag.js";let g="";async function mt(t){return p.isRuntimeMode?await import("../../client/server-entry.js"):await import(z(`${et(t)}/server-entry.js`)+"?"+new Date)}async function Lt(t){if(!p.isRuntimeMode){const e=rt.getCompilationProblems();if(e.length>0){const{renderCompilationError:n}=await import("./render-compilation-error.js");return n(e)}}const{Loader:u,App:R,routes:w,ServerStyleSheet:E,StyleSheetManager:C,ErrorDetails:P,initL10n:D}=await mt(t.serverOutDir),{page:h,store:{globalData:S,hasSitemap:y},sharedData:v,ssrHref:b}=t,d=S?.l10n,i=X(Y(h.slug),d?.defaultLocale,d?.locales),f=t.page.request?.url||"http://localhost/";await u.prepare({...h,slug:Z(new URL(f).pathname)},v,S,i),await D(i);const c=new E,{query:M,dataRoutes:x}=K([{Component:R,path:m("*"),children:w}]),s=await M(new Request(f));if(s instanceof Response)throw s;const B=Q(x,s);try{globalThis.SSR_HOSTNAME=b,globalThis.SSR_USER_AGENT=t.userAgent,globalThis.SSR_OMIT_SUSPENSE=t.omitSuspense;const e={},n=l(r.createElement(G,{context:e},r.createElement(C,{sheet:c.instance,shouldForwardProp:st},r.createElement(J,{router:B,context:s})))),L=y?lt():"",a=e.helmet,j=a?.title.toString()||"",k=t.store.globalData?.logo?.favicon,o=t.store.config,_=`<link rel="preload" as="fetch" crossorigin="anonymous" href="${m("/app-data.json")}" />`,F=it(t.page.slug),H=ct(t.page.sharedDataIds),U=at(o),A=t.page.product?.name?tt(`product-${ot(t.page.product.name)}`):"",N=t.store.ssr.headTags.join(""),$=t.store.ssr.postBodyTags.join(""),I=t.store.ssr.preBodyTags.join(""),O=`<link rel="stylesheet" href="${m(T)}/browser-entry.css" />`;!p.isDevelopMode&&!g&&(g=`<style>${await V(W(t.outdir,T,"browser-entry.css"),"utf-8")}</style>`);const q={bodyHtml:n,linkTags:o.linkTags+c.getStyleTags()+(a?.link.toString()||""),title:j,favicon:k,headScriptTags:L+U+o.headScriptTags+N+(a?.script.toString()||"")+(a?.meta.toString()||""),preload:[_,F,...H],postBodyScriptTags:o.postBodyScriptTags+$,preBodyScriptTags:o.preBodyScriptTags||""+I,lang:i,productClass:A,runtimeCss:g||O};return{html:nt(q),statusCode:200}}catch(e){return t.telemetry.sendSsrErrorCaughtMessage([{id:"ssr",object:"error",uri:"urn:redocly:realm:cli:error:ssr",message:e.message}]),{html:l(r.createElement(P,{error:e})),statusCode:500,error:e}}finally{c.seal(),u.clear()}}function jt(t){return l(r.createElement(r.Fragment,null,...t))}export{Lt as render,jt as renderComponents};
@@ -1 +1 @@
1
- import C from"@markdoc/markdoc";import{getPathnameForLocale as A}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as g}from"../constants/common.js";import{DEFAULT_TITLE as T}from"./constants/common.js";import{GATED_MARKDOC_TAGS as R}from"./constants/entitlements.js";import{MARKDOC_PARTIALS_DATA_KEY as m,MARKDOC_PARTIALS_DEPS_KEY as M}from"./constants/common.js";import{isObject as O}from"../utils/guards/is-object.js";import{mapObject as k}from"../utils/object/map-object.js";import{getValueDeep as y}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as L}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as h}from"../utils/path/normalize-route-slug.js";import{isLocalLink as D}from"../utils/path/is-local-link.js";import{reporter as S}from"./tools/notifiers/reporter.js";import{logger as u}from"./tools/notifiers/logger.js";import{sha1 as H}from"./utils/crypto/sha1.js";import{envConfig as G,setEnv as _}from"./config/env-config.js";import{KvService as B}from"./persistence/kv/services/kv-service.js";import{SandboxContext as F}from"./utils/sandbox-context.js";import{writeSharedData as I}from"./utils/index.js";import{renderComponents as N}from"./ssr/render.js";import{readStaticData as V,writeStaticData as q}from"./utils/static-data.js";import{parseAndResolveMarkdoc as j}from"./plugins/markdown/compiler.js";import{getMarkdocOptions as U}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as w}from"./entitlements/entitlements-provider.js";import{isL10nPath as x}from"./fs/utils/is-l10n-path.js";import{resolveGlobMapValue as K}from"./utils/globs.js";import{replaceEnvVariablesDeep as J}from"./utils/envs/replace-env-variables-deep.js";import{findRedirect as W}from"./utils/redirects/find-redirect.js";import{followRedirectChain as $}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as z}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as Y}from"../cli/telemetry/helpers/trace-step.js";const v={routesBySlug:"map",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",config:"object",ssr:"object",searchFacets:"map",routesPartials:"map",mcpToolHandlers:"map",skills:"map"},E="PLAN_GATES",Z=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],Oe="userDefinedApiFunctions";class P{routesBySlug=new Map;replacedEnvVars={};unsetEnvVars=new Set;lifecycleContext;newRoutes=[];#t={};routesByFsPath=new Map;apiRoutes=[];middleware=[];routesSharedData=new Map;sharedDataDeps=new Map;sharedDataMarkdocComponents=new Map;routesDynamicComponents=new Map;routesPartials=new Map;ssr={preBodyTags:[],postBodyTags:[],headTags:[]};searchFacets=new Map;searchEngine;templates=new Map;browserPlugins=new Set;apiRoutesRequestHandlers=new Map;mcpToolHandlers=new Map;skills=new Map;serverPropsGetters=new Map;pagePropsGetters=new Map;listeners=new Map;globalData={};#o=void 0;config={configFilePath:"",redirects:{},wildcardRedirectsTree:{},access:{rbac:{},requiresLogin:!1},directoryPermissions:{},devLogin:!1,ssoDirect:{}};#i;serverMode;serverOutDir;outdir;buildRevision=0;hasSitemap=!1;compilationErrors=[];#n;userCodeReady;#s=Promise.resolve();#c;#l=Promise.resolve();#d;#u=Promise.resolve();#h;#e=!1;#r=0;#f=0;#a=new Map;constructor({outdir:e,contentDir:t,serverMode:s=!1,serverOutDir:r}){this.#i=t,this.outdir=e,this.serverMode=s,this.serverOutDir=r,F.register({serverOutDir:r,readRoots:[e,process.cwd()]}),this.userCodeReady=new Promise(a=>{this.#n=a})}on(e,t){const s=this.listeners.get(e);s?s.add(t):this.listeners.set(e,new Set([t]))}queueEvent=(e,t,...s)=>{this.#a.set(e+String(t),[e,t,...s])};runListeners=(e,t,...s)=>{for(const r of this.listeners.get(e)||new Set)t?r(t,...s):r(...s)};startPluginsRun(){this.clear(),this.#s=new Promise(e=>{this.#c=e})}waitForPluginsLifecycle(){return Promise.all([this.#s,this.#l,this.#u])}waitForPluginsRun(){return this.#s}finishPluginsRun(){this.#c?.();for(const e of this.#a.values())this.runListeners(...e);this.#a.clear()}startEsbuildRun(){this.#l=new Promise(e=>{this.#d=e})}finishEsbuildRun(){this.#d?.()}markRequestHandlersEntryOutdated(){this.#r++,!this.#e&&(this.#e=!0,this.#u=new Promise(e=>{this.#h=e}))}noteRequestHandlersEntryBuildStart(){this.#f=this.#r}markRequestHandlersEntryUpdated(){this.#e&&(this.#r>this.#f||(this.#e=!1,this.#h?.()))}get contentDir(){if(this.serverMode)throw new Error("contentDir should not be used in server mode");return this.#i}markUserCodeReady(){this.#n?.(!0)}async reloadMarkdocOptions(){await Y("com.redocly.build.reloadMarkdocOptions",async()=>{const e=w.instance(),t=await U(this.serverOutDir),s=Object.fromEntries(Object.entries(t.tags).filter(([r])=>R[r]!=null?e.canAccessFeature(R[r]):!0));this.#o={...t,tags:s}})}get markdocOptions(){return{...this.#o,partials:this.getGlobalConfig(m),themeConfig:this.config.markdown}}setGlobalData=e=>{const t=this.globalData,s={...this.globalData,...e};this.globalData=s,JSON.stringify(s)!==JSON.stringify(t)&&this.queueEvent("global-data-updated",void 0,s)};getGlobalData=()=>this.globalData;getKv=async()=>B.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:e,context:t,deps:s,resource:r})=>{const{data:{info:a,ast:o},compoundHash:l}=await j(e,this.markdocOptions,{actions:this,context:t},r);for(const i of a.sharedDataDeps||[]){for(const n of s?.routeSlugs||[])this.addRouteSharedData(n,i,i);for(const n of s?.sharedDataIds||[]){const c=this.sharedDataDeps.get(n)||new Set;c.add(i),this.sharedDataDeps.set(n,c)}}for(const i of a.dynamicMarkdocComponents||[]){for(const n of s?.routeSlugs||[]){const c=this.routesDynamicComponents.get(n)||new Set;c.add(i),this.routesDynamicComponents.set(n,c)}for(const n of s?.sharedDataIds||[]){const c=this.sharedDataMarkdocComponents.get(n)||new Set;c.add(i),this.sharedDataMarkdocComponents.set(n,c)}}if(s?.routeSlugs&&a.partials?.length)for(const i of s.routeSlugs){const n=this.routesPartials.get(i)||[];for(const c of a.partials)n.includes(c)||n.push(c);this.routesPartials.set(i,n)}return{info:a,ast:o,compoundHash:l}};async loadApiDocuments(e){return(await e.cache.load(".","api-docs")).data}setSearchEngine(e){this.searchEngine=e}setSearchFacets=e=>{this.searchFacets=e};setGlobalConfig=e=>{const t=Object.keys(e);for(const o of t)for(const l in this.replacedEnvVars)if(l===o||l.startsWith(`${o}:`)){const i=l.split(":"),{error:n,value:c}=y(e,i);(n||c!==this.replacedEnvVars[l].replaced)&&delete this.replacedEnvVars[l]}const{resolvedObj:s,unsetEnvVars:r,replacedValues:a}=J(e);for(const o of r)this.unsetEnvVars.add(o);Object.assign(this.replacedEnvVars,a),Object.assign(this.config,s)};getConfig=()=>this.config;getGlobalConfig=e=>this.config[e];getSearchFacets=()=>this.searchFacets;addRedirect=(e,t,s={})=>{if(!w.instance().canAccessFeature("redirects")&&e!=="/")return;this.config.redirects||(this.config.redirects={});const o=h(e).toLowerCase();this.config.redirects[o]=t;const{trackOriginalSource:l=!0}=s,i=this.getGlobalConfig("originalRedirectSources");l&&Array.isArray(i)&&!i.includes(o)&&i.push(o),o.endsWith("*")&&z(this.config.wildcardRedirectsTree,o)};getRedirect=e=>{const t=h(e).toLowerCase(),s=W(t,this.config.redirects,this.config.wildcardRedirectsTree);if(!s)return null;if(D(s.to)){const r=h(s.to).toLowerCase();if(!r.endsWith("*")&&$(r,[t],this.config.redirects,this.config.wildcardRedirectsTree).type==="cycle")return null}return{to:s.to,type:s.type}};createSharedData=async(e,t,s)=>{if(s&&this.#t[e]===s)return e;const r=JSON.stringify(t),a=s??H(r);return this.#t[e]===a||(this.#t[e]=a,await I(e,r,this.outdir),this.queueEvent("shared-data-updated",e)),e};addRouteSharedData=(e,t,s)=>{const r=L(e),a=this.routesSharedData.get(r)||{};a[t]=s,this.routesSharedData.set(r,a),u.verbose(`Adding shared data to ${e}, ${t}, ${s}`)};getRouteSharedDataByFsPath=e=>{const t=this.routesByFsPath.get(e);return t?this.routesSharedData.get(t)||{}:{}};getPartialsForRoute=e=>{const t=this.getGlobalConfig(m)||{},s=this.routesPartials.get(e);if(!s||s.length===0)return{};const r=this.getGlobalConfig(M)||{},a=new Set(s),o=Array.from(s);for(let i=0;i<o.length;i++){const n=o[i],c=r[n]?.partials??[];for(const d of c)a.has(d)||(a.add(d),o.push(d))}const l={};for(const i of a)t[i]&&(l[i]=t[i]);return l};addRoute=e=>{const s={...K(e.fsPath,this.config.metadataGlobs),...e.metadata||{}};this.newRoutes.push({...e,metadata:s}),u.verbose("Created route %s",e.slug)};addRouteSharedDataToAllLocales=(e,t,s)=>{const r=[g,...this.lifecycleContext?.fs.localeFolders||[]].map(a=>({code:a,name:a}));for(const a of r){const o=A(e,g,a.code,r);this.addRouteSharedData(o,t,s)}};addApiRoute=e=>{this.apiRoutes.push(e),u.verbose("Created API route %s",e.slug)};addMcpTools=(e,t)=>{for(const s of t){const r=this.mcpToolHandlers.get(s.name);r&&r.importPath!==e&&u.warn('MCP tool "%s" from %s overwrites the tool registered from %s',s.name,e,r.importPath),this.mcpToolHandlers.set(s.name,{...s,importPath:e}),u.verbose("Created MCP tool %s",s.name)}};getMcpTools=()=>Array.from(this.mcpToolHandlers.values());markGatedMcpTools=e=>{for(const t of e){const s=this.mcpToolHandlers.get(t);s&&this.mcpToolHandlers.set(t,{...s,hasAvailabilityGate:!0})}};addSkills=e=>{for(const t of e)this.skills.set(t.slug,t),u.verbose("Discovered skill %s",t.slug)};getSkills=()=>Array.from(this.skills.values());removeMcpTools=e=>{for(const t of e)this.mcpToolHandlers.delete(t)};addMiddleware=e=>{this.middleware.push(e),u.verbose("Created middleware %s",e.id)};setResourceResponseHeaders=(e,t)=>{this.config.responseHeaders||(this.config.responseHeaders={});const s=new Set(t.map(o=>o.name.toLowerCase())),a=[...(this.config.responseHeaders[e]??[]).filter(o=>!s.has(o.name.toLowerCase())),...t];this.config.responseHeaders[e]=a};getRouteByFsPath=e=>{const t=this.routesByFsPath.get(e);return t?this.getRouteBySlug(t):void 0};getRouteBySlug=(e,t={})=>{const{followRedirect:s=!0}=t,r=this.getRedirect(e);return s&&r?this.routesBySlug.get(h(r.to)):this.routesBySlug.get(e)};hasRouteOrRedirectBySlug=e=>{if(this.routesBySlug.has(e))return!0;const t=this.getRedirect(e);if(!t)return!1;if(!D(t.to))return!0;const s=h(t.to);return this.routesBySlug.has(s)};getRoutesByTemplateId=e=>this.newRoutes.filter(t=>t.templateId===e);getAllRoutesForLocale=(e=g)=>{const t=Array.from(this.routesBySlug.values()),s=e.toLowerCase();return t.filter(r=>e===g?!x(r.fsPath):r.slug.startsWith(`/${s}`))};getAllRoutes=()=>Array.from(this.routesBySlug.values());getAllApiRoutes=()=>this.apiRoutes;getAllMiddleware=()=>this.middleware;getTemplate=e=>this.templates.get(e);getRequestHandler=e=>this.apiRoutesRequestHandlers.get(e);createTemplate=(e,t)=>(this.templates.set(e,t),e);addBrowserPlugin=e=>{this.browserPlugins.add(e)};createRequestHandler=(e,t)=>(this.apiRoutesRequestHandlers.set(e,t),e);clearRequestHandlersByPrefix=e=>{for(const t of this.apiRoutesRequestHandlers.keys())t.startsWith(e)&&this.apiRoutesRequestHandlers.delete(t)};registerServerPropsGetter=(e,t)=>(this.serverPropsGetters.set(e,t),e);registerPagePropsGetter=(e,t)=>{this.pagePropsGetters.set(e,t)};async writeRouteStaticData(e,t){const s=await this.resolveRouteStaticData(e,t,!1);s&&q(e.slug,s,this.outdir)}async resolveRouteStaticData(e,t,s){if(this.serverMode)return V(e.slug,this.outdir);const r={...this,contentDir:this.contentDir,parseMarkdoc:({input:c,context:d,resource:f})=>this.parseMarkdoc({input:c,context:d,deps:{routeSlugs:[e.slug]},resource:f})},a=await e.getStaticData?.(e,r)||{},o=new Set(this.routesDynamicComponents.get(e.slug)),l=this.routesSharedData.get(e.slug)||{};for(const c of Object.values(l)){const d=this.sharedDataMarkdocComponents.get(c);d&&d.forEach(p=>o.add(p));const f=this.sharedDataDeps.get(c);f&&f.forEach(p=>this.addRouteSharedData(e.slug,p,p))}const i=this.getGlobalConfig("seo"),n=a?.frontmatter||{};return{...a,frontmatter:{...n,seo:{...n?.seo,title:n?.seo?.title||await e.getNavText?.()}},props:{...a.props,dynamicMarkdocComponents:Array.from(o),metadata:{...a?.props?.metadata,...e.metadata},seo:{title:T,...i,...a.props?.seo},compilationErrors:this.compilationErrors},lastModified:s||!e.fsPath?null:await this.lifecycleContext?.fs.getLastModified(e.fsPath)}}addSsrComponents(e,t){if(!e?.length)return;const s=typeof e[0]=="string"?e.join(""):N(e);s&&(t==="head"?this.ssr.headTags.push(s):t==="preBody"?this.ssr.preBodyTags.push(s):this.ssr.postBodyTags.push(s))}clear=()=>{this.routesByFsPath.clear(),this.templates.clear(),this.newRoutes=[],this.routesBySlug.clear(),this.apiRoutes=[],this.middleware=[],this.routesSharedData.clear(),this.sharedDataDeps.clear(),this.sharedDataMarkdocComponents.clear(),this.routesDynamicComponents.clear(),this.routesPartials.clear(),this.skills.clear(),this.config.redirects={},this.config.wildcardRedirectsTree={},this.config.directoryPermissions={},this.ssr={preBodyTags:[],postBodyTags:[],headTags:[]}};async toJson(){const e=[];for(const[s,r]of Object.entries(v))switch(r){case"map":const a=Array.from(this[s].entries());e.push([s,a]);break;case"object":if(s==="config"){e.push([s,await this.getConfigWithEnvPlaceholders()]);break}e.push([s,this[s]]);break;default:throw new Error("Invalid format")}const t=Object.fromEntries(e);return t[E]=G.PLAN_GATES,t}static fromJson(e,t){const s=new P(t);for(const[a,o]of Object.entries(v))switch(o){case"map":s[a]=new Map(e[a]);break;case"object":if(a==="config"){s.setGlobalConfig(e[a]);break}s[a]=e[a];break;default:throw new Error("Invalid format")}s.config[m]=Q(s.config[m]||{});const r=e[E];return r&&_("PLAN_GATES",r),s}async getConfigWithEnvPlaceholders(){const e=JSON.parse(JSON.stringify(this.config));for(const t in this.replacedEnvVars){const{original:s}=this.replacedEnvVars[t],r=t.split(":"),a=r.pop(),{error:o,value:l}=y(e,r);if(o||!O(l)&&!Array.isArray(l)){await S.panicOnBuild(`Failed to replace env var with env name for ${t}`);continue}l[a]=s}return e}async reportUnsetEnvVars(){if(this.unsetEnvVars.size===0)return;const e=Array.from(this.unsetEnvVars).filter(s=>!Z.includes(s));if(e.length===0)return;const t=`Failed to resolve config. The following environment variables are not set: ${e.join(", ")}`;await S.panicOnBuildContentError(t)}}function Q(b){return k(b,e=>C.Ast.fromJSON(JSON.stringify(e)))}export{P as Store,Oe as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
1
+ import A from"@markdoc/markdoc";import{getPathnameForLocale as T}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as g}from"../constants/common.js";import{DEFAULT_TITLE as M}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 k}from"../utils/guards/is-object.js";import{mapObject as L}from"../utils/object/map-object.js";import{getValueDeep as y}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as D}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as h}from"../utils/path/normalize-route-slug.js";import{isLocalLink as S}from"../utils/path/is-local-link.js";import{reporter as w}from"./tools/notifiers/reporter.js";import{logger as 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 U}from"./plugins/markdown/compiler.js";import{getMarkdocOptions as j}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as v}from"./entitlements/entitlements-provider.js";import{isL10nPath as x}from"./fs/utils/is-l10n-path.js";import{resolveGlobMapValue as K}from"./utils/globs.js";import{replaceEnvVariablesDeep as J}from"./utils/envs/replace-env-variables-deep.js";import{findRedirect as W}from"./utils/redirects/find-redirect.js";import{followRedirectChain as $}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as z}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as Y}from"../cli/telemetry/helpers/trace-step.js";const E={routesBySlug:"map",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",config:"object",ssr:"object",searchFacets:"map",routesPartials:"map",mcpToolHandlers:"map",skills:"map"},P="PLAN_GATES",Z=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],Oe="userDefinedApiFunctions";class b{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:s,serverMode:t=!1,serverOutDir:r}){this.#i=s,this.outdir=e,this.serverMode=t,this.serverOutDir=r,F.register({serverOutDir:r,readRoots:[e,process.cwd()]}),this.userCodeReady=new Promise(a=>{this.#n=a})}on(e,s){const t=this.listeners.get(e);t?t.add(s):this.listeners.set(e,new Set([s]))}queueEvent=(e,s,...t)=>{this.#a.set(e+String(s),[e,s,...t])};runListeners=(e,s,...t)=>{for(const r of this.listeners.get(e)||new Set)s?r(s,...t):r(...t)};startPluginsRun(){this.clear(),this.#s=new Promise(e=>{this.#c=e})}waitForPluginsLifecycle(){return Promise.all([this.#s,this.#l,this.#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=v.instance(),s=await j(this.serverOutDir),t=Object.fromEntries(Object.entries(s.tags).filter(([r])=>R[r]!=null?e.canAccessFeature(R[r]):!0));this.#o={...s,tags:t}})}get markdocOptions(){return{...this.#o,partials:this.getGlobalConfig(m),themeConfig:this.config.markdown}}setGlobalData=e=>{const s=this.globalData,t={...this.globalData,...e};this.globalData=t,JSON.stringify(t)!==JSON.stringify(s)&&this.queueEvent("global-data-updated",void 0,t)};getGlobalData=()=>this.globalData;getKv=async()=>B.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:e,context:s,deps:t,resource:r})=>{const{data:{info:a,ast:i},compoundHash:l}=await U(e,this.markdocOptions,{actions:this,context:s},r);for(const o of a.sharedDataDeps||[]){for(const n of t?.routeSlugs||[])this.addRouteSharedData(n,o,o);for(const n of t?.sharedDataIds||[]){const c=this.sharedDataDeps.get(n)||new Set;c.add(o),this.sharedDataDeps.set(n,c)}}for(const o of a.dynamicMarkdocComponents||[]){for(const n of t?.routeSlugs||[]){const c=this.routesDynamicComponents.get(n)||new Set;c.add(o),this.routesDynamicComponents.set(n,c)}for(const n of t?.sharedDataIds||[]){const c=this.sharedDataMarkdocComponents.get(n)||new Set;c.add(o),this.sharedDataMarkdocComponents.set(n,c)}}if(t?.routeSlugs&&a.partials?.length)for(const o of t.routeSlugs){const n=this.routesPartials.get(o)||[];for(const c of a.partials)n.includes(c)||n.push(c);this.routesPartials.set(o,n)}return{info:a,ast:i,compoundHash:l}};async loadApiDocuments(e){return(await e.cache.load(".","api-docs")).data}setSearchEngine(e){this.searchEngine=e}setSearchFacets=e=>{this.searchFacets=e};setGlobalConfig=e=>{const s=Object.keys(e);for(const l of s)for(const o in this.replacedEnvVars)if(o===l||o.startsWith(`${l}:`)){const n=o.split(":"),{error:c,value:d}=y(e,n);(c||d!==this.replacedEnvVars[o].replaced)&&delete this.replacedEnvVars[o]}const{resolvedObj:t,unsetEnvVars:r,replacedValues:a}=J(e);for(const l of r)this.unsetEnvVars.add(l);Object.assign(this.replacedEnvVars,a);const i=t.seo?.siteUrl?.trim();i&&(t.seo={...t.seo,siteUrl:URL.canParse(i)?new URL(i).origin:D(i)}),Object.assign(this.config,t)};getConfig=()=>this.config;getGlobalConfig=e=>this.config[e];getSearchFacets=()=>this.searchFacets;addRedirect=(e,s,t={})=>{if(!v.instance().canAccessFeature("redirects")&&e!=="/")return;this.config.redirects||(this.config.redirects={});const i=h(e).toLowerCase();this.config.redirects[i]=s;const{trackOriginalSource:l=!0}=t,o=this.getGlobalConfig("originalRedirectSources");l&&Array.isArray(o)&&!o.includes(i)&&o.push(i),i.endsWith("*")&&z(this.config.wildcardRedirectsTree,i)};getRedirect=e=>{const s=h(e).toLowerCase(),t=W(s,this.config.redirects,this.config.wildcardRedirectsTree);if(!t)return null;if(S(t.to)){const r=h(t.to).toLowerCase();if(!r.endsWith("*")&&$(r,[s],this.config.redirects,this.config.wildcardRedirectsTree).type==="cycle")return null}return{to:t.to,type:t.type}};createSharedData=async(e,s,t)=>{if(t&&this.#t[e]===t)return e;const r=JSON.stringify(s),a=t??H(r);return this.#t[e]===a||(this.#t[e]=a,await I(e,r,this.outdir),this.queueEvent("shared-data-updated",e)),e};addRouteSharedData=(e,s,t)=>{const r=D(e),a=this.routesSharedData.get(r)||{};a[s]=t,this.routesSharedData.set(r,a),u.verbose(`Adding shared data to ${e}, ${s}, ${t}`)};getRouteSharedDataByFsPath=e=>{const s=this.routesByFsPath.get(e);return s?this.routesSharedData.get(s)||{}:{}};getPartialsForRoute=e=>{const s=this.getGlobalConfig(m)||{},t=this.routesPartials.get(e);if(!t||t.length===0)return{};const r=this.getGlobalConfig(O)||{},a=new Set(t),i=Array.from(t);for(let o=0;o<i.length;o++){const n=i[o],c=r[n]?.partials??[];for(const d of c)a.has(d)||(a.add(d),i.push(d))}const l={};for(const o of a)s[o]&&(l[o]=s[o]);return l};addRoute=e=>{const t={...K(e.fsPath,this.config.metadataGlobs),...e.metadata||{}};this.newRoutes.push({...e,metadata:t}),u.verbose("Created route %s",e.slug)};addRouteSharedDataToAllLocales=(e,s,t)=>{const r=[g,...this.lifecycleContext?.fs.localeFolders||[]].map(a=>({code:a,name:a}));for(const a of r){const i=T(e,g,a.code,r);this.addRouteSharedData(i,s,t)}};addApiRoute=e=>{this.apiRoutes.push(e),u.verbose("Created API route %s",e.slug)};addMcpTools=(e,s)=>{for(const t of s){const r=this.mcpToolHandlers.get(t.name);r&&r.importPath!==e&&u.warn('MCP tool "%s" from %s overwrites the tool registered from %s',t.name,e,r.importPath),this.mcpToolHandlers.set(t.name,{...t,importPath:e}),u.verbose("Created MCP tool %s",t.name)}};getMcpTools=()=>Array.from(this.mcpToolHandlers.values());markGatedMcpTools=e=>{for(const s of e){const t=this.mcpToolHandlers.get(s);t&&this.mcpToolHandlers.set(s,{...t,hasAvailabilityGate:!0})}};addSkills=e=>{for(const s of e)this.skills.set(s.slug,s),u.verbose("Discovered skill %s",s.slug)};getSkills=()=>Array.from(this.skills.values());removeMcpTools=e=>{for(const s of e)this.mcpToolHandlers.delete(s)};addMiddleware=e=>{this.middleware.push(e),u.verbose("Created middleware %s",e.id)};setResourceResponseHeaders=(e,s)=>{this.config.responseHeaders||(this.config.responseHeaders={});const t=new Set(s.map(i=>i.name.toLowerCase())),a=[...(this.config.responseHeaders[e]??[]).filter(i=>!t.has(i.name.toLowerCase())),...s];this.config.responseHeaders[e]=a};getRouteByFsPath=e=>{const s=this.routesByFsPath.get(e);return s?this.getRouteBySlug(s):void 0};getRouteBySlug=(e,s={})=>{const{followRedirect:t=!0}=s,r=this.getRedirect(e);return t&&r?this.routesBySlug.get(h(r.to)):this.routesBySlug.get(e)};hasRouteOrRedirectBySlug=e=>{if(this.routesBySlug.has(e))return!0;const s=this.getRedirect(e);if(!s)return!1;if(!S(s.to))return!0;const t=h(s.to);return this.routesBySlug.has(t)};getRoutesByTemplateId=e=>this.newRoutes.filter(s=>s.templateId===e);getAllRoutesForLocale=(e=g)=>{const s=Array.from(this.routesBySlug.values()),t=e.toLowerCase();return s.filter(r=>e===g?!x(r.fsPath):r.slug.startsWith(`/${t}`))};getAllRoutes=()=>Array.from(this.routesBySlug.values());getAllApiRoutes=()=>this.apiRoutes;getAllMiddleware=()=>this.middleware;getTemplate=e=>this.templates.get(e);getRequestHandler=e=>this.apiRoutesRequestHandlers.get(e);createTemplate=(e,s)=>(this.templates.set(e,s),e);addBrowserPlugin=e=>{this.browserPlugins.add(e)};createRequestHandler=(e,s)=>(this.apiRoutesRequestHandlers.set(e,s),e);clearRequestHandlersByPrefix=e=>{for(const s of this.apiRoutesRequestHandlers.keys())s.startsWith(e)&&this.apiRoutesRequestHandlers.delete(s)};registerServerPropsGetter=(e,s)=>(this.serverPropsGetters.set(e,s),e);registerPagePropsGetter=(e,s)=>{this.pagePropsGetters.set(e,s)};async writeRouteStaticData(e,s){const t=await this.resolveRouteStaticData(e,s,!1);t&&q(e.slug,t,this.outdir)}async resolveRouteStaticData(e,s,t){if(this.serverMode)return V(e.slug,this.outdir);const r={...this,contentDir:this.contentDir,parseMarkdoc:({input:c,context:d,resource:f})=>this.parseMarkdoc({input:c,context:d,deps:{routeSlugs:[e.slug]},resource:f})},a=await e.getStaticData?.(e,r)||{},i=new Set(this.routesDynamicComponents.get(e.slug)),l=this.routesSharedData.get(e.slug)||{};for(const c of Object.values(l)){const d=this.sharedDataMarkdocComponents.get(c);d&&d.forEach(p=>i.add(p));const f=this.sharedDataDeps.get(c);f&&f.forEach(p=>this.addRouteSharedData(e.slug,p,p))}const o=this.getGlobalConfig("seo"),n=a?.frontmatter||{};return{...a,frontmatter:{...n,seo:{...n?.seo,title:n?.seo?.title||await e.getNavText?.()}},props:{...a.props,dynamicMarkdocComponents:Array.from(i),metadata:{...a?.props?.metadata,...e.metadata},seo:{title:M,...o,...a.props?.seo,siteUrl:o?.siteUrl},compilationErrors:this.compilationErrors},lastModified:t||!e.fsPath?null:await this.lifecycleContext?.fs.getLastModified(e.fsPath)}}addSsrComponents(e,s){if(!e?.length)return;const t=typeof e[0]=="string"?e.join(""):N(e);t&&(s==="head"?this.ssr.headTags.push(t):s==="preBody"?this.ssr.preBodyTags.push(t):this.ssr.postBodyTags.push(t))}clear=()=>{this.routesByFsPath.clear(),this.templates.clear(),this.newRoutes=[],this.routesBySlug.clear(),this.apiRoutes=[],this.middleware=[],this.routesSharedData.clear(),this.sharedDataDeps.clear(),this.sharedDataMarkdocComponents.clear(),this.routesDynamicComponents.clear(),this.routesPartials.clear(),this.skills.clear(),this.config.redirects={},this.config.wildcardRedirectsTree={},this.config.directoryPermissions={},this.ssr={preBodyTags:[],postBodyTags:[],headTags:[]}};async toJson(){const e=[];for(const[t,r]of Object.entries(E))switch(r){case"map":const a=Array.from(this[t].entries());e.push([t,a]);break;case"object":if(t==="config"){e.push([t,await this.getConfigWithEnvPlaceholders()]);break}e.push([t,this[t]]);break;default:throw new Error("Invalid format")}const s=Object.fromEntries(e);return s[P]=G.PLAN_GATES,s}static fromJson(e,s){const t=new b(s);for(const[a,i]of Object.entries(E))switch(i){case"map":t[a]=new Map(e[a]);break;case"object":if(a==="config"){t.setGlobalConfig(e[a]);break}t[a]=e[a];break;default:throw new Error("Invalid format")}t.config[m]=Q(t.config[m]||{});const r=e[P];return r&&_("PLAN_GATES",r),t}async getConfigWithEnvPlaceholders(){const e=JSON.parse(JSON.stringify(this.config));for(const s in this.replacedEnvVars){const{original:t}=this.replacedEnvVars[s],r=s.split(":"),a=r.pop(),{error:i,value:l}=y(e,r);if(i||!k(l)&&!Array.isArray(l)){await w.panicOnBuild(`Failed to replace env var with env name for ${s}`);continue}l[a]=t}return e}async reportUnsetEnvVars(){if(this.unsetEnvVars.size===0)return;const e=Array.from(this.unsetEnvVars).filter(t=>!Z.includes(t));if(e.length===0)return;const s=`Failed to resolve config. The following environment variables are not set: ${e.join(", ")}`;await w.panicOnBuildContentError(s)}}function Q(C){return L(C,e=>A.Ast.fromJSON(JSON.stringify(e)))}export{b as Store,Oe as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
@@ -30,7 +30,7 @@ export type SearchParameters = {
30
30
  export type SearchIndex = {
31
31
  add(document: SearchDocument): void;
32
32
  search(params: SearchParameters): Promise<SearchResponse>;
33
- export(handler: ExportHandler<string | SearchDocument>): void;
33
+ export(handler: ExportHandler<string | IndexedSearchDocument>): void;
34
34
  import(searchData: FlexSearchData): void;
35
35
  clearDocuments(): void;
36
36
  };
@@ -40,7 +40,7 @@ export type AddDocumentsParams = {
40
40
  outDir: string;
41
41
  };
42
42
  export type FlexSearchData = {
43
- documents: [number, SearchDocument][];
43
+ documents: [number, IndexedSearchDocument][];
44
44
  index: Record<string, any>;
45
45
  };
46
46
  export type TypesenseSearchData = {
@@ -86,6 +86,11 @@ export type SearchDocument = {
86
86
  rbacTeams?: string[];
87
87
  [SEARCH_DOCUMENT_METADATA_KEY]?: SearchDocumentMetadata;
88
88
  };
89
+ /** Parameter fields kept in the FlexSearch index and its export; the rest never leaves the build. */
90
+ export type IndexedParameter = Pick<OperationParameter, 'name' | 'description' | 'place' | 'path' | 'deepLink'>;
91
+ export type IndexedSearchDocument = Omit<SearchDocument, 'parameters'> & {
92
+ parameters?: IndexedParameter[];
93
+ };
89
94
  export type TypesenseSearchDocument = Omit<SearchDocument, typeof SEARCH_DOCUMENT_METADATA_KEY> & {
90
95
  metadata_curated?: boolean;
91
96
  metadata_keywords_includes?: string[];
@@ -1,5 +1,9 @@
1
1
  import picomatch from 'picomatch';
2
2
  export declare const getDeeperGlobPattern: (a: ReturnType<typeof picomatch.scan>, b: ReturnType<typeof picomatch.scan>) => import("picomatch/lib/scan").State;
3
+ /**
4
+ * Parses every pattern once, so the returned function is cheap to call multiple times.
5
+ */
6
+ export declare function compileGlobMatcher(patterns: string[]): (relativePath: string) => boolean;
3
7
  export declare function pathMatchesGlobPattern(relativePath: string, pattern: string): boolean;
4
8
  export declare function getMostSpecificMatchingGlobKey(relativePath: string, globMap?: Record<string, unknown>): string | undefined;
5
9
  export declare function resolveGlobMapValue<T>(relativePath: string, globMap?: Record<string, T>): T | undefined;
@@ -1 +1 @@
1
- import s from"picomatch";import{minimatch as l}from"minimatch";import u from"is-glob";import c from"path";import{removeTrailingSlash as f}from"../../utils/url/remove-trailing-slash.js";import{removeLeadingSlash as m}from"../../utils/url/remove-leading-slash.js";const p=(t,e)=>{const n=t.tokens??[],o=e.tokens??[];if(t.isGlob!==e.isGlob)return t.isGlob?e:t;if(n.length!==o.length)return n.length>o.length?t:e;for(let r=0;r<n.length;r++){if(n[r].isGlob!==o[r].isGlob)return n[r].isGlob?e:t;if(n[r].value!==o[r].value)return n[r].value.length>o[r].value.length?t:e}return t};function h(t,e){if(u(e)){if(l(t,e))return!0}else{const n=f(m(c.posix.normalize(e))),o=t.startsWith(n+"/");if(t===n||o)return!0}return!1}function a(t,e){if(!e)return;const n=[];for(const i of Object.keys(e))h(t,i)&&n.push(i);if(!n.length)return;const o=n.map(i=>s.scan(i,{tokens:!0,parts:!0}));let r=o[0];for(let i=1;i<o.length;i++)r=p(r,o[i]);return r.input}function v(t,e){const n=a(t,e);return n!=null&&e?e[n]:void 0}function y(t){const e=s.makeRe(t);return e||void 0}export{y as convertGlobToRegex,p as getDeeperGlobPattern,a as getMostSpecificMatchingGlobKey,h as pathMatchesGlobPattern,v as resolveGlobMapValue};
1
+ import s from"picomatch";import{minimatch as m,Minimatch as p}from"minimatch";import c from"is-glob";import l from"path";import{removeTrailingSlash as u}from"../../utils/url/remove-trailing-slash.js";import{removeLeadingSlash as f}from"../../utils/url/remove-leading-slash.js";const h=(n,t)=>{const e=n.tokens??[],o=t.tokens??[];if(n.isGlob!==t.isGlob)return n.isGlob?t:n;if(e.length!==o.length)return e.length>o.length?n:t;for(let r=0;r<e.length;r++){if(e[r].isGlob!==o[r].isGlob)return e[r].isGlob?t:n;if(e[r].value!==o[r].value)return e[r].value.length>o[r].value.length?n:t}return n};function v(n){const t=n.map(e=>{if(c(e)){const r=new p(e);return i=>r.match(i)}const o=u(f(l.posix.normalize(e)));return r=>r===o||r.startsWith(o+"/")});return e=>t.some(o=>o(e))}function a(n,t){if(c(t)){if(m(n,t))return!0}else{const e=u(f(l.posix.normalize(t))),o=n.startsWith(e+"/");if(n===e||o)return!0}return!1}function d(n,t){if(!t)return;const e=[];for(const i of Object.keys(t))a(n,i)&&e.push(i);if(!e.length)return;const o=e.map(i=>s.scan(i,{tokens:!0,parts:!0}));let r=o[0];for(let i=1;i<o.length;i++)r=h(r,o[i]);return r.input}function P(n,t){const e=d(n,t);return e!=null&&t?t[e]:void 0}function T(n){const t=s.makeRe(n);return t||void 0}export{v as compileGlobMatcher,T as convertGlobToRegex,h as getDeeperGlobPattern,d as getMostSpecificMatchingGlobKey,a as pathMatchesGlobPattern,P as resolveGlobMapValue};
@@ -1 +1 @@
1
- import{HIGHLIGHTED_TEXT_MAX_LENGTH as a}from"../../constants/plugins/search.js";function L(n,g){const p=n.split(/\s+/g),d=t=>t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),e=p.reduce((t,h)=>{if(!h||!t?.replace)return t;const u=new RegExp(`(^|\\s)${d(h)}`,"gi");return t.replace(u,(l,H,x,k)=>{const m=k.substring(0,x),T=(m.match(/<mark>/g)??[]).length,$=(m.match(/<\/mark>/g)??[]).length;return T>$?l:`<mark>${l}</mark>`})},g);if(!e)return g||"";if(e.replace(/<mark>/g,"").replace(/<\/mark>/g,"").length<a)return e;const s=e.indexOf("<mark>"),i=e.indexOf("</mark>")+7,r=a/2+n.length/2,o=s<r,c=i+r<e.length,f=e.substring(o?0:s-r,c?i+r:e.length);return`${o?"":"..."}${f}${c?"...":""}`}export{L as highlightTextForSearch};
1
+ import{HIGHLIGHTED_TEXT_MAX_LENGTH as l}from"../../constants/plugins/search.js";const h=i=>i.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");function m(i,n){if(!n)return"";const o=[...new Set(i.split(/\s+/g).filter(Boolean))].sort((t,e)=>e.length-t.length);if(!o.length)return n;const a=new RegExp(`(^|\\s)(${o.map(h).join("|")})`,"gi");let s=n,g="",c="";if(n.length>l){const t=a.exec(n);a.lastIndex=0;const e=Math.max(0,(t?.index??0)-l/2),r=Math.min(n.length,e+l);s=n.substring(e,r),g=e>0?"...":"",c=r<n.length?"...":""}return g+s.replace(a,(t,e,r)=>`${e}<mark>${r}</mark>`)+c}export{m as highlightTextForSearch};
@@ -1 +1 @@
1
- import o from"path";import{stat as s,readFile as n}from"fs/promises";import{writeFileSync as c}from"fs";import{removeTrailingSlash as m}from"../../utils/url/remove-trailing-slash.js";import{ensureDir as f}from"./fs.js";import{sanitizeWindowsFilename as p}from"./sanitize-windows-filename.js";const y=(t,r,a)=>{const i=e(t,a);c(f(i),JSON.stringify(r),"utf-8")},g=async(t,r)=>{const a=e(t,r);return await u(a)?n(a,"utf-8").then(i=>JSON.parse(i)):{}},e=(t,r)=>{const a=m(t.slice(1)),i=process.platform==="win32"?p(a):a;return o.join(r,"static-data",`${i}.json`)};async function u(t){try{return await s(t),!0}catch{return!1}}export{g as readStaticData,y as writeStaticData};
1
+ import o from"path";import{stat as n,readFile as s}from"fs/promises";import{writeFileSync as c}from"fs";import{removeTrailingSlash as m}from"../../utils/url/remove-trailing-slash.js";import{STATIC_DATA_FOLDER as f}from"../constants/common.js";import{ensureDir as p}from"./fs.js";import{sanitizeWindowsFilename as u}from"./sanitize-windows-filename.js";const F=(t,a,r)=>{const i=e(t,r);c(p(i),JSON.stringify(a),"utf-8")},T=async(t,a)=>{const r=e(t,a);return await l(r)?s(r,"utf-8").then(i=>JSON.parse(i)):{}},e=(t,a)=>{const r=m(t.slice(1)),i=process.platform==="win32"?u(r):r;return o.join(a,f,`${i}.json`)};async function l(t){try{return await n(t),!0}catch{return!1}}export{T as readStaticData,F as writeStaticData};
@@ -2,6 +2,7 @@ import '../node-crypto-polyfill.js';
2
2
  import { AuthProviderType } from '@redocly/config';
3
3
  import type { AuthProviderConfig, OidcIssuerMetadata, OidcProviderConfig, RbacConfig, Saml2ProviderConfig, SsoConfig } from '@redocly/config';
4
4
  import type { UserIDToken } from '../types';
5
+ import { InternalTokenTypes } from './jwt/token-types.js';
5
6
  export type AuthProviderLoginParams = OidcLoginParams | Saml2LoginParams;
6
7
  export type OidcJwk = {
7
8
  n: string;
@@ -58,6 +59,13 @@ export declare function buildOidcLoginUrl(origin: string, { authorizationEndpoin
58
59
  }>;
59
60
  };
60
61
  export declare function buildOidcLogoutUrl(endSessionEndpoint: string, postLogoutUrl: string, idTokenHint: string | undefined, state?: string): string;
62
+ /**
63
+ * True when the provider sets no `audience`, or the token's `aud` or alternative
64
+ * audience claim names one of its values.
65
+ */
66
+ export declare function idTokenAudienceIsAllowed(tokenPayload: Record<string, unknown>, idpConfig: {
67
+ audience?: string[];
68
+ } | undefined): boolean;
61
69
  export declare function isIdpIssuedToken(token: string | undefined): boolean;
62
70
  type McpAuthorizationCodePayload = {
63
71
  client_id: string;
@@ -85,7 +93,7 @@ export declare function createMcpAuthorizationCode(params: {
85
93
  }): Promise<string>;
86
94
  export declare function verifyMcpAuthorizationCode(code: string): Promise<McpAuthorizationCodePayload>;
87
95
  export type McpRefreshTokenPayload = {
88
- type: 'mcp_refresh_token';
96
+ type: typeof InternalTokenTypes.MCP_REFRESH_TOKEN;
89
97
  client_id: string;
90
98
  idp_id?: string;
91
99
  claims: Record<string, unknown>;
@@ -1,12 +1,12 @@
1
- import"../node-crypto-polyfill.js";import{DOMParser as b}from"@xmldom/xmldom";import{SignedXml as Y}from"xml-crypto";import G from"xpath";import{deflateSync as X,inflateSync as Q}from"fflate";import{createHash as Z}from"crypto";import{ulid as ee}from"ulid";import{AuthProviderType as p,DEFAULT_TEAM_CLAIM_NAME as D}from"@redocly/config";import{AUTH_URL as te,JWT_SECRET_KEY as w,MAX_AUTH_TOKEN_COOKIE_BYTES as ne}from"../constants/common.js";import{envConfig as re}from"../config/env-config.js";import{decryptClaimValue as oe}from"../utils/crypto/decrypt-claim-value.js";import{getPathPrefix as se,withPathPrefix as ae}from"@redocly/theme/core/utils";import{ServerRoutes as N}from"../../constants/api.js";import{AuthCookieNames as x,DEFAULT_AUTHENTICATED_TEAM as ie,REQUIRED_OIDC_SCOPES as P}from"../../constants/common.js";import{appendQueryParams as ce}from"../../utils/url/append-query-params.js";import{parsePreviewUrl as v}from"../../utils/url/parse-preview-url.js";import{logger as ue}from"../tools/notifiers/logger.js";import{randomString as le}from"../utils/crypto/random-string.js";import{randomUUID as U}from"../utils/crypto/random-uuid.js";import{AlgorithmTypes as f,JwtTokenExpired as de}from"./jwt/types.js";import*as u from"./jwt/jwt.js";import{parseTeamClaimToArray as me}from"../utils/index.js";import{parseTeamNameTemplate as pe}from"../utils/rbac-expand.js";import{arrayBufferToBase64 as fe,decodeBase64 as j,encodeBase64URL as he,urlSafeBase64 as B}from"./jwt/encode.js";import{formatSamlCertificate as ye}from"./utils/format-saml-certificate.js";function V(e){return e?.type===p.OIDC}function we(e){return e?.type===p.SAML2}async function ct(e,t){if(V(t))return Se(e,t);if(we(t))return Ae(e,t)}async function Se(e,t){const n=await W(e,t),r=new Set((t.scopes||[]).concat(P)),o=t.authorizationRequestCustomParams||{};return{type:p.OIDC,idpId:e,name:"OAuth provider",authorizationEndpoint:n.authorization_endpoint,clientId:t.clientId,responseType:"code",scope:Array.from(r).join(" "),extraParams:o,pkce:t.pkce}}function Ae(e,t){return{type:p.SAML2,idpId:e,name:"SAML2 provider",ssoUrl:t.ssoUrl,issuerId:t.issuerId,entityId:t.entityId||t.issuerId}}function $(e){return Array.from(new Set((e.scopes||[]).concat(P))).join(" ")}async function H(e,t,n){return await fetch(e,{method:"POST",body:new URLSearchParams({client_id:t.clientId,...t.clientSecret?{client_secret:t.clientSecret}:{},...n}).toString(),headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"}}).then(r=>r.json())}async function ut(e,t,n,r,o={}){return await H(e,r,{scope:$(r),code:t,redirect_uri:F(n),grant_type:"authorization_code",...o})}async function lt(e,t,n){return await H(e,n,{scope:$(n),grant_type:"refresh_token",refresh_token:t})}function _e(e,{authorizationEndpoint:t,clientId:n,responseType:r,scope:o,extraParams:s,idpId:a,pkce:l},d,S,m){if(!t||!n||!r||!o)return{loginUrl:void 0};const i=new URL(t),y=m?.redirectUriOverride??`${e}${ae(N.OIDC_CALLBACK)}`,A={state:U(),idpId:a,redirectUri:y,redirectTo:d,branch:m?.branchOverride??ge(e),inviteCode:S,source:m?.sourceOverride??"portal",uiLocales:m?.uiLocales},_={};if(l){const h=B(le(50)),T=B(Z("sha256").update(h).digest("base64")),k="S256";i.searchParams.append("code_challenge",T),i.searchParams.append("code_challenge_method",k),_.code_verifier={value:h,options:{secure:!0,httpOnly:!0,expires:new Date(Date.now()+1e3*60*10),path:se()||"/"}}}i.searchParams.append("client_id",n),i.searchParams.append("scope",o),i.searchParams.append("response_type",r),i.searchParams.append("redirect_uri",F(y)),i.searchParams.append("state",he(JSON.stringify(A))),m?.uiLocales&&i.searchParams.append("ui_locales",m.uiLocales);for(const h in s)s[h]!==void 0&&i.searchParams.append(h,s[h]);return{loginUrl:i.toString(),cookies:_}}function dt(e,t,n,r){const o=new URL(e);return n&&(o.searchParams.append("post_logout_redirect_uri",t),r&&o.searchParams.append("state",r),o.searchParams.append("id_token_hint",n)),o.toString()}function mt(e){if(!e)return!1;try{return u.decode(e).header.alg===f.RS256}catch{return!1}}async function pt(e){const t=Math.floor(Date.now()/1e3),n=t+(e.ttlSec??600);return u.sign({type:"mcp_auth_code",client_id:e.clientId,redirect_uri:e.redirectUri,id_token:e.idToken,...e.idpId?{idp_id:e.idpId}:{},...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},...e.codeChallenge?{code_challenge:e.codeChallenge}:{},...e.codeChallengeMethod?{code_challenge_method:e.codeChallengeMethod}:{},iat:t,exp:n},w,f.HS256)}async function ft(e){await u.verify(e,w,f.HS256);const{payload:t}=u.decode(e);if(t.type!=="mcp_auth_code")throw new Error("Invalid authorization code type");if(!t.client_id||!t.redirect_uri)throw new Error("Authorization code missing required claims");if(typeof t.exp=="number"&&Date.now()>=t.exp*1e3)throw new Error("Authorization code expired");return t}async function ht(e){const t=Math.floor(Date.now()/1e3);return u.sign({type:"mcp_refresh_token",client_id:e.clientId,...e.idpId?{idp_id:e.idpId}:{},claims:e.claims,...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},session_exp:e.sessionExp,iat:t,exp:e.sessionExp},w,f.HS256)}async function yt(e){await u.verify(e,w,f.HS256);const{payload:t}=u.decode(e);if(t.type!=="mcp_refresh_token")throw new Error("Invalid refresh token type");if(!t.client_id||typeof t.claims!="object"||t.claims===null)throw new Error("Refresh token missing required claims");if(typeof t.session_exp!="number")throw new Error("Refresh token missing session expiry");return t}function wt(e){const t=e||ee(),n=t.startsWith("mcp_")?t:`mcp_${t}`;return{id:n,object:"mcpSession",uri:`urn:redocly:realm:mcp:session:${n}`}}function F(e){const t=v(e);if(!t)return e;const n=xe(t.hostname);if(n)return t.hostname=`previewauth--${n.previewBranch}${n.after}`,z(e,t);const r=K(t.hostname);return r?(t.hostname=`${r.projectSlug}.previewauth.${r.after}`,z(e,t)):e}function ge(e){const t=v(e);if(t)return K(t.hostname)?.previewBranch}function z(e,t){return e.replace(/^https?:\/\/[^/?#]+/i,`${t.protocol}//${t.host}`)}function K(e){const t=e.split(".preview.",2);if(t.length<2)return null;const[n,r]=t,o=n.indexOf("--");if(o===-1)return null;const s=n.slice(0,o),a=n.slice(o+2);return!s||!a||a.includes(".")?null:{projectSlug:s,previewBranch:a,after:r}}function xe(e){const t=e.indexOf("."),n=t===-1?e:e.slice(0,t);if(!n.startsWith("preview-"))return null;const r=n.indexOf("--");if(r===-1)return null;const o=n.slice(r+2);if(!o)return null;const s=t===-1?"":e.slice(t);return{previewBranch:o,after:s}}function Te(e){return e.type===p.OIDC}function ke(e){return e.type===p.SAML2}function St(e,t,n,r,o){return Te(e)?_e(t,e,n,r,{uiLocales:o}):ke(e)?Ie(t,e,n,r,o):{}}function Ie(e,t,n,r,o){const a=`<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
1
+ import"../node-crypto-polyfill.js";import{DOMParser as v}from"@xmldom/xmldom";import{SignedXml as Z}from"xml-crypto";import Q from"xpath";import{deflateSync as ee,inflateSync as te}from"fflate";import{createHash as ne}from"crypto";import{ulid as re}from"ulid";import{AuthProviderType as p,DEFAULT_TEAM_CLAIM_NAME as U}from"@redocly/config";import{ALTERNATIVE_AUD_CLAIM_NAME as oe,AUTH_URL as se,JWT_SECRET_KEY as w,MAX_AUTH_TOKEN_COOKIE_BYTES as ae}from"../constants/common.js";import{ensureArray as k}from"../../utils/array/ensure-array.js";import{envConfig as ie}from"../config/env-config.js";import{decryptClaimValue as ce}from"../utils/crypto/decrypt-claim-value.js";import{getPathPrefix as ue,withPathPrefix as le}from"@redocly/theme/core/utils";import{ServerRoutes as j}from"../../constants/api.js";import{AuthCookieNames as g,DEFAULT_AUTHENTICATED_TEAM as de,REQUIRED_OIDC_SCOPES as V}from"../../constants/common.js";import{appendQueryParams as me}from"../../utils/url/append-query-params.js";import{parsePreviewUrl as B}from"../../utils/url/parse-preview-url.js";import{logger as R}from"../tools/notifiers/logger.js";import{randomString as pe}from"../utils/crypto/random-string.js";import{randomUUID as H}from"../utils/crypto/random-uuid.js";import{AlgorithmTypes as u,JwtTokenExpired as fe}from"./jwt/types.js";import*as l from"./jwt/jwt.js";import{InternalTokenTypes as T}from"./jwt/token-types.js";import{parseTeamClaimToArray as he}from"../utils/index.js";import{parseTeamNameTemplate as ye}from"../utils/rbac-expand.js";import{arrayBufferToBase64 as Ae,decodeBase64 as $,encodeBase64URL as we,urlSafeBase64 as F}from"./jwt/encode.js";import{formatSamlCertificate as Se}from"./utils/format-saml-certificate.js";const _e=new Set(Object.values(T));function O(e){return e?.type===p.OIDC}function Te(e){return e?.type===p.SAML2}async function At(e,t){if(O(t))return xe(e,t);if(Te(t))return ge(e,t)}async function xe(e,t){const n=await Y(e,t),r=new Set((t.scopes||[]).concat(V)),o=t.authorizationRequestCustomParams||{};return{type:p.OIDC,idpId:e,name:"OAuth provider",authorizationEndpoint:n.authorization_endpoint,clientId:t.clientId,responseType:"code",scope:Array.from(r).join(" "),extraParams:o,pkce:t.pkce}}function ge(e,t){return{type:p.SAML2,idpId:e,name:"SAML2 provider",ssoUrl:t.ssoUrl,issuerId:t.issuerId,entityId:t.entityId||t.issuerId}}function K(e){return Array.from(new Set((e.scopes||[]).concat(V))).join(" ")}async function z(e,t,n){return await fetch(e,{method:"POST",body:new URLSearchParams({client_id:t.clientId,...t.clientSecret?{client_secret:t.clientSecret}:{},...n}).toString(),headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"}}).then(r=>r.json())}async function wt(e,t,n,r,o={}){return await z(e,r,{scope:K(r),code:t,redirect_uri:W(n),grant_type:"authorization_code",...o})}async function St(e,t,n){return await z(e,n,{scope:K(n),grant_type:"refresh_token",refresh_token:t})}function Ie(e,{authorizationEndpoint:t,clientId:n,responseType:r,scope:o,extraParams:s,idpId:a,pkce:f},d,h,m){if(!t||!n||!r||!o)return{loginUrl:void 0};const i=new URL(t),A=m?.redirectUriOverride??`${e}${le(j.OIDC_CALLBACK)}`,S={state:H(),idpId:a,redirectUri:A,redirectTo:d,branch:m?.branchOverride??Re(e),inviteCode:h,source:m?.sourceOverride??"portal",uiLocales:m?.uiLocales},_={};if(f){const y=F(pe(50)),I=F(ne("sha256").update(y).digest("base64")),E="S256";i.searchParams.append("code_challenge",I),i.searchParams.append("code_challenge_method",E),_.code_verifier={value:y,options:{secure:!0,httpOnly:!0,expires:new Date(Date.now()+1e3*60*10),path:ue()||"/"}}}i.searchParams.append("client_id",n),i.searchParams.append("scope",o),i.searchParams.append("response_type",r),i.searchParams.append("redirect_uri",W(A)),i.searchParams.append("state",we(JSON.stringify(S))),m?.uiLocales&&i.searchParams.append("ui_locales",m.uiLocales);for(const y in s)s[y]!==void 0&&i.searchParams.append(y,s[y]);return{loginUrl:i.toString(),cookies:_}}function _t(e,t,n,r){const o=new URL(e);return n&&(o.searchParams.append("post_logout_redirect_uri",t),r&&o.searchParams.append("state",r),o.searchParams.append("id_token_hint",n)),o.toString()}function Ee(e,t){const n=t?.audience;return n?.length?[...k(e.aud||[]),...k(e[oe]||[])].some(o=>n.includes(o)):!0}function ke(e,t){return!O(t)||!je(t)||!t.clientId?!0:k(e.aud||[]).includes(t.clientId)}function Tt(e){if(!e)return!1;try{return l.decode(e).header.alg===u.RS256}catch{return!1}}async function xt(e){const t=Math.floor(Date.now()/1e3),n=t+(e.ttlSec??600);return l.sign({type:T.MCP_AUTHORIZATION_CODE,client_id:e.clientId,redirect_uri:e.redirectUri,id_token:e.idToken,...e.idpId?{idp_id:e.idpId}:{},...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},...e.codeChallenge?{code_challenge:e.codeChallenge}:{},...e.codeChallengeMethod?{code_challenge_method:e.codeChallengeMethod}:{},iat:t,exp:n},w,u.HS256)}async function gt(e){await l.verify(e,w,u.HS256);const{payload:t}=l.decode(e);if(t.type!==T.MCP_AUTHORIZATION_CODE)throw new Error("Invalid authorization code type");if(!t.client_id||!t.redirect_uri)throw new Error("Authorization code missing required claims");if(typeof t.exp=="number"&&Date.now()>=t.exp*1e3)throw new Error("Authorization code expired");return t}async function It(e){const t=Math.floor(Date.now()/1e3);return l.sign({type:T.MCP_REFRESH_TOKEN,client_id:e.clientId,...e.idpId?{idp_id:e.idpId}:{},claims:e.claims,...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},session_exp:e.sessionExp,iat:t,exp:e.sessionExp},w,u.HS256)}async function Et(e){await l.verify(e,w,u.HS256);const{payload:t}=l.decode(e);if(t.type!==T.MCP_REFRESH_TOKEN)throw new Error("Invalid refresh token type");if(!t.client_id||typeof t.claims!="object"||t.claims===null)throw new Error("Refresh token missing required claims");if(typeof t.session_exp!="number")throw new Error("Refresh token missing session expiry");return t}function kt(e){const t=e||re(),n=t.startsWith("mcp_")?t:`mcp_${t}`;return{id:n,object:"mcpSession",uri:`urn:redocly:realm:mcp:session:${n}`}}function W(e){const t=B(e);if(!t)return e;const n=Oe(t.hostname);if(n)return t.hostname=`previewauth--${n.previewBranch}${n.after}`,J(e,t);const r=q(t.hostname);return r?(t.hostname=`${r.projectSlug}.previewauth.${r.after}`,J(e,t)):e}function Re(e){const t=B(e);if(t)return q(t.hostname)?.previewBranch}function J(e,t){return e.replace(/^https?:\/\/[^/?#]+/i,`${t.protocol}//${t.host}`)}function q(e){const t=e.split(".preview.",2);if(t.length<2)return null;const[n,r]=t,o=n.indexOf("--");if(o===-1)return null;const s=n.slice(0,o),a=n.slice(o+2);return!s||!a||a.includes(".")?null:{projectSlug:s,previewBranch:a,after:r}}function Oe(e){const t=e.indexOf("."),n=t===-1?e:e.slice(0,t);if(!n.startsWith("preview-"))return null;const r=n.indexOf("--");if(r===-1)return null;const o=n.slice(r+2);if(!o)return null;const s=t===-1?"":e.slice(t);return{previewBranch:o,after:s}}function Me(e){return e.type===p.OIDC}function Ce(e){return e.type===p.SAML2}function Rt(e,t,n,r,o){return Me(e)?Ie(t,e,n,r,{uiLocales:o}):Ce(e)?Le(t,e,n,r,o):{}}function Le(e,t,n,r,o){const a=`<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
2
2
  xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
3
3
  Version="2.0"
4
- ID="_${U()}"
4
+ ID="_${H()}"
5
5
  IssueInstant="${new Date().toISOString()}"
6
- AssertionConsumerServiceURL="${e}${N.SAML_CALLBACK}"
6
+ AssertionConsumerServiceURL="${e}${j.SAML_CALLBACK}"
7
7
  AttributeConsumingServiceIndex="0">
8
8
  <saml:Issuer>${t.entityId}</saml:Issuer>
9
9
  <samlp:NameIDPolicy
10
10
  AllowCreate="true"
11
11
  Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"/>
12
- </samlp:AuthnRequest>`,l=Ee(a);return{loginUrl:ce(t.ssoUrl,{SAMLRequest:l,RelayState:JSON.stringify({idpId:t.idpId,redirectTo:n,inviteCode:r,source:"portal",uiLocales:o})})}}function Ee(e){return fe(X(new TextEncoder().encode(e)).buffer)}function At(e){const t=j(e);if(t.startsWith("<samlp:Response")||t.indexOf("<saml2p:Response")>-1)return t;const n=Q(new Uint8Array(atob(e).split("").map(r=>r.charCodeAt(0))));return new TextDecoder().decode(n)}function _t(e){try{return JSON.parse(j(e||""))}catch{throw new Error("Invalid OAuth2 state")}}function gt(e){const t=new b().parseFromString(e,"application/xml"),r=c(t,"//*[local-name(.)='StatusCode']/@Value")[0]?.nodeValue?.endsWith("Success")||!1,s=c(t,"//*[local-name(.)='Response']/@Destination")[0]?.nodeValue||"",a=c(t,"//*[local-name(.)='Assertion']//*[local-name(.)='Issuer']/text()")[0],l=a&&a.nodeValue||void 0,d=c(t,"//*[local-name(.)='Audience']/text()")[0],S=d&&d.nodeValue||void 0,i=c(t,"//*[local-name(.)='Assertion']//*[local-name(.)='X509Certificate']/text()")[0]?.nodeValue||"",y=c(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/text()")[0],A=y&&y.nodeValue||"",_=c(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/@Format")[0],h=_&&_.nodeValue||"",T=c(t,"//*[local-name(.)='Conditions']/@NotOnOrAfter")[0],k=Re(T),R={},M=c(t,"//*[local-name(.)='AttributeStatement']//*[local-name(.)='Attribute']");if(M.length)for(const O of M){const L=c(O,"./@Name")[0];if(L.nodeValue){const C=c(O,"./*[local-name(.)='AttributeValue']/text()")[0];C?.nodeValue&&(R[L.nodeValue]=C.nodeValue)}}return{uid:A,success:r,expiresAt:k,issuerId:l,entityId:S,attrs:R,cert:i,nameFormat:h,destination:s}}function Re(e){const t=typeof e?.nodeValue=="string"&&I(Date.parse(e.nodeValue)),n=I(Date.now()),r=I(Date.now()+720*60*1e3);return t?t>n&&t<r?r:t:n}function I(e){return Math.floor(e/1e3)}const E={},g={jwks:{}};async function W(e,t){if(!E[e]){const n=t.configurationUrl?await J(t.configurationUrl):t.configuration;E[e]=Me()?Le(n):n}return E[e]}function Me(){const e=re.REDOCLY_ENFORCE_RESIDENCY;return!!e&&e.includes("host.docker.internal")}const Oe=["authorization_endpoint","end_session_endpoint"];function Le(e){if(typeof e!="object"||e===null)return e;const t={...e};for(const n of Object.keys(t)){if(Oe.includes(String(n)))continue;const r=t[n];typeof r=="string"&&r.includes("://localhost")&&(t[n]=r.replace("://localhost","://host.docker.internal"))}return t}async function Ce(e){for(const t of Object.keys(e)){const n=e[t];if(!V(n))continue;const r=await W(t,n);if(r.jwks_uri){const o=await J(r.jwks_uri);for(const s of o.keys)g.jwks[s.kid]={...s,idpId:t}}}}async function J(e){return fetch(e,{headers:{Accept:"application/json"}}).then(t=>t.json())}async function xt(e){return fetch(`${te}/oidc/userinfo`,{headers:{Accept:"application/json",Authorization:`Bearer ${e}`}}).then(t=>t.status===200?t.json():void 0).catch(()=>{})}function Tt(e){if(!e.configurationUrl)return!1;const t=new URL(e.configurationUrl);return["localhost","127.0.0.1","blueharvest.cloud","bhstage.cloud","cloud.redocly.com","beta.redocly.com","cloud.eu.redocly.com","beta.eu.redocly.com"].some(r=>be(t.hostname,r))}function be(e,t){return e===t||e.endsWith(`.${t}`)}async function kt(e,t){const n=new b().parseFromString(e,"application/xml"),r=c(n,"//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0];if(!r)throw new Error("Cannot find Signature in the SAML response");const o=ye(t),s=new Y({publicCert:o});s.loadSignature(r);try{return s.checkSignature(e)}catch{return!1}}function De(e){const t=new Set,n=r=>{if(!(!r||typeof r!="object"||Array.isArray(r)))for(const[o,s]of Object.entries(r))typeof s=="string"?t.add(o):n(s)};return n(e),t}function Ne(e,t,n){if(!t)return e;const r=De(t),o=s=>{const a=n?.[s]||s;return r.has(a)||!!pe(t,a)};return[...e.filter(o),...e.filter(s=>!o(s))]}async function It(e,t,n,r){const o=n?e[n]:void 0,s=Array.isArray(o)?Ne(o.map(String),r?.rbacConfig,r?.teamsMapping):[];let a=s.length;const l=s.length+1;for(let d=0;d<l;d++){const S={...e,idpId:t};n&&s.length&&(S[n]=s.slice(0,a));const m=await u.sign(S,w,f.HS256),i=Buffer.byteLength(m)-ne;if(i<=0)return{token:m,teamsTotal:s.length,teamsKept:a,droppedTeams:s.slice(a)};if(a===0)return null;const y=Buffer.byteLength(JSON.stringify(s.slice(0,a))),A=Math.max(1,y/a*4/3);a=Math.max(0,a-Math.max(1,Math.ceil(i/A)))}return null}function Et(e,t,n,r){t==="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"&&(e=n["http://schemas.microsoft.com/identity/claims/objectidentifier"]);let o;(t==="urn:oasis:names:tc:SAML:2.0:nameid-format:email"||t==="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")&&(o=e),t==="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"&&e?.match(/.+@.+/)&&(o=e);const s=n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],a=s?.match(/.+@.+/);return o=o||n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]||(a?s:void 0),o=o?.toLowerCase(),{sub:e,given_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"],family_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"],name:n["http://schemas.microsoft.com/identity/claims/displayname"]||s,email:o,email_verified:!0,teams:r?me(n[r]):[]}}function q(e,t={}){return e.map(n=>t[n]||n)}async function Rt(e,t){if(!t)return{};const n=t[x.AUTHORIZATION];if(!n)return{};try{const r=u.decode(n);if(r.header.alg===f.RS256){g.jwks[r.header.kid]===void 0&&await Ce(e);const d=g.jwks[r.header.kid];if(!d)return g.jwks[r.header.kid]=null,{};await u.verify(n,d,f.RS256)}else await u.verify(n,w,f.HS256);const o=r.payload.idpId||g.jwks[r.header.kid]?.idpId,s=e[o]||{},a=Ue(s),l=ve(s);return{...r.payload,email:r.payload.email?.toLowerCase(),idpId:o,teams:Array.from(new Set([...q(r.payload.teams||[],l),..."defaultTeams"in s&&s.defaultTeams||[],...q("teamsClaimName"in s&&r.payload[a||""]||[],l),ie])),name:Pe(r.payload),isAuthenticated:!0,idpAccessToken:oe(r.payload.idp_access_token,w)||t[x.IDP_ACCESS_TOKEN],federatedAccessToken:t[x.FEDERATED_ACCESS_TOKEN],federatedIdToken:t[x.FEDERATED_ID_TOKEN],authCookie:n}}catch(r){r instanceof de||ue.error("Malformed JWT token: %s",r.message)}return{}}function Pe(e){return(e.firstName&&e.lastName?`${e.firstName} ${e.lastName}`:e.name||e.given_name||e.firstName||e.lastName)||e.email}function ve(e){switch(e.type){case p.SAML2:return e.teamsAttributeMap;case p.OIDC:return e.teamsClaimMap;default:return}}function Ue(e){switch(e.type){case p.SAML2:return e.teamsAttributeName??D;case p.OIDC:return e.teamsClaimName;default:return D}}function c(e,t){return G.select(t,e)||[]}export{St as buildLoginUrl,_e as buildOidcLoginUrl,dt as buildOidcLogoutUrl,Ie as buildSAML2LoginUrl,pt as createMcpAuthorizationCode,ht as createMcpRefreshToken,wt as createMcpSessionResource,At as decodeSamlResponse,Ce as discoverJwks,Ee as encodeSAML2,Et as extractUserClaims,ct as getAuthProviderLoginParams,Se as getOidcLoginParams,W as getOidcMetadata,xt as getRedoclyTokenPayload,Ae as getSaml2LoginParams,Rt as getUserParamsFromCookies,Pe as getUsernameFromPayload,mt as isIdpIssuedToken,V as isOidcProviderConfig,Tt as isRedoclySso,we as isSaml2ProviderConfig,ut as oidcExchangeCodeForToken,g as oidcJwksCache,E as oidcMetadataCache,lt as oidcRefreshTokenGrant,Ne as orderTeamsRbacFirst,_t as parseOidcState,ge as parsePreviewBranch,gt as parseSamlResponse,F as rewritePreviewAuthRedirectUri,It as signTokenWithinCookieSizeLimit,ft as verifyMcpAuthorizationCode,yt as verifyMcpRefreshToken,kt as verifySAMLResponse};
12
+ </samlp:AuthnRequest>`,f=Ne(a);return{loginUrl:me(t.ssoUrl,{SAMLRequest:f,RelayState:JSON.stringify({idpId:t.idpId,redirectTo:n,inviteCode:r,source:"portal",uiLocales:o})})}}function Ne(e){return Ae(ee(new TextEncoder().encode(e)).buffer)}function Ot(e){const t=$(e);if(t.startsWith("<samlp:Response")||t.indexOf("<saml2p:Response")>-1)return t;const n=te(new Uint8Array(atob(e).split("").map(r=>r.charCodeAt(0))));return new TextDecoder().decode(n)}function Mt(e){try{return JSON.parse($(e||""))}catch{throw new Error("Invalid OAuth2 state")}}function Ct(e){const t=new v().parseFromString(e,"application/xml"),r=c(t,"//*[local-name(.)='StatusCode']/@Value")[0]?.nodeValue?.endsWith("Success")||!1,s=c(t,"//*[local-name(.)='Response']/@Destination")[0]?.nodeValue||"",a=c(t,"//*[local-name(.)='Assertion']//*[local-name(.)='Issuer']/text()")[0],f=a&&a.nodeValue||void 0,d=c(t,"//*[local-name(.)='Audience']/text()")[0],h=d&&d.nodeValue||void 0,i=c(t,"//*[local-name(.)='Assertion']//*[local-name(.)='X509Certificate']/text()")[0]?.nodeValue||"",A=c(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/text()")[0],S=A&&A.nodeValue||"",_=c(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/@Format")[0],y=_&&_.nodeValue||"",I=c(t,"//*[local-name(.)='Conditions']/@NotOnOrAfter")[0],E=De(I),L={},N=c(t,"//*[local-name(.)='AttributeStatement']//*[local-name(.)='Attribute']");if(N.length)for(const D of N){const b=c(D,"./@Name")[0];if(b.nodeValue){const P=c(D,"./*[local-name(.)='AttributeValue']/text()")[0];P?.nodeValue&&(L[b.nodeValue]=P.nodeValue)}}return{uid:S,success:r,expiresAt:E,issuerId:f,entityId:h,attrs:L,cert:i,nameFormat:y,destination:s}}function De(e){const t=typeof e?.nodeValue=="string"&&M(Date.parse(e.nodeValue)),n=M(Date.now()),r=M(Date.now()+720*60*1e3);return t?t>n&&t<r?r:t:n}function M(e){return Math.floor(e/1e3)}const C={},x={jwks:{}};async function Y(e,t){if(!C[e]){const n=t.configurationUrl?await G(t.configurationUrl):t.configuration;C[e]=be()?ve(n):n}return C[e]}function be(){const e=ie.REDOCLY_ENFORCE_RESIDENCY;return!!e&&e.includes("host.docker.internal")}const Pe=["authorization_endpoint","end_session_endpoint"];function ve(e){if(typeof e!="object"||e===null)return e;const t={...e};for(const n of Object.keys(t)){if(Pe.includes(String(n)))continue;const r=t[n];typeof r=="string"&&r.includes("://localhost")&&(t[n]=r.replace("://localhost","://host.docker.internal"))}return t}async function Ue(e){for(const t of Object.keys(e)){const n=e[t];if(!O(n))continue;const r=await Y(t,n);if(r.jwks_uri){const o=await G(r.jwks_uri);for(const s of o.keys)x.jwks[s.kid]={...s,idpId:t}}}}async function G(e){return fetch(e,{headers:{Accept:"application/json"}}).then(t=>t.json())}async function Lt(e){return fetch(`${se}/oidc/userinfo`,{headers:{Accept:"application/json",Authorization:`Bearer ${e}`}}).then(t=>t.status===200?t.json():void 0).catch(()=>{})}function je(e){if(!e.configurationUrl)return!1;const t=new URL(e.configurationUrl);return["localhost","127.0.0.1","blueharvest.cloud","bhstage.cloud","cloud.redocly.com","beta.redocly.com","cloud.eu.redocly.com","beta.eu.redocly.com"].some(r=>Ve(t.hostname,r))}function Ve(e,t){return e===t||e.endsWith(`.${t}`)}async function Nt(e,t){const n=new v().parseFromString(e,"application/xml"),r=c(n,"//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0];if(!r)throw new Error("Cannot find Signature in the SAML response");const o=Se(t),s=new Z({publicCert:o});s.loadSignature(r);try{return s.checkSignature(e)}catch{return!1}}function Be(e){const t=new Set,n=r=>{if(!(!r||typeof r!="object"||Array.isArray(r)))for(const[o,s]of Object.entries(r))typeof s=="string"?t.add(o):n(s)};return n(e),t}function He(e,t,n){if(!t)return e;const r=Be(t),o=s=>{const a=n?.[s]||s;return r.has(a)||!!ye(t,a)};return[...e.filter(o),...e.filter(s=>!o(s))]}async function Dt(e,t,n,r){const o=n?e[n]:void 0,s=Array.isArray(o)?He(o.map(String),r?.rbacConfig,r?.teamsMapping):[];let a=s.length;const f=s.length+1;for(let d=0;d<f;d++){const h={...e,idpId:t};n&&s.length&&(h[n]=s.slice(0,a));const m=await l.sign(h,w,u.HS256),i=Buffer.byteLength(m)-ae;if(i<=0)return{token:m,teamsTotal:s.length,teamsKept:a,droppedTeams:s.slice(a)};if(a===0)return null;const A=Buffer.byteLength(JSON.stringify(s.slice(0,a))),S=Math.max(1,A/a*4/3);a=Math.max(0,a-Math.max(1,Math.ceil(i/S)))}return null}function bt(e,t,n,r){t==="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"&&(e=n["http://schemas.microsoft.com/identity/claims/objectidentifier"]);let o;(t==="urn:oasis:names:tc:SAML:2.0:nameid-format:email"||t==="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")&&(o=e),t==="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"&&e?.match(/.+@.+/)&&(o=e);const s=n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],a=s?.match(/.+@.+/);return o=o||n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]||(a?s:void 0),o=o?.toLowerCase(),{sub:e,given_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"],family_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"],name:n["http://schemas.microsoft.com/identity/claims/displayname"]||s,email:o,email_verified:!0,teams:r?he(n[r]):[]}}function X(e,t={}){return e.map(n=>t[n]||n)}async function Pt(e,t){if(!t)return{};const n=t[g.AUTHORIZATION];if(!n)return{};try{const r=l.decode(n);if(typeof r.payload.type=="string"&&_e.has(r.payload.type))return{};if(r.header.alg===u.RS256){x.jwks[r.header.kid]===void 0&&await Ue(e);const h=x.jwks[r.header.kid];if(!h)return x.jwks[r.header.kid]=null,{};await l.verify(n,h,u.RS256)}else await l.verify(n,w,u.HS256);const s=r.payload.idpId||x.jwks[r.header.kid]?.idpId,a=e[s]||{};if(r.header.alg===u.RS256&&typeof r.payload.exp!="number")return R.warn("Rejected id_token: it has no exp claim"),{};if(r.header.alg===u.RS256&&!(Ee(r.payload,a)&&ke(r.payload,a)))return R.warn("Rejected id_token: it was issued for another project"),{};const f=Ke(a),d=Fe(a);return{...r.payload,email:r.payload.email?.toLowerCase(),idpId:s,teams:Array.from(new Set([...X(r.payload.teams||[],d),..."defaultTeams"in a&&a.defaultTeams||[],...X("teamsClaimName"in a&&r.payload[f||""]||[],d),de])),name:$e(r.payload),isAuthenticated:!0,idpAccessToken:ce(r.payload.idp_access_token,w)||t[g.IDP_ACCESS_TOKEN],federatedAccessToken:t[g.FEDERATED_ACCESS_TOKEN],federatedIdToken:t[g.FEDERATED_ID_TOKEN],authCookie:n}}catch(r){r instanceof fe||R.error("Malformed JWT token: %s",r.message)}return{}}function $e(e){return(e.firstName&&e.lastName?`${e.firstName} ${e.lastName}`:e.name||e.given_name||e.firstName||e.lastName)||e.email}function Fe(e){switch(e.type){case p.SAML2:return e.teamsAttributeMap;case p.OIDC:return e.teamsClaimMap;default:return}}function Ke(e){switch(e.type){case p.SAML2:return e.teamsAttributeName??U;case p.OIDC:return e.teamsClaimName;default:return U}}function c(e,t){return Q.select(t,e)||[]}export{Rt as buildLoginUrl,Ie as buildOidcLoginUrl,_t as buildOidcLogoutUrl,Le as buildSAML2LoginUrl,xt as createMcpAuthorizationCode,It as createMcpRefreshToken,kt as createMcpSessionResource,Ot as decodeSamlResponse,Ue as discoverJwks,Ne as encodeSAML2,bt as extractUserClaims,At as getAuthProviderLoginParams,xe as getOidcLoginParams,Y as getOidcMetadata,Lt as getRedoclyTokenPayload,ge as getSaml2LoginParams,Pt as getUserParamsFromCookies,$e as getUsernameFromPayload,Ee as idTokenAudienceIsAllowed,Tt as isIdpIssuedToken,O as isOidcProviderConfig,je as isRedoclySso,Te as isSaml2ProviderConfig,wt as oidcExchangeCodeForToken,x as oidcJwksCache,C as oidcMetadataCache,St as oidcRefreshTokenGrant,He as orderTeamsRbacFirst,Mt as parseOidcState,Re as parsePreviewBranch,Ct as parseSamlResponse,W as rewritePreviewAuthRedirectUri,Dt as signTokenWithinCookieSizeLimit,gt as verifyMcpAuthorizationCode,Et as verifyMcpRefreshToken,Nt as verifySAMLResponse};
@@ -1 +1 @@
1
- import"../../node-crypto-polyfill.js";import{utf8ToUint8Array as u,encodeBase64URL as w,arrayBufferToBase64URL as m,decodeBase64URL as d}from"./encode.js";import{decodeJwtSignature as g}from"./decode-jwt-signature.js";import{AlgorithmTypes as s,JwtAlgorithmNotImplemented as H,JwtTokenInvalid as S,JwtTokenNotBefore as b,JwtTokenExpired as E,JwtTokenSignatureMismatched as K,CryptoKeyFormat as h,CryptoKeyUsage as l}from"./types.js";const p=t=>{switch(t.toUpperCase()){case"HS256":return{name:"HMAC",hash:{name:"SHA-256"}};case"HS384":return{name:"HMAC",hash:{name:"SHA-384"}};case"HS512":return{name:"HMAC",hash:{name:"SHA-512"}};case"RS256":return{name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"}};case"ES256":return{name:"ECDSA",namedCurve:"P-256",hash:{name:"SHA-256"}};default:throw new H(t)}},A=async(t,e,n=s.HS256)=>{if(!crypto.subtle||!crypto.subtle.importKey)throw new Error("`crypto.subtle.importKey` is undefined. JWT auth middleware requires it.");e.startsWith("-----BEGIN PRIVATE KEY-----")&&(e=e.replace("-----BEGIN PRIVATE KEY-----","").replace("-----END PRIVATE KEY-----","").split(/\\n|\n/).join("").trim(),e=atob(e));const r=n===s.RS256||n===s.ES256,a=await crypto.subtle.importKey(r?h.PKCS8:h.RAW,r?T(e):u(e),p(n),!1,[l.Sign]);return await crypto.subtle.sign(p(n),a,u(t))};function T(t){const e=new ArrayBuffer(t.length),n=new Uint8Array(e);for(let r=0,a=t.length;r<a;r++)n[r]=t.charCodeAt(r);return e}const U=async(t,e,n=s.HS256,r)=>{const a=await w(JSON.stringify(t)),i=`${await w(JSON.stringify({alg:n,typ:"JWT",kid:r}))}.${a}`,c=await m(await A(i,e,n));return`${i}.${c}`},v=async(t,e,n=s.HS256,r=!1)=>{const a=t.split(".");if(a.length!==3)throw new S(t);const{payload:o}=C(t),i=Math.floor(Date.now()/1e3);if(typeof o.nbf=="number"&&o.nbf>i)throw new b(t);if(!r&&typeof o.exp=="number"&&o.exp<=i)throw new E(t);const c=a.slice(0,2).join("."),f=a[2];let y=!1;if(typeof e!="string"?y=await R(c,f,e,n):y=await m(await A(c,e,n))===f,!y)throw new K(t);return!0},C=t=>{try{const[e,n]=t.split("."),r=JSON.parse(d(e)),a=JSON.parse(d(n));return{header:r,payload:a}}catch{throw new S(t)}},R=async(t,e,n,r)=>{const a=await crypto.subtle.importKey("jwk",n,p(r),!1,[l.Verify]);return await crypto.subtle.verify(p(r),a,g(e),u(t))};export{C as decode,U as sign,v as verify};
1
+ import"../../node-crypto-polyfill.js";import{timingSafeEqual as H}from"node:crypto";import{utf8ToUint8Array as u,encodeBase64URL as m,arrayBufferToBase64URL as w,decodeBase64URL as S}from"./encode.js";import{decodeJwtSignature as b}from"./decode-jwt-signature.js";import{AlgorithmTypes as s,JwtAlgorithmNotImplemented as E,JwtTokenInvalid as d,JwtTokenNotBefore as K,JwtTokenExpired as T,JwtTokenSignatureMismatched as B,CryptoKeyFormat as h,CryptoKeyUsage as l}from"./types.js";const p=t=>{switch(t.toUpperCase()){case"HS256":return{name:"HMAC",hash:{name:"SHA-256"}};case"HS384":return{name:"HMAC",hash:{name:"SHA-384"}};case"HS512":return{name:"HMAC",hash:{name:"SHA-512"}};case"RS256":return{name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"}};case"ES256":return{name:"ECDSA",namedCurve:"P-256",hash:{name:"SHA-256"}};default:throw new E(t)}},g=async(t,e,n=s.HS256)=>{if(!crypto.subtle||!crypto.subtle.importKey)throw new Error("`crypto.subtle.importKey` is undefined. JWT auth middleware requires it.");e.startsWith("-----BEGIN PRIVATE KEY-----")&&(e=e.replace("-----BEGIN PRIVATE KEY-----","").replace("-----END PRIVATE KEY-----","").split(/\\n|\n/).join("").trim(),e=atob(e));const r=n===s.RS256||n===s.ES256,a=await crypto.subtle.importKey(r?h.PKCS8:h.RAW,r?C(e):u(e),p(n),!1,[l.Sign]);return await crypto.subtle.sign(p(n),a,u(t))};function C(t){const e=new ArrayBuffer(t.length),n=new Uint8Array(e);for(let r=0,a=t.length;r<a;r++)n[r]=t.charCodeAt(r);return e}const V=async(t,e,n=s.HS256,r)=>{const a=await m(JSON.stringify(t)),i=`${await m(JSON.stringify({alg:n,typ:"JWT",kid:r}))}.${a}`,c=await w(await g(i,e,n));return`${i}.${c}`},M=async(t,e,n=s.HS256,r=!1)=>{const a=t.split(".");if(a.length!==3)throw new d(t);const{payload:o}=J(t),i=Math.floor(Date.now()/1e3);if(typeof o.nbf=="number"&&o.nbf>i)throw new K(t);if(!r&&typeof o.exp=="number"&&o.exp<=i)throw new T(t);const c=a.slice(0,2).join("."),y=a[2];let f=!1;if(typeof e!="string")f=await N(c,y,e,n);else{const A=await w(await g(c,e,n));f=R(A,y)}if(!f)throw new B(t);return!0};function R(t,e){const n=Buffer.from(t),r=Buffer.from(e);return n.length===r.length&&H(n,r)}const J=t=>{try{const[e,n]=t.split("."),r=JSON.parse(S(e)),a=JSON.parse(S(n));return{header:r,payload:a}}catch{throw new d(t)}},N=async(t,e,n,r)=>{const a=await crypto.subtle.importKey("jwk",n,p(r),!1,[l.Verify]);return await crypto.subtle.verify(p(r),a,b(e),u(t))};export{J as decode,V as sign,M as verify};
@@ -0,0 +1,11 @@
1
+ export declare const InternalTokenTypes: {
2
+ readonly MCP_CONTEXT: 'mcp_context';
3
+ readonly MCP_AUTHORIZATION_CODE: 'mcp_auth_code';
4
+ readonly MCP_REFRESH_TOKEN: 'mcp_refresh_token';
5
+ readonly AI_ASSISTANT_ANONYMOUS: 'ai_assistant_anonymous';
6
+ };
7
+ export declare const McpAccessTokenMarker: {
8
+ readonly CLAIM: 'token_use';
9
+ readonly VALUE: 'mcp_access_token';
10
+ };
11
+ //# sourceMappingURL=token-types.d.ts.map
@@ -0,0 +1 @@
1
+ const e={MCP_CONTEXT:"mcp_context",MCP_AUTHORIZATION_CODE:"mcp_auth_code",MCP_REFRESH_TOKEN:"mcp_refresh_token",AI_ASSISTANT_ANONYMOUS:"ai_assistant_anonymous"},_={CLAIM:"token_use",VALUE:"mcp_access_token"};export{e as InternalTokenTypes,_ as McpAccessTokenMarker};
@@ -1 +1 @@
1
- import{DEFAULT_SEARCH_ENGINE as r}from"@redocly/config";import{logger as e}from"../../tools/notifiers/logger.js";import{SearchEngine as t}from"../../plugins/search/engines/search-engine.js";function E(a){return async(o,i)=>{if(!a.searchEngine&&!a.config?.search?.hide){e.info("Loading search data...");const c=a.config?.search?.engine??r,n=new t(c,a.globalData.l10n);await n.initIndexSchema(a.searchFacets),await n.import(a.outdir),a.setSearchEngine(n),e.info("Search data loaded")}await i()}}export{E as ensureSearchData};
1
+ import{DEFAULT_SEARCH_ENGINE as t}from"@redocly/config";import{logger as c}from"../../tools/notifiers/logger.js";import{SearchEngine as o}from"../../plugins/search/engines/search-engine.js";function f(a){let e;const r=async()=>{c.info("Loading search data...");const i=a.config?.search?.engine??t,n=new o(i,a.globalData.l10n);await n.initIndexSchema(a.searchFacets),await n.import(a.outdir),a.setSearchEngine(n),c.info("Search data loaded")};return async(i,n)=>{!a.searchEngine&&!a.config?.search?.hide&&(e??=r().finally(()=>{e=void 0}),await e),await n()}}export{f as ensureSearchData};
@@ -1 +1 @@
1
- import{setCookie as L,deleteCookie as P}from"hono/cookie";import{AuthProviderType as ee,DEFAULT_TEAM_CLAIM_NAME as re}from"@redocly/config";import{withPathPrefix as w,getPathPrefix as A}from"@redocly/theme/core/utils";import{compareURIs as oe}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 U,MAX_AUTH_TOKEN_COOKIE_BYTES as F,ORG_SLUG as te,ORG_ID as ne}from"../../constants/common.js";import{AUTH_SEGMENT as ie,ServerRoutes as D}from"../../../constants/api.js";import{AuthCookieNames as _,DEFAULT_COOKIE_EXPIRATION as K}from"../../../constants/common.js";import{sanitizeRedirectPathname as z}from"../../../utils/url/sanitize-redirect-pathname.js";import{telemetry as k}from"../../telemetry/index.js";import{envConfig as j}from"../../config/env-config.js";import{fetchWithClient as se}from"../../utils/fetch-with-client.js";import{clearAuthCookies as J,clearPostLogoutCookie as v,hasPostLogoutCookie as ae,setPostLogoutCookie as G}from"../../utils/cookie.js";import{getAuthProviderLoginParams as de,isOidcProviderConfig as b,isSaml2ProviderConfig as le,oidcExchangeCodeForToken as ce,buildLoginUrl as ue,decodeSamlResponse as pe,extractUserClaims as ge,parseSamlResponse as fe,parseOidcState as me,verifySAMLResponse as he,getUsernameFromPayload as Ce,buildOidcLogoutUrl as Ie,isIdpIssuedToken as _e,getOidcMetadata as Z,getRedoclyTokenPayload as Ae,isRedoclySso as Re,rewritePreviewAuthRedirectUri as Oe,parsePreviewBranch as V,buildOidcLoginUrl as Te,createMcpSessionResource as M,signTokenWithinCookieSizeLimit as W}from"../auth.js";import*as E from"../jwt/jwt.js";import{AlgorithmTypes as $}from"../jwt/types.js";import{handleErrorPageRender as ye}from"../utils.js";import{encodeBase64URL as Le}from"../jwt/encode.js";import{resolveUiLocalesForIdpLogin as we}from"./helpers/resolve-ui-locales-for-idp-login.js";async function Je(n){if(j.isProductionEnv)return n.newResponse(null,404,{});const{password:e,...r}=await n.req.json(),d=await E.sign({...r,name:r.username||r.email||"Unknown"},U,$.HS256);return L(n,_.AUTHORIZATION,d,{path:A()||"/",httpOnly:!0,secure:!0,sameSite:"none"}),v(n),n.newResponse(null,200,{})}function Ge(){return async n=>{const e=n.get("logger"),r=encodeURIComponent(n.req.query("message")||"");e.error(`Login error: ${r}`);const d=`${D.LOGIN}/?error=${encodeURIComponent(r)}`;return n.newResponse(null,301,{Location:d})}}function X(n){if(!n||!n.includes(D.MCP_CALLBACK))return null;try{const e=n.split("/"),r=e[e.length-1];if(r){const d=Buffer.from(r,"base64url").toString("utf-8");return JSON.parse(d).mcpSessionId||null}}catch{}return null}function Ze(n){return async e=>{const r=e.get("logger"),d=n.getConfig().ssoDirect;let o;try{o=me(e.req.query("state"))}catch{return r.warn("OIDC login error: Invalid OAuth2 state"),e.text("Bad request: Invalid OAuth2 state",400)}const h=o.idpId,i=o.source==="mcp"||o.redirectTo&&typeof o.redirectTo=="string"&&o.redirectTo.includes(D.MCP_CALLBACK),u=i?X(typeof o.redirectTo=="string"?o.redirectTo:void 0):null,s=d?.[h];if(!b(s))return r.error("OIDC login error: missing OIDC provider config"),e.text("Forbidden",403);const p=await Z(h,s);if(d&&!p.token_endpoint){const m="Invalid OIDC configuration: token_endpoint is required";return r.error(`OIDC login error: ${m}`),e.text(m,500)}try{const m=p.token_endpoint,g=e.req.query("code"),C=e.req.query("error");if(C)return i&&k.sendMcpAuthorizationFailedMessage([{...M(u),error:`OIDC error: ${C}`,error_details:e.req.query("error_description")||null}]),ye(e,n,{slug:"/"},403,"403OIDC");if(!g){const f="Code is expected but not present";return r.error(`OIDC login error: ${f}`),i&&k.sendMcpAuthorizationFailedMessage([{...M(u),error:f,error_details:null}]),new Response(`Forbidden: ${f}`,{status:403})}const O=typeof o.redirectUri=="string"?o.redirectUri:new URL(w(D.OIDC_CALLBACK),e.req.url).toString(),I=e.get("cookies")?.code_verifier,a=await ce(m,g,O,s,{...s.tokenRequestCustomParams,...I?{code_verifier:I}:{}});if(a.error)return r.error(`Error from OIDC provider: "${a.error}"`),i&&k.sendMcpAuthorizationFailedMessage([{...M(u),error:`Token exchange error: ${a.error}`,error_details:a.error_description||null}]),e.text(`Forbidden: ${a.error_description||a.error}`,403);if(!a?.id_token){const f="No id_token, please, add openid to scopes";return r.error(`OIDC login error: ${f}`),i&&k.sendMcpAuthorizationFailedMessage([{...M(u),error:f,error_details:null}]),new Response(`Forbidden: ${f}`,{status:403})}const{payload:l,header:y}=E.decode(a.id_token),t=y.alg===$.RS256;if(s.audience?.length&&![...N(l.aud||[]),...N(l[H]||[])].some(T=>s.audience?.includes(T))){const T="No valid audience found in id_token";return r.error(`OIDC login error: ${T}`),i&&k.sendMcpAuthorizationFailedMessage([{...M(u),error:T,error_details:null}]),new Response(`Forbidden: ${T}`)}let c=t?a.id_token:await E.sign({...l,idpId:h},U,$.HS256);if(Buffer.byteLength(c)>F){const f=await W(l,h,s.teamsClaimName,{rbacConfig:n.getConfig().access?.rbac,teamsMapping:s.teamsClaimMap});if(!f){const T=`The id_token claims are too large for the session cookie: ${Buffer.byteLength(JSON.stringify(l))} bytes of claims`;return r.error(`OIDC login error: ${T}`),i&&k.sendMcpAuthorizationFailedMessage([{...M(u),error:T,error_details:null}]),e.text(`Forbidden: ${T}`,403)}f.teamsKept<f.teamsTotal&&r.warn(`OIDC login: the "${s.teamsClaimName}" claim is too large for the session cookie size limit; kept ${f.teamsKept} of ${f.teamsTotal} values, with teams from the RBAC configuration first; RBAC ignores the dropped teams: ${JSON.stringify(f.droppedTeams)}`),c=f.token}Ce(l)||r.warn("To display your username, the required 'email' or 'full_profile' scope must be added to the identity provider configuration");const S=s?.tokenExpirationTime?Date.now()+s.tokenExpirationTime*1e3:l.exp*1e3||Date.now()+K*1e3;if(s.introspectEndpoint){const f=await fetch(s.introspectEndpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({access_token:a.access_token})});if(f.ok){const q=(await f.json()).ext?.federatedIdentity;q&&(L(e,_.FEDERATED_ACCESS_TOKEN,q.access_token||"",{path:A()||"/",httpOnly:!1,expires:new Date(S)}),L(e,_.FEDERATED_ID_TOKEN,q.id_token||"",{path:A()||"/",httpOnly:!1,expires:new Date(S)}))}else r.warn(`OIDC introspect error: ${f.statusText}`)}if(L(e,_.AUTHORIZATION,c,{path:A()||"/",httpOnly:!0,expires:new Date(S)}),c!==a.id_token&&Buffer.byteLength(a.id_token)<=F?L(e,_.IDP_ID_TOKEN,a.id_token||"",{path:A()||"/",httpOnly:!0,expires:new Date(S)}):P(e,_.IDP_ID_TOKEN,{path:A()||"/"}),L(e,_.IDP_ACCESS_TOKEN,a.access_token||"",{path:A()||"/",httpOnly:!0,expires:new Date(S)}),a.refresh_token?L(e,_.IDP_REFRESH_TOKEN,a.refresh_token,{path:A()||"/",httpOnly:!0,expires:new Date(S)}):P(e,_.IDP_REFRESH_TOKEN,{path:A()||"/"}),P(e,"code_verifier",{path:A()||"/"}),v(e),i&&o.redirectTo&&typeof o.redirectTo=="string"&&o.redirectTo.includes(D.MCP_CALLBACK)){const T=`${e.req.url.split("?")[0].replace(D.OIDC_CALLBACK,"")}${o.redirectTo}`;return e.newResponse(null,302,{Location:T})}const Y=typeof o.redirectTo=="string"?o.redirectTo:void 0;let Q=z(new URL(Y||"/",e.req.url).pathname);const x=e.newResponse(null,302,{Location:Q});return r.updateContext({email:l.email,subject:l.sub}),r.info("OIDC login successful"),x}catch(m){const g=m instanceof Error?m.message:String(m),C=m instanceof Error?m.stack:String(m);if(r.error(`OIDC login error: ${g}`),i&&k.sendMcpAuthorizationFailedMessage([{...M(u),error:g,error_details:C}]),m.error==="access_denied")return r.info("Access denied"),e.text("Forbidden",403)}const R="Something went wrong";return r.error(`OIDC login error: ${R}`),i&&k.sendMcpAuthorizationFailedMessage([{...M(u),error:R,error_details:null}]),e.text(R,500)}}function Ve(n){return async e=>{const r=e.get("logger"),o=e.get("auth").claims?.idpId,i=n.getConfig().ssoDirect?.[o];if(e.req.method==="POST")return b(i)||(J(e),G(e)),r.info("Logout successful"),e.newResponse(null,200,{});let u;if(b(i)){const s=(await Z(o,i)).end_session_endpoint;if(s){const p=new URL(e.req.url),R=e.req.header("x-forwarded-proto")||p.protocol.slice(0,-1)||"https",m=e.req.header("x-forwarded-host")||p.host,g=`${R}://${m}`,C=V(g),O=C?Le(JSON.stringify({branch:V(g)})):void 0,I=C?`${Oe(g)}/${ie}/logout`:`${g}${w(D.POST_LOGOUT)}`,a=e.get("cookies"),l=a?.[_.AUTHORIZATION],y=a?.[_.IDP_ID_TOKEN]||(_e(l)?l:void 0);u=Ie(s,I,y,O)}}return r.info("Logout successful"),J(e),G(e),e.newResponse(null,302,{Location:u||w("/")})}}function We(n){return async e=>{const r=n.getConfig().access?.logoutReturnUrl,d=r||w("/");return e.newResponse(null,302,{Location:d})}}function Xe(n){return async e=>{const r=e.get("logger"),d=e.req.param("code"),o=j.BH_API_URL,h=(i,u,s)=>i&&u?`${i} ${u.charAt(0)}`:s;try{if(!o)throw new Error("BH_API_URL is not set");const i=n.getConfig().ssoDirect;if(!i||!Object.keys(i).length)return r.warn("Invite no sso configured to handle"),e.redirect(w("/"));const u=await se(`${o}/public-user-invites/${d}`);if(!u.ok)return u.status===404?(r.warn(`Invite ${d} not found redirect to homepage`),e.redirect(w("/"))):(r.error("Invite error",await u.text()),e.redirect(w("/")));const s=await u.json(),p=new URL(w("/invite"),e.req.url);return p.searchParams.set("code",d),p.searchParams.set("org",s.organization.name),p.searchParams.set("invitedBy",h(s.invitedBy.firstName,s.invitedBy.lastName,s.invitedBy.name)),e.newResponse(null,302,{Location:p.toString()})}catch(i){return r.error("Error processing invite",{error:i,inviteCode:d}),e.text(i.message||"Failed to process invite",400)}}}function Ye(n){return async e=>{const r=e.get("logger"),d=n.getConfig().ssoDirect,o=new URL(e.req.url),h=e.req.query("inviteCode"),i=e.req.header("x-forwarded-proto")||o.protocol.slice(0,-1)||"https",u=e.req.header("x-forwarded-host")||o.host,s=`${i}://${u}`;let p=o.searchParams.get("idpId");const R=o.searchParams.get("redirectTo"),m=Object.keys(d||{})[0];p=p||m;const g=o.searchParams.get("mcp_redirect_uri"),C=!!g;if(!d?.[p]){const c="Invalid idpId";if(r.error(`IdP login error: ${c}`),C){const B=X(R||void 0);k.sendMcpAuthorizationFailedMessage([{...M(B),error:c,error_details:null}])}return e.text(`Forbidden: ${c}`,403)}const I=we({localePrefixParam:o.searchParams.get("localePrefix"),l10n:n.getGlobalData()?.l10n}),a=p&&d?await de(p,d[p]):void 0,l={};for(const c of Object.keys(a?.extraParams||{}))l[c]=o.searchParams.get(c)||a?.extraParams?.[c]||void 0;ae(e)&&(l.prompt="login",l.post_logout="1");let y,t={};if(C&&g&&a&&a.type===ee.OIDC){r.info(`Building MCP OAuth login URL with redirect_uri: ${g}`);const c=Te("",{...a,extraParams:l},R,h,{redirectUriOverride:g,sourceOverride:"mcp",branchOverride:void 0,uiLocales:I});y=c.loginUrl,t=c.cookies||{}}else if(a){const c=ue({...a,extraParams:l},s,R,h,I);y=c.loginUrl,t=c.cookies||{}}return Object.keys(t).forEach(c=>{L(e,c,t[c].value,t[c].options)}),r.info(`IdP login initiated for ID '${p}'`),e.newResponse(null,302,{Location:y||new URL(e.req.url).pathname})}}function Qe(n){return async e=>{const r=e.get("logger"),d=await e.req.formData(),o=d.get("SAMLResponse"),h=d.get("RelayState");if(typeof o!="string"||typeof h!="string"){const t="SAMLResponse is required";return r.error(`SAML2 login error: ${t}`),e.text(`Bad request: ${t}`,400)}const i=pe(o),{success:u,uid:s,nameFormat:p,attrs:R,issuerId:m,expiresAt:g}=fe(i),{idpId:C,redirectTo:O}=JSON.parse(h);if(!u){const t="SAML2 assertion is not successful";return r.error(`SAML2 login error: ${t}`),e.text(`Permission denied: ${t}`,401)}if(!g||Math.ceil(Date.now()/1e3)>=g){const t="SAML2 Token Expired";return r.error(`SAML2 login error: ${t}`),e.text(t,401)}const I=n.getConfig().ssoDirect?.[C];if(!I||!le(I)){const t="Cannot find valid IdP";return r.error(`SAML2 login error: ${t}`),e.text(`Permission denied: ${t}`,401)}if(!(I.issuerId&&m&&oe(I.issuerId,m))){const t="IssuerID is misconfigured or untrusted assertions issuer received";return r.error(`SAML2 login error: ${t}`),e.text(`Permission denied: ${t}`,401)}if(!await he(i,I.x509PublicCert)){const t="SAMLResponse signature invalid";return r.error(`SAML2 login error: ${t}`),e.text(t,401)}const l=ge(s,p,R,I.teamsAttributeName??re);if(!l.sub){const t="The provider did not return a valid user identity.";return r.error(`SAML2 login error: ${t}`),e.text(t,400)}if(!l.email){const t="The provider did not return a valid user email.";return r.error(`SAML2 login error: ${t}`),e.text(t,400)}let y=await E.sign({...l,idpId:C},U,$.HS256);if(Buffer.byteLength(y)>F){const t=await W({...l},C,"teams",{rbacConfig:n.getConfig().access?.rbac,teamsMapping:I.teamsAttributeMap});if(!t){const c=`The SAML2 claims are too large for the session cookie: ${Buffer.byteLength(JSON.stringify(l))} bytes of claims`;return r.error(`SAML2 login error: ${c}`),e.text(`Forbidden: ${c}`,403)}t.teamsKept<t.teamsTotal&&r.warn(`SAML2 login: the "teams" claim is too large for the session cookie size limit; kept ${t.teamsKept} of ${t.teamsTotal} values, with teams from the RBAC configuration first; RBAC ignores the dropped teams: ${JSON.stringify(t.droppedTeams)}`),y=t.token}return L(e,_.AUTHORIZATION,y,{path:A()||"/",httpOnly:!0,expires:new Date(g*1e3)}),P(e,_.IDP_REFRESH_TOKEN,{path:A()||"/"}),v(e),r.updateContext({email:l.email,subject:l.sub}),r.info("SAML2 login successful"),e.newResponse(null,302,{Location:O||"/"})}}function xe(n){return async e=>{const r=e.get("logger"),d=new URL(e.req.query("redirectTo")||"/",e.req.url),o=w(z(d.pathname)),h=n.getConfig().ssoDirect,i=Object.entries(h||{}).find(([,O])=>b(O)&&Re(O));if(!(h&&i))return e.newResponse(null,302,{Location:o});const s=e.req.query("token"),p=s&&await Ae(s);if(!p)return e.newResponse(null,302,{Location:o});if(!N(p[H]||[]).some(O=>O===te||O===ne))return e.newResponse(null,302,{Location:o});const g=await E.sign({...p,idpId:i?.at(0)},U,$.HS256),C=Date.now()+K*1e3;return L(e,_.AUTHORIZATION,g,{path:A()||"/",httpOnly:!0,expires:new Date(C),sameSite:"None",secure:!0}),P(e,_.IDP_REFRESH_TOKEN,{path:A()||"/"}),v(e),r.info("Token login successful"),e.newResponse(null,302,{Location:o})}}export{Je as authorizeHandler,Ye as idpLoginHandler,Xe as inviteHandler,Ve as logoutHandler,Ze as oidcCallbackHandler,We as postLogoutHandler,Ge as redoclyLoginCallbackHandler,xe as redoclyTokenLoginHandler,Qe as samlCallbackHandler};
1
+ import{setCookie as w,deleteCookie as P}from"hono/cookie";import{AuthProviderType as Q,DEFAULT_TEAM_CLAIM_NAME as x}from"@redocly/config";import{withPathPrefix as y,getPathPrefix as A}from"@redocly/theme/core/utils";import{compareURIs as ee}from"../../../utils/url/compare-uris.js";import{ensureArray as re}from"../../../utils/array/ensure-array.js";import{ALTERNATIVE_AUD_CLAIM_NAME as oe,JWT_SECRET_KEY as U,MAX_AUTH_TOKEN_COOKIE_BYTES as N,ORG_SLUG as te,ORG_ID as ne}from"../../constants/common.js";import{AUTH_SEGMENT as ie,ServerRoutes as k}from"../../../constants/api.js";import{AuthCookieNames as _,DEFAULT_COOKIE_EXPIRATION as B}from"../../../constants/common.js";import{sanitizeRedirectPathname as H}from"../../../utils/url/sanitize-redirect-pathname.js";import{telemetry as L}from"../../telemetry/index.js";import{envConfig as K}from"../../config/env-config.js";import{fetchWithClient as se}from"../../utils/fetch-with-client.js";import{clearAuthCookies as z,clearPostLogoutCookie as v,hasPostLogoutCookie as ae,setPostLogoutCookie as j}from"../../utils/cookie.js";import{getAuthProviderLoginParams as de,isOidcProviderConfig as b,isSaml2ProviderConfig as le,oidcExchangeCodeForToken as ce,buildLoginUrl as ue,decodeSamlResponse as pe,extractUserClaims as ge,parseSamlResponse as fe,parseOidcState as me,verifySAMLResponse as he,getUsernameFromPayload as Ce,buildOidcLogoutUrl as Ie,isIdpIssuedToken as _e,getOidcMetadata as J,getRedoclyTokenPayload as Ae,idTokenAudienceIsAllowed as Re,isRedoclySso as Oe,rewritePreviewAuthRedirectUri as Te,parsePreviewBranch as G,buildOidcLoginUrl as we,createMcpSessionResource as M,signTokenWithinCookieSizeLimit as Z}from"../auth.js";import*as E from"../jwt/jwt.js";import{AlgorithmTypes as $}from"../jwt/types.js";import{handleErrorPageRender as ye}from"../utils.js";import{encodeBase64URL as Le}from"../jwt/encode.js";import{resolveUiLocalesForIdpLogin as Me}from"./helpers/resolve-ui-locales-for-idp-login.js";async function Ge(n){if(K.isProductionEnv)return n.newResponse(null,404,{});const{password:e,...r}=await n.req.json(),d=await E.sign({...r,name:r.username||r.email||"Unknown"},U,$.HS256);return w(n,_.AUTHORIZATION,d,{path:A()||"/",httpOnly:!0,secure:!0,sameSite:"none"}),v(n),n.newResponse(null,200,{})}function Ze(){return async n=>{const e=n.get("logger"),r=encodeURIComponent(n.req.query("message")||"");e.error(`Login error: ${r}`);const d=`${k.LOGIN}/?error=${encodeURIComponent(r)}`;return n.newResponse(null,301,{Location:d})}}function V(n){if(!n||!n.includes(k.MCP_CALLBACK))return null;try{const e=n.split("/"),r=e[e.length-1];if(r){const d=Buffer.from(r,"base64url").toString("utf-8");return JSON.parse(d).mcpSessionId||null}}catch{}return null}function Ve(n){return async e=>{const r=e.get("logger"),d=n.getConfig().ssoDirect;let o;try{o=me(e.req.query("state"))}catch{return r.warn("OIDC login error: Invalid OAuth2 state"),e.text("Bad request: Invalid OAuth2 state",400)}const h=o.idpId,i=o.source==="mcp"||o.redirectTo&&typeof o.redirectTo=="string"&&o.redirectTo.includes(k.MCP_CALLBACK),u=i?V(typeof o.redirectTo=="string"?o.redirectTo:void 0):null,s=d?.[h];if(!b(s))return r.error("OIDC login error: missing OIDC provider config"),e.text("Forbidden",403);const p=await J(h,s);if(d&&!p.token_endpoint){const m="Invalid OIDC configuration: token_endpoint is required";return r.error(`OIDC login error: ${m}`),e.text(m,500)}try{const m=p.token_endpoint,f=e.req.query("code"),C=e.req.query("error");if(C)return i&&L.sendMcpAuthorizationFailedMessage([{...M(u),error:`OIDC error: ${C}`,error_details:e.req.query("error_description")||null}]),ye(e,n,{slug:"/"},403,"403OIDC");if(!f){const g="Code is expected but not present";return r.error(`OIDC login error: ${g}`),i&&L.sendMcpAuthorizationFailedMessage([{...M(u),error:g,error_details:null}]),new Response(`Forbidden: ${g}`,{status:403})}const O=typeof o.redirectUri=="string"?o.redirectUri:new URL(y(k.OIDC_CALLBACK),e.req.url).toString(),I=e.get("cookies")?.code_verifier,a=await ce(m,f,O,s,{...s.tokenRequestCustomParams,...I?{code_verifier:I}:{}});if(a.error)return r.error(`Error from OIDC provider: "${a.error}"`),i&&L.sendMcpAuthorizationFailedMessage([{...M(u),error:`Token exchange error: ${a.error}`,error_details:a.error_description||null}]),e.text(`Forbidden: ${a.error_description||a.error}`,403);if(!a?.id_token){const g="No id_token, please, add openid to scopes";return r.error(`OIDC login error: ${g}`),i&&L.sendMcpAuthorizationFailedMessage([{...M(u),error:g,error_details:null}]),new Response(`Forbidden: ${g}`,{status:403})}const{payload:l,header:T}=E.decode(a.id_token),t=T.alg===$.RS256;if(s.audience?.length&&!Re(l,s)){const g="No valid audience found in id_token";return r.error(`OIDC login error: ${g}`),i&&L.sendMcpAuthorizationFailedMessage([{...M(u),error:g,error_details:null}]),new Response(`Forbidden: ${g}`)}let c=t?a.id_token:await E.sign({...l,idpId:h},U,$.HS256);if(Buffer.byteLength(c)>N){const g=await Z(l,h,s.teamsClaimName,{rbacConfig:n.getConfig().access?.rbac,teamsMapping:s.teamsClaimMap});if(!g){const S=`The id_token claims are too large for the session cookie: ${Buffer.byteLength(JSON.stringify(l))} bytes of claims`;return r.error(`OIDC login error: ${S}`),i&&L.sendMcpAuthorizationFailedMessage([{...M(u),error:S,error_details:null}]),e.text(`Forbidden: ${S}`,403)}g.teamsKept<g.teamsTotal&&r.warn(`OIDC login: the "${s.teamsClaimName}" claim is too large for the session cookie size limit; kept ${g.teamsKept} of ${g.teamsTotal} values, with teams from the RBAC configuration first; RBAC ignores the dropped teams: ${JSON.stringify(g.droppedTeams)}`),c=g.token}Ce(l)||r.warn("To display your username, the required 'email' or 'full_profile' scope must be added to the identity provider configuration");const D=s?.tokenExpirationTime?Date.now()+s.tokenExpirationTime*1e3:l.exp*1e3||Date.now()+B*1e3;if(s.introspectEndpoint){const g=await fetch(s.introspectEndpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({access_token:a.access_token})});if(g.ok){const q=(await g.json()).ext?.federatedIdentity;q&&(w(e,_.FEDERATED_ACCESS_TOKEN,q.access_token||"",{path:A()||"/",httpOnly:!1,expires:new Date(D)}),w(e,_.FEDERATED_ID_TOKEN,q.id_token||"",{path:A()||"/",httpOnly:!1,expires:new Date(D)}))}else r.warn(`OIDC introspect error: ${g.statusText}`)}if(w(e,_.AUTHORIZATION,c,{path:A()||"/",httpOnly:!0,expires:new Date(D)}),c!==a.id_token&&Buffer.byteLength(a.id_token)<=N?w(e,_.IDP_ID_TOKEN,a.id_token||"",{path:A()||"/",httpOnly:!0,expires:new Date(D)}):P(e,_.IDP_ID_TOKEN,{path:A()||"/"}),w(e,_.IDP_ACCESS_TOKEN,a.access_token||"",{path:A()||"/",httpOnly:!0,expires:new Date(D)}),a.refresh_token?w(e,_.IDP_REFRESH_TOKEN,a.refresh_token,{path:A()||"/",httpOnly:!0,expires:new Date(D)}):P(e,_.IDP_REFRESH_TOKEN,{path:A()||"/"}),P(e,"code_verifier",{path:A()||"/"}),v(e),i&&o.redirectTo&&typeof o.redirectTo=="string"&&o.redirectTo.includes(k.MCP_CALLBACK)){const S=`${e.req.url.split("?")[0].replace(k.OIDC_CALLBACK,"")}${o.redirectTo}`;return e.newResponse(null,302,{Location:S})}const W=typeof o.redirectTo=="string"?o.redirectTo:void 0;let X=H(new URL(W||"/",e.req.url).pathname);const Y=e.newResponse(null,302,{Location:X});return r.updateContext({email:l.email,subject:l.sub}),r.info("OIDC login successful"),Y}catch(m){const f=m instanceof Error?m.message:String(m),C=m instanceof Error?m.stack:String(m);if(r.error(`OIDC login error: ${f}`),i&&L.sendMcpAuthorizationFailedMessage([{...M(u),error:f,error_details:C}]),m.error==="access_denied")return r.info("Access denied"),e.text("Forbidden",403)}const R="Something went wrong";return r.error(`OIDC login error: ${R}`),i&&L.sendMcpAuthorizationFailedMessage([{...M(u),error:R,error_details:null}]),e.text(R,500)}}function We(n){return async e=>{const r=e.get("logger"),o=e.get("auth").claims?.idpId,i=n.getConfig().ssoDirect?.[o];if(e.req.method==="POST")return b(i)||(z(e),j(e)),r.info("Logout successful"),e.newResponse(null,200,{});let u;if(b(i)){const s=(await J(o,i)).end_session_endpoint;if(s){const p=new URL(e.req.url),R=e.req.header("x-forwarded-proto")||p.protocol.slice(0,-1)||"https",m=e.req.header("x-forwarded-host")||p.host,f=`${R}://${m}`,C=G(f),O=C?Le(JSON.stringify({branch:G(f)})):void 0,I=C?`${Te(f)}/${ie}/logout`:`${f}${y(k.POST_LOGOUT)}`,a=e.get("cookies"),l=a?.[_.AUTHORIZATION],T=a?.[_.IDP_ID_TOKEN]||(_e(l)?l:void 0);u=Ie(s,I,T,O)}}return r.info("Logout successful"),z(e),j(e),e.newResponse(null,302,{Location:u||y("/")})}}function Xe(n){return async e=>{const r=n.getConfig().access?.logoutReturnUrl,d=r||y("/");return e.newResponse(null,302,{Location:d})}}function Ye(n){return async e=>{const r=e.get("logger"),d=e.req.param("code"),o=K.BH_API_URL,h=(i,u,s)=>i&&u?`${i} ${u.charAt(0)}`:s;try{if(!o)throw new Error("BH_API_URL is not set");const i=n.getConfig().ssoDirect;if(!i||!Object.keys(i).length)return r.warn("Invite no sso configured to handle"),e.redirect(y("/"));const u=await se(`${o}/public-user-invites/${d}`);if(!u.ok)return u.status===404?(r.warn(`Invite ${d} not found redirect to homepage`),e.redirect(y("/"))):(r.error("Invite error",await u.text()),e.redirect(y("/")));const s=await u.json(),p=new URL(y("/invite"),e.req.url);return p.searchParams.set("code",d),p.searchParams.set("org",s.organization.name),p.searchParams.set("invitedBy",h(s.invitedBy.firstName,s.invitedBy.lastName,s.invitedBy.name)),e.newResponse(null,302,{Location:p.toString()})}catch(i){return r.error("Error processing invite",{error:i,inviteCode:d}),e.text(i.message||"Failed to process invite",400)}}}function Qe(n){return async e=>{const r=e.get("logger"),d=n.getConfig().ssoDirect,o=new URL(e.req.url),h=e.req.query("inviteCode"),i=e.req.header("x-forwarded-proto")||o.protocol.slice(0,-1)||"https",u=e.req.header("x-forwarded-host")||o.host,s=`${i}://${u}`;let p=o.searchParams.get("idpId");const R=o.searchParams.get("redirectTo"),m=Object.keys(d||{})[0];p=p||m;const f=o.searchParams.get("mcp_redirect_uri"),C=!!f;if(!d?.[p]){const c="Invalid idpId";if(r.error(`IdP login error: ${c}`),C){const F=V(R||void 0);L.sendMcpAuthorizationFailedMessage([{...M(F),error:c,error_details:null}])}return e.text(`Forbidden: ${c}`,403)}const I=Me({localePrefixParam:o.searchParams.get("localePrefix"),l10n:n.getGlobalData()?.l10n}),a=p&&d?await de(p,d[p]):void 0,l={};for(const c of Object.keys(a?.extraParams||{}))l[c]=o.searchParams.get(c)||a?.extraParams?.[c]||void 0;ae(e)&&(l.prompt="login",l.post_logout="1");let T,t={};if(C&&f&&a&&a.type===Q.OIDC){r.info(`Building MCP OAuth login URL with redirect_uri: ${f}`);const c=we("",{...a,extraParams:l},R,h,{redirectUriOverride:f,sourceOverride:"mcp",branchOverride:void 0,uiLocales:I});T=c.loginUrl,t=c.cookies||{}}else if(a){const c=ue({...a,extraParams:l},s,R,h,I);T=c.loginUrl,t=c.cookies||{}}return Object.keys(t).forEach(c=>{w(e,c,t[c].value,t[c].options)}),r.info(`IdP login initiated for ID '${p}'`),e.newResponse(null,302,{Location:T||new URL(e.req.url).pathname})}}function xe(n){return async e=>{const r=e.get("logger"),d=await e.req.formData(),o=d.get("SAMLResponse"),h=d.get("RelayState");if(typeof o!="string"||typeof h!="string"){const t="SAMLResponse is required";return r.error(`SAML2 login error: ${t}`),e.text(`Bad request: ${t}`,400)}const i=pe(o),{success:u,uid:s,nameFormat:p,attrs:R,issuerId:m,expiresAt:f}=fe(i),{idpId:C,redirectTo:O}=JSON.parse(h);if(!u){const t="SAML2 assertion is not successful";return r.error(`SAML2 login error: ${t}`),e.text(`Permission denied: ${t}`,401)}if(!f||Math.ceil(Date.now()/1e3)>=f){const t="SAML2 Token Expired";return r.error(`SAML2 login error: ${t}`),e.text(t,401)}const I=n.getConfig().ssoDirect?.[C];if(!I||!le(I)){const t="Cannot find valid IdP";return r.error(`SAML2 login error: ${t}`),e.text(`Permission denied: ${t}`,401)}if(!(I.issuerId&&m&&ee(I.issuerId,m))){const t="IssuerID is misconfigured or untrusted assertions issuer received";return r.error(`SAML2 login error: ${t}`),e.text(`Permission denied: ${t}`,401)}if(!await he(i,I.x509PublicCert)){const t="SAMLResponse signature invalid";return r.error(`SAML2 login error: ${t}`),e.text(t,401)}const l=ge(s,p,R,I.teamsAttributeName??x);if(!l.sub){const t="The provider did not return a valid user identity.";return r.error(`SAML2 login error: ${t}`),e.text(t,400)}if(!l.email){const t="The provider did not return a valid user email.";return r.error(`SAML2 login error: ${t}`),e.text(t,400)}let T=await E.sign({...l,idpId:C},U,$.HS256);if(Buffer.byteLength(T)>N){const t=await Z({...l},C,"teams",{rbacConfig:n.getConfig().access?.rbac,teamsMapping:I.teamsAttributeMap});if(!t){const c=`The SAML2 claims are too large for the session cookie: ${Buffer.byteLength(JSON.stringify(l))} bytes of claims`;return r.error(`SAML2 login error: ${c}`),e.text(`Forbidden: ${c}`,403)}t.teamsKept<t.teamsTotal&&r.warn(`SAML2 login: the "teams" claim is too large for the session cookie size limit; kept ${t.teamsKept} of ${t.teamsTotal} values, with teams from the RBAC configuration first; RBAC ignores the dropped teams: ${JSON.stringify(t.droppedTeams)}`),T=t.token}return w(e,_.AUTHORIZATION,T,{path:A()||"/",httpOnly:!0,expires:new Date(f*1e3)}),P(e,_.IDP_REFRESH_TOKEN,{path:A()||"/"}),v(e),r.updateContext({email:l.email,subject:l.sub}),r.info("SAML2 login successful"),e.newResponse(null,302,{Location:O||"/"})}}function er(n){return async e=>{const r=e.get("logger"),d=new URL(e.req.query("redirectTo")||"/",e.req.url),o=y(H(d.pathname)),h=n.getConfig().ssoDirect,i=Object.entries(h||{}).find(([,O])=>b(O)&&Oe(O));if(!(h&&i))return e.newResponse(null,302,{Location:o});const s=e.req.query("token"),p=s&&await Ae(s);if(!p)return e.newResponse(null,302,{Location:o});if(!re(p[oe]||[]).some(O=>O===te||O===ne))return e.newResponse(null,302,{Location:o});const f=await E.sign({...p,idpId:i?.at(0)},U,$.HS256),C=Date.now()+B*1e3;return w(e,_.AUTHORIZATION,f,{path:A()||"/",httpOnly:!0,expires:new Date(C),sameSite:"None",secure:!0}),P(e,_.IDP_REFRESH_TOKEN,{path:A()||"/"}),v(e),r.info("Token login successful"),e.newResponse(null,302,{Location:o})}}export{Ge as authorizeHandler,Qe as idpLoginHandler,Ye as inviteHandler,We as logoutHandler,Ve as oidcCallbackHandler,Xe as postLogoutHandler,Ze as redoclyLoginCallbackHandler,er as redoclyTokenLoginHandler,xe as samlCallbackHandler};