@redocly/realm 0.138.0-next.1 → 0.138.0-next.10

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 (296) hide show
  1. package/CHANGELOG.md +182 -0
  2. package/dist/bin.js +0 -0
  3. package/dist/cli/build/analytics/collect-analytics.js +1 -1
  4. package/dist/cli/build/analytics/collectors/get-sidebars-usage.d.ts +12 -0
  5. package/dist/cli/build/analytics/collectors/get-sidebars-usage.js +1 -0
  6. package/dist/cli/build/index.js +1 -1
  7. package/dist/cli/develop.js +1 -1
  8. package/dist/cli/eject/resolveEjectParams.js +1 -1
  9. package/dist/cli/generate/string-utils.js +1 -1
  10. package/dist/cli/stats/collectors/extension-sampling.js +1 -1
  11. package/dist/cli/translations/collect-config-translations.js +1 -1
  12. package/dist/client/App.js +1 -1
  13. package/dist/client/TestProvider.d.ts +3 -1
  14. package/dist/client/TestProvider.js +1 -1
  15. package/dist/client/app/Link.js +1 -1
  16. package/dist/client/app/ReviewComments/ReviewComments.js +2 -2
  17. package/dist/client/app/Sidebar/Sidebar.js +1 -1
  18. package/dist/client/app/Sidebar/useSidebarItems.js +1 -1
  19. package/dist/client/app/hooks/codeHighlight/treeview-transformer.js +1 -1
  20. package/dist/client/app/hooks/codeHighlight/useCodeHighlight.js +1 -1
  21. package/dist/client/app/hooks/index.d.ts +1 -0
  22. package/dist/client/app/hooks/index.js +1 -1
  23. package/dist/client/app/hooks/markdown/useMarkdownText.js +1 -1
  24. package/dist/client/app/hooks/useConsent.d.ts +11 -0
  25. package/dist/client/app/hooks/useConsent.js +1 -0
  26. package/dist/client/app/l10n/L10nContext.d.ts +10 -0
  27. package/dist/client/app/l10n/L10nContext.js +1 -0
  28. package/dist/client/app/l10n/hooks/useL10n.js +1 -1
  29. package/dist/client/app/l10n/hooks/useTranslate.js +1 -1
  30. package/dist/client/app/l10n/hooks/useTranslationKeys.d.ts +2 -0
  31. package/dist/client/app/l10n/hooks/useTranslationKeys.js +1 -1
  32. package/dist/client/app/l10n/ssrL10n.d.ts +4 -0
  33. package/dist/client/app/l10n/ssrL10n.js +1 -0
  34. package/dist/client/app/markdoc/custom-components/openapi/openapi-example.js +1 -1
  35. package/dist/client/app/markdoc/custom-components/openapi/openapi-response-sample.js +1 -1
  36. package/dist/client/app/markdoc/custom-components/openapi/replay-openapi.js +1 -1
  37. package/dist/client/app/markdoc/hooks/use-store.d.ts +2 -3
  38. package/dist/client/app/pages/DevLogin/DevLoginForm.js +1 -1
  39. package/dist/client/app/pages/Invite/Invite.js +1 -1
  40. package/dist/client/app/search/index.d.ts +1 -1
  41. package/dist/client/app/search/index.js +1 -1
  42. package/dist/client/app/search/useSearch.d.ts +3 -0
  43. package/dist/client/app/search/useSearch.js +1 -1
  44. package/dist/client/app/webmcp/index.d.ts +2 -0
  45. package/dist/client/app/webmcp/index.js +1 -0
  46. package/dist/client/app/webmcp/tools/api-tools.d.ts +8 -0
  47. package/dist/client/app/webmcp/tools/api-tools.js +1 -0
  48. package/dist/client/app/webmcp/tools/shared.d.ts +13 -0
  49. package/dist/client/app/webmcp/tools/shared.js +1 -0
  50. package/dist/client/app/webmcp/tools/site-tools.d.ts +5 -0
  51. package/dist/client/app/webmcp/tools/site-tools.js +3 -0
  52. package/dist/client/app/webmcp/types.d.ts +36 -0
  53. package/dist/client/app/webmcp/types.js +0 -0
  54. package/dist/client/app/webmcp/useWebMcpTools.d.ts +9 -0
  55. package/dist/client/app/webmcp/useWebMcpTools.js +1 -0
  56. package/dist/client/providers/page-data/hooks.d.ts +2 -0
  57. package/dist/client/providers/page-data/hooks.js +1 -1
  58. package/dist/client/providers/theme/ThemeDataProvider.js +1 -1
  59. package/dist/client/runtime/loader.js +1 -1
  60. package/dist/client/server-entry.d.ts +2 -1
  61. package/dist/client/server-entry.js +1 -1
  62. package/dist/client/templates/api-docs/helpers.js +1 -1
  63. package/dist/client/templates/api-docs/template.js +7 -3
  64. package/dist/client/utils/catalog/parse-filter-query.js +1 -1
  65. package/dist/client/{app/Sidebar/helpers → utils}/filter-out-versioned-items.d.ts +12 -2
  66. package/dist/client/utils/filter-out-versioned-items.js +1 -0
  67. package/dist/client/utils/navigation-tabs.d.ts +14 -0
  68. package/dist/client/utils/navigation-tabs.js +1 -0
  69. package/dist/compiled/svgo/svgo-node.js +39 -39
  70. package/dist/constants/common.d.ts +1 -0
  71. package/dist/constants/common.js +1 -1
  72. package/dist/constants/l10n/langs/ar.js +1 -1
  73. package/dist/constants/l10n/langs/de.js +1 -1
  74. package/dist/constants/l10n/langs/en.js +1 -1
  75. package/dist/constants/l10n/langs/es.js +1 -1
  76. package/dist/constants/l10n/langs/fr.js +1 -1
  77. package/dist/constants/l10n/langs/hi.js +1 -1
  78. package/dist/constants/l10n/langs/it.js +1 -1
  79. package/dist/constants/l10n/langs/ja.js +1 -1
  80. package/dist/constants/l10n/langs/ko.js +1 -1
  81. package/dist/constants/l10n/langs/pl.js +1 -1
  82. package/dist/constants/l10n/langs/pt-BR.js +1 -1
  83. package/dist/constants/l10n/langs/pt.js +1 -1
  84. package/dist/constants/l10n/langs/ru.js +1 -1
  85. package/dist/constants/l10n/langs/uk.js +1 -1
  86. package/dist/constants/l10n/langs/zh.js +1 -1
  87. package/dist/markdoc/nodes/table.d.ts +2 -0
  88. package/dist/markdoc/nodes/table.js +1 -1
  89. package/dist/server/config/env-config.d.ts +0 -6
  90. package/dist/server/config/env-schema.d.ts +0 -15
  91. package/dist/server/config/env-schemas/feature-flags.d.ts +0 -12
  92. package/dist/server/config/env-schemas/feature-flags.js +1 -1
  93. package/dist/server/constants/common.d.ts +3 -1
  94. package/dist/server/constants/common.js +1 -1
  95. package/dist/server/constants/entitlements.js +1 -1
  96. package/dist/server/constants/plugins/consent.d.ts +3 -0
  97. package/dist/server/constants/plugins/consent.js +1 -0
  98. package/dist/server/constants/plugins/search.js +1 -1
  99. package/dist/server/esbuild/esbuild-logger.js +3 -3
  100. package/dist/server/plugins/analytics/adobe/index.js +1 -1
  101. package/dist/server/plugins/analytics/amplitude/browser-hooks.js +1 -1
  102. package/dist/server/plugins/analytics/amplitude/index.js +1 -1
  103. package/dist/server/plugins/analytics/fullstory/index.js +1 -1
  104. package/dist/server/plugins/analytics/ga/index.js +1 -1
  105. package/dist/server/plugins/analytics/gtm/browser-hooks.js +1 -1
  106. package/dist/server/plugins/analytics/gtm/index.js +1 -1
  107. package/dist/server/plugins/analytics/heap/browser-hooks.js +1 -1
  108. package/dist/server/plugins/analytics/rudderstack/index.js +1 -1
  109. package/dist/server/plugins/analytics/segment/index.js +1 -1
  110. package/dist/server/plugins/api-docs/index.js +1 -1
  111. package/dist/server/plugins/api-docs/utils/apply-nav-item-to-root.d.ts +9 -0
  112. package/dist/server/plugins/api-docs/utils/apply-nav-item-to-root.js +1 -0
  113. package/dist/server/plugins/api-docs/utils/decorators.js +1 -1
  114. package/dist/server/plugins/catalog-entities/database/repositories/common/revision-repository.d.ts +1 -1
  115. package/dist/server/plugins/catalog-entities/database/repositories/common/revision-repository.js +1 -1
  116. package/dist/server/plugins/catalog-entities/database/repositories/common/version-repository.d.ts +5 -1
  117. package/dist/server/plugins/catalog-entities/database/repositories/common/version-repository.js +7 -1
  118. package/dist/server/plugins/catalog-entities/database/repositories/entities/entities-write-repository.js +1 -1
  119. package/dist/server/plugins/catalog-entities/utils/ajv-validator.js +1 -1
  120. package/dist/server/plugins/catalog-entities/utils/extract-parts-from-components-ref.js +1 -1
  121. package/dist/server/plugins/catalog-entities/utils/is-valid-tag-name.js +1 -1
  122. package/dist/server/plugins/catalog-entities/utils/openapi-component-content-extractor.js +1 -1
  123. package/dist/server/plugins/config-parser/format-custom-tags.d.ts +1 -0
  124. package/dist/server/plugins/config-parser/format-custom-tags.js +1 -1
  125. package/dist/server/plugins/config-parser/index.js +1 -1
  126. package/dist/server/plugins/config-parser/loaders/redocly-config-loader.js +1 -1
  127. package/dist/server/plugins/config-parser/loaders/versions-config-loader.js +1 -1
  128. package/dist/server/plugins/config-parser/safe-parse.js +1 -1
  129. package/dist/server/plugins/consent/build-consent-script.d.ts +6 -0
  130. package/dist/server/plugins/consent/build-consent-script.js +230 -0
  131. package/dist/server/plugins/consent/gate-ssr-components.d.ts +5 -0
  132. package/dist/server/plugins/consent/gate-ssr-components.js +1 -0
  133. package/dist/server/plugins/consent/index.d.ts +3 -0
  134. package/dist/server/plugins/consent/index.js +1 -0
  135. package/dist/server/plugins/consent/is-consent-enabled.d.ts +8 -0
  136. package/dist/server/plugins/consent/is-consent-enabled.js +1 -0
  137. package/dist/server/plugins/consent/zone-modes.d.ts +8 -0
  138. package/dist/server/plugins/consent/zone-modes.js +1 -0
  139. package/dist/server/plugins/default-theme/extract-team-names.js +1 -1
  140. package/dist/server/plugins/dev-onboarding/template/App.js +5 -5
  141. package/dist/server/plugins/dev-onboarding/template/components/ApiKeyOverview.js +3 -3
  142. package/dist/server/plugins/get-reserved-routes.d.ts +4 -2
  143. package/dist/server/plugins/get-reserved-routes.js +1 -1
  144. package/dist/server/plugins/l10n/index.js +1 -1
  145. package/dist/server/plugins/lifecycle.js +2 -2
  146. package/dist/server/plugins/markdown/markdoc/markdoc-options.d.ts +2 -0
  147. package/dist/server/plugins/markdown/search/get-ai-search-documents.js +9 -9
  148. package/dist/server/plugins/markdown/search/nodes/tag-node.d.ts +1 -1
  149. package/dist/server/plugins/markdown/search/nodes/tag-node.js +1 -1
  150. package/dist/server/plugins/markdown/search/to-markdown.js +1 -1
  151. package/dist/server/plugins/markdown/utils/sanitize-malformed-md-content.js +1 -1
  152. package/dist/server/plugins/mcp/audit/audit-log.js +1 -1
  153. package/dist/server/plugins/mcp/audit/secret-patterns.js +1 -1
  154. package/dist/server/plugins/mcp/auth/assistant-anonymous-token.d.ts +9 -0
  155. package/dist/server/plugins/mcp/auth/assistant-anonymous-token.js +1 -0
  156. package/dist/server/plugins/mcp/auth/auth-handlers.d.ts +5 -3
  157. package/dist/server/plugins/mcp/auth/auth-handlers.js +1 -1
  158. package/dist/server/plugins/mcp/codemode/build-tool-list.d.ts +6 -1
  159. package/dist/server/plugins/mcp/codemode/build-tool-list.js +2 -2
  160. package/dist/server/plugins/mcp/codemode/build-tool-type-declarations.js +4 -4
  161. package/dist/server/plugins/mcp/codemode/capabilities/fetch/index.js +3 -3
  162. package/dist/server/plugins/mcp/codemode/capabilities/types.d.ts +0 -1
  163. package/dist/server/plugins/mcp/codemode/capabilities/utils.d.ts +1 -2
  164. package/dist/server/plugins/mcp/codemode/capabilities/utils.js +4 -4
  165. package/dist/server/plugins/mcp/codemode/code-execution-audit.d.ts +3 -2
  166. package/dist/server/plugins/mcp/codemode/code-execution-audit.js +1 -1
  167. package/dist/server/plugins/mcp/codemode/code-execution-telemetry.d.ts +3 -4
  168. package/dist/server/plugins/mcp/codemode/code-execution-telemetry.js +1 -1
  169. package/dist/server/plugins/mcp/codemode/prompts.js +1 -1
  170. package/dist/server/plugins/mcp/codemode/tools/describe-tools.js +2 -2
  171. package/dist/server/plugins/mcp/codemode/tools/execute-schema.d.ts +0 -2
  172. package/dist/server/plugins/mcp/codemode/tools/execute-schema.js +1 -1
  173. package/dist/server/plugins/mcp/codemode/tools/execute.js +1 -1
  174. package/dist/server/plugins/mcp/codemode/tools/manage-credentials.js +2 -2
  175. package/dist/server/plugins/mcp/custom-tools/scan-custom-tools.js +1 -1
  176. package/dist/server/plugins/mcp/docs-mcp/tool-schemas.js +1 -1
  177. package/dist/server/plugins/mcp/docs-mcp/tools/core/create-anonymous-session.js +1 -1
  178. package/dist/server/plugins/mcp/docs-mcp/tools/core/whoami.js +1 -1
  179. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/constants.d.ts +2 -0
  180. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/constants.js +1 -1
  181. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/endpoint-suggestions.d.ts +6 -0
  182. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/endpoint-suggestions.js +1 -1
  183. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/get-endpoint-info.js +1 -1
  184. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/get-security-schemes.js +1 -1
  185. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/load-api-description.d.ts +2 -0
  186. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/load-api-description.js +1 -1
  187. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/utils.d.ts +6 -2
  188. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/utils.js +1 -1
  189. package/dist/server/plugins/mcp/docs-mcp/utils/resolve-refs.d.ts +20 -14
  190. package/dist/server/plugins/mcp/docs-mcp/utils/resolve-refs.js +1 -1
  191. package/dist/server/plugins/mcp/gateway-mcp/credential-validation.js +1 -1
  192. package/dist/server/plugins/mcp/gateway-mcp/elicitation.d.ts +16 -10
  193. package/dist/server/plugins/mcp/gateway-mcp/elicitation.js +5 -5
  194. package/dist/server/plugins/mcp/gateway-mcp/fetch/allowed-hosts.js +1 -1
  195. package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-bridge.js +1 -1
  196. package/dist/server/plugins/mcp/gateway-mcp/fetch/ssrf-guard.js +1 -1
  197. package/dist/server/plugins/mcp/gateway-mcp/fetch/url-placeholders.js +1 -1
  198. package/dist/server/plugins/mcp/gateway-mcp/rate-limit/ip-key.js +1 -1
  199. package/dist/server/plugins/mcp/gateway-mcp/upstream-auth-scheme.d.ts +0 -6
  200. package/dist/server/plugins/mcp/gateway-mcp/upstream-auth-scheme.js +2 -2
  201. package/dist/server/plugins/mcp/handlers/docs-mcp-handler.js +1 -1
  202. package/dist/server/plugins/mcp/handlers/mcp-credentials-handler.js +1 -1
  203. package/dist/server/plugins/mcp/handlers/mcp-credentials-page.js +35 -35
  204. package/dist/server/plugins/mcp/index.js +1 -1
  205. package/dist/server/plugins/mcp/servers/docs-server.js +1 -1
  206. package/dist/server/plugins/mcp/servers/mcp-server.js +2 -4
  207. package/dist/server/plugins/mcp/types.d.ts +5 -11
  208. package/dist/server/plugins/mcp/utils/disabled-tools.d.ts +3 -0
  209. package/dist/server/plugins/mcp/utils/disabled-tools.js +1 -0
  210. package/dist/server/plugins/mcp/utils/json-schema-validator.d.ts +2 -2
  211. package/dist/server/plugins/mcp/utils/json-schema-validator.js +1 -1
  212. package/dist/server/plugins/mcp/utils/mcp-client.js +1 -1
  213. package/dist/server/plugins/mcp/utils/public-endpoint.d.ts +9 -0
  214. package/dist/server/plugins/mcp/utils/public-endpoint.js +1 -0
  215. package/dist/server/plugins/mcp/utils/telemetry-attributes.d.ts +2 -1
  216. package/dist/server/plugins/mcp/utils/telemetry-attributes.js +1 -1
  217. package/dist/server/plugins/mcp/utils/tool-registration.d.ts +2 -1
  218. package/dist/server/plugins/mcp/utils/tool-registration.js +1 -1
  219. package/dist/server/plugins/mcp/utils/xmcp-utils.d.ts +0 -7
  220. package/dist/server/plugins/mcp/utils/xmcp-utils.js +1 -1
  221. package/dist/server/plugins/mcp/workers/execute-mcp-tool.js +1 -1
  222. package/dist/server/plugins/mcp/workers/mcp-tool-telemetry.d.ts +5 -7
  223. package/dist/server/plugins/mcp/workers/mcp-tool-telemetry.js +1 -1
  224. package/dist/server/plugins/nav-utils.js +1 -1
  225. package/dist/server/plugins/scorecard-classic/index.js +1 -1
  226. package/dist/server/plugins/scorecard-classic/template/Grid/GridHeader.js +21 -8
  227. package/dist/server/plugins/scorecard-classic/template/ScorecardDashboard.js +3 -3
  228. package/dist/server/plugins/scorecard-classic/template/index.styles.d.ts +1 -0
  229. package/dist/server/plugins/scorecard-classic/template/index.styles.js +15 -5
  230. package/dist/server/plugins/scorecard-classic/template/useData.js +1 -1
  231. package/dist/server/plugins/scorecard-classic/template/views.js +1 -1
  232. package/dist/server/plugins/search/engines/flexsearch/index.js +2 -1
  233. package/dist/server/plugins/search/engines/flexsearch/search-index.d.ts +2 -2
  234. package/dist/server/plugins/search/engines/flexsearch/search-index.js +1 -1
  235. package/dist/server/plugins/search/llmstxt/index.js +1 -1
  236. package/dist/server/plugins/sidebars/index.js +7 -3
  237. package/dist/server/plugins/sidebars/tabs.d.ts +19 -0
  238. package/dist/server/plugins/sidebars/tabs.js +1 -0
  239. package/dist/server/plugins/sso/index.js +1 -1
  240. package/dist/server/ssr/render.js +1 -1
  241. package/dist/server/store.d.ts +2 -0
  242. package/dist/server/store.js +1 -1
  243. package/dist/server/tools/notifiers/helpers/privacy/mask-subject.js +1 -1
  244. package/dist/server/tools/notifiers/helpers/privacy/remove-pii.js +1 -1
  245. package/dist/server/types/plugins/search.d.ts +7 -5
  246. package/dist/server/utils/catalog-classic/get-catalog-shared-data-id.d.ts +3 -0
  247. package/dist/server/utils/catalog-classic/get-catalog-shared-data-id.js +1 -0
  248. package/dist/server/utils/envs/interpolate-env-variables.js +1 -1
  249. package/dist/server/utils/rbac-expand.js +1 -1
  250. package/dist/server/utils/rbac.js +1 -1
  251. package/dist/server/utils/redact-url.d.ts +6 -0
  252. package/dist/server/utils/redact-url.js +1 -0
  253. package/dist/server/utils/report-all-errors.js +1 -1
  254. package/dist/server/utils/sandbox-context.js +1 -1
  255. package/dist/server/utils/search/highlight-text-for-search.d.ts +1 -0
  256. package/dist/server/utils/search/highlight-text-for-search.js +1 -1
  257. package/dist/server/utils/search/text-match.d.ts +6 -0
  258. package/dist/server/utils/search/text-match.js +1 -0
  259. package/dist/server/utils/static-data.js +1 -1
  260. package/dist/server/utils/write-problems-report.d.ts +3 -0
  261. package/dist/server/utils/write-problems-report.js +1 -0
  262. package/dist/server/web-server/auth.d.ts +16 -2
  263. package/dist/server/web-server/auth.js +4 -4
  264. package/dist/server/web-server/jwt/jwt.js +1 -1
  265. package/dist/server/web-server/jwt/token-types.d.ts +11 -0
  266. package/dist/server/web-server/jwt/token-types.js +1 -0
  267. package/dist/server/web-server/jwt/types.d.ts +4 -4
  268. package/dist/server/web-server/jwt/types.js +1 -1
  269. package/dist/server/web-server/middleware/ensureSearchData.js +1 -1
  270. package/dist/server/web-server/middleware/responseHeadersMiddleware.js +1 -1
  271. package/dist/server/web-server/middleware/telemetry-middleware.js +1 -1
  272. package/dist/server/web-server/routes/ask-ai.js +1 -1
  273. package/dist/server/web-server/routes/auth.d.ts +1 -1
  274. package/dist/server/web-server/routes/auth.js +1 -1
  275. package/dist/server/web-server/routes/cors-proxy.js +2 -2
  276. package/dist/server/web-server/routes/dynamic-route.js +1 -1
  277. package/dist/server/web-server/routes/index.js +1 -1
  278. package/dist/server/web-server/routes/mcp-routes/mcp-oauth.js +1 -1
  279. package/dist/server/web-server/routes/mcp-routes/mcp-server-card.js +1 -1
  280. package/dist/server/web-server/routes/semantic-search.d.ts +1 -1
  281. package/dist/server/web-server/routes/semantic-search.js +1 -1
  282. package/dist/server/web-server/routes/static-content.js +1 -1
  283. package/dist/server/workers/worker-pool.js +1 -1
  284. package/dist/types/entitlements.d.ts +1 -0
  285. package/dist/utils/path/parse-path-versions.js +1 -1
  286. package/dist/utils/string/to-kebab-case.js +1 -1
  287. package/package.json +9 -9
  288. package/dist/client/app/Sidebar/helpers/filter-out-versioned-items.js +0 -1
  289. package/dist/server/plugins/mcp/auth/token-audience-telemetry.d.ts +0 -7
  290. package/dist/server/plugins/mcp/auth/token-audience-telemetry.js +0 -1
  291. package/dist/server/plugins/mcp/auth/token-audience.d.ts +0 -6
  292. package/dist/server/plugins/mcp/auth/token-audience.js +0 -1
  293. /package/dist/server/{plugins/mcp/gateway-mcp/fetch → web-server/utils}/ssrf-blocklist.d.ts +0 -0
  294. /package/dist/server/{plugins/mcp/gateway-mcp/fetch → web-server/utils}/ssrf-blocklist.js +0 -0
  295. /package/dist/server/{plugins/mcp/gateway-mcp/fetch → web-server/utils}/ssrf-safe-lookup.d.ts +0 -0
  296. /package/dist/server/{plugins/mcp/gateway-mcp/fetch → web-server/utils}/ssrf-safe-lookup.js +0 -0
