@redocly/redoc-reef 0.140.0-next.8 → 0.140.0-next.9

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 CHANGED
@@ -1,5 +1,12 @@
1
1
  # @redocly/redoc-reef
2
2
 
3
+ ## 0.140.0-next.9
4
+
5
+ ### Minor Changes
6
+
7
+ - c99079a7487: Renamed the `reunite` option and the `access.rbac.reunite` scope in `redocly.yaml` to `platform` and deprecated the old names.
8
+ Using the old names does not fail the build, but produces warnings in the build log.
9
+
3
10
  ## 0.140.0-next.8
4
11
 
5
12
  ### Patch Changes
@@ -1 +1 @@
1
- import w from"path";import{lintConfig as $,loadConfig as E,createConfigTypes as L}from"@redocly/openapi-core";import{rootRedoclyConfigSchema as q}from"@redocly/config";import{deepMerge as x}from"../../../../../utils/object/deep-merge.js";import{isPlainObject as g}from"../../../../../utils/guards/is-plain-object.js";import{logger as d}from"../../../../tools/notifiers/logger.js";import{BRANCH_ENV_PREFIX as j}from"../../../../../constants/common.js";import{sanitizeBranchName as O}from"../../../../utils/envs/sanitize-branch-name.js";import{envConfig as R}from"../../../../config/env-config.js";import{ajv as S,safeParsePartial as B}from"../../safe-parse.js";import{formatConfigProblem as I}from"../../format-error.js";import{ExternalResolver as M}from"../../../../fs/utils/external-ref-resolver.js";import{resolveMutuallyExclusiveProps as N}from"../../resolve-mutual-exclusion.js";function y(e,t,s,r){t in e&&e[t]&&typeof e[t]=="string"&&(/^https?:\/\/.*/.test(e[t])||r(new Error(`Invalid ${s} URL: "${e[t]}". ${s} must start with "http://" or "https://".`)))}function k(e,t){if("access"in e&&e.access&&typeof e.access=="object"){const s=e.access;y(s,"logoutReturnUrl","access.logoutReturnUrl",t),y(s,"residency","access.residency",t);const r=["requiresLogin","logoutReturnUrl","residency","sso","rbac"];for(const n of r)n in s&&s[n]!==void 0&&n in e&&e[n]!==void 0&&t(new Error(`Property '${n}' is defined both at root level and in 'access' object. Please use 'access.${n}' to define this configuration.`))}}function _(e){return S.compile(q.properties.graphql.properties.servers.items.properties.url)(e)}function C(e,t,s){if(g(e)&&Array.isArray(e.servers))for(const r of e.servers){const n=g(r)?r.url:void 0;if(typeof n=="string"&&!_(n)){const c=e.servers.indexOf(r),a=`${t}.servers[${c}].url`;s(new Error(`Invalid ${a}: "${n}". It must be an absolute http:// or https:// URL.`))}}}function D(e,t){if(C(e.graphql,"graphql",t),!!g(e.apis))for(const[s,r]of Object.entries(e.apis))g(r)&&C(r.graphql,`apis.${s}.graphql`,t)}async function ie(e,t,s,r){const c=t.getFileInfo(e)?.realRelativePath||e;async function a(){const p=new M(t),f=w.join(t.cwd,c),l=await E({configPath:f,externalRefResolver:p}),m=await r(l.resolvedConfig);if(m===void 0)return l.resolvedConfig;const h=l.document?.source,P=[...h?await $({config:l,externalConfigTypes:L(m,l)}):[],...h?N(l.resolvedConfig,h):[]];if(P.length>0)for(const U of P)s(new Error(I(U,t.cwd)));return l.resolvedConfig}const i=t.exists(e)?await a():{},T=F(i),b=await r(i);y(i,"residency","Residency",s),y(i,"logoutReturnUrl","Logout return URL",s),k(i,s),D(i,s);let u=b?B(b,i):i;u=z(u);const{env:v}=u;if(v){const p=R.REDOCLY_ENV,f=v[p]||{},l=R.PUBLIC_REDOCLY_BRANCH_NAME||"",m=l?O(l):"",h=m&&v[`${j}${m}`]||{};u=x(u,h,f)}const o=V(u,T);if(o.imports&&o.imports.length>0){d.warn("The 'imports' property is deprecated. Please use 'plugins' property instead.");const p=new Set([...o.plugins||[],...o.imports.map(f=>w.posix.join(f,"plugin.js"))]);o.plugins=Array.from(p),delete o.imports}if(o.catalog&&(d.warn("The 'catalog' property is deprecated. Please use 'catalogClassic' property instead."),o.catalogClassic={...o.catalog},delete o.catalog),o.scorecard&&(d.warn("The 'scorecard' property is deprecated. Please use 'scorecardClassic' property instead."),o.scorecardClassic=o.scorecard,delete o.scorecard),o.search?.ai){d.warn("The 'search.ai' property is deprecated. Please use 'aiAssistant' property instead.");const p={...o.search?.ai,...o.aiAssistant,suggestions:o.aiAssistant?.suggestions?.length?o.aiAssistant.suggestions:o.search?.ai.suggestions||[]};o.aiAssistant=p,delete o.search.ai}return G(o)}function G(e){const t={...e},r={..."access"in e&&e.access&&typeof e.access=="object"?e.access:{}},n=["requiresLogin","logoutReturnUrl","residency","sso","idps","rbac"],c=[];for(const a of n){const i=t[a];i!==void 0&&(c.push(a),a==="requiresLogin"&&r.requiresLogin===void 0&&(r.requiresLogin=i),a==="logoutReturnUrl"&&r.logoutReturnUrl===void 0&&(r.logoutReturnUrl=i),a==="residency"&&r.residency===void 0&&(r.residency=i),a==="sso"&&r.sso===void 0&&(r.sso=i),a==="idps"&&r.idps===void 0&&(r.idps=i),a==="rbac"&&r.rbac===void 0&&(r.rbac=i),delete t[a])}return c.length>0&&d.warn(`The following properties at root level are deprecated: ${c.join(", ")}. Please move them to the 'access' object.`),Object.keys(r).length>0&&(t.access=r),t}function z(e){const t=Object.entries(e.env??{});[e,...t.map(([,n])=>n)].some(n=>n?.mcp?.docs)&&d.warn("The 'mcp.docs' property is deprecated. Please use the 'mcp' properties instead.");const s=e.mcp?{...e,mcp:A(e.mcp)}:e;if(!e.env)return s;const r=t.map(([n,c])=>[n,c?.mcp?{...c,mcp:A(c.mcp)}:c]);return{...s,env:Object.fromEntries(r)}}function A(e){const t=e.docs??{},s={};(e.hide??t.hide)!==void 0&&(s.hide=e.hide===!0||t.hide===!0);const r=e.name??t.name;r!==void 0&&(s.name=r);const n=e.ignore??t.ignore;n!==void 0&&(s.ignore=n);const c=e.publicEndpoint??t.publicEndpoint;return c!==void 0&&(s.publicEndpoint=c),{...e,...s,docs:{...t,...s}}}function F(e){if(!e.theme)return[];d.warn("The 'theme' property in redocly.yaml is deprecated. Please move all of the properties from 'theme' to the root of the config.");const t=[];for(const s of Object.keys(e.theme))e[s]==null?t.push(s):d.warn(`Detected both '${s}' and 'theme.${s}' properties in redocly.yaml. The 'theme.${s}' property will be ignored and needs to be removed or merged into the '${s}'.`);return t}function V(e,t){if(!e.theme||t.length===0)return e;const s={...e};for(const r of t)s[r]=e.theme[r];return delete s.theme,s}export{G as ensureAccessBackwardsCompatibility,ie as readAndValidateConfig};
1
+ import R from"path";import{lintConfig as O,loadConfig as j,createConfigTypes as E}from"@redocly/openapi-core";import{rbacConfigSchema as q,rootRedoclyConfigSchema as S,withPlatformSection as x}from"@redocly/config";import{deepMerge as B}from"../../../../../utils/object/deep-merge.js";import{isPlainObject as y}from"../../../../../utils/guards/is-plain-object.js";import{logger as p}from"../../../../tools/notifiers/logger.js";import{BRANCH_ENV_PREFIX as k}from"../../../../../constants/common.js";import{sanitizeBranchName as I}from"../../../../utils/envs/sanitize-branch-name.js";import{envConfig as C}from"../../../../config/env-config.js";import{ajv as M,safeParsePartial as N}from"../../safe-parse.js";import{formatConfigProblem as _}from"../../format-error.js";import{ExternalResolver as D}from"../../../../fs/utils/external-ref-resolver.js";import{resolveMutuallyExclusiveProps as F}from"../../resolve-mutual-exclusion.js";function v(e,t,s,r){t in e&&e[t]&&typeof e[t]=="string"&&(/^https?:\/\/.*/.test(e[t])||r(new Error(`Invalid ${s} URL: "${e[t]}". ${s} must start with "http://" or "https://".`)))}function G(e,t){if("access"in e&&e.access&&typeof e.access=="object"){const s=e.access;v(s,"logoutReturnUrl","access.logoutReturnUrl",t),v(s,"residency","access.residency",t);const r=["requiresLogin","logoutReturnUrl","residency","sso","rbac"];for(const o of r)o in s&&s[o]!==void 0&&o in e&&e[o]!==void 0&&t(new Error(`Property '${o}' is defined both at root level and in 'access' object. Please use 'access.${o}' to define this configuration.`))}}function z(e){return M.compile(S.properties.graphql.properties.servers.items.properties.url)(e)}function T(e,t,s){if(y(e)&&Array.isArray(e.servers))for(const r of e.servers){const o=y(r)?r.url:void 0;if(typeof o=="string"&&!z(o)){const a=e.servers.indexOf(r),c=`${t}.servers[${a}].url`;s(new Error(`Invalid ${c}: "${o}". It must be an absolute http:// or https:// URL.`))}}}function V(e,t){if(T(e.graphql,"graphql",t),!!y(e.apis))for(const[s,r]of Object.entries(e.apis))y(r)&&T(r.graphql,`apis.${s}.graphql`,t)}async function ue(e,t,s,r){const a=t.getFileInfo(e)?.realRelativePath||e;async function c(){const f=new D(t),m=R.join(t.cwd,a),d=await j({configPath:m,externalRefResolver:f}),h=await r(d.resolvedConfig);if(h===void 0)return d.resolvedConfig;const g=d.document?.source,P=[...g?await O({config:d,externalConfigTypes:E(h,d)}):[],...g?F(d.resolvedConfig,g):[]];if(P.length>0)for(const L of P)s(new Error(_(L,t.cwd)));return d.resolvedConfig}const n=t.exists(e)?await c():{},l=X(n),w=await r(n);v(n,"residency","Residency",s),v(n,"logoutReturnUrl","Logout return URL",s),G(n,s),V(n,s);let u=w?N(w,n):n;u=H(u),u=W(u,s);const{env:b}=u;if(b){const f=C.REDOCLY_ENV,m=b[f]||{},d=C.PUBLIC_REDOCLY_BRANCH_NAME||"",h=d?I(d):"",g=h&&b[`${k}${h}`]||{};u=B(u,g,m)}const i=J(u,l);if(i.imports&&i.imports.length>0){p.warn("The 'imports' property is deprecated. Please use 'plugins' property instead.");const f=new Set([...i.plugins||[],...i.imports.map(m=>R.posix.join(m,"plugin.js"))]);i.plugins=Array.from(f),delete i.imports}if(i.catalog&&(p.warn("The 'catalog' property is deprecated. Please use 'catalogClassic' property instead."),i.catalogClassic={...i.catalog},delete i.catalog),i.scorecard&&(p.warn("The 'scorecard' property is deprecated. Please use 'scorecardClassic' property instead."),i.scorecardClassic=i.scorecard,delete i.scorecard),i.search?.ai){p.warn("The 'search.ai' property is deprecated. Please use 'aiAssistant' property instead.");const f={...i.search?.ai,...i.aiAssistant,suggestions:i.aiAssistant?.suggestions?.length?i.aiAssistant.suggestions:i.search?.ai.suggestions||[]};i.aiAssistant=f,delete i.search.ai}return Y(i)}function Y(e){const t={...e},r={..."access"in e&&e.access&&typeof e.access=="object"?e.access:{}},o=["requiresLogin","logoutReturnUrl","residency","sso","idps","rbac"],a=[];for(const c of o){const n=t[c];n!==void 0&&(a.push(c),c==="requiresLogin"&&r.requiresLogin===void 0&&(r.requiresLogin=n),c==="logoutReturnUrl"&&r.logoutReturnUrl===void 0&&(r.logoutReturnUrl=n),c==="residency"&&r.residency===void 0&&(r.residency=n),c==="sso"&&r.sso===void 0&&(r.sso=n),c==="idps"&&r.idps===void 0&&(r.idps=n),c==="rbac"&&r.rbac===void 0&&(r.rbac=n),delete t[c])}return a.length>0&&p.warn(`The following properties at root level are deprecated: ${a.join(", ")}. Please move them to the 'access' object.`),Object.keys(r).length>0&&(t.access=r),t}function H(e){const t=Object.entries(e.env??{});[e,...t.map(([,o])=>o)].some(o=>o?.mcp?.docs)&&p.warn("The 'mcp.docs' property is deprecated. Please use the 'mcp' properties instead.");const s=e.mcp?{...e,mcp:A(e.mcp)}:e;if(!e.env)return s;const r=t.map(([o,a])=>[o,a?.mcp?{...a,mcp:A(a.mcp)}:a]);return{...s,env:Object.fromEntries(r)}}function A(e){const t=e.docs??{},s={};(e.hide??t.hide)!==void 0&&(s.hide=e.hide===!0||t.hide===!0);const r=e.name??t.name;r!==void 0&&(s.name=r);const o=e.ignore??t.ignore;o!==void 0&&(s.ignore=o);const a=e.publicEndpoint??t.publicEndpoint;return a!==void 0&&(s.publicEndpoint=a),{...e,...s,docs:{...t,...s}}}function K(e){if(!y(e)||e.platform===void 0)return!1;const t=Object.keys(e).filter(s=>s!=="platform");return t.length>0&&t.every(s=>!Object.hasOwn(q.properties,s))}function W(e,t){const s=Object.entries(e.env??{}),r=new Set;let o=!1;for(const n of[e,...s.map(([,l])=>l)])n?.reunite!==void 0&&r.add("The 'reunite' property is deprecated. Please use 'platform' property instead."),n?.reunite!==void 0&&n?.platform!==void 0&&r.add("Both 'platform' and 'reunite' are set. 'reunite' is ignored, so remove it."),[n?.rbac,n?.access?.rbac].some(l=>l?.reunite!==void 0)&&r.add("The 'access.rbac.reunite' property is deprecated. Please use 'access.rbac.platform' property instead."),[n?.rbac,n?.access?.rbac].some(K)&&(o=!0);for(const n of r)p.warn(n);o&&t(new Error("You are using an incorrect format of 'rbac:' configuration. See: https://redocly.com/docs/realm/access"));const a=U(e);if(!e.env)return a;const c=s.map(([n,l])=>[n,l&&U(l)]);return{...a,env:Object.fromEntries(c)}}function U(e){const t=x(e),s=e.rbac?{...t,rbac:$(e.rbac)}:t;return e.access?.rbac?{...s,access:{...e.access,rbac:$(e.access.rbac)}}:s}function $(e){const{reunite:t,...s}=e;return t===void 0?s:{...s,platform:e.platform??t}}function X(e){if(!e.theme)return[];p.warn("The 'theme' property in redocly.yaml is deprecated. Please move all of the properties from 'theme' to the root of the config.");const t=[];for(const s of Object.keys(e.theme))e[s]==null?t.push(s):p.warn(`Detected both '${s}' and 'theme.${s}' properties in redocly.yaml. The 'theme.${s}' property will be ignored and needs to be removed or merged into the '${s}'.`);return t}function J(e,t){if(!e.theme||t.length===0)return e;const s={...e};for(const r of t)s[r]=e.theme[r];return delete s.theme,s}export{Y as ensureAccessBackwardsCompatibility,ue as readAndValidateConfig};
@@ -1,10 +1,10 @@
1
1
  import type { LifecycleContext, ProcessContentActions } from '../../types';
