@redocly/redoc 0.139.0-next.1 → 0.139.0-next.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +192 -0
- package/dist/cli/eject/resolveEjectParams.js +1 -1
- package/dist/client/app/Sidebar/RequestAccessButton.js +2 -2
- package/dist/client/app/hooks/useL10nConfig.d.ts +1 -0
- package/dist/client/app/markdoc/custom-components/openapi/openapi-example.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/replay-openapi.js +1 -1
- package/dist/client/app/search/useSearch.js +1 -1
- package/dist/client/app/utils/programmatic-scroll.js +1 -1
- package/dist/client/app/webmcp/tools/api-tools.js +1 -1
- package/dist/client/app/webmcp/tools/site-tools.js +2 -2
- package/dist/client/app/webmcp/useWebMcpTools.js +1 -1
- package/dist/client/templates/api-docs/template.js +10 -8
- package/dist/constants/common.d.ts +1 -0
- package/dist/constants/common.js +1 -1
- package/dist/constants/l10n/langs/ar.js +1 -1
- package/dist/constants/l10n/langs/de.js +1 -1
- package/dist/constants/l10n/langs/en.js +1 -1
- package/dist/constants/l10n/langs/es.js +1 -1
- package/dist/constants/l10n/langs/fr.js +1 -1
- package/dist/constants/l10n/langs/hi.js +1 -1
- package/dist/constants/l10n/langs/it.js +1 -1
- package/dist/constants/l10n/langs/ja.js +1 -1
- package/dist/constants/l10n/langs/ko.js +1 -1
- package/dist/constants/l10n/langs/pl.js +1 -1
- package/dist/constants/l10n/langs/pt-BR.js +1 -1
- package/dist/constants/l10n/langs/pt.js +1 -1
- package/dist/constants/l10n/langs/ru.js +1 -1
- package/dist/constants/l10n/langs/uk.js +1 -1
- package/dist/constants/l10n/langs/zh.js +1 -1
- package/dist/markdoc/helpers/get-inner-text.d.ts +2 -0
- package/dist/markdoc/helpers/get-inner-text.js +2 -2
- package/dist/markdoc/helpers/remove-markdoc-tags.js +1 -1
- package/dist/server/config/env-schemas/feature-flags.d.ts +1 -1
- package/dist/server/constants/entitlements.d.ts +2 -2
- package/dist/server/constants/entitlements.js +1 -1
- package/dist/server/entitlements/entitlements-provider.d.ts +2 -2
- package/dist/server/entitlements/is-valid-plan.d.ts +2 -2
- package/dist/server/entitlements/is-valid-plan.js +1 -1
- package/dist/server/fs/content-fs.js +1 -1
- package/dist/server/fs/utils/external-ref-resolver.js +2 -2
- package/dist/server/fs/utils/is-translation-fallback.d.ts +9 -0
- package/dist/server/fs/utils/is-translation-fallback.js +1 -0
- package/dist/server/node-bundle-entry.js +1 -1
- package/dist/server/plugins/api-docs/get-server-props.js +1 -1
- package/dist/server/plugins/api-docs/index.js +1 -1
- package/dist/server/plugins/api-docs/loader.js +3 -3
- package/dist/server/plugins/api-docs/search/get-ai-search-documents.d.ts +6 -3
- package/dist/server/plugins/api-docs/search/get-ai-search-documents.js +69 -69
- package/dist/server/plugins/api-docs/utils/inject-metadata.d.ts +19 -0
- package/dist/server/plugins/api-docs/utils/inject-metadata.js +1 -0
- package/dist/server/plugins/arazzo-docs/arazzo-doc-loader.js +2 -2
- package/dist/server/plugins/arazzo-docs/index.js +1 -1
- package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/openapi-entities-extractor.js +1 -1
- package/dist/server/plugins/catalog-entities/template/index.js +1 -1
- package/dist/server/plugins/config-parser/loaders/versions-config-loader.js +1 -1
- package/dist/server/plugins/default-theme/resolve-logo.js +1 -1
- package/dist/server/plugins/dev-onboarding/api/routes/index.js +1 -1
- package/dist/server/plugins/dev-onboarding/api/routes/products.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/App.js +4 -4
- package/dist/server/plugins/dev-onboarding/template/AppsList.js +7 -7
- package/dist/server/plugins/dev-onboarding/template/CreateAppDialog.js +3 -3
- package/dist/server/plugins/dev-onboarding/template/components/ApiKeyItem.js +4 -4
- package/dist/server/plugins/dev-onboarding/template/components/ApiLogDetails.js +7 -7
- package/dist/server/plugins/dev-onboarding/template/components/ApiLogs.js +5 -5
- package/dist/server/plugins/dev-onboarding/template/components/ApiLogsActions.js +6 -6
- package/dist/server/plugins/dev-onboarding/template/components/ApisList.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/CreateApiKey.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/DeleteAppDialog.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/EditApis.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/EditAppDescriptionDialog.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/EditAppDialog.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/EditCallbackUrlDialog.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/RevokeAPIKey.js +5 -5
- package/dist/server/plugins/dev-onboarding/template/components/RollAPIKey.js +5 -5
- package/dist/server/plugins/dev-onboarding/template/components/UpdateApiKey.js +1 -1
- package/dist/server/plugins/l10n/index.js +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-json-example-ref.js +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-link.js +1 -1
- package/dist/server/plugins/markdown/get-server-props.js +1 -1
- package/dist/server/plugins/markdown/index.js +1 -1
- package/dist/server/plugins/markdown/markdoc/plugins/utils.js +1 -1
- package/dist/server/plugins/markdown/markdoc/resolve-raw-partials.js +1 -1
- package/dist/server/plugins/markdown/markdown-static-data-loader.js +1 -1
- package/dist/server/plugins/markdown/read-file-content.js +1 -1
- package/dist/server/plugins/markdown/search/search-resolver.js +1 -1
- package/dist/server/plugins/mcp/codemode/prompts.d.ts +0 -1
- package/dist/server/plugins/mcp/codemode/prompts.js +3 -3
- package/dist/server/plugins/mcp/constants.d.ts +0 -2
- package/dist/server/plugins/mcp/constants.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tool-schemas.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/endpoint-suggestions.d.ts +0 -2
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/endpoint-suggestions.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/allowed-http-methods.d.ts +0 -5
- package/dist/server/plugins/mcp/gateway-mcp/allowed-http-methods.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/caller-api-calls.d.ts +3 -4
- package/dist/server/plugins/mcp/gateway-mcp/caller-api-calls.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/eligibility.d.ts +1 -12
- package/dist/server/plugins/mcp/gateway-mcp/eligibility.js +1 -1
- package/dist/server/plugins/mcp/handlers/errors.d.ts +0 -2
- package/dist/server/plugins/mcp/handlers/errors.js +1 -1
- package/dist/server/plugins/mcp/utils.d.ts +0 -8
- package/dist/server/plugins/mcp/utils.js +1 -1
- package/dist/server/plugins/nav-utils.js +1 -1
- package/dist/server/plugins/pages/index.js +1 -1
- package/dist/server/plugins/register-page-props.d.ts +1 -1
- package/dist/server/plugins/register-page-props.js +1 -1
- package/dist/server/plugins/sidebars/index.js +1 -1
- package/dist/server/plugins/sidebars/utils.js +1 -1
- package/dist/server/store.d.ts +3 -1
- package/dist/server/store.js +1 -1
- package/dist/server/types/index.d.ts +1 -0
- package/dist/server/types/plugins/markdown.d.ts +1 -0
- package/dist/server/types/static-files.d.ts +6 -0
- package/dist/server/types/static-files.js +0 -0
- package/dist/server/utils/content/build-static-file-paths.d.ts +15 -0
- package/dist/server/utils/content/build-static-file-paths.js +1 -0
- package/dist/server/utils/envs/load-env-variables.js +1 -1
- package/dist/server/utils/lifecycle-hooks.js +1 -1
- package/dist/server/utils/rbac-expand.d.ts +4 -0
- package/dist/server/utils/rbac-expand.js +1 -1
- package/dist/server/utils/rbac.js +1 -1
- package/dist/server/utils/redirects/find-redirect.d.ts +4 -3
- package/dist/server/utils/redirects/find-redirect.js +1 -1
- package/dist/server/utils/resolve-asset-path.js +1 -1
- package/dist/server/web-server/auth.d.ts +2 -0
- package/dist/server/web-server/auth.js +3 -3
- package/dist/server/web-server/jwt/jwt.js +1 -1
- package/dist/server/web-server/middleware/catalogAuthMiddleware.js +1 -1
- package/dist/server/web-server/routes/mcp-routes/mcp-oauth.js +1 -1
- package/dist/types/entitlements.d.ts +2 -1
- package/dist/types/global-data.d.ts +1 -0
- package/dist/utils/url/api-keys-url.d.ts +11 -0
- package/dist/utils/url/api-keys-url.js +1 -0
- package/package.json +7 -7
- package/dist/server/plugins/mcp/docs-mcp/tools/core/utils.d.ts +0 -3
- package/dist/server/plugins/mcp/docs-mcp/tools/core/utils.js +0 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as v from"path";import{REDOCLY_ROUTE_RBAC as b,REDOCLY_TEAMS_RBAC as h}from"@redocly/config";import{VERSION_SEPARATOR as M}from"../constants/common.js";import{DEFAULT_LOCALE_PLACEHOLDER as _}from"../../constants/common.js";import{removeTrailingSlash as j}from"../../utils/url/remove-trailing-slash.js";import{isPrimitive as W}from"../../utils/guards/is-primitive.js";import{slash as w}from"../../utils/path/slash.js";import{isAbsoluteUrl as z}from"../../utils/url/is-absolute-url.js";import{normalizeRouteSlug as J}from"../../utils/path/normalize-route-slug.js";import{parsePathVersions as C}from"../../utils/path/parse-path-versions.js";import{logger as A}from"../tools/notifiers/logger.js";import{reporter as S}from"../tools/notifiers/reporter.js";import{shaDirPathShort as R}from"../utils/crypto/sha-dir-path-short.js";import{copyStaticFile as U,FileNotFoundError as E}from"../utils/fs.js";import{isIconPath as H,resolveAssetPath as Y}from"../utils/index.js";import{resolveSrcSet as G}from"../utils/resolve-src-set.js";import{isL10nPath as B}from"../fs/utils/is-l10n-path.js";import{getLocaleFromRelativePath as q}from"../fs/utils/get-locale-from-relative-path.js";async function N(e,i,n,t,a){if(!e)return;const f={...a,ignoreCustomSidebar:!0};if(W(e)){if(typeof e=="string"){const r=F(n.contentDir,i,a,e,t.fs);if(t.fs.exists(r)){if(n.getRouteByFsPath(r))return K({page:e},i,n,t,f);{const g=t.fs.getFileInfo(r);return g?U(n.contentDir,g.realRelativePath,n.outdir):void 0}}}return e}return Array.isArray(e)?I(D(e),i,n,t,f):Object.fromEntries(await Promise.all(Object.entries(e).map(async([r,l])=>[r,await N(l,i,n,t,a)])))}function D(e){return e.map(i=>({...i,items:i.items?D(i.items):void 0}))}async function I(e,i,n,t,a){if(Array.isArray(e))return(await Promise.all(e.map(f=>K(f,i,n,t,a)))).flatMap(f=>f)}async function y(e,i,n,t){let a,f;if(typeof e.icon=="object"){if("srcSet"in e.icon)try{a=await G(e.icon.srcSet,n.fs,{fromFileRelativePath:t.navFile,contentDir:i.contentDir,outdir:i.outdir})}catch(d){d instanceof E?await S.panicOnBuildContentError(`Cannot resolve "item.icon.srcSet" from ${t.navFile}: ${d.message}`):await S.panicOnBuild(`Cannot resolve "item.icon.srcSet" from ${t.navFile}: ${d.message}`)}}else if(typeof e.icon=="string")if(z(e.icon)||H(e.icon))try{f=await Y(e.icon,n.fs,{fromFileRelativePath:t.navFile,contentDir:i.contentDir,outdir:i.outdir})}catch(d){d instanceof E?await S.panicOnBuildContentError(`Cannot resolve "item.icon" from ${t.navFile}: file ${f} does not exist`):await S.panicOnBuild(`Cannot resolve "item.icon" from ${t.navFile}: ${d.message}`)}else f=e.icon;return{icon:f,srcSet:a}}function Q(e){return i=>{if(i.type==="link"||i.type==="group"){const n=i.metadata;return n?Object.entries(e).every(([t,a])=>Array.isArray(a)?a.some(f=>n[t]===f):n[t]===a):!1}return!0}}async function K(e,i,n,t,a){if(e?.tab!==void 0||e?.menu!==void 0){const s=`Navigation tabs ("tab"/"menu") are only allowed at the top level of a sidebars.yaml file: ${JSON.stringify(e)}`;return await S.panicOnBuildContentError(`Invalid nav item in %rp. ${s}`,a.navFile),{type:"error",label:s}}if(e?.directory&&e.items?.length)return{type:"error",label:`Can't have both "directory" and "items" in the nav item: ${JSON.stringify(e)}`};e?.directory&&e?.group&&(e={...e,directory:void 0,items:[{directory:e.directory}]});const f=e?.directory?F(n.contentDir,i,a,e.directory,t.fs):void 0;if(f!==void 0){const s=f===""?"":f+"/",{locale:u,ignoredRoutes:o}=a,p=n.getAllRoutesForLocale(u).filter(c=>!o?.has(c.baseSlug||"")&&c.fsPath.startsWith(s)&&!c.excludeFromSidebar).sort((c,P)=>c.fsPath===P.fsPath?0:c.baseSlug.localeCompare(P.baseSlug,void 0,{numeric:!0})),m=e.includeByMetadata?Q(e.includeByMetadata):Boolean;if(!p.length)return[];const T=(await Promise.all(p.map(async c=>{const P=c.versions?.find(x=>x.active),L=P&&{version:P.version,isDefault:P.default,versionFolderId:P.folderId},V=ee(v.posix.relative(f,c.fsPath)),O=c.getSidebar?.(c);return!a.ignoreCustomSidebar&&O?.length?{type:"group",fsPath:c.fsPath,metadata:c.metadata,link:c.slug,routeSlug:c.slug,label:await c.getNavText?.()||c.slug,[h]:c[h],[b]:c[b],sidebarItems:k(O,L,c[h],c[b]),...L,relativePathFromDirectory:v.relative(f,c.fsPath).replace(/@(?<!@[^/]*?@)[^/]+\//,"")}:{type:"link",fsPath:c.fsPath,metadata:c.metadata,[h]:c[h],[b]:c[b],label:await c.getNavText?.()||c.slug,link:c.slug,routeSlug:c.slug,...L,relativePathFromDirectory:V}}))).filter(m);return e.flatten?$(T,a):Z(T,a)}let d=typeof e=="string"?e:e?.page,r,l;if(d){if(d.includes("#")){const[s,u]=d.split("#");u?l=u:A.warn(`Invalid heading anchor format in sidebar: "${u}". Heading anchors should contain only alphanumeric characters, hyphens, and underscores.`),d=s}if(d=j(d),a.locale&&a.locale!==_){const s=J(v.join(a.locale.toLowerCase(),d));r=n.getRouteBySlug(s)}r||(r=n.getRouteBySlug(d)),r||(r=n.getRouteByFsPath(F(n.contentDir,i,a,d,t.fs)))}const g=r?.metadata;if(e?.$ref){let s=F(n.contentDir,i,a,e.$ref,t.fs);if(a.ref!==void 0&&(s=v.posix.join(a.ref,e.$ref)),!t.fs.exists(s))return await S.panicOnBuildContentError(`Failed to load ${s} file. Make sure sidebar $ref path is correct.`),[];const u=await t.cache.load(s,"yaml"),o=v.dirname(s);return await I(u.data,i,n,t,{...a,navFile:s,ref:o})||[]}if(!r){const s=a.excludedFromLinkCheckerPatterns?.catalog.some(o=>o.test(d||"")),u=a.excludedFromLinkCheckerPatterns?.apiFunctions.some(o=>o.test(d||e.href||""));(u||s)&&(e={...e,href:d||e.href,page:void 0,target:e.target??(u?"_blank":void 0)}),await X(e,a,t,i)}if(r&&!e.disconnect&&!a.ignoreCustomSidebar&&(l||r?.getSidebar)){const s=r.versions?.find(o=>o.active),u=s&&{version:s.version,isDefault:s.default,versionFolderId:s.folderId};if(l){let o=[];if(r.getSidebar&&(o=r.getSidebar(r,{...e,...await y(e,n,t,a)}),o&&o.length>0)){const p=o[0];p&&p.link&&(p.link=`${p.link}#${l}`)}if(!o||o.length===0){const p=e.group||e.label||l;o=[{type:"link",fsPath:r.fsPath,metadata:r.metadata,link:`${r.slug}#${l}`,routeSlug:r.slug,label:p,labelTranslationKey:e.labelTranslationKey,...await y(e,n,t,a),[h]:r[h],[b]:{slug:r.slug,fsPath:r.fsPath},...u}]}return k(o,u,r[h],r[b])}if(r?.getSidebar){const o=r.getSidebar(r,{...e,...await y(e,n,t,a)}),p=e.group&&o?.[0]?.routeSlug===r.slug?o.slice(1):o;return k(a.groupCustomSidebars||e.group?[{type:"group",fsPath:r.fsPath,link:r.slug,routeSlug:r.slug,label:e.group||e.label||await r.getNavText?.()||r.slug,items:p,labelTranslationKey:e.labelTranslationKey,metadata:g,badges:e.badges,...await y(e,n,t,a)}]:o,u,r[h],r[b])}}if(r&&!e.group){const s=l?`${r.slug}#${l}`:r.slug;let u=e.label||l||"";u||(u=await r.getNavText?.()||r.slug);const o=r.versions?.find(m=>m.active),p=o&&{version:o.version,isDefault:o.default,versionFolderId:o.folderId};return{type:"link",fsPath:r.fsPath,linkedSidebars:e.linkedSidebars,metadata:g,[h]:e?.rbac||r[h],[b]:{slug:r.slug,fsPath:r.fsPath},label:u,labelTranslationKey:e.labelTranslationKey,link:s,items:await I(e.items,i,n,t,a),separatorLine:e.separatorLine,linePosition:e.linePosition,routeSlug:e.disconnect?void 0:s,external:e.external,target:e.target,badges:e.badges,...await y(e,n,t,a),...p,additionalProps:e.additionalProps}}else if(e?.group){const s=r?.versions?.find(o=>o.active),u=C(a?.navFile);return{type:"group",fsPath:r?.fsPath,linkedSidebars:e.linkedSidebars,metadata:g,version:s?.version||u?.versionName,versionFolderId:s?.folderId||(u?.versionFolderPath?R(u.versionFolderPath):void 0),label:e.group,link:r?.slug,routeSlug:r?.slug,badges:e.badges,labelTranslationKey:e.groupTranslationKey,items:await I(e.items,i,n,t,a),expanded:e.expanded?e.expanded.toString():void 0,selectFirstItemOnExpand:e.selectFirstItemOnExpand,menuStyle:e.menuStyle,separatorLine:e.separatorLine,linePosition:e.linePosition,...await y(e,n,t,a),[h]:e?.rbac,[b]:{slug:r?.slug||"",fsPath:r?.fsPath||""},additionalProps:e.additionalProps}}else{if(e?.href||e?.label)return{type:"link",metadata:g,link:e.href||"",label:e?.label||e?.href||"",labelTranslationKey:e.labelTranslationKey,external:e.external,target:e.target,separatorLine:e.separatorLine,linePosition:e.linePosition,badges:e.badges,...await y(e,n,t,a),[h]:e?.rbac,additionalProps:e.additionalProps};if(e?.separator!=null||e?.separatorLine!=null){const s=C(a?.navFile);return{type:"separator",metadata:g,version:s?.versionName,versionFolderId:s?.versionFolderPath?R(s.versionFolderPath):void 0,label:e.separator,labelTranslationKey:e.separatorTranslationKey,separatorLine:e.separatorLine,linePosition:e.linePosition,...await y(e,n,t,a),[h]:e?.rbac,additionalProps:e.additionalProps}}else{const s=C(a?.navFile);return{type:"error",label:`Can't resolve page: ${JSON.stringify(e)}`,version:s?.versionName,versionFolderId:s?.versionFolderPath?R(s.versionFolderPath):void 0}}}}async function X(e,i,n,t){const{href:a,page:f}=e;if(a||!f)return;const d=F(n.fs.cwd,t,i,f,n.fs),r=n.fs.exists(d),l=await n.isPathIgnored(d),g=(r?"The page is ignored: ":"Can't resolve page: ")+f;(!r||l)&&S.reportBrokenLink({type:"BROKEN_LINK",brokenLinkType:"LINK",title:e.label||e.group||"",link:d,rawLink:f,message:g,sourceFileRelativePath:i.navFile,sourceFileLocation:{line:0}})}function F(e,i,n,t="",a){if(n.ref!==void 0)return w(v.relative(e,v.resolve(e,v.join(n.ref,t))));if(t.startsWith("/")){const f=B(i)?q(i):void 0,d=f?`${a.localizationFolder}/${f}/`:"",r=t.slice(1);return B(r)?r:d+w(r)}return w(v.relative(e,v.resolve(e,i,t)))}function Z(e,i){const n={};for(const r of e){const l=v.dirname(r.relativePathFromDirectory),g=r.version?M+r.version:"",s=v.basename(r.relativePathFromDirectory)+g;d(l,r,s)}const t=f(a(n));return $(t,i);function a(r){const l=[];for(const g of Object.keys(r)){const s=r[g];l.push({...s,items:s.items?a(s.items):void 0})}return l}function f(r){return r.sort((l,g)=>{const s=typeof l=="string"?l.toLowerCase():l.relativePathFromDirectory||l.label||"",u=typeof g=="string"?g.toLowerCase():g.relativePathFromDirectory||g.label||"";return s.startsWith("index.")?-1:u.startsWith("index.")?1:s.localeCompare(u,void 0,{numeric:!0})})}function d(r,l,g){r==="."&&(r="");const s=r.split("/").filter(Boolean);let u=n;for(const o of s){const p=o+(l?.version||"");u[p]||(u[p]={type:"group",version:l?.version,label:o,isDefault:l?.isDefault,versionFolderId:l.versionFolderId,items:{}}),u=u[p].items}u[g]=l}}function $(e,i){return e.flatMap(n=>n?.sidebarItems?i.groupCustomSidebars?{...n,items:n.sidebarItems,sidebarItems:void 0,relativePathFromDirectory:void 0}:n.sidebarItems:{...n,items:n.items?$(n.items,i):void 0})}function k(e,i,n,t){return e.map(a=>({...a,...i,[h]:a[h]||n,[b]:a[b]||t,items:a.type==="group"&&a.items?k(a.items,i,n):a.items}))}function ee(e){return e.replace(/@(?<!@[^/]*?@)[^/]+\//,"")}async function ye(e,i,n,t){const a=(e?.items||[]).filter(r=>r.linkedSidebars?.length).map(r=>({...r,items:void 0})),f=await N(a,i.contentDir,i,n,t),d=new Map;for(const r of f)if(r.linkedSidebars?.length)for(const l of r.linkedSidebars){if(d.has(l)){A.warn(`Only one navbar item can belong to sidebar. "${l}" on 'linkedSidebars' property on "${r.label}" will be ignored.`);continue}d.set(l,{label:r.label,link:r.link})}return d}export{ye as collectItemsLinkedToSidebars,D as normalizeItems,K as resolveItem,I as resolveItems,N as resolveLinksFromConfig};
|
|
1
|
+
import*as h from"path";import{REDOCLY_ROUTE_RBAC as b,REDOCLY_TEAMS_RBAC as p}from"@redocly/config";import{VERSION_SEPARATOR as x}from"../constants/common.js";import{DEFAULT_LOCALE_PLACEHOLDER as M}from"../../constants/common.js";import{removeTrailingSlash as j}from"../../utils/url/remove-trailing-slash.js";import{isPrimitive as W}from"../../utils/guards/is-primitive.js";import{slash as w}from"../../utils/path/slash.js";import{isAbsoluteUrl as z}from"../../utils/url/is-absolute-url.js";import{normalizeRouteSlug as J}from"../../utils/path/normalize-route-slug.js";import{parsePathVersions as C}from"../../utils/path/parse-path-versions.js";import{logger as A}from"../tools/notifiers/logger.js";import{reporter as F}from"../tools/notifiers/reporter.js";import{shaDirPathShort as R}from"../utils/crypto/sha-dir-path-short.js";import{copyStaticFile as U,FileNotFoundError as D}from"../utils/fs.js";import{isIconPath as H,resolveAssetPath as Y}from"../utils/index.js";import{resolveSrcSet as G}from"../utils/resolve-src-set.js";import{isL10nPath as E}from"../fs/utils/is-l10n-path.js";import{getLocaleFromRelativePath as q}from"../fs/utils/get-locale-from-relative-path.js";async function B(e,i,n,s,a){if(!e)return;const f={...a,ignoreCustomSidebar:!0};if(W(e)){if(typeof e=="string"){const r=I(n.contentDir,i,a,e,s.fs);if(s.fs.exists(r)){if(n.getRouteByFsPath(r))return K({page:e},i,n,s,f);{const g=s.fs.getFileInfo(r);return g?U(n.contentDir,g.realRelativePath,n.outdir):void 0}}}return e}return Array.isArray(e)?k(N(e),i,n,s,f):Object.fromEntries(await Promise.all(Object.entries(e).map(async([r,l])=>[r,await B(l,i,n,s,a)])))}function N(e){return e.map(i=>({...i,items:i.items?N(i.items):void 0}))}async function k(e,i,n,s,a){if(Array.isArray(e))return(await Promise.all(e.map(f=>K(f,i,n,s,a)))).flatMap(f=>f)}async function y(e,i,n,s){let a,f;if(typeof e.icon=="object"){if("srcSet"in e.icon)try{a=await G(e.icon.srcSet,n.fs,{fromFileRelativePath:s.navFile,contentDir:i.contentDir,outdir:i.outdir})}catch(d){d instanceof D?await F.panicOnBuildContentError(`Cannot resolve "item.icon.srcSet" from ${s.navFile}: ${d.message}`):await F.panicOnBuild(`Cannot resolve "item.icon.srcSet" from ${s.navFile}: ${d.message}`)}}else if(typeof e.icon=="string")if(z(e.icon)||H(e.icon))try{f=await Y(e.icon,n.fs,{fromFileRelativePath:s.navFile,contentDir:i.contentDir,outdir:i.outdir})}catch(d){d instanceof D?await F.panicOnBuildContentError(`Cannot resolve "item.icon" from ${s.navFile}: file ${f} does not exist`):await F.panicOnBuild(`Cannot resolve "item.icon" from ${s.navFile}: ${d.message}`)}else f=e.icon;return{icon:f,srcSet:a}}function Q(e){return i=>{if(i.type==="link"||i.type==="group"){const n=i.metadata;return n?Object.entries(e).every(([s,a])=>Array.isArray(a)?a.some(f=>n[s]===f):n[s]===a):!1}return!0}}async function K(e,i,n,s,a){if(e?.tab!==void 0||e?.menu!==void 0){const t=`Navigation tabs ("tab"/"menu") are only allowed at the top level of a sidebars.yaml file: ${JSON.stringify(e)}`;return await F.panicOnBuildContentError(`Invalid nav item in %rp. ${t}`,a.navFile),{type:"error",label:t}}if(e?.directory&&e.items?.length)return{type:"error",label:`Can't have both "directory" and "items" in the nav item: ${JSON.stringify(e)}`};e?.directory&&e?.group&&(e={...e,directory:void 0,items:[{directory:e.directory}]});const f=e?.directory?I(n.contentDir,i,a,e.directory,s.fs):void 0;if(f!==void 0){const t=f===""?"":f+"/",{locale:u,ignoredRoutes:o}=a,v=n.getAllRoutesForLocale(u).filter(c=>!o?.has(c.baseSlug||"")&&c.fsPath.startsWith(t)&&!c.excludeFromSidebar).sort((c,P)=>c.fsPath===P.fsPath?0:c.baseSlug.localeCompare(P.baseSlug,void 0,{numeric:!0})),L=e.includeByMetadata?Q(e.includeByMetadata):Boolean;if(!v.length)return[];const T=(await Promise.all(v.map(async c=>{const P=c.versions?.find(_=>_.active),S=P&&{version:P.version,isDefault:P.default,versionFolderId:P.folderId},V=ee(h.posix.relative(f,c.fsPath)),O=c.getSidebar?.(c);return!a.ignoreCustomSidebar&&O?.length?{type:"group",fsPath:c.fsPath,metadata:c.metadata,link:c.slug,routeSlug:c.slug,label:await c.getNavText?.()||c.slug,[p]:c[p],[b]:c[b],sidebarItems:m(O,S,c[p],c[b]),version:S?.version,isDefault:S?.isDefault,versionFolderId:S?.versionFolderId,relativePathFromDirectory:h.relative(f,c.fsPath).replace(/@(?<!@[^/]*?@)[^/]+\//,"")}:{type:"link",fsPath:c.fsPath,metadata:c.metadata,[p]:c[p],[b]:c[b],label:await c.getNavText?.()||c.slug,link:c.slug,routeSlug:c.slug,version:S?.version,isDefault:S?.isDefault,versionFolderId:S?.versionFolderId,relativePathFromDirectory:V}}))).filter(L);return e.flatten?$(T,a):Z(T,a)}let d=typeof e=="string"?e:e?.page,r,l;if(d){if(d.includes("#")){const[t,u]=d.split("#");u?l=u:A.warn(`Invalid heading anchor format in sidebar: "${u}". Heading anchors should contain only alphanumeric characters, hyphens, and underscores.`),d=t}if(d=j(d),a.locale&&a.locale!==M){const t=J(h.join(a.locale.toLowerCase(),d));r=n.getRouteBySlug(t)}r||(r=n.getRouteBySlug(d)),r||(r=n.getRouteByFsPath(I(n.contentDir,i,a,d,s.fs)))}const g=r?.metadata;if(e?.$ref){let t=I(n.contentDir,i,a,e.$ref,s.fs);if(a.ref!==void 0&&(t=h.posix.join(a.ref,e.$ref)),!s.fs.exists(t))return await F.panicOnBuildContentError(`Failed to load ${t} file. Make sure sidebar $ref path is correct.`),[];const u=await s.cache.load(t,"yaml"),o=h.dirname(t);return await k(u.data,i,n,s,{...a,navFile:t,ref:o})||[]}if(!r){const t=a.excludedFromLinkCheckerPatterns?.catalog.some(o=>o.test(d||"")),u=a.excludedFromLinkCheckerPatterns?.apiFunctions.some(o=>o.test(d||e.href||""));(u||t)&&(e={...e,href:d||e.href,page:void 0,target:e.target??(u?"_blank":void 0)}),await X(e,a,s,i)}if(r&&!e.disconnect&&!a.ignoreCustomSidebar&&(l||r?.getSidebar)){const t=r.versions?.find(o=>o.active),u=t&&{version:t.version,isDefault:t.default,versionFolderId:t.folderId};if(l){let o=[];if(r.getSidebar&&(o=r.getSidebar(r,{...e,...await y(e,n,s,a)}),o&&o.length>0)){const v=o[0];v&&v.link&&(v.link=`${v.link}#${l}`)}if(!o||o.length===0){const v=e.group||e.label||l;o=[{type:"link",fsPath:r.fsPath,metadata:r.metadata,link:`${r.slug}#${l}`,routeSlug:r.slug,label:v,labelTranslationKey:e.labelTranslationKey,...await y(e,n,s,a),[p]:r[p],[b]:{slug:r.slug,fsPath:r.fsPath},...u}]}return m(o,u,r[p],r[b])}if(r?.getSidebar){const o=r.getSidebar(r,{...e,...await y(e,n,s,a)}),v=e.group&&o?.[0]?.routeSlug===r.slug?o.slice(1):o;return m(a.groupCustomSidebars||e.group?[{type:"group",fsPath:r.fsPath,link:r.slug,routeSlug:r.slug,label:e.group||e.label||await r.getNavText?.()||r.slug,items:v,labelTranslationKey:e.labelTranslationKey,metadata:g,badges:e.badges,...await y(e,n,s,a)}]:o,u,r[p],r[b])}}if(r&&!e.group){const t=l?`${r.slug}#${l}`:r.slug;let u=e.label||l||"";u||(u=await r.getNavText?.()||r.slug);const o=r.versions?.find(L=>L.active),v=o&&{version:o.version,isDefault:o.default,versionFolderId:o.folderId};return{type:"link",fsPath:r.fsPath,linkedSidebars:e.linkedSidebars,metadata:g,[p]:e?.rbac||r[p],[b]:{slug:r.slug,fsPath:r.fsPath},label:u,labelTranslationKey:e.labelTranslationKey,link:t,items:await k(e.items,i,n,s,a),separatorLine:e.separatorLine,linePosition:e.linePosition,routeSlug:e.disconnect?void 0:t,external:e.external,target:e.target,badges:e.badges,...await y(e,n,s,a),...v,additionalProps:e.additionalProps}}else if(e?.group){const t=r?.versions?.find(o=>o.active),u=C(a?.navFile);return{type:"group",fsPath:r?.fsPath,linkedSidebars:e.linkedSidebars,metadata:g,version:t?.version||u?.versionName,versionFolderId:t?.folderId||(u?.versionFolderPath?R(u.versionFolderPath):void 0),label:e.group,link:r?.slug,routeSlug:r?.slug,badges:e.badges,labelTranslationKey:e.groupTranslationKey,items:await k(e.items,i,n,s,a),expanded:e.expanded?e.expanded.toString():void 0,selectFirstItemOnExpand:e.selectFirstItemOnExpand,menuStyle:e.menuStyle,separatorLine:e.separatorLine,linePosition:e.linePosition,...await y(e,n,s,a),[p]:e?.rbac,[b]:{slug:r?.slug||"",fsPath:r?.fsPath||""},additionalProps:e.additionalProps}}else{if(e?.href||e?.label)return{type:"link",metadata:g,link:e.href||"",label:e?.label||e?.href||"",labelTranslationKey:e.labelTranslationKey,external:e.external,target:e.target,separatorLine:e.separatorLine,linePosition:e.linePosition,badges:e.badges,...await y(e,n,s,a),[p]:e?.rbac,additionalProps:e.additionalProps};if(e?.separator!=null||e?.separatorLine!=null){const t=C(a?.navFile);return{type:"separator",metadata:g,version:t?.versionName,versionFolderId:t?.versionFolderPath?R(t.versionFolderPath):void 0,label:e.separator,labelTranslationKey:e.separatorTranslationKey,separatorLine:e.separatorLine,linePosition:e.linePosition,...await y(e,n,s,a),[p]:e?.rbac,additionalProps:e.additionalProps}}else{const t=C(a?.navFile);return{type:"error",label:`Can't resolve page: ${JSON.stringify(e)}`,version:t?.versionName,versionFolderId:t?.versionFolderPath?R(t.versionFolderPath):void 0}}}}async function X(e,i,n,s){const{href:a,page:f}=e;if(a||!f)return;const d=I(n.fs.cwd,s,i,f,n.fs),r=n.fs.exists(d),l=await n.isPathIgnored(d),g=(r?"The page is ignored: ":"Can't resolve page: ")+f;(!r||l)&&F.reportBrokenLink({type:"BROKEN_LINK",brokenLinkType:"LINK",title:e.label||e.group||"",link:d,rawLink:f,message:g,sourceFileRelativePath:i.navFile,sourceFileLocation:{line:0}})}function I(e,i,n,s="",a){if(n.ref!==void 0)return w(h.relative(e,h.resolve(e,h.join(n.ref,s))));if(s.startsWith("/")){const f=E(i)?q(i):void 0,d=f?`${a.localizationFolder}/${f}/`:"",r=s.slice(1);return E(r)?r:d+w(r)}return w(h.relative(e,h.resolve(e,i,s)))}function Z(e,i){const n={};for(const r of e){const l=h.dirname(r.relativePathFromDirectory),g=r.version?x+r.version:"",t=h.basename(r.relativePathFromDirectory)+g;d(l,r,t)}const s=f(a(n));return $(s,i);function a(r){const l=[];for(const g of Object.keys(r)){const t=r[g];l.push({...t,items:t.items?a(t.items):void 0})}return l}function f(r){return r.sort((l,g)=>{const t=typeof l=="string"?l.toLowerCase():l.relativePathFromDirectory||l.label||"",u=typeof g=="string"?g.toLowerCase():g.relativePathFromDirectory||g.label||"";return t.startsWith("index.")?-1:u.startsWith("index.")?1:t.localeCompare(u,void 0,{numeric:!0})})}function d(r,l,g){r==="."&&(r="");const t=r.split("/").filter(Boolean);let u=n;for(const o of t){const v=o+(l?.version||"");u[v]||(u[v]={type:"group",version:l?.version,label:o,isDefault:l?.isDefault,versionFolderId:l.versionFolderId,items:{}}),u=u[v].items}u[g]=l}}function $(e,i){return e.flatMap(n=>n?.sidebarItems?i.groupCustomSidebars?{...n,items:n.sidebarItems,sidebarItems:void 0,relativePathFromDirectory:void 0}:n.sidebarItems:{...n,items:n.items?$(n.items,i):void 0})}function m(e,i,n,s){return e.map(a=>({...a,...i,[p]:a[p]||n,[b]:a[b]||s,items:a.type==="group"&&a.items?m(a.items,i,n):a.items}))}function ee(e){return e.replace(/@(?<!@[^/]*?@)[^/]+\//,"")}async function ye(e,i,n,s){const a=(e?.items||[]).filter(r=>r.linkedSidebars?.length).map(({items:r,...l})=>l),f=await B(a,i.contentDir,i,n,s),d=new Map;for(const r of f)if(r.linkedSidebars?.length)for(const l of r.linkedSidebars){if(d.has(l)){A.warn(`Only one navbar item can belong to sidebar. "${l}" on 'linkedSidebars' property on "${r.label}" will be ignored.`);continue}d.set(l,{label:r.label,link:r.link})}return d}export{ye as collectItemsLinkedToSidebars,N as normalizeItems,K as resolveItem,k as resolveItems,B as resolveLinksFromConfig};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import g from"node:path";import{REDOCLY_TEAMS_RBAC as h}from"@redocly/config";import{slash as S}from"../../../utils/path/slash.js";import{canAccessFeature as y}from"../../utils/entitlements/can-access-feature.js";import{staticDataLoader as b}from"./loaders/static-data-loader.js";import{findFrontmatterSlugs as F,getSidebarSharedDataId as P,resolveFrontmatterSlugs as
|
|
1
|
+
import g from"node:path";import{REDOCLY_TEAMS_RBAC as h}from"@redocly/config";import{slash as S}from"../../../utils/path/slash.js";import{canAccessFeature as y}from"../../utils/entitlements/can-access-feature.js";import{staticDataLoader as b}from"./loaders/static-data-loader.js";import{findFrontmatterSlugs as F,getSidebarSharedDataId as P,resolveFrontmatterSlugs as v}from"../utils.js";import{reactFrontmatterLoader as w}from"./loaders/react-frontmatter-loader.js";import{registerPageProps as D}from"../register-page-props.js";import{telemetryTraceStep as R}from"../../../cli/telemetry/helpers/trace-step.js";import{searchResolver as E}from"./search/search-resolver.js";async function x(){return{loaders:{"react-frontmatter":w},id:"react-pages",requiredEntitlements:["reactPages"],processContent:async(r,a)=>{await R("com.redocly.build.plugin.pages",async()=>{const{fs:o,cache:f}=a;for(const n of o.scan(/\.page\.tsx?$/)){const{relativePath:e,realRelativePath:l}=n;if(await a.isPathIgnored(e))continue;const p=r.createTemplate(S(e),g.resolve(o.cwd,l));D(n,o,r);const{data:t}=await f.load(e,"react-frontmatter"),u=await F(e,"react-frontmatter",t,a),s=v(u,e);let c=null;t?.sidebar&&(c=await P(t.sidebar,e,a.fs)),(s.length?s.reverse():[void 0]).forEach(i=>{r.addRoute({excludeFromSearch:t?.excludeFromSearch||!1,slug:i,templateId:p,fsPath:e,sharedData:c?[{id:c,key:"sidebar"}]:void 0,[h]:y("rbac")?t?.rbac:void 0,getNavText:async()=>t?.seo?.title,getStaticData:async(m,d)=>({props:await b({frontmatter:t,relativePath:e},a,d)}),getSearchDocuments:E(t,e,r.getSearchFacets,r.setSearchFacets)})});for(const[i,{to:m,type:d}]of Object.entries(t?.redirects||{}))r.addRedirect(i,{to:m||s[0],type:d||301})}})}}}export{x as customPagesPlugin};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { ContentFs } from '../fs/content-fs.js';
|
|
2
2
|
import type { FileInfo, ProcessContentActions } from '../types';
|
|
3
|
-
export declare function registerPageProps(file: FileInfo, fs: ContentFs, actions: ProcessContentActions):
|
|
3
|
+
export declare function registerPageProps(file: FileInfo, fs: ContentFs, actions: ProcessContentActions): void;
|
|
4
4
|
//# sourceMappingURL=register-page-props.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import a from"path";function l(e,t,p){const{realRelativePath:r,relativePath:o}=e;let s;r.match(/\.md$/)&&(s=r.replace(/\.md$/,".props.ts")),r.match(/\.page\.tsx?$/)&&(s=r.replace(/\.page\.tsx?$/,".props.ts"));const i=s?n(s,t):void 0;i&&p.registerPagePropsGetter(o,i)}function n(e,t){return t.exists(e)?a.resolve(t.cwd,e):void 0}export{l as registerPageProps};
|
|
@@ -4,4 +4,4 @@ import{writeFileSync as at}from"node:fs";import*as g from"path";import{removeMar
|
|
|
4
4
|
- ${y.join(`
|
|
5
5
|
- `)}`,m);return}const{fromDir:k,resolveOptions:n}=V(m,f,p);return Ft(l,k,a,c,n)}))).flat().filter(M),e=vt(r),s=It(e);if(s.length){await I.panicOnBuildContentError(`Navigation tabs conflict in %rp. A route can belong to one tab only:
|
|
6
6
|
- ${s.map(({routeSlug:l,owners:f})=>`${l} is claimed by ${f.map(m=>`"${m}"`).join(" and ")}`).join(`
|
|
7
|
-
- `)}`,i);return}return{routeOwners:e,sharedData:{items:[],tabs:r},navItems:Ct(r)}}function V(t,i,o){return{fromDir:g.dirname(g.join(u,t)),resolveOptions:{locale:i,ignoredRoutes:o,navFile:t,excludedFromLinkCheckerPatterns:T}}}async function rt(t,{routeOwners:i,sharedData:o,navItems:r}){const e=Q(t),s=new Set;for(const{routeSlug:f,items:m}of i){const p=a.getRouteBySlug(f)?.fsPath??"",y=x[p];y&&(Array.from(s).find(n=>n.key===y.key&&n.version===y.version)||s.add(y)),a.addRouteSharedData(f,"sidebar",e),F.set(J(f),m)}const l=s.size===1?Array.from(s)[0]:void 0;return await a.createSharedData(e,{relatedNavbarItem:tt?.get(t),...o,catalogEntity:l?{key:l.key,version:l.version}:void 0}),{firstLink:B(r,f=>!!f.link),resolved:r}}async function K(t,i,o,r=!1){const e=structuredClone(i);o&&o!==E&&e.items.forEach(n=>{n.directory=g.posix.join(d.localizationFolder||"",o,n.directory||"")});const s=await q(e.items,u,a,c,{groupCustomSidebars:!0,locale:o,navFile:Y,excludedFromLinkCheckerPatterns:T});if(!s){await I.panicOnBuild(`Failed to resolve catalog configuration. Make sure catalog ${t} has valid config`);return}Bt(s);const l=o===E?"":"/"+o.toLowerCase(),f=o===E?"":`-${o}`,m=J(g.posix.join(l,e.slug));if(r)for(const n of s){const S=B(n.items||[],v=>!!v.routeSlug);if(!S?.routeSlug)continue;const b=F.get(S.routeSlug);if(!b)continue;const O=N(b,"routeSlug"),R="current-catalog-info-"+n.routeSlug+f,P={catalog:{label:e.title,titleTranslationKey:e.titleTranslationKey,link:m,icon:e?.icon},item:{label:n.metadata?.title,link:n.link,icon:n.icon}};await a.createSharedData(R,P);for(const v of O)a.addRouteSharedData(v,"current-catalog-info",R),C.add(v);const X=B(b,v=>!!v.link&&!v.external&&ut(v.link)&&(!n.version||v.version===n.version));X&&(n.sidebar=[{...X,items:void 0}])}const p={},y=new Set;for(const n of s){if(!(n.routeSlug||n.sidebar?.[0]?.routeSlug)||!n.fsPath||y.has(n.fsPath))continue;y.add(n.fsPath);const b=g.dirname(n.fsPath);p[b]=(p[b]||0)+1}if(r){const n=await a.createSharedData(pt(t,f),s.flatMap(S=>{const b=g.dirname(S.fsPath??""),O=p[b]===1,R=P=>({...P,fsPath:O?b:P.fsPath??""});return S.type==="group"&&S.items?.every(P=>P.type==="group")?(S.items||[]).map(R):R(S)}));a.addRouteSharedData(m,"catalog",n),Ot(o,s,j,p)}const k=N(s,"routeSlug");for(const n of k)C.add(n)}})}}}function Ot(u,a,c,h){if(u===E)for(const d of a){const D=d.routeSlug||d.sidebar?.[0]?.routeSlug;if(!D||!d.fsPath)continue;const F=g.dirname(d.fsPath),C=h[F]===1;c.set(D,{rootFileFsPath:d.fsPath,fsPath:C?F:d.fsPath,metadata:d.metadata||{},title:d.metadata?.title||d.label||"Untitled",version:d.version||"latest"})}}function Bt(u){const a=c=>{typeof c.metadata?.description=="string"&&(c.metadata={...c.metadata,description:nt(c.metadata.description)})};for(const c of u)if(c.routeSlug)a(c);else for(const h of c.items||[])a(h)}function Q(u){return st+dt(u)}async function fe(u,a,c){const h=g.posix.join(g.dirname(u),a);return
|
|
7
|
+
- `)}`,i);return}return{routeOwners:e,sharedData:{items:[],tabs:r},navItems:Ct(r)}}function V(t,i,o){return{fromDir:g.dirname(g.join(u,t)),resolveOptions:{locale:i,ignoredRoutes:o,navFile:t,excludedFromLinkCheckerPatterns:T}}}async function rt(t,{routeOwners:i,sharedData:o,navItems:r}){const e=Q(t),s=new Set;for(const{routeSlug:f,items:m}of i){const p=a.getRouteBySlug(f)?.fsPath??"",y=x[p];y&&(Array.from(s).find(n=>n.key===y.key&&n.version===y.version)||s.add(y)),a.addRouteSharedData(f,"sidebar",e),F.set(J(f),m)}const l=s.size===1?Array.from(s)[0]:void 0;return await a.createSharedData(e,{relatedNavbarItem:tt?.get(t),...o,catalogEntity:l?{key:l.key,version:l.version}:void 0}),{firstLink:B(r,f=>!!f.link),resolved:r}}async function K(t,i,o,r=!1){const e=structuredClone(i);o&&o!==E&&e.items.forEach(n=>{n.directory=g.posix.join(d.localizationFolder||"",o,n.directory||"")});const s=await q(e.items,u,a,c,{groupCustomSidebars:!0,locale:o,navFile:Y,excludedFromLinkCheckerPatterns:T});if(!s){await I.panicOnBuild(`Failed to resolve catalog configuration. Make sure catalog ${t} has valid config`);return}Bt(s);const l=o===E?"":"/"+o.toLowerCase(),f=o===E?"":`-${o}`,m=J(g.posix.join(l,e.slug));if(r)for(const n of s){const S=B(n.items||[],v=>!!v.routeSlug);if(!S?.routeSlug)continue;const b=F.get(S.routeSlug);if(!b)continue;const O=N(b,"routeSlug"),R="current-catalog-info-"+n.routeSlug+f,P={catalog:{label:e.title,titleTranslationKey:e.titleTranslationKey,link:m,icon:e?.icon},item:{label:n.metadata?.title,link:n.link,icon:n.icon}};await a.createSharedData(R,P);for(const v of O)a.addRouteSharedData(v,"current-catalog-info",R),C.add(v);const X=B(b,v=>!!v.link&&!v.external&&ut(v.link)&&(!n.version||v.version===n.version));X&&(n.sidebar=[{...X,items:void 0}])}const p={},y=new Set;for(const n of s){if(!(n.routeSlug||n.sidebar?.[0]?.routeSlug)||!n.fsPath||y.has(n.fsPath))continue;y.add(n.fsPath);const b=g.dirname(n.fsPath);p[b]=(p[b]||0)+1}if(r){const n=await a.createSharedData(pt(t,f),s.flatMap(S=>{const b=g.dirname(S.fsPath??""),O=p[b]===1,R=P=>({...P,fsPath:O?b:P.fsPath??""});return S.type==="group"&&S.items?.every(P=>P.type==="group")?(S.items||[]).map(R):R(S)}));a.addRouteSharedData(m,"catalog",n),Ot(o,s,j,p)}const k=N(s,"routeSlug");for(const n of k)C.add(n)}})}}}function Ot(u,a,c,h){if(u===E)for(const d of a){const D=d.routeSlug||d.sidebar?.[0]?.routeSlug;if(!D||!d.fsPath)continue;const F=g.dirname(d.fsPath),C=h[F]===1;c.set(D,{rootFileFsPath:d.fsPath,fsPath:C?F:d.fsPath,metadata:d.metadata||{},title:d.metadata?.title||d.label||"Untitled",version:d.version||"latest"})}}function Bt(u){const a=c=>{typeof c.metadata?.description=="string"&&(c.metadata={...c.metadata,description:nt(c.metadata.description)})};for(const c of u)if(c.routeSlug)a(c);else for(const h of c.items||[])a(h)}function Q(u){return st+dt(u)}async function fe(u,a,c){const h=g.posix.join(g.dirname(u),a);return c.exists(h)?Q(h):(await I.panicOnBuildContentErrorForRealFile("File %rp: Failed to create relative path for sidebars.yaml using %s",u,c,u,a),null)}function Mt(u){const a=u.replaceAll("/","_");if(a.length>Lt){const c=a.slice(0,Tt),h=gt(u).slice(0,_t).replaceAll("/","_");return c+h}else return a}export{fe as resolveSidebarId,ce as sidebarsPlugin,Bt as stripMarkdocFromDescriptions};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import s from"path";import{collectPropValueDeep as g}from"../../../utils/tree/collect-prop-value-deep.js";import{removeTrailingSlash as f}from"../../../utils/url/remove-trailing-slash.js";import{slash as C}from"../../../utils/path/slash.js";import{removeLeadingSlash as u}from"../../../utils/url/remove-leading-slash.js";import{reporter as p}from"../../tools/notifiers/reporter.js";import{getCompleteCatalogConfig as h}from"../catalog-entities/get-complete-catalog-config.js";async function k({fs:r,cache:i},e,n){const a=new Map;for(const t of n){if(!
|
|
1
|
+
import s from"path";import{collectPropValueDeep as g}from"../../../utils/tree/collect-prop-value-deep.js";import{removeTrailingSlash as f}from"../../../utils/url/remove-trailing-slash.js";import{slash as C}from"../../../utils/path/slash.js";import{removeLeadingSlash as u}from"../../../utils/url/remove-leading-slash.js";import{reporter as p}from"../../tools/notifiers/reporter.js";import{getCompleteCatalogConfig as h}from"../catalog-entities/get-complete-catalog-config.js";async function k({fs:r,cache:i},e,n){const a=new Map;for(const t of n){if(!r.exists(t))continue;const o=s.dirname(s.join(e,t)),m=(await i.load(t,"yaml")).data;try{const c=g(m,"$ref");for(const d of c){const l=C(s.relative(e,s.join(o,d)));a.has(l)&&await p.panicOnBuildContentError(`Sidebar reference '${l}' was used multiple times.`),a.set(l,t)}}catch(c){if(c.message.includes("not iterable")){await p.panicOnBuildContentErrorForRealFile('Invalid sidebar format at %rp, "items" should be an array',t,r,t);continue}throw c}}return a}function v(r){function i(e,n){if(n.includes(e))return[...n,e];const a=r.get(e);if(a){const t=i(a,[...n,e]);if(t)return t}return!1}for(const e of r.keys()){const n=i(e,[]);if(n)return n}return!1}function P(r){const i=r.apiFunctions?.folders?.map(o=>new RegExp(`^/?(${u(f(o))})(/|$)`))||[],e=[new RegExp("^/?(api)(/|$)")],n=h(r.entitiesCatalog),t=Object.values(n?.catalogs||{}).map(o=>o?.slug?new RegExp(`^/?(catalogs\\/${u(f(o?.slug||""))})(/|$)`):null).filter(o=>o!==null);return{apiFunctions:[...i,...e],catalog:t}}export{P as getExcludedFromLinkCheckerPatterns,k as getSidebarReferences,v as hasCircularDependency};
|
package/dist/server/store.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Node } from '@markdoc/markdoc';
|
|
2
2
|
import type { JSX } from 'react';
|
|
3
3
|
import type { CommonError, GlobalData } from '../types/index.js';
|
|
4
|
-
import type { MiddlewareDetails, PageRouteInit, PageRouteDetails, RouteDetails, LifecycleContext, ApiRoute, ParseMarkdocOpts, WildcardRedirectsTree, RedirectOptions, McpToolRegistration, McpToolSchema, MarkdocOptions } from './types';
|
|
4
|
+
import type { MiddlewareDetails, PageRouteInit, PageRouteDetails, RouteDetails, LifecycleContext, ApiRoute, ParseMarkdocOpts, WildcardRedirectsTree, StaticFilePaths, RedirectOptions, McpToolRegistration, McpToolSchema, MarkdocOptions } from './types';
|
|
5
5
|
import type { Skill } from './types/plugins/skills.js';
|
|
6
6
|
import type { RedoclyConfig, RedirectConfig, CompilationError, PageStaticData } from '@redocly/config';
|
|
7
7
|
import type { SearchFacet } from '@redocly/theme/core/types';
|
|
@@ -28,6 +28,7 @@ export type ComputedRedoclyConfig = {
|
|
|
28
28
|
export declare class Store {
|
|
29
29
|
#private;
|
|
30
30
|
routesBySlug: Map<string, PageRouteDetails>;
|
|
31
|
+
staticFilePaths: StaticFilePaths;
|
|
31
32
|
replacedEnvVars: Record<string, {
|
|
32
33
|
original: string;
|
|
33
34
|
replaced: string;
|
|
@@ -112,6 +113,7 @@ export declare class Store {
|
|
|
112
113
|
loadApiDocuments(context: LifecycleContext): Promise<BundledApiDefinition[]>;
|
|
113
114
|
setSearchEngine(engine: SearchEngine): void;
|
|
114
115
|
setSearchFacets: (searchFacets: Map<string, SearchFacet>) => void;
|
|
116
|
+
setStaticFilePaths: (staticFilePaths: StaticFilePaths) => void;
|
|
115
117
|
setGlobalConfig: (data: Partial<RedoclyConfig>) => void;
|
|
116
118
|
getConfig: () => RedoclyConfig;
|
|
117
119
|
getGlobalConfig: <T = unknown>(key: string) => T;
|
package/dist/server/store.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import A from"@markdoc/markdoc";import{parseAndResolveMarkdoc as T}from"./plugins/markdown/compiler.js";import{getPathnameForLocale as M}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as g}from"../constants/common.js";import{DEFAULT_TITLE as O,MARKDOC_PARTIALS_DATA_KEY as m,MARKDOC_PARTIALS_DEPS_KEY as k}from"./constants/common.js";import{GATED_MARKDOC_TAGS as R}from"./constants/entitlements.js";import{isObject as L}from"../utils/guards/is-object.js";import{mapObject as H}from"../utils/object/map-object.js";import{getValueDeep as y}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as D}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as h}from"../utils/path/normalize-route-slug.js";import{isLocalLink as S}from"../utils/path/is-local-link.js";import{reporter as w}from"./tools/notifiers/reporter.js";import{logger as u}from"./tools/notifiers/logger.js";import{sha1 as G}from"./utils/crypto/sha1.js";import{envConfig as _,setEnv as B}from"./config/env-config.js";import{KvService as F}from"./persistence/kv/services/kv-service.js";import{SandboxContext as I}from"./utils/sandbox-context.js";import{writeSharedData as N}from"./utils/index.js";import{renderComponents as V}from"./ssr/render.js";import{readStaticData as q,writeStaticData as U}from"./utils/static-data.js";import{getMarkdocOptions as j}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as v}from"./entitlements/entitlements-provider.js";import{isRouteReserved as x}from"./plugins/get-reserved-routes.js";import{isL10nPath as K}from"./fs/utils/is-l10n-path.js";import{resolveGlobMapValue as J}from"./utils/globs.js";import{replaceEnvVariablesDeep as W}from"./utils/envs/replace-env-variables-deep.js";import{findRedirect as $}from"./utils/redirects/find-redirect.js";import{followRedirectChain as z}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as Y}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as Z}from"../cli/telemetry/helpers/trace-step.js";const E={routesBySlug:"map",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",config:"object",ssr:"object",searchFacets:"map",routesPartials:"map",mcpToolHandlers:"map",skills:"map",devLogin:"object"},P="PLAN_GATES",Q=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],ke="userDefinedApiFunctions";class b{routesBySlug=new Map;replacedEnvVars={};unsetEnvVars=new Set;lifecycleContext;newRoutes=[];#t={};routesByFsPath=new Map;apiRoutes=[];middleware=[];routesSharedData=new Map;sharedDataDeps=new Map;sharedDataMarkdocComponents=new Map;routesDynamicComponents=new Map;routesPartials=new Map;ssr={preBodyTags:[],postBodyTags:[],headTags:[]};searchFacets=new Map;searchEngine;templates=new Map;browserPlugins=new Set;apiRoutesRequestHandlers=new Map;mcpToolHandlers=new Map;skills=new Map;serverPropsGetters=new Map;pagePropsGetters=new Map;listeners=new Map;globalData={};devLogin=!1;#a=void 0;config={configFilePath:"",redirects:{},wildcardRedirectsTree:{},access:{rbac:{},requiresLogin:!1},directoryPermissions:{},devLogin:!1,ssoDirect:{}};#i;serverMode;serverOutDir;outdir;buildRevision=0;hasSitemap=!1;compilationErrors=[];#n;userCodeReady;#s=Promise.resolve();#c;#l=Promise.resolve();#d;#u=Promise.resolve();#h;#e=!1;#r=0;#f=0;#o=new Map;constructor({outdir:e,contentDir:s,serverMode:t=!1,serverOutDir:r}){this.#i=s,this.outdir=e,this.serverMode=t,this.serverOutDir=r,I.register({serverOutDir:r,readRoots:[e,process.cwd()]}),this.userCodeReady=new Promise(o=>{this.#n=o})}on(e,s){const t=this.listeners.get(e);t?t.add(s):this.listeners.set(e,new Set([s]))}queueEvent=(e,s,...t)=>{this.#o.set(e+String(s),[e,s,...t])};runListeners=(e,s,...t)=>{for(const r of this.listeners.get(e)||new Set)s?r(s,...t):r(...t)};startPluginsRun(){this.clear(),this.#s=new Promise(e=>{this.#c=e})}waitForPluginsLifecycle(){return Promise.all([this.#s,this.#l,this.#u])}waitForPluginsRun(){return this.#s}finishPluginsRun(){this.#c?.();for(const e of this.#o.values())this.runListeners(...e);this.#o.clear()}startEsbuildRun(){this.#l=new Promise(e=>{this.#d=e})}finishEsbuildRun(){this.#d?.()}markRequestHandlersEntryOutdated(){this.#r++,!this.#e&&(this.#e=!0,this.#u=new Promise(e=>{this.#h=e}))}noteRequestHandlersEntryBuildStart(){this.#f=this.#r}markRequestHandlersEntryUpdated(){this.#e&&(this.#r>this.#f||(this.#e=!1,this.#h?.()))}get contentDir(){if(this.serverMode)throw new Error("contentDir should not be used in server mode");return this.#i}markUserCodeReady(){this.#n?.(!0)}async reloadMarkdocOptions(){await Z("com.redocly.build.reloadMarkdocOptions",async()=>{const e=v.instance(),s=await j(this.serverOutDir),t=Object.fromEntries(Object.entries(s.tags).filter(([r])=>R[r]!=null?e.canAccessFeature(R[r]):!0));this.#a={...s,tags:t}})}get markdocOptions(){return{...this.#a,partials:this.getGlobalConfig(m),themeConfig:this.config.markdown}}setGlobalData=e=>{const s=this.globalData,t={...this.globalData,...e};this.globalData=t,JSON.stringify(t)!==JSON.stringify(s)&&this.queueEvent("global-data-updated",void 0,t)};getGlobalData=()=>this.globalData;getKv=async()=>F.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:e,context:s,deps:t,resource:r})=>{const{data:{info:o,ast:i},compoundHash:l}=await T(e,this.markdocOptions,{actions:this,context:s},r);for(const a of o.sharedDataDeps||[]){for(const n of t?.routeSlugs||[])this.addRouteSharedData(n,a,a);for(const n of t?.sharedDataIds||[]){const c=this.sharedDataDeps.get(n)||new Set;c.add(a),this.sharedDataDeps.set(n,c)}}for(const a of o.dynamicMarkdocComponents||[]){for(const n of t?.routeSlugs||[]){const c=this.routesDynamicComponents.get(n)||new Set;c.add(a),this.routesDynamicComponents.set(n,c)}for(const n of t?.sharedDataIds||[]){const c=this.sharedDataMarkdocComponents.get(n)||new Set;c.add(a),this.sharedDataMarkdocComponents.set(n,c)}}if(t?.routeSlugs&&o.partials?.length)for(const a of t.routeSlugs){const n=this.routesPartials.get(a)||[];for(const c of o.partials)n.includes(c)||n.push(c);this.routesPartials.set(a,n)}return{info:o,ast:i,compoundHash:l}};async loadApiDocuments(e){return(await e.cache.load(".","api-docs")).data}setSearchEngine(e){this.searchEngine=e}setSearchFacets=e=>{this.searchFacets=e};setGlobalConfig=e=>{const s=Object.keys(e);for(const l of s)for(const a in this.replacedEnvVars)if(a===l||a.startsWith(`${l}:`)){const n=a.split(":"),{error:c,value:d}=y(e,n);(c||d!==this.replacedEnvVars[a].replaced)&&delete this.replacedEnvVars[a]}const{resolvedObj:t,unsetEnvVars:r,replacedValues:o}=W(e);for(const l of r)this.unsetEnvVars.add(l);Object.assign(this.replacedEnvVars,o);const i=t.seo?.siteUrl?.trim();i&&(t.seo={...t.seo,siteUrl:URL.canParse(i)?new URL(i).origin:D(i)}),Object.assign(this.config,t)};getConfig=()=>this.config;getGlobalConfig=e=>this.config[e];getSearchFacets=()=>this.searchFacets;addRedirect=(e,s,t={})=>{if(!v.instance().canAccessFeature("redirects")&&e!=="/")return;this.config.redirects||(this.config.redirects={});const i=h(e).toLowerCase();this.config.redirects[i]=s;const{trackOriginalSource:l=!0}=t,a=this.getGlobalConfig("originalRedirectSources");l&&Array.isArray(a)&&!a.includes(i)&&a.push(i),i.endsWith("*")&&Y(this.config.wildcardRedirectsTree,i)};getRedirect=e=>{const s=h(e).toLowerCase(),t=$(s,this.config.redirects,this.config.wildcardRedirectsTree);if(!t)return null;if(S(t.to)){const r=h(t.to).toLowerCase();if(!r.endsWith("*")&&z(r,[s],this.config.redirects,this.config.wildcardRedirectsTree).type==="cycle")return null}return{to:t.to,type:t.type}};createSharedData=async(e,s,t)=>{if(t&&this.#t[e]===t)return e;const r=JSON.stringify(s),o=t??G(r);return this.#t[e]===o||(this.#t[e]=o,await N(e,r,this.outdir),this.queueEvent("shared-data-updated",e)),e};addRouteSharedData=(e,s,t)=>{const r=D(e),o=this.routesSharedData.get(r)||{};o[s]=t,this.routesSharedData.set(r,o),u.verbose(`Adding shared data to ${e}, ${s}, ${t}`)};getRouteSharedDataByFsPath=e=>{const s=this.routesByFsPath.get(e);return s?this.routesSharedData.get(s)||{}:{}};getPartialsForRoute=e=>{const s=this.getGlobalConfig(m)||{},t=this.routesPartials.get(e);if(!t||t.length===0)return{};const r=this.getGlobalConfig(k)||{},o=new Set(t),i=Array.from(t);for(let a=0;a<i.length;a++){const n=i[a],c=r[n]?.partials??[];for(const d of c)o.has(d)||(o.add(d),i.push(d))}const l={};for(const a of o)s[a]&&(l[a]=s[a]);return l};addRoute=e=>{const t={...J(e.fsPath,this.config.metadataGlobs),...e.metadata||{}};this.newRoutes.push({...e,metadata:t}),u.verbose("Created route %s",e.slug)};addRouteSharedDataToAllLocales=(e,s,t)=>{const r=[g,...this.lifecycleContext?.fs.localeFolders||[]].map(o=>({code:o,name:o}));for(const o of r){const i=M(e,g,o.code,r);this.addRouteSharedData(i,s,t)}};addApiRoute=e=>{this.apiRoutes.push(e),u.verbose("Created API route %s",e.slug)};addMcpTools=(e,s)=>{for(const t of s){const r=this.mcpToolHandlers.get(t.name);r&&r.importPath!==e&&u.warn('MCP tool "%s" from %s overwrites the tool registered from %s',t.name,e,r.importPath),this.mcpToolHandlers.set(t.name,{...t,importPath:e}),u.verbose("Created MCP tool %s",t.name)}};getMcpTools=()=>Array.from(this.mcpToolHandlers.values());markGatedMcpTools=e=>{for(const s of e){const t=this.mcpToolHandlers.get(s);t&&this.mcpToolHandlers.set(s,{...t,hasAvailabilityGate:!0})}};addSkills=e=>{for(const s of e)this.skills.set(s.slug,s),u.verbose("Discovered skill %s",s.slug)};getSkills=()=>Array.from(this.skills.values());removeMcpTools=e=>{for(const s of e)this.mcpToolHandlers.delete(s)};addMiddleware=e=>{this.middleware.push(e),u.verbose("Created middleware %s",e.id)};setResourceResponseHeaders=(e,s)=>{this.config.responseHeaders||(this.config.responseHeaders={});const t=new Set(s.map(i=>i.name.toLowerCase())),o=[...(this.config.responseHeaders[e]??[]).filter(i=>!t.has(i.name.toLowerCase())),...s];this.config.responseHeaders[e]=o};getRouteByFsPath=e=>{const s=this.routesByFsPath.get(e);return s?this.getRouteBySlug(s):void 0};getRouteBySlug=(e,s={})=>{const{followRedirect:t=!0}=s,r=this.getRedirect(e);return t&&r?this.routesBySlug.get(h(r.to)):this.routesBySlug.get(e)};hasRouteOrRedirectBySlug=e=>{if(this.routesBySlug.has(e)||x(e,this.getConfig()))return!0;const s=this.getRedirect(e);if(!s)return!1;if(!S(s.to))return!0;const t=h(s.to);return this.routesBySlug.has(t)};getRoutesByTemplateId=e=>this.newRoutes.filter(s=>s.templateId===e);getAllRoutesForLocale=(e=g)=>{const s=Array.from(this.routesBySlug.values()),t=e.toLowerCase();return s.filter(r=>e===g?!K(r.fsPath):r.slug.startsWith(`/${t}`))};getAllRoutes=()=>Array.from(this.routesBySlug.values());getAllApiRoutes=()=>this.apiRoutes;getAllMiddleware=()=>this.middleware;getTemplate=e=>this.templates.get(e);getRequestHandler=e=>this.apiRoutesRequestHandlers.get(e);createTemplate=(e,s)=>(this.templates.set(e,s),e);addBrowserPlugin=e=>{this.browserPlugins.add(e)};createRequestHandler=(e,s)=>(this.apiRoutesRequestHandlers.set(e,s),e);clearRequestHandlersByPrefix=e=>{for(const s of this.apiRoutesRequestHandlers.keys())s.startsWith(e)&&this.apiRoutesRequestHandlers.delete(s)};registerServerPropsGetter=(e,s)=>(this.serverPropsGetters.set(e,s),e);registerPagePropsGetter=(e,s)=>{this.pagePropsGetters.set(e,s)};async writeRouteStaticData(e,s){const t=await this.resolveRouteStaticData(e,s,!1);t&&U(e.slug,t,this.outdir)}async resolveRouteStaticData(e,s,t){if(this.serverMode)return q(e.slug,this.outdir);const r={...this,contentDir:this.contentDir,parseMarkdoc:({input:c,context:d,resource:f})=>this.parseMarkdoc({input:c,context:d,deps:{routeSlugs:[e.slug]},resource:f})},o=await e.getStaticData?.(e,r)||{},i=new Set(this.routesDynamicComponents.get(e.slug)),l=this.routesSharedData.get(e.slug)||{};for(const c of Object.values(l)){const d=this.sharedDataMarkdocComponents.get(c);d&&d.forEach(p=>i.add(p));const f=this.sharedDataDeps.get(c);f&&f.forEach(p=>this.addRouteSharedData(e.slug,p,p))}const a=this.getGlobalConfig("seo"),n=o?.frontmatter||{};return{...o,frontmatter:{...n,seo:{...n?.seo,title:n?.seo?.title||await e.getNavText?.()}},props:{...o.props,dynamicMarkdocComponents:Array.from(i),metadata:{...o?.props?.metadata,...e.metadata},seo:{title:O,...a,...o.props?.seo,siteUrl:a?.siteUrl},compilationErrors:this.compilationErrors},lastModified:t||!e.fsPath?null:await this.lifecycleContext?.fs.getLastModified(e.fsPath)}}addSsrComponents(e,s){if(!e?.length)return;const t=typeof e[0]=="string"?e.join(""):V(e);t&&(s==="head"?this.ssr.headTags.push(t):s==="preBody"?this.ssr.preBodyTags.push(t):this.ssr.postBodyTags.push(t))}clear=()=>{this.routesByFsPath.clear(),this.templates.clear(),this.newRoutes=[],this.routesBySlug.clear(),this.apiRoutes=[],this.middleware=[],this.routesSharedData.clear(),this.sharedDataDeps.clear(),this.sharedDataMarkdocComponents.clear(),this.routesDynamicComponents.clear(),this.routesPartials.clear(),this.skills.clear(),this.config.redirects={},this.config.wildcardRedirectsTree={},this.config.directoryPermissions={},this.ssr={preBodyTags:[],postBodyTags:[],headTags:[]}};async toJson(){const e=[];for(const[t,r]of Object.entries(E))switch(r){case"map":{const o=Array.from(this[t].entries());e.push([t,o]);break}case"object":if(t==="config"){e.push([t,await this.getConfigWithEnvPlaceholders()]);break}e.push([t,this[t]]);break;default:throw new Error("Invalid format")}const s=Object.fromEntries(e);return s[P]=_.PLAN_GATES,s}static fromJson(e,s){const t=new b(s);for(const[o,i]of Object.entries(E))switch(i){case"map":t[o]=new Map(e[o]);break;case"object":if(o==="config"){t.setGlobalConfig(e[o]);break}t[o]=e[o];break;default:throw new Error("Invalid format")}t.config[m]=X(t.config[m]||{});const r=e[P];return r&&B("PLAN_GATES",r),t}async getConfigWithEnvPlaceholders(){const e=JSON.parse(JSON.stringify(this.config));for(const s in this.replacedEnvVars){const{original:t}=this.replacedEnvVars[s],r=s.split(":"),o=r.pop(),{error:i,value:l}=y(e,r);if(i||!L(l)&&!Array.isArray(l)){await w.panicOnBuild(`Failed to replace env var with env name for ${s}`);continue}l[o]=t}return e}async reportUnsetEnvVars(){if(this.unsetEnvVars.size===0)return;const e=Array.from(this.unsetEnvVars).filter(t=>!Q.includes(t));if(e.length===0)return;const s=`Failed to resolve config. The following environment variables are not set: ${e.join(", ")}`;await w.panicOnBuildContentError(s)}}function X(C){return H(C,e=>A.Ast.fromJSON(JSON.stringify(e)))}export{b as Store,ke as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
|
|
1
|
+
import A from"@markdoc/markdoc";import{parseAndResolveMarkdoc as T}from"./plugins/markdown/compiler.js";import{getPathnameForLocale as M}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as g}from"../constants/common.js";import{DEFAULT_TITLE as O,MARKDOC_PARTIALS_DATA_KEY as m,MARKDOC_PARTIALS_DEPS_KEY as k}from"./constants/common.js";import{GATED_MARKDOC_TAGS as R}from"./constants/entitlements.js";import{isObject as L}from"../utils/guards/is-object.js";import{mapObject as H}from"../utils/object/map-object.js";import{getValueDeep as y}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as D}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as u}from"../utils/path/normalize-route-slug.js";import{isLocalLink as S}from"../utils/path/is-local-link.js";import{reporter as w}from"./tools/notifiers/reporter.js";import{logger as h}from"./tools/notifiers/logger.js";import{sha1 as F}from"./utils/crypto/sha1.js";import{envConfig as G,setEnv as _}from"./config/env-config.js";import{KvService as B}from"./persistence/kv/services/kv-service.js";import{SandboxContext as I}from"./utils/sandbox-context.js";import{writeSharedData as N}from"./utils/index.js";import{renderComponents as V}from"./ssr/render.js";import{readStaticData as q,writeStaticData as j}from"./utils/static-data.js";import{getMarkdocOptions as U}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as v}from"./entitlements/entitlements-provider.js";import{isRouteReserved as x}from"./plugins/get-reserved-routes.js";import{isL10nPath as K}from"./fs/utils/is-l10n-path.js";import{resolveGlobMapValue as J}from"./utils/globs.js";import{replaceEnvVariablesDeep as W}from"./utils/envs/replace-env-variables-deep.js";import{findRedirect as $}from"./utils/redirects/find-redirect.js";import{followRedirectChain as z}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as Y}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as Z}from"../cli/telemetry/helpers/trace-step.js";const E={routesBySlug:"map",staticFilePaths:"object",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",config:"object",ssr:"object",searchFacets:"map",routesPartials:"map",mcpToolHandlers:"map",skills:"map",devLogin:"object"},P="PLAN_GATES",Q=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],ke="userDefinedApiFunctions";class b{routesBySlug=new Map;staticFilePaths={};replacedEnvVars={};unsetEnvVars=new Set;lifecycleContext;newRoutes=[];#t={};routesByFsPath=new Map;apiRoutes=[];middleware=[];routesSharedData=new Map;sharedDataDeps=new Map;sharedDataMarkdocComponents=new Map;routesDynamicComponents=new Map;routesPartials=new Map;ssr={preBodyTags:[],postBodyTags:[],headTags:[]};searchFacets=new Map;searchEngine;templates=new Map;browserPlugins=new Set;apiRoutesRequestHandlers=new Map;mcpToolHandlers=new Map;skills=new Map;serverPropsGetters=new Map;pagePropsGetters=new Map;listeners=new Map;globalData={};devLogin=!1;#o=void 0;config={configFilePath:"",redirects:{},wildcardRedirectsTree:{},access:{rbac:{},requiresLogin:!1},directoryPermissions:{},devLogin:!1,ssoDirect:{}};#i;serverMode;serverOutDir;outdir;buildRevision=0;hasSitemap=!1;compilationErrors=[];#n;userCodeReady;#s=Promise.resolve();#c;#l=Promise.resolve();#d;#h=Promise.resolve();#u;#e=!1;#r=0;#f=0;#a=new Map;constructor({outdir:e,contentDir:s,serverMode:t=!1,serverOutDir:r}){this.#i=s,this.outdir=e,this.serverMode=t,this.serverOutDir=r,I.register({serverOutDir:r,readRoots:[e,process.cwd()]}),this.userCodeReady=new Promise(a=>{this.#n=a})}on(e,s){const t=this.listeners.get(e);t?t.add(s):this.listeners.set(e,new Set([s]))}queueEvent=(e,s,...t)=>{this.#a.set(e+String(s),[e,s,...t])};runListeners=(e,s,...t)=>{for(const r of this.listeners.get(e)||new Set)s?r(s,...t):r(...t)};startPluginsRun(){this.clear(),this.#s=new Promise(e=>{this.#c=e})}waitForPluginsLifecycle(){return Promise.all([this.#s,this.#l,this.#h])}waitForPluginsRun(){return this.#s}finishPluginsRun(){this.#c?.();for(const e of this.#a.values())this.runListeners(...e);this.#a.clear()}startEsbuildRun(){this.#l=new Promise(e=>{this.#d=e})}finishEsbuildRun(){this.#d?.()}markRequestHandlersEntryOutdated(){this.#r++,!this.#e&&(this.#e=!0,this.#h=new Promise(e=>{this.#u=e}))}noteRequestHandlersEntryBuildStart(){this.#f=this.#r}markRequestHandlersEntryUpdated(){this.#e&&(this.#r>this.#f||(this.#e=!1,this.#u?.()))}get contentDir(){if(this.serverMode)throw new Error("contentDir should not be used in server mode");return this.#i}markUserCodeReady(){this.#n?.(!0)}async reloadMarkdocOptions(){await Z("com.redocly.build.reloadMarkdocOptions",async()=>{const e=v.instance(),s=await U(this.serverOutDir),t=Object.fromEntries(Object.entries(s.tags).filter(([r])=>R[r]!=null?e.canAccessFeature(R[r]):!0));this.#o={...s,tags:t}})}get markdocOptions(){return{...this.#o,partials:this.getGlobalConfig(m),themeConfig:this.config.markdown}}setGlobalData=e=>{const s=this.globalData,t={...this.globalData,...e};this.globalData=t,JSON.stringify(t)!==JSON.stringify(s)&&this.queueEvent("global-data-updated",void 0,t)};getGlobalData=()=>this.globalData;getKv=async()=>B.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:e,context:s,deps:t,resource:r})=>{const{data:{info:a,ast:i},compoundHash:l}=await T(e,this.markdocOptions,{actions:this,context:s},r);for(const o of a.sharedDataDeps||[]){for(const n of t?.routeSlugs||[])this.addRouteSharedData(n,o,o);for(const n of t?.sharedDataIds||[]){const c=this.sharedDataDeps.get(n)||new Set;c.add(o),this.sharedDataDeps.set(n,c)}}for(const o of a.dynamicMarkdocComponents||[]){for(const n of t?.routeSlugs||[]){const c=this.routesDynamicComponents.get(n)||new Set;c.add(o),this.routesDynamicComponents.set(n,c)}for(const n of t?.sharedDataIds||[]){const c=this.sharedDataMarkdocComponents.get(n)||new Set;c.add(o),this.sharedDataMarkdocComponents.set(n,c)}}if(t?.routeSlugs&&a.partials?.length)for(const o of t.routeSlugs){const n=this.routesPartials.get(o)||[];for(const c of a.partials)n.includes(c)||n.push(c);this.routesPartials.set(o,n)}return{info:a,ast:i,compoundHash:l}};async loadApiDocuments(e){return(await e.cache.load(".","api-docs")).data}setSearchEngine(e){this.searchEngine=e}setSearchFacets=e=>{this.searchFacets=e};setStaticFilePaths=e=>{this.staticFilePaths=e};setGlobalConfig=e=>{const s=Object.keys(e);for(const l of s)for(const o in this.replacedEnvVars)if(o===l||o.startsWith(`${l}:`)){const n=o.split(":"),{error:c,value:d}=y(e,n);(c||d!==this.replacedEnvVars[o].replaced)&&delete this.replacedEnvVars[o]}const{resolvedObj:t,unsetEnvVars:r,replacedValues:a}=W(e);for(const l of r)this.unsetEnvVars.add(l);Object.assign(this.replacedEnvVars,a);const i=t.seo?.siteUrl?.trim();i&&(t.seo={...t.seo,siteUrl:URL.canParse(i)?new URL(i).origin:D(i)}),Object.assign(this.config,t)};getConfig=()=>this.config;getGlobalConfig=e=>this.config[e];getSearchFacets=()=>this.searchFacets;addRedirect=(e,s,t={})=>{if(!v.instance().canAccessFeature("redirects")&&e!=="/")return;this.config.redirects||(this.config.redirects={});const i=u(e).toLowerCase();this.config.redirects[i]=s;const{trackOriginalSource:l=!0}=t,o=this.getGlobalConfig("originalRedirectSources");l&&Array.isArray(o)&&!o.includes(i)&&o.push(i),i.endsWith("*")&&Y(this.config.wildcardRedirectsTree,i)};getRedirect=e=>{const s=u(e).toLowerCase(),t=$(s,this.config.redirects,this.config.wildcardRedirectsTree,this.staticFilePaths);if(!t)return null;if(S(t.to)){const r=u(t.to).toLowerCase();if(!r.endsWith("*")&&z(r,[s],this.config.redirects,this.config.wildcardRedirectsTree).type==="cycle")return null}return{to:t.to,type:t.type}};createSharedData=async(e,s,t)=>{if(t&&this.#t[e]===t)return e;const r=JSON.stringify(s),a=t??F(r);return this.#t[e]===a||(this.#t[e]=a,await N(e,r,this.outdir),this.queueEvent("shared-data-updated",e)),e};addRouteSharedData=(e,s,t)=>{const r=D(e),a=this.routesSharedData.get(r)||{};a[s]=t,this.routesSharedData.set(r,a),h.verbose(`Adding shared data to ${e}, ${s}, ${t}`)};getRouteSharedDataByFsPath=e=>{const s=this.routesByFsPath.get(e);return s?this.routesSharedData.get(s)||{}:{}};getPartialsForRoute=e=>{const s=this.getGlobalConfig(m)||{},t=this.routesPartials.get(e);if(!t||t.length===0)return{};const r=this.getGlobalConfig(k)||{},a=new Set(t),i=Array.from(t);for(let o=0;o<i.length;o++){const n=i[o],c=r[n]?.partials??[];for(const d of c)a.has(d)||(a.add(d),i.push(d))}const l={};for(const o of a)s[o]&&(l[o]=s[o]);return l};addRoute=e=>{const t={...J(e.fsPath,this.config.metadataGlobs),...e.metadata||{}};this.newRoutes.push({...e,metadata:t}),h.verbose("Created route %s",e.slug)};addRouteSharedDataToAllLocales=(e,s,t)=>{const r=[g,...this.lifecycleContext?.fs.localeFolders||[]].map(a=>({code:a,name:a}));for(const a of r){const i=M(e,g,a.code,r);this.addRouteSharedData(i,s,t)}};addApiRoute=e=>{this.apiRoutes.push(e),h.verbose("Created API route %s",e.slug)};addMcpTools=(e,s)=>{for(const t of s){const r=this.mcpToolHandlers.get(t.name);r&&r.importPath!==e&&h.warn('MCP tool "%s" from %s overwrites the tool registered from %s',t.name,e,r.importPath),this.mcpToolHandlers.set(t.name,{...t,importPath:e}),h.verbose("Created MCP tool %s",t.name)}};getMcpTools=()=>Array.from(this.mcpToolHandlers.values());markGatedMcpTools=e=>{for(const s of e){const t=this.mcpToolHandlers.get(s);t&&this.mcpToolHandlers.set(s,{...t,hasAvailabilityGate:!0})}};addSkills=e=>{for(const s of e)this.skills.set(s.slug,s),h.verbose("Discovered skill %s",s.slug)};getSkills=()=>Array.from(this.skills.values());removeMcpTools=e=>{for(const s of e)this.mcpToolHandlers.delete(s)};addMiddleware=e=>{this.middleware.push(e),h.verbose("Created middleware %s",e.id)};setResourceResponseHeaders=(e,s)=>{this.config.responseHeaders||(this.config.responseHeaders={});const t=new Set(s.map(i=>i.name.toLowerCase())),a=[...(this.config.responseHeaders[e]??[]).filter(i=>!t.has(i.name.toLowerCase())),...s];this.config.responseHeaders[e]=a};getRouteByFsPath=e=>{const s=this.routesByFsPath.get(e);return s?this.getRouteBySlug(s):void 0};getRouteBySlug=(e,s={})=>{const{followRedirect:t=!0}=s,r=this.getRedirect(e);return t&&r?this.routesBySlug.get(u(r.to)):this.routesBySlug.get(e)};hasRouteOrRedirectBySlug=e=>{if(this.routesBySlug.has(e)||x(e,this.getConfig()))return!0;const s=this.getRedirect(e);if(!s)return!1;if(!S(s.to))return!0;const t=u(s.to);return this.routesBySlug.has(t)};getRoutesByTemplateId=e=>this.newRoutes.filter(s=>s.templateId===e);getAllRoutesForLocale=(e=g)=>{const s=Array.from(this.routesBySlug.values()),t=e.toLowerCase();return s.filter(r=>e===g?!K(r.fsPath):r.slug.startsWith(`/${t}`))};getAllRoutes=()=>Array.from(this.routesBySlug.values());getAllApiRoutes=()=>this.apiRoutes;getAllMiddleware=()=>this.middleware;getTemplate=e=>this.templates.get(e);getRequestHandler=e=>this.apiRoutesRequestHandlers.get(e);createTemplate=(e,s)=>(this.templates.set(e,s),e);addBrowserPlugin=e=>{this.browserPlugins.add(e)};createRequestHandler=(e,s)=>(this.apiRoutesRequestHandlers.set(e,s),e);clearRequestHandlersByPrefix=e=>{for(const s of this.apiRoutesRequestHandlers.keys())s.startsWith(e)&&this.apiRoutesRequestHandlers.delete(s)};registerServerPropsGetter=(e,s)=>(this.serverPropsGetters.set(e,s),e);registerPagePropsGetter=(e,s)=>{this.pagePropsGetters.set(e,s)};async writeRouteStaticData(e,s){const t=await this.resolveRouteStaticData(e,s,!1);t&&j(e.slug,t,this.outdir)}async resolveRouteStaticData(e,s,t){if(this.serverMode)return q(e.slug,this.outdir);const r={...this,contentDir:this.contentDir,parseMarkdoc:({input:c,context:d,resource:f})=>this.parseMarkdoc({input:c,context:d,deps:{routeSlugs:[e.slug]},resource:f})},a=await e.getStaticData?.(e,r)||{},i=new Set(this.routesDynamicComponents.get(e.slug)),l=this.routesSharedData.get(e.slug)||{};for(const c of Object.values(l)){const d=this.sharedDataMarkdocComponents.get(c);d&&d.forEach(p=>i.add(p));const f=this.sharedDataDeps.get(c);f&&f.forEach(p=>this.addRouteSharedData(e.slug,p,p))}const o=this.getGlobalConfig("seo"),n=a?.frontmatter||{};return{...a,frontmatter:{...n,seo:{...n?.seo,title:n?.seo?.title||await e.getNavText?.()}},props:{...a.props,dynamicMarkdocComponents:Array.from(i),metadata:{...a?.props?.metadata,...e.metadata},seo:{title:O,...o,...a.props?.seo,siteUrl:o?.siteUrl},compilationErrors:this.compilationErrors},lastModified:t||!e.fsPath?null:await this.lifecycleContext?.fs.getLastModified(e.fsPath)}}addSsrComponents(e,s){if(!e?.length)return;const t=typeof e[0]=="string"?e.join(""):V(e);t&&(s==="head"?this.ssr.headTags.push(t):s==="preBody"?this.ssr.preBodyTags.push(t):this.ssr.postBodyTags.push(t))}clear=()=>{this.routesByFsPath.clear(),this.templates.clear(),this.newRoutes=[],this.routesBySlug.clear(),this.apiRoutes=[],this.middleware=[],this.routesSharedData.clear(),this.sharedDataDeps.clear(),this.sharedDataMarkdocComponents.clear(),this.routesDynamicComponents.clear(),this.routesPartials.clear(),this.skills.clear(),this.config.redirects={},this.config.wildcardRedirectsTree={},this.config.directoryPermissions={},this.ssr={preBodyTags:[],postBodyTags:[],headTags:[]}};async toJson(){const e=[];for(const[t,r]of Object.entries(E))switch(r){case"map":{const a=Array.from(this[t].entries());e.push([t,a]);break}case"object":if(t==="config"){e.push([t,await this.getConfigWithEnvPlaceholders()]);break}e.push([t,this[t]]);break;default:throw new Error("Invalid format")}const s=Object.fromEntries(e);return s[P]=G.PLAN_GATES,s}static fromJson(e,s){const t=new b(s);for(const[a,i]of Object.entries(E))switch(i){case"map":t[a]=new Map(e[a]);break;case"object":if(a==="config"){t.setGlobalConfig(e[a]);break}t[a]=e[a];break;default:throw new Error("Invalid format")}t.config[m]=X(t.config[m]||{});const r=e[P];return r&&_("PLAN_GATES",r),t}async getConfigWithEnvPlaceholders(){const e=JSON.parse(JSON.stringify(this.config));for(const s in this.replacedEnvVars){const{original:t}=this.replacedEnvVars[s],r=s.split(":"),a=r.pop(),{error:i,value:l}=y(e,r);if(i||!L(l)&&!Array.isArray(l)){await w.panicOnBuild(`Failed to replace env var with env name for ${s}`);continue}l[a]=t}return e}async reportUnsetEnvVars(){if(this.unsetEnvVars.size===0)return;const e=Array.from(this.unsetEnvVars).filter(t=>!Q.includes(t));if(e.length===0)return;const s=`Failed to resolve config. The following environment variables are not set: ${e.join(", ")}`;await w.panicOnBuildContentError(s)}}function X(C){return H(C,e=>A.Ast.fromJSON(JSON.stringify(e)))}export{b as Store,ke as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every file of the `static` folder, keyed by its lowercased URL path with the real URL path as the value.
|
|
3
|
+
* Built once per build; files added while developing appear after a restart.
|
|
4
|
+
*/
|
|
5
|
+
export type StaticFilePaths = Record<string, string>;
|
|
6
|
+
//# sourceMappingURL=static-files.d.ts.map
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { StaticFilePaths } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Indexes the files of the `static` folder by their lowercased URL path,
|
|
4
|
+
* so a path can be looked up regardless of its casing.
|
|
5
|
+
*
|
|
6
|
+
* @param contentDir - The project content directory that holds the `static` folder.
|
|
7
|
+
* @returns The index, empty when the folder is missing.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // static/1.0.7/Tags.yaml and static/robots.txt
|
|
11
|
+
* buildStaticFilePaths(contentDir);
|
|
12
|
+
* // Returns: { '/1.0.7/tags.yaml': '/1.0.7/Tags.yaml', '/robots.txt': '/robots.txt' }
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildStaticFilePaths(contentDir: string): StaticFilePaths;
|
|
15
|
+
//# sourceMappingURL=build-static-file-paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import i from"path";import{PUBLIC_STATIC_FOLDER as s}from"../../constants/common.js";import{slash as m}from"../../../utils/path/slash.js";import{readFileNames as c}from"../fs.js";function h(a){const t=i.join(a,s),o={};for(const e of c(t)){const r="/"+m(i.relative(t,e));o[r.toLowerCase()]=r}return o}export{h as buildStaticFilePaths};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as
|
|
1
|
+
import*as o from"dotenv";import*as n from"path";import{simpleGit as p}from"simple-git";import{envConfig as s,reloadEnvConfig as c,setEnv as m}from"../../config/env-config.js";import{readEnvVariable as f}from"../../utils/envs/read-env-variable.js";import{sanitizeBranchName as E}from"../../utils/envs/sanitize-branch-name.js";async function l(e){try{return(await p(e??process.cwd()).revparse(["--abbrev-ref","HEAD"])).trim()}catch{return""}}const h=["REDOCLY_EXP_REPLAY_GRAPHQL_ENABLED"];async function N(e){const a=Object.fromEntries(h.map(t=>[t,f(t)]));o.config({path:n.resolve(e??"",".env")}),c();const i=s.PUBLIC_REDOCLY_BRANCH_NAME||await l(e);if(i){const t=E(i);o.config({path:n.resolve(e??"",`.env.branch.${t}`),override:!0}),c()}const v=s.REDOCLY_ENV;let r;switch(v){case"production":r=".env.production";break;case"preview":r=".env.preview";break;default:r=".env.development";break}o.config({path:n.resolve(e??"",r),override:!0}),m(a)}export{N as loadEnvVariables};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as
|
|
1
|
+
import*as r from"path";import{cliCommandNames as e}from"../../constants/common.js";import{PUBLIC_STATIC_FOLDER as n}from"../constants/common.js";import{logger as m}from"../tools/notifiers/logger.js";import{blue as l,gray as f}from"../tools/notifiers/helpers/colors.js";import{loadEnvVariables as c}from"./envs/load-env-variables.js";import{buildStaticFilePaths as p}from"./content/build-static-file-paths.js";import{copyFolderRecursiveSync as s}from"./fs.js";import{validateInstalledVersion as E}from"./validate-installed-version.js";import{PACKAGE_NAME as P}from"../../config/product-gates.js";import{PORTAL_VERSION as u}from"../version.js";import{setEnv as d}from"../config/env-config.js";function L(o){return o?"true":"false"}function v(o){const t={};o["log-level"]&&(t.REDOCLY_LOG_LEVEL=String(o["log-level"])),t.INSPECT_MODE="inspect"in o?L(o.inspect):"false",o.pathPrefix&&(t.REDOCLY_PREFIX_PATHS=o.pathPrefix),d(t)}function C({contentDir:o,outdir:t}){s(r.join(o,n),r.join(t,n))}async function b(o,t,i){await c(t["project-dir"]);const a=o===e.DEVELOP?" Previewing with":"Building with";switch(m.logInFooter("product",`${a} ${l(`${P}@${u}`)}`),m.logInFooter("server",` \u{1F310} Preview URL: ${f("server starting...")}`),o){case e.DEVELOP:case e.BUILD:case e.PREPARE:v(t);break;default:break}await E(),C(i),i.setStaticFilePaths(p(i.contentDir))}export{b as beforeCommand};
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* LEGACY: classic team folders (`teamFolders`, `teamNamePatterns`, `teamFoldersBaseRoles`).
|
|
3
|
+
* One customer uses it. Remove this module and its callers once that customer runs
|
|
4
|
+
* on Realm Plus, which derives team rules from directories instead (`{folder}` rules).
|
|
5
|
+
*
|
|
2
6
|
* (!) We have a copy of this file in the packages/shared, so we need to keep them in sync.
|
|
3
7
|
*/
|
|
4
8
|
import type { RbacConfig, RbacScopeItems } from '@redocly/config';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getHigherRole as c}from"./rbac.js";function
|
|
1
|
+
import{getHigherRole as c}from"./rbac.js";function R(e,r){if(!e)return;if(!p(e))return e;const o={reunite:{},content:f(e)},n=i(e,r);for(const t of n)o.reunite[t.teamName]=t.projectRole,o.content={...o.content,...Object.fromEntries((e.teamFolders||[]).flatMap(a=>{const l={...e.teamFoldersBaseRoles||{authenticated:"READ"},[t.teamName]:t.projectRole,"redocly.owners":"ADMIN"},m=a.replaceAll("{teamPathSegment}",t.teamPathSegment),s=m+"/**";return[[m,l],[s,l]]}))};return{...e,reunite:{...e.reunite,...o.reunite},content:{...d(e.content||{},o.content)}}}function p(e){return!!(e.teamNamePatterns&&e.teamFolders)}function u(e,r){for(const o of e.teamNamePatterns||[]){if(!o.includes("{teamPathSegment}")&&!o.includes("{projectRole}"))continue;const n="^"+o.replace("{teamPathSegment}","(?<teamPathSegment>[a-zA-Z0-9_.-]+)").replace("{projectRole}","(?<projectRole>admin|maintain|triage|write|read|none)")+"$",a=new RegExp(n,"i").exec(r)?.groups;if(a?.projectRole||a?.teamPathSegment)return{teamName:r,projectRole:a.projectRole?.toUpperCase(),teamPathSegment:a.teamPathSegment?.toLowerCase()}}return null}function g(e,r){for(const o of e?.teamFolders||[]){if(!o.includes("{teamPathSegment}"))continue;const n=`^${o.replace("{teamPathSegment}","(?<teamPathSegment>[a-zA-Z0-9_.-]+)")}($|/)`,t=new RegExp(n,"i");for(const a of r){const l=t.exec(a)?.groups;if(l?.teamPathSegment)return{teamPathSegment:l.teamPathSegment.toLowerCase()}}}return null}function i(e,r){const o=r.map(t=>u(e,t));let n=null;for(const t of o)t?.projectRole&&!t.teamPathSegment&&(n=n?c(n,t.projectRole):t.projectRole);return o.map(t=>!t?.teamPathSegment||!t.projectRole&&!n?null:(t.projectRole=t.projectRole??n??void 0,t)).filter(Boolean)}function f(e){const r={};if(e.teamFoldersBaseRoles)for(const o of e.teamFolders){const n=o.replaceAll("{teamPathSegment}","**");r[n]=e.teamFoldersBaseRoles}return r}function d(e,r){const o={...e};for(const[n,t]of Object.entries(r))o[n]?o[n]={...o[n],...t}:o[n]=t;return o}export{p as canExpandConfig,R as expandRbacConfig,f as getTeamFolderDefaults,g as parseTeamFoldersTemplate,u as parseTeamNameTemplate,i as parseTeams};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import R from"path";import P from"picomatch";import"../node-crypto-polyfill.js";import{REDOCLY_TEAMS_RBAC as x,REDOCLY_ROUTE_RBAC as E}from"@redocly/config";import{MCP_ROUTE as U,PUBLIC_MCP_ROUTE as k}from"@redocly/theme/core/constants/mcp";import{ServerRoutes as w}from"../../constants/api.js";import{DEFAULT_ANONYMOUS_VISITOR_TEAM as D,PUBLIC_RBAC_SCOPE_ITEM as y,RBAC_ALL_OTHER_TEAMS as l,DEFAULT_RBAC_SCOPE as L,GRAPHQL_SPEC_SLUG as M}from"../../constants/common.js";import{DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER as g,PUBLIC_API_DEFINITIONS_FOLDER as C,PUBLIC_ASSETS_FOLDER as B}from"../constants/common.js";import{MCP_CREDENTIALS_ROUTE as b}from"../constants/mcp.js";import{ANONYMOUS_AUTH_DETAILS as W}from"../constants/rbac.js";import{removeTrailingSlash as T}from"../../utils/url/remove-trailing-slash.js";import{removeLeadingSlash as $}from"../../utils/url/remove-leading-slash.js";import{parsePathVersions as v}from"../../utils/path/parse-path-versions.js";import{reporter as Y}from"../tools/notifiers/reporter.js";import{bold as G}from"../tools/notifiers/helpers/colors.js";import{shaDirPathShort as H}from"../utils/crypto/sha-dir-path-short.js";import{isTruthy as z}from"../../utils/guards/is-truthy.js";import{canExpandConfig as K,expandRbacConfig as V,getTeamFolderDefaults as J,parseTeamFoldersTemplate as Q,parseTeamNameTemplate as X}from"./rbac-expand.js";import{getUserParamsFromCookies as Z}from"../web-server/auth.js";import{getDeeperGlobPattern as q}from"./globs.js";import{EntitlementsProvider as tt}from"../entitlements/entitlements-provider.js";const j=["NONE","READ","TRIAGE","WRITE","MAINTAIN","ADMIN"],et=new Set(["x-parsed-md-description","x-parsed-md-summary"]),rt=new Set(Object.values(w).map(t=>T(t.split(":")[0])));function It(t,e){const r=j.indexOf(t.toUpperCase()),n=j.indexOf(e.toUpperCase());return r>n?t:e}const A={};function I(t,e){if(!t?.content)return y;const r=t.content,{slug:n,fsPath:o}=e;if(!n&&!o)return y;const s=a=>{const d=`slug:${a}`,p=A[d]??P(a);A[d]=p;const _=`fsPath:${a}`,S=A[_]??P($(a));return A[_]=S,!!(n&&p(n))||!!(o&&S(o))};if(st(n||o||"")&&Object.keys(r).filter(p=>s(p)).length===0)return r[L]||y;const f=Object.keys(r).filter(a=>s(a));if(f.length==0)return y;const u=f.map(a=>P.scan(a,{tokens:!0,parts:!0}));let h=u[0];for(let a=1;a<u.length;a++)h=q(h,u[a]);return r[h.input]}function F(t,e,r){const n=r?[...e,r]:e,o=[];for(const s of n)t[s]?o.push(t[s]):t[l]&&s!==r&&o.push(t[l]);return o.some(s=>s.toLowerCase()!=="none")}function Ft(t,e,r={},n=!1){if(n&&Object.keys(r).length===0)return e.isAuthenticated;const o=r.features?.[t];return o?F(o,e.teams):!0}function Nt(t,e){return O(t,W,e.access?.rbac||{},e.access?.requiresLogin||!1)}function O(t,e={},r={},n=!1){if(t.slug&&typeof t.slug=="string"&&rt.has(T(t.slug))||(t.slug===U||t.slug===k||t.slug===b)&&tt.instance().canAccessFeature("mcp"))return!0;if(n&&Object.keys(r).length===0)return!!e.isAuthenticated;const o=V(r,e.teams||[]),s=t[x]||I(o,t[E]||{});return Object.keys(s||{}).length===0?!1:Object.keys(s).length===1&&s[l]&&s[l].toLowerCase()!=="none"?!0:F(s,e?.teams||[],e?.email)}function Ut(t,e,r,n){if(!t.startsWith(C)&&!t.startsWith(g))return!0;const o=t.replace(new RegExp(`^${C}/`),"").replace(new RegExp(`^${g}/`),"").replace(new RegExp(`^${M}/`),""),c=o==="."?"":o,i={[E]:{slug:t,fsPath:c},slug:t};return O(i,n,e,r)}function kt(t,e,r,n,o){if(!t.startsWith(B))return!0;const s=t.match(/\..{64}\.([A-Fa-f0-9]{8})\.[^.]+$/)?.[1];if(!s)return!0;const c=n[s];if(!c)return!0;const{base:i,ext:f}=R.parse(t),u=i.split(".")[0],h=f.split(".").join(""),d=c==="."?"":c,p={[E]:{slug:t,fsPath:R.posix.join(d,`${u}.${h}`)},slug:t};return O(p,o,e,r)}async function wt(t,e){const{isAuthenticated:r=!1,idpAccessToken:n,federatedAccessToken:o,federatedIdToken:s,...c}=await Z(t,e),{teams:i=[]}=c;let f;return r?f=i.filter(u=>u!==D):f=[D],{isAuthenticated:r,idpAccessToken:n,teams:f,claims:c}}function N(t,e,r={},n=!1){if(!t)return t;if(Array.isArray(t)){const o=[];for(const s of t){const c=N(s,e,r,n);c!==void 0&&o.push(c)}return o}if(typeof t=="object"){if(!O(t,e,r,n))return;let o=!1;const s={};for(const c in t){if(c===x||c===E)continue;if(et.has(c)){s[c]=t[c];continue}const i=N(t[c],e,r,n);if(!t.link&&(c==="items"||c==="menu"&&t.type==="tab")&&Array.isArray(i)&&i.length===0&&t[c].length!==0){o=!0;continue}i!==void 0&&(s[c]=i),c==="paths"&&nt(i)&&ot(i)}return o?void 0:s}return t}function Mt(t){return typeof t=="string"?t.split(" ").filter(Boolean):Array.isArray(t)?t.map(e=>e.toString()):[]}function Bt(t,e){if(!e)return;const r=e.content;if(!r)return e;const n=Object.entries(r).flatMap(([s,c])=>s===L?[[s,c]]:[[s,c]].concat(t.localeFolders.map(i=>[s.startsWith("/")?`/${i.toLocaleLowerCase()}${s}`:R.posix.join(t.localizationFolder,i,s),c]))),o=Object.fromEntries(n);return{...e,content:o}}async function bt(t,e){if(!e)return{};const r={},n=new Set(
|
|
1
|
+
import R from"path";import P from"picomatch";import"../node-crypto-polyfill.js";import{REDOCLY_TEAMS_RBAC as x,REDOCLY_ROUTE_RBAC as E}from"@redocly/config";import{MCP_ROUTE as U,PUBLIC_MCP_ROUTE as k}from"@redocly/theme/core/constants/mcp";import{ServerRoutes as w}from"../../constants/api.js";import{DEFAULT_ANONYMOUS_VISITOR_TEAM as D,PUBLIC_RBAC_SCOPE_ITEM as y,RBAC_ALL_OTHER_TEAMS as l,DEFAULT_RBAC_SCOPE as L,GRAPHQL_SPEC_SLUG as M}from"../../constants/common.js";import{DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER as g,PUBLIC_API_DEFINITIONS_FOLDER as C,PUBLIC_ASSETS_FOLDER as B}from"../constants/common.js";import{MCP_CREDENTIALS_ROUTE as b}from"../constants/mcp.js";import{ANONYMOUS_AUTH_DETAILS as W}from"../constants/rbac.js";import{removeTrailingSlash as T}from"../../utils/url/remove-trailing-slash.js";import{removeLeadingSlash as $}from"../../utils/url/remove-leading-slash.js";import{parsePathVersions as v}from"../../utils/path/parse-path-versions.js";import{reporter as Y}from"../tools/notifiers/reporter.js";import{bold as G}from"../tools/notifiers/helpers/colors.js";import{shaDirPathShort as H}from"../utils/crypto/sha-dir-path-short.js";import{isTruthy as z}from"../../utils/guards/is-truthy.js";import{canExpandConfig as K,expandRbacConfig as V,getTeamFolderDefaults as J,parseTeamFoldersTemplate as Q,parseTeamNameTemplate as X}from"./rbac-expand.js";import{getUserParamsFromCookies as Z}from"../web-server/auth.js";import{getDeeperGlobPattern as q}from"./globs.js";import{EntitlementsProvider as tt}from"../entitlements/entitlements-provider.js";const j=["NONE","READ","TRIAGE","WRITE","MAINTAIN","ADMIN"],et=new Set(["x-parsed-md-description","x-parsed-md-summary"]),rt=new Set(Object.values(w).map(t=>T(t.split(":")[0])));function It(t,e){const r=j.indexOf(t.toUpperCase()),n=j.indexOf(e.toUpperCase());return r>n?t:e}const A={};function I(t,e){if(!t?.content)return y;const r=t.content,{slug:n,fsPath:o}=e;if(!n&&!o)return y;const s=a=>{const d=`slug:${a}`,p=A[d]??P(a);A[d]=p;const _=`fsPath:${a}`,S=A[_]??P($(a));return A[_]=S,!!(n&&p(n))||!!(o&&S(o))};if(st(n||o||"")&&Object.keys(r).filter(p=>s(p)).length===0)return r[L]||y;const f=Object.keys(r).filter(a=>s(a));if(f.length==0)return y;const u=f.map(a=>P.scan(a,{tokens:!0,parts:!0}));let h=u[0];for(let a=1;a<u.length;a++)h=q(h,u[a]);return r[h.input]}function F(t,e,r){const n=r?[...e,r]:e,o=[];for(const s of n)t[s]?o.push(t[s]):t[l]&&s!==r&&o.push(t[l]);return o.some(s=>s.toLowerCase()!=="none")}function Ft(t,e,r={},n=!1){if(n&&Object.keys(r).length===0)return e.isAuthenticated;const o=r.features?.[t];return o?F(o,e.teams):!0}function Nt(t,e){return O(t,W,e.access?.rbac||{},e.access?.requiresLogin||!1)}function O(t,e={},r={},n=!1){if(t.slug&&typeof t.slug=="string"&&rt.has(T(t.slug))||(t.slug===U||t.slug===k||t.slug===b)&&tt.instance().canAccessFeature("mcp"))return!0;if(n&&Object.keys(r).length===0)return!!e.isAuthenticated;const o=V(r,e.teams||[]),s=t[x]||I(o,t[E]||{});return Object.keys(s||{}).length===0?!1:Object.keys(s).length===1&&s[l]&&s[l].toLowerCase()!=="none"?!0:F(s,e?.teams||[],e?.email)}function Ut(t,e,r,n){if(!t.startsWith(C)&&!t.startsWith(g))return!0;const o=t.replace(new RegExp(`^${C}/`),"").replace(new RegExp(`^${g}/`),"").replace(new RegExp(`^${M}/`),""),c=o==="."?"":o,i={[E]:{slug:t,fsPath:c},slug:t};return O(i,n,e,r)}function kt(t,e,r,n,o){if(!t.startsWith(B))return!0;const s=t.match(/\..{64}\.([A-Fa-f0-9]{8})\.[^.]+$/)?.[1];if(!s)return!0;const c=n[s];if(!c)return!0;const{base:i,ext:f}=R.parse(t),u=i.split(".")[0],h=f.split(".").join(""),d=c==="."?"":c,p={[E]:{slug:t,fsPath:R.posix.join(d,`${u}.${h}`)},slug:t};return O(p,o,e,r)}async function wt(t,e){const{isAuthenticated:r=!1,idpAccessToken:n,federatedAccessToken:o,federatedIdToken:s,...c}=await Z(t,e),{teams:i=[]}=c;let f;return r?f=i.filter(u=>u!==D):f=[D],{isAuthenticated:r,idpAccessToken:n,teams:f,claims:c}}function N(t,e,r={},n=!1){if(!t)return t;if(Array.isArray(t)){const o=[];for(const s of t){const c=N(s,e,r,n);c!==void 0&&o.push(c)}return o}if(typeof t=="object"){if(!O(t,e,r,n))return;let o=!1;const s={};for(const c in t){if(c===x||c===E)continue;if(et.has(c)){s[c]=t[c];continue}const i=N(t[c],e,r,n);if(!t.link&&(c==="items"||c==="menu"&&t.type==="tab")&&Array.isArray(i)&&i.length===0&&t[c].length!==0){o=!0;continue}i!==void 0&&(s[c]=i),c==="paths"&&nt(i)&&ot(i)}return o?void 0:s}return t}function Mt(t){return typeof t=="string"?t.split(" ").filter(Boolean):Array.isArray(t)?t.map(e=>e.toString()):[]}function Bt(t,e){if(!e)return;const r=e.content;if(!r)return e;const n=Object.entries(r).flatMap(([s,c])=>s===L?[[s,c]]:[[s,c]].concat(t.localeFolders.map(i=>[s.startsWith("/")?`/${i.toLocaleLowerCase()}${s}`:R.posix.join(t.localizationFolder,i,s),c]))),o=Object.fromEntries(n);return{...e,content:o}}async function bt(t,e){if(!e)return{};const r={},n=new Set(t.scan().flatMap(({relativePath:o})=>{const{versionFolderPath:s}=v(o)||{},c=R.dirname(o);return s?[s,c]:c}));for(const o of n)r[H(o)]=o;return r}const m=t=>typeof t=="object"&&t!==null&&!Array.isArray(t);function nt(t){return m(t)&&Object.keys(t).length>0}function ot(t){for(const e of Object.keys(t)){const r=t[e];m(r)&&Object.keys(r).length===0&&delete t[e]}}function st(t){return t?t.split("/").filter(Boolean).some(r=>r.startsWith(".")):!1}const ct=t=>{if(t&&m(t)&&("content"in t&&m(t.content)||"reunite"in t&&m(t.reunite)||"features"in t&&m(t.features)||t.teamFolders&&t.teamNamePatterns)){const e=Object.values(t.content||{});if(e.length===0)return!0;if(e.every(m))return e.every(r=>Object.values(r).every(n=>typeof n=="string"))}return!1},Wt=async t=>{if(t){if(Object.keys(t).length===0)return{};if(ct(t))return it(t);await Y.panicOnContentError(`You are using an incorrect format of ${G("rbac:")} configuration. See: https://redocly.com/docs/realm/access`)}},it=t=>{const e={...t};if(e.content){const r={};for(const n in e.content)if(e.content[n]!==void 0){const o=T(n);r[o]=e.content[n]}e.content=r}return e};function $t(t,e){const r=t.fsPath,n=t.slug,o=[],s=K(e)&&(r||n)?Q(e,[r,n].filter(z)):null;if(s){const i=e?.teamNamePatterns?.map(f=>f.replace("{teamPathSegment}",s.teamPathSegment).replace("{projectRole}","read"))??[];o.push(...i)}const c=I(s?{content:{...J(e),...e.content}}:e,t);return o.push(...at(c)),ft(e,o)}function at(t){if(!t)return[];const e=[],r=l in t?{authenticated:t[l],anonymous:t[l]}:{};for(const[n,o]of Object.entries({...r,...t}))o.toLowerCase()!=="none"&&n!==l&&e.push(n);return e}function ft(t,e){return e.map(n=>X(t,n)??{teamName:n}).map(n=>n.projectRole&&n.projectRole!=="READ"?n.teamName?.toLowerCase().replace(n.projectRole?.toLowerCase?.()??"","read")??"":n.teamName?.toLowerCase()??"")}export{j as PROJECT_ROLES_ORDERED_BY_ACCESS_LEVEL,Bt as applyL10nToRbacConfig,kt as canAccessAsset,Ft as canAccessFeature,O as canAccessResource,Ut as canDownloadApiDefinition,ft as expandTeamsForRead,at as extractTeamsFromScopeItems,N as filterDataByAccessDeep,wt as getAuthDetailsFromCookies,It as getHigherRole,$t as getRbacTeamsListForResource,I as getScopeItemsForResource,F as hasScopeAccess,ct as isRbacConfigValid,Nt as isResourcePubliclyAccessible,it as normalizeRbacConfig,Wt as parseRbacConfig,Mt as parseTeamClaimToArray,bt as resolveDirectoryHashes};
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import type { RedirectConfig, RedirectsConfig } from '@redocly/config';
|
|
2
|
-
import type { WildcardRedirectsTree } from '../../types';
|
|
2
|
+
import type { StaticFilePaths, WildcardRedirectsTree } from '../../types';
|
|
3
3
|
/**
|
|
4
4
|
* Looks up a redirect rule for a slug
|
|
5
5
|
*
|
|
6
6
|
* **IMPORTANT:** The `slug` parameter must be normalized before passing to this function.
|
|
7
7
|
*
|
|
8
|
-
* - For wildcard rules whose target ends with `*`, the remainder of the path is preserved
|
|
8
|
+
* - For wildcard rules whose target ends with `*`, the remainder of the path is preserved;
|
|
9
|
+
* a target found in `staticFilePaths` is replaced by that file path, with its real casing.
|
|
9
10
|
* - `from` is the matched config key (path or pattern); exposed for `followRedirectChain` rule tracking.
|
|
10
11
|
*
|
|
11
12
|
* @returns Redirect config with resolved `to`, plus `from`, or `null` if no rule matches or the rule would not change the slug.
|
|
12
13
|
*/
|
|
13
|
-
export declare function findRedirect(slug: string, redirects: RedirectsConfig, wildcardRedirectsTree: WildcardRedirectsTree): (WithRequired<RedirectConfig, 'to'> & {
|
|
14
|
+
export declare function findRedirect(slug: string, redirects: RedirectsConfig, wildcardRedirectsTree: WildcardRedirectsTree, staticFilePaths?: StaticFilePaths): (WithRequired<RedirectConfig, 'to'> & {
|
|
14
15
|
from: string;
|
|
15
16
|
}) | null;
|
|
16
17
|
//# sourceMappingURL=find-redirect.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{normalizeRouteSlug as
|
|
1
|
+
import{normalizeRouteSlug as l}from"../../../utils/path/normalize-route-slug.js";import{removeLeadingSlash as u}from"../../../utils/url/remove-leading-slash.js";function w(t,n,c,e={}){const o=n[t];if(o?.to){const f=o.to;return l(f).toLowerCase()===t?null:{to:f,type:o.type,from:t}}const i=p(t,c);if(!i)return null;const a=n[i],d=a?.to;if(!d)return null;let r=d;if(r.endsWith("*")){const f=i.split("*")[0];r=r.replace(/\*$/,t.substring(f.length)),r=e[l(r).toLowerCase()]??r}return l(r).toLowerCase()===t?null:{...a,to:r,from:i}}function p(t,n){const c=u(t).split("/");let e=n;for(const o of c){if(!e[o])break;e=e[o]}return e["*"]||null}export{w as findRedirect};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import n from"node:path";import{withPathPrefix as f}from"@redocly/theme/core/utils";import{PUBLIC_STATIC_FOLDER as e}from"../constants/common.js";import{isLocalLink as p}from"../../utils/path/is-local-link.js";import{copyStaticFile as u,FileNotFoundError as x}from"./fs.js";async function w(r,o,i){if(!p(r))return r;const t=r.startsWith("/")?n.posix.join(e,r):void 0;if(t&&o.exists(t))return f(r);const c=r.startsWith("/")?r.slice(1):n.posix.join(n.dirname(i.fromFileRelativePath),r),m=o.exists(c)?o.getFileInfo(c):null;if(!m)throw new x(`Cannot resolve asset path: ${r}`,r);return u(i.contentDir,m.realRelativePath,i.outdir)}export{w as resolveAssetPath};
|
|
@@ -155,7 +155,9 @@ export declare const oidcJwksCache: {
|
|
|
155
155
|
jwks: Record<string, (OidcJwk & {
|
|
156
156
|
idpIds: string[];
|
|
157
157
|
}) | null>;
|
|
158
|
+
missedAt: Record<string, number>;
|
|
158
159
|
};
|
|
160
|
+
export declare const UNKNOWN_KID_RETRY_MS = 60000;
|
|
159
161
|
export declare function getOidcMetadata(idpId: string, providerConfig: OidcProviderConfig): Promise<OidcIssuerMetadata>;
|
|
160
162
|
export declare function discoverJwks(ssoConfig: SsoConfig): Promise<void>;
|
|
161
163
|
export declare function getRedoclyTokenPayload(token: string): Promise<Record<string, unknown>>;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import"../node-crypto-polyfill.js";import{DOMParser as j}from"@xmldom/xmldom";import{SignedXml as Z}from"xml-crypto";import ee from"xpath";import{deflateSync as te,inflateSync as ne}from"fflate";import{createHash as re}from"crypto";import{ulid as oe}from"ulid";import{AuthProviderType as f,DEFAULT_TEAM_CLAIM_NAME as U}from"@redocly/config";import{ALTERNATIVE_AUD_CLAIM_NAME as se,AUTH_URL as ie,JWT_SECRET_KEY as y,MAX_AUTH_TOKEN_COOKIE_BYTES as ae}from"../constants/common.js";import{ensureArray as R}from"../../utils/array/ensure-array.js";import{envConfig as ce}from"../config/env-config.js";import{decryptClaimValue as ue}from"../utils/crypto/decrypt-claim-value.js";import{decryptAesGcm as de}from"../utils/crypto/decrypt-aes-gcm.js";import{encryptAesGcm as le}from"../utils/crypto/encrypt-aes-gcm.js";import{getPathPrefix as me,withPathPrefix as pe,withoutPathPrefix as fe}from"@redocly/theme/core/utils";import{ServerRoutes as
|
|
1
|
+
import"../node-crypto-polyfill.js";import{DOMParser as j}from"@xmldom/xmldom";import{SignedXml as Z}from"xml-crypto";import ee from"xpath";import{deflateSync as te,inflateSync as ne}from"fflate";import{createHash as re}from"crypto";import{ulid as oe}from"ulid";import{AuthProviderType as f,DEFAULT_TEAM_CLAIM_NAME as U}from"@redocly/config";import{ALTERNATIVE_AUD_CLAIM_NAME as se,AUTH_URL as ie,JWT_SECRET_KEY as y,MAX_AUTH_TOKEN_COOKIE_BYTES as ae}from"../constants/common.js";import{ensureArray as R}from"../../utils/array/ensure-array.js";import{envConfig as ce}from"../config/env-config.js";import{decryptClaimValue as ue}from"../utils/crypto/decrypt-claim-value.js";import{decryptAesGcm as de}from"../utils/crypto/decrypt-aes-gcm.js";import{encryptAesGcm as le}from"../utils/crypto/encrypt-aes-gcm.js";import{getPathPrefix as me,withPathPrefix as pe,withoutPathPrefix as fe}from"@redocly/theme/core/utils";import{ServerRoutes as K}from"../../constants/api.js";import{AuthCookieNames as I,DEFAULT_AUTHENTICATED_TEAM as he,REQUIRED_OIDC_SCOPES as V}from"../../constants/common.js";import{appendQueryParams as ye}from"../../utils/url/append-query-params.js";import{parsePreviewUrl as B}from"../../utils/url/parse-preview-url.js";import{logger as O}from"../tools/notifiers/logger.js";import{randomString as Ae}from"../utils/crypto/random-string.js";import{randomUUID as M}from"../utils/crypto/random-uuid.js";import{AlgorithmTypes as l,JwtTokenExpired as _e}from"./jwt/types.js";import*as p from"./jwt/jwt.js";import{InternalTokenTypes as x}from"./jwt/token-types.js";import{parseTeamClaimToArray as we}from"../utils/index.js";import{parseTeamNameTemplate as Se}from"../utils/rbac-expand.js";import{arrayBufferToBase64 as Te,decodeBase64 as $,encodeBase64URL as xe,urlSafeBase64 as F}from"./jwt/encode.js";import{formatSamlCertificate as ge}from"./utils/format-saml-certificate.js";const Ie=new Set(Object.values(x));function g(e){return e?.type===f.OIDC}function Ee(e){return e?.type===f.SAML2}async function Mt(e,t){if(g(t))return ke(e,t);if(Ee(t))return Re(e,t)}async function ke(e,t){const n=await G(e,t),r=new Set((t.scopes||[]).concat(V)),o=t.authorizationRequestCustomParams||{};return{type:f.OIDC,idpId:e,name:"OAuth provider",authorizationEndpoint:n.authorization_endpoint,clientId:t.clientId,responseType:"code",scope:Array.from(r).join(" "),extraParams:o,pkce:t.pkce}}function Re(e,t){return{type:f.SAML2,idpId:e,name:"SAML2 provider",ssoUrl:t.ssoUrl,issuerId:t.issuerId,entityId:t.entityId||t.issuerId}}function H(e){return Array.from(new Set((e.scopes||[]).concat(V))).join(" ")}async function W(e,t,n){return await fetch(e,{method:"POST",body:new URLSearchParams({client_id:t.clientId,...t.clientSecret?{client_secret:t.clientSecret}:{},...n}).toString(),headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"}}).then(r=>r.json())}async function Lt(e,t,n,r,o={}){return await W(e,r,{scope:H(r),code:t,redirect_uri:J(n),grant_type:"authorization_code",...o})}async function Nt(e,t,n){return await W(e,n,{scope:H(n),grant_type:"refresh_token",refresh_token:t})}function Oe(e,{authorizationEndpoint:t,clientId:n,responseType:r,scope:o,extraParams:s,idpId:i,pkce:c},d,h,u){if(!t||!n||!r||!o)return{loginUrl:void 0};const a=new URL(t),_=u?.redirectUriOverride??`${e}${pe(K.OIDC_CALLBACK)}`,S={state:M(),idpId:i,redirectUri:_,redirectTo:d,branch:u?.branchOverride??De(e),inviteCode:h,source:u?.sourceOverride??"portal",uiLocales:u?.uiLocales},T={};if(c){const A=F(Ae(50)),E=F(re("sha256").update(A).digest("base64")),k="S256";a.searchParams.append("code_challenge",E),a.searchParams.append("code_challenge_method",k),T.code_verifier={value:A,options:{secure:!0,httpOnly:!0,expires:new Date(Date.now()+1e3*60*10),path:me()||"/"}}}a.searchParams.append("client_id",n),a.searchParams.append("scope",o),a.searchParams.append("response_type",r),a.searchParams.append("redirect_uri",J(_)),a.searchParams.append("state",xe(JSON.stringify(S))),u?.uiLocales&&a.searchParams.append("ui_locales",u.uiLocales);for(const A in s)s[A]!==void 0&&a.searchParams.append(A,s[A]);return{loginUrl:a.toString(),cookies:T}}function Dt(e,t,n,r){const o=new URL(e);return n&&(o.searchParams.append("post_logout_redirect_uri",t),r&&o.searchParams.append("state",r),o.searchParams.append("id_token_hint",n)),o.toString()}function Me(e,t){const n=R(t?.audience??[]);return!n.length||z(e,n)}function z(e,t){const n=[e.aud,e[se]].flat();return t.some(r=>r!==""&&n.includes(r))}function Le(e,t){return!t||!g(t)?!0:Me(e,t)&&Ne(e,t)}function Ne(e,t){return!g(t)||!Ye(t)||!t.clientId?!0:R(e.aud||[]).includes(t.clientId)}function Ct(e){if(!e)return!1;try{return p.decode(e).header.alg===l.RS256}catch{return!1}}async function bt(e){const t=Math.floor(Date.now()/1e3),n=t+(e.ttlSec??600);return p.sign({type:x.MCP_AUTHORIZATION_CODE,client_id:e.clientId,redirect_uri:e.redirectUri,id_token_enc:le(e.idToken,y),...e.idpId?{idp_id:e.idpId}:{},...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},...e.codeChallenge?{code_challenge:e.codeChallenge}:{},...e.codeChallengeMethod?{code_challenge_method:e.codeChallengeMethod}:{},iat:t,exp:n},y,l.HS256)}async function Pt(e){await p.verify(e,y,l.HS256);const{payload:t}=p.decode(e);if(t.type!==x.MCP_AUTHORIZATION_CODE)throw new Error("Invalid authorization code type");if(!t.client_id||!t.redirect_uri)throw new Error("Authorization code missing required claims");if(typeof t.exp=="number"&&Date.now()>=t.exp*1e3)throw new Error("Authorization code expired");if(typeof t.id_token_enc!="string")throw new Error("Authorization code missing session");return{...t,id_token:de(t.id_token_enc,y)}}async function vt(e){const t=Math.floor(Date.now()/1e3);return p.sign({type:x.MCP_REFRESH_TOKEN,client_id:e.clientId,...e.idpId?{idp_id:e.idpId}:{},claims:e.claims,...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},session_exp:e.sessionExp,iat:t,exp:e.sessionExp,jti:M()},y,l.HS256)}async function jt(e){await p.verify(e,y,l.HS256);const{payload:t}=p.decode(e);if(t.type!==x.MCP_REFRESH_TOKEN)throw new Error("Invalid refresh token type");if(!t.client_id||typeof t.claims!="object"||t.claims===null)throw new Error("Refresh token missing required claims");if(typeof t.session_exp!="number")throw new Error("Refresh token missing session expiry");return t}function Ut(e){const t=e||oe(),n=t.startsWith("mcp_")?t:`mcp_${t}`;return{id:n,object:"mcpSession",uri:`urn:redocly:realm:mcp:session:${n}`}}function J(e){const t=B(e);if(!t)return e;const n=Ce(t.hostname);if(n)return t.hostname=`previewauth--${n.previewBranch}${n.after}`,Y(e,t);const r=q(t.hostname);return r?(t.hostname=`${r.projectSlug}.previewauth.${r.after}`,Y(e,t)):e}function De(e){const t=B(e);if(t)return q(t.hostname)?.previewBranch}function Y(e,t){const n=e.replace(/^https?:\/\/[^/?#]+/i,"");return`${t.protocol}//${t.host}${fe(n)}`}function q(e){const t=e.split(".preview.",2);if(t.length<2)return null;const[n,r]=t,o=n.indexOf("--");if(o===-1)return null;const s=n.slice(0,o),i=n.slice(o+2);return!s||!i||i.includes(".")?null:{projectSlug:s,previewBranch:i,after:r}}function Ce(e){const t=e.indexOf("."),n=t===-1?e:e.slice(0,t);if(!n.startsWith("preview-"))return null;const r=n.indexOf("--");if(r===-1)return null;const o=n.slice(r+2);if(!o)return null;const s=t===-1?"":e.slice(t);return{previewBranch:o,after:s}}function be(e){return e.type===f.OIDC}function Pe(e){return e.type===f.SAML2}function Kt(e,t,n,r,o){return be(e)?Oe(t,e,n,r,{uiLocales:o}):Pe(e)?ve(t,e,n,r,o):{}}function ve(e,t,n,r,o){const i=`<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
|
|
2
2
|
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
|
|
3
3
|
Version="2.0"
|
|
4
4
|
ID="_${M()}"
|
|
5
5
|
IssueInstant="${new Date().toISOString()}"
|
|
6
|
-
AssertionConsumerServiceURL="${e}${
|
|
6
|
+
AssertionConsumerServiceURL="${e}${K.SAML_CALLBACK}"
|
|
7
7
|
AttributeConsumingServiceIndex="0">
|
|
8
8
|
<saml:Issuer>${t.entityId}</saml:Issuer>
|
|
9
9
|
<samlp:NameIDPolicy
|
|
10
10
|
AllowCreate="true"
|
|
11
11
|
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"/>
|
|
12
|
-
</samlp:AuthnRequest>`,c=je(i);return{loginUrl:ye(t.ssoUrl,{SAMLRequest:c,RelayState:JSON.stringify({idpId:t.idpId,redirectTo:n,inviteCode:r,source:"portal",uiLocales:o})})}}function je(e){return Te(te(new TextEncoder().encode(e)).buffer)}function
|
|
12
|
+
</samlp:AuthnRequest>`,c=je(i);return{loginUrl:ye(t.ssoUrl,{SAMLRequest:c,RelayState:JSON.stringify({idpId:t.idpId,redirectTo:n,inviteCode:r,source:"portal",uiLocales:o})})}}function je(e){return Te(te(new TextEncoder().encode(e)).buffer)}function Vt(e){const t=$(e);if(t.startsWith("<samlp:Response")||t.indexOf("<saml2p:Response")>-1)return t;const n=ne(new Uint8Array(atob(e).split("").map(r=>r.charCodeAt(0))));return new TextDecoder().decode(n)}function Bt(e){try{return JSON.parse($(e||""))}catch{throw new Error("Invalid OAuth2 state")}}function $t(e){const t=new j().parseFromString(e,"application/xml"),r=m(t,"//*[local-name(.)='StatusCode']/@Value")[0]?.nodeValue?.endsWith("Success")||!1,s=m(t,"//*[local-name(.)='Response']/@Destination")[0]?.nodeValue||"",i=m(t,"//*[local-name(.)='Assertion']//*[local-name(.)='Issuer']/text()")[0],c=i&&i.nodeValue||void 0,d=m(t,"//*[local-name(.)='Audience']/text()")[0],h=d&&d.nodeValue||void 0,a=m(t,"//*[local-name(.)='Assertion']//*[local-name(.)='X509Certificate']/text()")[0]?.nodeValue||"",_=m(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/text()")[0],S=_&&_.nodeValue||"",T=m(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/@Format")[0],A=T&&T.nodeValue||"",E=m(t,"//*[local-name(.)='Conditions']/@NotOnOrAfter")[0],k=Ue(E),D={},C=m(t,"//*[local-name(.)='AttributeStatement']//*[local-name(.)='Attribute']");if(C.length)for(const b of C){const P=m(b,"./@Name")[0];if(P.nodeValue){const v=m(b,"./*[local-name(.)='AttributeValue']/text()")[0];v?.nodeValue&&(D[P.nodeValue]=v.nodeValue)}}return{uid:S,success:r,expiresAt:k,issuerId:c,entityId:h,attrs:D,cert:a,nameFormat:A,destination:s}}function Ue(e){const t=typeof e?.nodeValue=="string"&&L(Date.parse(e.nodeValue)),n=L(Date.now()),r=L(Date.now()+720*60*1e3);return t?t>n&&t<r?r:t:n}function L(e){return Math.floor(e/1e3)}const N={},w={jwks:{},missedAt:{}},Ke=6e4;function Ve(e){const t=w.jwks[e];return t===void 0?!0:t===null&&Date.now()-(w.missedAt[e]??0)>=Ke}async function G(e,t){if(!N[e]){const n=t.configurationUrl?await Q(t.configurationUrl):t.configuration;N[e]=Be()?Fe(n):n}return N[e]}function Be(){const e=ce.REDOCLY_ENFORCE_RESIDENCY;return!!e&&e.includes("host.docker.internal")}const $e=["authorization_endpoint","end_session_endpoint"];function Fe(e){if(typeof e!="object"||e===null)return e;const t={...e};for(const n of Object.keys(t)){if($e.includes(String(n)))continue;const r=t[n];typeof r=="string"&&r.includes("://localhost")&&(t[n]=r.replace("://localhost","://host.docker.internal"))}return t}const He=["kty","n","e","crv","x","y"];function We(e,t){return He.every(n=>e[n]===t[n])}async function ze(e){const t={};for(const n of Object.keys(e)){const r=e[n];if(!g(r))continue;const o=await G(n,r);if(o.jwks_uri){const s=await Q(o.jwks_uri);for(const i of s.keys){const c=t[i.kid],d=c&&We(c,i)?[...c.idpIds,n]:[n];t[i.kid]={...i,idpIds:d},w.jwks[i.kid]=t[i.kid]}}}}function Je(e,t,n){const r=w.jwks[e]?.idpIds??[];return r.find(o=>{const s=n[o];if(!g(s))return!1;const i=R(s.audience??[]);return z(t,i.length?i:[s.clientId])})??r.at(-1)}async function Q(e){return fetch(e,{headers:{Accept:"application/json"}}).then(t=>t.json())}async function Ft(e){return fetch(`${ie}/oidc/userinfo`,{headers:{Accept:"application/json",Authorization:`Bearer ${e}`}}).then(t=>t.status===200?t.json():void 0).catch(()=>{})}function Ye(e){if(!e.configurationUrl)return!1;const t=new URL(e.configurationUrl);return["localhost","127.0.0.1","blueharvest.cloud","bhstage.cloud","cloud.redocly.com","beta.redocly.com","cloud.eu.redocly.com","beta.eu.redocly.com"].some(r=>qe(t.hostname,r))}function qe(e,t){return e===t||e.endsWith(`.${t}`)}async function Ht(e,t){const n=new j().parseFromString(e,"application/xml"),r=m(n,"//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0];if(!r)throw new Error("Cannot find Signature in the SAML response");const o=ge(t),s=new Z({publicCert:o});s.loadSignature(r);try{return s.checkSignature(e)}catch{return!1}}function Ge(e){const t=new Set,n=r=>{if(!(!r||typeof r!="object"||Array.isArray(r)))for(const[o,s]of Object.entries(r))typeof s=="string"?t.add(o):n(s)};return n(e),t}function Qe(e,t,n){if(!t)return e;const r=Ge(t),o=s=>{const i=n?.[s]||s;return r.has(i)||!!Se(t,i)};return[...e.filter(o),...e.filter(s=>!o(s))]}async function Wt(e,t,n,r){const o=n?e[n]:void 0,s=Array.isArray(o)?Qe(o.map(String),r?.rbacConfig,r?.teamsMapping):[];let i=s.length;const c=s.length+1;for(let d=0;d<c;d++){const h={...e,idpId:t};n&&s.length&&(h[n]=s.slice(0,i));const u=await p.sign(h,y,l.HS256),a=Buffer.byteLength(u)-ae;if(a<=0)return{token:u,teamsTotal:s.length,teamsKept:i,droppedTeams:s.slice(i)};if(i===0)return null;const _=Buffer.byteLength(JSON.stringify(s.slice(0,i))),S=Math.max(1,_/i*4/3);i=Math.max(0,i-Math.max(1,Math.ceil(a/S)))}return null}function zt(e,t,n,r){t==="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"&&(e=n["http://schemas.microsoft.com/identity/claims/objectidentifier"]);let o;(t==="urn:oasis:names:tc:SAML:2.0:nameid-format:email"||t==="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")&&(o=e),t==="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"&&e?.match(/^.+@.+/m)&&(o=e);const s=n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],i=s?.match(/^.+@.+/m);return o=o||n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]||(i?s:void 0),o=o?.toLowerCase(),{sub:e,given_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"],family_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"],name:n["http://schemas.microsoft.com/identity/claims/displayname"]||s,email:o,email_verified:!0,teams:r?we(n[r]):[]}}function X(e,t={}){return e.map(n=>t[n]||n)}async function Jt(e,t){if(!t)return{};const n=t[I.AUTHORIZATION];if(!n)return{};try{const r=p.decode(n);if(typeof r.payload.type=="string"&&Ie.has(r.payload.type))return{};if(r.header.alg===l.RS256){const h=Ve(r.header.kid);h&&await ze(e);const u=w.jwks[r.header.kid];if(!u)return h&&(w.missedAt[r.header.kid]=Date.now()),w.jwks[r.header.kid]=null,{};await p.verify(n,u,l.RS256)}else await p.verify(n,y,l.HS256);const s=r.header.alg===l.RS256?Je(r.header.kid,r.payload,e):r.payload.idpId,i=e[s]||{};if(r.header.alg===l.RS256&&typeof r.payload.exp!="number")return O.warn("Rejected id_token: it has no exp claim"),{};if(r.header.alg===l.RS256&&!Le(r.payload,i))return O.warn("Rejected id_token: it was issued for another project"),{};const c=et(i),d=Ze(i);return{...r.payload,email:r.payload.email?.toLowerCase(),idpId:s,teams:Array.from(new Set([...X(r.payload.teams||[],d),..."defaultTeams"in i&&i.defaultTeams||[],...X("teamsClaimName"in i&&r.payload[c||""]||[],d),he])),name:Xe(r.payload),isAuthenticated:!0,idpAccessToken:ue(r.payload.idp_access_token,y)||t[I.IDP_ACCESS_TOKEN],federatedAccessToken:t[I.FEDERATED_ACCESS_TOKEN],federatedIdToken:t[I.FEDERATED_ID_TOKEN],authCookie:n}}catch(r){r instanceof _e||O.error("Malformed JWT token: %s",r.message)}return{}}function Xe(e){return(e.firstName&&e.lastName?`${e.firstName} ${e.lastName}`:e.name||e.given_name||e.firstName||e.lastName)||e.email}function Ze(e){switch(e.type){case f.SAML2:return e.teamsAttributeMap;case f.OIDC:return e.teamsClaimMap;default:return}}function et(e){switch(e.type){case f.SAML2:return e.teamsAttributeName??U;case f.OIDC:return e.teamsClaimName;default:return U}}function m(e,t){return ee.select(t,e)||[]}export{Ke as UNKNOWN_KID_RETRY_MS,Kt as buildLoginUrl,Oe as buildOidcLoginUrl,Dt as buildOidcLogoutUrl,ve as buildSAML2LoginUrl,bt as createMcpAuthorizationCode,vt as createMcpRefreshToken,Ut as createMcpSessionResource,Vt as decodeSamlResponse,ze as discoverJwks,je as encodeSAML2,zt as extractUserClaims,Mt as getAuthProviderLoginParams,ke as getOidcLoginParams,G as getOidcMetadata,Ft as getRedoclyTokenPayload,Re as getSaml2LoginParams,Jt as getUserParamsFromCookies,Xe as getUsernameFromPayload,Me as idTokenAudienceIsAllowed,Le as idTokenIsForThisProject,Ct as isIdpIssuedToken,g as isOidcProviderConfig,Ye as isRedoclySso,Ee as isSaml2ProviderConfig,Lt as oidcExchangeCodeForToken,w as oidcJwksCache,N as oidcMetadataCache,Nt as oidcRefreshTokenGrant,Qe as orderTeamsRbacFirst,Bt as parseOidcState,De as parsePreviewBranch,$t as parseSamlResponse,J as rewritePreviewAuthRedirectUri,Wt as signTokenWithinCookieSizeLimit,Pt as verifyMcpAuthorizationCode,jt as verifyMcpRefreshToken,Ht as verifySAMLResponse};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import"../../node-crypto-polyfill.js";import{timingSafeEqual as H}from"node:crypto";import{utf8ToUint8Array as u,encodeBase64URL as m,arrayBufferToBase64URL as w,decodeBase64URL as S}from"./encode.js";import{decodeJwtSignature as b}from"./decode-jwt-signature.js";import{AlgorithmTypes as s,JwtAlgorithmNotImplemented as E,JwtTokenInvalid as d,JwtTokenNotBefore as K,JwtTokenExpired as T,JwtTokenSignatureMismatched as B,CryptoKeyFormat as h,CryptoKeyUsage as l}from"./types.js";const p=e=>{switch(e.toUpperCase()){case"HS256":return{name:"HMAC",hash:{name:"SHA-256"}};case"HS384":return{name:"HMAC",hash:{name:"SHA-384"}};case"HS512":return{name:"HMAC",hash:{name:"SHA-512"}};case"RS256":return{name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"}};case"ES256":return{name:"ECDSA",namedCurve:"P-256",hash:{name:"SHA-256"}};default:throw new E(e)}},g=async(e,t,n=s.HS256)=>{if(!crypto.subtle||!crypto.subtle.importKey)throw new Error("`crypto.subtle.importKey` is undefined. JWT auth middleware requires it.");t.startsWith("-----BEGIN PRIVATE KEY-----")&&(t=t.replace("-----BEGIN PRIVATE KEY-----","").replace("-----END PRIVATE KEY-----","").split(/\\n|\n/).join("").trim(),t=atob(t));const r=n===s.RS256||n===s.ES256,o=await crypto.subtle.importKey(r?h.PKCS8:h.RAW,r?C(t):u(t),p(n),!1,[l.Sign]);return await crypto.subtle.sign(p(n),o,u(e))};function C(e){const t=new ArrayBuffer(e.length),n=new Uint8Array(t);for(let r=0,o=e.length;r<o;r++)n[r]=e.charCodeAt(r);return t}const V=async(e,t,n=s.HS256,r)=>{const o=
|
|
1
|
+
import"../../node-crypto-polyfill.js";import{timingSafeEqual as H}from"node:crypto";import{utf8ToUint8Array as u,encodeBase64URL as m,arrayBufferToBase64URL as w,decodeBase64URL as S}from"./encode.js";import{decodeJwtSignature as b}from"./decode-jwt-signature.js";import{AlgorithmTypes as s,JwtAlgorithmNotImplemented as E,JwtTokenInvalid as d,JwtTokenNotBefore as K,JwtTokenExpired as T,JwtTokenSignatureMismatched as B,CryptoKeyFormat as h,CryptoKeyUsage as l}from"./types.js";const p=e=>{switch(e.toUpperCase()){case"HS256":return{name:"HMAC",hash:{name:"SHA-256"}};case"HS384":return{name:"HMAC",hash:{name:"SHA-384"}};case"HS512":return{name:"HMAC",hash:{name:"SHA-512"}};case"RS256":return{name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"}};case"ES256":return{name:"ECDSA",namedCurve:"P-256",hash:{name:"SHA-256"}};default:throw new E(e)}},g=async(e,t,n=s.HS256)=>{if(!crypto.subtle||!crypto.subtle.importKey)throw new Error("`crypto.subtle.importKey` is undefined. JWT auth middleware requires it.");t.startsWith("-----BEGIN PRIVATE KEY-----")&&(t=t.replace("-----BEGIN PRIVATE KEY-----","").replace("-----END PRIVATE KEY-----","").split(/\\n|\n/).join("").trim(),t=atob(t));const r=n===s.RS256||n===s.ES256,o=await crypto.subtle.importKey(r?h.PKCS8:h.RAW,r?C(t):u(t),p(n),!1,[l.Sign]);return await crypto.subtle.sign(p(n),o,u(e))};function C(e){const t=new ArrayBuffer(e.length),n=new Uint8Array(t);for(let r=0,o=e.length;r<o;r++)n[r]=e.charCodeAt(r);return t}const V=async(e,t,n=s.HS256,r)=>{const o=m(JSON.stringify(e)),i=`${m(JSON.stringify({alg:n,typ:"JWT",kid:r}))}.${o}`,c=await w(await g(i,t,n));return`${i}.${c}`},k=async(e,t,n=s.HS256,r=!1)=>{const o=e.split(".");if(o.length!==3)throw new d;const{payload:a}=J(e),i=Math.floor(Date.now()/1e3);if(typeof a.nbf=="number"&&a.nbf>i)throw new K;if(!r&&typeof a.exp=="number"&&a.exp<=i)throw new T;const c=o.slice(0,2).join("."),y=o[2];let f=!1;if(typeof t!="string")f=await N(c,y,t,n);else{const A=await w(await g(c,t,n));f=R(A,y)}if(!f)throw new B;return!0};function R(e,t){const n=Buffer.from(e),r=Buffer.from(t);return n.length===r.length&&H(n,r)}const J=e=>{try{const[t,n]=e.split("."),r=JSON.parse(S(t)),o=JSON.parse(S(n));return{header:r,payload:o}}catch{throw new d}},N=async(e,t,n,r)=>{const o=await crypto.subtle.importKey("jwk",n,p(r),!1,[l.Verify]);return await crypto.subtle.verify(p(r),o,b(t),u(e))};export{J as decode,V as sign,k as verify};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{KvService as
|
|
1
|
+
import{KvService as A}from"../../persistence/kv/services/kv-service.js";import{DEFAULT_AUTHENTICATED_TEAM as h}from"../../../constants/common.js";import{envConfig as c}from"../../config/env-config.js";import{fetchWithClient as l}from"../../utils/fetch-with-client.js";import{CACHE_CONTROL_NO_STORE_HEADER_VALUE as y,JWT_SECRET_KEY as T}from"../../constants/common.js";import*as m from"../jwt/jwt.js";import{AlgorithmTypes as f}from"../jwt/types.js";const p=60,I=e=>["POST","PUT","DELETE","PATCH"].includes(e),E=e=>["GET"].includes(e);function O({serverOutDir:e,protectReadMethods:t=!0}){return async(a,r)=>await _(a,r,e,t)}const _=async(e,t,a,r=!0)=>{e.header("Cache-Control",y);const n=e.req.method,i=I(n)||E(n)&&r,o=e.req.header("apiKey");if(o)return await w(e,t,o,a);const s=e.req.header("authorization")?.replace("Bearer ","");return s?await C(e,t,s):i?e.json({message:"API key is required"},401):await t()},w=async(e,t,a,r)=>{if(!c.BH_API_URL||!c.ORGANIZATION_ID)return e.json({message:"API key validation service not configured"},500);try{const n=await A.getInstance({baseDbDir:r}),i=await g(n,a);if(i)return e.set("apiKeyTeams",i.teams),await t();const o=new URL(`/api/orgs/${c.ORGANIZATION_ID}/session`,c.BH_API_URL).toString(),u=await l(o,{method:"GET",headers:{Authorization:`Bearer ${a}`}});if(!u.ok)return e.json({message:"Invalid API key"},401);const s=await u.json(),d=[h,...s.teams];return e.set("apiKeyTeams",d),await n.set(["api-keys","reunite",a],{valid:!0,teams:d},{ttlInSeconds:p}),await t()}catch{return e.json({message:"API key validation failed"},400)}},C=async(e,t,a)=>{try{const r=await m.verify(a,T,f.HS256),n=m.decode(a).payload.isInternalConnection;return!r||!n?e.json({message:"API key is required"},401):await t()}catch{return e.json({message:"API key validation failed"},400)}},g=async(e,t)=>{try{return await e.get(["api-keys","reunite",t])}catch{return null}};export{O as catalogAuthMiddleware};
|