@@ -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 L,deleteCookie as P}from"hono/cookie";import{AuthProviderType as Q,DEFAULT_TEAM_CLAIM_NAME as x}from"@redocly/config";import{withPathPrefix as w,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 v,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 y}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 U,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 Le,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 we}from"../utils.js";import{encodeBase64URL as ye}from"../jwt/encode.js";import{resolveUiLocalesForIdpLogin as Me}from"./helpers/resolve-ui-locales-for-idp-login.js";async function Ge(n,e){if(!e.devLogin||K.isProductionEnv)return n.newResponse(null,404,{});const{password:r,...a}=await n.req.json(),o=await E.sign({...a,name:a.username||a.email||"Unknown"},v,$.HS256);return L(n,_.AUTHORIZATION,o,{path:A()||"/",httpOnly:!0,secure:!0,sameSite:"none"}),U(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 a=`${k.LOGIN}/?error=${encodeURIComponent(r)}`;return n.newResponse(null,301,{Location:a})}}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 a=Buffer.from(r,"base64url").toString("utf-8");return JSON.parse(a).mcpSessionId||null}}catch{}return null}function Ve(n){return async e=>{const r=e.get("logger"),a=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=a?.[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(a&&!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&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:`OIDC error: ${C}`,error_details:e.req.query("error_description")||null}]),we(e,n,{slug:"/"},403,"403OIDC");if(!f){const g="Code is expected but not present";return r.error(`OIDC login error: ${g}`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:g,error_details:null}]),new Response(`Forbidden: ${g}`,{status:403})}const O=typeof o.redirectUri=="string"?o.redirectUri:new URL(w(k.OIDC_CALLBACK),e.req.url).toString(),I=e.get("cookies")?.code_verifier,d=await ce(m,f,O,s,{...s.tokenRequestCustomParams,...I?{code_verifier:I}:{}});if(d.error)return r.error(`Error from OIDC provider: "${d.error}"`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:`Token exchange error: ${d.error}`,error_details:d.error_description||null}]),e.text(`Forbidden: ${d.error_description||d.error}`,403);if(!d?.id_token){const g="No id_token, please, add openid to scopes";return r.error(`OIDC login error: ${g}`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:g,error_details:null}]),new Response(`Forbidden: ${g}`,{status:403})}const{payload:l,header:T}=E.decode(d.id_token),t=T.alg===$.RS256;if(!Re(l,s)){const g="No valid audience found in id_token";return r.error(`OIDC login error: ${g}`),i&&y.sendMcpAuthorizationFailedMessage([{...M(u),error:g,error_details:null}]),new Response(`Forbidden: ${g}`,{status:403})}let c=t?d.id_token:await E.sign({...l,idpId:h},v,$.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&&y.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:d.access_token})});if(g.ok){const q=(await g.json()).ext?.federatedIdentity;q&&(L(e,_.FEDERATED_ACCESS_TOKEN,q.access_token||"",{path:A()||"/",httpOnly:!1,expires:new Date(D)}),L(e,_.FEDERATED_ID_TOKEN,q.id_token||"",{path:A()||"/",httpOnly:!1,expires:new Date(D)}))}else r.warn(`OIDC introspect error: ${g.statusText}`)}if(L(e,_.AUTHORIZATION,c,{path:A()||"/",httpOnly:!0,expires:new Date(D)}),c!==d.id_token&&Buffer.byteLength(d.id_token)<=N?L(e,_.IDP_ID_TOKEN,d.id_token||"",{path:A()||"/",httpOnly:!0,expires:new Date(D)}):P(e,_.IDP_ID_TOKEN,{path:A()||"/"}),L(e,_.IDP_ACCESS_TOKEN,d.access_token||"",{path:A()||"/",httpOnly:!0,expires:new Date(D)}),d.refresh_token?L(e,_.IDP_REFRESH_TOKEN,d.refresh_token,{path:A()||"/",httpOnly:!0,expires:new Date(D)}):P(e,_.IDP_REFRESH_TOKEN,{path:A()||"/"}),P(e,"code_verifier",{path:A()||"/"}),U(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&&y.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&&y.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?ye(JSON.stringify({branch:G(f)})):void 0,I=C?`${Te(f)}/${ie}/logout`:`${f}${w(k.POST_LOGOUT)}`,d=e.get("cookies"),l=d?.[_.AUTHORIZATION],T=d?.[_.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||w("/")})}}function Xe(n){return async e=>{const r=n.getConfig().access?.logoutReturnUrl,a=r||w("/");return e.newResponse(null,302,{Location:a})}}function Ye(n){return async e=>{const r=e.get("logger"),a=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(w("/"));const u=await se(`${o}/public-user-invites/${a}`);if(!u.ok)return u.status===404?(r.warn(`Invite ${a} 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",a),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:a}),e.text(i.message||"Failed to process invite",400)}}}function Qe(n){return async e=>{const r=e.get("logger"),a=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(a||{})[0];p=p||m;const f=o.searchParams.get("mcp_redirect_uri"),C=!!f;if(!a?.[p]){const c="Invalid idpId";if(r.error(`IdP login error: ${c}`),C){const F=V(R||void 0);y.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}),d=p&&a?await de(p,a[p]):void 0,l={};for(const c of Object.keys(d?.extraParams||{}))l[c]=o.searchParams.get(c)||d?.extraParams?.[c]||void 0;ae(e)&&(l.prompt="login",l.post_logout="1");let T,t={};if(C&&f&&d&&d.type===Q.OIDC){r.info(`Building MCP OAuth login URL with redirect_uri: ${f}`);const c=Le("",{...d,extraParams:l},R,h,{redirectUriOverride:f,sourceOverride:"mcp",branchOverride:void 0,uiLocales:I});T=c.loginUrl,t=c.cookies||{}}else if(d){const c=ue({...d,extraParams:l},s,R,h,I);T=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:T||new URL(e.req.url).pathname})}}function xe(n){return async e=>{const r=e.get("logger"),a=await e.req.formData(),o=a.get("SAMLResponse"),h=a.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},v,$.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 L(e,_.AUTHORIZATION,T,{path:A()||"/",httpOnly:!0,expires:new Date(f*1e3)}),P(e,_.IDP_REFRESH_TOKEN,{path:A()||"/"}),U(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"),a=new URL(e.req.query("redirectTo")||"/",e.req.url),o=w(H(a.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)},v,$.HS256),C=Date.now()+B*1e3;return L(e,_.AUTHORIZATION,f,{path:A()||"/",httpOnly:!0,expires:new Date(C),sameSite:"None",secure:!0}),P(e,_.IDP_REFRESH_TOKEN,{path:A()||"/"}),U(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};
@@ -1,2 +1,2 @@
1
- import $ from"node:dns";import{isIP as E}from"node:net";import{withPathPrefix as L}from"@redocly/theme/core/utils";import{ServerRoutes as A}from"../../../constants/api.js";import{envConfig as H}from"../../config/env-config.js";import{getRequestOrigin as D}from"../utils/get-request-origin.js";const M=new Set(["connection","keep-alive","proxy-authenticate","proxy-connection","proxy-authorization","te","trailer","transfer-encoding","upgrade","host"]),k=new Set(["cookie","cookie2","accept-encoding"]),b=new Set(["set-cookie","set-cookie2","content-encoding","content-length"]),y="x-redocly-proxy-streaming",x="x-http-method-override",S="x-redocly-cookie";function oe(n,e=L(A.CORS_PROXY)){return async t=>{const o=new URL(t.req.url).pathname;if(o===e||o===`${e}/`)return t.text(`Realm CORS proxy endpoint.
2
- Usage: ${e}/https://api.example.com/path`);const r=J(t.req.url,e);if(!r)return t.text("Invalid proxied URL",400);const a=n?.getConfig().corsProxy?.allowedTargets;if(a&&a.length>0&&!a.some(p=>V(r,p)))return t.text("Target URL is not in the allowed list for CORS proxy",403);const f=D(t),h=r.origin===f,m=r.pathname===e||r.pathname.startsWith(`${e}/`);if(h&&!m)return new Response("Please use a direct request",{status:308,headers:{Location:r.toString(),Vary:"origin","Cache-Control":"private"}});const u=await Y(r.hostname);if((!H.isDevelopMode||H.isReunite)&&u&&F(u))return t.text("Requests to private network addresses are not allowed",403);const i=new Headers,I=v(t.req.raw.headers);for(const[s,p]of t.req.raw.headers)I.has(s.toLowerCase())||k.has(s.toLowerCase())||i.append(s,p);const g=i.get(S);if(g){const s=t.req.raw.headers.get("cookie")||"";i.set("cookie",s?`${s}; ${g}`:g),i.delete(S)}const O=t.req.raw.headers.get("origin")||"";U(O)&&i.delete("origin");let l=t.req.method;const R=i.get(x);R&&(l=R.toUpperCase(),i.delete(x));const w={method:l,headers:i,redirect:"manual"};l!=="GET"&&l!=="HEAD"&&t.req.raw.body&&(w.body=t.req.raw.body,w.duplex="half");let c;try{c=await fetch(r,w)}catch(s){const p=s instanceof Error?s.message:"unknown error",_=s instanceof Error&&s.cause instanceof Error?`: ${s.cause.message}`:"";return t.text(`Failed to proxy request: ${p}${_}`,502)}const T=c.headers.get("content-type")||"";if(G(T)&&t.req.raw.headers.get("sec-fetch-mode")==="navigate")return t.text("Direct browser navigation to proxied HTML or JavaScript content is not allowed",403);const d=new Headers(c.headers),q=v(c.headers);for(const s of q)d.delete(s);for(const s of b)d.delete(s);return d.set(y,"1"),d.set("x-content-type-options","nosniff"),d.has("content-type")||d.set("content-type","text/plain"),new Response(c.body,{status:c.status,statusText:c.statusText,headers:d})}}function N(n){try{return decodeURIComponent(n)}catch{return n}}function W(n){return n.replace(/^(https?):\/(?!\/)/i,"$1://")}function z(n,e){return e?n.includes("?")?e==="?"?n:`${n.endsWith("?")||n.endsWith("&")?n:`${n}&`}${e.slice(1)}`:`${n}${e}`:n}function v(n){const e=new Set(M),t=n.get("connection");if(!t)return e;for(const o of t.split(",")){const r=o.trim().toLowerCase();r&&e.add(r)}return e}function U(n){const e=n.toLowerCase();return e.includes(".redocly.app")||e.includes("localhost")}async function Y(n){const e=P(n);if(E(e))return e;try{const{address:t}=await $.promises.lookup(e);return t}catch{return null}}function F(n){const e=P(n).toLowerCase();if(!E(e))return!0;const t=X(e);return t?C(t):e.includes(":")?j(e):C(e)}function P(n){return n.startsWith("[")&&n.endsWith("]")?n.slice(1,-1):n}function X(n){const e=n.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i);if(e){const o=parseInt(e[1],16),r=parseInt(e[2],16);return[o>>8&255,o&255,r>>8&255,r&255].join(".")}return n.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/i)?.[1]??null}function C(n){const e=n.split(".").map(Number);if(e.length!==4||e.some(r=>isNaN(r)))return!0;const[t,o]=e;return t===0||t===10||t===127||t===172&&o>=16&&o<=31||t===192&&o===168||t===169&&o===254||t===100&&o>=64&&o<=127}function j(n){const e=B(n);return n==="::1"||n==="::"||e!==null&&(e&65024)===64512||e!==null&&(e&65472)===65152}function B(n){const[e]=n.split(":");if(!e)return null;const t=parseInt(e,16);return Number.isNaN(t)?null:t}function V(n,e){let t;try{t=new URL(e)}catch{return!1}if(n.origin!==t.origin)return!1;const o=t.pathname.endsWith("/")?t.pathname:`${t.pathname}/`;return n.pathname.startsWith(o)||n.pathname===t.pathname}function G(n){const e=n.toLowerCase();return e.includes("text/html")||e.includes("javascript")||e.includes("application/xhtml")||e.includes("application/xml")||e.includes("image/svg")}function J(n,e){const t=new URL(n),o=t.pathname===e,r=t.pathname.startsWith(`${e}/`);if(!o&&!r)return null;const a=t.pathname.slice(e.length).replace(/^\/+/,"");if(!a)return null;const f=[a,N(a)];for(const h of f){const m=W(h),u=z(m,t.search);try{const i=new URL(u);if(i.protocol==="http:"||i.protocol==="https:")return i}catch{continue}}return null}const re=y;export{re as CORS_PROXY_STREAM_HEADER,oe as corsProxyHandler,F as isPrivateIp,J as resolveCorsProxyTarget};
1
+ import D from"node:dns";import{isIP as x}from"node:net";import{Agent as M}from"undici";import{withPathPrefix as $}from"@redocly/theme/core/utils";import{ServerRoutes as N}from"../../../constants/api.js";import{envConfig as S}from"../../config/env-config.js";import{getRequestOrigin as b}from"../utils/get-request-origin.js";import{SsrfBlockedError as U}from"../utils/ssrf-blocklist.js";import{createSsrfSafeLookup as W}from"../utils/ssrf-safe-lookup.js";const z=new Set(["connection","keep-alive","proxy-authenticate","proxy-connection","proxy-authorization","te","trailer","transfer-encoding","upgrade","host"]),Y=new Set(["cookie","cookie2","accept-encoding"]),B=new Set(["set-cookie","set-cookie2","content-encoding","content-length","clear-site-data","strict-transport-security","alt-svc","nel","report-to","reporting-endpoints","set-login","accept-ch","critical-ch","service-worker-allowed"]),v="x-redocly-proxy-streaming",P="x-http-method-override",T="x-redocly-cookie",A="Requests to private network addresses are not allowed",F=new M({connect:{lookup:W()}});function me(t,e=$(N.CORS_PROXY)){const n=async r=>{const s=new URL(r.req.url).pathname;if(s===e||s===`${e}/`)return r.text(`Realm CORS proxy endpoint.
2
+ Usage: ${e}/https://api.example.com/path`);const i=oe(r.req.url,e);if(!i)return r.text("Invalid proxied URL",400);const l=t?.getConfig().corsProxy?.allowedTargets;if(l&&l.length>0&&!l.some(p=>te(i,p)))return r.text("Target URL is not in the allowed list for CORS proxy",403);const m=b(r),g=i.origin===m,w=i.pathname===e||i.pathname.startsWith(`${e}/`);if(g&&!w)return new Response("Please use a direct request",{status:308,headers:{Location:i.toString(),Vary:"origin"}});const u=await J(i.hostname),R=!S.isDevelopMode||S.isReunite;if(R&&u&&K(u))return r.text(A,403);const a=new Headers,C=I(r.req.raw.headers);for(const[o,p]of r.req.raw.headers)C.has(o.toLowerCase())||Y.has(o.toLowerCase())||a.append(o,p);const E=a.get(T);E&&(a.set("cookie",E),a.delete(T));const k=r.req.raw.headers.get("origin")||"";j(k)&&a.delete("origin");let f=r.req.method;const y=a.get(P);y&&(f=y.toUpperCase(),a.delete(P));const h={method:f,headers:a,redirect:"manual"};R&&(h.dispatcher=F),f!=="GET"&&f!=="HEAD"&&r.req.raw.body&&(h.body=r.req.raw.body,h.duplex="half");let c;try{c=await fetch(i,h)}catch(o){if(o instanceof Error&&o.cause instanceof U)return r.text(A,403);const p=o instanceof Error?o.message:"unknown error",L=o instanceof Error&&o.cause instanceof Error?`: ${o.cause.message}`:"";return r.text(`Failed to proxy request: ${p}${L}`,502)}const H=r.req.raw.headers.get("sec-fetch-mode");if((H==="navigate"||H==="no-cors")&&ne(c))return r.text("Direct browser navigation to proxied HTML, JavaScript, or redirect responses is not allowed",403);const d=new Headers(c.headers),q=I(c.headers);for(const o of q)d.delete(o);for(const o of B)d.delete(o);return d.set(v,"1"),d.set("x-content-type-options","nosniff"),d.has("content-type")||d.set("content-type","text/plain"),new Response(c.body,{status:c.status,statusText:c.statusText,headers:d})};return async r=>{const s=await n(r);return s.headers.set("cache-control","no-store"),s}}function G(t){try{return decodeURIComponent(t)}catch{return t}}function V(t){return t.replace(/^(https?):\/(?!\/)/i,"$1://")}function X(t,e){return e?t.includes("?")?e==="?"?t:`${t.endsWith("?")||t.endsWith("&")?t:`${t}&`}${e.slice(1)}`:`${t}${e}`:t}function I(t){const e=new Set(z),n=t.get("connection");if(!n)return e;for(const r of n.split(",")){const s=r.trim().toLowerCase();s&&e.add(s)}return e}function j(t){const e=t.toLowerCase();return e.includes(".redocly.app")||e.includes("localhost")}async function J(t){const e=O(t);if(x(e))return e;try{const{address:n}=await D.promises.lookup(e);return n}catch{return null}}function K(t){const e=O(t).toLowerCase();if(!x(e))return!0;const n=Q(e);return n?_(n):e.includes(":")?Z(e):_(e)}function O(t){return t.startsWith("[")&&t.endsWith("]")?t.slice(1,-1):t}function Q(t){const e=t.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i);if(e){const r=parseInt(e[1],16),s=parseInt(e[2],16);return[r>>8&255,r&255,s>>8&255,s&255].join(".")}return t.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/i)?.[1]??null}function _(t){const e=t.split(".").map(Number);if(e.length!==4||e.some(s=>isNaN(s)))return!0;const[n,r]=e;return n===0||n===10||n===127||n===172&&r>=16&&r<=31||n===192&&r===168||n===169&&r===254||n===100&&r>=64&&r<=127}function Z(t){const e=ee(t);return t==="::1"||t==="::"||e!==null&&(e&65024)===64512||e!==null&&(e&65472)===65152}function ee(t){const[e]=t.split(":");if(!e)return null;const n=parseInt(e,16);return Number.isNaN(n)?null:n}function te(t,e){let n;try{n=new URL(e)}catch{return!1}if(t.origin!==n.origin)return!1;const r=n.pathname.endsWith("/")?n.pathname:`${n.pathname}/`;return t.pathname.startsWith(r)||t.pathname===n.pathname}function ne(t){const e=t.status>=300&&t.status<400&&t.headers.has("location")||t.headers.has("refresh"),n=t.status===401&&t.headers.has("www-authenticate");return e||n||re(t.headers.get("content-type")||"")}function re(t){const e=t.toLowerCase();return e.includes("text/html")||e.includes("javascript")||e.includes("application/xhtml")||e.includes("application/xml")||e.includes("text/xml")||e.includes("ecmascript")||e.includes("image/svg")}function oe(t,e){const n=new URL(t),r=n.pathname===e,s=n.pathname.startsWith(`${e}/`);if(!r&&!s)return null;const i=n.pathname.slice(e.length).replace(/^\/+/,"");if(!i)return null;const l=[i,G(i)];for(const m of l){const g=V(m),w=X(g,n.search);try{const u=new URL(w);if(u.protocol==="http:"||u.protocol==="https:")return u}catch{continue}}return null}const ge=v;export{ge as CORS_PROXY_STREAM_HEADER,me as corsProxyHandler,K as isPrivateIp,oe as resolveCorsProxyTarget};
@@ -1 +1 @@
1
- import m from"path";import{REDOCLY_ROUTE_RBAC as B,REDOCLY_TEAMS_RBAC as F}from"@redocly/config";import{combineUrls as z,withoutPathPrefix as ee,withPathPrefix as E}from"@redocly/theme/core/utils";import{CACHE_CONTROL_NO_STORE_HEADER_VALUE as X,CACHE_CONTROL_PUBLIC_HTML as te,DEFAULT_IMMUTABLE_CACHE_MAX_AGE as ne,X_REDOCLY_CACHE_CONTROL_HEADER as oe,X_REDOCLY_CACHE_CONTROL_PUBLIC_HTML as re}from"../../constants/common.js";import{removeTrailingSlash as ie}from"../../../utils/url/remove-trailing-slash.js";import{findInIterable as ae}from"../../../utils/collection/find-in-iterable.js";import{sanitizeRedirectPathname as se}from"../../../utils/url/sanitize-redirect-pathname.js";import{envConfig as ce}from"../../config/env-config.js";import{sanitizePath as le}from"../../../utils/path/sanitize-path.js";import{normalizeRouteSlug as me}from"../../../utils/path/normalize-route-slug.js";import{isPathInFolder as ue}from"../../../utils/path/is-path-in-folder.js";import{getLlmsTxtMdPathBySlug as de}from"../../utils/llmstxt/get-llms-txt-md-path-by-slug.js";import{resolveLlmsTxtMd as Y}from"../../utils/llmstxt/resolve-llms-txt-md.js";import{removeLeadingSlash as fe}from"../../../utils/url/remove-leading-slash.js";import{processRedirects as ge}from"./helpers/process-redirects.js";import{renderPage as pe,getServerProps as he}from"../../ssr/index.js";import{canAccessAsset as Ce,canAccessResource as $}from"../../utils/rbac.js";import{handleErrorPageRender as k,handleUnauthorized as N,handleUnauthorizedAsset as G}from"../utils.js";import{DEFAULT_MAX_AGE_FOR_MIME_TYPE as Re,MIME_TYPES as Ae}from"../mime-types.js";import{fileExistsAsync as Ee}from"../../utils/index.js";import{isAiAgentRequest as Le}from"../../utils/ai-agent-detection.js";import{getRedirectRoute as _e}from"../utils/legacy-openapi-redirects.js";import{getContentTypeHeaderValue as j}from"../utils/content-type.js";import{telemetry as we}from"../../../cli/telemetry/index.js";import{telemetry as Te}from"../../telemetry/index.js";function Ke(e,V,W,L){return async n=>{const _=n.get("logger"),r=n.req,a=new URL(r.url),i=ee(le(decodeURIComponent(a.pathname))),w=m.parse(i).ext===".md",T=Le({accept:r.header("accept"),signatureAgent:r.header("signature-agent"),signature:r.header("signature"),signatureInput:r.header("signature-input"),userAgent:r.header("user-agent")}),s=me(i),o=(r.method==="GET"||r.method==="HEAD")&&!w?e.getRouteBySlug(s,{followRedirect:!1})||ae(e.routesBySlug?.values(),t=>t.hasClientRoutes&&(i===t.slug||i.startsWith(t.slug+"/"))):void 0,P=e.getRedirect(s);if(P){const t=ge({redirect:P,reqUrlSearch:a.search});return Te.sendRedirectMessage([{id:s,object:"redirect",uri:`urn:redocly:realm:api:redirect:${s}`,from:s,templateId:t.type.toString()}]),n.newResponse(null,t.type,{Location:t.location})}const g=ce.isProductionEnv?301:302;if(o?.metadata?.type==="openapi"){const t=_e(a.pathname);if(t)return _.info("Legacy OpenAPI docs redirect from "+a.pathname),n.newResponse(null,g,{Location:encodeURI(t)});if(a.pathname.match(/[A-Z]/))return _.warn("Redirect to lowercase route to avoid 404 error"),n.newResponse(null,g,{Location:encodeURI(a.pathname.toLowerCase())})}if(i.endsWith("/")&&i!=="/"){const t=se(new URL(s||"/",n.req.url).pathname);return n.newResponse(null,g,{Location:encodeURI(E((t==="/"?"/":ie(t))+a.search))})}const{isAuthenticated:u,teams:p,claims:{email:h}}=n.get("auth");if(o&&!$(o,{isAuthenticated:u,email:h,teams:p},e.config.access?.rbac,e.config.access?.requiresLogin))return u?k(n,e,{slug:o.slug,[F]:o[F],[B]:o[B]},403):N(n,e,o.slug);const d=o&&T?de(o.slug):void 0,U=d?await Y(d,e,L):void 0,M=U?.exists??!1;if(o&&(!T||!M)){const t=await V(o),x=await he(o,n,t,e),{html:J,statusCode:D,error:K}=await pe(o,x,n,e,we),H=e.config.access?.rbac,q=!e.config.access?.requiresLogin&&(!H||!Object.keys(H).length),v=D>=400||!!K||!!e.compilationErrors?.length,Q=q&&!v;let f;return v?f=X:q?f=te:f="private",n.html(J,D,{"Cache-Control":f,...Q&&{[oe]:re}})}const C=d&&M?d:i,Z=fe(C),c=m.resolve(e.outdir,Z);if(!ue(c,e.outdir))return G(n);let l,b;if(w){const t=await Y(i,e,L);if(l=t.route,b=t.content,l&&!$(l,{isAuthenticated:u,email:h,teams:p},e.config.access?.rbac,e.config.access?.requiresLogin))return N(n,e,i)}const y=e.getGlobalConfig("access");if(!Ce(C,y?.rbac||{},y?.requiresLogin||!1,e.getGlobalConfig("directoryPaths"),{isAuthenticated:u,email:h,teams:p}))return G(n);const R=Ae[m.extname(c)]||"text/plain",A=c.match(/assets\/.*\.[a-f0-9]{8,}\..+/)||c.match(/runtime\/chunks\/.*/)?ne:Re[R],O=A?{"Cache-Control":`public, max-age=${A}, immutable`,Expires:new Date(Date.now()+A*1e3).toUTCString()}:{"Cache-Control":X},I=r.query("download")!=null,S=U?.content??b;if(S!==void 0)return n.newResponse(S,200,{"Content-Type":j(R),"Access-Control-Allow-Origin":"*",...O,...l&&{"X-Robots-Tag":"noindex, follow",...e.config.seo?.siteUrl&&{Link:`<${z(e.config.seo.siteUrl,E(l.slug))}>; rel="canonical"`}},...I&&{"Content-Disposition":`attachment; filename="${m.basename(C)}"`}});if(await Ee(c)){const t=await W(c);return n.newResponse(t,200,{"Content-Type":j(R),"Access-Control-Allow-Origin":"*",...O,...l&&{"X-Robots-Tag":"noindex, follow",...e.config.seo?.siteUrl&&{Link:`<${z(e.config.seo.siteUrl,E(l.slug))}>; rel="canonical"`}},...I&&{"Content-Disposition":`attachment; filename="${m.basename(c)}"`}})}else return k(n,e,{slug:s},404)}}export{Ke as dynamicRouteHandler};
1
+ import m from"path";import{realpath as N}from"fs/promises";import{REDOCLY_ROUTE_RBAC as Y,REDOCLY_TEAMS_RBAC as z}from"@redocly/config";import{combineUrls as $,withoutPathPrefix as ne,withPathPrefix as T}from"@redocly/theme/core/utils";import{CACHE_CONTROL_NO_STORE_HEADER_VALUE as k,CACHE_CONTROL_PUBLIC_HTML as oe,DEFAULT_IMMUTABLE_CACHE_MAX_AGE as re,STATIC_DATA_FOLDER as ie,X_REDOCLY_CACHE_CONTROL_HEADER as ae,X_REDOCLY_CACHE_CONTROL_PUBLIC_HTML as se}from"../../constants/common.js";import{removeTrailingSlash as ce}from"../../../utils/url/remove-trailing-slash.js";import{findInIterable as le}from"../../../utils/collection/find-in-iterable.js";import{sanitizeRedirectPathname as me}from"../../../utils/url/sanitize-redirect-pathname.js";import{envConfig as ue}from"../../config/env-config.js";import{sanitizePath as de}from"../../../utils/path/sanitize-path.js";import{normalizeRouteSlug as fe}from"../../../utils/path/normalize-route-slug.js";import{isPathInFolder as G}from"../../../utils/path/is-path-in-folder.js";import{getLlmsTxtMdPathBySlug as ge}from"../../utils/llmstxt/get-llms-txt-md-path-by-slug.js";import{resolveLlmsTxtMd as j}from"../../utils/llmstxt/resolve-llms-txt-md.js";import{removeLeadingSlash as pe}from"../../../utils/url/remove-leading-slash.js";import{AI_INDEX_EXPORT_FOLDER as he,SEARCH_DATA_EXPORT_FOLDER as Re}from"../../constants/plugins/search.js";import{processRedirects as Ce}from"./helpers/process-redirects.js";import{renderPage as Ae,getServerProps as Ee}from"../../ssr/index.js";import{canAccessAsset as Le,canAccessResource as V}from"../../utils/rbac.js";import{handleErrorPageRender as w,handleUnauthorized as W,handleUnauthorizedAsset as Z}from"../utils.js";import{DEFAULT_MAX_AGE_FOR_MIME_TYPE as _e,MIME_TYPES as Te}from"../mime-types.js";import{fileExistsAsync as we}from"../../utils/index.js";import{isAiAgentRequest as Oe}from"../../utils/ai-agent-detection.js";import{getRedirectRoute as Pe}from"../utils/legacy-openapi-redirects.js";import{getContentTypeHeaderValue as x}from"../utils/content-type.js";import{telemetry as ye}from"../../../cli/telemetry/index.js";import{telemetry as Ue}from"../../telemetry/index.js";const Me=[Re,he,ie];async function be(e,p){if(m.extname(e)===".md")return!1;try{const[h,u]=await Promise.all([N(e),N(p)]);return Me.some(t=>G(h,m.join(u,t)))}catch{return!1}}function at(e,p,h,u){return async t=>{const O=t.get("logger"),r=t.req,c=new URL(r.url),i=ne(de(decodeURIComponent(c.pathname))),P=m.parse(i).ext===".md",y=Oe({accept:r.header("accept"),signatureAgent:r.header("signature-agent"),signature:r.header("signature"),signatureInput:r.header("signature-input"),userAgent:r.header("user-agent")}),a=fe(i),o=(r.method==="GET"||r.method==="HEAD")&&!P?e.getRouteBySlug(a,{followRedirect:!1})||le(e.routesBySlug?.values(),n=>n.hasClientRoutes&&(i===n.slug||i.startsWith(n.slug+"/"))):void 0,U=e.getRedirect(a);if(U){const n=Ce({redirect:U,reqUrlSearch:c.search});return Ue.sendRedirectMessage([{id:a,object:"redirect",uri:`urn:redocly:realm:api:redirect:${a}`,from:a,templateId:n.type.toString()}]),t.newResponse(null,n.type,{Location:n.location})}const R=ue.isProductionEnv?301:302;if(o?.metadata?.type==="openapi"){const n=Pe(c.pathname);if(n)return O.info("Legacy OpenAPI docs redirect from "+c.pathname),t.newResponse(null,R,{Location:encodeURI(n)});if(c.pathname.match(/[A-Z]/))return O.warn("Redirect to lowercase route to avoid 404 error"),t.newResponse(null,R,{Location:encodeURI(c.pathname.toLowerCase())})}if(i.endsWith("/")&&i!=="/"){const n=me(new URL(a||"/",t.req.url).pathname);return t.newResponse(null,R,{Location:encodeURI(T((n==="/"?"/":ce(n))+c.search))})}const{isAuthenticated:d,teams:C,claims:{email:A}}=t.get("auth");if(o&&!V(o,{isAuthenticated:d,email:A,teams:C},e.config.access?.rbac,e.config.access?.requiresLogin))return d?w(t,e,{slug:o.slug,[z]:o[z],[Y]:o[Y]},403):W(t,e,o.slug);const f=o&&y?ge(o.slug):void 0,M=f?await j(f,e,u):void 0,b=M?.exists??!1;if(o&&(!y||!b)){const n=await p(o),K=await Ee(o,t,n,e),{html:Q,statusCode:q,error:ee}=await Ae(o,K,t,e,ye),v=e.config.access?.rbac,B=!e.config.access?.requiresLogin&&(!v||!Object.keys(v).length),X=q>=400||!!ee||!!e.compilationErrors?.length,te=B&&!X;let g;return X?g=k:B?g=oe:g="private",t.html(Q,q,{"Cache-Control":g,...te&&{[ae]:se}})}const E=f&&b?f:i,J=pe(E),s=m.resolve(e.outdir,J);if(!G(s,e.outdir))return Z(t);if(await be(s,e.outdir))return w(t,e,{slug:a},404);let l,D;if(P){const n=await j(i,e,u);if(l=n.route,D=n.content,l&&!V(l,{isAuthenticated:d,email:A,teams:C},e.config.access?.rbac,e.config.access?.requiresLogin))return W(t,e,i)}const I=e.getGlobalConfig("access");if(!Le(E,I?.rbac||{},I?.requiresLogin||!1,e.getGlobalConfig("directoryPaths"),{isAuthenticated:d,email:A,teams:C}))return Z(t);const L=Te[m.extname(s)]||"text/plain",_=s.match(/assets\/.*\.[a-f0-9]{8,}\..+/)||s.match(/runtime\/chunks\/.*/)?re:_e[L],S=_?{"Cache-Control":`public, max-age=${_}, immutable`,Expires:new Date(Date.now()+_*1e3).toUTCString()}:{"Cache-Control":k},F=r.query("download")!=null,H=M?.content??D;if(H!==void 0)return t.newResponse(H,200,{"Content-Type":x(L),"Access-Control-Allow-Origin":"*",...S,...l&&{"X-Robots-Tag":"noindex, follow",...e.config.seo?.siteUrl&&{Link:`<${$(e.config.seo.siteUrl,T(l.slug))}>; rel="canonical"`}},...F&&{"Content-Disposition":`attachment; filename="${m.basename(E)}"`}});if(await we(s)){const n=await h(s);return t.newResponse(n,200,{"Content-Type":x(L),"Access-Control-Allow-Origin":"*",...S,...l&&{"X-Robots-Tag":"noindex, follow",...e.config.seo?.siteUrl&&{Link:`<${$(e.config.seo.siteUrl,T(l.slug))}>; rel="canonical"`}},...F&&{"Content-Disposition":`attachment; filename="${m.basename(s)}"`}})}else return w(t,e,{slug:a},404)}}export{at as dynamicRouteHandler};
@@ -1 +1 @@
1
- import{serveStatic as c}from"hono/serve-static";import{withPathPrefix as i,withoutPathPrefix as H}from"@redocly/theme/core/utils";import{ServerRoutes as l}from"../../../constants/api.js";import{LLMS_TXT_FILE_NAME as L,PUBLIC_STATIC_FOLDER as C}from"../../constants/common.js";import{envConfig as I}from"../../config/env-config.js";import{authMiddleware as g}from"../middleware/authMiddleware.js";import{ensureSearchData as h}from"../middleware/ensureSearchData.js";import{dynamicMiddleware as D}from"../middleware/dynamic-middleware/dynamic-middleware.js";import{installRoutes as P}from"../../plugins/dev-onboarding/api/routes/index.js";import{installSkillsRoutes as N}from"./skills/skills-routes.js";import{llmsTxtHandler as M}from"./llms-txt.js";import{authorizeHandler as w,oidcCallbackHandler as B,logoutHandler as E,postLogoutHandler as v,idpLoginHandler as F,redoclyLoginCallbackHandler as G,samlCallbackHandler as U,redoclyTokenLoginHandler as Y,inviteHandler as y}from"./auth.js";import{appDataHandler as K}from"./app-data.js";import{searchFacetsHandler as k,searchHandler as x}from"./search.js";import{dynamicRouteHandler as b}from"./dynamic-route.js";import{pageDataHandler as V,sharedPageDataHandler as q}from"./page-data.js";import{pathPrefixRedirectHandler as X}from"./path-prefix-redirect.js";import{getRoutesByLineHandler as _,resolvePathHandler as S,resolvePathsHandler as $,resolveSlugHandler as f}from"./resolve-route.js";import{feedbackHandler as j}from"./feedback.js";import{loggerMiddleware as z}from"../middleware/loggerMiddleware.js";import{responseHeadersMiddleware as W}from"../middleware/responseHeadersMiddleware.js";import{idleTimeoutMiddleware as Z}from"../middleware/idleTimeoutMiddleware.js";import{otelTracesHandler as u}from"./otel/otel.js";import{healthCheckHandler as J}from"./health.js";import{askAiHandler as Q,aiAssistantEscalationsHandler as aa,aiAssistantFeedbackHandler as ea,AI_ASSISTANT_IDENTITY_STATUS_HEADER as ia}from"./ask-ai.js";import{semanticSearchHandler as la}from"./semantic-search.js";import{replayOauth2RedirectCallbackHandler as ta}from"./replay-oauth2-redirect.js";import{corsProxyHandler as r}from"./cors-proxy.js";import{corsMiddleware as t}from"../middleware/corsMiddleware.js";import{installApiRoutes as ma}from"./api-routes/api-routes.js";import{cookieMiddleware as oa}from"../middleware/cookieMiddleware.js";import{staticContentHandler as da}from"../routes/static-content.js";import{infoHandler as O}from"./info.js";import{catalogHandler as Aa}from"./catalog/catalog.js";import{catalogRelationsHandler as na}from"./catalog/catalog-relations.js";import{bffCatalogHandler as sa}from"./catalog/bff-catalog.js";import{bffCatalogRevisionsHandler as pa}from"./catalog/bff-catalog-revisions.js";import{bffCatalogRelatedEntitiesHandler as Ta}from"./catalog/bff-catalog-related-entities.js";import{catalogAuthMiddleware as m}from"../middleware/catalogAuthMiddleware.js";import{telemetryMiddleware as Ea}from"../middleware/telemetry-middleware.js";import{errorHandler as _a}from"./error.js";import{installMcpRoutes as Sa}from"./mcp-routes/mcp-routes.js";import{jsonValidatorMiddleware as n}from"../middleware/jsonValidatorMiddleware.js";import{otelTracesRequestSchema as fa}from"../../../api-schemas/otel-traces.js";import{searchFacetsRequestSchema as ra}from"../../../api-schemas/search-facets.js";import{searchRequestSchema as Oa}from"../../../api-schemas/search.js";function Te(a,e,o){const{resolveRouteData:s,readStaticAsset:p,llmsTxtGenerator:d}=o;a.use("*",Z()),a.use("*",oa()),a.use("*",D(e)),a.use("*",g(e)),a.use("*",z()),a.use("*",W(e)),a.use("*",Ea()),a.use(i("*"),c({root:`./${C}`,getContent:(A,R)=>da(A,R,e,p),rewriteRequestPath:A=>H(A)})),a.use(i(l.FEEDBACK),t({allowMethods:["POST"]})),a.use(i(l.ASK_AI),t({allowMethods:["POST"],exposeHeaders:[ia]})),a.use(i(l.AI_ASSISTANT_ESCALATIONS),t({allowMethods:["POST"]})),a.use(i(l.AI_ASSISTANT_FEEDBACK),t({allowMethods:["POST"]})),a.use(i(l.SEMANTIC_SEARCH),t({allowMethods:["POST"]})),a.use("*",Ra(e));const T=h(e);a.use(i(l.INFO),O()),I.NEW_CATALOG_ENABLED&&(a.use(i(l.CATALOG_ENTITIES),m({serverOutDir:e.serverOutDir}),Aa(e)),a.use(i(l.CATALOG_ENTITIES_RELATIONS),m({serverOutDir:e.serverOutDir}),na(e)),a.get(i(l.BFF_CATALOG_ENTITIES),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),sa(e)),a.get(i(l.BFF_CATALOG_RELATED_ENTITIES),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),Ta(e)),a.get(i(l.BFF_CATALOG_REVISIONS),m({serverOutDir:e.serverOutDir,protectReadMethods:!1}),pa(e))),a.get(i(l.SHARED_PAGE_DATA),q(e)),a.get(i(l.PAGE_DATA),V(e,s)),a.get(i(l.APP_DATA),K(e)),a.post(i(l.SEARCH),T,n(Oa),x(e)),a.post(i(l.SEARCH_FACETS),T,n(ra),k(e)),a.post(i(l.AUTHORIZATION),w),a.post(i(l.LOGOUT),E(e)),a.get(i(l.LOGOUT),E(e)),a.get(i(l.POST_LOGOUT),v(e)),a.get(i(l.OIDC_CALLBACK),B(e)),a.get(i(l.REDOCLY_TOKEN_LOGIN),Y(e)),a.get(i(l.REDOCLY_LOGIN_CALLBACK),G()),a.get(i(l.IDP_LOGIN),F(e)),a.post(i(l.SAML_CALLBACK),U(e)),a.get(i(l.INVITE),y(e)),a.get(i(l.HEALTH),J),Sa(a,e),P(a,e),N(a,e),a.all(i(l.CORS_PROXY),r(e)),a.all(i(`${l.CORS_PROXY}/*`),r(e)),ma(a,e),a.post(i(l.FEEDBACK),j(e)),a.post(i(l.RESOLVE_ROUTE_BY_PATH),S(e)),a.post(i(l.RESOLVE_ROUTES_BY_PATHS),$(e)),a.post(i(l.RESOLVE_ROUTE_BY_SLUG),f(e)),a.post(i(l.ASK_AI),Q(e)),a.post(i(l.AI_ASSISTANT_ESCALATIONS),aa(e)),a.post(i(l.AI_ASSISTANT_FEEDBACK),ea(e)),a.post(i(l.SEMANTIC_SEARCH),la(e)),a.get(i(l.GET_ROUTES_BY_LINE),_(e)),a.post(i(l.OTEL_TRACES),n(fa),u),a.get(i(l.REPLAY_OAUTH2_CALLBACK),ta),d&&a.get(i(`/${L}`),M(d)),a.all(i("/*"),b(e,s,p,d)),a.get("*",X),a.onError(_a)}function Ra(a){return async(e,o)=>{await a.waitForPluginsLifecycle(),await o()}}function Ee(a,e){a.get(i(l.INFO),O()),a.post(i(l.RESOLVE_ROUTE_BY_PATH),S(e)),a.post(i(l.RESOLVE_ROUTE_BY_SLUG),f(e)),a.get(i(l.GET_ROUTES_BY_LINE),_(e))}export{Ee as installDevRoutes,Te as installProdRoutes,Ra as waitForPluginsLifecycle};
1
+ import{serveStatic as c}from"hono/serve-static";import{withPathPrefix as i,withoutPathPrefix as H}from"@redocly/theme/core/utils";import{ServerRoutes as l}from"../../../constants/api.js";import{LLMS_TXT_FILE_NAME as L,PUBLIC_STATIC_FOLDER as C}from"../../constants/common.js";import{envConfig as I}from"../../config/env-config.js";import{authMiddleware as g}from"../middleware/authMiddleware.js";import{ensureSearchData as h}from"../middleware/ensureSearchData.js";import{dynamicMiddleware as D}from"../middleware/dynamic-middleware/dynamic-middleware.js";import{installRoutes as P}from"../../plugins/dev-onboarding/api/routes/index.js";import{installSkillsRoutes as N}from"./skills/skills-routes.js";import{llmsTxtHandler as M}from"./llms-txt.js";import{authorizeHandler as w,oidcCallbackHandler as B,logoutHandler as E,postLogoutHandler as v,idpLoginHandler as F,redoclyLoginCallbackHandler as G,samlCallbackHandler as U,redoclyTokenLoginHandler as Y,inviteHandler as y}from"./auth.js";import{appDataHandler as K}from"./app-data.js";import{searchFacetsHandler as k,searchHandler as x}from"./search.js";import{dynamicRouteHandler as b}from"./dynamic-route.js";import{pageDataHandler as V,sharedPageDataHandler as q}from"./page-data.js";import{pathPrefixRedirectHandler as X}from"./path-prefix-redirect.js";import{getRoutesByLineHandler as _,resolvePathHandler as S,resolvePathsHandler as $,resolveSlugHandler as f}from"./resolve-route.js";import{feedbackHandler as j}from"./feedback.js";import{loggerMiddleware as z}from"../middleware/loggerMiddleware.js";import{responseHeadersMiddleware as W}from"../middleware/responseHeadersMiddleware.js";import{idleTimeoutMiddleware as Z}from"../middleware/idleTimeoutMiddleware.js";import{otelTracesHandler as u}from"./otel/otel.js";import{healthCheckHandler as J}from"./health.js";import{askAiHandler as Q,aiAssistantEscalationsHandler as aa,aiAssistantFeedbackHandler as ea,AI_ASSISTANT_IDENTITY_STATUS_HEADER as ia}from"./ask-ai.js";import{semanticSearchHandler as la}from"./semantic-search.js";import{replayOauth2RedirectCallbackHandler as ta}from"./replay-oauth2-redirect.js";import{corsProxyHandler as r}from"./cors-proxy.js";import{corsMiddleware as m}from"../middleware/corsMiddleware.js";import{installApiRoutes as ma}from"./api-routes/api-routes.js";import{cookieMiddleware as oa}from"../middleware/cookieMiddleware.js";import{staticContentHandler as da}from"../routes/static-content.js";import{infoHandler as O}from"./info.js";import{catalogHandler as Aa}from"./catalog/catalog.js";import{catalogRelationsHandler as na}from"./catalog/catalog-relations.js";import{bffCatalogHandler as sa}from"./catalog/bff-catalog.js";import{bffCatalogRevisionsHandler as pa}from"./catalog/bff-catalog-revisions.js";import{bffCatalogRelatedEntitiesHandler as Ta}from"./catalog/bff-catalog-related-entities.js";import{catalogAuthMiddleware as o}from"../middleware/catalogAuthMiddleware.js";import{telemetryMiddleware as Ea}from"../middleware/telemetry-middleware.js";import{errorHandler as _a}from"./error.js";import{installMcpRoutes as Sa}from"./mcp-routes/mcp-routes.js";import{jsonValidatorMiddleware as n}from"../middleware/jsonValidatorMiddleware.js";import{otelTracesRequestSchema as fa}from"../../../api-schemas/otel-traces.js";import{searchFacetsRequestSchema as ra}from"../../../api-schemas/search-facets.js";import{searchRequestSchema as Oa}from"../../../api-schemas/search.js";function Te(a,e,d){const{resolveRouteData:s,readStaticAsset:p,llmsTxtGenerator:A}=d;a.use("*",Z()),a.use("*",oa()),a.use("*",D(e)),a.use("*",g(e)),a.use("*",z()),a.use("*",W(e)),a.use("*",Ea()),a.use(i("*"),c({root:`./${C}`,getContent:(t,R)=>da(t,R,e,p),rewriteRequestPath:t=>H(t)})),a.use(i(l.FEEDBACK),m({allowMethods:["POST"]})),a.use(i(l.ASK_AI),m({allowMethods:["POST"],exposeHeaders:[ia]})),a.use(i(l.AI_ASSISTANT_ESCALATIONS),m({allowMethods:["POST"]})),a.use(i(l.AI_ASSISTANT_FEEDBACK),m({allowMethods:["POST"]})),a.use(i(l.SEMANTIC_SEARCH),m({allowMethods:["POST"]})),a.use("*",Ra(e));const T=h(e);a.use(i(l.INFO),O()),I.NEW_CATALOG_ENABLED&&(a.use(i(l.CATALOG_ENTITIES),o({serverOutDir:e.serverOutDir}),Aa(e)),a.use(i(l.CATALOG_ENTITIES_RELATIONS),o({serverOutDir:e.serverOutDir}),na(e)),a.get(i(l.BFF_CATALOG_ENTITIES),o({serverOutDir:e.serverOutDir,protectReadMethods:!1}),sa(e)),a.get(i(l.BFF_CATALOG_RELATED_ENTITIES),o({serverOutDir:e.serverOutDir,protectReadMethods:!1}),Ta(e)),a.get(i(l.BFF_CATALOG_REVISIONS),o({serverOutDir:e.serverOutDir,protectReadMethods:!1}),pa(e))),a.get(i(l.SHARED_PAGE_DATA),q(e)),a.get(i(l.PAGE_DATA),V(e,s)),a.get(i(l.APP_DATA),K(e)),a.post(i(l.SEARCH),T,n(Oa),x(e)),a.post(i(l.SEARCH_FACETS),T,n(ra),k(e)),a.post(i(l.AUTHORIZATION),t=>w(t,e)),a.post(i(l.LOGOUT),E(e)),a.get(i(l.LOGOUT),E(e)),a.get(i(l.POST_LOGOUT),v(e)),a.get(i(l.OIDC_CALLBACK),B(e)),a.get(i(l.REDOCLY_TOKEN_LOGIN),Y(e)),a.get(i(l.REDOCLY_LOGIN_CALLBACK),G()),a.get(i(l.IDP_LOGIN),F(e)),a.post(i(l.SAML_CALLBACK),U(e)),a.get(i(l.INVITE),y(e)),a.get(i(l.HEALTH),J),Sa(a,e),P(a,e),N(a,e),a.all(i(l.CORS_PROXY),r(e)),a.all(i(`${l.CORS_PROXY}/*`),r(e)),ma(a,e),a.post(i(l.FEEDBACK),j(e)),a.post(i(l.RESOLVE_ROUTE_BY_PATH),S(e)),a.post(i(l.RESOLVE_ROUTES_BY_PATHS),$(e)),a.post(i(l.RESOLVE_ROUTE_BY_SLUG),f(e)),a.post(i(l.ASK_AI),Q(e)),a.post(i(l.AI_ASSISTANT_ESCALATIONS),aa(e)),a.post(i(l.AI_ASSISTANT_FEEDBACK),ea(e)),a.post(i(l.SEMANTIC_SEARCH),la(e)),a.get(i(l.GET_ROUTES_BY_LINE),_(e)),a.post(i(l.OTEL_TRACES),n(fa),u),a.get(i(l.REPLAY_OAUTH2_CALLBACK),ta),A&&a.get(i(`/${L}`),M(A)),a.all(i("/*"),b(e,s,p,A)),a.get("*",X),a.onError(_a)}function Ra(a){return async(e,d)=>{await a.waitForPluginsLifecycle(),await d()}}function Ee(a,e){a.get(i(l.INFO),O()),a.post(i(l.RESOLVE_ROUTE_BY_PATH),S(e)),a.post(i(l.RESOLVE_ROUTE_BY_SLUG),f(e)),a.get(i(l.GET_ROUTES_BY_LINE),_(e))}export{Ee as installDevRoutes,Te as installProdRoutes,Ra as waitForPluginsLifecycle};
@@ -1 +1 @@
1
- import{getCookie as A}from"hono/cookie";import{createHash as J,timingSafeEqual as K}from"node:crypto";import{ulid as V}from"ulid";import{isRedoclySso as Y,isOidcProviderConfig as P,createMcpAuthorizationCode as Z,verifyMcpAuthorizationCode as W,createMcpRefreshToken as $,verifyMcpRefreshToken as x,createMcpSessionResource as y,oidcJwksCache as j,oidcRefreshTokenGrant as Q,getOidcMetadata as X,discoverJwks as ee}from"../../auth.js";import{isMcpContentProtected as v}from"../../../plugins/mcp/auth/auth-handlers.js";import{SUPPORTED_MCP_SCOPES as M}from"../../../plugins/mcp/constants.js";import{AUTH_URL as re,JWT_SECRET_KEY as m,MCP_ACCESS_TOKEN_TTL_SEC as z,MCP_REFRESH_SESSION_LIFETIME_SEC as te}from"../../../constants/common.js";import{ServerRoutes as T}from"../../../../constants/api.js";import{AuthCookieNames as R}from"../../../../constants/common.js";import{withPathPrefix as I}from"@redocly/theme/core/utils";import{telemetry as w}from"../../../telemetry/index.js";import{envConfig as U}from"../../../config/env-config.js";import*as S from"../../jwt/jwt.js";import{AlgorithmTypes as D,JwtAlgorithmNotImplemented as ne,JwtTokenExpired as oe,JwtTokenInvalid as ie,JwtTokenNotBefore as se,JwtTokenSignatureMismatched as ce}from"../../jwt/types.js";import{getRequestOrigin as q}from"../../utils/get-request-origin.js";import{encryptClaimValue as L}from"../../../utils/crypto/encrypt-claim-value.js";import{decryptAesGcm as de}from"../../../utils/crypto/decrypt-aes-gcm.js";import{encryptAesGcm as F}from"../../../utils/crypto/encrypt-aes-gcm.js";import{fetchClientMetadataDocument as ae,isClientMetadataDocumentUrl as N,isRedirectUriRegistered as le}from"./mcp-client-metadata.js";const s=(r,e,t=200,o)=>r.json(e,t,{"Content-Type":"application/json",...o??{}}),H=new Set(["S256"]),b=["authorization_code","refresh_token"],pe="https://claude.ai/oauth/claude-code-client-metadata";function _e(r,e,t){if(!H.has(e||""))return!1;const o=J("sha256").update(t).digest("base64url"),c=Buffer.from(o),i=Buffer.from(r);return c.length!==i.length?!1:K(c,i)}async function ue(r){const e=Math.floor(Date.now()/1e3);return S.sign({type:"mcp_context",...r,iat:e,exp:e+600},m,D.HS256)}async function fe(r){await S.verify(r,m,D.HS256);const{payload:e}=S.decode(r);if(e.type!=="mcp_context")throw new Error("Invalid context token type");return e}function Ne(r){return async e=>{if(!v(r))return s(e,{error:"not_found"},404);if(e.req.method!=="GET")return s(e,{error:"Method not allowed"},405,{Allow:"GET"});const t=q(e);return s(e,{resource:`${t}${I("/mcp")}`,authorization_servers:[t],bearer_methods_supported:["header"],resource_documentation:`${t}${T.MCP_OAUTH_AUTHORIZATION_SERVER}`,scopes_supported:[...M],bearer_token_types_supported:["Bearer"]},200,{"Access-Control-Allow-Origin":"*"})}}function He(r){return async e=>{if(!v(r))return s(e,{error:"not_found"},404);const t=q(e);return s(e,{issuer:t,authorization_endpoint:`${t}${I(T.MCP_AUTHORIZATION)}`,token_endpoint:`${t}${I(T.MCP_TOKEN_PORTAL)}`,scopes_supported:[...M],registration_endpoint:`${t}${I(T.MCP_DYNAMIC_CLIENT_REGISTRATION)}`,response_types_supported:["code"],grant_types_supported:[...b],subject_types_supported:["public"],id_token_signing_alg_values_supported:["RS256"],code_challenge_methods_supported:["S256"],token_endpoint_auth_methods_supported:["none"],client_id_metadata_document_supported:!0},200,{"Access-Control-Allow-Origin":"*"})}}function be(r){return async e=>{if(!v(r))return s(e,{error:"not_found"},404);if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{return s(e,{client_id:U.OAUTH_CLIENT_ID||"",client_name:"MCP Client",redirect_uris:[],grant_types:[...b],response_types:["code"],scope:M.join(" "),subject_type:"public",token_endpoint_auth_method:"none",created_at:new Date().toISOString(),updated_at:new Date().toISOString()},201,{"Access-Control-Allow-Origin":"*"})}catch(t){return s(e,{error:"invalid_request",error_description:t?.message||"Unable to register client"},500,{"Access-Control-Allow-Origin":"*"})}}}function G(r){const e=r||{},t=Object.keys(e),o=t.find(i=>P(e[i])&&Y(e[i])),c=t.find(i=>P(e[i]));return o||c||t[0]}function $e(r){return async e=>{if(!v(r))return s(e,{error:"not_found"},404);const t=new URL(e.req.url),{searchParams:o}=t,c=o.get("redirect_uri"),i=o.get("client_id"),a=o.get("code_challenge"),n=o.get("code_challenge_method"),l=V();if(a&&!H.has(n||""))return s(e,{error:"invalid_request",error_description:`Unsupported code_challenge_method. Supported: ${[...H].join(", ")}`},400);if(N(i)&&!a)return s(e,{error:"invalid_request",error_description:"code_challenge is required for client_id_metadata_document clients"},400);if(N(i)&&c)try{const d=await ae(i),p=i===pe;if(!le(c,d.redirect_uris,{ignorePort:p}))return s(e,{error:"invalid_request",error_description:"redirect_uri is not registered for this client"},400)}catch(d){const p=d instanceof Error?d.message:String(d);return s(e,{error:"invalid_client",error_description:`Unable to resolve client metadata document: ${p}`},400)}w.sendMcpAuthorizationStartedMessage([{...y(l),redirect_uri:c||null}]);const f=q(e),_={isMcpFlow:!0,originalRedirectUri:c,mcpClientId:i,mcpState:o.get("state"),mcpSessionId:l,mcpCodeChallenge:a,mcpCodeChallengeMethod:a?n:null,timestamp:Date.now()};try{const d=await ue(_),p=new URL(I(T.IDP_LOGIN),f);p.searchParams.set("redirectTo",`${T.MCP_CALLBACK}/${d}`);const k=G(r.getConfig().ssoDirect);return k?(p.searchParams.set("idpId",k),e.redirect(p.toString())):s(e,{error:"not_found"},404)}catch(d){const p=d instanceof Error?d.message:String(d),k=d instanceof Error?d.stack:String(d);w.sendMcpAuthorizationFailedMessage([{...y(l),error:p,error_details:k}]);const C=new URL(I(`${re}/oauth2/auth`));return C.search=o.toString(),e.redirect(C.toString())}}}async function B(r){const e=Math.floor(Date.now()/1e3),t=await S.sign({...r.claims,...r.idpId?{idpId:r.idpId}:{},...r.idpAccessTokenEnc?{idp_access_token:r.idpAccessTokenEnc}:{},...r.ttlSec?{iat:e,exp:e+r.ttlSec}:{}},m,D.HS256,r.kid),o=r.ttlSec??(typeof r.claims.exp=="number"?Math.max(r.claims.exp-e,0):z);return{accessToken:t,expiresIn:o}}async function he(r,e,t){const o=g=>s(r,{error:"invalid_grant",error_description:g},400),c=r.get("logger"),i=t.get("refresh_token"),a=t.get("client_id")||void 0;if(!i)return s(r,{error:"invalid_request",error_description:"refresh_token is required"},400);let n;try{n=await x(i)}catch(g){return c?.warn("MCP refresh token rejected: %s",g instanceof Error?g.message:String(g)),o("Invalid refresh token")}if(a&&a!==n.client_id)return o("Client mismatch");const l=n.idp_id?e.getConfig().ssoDirect?.[n.idp_id]:void 0;let f,_,d=n.idp_access_token_enc,p=n.idp_refresh_token_enc;if(n.idp_refresh_token_enc){let g;try{g=de(n.idp_refresh_token_enc,m)}catch{return o("Invalid refresh token")}if(!n.idp_id||!P(l))return o("Unknown identity provider");let u;try{const h=await X(n.idp_id,l);if(!h?.token_endpoint)return o("Identity provider has no token endpoint");u=await Q(h.token_endpoint,g,l)}catch(h){return c?.warn("MCP refresh failed to reach the IdP token endpoint: %s",h instanceof Error?h.message:String(h)),s(r,{error:"server_error",error_description:"Failed to refresh session"},500)}if(u.error||!(u.id_token||u.access_token))return c?.warn("MCP refresh rejected by IdP: %s",u.error||"empty token response"),o("Refresh rejected by identity provider");if(u.refresh_token&&(p=F(u.refresh_token,m)),u.access_token&&(d=L(u.access_token,m)),u.id_token)try{const h=S.decode(u.id_token);f=h.payload,_=h.header.kid}catch{return o("Invalid token response from identity provider")}}const k=P(l)&&l.tokenExpirationTime||z,C=f??n.claims,E=await B({claims:C,kid:_,idpId:n.idp_id,idpAccessTokenEnc:d,ttlSec:f?void 0:k}),O=await $({clientId:n.client_id,idpId:n.idp_id,claims:C,idpAccessTokenEnc:d,idpRefreshTokenEnc:p,sessionExp:n.session_exp});return s(r,{access_token:E.accessToken,token_type:"Bearer",expires_in:E.expiresIn,refresh_token:O,scope:M.join(" ")},200,{"Cache-Control":"no-store",Pragma:"no-cache"})}function je(r){return async e=>{if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{const t=await e.req.formData(),o=t.get("grant_type"),c=t.get("code"),i=t.get("redirect_uri")||void 0,a=t.get("code_verifier")||void 0;if(!b.includes(o))return s(e,{error:"invalid_request",error_description:"Invalid grant type"},400);if(o==="refresh_token")return await he(e,r,t);if(!c)return s(e,{error:"invalid_request",error_description:"Missing authorization code"},400);try{const n=await W(c);if(i&&i!==n.redirect_uri)return s(e,{error:"invalid_grant",error_description:"redirect_uri mismatch"},400);if(n.code_challenge){if(!a)return s(e,{error:"invalid_grant",error_description:"code_verifier required"},400);if(!_e(n.code_challenge,n.code_challenge_method,a))return s(e,{error:"invalid_grant",error_description:"code_verifier mismatch"},400)}if(U.OAUTH_CLIENT_ID&&n.client_id&&!N(n.client_id)&&n.client_id!==U.OAUTH_CLIENT_ID)return s(e,{error:"invalid_client",error_description:"Client mismatch"},400);const l=n.id_token;if(typeof l!="string"||l.length===0)return s(e,{error:"invalid_grant",error_description:"Missing id_token in authorization code"},400);const f=n.idp_access_token_enc??(n.idp_access_token?L(n.idp_access_token,m):void 0),_=S.decode(l),{accessToken:d,expiresIn:p}=await B({claims:_.payload,kid:_.header.kid,idpId:n.idp_id,idpAccessTokenEnc:f}),C=Math.floor(Date.now()/1e3)+te,E=await $({clientId:n.client_id,idpId:n.idp_id,claims:_.payload,idpAccessTokenEnc:f,idpRefreshTokenEnc:n.idp_refresh_token_enc,sessionExp:C}),O={access_token:d,token_type:"Bearer",expires_in:p,refresh_token:E,scope:M.join(" "),id_token:l};return s(e,O,200,{"Cache-Control":"no-store",Pragma:"no-cache"})}catch(n){return e.get("logger")?.warn("MCP token exchange failed: %s",n instanceof Error?n.message:String(n)),s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400)}}catch(t){const o=t instanceof Error?t.message:String(t);return s(e,{error:"server_error",error_description:"Failed to process token request",error_details:o},500)}}}async function me(r,e){try{const{payload:t,header:o}=S.decode(r);if(typeof t.idpId=="string"&&t.idpId)return t.idpId;if(o.kid){j.jwks[o.kid]==null&&await ee(e||{});const c=j.jwks[o.kid];if(c?.idpId)return c.idpId}}catch{}return G(e)}function ze(r){return async e=>{const t=new URL(e.req.url);let o=t.searchParams.get("context");if(!o&&t.pathname.startsWith(I(`${T.MCP_CALLBACK}/`))){const i=t.pathname.split("/");o=i[i.length-1]}if(!o)return e.text("Missing context parameter",400);let c=null;try{const i=await fe(o);if(c=i.mcpSessionId||null,!i.isMcpFlow||!i.originalRedirectUri)throw new Error("Invalid MCP context");const a=A(e,R.IDP_ID_TOKEN)||A(e,R.AUTHORIZATION),n=A(e,R.IDP_ACCESS_TOKEN),l=A(e,R.IDP_REFRESH_TOKEN);if(!a)return w.sendMcpAuthorizationFailedMessage([{...y(c),error:"Authentication required",error_details:null}]),e.text("Authentication required",401);const f=await Z({idToken:a,idpId:await me(a,r.getConfig().ssoDirect),idpAccessTokenEnc:n?L(n,m):void 0,idpRefreshTokenEnc:l?F(l,m):void 0,clientId:i.mcpClientId||"",redirectUri:i.originalRedirectUri,codeChallenge:i.mcpCodeChallenge||void 0,codeChallengeMethod:i.mcpCodeChallengeMethod||void 0,ttlSec:600}),_=new URL(i.originalRedirectUri);return _.searchParams.set("code",f),i.mcpState&&_.searchParams.set("state",i.mcpState),w.sendMcpAuthorizationCompletedMessage([{...y(c),redirect_uri:i.originalRedirectUri||null}]),e.redirect(_.toString())}catch(i){const a=ge(i);return w.sendMcpAuthorizationFailedMessage([{...y(c),...a}]),e.text(`Invalid MCP callback: ${a.error}`,400)}}}function ge(r){return r instanceof Error&&[ne,oe,ie,se,ce].some(e=>r instanceof e)?{error:r.name,error_details:null}:r instanceof Error?{error:r.message,error_details:r.stack??null}:{error:String(r),error_details:null}}export{b as SUPPORTED_MCP_GRANT_TYPES,ue as createMcpContextToken,$e as mcpAuthorizationHandler,ze as mcpCallbackHandler,be as mcpDynamicClientRegistrationHandler,He as mcpOAuthAuthorizationServerHandler,Ne as mcpOAuthProtectedResourceHandler,je as mcpTokenPortalHandler,G as resolveMcpIdpId,fe as verifyAndParseMcpContextToken,_e as verifyPkce};
1
+ import{getCookie as U}from"hono/cookie";import{createHash as V,timingSafeEqual as Z}from"node:crypto";import{ulid as Y}from"ulid";import{isRedoclySso as W,isOidcProviderConfig as w,createMcpAuthorizationCode as X,verifyMcpAuthorizationCode as Q,createMcpRefreshToken as $,verifyMcpRefreshToken as x,createMcpSessionResource as S,oidcRefreshTokenGrant as ee,getOidcMetadata as re,getUserParamsFromCookies as te,idTokenIsForThisProject as ne}from"../../auth.js";import{isMcpContentProtected as P}from"../../../plugins/mcp/auth/auth-handlers.js";import{SUPPORTED_MCP_SCOPES as y}from"../../../plugins/mcp/constants.js";import{AUTH_URL as oe,JWT_SECRET_KEY as m,MCP_ACCESS_TOKEN_TTL_SEC as z,MCP_REFRESH_SESSION_LIFETIME_SEC as ie}from"../../../constants/common.js";import{ServerRoutes as T}from"../../../../constants/api.js";import{AuthCookieNames as v}from"../../../../constants/common.js";import{withPathPrefix as I}from"@redocly/theme/core/utils";import{telemetry as A}from"../../../telemetry/index.js";import{envConfig as D}from"../../../config/env-config.js";import*as M from"../../jwt/jwt.js";import{InternalTokenTypes as F,McpAccessTokenMarker as R}from"../../jwt/token-types.js";import{AlgorithmTypes as L,JwtAlgorithmNotImplemented as se,JwtTokenExpired as ce,JwtTokenInvalid as de,JwtTokenNotBefore as ae,JwtTokenSignatureMismatched as le}from"../../jwt/types.js";import{getRequestOrigin as N}from"../../utils/get-request-origin.js";import{encryptClaimValue as q}from"../../../utils/crypto/encrypt-claim-value.js";import{decryptAesGcm as pe}from"../../../utils/crypto/decrypt-aes-gcm.js";import{encryptAesGcm as G}from"../../../utils/crypto/encrypt-aes-gcm.js";import{fetchClientMetadataDocument as _e,isClientMetadataDocumentUrl as H,isRedirectUriRegistered as ue}from"./mcp-client-metadata.js";const s=(r,e,n=200,o)=>r.json(e,n,{"Content-Type":"application/json",...o??{}}),b=new Set(["S256"]),j=["authorization_code","refresh_token"],fe="https://claude.ai/oauth/claude-code-client-metadata";function he(r,e,n){if(!b.has(e||""))return!1;const o=V("sha256").update(n).digest("base64url"),c=Buffer.from(o),i=Buffer.from(r);return c.length!==i.length?!1:Z(c,i)}async function me(r){const e=Math.floor(Date.now()/1e3);return M.sign({type:F.MCP_CONTEXT,...r,iat:e,exp:e+600},m,L.HS256)}async function ge(r){await M.verify(r,m,L.HS256);const{payload:e}=M.decode(r);if(e.type!==F.MCP_CONTEXT)throw new Error("Invalid context token type");return e}function je(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);if(e.req.method!=="GET")return s(e,{error:"Method not allowed"},405,{Allow:"GET"});const n=N(e);return s(e,{resource:`${n}${I("/mcp")}`,authorization_servers:[n],bearer_methods_supported:["header"],resource_documentation:`${n}${T.MCP_OAUTH_AUTHORIZATION_SERVER}`,scopes_supported:[...y],bearer_token_types_supported:["Bearer"]},200,{"Access-Control-Allow-Origin":"*"})}}function $e(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);const n=N(e);return s(e,{issuer:n,authorization_endpoint:`${n}${I(T.MCP_AUTHORIZATION)}`,token_endpoint:`${n}${I(T.MCP_TOKEN_PORTAL)}`,scopes_supported:[...y],registration_endpoint:`${n}${I(T.MCP_DYNAMIC_CLIENT_REGISTRATION)}`,response_types_supported:["code"],grant_types_supported:[...j],subject_types_supported:["public"],id_token_signing_alg_values_supported:["RS256"],code_challenge_methods_supported:["S256"],token_endpoint_auth_methods_supported:["none"],client_id_metadata_document_supported:!0},200,{"Access-Control-Allow-Origin":"*"})}}function ze(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{return s(e,{client_id:D.OAUTH_CLIENT_ID||"",client_name:"MCP Client",redirect_uris:[],grant_types:[...j],response_types:["code"],scope:y.join(" "),subject_type:"public",token_endpoint_auth_method:"none",created_at:new Date().toISOString(),updated_at:new Date().toISOString()},201,{"Access-Control-Allow-Origin":"*"})}catch(n){return s(e,{error:"invalid_request",error_description:n?.message||"Unable to register client"},500,{"Access-Control-Allow-Origin":"*"})}}}function B(r){const e=r||{},n=Object.keys(e),o=n.find(i=>w(e[i])&&W(e[i])),c=n.find(i=>w(e[i]));return o||c||n[0]}function Fe(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);const n=new URL(e.req.url),{searchParams:o}=n,c=o.get("redirect_uri"),i=o.get("client_id"),p=o.get("code_challenge"),t=o.get("code_challenge_method"),l=Y();if(p&&!b.has(t||""))return s(e,{error:"invalid_request",error_description:`Unsupported code_challenge_method. Supported: ${[...b].join(", ")}`},400);if(H(i)&&!p)return s(e,{error:"invalid_request",error_description:"code_challenge is required for client_id_metadata_document clients"},400);if(H(i)&&c)try{const d=await _e(i),a=i===fe;if(!ue(c,d.redirect_uris,{ignorePort:a}))return s(e,{error:"invalid_request",error_description:"redirect_uri is not registered for this client"},400)}catch(d){const a=d instanceof Error?d.message:String(d);return s(e,{error:"invalid_client",error_description:`Unable to resolve client metadata document: ${a}`},400)}A.sendMcpAuthorizationStartedMessage([{...S(l),redirect_uri:c||null}]);const _=N(e),f={isMcpFlow:!0,originalRedirectUri:c,mcpClientId:i,mcpState:o.get("state"),mcpSessionId:l,mcpCodeChallenge:p,mcpCodeChallengeMethod:p?t:null,timestamp:Date.now()};try{const d=await me(f),a=new URL(I(T.IDP_LOGIN),_);a.searchParams.set("redirectTo",`${T.MCP_CALLBACK}/${d}`);const C=B(r.getConfig().ssoDirect);return C?(a.searchParams.set("idpId",C),e.redirect(a.toString())):s(e,{error:"not_found"},404)}catch(d){const a=d instanceof Error?d.message:String(d),C=d instanceof Error?d.stack:String(d);A.sendMcpAuthorizationFailedMessage([{...S(l),error:a,error_details:C}]);const g=new URL(I(`${oe}/oauth2/auth`));return g.search=o.toString(),e.redirect(g.toString())}}}async function K(r){const e=Math.floor(Date.now()/1e3),n=await M.sign({...r.claims,[R.CLAIM]:R.VALUE,...r.idpId?{idpId:r.idpId}:{},...r.idpAccessTokenEnc?{idp_access_token:r.idpAccessTokenEnc}:{},...r.ttlSec?{iat:e,exp:e+r.ttlSec}:{}},m,L.HS256,r.kid),o=r.ttlSec??(typeof r.claims.exp=="number"?Math.max(r.claims.exp-e,0):z);return{accessToken:n,expiresIn:o}}async function ke(r,e,n){const o=k=>s(r,{error:"invalid_grant",error_description:k},400),c=r.get("logger"),i=n.get("refresh_token"),p=n.get("client_id")||void 0;if(!i)return s(r,{error:"invalid_request",error_description:"refresh_token is required"},400);let t;try{t=await x(i)}catch(k){return c?.warn("MCP refresh token rejected: %s",k instanceof Error?k.message:String(k)),o("Invalid refresh token")}if(p&&p!==t.client_id)return o("Client mismatch");const l=t.idp_id?e.getConfig().ssoDirect?.[t.idp_id]:void 0;let _,f,d=t.idp_access_token_enc,a=t.idp_refresh_token_enc;if(t.idp_refresh_token_enc){let k;try{k=pe(t.idp_refresh_token_enc,m)}catch{return o("Invalid refresh token")}if(!t.idp_id||!w(l))return o("Unknown identity provider");let u;try{const h=await re(t.idp_id,l);if(!h?.token_endpoint)return o("Identity provider has no token endpoint");u=await ee(h.token_endpoint,k,l)}catch(h){return c?.warn("MCP refresh failed to reach the IdP token endpoint: %s",h instanceof Error?h.message:String(h)),s(r,{error:"server_error",error_description:"Failed to refresh session"},500)}if(u.error||!(u.id_token||u.access_token))return c?.warn("MCP refresh rejected by IdP: %s",u.error||"empty token response"),o("Refresh rejected by identity provider");if(u.refresh_token&&(a=G(u.refresh_token,m)),u.access_token&&(d=q(u.access_token,m)),u.id_token){try{const h=M.decode(u.id_token);_=h.payload,f=h.header.kid}catch{return o("Invalid token response from identity provider")}if(_.iss!==t.claims.iss||_.sub!==t.claims.sub)return c?.warn("MCP refresh rejected: the id_token names another issuer or subject"),o("Invalid refresh token")}}const C=w(l)&&l.tokenExpirationTime||z,g=_??t.claims;if(!ne(g,l))return c?.warn("MCP refresh rejected: the id_token was issued for another project"),o("Invalid refresh token");const E=await K({claims:g,kid:f,idpId:t.idp_id,idpAccessTokenEnc:d,ttlSec:_?void 0:C}),O=await $({clientId:t.client_id,idpId:t.idp_id,claims:g,idpAccessTokenEnc:d,idpRefreshTokenEnc:a,sessionExp:t.session_exp});return s(r,{access_token:E.accessToken,token_type:"Bearer",expires_in:E.expiresIn,refresh_token:O,scope:y.join(" ")},200,{"Cache-Control":"no-store",Pragma:"no-cache"})}function Ge(r){return async e=>{if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{const n=await e.req.formData(),o=n.get("grant_type"),c=n.get("code"),i=n.get("redirect_uri")||void 0,p=n.get("code_verifier")||void 0;if(!j.includes(o))return s(e,{error:"invalid_request",error_description:"Invalid grant type"},400);if(o==="refresh_token")return await ke(e,r,n);if(!c)return s(e,{error:"invalid_request",error_description:"Missing authorization code"},400);try{const t=await Q(c);if(i&&i!==t.redirect_uri)return s(e,{error:"invalid_grant",error_description:"redirect_uri mismatch"},400);if(t.code_challenge){if(!p)return s(e,{error:"invalid_grant",error_description:"code_verifier required"},400);if(!he(t.code_challenge,t.code_challenge_method,p))return s(e,{error:"invalid_grant",error_description:"code_verifier mismatch"},400)}if(D.OAUTH_CLIENT_ID&&t.client_id&&!H(t.client_id)&&t.client_id!==D.OAUTH_CLIENT_ID)return s(e,{error:"invalid_client",error_description:"Client mismatch"},400);const l=t.id_token;if(typeof l!="string"||l.length===0)return s(e,{error:"invalid_grant",error_description:"Missing id_token in authorization code"},400);const _=await J(l,r.getConfig().ssoDirect);if(!_)return s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400);const f=t.idp_access_token_enc??(t.idp_access_token?q(t.idp_access_token,m):void 0),{accessToken:d,expiresIn:a}=await K({claims:_.payload,kid:_.kid,idpId:t.idp_id,idpAccessTokenEnc:f}),g=Math.floor(Date.now()/1e3)+ie,E=await $({clientId:t.client_id,idpId:t.idp_id,claims:_.payload,idpAccessTokenEnc:f,idpRefreshTokenEnc:t.idp_refresh_token_enc,sessionExp:g}),O={access_token:d,token_type:"Bearer",expires_in:a,refresh_token:E,scope:y.join(" ")};return s(e,O,200,{"Cache-Control":"no-store",Pragma:"no-cache"})}catch(t){return e.get("logger")?.warn("MCP token exchange failed: %s",t instanceof Error?t.message:String(t)),s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400)}}catch(n){const o=n instanceof Error?n.message:String(n);return s(e,{error:"server_error",error_description:"Failed to process token request",error_details:o},500)}}}async function J(r,e){const n=await te(e||{},{[v.AUTHORIZATION]:r});if(n.isAuthenticated!==!0)return;const{payload:o,header:c}=M.decode(r);if(o[R.CLAIM]!==R.VALUE)return{payload:o,kid:c.kid,idpId:typeof n.idpId=="string"?n.idpId:void 0}}function Be(r){return async e=>{const n=new URL(e.req.url);let o=n.searchParams.get("context");if(!o&&n.pathname.startsWith(I(`${T.MCP_CALLBACK}/`))){const i=n.pathname.split("/");o=i[i.length-1]}if(!o)return e.text("Missing context parameter",400);let c=null;try{const i=await ge(o);if(c=i.mcpSessionId||null,!i.isMcpFlow||!i.originalRedirectUri)throw new Error("Invalid MCP context");const p=r.getConfig().ssoDirect,t=U(e,v.AUTHORIZATION),l=t?await J(t,p):void 0,_=U(e,v.IDP_ACCESS_TOKEN),f=U(e,v.IDP_REFRESH_TOKEN);if(!t||!l)return A.sendMcpAuthorizationFailedMessage([{...S(c),error:"Authentication required",error_details:null}]),e.text("Authentication required",401);const d=await X({idToken:t,idpId:l.idpId??B(p),idpAccessTokenEnc:_?q(_,m):void 0,idpRefreshTokenEnc:f?G(f,m):void 0,clientId:i.mcpClientId||"",redirectUri:i.originalRedirectUri,codeChallenge:i.mcpCodeChallenge||void 0,codeChallengeMethod:i.mcpCodeChallengeMethod||void 0,ttlSec:600}),a=new URL(i.originalRedirectUri);return a.searchParams.set("code",d),i.mcpState&&a.searchParams.set("state",i.mcpState),A.sendMcpAuthorizationCompletedMessage([{...S(c),redirect_uri:i.originalRedirectUri||null}]),e.redirect(a.toString())}catch(i){const p=Ce(i);return A.sendMcpAuthorizationFailedMessage([{...S(c),...p}]),e.text(`Invalid MCP callback: ${p.error}`,400)}}}function Ce(r){return r instanceof Error&&[se,ce,de,ae,le].some(e=>r instanceof e)?{error:r.name,error_details:null}:r instanceof Error?{error:r.message,error_details:r.stack??null}:{error:String(r),error_details:null}}export{j as SUPPORTED_MCP_GRANT_TYPES,me as createMcpContextToken,Fe as mcpAuthorizationHandler,Be as mcpCallbackHandler,ze as mcpDynamicClientRegistrationHandler,$e as mcpOAuthAuthorizationServerHandler,je as mcpOAuthProtectedResourceHandler,Ge as mcpTokenPortalHandler,B as resolveMcpIdpId,ge as verifyAndParseMcpContextToken,he as verifyPkce};
@@ -1 +1 @@
1
- import{combineUrls as b,withPathPrefix as u}from"@redocly/theme/core/utils";import{DEFAULT_DOCS_MCP_SERVER_NAME as S,MCP_PROTOCOL_VERSION as T}from"../../../plugins/mcp/constants.js";import{RESERVED_ROUTES as g}from"../../../plugins/get-reserved-routes.js";import{isMcpCodeModeEnabled as v}from"../../../plugins/mcp/utils/mode.js";import{CODE_MODE_TOOL_NAMES as M,FETCH_TOOL_NAME as O}from"../../../plugins/mcp/codemode/constants.js";import{EXECUTE_TOOL_SCHEMA as R}from"../../../plugins/mcp/codemode/tools/execute-schema.js";import{describeToolsSchema as L}from"../../../plugins/mcp/codemode/tools/describe-tools.js";import{normalizeToolDisplayName as y}from"../../../plugins/mcp/utils/tool-registration.js";import{buildCodeModeInstructions as D,buildExecuteDescription as I}from"../../../plugins/mcp/codemode/prompts.js";import{toToolListEntries as P}from"../../../plugins/mcp/codemode/build-tool-list.js";import{composeExecuteAnnotations as U}from"../../../plugins/mcp/codemode/capabilities/index.js";import{ALL_CAPABILITIES as V}from"../../../plugins/mcp/codemode/capabilities/registry.js";import{isGatewayCapabilityEnabled as H}from"../../../plugins/mcp/gateway-mcp/eligibility.js";import{shouldHandleMcpAuth as x}from"../../../utils/rbac/should-handle-mcp-auth.js";import{resolvePublicBaseUrl as N}from"../../../web-server/utils/resolve-public-base-url.js";import{telemetryTraceStep as w}from"../../../telemetry/helpers/trace-step.js";import{MCP_AUTH_SCHEMES as j}from"./constants.js";const k="https://static.modelcontextprotocol.io/schemas/mcp-server-card/v1.json",q="1.0";function ne(r){const s=v(),C=new Date().toISOString().slice(0,10);return async m=>await w("com.redocly.mcpServerCard.served",async e=>{e?.setAttribute("id","mcpServerCardId"),e?.setAttribute("object","mcpServerCard"),e?.setAttribute("uri","urn:redocly:realm:api:mcpServerCard:mcpServerCardId");const o=r.getConfig(),a=o.mcp?.docs?.name||S,n=r.getMcpTools().filter(t=>!M.has(t.name)&&!t.hasAvailabilityGate),i=H(o.mcp)?[...V]:[],l=i.some(t=>t.id===O),d=P(n,i),c=(s?[{...R,description:I(l,d),annotations:U(i)},L(n,i)]:n).map(y).map(({name:t,title:f,description:h,schema:_,annotations:A})=>({name:t,title:f,description:h,inputSchema:_,annotations:A})),E=o.skills?.hide===!0?[]:r.getSkills(),p={$schema:k,version:q,protocolVersion:T,serverInfo:{name:a,title:a,version:C},description:o.seo?.description,documentationUrl:b(N(r,m),u("/")),transport:{type:"streamable-http",endpoint:u(g.mcp)},capabilities:{logging:{},tools:{listChanged:!0},...E.length>0?{resources:{listChanged:!0},completions:{}}:{}},authentication:x(!!o.access?.requiresLogin,o.access?.rbac)?{required:!0,schemes:j}:void 0,instructions:s?D(l,d):void 0,tools:c.length>0?c:["dynamic"]};return e?.setAttribute("mode",s?"code":"tools"),e?.setAttribute("toolsCount",c.length),e?.setAttribute("authRequired",p.authentication!==void 0),m.json(p,200,{"Access-Control-Allow-Origin":"*","Cache-Control":"public, max-age=3600"})})}export{ne as mcpServerCardHandler};
1
+ import{combineUrls as b,withPathPrefix as u}from"@redocly/theme/core/utils";import{DEFAULT_DOCS_MCP_SERVER_NAME as S,MCP_PROTOCOL_VERSION as T}from"../../../plugins/mcp/constants.js";import{RESERVED_ROUTES as g}from"../../../plugins/get-reserved-routes.js";import{isMcpCodeModeEnabled as v}from"../../../plugins/mcp/utils/mode.js";import{CODE_MODE_TOOL_NAMES as M,FETCH_TOOL_NAME as O}from"../../../plugins/mcp/codemode/constants.js";import{EXECUTE_TOOL_SCHEMA as R}from"../../../plugins/mcp/codemode/tools/execute-schema.js";import{describeToolsSchema as L}from"../../../plugins/mcp/codemode/tools/describe-tools.js";import{normalizeToolDisplayName as y}from"../../../plugins/mcp/utils/tool-registration.js";import{buildCodeModeInstructions as D,buildExecuteDescription as I}from"../../../plugins/mcp/codemode/prompts.js";import{toToolListEntries as P}from"../../../plugins/mcp/codemode/build-tool-list.js";import{composeExecuteAnnotations as U}from"../../../plugins/mcp/codemode/capabilities/index.js";import{ALL_CAPABILITIES as V}from"../../../plugins/mcp/codemode/capabilities/registry.js";import{isGatewayCapabilityEnabled as H}from"../../../plugins/mcp/gateway-mcp/eligibility.js";import{shouldHandleMcpAuth as x}from"../../../utils/rbac/should-handle-mcp-auth.js";import{resolvePublicBaseUrl as N}from"../../../web-server/utils/resolve-public-base-url.js";import{telemetryTraceStep as w}from"../../../telemetry/helpers/trace-step.js";import{MCP_AUTH_SCHEMES as j}from"./constants.js";const k="https://static.modelcontextprotocol.io/schemas/mcp-server-card/v1.json",q="1.0";function ne(r){const n=v(),C=new Date().toISOString().slice(0,10);return async m=>await w("com.redocly.mcpServerCard.served",async e=>{e?.setAttribute("id","mcpServerCardId"),e?.setAttribute("object","mcpServerCard"),e?.setAttribute("uri","urn:redocly:realm:api:mcpServerCard:mcpServerCardId");const o=r.getConfig(),a=o.mcp?.docs?.name||S,i=r.getMcpTools().filter(t=>!M.has(t.name)&&!t.hasAvailabilityGate),s=H(o.mcp)?[...V]:[],l=s.some(t=>t.id===O),d=P(i,s),c=(n?[{...R,description:I(l,d),annotations:U(i,s)},L(i,s)]:i).map(y).map(({name:t,title:f,description:h,schema:_,annotations:A})=>({name:t,title:f,description:h,inputSchema:_,annotations:A})),E=o.skills?.hide===!0?[]:r.getSkills(),p={$schema:k,version:q,protocolVersion:T,serverInfo:{name:a,title:a,version:C},description:o.seo?.description,documentationUrl:b(N(r,m),u("/")),transport:{type:"streamable-http",endpoint:u(g.mcp)},capabilities:{logging:{},tools:{listChanged:!0},...E.length>0?{resources:{listChanged:!0},completions:{}}:{}},authentication:x(!!o.access?.requiresLogin,o.access?.rbac)?{required:!0,schemes:j}:void 0,instructions:n?D(l,d):void 0,tools:c.length>0?c:["dynamic"]};return e?.setAttribute("mode",n?"code":"tools"),e?.setAttribute("toolsCount",c.length),e?.setAttribute("authRequired",p.authentication!==void 0),m.json(p,200,{"Access-Control-Allow-Origin":"*","Cache-Control":"public, max-age=3600"})})}export{ne as mcpServerCardHandler};
@@ -1,4 +1,4 @@
1
1
  import type { Handler } from 'hono';