2
2
  import type { DetailedScorecardData } from './types.js';
3
3
  import { type ScorecardConfig, type RedoclyConfig } from '@redocly/theme/config';
4
- export declare function computeScorecard({ actions, context, scorecardConfig, reuniteConfig, }: {
4
+ export declare function computeScorecard({ actions, context, scorecardConfig, platformConfig, }: {
5
5
  actions: ProcessContentActions;
6
6
  context: LifecycleContext;
7
7
  scorecardConfig: ScorecardConfig;
8
- reuniteConfig: RedoclyConfig['reunite'];
8
+ platformConfig: RedoclyConfig['platform'];
9
9
  }): Promise<Record<string, DetailedScorecardData>>;
10
10
  //# sourceMappingURL=compute-scorecard.d.ts.map
@@ -1,4 +1,4 @@
1
- import w from"node:path";import S from"picomatch";import{removeLeadingSlash as O}from"@redocly/theme/core/utils";import{parsePathVersions as D}from"../../../utils/path/parse-path-versions.js";import{logger as c}from"../../tools/notifiers/logger.js";import{ScorecardStatus as d}from"./types.js";import{getTarget as M}from"./loaders/scorecard.js";const R=15;async function $({actions:t,context:i,scorecardConfig:e,reuniteConfig:o}){c.info("Computing scorecard...");const a=e.levels||[],n=a[0],u=await t.loadApiDocuments(i),r=B(u),h=e.ignore||[],v=S(h),m=new Set,f=c.startTiming(),p={},l={};for(const s of r){if(!L(s,v,m))continue;const g=s.customOutputRelativeFile||s.realRelativePath;m.add(g);const P=await N(s,i,e,a,n);p[s.realRelativePath]=P,l[P.relativePath]=P}return c.infoTime(f,"Scorecard processed"),T(p,o,e),l}function L(t,i,e){if(t.isVirtual||!t.definition)return!1;const o=t.customOutputRelativeFile||t.realRelativePath,a=O(t.realRelativePath);return i(a)?(c.info(`Skipping scorecard calculation for ignored file: ${t.realRelativePath}`),!1):!e.has(o)}function F(t,i,e){return t-1<i?d.BelowMinimum:i<e-1?d.Minimum:d.Highest}async function N(t,i,e,o,a){let n=a.name;if(e.targets?.length){const l=await i.getConfig(w.posix.dirname(t.realRelativePath)),s=t.definition.info?.["x-metadata"]||{},g={title:t.definition.info?.title,version:t.definition.info?.version,...s,...l.metadata};n=(await M(e.targets,g))?.minimumLevel||a.name}const{data:{levels:u,scorecardLevelIdx:r,scorecardLevel:h}}=await i.cache.load(t.realRelativePath,"scorecard"),v=o.findIndex(l=>l.name===n),m=F(r,v,o.length),f=t.customOutputRelativeFile||t.realRelativePath,p=D(f)?.versionName;return{levels:u,status:m,targetLevel:n,scorecardLevel:h,scorecardLevelIdx:r,relativePath:f,title:t.definition.info?.title,version:p}}function T(t,i,e){const o=Object.values(t).filter(r=>r.status===d.BelowMinimum);if(o.length===0)return;const a=o.map(r=>r.relativePath);let n=`Detected OpenAPI definitions with score below minimum level:
1
+ import w from"node:path";import S from"picomatch";import{removeLeadingSlash as O}from"@redocly/theme/core/utils";import{parsePathVersions as D}from"../../../utils/path/parse-path-versions.js";import{logger as c}from"../../tools/notifiers/logger.js";import{ScorecardStatus as d}from"./types.js";import{getTarget as M}from"./loaders/scorecard.js";const R=15;async function $({actions:t,context:i,scorecardConfig:e,platformConfig:o}){c.info("Computing scorecard...");const a=e.levels||[],n=a[0],u=await t.loadApiDocuments(i),r=B(u),h=e.ignore||[],v=S(h),m=new Set,f=c.startTiming(),p={},l={};for(const s of r){if(!L(s,v,m))continue;const g=s.customOutputRelativeFile||s.realRelativePath;m.add(g);const P=await N(s,i,e,a,n);p[s.realRelativePath]=P,l[P.relativePath]=P}return c.infoTime(f,"Scorecard processed"),T(p,o,e),l}function L(t,i,e){if(t.isVirtual||!t.definition)return!1;const o=t.customOutputRelativeFile||t.realRelativePath,a=O(t.realRelativePath);return i(a)?(c.info(`Skipping scorecard calculation for ignored file: ${t.realRelativePath}`),!1):!e.has(o)}function F(t,i,e){return t-1<i?d.BelowMinimum:i<e-1?d.Minimum:d.Highest}async function N(t,i,e,o,a){let n=a.name;if(e.targets?.length){const l=await i.getConfig(w.posix.dirname(t.realRelativePath)),s=t.definition.info?.["x-metadata"]||{},g={title:t.definition.info?.title,version:t.definition.info?.version,...s,...l.metadata};n=(await M(e.targets,g))?.minimumLevel||a.name}const{data:{levels:u,scorecardLevelIdx:r,scorecardLevel:h}}=await i.cache.load(t.realRelativePath,"scorecard"),v=o.findIndex(l=>l.name===n),m=F(r,v,o.length),f=t.customOutputRelativeFile||t.realRelativePath,p=D(f)?.versionName;return{levels:u,status:m,targetLevel:n,scorecardLevel:h,scorecardLevelIdx:r,relativePath:f,title:t.definition.info?.title,version:p}}function T(t,i,e){const o=Object.values(t).filter(r=>r.status===d.BelowMinimum);if(o.length===0)return;const a=o.map(r=>r.relativePath);let n=`Detected OpenAPI definitions with score below minimum level:
2
2
  - ${a.slice(0,R).map(()=>"%rp").join(`
3
3
  - `)}`;a.length>R&&(n+=`
4
4
  ... and ${a.length-R} more`),i?.ignoreLint??e.ignoreNonCompliant?c.warn(n,...a):c.error(n,...a)}function B(t){const i=[];for(const e of t){if(e.type!=="openapi")continue;const o=e.document;i.push({realRelativePath:e.realRelativePath,customOutputRelativeFile:e.customOutputRelativeFile,isVirtual:e.isVirtual,definition:{info:o.info}})}return i}export{$ as computeScorecard};
@@ -1 +1 @@
1
- import{writeFileSync as D}from"node:fs";import L from"path";import{SCORECARD_CLASSIC_FOLDER as P,SCORECARD_OUTPUT_FILE_NAME as w}from"../../constants/common.js";import{mapObject as E}from"../../../utils/object/map-object.js";import{envConfig as j}from"../../config/env-config.js";import{removeLeadingSlash as _}from"../../../utils/url/remove-leading-slash.js";import{combineUrls as O}from"@redocly/theme/core/utils";import{logger as F}from"../../tools/notifiers/logger.js";import{getRouteSlugToCatalogSlugMap as N}from"../../utils/catalog-classic/get-route-slug-to-catalog-slug-map.js";import{getCatalogSharedDataId as q}from"../../utils/catalog-classic/get-catalog-shared-data-id.js";import{deepEqual as I}from"../../../utils/object/deep-equal.js";import{ensureDir as U}from"../../utils/index.js";import{getTemplatePath as k}from"./get-template-path.js";import{getAllRuleNames as M}from"./lint.js";import{getScorecardConfig as T}from"./get-scorecard-config.js";import{getScorecardSlug as W}from"./shared-utils.js";import{scorecardConfigLoader as B}from"./loaders/scorecard-config.js";import{scorecardLoader as J}from"./loaders/scorecard.js";import{computeScorecard as $}from"./compute-scorecard.js";import{telemetryTraceStep as x}from"../../../cli/telemetry/helpers/trace-step.js";const G="scorecardClassic";let u={},R={};async function ge({id:b,requiredEntitlements:A,loadersPrefix:v=""}){let p=null,y=null,h=!1;return{id:b??"scorecardClassic",requiredEntitlements:A??["scorecard"],loaders:{[`${v}scorecard-config`]:B,[`${v}scorecard`]:J},async processContent(r,l){await x("com.redocly.build.plugin.scorecardClassic",async C=>{const a=r.createTemplate(G,k("./template/index.js")),g=await l.getConfig(),m=T(g),f=g?.reunite??{},i=[()=>p===null,()=>!I(u,m),()=>!I(R,f)].some(e=>e());u=m,C?.setAttribute("config",JSON.stringify(u||{})),R=f;const n=g.catalogClassic??{},c=u.levels||[];if(c.length<1)throw Error("Invalid configuration of levels config");i&&(p=await $({actions:r,context:l,scorecardConfig:u,reuniteConfig:R}));const{data:{configs:t}}=await l.cache.load("",`${v}scorecard-config`),{perLevel:d,all:s}=M(t);y={levelNames:c.map(e=>e.name),rules:s,rulesPerLevel:d};for(const[e,o]of Object.entries(n)){const S=O(o.slug,"scorecard/");r.addRoute({duplicateInAllLocales:!0,slug:S,fsPath:O(o.slug,"scorecard/"),templateId:a,hasClientRoutes:!0,excludeFromSidebar:!0,serverPropsGetterIds:[],sharedData:[{id:"scorecard",key:"scorecard"},{key:"catalog",id:q(e)}],getNavText:()=>Promise.resolve(o.title||"API design scorecard"),getStaticData:async()=>({props:{catalogId:e,catalogConfig:{...o,items:void 0},scorecardConfig:u,scorecardInfo:y}})})}})},afterRoutesCreated:async(r,l)=>{const C=await l.getConfig(),a=C.catalogClassic??{},g=T(C);if(Object.keys(a).length){const i=Object.keys(a).length>1?await N(r,l,a):null,n={};for(const[c,t]of Object.entries(p||{})){const d=r.getRouteByFsPath(c),s=d?.slug;if(s){if(d&&!g.ignoreMetadata){const e=i?i.get(s)??Object.values(a)[0].slug:Object.values(a)[0].slug,o=O(e,"scorecard/");d.metadata={...d.metadata,scorecardStatus:t.status,scorecardLevel:t.scorecardLevel,scorecardLevelIdx:t.scorecardLevelIdx,scorecardLevels:E(t.levels,S=>({uniqueErrors:S.uniqueErrors,uniqueWarnings:S.uniqueWarnings})),scoreCardSlug:o+"apis/"+encodeURIComponent(_(s))}}if(n[s]={...t,levels:E(t.levels,e=>({...e,problems:e.problems.map(o=>({ruleId:o.ruleId,severity:o.severity}))}))},!h){const e=L.resolve(r.outdir,P,W(s));D(U(e),JSON.stringify(t))}}}await r.createSharedData("scorecard",n)}const m={};for(const[i,n]of Object.entries(p||{})){const c=r.getRouteByFsPath(i)?.slug;c&&(m[c]=n)}const f=j.REDOCLY_METADATA_OUTPUT_FOLDER;!h&&f&&(F.info("Writing scorecard data..."),D(L.join(f,w),JSON.stringify({scorecardData:m,...y}))),h=!0}}}export{ge as scorecardClassicPlugin};
1
+ import{writeFileSync as R}from"node:fs";import L from"path";import{SCORECARD_CLASSIC_FOLDER as A,SCORECARD_OUTPUT_FILE_NAME as w}from"../../constants/common.js";import{mapObject as E}from"../../../utils/object/map-object.js";import{envConfig as j}from"../../config/env-config.js";import{removeLeadingSlash as _}from"../../../utils/url/remove-leading-slash.js";import{combineUrls as O}from"@redocly/theme/core/utils";import{logger as F}from"../../tools/notifiers/logger.js";import{getRouteSlugToCatalogSlugMap as N}from"../../utils/catalog-classic/get-route-slug-to-catalog-slug-map.js";import{getCatalogSharedDataId as q}from"../../utils/catalog-classic/get-catalog-shared-data-id.js";import{deepEqual as I}from"../../../utils/object/deep-equal.js";import{ensureDir as U}from"../../utils/index.js";import{getTemplatePath as k}from"./get-template-path.js";import{getAllRuleNames as M}from"./lint.js";import{getScorecardConfig as P}from"./get-scorecard-config.js";import{getScorecardSlug as W}from"./shared-utils.js";import{scorecardConfigLoader as B}from"./loaders/scorecard-config.js";import{scorecardLoader as J}from"./loaders/scorecard.js";import{computeScorecard as $}from"./compute-scorecard.js";import{telemetryTraceStep as x}from"../../../cli/telemetry/helpers/trace-step.js";const G="scorecardClassic";let g={},D={};async function ue({id:T,requiredEntitlements:b,loadersPrefix:v=""}){let p=null,y=null,h=!1;return{id:T??"scorecardClassic",requiredEntitlements:b??["scorecard"],loaders:{[`${v}scorecard-config`]:B,[`${v}scorecard`]:J},async processContent(r,l){await x("com.redocly.build.plugin.scorecardClassic",async C=>{const a=r.createTemplate(G,k("./template/index.js")),u=await l.getConfig(),m=P(u),f=u?.platform??{},i=[()=>p===null,()=>!I(g,m),()=>!I(D,f)].some(e=>e());g=m,C?.setAttribute("config",JSON.stringify(g||{})),D=f;const n=u.catalogClassic??{},c=g.levels||[];if(c.length<1)throw Error("Invalid configuration of levels config");i&&(p=await $({actions:r,context:l,scorecardConfig:g,platformConfig:D}));const{data:{configs:o}}=await l.cache.load("",`${v}scorecard-config`),{perLevel:d,all:s}=M(o);y={levelNames:c.map(e=>e.name),rules:s,rulesPerLevel:d};for(const[e,t]of Object.entries(n)){const S=O(t.slug,"scorecard/");r.addRoute({duplicateInAllLocales:!0,slug:S,fsPath:O(t.slug,"scorecard/"),templateId:a,hasClientRoutes:!0,excludeFromSidebar:!0,serverPropsGetterIds:[],sharedData:[{id:"scorecard",key:"scorecard"},{key:"catalog",id:q(e)}],getNavText:()=>Promise.resolve(t.title||"API design scorecard"),getStaticData:async()=>({props:{catalogId:e,catalogConfig:{...t,items:void 0},scorecardConfig:g,scorecardInfo:y}})})}})},afterRoutesCreated:async(r,l)=>{const C=await l.getConfig(),a=C.catalogClassic??{},u=P(C);if(Object.keys(a).length){const i=Object.keys(a).length>1?await N(r,l,a):null,n={};for(const[c,o]of Object.entries(p||{})){const d=r.getRouteByFsPath(c),s=d?.slug;if(s){if(d&&!u.ignoreMetadata){const e=i?i.get(s)??Object.values(a)[0].slug:Object.values(a)[0].slug,t=O(e,"scorecard/");d.metadata={...d.metadata,scorecardStatus:o.status,scorecardLevel:o.scorecardLevel,scorecardLevelIdx:o.scorecardLevelIdx,scorecardLevels:E(o.levels,S=>({uniqueErrors:S.uniqueErrors,uniqueWarnings:S.uniqueWarnings})),scoreCardSlug:t+"apis/"+encodeURIComponent(_(s))}}if(n[s]={...o,levels:E(o.levels,e=>({...e,problems:e.problems.map(t=>({ruleId:t.ruleId,severity:t.severity}))}))},!h){const e=L.resolve(r.outdir,A,W(s));R(U(e),JSON.stringify(o))}}}await r.createSharedData("scorecard",n)}const m={};for(const[i,n]of Object.entries(p||{})){const c=r.getRouteByFsPath(i)?.slug;c&&(m[c]=n)}const f=j.REDOCLY_METADATA_OUTPUT_FOLDER;!h&&f&&(F.info("Writing scorecard data..."),R(L.join(f,w),JSON.stringify({scorecardData:m,...y}))),h=!0}}}export{ue as scorecardClassicPlugin};
@@ -1 +1 @@
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
+ import{getHigherRole as c}from"./rbac.js";function R(e,r){if(!e)return;if(!p(e))return e;const o={platform:{},content:i(e)},n=f(e,r);for(const t of n)o.platform[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,platform:{...e.platform,...o.platform},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 f(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 i(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,i as getTeamFolderDefaults,g as parseTeamFoldersTemplate,u as parseTeamNameTemplate,f as parseTeams};
@@ -146,6 +146,9 @@ export declare const normalizeRbacConfig: (rbacConfig: RbacConfig) => {
146
146
  [x: string]: string;
147
147
  } | undefined;
148
148
  } | undefined;
149
+ platform?: {
150
+ [x: string]: string;
151
+ } | undefined;
149
152
  reunite?: {
150
153
  [x: string]: string;
151
154
  } | undefined;
@@ -1 +1 @@
1
- import y from"path";import O from"picomatch";import"../node-crypto-polyfill.js";import{REDOCLY_TEAMS_RBAC as T,REDOCLY_ROUTE_RBAC as R}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 _,RBAC_ALL_OTHER_TEAMS as l,DEFAULT_RBAC_SCOPE as x,GRAPHQL_SPEC_SLUG as B}from"../../constants/common.js";import{DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER as g,PUBLIC_API_DEFINITIONS_FOLDER as I,PUBLIC_ASSETS_FOLDER as b,RUNTIME_RESOURCES_DIR as $,SCORECARD_CLASSIC_FOLDER as W,SITEMAP_FILE_NAME as v,LLMS_TXT_FILE_NAME as Y}from"../constants/common.js";import{MCP_CREDENTIALS_ROUTE as G}from"../constants/mcp.js";import{ANONYMOUS_AUTH_DETAILS as H}from"../constants/rbac.js";import{removeTrailingSlash as S}from"../../utils/url/remove-trailing-slash.js";import{removeLeadingSlash as z}from"../../utils/url/remove-leading-slash.js";import{parsePathVersions as K}from"../../utils/path/parse-path-versions.js";import{reporter as V}from"../tools/notifiers/reporter.js";import{bold as J}from"../tools/notifiers/helpers/colors.js";import{shaDirPathShort as Q}from"../utils/crypto/sha-dir-path-short.js";import{isTruthy as X}from"../../utils/guards/is-truthy.js";import{isPathInFolder as C}from"../../utils/path/is-path-in-folder.js";import{canExpandConfig as Z,expandRbacConfig as q,getTeamFolderDefaults as tt,parseTeamFoldersTemplate as et,parseTeamNameTemplate as rt}from"./rbac-expand.js";import{getUserParamsFromCookies as nt}from"../web-server/auth.js";import{getDeeperGlobPattern as ot}from"./globs.js";import{EntitlementsProvider as st}from"../entitlements/entitlements-provider.js";const j=["NONE","READ","TRIAGE","WRITE","MAINTAIN","ADMIN"],ct=new Set(["x-parsed-md-description","x-parsed-md-summary"]),at=new Set(Object.values(w).map(t=>S(t.split(":")[0])));function wt(t,e){const r=j.indexOf(t.toUpperCase()),n=j.indexOf(e.toUpperCase());return r>n?t:e}const P={};function F(t,e){if(!t?.content)return _;const r=t.content,{slug:n,fsPath:o}=e;if(!n&&!o)return _;const s=f=>{const E=`slug:${f}`,p=P[E]??O(f);P[E]=p;const A=`fsPath:${f}`,L=P[A]??O(z(f));return P[A]=L,!!(n&&p(n))||!!(o&&L(o))};if(ut(n||o||"")&&Object.keys(r).filter(p=>s(p)).length===0)return r[x]||_;const i=Object.keys(r).filter(f=>s(f));if(i.length==0)return _;const u=i.map(f=>O.scan(f,{tokens:!0,parts:!0}));let h=u[0];for(let f=1;f<u.length;f++)h=ot(h,u[f]);return r[h.input]}function N(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 Bt(t,e,r={},n=!1){if(n&&Object.keys(r).length===0)return e.isAuthenticated;const o=r.features?.[t];return o?N(o,e.teams):!0}function bt(t,e){return d(t,H,e.access?.rbac||{},e.access?.requiresLogin||!1)}function d(t,e={},r={},n=!1){if(t.slug&&typeof t.slug=="string"&&at.has(S(t.slug))||(t.slug===U||t.slug===k||t.slug===G)&&st.instance().canAccessFeature("mcp"))return!0;if(n&&Object.keys(r).length===0)return!!e.isAuthenticated;const o=q(r,e.teams||[]),s=t[T]||F(o,t[R]||{});return Object.keys(s||{}).length===0?!1:Object.keys(s).length===1&&s[l]&&s[l].toLowerCase()!=="none"?!0:N(s,e?.teams||[],e?.email)}function $t(t,e,r,n,o){if(!t.startsWith(I)&&!t.startsWith(g))return!0;if(o){const u={[T]:o.rbac,[R]:{slug:o.slug,fsPath:o.sourcePath}};return d(u,n,e,r)}const s=t.replace(new RegExp(`^${I}/`),"").replace(new RegExp(`^${g}/`),"").replace(new RegExp(`^${B}/`),""),a=s==="."?"":s,i={[R]:{slug:t,fsPath:a},slug:t};return d(i,n,e,r)}function Wt(t,e,r,n,o){if(!C(t,b))return[`/${v}`,`/${Y}`].includes(t)||[$,W].some(A=>C(t,A));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:a,ext:i}=y.parse(t),u=a.split(".")[0],h=i.split(".").join(""),E=c==="."?"":c,p={[R]:{slug:t,fsPath:y.posix.join(E,`${u}.${h}`)},slug:t};return d(p,o,e,r)}async function vt(t,e){const{isAuthenticated:r=!1,idpAccessToken:n,federatedAccessToken:o,federatedIdToken:s,...c}=await nt(t,e),{teams:a=[]}=c;let i;return r?i=a.filter(u=>u!==D):i=[D],{isAuthenticated:r,idpAccessToken:n,teams:i,claims:c}}function M(t,e,r={},n=!1){if(!t)return t;if(Array.isArray(t)){const o=[];for(const s of t){const c=M(s,e,r,n);c!==void 0&&o.push(c)}return o}if(typeof t=="object"){if(!d(t,e,r,n))return;let o=!1;const s={};for(const c in t){if(c===T||c===R)continue;if(ct.has(c)){s[c]=t[c];continue}const a=M(t[c],e,r,n);if(!t.link&&(c==="items"||c==="menu"&&t.type==="tab")&&Array.isArray(a)&&a.length===0&&t[c].length!==0){o=!0;continue}a!==void 0&&(s[c]=a),c==="paths"&&it(a)&&ft(a)}return o?void 0:s}return t}function Yt(t){return typeof t=="string"?t.split(" ").filter(Boolean):Array.isArray(t)?t.map(e=>e.toString()):[]}function Gt(t,e){if(!e)return;const r=e.content;if(!r)return e;const n=Object.entries(r).flatMap(([s,c])=>s===x?[[s,c]]:[[s,c]].concat(t.localeFolders.map(a=>[s.startsWith("/")?`/${a.toLocaleLowerCase()}${s}`:y.posix.join(t.localizationFolder,a,s),c]))),o=Object.fromEntries(n);return{...e,content:o}}async function Ht(t,e){if(!e)return{};const r={},n=new Set(t.scan().flatMap(({relativePath:o})=>{const{versionFolderPath:s}=K(o)||{},c=y.dirname(o);return s?[s,c]:c}));for(const o of n)r[Q(o)]=o;return r}const m=t=>typeof t=="object"&&t!==null&&!Array.isArray(t);function it(t){return m(t)&&Object.keys(t).length>0}function ft(t){for(const e of Object.keys(t)){const r=t[e];m(r)&&Object.keys(r).length===0&&delete t[e]}}function ut(t){return t?t.split("/").filter(Boolean).some(r=>r.startsWith(".")):!1}const lt=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},zt=async t=>{if(t){if(Object.keys(t).length===0)return{};if(lt(t))return mt(t);await V.panicOnContentError(`You are using an incorrect format of ${J("rbac:")} configuration. See: https://redocly.com/docs/realm/access`)}},mt=t=>{const e={...t};if(e.content){const r={};for(const n in e.content)if(e.content[n]!==void 0){const o=S(n);r[o]=e.content[n]}e.content=r}return e};function Kt(t,e){const r=t.fsPath,n=t.slug,o=[],s=Z(e)&&(r||n)?et(e,[r,n].filter(X)):null;if(s){const a=e?.teamNamePatterns?.map(i=>i.replace("{teamPathSegment}",s.teamPathSegment).replace("{projectRole}","read"))??[];o.push(...a)}const c=F(s?{content:{...tt(e),...e.content}}:e,t);return o.push(...pt(c)),ht(e,o)}function pt(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 ht(t,e){return e.map(n=>rt(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,Gt as applyL10nToRbacConfig,Wt as canAccessAsset,Bt as canAccessFeature,d as canAccessResource,$t as canDownloadApiDefinition,ht as expandTeamsForRead,pt as extractTeamsFromScopeItems,M as filterDataByAccessDeep,vt as getAuthDetailsFromCookies,wt as getHigherRole,Kt as getRbacTeamsListForResource,F as getScopeItemsForResource,N as hasScopeAccess,lt as isRbacConfigValid,bt as isResourcePubliclyAccessible,mt as normalizeRbacConfig,zt as parseRbacConfig,Yt as parseTeamClaimToArray,Ht as resolveDirectoryHashes};
1
+ import y from"path";import O from"picomatch";import"../node-crypto-polyfill.js";import{REDOCLY_TEAMS_RBAC as T,REDOCLY_ROUTE_RBAC as R}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 _,RBAC_ALL_OTHER_TEAMS as l,DEFAULT_RBAC_SCOPE as x,GRAPHQL_SPEC_SLUG as B}from"../../constants/common.js";import{DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER as g,PUBLIC_API_DEFINITIONS_FOLDER as I,PUBLIC_ASSETS_FOLDER as b,RUNTIME_RESOURCES_DIR as $,SCORECARD_CLASSIC_FOLDER as W,SITEMAP_FILE_NAME as v,LLMS_TXT_FILE_NAME as Y}from"../constants/common.js";import{MCP_CREDENTIALS_ROUTE as G}from"../constants/mcp.js";import{ANONYMOUS_AUTH_DETAILS as H}from"../constants/rbac.js";import{removeTrailingSlash as S}from"../../utils/url/remove-trailing-slash.js";import{removeLeadingSlash as z}from"../../utils/url/remove-leading-slash.js";import{parsePathVersions as K}from"../../utils/path/parse-path-versions.js";import{reporter as V}from"../tools/notifiers/reporter.js";import{bold as J}from"../tools/notifiers/helpers/colors.js";import{shaDirPathShort as Q}from"../utils/crypto/sha-dir-path-short.js";import{isTruthy as X}from"../../utils/guards/is-truthy.js";import{isPathInFolder as C}from"../../utils/path/is-path-in-folder.js";import{canExpandConfig as Z,expandRbacConfig as q,getTeamFolderDefaults as tt,parseTeamFoldersTemplate as et,parseTeamNameTemplate as rt}from"./rbac-expand.js";import{getUserParamsFromCookies as nt}from"../web-server/auth.js";import{getDeeperGlobPattern as ot}from"./globs.js";import{EntitlementsProvider as st}from"../entitlements/entitlements-provider.js";const j=["NONE","READ","TRIAGE","WRITE","MAINTAIN","ADMIN"],ct=new Set(["x-parsed-md-description","x-parsed-md-summary"]),at=new Set(Object.values(w).map(t=>S(t.split(":")[0])));function wt(t,e){const r=j.indexOf(t.toUpperCase()),n=j.indexOf(e.toUpperCase());return r>n?t:e}const P={};function F(t,e){if(!t?.content)return _;const r=t.content,{slug:n,fsPath:o}=e;if(!n&&!o)return _;const s=f=>{const E=`slug:${f}`,p=P[E]??O(f);P[E]=p;const A=`fsPath:${f}`,L=P[A]??O(z(f));return P[A]=L,!!(n&&p(n))||!!(o&&L(o))};if(ut(n||o||"")&&Object.keys(r).filter(p=>s(p)).length===0)return r[x]||_;const i=Object.keys(r).filter(f=>s(f));if(i.length==0)return _;const u=i.map(f=>O.scan(f,{tokens:!0,parts:!0}));let h=u[0];for(let f=1;f<u.length;f++)h=ot(h,u[f]);return r[h.input]}function N(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 Bt(t,e,r={},n=!1){if(n&&Object.keys(r).length===0)return e.isAuthenticated;const o=r.features?.[t];return o?N(o,e.teams):!0}function bt(t,e){return d(t,H,e.access?.rbac||{},e.access?.requiresLogin||!1)}function d(t,e={},r={},n=!1){if(t.slug&&typeof t.slug=="string"&&at.has(S(t.slug))||(t.slug===U||t.slug===k||t.slug===G)&&st.instance().canAccessFeature("mcp"))return!0;if(n&&Object.keys(r).length===0)return!!e.isAuthenticated;const o=q(r,e.teams||[]),s=t[T]||F(o,t[R]||{});return Object.keys(s||{}).length===0?!1:Object.keys(s).length===1&&s[l]&&s[l].toLowerCase()!=="none"?!0:N(s,e?.teams||[],e?.email)}function $t(t,e,r,n,o){if(!t.startsWith(I)&&!t.startsWith(g))return!0;if(o){const u={[T]:o.rbac,[R]:{slug:o.slug,fsPath:o.sourcePath}};return d(u,n,e,r)}const s=t.replace(new RegExp(`^${I}/`),"").replace(new RegExp(`^${g}/`),"").replace(new RegExp(`^${B}/`),""),a=s==="."?"":s,i={[R]:{slug:t,fsPath:a},slug:t};return d(i,n,e,r)}function Wt(t,e,r,n,o){if(!C(t,b))return[`/${v}`,`/${Y}`].includes(t)||[$,W].some(A=>C(t,A));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:a,ext:i}=y.parse(t),u=a.split(".")[0],h=i.split(".").join(""),E=c==="."?"":c,p={[R]:{slug:t,fsPath:y.posix.join(E,`${u}.${h}`)},slug:t};return d(p,o,e,r)}async function vt(t,e){const{isAuthenticated:r=!1,idpAccessToken:n,federatedAccessToken:o,federatedIdToken:s,...c}=await nt(t,e),{teams:a=[]}=c;let i;return r?i=a.filter(u=>u!==D):i=[D],{isAuthenticated:r,idpAccessToken:n,teams:i,claims:c}}function M(t,e,r={},n=!1){if(!t)return t;if(Array.isArray(t)){const o=[];for(const s of t){const c=M(s,e,r,n);c!==void 0&&o.push(c)}return o}if(typeof t=="object"){if(!d(t,e,r,n))return;let o=!1;const s={};for(const c in t){if(c===T||c===R)continue;if(ct.has(c)){s[c]=t[c];continue}const a=M(t[c],e,r,n);if(!t.link&&(c==="items"||c==="menu"&&t.type==="tab")&&Array.isArray(a)&&a.length===0&&t[c].length!==0){o=!0;continue}a!==void 0&&(s[c]=a),c==="paths"&&it(a)&&ft(a)}return o?void 0:s}return t}function Yt(t){return typeof t=="string"?t.split(" ").filter(Boolean):Array.isArray(t)?t.map(e=>e.toString()):[]}function Gt(t,e){if(!e)return;const r=e.content;if(!r)return e;const n=Object.entries(r).flatMap(([s,c])=>s===x?[[s,c]]:[[s,c]].concat(t.localeFolders.map(a=>[s.startsWith("/")?`/${a.toLocaleLowerCase()}${s}`:y.posix.join(t.localizationFolder,a,s),c]))),o=Object.fromEntries(n);return{...e,content:o}}async function Ht(t,e){if(!e)return{};const r={},n=new Set(t.scan().flatMap(({relativePath:o})=>{const{versionFolderPath:s}=K(o)||{},c=y.dirname(o);return s?[s,c]:c}));for(const o of n)r[Q(o)]=o;return r}const m=t=>typeof t=="object"&&t!==null&&!Array.isArray(t);function it(t){return m(t)&&Object.keys(t).length>0}function ft(t){for(const e of Object.keys(t)){const r=t[e];m(r)&&Object.keys(r).length===0&&delete t[e]}}function ut(t){return t?t.split("/").filter(Boolean).some(r=>r.startsWith(".")):!1}const lt=t=>{if(t&&m(t)&&("content"in t&&m(t.content)||"platform"in t&&m(t.platform)||"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},zt=async t=>{if(t){if(Object.keys(t).length===0)return{};if(lt(t))return mt(t);await V.panicOnContentError(`You are using an incorrect format of ${J("rbac:")} configuration. See: https://redocly.com/docs/realm/access`)}},mt=t=>{const e={...t};if(e.content){const r={};for(const n in e.content)if(e.content[n]!==void 0){const o=S(n);r[o]=e.content[n]}e.content=r}return e};function Kt(t,e){const r=t.fsPath,n=t.slug,o=[],s=Z(e)&&(r||n)?et(e,[r,n].filter(X)):null;if(s){const a=e?.teamNamePatterns?.map(i=>i.replace("{teamPathSegment}",s.teamPathSegment).replace("{projectRole}","read"))??[];o.push(...a)}const c=F(s?{content:{...tt(e),...e.content}}:e,t);return o.push(...pt(c)),ht(e,o)}function pt(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 ht(t,e){return e.map(n=>rt(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,Gt as applyL10nToRbacConfig,Wt as canAccessAsset,Bt as canAccessFeature,d as canAccessResource,$t as canDownloadApiDefinition,ht as expandTeamsForRead,pt as extractTeamsFromScopeItems,M as filterDataByAccessDeep,vt as getAuthDetailsFromCookies,wt as getHigherRole,Kt as getRbacTeamsListForResource,F as getScopeItemsForResource,N as hasScopeAccess,lt as isRbacConfigValid,bt as isResourcePubliclyAccessible,mt as normalizeRbacConfig,zt as parseRbacConfig,Yt as parseTeamClaimToArray,Ht as resolveDirectoryHashes};
@@ -1 +1 @@
1
- import{envConfig as p}from"../config/env-config.js";import{logger as l}from"../tools/notifiers/logger.js";import{reporter as r}from"../tools/notifiers/reporter.js";import{isMarkdocError as d}from"../plugins/markdown/errors.js";import{telemetryTraceStep as g}from"../../cli/telemetry/helpers/trace-step.js";import{writeProblemsReport as u}from"./write-problems-report.js";async function M(e){await g("com.redocly.build.validationErrors",async o=>{let t=0;for(const[c,f]of e.cache.errors.entries()){const m=c.split(":")[1];for(const i of f)d(i)?(r.reportMarkdocProblem(i),i.type!=="BROKEN_LINK"&&t++):await r.panicOnBuild(i.message,m)}const a=await e.getConfig();if(!p.isBuildMode)return;const n=r.getPageRenderProblems().length;o?.setAttribute("markdocErrors",t.toString()),o?.setAttribute("pageRenderErrors",n.toString()),t>0&&!a?.reunite?.ignoreMarkdocErrors?await s(`Need to fix ${t} Markdoc issue(s) or set the \`ignoreMarkdocErrors\` option to \`true\` in the \`reunite\` section of your \`redocly.yaml\``):n>0&&await s(`Need to fix ${n} runtime error(s) in React pages`)})}async function s(e){r.printErrors();try{u()}catch(o){l.warn("Failed to write the problems report: "+o.message)}await r.panic(e)}export{M as reportAllErrors};
1
+ import{envConfig as p}from"../config/env-config.js";import{logger as l}from"../tools/notifiers/logger.js";import{reporter as r}from"../tools/notifiers/reporter.js";import{isMarkdocError as d}from"../plugins/markdown/errors.js";import{telemetryTraceStep as g}from"../../cli/telemetry/helpers/trace-step.js";import{writeProblemsReport as u}from"./write-problems-report.js";async function M(o){await g("com.redocly.build.validationErrors",async e=>{let t=0;for(const[c,f]of o.cache.errors.entries()){const m=c.split(":")[1];for(const i of f)d(i)?(r.reportMarkdocProblem(i),i.type!=="BROKEN_LINK"&&t++):await r.panicOnBuild(i.message,m)}const n=await o.getConfig();if(!p.isBuildMode)return;const s=r.getPageRenderProblems().length;e?.setAttribute("markdocErrors",t.toString()),e?.setAttribute("pageRenderErrors",s.toString()),t>0&&!n?.platform?.ignoreMarkdocErrors?await a(`Need to fix ${t} Markdoc issue(s) or set the \`ignoreMarkdocErrors\` option to \`true\` in the \`platform\` section of your \`redocly.yaml\``):s>0&&await a(`Need to fix ${s} runtime error(s) in React pages`)})}async function a(o){r.printErrors();try{u()}catch(e){l.warn("Failed to write the problems report: "+e.message)}await r.panic(o)}export{M as reportAllErrors};
@@ -1,7 +1,8 @@
1
1
  import { type LookupFunction } from 'node:net';
2
2
  /**
3
3
  * Returns a drop-in replacement for `dns.lookup` that errors with
4
- * {@link SsrfBlockedError} if any resolved address is in the blocklist.
4
+ * {@link SsrfBlockedError} if any resolved address is in the blocklist. The error message names
5
+ * the hostname only, never a resolved address, so callers may show it to the client.
5
6
  *
6
7
  * Install on an http/https agent (`connect.lookup` for undici, or `lookup` for a node:https Agent)
7
8
  * so that every socket connection — including those made while following redirects — is re-validated
@@ -1 +1 @@
1
- import{lookup as c}from"node:dns";import{isIP as k}from"node:net";import{isBlockedIp as v,isLoopback as w,SsrfBlockedError as n}from"./ssrf-blocklist.js";function L({allowLoopback:p=!1}={}){const f=o=>v(o)&&!(p&&w(o));return(o,m,r)=>{const s=m,l=t=>{if(s.all){r(null,t);return}const[e]=t;if(!e){r(new n(`Hostname "${o}" did not resolve`),"",0);return}r(null,e.address,e.family)},u=k(o);if(u!==0){if(f(o)){r(new n(`Hostname "${o}" is a blocked address`),"",0);return}l([{address:o,family:u}]);return}c(o,{...s,all:!0},(t,e)=>{if(t){r(t,"",0);return}const d=e.filter(({address:i})=>f(i));if(d.length>0){const i=d.map(({address:$})=>$).join(", ");r(new n(`Hostname "${o}" resolves to a blocked address: ${i}`),"",0);return}if(!e.length){r(new n(`Hostname "${o}" did not resolve`),"",0);return}l(e)})}}export{L as createSsrfSafeLookup};
1
+ import{lookup as m}from"node:dns";import{isIP as w}from"node:net";import{isBlockedIp as H,isLoopback as $,SsrfBlockedError as i}from"./ssrf-blocklist.js";function B({allowLoopback:l=!1}={}){const n=r=>H(r)&&!(l&&$(r));return(r,d,o)=>{const f=d,s=t=>{if(f.all){o(null,t);return}const[e]=t;if(!e){o(new i(`Hostname "${r}" did not resolve`),"",0);return}o(null,e.address,e.family)},u=w(r);if(u!==0){if(n(r)){o(new i(`Hostname "${r}" is a blocked address`),"",0);return}s([{address:r,family:u}]);return}m(r,{...f,all:!0},(t,e)=>{if(t){o(t,"",0);return}if(e.some(({address:p})=>n(p))){o(new i(`Hostname "${r}" resolves to a blocked address`),"",0);return}if(!e.length){o(new i(`Hostname "${r}" did not resolve`),"",0);return}s(e)})}}export{B as createSsrfSafeLookup};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/redoc-reef",
3
- "version": "0.140.0-next.8",
3
+ "version": "0.140.0-next.9",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,7 +34,7 @@
34
34
  "@opentelemetry/semantic-conventions": "1.40.0",
35
35
  "@redocly/ajv": "8.18.0",
36
36
  "@redocly/api-docs": "0.5.0-next.6",
37
- "@redocly/config": "0.69.1",
37
+ "@redocly/config": "0.70.0",
38
38
  "@redocly/markdoc": "0.7.0-beta.2",
39
39
  "@redocly/openapi-core": "2.72.1",
40
40
  "@redocly/portal-legacy-ui": "0.19.0",