2
2
  import type { Store } from '../../store';
3
- export declare function semanticSearchHandler(_store: Store): Handler;
3
+ export declare function semanticSearchHandler(store: Store): Handler;
4
4
  //# sourceMappingURL=semantic-search.d.ts.map
@@ -1 +1 @@
1
- import{withPathPrefix as p}from"@redocly/theme/core/utils";import{SEMANTIC_SEARCH_API_URL as s}from"../../constants/common.js";import{fetchWithClient as u}from"../../utils/fetch-with-client.js";function T(d){return async t=>{if(!s)return t.newResponse(null,404);const n=t.get("auth"),o={"Content-Type":"application/json"};n.idpAccessToken&&(o.Cookie=`accessToken=${n.idpAccessToken}`);const a=await t.req.json();try{const e=await u(s,{method:"POST",body:JSON.stringify(a),headers:o});if(e.ok){const c=(await e.json()).documents.map(r=>({...r,url:p(r.url)}));return t.json(c)}const i=await e.text();return t.newResponse(i,e.status,{"Content-Type":"application/json"})}catch(e){return t.json({error:"Failed to fetch semantic search results",details:String(e)},500)}}}export{T as semanticSearchHandler};
1
+ import{withPathPrefix as p}from"@redocly/theme/core/utils";import{SEMANTIC_SEARCH_API_URL as s}from"../../constants/common.js";import{fetchWithClient as d}from"../../utils/fetch-with-client.js";import{handleUnauthorizedApiRequest as h}from"../utils.js";function y(i){return async e=>{if(!s)return e.newResponse(null,404);const n=e.get("auth");if(i.getConfig().access?.requiresLogin&&!n.isAuthenticated)return h(e);const o={"Content-Type":"application/json"};n.idpAccessToken&&(o.Cookie=`accessToken=${n.idpAccessToken}`);const a=await e.req.json();try{const t=await d(s,{method:"POST",body:JSON.stringify(a),headers:o});if(t.ok){const u=(await t.json()).documents.map(r=>({...r,url:p(r.url)}));return e.json(u)}const c=await t.text();return e.newResponse(c,t.status,{"Content-Type":"application/json"})}catch(t){return e.json({error:"Failed to fetch semantic search results",details:String(t)},500)}}}export{y as semanticSearchHandler};
@@ -1 +1 @@
1
- import s from"path";import{REDOCLY_ROUTE_RBAC as C}from"@redocly/config";import{PUBLIC_STATIC_FOLDER as r}from"../../constants/common.js";import{isPathInFolder as g}from"../../../utils/path/is-path-in-folder.js";import{fileExistsAsync as T}from"../../utils/index.js";import{handleUnauthorizedAsset as h}from"../utils.js";import{MIME_TYPES as w}from"../mime-types.js";import{getContentTypeHeaderValue as y}from"../utils/content-type.js";import{canAccessResource as E}from"../../utils/rbac.js";const U=async(e,o,n,a)=>{const c=o.req,{isAuthenticated:m,teams:l,claims:{email:u}}=o.get("auth"),i=s.join(r,r);e.startsWith(i)&&(e=e.replace(i,r));const t=s.resolve(n.outdir,e);if(!g(t,n.outdir))return null;if(await T(t)){const p={[C]:{slug:e,fsPath:t},slug:e};if(!E(p,{isAuthenticated:m,email:u,teams:l},n.config.access?.rbac,n.config.access?.requiresLogin))return h(o);const f=c.query("download")!=null,d=w[s.extname(t)]||"text/plain",A=await a(t);return o.newResponse(A,200,{"Content-Type":y(d),"Access-Control-Allow-Origin":"*",...f&&{"Content-Disposition":`attachment; filename="${s.basename(t)}"`}})}else return null};export{U as staticContentHandler};
1
+ import o from"path";import{REDOCLY_ROUTE_RBAC as C}from"@redocly/config";import{PUBLIC_STATIC_FOLDER as r}from"../../constants/common.js";import{isPathInFolder as g}from"../../../utils/path/is-path-in-folder.js";import{fileExistsAsync as T}from"../../utils/index.js";import{handleUnauthorizedAsset as h}from"../utils.js";import{MIME_TYPES as w}from"../mime-types.js";import{getContentTypeHeaderValue as y}from"../utils/content-type.js";import{canAccessResource as E}from"../../utils/rbac.js";const U=async(e,n,s,a)=>{const c=n.req,{isAuthenticated:m,teams:l,claims:{email:u}}=n.get("auth"),i=o.join(r,r);e.startsWith(i)&&(e=e.replace(i,r));const t=o.resolve(s.outdir,e);if(!g(t,o.join(s.outdir,r)))return null;if(await T(t)){const p={[C]:{slug:e,fsPath:t},slug:e};if(!E(p,{isAuthenticated:m,email:u,teams:l},s.config.access?.rbac,s.config.access?.requiresLogin))return h(n);const f=c.query("download")!=null,d=w[o.extname(t)]||"text/plain",A=await a(t);return n.newResponse(A,200,{"Content-Type":y(d),"Access-Control-Allow-Origin":"*",...f&&{"Content-Disposition":`attachment; filename="${o.basename(t)}"`}})}else return null};export{U as staticContentHandler};
@@ -1 +1 @@
1
- import l from"workerpool";import{mkdirSync as m}from"node:fs";import{basename as u,dirname as c,resolve as p}from"node:path";import{fileURLToPath as f}from"node:url";import{envConfig as n}from"../config/env-config.js";import{SandboxContext as v}from"../utils/sandbox-context.js";const h=(()=>{const o=c(f(import.meta.url));return u(o)==="chunks"?c(o):o})();class P{#t=null;#r;constructor(r){this.#r=r,this.#r.lazy||(this.#t=this.#e())}#e(){const{workerScript:r,lazy:t,sandbox:i,resolveForkEnv:s,...e}=this.#r,a=this.#s(r);return s&&e.forkOpts&&(e.forkOpts={...e.forkOpts,env:s()}),this.#o(i)?l.pool(a,{...e,forkOpts:this.#i(e.forkOpts)}):l.pool(a,{...e})}#o(r){return!!r&&n.isReunite&&n.REDOCLY_DISABLE_API_FUNCTION_SANDBOX!==!0}#i(r){const t=v.resolve(h);try{m(t.tmpDir,{recursive:!0})}catch{}return{...r,cwd:t.serverOutDir,execArgv:[...t.execArgv,...r?.execArgv??[]],env:{...r?.env,...t.env}}}#s(r){let t=r;return!t.endsWith(".js")&&!t.endsWith(".mjs")&&(t=`${t}.${n.isDevelopMode?"js":"mjs"}`),t.startsWith(".")?p(h,t):t}#n(){return this.#t||(this.#t=this.#e()),this.#t}async exec(r,t,i){let e=this.#n().exec(r,t);return i?.timeout&&(e=e.timeout(i.timeout)),await e}async terminate(){this.#t&&(await this.#t.terminate(),this.#t=null)}}export{P as WorkerPool,h as workerBaseDir};
1
+ import a from"workerpool";import{mkdirSync as m}from"node:fs";import{basename as u,dirname as l,resolve as p}from"node:path";import{fileURLToPath as f}from"node:url";import{envConfig as c}from"../config/env-config.js";import{SandboxContext as v}from"../utils/sandbox-context.js";const h=(()=>{const o=l(f(import.meta.url));return u(o)==="chunks"?l(o):o})();class b{#t=null;#r;constructor(r){this.#r=r,this.#r.lazy||(this.#t=this.#e())}#e(){const{workerScript:r,lazy:t,sandbox:i,resolveForkEnv:s,...e}=this.#r,n=this.#s(r);return s&&e.forkOpts&&(e.forkOpts={...e.forkOpts,env:s()}),this.#o(i)?a.pool(n,{...e,forkOpts:this.#i(e.forkOpts)}):a.pool(n,{...e})}#o(r){return!!r&&c.isReunite}#i(r){const t=v.resolve(h);try{m(t.tmpDir,{recursive:!0})}catch{}return{...r,cwd:t.serverOutDir,execArgv:[...t.execArgv,...r?.execArgv??[]],env:{...r?.env,...t.env}}}#s(r){let t=r;return!t.endsWith(".js")&&!t.endsWith(".mjs")&&(t=`${t}.${c.isDevelopMode?"js":"mjs"}`),t.startsWith(".")?p(h,t):t}#n(){return this.#t||(this.#t=this.#e()),this.#t}async exec(r,t,i){let e=this.#n().exec(r,t);return i?.timeout&&(e=e.timeout(i.timeout)),await e}async terminate(){this.#t&&(await this.#t.terminate(),this.#t=null)}}export{b as WorkerPool,h as workerBaseDir};
@@ -40,6 +40,7 @@ export type PlanGates = {
40
40
  [FEATURE.gatewayFetchRatePerMin]: number;
41
41
  [FEATURE.gatewayFetchQuotaPerDay]: number;
42
42
  [FEATURE.gatewayFetchBudgetPerExec]: number;
43
+ [FEATURE.cookieConsent]: boolean;
43
44
  };
44
45
  export type Entitlements = {
45
46
  [key in Feature]: boolean | number | string;
@@ -1 +1 @@
1
- const s=new RegExp("((?!@i18n\\/|@l10n\\/)\\/?@.*?(?=\\/))","g");function l(n){if(n.match(s)?.length!==1)return null;const[i,e,t]=n.split(new RegExp("(?:^@(?:i18n|l10n)\\/)?(.*?\\/?)@(.*)")),[o,r]=t.split(new RegExp("\\/(.*)","s"));return{versionFolderPath:e||"/",filePathInVersion:r,versionName:o}}export{l as parsePathVersions};
1
+ const s=new RegExp("((?!@i18n\\/|@l10n\\/)\\/?@.*?(?=\\/))","g");function l(n){if(n.match(s)?.length!==1)return null;const[i,e,t]=n.split(new RegExp("(?<![^\\n])(?:^@(?:i18n|l10n)\\/)?([^@\\n]*)@(.*)")),[o,r]=t.split(new RegExp("\\/(.*)","s"));return{versionFolderPath:e||"/",filePathInVersion:r,versionName:o}}export{l as parsePathVersions};
@@ -1 +1 @@
1
- function r(e){return e?e.trim().replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").replace(/[^\w\s-]+/g," ").toLowerCase().replace(/[\s_]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,""):""}export{r as toKebabCase};
1
+ function r(e){return e?e.trim().replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").replace(/[^\w\s-]+/g," ").toLowerCase().replace(/[\s_]+/g,"-").replace(/-+/g,"-").replace(/^-+|(?<!-)-+$/g,""):""}export{r as toKebabCase};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/realm",
3
- "version": "0.138.0-next.1",
3
+ "version": "0.138.0-next.10",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,13 +33,13 @@
33
33
  "@opentelemetry/sdk-trace-web": "2.8.0",
34
34
  "@opentelemetry/semantic-conventions": "1.40.0",
35
35
  "@redocly/ajv": "8.18.0",
36
- "@redocly/api-docs": "0.3.0-next.1",
37
- "@redocly/config": "0.57.0",
38
- "@redocly/openapi-core": "2.53.0",
36
+ "@redocly/api-docs": "0.3.0-next.7",
37
+ "@redocly/config": "0.62.0",
38
+ "@redocly/openapi-core": "2.55.0",
39
39
  "@redocly/portal-legacy-ui": "0.19.0",
40
- "@redocly/portal-plugin-mock-server": "0.22.0",
41
- "@redocly/realm-asyncapi-sdk": "0.16.0-next.0",
42
- "@redocly/theme": "0.70.0-next.1",
40
+ "@redocly/portal-plugin-mock-server": "0.23.0-next.0",
41
+ "@redocly/realm-asyncapi-sdk": "0.16.0-next.2",
42
+ "@redocly/theme": "0.70.0-next.7",
43
43
  "@shikijs/transformers": "3.21.0",
44
44
  "@tanstack/react-query": "5.62.3",
45
45
  "@tanstack/react-table": "8.21.3",
@@ -91,8 +91,8 @@
91
91
  "styled-components": "^6.4.2",
92
92
  "tty-browserify": "0.0.1",
93
93
  "typesense": "2.1.0",
94
- "ulid": "^2.3.0",
95
- "undici": "^6.27.0",
94
+ "ulid": "^3.0.2",
95
+ "undici": "^6.28.0",
96
96
  "web-vitals": "3.3.1",
97
97
  "workerpool": "9.2.0",
98
98
  "ws": "^8.21.0",
@@ -1 +0,0 @@
1
- function f(o,n,e={}){for(const r of o)r.versionFolderId&&r.version&&(e[r.versionFolderId]=e[r.versionFolderId]??new Set).add(r.version);return o.filter(r=>r.version==null?r:!n||r.versionFolderId!==n.folderId?r.isDefault:r.versionFolderId&&e[r.versionFolderId]?.has(n.version)?r.version===n.version:r.isDefault).map(r=>{if(Array.isArray(r.items)&&r.items.length){const s=f(r.items,n,e);return s.length===0?null:{...r,items:s}}return r}).filter(r=>r!==null)}export{f as filterOutVersionedItems};
@@ -1,7 +0,0 @@
1
- type TokenAudienceMismatchEvent = {
2
- audience: string;
3
- enforced: boolean;
4
- };
5
- export declare function reportMcpTokenAudienceMismatch({ audience, enforced, }: TokenAudienceMismatchEvent): void;
6
- export {};
7
- //# sourceMappingURL=token-audience-telemetry.d.ts.map
@@ -1 +0,0 @@
1
- import{telemetry as t}from"../../../telemetry/index.js";import{logger as o}from"../../../tools/notifiers/logger.js";import{McpServerType as c}from"../constants.js";import{serverAttributes as m}from"../utils/telemetry-attributes.js";function a({audience:e,enforced:r}){try{t.sendMcpTokenAudienceMismatchMessage([{...m(c.Docs),audience:e,enforced:r}])}catch{}o.warn(`MCP token audience mismatch [audience=${e}] enforced=${r}`)}export{a as reportMcpTokenAudienceMismatch};
@@ -1,6 +0,0 @@
1
- import type { McpUserInfo } from '../types.js';
2
- export type TokenAudienceResult = 'match' | 'mismatch' | 'skipped';
3
- export declare function toAudienceList(aud: McpUserInfo['aud']): string[];
4
- export declare function checkTokenAudience(aud: McpUserInfo['aud']): TokenAudienceResult;
5
- export declare function isTokenAudienceEnforced(): boolean;
6
- //# sourceMappingURL=token-audience.d.ts.map
@@ -1 +0,0 @@
1
- import{envConfig as t}from"../../../config/env-config.js";function i(n){return n?(Array.isArray(n)?n:[n]).filter(e=>typeof e=="string"&&!!e):[]}function c(n){const e=t.ORGANIZATION_ID,r=i(n);return!e||r.length===0?"skipped":r.includes(e)?"match":"mismatch"}function f(){return t.REDOCLY_MCP_ENFORCE_TOKEN_AUDIENCE===!0}export{c as checkTokenAudience,f as isTokenAudienceEnforced,i as toAudienceList};